Skip to content

Commit 0b76ae2

Browse files
authored
Merge pull request #12232 from kobergj/feat/OCISDEV-794-ldap-group-additional-objectclasses
feat(graph): [OCISDEV-794] allow multiple objectClasses on group creation
2 parents 147d428 + b76032d commit 0b76ae2

7 files changed

Lines changed: 61 additions & 35 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Enhancement: Allow multiple objectClasses on group creation
2+
3+
Added support for configuring additional LDAP objectClasses when creating groups.
4+
The new `OCIS_LDAP_GROUP_ADDITIONAL_OBJECTCLASSES` / `GRAPH_LDAP_GROUP_ADDITIONAL_OBJECTCLASSES`
5+
environment variable accepts a list of extra objectClasses that are set alongside the
6+
primary `GRAPH_LDAP_GROUP_OBJECTCLASS` when a new group is created in LDAP.
7+
8+
https://github.com/owncloud/ocis/pull/12229

deployments/examples/ocis_multi/config/ldap/schemas/10_owncloud_schema.ldif

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,5 @@ olcObjectClasses: ( ownCloudOid:1.2.2 NAME 'ownCloudUser'
5252
MAY ( ocExternalIdentity $ ownCloudUserEnabled $ ownCloudUserType $ ocLastSignInTimestamp $ ownCloudMemberOf $ ownCloudGuestOf $ ownCloudRole) )
5353
olcObjectClasses: ( ownCloudOid:1.2.3 NAME 'ownCloudGroup'
5454
DESC 'ownCloud Group LDAP Schema'
55-
SUP groupOfNames
56-
STRUCTURAL
55+
AUXILIARY
5756
MAY ( ownCloudMemberOf ) )

deployments/examples/ocis_multi/docker-compose.yml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ services:
6060
command: [ "-c", "ocis init || true; exec ocis server" ]
6161
environment:
6262
# Keycloak IDP specific configuration
63-
PROXY_AUTOPROVISION_ACCOUNTS: "true"
6463
PROXY_ROLE_ASSIGNMENT_DRIVER: "oidc"
6564
OCIS_OIDC_ISSUER: https://${KEYCLOAK_DOMAIN:-keycloak.owncloud.test}/realms/${KEYCLOAK_REALM:-oCIS}
6665
PROXY_OIDC_REWRITE_WELLKNOWN: "true"
@@ -75,6 +74,9 @@ services:
7574
PROXY_USER_CS3_CLAIM: "username"
7675
# INSECURE: needed if oCIS / Traefik is using self generated certificates
7776
OCIS_INSECURE: "${INSECURE:-true}"
77+
OCIS_ADD_RUN_SERVICES: "auth-app"
78+
PROXY_ENABLE_APP_AUTH: true
79+
AUTH_APP_ENABLE_IMPERSONATION: true
7880
OCIS_EXCLUDE_RUN_SERVICES: "idp,idm"
7981
GRAPH_ASSIGN_DEFAULT_USER_ROLE: "false"
8082
GRAPH_USERNAME_MATCH: "none"
@@ -90,7 +92,8 @@ services:
9092
OCIS_LDAP_BIND_PASSWORD: ${LDAP_ADMIN_PASSWORD:-admin}
9193
OCIS_LDAP_GROUP_BASE_DN: "ou=groups,dc=owncloud,dc=com"
9294
GRAPH_LDAP_GROUP_CREATE_BASE_DN: "ou=groups-ec730a6c-1b63-4b45-b83b-9e2311afdf85,ou=groups,dc=owncloud,dc=com"
93-
OCIS_LDAP_GROUP_OBJECTCLASS: "owncloudGroup"
95+
OCIS_LDAP_GROUP_OBJECTCLASS: "groupOfNames"
96+
OCIS_LDAP_GROUP_ADDITIONAL_OBJECTCLASSES: "ownCloudGroup"
9497
OCIS_LDAP_USER_BASE_DN: "ou=users,dc=owncloud,dc=com"
9598
OCIS_LDAP_USER_OBJECTCLASS: "inetOrgPerson"
9699
LDAP_LOGIN_ATTRIBUTES: "uid"
@@ -121,8 +124,6 @@ services:
121124
OCIS_MULTI_INSTANCE_GUEST_ROLE: "user-light"
122125
OCIS_LDAP_CROSS_INSTANCE_REFERENCE_TEMPLATE: "{{.Username}}@{{.Instancename}}.owncloud.test"
123126
OCIS_LDAP_INSTANCE_URL_TEMPLATE: "https://{{.Instancename}}.owncloud.test"
124-
# FIXME: sync groups properly to keycloak and remove the next line
125-
PROXY_AUTOPROVISION_CLAIM_GROUPS: ""
126127
# specific for deployment example
127128
PROXY_ROLE_ASSIGNMENT_OIDC_CLAIM: ownCloudRole
128129
volumes:
@@ -153,7 +154,6 @@ services:
153154
command: ["-c", "ocis init || true; ocis server"]
154155
environment:
155156
# Keycloak IDP specific configuration
156-
PROXY_AUTOPROVISION_ACCOUNTS: "true"
157157
PROXY_ROLE_ASSIGNMENT_DRIVER: "oidc"
158158
OCIS_OIDC_ISSUER: https://${KEYCLOAK_DOMAIN:-keycloak.owncloud.test}/realms/${KEYCLOAK_REALM:-oCIS}
159159
PROXY_OIDC_REWRITE_WELLKNOWN: "true"
@@ -222,8 +222,6 @@ services:
222222
OCIS_MULTI_INSTANCE_GUEST_ROLE: "user-light"
223223
OCIS_LDAP_CROSS_INSTANCE_REFERENCE_TEMPLATE: "{{.Username}}@{{.Instancename}}.owncloud.test"
224224
OCIS_LDAP_INSTANCE_URL_TEMPLATE: "https://{{.Instancename}}.owncloud.test"
225-
# FIXME: sync groups properly to keycloak and remove the next line
226-
PROXY_AUTOPROVISION_CLAIM_GROUPS: ""
227225
PROXY_ROLE_ASSIGNMENT_OIDC_CLAIM: ownCloudRole
228226
volumes:
229227
- ./config/ocis/csp-ocm.yaml:/etc/ocis/csp-ocm.yaml

services/graph/pkg/config/config.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,16 @@ type LDAP struct {
8080
DisableUserMechanism string `yaml:"disable_user_mechanism" env:"OCIS_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'." introductionVersion:"pre5.0"`
8181
LdapDisabledUsersGroupDN string `yaml:"ldap_disabled_users_group_dn" env:"OCIS_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'." introductionVersion:"pre5.0"`
8282

83-
GroupBaseDN string `yaml:"group_base_dn" env:"OCIS_LDAP_GROUP_BASE_DN;GRAPH_LDAP_GROUP_BASE_DN" desc:"Search base DN for looking up LDAP groups." introductionVersion:"pre5.0"`
84-
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." introductionVersion:"pre5.0"`
85-
GroupSearchScope string `yaml:"group_search_scope" env:"OCIS_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'." introductionVersion:"pre5.0"`
86-
GroupFilter string `yaml:"group_filter" env:"OCIS_LDAP_GROUP_FILTER;GRAPH_LDAP_GROUP_FILTER" desc:"LDAP filter to add to the default filters for group searches." introductionVersion:"pre5.0"`
87-
GroupObjectClass string `yaml:"group_objectclass" env:"OCIS_LDAP_GROUP_OBJECTCLASS;GRAPH_LDAP_GROUP_OBJECTCLASS" desc:"The object class to use for groups in the default group search filter ('groupOfNames')." introductionVersion:"pre5.0"`
88-
GroupNameAttribute string `yaml:"group_name_attribute" env:"OCIS_LDAP_GROUP_SCHEMA_GROUPNAME;GRAPH_LDAP_GROUP_NAME_ATTRIBUTE" desc:"LDAP Attribute to use for the name of groups." introductionVersion:"pre5.0"`
89-
GroupMemberAttribute string `yaml:"group_member_attribute" env:"OCIS_LDAP_GROUP_SCHEMA_MEMBER;GRAPH_LDAP_GROUP_MEMBER_ATTRIBUTE" desc:"LDAP Attribute that is used for group members." introductionVersion:"pre5.0"`
90-
GroupIDAttribute string `yaml:"group_id_attribute" env:"OCIS_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." introductionVersion:"pre5.0"`
91-
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." introductionVersion:"pre5.0"`
83+
GroupBaseDN string `yaml:"group_base_dn" env:"OCIS_LDAP_GROUP_BASE_DN;GRAPH_LDAP_GROUP_BASE_DN" desc:"Search base DN for looking up LDAP groups." introductionVersion:"pre5.0"`
84+
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." introductionVersion:"pre5.0"`
85+
GroupSearchScope string `yaml:"group_search_scope" env:"OCIS_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'." introductionVersion:"pre5.0"`
86+
GroupFilter string `yaml:"group_filter" env:"OCIS_LDAP_GROUP_FILTER;GRAPH_LDAP_GROUP_FILTER" desc:"LDAP filter to add to the default filters for group searches." introductionVersion:"pre5.0"`
87+
GroupObjectClass string `yaml:"group_objectclass" env:"OCIS_LDAP_GROUP_OBJECTCLASS;GRAPH_LDAP_GROUP_OBJECTCLASS" desc:"The object class to use for groups in the default group search filter ('groupOfNames')." introductionVersion:"pre5.0"`
88+
GroupAdditionalObjectClasses []string `yaml:"group_additional_objectclasses" env:"OCIS_LDAP_GROUP_ADDITIONAL_OBJECTCLASSES;GRAPH_LDAP_GROUP_ADDITIONAL_OBJECTCLASSES" desc:"Additional object classes to set when creating new groups (e.g. 'posixGroup'). The value of 'GRAPH_LDAP_GROUP_OBJECTCLASS' is always included." introductionVersion:"Deledda"`
89+
GroupNameAttribute string `yaml:"group_name_attribute" env:"OCIS_LDAP_GROUP_SCHEMA_GROUPNAME;GRAPH_LDAP_GROUP_NAME_ATTRIBUTE" desc:"LDAP Attribute to use for the name of groups." introductionVersion:"pre5.0"`
90+
GroupMemberAttribute string `yaml:"group_member_attribute" env:"OCIS_LDAP_GROUP_SCHEMA_MEMBER;GRAPH_LDAP_GROUP_MEMBER_ATTRIBUTE" desc:"LDAP Attribute that is used for group members." introductionVersion:"pre5.0"`
91+
GroupIDAttribute string `yaml:"group_id_attribute" env:"OCIS_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." introductionVersion:"pre5.0"`
92+
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." introductionVersion:"pre5.0"`
9293

9394
EducationResourcesEnabled bool `yaml:"education_resources_enabled" env:"GRAPH_LDAP_EDUCATION_RESOURCES_ENABLED" desc:"Enable LDAP support for managing education related resources." introductionVersion:"pre5.0"`
9495
EducationConfig LDAPEducationConfig

services/graph/pkg/identity/ldap.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@ type LDAP struct {
6666
groupBaseDN string
6767
groupCreateBaseDN string
6868
groupFilter string
69-
groupObjectClass string
70-
groupIDisOctetString bool
69+
groupObjectClass string
70+
groupAdditionalObjectClasses []string
71+
groupIDisOctetString bool
7172
groupScope int
7273
groupAttributeMap groupAttributeMap
7374

@@ -202,6 +203,7 @@ func NewLDAPBackend(lc ldap.Client, config config.LDAP, logger *log.Logger, inst
202203
groupCreateBaseDN: config.GroupCreateBaseDN,
203204
groupFilter: config.GroupFilter,
204205
groupObjectClass: config.GroupObjectClass,
206+
groupAdditionalObjectClasses: config.GroupAdditionalObjectClasses,
205207
groupIDisOctetString: config.GroupIDIsOctetString,
206208
groupScope: groupScope,
207209
groupAttributeMap: gam,
@@ -1306,8 +1308,9 @@ func replaceDN(fullDN *ldap.DN, newDN string) (string, error) {
13061308
func (i *LDAP) CreateLDAPGroupByDN(dn string) error {
13071309
ar := ldap.NewAddRequest(dn, nil)
13081310

1311+
objectClasses := append([]string{i.groupObjectClass, "top"}, i.groupAdditionalObjectClasses...)
13091312
attrs := map[string][]string{
1310-
"objectClass": {i.groupObjectClass, "top"},
1313+
"objectClass": objectClasses,
13111314
"member": {""},
13121315
}
13131316

services/graph/pkg/identity/ldap_group.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,8 @@ func (i *LDAP) groupToLDAPAttrValues(group libregraph.Group) (map[string][]strin
459459
i.groupAttributeMap.member: {""},
460460
}
461461

462+
attrs["objectClass"] = append(attrs["objectClass"], i.groupAdditionalObjectClasses...)
463+
462464
if !i.useServerUUID {
463465
attrs["owncloudUUID"] = []string{uuid.Must(uuid.NewV4()).String()}
464466
attrs["objectClass"] = append(attrs["objectClass"], "owncloud")

services/graph/pkg/identity/ldap_group_test.go

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -455,20 +455,31 @@ func TestUpdateGroupName(t *testing.T) {
455455

456456
func TestGroupToLDAPAttrValuesUsesConfiguredObjectClass(t *testing.T) {
457457
tests := []struct {
458-
name string
459-
groupObjectClass string
458+
name string
459+
groupObjectClass string
460+
groupAdditionalObjectClasses []string
461+
expectedObjectClasses []string
460462
}{
461463
{
462-
name: "default groupOfNames",
463-
groupObjectClass: "groupOfNames",
464+
name: "default groupOfNames",
465+
groupObjectClass: "groupOfNames",
466+
expectedObjectClasses: []string{"groupOfNames", "top", "owncloud"},
464467
},
465468
{
466-
name: "custom groupOfUniqueNames",
467-
groupObjectClass: "groupOfUniqueNames",
469+
name: "custom groupOfUniqueNames",
470+
groupObjectClass: "groupOfUniqueNames",
471+
expectedObjectClasses: []string{"groupOfUniqueNames", "top", "owncloud"},
468472
},
469473
{
470-
name: "custom posixGroup",
471-
groupObjectClass: "posixGroup",
474+
name: "custom posixGroup",
475+
groupObjectClass: "posixGroup",
476+
expectedObjectClasses: []string{"posixGroup", "top", "owncloud"},
477+
},
478+
{
479+
name: "additional objectClasses",
480+
groupObjectClass: "groupOfNames",
481+
groupAdditionalObjectClasses: []string{"posixGroup", "extensibleObject"},
482+
expectedObjectClasses: []string{"groupOfNames", "top", "posixGroup", "extensibleObject", "owncloud"},
472483
},
473484
}
474485

@@ -477,6 +488,7 @@ func TestGroupToLDAPAttrValuesUsesConfiguredObjectClass(t *testing.T) {
477488
// Setup config with custom groupObjectClass
478489
testConfig := lconfig
479490
testConfig.GroupObjectClass = tt.groupObjectClass
491+
testConfig.GroupAdditionalObjectClasses = tt.groupAdditionalObjectClasses
480492

481493
lm := &mocks.Client{}
482494
b, err := getMockedBackend(lm, testConfig, &logger)
@@ -500,14 +512,17 @@ func TestGroupToLDAPAttrValuesUsesConfiguredObjectClass(t *testing.T) {
500512
t.Fatal("Expected objectClass attribute to be present")
501513
}
502514

503-
// Check objectClass has exactly 3 elements
504-
if len(objectClasses) != 3 {
505-
t.Errorf("Expected objectClass to have exactly 3 elements, got %d: %v", len(objectClasses), objectClasses)
515+
if len(objectClasses) != len(tt.expectedObjectClasses) {
516+
t.Errorf("Expected objectClass to have %d elements, got %d: %v", len(tt.expectedObjectClasses), len(objectClasses), objectClasses)
506517
}
507518

508-
// Check first element is the configured groupObjectClass (exact match)
509-
if objectClasses[0] != tt.groupObjectClass {
510-
t.Errorf("Expected first objectClass to be '%s', got '%s'", tt.groupObjectClass, objectClasses[0])
519+
for i, expected := range tt.expectedObjectClasses {
520+
if i >= len(objectClasses) {
521+
break
522+
}
523+
if objectClasses[i] != expected {
524+
t.Errorf("Expected objectClass[%d] to be '%s', got '%s'", i, expected, objectClasses[i])
525+
}
511526
}
512527
})
513528
}

0 commit comments

Comments
 (0)