diff --git a/doc/Yunohost_LDAP_documentation/LDAP_Liaison_logique_entre_objets.png b/doc/Yunohost_LDAP_documentation/LDAP_Liaison_logique_entre_objets.png new file mode 100644 index 00000000..5947eaa5 Binary files /dev/null and b/doc/Yunohost_LDAP_documentation/LDAP_Liaison_logique_entre_objets.png differ diff --git a/doc/Yunohost_LDAP_documentation/LDAP_Representation_logique.png b/doc/Yunohost_LDAP_documentation/LDAP_Representation_logique.png new file mode 100644 index 00000000..518f3473 Binary files /dev/null and b/doc/Yunohost_LDAP_documentation/LDAP_Representation_logique.png differ diff --git a/doc/Yunohost_LDAP_documentation/LDAP_Schema_1.odg b/doc/Yunohost_LDAP_documentation/LDAP_Schema_1.odg new file mode 100644 index 00000000..b9cf912b Binary files /dev/null and b/doc/Yunohost_LDAP_documentation/LDAP_Schema_1.odg differ diff --git a/doc/Yunohost_LDAP_documentation/LDAP_Schema_2.odg b/doc/Yunohost_LDAP_documentation/LDAP_Schema_2.odg new file mode 100644 index 00000000..a73e8d54 Binary files /dev/null and b/doc/Yunohost_LDAP_documentation/LDAP_Schema_2.odg differ diff --git a/doc/Yunohost_LDAP_documentation/Schema_LDAP_1.png b/doc/Yunohost_LDAP_documentation/Schema_LDAP_1.png new file mode 100644 index 00000000..ca913b4c Binary files /dev/null and b/doc/Yunohost_LDAP_documentation/Schema_LDAP_1.png differ diff --git a/doc/Yunohost_LDAP_documentation/Schema_LDAP_2.png b/doc/Yunohost_LDAP_documentation/Schema_LDAP_2.png new file mode 100644 index 00000000..27b183f7 Binary files /dev/null and b/doc/Yunohost_LDAP_documentation/Schema_LDAP_2.png differ diff --git a/doc/ldap.rst b/doc/ldap.rst index 221b55da..a30bc3fc 100644 --- a/doc/ldap.rst +++ b/doc/ldap.rst @@ -122,7 +122,7 @@ Here is the method docstring: Users LDAP schema ----------------- -According to :file:`ldapvi` this is the user schema (on YunoHost 2.7): +According to :file:`ldapvi` this is the user schema (on YunoHost >3.7): :: @@ -131,6 +131,7 @@ According to :file:`ldapvi` this is the user schema (on YunoHost 2.7): objectClass: mailAccount objectClass: inetOrgPerson objectClass: posixAccount + objectClass: userPermissionYnh loginShell: /bin/false uidNumber: 80833 maildrop: the_unix_username # why? @@ -147,6 +148,11 @@ According to :file:`ldapvi` this is the user schema (on YunoHost 2.7): mail: webmaster@domain.com mail: postmaster@domain.com givenName: first_name + memberOf: cn=the_unix_username,ou=groups,dc=yunohost,dc=org + memberOf: cn=all_users,ou=groups,dc=yunohost,dc=org + permission: cn=main.mail,ou=permission,dc=yunohost,dc=org + permission: cn=main.metronome,ou=permission,dc=yunohost,dc=org + The admin user is a special case that looks like this: @@ -176,15 +182,6 @@ Other user related schemas: gidNumber: 4001 cn: admins - # path: cn=sftpusers,ou=groups,dc=yunohost,dc=org - objectClass: posixGroup - objectClass: top - gidNumber: 4002 - cn: sftpusers - memberUid: admin - memberUid: alice - # and all other users - # path: cn=admin,ou=sudo,dc=yunohost,dc=org # this entry seems to specify which unix user is a sudoer cn: admin @@ -220,9 +217,90 @@ Apparently we could also access one user using the following path (and not query If you want specific attributes look at the general documentation on how to read from LDAP a bit above of this section. -Users LDAP schema + +Group LDAP schema ----------------- +According to :file:`ldapvi` this is the user schema (on YunoHost >3.4): + +The groups will look like this: + +:: + dn: cn=the_unix_username,ou=groups,dc=yunohost,dc=org + objectClass: top + objectClass: groupOfNamesYnh + objectClass: posixGroup + gidNumber: 48335 + cn: the_unix_username + structuralObjectClass: posixGroup + member: uid=the_unix_username,ou=users,dc=yunohost,dc=org + +By default you will find in all case a group named `all_users` which will contains all Yunohost users. + +:: + # path dn: cn=all_users,ou=groups,dc=yunohost,dc=org + objectClass: posixGroup + objectClass: groupOfNamesYnh + gidNumber: 4002 + cn: all_users + structuralObjectClass: posixGroup + permission: cn=main.mail,ou=permission,dc=yunohost,dc=org + permission: cn=main.metronome,ou=permission,dc=yunohost,dc=org + member: uid=the_unix_username,ou=users,dc=yunohost,dc=org + memberUid: the_unix_username + +Reading group from LDAP +----------------------- + +The group schema is located at this path: :file:`ou=groups,dc=yunohost,dc=org` + +The queries we uses are the 2 following python calls: + +:: + # all groups + auth.search('ou=groups,dc=yunohost,dc=org', '(objectclass=groupOfNamesYnh)') + + # one groups + auth.search(base='ou=groups,dc=yunohost,dc=org', filter='cn=' + groupname) + + +Permission LDAP schema +---------------------- + +According to :file:`ldapvi` this is the user schema (on YunoHost >3.4): + +The permission will look like this: + +:: + dn: cn=main.mail,ou=permission,dc=yunohost,dc=org + objectClass: posixGroup + objectClass: permissionYnh + gidNumber: 5001 + groupPermission: cn=all_users,ou=groups,dc=yunohost,dc=org + cn: main.mail + structuralObjectClass: posixGroup + memberUid: the_unix_username + inheritPermission: uid=the_unix_username,ou=users,dc=yunohost,dc=org + +By default you will have a permission for the mail and for metronome. When you install an application a permission also created. + +Reading permissions from LDAP +----------------------- + +The permission schema is located at this path: :file:`ou=permission,dc=yunohost,dc=org` + +The queries we uses are the 2 following python calls: + +:: + # For all permission + auth.search('ou=permission,dc=yunohost,dc=org', '(objectclass=permissionYnh)') + + # For one permission + auth.search(base='ou=permission,dc=yunohost,dc=org', filter='cn=' + permission_name) + +Domain LDAP schema +------------------ + According to :file:`ldapvi` this is the domain schema (on YunoHost 2.7): :: @@ -236,6 +314,8 @@ According to :file:`ldapvi` this is the domain schema (on YunoHost 2.7): Adding data in LDAP =================== +If you add an object linked to user, group or permission you need run the function `permission_sync_to_user` to keep integrity of permission in LDAP. + Adding stuff in LDAP seems pretty simple, according to existing code it looks like this: :: @@ -252,7 +332,7 @@ They weird stuff is the path you need to create. This looks like that for domain # user auth.add('uid=%s,ou=users' % username, attr_dict) -You need to respect the expected attributes. Refer to the schemas for that. +You need to respect the expected attributes. Refer to the schema for that. :file:`auth.add` seems to return something false when it failed (None probably) so you need to check it's return code. @@ -300,6 +380,8 @@ Here is how it's done for a new domain: Updating LDAP data ================== +If you add an object linked to user, group or permission you need run the function `permission_sync_to_user` to keep integrity of permission in LDAP. + Update a user from LDAP looks like a simplified version of searching. The syntax is the following one: :: @@ -332,8 +414,8 @@ Refer to the user schema to know which attributes you can modify. Validate uniqueness =================== -There is a method to validate the uniquess of some entry that is used during -user creation. I haven't used it and I'm not sure on how it work. +There is a method to validate the uniqueness of some entry that is used during +user creation. It's useful by example to be sure that we have no conflict about email between each user. Here is how it's used (I don't understand why a path is not provided): @@ -349,9 +431,24 @@ And here is its docstring: .. automethod:: moulinette.authenticators.ldap.Authenticator.update +Get conflict +============ + +Like the last function `validate_uniqueness` but give instead of rising an error this function return which attribute with witch value generate a conflict. + +:: + # Validate uniqueness of groupname in LDAP + conflict = auth.get_conflict({ + 'cn': groupname + }, base_dn='ou=groups,dc=yunohost,dc=org') + if conflict: + raise YunohostError('group_name_already_exist', name=groupname) + Remove entries from LDAP ======================== +If you add an object linked to user, group or permission you need run the function `permission_sync_to_user` to keep integrity of permission in LDAP. + Remove entries from LDAP is very simple, quite close to adding stuff except you don't need to specify the attributes dict, you just need to entrie path: :: @@ -386,3 +483,82 @@ Reading parsing a ldif to be able to insert in the LDAP database is really easy. Note that the main difference of what the auth object return with the search method is that this function return a 2-tuples with the "dn" and the LDAP entry. + +============================= +LDAP architecture in Yunohost +============================= + +In Yunohost to be able to manage the user and the permission we use 3 parts: + +* User object +* Permission object +* Group object + +We can see the interaction between these object as this following: + +.. image:: Yunohost_LDAP_documentation/LDAP_Liaison_logique_entre_objets.png + +As you can see there are link between these 3 objets: + +* The first link is between the user and the group. It define which user is in which group. Note that all user has a group with his name. Note that in all Yunohost instance you have a group named `all_users`. In this group you will find all Yunohost users. +* The second link is between the permission and the groups. This link is defined by the administrator. By default all permission are linked to the group `all_users`, so all user will be allowed to access to this permission. +* The third link between the User and the Permission is more technical. It give the possibility to the application to get a list of all user allowed to access to. This link is dynamically generated by core. The function `permission_sync_to_user` in the module `permission` do this work. + +The option `force` of the function `permission_sync_to_user` is used when you add the data to LDAP with `slapadd`. `slapadd` update the LDAP database without the LDAP demon process. The advantage of this is that you can bypass the integrity check (like the link between the object by the memberOf overlay). The disadvantage is that the the memberOf overlay wont update anything so if you don't fix the integrity after after to run `slapadd`, the permission in LDAP might be corrupted. Running the function permission_sync_to_user` with the option `force` will do this work to fix all integrity error. + +To be able to have an attribute in both is of theses 3 link we use the `memberOf` overlay in LDAP. This following line define the configuration to have these 3 link dynamically updated : + +:: + # Link user <-> group + #dn: olcOverlay={0}memberof,olcDatabase={1}mdb,cn=config + overlay memberof + memberof-group-oc groupOfNamesYnh + memberof-member-ad member + memberof-memberof-ad memberOf + memberof-dangling error + memberof-refint TRUE + + # Link permission <-> groupes + #dn: olcOverlay={1}memberof,olcDatabase={1}mdb,cn=config + overlay memberof + memberof-group-oc permissionYnh + memberof-member-ad groupPermission + memberof-memberof-ad permission + memberof-dangling error + memberof-refint TRUE + + # Link permission <-> user + #dn: olcOverlay={2}memberof,olcDatabase={1}mdb,cn=config + overlay memberof + memberof-group-oc permissionYnh + memberof-member-ad inheritPermission + memberof-memberof-ad permission + memberof-dangling error + memberof-refint TRUE + +This foolwing example show how will be represented in LDAP as simple concept of permission. + +.. image:: Yunohost_LDAP_documentation/LDAP_Representation_logique.png + +This schema show what will be in LDAP in these following schema: + +.. image:: Yunohost_LDAP_documentation/LDAP_Schema_1.png +.. image:: Yunohost_LDAP_documentation/LDAP_Schema_2.png + +========================================= +LDAP integration in Yunohost applications +========================================= + +To have a complete integration of LDAP in your application you need to configure LDAP as follow : + +:: + Host: ldap://localhost + Port: 389 + Base DN: dc=yunohost,dc=org + User DN: ou=users,dc=yunohost,dc=org + Group DN: ou=groups,dc=yunohost,dc=org + fiter : (&(objectClass=posixAccount)(permission=cn=YOUR_APP.main,ou=permission,dc=yunohost,dc=org)) + LDAP Username: uid + LDAP Email Address: mail + +By this your application will get the list of all user allowed to access to your application. diff --git a/doc/ldap_graph.dot b/doc/ldap_graph.dot index 4dcee28b..ed4f6a73 100644 --- a/doc/ldap_graph.dot +++ b/doc/ldap_graph.dot @@ -84,6 +84,24 @@ strict digraph "" { n2 [label=< + + + + +
+ + dn: ou=users,dc=yunohost,dc=org +
+ objectClass: organizationalUnit +
+ ou: users +
+ >] + n0->n2 + + + + n3 [label=<
@@ -97,11 +115,11 @@ strict digraph "" {
>] - n0->n2 + n0->n3 - n3 [label=< + n4 [label=<
@@ -115,11 +133,11 @@ strict digraph "" {
>] - n0->n3 + n0->n4 - n4 [label=< + n5 [label=<
@@ -133,11 +151,11 @@ strict digraph "" {
>] - n0->n4 + n0->n5 - n5 [label=< + n6 [label=<
@@ -151,29 +169,71 @@ strict digraph "" {
>] - n0->n5 - - - - n6 [label=< - - - - -
- - dn: ou=users,dc=yunohost,dc=org -
- objectClass: organizationalUnit -
- ou: users -
- >] n0->n6 n7 [label=< + + + + +
+ + dn: ou=permission,dc=yunohost,dc=org +
+ objectClass: organizationalUnit +
+ ou: permission +
+ >] + n0->n7 + + + + n8 [label=< + + + + + + + + + + + + +
+ + dn: cn=all_users,ou=groups,dc=yunohost,dc=org +
+ objectClass: posixGroup +
+ objectClass: groupOfNamesYnh +
+ gidNumber: 4002 +
+ cn: all_users +
+ permission: cn=main.mail,ou=permission,dc=yunohost,dc=org +
+ permission: cn=main.metronome,ou=permission,dc=yunohost,dc=org +
+ member: uid=alice,ou=users,dc=yunohost,dc=org +
+ member: uid=example_admin_user,ou=users,dc=yunohost,dc=org +
+ memberUid: alice +
+ memberUid: example_admin_user +
+ >] + n4->n8 + + + + n9 [label=<
@@ -193,44 +253,11 @@ strict digraph "" {
>] - n3->n7 + n4->n9 - n8 [label=< - - - - - - - - - -
- - dn: cn=sftpusers,ou=groups,dc=yunohost,dc=org -
- objectClass: posixGroup -
- gidNumber: 4002 -
- cn: sftpusers -
- memberUid: admin -
- memberUid: neutrinet -
- memberUid: alice -
- memberUid: bob -
- >] - n3->n8 - - - - n9 [label=< + n10 [label=<
@@ -256,11 +283,89 @@ strict digraph "" {
>] - n4->n9 + n5->n10 - n10 [label=< + n11 [label=< + + + + + + + + + + + +
+ + dn: cn=main.mail,ou=permission,dc=yunohost,dc=org +
+ objectClass: posixGroup +
+ objectClass: permissionYnh +
+ gidNumber: 5001 +
+ groupPermission: cn=all_users,ou=groups,dc=yunohost,dc=org +
+ cn: main.mail +
+ memberUid: alice +
+ memberUid: example_admin_user +
+ inheritPermission: uid=alice,ou=users,dc=yunohost,dc=org +
+ inheritPermission: uid=example_admin_user,ou=users,dc=yunohost,dc=org +
+ >] + n7->n11 + + + + n12 [label=< + + + + + + + + + + + +
+ + dn: cn=main.metronome,ou=permission,dc=yunohost,dc=org +
+ objectClass: posixGroup +
+ objectClass: permissionYnh +
+ gidNumber: 5002 +
+ groupPermission: cn=all_users,ou=groups,dc=yunohost,dc=org +
+ cn: main.metronome +
+ memberUid: alice +
+ memberUid: example_admin_user +
+ inheritPermission: uid=alice,ou=users,dc=yunohost,dc=org +
+ inheritPermission: uid=example_admin_user,ou=users,dc=yunohost,dc=org +
+ >] + n7->n12 + + + + n13 [label=<
@@ -274,11 +379,11 @@ strict digraph "" {
>] - n2->n10 + n3->n13 - n11 [label=< + n14 [label=< + + +
@@ -295,12 +400,15 @@ strict digraph "" {
objectClass: posixAccount +
+ objectClass: userPermissionYnh
loginShell: /bin/false
- uidNumber: 80833 + uidNumber: 23431
maildrop: example_admin_user @@ -315,7 +423,7 @@ strict digraph "" { mailuserquota: 0
- gidNumber: 80833 + gidNumber: 23431
sn: lastname @@ -340,14 +448,47 @@ strict digraph "" {
givenName: firstname +
+ permission: cn=main.mail,ou=permission,dc=yunohost,dc=org +
+ permission: cn=main.metronome,ou=permission,dc=yunohost,dc=org
>] - n6->n11 + n2->n14 - n12 [label=< + n15 [label=< + + + + + + + +
+ + dn: cn=example_admin_user,ou=groups,dc=yunohost,dc=org +
+ objectClass: groupOfNamesYnh +
+ objectClass: posixGroup +
+ gidNumber: 23431 +
+ cn: example_admin_user +
+ member: uid=example_admin_user,ou=users,dc=yunohost,dc=org +
+ >] + n4->n15 + + + + n16 [label=< + + +
@@ -366,7 +507,13 @@ strict digraph "" { objectClass: posixAccount
- uidNumber: 41580 + objectClass: userPermissionYnh +
+ loginShell: /bin/false +
+ uidNumber: 98803
maildrop: alice @@ -381,7 +528,7 @@ strict digraph "" { mailuserquota: 0
- gidNumber: 41580 + gidNumber: 98803
sn: pouet @@ -390,17 +537,47 @@ strict digraph "" { homeDirectory: /home/alice
- mail: alice@ynh.local + mail: alice@domain.com
givenName: alice
- loginShell: /bin/bash + permission: cn=main.mail,ou=permission,dc=yunohost,dc=org +
+ permission: cn=main.metronome,ou=permission,dc=yunohost,dc=org
>] - n6->n12 + n2->n16 + + + + n17 [label=< + + + + + + + +
+ + dn: cn=alice,ou=groups,dc=yunohost,dc=org +
+ objectClass: groupOfNamesYnh +
+ objectClass: posixGroup +
+ gidNumber: 98803 +
+ cn: alice +
+ member: uid=alice,ou=users,dc=yunohost,dc=org +
+ >] + n4->n17 } diff --git a/doc/ldap_graph.png b/doc/ldap_graph.png index 667a8284..3383100e 100644 Binary files a/doc/ldap_graph.png and b/doc/ldap_graph.png differ diff --git a/doc/ldapsearch.result b/doc/ldapsearch.result index ba4d4210..5adb9175 100644 --- a/doc/ldapsearch.result +++ b/doc/ldapsearch.result @@ -27,6 +27,12 @@ description: LDAP Administrator uidNumber: 1007 uid: admin +# users, yunohost.org +dn: ou=users,dc=yunohost,dc=org +objectClass: organizationalUnit +objectClass: top +ou: users + # domains, yunohost.org dn: ou=domains,dc=yunohost,dc=org objectClass: organizationalUnit @@ -51,11 +57,24 @@ objectClass: organizationalUnit objectClass: top ou: apps -# users, yunohost.org -dn: ou=users,dc=yunohost,dc=org +# permission, yunohost.org +dn: ou=permission,dc=yunohost,dc=org objectClass: organizationalUnit objectClass: top -ou: users +ou: permission + +# all_users, groups, yunohost.org +dn: cn=all_users,ou=groups,dc=yunohost,dc=org +objectClass: posixGroup +objectClass: groupOfNamesYnh +gidNumber: 4002 +cn: all_users +permission: cn=main.mail,ou=permission,dc=yunohost,dc=org +permission: cn=main.metronome,ou=permission,dc=yunohost,dc=org +member: uid=alice,ou=users,dc=yunohost,dc=org +member: uid=example_admin_user,ou=users,dc=yunohost,dc=org +memberUid: alice +memberUid: example_admin_user # admins, groups, yunohost.org dn: cn=admins,ou=groups,dc=yunohost,dc=org @@ -65,17 +84,6 @@ memberUid: admin gidNumber: 4001 cn: admins -# sftpusers, groups, yunohost.org -dn: cn=sftpusers,ou=groups,dc=yunohost,dc=org -objectClass: posixGroup -objectClass: top -gidNumber: 4002 -cn: sftpusers -memberUid: admin -memberUid: neutrinet -memberUid: alice -memberUid: bob - # admin, sudo, yunohost.org dn: cn=admin,ou=sudo,dc=yunohost,dc=org cn: admin @@ -86,6 +94,30 @@ objectClass: top sudoOption: !authenticate sudoHost: ALL +# main.mail, permission, yunohost.org +dn: cn=main.mail,ou=permission,dc=yunohost,dc=org +objectClass: posixGroup +objectClass: permissionYnh +gidNumber: 5001 +groupPermission: cn=all_users,ou=groups,dc=yunohost,dc=org +cn: main.mail +memberUid: alice +memberUid: example_admin_user +inheritPermission: uid=alice,ou=users,dc=yunohost,dc=org +inheritPermission: uid=example_admin_user,ou=users,dc=yunohost,dc=org + +# main.metronome, permission, yunohost.org +dn: cn=main.metronome,ou=permission,dc=yunohost,dc=org +objectClass: posixGroup +objectClass: permissionYnh +gidNumber: 5002 +groupPermission: cn=all_users,ou=groups,dc=yunohost,dc=org +cn: main.metronome +memberUid: alice +memberUid: example_admin_user +inheritPermission: uid=alice,ou=users,dc=yunohost,dc=org +inheritPermission: uid=example_admin_user,ou=users,dc=yunohost,dc=org + # domain.com, domains, yunohost.org dn: virtualdomain=domain.com,ou=domains,dc=yunohost,dc=org objectClass: mailDomain @@ -98,13 +130,14 @@ uid: example_admin_user objectClass: mailAccount objectClass: inetOrgPerson objectClass: posixAccount +objectClass: userPermissionYnh loginShell: /bin/false -uidNumber: 80833 +uidNumber: 23431 maildrop: example_admin_user cn: firstname lastname displayName: firstname lastname mailuserquota: 0 -gidNumber: 80833 +gidNumber: 23431 sn: lastname homeDirectory: /home/example_admin_user mail: example_admin_user@domain.com @@ -113,6 +146,17 @@ mail: admin@domain.com mail: webmaster@domain.com mail: postmaster@domain.com givenName: firstname +permission: cn=main.mail,ou=permission,dc=yunohost,dc=org +permission: cn=main.metronome,ou=permission,dc=yunohost,dc=org + +# example_admin_user, groups, yunohost.org +dn: cn=example_admin_user,ou=groups,dc=yunohost,dc=org +objectClass: top +objectClass: groupOfNamesYnh +objectClass: posixGroup +gidNumber: 23431 +cn: example_admin_user +member: uid=example_admin_user,ou=users,dc=yunohost,dc=org # alice, users, yunohost.org dn: uid=alice,ou=users,dc=yunohost,dc=org @@ -120,21 +164,33 @@ uid: alice objectClass: mailAccount objectClass: inetOrgPerson objectClass: posixAccount -uidNumber: 41580 +objectClass: userPermissionYnh +loginShell: /bin/false +uidNumber: 98803 maildrop: alice cn: alice pouet displayName: alice pouet mailuserquota: 0 -gidNumber: 41580 +gidNumber: 98803 sn: pouet homeDirectory: /home/alice -mail: alice@ynh.local +mail: alice@domain.com givenName: alice -loginShell: /bin/bash +permission: cn=main.mail,ou=permission,dc=yunohost,dc=org +permission: cn=main.metronome,ou=permission,dc=yunohost,dc=org + +# alice, groups, yunohost.org +dn: cn=alice,ou=groups,dc=yunohost,dc=org +objectClass: top +objectClass: groupOfNamesYnh +objectClass: posixGroup +gidNumber: 98803 +cn: alice +member: uid=alice,ou=users,dc=yunohost,dc=org # search result search: 2 result: 0 Success -# numResponses: 21 -# numEntries: 20 +# numResponses: 19 +# numEntries: 18