You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
6.2 KiB
181 lines
6.2 KiB
<?php |
|
|
|
namespace App\Http\Controllers; |
|
|
|
use App\Models\ApiToken; |
|
use App\Models\ServiceToken; |
|
use App\Services\ApiTokenService; |
|
use App\Services\ServiceTokenService; |
|
use Illuminate\Http\RedirectResponse; |
|
use Illuminate\Http\Request; |
|
use Illuminate\View\View; |
|
use Laravel\Passport\Client; |
|
use Laravel\Passport\ClientRepository; |
|
|
|
class ApiClientController extends Controller |
|
{ |
|
public function __construct( |
|
private readonly ApiTokenService $apiTokens, |
|
private readonly ServiceTokenService $serviceTokens, |
|
private readonly ClientRepository $clientRepository |
|
) { |
|
} |
|
|
|
public function index(): View |
|
{ |
|
return view('api-clients.index', [ |
|
'apiTokens' => $this->apiTokens->listAll(), |
|
'oauthClients' => Client::orderByDesc('created_at')->get(), |
|
'serviceTokens' => $this->serviceTokens->listAll(), |
|
'scopes' => config('api.scopes'), |
|
'newToken' => session('new_api_token'), |
|
'newClient' => session('new_oauth_client'), |
|
'newServiceToken' => session('new_service_token'), |
|
]); |
|
} |
|
|
|
public function storeToken(Request $request): RedirectResponse |
|
{ |
|
$data = $request->validate([ |
|
'name' => ['required', 'string', 'max:100'], |
|
'scopes' => ['required', 'array', 'min:1'], |
|
'scopes.*' => ['string', 'in:' . implode(',', array_keys(config('api.scopes')))], |
|
'expires_in_days' => ['nullable', 'integer', 'min:1', 'max:3650'], |
|
]); |
|
|
|
$result = $this->apiTokens->create( |
|
$data['name'], |
|
$data['scopes'], |
|
$data['expires_in_days'] ?? null |
|
); |
|
|
|
$request->attributes->set('audit_metadata', [ |
|
'name' => $data['name'], |
|
'scopes' => $data['scopes'], |
|
]); |
|
|
|
return redirect() |
|
->route('api-clients.index') |
|
->with('success', 'API token created. Copy it now — it will not be shown again.') |
|
->with('new_api_token', [ |
|
'name' => $result['token']->name, |
|
'token' => $result['plain_text'], |
|
]); |
|
} |
|
|
|
public function destroyToken(Request $request, ApiToken $apiToken): RedirectResponse |
|
{ |
|
$request->attributes->set('audit_metadata', ['name' => $apiToken->name]); |
|
$this->apiTokens->revoke($apiToken); |
|
|
|
return redirect()->route('api-clients.index')->with('success', 'API token revoked.'); |
|
} |
|
|
|
public function storeOAuthClient(Request $request): RedirectResponse |
|
{ |
|
$data = $request->validate([ |
|
'name' => ['required', 'string', 'max:100'], |
|
]); |
|
|
|
$client = $this->clientRepository->createClientCredentialsGrantClient($data['name']); |
|
|
|
$request->attributes->set('audit_metadata', ['name' => $data['name']]); |
|
|
|
return redirect() |
|
->route('api-clients.index') |
|
->with('success', 'OAuth2 client created. Copy credentials now.') |
|
->with('new_oauth_client', [ |
|
'name' => $client->name, |
|
'client_id' => $client->id, |
|
'client_secret' => $client->plainSecret, |
|
]); |
|
} |
|
|
|
public function destroyOAuthClient(Request $request, string $clientId): RedirectResponse |
|
{ |
|
$client = Client::findOrFail($clientId); |
|
$request->attributes->set('audit_metadata', ['name' => $client->name]); |
|
$client->update(['revoked' => true]); |
|
|
|
return redirect()->route('api-clients.index')->with('success', 'OAuth2 client revoked.'); |
|
} |
|
|
|
// Service Tokens (microservice Bearer token management) |
|
|
|
public function storeServiceToken(Request $request): RedirectResponse |
|
{ |
|
$data = $request->validate([ |
|
'name' => ['required', 'string', 'max:100'], |
|
'service' => ['required', 'string', 'max:50'], |
|
'expires_in_days' => ['nullable', 'integer', 'min:1', 'max:3650'], |
|
]); |
|
|
|
$result = $this->serviceTokens->create( |
|
name: $data['name'], |
|
service: $data['service'], |
|
expiresInDays: $data['expires_in_days'] ?? null |
|
); |
|
|
|
return redirect() |
|
->route('api-clients.index') |
|
->with('success', 'Service token created. Copy it now — it will not be shown again.') |
|
->with('new_service_token', [ |
|
'name' => $result['model']->name, |
|
'service' => $result['model']->service, |
|
'token' => $result['token'], |
|
]); |
|
} |
|
|
|
public function revokeServiceToken(Request $request, string $id): RedirectResponse |
|
{ |
|
$token = $this->serviceTokens->getById($id); |
|
|
|
if (!$token) { |
|
return redirect()->route('api-clients.index')->with('error', 'Service token not found.'); |
|
} |
|
|
|
$this->serviceTokens->revoke($token); |
|
|
|
return redirect()->route('api-clients.index')->with('success', 'Service token revoked.'); |
|
} |
|
|
|
public function regenerateServiceToken(Request $request, string $id): RedirectResponse |
|
{ |
|
$data = $request->validate([ |
|
'expires_in_days' => ['nullable', 'integer', 'min:1', 'max:3650'], |
|
]); |
|
|
|
$token = $this->serviceTokens->getById($id); |
|
|
|
if (!$token) { |
|
return redirect()->route('api-clients.index')->with('error', 'Service token not found.'); |
|
} |
|
|
|
$result = $this->serviceTokens->regenerate( |
|
oldToken: $token, |
|
expiresInDays: $data['expires_in_days'] ?? null |
|
); |
|
|
|
return redirect() |
|
->route('api-clients.index') |
|
->with('success', 'Service token regenerated. Copy the new token now.') |
|
->with('new_service_token', [ |
|
'name' => $result['model']->name, |
|
'service' => $result['model']->service, |
|
'token' => $result['token'], |
|
]); |
|
} |
|
|
|
public function destroyServiceToken(Request $request, string $id): RedirectResponse |
|
{ |
|
$token = $this->serviceTokens->getById($id); |
|
|
|
if (!$token) { |
|
return redirect()->route('api-clients.index')->with('error', 'Service token not found.'); |
|
} |
|
|
|
$this->serviceTokens->delete($token); |
|
|
|
return redirect()->route('api-clients.index')->with('success', 'Service token deleted.'); |
|
} |
|
}
|
|
|