Skip to content

Commit

Permalink
graph: Allow using binary LDAP UUIDs as user ids
Browse files Browse the repository at this point in the history
In Active Directories UUID attributes such as "objectGUID" use a binary
syntax (oposed to the standard UUID syntax defined in RFC4530). This
introduces a flag to enable support for binary UUIDs as the id for users
and groups (similar to what the "users" and "groups" services already
support)

Fixes: owncloud#5815
  • Loading branch information
rhafer committed May 2, 2023
1 parent ee9a524 commit 600e88e
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 33 deletions.
16 changes: 9 additions & 7 deletions services/graph/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,18 +64,20 @@ type LDAP struct {
UserDisplayNameAttribute string `yaml:"user_displayname_attribute" env:"LDAP_USER_SCHEMA_DISPLAY_NAME;GRAPH_LDAP_USER_DISPLAYNAME_ATTRIBUTE" desc:"LDAP Attribute to use for the displayname of users."`
UserNameAttribute string `yaml:"user_name_attribute" env:"OCIS_LDAP_USER_SCHEMA_USERNAME;LDAP_USER_SCHEMA_USERNAME;GRAPH_LDAP_USER_NAME_ATTRIBUTE" desc:"LDAP Attribute to use for username of users." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_USER_SCHEMA_USERNAME changing name for consistency" deprecationReplacement:"OCIS_LDAP_USER_SCHEMA_USERNAME"`
UserIDAttribute string `yaml:"user_id_attribute" env:"OCIS_LDAP_USER_SCHEMA_ID;LDAP_USER_SCHEMA_ID;GRAPH_LDAP_USER_UID_ATTRIBUTE" desc:"LDAP Attribute to use as the unique ID for users. This should be a stable globally unique ID like a UUID." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_USER_SCHEMA_ID changing name for consistency" deprecationReplacement:"OCIS_LDAP_USER_SCHEMA_ID"`
UserIDIsOctetString bool `yaml:"user_id_is_octet_string" env:"OCIS_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING;GRAPH_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING" desc:"Set this to true if the defined 'ID' attribute for users is of the 'OCTETSTRING' syntax. This is required when using the 'objectGUID' attribute of Active Directory for the user ID's."`
UserTypeAttribute string `yaml:"user_type_attribute" env:"OCIS_LDAP_USER_SCHEMA_USER_TYPE;LDAP_USER_SCHEMA_USER_TYPE;GRAPH_LDAP_USER_TYPE_ATTRIBUTE" desc:"LDAP Attribute to distinguish between 'Member' and 'Guest' users. Default is 'ownCloudUserType'." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_USER_SCHEMA_USER_TYPE changing name for consistency" deprecationReplacement:"OCIS_LDAP_USER_SCHEMA_USER_TYPE"`
UserEnabledAttribute string `yaml:"user_enabled_attribute" env:"OCIS_LDAP_USER_ENABLED_ATTRIBUTE;LDAP_USER_ENABLED_ATTRIBUTE;GRAPH_USER_ENABLED_ATTRIBUTE" desc:"LDAP Attribute to use as a flag telling if the user is enabled or disabled." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_USER_ENABLED_ATTRIBUTE changing name for consistency" deprecationReplacement:"OCIS_LDAP_USER_ENABLED_ATTRIBUTE"`
DisableUserMechanism string `yaml:"disable_user_mechanism" env:"OCIS_LDAP_DISABLE_USER_MECHANISM;LDAP_DISABLE_USER_MECHANISM;GRAPH_DISABLE_USER_MECHANISM" desc:"An option to control the behavior for disabling users. Supported options are 'none', 'attribute' and 'group'. If set to 'group', disabling a user via API will add the user to the configured group for disabled users, if set to 'attribute' this will be done in the ldap user entry, if set to 'none' the disable request is not processed. Default is 'attribute'." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_DISABLE_USER_MECHANISM changing name for consistency" deprecationReplacement:"OCIS_LDAP_DISABLE_USER_MECHANISM"`
LdapDisabledUsersGroupDN string `yaml:"ldap_disabled_users_group_dn" env:"OCIS_LDAP_DISABLED_USERS_GROUP_DN;LDAP_DISABLED_USERS_GROUP_DN;GRAPH_DISABLED_USERS_GROUP_DN" desc:"The distinguished name of the group to which added users will be classified as disabled when 'disable_user_mechanism' is set to 'group'." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_DISABLED_USERS_GROUP_DN changing name for consistency" deprecationReplacement:"OCIS_LDAP_DISABLED_USERS_GROUP_DN"`

GroupBaseDN string `yaml:"group_base_dn" env:"OCIS_LDAP_GROUP_BASE_DN;LDAP_GROUP_BASE_DN;GRAPH_LDAP_GROUP_BASE_DN" desc:"Search base DN for looking up LDAP groups." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_GROUP_BASE_DN changing name for consistency" deprecationReplacement:"OCIS_LDAP_GROUP_BASE_DN"`
GroupCreateBaseDN string `yaml:"group_create_base_dn" env:"GRAPH_LDAP_GROUP_CREATE_BASE_DN" desc:"Parent DN under which new groups are created. This DN needs to be subordinate to the 'GRAPH_LDAP_GROUP_BASE_DN'. This setting is only relevant when 'GRAPH_LDAP_SERVER_WRITE_ENABLED' is 'true'. It defaults to the value of 'GRAPH_LDAP_GROUP_BASE_DN'. All groups outside of this subtree are treated as readonly groups and cannot be updated."`
GroupSearchScope string `yaml:"group_search_scope" env:"OCIS_LDAP_GROUP_SCOPE;LDAP_GROUP_SCOPE;GRAPH_LDAP_GROUP_SEARCH_SCOPE" desc:"LDAP search scope to use when looking up groups. Supported scopes are 'base', 'one' and 'sub'." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_GROUP_SCOPE changing name for consistency" deprecationReplacement:"OCIS_LDAP_GROUP_SCOPE"`
GroupFilter string `yaml:"group_filter" env:"OCIS_LDAP_GROUP_FILTER;LDAP_GROUP_FILTER;GRAPH_LDAP_GROUP_FILTER" desc:"LDAP filter to add to the default filters for group searches." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_GROUP_FILTER changing name for consistency" deprecationReplacement:"OCIS_LDAP_GROUP_FILTER"`
GroupObjectClass string `yaml:"group_objectclass" env:"OCIS_LDAP_GROUP_OBJECTCLASS;LDAP_GROUP_OBJECTCLASS;GRAPH_LDAP_GROUP_OBJECTCLASS" desc:"The object class to use for groups in the default group search filter ('groupOfNames')." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_GROUP_OBJECTCLASS changing name for consistency" deprecationReplacement:"OCIS_LDAP_GROUP_OBJECTCLASS"`
GroupNameAttribute string `yaml:"group_name_attribute" env:"OCIS_LDAP_GROUP_SCHEMA_GROUPNAME;LDAP_GROUP_SCHEMA_GROUPNAME;GRAPH_LDAP_GROUP_NAME_ATTRIBUTE" desc:"LDAP Attribute to use for the name of groups." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_GROUP_SCHEMA_GROUPNAME changing name for consistency" deprecationReplacement:"OCIS_LDAP_GROUP_SCHEMA_GROUPNAME"`
GroupIDAttribute string `yaml:"group_id_attribute" env:"OCIS_LDAP_GROUP_SCHEMA_ID;LDAP_GROUP_SCHEMA_ID;GRAPH_LDAP_GROUP_ID_ATTRIBUTE" desc:"LDAP Attribute to use as the unique id for groups. This should be a stable globally unique ID like a UUID." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_GROUP_SCHEMA_ID changing name for consistency" deprecationReplacement:"OCIS_LDAP_GROUP_SCHEMA_ID"`
GroupBaseDN string `yaml:"group_base_dn" env:"OCIS_LDAP_GROUP_BASE_DN;LDAP_GROUP_BASE_DN;GRAPH_LDAP_GROUP_BASE_DN" desc:"Search base DN for looking up LDAP groups." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_GROUP_BASE_DN changing name for consistency" deprecationReplacement:"OCIS_LDAP_GROUP_BASE_DN"`
GroupCreateBaseDN string `yaml:"group_create_base_dn" env:"GRAPH_LDAP_GROUP_CREATE_BASE_DN" desc:"Parent DN under which new groups are created. This DN needs to be subordinate to the 'GRAPH_LDAP_GROUP_BASE_DN'. This setting is only relevant when 'GRAPH_LDAP_SERVER_WRITE_ENABLED' is 'true'. It defaults to the value of 'GRAPH_LDAP_GROUP_BASE_DN'. All groups outside of this subtree are treated as readonly groups and cannot be updated."`
GroupSearchScope string `yaml:"group_search_scope" env:"OCIS_LDAP_GROUP_SCOPE;LDAP_GROUP_SCOPE;GRAPH_LDAP_GROUP_SEARCH_SCOPE" desc:"LDAP search scope to use when looking up groups. Supported scopes are 'base', 'one' and 'sub'." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_GROUP_SCOPE changing name for consistency" deprecationReplacement:"OCIS_LDAP_GROUP_SCOPE"`
GroupFilter string `yaml:"group_filter" env:"OCIS_LDAP_GROUP_FILTER;LDAP_GROUP_FILTER;GRAPH_LDAP_GROUP_FILTER" desc:"LDAP filter to add to the default filters for group searches." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_GROUP_FILTER changing name for consistency" deprecationReplacement:"OCIS_LDAP_GROUP_FILTER"`
GroupObjectClass string `yaml:"group_objectclass" env:"OCIS_LDAP_GROUP_OBJECTCLASS;LDAP_GROUP_OBJECTCLASS;GRAPH_LDAP_GROUP_OBJECTCLASS" desc:"The object class to use for groups in the default group search filter ('groupOfNames')." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_GROUP_OBJECTCLASS changing name for consistency" deprecationReplacement:"OCIS_LDAP_GROUP_OBJECTCLASS"`
GroupNameAttribute string `yaml:"group_name_attribute" env:"OCIS_LDAP_GROUP_SCHEMA_GROUPNAME;LDAP_GROUP_SCHEMA_GROUPNAME;GRAPH_LDAP_GROUP_NAME_ATTRIBUTE" desc:"LDAP Attribute to use for the name of groups." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_GROUP_SCHEMA_GROUPNAME changing name for consistency" deprecationReplacement:"OCIS_LDAP_GROUP_SCHEMA_GROUPNAME"`
GroupIDAttribute string `yaml:"group_id_attribute" env:"OCIS_LDAP_GROUP_SCHEMA_ID;LDAP_GROUP_SCHEMA_ID;GRAPH_LDAP_GROUP_ID_ATTRIBUTE" desc:"LDAP Attribute to use as the unique id for groups. This should be a stable globally unique ID like a UUID." deprecationVersion:"3.0" removalVersion:"4.0.0" deprecationInfo:"LDAP_GROUP_SCHEMA_ID changing name for consistency" deprecationReplacement:"OCIS_LDAP_GROUP_SCHEMA_ID"`
GroupIDIsOctetString bool `yaml:"group_id_is_octet_string" env:"OCIS_LDAP_GROUP_SCHEMA_ID_IS_OCTETSTRING;GRAPH_LDAP_GROUP_SCHEMA_ID_IS_OCTETSTRING" desc:"Set this to true if the defined 'ID' attribute for groups is of the 'OCTETSTRING' syntax. This is required when using the 'objectGUID' attribute of Active Directory for the group ID's."`

EducationResourcesEnabled bool `yaml:"education_resources_enabled" env:"GRAPH_LDAP_EDUCATION_RESOURCES_ENABLED" desc:"Enable LDAP support for managing education related resources."`
EducationConfig LDAPEducationConfig
Expand Down
92 changes: 73 additions & 19 deletions services/graph/pkg/identity/ldap.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

"github.com/CiscoM31/godata"
"github.com/go-ldap/ldap/v3"
"github.com/gofrs/uuid"
"github.com/google/uuid"
"github.com/libregraph/idm/pkg/ldapdn"
libregraph "github.com/owncloud/libre-graph-api-go"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
Expand Down Expand Up @@ -48,21 +48,23 @@ type LDAP struct {
refintEnabled bool
usePwModifyExOp bool

userBaseDN string
userFilter string
userObjectClass string
userScope int
userAttributeMap userAttributeMap
userBaseDN string
userFilter string
userObjectClass string
userIDisOctetString bool
userScope int
userAttributeMap userAttributeMap

disableUserMechanism DisableUserMechanismType
localUserDisableGroupDN string

groupBaseDN string
groupCreateBaseDN string
groupFilter string
groupObjectClass string
groupScope int
groupAttributeMap groupAttributeMap
groupBaseDN string
groupCreateBaseDN string
groupFilter string
groupObjectClass string
groupIDisOctetString bool
groupScope int
groupAttributeMap groupAttributeMap

educationConfig educationConfig

Expand Down Expand Up @@ -146,12 +148,14 @@ func NewLDAPBackend(lc ldap.Client, config config.LDAP, logger *log.Logger) (*LD
userBaseDN: config.UserBaseDN,
userFilter: config.UserFilter,
userObjectClass: config.UserObjectClass,
userIDisOctetString: config.UserIDIsOctetString,
userScope: userScope,
userAttributeMap: uam,
groupBaseDN: config.GroupBaseDN,
groupCreateBaseDN: config.GroupCreateBaseDN,
groupFilter: config.GroupFilter,
groupObjectClass: config.GroupObjectClass,
groupIDisOctetString: config.GroupIDIsOctetString,
groupScope: groupScope,
groupAttributeMap: gam,
educationConfig: educationConfig,
Expand Down Expand Up @@ -262,7 +266,12 @@ func (i *LDAP) UpdateUser(ctx context.Context, nameOrID string, user libregraph.

// Don't allow updates of the ID
if user.GetId() != "" {
if e.GetEqualFoldAttributeValue(i.userAttributeMap.id) != user.GetId() {
id, err := i.ldapUUIDtoString(e, i.userAttributeMap.id, i.userIDisOctetString)
if err != nil {
i.logger.Warn().Str("dn", e.DN).Str(i.userAttributeMap.id, e.GetAttributeValue(i.userAttributeMap.id)).Msg("Invalid User. Cannot convert UUID")
return nil, errorcode.New(errorcode.GeneralException, "error converting uuid")
}
if id != user.GetId() {
return nil, errorcode.New(errorcode.NotAllowed, "changing the UserId is not allowed")
}
}
Expand Down Expand Up @@ -430,15 +439,43 @@ func (i *LDAP) searchLDAPEntryByFilter(basedn string, attrs []string, filter str
return res.Entries[0], nil
}

func filterEscapeUUID(binary bool, id string) (string, error) {
var escaped string
if binary {
pid, err := uuid.Parse(id)
if err != nil {
err := fmt.Errorf("error parsing id '%s' as UUID: %w", id, err)
return "", err
}
for _, b := range pid {
escaped = fmt.Sprintf("%s\\%02x", escaped, b)
}
} else {
escaped = ldap.EscapeFilter(id)
}
return escaped, nil
}

func (i *LDAP) getLDAPUserByID(id string) (*ldap.Entry, error) {
id = ldap.EscapeFilter(id)
filter := fmt.Sprintf("(%s=%s)", i.userAttributeMap.id, id)
idString, err := filterEscapeUUID(i.userIDisOctetString, id)
if err != nil {
return nil, fmt.Errorf("Invalid User id: %w", err)
}
filter := fmt.Sprintf("(%s=%s)", i.userAttributeMap.id, idString)
return i.getLDAPUserByFilter(filter)
}

func (i *LDAP) getLDAPUserByNameOrID(nameOrID string) (*ldap.Entry, error) {
nameOrID = ldap.EscapeFilter(nameOrID)
filter := fmt.Sprintf("(|(%s=%s)(%s=%s))", i.userAttributeMap.userName, nameOrID, i.userAttributeMap.id, nameOrID)
idString, err := filterEscapeUUID(i.userIDisOctetString, nameOrID)
// err != nil just means that this is not a uuid so we can skip the uuid filterpart
// and just filter by name
filter := ""
if err == nil {
filter = fmt.Sprintf("(|(%s=%s)(%s=%s))", i.userAttributeMap.userName, ldap.EscapeFilter(nameOrID), i.userAttributeMap.id, idString)
} else {
filter = fmt.Sprintf("(%s=%s)", i.userAttributeMap.userName, ldap.EscapeFilter(nameOrID))
}

return i.getLDAPUserByFilter(filter)
}

Expand All @@ -451,6 +488,7 @@ func (i *LDAP) getLDAPUserByFilter(filter string) (*ldap.Entry, error) {
i.userAttributeMap.userName,
i.userAttributeMap.surname,
i.userAttributeMap.givenName,

i.userAttributeMap.accountEnabled,
i.userAttributeMap.userType,
}
Expand Down Expand Up @@ -683,13 +721,29 @@ func (i *LDAP) updateUserPassowrd(ctx context.Context, dn, password string) erro
return err
}

func (i *LDAP) ldapUUIDtoString(e *ldap.Entry, attrType string, binary bool) (string, error) {
if binary {
rawValue := e.GetEqualFoldRawAttributeValue(attrType)
value, err := uuid.FromBytes(rawValue)
if err == nil {
return value.String(), nil
}
return "", err
} else {
return e.GetEqualFoldAttributeValue(attrType), nil
}
}

func (i *LDAP) createUserModelFromLDAP(e *ldap.Entry) *libregraph.User {
if e == nil {
return nil
}

opsan := e.GetEqualFoldAttributeValue(i.userAttributeMap.userName)
id := e.GetEqualFoldAttributeValue(i.userAttributeMap.id)
id, err := i.ldapUUIDtoString(e, i.userAttributeMap.id, i.userIDisOctetString)
if err != nil {
i.logger.Warn().Str("dn", e.DN).Str(i.userAttributeMap.id, e.GetAttributeValue(i.userAttributeMap.id)).Msg("Invalid User. Cannot convert UUID")
}
givenName := e.GetEqualFoldAttributeValue(i.userAttributeMap.givenName)
surname := e.GetEqualFoldAttributeValue(i.userAttributeMap.surname)

Expand Down Expand Up @@ -720,7 +774,7 @@ func (i *LDAP) userToLDAPAttrValues(user libregraph.User) (map[string][]string,
}

if !i.useServerUUID {
attrs["owncloudUUID"] = []string{uuid.Must(uuid.NewV4()).String()}
attrs["owncloudUUID"] = []string{uuid.New().String()}
}

if user.AccountEnabled != nil {
Expand Down
7 changes: 6 additions & 1 deletion services/graph/pkg/identity/ldap_education_class.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,12 @@ func (i *LDAP) UpdateEducationClass(ctx context.Context, id string, class libreg
var updateNeeded bool

if class.GetId() != "" {
if g.GetEqualFoldAttributeValue(i.groupAttributeMap.id) != class.GetId() {
id, err := i.ldapUUIDtoString(g, i.groupAttributeMap.id, i.groupIDisOctetString)
if err != nil {
i.logger.Warn().Str("dn", g.DN).Str(i.userAttributeMap.id, g.GetAttributeValue(i.userAttributeMap.id)).Msg("Invalid class. Cannot convert UUID")
return nil, errorcode.New(errorcode.GeneralException, "error converting uuid")
}
if id != class.GetId() {
return nil, errorcode.New(errorcode.NotAllowed, "changing the GroupID is not allowed")
}
}
Expand Down
7 changes: 6 additions & 1 deletion services/graph/pkg/identity/ldap_education_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,12 @@ func (i *LDAP) UpdateEducationUser(ctx context.Context, nameOrID string, user li

// Don't allow updates of the ID
if user.GetId() != "" {
if e.GetEqualFoldAttributeValue(i.userAttributeMap.id) != user.GetId() {
id, err := i.ldapUUIDtoString(e, i.userAttributeMap.id, i.userIDisOctetString)
if err != nil {
i.logger.Warn().Str("dn", e.DN).Str(i.userAttributeMap.id, e.GetAttributeValue(i.userAttributeMap.id)).Msg("Invalid User. Cannot convert UUID")
return nil, errorcode.New(errorcode.GeneralException, "error converting uuid")
}
if id != user.GetId() {
return nil, errorcode.New(errorcode.NotAllowed, "changing the UserId is not allowed")
}
}
Expand Down
23 changes: 18 additions & 5 deletions services/graph/pkg/identity/ldap_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,14 +416,24 @@ func (i *LDAP) groupToLDAPAttrValues(group libregraph.Group) (map[string][]strin
}

func (i *LDAP) getLDAPGroupByID(id string, requestMembers bool) (*ldap.Entry, error) {
id = ldap.EscapeFilter(id)
filter := fmt.Sprintf("(%s=%s)", i.groupAttributeMap.id, id)
idString, err := filterEscapeUUID(i.groupIDisOctetString, id)
if err != nil {
return nil, fmt.Errorf("Invalid group id: %w", err)
}
filter := fmt.Sprintf("(%s=%s)", i.groupAttributeMap.id, idString)
return i.getLDAPGroupByFilter(filter, requestMembers)
}

func (i *LDAP) getLDAPGroupByNameOrID(nameOrID string, requestMembers bool) (*ldap.Entry, error) {
nameOrID = ldap.EscapeFilter(nameOrID)
filter := fmt.Sprintf("(|(%s=%s)(%s=%s))", i.groupAttributeMap.name, nameOrID, i.groupAttributeMap.id, nameOrID)
idString, err := filterEscapeUUID(i.groupIDisOctetString, nameOrID)
// err != nil just means that this is not a uuid so we can skip the uuid filterpart
// and just filter by name
filter := ""
if err == nil {
filter = fmt.Sprintf("(|(%s=%s)(%s=%s))", i.groupAttributeMap.name, ldap.EscapeFilter(nameOrID), i.groupAttributeMap.id, idString)
} else {
filter = fmt.Sprintf("(%s=%s)", i.userAttributeMap.userName, ldap.EscapeFilter(nameOrID))
}
return i.getLDAPGroupByFilter(filter, requestMembers)
}

Expand Down Expand Up @@ -510,7 +520,10 @@ func (i *LDAP) getGroupsForUser(dn string) ([]*ldap.Entry, error) {

func (i *LDAP) createGroupModelFromLDAP(e *ldap.Entry) *libregraph.Group {
name := e.GetEqualFoldAttributeValue(i.groupAttributeMap.name)
id := e.GetEqualFoldAttributeValue(i.groupAttributeMap.id)
id, err := i.ldapUUIDtoString(e, i.groupAttributeMap.id, i.groupIDisOctetString)
if err != nil {
i.logger.Warn().Str("dn", e.DN).Str(i.groupAttributeMap.id, e.GetAttributeValue(i.groupAttributeMap.id)).Msg("Invalid User. Cannot convert UUID")
}
groupTypes := []string{}

if i.isLDAPGroupReadOnly(e) {
Expand Down

0 comments on commit 600e88e

Please sign in to comment.