controllers/keycloakclient/chain/process_permissions.go (188 lines of code) (raw):
package chain
import (
"context"
"fmt"
"github.com/Nerzal/gocloak/v12"
ctrl "sigs.k8s.io/controller-runtime"
keycloakApi "github.com/epam/edp-keycloak-operator/api/v1"
"github.com/epam/edp-keycloak-operator/pkg/client/keycloak"
"github.com/epam/edp-keycloak-operator/pkg/client/keycloak/adapter"
)
const permissionLogKey = "permission"
type ProcessPermissions struct {
keycloakApiClient keycloak.Client
}
func NewProcessPermissions(keycloakApiClient keycloak.Client) *ProcessPermissions {
return &ProcessPermissions{keycloakApiClient: keycloakApiClient}
}
func (h *ProcessPermissions) Serve(ctx context.Context, keycloakClient *keycloakApi.KeycloakClient, realmName string) error {
log := ctrl.LoggerFrom(ctx)
if keycloakClient.Spec.Authorization == nil {
log.Info("Authorization settings are not specified")
return nil
}
clientID, err := h.keycloakApiClient.GetClientID(keycloakClient.Spec.ClientId, realmName)
if err != nil {
return fmt.Errorf("failed to get client id: %w", err)
}
existingPermissions, err := h.keycloakApiClient.GetPermissions(ctx, realmName, clientID)
if err != nil {
return fmt.Errorf("failed to get permissions: %w", err)
}
for i := 0; i < len(keycloakClient.Spec.Authorization.Permissions); i++ {
log.Info("Processing permission", permissionLogKey, keycloakClient.Spec.Authorization.Permissions[i].Name)
var permissionRepresentation *gocloak.PermissionRepresentation
if permissionRepresentation, err = h.toPermissionRepresentation(ctx, &keycloakClient.Spec.Authorization.Permissions[i], clientID, realmName); err != nil {
return fmt.Errorf("failed to convert permission: %w", err)
}
existingPermission, ok := existingPermissions[keycloakClient.Spec.Authorization.Permissions[i].Name]
if ok {
permissionRepresentation.ID = existingPermission.ID
if err = h.keycloakApiClient.UpdatePermission(ctx, realmName, clientID, *permissionRepresentation); err != nil {
return fmt.Errorf("failed to update permission: %w", err)
}
log.Info("Permission updated", permissionLogKey, keycloakClient.Spec.Authorization.Permissions[i].Name)
delete(existingPermissions, keycloakClient.Spec.Authorization.Permissions[i].Name)
continue
}
if _, err = h.keycloakApiClient.CreatePermission(ctx, realmName, clientID, *permissionRepresentation); err != nil {
return fmt.Errorf("failed to create permission: %w", err)
}
log.Info("Permission created", permissionLogKey, keycloakClient.Spec.Authorization.Permissions[i].Name)
}
if err = h.deletePermissions(ctx, existingPermissions, realmName, clientID); err != nil {
return err
}
return nil
}
func (h *ProcessPermissions) deletePermissions(ctx context.Context, existingPermissions map[string]gocloak.PermissionRepresentation, realmName string, clientID string) error {
log := ctrl.LoggerFrom(ctx)
for name := range existingPermissions {
if name == "Default Permission" {
continue
}
if err := h.keycloakApiClient.DeletePermission(ctx, realmName, clientID, *existingPermissions[name].ID); err != nil {
if !adapter.IsErrNotFound(err) {
return fmt.Errorf("failed to delete permission: %w", err)
}
}
log.Info("Permission deleted", permissionLogKey, name)
}
return nil
}
// toPermissionRepresentation converts keycloakApi.Permission to gocloak.PermissionRepresentation.
func (h *ProcessPermissions) toPermissionRepresentation(ctx context.Context, permission *keycloakApi.Permission, clientID, realm string) (*gocloak.PermissionRepresentation, error) {
keycloakPermission := getBasePermissionRepresentation(permission)
if err := h.mapResources(ctx, permission, keycloakPermission, realm, clientID); err != nil {
return nil, fmt.Errorf("failed to map resources: %w", err)
}
if err := h.mapPolicies(ctx, permission, keycloakPermission, realm, clientID); err != nil {
return nil, fmt.Errorf("failed to map policies: %w", err)
}
if permission.Type == keycloakApi.PermissionTypeScope {
if err := h.mapScopes(ctx, permission, keycloakPermission, realm, clientID); err != nil {
return nil, fmt.Errorf("failed to map scopes: %w", err)
}
}
return keycloakPermission, nil
}
func (h *ProcessPermissions) mapResources(
ctx context.Context,
permission *keycloakApi.Permission,
keycloakPermission *gocloak.PermissionRepresentation,
realm,
clientID string,
) error {
if len(permission.Resources) == 0 {
keycloakPermission.Resources = &[]string{}
return nil
}
existingResources, err := h.keycloakApiClient.GetResources(ctx, realm, clientID)
if err != nil {
return fmt.Errorf("failed to get resources: %w", err)
}
permissionResources := make([]string, 0, len(permission.Resources))
for _, r := range permission.Resources {
existingResource, ok := existingResources[r]
if !ok {
return fmt.Errorf("resource %s does not exist", r)
}
if existingResource.ID == nil {
return fmt.Errorf("resource %s does not have ID", r)
}
permissionResources = append(permissionResources, *existingResource.ID)
}
keycloakPermission.Resources = &permissionResources
return nil
}
func (h *ProcessPermissions) mapPolicies(
ctx context.Context,
permission *keycloakApi.Permission,
keycloakPermission *gocloak.PermissionRepresentation,
realm,
clientID string,
) error {
if len(permission.Policies) == 0 {
keycloakPermission.Policies = &[]string{}
return nil
}
existingPolicies, err := h.keycloakApiClient.GetPolicies(ctx, realm, clientID)
if err != nil {
return fmt.Errorf("failed to get polices: %w", err)
}
permissionPolicies := make([]string, 0, len(permission.Policies))
for _, r := range permission.Policies {
existingPolicy, ok := existingPolicies[r]
if !ok {
return fmt.Errorf("policy %s does not exist", r)
}
if existingPolicy.ID == nil {
return fmt.Errorf("policy %s does not have ID", r)
}
permissionPolicies = append(permissionPolicies, *existingPolicy.ID)
}
keycloakPermission.Policies = &permissionPolicies
return nil
}
func (h *ProcessPermissions) mapScopes(
ctx context.Context,
permission *keycloakApi.Permission,
keycloakPermission *gocloak.PermissionRepresentation,
realm,
clientID string,
) error {
if len(permission.Scopes) == 0 {
keycloakPermission.Scopes = &[]string{}
return nil
}
existingScopes, err := h.keycloakApiClient.GetScopes(ctx, realm, clientID)
if err != nil {
return fmt.Errorf("failed to get scopes: %w", err)
}
permissionScopes := make([]string, 0, len(permission.Scopes))
for _, r := range permission.Scopes {
existingScope, ok := existingScopes[r]
if !ok {
return fmt.Errorf("scope %s does not exist", r)
}
if existingScope.ID == nil {
return fmt.Errorf("scope %s does not have ID", r)
}
permissionScopes = append(permissionScopes, *existingScope.ID)
}
keycloakPermission.Scopes = &permissionScopes
return nil
}
func getBasePermissionRepresentation(policy *keycloakApi.Permission) *gocloak.PermissionRepresentation {
keycloakPermission := &gocloak.PermissionRepresentation{}
name := policy.Name
keycloakPermission.Name = &name
pType := policy.Type
keycloakPermission.Type = &pType
desc := policy.Description
decisionStrategy := gocloak.DecisionStrategy(policy.DecisionStrategy)
keycloakPermission.DecisionStrategy = &decisionStrategy
keycloakPermission.Description = &desc
logic := gocloak.Logic(policy.Logic)
keycloakPermission.Logic = &logic
return keycloakPermission
}