Files
sam-api/app/Services/Authz/UserRoleService.php

195 lines
6.5 KiB
PHP

<?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)));
// 존재 확인(필요시 에러 처리 확장 가능)
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);
// 현재 테넌트의 역할만
$builder = $user->roles()
->where('roles.tenant_id', $tenantId)
->where('roles.guard_name', self::$guard)
->select(['roles.id','roles.tenant_id','roles.name','roles.description','roles.guard_name','roles.created_at','roles.updated_at'])
->orderBy('roles.id', 'desc');
return ApiResponse::response('get', $builder);
}
/** 부여 (중복 무시) */
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);
}
$user->assignRole($names); // teams 컨텍스트 적용됨
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 허용 시: $user->syncRoles([]) 로 전부 제거 가능
return ApiResponse::error('유효한 역할이 없습니다.', 422);
}
$user->syncRoles($names);
return ApiResponse::response('success');
}
}