-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Conversation
@ericchiang can you check this? |
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 |
fix ldap group test merge branch
Ldap group unit testing with travis
Hi @ericchiang I added Integration testing to this feature: The LDAP schema is described in the test but I made an image for convenience 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 |
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 |
Noticed in dexidp#1058 that our gofmt make target isn't actually erroring if someone commits misformatted code.
@ericchiang Fixed |
connector/ldap/ldap.go
Outdated
// 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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
Documentation/ldap-connector.md
Outdated
@@ -125,6 +125,9 @@ connectors: | |||
|
|||
# Represents group name. | |||
nameAttr: name | |||
|
|||
#Add also parents of groups | |||
inheritance: true |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
connector/ldap/ldap.go
Outdated
// 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)) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@ericchiang I added an infinite loop test using this LDAP schema |
Any update @ericchiang ? |
@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. |
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 { |
There was a problem hiding this comment.
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
Hi @ericchiang I was evaluating Docker Enterprise today and this is the feature I am asking for (read the description of iteration/ recursive search) |
This is an interesting feature. Is this still being pursued? (I hope so 🤞) |
@paroque28 Two notes/questions that might be useful to you, as this seems to be Active Directory related:
|
Hi, @srenatus thank you for your comment. The Microsoft connector sounds excellent, I'll put an eye on that. Thank you again |
@srenatus I cannot use Microsoft connector since my LDAP server is in a secured network |
This came up for my team recently as we're using Dex to integrate with our FreeIPA backend where |
Noticed in dexidp#1058 that our gofmt make target isn't actually erroring if someone commits misformatted code.
hello guys, is this feature somehow supported or just abandoned? |
in case if someone stumbled upon here to find the nested group lookup solution: thanks, @srenatus for the hint!
|
Saved my life |
Abandoned |
"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? |
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. |
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](https://user-images.githubusercontent.com/6299297/30230342-1ea737aa-94a3-11e7-9c79-cbc96678723d.PNG)
![inheritance disabled](https://user-images.githubusercontent.com/6299297/30230386-53b5f120-94a3-11e7-8030-d523e3f14b2b.PNG)