pkg/client/keycloak/adapter/gocloak_adapter_roles.go (91 lines of code) (raw):
package adapter
import (
"context"
"fmt"
"github.com/Nerzal/gocloak/v12"
"github.com/pkg/errors"
"github.com/epam/edp-keycloak-operator/pkg/client/keycloak/dto"
)
func (a GoCloakAdapter) SyncRealmRole(ctx context.Context, realmName string, role *dto.PrimaryRealmRole) error {
if err := a.createOrUpdateRealmRole(ctx, realmName, role); err != nil {
return errors.Wrap(err, "error during createOrUpdateRealmRole")
}
if err := a.makeRoleDefault(ctx, realmName, role); err != nil {
return errors.Wrap(err, "error during makeRoleDefault")
}
return nil
}
func (a GoCloakAdapter) createOrUpdateRealmRole(ctx context.Context, realmName string, role *dto.PrimaryRealmRole) error {
exists := true
currentRealmRole, err := a.client.GetRealmRole(ctx, a.token.AccessToken, realmName, role.Name)
if err != nil {
if !IsErrNotFound(err) {
return fmt.Errorf("failed to get realm role: %w", err)
}
exists = false
}
if exists {
role.ID = currentRealmRole.ID
}
if !exists {
var roleID string
if roleID, err = a.CreatePrimaryRealmRole(ctx, realmName, role); err != nil {
return err
}
role.ID = &roleID
}
if role.IsComposite {
if err = a.syncRoleComposites(ctx, realmName, role); err != nil {
return err
}
}
if exists {
currentRealmRole.Composite = &role.IsComposite
currentRealmRole.Attributes = &role.Attributes
currentRealmRole.Description = &role.Description
if err = a.client.UpdateRealmRole(ctx, a.token.AccessToken, realmName, role.Name, *currentRealmRole); err != nil {
return errors.Wrap(err, "unable to update realm role")
}
}
return nil
}
func (a GoCloakAdapter) ExistRealmRole(realmName string, roleName string) (bool, error) {
reqLog := a.log.WithValues("realm name", realmName, "role name", roleName)
reqLog.Info("Start check existing realm role...")
_, err := a.client.GetRealmRole(context.Background(), a.token.AccessToken, realmName, roleName)
res, err := strip404(err)
if err != nil {
return false, err
}
reqLog.Info("Check existing realm role has been finished", "result", res)
return res, nil
}
func (a GoCloakAdapter) DeleteRealmRole(ctx context.Context, realm, roleName string) error {
if err := a.client.DeleteRealmRole(ctx, a.token.AccessToken, realm, roleName); err != nil {
return errors.Wrap(err, "unable to delete realm role")
}
return nil
}
// makeRoleDefault makes the role default if it is required.
// For this purpose, the role is added to the composite role with the name "default-roles-{realmName}".
func (a GoCloakAdapter) makeRoleDefault(ctx context.Context, realmName string, role *dto.PrimaryRealmRole) error {
if !role.IsDefault {
return nil
}
if err := a.client.AddRealmRoleComposite(
ctx,
a.token.AccessToken,
realmName,
GetDefaultCompositeRoleName(realmName),
[]gocloak.Role{
{
ID: role.ID,
Name: &role.Name,
},
},
); err != nil {
return fmt.Errorf("failed to make the role default: %w", err)
}
return nil
}
// GetDefaultCompositeRoleName returns the name of the composite role, which stores all default roles for the given realm.
// The name is generated according to the Keycloak documentation: https://www.keycloak.org/docs/22.0.5/release_notes/#default-roles-processing-improvement
func GetDefaultCompositeRoleName(realmName string) string {
return "default-roles-" + realmName
}