- Validator::make를 FormRequest로 분리 (6개 생성) - 하드코딩 한글 문자열을 i18n 키로 교체 - RoleMenuPermission 데드코드 제거 - Role 모델 SpatieRole 상속으로 일원화 - 권한 변경 후 캐시 무효화 추가 (AccessService::bumpVersion) - 미문서화 8개 Swagger 엔드포인트 추가 - 역할/권한 라우트에 perm.map+permission 미들웨어 추가
168 lines
4.8 KiB
PHP
168 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Authz;
|
|
|
|
use App\Models\Members\User;
|
|
use App\Models\Permissions\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 invalidateCache(int $tenantId): void
|
|
{
|
|
AccessService::bumpVersion($tenantId);
|
|
app(PermissionRegistrar::class)->forgetCachedPermissions();
|
|
}
|
|
|
|
/** 유저 로드 (존재 체크) */
|
|
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)) {
|
|
Role::query()
|
|
->where('tenant_id', $tenantId)
|
|
->where('guard_name', self::$guard)
|
|
->whereIn('name', $names)
|
|
->count();
|
|
}
|
|
|
|
return $names;
|
|
}
|
|
|
|
/** 목록 */
|
|
public static function list(int $userId)
|
|
{
|
|
$tenantId = (int) app('tenant_id');
|
|
|
|
$user = self::loadUserOrError($userId);
|
|
if (! $user) {
|
|
return ['error' => __('error.role.user_not_found'), 'code' => 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 $builder->get();
|
|
}
|
|
|
|
/** 부여 (중복 무시) */
|
|
public static function grant(int $userId, array $params = [])
|
|
{
|
|
$tenantId = (int) app('tenant_id');
|
|
|
|
$user = self::loadUserOrError($userId);
|
|
if (! $user) {
|
|
return ['error' => __('error.role.user_not_found'), 'code' => 404];
|
|
}
|
|
|
|
self::setTeam($tenantId);
|
|
|
|
$names = self::resolveRoleNames($tenantId, $params);
|
|
if (empty($names)) {
|
|
return ['error' => __('error.role.no_valid_roles'), 'code' => 422];
|
|
}
|
|
|
|
$user->assignRole($names);
|
|
|
|
self::invalidateCache($tenantId);
|
|
|
|
return 'success';
|
|
}
|
|
|
|
/** 회수 (없는 건 무시) */
|
|
public static function revoke(int $userId, array $params = [])
|
|
{
|
|
$tenantId = (int) app('tenant_id');
|
|
|
|
$user = self::loadUserOrError($userId);
|
|
if (! $user) {
|
|
return ['error' => __('error.role.user_not_found'), 'code' => 404];
|
|
}
|
|
|
|
self::setTeam($tenantId);
|
|
|
|
$names = self::resolveRoleNames($tenantId, $params);
|
|
if (empty($names)) {
|
|
return ['error' => __('error.role.no_valid_roles'), 'code' => 422];
|
|
}
|
|
|
|
$user->removeRole($names);
|
|
|
|
self::invalidateCache($tenantId);
|
|
|
|
return 'success';
|
|
}
|
|
|
|
/** 동기화(완전 교체) */
|
|
public static function sync(int $userId, array $params = [])
|
|
{
|
|
$tenantId = (int) app('tenant_id');
|
|
|
|
$user = self::loadUserOrError($userId);
|
|
if (! $user) {
|
|
return ['error' => __('error.role.user_not_found'), 'code' => 404];
|
|
}
|
|
|
|
self::setTeam($tenantId);
|
|
|
|
$names = self::resolveRoleNames($tenantId, $params);
|
|
if (empty($names)) {
|
|
return ['error' => __('error.role.no_valid_roles'), 'code' => 422];
|
|
}
|
|
|
|
$user->syncRoles($names);
|
|
|
|
self::invalidateCache($tenantId);
|
|
|
|
return 'success';
|
|
}
|
|
}
|