Databases

Various databases can be used as long as they implement the database plugin interfaces.

Database Tables (scroll down for complete schema and discussion):

NameFunction
usersyour users, of course
ldapgroupsprimary and secondary groups available
includegroupsstore 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 = 5002

13 primarygroup = 5502

14 passsha256 = "6478579e37aff45f013e14eeb30b3cc56c72ccdc310123bcdf53e0333e3f416a" # dogood

15 

16[[users]]

17 name = "serviceuser"

18 mail = "serviceuser@example.com"

19 uidnumber = 5003

20 passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret

21 primarygroup = 5502

22 [[users.capabilities]]

23 action = "search"

24 object = "*"

25 

26[[users]]

27 name = "user4"

28 uidnumber = 5003

29 primarygroup = 5504

30 othergroups = [5505, 5506]

31 passsha256 = "652c7dc687d98c9889304ed2e408c74b611e86a40caa51c4b43f1dd5913c5cd0" # mysecret

32 [[users.customattributes]]

33 employeetype = ["Intern", "Temp"]

34 employeenumber = [12345, 54321]

35 

36[[groups]]

37 name = "superheros"

38 gidnumber = 5501

39 

40[[groups]]

41 name = "svcaccts"

42 gidnumber = 5502

43 

44[[groups]]

45 name = "civilians"

46 gidnumber = 5503

47 includegroups = [ 5501 ]

48 

49[[groups]]

50 name = "caped"

51 gidnumber = 5504

52 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=com

10memberOf: cn=svcaccts,ou=groups,dc=militate,dc=com

11 

12uid: serviceuser

13ou: caped

14memberOf: cn=caped,ou=groups,dc=militate,dc=com

15 

16uid: user4

17ou: caped

18memberOf: cn=caped,ou=groups,dc=militate,dc=com

19memberOf: cn=lovesailing,ou=groups,dc=militate,dc=com

20memberOf: 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

http://localhost:8000/docs/content/images/glauth-simple-schema.png

users table

this table contains all LDAP information pertaining to user accounts, including links to other tables

FieldFunction
idinternal id number, used by glauth
nameLDAP name (i.e. cn, uid)
uidnumberLDAP UID attribute
primarygroupAn LDAP group’s GID attribute; also used to build ou attribute; used to build memberOf
othergroupsA comma-separated list of GID attributes; used to build memberOf
givennameLDAP GivenName attribute, i.e. an account’s first name
snLDAP sn attribute, i.e. an account’s last name
mailLDAP mail attribute, i.e. email address; also used as userPrincipalName
loginshellLDAP loginShell attribute, pushed to the client, may be ignored
homedirectoryLDAP homeDirectory attribute, pushed to the client, may be ignored
disabledLDAP accountStatus attribute, if non-zero returns “inactive”
passha256SHA256 account password
passbcryptBCRYPT-encrypted account password
otpsecretOTP secret, for two-factor authentication
yubikeyUBIKey, for two-factor authentication
sshkeysA comma-separated list of sshPublicKey attributes
custattrA JSON-encoded string, containing arbitrary additional attributes; must be {} by default

ldapgroups table

this table represents primary and secondary LDAP groups

FieldFunction
idinternal id number, used by glauth
nameLDAP group name (i.e. cn or ou depending on context)
gidnumberLDAP GID attribute

includegroups table

this table is used to represent groups containing other groups and inheriting their attributes

FieldFunction
idinternal id number, used by glauth
parentgroupidthe LDAP group id containing another group, used by glauth
includegroupidthe 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

FieldFunction
idinternal id number, used by glauth
useridinternal user id number, used by glauth
actionstring representing an allowed action, e.g. “search”
objectstring 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 joined 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.