latest
This commit is contained in:
1 parent
d0834df8df
commit
6dc35134fa
171 files changed
+19241
-49
No files matched your search
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Actions\Auth\LoginAction;
|
||||
use App\Actions\Auth\VerifyTwoFactorAction;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Auth\LoginRequest;
|
||||
use App\Http\Requests\Auth\TwoFactorChallengeRequest;
|
||||
use App\Http\Requests\Auth\TwoFactorConfirmRequest;
|
||||
use App\Http\Requests\Auth\TwoFactorDisableRequest;
|
||||
use App\Http\Resources\UserResource;
|
||||
use App\Services\Audit\AuditLogger;
|
||||
use App\Services\TwoFactorService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
public function login(LoginRequest $request, LoginAction $action, AuditLogger $audit)
|
||||
{
|
||||
$result = $action->execute($request, $request->string('login')->toString(), $request->string('password')->toString());
|
||||
|
||||
if ($result['status'] === 'requires_2fa') {
|
||||
return response()->json(['requires_2fa' => true, 'challenge_token' => $result['challenge_token']]);
|
||||
}
|
||||
|
||||
$audit->log($result['user'], 'auth.login');
|
||||
|
||||
return UserResource::make($result['user']);
|
||||
}
|
||||
|
||||
public function verifyTwoFactor(TwoFactorChallengeRequest $request, VerifyTwoFactorAction $action, AuditLogger $audit)
|
||||
{
|
||||
$user = $action->execute($request, $request->string('challenge_token')->toString(), $request->string('code')->toString());
|
||||
|
||||
$audit->log($user, 'auth.2fa_verified');
|
||||
|
||||
return UserResource::make($user);
|
||||
}
|
||||
|
||||
public function logout(Request $request, AuditLogger $audit)
|
||||
{
|
||||
$audit->log($request->user(), 'auth.logout');
|
||||
|
||||
Auth::guard('web')->logout();
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
public function me(Request $request)
|
||||
{
|
||||
return UserResource::make($request->user());
|
||||
}
|
||||
|
||||
public function enableTwoFactor(Request $request, TwoFactorService $twoFactor)
|
||||
{
|
||||
$user = $request->user();
|
||||
$secret = $twoFactor->generateSecret();
|
||||
|
||||
// Stored but not yet "enabled" until confirmed with a valid code.
|
||||
$user->forceFill(['two_factor_secret' => $secret])->save();
|
||||
|
||||
return response()->json([
|
||||
'secret' => $secret,
|
||||
'otpauth_url' => $twoFactor->otpAuthUrl($user, $secret),
|
||||
]);
|
||||
}
|
||||
|
||||
public function confirmTwoFactor(TwoFactorConfirmRequest $request, TwoFactorService $twoFactor, AuditLogger $audit)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
if (! $user->two_factor_secret || ! $twoFactor->verifyCode($user->two_factor_secret, $request->string('code')->toString())) {
|
||||
throw ValidationException::withMessages(['code' => 'That code is invalid.']);
|
||||
}
|
||||
|
||||
$user->forceFill([
|
||||
'two_factor_enabled' => true,
|
||||
'two_factor_confirmed_at' => now(),
|
||||
])->save();
|
||||
|
||||
$recoveryCodes = $twoFactor->generateRecoveryCodes($user);
|
||||
|
||||
$audit->log($user, 'auth.2fa_enabled');
|
||||
|
||||
return response()->json(['recovery_codes' => $recoveryCodes]);
|
||||
}
|
||||
|
||||
public function disableTwoFactor(TwoFactorDisableRequest $request, AuditLogger $audit)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
if (! Hash::check($request->string('password')->toString(), $user->password)) {
|
||||
throw ValidationException::withMessages(['password' => 'That password is incorrect.']);
|
||||
}
|
||||
|
||||
$user->twoFactorRecoveryCodes()->delete();
|
||||
$user->forceFill([
|
||||
'two_factor_enabled' => false,
|
||||
'two_factor_secret' => null,
|
||||
'two_factor_confirmed_at' => null,
|
||||
])->save();
|
||||
|
||||
$audit->log($user, 'auth.2fa_disabled');
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\AuditLogResource;
|
||||
use App\Models\AuditLog;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Spatie\Permission\Models\Role;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function stats(Request $request)
|
||||
{
|
||||
abort_unless($request->user()->can('dashboard.view'), 403);
|
||||
|
||||
$usersByRole = Role::where('name', '!=', 'super-admin')
|
||||
->pluck('name')
|
||||
->mapWithKeys(fn (string $role) => [$role => User::role($role)->count()]);
|
||||
|
||||
return response()->json([
|
||||
'total_users' => User::count(),
|
||||
'users_by_role' => (object) $usersByRole->all(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function activity(Request $request)
|
||||
{
|
||||
abort_unless($request->user()->can('audit.view'), 403);
|
||||
|
||||
$logs = AuditLog::with('user')->latest()->limit(10)->get();
|
||||
|
||||
return AuditLogResource::collection($logs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class MqttController extends Controller
|
||||
{
|
||||
public function token(Request $request)
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
$roleKey = match (true) {
|
||||
$user->hasAnyRole(['super-admin', 'admin']) => 'admin',
|
||||
$user->hasRole('manager') => 'manager',
|
||||
default => 'user',
|
||||
};
|
||||
|
||||
$credentials = config("mqtt.role_credentials.{$roleKey}");
|
||||
|
||||
return response()->json([
|
||||
'ws_url' => config('mqtt.ws_url'),
|
||||
'username' => $credentials['username'],
|
||||
'password' => $credentials['password'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\RoleResource;
|
||||
use Illuminate\Http\Request;
|
||||
use Spatie\Permission\Models\Role;
|
||||
|
||||
class RoleController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
abort_unless($request->user()->can('roles.view'), 403);
|
||||
|
||||
// Super Admin is intentionally excluded — it's granted only via
|
||||
// seeding/tinker, never assignable through the Users module.
|
||||
return RoleResource::collection(Role::where('name', '!=', 'super-admin')->orderBy('name')->get());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Actions\Users\AssignRoleAction;
|
||||
use App\Actions\Users\CreateUserAction;
|
||||
use App\Actions\Users\DeleteUserAction;
|
||||
use App\Actions\Users\UpdateUserAction;
|
||||
use App\Events\RoleAssigned;
|
||||
use App\Events\UserCreated;
|
||||
use App\Events\UserDeleted;
|
||||
use App\Events\UserUpdated;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Users\AssignRoleRequest;
|
||||
use App\Http\Requests\Users\StoreUserRequest;
|
||||
use App\Http\Requests\Users\UpdateUserRequest;
|
||||
use App\Http\Resources\UserResource;
|
||||
use App\Models\User;
|
||||
use App\Services\Audit\AuditLogger;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('viewAny', User::class);
|
||||
|
||||
$query = User::query()->with('roles');
|
||||
|
||||
if ($search = $request->string('search')->toString()) {
|
||||
$query->where(function ($q) use ($search) {
|
||||
$q->where('name', 'like', "%{$search}%")
|
||||
->orWhere('username', 'like', "%{$search}%")
|
||||
->orWhere('email', 'like', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
$sort = in_array($request->query('sort'), ['name', 'username', 'email', 'created_at'], true)
|
||||
? $request->query('sort')
|
||||
: 'created_at';
|
||||
$direction = $request->query('direction') === 'asc' ? 'asc' : 'desc';
|
||||
|
||||
$users = $query->orderBy($sort, $direction)->paginate($request->integer('per_page', 15));
|
||||
|
||||
return UserResource::collection($users);
|
||||
}
|
||||
|
||||
public function store(StoreUserRequest $request, CreateUserAction $action, AuditLogger $audit)
|
||||
{
|
||||
$user = $action->execute($request->validated(), $request->user());
|
||||
|
||||
event(new UserCreated($user, $request->user()));
|
||||
$audit->log($request->user(), 'user.created', $user);
|
||||
|
||||
return UserResource::make($user)->response()->setStatusCode(201);
|
||||
}
|
||||
|
||||
public function show(Request $request, User $user)
|
||||
{
|
||||
$this->authorize('view', $user);
|
||||
|
||||
return UserResource::make($user->load('roles'));
|
||||
}
|
||||
|
||||
public function update(UpdateUserRequest $request, User $user, UpdateUserAction $action, AuditLogger $audit)
|
||||
{
|
||||
$user = $action->execute($user, $request->validated());
|
||||
|
||||
event(new UserUpdated($user, $request->user()));
|
||||
$audit->log($request->user(), 'user.updated', $user);
|
||||
|
||||
return UserResource::make($user);
|
||||
}
|
||||
|
||||
public function destroy(Request $request, User $user, DeleteUserAction $action, AuditLogger $audit)
|
||||
{
|
||||
$this->authorize('delete', $user);
|
||||
|
||||
$action->execute($user);
|
||||
|
||||
event(new UserDeleted($user, $request->user()));
|
||||
$audit->log($request->user(), 'user.deleted', $user);
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
public function assignRole(AssignRoleRequest $request, User $user, AssignRoleAction $action, AuditLogger $audit)
|
||||
{
|
||||
$role = $request->string('role')->toString();
|
||||
$user = $action->execute($user, $role);
|
||||
|
||||
event(new RoleAssigned($user, $request->user(), $role));
|
||||
$audit->log($request->user(), 'user.role_assigned', $user, ['role' => $role]);
|
||||
|
||||
return UserResource::make($user);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
}
|
||||
Reference in new issue
Block a user