Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LDAP parent groups search (For permissions to groups and its subgroups) #1058

Closed
wants to merge 13 commits into from

Conversation

paroque28
Copy link

This allows group search to add parent groups, used when you want to add permissions to a group and its subgroups.
Now, with the flag "inheritance", the group search adds all the upper groups of certain user. Allowing to have permissions for groups and sub groups.

This first abstracts all the query of groups and use it to reuse code. The method is called queryGroups() and it receives only the DN/uid of an object and returns its groups.
Then I created an iterative loop to look for parent groups of the current group's list. The algorithm skips if the group is already added, preventing infinite loops.
The search loop ends when no more parents are found.

I attached an image with inheritance enabled and one with out
inheritance enabled
inheritance disabled

@paroque28
Copy link
Author

@ericchiang can you check this?

@ericchiang
Copy link
Contributor

Hi @paroque28 thanks for the feature request. We normally require some conversation about what problem you're trying to solve before submitting a PR and doing a code review.

Can you please post an example LDAP schema and query that this new feature enables? The concept of "parent group" is extremely general and can be implemented by LDAP in many different ways. Why can't this be done with the existing code? What LDAP providers implement this?

Also, all large changes to the LDAP code in dex require an integration tests (but after we've agreed to accept the feature) https://github.com/coreos/dex/blob/master/connector/ldap/ldap_test.go#L50

@paroque28
Copy link
Author

paroque28 commented Sep 14, 2017

Hi @ericchiang I added Integration testing to this feature:
https://github.com/paroque28/dex/blob/master/connector/ldap/ldap_test.go#L213
It passed :):
"--- PASS: TestGroupInheritanceQuery (0.85s)"

The LDAP schema is described in the test but I made an image for convenience
ldap

The need for this feature is seen when you want to know if "Jane" (for example) is a member of IT. With actual implementation, you can't know. Therefore you can't, for example, allow all IT members to access certain namespace on Tectonic.

Regards
Pablo

@paroque28
Copy link
Author

paroque28 commented Sep 14, 2017

With that LDAP schema and inheritance flag enabled, Jane would return:

groups: ["team-one", "team-two", "IT", "developers"]

instead of current response:

 groups=["team-one", "team-two"]

As described here https://github.com/paroque28/dex/blob/master/connector/ldap/ldap_test.go#L298

ericchiang pushed a commit to ericchiang/dex that referenced this pull request Sep 14, 2017
Noticed in dexidp#1058 that our gofmt make target isn't actually erroring
if someone commits misformatted code.
@paroque28
Copy link
Author

@ericchiang Fixed

// TODO(ericchiang): Is this going to spam the logs?
c.logger.Errorf("ldap: groups search with filter %q returned no groups", filter)
// If there's no remaining levels -> exit loop
if len(nextLevelGroups) == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What prevents this from looping infinitely?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The infinite loop is prevented here: https://github.com/paroque28/dex/blob/master/connector/ldap/ldap.go#L542
I'm going to add integration testing for this

@@ -125,6 +125,9 @@ connectors:

# Represents group name.
nameAttr: name

#Add also parents of groups
inheritance: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't actually define how groups are determined to be a child of another group.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

// Abrstraction query of Groups and reuse code
func (c *ldapConnector) queryGroups(ctx context.Context, dn string) ([]*ldap.Entry, string, error) {
var groups []*ldap.Entry
filter := fmt.Sprintf("(%s=%s)", c.GroupSearch.GroupAttr, ldap.EscapeFilter(dn))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can assume that the attribute used for determining membership of a user will be the same for determining the membership of the group.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually, on ActiveDirectory GroupAttr is "member".
I find it wrong to have a LDAP setup where for example I have the attribute "member" for users and some other word for groups. It can happen of course but I find more confusing for most users to have two different attributes being the same most of the cases.

For example:
It can happen that someone instead of assigning

groupAttr:"member"
recursionAttr:"member"

assigns

groupAttr:"member"
recursionAttr:"memberOf"

by thinking the attributes make different things. But is up to you to make the decision.
What do you prefer?

@@ -50,6 +50,11 @@ import (
// groupAttr: member
// nameAttr: name
//
// #Used when the filter member:1.2.840.113556.1.4.1941: timesout
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

About how many groups does it take for this query to timeout?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really know but I tried this and it timed out after one minute. I find it unacceptable to have queries that last longer than 20 seconds (Since it's just a login).
With this implementation, the duration of login reduced in just a couple of seconds

@@ -50,6 +50,11 @@ import (
// groupAttr: member
// nameAttr: name
//
// #Used when the filter member:1.2.840.113556.1.4.1941: timesout
// #Better implementation of recursive search
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can refer to this as "recursive" instead of "inheritance"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@paroque28
Copy link
Author

paroque28 commented Sep 19, 2017

@ericchiang I added an infinite loop test using this LDAP schema
ldap_test

@paroque28
Copy link
Author

Any update @ericchiang ?

@ericchiang
Copy link
Contributor

@paroque28 I'm still not convinced that the current proposal expresses how groups can be members of other groups in a general enough way. This works well for how AD does it, but what about schema where the CN is used as the attribute value instead of the DN? Can the attribute name be different for user membership instead of group membership? I don't think the current proposed change coves these scenarios.

@paroque28
Copy link
Author

Hi @ericchiang , I added an attribute called "recursionGroupAttr" (I didn't know how to name it). I think this solves what you said before.

if c.GroupSearch.RecursionGroupAttr == "" {
return nil, fmt.Errorf("ldap: recursionGroupAttr attribute is not set but recursive search is enabled")
}
if obtainedGroups, _, err := c.queryGroups(ctx, c.GroupSearch.RecursionGroupAttr, group.DN); err == nil {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where the new attribute is used

@paroque28
Copy link
Author

Hi @ericchiang I was evaluating Docker Enterprise today and this is the feature I am asking for (read the description of iteration/ recursive search)
docker

@srenatus
Copy link
Contributor

This is an interesting feature. Is this still being pursued? (I hope so 🤞)
😄

@rithujohn191 rithujohn191 self-requested a review November 16, 2017 00:08
@srenatus
Copy link
Contributor

@paroque28 Two notes/questions that might be useful to you, as this seems to be Active Directory related:

  1. Have you considered using the microsoft connector that has recently been merged? I've verified that it will return nested groups just fine.
  2. If you depend on using LDAP to talk to AD, can you try setting groupAttr to member:1.2.840.113556.1.4.1941:? According to this document, this should return

    All groups specified user belongs to,
    including due to group nesting

@paroque28
Copy link
Author

Hi, @srenatus thank you for your comment. The Microsoft connector sounds excellent, I'll put an eye on that.
And regarding the groupAttr option, unfortunately, I cannot use that since it is a highly inefficient algorithm and my queries times out.

Thank you again

@paroque28
Copy link
Author

@srenatus I cannot use Microsoft connector since my LDAP server is in a secured network

@thomasem
Copy link

thomasem commented Jan 22, 2018

This came up for my team recently as we're using Dex to integrate with our FreeIPA backend where memberOf is an attribute on the user with parent groups already resolved. I was wondering what the going thoughts were on taking that approach instead of querying necessarily from the group perspective?

pradeephkcisco added a commit to CiscoM31/dex that referenced this pull request Aug 16, 2018
mmrath pushed a commit to mmrath/dex that referenced this pull request Sep 2, 2019
Noticed in dexidp#1058 that our gofmt make target isn't actually erroring
if someone commits misformatted code.
@vavdoshka
Copy link

hello guys, is this feature somehow supported or just abandoned?

@vavdoshka
Copy link

vavdoshka commented Jun 21, 2021

in case if someone stumbled upon here to find the nested group lookup solution:

thanks, @srenatus for the hint!

groupSearch:
  baseDN: OU=ACME,DC=noway,DC=com
  filter: "(objectClass=group)"
  userAttr: DN
  groupAttr: "member:1.2.840.113556.1.4.1941:"
  nameAttr: cn

@lance0805
Copy link

in case if someone stumbled upon here to find the nested group lookup solution:

thanks, @srenatus for the hint!

groupSearch:
  baseDN: OU=ACME,DC=noway,DC=com
  filter: "(objectClass=group)"
  userAttr: DN
  groupAttr: "member:1.2.840.113556.1.4.1941:"
  nameAttr: cn

Saved my life

@paroque28 paroque28 closed this Sep 2, 2021
@paroque28
Copy link
Author

Abandoned

@anuviswa
Copy link

"member:1.2.840.113556.1.4.1941:" this seems an AD specific solution. Can we reconsider this PR? Is there a reason this PR has been abandoned?

@jsquyres
Copy link

We have interest in reviving this PR, particularly to handle the case @anuviswa mentioned above (using Dex with non-Microsoft LDAP servers).

@nabokihms @sagikazarmark It looks like you both have recent commits in Dex, and you are both listed in MAINTAINERS. If we revive this PR, is there interest in this functionality from the Dex community? We'd be happy to shepherd this PR through the process of getting it into shape that the community would find acceptable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants