Files
RenewCMS/internal/infrastructure/useCases/roleSupport.go
T

161 lines
3.9 KiB
Go

package useCases
import (
"RenewCMS/internal/domain/audit"
"RenewCMS/internal/domain/builtintype"
"RenewCMS/internal/domain/contenttype"
"RenewCMS/internal/domain/role"
"RenewCMS/internal/domain/user"
"context"
"errors"
"slices"
)
func builtInRole(roles role.Repository, name string) (role.Role, error) {
found, err := roles.GetByName(name)
if err != nil {
return role.Role{}, err
}
if !found.BuiltIn {
return role.Role{}, role.ErrRoleNotFound
}
return found, nil
}
func roleTarget(found role.Role) audit.Target {
return audit.NewTarget(audit.TargetRole, found.ID, found.Name)
}
func allowedTypeKeys(contentTypes contenttype.Repository) ([]string, error) {
types, err := contentTypes.List()
if err != nil {
return nil, err
}
keys := []string{builtintype.KeyArticle}
for _, contentType := range types {
keys = append(keys, contentType.Key)
}
slices.Sort(keys)
return keys, nil
}
func ensureCovered(caller ContentCaller, contentTypes contenttype.Repository, candidate role.Role) error {
typeKeys, err := allowedTypeKeys(contentTypes)
if err != nil {
return err
}
return ensureCoveredBy(caller, typeKeys, candidate)
}
func ensureCoveredBy(caller ContentCaller, typeKeys []string, candidate role.Role) error {
return caller.Grants.Covers(candidate.GrantSet(), typeKeys)
}
func assignableRole(roles role.Repository, id uint32) (role.Role, error) {
if id == 0 {
return role.Role{}, role.ErrUnknownRole
}
found, err := roles.Get(id)
if errors.Is(err, role.ErrRoleNotFound) {
return role.Role{}, role.ErrUnknownRole
}
if err != nil {
return role.Role{}, err
}
if found.Locked {
return role.Role{}, role.ErrRoleLocked
}
return found, nil
}
func coveredAssignableRole(caller ContentCaller, roles role.Repository, contentTypes contenttype.Repository, id uint32) (role.Role, error) {
found, err := assignableRole(roles, id)
if err != nil {
return role.Role{}, err
}
if err := ensureCovered(caller, contentTypes, found); err != nil {
return role.Role{}, err
}
return found, nil
}
func ensureNotOwnRole(roles role.Repository, users user.Repository, caller ContentCaller, target role.Role) error {
account, err := users.Get(caller.UserID)
if err != nil {
return err
}
own, err := resolveRole(roles, account)
if errors.Is(err, role.ErrRoleNotFound) {
return nil
}
if err != nil {
return err
}
if own.ID == target.ID {
return role.ErrOwnRole
}
return nil
}
func ensureEditable(target role.Role) error {
if target.Locked {
return role.ErrRoleLocked
}
return nil
}
func ensureNotBuiltIn(target role.Role) error {
if target.BuiltIn {
return role.ErrBuiltInRole
}
return nil
}
func ensureNameFree(roles role.Repository, name string, excludedID uint32) error {
existing, err := roles.List()
if err != nil {
return err
}
for _, other := range existing {
if other.ID != excludedID && role.SameName(other.Name, name) {
return role.ErrRoleNameTaken
}
}
return nil
}
func validateRoleGrants(contentTypes contenttype.Repository, grants []role.Grant) error {
if err := role.ValidateGrants(false, grants); err != nil {
return err
}
typeKeys, err := allowedTypeKeys(contentTypes)
if err != nil {
return err
}
return role.ValidateGrantTypes(grants, typeKeys)
}
func normalizedGrants(grants []role.Grant) []role.Grant {
return role.Role{Grants: grants}.EffectiveGrants()
}
var writeRefusals = []error{role.ErrRoleConflict, role.ErrRoleHasMembers, role.ErrRoleNotFound, role.ErrRoleNameTaken, role.ErrInvalidReassign}
func recordRoleWriteFailure(ctx context.Context, recorder audit.Recorder, event audit.Event, err error) {
for _, refusal := range writeRefusals {
if errors.Is(err, refusal) {
return
}
}
recordFailure(ctx, recorder, event, err)
}
func grantsParams(diff role.GrantDiff, members int) map[string]any {
return map[string]any{
audit.ParamAdded: diff.Added,
audit.ParamRemoved: diff.Removed,
audit.ParamChanged: diff.Changed,
audit.ParamMembers: members,
}
}