Databases
Various databases can be used as long as they implement the database plugin interfaces.
Database Tables (scroll down for complete schema and discussion):
Name | Function |
---|---|
users | your users, of course |
ldapgroups | primary and secondary groups available |
includegroups | store group indirections (equivalent to includegroups directive) |
Note that, in users
, othergroups
is a comma-separated list of group ids.
Here is how to insert some example data in your database using its REPL:
1INSERT INTO ldapgroups(name, gidnumber) 2 VALUES('superheros', 5501); 3INSERT INTO ldapgroups(name, gidnumber) 4 VALUES('svcaccts', 5502); 5INSERT INTO ldapgroups(name, gidnumber) 6 VALUES('civilians', 5503); 7INSERT INTO ldapgroups(name, gidnumber) 8 VALUES('caped', 5504); 9INSERT INTO ldapgroups(name, gidnumber)10 VALUES('lovesailing', 5505);11INSERT INTO ldapgroups(name, gidnumber)12 VALUES('smoker', 5506);13INSERT INTO includegroups(parentgroupid, includegroupid)14 VALUES(5503, 5501);15INSERT INTO includegroups(parentgroupid, includegroupid)16 VALUES(5504, 5502);17INSERT INTO includegroups(parentgroupid, includegroupid)18 VALUES(5504, 5501);19INSERT INTO users(name, uidnumber, primarygroup, passsha256)20 VALUES('hackers', 5001, 5501,21 '6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a');22INSERT INTO users(name, uidnumber, primarygroup, passsha256)23 VALUES('johndoe', 5002, 5502,24 '6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a');25INSERT INTO users(name, mail, uidnumber, primarygroup, passsha256)26 VALUES('serviceuser', "serviceuser@example.com", 5003, 5502,27 '652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0');28INSERT INTO users(name, uidnumber, primarygroup, passsha256, othergroups)29 VALUES('user4', 5004, 5504,30 '652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0',31 '5505,5506');32INSERT INTO capabilities(userid, action, object)33 VALUES(5001, "search", "ou=superheros,dc=glauth,dc=com");34INSERT INTO capabilities(userid, action, object)35 VALUES(5003, "search", "*");
This should be equivalent to this configuration:
1[[users]] 2 name = "hackers" 3 uidnumber = 5001 4 primarygroup = 5501 5 passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood 6 [[users.capabilities]] 7 action = "search" 8 object = "ou=superheros,dc=glauth,dc=com" 9 10[[users]]11 name = "johndoe"12 uidnumber = 500213 primarygroup = 550214 passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood15 16[[users]]17 name = "serviceuser"18 mail = "serviceuser@example.com"19 uidnumber = 500320 passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret21 primarygroup = 550222 [[users.capabilities]]23 action = "search"24 object = "*"25 26[[users]]27 name = "user4"28 uidnumber = 500329 primarygroup = 550430 othergroups = [5505, 5506]31 passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret32 [[users.customattributes]]33 employeetype = ["Intern", "Temp"]34 employeenumber = [12345, 54321]35 36[[groups]]37 name = "superheros"38 gidnumber = 550139 40[[groups]]41 name = "svcaccts"42 gidnumber = 550243 44[[groups]]45 name = "civilians"46 gidnumber = 550347 includegroups = [ 5501 ]48 49[[groups]]50 name = "caped"51 gidnumber = 550452 includegroups = [ 5502, 5501 ]
and LDAP should return these memberOf
values:
1uid: hackers 2ou: superheros 3memberOf: cn=caped,ou=groups,dc=militate,dc=com 4memberOf: cn=civilians,ou=groups,dc=militate,dc=com 5memberOf: cn=superheros,ou=groups,dc=militate,dc=com 6 7uid: johndoe 8ou: svcaccts 9memberOf: cn=caped,ou=groups,dc=militate,dc=com10memberOf: cn=svcaccts,ou=groups,dc=militate,dc=com11 12uid: serviceuser13ou: caped14memberOf: cn=caped,ou=groups,dc=militate,dc=com15 16uid: user417ou: caped18memberOf: cn=caped,ou=groups,dc=militate,dc=com19memberOf: cn=lovesailing,ou=groups,dc=militate,dc=com20memberOf: cn=smoker,ou=groups,dc=militate,dc=com
If you have the ldap client package installed, this can be easily confirmed by running
1ldapsearch -LLL -H ldap://localhost:3893 -D cn=serviceuser,ou=svcaccts,dc=glauth,dc=com -w mysecret -x -bdc=glauth,dc=com cn=hackers
and so on.
Database Schema
users table
this table contains all LDAP information pertaining to user accounts, including links to other tables
Field | Function |
---|---|
id | internal id number, used by glauth |
name | LDAP name (i.e. cn , uid ) |
uidnumber | LDAP UID attribute |
primarygroup | An LDAP group’s GID attribute; also used to build ou attribute; used to build memberOf |
othergroups | A comma-separated list of GID attributes; used to build memberOf |
givenname | LDAP GivenName attribute, i.e. an account’s first name |
sn | LDAP sn attribute, i.e. an account’s last name |
LDAP mail attribute, i.e. email address; also used as userPrincipalName | |
loginshell | LDAP loginShell attribute, pushed to the client, may be ignored |
homedirectory | LDAP homeDirectory attribute, pushed to the client, may be ignored |
disabled | LDAP accountStatus attribute, if non-zero returns “inactive” |
passha256 | SHA256 account password |
passbcrypt | BCRYPT-encrypted account password |
otpsecret | OTP secret, for two-factor authentication |
yubikey | UBIKey, for two-factor authentication |
sshkeys | A comma-separated list of sshPublicKey attributes |
custattr | A JSON-encoded string, containing arbitrary additional attributes; must be {} by default |
ldapgroups table
this table represents primary and secondary LDAP groups
Field | Function |
---|---|
id | internal id number, used by glauth |
name | LDAP group name (i.e. cn or ou depending on context) |
gidnumber | LDAP GID attribute |
includegroups table
this table is used to represent groups containing other groups and inheriting their attributes
Field | Function |
---|---|
id | internal id number, used by glauth |
parentgroupid | the LDAP group id containing another group, used by glauth |
includegroupid | the LDAP group id contained in the parent group, used by glauth |
capabilities table
this table is used to retrieve capabilities granted to users linked to it from the users table
Field | Function |
---|---|
id | internal id number, used by glauth |
userid | internal user id number, used by glauth |
action | string representing an allowed action, e.g. “search” |
object | string representing scope of allowed action, e.g. “ou=superheros,dc=glauth,dc=com” |
Discussion: database schema
While GLAuth is not meant to support millions of user accounts, some decent performance is still expected! In fact, when searching through records using a database query, we should see a performance of O(log n) as opposed to, when searching through a flat config, O(n).
While it would be friendlier to offer related attributes in join
ed tables, we may end up re-creating a “browse” scenario unintentionally.
For instance, when retrieving custom attributes, we could go through an attribute table: custattr[userid, attribute, value#n]
However, this means that a join
statement between the account table and the custom attribute table would yield the cartesian product of each account x attributes; we would need to iterate through the results and collate them.
Alternatively, in Postgres and MySQL, we could rely on the database engine’s built-in support for crosstab
which pivots the second table’s results into corresponding columns. This would not be supported in SQLite and would also mean building pretty nasty execution plans.
So, what’s the decision?
In GLAuth 2.x, when including information that does not benefit from being normalized (e.g. custom attributes) we are following the “nosql” trend (irony!) of storing this data in a JSON structure.