diff --git a/services/auth-basic/pkg/config/config.go b/services/auth-basic/pkg/config/config.go index d70d781d42..5e39cbf37f 100644 --- a/services/auth-basic/pkg/config/config.go +++ b/services/auth-basic/pkg/config/config.go @@ -84,6 +84,7 @@ type LDAPProvider struct { type LDAPUserSchema struct { ID string `yaml:"id" env:"OC_LDAP_USER_SCHEMA_ID;AUTH_BASIC_LDAP_USER_SCHEMA_ID" desc:"LDAP Attribute to use as the unique ID for users. This should be a stable globally unique ID like a UUID." introductionVersion:"1.0.0"` + TenantID string `yaml:"tenant_id" env:"OC_LDAP_USER_SCHEMA_TENANT_ID;AUTH_BASIC_LDAP_USER_SCHEMA_TENANT_ID" desc:"LDAP Attribute to use for the tenant ID of users. This is used to identify the tenant of a user in a multi-tenant environment." introductionVersion:"%%NEXT%%"` IDIsOctetString bool `yaml:"id_is_octet_string" env:"OC_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING;AUTH_BASIC_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 e.g. required when using the 'objectGUID' attribute of Active Directory for the user IDs." introductionVersion:"1.0.0"` Mail string `yaml:"mail" env:"OC_LDAP_USER_SCHEMA_MAIL;AUTH_BASIC_LDAP_USER_SCHEMA_MAIL" desc:"LDAP Attribute to use for the email address of users." introductionVersion:"1.0.0"` DisplayName string `yaml:"display_name" env:"OC_LDAP_USER_SCHEMA_DISPLAYNAME;AUTH_BASIC_LDAP_USER_SCHEMA_DISPLAYNAME" desc:"LDAP Attribute to use for the displayname of users." introductionVersion:"1.0.0"` diff --git a/services/auth-basic/pkg/revaconfig/config.go b/services/auth-basic/pkg/revaconfig/config.go index ae99e2ea04..cc8ebc343c 100644 --- a/services/auth-basic/pkg/revaconfig/config.go +++ b/services/auth-basic/pkg/revaconfig/config.go @@ -77,6 +77,7 @@ func ldapConfigFromString(cfg config.LDAPProvider) map[string]interface{} { "idp": cfg.IDP, "user_schema": map[string]interface{}{ "id": cfg.UserSchema.ID, + "tenantId": cfg.UserSchema.TenantID, "idIsOctetString": cfg.UserSchema.IDIsOctetString, "mail": cfg.UserSchema.Mail, "displayName": cfg.UserSchema.DisplayName, diff --git a/services/graph/pkg/config/config.go b/services/graph/pkg/config/config.go index de8adbc67a..bad9728278 100644 --- a/services/graph/pkg/config/config.go +++ b/services/graph/pkg/config/config.go @@ -73,6 +73,7 @@ type LDAP struct { UserNameAttribute string `yaml:"user_name_attribute" env:"OC_LDAP_USER_SCHEMA_USERNAME;GRAPH_LDAP_USER_NAME_ATTRIBUTE" desc:"LDAP Attribute to use for username of users." introductionVersion:"1.0.0"` UserIDAttribute string `yaml:"user_id_attribute" env:"OC_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." introductionVersion:"1.0.0"` UserIDIsOctetString bool `yaml:"user_id_is_octet_string" env:"OC_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." introductionVersion:"1.0.0"` + UserTenantIDAttribute string `yaml:"user_tenant_id_attribute" env:"OC_LDAP_USER_SCHEMA_TENANT_ID;GRAPH_LDAP_USER_SCHEMA_TENANT_ID" desc:"LDAP Attribute to use for the tenant ID of users. This is used to identify the tenant of a user in a multi-tenant environment." introductionVersion:"%%NEXT%%"` UserTypeAttribute string `yaml:"user_type_attribute" env:"OC_LDAP_USER_SCHEMA_USER_TYPE;GRAPH_LDAP_USER_TYPE_ATTRIBUTE" desc:"LDAP Attribute to distinguish between 'Member' and 'Guest' users. Default is 'openCloudUserType'." introductionVersion:"1.0.0"` UserEnabledAttribute string `yaml:"user_enabled_attribute" env:"OC_LDAP_USER_ENABLED_ATTRIBUTE;GRAPH_USER_ENABLED_ATTRIBUTE" desc:"LDAP Attribute to use as a flag telling if the user is enabled or disabled." introductionVersion:"1.0.0"` DisableUserMechanism string `yaml:"disable_user_mechanism" env:"OC_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:"1.0.0"` diff --git a/services/graph/pkg/config/defaults/defaultconfig.go b/services/graph/pkg/config/defaults/defaultconfig.go index 4ed274c70a..21888f1aa3 100644 --- a/services/graph/pkg/config/defaults/defaultconfig.go +++ b/services/graph/pkg/config/defaults/defaultconfig.go @@ -96,6 +96,7 @@ func DefaultConfig() *config.Config { // FIXME: switch this to some more widely available attribute by default // ideally this needs to be constant for the lifetime of a users UserIDAttribute: "openCloudUUID", + UserTenantIDAttribute: "", UserTypeAttribute: "openCloudUserType", UserEnabledAttribute: "openCloudUserEnabled", DisableUserMechanism: "attribute", diff --git a/services/graph/pkg/identity/cs3.go b/services/graph/pkg/identity/cs3.go index de375df599..0afc9f38e9 100644 --- a/services/graph/pkg/identity/cs3.go +++ b/services/graph/pkg/identity/cs3.go @@ -10,12 +10,12 @@ import ( cs3group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" cs3user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + libregraph "github.com/opencloud-eu/libre-graph-api-go" "github.com/opencloud-eu/opencloud/pkg/log" "github.com/opencloud-eu/opencloud/pkg/shared" "github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode" "github.com/opencloud-eu/opencloud/services/graph/pkg/odata" "github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool" - libregraph "github.com/opencloud-eu/libre-graph-api-go" ) var ( @@ -167,7 +167,7 @@ func (i *CS3) GetGroups(ctx context.Context, oreq *godata.GoDataRequest) ([]*lib // CreateGroup implements the Backend Interface. It's currently not supported for the CS3 backend func (i *CS3) CreateGroup(ctx context.Context, group libregraph.Group) (*libregraph.Group, error) { - return nil, errorcode.New(errorcode.NotSupported, "not implemented") + return nil, errNotImplemented } // GetGroup implements the Backend Interface. @@ -202,25 +202,25 @@ func (i *CS3) GetGroup(ctx context.Context, groupID string, queryParam url.Value // DeleteGroup implements the Backend Interface. It's currently not supported for the CS3 backend func (i *CS3) DeleteGroup(ctx context.Context, id string) error { - return errorcode.New(errorcode.NotSupported, "not implemented") + return errNotImplemented } // UpdateGroupName implements the Backend Interface. It's currently not supported for the CS3 backend func (i *CS3) UpdateGroupName(ctx context.Context, groupID string, groupName string) error { - return errorcode.New(errorcode.NotSupported, "not implemented") + return errNotImplemented } // GetGroupMembers implements the Backend Interface. It's currently not supported for the CS3 backend func (i *CS3) GetGroupMembers(ctx context.Context, groupID string, _ *godata.GoDataRequest) ([]*libregraph.User, error) { - return nil, errorcode.New(errorcode.NotSupported, "not implemented") + return nil, errNotImplemented } // AddMembersToGroup implements the Backend Interface. It's currently not supported for the CS3 backend func (i *CS3) AddMembersToGroup(ctx context.Context, groupID string, memberID []string) error { - return errorcode.New(errorcode.NotSupported, "not implemented") + return errNotImplemented } // RemoveMemberFromGroup implements the Backend Interface. It's currently not supported for the CS3 backend func (i *CS3) RemoveMemberFromGroup(ctx context.Context, groupID string, memberID string) error { - return errorcode.New(errorcode.NotSupported, "not implemented") + return errNotImplemented } diff --git a/services/graph/pkg/identity/ldap.go b/services/graph/pkg/identity/ldap.go index 9ca26bf51b..bc6ae71b94 100644 --- a/services/graph/pkg/identity/ldap.go +++ b/services/graph/pkg/identity/ldap.go @@ -15,6 +15,7 @@ import ( "github.com/google/uuid" "github.com/libregraph/idm/pkg/ldapdn" libregraph "github.com/opencloud-eu/libre-graph-api-go" + ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx" "github.com/opencloud-eu/opencloud/pkg/log" "github.com/opencloud-eu/opencloud/services/graph/pkg/config" @@ -80,6 +81,7 @@ type LDAP struct { type userAttributeMap struct { displayName string id string + tenantId string mail string userName string givenName string @@ -115,6 +117,7 @@ func NewLDAPBackend(lc ldap.Client, config config.LDAP, logger *log.Logger) (*LD uam := userAttributeMap{ displayName: config.UserDisplayNameAttribute, id: config.UserIDAttribute, + tenantId: config.UserTenantIDAttribute, mail: config.UserEmailAttribute, userName: config.UserNameAttribute, accountEnabled: config.UserEnabledAttribute, @@ -614,7 +617,17 @@ func (i *LDAP) FilterUsers(ctx context.Context, oreq *godata.GoDataRequest, filt i.userAttributeMap.displayName, search, ) } - userFilter = fmt.Sprintf("(&%s(objectClass=%s)%s%s)", i.userFilter, i.userObjectClass, queryFilter, userFilter) + + // apply tenant filter if applicable + var tenantFilter string + if i.userAttributeMap.tenantId != "" { + currentUser, ok := ctxpkg.ContextGetUser(ctx) + if ok && currentUser.Id.GetTenantId() != "" { + tenantFilter = fmt.Sprintf("(%s=%s)", i.userAttributeMap.tenantId, ldap.EscapeFilter(currentUser.Id.GetTenantId())) + } + } + + userFilter = fmt.Sprintf("(&%s(objectClass=%s)%s%s%s)", i.userFilter, i.userObjectClass, queryFilter, userFilter, tenantFilter) searchRequest := ldap.NewSearchRequest( i.userBaseDN, i.userScope, ldap.NeverDerefAliases, 0, 0, false, userFilter, diff --git a/services/idm/ldif/base.ldif.tmpl b/services/idm/ldif/base.ldif.tmpl index b70c284acb..0d5a1c5c81 100644 --- a/services/idm/ldif/base.ldif.tmpl +++ b/services/idm/ldif/base.ldif.tmpl @@ -31,6 +31,7 @@ displayName: Admin description: An admin for this OpenCloud instance. mail: admin@example.org openCloudUUID: {{ .ID }} +openCloudTenantId: {{ .TenantID }} openCloudExternalIdentity: $ {{ .Issuer }} $ {{ .ID }} {{ else -}} dn: uid={{ .Name }},ou=sysusers,o=libregraph-idm diff --git a/services/idm/ldif/demousers.ldif.tmpl b/services/idm/ldif/demousers.ldif.tmpl index 881c90f9f2..798265e716 100644 --- a/services/idm/ldif/demousers.ldif.tmpl +++ b/services/idm/ldif/demousers.ldif.tmpl @@ -15,6 +15,7 @@ mail: alan@example.org openCloudUserEnabled: TRUE openCloudUUID: b1f74ec4-dd7e-11ef-a543-03775734d0f7 openCloudExternalIdentity: $ {{.}} $ b1f74ec4-dd7e-11ef-a543-03775734d0f7 +openCloudTenantId: cd22ea13-f6b4-4f5f-a2c2-69b5a0f07a8b userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTE2JGg1NUxqckhWVjdEdXVzTkxjbXRoa0EkMzZ3aGZSMjdyTDFOYXQxa0xTajdrVGFubTBnb3VKRGZ0ck9DTStuRHo5cw== dn: uid=lynn,ou=users,o=libregraph-idm @@ -34,6 +35,7 @@ mail: lynn@example.org openCloudUserEnabled: TRUE openCloudUUID: 60708dda-e897-11ef-919f-bbb7437d6ec2 openCloudExternalIdentity: $ {{.}} $ 60708dda-e897-11ef-919f-bbb7437d6ec2 +openCloudTenantId: cd22ea13-f6b4-4f5f-a2c2-69b5a0f07a8b userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTE2JGg1NUxqckhWVjdEdXVzTkxjbXRoa0EkMzZ3aGZSMjdyTDFOYXQxa0xTajdrVGFubTBnb3VKRGZ0ck9DTStuRHo5cw== dn: uid=mary,ou=users,o=libregraph-idm @@ -53,6 +55,7 @@ mail: mary@example.org openCloudUserEnabled: TRUE openCloudUUID: 056fc874-dd7f-11ef-ba84-af6fca4b7289 openCloudExternalIdentity: $ {{.}} $ 056fc874-dd7f-11ef-ba84-af6fca4b7289 +openCloudTenantId: cd22ea13-f6b4-4f5f-a2c2-69b5a0f07a8b userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTE2JGg1NUxqckhWVjdEdXVzTkxjbXRoa0EkMzZ3aGZSMjdyTDFOYXQxa0xTajdrVGFubTBnb3VKRGZ0ck9DTStuRHo5cw== dn: uid=margaret,ou=users,o=libregraph-idm @@ -72,6 +75,7 @@ mail: margaret@example.org openCloudUserEnabled: TRUE openCloudUUID: 801abee4-dd7f-11ef-a324-83f55a754b62 openCloudExternalIdentity: $ {{.}} $ 801abee4-dd7f-11ef-a324-83f55a754b62 +openCloudTenantId: cd22ea13-f6b4-4f5f-a2c2-69b5a0f07a8b userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTE2JGg1NUxqckhWVjdEdXVzTkxjbXRoa0EkMzZ3aGZSMjdyTDFOYXQxa0xTajdrVGFubTBnb3VKRGZ0ck9DTStuRHo5cw== dn: uid=dennis,ou=users,o=libregraph-idm @@ -91,6 +95,7 @@ mail: dennis@example.org openCloudUserEnabled: TRUE openCloudUUID: cd88bf9a-dd7f-11ef-a609-7f78deb2345f openCloudExternalIdentity: $ {{.}} $ cd88bf9a-dd7f-11ef-a609-7f78deb2345f +openCloudTenantId: cd22ea13-f6b4-4f5f-a2c2-69b5a0f07a8b userPassword:: e0FSR09OMn0kYXJnb24yaWQkdj0xOSRtPTY1NTM2LHQ9MSxwPTE2JGg1NUxqckhWVjdEdXVzTkxjbXRoa0EkMzZ3aGZSMjdyTDFOYXQxa0xTajdrVGFubTBnb3VKRGZ0ck9DTStuRHo5cw== dn: cn=users,ou=groups,o=libregraph-idm diff --git a/services/idm/pkg/command/server.go b/services/idm/pkg/command/server.go index 1789ce98f3..fda1404d2e 100644 --- a/services/idm/pkg/command/server.go +++ b/services/idm/pkg/command/server.go @@ -132,6 +132,7 @@ func bootstrap(logger log.Logger, cfg *config.Config, srvcfg server.Config) erro Name string Password string ID string + TenantID string Issuer string } @@ -151,12 +152,16 @@ func bootstrap(logger log.Logger, cfg *config.Config, srvcfg server.Config) erro } if cfg.AdminUserID != "" { - serviceUsers = append(serviceUsers, svcUser{ + adminUser := svcUser{ Name: "admin", Password: cfg.ServiceUserPasswords.OCAdmin, ID: cfg.AdminUserID, Issuer: cfg.DemoUsersIssuerUrl, - }) + } + if cfg.CreateDemoUsers { + adminUser.TenantID = "cd22ea13-f6b4-4f5f-a2c2-69b5a0f07a8b" + } + serviceUsers = append(serviceUsers, adminUser) } bdb := &ldbbolt.LdbBolt{} diff --git a/services/users/pkg/config/config.go b/services/users/pkg/config/config.go index 0ae8658941..63b426cc84 100644 --- a/services/users/pkg/config/config.go +++ b/services/users/pkg/config/config.go @@ -86,6 +86,7 @@ type LDAPDriver struct { type LDAPUserSchema struct { ID string `yaml:"id" env:"OC_LDAP_USER_SCHEMA_ID;USERS_LDAP_USER_SCHEMA_ID" desc:"LDAP Attribute to use as the unique ID for users. This should be a stable globally unique ID like a UUID." introductionVersion:"1.0.0"` + TenantID string `yaml:"tenant_id" env:"OC_LDAP_USER_SCHEMA_TENANT_ID;USERS_LDAP_USER_SCHEMA_TENANT_ID" desc:"LDAP Attribute to use for the tenant ID of users. This is used to identify the tenant of a user in a multi-tenant environment." introductionVersion:"%%NEXT%%"` IDIsOctetString bool `yaml:"id_is_octet_string" env:"OC_LDAP_USER_SCHEMA_ID_IS_OCTETSTRING;USERS_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 e.g. required when using the 'objectGUID' attribute of Active Directory for the user ID's." introductionVersion:"1.0.0"` Mail string `yaml:"mail" env:"OC_LDAP_USER_SCHEMA_MAIL;USERS_LDAP_USER_SCHEMA_MAIL" desc:"LDAP Attribute to use for the email address of users." introductionVersion:"1.0.0"` DisplayName string `yaml:"display_name" env:"OC_LDAP_USER_SCHEMA_DISPLAYNAME;USERS_LDAP_USER_SCHEMA_DISPLAYNAME" desc:"LDAP Attribute to use for the displayname of users." introductionVersion:"1.0.0"` diff --git a/services/users/pkg/revaconfig/config.go b/services/users/pkg/revaconfig/config.go index 8f72b2e789..71cc2b9c6c 100644 --- a/services/users/pkg/revaconfig/config.go +++ b/services/users/pkg/revaconfig/config.go @@ -79,6 +79,7 @@ func ldapConfigFromString(cfg config.LDAPDriver) map[string]interface{} { "idp": cfg.IDP, "user_schema": map[string]interface{}{ "id": cfg.UserSchema.ID, + "tenantId": cfg.UserSchema.TenantID, "idIsOctetString": cfg.UserSchema.IDIsOctetString, "mail": cfg.UserSchema.Mail, "displayName": cfg.UserSchema.DisplayName,