167 lines
5.5 KiB
Go
167 lines
5.5 KiB
Go
|
|
package usecase
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
|
||
|
|
libmongo "gateway/internal/library/mongo"
|
||
|
|
redislib "gateway/internal/library/redis"
|
||
|
|
permcfg "gateway/internal/model/permission/config"
|
||
|
|
permission "gateway/internal/model/permission/domain"
|
||
|
|
domrepo "gateway/internal/model/permission/domain/repository"
|
||
|
|
dom "gateway/internal/model/permission/domain/usecase"
|
||
|
|
permrepo "gateway/internal/model/permission/repository"
|
||
|
|
)
|
||
|
|
|
||
|
|
// FactoryParam configures the permission module. Repositories may be
|
||
|
|
// pre-built (used by tests / cmd seed) or auto-constructed from MongoConf.
|
||
|
|
type FactoryParam struct {
|
||
|
|
MongoConf *libmongo.Conf
|
||
|
|
Redis *redislib.Client
|
||
|
|
Config permcfg.Config
|
||
|
|
|
||
|
|
// Optional pre-built repositories. When set, MongoConf is ignored
|
||
|
|
// for that repository.
|
||
|
|
Permissions domrepo.PermissionRepository
|
||
|
|
Roles domrepo.RoleRepository
|
||
|
|
RolePermissions domrepo.RolePermissionRepository
|
||
|
|
UserRoles domrepo.UserRoleRepository
|
||
|
|
RoleMappings domrepo.RoleMappingRepository
|
||
|
|
|
||
|
|
// Optional Casbin model text (overrides Config.Casbin.ModelPath).
|
||
|
|
CasbinModelText string
|
||
|
|
}
|
||
|
|
|
||
|
|
// Module bundles all permission usecase ports.
|
||
|
|
type Module struct {
|
||
|
|
Permission dom.PermissionUseCase
|
||
|
|
Role dom.RoleUseCase
|
||
|
|
RolePermission dom.RolePermissionUseCase
|
||
|
|
UserRole dom.UserRoleUseCase
|
||
|
|
RoleMapping dom.RoleMappingUseCase
|
||
|
|
AuthorizationQuery dom.AuthorizationQueryUseCase
|
||
|
|
RBAC dom.RBACUseCase
|
||
|
|
|
||
|
|
Permissions domrepo.PermissionRepository
|
||
|
|
Roles domrepo.RoleRepository
|
||
|
|
RolePermissions domrepo.RolePermissionRepository
|
||
|
|
UserRoles domrepo.UserRoleRepository
|
||
|
|
RoleMappings domrepo.RoleMappingRepository
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewModuleFromParam wires the seven usecases against the configured
|
||
|
|
// repositories. Mongo is required for catalog/role/user-role/mapping;
|
||
|
|
// Redis is required for the Casbin enforcer + pub/sub broadcast.
|
||
|
|
//
|
||
|
|
// When Redis is missing, RBAC stays nil and Permission/Role mutations
|
||
|
|
// continue to work but Check() always denies. Mongo missing returns an
|
||
|
|
// error because the catalog cannot live anywhere else.
|
||
|
|
func NewModuleFromParam(param FactoryParam) (*Module, error) {
|
||
|
|
cfg := param.Config.Defaults()
|
||
|
|
mod := &Module{
|
||
|
|
Permissions: param.Permissions,
|
||
|
|
Roles: param.Roles,
|
||
|
|
RolePermissions: param.RolePermissions,
|
||
|
|
UserRoles: param.UserRoles,
|
||
|
|
RoleMappings: param.RoleMappings,
|
||
|
|
}
|
||
|
|
|
||
|
|
if mod.Permissions == nil {
|
||
|
|
if param.MongoConf == nil || param.MongoConf.Host == "" {
|
||
|
|
return nil, fmt.Errorf("permission: mongo config required")
|
||
|
|
}
|
||
|
|
mod.Permissions = permrepo.NewPermissionRepository(permrepo.PermissionRepositoryParam{Conf: param.MongoConf})
|
||
|
|
}
|
||
|
|
if mod.Roles == nil {
|
||
|
|
mod.Roles = permrepo.NewRoleRepository(permrepo.RoleRepositoryParam{Conf: param.MongoConf})
|
||
|
|
}
|
||
|
|
if mod.RolePermissions == nil {
|
||
|
|
mod.RolePermissions = permrepo.NewRolePermissionRepository(permrepo.RolePermissionRepositoryParam{Conf: param.MongoConf})
|
||
|
|
}
|
||
|
|
if mod.UserRoles == nil {
|
||
|
|
mod.UserRoles = permrepo.NewUserRoleRepository(permrepo.UserRoleRepositoryParam{Conf: param.MongoConf})
|
||
|
|
}
|
||
|
|
if mod.RoleMappings == nil {
|
||
|
|
mod.RoleMappings = permrepo.NewRoleMappingRepository(permrepo.RoleMappingRepositoryParam{Conf: param.MongoConf})
|
||
|
|
}
|
||
|
|
|
||
|
|
mod.Permission = NewPermissionUseCase(PermissionUseCaseParam{Permissions: mod.Permissions})
|
||
|
|
|
||
|
|
var reloader PolicyReloader
|
||
|
|
if cfg.Casbin.Enabled && param.Redis != nil && param.Redis.Zero() != nil {
|
||
|
|
// Plug the bridge so rbac_usecase can build a Redis adapter
|
||
|
|
// without importing repository (avoids cycle).
|
||
|
|
RedisAdapterFactory = func(client *redislib.Client) (domrepo.CasbinPolicyAdapter, error) {
|
||
|
|
if client == nil || client.Zero() == nil {
|
||
|
|
return nil, nil
|
||
|
|
}
|
||
|
|
return permrepo.NewCasbinRedisAdapter(client)
|
||
|
|
}
|
||
|
|
rbacUC, err := NewRBACUseCase(RBACUseCaseParam{
|
||
|
|
Roles: mod.Roles,
|
||
|
|
Permissions: mod.Permissions,
|
||
|
|
RolePermissions: mod.RolePermissions,
|
||
|
|
UserRoles: mod.UserRoles,
|
||
|
|
Redis: param.Redis,
|
||
|
|
ModelPath: cfg.Casbin.ModelPath,
|
||
|
|
CasbinModelText: param.CasbinModelText,
|
||
|
|
ReloadChannel: cfg.Reload.Channel,
|
||
|
|
})
|
||
|
|
if err != nil && !errors.Is(err, permission.ErrCasbinNotConfigured) {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
mod.RBAC = rbacUC
|
||
|
|
if rbacUC != nil {
|
||
|
|
reloader = rbacUC.BroadcastReload
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
mod.Role = NewRoleUseCase(RoleUseCaseParam{
|
||
|
|
Roles: mod.Roles,
|
||
|
|
RolePermissions: mod.RolePermissions,
|
||
|
|
UserRoles: mod.UserRoles,
|
||
|
|
})
|
||
|
|
mod.RolePermission = NewRolePermissionUseCase(RolePermissionUseCaseParam{
|
||
|
|
Roles: mod.Roles,
|
||
|
|
Permissions: mod.Permissions,
|
||
|
|
RolePermissions: mod.RolePermissions,
|
||
|
|
Reloader: reloader,
|
||
|
|
})
|
||
|
|
mod.UserRole = NewUserRoleUseCase(UserRoleUseCaseParam{
|
||
|
|
Roles: mod.Roles,
|
||
|
|
UserRoles: mod.UserRoles,
|
||
|
|
Reloader: reloader,
|
||
|
|
})
|
||
|
|
mod.RoleMapping = NewRoleMappingUseCase(RoleMappingUseCaseParam{
|
||
|
|
Roles: mod.Roles,
|
||
|
|
Mappings: mod.RoleMappings,
|
||
|
|
})
|
||
|
|
mod.AuthorizationQuery = NewAuthorizationQueryUseCase(AuthorizationQueryUseCaseParam{
|
||
|
|
Roles: mod.Roles,
|
||
|
|
Permissions: mod.Permissions,
|
||
|
|
RolePermissions: mod.RolePermissions,
|
||
|
|
UserRoles: mod.UserRoles,
|
||
|
|
})
|
||
|
|
|
||
|
|
return mod, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// StartBackground starts the policy reload subscriber when configured.
|
||
|
|
// Safe to call when RBAC is nil (no-op).
|
||
|
|
func (m *Module) StartBackground(ctx context.Context) error {
|
||
|
|
if m == nil || m.RBAC == nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
return m.RBAC.StartReloadSubscriber(ctx)
|
||
|
|
}
|
||
|
|
|
||
|
|
// StopBackground tears down the subscriber. Safe to call when never
|
||
|
|
// started.
|
||
|
|
func (m *Module) StopBackground() {
|
||
|
|
if m == nil || m.RBAC == nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
m.RBAC.StopReloadSubscriber()
|
||
|
|
}
|