diff --git a/api/v1beta1/owner_role.go b/api/v1beta1/owner_role.go index 25ff22d2..dc73309d 100644 --- a/api/v1beta1/owner_role.go +++ b/api/v1beta1/owner_role.go @@ -12,12 +12,37 @@ const ( ClusterRoleNamesAnnotation = "clusterrolenames.capsule.clastix.io" ) -func (in OwnerSpec) GetRoles(tenant Tenant) []string { +// GetRoles read the annotation available in the Tenant specification and if it matches the pattern +// clusterrolenames.capsule.clastix.io/${KIND}.${NAME} returns the associated roles. +// Kubernetes annotations and labels must respect RFC 1123 about DNS names and this could be cumbersome in two cases: +// 1. identifying users based on their email address +// 2. the overall length of the annotation key that is exceeding 63 characters +// For emails, the symbol @ can be replaced with the placeholder __AT__. +// For the latter one, the index of the owner can be used to force the retrieval. +func (in OwnerSpec) GetRoles(tenant Tenant, index int) []string { for key, value := range tenant.GetAnnotations() { - if key == fmt.Sprintf("%s/%s.%s", ClusterRoleNamesAnnotation, strings.ToLower(in.Kind.String()), strings.ToLower(in.Name)) { + if !strings.HasPrefix(key, fmt.Sprintf("%s/", ClusterRoleNamesAnnotation)) { + continue + } + + for symbol, replace := range in.convertMap() { + key = strings.ReplaceAll(key, symbol, replace) + } + + nameBased := key == fmt.Sprintf("%s/%s.%s", ClusterRoleNamesAnnotation, strings.ToLower(in.Kind.String()), strings.ToLower(in.Name)) + + indexBased := key == fmt.Sprintf("%s/%d", ClusterRoleNamesAnnotation, index) + + if nameBased || indexBased { return strings.Split(value, ",") } } return []string{"admin", "capsule-namespace-deleter"} } + +func (in OwnerSpec) convertMap() map[string]string { + return map[string]string{ + "__AT__": "@", + } +} diff --git a/controllers/tenant/rolebindings.go b/controllers/tenant/rolebindings.go index 9b65ba87..96682047 100644 --- a/controllers/tenant/rolebindings.go +++ b/controllers/tenant/rolebindings.go @@ -61,8 +61,8 @@ func (r *Manager) syncRoleBindings(ctx context.Context, tenant *capsulev1beta1.T // getting requested Role Binding keys keys := make([]string, 0, len(tenant.Spec.Owners)) // Generating for dynamic tenant owners cluster roles - for _, owner := range tenant.Spec.Owners { - for _, clusterRoleName := range owner.GetRoles(*tenant) { + for index, owner := range tenant.Spec.Owners { + for _, clusterRoleName := range owner.GetRoles(*tenant, index) { cr := r.ownerClusterRoleBindings(owner, clusterRoleName) keys = append(keys, hashFn(cr)) @@ -103,8 +103,8 @@ func (r *Manager) syncAdditionalRoleBinding(ctx context.Context, tenant *capsule var roleBindings []capsulev1beta1.AdditionalRoleBindingsSpec - for _, owner := range tenant.Spec.Owners { - for _, clusterRoleName := range owner.GetRoles(*tenant) { + for index, owner := range tenant.Spec.Owners { + for _, clusterRoleName := range owner.GetRoles(*tenant, index) { roleBindings = append(roleBindings, r.ownerClusterRoleBindings(owner, clusterRoleName)) } }