189 lines
7.2 KiB
PHP
189 lines
7.2 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Api;
|
||
|
||
use App\Http\Controllers\Controller;
|
||
use App\Http\Requests\StoreUserRequest;
|
||
use App\Http\Requests\UpdateUserRequest;
|
||
use App\Models\ServerResource;
|
||
use App\Models\User;
|
||
use App\Models\UserServerPermission;
|
||
use hg\apidoc\annotation as Apidoc;
|
||
use Illuminate\Http\JsonResponse;
|
||
use Illuminate\Http\Request;
|
||
use Spatie\Permission\Models\Permission;
|
||
|
||
#[Apidoc\Title('用户管理')]
|
||
class UserController extends Controller
|
||
{
|
||
public function __construct()
|
||
{
|
||
$this->middleware('auth:api');
|
||
$this->middleware('permission:platform.users.view,api')->only(['index', 'show']);
|
||
$this->middleware('permission:platform.users.manage,api')->only(['store', 'update', 'destroy', 'syncPermissions']);
|
||
}
|
||
|
||
#[Apidoc\Title('用户列表'), Apidoc\Method('GET'), Apidoc\Url('/users')]
|
||
public function index(Request $request): JsonResponse
|
||
{
|
||
$validated = $request->validate([
|
||
'per_page' => ['nullable', 'integer', 'min:1', 'max:100'],
|
||
'sort_by' => ['nullable', 'string', 'in:id,nickname,email,phone,created_at'],
|
||
'sort_order' => ['nullable', 'string', 'in:asc,desc'],
|
||
]);
|
||
|
||
$sortBy = $validated['sort_by'] ?? 'created_at';
|
||
$sortOrder = $validated['sort_order'] ?? 'desc';
|
||
$perPage = (int) ($validated['per_page'] ?? 20);
|
||
|
||
$users = User::query()
|
||
->with('roles')
|
||
->orderBy($sortBy, $sortOrder)
|
||
->paginate($perPage);
|
||
|
||
return response()->json(['code' => 0, 'message' => 'ok', 'data' => $users]);
|
||
}
|
||
|
||
#[Apidoc\Title('创建用户'), Apidoc\Method('POST'), Apidoc\Url('/users')]
|
||
public function store(StoreUserRequest $request): JsonResponse
|
||
{
|
||
$user = User::query()->create($request->validated());
|
||
|
||
if ($request->filled('role_ids')) {
|
||
$user->syncRoles($request->validated('role_ids'));
|
||
}
|
||
|
||
$this->auditLog($request, 'user_create', ['metadata' => ['target_user_id' => $user->id]]);
|
||
|
||
return response()->json(['code' => 0, 'message' => 'ok', 'data' => $user->load('roles')], 201);
|
||
}
|
||
|
||
#[Apidoc\Title('用户详情'), Apidoc\Method('GET'), Apidoc\Url('/users/{id}')]
|
||
public function show(int $id): JsonResponse
|
||
{
|
||
$user = User::query()->with(['roles', 'permissions', 'serverResources'])->findOrFail($id);
|
||
|
||
return response()->json(['code' => 0, 'message' => 'ok', 'data' => $user]);
|
||
}
|
||
|
||
#[Apidoc\Title('更新用户'), Apidoc\Method('PUT'), Apidoc\Url('/users/{id}')]
|
||
public function update(UpdateUserRequest $request, int $id): JsonResponse
|
||
{
|
||
$user = User::query()->findOrFail($id);
|
||
$user->fill($request->safe()->except(['role_ids']));
|
||
|
||
if ($request->filled('password')) {
|
||
$user->password = $request->validated('password');
|
||
}
|
||
|
||
$user->save();
|
||
|
||
if ($request->has('role_ids')) {
|
||
$user->syncRoles($request->validated('role_ids'));
|
||
}
|
||
|
||
$this->auditLog($request, 'user_update', ['metadata' => ['target_user_id' => $user->id]]);
|
||
|
||
return response()->json(['code' => 0, 'message' => 'ok', 'data' => $user->load('roles')]);
|
||
}
|
||
|
||
#[Apidoc\Title('同步用户权限'), Apidoc\Method('PUT'), Apidoc\Url('/users/{id}/permissions')]
|
||
public function syncPermissions(Request $request, int $id): JsonResponse
|
||
{
|
||
$validated = $request->validate([
|
||
'permission_ids' => ['present', 'array'],
|
||
'permission_ids.*' => ['integer', 'exists:permissions,id'],
|
||
]);
|
||
|
||
$user = User::query()->findOrFail($id);
|
||
$user->syncPermissions($validated['permission_ids']);
|
||
$this->syncServerResourcePermissionsByDirectPermissions($user, $validated['permission_ids']);
|
||
|
||
$this->auditLog($request, 'user_permissions_update', ['metadata' => ['target_user_id' => $user->id, 'permission_ids' => $validated['permission_ids']]]);
|
||
|
||
return response()->json(['code' => 0, 'message' => 'ok', 'data' => $user->load(['roles', 'permissions'])]);
|
||
}
|
||
|
||
#[Apidoc\Title('删除用户'), Apidoc\Method('DELETE'), Apidoc\Url('/users/{id}')]
|
||
public function destroy(Request $request, int $id): JsonResponse
|
||
{
|
||
$user = User::query()->findOrFail($id);
|
||
$this->auditLog($request, 'user_delete', ['metadata' => ['target_user_id' => $user->id]]);
|
||
$user->delete();
|
||
|
||
return response()->json(['code' => 0, 'message' => 'ok', 'data' => null]);
|
||
}
|
||
|
||
private function syncServerResourcePermissionsByDirectPermissions(User $user, array $permissionIds): void
|
||
{
|
||
$resourcePermissions = Permission::query()
|
||
->select(['id', 'name', 'description'])
|
||
->whereIn('id', $permissionIds)
|
||
->where('guard_name', 'api')
|
||
->where('name', 'like', 'resource.servers.use.%')
|
||
->get();
|
||
|
||
$grantedResourceIds = $resourcePermissions
|
||
->map(fn (Permission $permission): ?int => $this->resourceIdFromPermissionDescription((string) $permission->description))
|
||
->filter(fn (?int $resourceId): bool => $resourceId !== null)
|
||
->map(fn (int $resourceId): int => (int) $resourceId)
|
||
->values()
|
||
->all();
|
||
|
||
if (count($grantedResourceIds) > 0) {
|
||
$existingResourceIds = ServerResource::query()
|
||
->whereIn('id', $grantedResourceIds)
|
||
->whereNotNull('parent_id')
|
||
->pluck('id')
|
||
->map(fn (int $resourceId): int => (int) $resourceId)
|
||
->all();
|
||
|
||
foreach ($existingResourceIds as $resourceId) {
|
||
UserServerPermission::query()->updateOrCreate(
|
||
[
|
||
'user_id' => $user->id,
|
||
'server_resource_id' => $resourceId,
|
||
],
|
||
[
|
||
'can_ssh' => true,
|
||
'can_sftp' => true,
|
||
'can_rdp' => true,
|
||
]
|
||
);
|
||
}
|
||
}
|
||
|
||
$managedResourceIds = Permission::query()
|
||
->where('guard_name', 'api')
|
||
->where('name', 'like', 'resource.servers.use.%')
|
||
->where('description', 'like', '服务器资源访问权限(资源ID:%')
|
||
->pluck('description')
|
||
->map(fn (string $description): ?int => $this->resourceIdFromPermissionDescription($description))
|
||
->filter(fn (?int $resourceId): bool => $resourceId !== null)
|
||
->map(fn (int $resourceId): int => $resourceId)
|
||
->values()
|
||
->all();
|
||
|
||
if (count($managedResourceIds) > 0) {
|
||
UserServerPermission::query()
|
||
->where('user_id', $user->id)
|
||
->whereIn('server_resource_id', $managedResourceIds)
|
||
->whereNotIn('server_resource_id', $grantedResourceIds)
|
||
->update([
|
||
'can_ssh' => false,
|
||
'can_sftp' => false,
|
||
'can_rdp' => false,
|
||
]);
|
||
}
|
||
}
|
||
|
||
private function resourceIdFromPermissionDescription(string $description): ?int
|
||
{
|
||
if (! preg_match('/资源ID:\s*(\d+)/u', $description, $matches)) {
|
||
return null;
|
||
}
|
||
|
||
return isset($matches[1]) ? (int) $matches[1] : null;
|
||
}
|
||
}
|