How to improve my LDAP schema? - optimization

I have a OpenLDAP Database and it holds some project objects that look like
dn: cn=Proj1,ou=Project,ou=ua,dc=org
cn: Proj1
objectClass: top
objectClass: posixGroup
member: 001ag
member: 002ag
System: ABEL
System: PCx
Budget: ABEL:1000000:0.3
Budget: PCx:300000:0.3
One can see that the Budget attribute is a ":"-separated string, where the first part holds the name of the system the budget is for, the second part holds some budget (which may change every month) and the last entry is a conversion factor for the budget of that system.
Seeing this, I thought this is bad database design, since attribute values should always be atomic. But how can I improve that in LDAP, so that I can do a direct ldapsearch or a direct ldapmodify of the budget of System "ABEL" instead of writing a script, that will have to parse and split the ":"-separated string?

It's a good idea to break things up into groups as much as you can until you get down to individually distinguishable elements, which in your case would be Systems. As you've realized, having the smallest element in the database be Project is a problem when you have more than one System.
I would put a sub-group for each project inside the main Project group, aka:
- ou=Project
+ ou=proj1
+ ou=proj2
+ ou=proj3
Inside each of these you can have an object for "member" or "System", whichever is the more distinguishable attribute. For the sake of an example I'll assume "member" is the better choice. Following this idea, inside each sub-group you would have objects like this:
- ou=Project
- ou=proj1
- dn: cn=sys1,ou=proj1,ou=Project,ou=ua,dc=org
cn: sys1
objectClass: top
objectClass: posixGroup
member: 001ag
System: ABEL
Budget: 1000000:0.3
- dn: cn=sys2,ou=proj1,ou=Project,ou=ua,dc=org
cn: sys2
objectClass: top
objectClass: posixGroup
member: 002ag
System: PCx
Budget: 300000:0.3
+ proj2
+ proj3
Now each system is its own entity, but the project is still grouped together as a whole.

Related

Where can I find the definition of attribute aliases in LDAP?

Context
I'm trying to interact with an OpenLDAP server, and have some issues understanding the behavior of said server.
note : I don't have admin rights on this server, I am just writing some code that tries to extract lists of users and groups from it.
The specific point about which I have a question :
When I run a query asking for attribute name, I get entries containing cn, sn and givenName, and no name attribute :
# sample output:
$ ldapsearch [options ...] "(|(objectClass=posixAccount)(objectClass=posixGroup))" name
...
# sample users:
dn: uid=id4321,ou=Users,dc=example,dc=com
cn: u1234
sn: Doe
givenName: John
dn: uid=id4322,ou=Users,dc=example,dc=com
cn: u2234
sn: Doe
givenName: Jane
dn: ...
# sample groups:
dn: cn=Domain Users,ou=Groups,dc=example,dc=com
cn: Domain Users
dn: cn=HR,ou=Groups,dc=example,dc=com
cn: HR
dn: ...
Question
Where can I find the correspondance "attribute name in the query" -> "attribute(s) name(s) in the response" ?
If part of that correspondance is in the LDAP or OpenLDAP standard, where is it defined ?
If part of that correspondance comes from the directory config/schema, how can I query for it ?
I have tried to Google with limited success, I'm trying to head to RFCs but I don't know which one to read first.
I think I found the answer to my question :
the attributes name, cn, sn and a bunch of other standard attributes are defined in RFC 4519
an attribute type may have a supertype (for example : the definition of cn is ( 2.5.4.3 NAME 'cn' SUP name ), the definition of sn is ( 2.5.4.4 NAME 'sn' SUP name ) ), and if a search query queries for an attribute, the server may include in its response attributes whose supertype match
so querying for name may return results with a sn, cn, givenName and initials attribute
the definition of "attribute types" and "supertype" is in RFC 4512

ApacheDS gidNumber not declared in objectClasses

I follow the tutorial on how to create groups and users in LDAP Apache Directory Studio.
I use the posixGroup to create a group
to create users inetOrgPerson, posixAccound, shadowAccount
However, entries do not contain gidNumber and uidNumber and can not be added.
How do I add gidNumber for groups and uid Number for users?
 I do not get gidNumber when I create it if I try to add it manually:
Error while creating entry
- [LDAP: error code 65 - OBJECT_CLASS_VIOLATION: failed for MessageType : ADD_REQUES java.lang.Exception: [LDAP: error code 65 -
OBJECT_CLASS_VIOLATION: failed for MessageType : ADD_REQUEST Message
ID : 13
Add Request : Entry
dn: cn=Vydaj,ou=Testgroups,dc=test,dc=com
objectClass: posixGroup
objectClass: top
gidNumber: 1000
cn: Vydaj : ERR_277 Attribute gidNumber not declared in objectClasses of entry cn=Vydaj,ou=Testgroups,test,dc=com]
at
org.apache.directory.studio.connection.core.io.api.DirectoryApiConnectionWrapper.checkResponse(DirectoryApiConnectionWrapper.java:1418)
at
org.apache.directory.studio.connection.core.io.api.DirectoryApiConnectionWrapper.access$11(DirectoryApiConnectionWrapper.java:1386)
at
org.apache.directory.studio.connection.core.io.api.DirectoryApiConnectionWrapper$6.run(DirectoryApiConnectionWrapper.java:1009)
at
org.apache.directory.studio.connection.core.io.api.DirectoryApiConnectionWrapper.runAndMonitor(DirectoryApiConnectionWrapper.java:1312)
at
org.apache.directory.studio.connection.core.io.api.DirectoryApiConnectionWrapper.checkConnectionAndRunAndMonitor(DirectoryApiConnectionWrapper.java:1256)
at
org.apache.directory.studio.connection.core.io.api.DirectoryApiConnectionWrapper.createEntry(DirectoryApiConnectionWrapper.java:1031)
at
org.apache.directory.studio.ldapbrowser.core.jobs.CreateEntryRunnable.createEntry(CreateEntryRunnable.java:225)
at
org.apache.directory.studio.ldapbrowser.core.jobs.CreateEntryRunnable.run(CreateEntryRunnable.java:124)
at
org.apache.directory.studio.connection.ui.RunnableContextRunner$1.run(RunnableContextRunner.java:129)
at
org.eclipse.jface.operation.ModalContext$ModalContextThread.run(ModalContext.java:119)
[LDAP: error code 65 - OBJECT_CLASS_VIOLATION: failed for
MessageType : ADD_REQUEST Message ID : 13
Add Request : Entry
dn: cn=Vydaj,ou=Testgroups,dc=test,dc=com
objectClass: posixGroup
objectClass: top
gidNumber: 1000
cn: Vydaj : ERR_277 Attribute gidNumber not declared in objectClasses of entry cn=Vydaj,ou=Testgroups,dc=test,dc=com]
You should be able to edit the values within the Apache Directory Studio "Create an Object" wizard -- when you create an object with objectClass posixGroup, you should have an "Integer Editor" where you type the group gidNumber. When you create a user with posixAccount as an objectClass, you'll get a dialog box that has several attributes highlighted in red. Double-clicking the red text, or the empty cell in the "Value" column next to the red text, will allow you to edit that attribute value.
You could always create a general user or group first and then add the appropriate POSIX objectClass and required attributes. Since the objectClass has mandatory attributes, this needs to be performed as a single operation. An example LDIF that takes an inetOrgPerson user account, adds posixAccount as an objectClass, and adds the POSIX attributes (which are mandatory v/s optional depend on your schema definition):
dn: cn=something,ou=someou,o=company
changetype: modify
add: objectClass
objectClass: posixAccount
-
add: uidNumber
uidNumber: 55555
-
add: gidNumber
gidNumber: 555
-
add: homeDirectory
homeDirectory: /home/userid
-
add: loginShell
loginShell: /bin/bash
-
add: gecos
gecos: UserDisplay Name
-
add: description
description: UserDisplay Name
-

LDAP best way to assign Roles to users

Is their any standard way to set user roles and permissions.
Option 1:
Create group and assign members in that.
My people dn is as follows .
DN: uid=55e44a75e4b0f16711714165,ou=people,dc=cofinding,dc=com
I created Groups for roles. and assign members in that
DN: cn=ADMIN,ou=roles,dc=cofinding,dc=com
And added members here who has role ADMIN. Near about 50K members added in ADMIn role.
Option 2 :Add custom Value role in people .e.g. I created dn as user_role
DN: uid=55e44a75e4b0f16711714165,ou=people,dc=cofinding,dc=com
In people we can add user_role=ADMIN,MASTER_ADMIN
Is their any other option or standard practice . As roles are very important in any authentication process.
First of all: roles have nothing to do with authentication, they are used in the authorization process. When authenticating, the system verifies who the user is based on some set of credentials; once this has been established the roles are consulted in the authorization process to determine whether the user should be granted access to some specific resource.
Answering your question on roles: Both ways would work. The first approach is probably the most common one (and most in line with the way things are normally structured in LDAP. Take a look at the groupOfNames and groupOfUniqueNames objectclasses for representing your role). Depending on the use case I would argue that the latter is probably more practical in many cases, but you need to take into account that it's not quite the standard 'LDAP way' of doing things.
There is another option that's kind of a hybrid between options #1 and #2.
Instead of using static groups (Option 1) you can use the so-called dynamic groups, or groups of URLs:
dn: cn=ADMIN,ou=roles,dc=cofinding,dc=com
objectClass: top
objectClass: groupOfURLs
cn: ADMIN
memberURL: ldap:///ou=people,dc=cofinding,dc=com??sub?user_role=ADMIN
The memberURL is a filter that determines which users belong to the group based on a certain attribute value and a base DN. In the example above, every user under ou=people,dc=cofinding,dc=com with the attribute user_role=ADMIN will automatically be added to the ADMIN group. Here is a sample admin user:
dn: uid=55e44a75e4b0f16711714165,ou=people,dc=cofinding,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
cn: John Doe
sn: Doe
uid: 55e44a75e4b0f16711714165
user_role: ADMIN
With this approach you get the best from both options (#1 and #2):
Your group entries don't grow in size.
You can query the group to get the list of members:
$ ldapsearch -p 1389 -D "cn=directory manager" -w password -b "cn=ADMIN,ou=roles,dc=cofinding,dc=com" "(objectClass=groupOfURLs)"
dn: cn=ADMIN,ou=roles,dc=cofinding,dc=com
cn: ADMIN
memberURL: ldap:///ou=people,dc=cofinding,dc=com??sub?user_role=ADMIN
member: uid=55e44a75e4b0f16711714165,ou=people,dc=cofinding,dc=com
You can use the memberOf virtual attribute in your search filters:
(memberOf=cn=ADMIN,ou=roles,dc=cofinding,dc=com)

How can I make a LDAP query that returns only groups having OU=Groups from all levels?

If I am looking for all Groups, I get too much garbage.
If I try to narrow down the base, I get too few.
Here is an example:
CN=A Team,OU=Groups,OU=Americas,DC=example,DC=com
CN=B TEAM,OU=Groups,OU=EMEA,DC=example,DC=com
CN=C Team,OU=Legacy Groups,DC=example,DC=com
CN=D Team,OU=Groups,OU=Bangalore,OU=APAC,DC=example,DC=com
CN=E Team,OU=Common Groups,DC=example,DC=com
I am looking for a LDAP filter that returns A B D E (without C) - mainly the logic would be get me all groups that do have last OU=Groups or OU=Common Groups
My current search is using:
Search base: CN=Users,DC=citrite,DC=net
Filter: (objectCategory=Group)
First, on Microsoft Active Directory is impossible to do this in a single search, that's because AD is not fully LDAP compatible.
LDAP-compliant servers support an extensible-match filter which provides the necessary
filtering. From RFC4511:
If the dnAttributes field is set to TRUE, the match is additionally
applied against all the AttributeValueAssertions in an entry's
distinguished name, and it evaluates to TRUE if there is at least
one attribute or subtype in the distinguished name for which the
filter item evaluates to TRUE. The dnAttributes field is present
to alleviate the need for multiple versions of generic matching
rules (such as word matching), where one applies to entries and
another applies to entries and DN attributes as well.
Note that the extensible-match filter technique only works with LDAP-compliant servers,
of which AD is not one.
For example, I added the following entries to a server:
dn: ou=legacy groups,o=training
objectClass: top
objectClass: organizationalUnit
ou: legacy groups
dn: ou=common groups,o=training
objectClass: top
objectClass: organizationalUnit
ou: common groups
dn: ou=groups,o=training
objectClass: top
objectClass: organizationalUnit
ou: groups
dn: cn=a,ou=common groups,o=training
objectClass: top
objectClass: groupOfUniqueNames
uniqueMember: uid=user.0,ou=people,o=training
cn: a
dn: cn=b,ou=groups,o=training
objectClass: top
objectClass: groupOfUniqueNames
uniqueMember: uid=user.0,ou=people,o=training
cn: b
dn: cn=c,ou=legacy groups,o=training
objectClass: top
objectClass: groupOfUniqueNames
uniqueMember: uid=user.0,ou=people,o=training
cn: c
Examine the filter in the following search after the above entries were added:
ldapsearch --propertiesFilePath ds-setup/11389/ldap-connection.properties \
--baseDN o=training \
--searchScope sub '(|(ou:dn:=groups)(ou:dn:=common groups))' 1.1
dn: ou=common groups,o=training
dn: cn=a,ou=common groups,o=training
dn: ou=groups,o=training
dn: cn=b,ou=groups,o=training
Note that ou=common groups, ou=groups, and their subordinates are returned, but not
ou=legacy groups and subordinates.
This example uses the modern syntax of the ldapsearch command line tool. If the user is
utilizing the legacy OpenLDAP version of ldapsearch, the parameters to the command line tool are
somewhat different, but that does not matter. What matters is the filter.

How are nested groups in LDAP normally implemented?

I'm coming to LDAP as a possible tool for managing access servers and source code at work, and while I've been able to grasp the basic concepts, like representing users and machines as entities, that create attributes, and defining which attributes should apply to an entity based on the objectClasses applied to them, there are a few errors that still make no sense to me, and I'm hoping someone can help explain how they work.
How do nested groups work?
I can understand what ou(organisational unit)'s are, and I can understand putting people inside them, and using the groupOfNames class to act as a container for members, like this LDIF snippet from zytrax:
# create FIRST Level groups branch
dn: ou=groups,dc=example,dc=com
objectclass:organizationalunit
ou: groups
description: generic groups branch
# create the itpeople entry under groups
dn: cn=itpeople,ou=groups,dc=example,dc=com
objectclass: groupofnames
cn: itpeople
description: IT security group
member: cn=William Smith,ou=people,dc=example,dc=com
# create the hrpeople entry under groups
dn: cn=hrpeople,ou=groups,dc=example,dc=com
objectclass: groupofnames
cn: hrpeople
description: Human Resources group
member: cn=Robert Smith,ou=people,dc=example,dc=com
How would I add further levels of nesting though?
What I'm after is something like this pseudocode here:
ou='Projects' /
description: This top level group has a few people in it that can create new groups, and control who's in them
member: cn=Robert Smith,ou=people,dc=example,dc=com
-- somethingsomethingAbitrarilyNestedGroup='project-name'
member: cn=Robert Smith,ou=people,dc=example,dc=com
-- groupOfNames = 'project-name development'
member: cn=Robert Smith,ou=people,dc=example,dc=com
member: cn=Jane Doe,ou=people,dc=example,dc=com
member: cn=server1$,ou=servers,dc=example,dc=com
-- groupOfNames = 'project-name staging'
member: cn=Jane Doe,ou=people,dc=example,dc=com
member: cn=server2$,ou=servers,dc=example,dc=com
Given this hierarchy,what's the best way to grant access to this group now?
I don't see a simple way to do the arbitrary group nesting here - among the normal classes available, without using an expensive closed source tool, yet it feels like it shouldn't be this complex.
How is this normally done using a tool like OpenLDAP, to let other ldap clients control group membership once they're authenticated as a user with the correct rights? ?
Your question is a bit confused - I'm not sure what you mean "what's the best way to grant access to this group now" in the context of the initial few paragraphs.
Nested groups are dead simple. If you're using the groupOfNames objectClass, just add another member attribute to your parent group, with the value being the DN of the child group.
From your pseudo-code:
# Assuming your "groups" OU already exists...
# First create the child groups
dn: cn=project-name development,ou=groups,dc=example,dc=com
objectclass: groupofnames
cn: project-name development
member: cn=Robert Smith,ou=people,dc=example,dc=com
member: cn=Jane Doe,ou=people,dc=example,dc=com
member: cn=server1$,ou=servers,dc=example,dc=com
dn: cn=project-name staging,ou=groups,dc=example,dc=com
objectclass: groupofnames
cn: project-name development
member: cn=Jane Doe,ou=people,dc=example,dc=com
member: cn=server2$,ou=servers,dc=example,dc=com
# Now create the parent group
dn: 'project-name,ou=groups,dc=example,dc=com'
objectclass: groupofnames
member: cn=Robert Smith,ou=people,dc=example,dc=com
member: cn=project-name staging,ou=groups,dc=example,dc=com
member: cn=project-name development,ou=groups,dc=example,dc=com
Hiearchy within OUs is really only to separate your LDAP tree into "logical" segments based on the structure of your organisation. So, for example, you could stick all your groups for managing the "Development Department" in their own OU, so it's nice and clear what they pertain to. Objects can reference each other, and nest quite happily, by referencing each other with appropriate attributes (in this case, member).
I believe that when referencing user objects you should be using uid= rather than cn=
The problem I have with nesting groups is that many applications that can refer to a group are not familiar with how to search the nested groups for member objects.