148 lines
4.2 KiB
Go
148 lines
4.2 KiB
Go
package usecase
|
|
|
|
import (
|
|
"context"
|
|
|
|
"gateway/internal/model/permission/domain"
|
|
"gateway/internal/model/permission/domain/entity"
|
|
"gateway/internal/model/permission/domain/enum"
|
|
domrepo "gateway/internal/model/permission/domain/repository"
|
|
dom "gateway/internal/model/permission/domain/usecase"
|
|
|
|
"github.com/zeromicro/go-zero/core/logx"
|
|
)
|
|
|
|
// UserRoleUseCaseParam injects role + user-role repositories.
|
|
type UserRoleUseCaseParam struct {
|
|
Roles domrepo.RoleRepository
|
|
UserRoles domrepo.UserRoleRepository
|
|
Reloader PolicyReloader
|
|
}
|
|
|
|
type userRoleUseCase struct {
|
|
roles domrepo.RoleRepository
|
|
userRoles domrepo.UserRoleRepository
|
|
reload PolicyReloader
|
|
}
|
|
|
|
// NewUserRoleUseCase returns the assignment manager used by tenant
|
|
// admins and SyncFromX flows.
|
|
func NewUserRoleUseCase(param UserRoleUseCaseParam) dom.UserRoleUseCase {
|
|
return &userRoleUseCase{
|
|
roles: param.Roles,
|
|
userRoles: param.UserRoles,
|
|
reload: param.Reloader,
|
|
}
|
|
}
|
|
|
|
func (uc *userRoleUseCase) Assign(ctx context.Context, param *dom.AssignParam) (*entity.UserRole, error) {
|
|
if param == nil || param.TenantID == "" || param.UID == "" || param.RoleID == "" {
|
|
return nil, errb.InputMissingRequired("tenant_id|uid|role_id")
|
|
}
|
|
role, err := uc.roles.GetByID(ctx, param.TenantID, param.RoleID)
|
|
if err != nil {
|
|
return nil, wrapRepoErr(err)
|
|
}
|
|
source := param.Source
|
|
if source == "" {
|
|
source = enum.RoleSourceManual
|
|
}
|
|
if !source.IsValid() {
|
|
return nil, errb.InputInvalidFormat("invalid source")
|
|
}
|
|
ur := &entity.UserRole{
|
|
TenantID: param.TenantID,
|
|
UID: param.UID,
|
|
RoleID: role.ID.Hex(),
|
|
Source: source,
|
|
}
|
|
if err := uc.userRoles.Insert(ctx, ur); err != nil {
|
|
return nil, wrapRepoErr(err, "assign role")
|
|
}
|
|
uc.broadcast(ctx, param.TenantID)
|
|
return ur, nil
|
|
}
|
|
|
|
func (uc *userRoleUseCase) Revoke(ctx context.Context, tenantID, uid, roleID string) error {
|
|
if err := uc.userRoles.Delete(ctx, tenantID, uid, roleID); err != nil {
|
|
return wrapRepoErr(err, "revoke role")
|
|
}
|
|
uc.broadcast(ctx, tenantID)
|
|
return nil
|
|
}
|
|
|
|
func (uc *userRoleUseCase) List(ctx context.Context, tenantID, uid string) ([]*dom.UserRoleSummary, error) {
|
|
rows, err := uc.userRoles.ListByUser(ctx, tenantID, uid)
|
|
if err != nil {
|
|
return nil, wrapRepoErr(err)
|
|
}
|
|
if len(rows) == 0 {
|
|
return nil, nil
|
|
}
|
|
ids := make([]string, 0, len(rows))
|
|
for _, ur := range rows {
|
|
ids = append(ids, ur.RoleID)
|
|
}
|
|
roles, err := uc.roles.ListByTenantAndIDs(ctx, tenantID, ids)
|
|
if err != nil {
|
|
return nil, wrapRepoErr(err)
|
|
}
|
|
roleByID := make(map[string]*entity.Role, len(roles))
|
|
for _, role := range roles {
|
|
roleByID[role.ID.Hex()] = role
|
|
}
|
|
out := make([]*dom.UserRoleSummary, 0, len(rows))
|
|
for _, ur := range rows {
|
|
summary := &dom.UserRoleSummary{UserRole: ur}
|
|
if role, ok := roleByID[ur.RoleID]; ok {
|
|
summary.RoleKey = role.Key
|
|
summary.RoleDisplayName = role.DisplayName
|
|
}
|
|
out = append(out, summary)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (uc *userRoleUseCase) ReplaceForSource(
|
|
ctx context.Context,
|
|
tenantID, uid string,
|
|
source enum.RoleSource,
|
|
roleKeys []string,
|
|
) error {
|
|
if !source.IsValid() {
|
|
return errb.InputInvalidFormat("invalid source")
|
|
}
|
|
if source == enum.RoleSourceManual {
|
|
// Manual assignments are managed via Assign/Revoke; protect from
|
|
// accidental wipe by SyncFromX flows (defence in depth).
|
|
return errb.ResInvalidState("manual source cannot be batch-replaced")
|
|
}
|
|
roleIDs := make([]string, 0, len(roleKeys))
|
|
for _, key := range roleKeys {
|
|
role, err := uc.roles.GetByKey(ctx, tenantID, key)
|
|
if err != nil {
|
|
// Skip unknown keys silently — keeps SyncFromX resilient when
|
|
// the IdP exposes groups the tenant has not mapped yet.
|
|
continue
|
|
}
|
|
roleIDs = append(roleIDs, role.ID.Hex())
|
|
}
|
|
if err := uc.userRoles.ReplaceForSource(ctx, tenantID, uid, source, roleIDs); err != nil {
|
|
return wrapRepoErr(err, "replace user roles")
|
|
}
|
|
uc.broadcast(ctx, tenantID)
|
|
return nil
|
|
}
|
|
|
|
func (uc *userRoleUseCase) broadcast(ctx context.Context, tenantID string) {
|
|
if uc.reload == nil {
|
|
return
|
|
}
|
|
if err := uc.reload(ctx, tenantID); err != nil {
|
|
logx.WithContext(ctx).Errorf("permission user-role: broadcast reload tenant=%s: %v", tenantID, err)
|
|
}
|
|
}
|
|
|
|
var _ dom.UserRoleUseCase = (*userRoleUseCase)(nil)
|
|
var _ = domain.ReservedRoleKeyPrefixes // ensure domain package referenced for go build
|