veo-labs / ldap-server-mock

Really simple basic mock for LDAP server
GNU Affero General Public License v3.0
66 stars 26 forks source link

Arbitrary search filters #21

Closed stevenhair closed 2 years ago

stevenhair commented 2 years ago

This implements LDAP filter queries and allows them to be dynamic. The filter no longer needs to be defined in the configuration.

And (&), or (|), not (!), and wildcard (*) queries are all supported. Note that the behavior for complex queries may be slightly different than an actual Active Directory instance - the implementation presented in this PR may actually be more robust and support some things (like wildcards in negated queries).

For example, given the following user database:

[
  {
  "dn":"CN=Foo User,OU=USERS,DC=example,DC=com",
  "objectClass":["top","person","organizationalPerson","user"],
  "cn":"Foo User",
  "sn":"User",
  "givenName":"Foo",
  "distinguishedName":"CN=Foo User,OU=USERS,DC=example,DC=com",
  "displayName":"Foo User",
  "memberOf":[
    "CN=GROUP1,DC=example,DC=com",
    "CN=GROUP2,DC=example,DC=com"
  ],
  "name":"Foo User",
  "sAMAccountName":"foo_user",
  "userPrincipalName":"foo@example.com",
  "objectCategory":"CN=Person,CN=Schema,CN=Configuration,DC=example,DC=com",
  "mail":"foo@example.com"
  },
  {
  "dn":"CN=Bar User,OU=USERS,DC=example,DC=com",
  "objectClass":["top","person","organizationalPerson","user"],
  "cn":"Bar User",
  "sn":"User",
  "givenName":"Bar",
  "distinguishedName":"CN=Bar User,OU=USERS,DC=example,DC=com",
  "displayName":"Bar User",
  "memberOf":[
    "CN=GROUP1,DC=example,DC=com",
    "CN=GROUP3,DC=example,DC=com"
  ],
  "name":"Bar User",
  "sAMAccountName":"bar_user",
  "userPrincipalName":"bar@example.com",
  "objectCategory":"CN=Person,CN=Schema,CN=Configuration,DC=example,DC=com",
  "mail":"bar@example.com"
  }
]

Here are some examples of valid queries (with their responses):

$ ldapsearch -x -H ldap://127.0.0.1:3004 -b "dc=hedgeservtest,DC=com" "(sAMAccountName=foo_user)"
# extended LDIF
#
# LDAPv3
# base <dc=hedgeservtest,DC=com> with scope subtree
# filter: (sAMAccountName=foo_user)
# requesting: ALL
#

# Foo User, USERS, example.com
dn: cn=Foo User,ou=USERS,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: Foo User
sn: User
givenName: Foo
distinguishedName: CN=Foo User,OU=USERS,DC=example,DC=com
displayName: Foo User
memberOf: CN=GROUP1,DC=example,DC=com
memberOf: CN=GROUP2,DC=example,DC=com
name: Foo User
sAMAccountName: foo_user
userPrincipalName: foo@example.com
objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=example,DC=com
mail: foo@example.com

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1
$ ldapsearch -x -H ldap://127.0.0.1:3004 -b "dc=hedgeservtest,DC=com" "(memberOf=CN=GROUP2,DC=example,DC=com)" -o dn
# extended LDIF
#
# LDAPv3
# base <dc=hedgeservtest,DC=com> with scope subtree
# filter: (memberOf=CN=GROUP2,DC=example,DC=com)
# requesting: -o dn
#

# Foo User, USERS, example.com
dn: cn=Foo User,ou=USERS,dc=example,dc=com

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1
$ ldapsearch -x -H ldap://127.0.0.1:3004 -b "dc=hedgeservtest,DC=com" "(|(sAMAccountName=foo_user)(sAMAccountName=bar_user))" -o dn
# extended LDIF
#
# LDAPv3
# base <dc=hedgeservtest,DC=com> with scope subtree
# filter: (|(sAMAccountName=foo_user)(sAMAccountName=bar_user))
# requesting: -o dn
#

# Foo User, USERS, example.com
dn: cn=Foo User,ou=USERS,dc=example,dc=com

# Bar User, USERS, example.com
dn: cn=Bar User,ou=USERS,dc=example,dc=com

# search result
search: 2
result: 0 Success

# numResponses: 3
# numEntries: 2

Closes #20 Closes #9

maxime-beguin commented 2 years ago

Thank you very much @stevenhair to take the time to develop this feature. I also would like to thank you for your very detailed comment about it! Thanks!

I have one or two suggestions before merging your pull request and deploy a new version of the project.