You've already forked php-auth
generated from siteworxpro/Php-Template
Add scope management functionality for clients and enhance client creation process
Some checks failed
🧪✨ Tests Workflow / 🛡️ 🔒 License Check (push) Successful in 1m5s
🧪✨ Tests Workflow / 🧪 ✨ Database Migrations (push) Successful in 1m25s
🧪✨ Tests Workflow / 🛡️ 🔒 Library Audit (push) Successful in 1m24s
🧪✨ Tests Workflow / 📝 ✨ Code Lint (push) Successful in 1m14s
🧪✨ Tests Workflow / 🐙 🔍 Code Sniffer (push) Failing after 1m20s
🧪✨ Tests Workflow / 🧪 ✅ Unit Tests (push) Successful in -19s
Some checks failed
🧪✨ Tests Workflow / 🛡️ 🔒 License Check (push) Successful in 1m5s
🧪✨ Tests Workflow / 🧪 ✨ Database Migrations (push) Successful in 1m25s
🧪✨ Tests Workflow / 🛡️ 🔒 Library Audit (push) Successful in 1m24s
🧪✨ Tests Workflow / 📝 ✨ Code Lint (push) Successful in 1m14s
🧪✨ Tests Workflow / 🐙 🔍 Code Sniffer (push) Failing after 1m20s
🧪✨ Tests Workflow / 🧪 ✅ Unit Tests (push) Successful in -19s
This commit is contained in:
@@ -6,6 +6,8 @@ namespace Siteworxpro\App\Cli\Commands\OAuth;
|
|||||||
|
|
||||||
use Siteworxpro\App\Cli\ClimateOutput;
|
use Siteworxpro\App\Cli\ClimateOutput;
|
||||||
use Siteworxpro\App\Cli\Commands\Command;
|
use Siteworxpro\App\Cli\Commands\Command;
|
||||||
|
use Siteworxpro\App\OAuth\Entities\Client;
|
||||||
|
use Siteworxpro\App\OAuth\Entities\Scope;
|
||||||
use Symfony\Component\Console\Attribute\AsCommand;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Question\Question;
|
use Symfony\Component\Console\Question\Question;
|
||||||
@@ -30,6 +32,8 @@ class ClientCapabilities extends Command
|
|||||||
$output->info('[1] Username Password: ' . ($capabilities['userPass'] ? 'Enabled' : 'Disabled'));
|
$output->info('[1] Username Password: ' . ($capabilities['userPass'] ? 'Enabled' : 'Disabled'));
|
||||||
$output->info('[2] Magic Link: ' . ($capabilities['magicLink'] ? 'Enabled' : 'Disabled'));
|
$output->info('[2] Magic Link: ' . ($capabilities['magicLink'] ? 'Enabled' : 'Disabled'));
|
||||||
$output->info('[3] Social Logins: ' . ($capabilities['socials'] ? 'Enabled' : 'Disabled'));
|
$output->info('[3] Social Logins: ' . ($capabilities['socials'] ? 'Enabled' : 'Disabled'));
|
||||||
|
$output->info('[4] External Client (require pkce): ' . (!$client->confidential ? 'Enabled' : 'Disabled'));
|
||||||
|
$output->info('[5] Manage Scopes');
|
||||||
|
|
||||||
$question = new Question('What do you want to edit: ', '');
|
$question = new Question('What do you want to edit: ', '');
|
||||||
$selection = $this->helper->ask($input, $output, $question);
|
$selection = $this->helper->ask($input, $output, $question);
|
||||||
@@ -48,6 +52,12 @@ class ClientCapabilities extends Command
|
|||||||
case '3':
|
case '3':
|
||||||
$output->info('Social Logins cannot be modified via CLI at this time.');
|
$output->info('Social Logins cannot be modified via CLI at this time.');
|
||||||
break;
|
break;
|
||||||
|
case '4':
|
||||||
|
$client->confidential = !$client->confidential;
|
||||||
|
break;
|
||||||
|
case '5':
|
||||||
|
$this->manageClientScopes($input, $output, $client);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
$output->error('Invalid selection. Please try again.');
|
$output->error('Invalid selection. Please try again.');
|
||||||
continue 2;
|
continue 2;
|
||||||
@@ -60,4 +70,48 @@ class ClientCapabilities extends Command
|
|||||||
|
|
||||||
return \Symfony\Component\Console\Command\Command::SUCCESS;
|
return \Symfony\Component\Console\Command\Command::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function manageClientScopes($input, ClimateOutput|OutputInterface $output, Client $client): void
|
||||||
|
{
|
||||||
|
$allScopes = Scope::all();
|
||||||
|
|
||||||
|
$output->info('These are scope that are available for this client.');
|
||||||
|
$output->info('Type "exit" to finish managing scopes.');
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
$clientScopes = $client->scopes()->get();
|
||||||
|
|
||||||
|
$output->info('Available Scopes:');
|
||||||
|
|
||||||
|
/** @var Scope $scope */
|
||||||
|
foreach ($allScopes as $scope) {
|
||||||
|
$status = $clientScopes->contains($scope) ? 'Enabled' : 'Disabled';
|
||||||
|
$output->info("$scope->id - $scope->name $status");
|
||||||
|
}
|
||||||
|
|
||||||
|
$question = new Question('Enter scope ID to toggle (or type "exit" to finish): ', '');
|
||||||
|
$question->setAutocompleterValues($allScopes->pluck('id')->toArray());
|
||||||
|
|
||||||
|
$scopeId = $this->helper->ask($input, $output, $question);
|
||||||
|
|
||||||
|
if (strtolower($scopeId) === 'exit') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope = $allScopes->where('id', $scopeId)->first();
|
||||||
|
|
||||||
|
if ($scope === null) {
|
||||||
|
$output->error('Scope not found. Please try again.');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($clientScopes->contains($scope)) {
|
||||||
|
$client->disableScope($scope);
|
||||||
|
$output->info("Scope '$scope->name' disabled for client.");
|
||||||
|
} else {
|
||||||
|
$client->enableScope($scope);
|
||||||
|
$output->info("Scope '$scope->name' enabled for client.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Siteworxpro\App\Cli\Commands\OAuth;
|
namespace Siteworxpro\App\Cli\Commands\OAuth;
|
||||||
|
|
||||||
use Siteworxpro\App\Cli\ClimateOutput;
|
|
||||||
use Siteworxpro\App\CommandBus\Commands\CreateClient as CreateClientCommand;
|
use Siteworxpro\App\CommandBus\Commands\CreateClient as CreateClientCommand;
|
||||||
use Siteworxpro\App\CommandBus\Exceptions\CommandHandlerException;
|
use Siteworxpro\App\CommandBus\Exceptions\CommandHandlerException;
|
||||||
use Siteworxpro\App\Models\Enums\ClientGrant as ClientGrantAlias;
|
use Siteworxpro\App\Models\Enums\ClientGrant as ClientGrantAlias;
|
||||||
@@ -12,9 +11,6 @@ use Siteworxpro\App\OAuth\Entities\Client;
|
|||||||
use Siteworxpro\App\Services\Facades\CommandBus;
|
use Siteworxpro\App\Services\Facades\CommandBus;
|
||||||
use Symfony\Component\Console\Attribute\AsCommand;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Input\ArgvInput;
|
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
|
||||||
use Symfony\Component\Console\Question\ChoiceQuestion;
|
use Symfony\Component\Console\Question\ChoiceQuestion;
|
||||||
use Symfony\Component\Console\Question\Question;
|
use Symfony\Component\Console\Question\Question;
|
||||||
|
|
||||||
@@ -31,8 +27,8 @@ class CreateClient extends \Siteworxpro\App\Cli\Commands\Command
|
|||||||
|
|
||||||
$question = new ChoiceQuestion('Enter client grants', [
|
$question = new ChoiceQuestion('Enter client grants', [
|
||||||
'authorization_code',
|
'authorization_code',
|
||||||
'client_credentials',
|
|
||||||
'refresh_token',
|
'refresh_token',
|
||||||
|
'client_credentials',
|
||||||
'password',
|
'password',
|
||||||
], 0);
|
], 0);
|
||||||
$question->setMultiselect(true);
|
$question->setMultiselect(true);
|
||||||
@@ -44,6 +40,18 @@ class CreateClient extends \Siteworxpro\App\Cli\Commands\Command
|
|||||||
$grantsEnum[] = ClientGrantAlias::from($grant);
|
$grantsEnum[] = ClientGrantAlias::from($grant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$question = $this->helper->ask(
|
||||||
|
$input,
|
||||||
|
$output,
|
||||||
|
new \Symfony\Component\Console\Question\ConfirmationQuestion(
|
||||||
|
'External Client (Require PKCE)? (y/N): ',
|
||||||
|
false,
|
||||||
|
'/^(y|yes)/i'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$command = new CreateClientCommand($clientName, $grantsEnum, $clientDescription);
|
$command = new CreateClientCommand($clientName, $grantsEnum, $clientDescription);
|
||||||
try {
|
try {
|
||||||
/** @var Client $client */
|
/** @var Client $client */
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class ListClients extends Command
|
|||||||
'Access Token Url' => Config::get('app.url') . '/client/access_token',
|
'Access Token Url' => Config::get('app.url') . '/client/access_token',
|
||||||
'OAuth Config Url' => Config::get('app.url') .
|
'OAuth Config Url' => Config::get('app.url') .
|
||||||
'/client/' . $client->id . '/.well-known/openid-configuration',
|
'/client/' . $client->id . '/.well-known/openid-configuration',
|
||||||
'Scopes' => $client->scopes->toArray(),
|
'Scopes' => $client->scopes->pluck('name')->toArray(),
|
||||||
'Capabilities' => $client->capabilities->toArray(),
|
'Capabilities' => $client->capabilities->toArray(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ readonly class CreateClient extends Command
|
|||||||
public function __construct(
|
public function __construct(
|
||||||
private string $clientName,
|
private string $clientName,
|
||||||
private array $clientGrants = [],
|
private array $clientGrants = [],
|
||||||
private string $clientDescription = ''
|
private string $clientDescription = '',
|
||||||
|
private bool $isExternal = false
|
||||||
) {
|
) {
|
||||||
foreach ($this->clientGrants as $grant) {
|
foreach ($this->clientGrants as $grant) {
|
||||||
if ($grant instanceof ClientGrant === false) {
|
if ($grant instanceof ClientGrant === false) {
|
||||||
@@ -32,6 +33,14 @@ readonly class CreateClient extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isExternal(): bool
|
||||||
|
{
|
||||||
|
return $this->isExternal;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class CreateClient extends CommandHandler
|
|||||||
$client->description = $command->getClientDescription();
|
$client->description = $command->getClientDescription();
|
||||||
$client->grant_types = new Collection($command->getClientGrants()); // @phpstan-ignore-line assign.propertyType
|
$client->grant_types = new Collection($command->getClientGrants()); // @phpstan-ignore-line assign.propertyType
|
||||||
$client->capabilities = new ClientCapabilities();
|
$client->capabilities = new ClientCapabilities();
|
||||||
|
$client->confidential = !$command->isExternal();
|
||||||
|
|
||||||
$client->save();
|
$client->save();
|
||||||
|
|
||||||
|
|||||||
@@ -14,4 +14,5 @@ namespace Siteworxpro\App\Models;
|
|||||||
*/
|
*/
|
||||||
class ClientScope extends Model
|
class ClientScope extends Model
|
||||||
{
|
{
|
||||||
|
public $timestamps = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -241,4 +241,23 @@ class Client extends Model implements ClientEntityInterface
|
|||||||
|
|
||||||
return $user->verifyPassword($password) ? $user : null;
|
return $user->verifyPassword($password) ? $user : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function disableScope(Scope $scope): void
|
||||||
|
{
|
||||||
|
/** @var ClientScope | null $clientScope */
|
||||||
|
$clientScope = ClientScope::where('client_id', $this->id)
|
||||||
|
->where('scope_id', $scope->id)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$clientScope?->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enableScope(Scope $scope): void
|
||||||
|
{
|
||||||
|
$clientScope = new ClientScope();
|
||||||
|
$clientScope->client_id = $this->id;
|
||||||
|
$clientScope->scope_id = $scope->id;
|
||||||
|
|
||||||
|
$clientScope->save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ class Scope extends Model implements ScopeEntityInterface
|
|||||||
{
|
{
|
||||||
use ScopeTrait;
|
use ScopeTrait;
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'id' => 'string',
|
||||||
|
];
|
||||||
|
|
||||||
public function getIdentifier(): string
|
public function getIdentifier(): string
|
||||||
{
|
{
|
||||||
return $this->name;
|
return $this->name;
|
||||||
|
|||||||
Reference in New Issue
Block a user