From cf5c707e87e05e30e9dcf4b13b615f55c9e7127f Mon Sep 17 00:00:00 2001 From: "Alexey R." Date: Tue, 31 Dec 2024 15:26:18 +0700 Subject: [PATCH] fix: Add ability to get groups dn from memberof attribute. In the case of using FreeIPA, the user has a set of memberOf attributes that contain the DNs of groups, sudo rules, and HBAC. The ability to use the memberof-dn combination in UserMatchers has been added to directly retrieve groups from the user entity without additional group queries. Additionally, memberOf values that do not have the suffix defined as BaseDN or do not start with the NameAttr ({NameAttr}=groupname,{BaseDN}) are discarded Signed-off-by: Alexey R. --- connector/ldap/ldap.go | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/connector/ldap/ldap.go b/connector/ldap/ldap.go index 856949d240..fd7828fc41 100644 --- a/connector/ldap/ldap.go +++ b/connector/ldap/ldap.go @@ -62,6 +62,8 @@ import ( type UserMatcher struct { UserAttr string `json:"userAttr"` GroupAttr string `json:"groupAttr"` + // Work only if UserAttr is 'memberOf' and GroupAttr is dn + GroupPrefix string `json:"groupPrefix"` } // Config holds configuration options for LDAP logins. @@ -593,6 +595,49 @@ func (c *ldapConnector) groups(ctx context.Context, user ldap.Entry) ([]string, var groups []*ldap.Entry for _, matcher := range c.GroupSearch.UserMatchers { + // When we get groups from memberof user.s entity attribute, we may + // don.t want (Or don.t need) to perform extra search query for each group. + // Also when groupattr is dn (which implies memberof freeipa), we cannot use + // ldapsearch to retrieve group by DN (LDAP restriction) + if strings.ToLower(matcher.UserAttr) == "memberof" && + strings.ToLower(matcher.GroupAttr) == "dn" { + for _, attr := range c.getAttrs(user, matcher.UserAttr) { + // If group dn has no ends with group search base dn - ignore it. + // In FreeIPA case, it also can contain hbac, sudo rules, etc... + if !strings.HasSuffix(attr, c.GroupSearch.BaseDN) { + continue + } + + // Trim {NameAttr}= prefix and baseDN suffix to get group name. + // For NameAttr=cn and BaseDN=cn=groups,dc=example,dc=com : + // cn=groupname,cn=groups,dc=example,dc=com -> groupname + groupName := strings.TrimSuffix( + strings.TrimPrefix(attr, + fmt.Sprintf("%s=", c.GroupSearch.NameAttr)), + fmt.Sprintf(",%s", c.GroupSearch.BaseDN)) + + // Is it needed compability with GroupSearch.Filter? (r9odt) + if !strings.HasPrefix(groupName, matcher.GroupPrefix) { + continue + } + + // Append result to group list as ldap.Entry to process it next wihtout + // extra changes in code. + groups = append(groups, &ldap.Entry{ + DN: attr, + Attributes: []*ldap.EntryAttribute{ + { + Name: c.GroupSearch.NameAttr, + Values: []string{ + groupName, + }, + }, + }, + }) + } + continue + } + for _, attr := range c.getAttrs(user, matcher.UserAttr) { filter := fmt.Sprintf("(%s=%s)", matcher.GroupAttr, ldap.EscapeFilter(attr)) if c.GroupSearch.Filter != "" {