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.
228 lines
6.0 KiB
228 lines
6.0 KiB
<?php |
|
|
|
namespace App\Services; |
|
|
|
use App\Models\ServiceToken; |
|
use App\Services\AuditLogService; |
|
use Illuminate\Support\Facades\Auth; |
|
|
|
/** |
|
* ServiceTokenService - Manage microservice Bearer tokens |
|
* |
|
* Provides CRUD operations for service tokens with audit logging. |
|
* Zero impact on existing GeoNetAgent agents (separate database). |
|
*/ |
|
class ServiceTokenService |
|
{ |
|
public function __construct( |
|
private AuditLogService $auditLogService |
|
) {} |
|
|
|
/** |
|
* Create a new service token |
|
* |
|
* @param string $name Token name (e.g., "GeoNetAgent-Production") |
|
* @param string $service Service name (e.g., "GeoNetAgent", "Databaselist") |
|
* @param int|null $expiresInDays Expiration in days (null = never expires) |
|
* @return array{token: string, model: ServiceToken} |
|
*/ |
|
public function create(string $name, string $service, ?int $expiresInDays = null): array |
|
{ |
|
$createdBy = Auth::check() ? Auth::user()->username : 'system'; |
|
|
|
$result = ServiceToken::generate( |
|
name: $name, |
|
service: $service, |
|
createdBy: $createdBy, |
|
expiresInDays: $expiresInDays |
|
); |
|
|
|
// Audit log |
|
$this->auditLogService->log( |
|
action: 'service_token_created', |
|
entityType: 'ServiceToken', |
|
entityId: $result['model']->id, |
|
details: [ |
|
'name' => $name, |
|
'service' => $service, |
|
'token_prefix' => $result['model']->token_prefix, |
|
'expires_at' => $result['model']->expires_at?->toIso8601String(), |
|
], |
|
userId: $createdBy |
|
); |
|
|
|
return $result; |
|
} |
|
|
|
/** |
|
* List all service tokens (without plain tokens) |
|
* |
|
* @return array<ServiceToken> |
|
*/ |
|
public function listAll(): array |
|
{ |
|
return ServiceToken::orderByDesc('created_at')->get()->all(); |
|
} |
|
|
|
/** |
|
* Get a specific service token by ID |
|
*/ |
|
public function getById(string $id): ?ServiceToken |
|
{ |
|
return ServiceToken::find($id); |
|
} |
|
|
|
/** |
|
* Revoke (deactivate) a service token |
|
*/ |
|
public function revoke(ServiceToken $token): void |
|
{ |
|
$token->update(['is_active' => false]); |
|
|
|
$createdBy = Auth::check() ? Auth::user()->username : 'system'; |
|
|
|
$this->auditLogService->log( |
|
action: 'service_token_revoked', |
|
entityType: 'ServiceToken', |
|
entityId: $token->id, |
|
details: [ |
|
'name' => $token->name, |
|
'service' => $token->service, |
|
'token_prefix' => $token->token_prefix, |
|
], |
|
userId: $createdBy |
|
); |
|
} |
|
|
|
/** |
|
* Permanently delete a service token |
|
*/ |
|
public function delete(ServiceToken $token): void |
|
{ |
|
$tokenId = $token->id; |
|
$tokenName = $token->name; |
|
$tokenService = $token->service; |
|
$tokenPrefix = $token->token_prefix; |
|
|
|
$token->delete(); |
|
|
|
$createdBy = Auth::check() ? Auth::user()->username : 'system'; |
|
|
|
$this->auditLogService->log( |
|
action: 'service_token_deleted', |
|
entityType: 'ServiceToken', |
|
entityId: $tokenId, |
|
details: [ |
|
'name' => $tokenName, |
|
'service' => $tokenService, |
|
'token_prefix' => $tokenPrefix, |
|
], |
|
userId: $createdBy |
|
); |
|
} |
|
|
|
/** |
|
* Regenerate a service token (create new, revoke old) |
|
* |
|
* @return array{token: string, model: ServiceToken} |
|
*/ |
|
public function regenerate(ServiceToken $oldToken, ?int $expiresInDays = null): array |
|
{ |
|
// Revoke old token |
|
$this->revoke($oldToken); |
|
|
|
// Create new token with same name and service |
|
return $this->create( |
|
name: $oldToken->name, |
|
service: $oldToken->service, |
|
expiresInDays: $expiresInDays |
|
); |
|
} |
|
|
|
/** |
|
* Verify a token (for external validation, not used in UI) |
|
* |
|
* @param string $plainToken Plain token to verify |
|
* @return ServiceToken|null |
|
*/ |
|
public function verify(string $plainToken): ?ServiceToken |
|
{ |
|
$token = ServiceToken::verify($plainToken); |
|
|
|
if ($token) { |
|
$token->touchLastUsed(); |
|
} |
|
|
|
return $token; |
|
} |
|
|
|
/** |
|
* Get tokens by service |
|
* |
|
* @param string $service Service name |
|
* @return array<ServiceToken> |
|
*/ |
|
public function getByService(string $service): array |
|
{ |
|
return ServiceToken::where('service', $service) |
|
->orderByDesc('created_at') |
|
->get() |
|
->all(); |
|
} |
|
|
|
/** |
|
* Get active tokens only |
|
* |
|
* @return array<ServiceToken> |
|
*/ |
|
public function getActive(): array |
|
{ |
|
return ServiceToken::active() |
|
->orderByDesc('created_at') |
|
->get() |
|
->all(); |
|
} |
|
|
|
/** |
|
* Get expired tokens |
|
* |
|
* @return array<ServiceToken> |
|
*/ |
|
public function getExpired(): array |
|
{ |
|
return ServiceToken::expired() |
|
->orderByDesc('expires_at') |
|
->get() |
|
->all(); |
|
} |
|
|
|
/** |
|
* Clean up expired tokens (optional maintenance task) |
|
* |
|
* @param int $olderThanDays Delete tokens expired more than X days ago |
|
* @return int Number of deleted tokens |
|
*/ |
|
public function cleanupExpired(int $olderThanDays = 30): int |
|
{ |
|
$cutoffDate = now()->subDays($olderThanDays); |
|
|
|
$deleted = ServiceToken::expired() |
|
->where('expires_at', '<', $cutoffDate) |
|
->delete(); |
|
|
|
if ($deleted > 0) { |
|
$this->auditLogService->log( |
|
action: 'service_tokens_cleanup', |
|
entityType: 'ServiceToken', |
|
entityId: null, |
|
details: [ |
|
'deleted_count' => $deleted, |
|
'older_than_days' => $olderThanDays, |
|
], |
|
userId: 'system' |
|
); |
|
} |
|
|
|
return $deleted; |
|
} |
|
}
|
|
|