fix : 권한관리 기능 추가 (각 기능 확인 필요)
- 메뉴관리 - 역할관리 - 부서관리 - 메뉴, 부서, 역할, 유저 - 권한 연동
This commit is contained in:
111
app/Services/Authz/AccessService.php
Normal file
111
app/Services/Authz/AccessService.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Authz;
|
||||
|
||||
use App\Models\Members\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Carbon\Carbon;
|
||||
|
||||
final class AccessService
|
||||
{
|
||||
public static function allows(User $user, string $permission, int $tenantId, ?string $guardName = null): bool
|
||||
{
|
||||
$guard = $guardName ?? config('auth.defaults.guard', 'api'); // ★ 기본 가드
|
||||
$ver = Cache::get("access:version:$tenantId", 1); // ★ 버전 토큰
|
||||
|
||||
$key = "access:$tenantId:{$user->id}:$guard:$permission:v{$ver}"; // ★ 키 강화
|
||||
return Cache::remember($key, now()->addSeconds(20), function () use ($user, $permission, $tenantId, $guard) {
|
||||
|
||||
// 1) 개인 DENY
|
||||
if (self::hasUserOverride($user->id, $permission, $tenantId, false, $guard)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2) Spatie can (팀 컨텍스트는 미들웨어에서 이미 세팅됨)
|
||||
if ($user->can($permission)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3) 부서 ALLOW
|
||||
if (self::departmentAllows($user->id, $permission, $tenantId, $guard)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4) 개인 ALLOW
|
||||
if (self::hasUserOverride($user->id, $permission, $tenantId, true, $guard)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
protected static function hasUserOverride(
|
||||
int $userId,
|
||||
string $permissionName,
|
||||
int $tenantId,
|
||||
bool $allow,
|
||||
?string $guardName = null
|
||||
): bool {
|
||||
$now = now();
|
||||
$guard = $guardName ?? config('auth.defaults.guard', 'api'); // ★
|
||||
|
||||
$q = DB::table('user_permission_overrides as uo')
|
||||
->join('permissions as p', 'p.id', '=', 'uo.permission_id')
|
||||
->whereNull('uo.deleted_at')
|
||||
->where('uo.user_id', $userId)
|
||||
->where('uo.tenant_id', $tenantId)
|
||||
->where('p.name', $permissionName)
|
||||
->where('p.tenant_id', $tenantId) // ★ 테넌트 일치
|
||||
->where('p.guard_name', $guard) // ★ 가드 일치
|
||||
->where(function ($w) use ($now) {
|
||||
$w->whereNull('uo.effective_from')->orWhere('uo.effective_from', '<=', $now);
|
||||
})
|
||||
->where(function ($w) use ($now) {
|
||||
$w->whereNull('uo.effective_to')->orWhere('uo.effective_to', '>=', $now);
|
||||
})
|
||||
->where('uo.is_allowed', $allow ? 1 : 0);
|
||||
|
||||
return $q->exists();
|
||||
}
|
||||
|
||||
protected static function departmentAllows(
|
||||
int $userId,
|
||||
string $permissionName,
|
||||
int $tenantId,
|
||||
?string $guardName = null
|
||||
): bool {
|
||||
$guard = $guardName ?? config('auth.defaults.guard', 'api'); // ★
|
||||
|
||||
$q = DB::table('department_user as du')
|
||||
->join('department_permissions as dp', function ($j) {
|
||||
$j->on('dp.department_id', '=', 'du.department_id')
|
||||
->whereNull('dp.deleted_at')
|
||||
->where('dp.is_allowed', 1);
|
||||
})
|
||||
->join('permissions as p', 'p.id', '=', 'dp.permission_id')
|
||||
->whereNull('du.deleted_at')
|
||||
->where('du.user_id', $userId)
|
||||
->where('du.tenant_id', $tenantId)
|
||||
->where('dp.tenant_id', $tenantId)
|
||||
->where('p.tenant_id', $tenantId) // ★ 테넌트 일치
|
||||
->where('p.guard_name', $guard) // ★ 가드 일치
|
||||
->where('p.name', $permissionName);
|
||||
|
||||
return $q->exists();
|
||||
}
|
||||
|
||||
public static function allowsOrAbort(User $user, string $permission, int $tenantId, ?string $guardName = null): void
|
||||
{
|
||||
if (! self::allows($user, $permission, $tenantId, $guardName)) {
|
||||
abort(403, 'Forbidden');
|
||||
}
|
||||
}
|
||||
|
||||
// (선택) 권한 변경 시 호출해 캐시 무효화
|
||||
public static function bumpVersion(int $tenantId): void
|
||||
{
|
||||
Cache::increment("access:version:$tenantId");
|
||||
}
|
||||
}
|
||||
214
app/Services/Authz/RolePermissionService.php
Normal file
214
app/Services/Authz/RolePermissionService.php
Normal file
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Authz;
|
||||
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
use Spatie\Permission\PermissionRegistrar;
|
||||
use App\Helpers\ApiResponse;
|
||||
|
||||
class RolePermissionService
|
||||
{
|
||||
protected static string $guard = 'api';
|
||||
|
||||
/** 현재 테넌트 컨텍스트로 팀 고정 */
|
||||
protected static function setTeam(int $tenantId): void
|
||||
{
|
||||
app(PermissionRegistrar::class)->setPermissionsTeamId($tenantId);
|
||||
}
|
||||
|
||||
/** 역할 로드 (테넌트/가드 검증) */
|
||||
protected static function loadRoleOrError(int $roleId, int $tenantId): ?Role
|
||||
{
|
||||
$role = Role::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('guard_name', self::$guard)
|
||||
->find($roleId);
|
||||
|
||||
return $role;
|
||||
}
|
||||
|
||||
/** A) permission_names[] → 그대로 사용
|
||||
* B) menus[] + actions[] → "menu:{id}.{act}" 배열로 변환(필요 시 Permission 생성)
|
||||
*/
|
||||
protected static function resolvePermissionNames(int $tenantId, array $params): array
|
||||
{
|
||||
$names = [];
|
||||
|
||||
if (!empty($params['permission_names']) && is_array($params['permission_names'])) {
|
||||
// 문자열 배열만 추림
|
||||
foreach ($params['permission_names'] as $n) {
|
||||
if (is_string($n) && $n !== '') $names[] = trim($n);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($params['menus']) && is_array($params['menus']) &&
|
||||
!empty($params['actions']) && is_array($params['actions'])) {
|
||||
|
||||
$allowed = config('authz.menu_actions', ['view','create','update','delete','approve']);
|
||||
$acts = array_values(array_unique(array_filter(array_map('trim', $params['actions']))));
|
||||
$acts = array_intersect($acts, $allowed);
|
||||
|
||||
$menuIds = array_values(array_unique(array_map('intval', $params['menus'])));
|
||||
|
||||
foreach ($menuIds as $mid) {
|
||||
foreach ($acts as $act) {
|
||||
$names[] = "menu:{$mid}.{$act}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 빈/중복 제거
|
||||
$names = array_values(array_unique(array_filter($names)));
|
||||
|
||||
// 존재하지 않는 Permission은 생성(tenant+guard 포함)
|
||||
foreach ($names as $permName) {
|
||||
Permission::firstOrCreate([
|
||||
'tenant_id' => $tenantId,
|
||||
'guard_name' => self::$guard,
|
||||
'name' => $permName,
|
||||
]);
|
||||
}
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
/** 역할의 퍼미션 목록 */
|
||||
public static function list(int $roleId)
|
||||
{
|
||||
$tenantId = (int) app('tenant_id');
|
||||
|
||||
$role = self::loadRoleOrError($roleId, $tenantId);
|
||||
if (!$role) {
|
||||
return ApiResponse::error('역할을 찾을 수 없습니다.', 404);
|
||||
}
|
||||
|
||||
self::setTeam($tenantId);
|
||||
|
||||
$perms = $role->permissions()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('guard_name', self::$guard)
|
||||
->orderBy('name')
|
||||
->get(['id','tenant_id','name','guard_name','created_at','updated_at']);
|
||||
|
||||
return ApiResponse::response('result', $perms);
|
||||
}
|
||||
|
||||
/** 부여 (중복 무시) */
|
||||
public static function grant(int $roleId, array $params = [])
|
||||
{
|
||||
$tenantId = (int) app('tenant_id');
|
||||
|
||||
$role = self::loadRoleOrError($roleId, $tenantId);
|
||||
if (!$role) {
|
||||
return ApiResponse::error('역할을 찾을 수 없습니다.', 404);
|
||||
}
|
||||
|
||||
// 유효성: 두 방식 중 하나만 요구하진 않지만, 최소 하나는 있어야 함
|
||||
$v = Validator::make($params, [
|
||||
'permission_names' => 'sometimes|array',
|
||||
'permission_names.*' => 'string|min:1',
|
||||
'menus' => 'sometimes|array',
|
||||
'menus.*' => 'integer|min:1',
|
||||
'actions' => 'sometimes|array',
|
||||
'actions.*' => [
|
||||
'string', Rule::in(config('authz.menu_actions', ['view','create','update','delete','approve'])),
|
||||
],
|
||||
]);
|
||||
if ($v->fails()) {
|
||||
return ApiResponse::error($v->errors()->first(), 422);
|
||||
}
|
||||
if (empty($params['permission_names']) && (empty($params['menus']) || empty($params['actions']))) {
|
||||
return ApiResponse::error('permission_names 또는 menus+actions 중 하나는 필요합니다.', 422);
|
||||
}
|
||||
|
||||
self::setTeam($tenantId);
|
||||
|
||||
$names = self::resolvePermissionNames($tenantId, $params);
|
||||
if (empty($names)) {
|
||||
return ApiResponse::error('유효한 퍼미션이 없습니다.', 422);
|
||||
}
|
||||
|
||||
// Spatie: 이름 배열 부여 OK (teams 컨텍스트 적용됨)
|
||||
$role->givePermissionTo($names);
|
||||
|
||||
return ApiResponse::response('success');
|
||||
}
|
||||
|
||||
/** 회수 (없는 건 무시) */
|
||||
public static function revoke(int $roleId, array $params = [])
|
||||
{
|
||||
$tenantId = (int) app('tenant_id');
|
||||
|
||||
$role = self::loadRoleOrError($roleId, $tenantId);
|
||||
if (!$role) {
|
||||
return ApiResponse::error('역할을 찾을 수 없습니다.', 404);
|
||||
}
|
||||
|
||||
$v = Validator::make($params, [
|
||||
'permission_names' => 'sometimes|array',
|
||||
'permission_names.*' => 'string|min:1',
|
||||
'menus' => 'sometimes|array',
|
||||
'menus.*' => 'integer|min:1',
|
||||
'actions' => 'sometimes|array',
|
||||
'actions.*' => [
|
||||
'string', Rule::in(config('authz.menu_actions', ['view','create','update','delete','approve'])),
|
||||
],
|
||||
]);
|
||||
if ($v->fails()) {
|
||||
return ApiResponse::error($v->errors()->first(), 422);
|
||||
}
|
||||
if (empty($params['permission_names']) && (empty($params['menus']) || empty($params['actions']))) {
|
||||
return ApiResponse::error('permission_names 또는 menus+actions 중 하나는 필요합니다.', 422);
|
||||
}
|
||||
|
||||
self::setTeam($tenantId);
|
||||
|
||||
$names = self::resolvePermissionNames($tenantId, $params);
|
||||
if (empty($names)) {
|
||||
return ApiResponse::error('유효한 퍼미션이 없습니다.', 422);
|
||||
}
|
||||
|
||||
$role->revokePermissionTo($names);
|
||||
|
||||
return ApiResponse::response('success');
|
||||
}
|
||||
|
||||
/** 동기화(완전 교체) */
|
||||
public static function sync(int $roleId, array $params = [])
|
||||
{
|
||||
$tenantId = (int) app('tenant_id');
|
||||
|
||||
$role = self::loadRoleOrError($roleId, $tenantId);
|
||||
if (!$role) {
|
||||
return ApiResponse::error('역할을 찾을 수 없습니다.', 404);
|
||||
}
|
||||
|
||||
$v = Validator::make($params, [
|
||||
'permission_names' => 'sometimes|array',
|
||||
'permission_names.*' => 'string|min:1',
|
||||
'menus' => 'sometimes|array',
|
||||
'menus.*' => 'integer|min:1',
|
||||
'actions' => 'sometimes|array',
|
||||
'actions.*' => [
|
||||
'string', Rule::in(config('authz.menu_actions', ['view','create','update','delete','approve'])),
|
||||
],
|
||||
]);
|
||||
if ($v->fails()) {
|
||||
return ApiResponse::error($v->errors()->first(), 422);
|
||||
}
|
||||
if (empty($params['permission_names']) && (empty($params['menus']) || empty($params['actions']))) {
|
||||
return ApiResponse::error('permission_names 또는 menus+actions 중 하나는 필요합니다.', 422);
|
||||
}
|
||||
|
||||
self::setTeam($tenantId);
|
||||
|
||||
$names = self::resolvePermissionNames($tenantId, $params); // 존재하지 않으면 생성
|
||||
// 동기화
|
||||
$role->syncPermissions($names);
|
||||
|
||||
return ApiResponse::response('success');
|
||||
}
|
||||
}
|
||||
141
app/Services/Authz/RoleService.php
Normal file
141
app/Services/Authz/RoleService.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Authz;
|
||||
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Spatie\Permission\PermissionRegistrar;
|
||||
use App\Helpers\ApiResponse;
|
||||
|
||||
class RoleService
|
||||
{
|
||||
protected static string $guard = 'api';
|
||||
|
||||
/** 목록 */
|
||||
public static function index(array $params = [])
|
||||
{
|
||||
$tenantId = (int) app('tenant_id');
|
||||
$page = (int)($params['page'] ?? 1);
|
||||
$size = (int)($params['size'] ?? 10);
|
||||
$q = trim((string)($params['q'] ?? ''));
|
||||
|
||||
$query = Role::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('guard_name', self::$guard);
|
||||
|
||||
if ($q !== '') {
|
||||
$query->where(function($w) use ($q) {
|
||||
$w->where('name', 'like', "%{$q}%")
|
||||
->orWhere('description', 'like', "%{$q}%");
|
||||
});
|
||||
}
|
||||
|
||||
$list = $query->orderBy('id','desc')
|
||||
->paginate($size, ['*'], 'page', $page);
|
||||
|
||||
return ApiResponse::response('result', $list);
|
||||
}
|
||||
|
||||
/** 생성 */
|
||||
public static function store(array $params = [])
|
||||
{
|
||||
$tenantId = (int) app('tenant_id');
|
||||
|
||||
$v = Validator::make($params, [
|
||||
'name' => [
|
||||
'required','string','max:100',
|
||||
Rule::unique('roles','name')->where(fn($q)=>$q
|
||||
->where('tenant_id',$tenantId)
|
||||
->where('guard_name', self::$guard)),
|
||||
],
|
||||
'description' => 'nullable|string|max:255',
|
||||
]);
|
||||
|
||||
if ($v->fails()) {
|
||||
return ApiResponse::error($v->errors()->first(), 422);
|
||||
}
|
||||
|
||||
app(PermissionRegistrar::class)->setPermissionsTeamId($tenantId);
|
||||
|
||||
$role = Role::create([
|
||||
'tenant_id' => $tenantId,
|
||||
'guard_name' => self::$guard,
|
||||
'name' => $v->validated()['name'],
|
||||
'description'=> $params['description'] ?? null,
|
||||
]);
|
||||
|
||||
return ApiResponse::response('result', $role);
|
||||
}
|
||||
|
||||
/** 단건 */
|
||||
public static function show(int $id)
|
||||
{
|
||||
$tenantId = (int) app('tenant_id');
|
||||
|
||||
$role = Role::where('tenant_id',$tenantId)
|
||||
->where('guard_name', self::$guard)
|
||||
->find($id);
|
||||
|
||||
if (!$role) {
|
||||
return ApiResponse::error('역할을 찾을 수 없습니다.', 404);
|
||||
}
|
||||
|
||||
return ApiResponse::response('result', $role);
|
||||
}
|
||||
|
||||
/** 수정 */
|
||||
public static function update(int $id, array $params = [])
|
||||
{
|
||||
$tenantId = (int) app('tenant_id');
|
||||
|
||||
$role = Role::where('tenant_id',$tenantId)
|
||||
->where('guard_name', self::$guard)
|
||||
->find($id);
|
||||
|
||||
if (!$role) {
|
||||
return ApiResponse::error('역할을 찾을 수 없습니다.', 404);
|
||||
}
|
||||
|
||||
$v = Validator::make($params, [
|
||||
'name' => [
|
||||
'sometimes','string','max:100',
|
||||
Rule::unique('roles','name')
|
||||
->where(fn($q)=>$q->where('tenant_id',$tenantId)->where('guard_name', self::$guard))
|
||||
->ignore($role->id),
|
||||
],
|
||||
'description' => 'sometimes|nullable|string|max:255',
|
||||
]);
|
||||
|
||||
if ($v->fails()) {
|
||||
return ApiResponse::error($v->errors()->first(), 422);
|
||||
}
|
||||
|
||||
$payload = $v->validated();
|
||||
$role->fill($payload)->save();
|
||||
|
||||
return ApiResponse::response('result', $role);
|
||||
}
|
||||
|
||||
/** 삭제 (현재는 하드삭제) */
|
||||
public static function destroy(int $id)
|
||||
{
|
||||
$tenantId = (int) app('tenant_id');
|
||||
|
||||
$role = Role::where('tenant_id',$tenantId)
|
||||
->where('guard_name', self::$guard)
|
||||
->find($id);
|
||||
|
||||
if (!$role) {
|
||||
return ApiResponse::error('역할을 찾을 수 없습니다.', 404);
|
||||
}
|
||||
|
||||
DB::transaction(function () use ($role) {
|
||||
// 연관 피벗은 스파티가 onDelete cascade 하므로 기본 동작으로 OK
|
||||
$role->delete(); // ※ 기본 Spatie Role은 SoftDeletes 미사용 → 하드 삭제
|
||||
});
|
||||
|
||||
return ApiResponse::response('success');
|
||||
}
|
||||
}
|
||||
203
app/Services/Authz/UserRoleService.php
Normal file
203
app/Services/Authz/UserRoleService.php
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Authz;
|
||||
|
||||
use App\Helpers\ApiResponse;
|
||||
use App\Models\Members\User;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Spatie\Permission\PermissionRegistrar;
|
||||
|
||||
class UserRoleService
|
||||
{
|
||||
protected static string $guard = 'api';
|
||||
|
||||
/** 팀(테넌트) 컨텍스트 고정 */
|
||||
protected static function setTeam(int $tenantId): void
|
||||
{
|
||||
app(PermissionRegistrar::class)->setPermissionsTeamId($tenantId);
|
||||
}
|
||||
|
||||
/** 유저 로드 (존재 체크) */
|
||||
protected static function loadUserOrError(int $userId): ?User
|
||||
{
|
||||
return User::find($userId);
|
||||
}
|
||||
|
||||
/** 입력으로 받은 role_names[] 또는 role_ids[] → 실제 역할 이름 배열로 변환(검증 포함) */
|
||||
protected static function resolveRoleNames(int $tenantId, array $params): array
|
||||
{
|
||||
$names = [];
|
||||
|
||||
// A) role_names[] 직접
|
||||
if (!empty($params['role_names']) && is_array($params['role_names'])) {
|
||||
foreach ($params['role_names'] as $n) {
|
||||
if (is_string($n) && $n !== '') $names[] = trim($n);
|
||||
}
|
||||
}
|
||||
|
||||
// B) role_ids[] → 이름으로 변환
|
||||
if (!empty($params['role_ids']) && is_array($params['role_ids'])) {
|
||||
$ids = array_values(array_unique(array_map('intval', $params['role_ids'])));
|
||||
if (!empty($ids)) {
|
||||
$rows = Role::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('guard_name', self::$guard)
|
||||
->whereIn('id', $ids)
|
||||
->pluck('name')
|
||||
->all();
|
||||
$names = array_merge($names, $rows);
|
||||
}
|
||||
}
|
||||
|
||||
// 정제
|
||||
$names = array_values(array_unique(array_filter($names)));
|
||||
|
||||
// 존재하지 않는 이름이 섞였는지 확인(실패 시 422로 안내하고 싶다면 여기서 검사)
|
||||
if (!empty($names)) {
|
||||
$count = Role::query()
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('guard_name', self::$guard)
|
||||
->whereIn('name', $names)
|
||||
->count();
|
||||
|
||||
if ($count !== count($names)) {
|
||||
// 존재하지 않는 역할 이름이 포함됨
|
||||
// 필요하면 어떤 이름이 없는지 찾아서 에러 반환하도록 개선 가능
|
||||
}
|
||||
}
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
/** 목록 */
|
||||
public static function list(int $userId)
|
||||
{
|
||||
$tenantId = (int) app('tenant_id');
|
||||
|
||||
$user = self::loadUserOrError($userId);
|
||||
if (!$user) {
|
||||
return ApiResponse::error('사용자를 찾을 수 없습니다.', 404);
|
||||
}
|
||||
|
||||
self::setTeam($tenantId);
|
||||
|
||||
// 현재 테넌트의 역할만
|
||||
$roles = $user->roles()
|
||||
->where('roles.tenant_id', $tenantId)
|
||||
->where('roles.guard_name', self::$guard)
|
||||
->orderBy('roles.id', 'desc')
|
||||
->get(['roles.id','roles.tenant_id','roles.name','roles.description','roles.guard_name','roles.created_at','roles.updated_at']);
|
||||
|
||||
return ApiResponse::response('result', $roles);
|
||||
}
|
||||
|
||||
/** 부여 (중복 무시) */
|
||||
public static function grant(int $userId, array $params = [])
|
||||
{
|
||||
$tenantId = (int) app('tenant_id');
|
||||
|
||||
$user = self::loadUserOrError($userId);
|
||||
if (!$user) {
|
||||
return ApiResponse::error('사용자를 찾을 수 없습니다.', 404);
|
||||
}
|
||||
|
||||
$v = Validator::make($params, [
|
||||
'role_names' => 'sometimes|array',
|
||||
'role_names.*' => 'string|min:1',
|
||||
'role_ids' => 'sometimes|array',
|
||||
'role_ids.*' => 'integer|min:1',
|
||||
]);
|
||||
if ($v->fails()) {
|
||||
return ApiResponse::error($v->errors()->first(), 422);
|
||||
}
|
||||
if (empty($params['role_names']) && empty($params['role_ids'])) {
|
||||
return ApiResponse::error('role_names 또는 role_ids 중 하나는 필요합니다.', 422);
|
||||
}
|
||||
|
||||
self::setTeam($tenantId);
|
||||
|
||||
$names = self::resolveRoleNames($tenantId, $params);
|
||||
if (empty($names)) {
|
||||
return ApiResponse::error('유효한 역할이 없습니다.', 422);
|
||||
}
|
||||
|
||||
// Spatie: 이름 배열로 부여 (teams 컨텍스트 적용)
|
||||
$user->assignRole($names);
|
||||
|
||||
return ApiResponse::response('success');
|
||||
}
|
||||
|
||||
/** 회수 (없는 건 무시) */
|
||||
public static function revoke(int $userId, array $params = [])
|
||||
{
|
||||
$tenantId = (int) app('tenant_id');
|
||||
|
||||
$user = self::loadUserOrError($userId);
|
||||
if (!$user) {
|
||||
return ApiResponse::error('사용자를 찾을 수 없습니다.', 404);
|
||||
}
|
||||
|
||||
$v = Validator::make($params, [
|
||||
'role_names' => 'sometimes|array',
|
||||
'role_names.*' => 'string|min:1',
|
||||
'role_ids' => 'sometimes|array',
|
||||
'role_ids.*' => 'integer|min:1',
|
||||
]);
|
||||
if ($v->fails()) {
|
||||
return ApiResponse::error($v->errors()->first(), 422);
|
||||
}
|
||||
if (empty($params['role_names']) && empty($params['role_ids'])) {
|
||||
return ApiResponse::error('role_names 또는 role_ids 중 하나는 필요합니다.', 422);
|
||||
}
|
||||
|
||||
self::setTeam($tenantId);
|
||||
|
||||
$names = self::resolveRoleNames($tenantId, $params);
|
||||
if (empty($names)) {
|
||||
return ApiResponse::error('유효한 역할이 없습니다.', 422);
|
||||
}
|
||||
|
||||
$user->removeRole($names); // 배열 허용
|
||||
|
||||
return ApiResponse::response('success');
|
||||
}
|
||||
|
||||
/** 동기화(완전 교체) */
|
||||
public static function sync(int $userId, array $params = [])
|
||||
{
|
||||
$tenantId = (int) app('tenant_id');
|
||||
|
||||
$user = self::loadUserOrError($userId);
|
||||
if (!$user) {
|
||||
return ApiResponse::error('사용자를 찾을 수 없습니다.', 404);
|
||||
}
|
||||
|
||||
$v = Validator::make($params, [
|
||||
'role_names' => 'sometimes|array',
|
||||
'role_names.*' => 'string|min:1',
|
||||
'role_ids' => 'sometimes|array',
|
||||
'role_ids.*' => 'integer|min:1',
|
||||
]);
|
||||
if ($v->fails()) {
|
||||
return ApiResponse::error($v->errors()->first(), 422);
|
||||
}
|
||||
if (empty($params['role_names']) && empty($params['role_ids'])) {
|
||||
return ApiResponse::error('role_names 또는 role_ids 중 하나는 필요합니다.', 422);
|
||||
}
|
||||
|
||||
self::setTeam($tenantId);
|
||||
|
||||
$names = self::resolveRoleNames($tenantId, $params);
|
||||
if (empty($names)) {
|
||||
// 빈 목록으로 sync = 모두 제거 의도라면 허용할 수도 있음.
|
||||
// 정책에 맞춰 처리: 여기서는 빈 목록이면 실패 처리
|
||||
return ApiResponse::error('유효한 역할이 없습니다.', 422);
|
||||
}
|
||||
|
||||
$user->syncRoles($names); // 교체
|
||||
|
||||
return ApiResponse::response('success');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user