BastionSSO/app/Http/Controllers/Api/UserController.php

189 lines
7.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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;
}
}