Feat: Add modular application types, connection protocols, and dynamic app creation modal
- Introduced `ApplicationTypeEnum` and updated `ConnectionProtocolEnum` with labeled options, hints, and implementations for enhanced flexibility. - Modularized the `apps` routes into a separate file for improved structure and maintainability. - Added `CreateAppSelectionForm` Livewire component to handle app creation flows with dynamic protocol and type selection. - Enhanced `connected-apps` UI with a modal-based app creation mechanism.
This commit is contained in:
parent
0921535ae2
commit
a04bb5d7d1
2
.ddev/php/xdebug.ini
Normal file
2
.ddev/php/xdebug.ini
Normal file
@ -0,0 +1,2 @@
|
||||
[xdebug]
|
||||
xdebug.client_port = 9009
|
||||
41
app/Enums/ApplicationTypeEnum.php
Normal file
41
app/Enums/ApplicationTypeEnum.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Contracts\EnumMorphsToOptionsContract;
|
||||
|
||||
enum ApplicationTypeEnum: string implements EnumMorphsToOptionsContract
|
||||
{
|
||||
case Web = 'web';
|
||||
case SPA = 'spa';
|
||||
case Machine = 'machine';
|
||||
|
||||
public static function asOptions(): array
|
||||
{
|
||||
return array_map(fn (self $enum) => [
|
||||
'name' => $enum->toLabel(),
|
||||
'value' => $enum->value,
|
||||
'hint' => $enum->toHint(),
|
||||
], self::cases());
|
||||
}
|
||||
|
||||
public function toLabel(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Web => 'Web Applications',
|
||||
self::SPA => 'Single Page Applications',
|
||||
self::Machine => 'Microservices',
|
||||
};
|
||||
}
|
||||
|
||||
public function toHint(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::Web => 'Server-side rendered web application, where authentication is handled by the server.',
|
||||
self::SPA => 'Single Page Application where client side framework e.g. React, Vue, Angular is used, and gets the authentication token from the server.',
|
||||
self::Machine => 'Application that runs on a machine, e.g. a serverless function, or a container.',
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -5,13 +5,40 @@
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Concerns\EnumValuesAsArray;
|
||||
use App\Contracts\EnumMorphsToOptionsContract;
|
||||
|
||||
enum ConnectionProtocolEnum: string
|
||||
enum ConnectionProtocolEnum: string implements EnumMorphsToOptionsContract
|
||||
{
|
||||
use EnumValuesAsArray;
|
||||
|
||||
case OIDC = 'oidc';
|
||||
case SAML = 'saml';
|
||||
case OAUTH2 = 'oauth2';
|
||||
case URL = 'url';
|
||||
|
||||
public static function asOptions(): array
|
||||
{
|
||||
return array_map(fn (self $enum) => [
|
||||
'name' => $enum->toLabel(),
|
||||
'value' => mb_strtolower($enum->name),
|
||||
'hint' => $enum->toHint(),
|
||||
], self::cases());
|
||||
}
|
||||
|
||||
public function toLabel(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::OIDC => 'OIDC - OpenID Connect',
|
||||
self::SAML => 'SAML 2.0',
|
||||
self::URL => 'Redirect URL',
|
||||
};
|
||||
}
|
||||
|
||||
public function toHint(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::OIDC => 'Modern authentication protocol built on OAuth 2.0 for secure user sign-in across web, mobile, and API-based applications.',
|
||||
self::SAML => 'XML-based Single Sign-On (SSO) protocol commonly used by enterprise applications and identity providers',
|
||||
self::URL => 'Simple URL that will redirect the user to the authentication page.',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
30
app/Livewire/Forms/CreateAppSelectionForm.php
Normal file
30
app/Livewire/Forms/CreateAppSelectionForm.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Livewire\Forms;
|
||||
|
||||
use App\Enums\{ApplicationTypeEnum, ConnectionProtocolEnum};
|
||||
use Illuminate\Validation\Rule;
|
||||
use Livewire\Form;
|
||||
|
||||
class CreateAppSelectionForm extends Form
|
||||
{
|
||||
public string $connectionProtocol = ConnectionProtocolEnum::OIDC->value;
|
||||
|
||||
public string $applicationType = ApplicationTypeEnum::Web->value;
|
||||
|
||||
protected function rules(): array
|
||||
{
|
||||
return [
|
||||
'connectionProtocol' => [
|
||||
'required',
|
||||
Rule::enum(ConnectionProtocolEnum::class),
|
||||
],
|
||||
'applicationType' => [
|
||||
'required',
|
||||
Rule::enum(ApplicationTypeEnum::class),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,9 @@
|
||||
use App\Concerns\HandlesOperations;
|
||||
use App\Data\Ui\ConnectedApps\ConnectedAppData;
|
||||
use App\Data\Ui\TableHeader;
|
||||
use App\Enums\ApplicationTypeEnum;
|
||||
use App\Enums\ConnectionProtocolEnum;
|
||||
use App\Livewire\Forms\CreateAppSelectionForm;
|
||||
use App\Services\ConnectedAppService;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Attributes\Title;
|
||||
@ -19,6 +22,8 @@ class extends Component {
|
||||
use WithPagination, HandlesOperations, Confirmation;
|
||||
|
||||
protected ConnectedAppService $service;
|
||||
public bool $showAppCreationModal = false;
|
||||
public CreateAppSelectionForm $appSelectionForm;
|
||||
|
||||
#[DataCollectionOf(TableHeader::class)]
|
||||
public DataCollection $headers;
|
||||
@ -64,13 +69,22 @@ public function delete(int $appId): void
|
||||
successMessage: 'The app has been deleted !'
|
||||
);
|
||||
}
|
||||
|
||||
public function createApp(): void
|
||||
{
|
||||
$this->appSelectionForm->validate();
|
||||
$this->redirectRoute('apps.create', [
|
||||
'protocol' => $this->appSelectionForm->connectionProtocol,
|
||||
'type' => $this->appSelectionForm->applicationType
|
||||
], navigate: true);
|
||||
}
|
||||
};
|
||||
?>
|
||||
|
||||
<div>
|
||||
<x-shared.card title="Connected Apps" class="pb-2">
|
||||
<x-slot:actions>
|
||||
<x-mary-button :link="route('apps.create')" wire:navigate icon="lucide.plus">Add App
|
||||
<x-mary-button @click="$wire.showAppCreationModal = true" icon="lucide.plus">Add App
|
||||
</x-mary-button>
|
||||
</x-slot:actions>
|
||||
<x-mary-table
|
||||
@ -85,6 +99,7 @@ public function delete(int $appId): void
|
||||
@scope('cell_status', $row)
|
||||
<x-shared.connection-status :status="$row->status"/>
|
||||
@endscope
|
||||
|
||||
@scope('actions', $row)
|
||||
<div class="flex">
|
||||
<x-mary-button icon="lucide.edit" wire:click="edit({{$row->id}})" spinner="edit({{$row->id}})"
|
||||
@ -97,4 +112,44 @@ class="btn-sm btn-circle btn-error btn-soft"/>
|
||||
@endscope
|
||||
</x-mary-table>
|
||||
</x-shared.card>
|
||||
|
||||
<x-mary-modal title='Create a new app' wire:model="showAppCreationModal" box-class="w-11/12 max-w-4xl">
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-6 mb-6">
|
||||
<article class="md:w-1/3 shrink-0">
|
||||
<h2 class="font-bold">Protocol</h2>
|
||||
</article>
|
||||
<div class="flex-1">
|
||||
<x-mary-radio
|
||||
wire:model.change.live="appSelectionForm.connectionProtocol"
|
||||
:options="ConnectionProtocolEnum::asOptions()"
|
||||
option-value="value"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-6 mt-6 border-t border-gray-200 pt-6"
|
||||
x-show="$wire.appSelectionForm.connectionProtocol === '{{ ConnectionProtocolEnum::OIDC->value }}'"
|
||||
x-cloak>
|
||||
|
||||
<article class="md:w-1/3 shrink-0">
|
||||
<h2 class="font-bold">Application Type</h2>
|
||||
<p class="text-sm mt-1 text-gray-500">
|
||||
Choose what type of application you are trying to connect. This helps to properly generate
|
||||
responses.
|
||||
</p>
|
||||
</article>
|
||||
<div class="flex-1">
|
||||
<x-mary-radio wire:model="appSelectionForm.applicationType" :options="ApplicationTypeEnum::asOptions()"
|
||||
option-value="value"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-slot:actions>
|
||||
<x-mary-button @click="$wire.showAppCreationModal = false">Cancel</x-mary-button>
|
||||
|
||||
<x-mary-button class="btn-primary" wire:click="createApp" spinner="createApp">Create</x-mary-button>
|
||||
</x-slot:actions>
|
||||
</x-mary-modal>
|
||||
|
||||
</div>
|
||||
|
||||
26
routes/apps.php
Normal file
26
routes/apps.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Enums\Permissions\{AppPermissionEnum, ConnectionProviderPermissionEnum};
|
||||
use Spatie\Permission\Middleware\PermissionMiddleware;
|
||||
|
||||
Route::prefix('apps')->name('apps.')->group(function (): void {
|
||||
Route::livewire('microsoft-federation', 'pages::connecton-providers.microsoft-federation')
|
||||
->middleware(PermissionMiddleware::using(ConnectionProviderPermissionEnum::Access))
|
||||
->name('microsoft-federation');
|
||||
|
||||
Route::middleware(PermissionMiddleware::using(AppPermissionEnum::Access))->group(function (): void {
|
||||
Route::livewire('create', 'pages::connected-apps.create')->name('create');
|
||||
Route::livewire('{id}/edit', 'pages::connected-apps.edit')->name('edit');
|
||||
Route::livewire('/', 'pages::connected-apps.index')->name('index');
|
||||
});
|
||||
|
||||
Route::prefix('connection-providers')->name('connectionProviders.')
|
||||
->middleware(PermissionMiddleware::using(ConnectionProviderPermissionEnum::Access))
|
||||
->group(function (): void {
|
||||
Route::livewire('create', 'pages::connecton-providers.create')->name('create');
|
||||
Route::livewire('{slug}/edit', 'pages::connecton-providers.edit')->name('edit');
|
||||
Route::livewire('index', 'pages::connecton-providers.index')->name('index');
|
||||
});
|
||||
});
|
||||
@ -3,11 +3,7 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Enums\DefaultUserRole;
|
||||
use App\Enums\Permissions\{AppPermissionEnum,
|
||||
ConnectionProviderPermissionEnum,
|
||||
PermissionPermissionEnum,
|
||||
RoleAndAppsPermissionEnum,
|
||||
UserPermissionEnum};
|
||||
use App\Enums\Permissions\{PermissionPermissionEnum, RoleAndAppsPermissionEnum, UserPermissionEnum};
|
||||
use App\Http\Controllers\{EntraController, KekaController};
|
||||
use App\Http\Controllers\KekaOutlookController;
|
||||
use App\Http\Controllers\{ResolveDashboardRouteController, SamlIdpController};
|
||||
@ -62,25 +58,7 @@
|
||||
});
|
||||
});
|
||||
|
||||
Route::prefix('apps')->name('apps.')->group(function (): void {
|
||||
Route::livewire('microsoft-federation', 'pages::connecton-providers.microsoft-federation')
|
||||
->middleware(PermissionMiddleware::using(ConnectionProviderPermissionEnum::Access))
|
||||
->name('microsoft-federation');
|
||||
|
||||
Route::middleware(PermissionMiddleware::using(AppPermissionEnum::Access))->group(function (): void {
|
||||
Route::livewire('create', 'pages::connected-apps.create')->name('create');
|
||||
Route::livewire('{id}/edit', 'pages::connected-apps.edit')->name('edit');
|
||||
Route::livewire('/', 'pages::connected-apps.index')->name('index');
|
||||
});
|
||||
|
||||
Route::prefix('connection-providers')->name('connectionProviders.')
|
||||
->middleware(PermissionMiddleware::using(ConnectionProviderPermissionEnum::Access))
|
||||
->group(function (): void {
|
||||
Route::livewire('create', 'pages::connecton-providers.create')->name('create');
|
||||
Route::livewire('{slug}/edit', 'pages::connecton-providers.edit')->name('edit');
|
||||
Route::livewire('index', 'pages::connecton-providers.index')->name('index');
|
||||
});
|
||||
});
|
||||
require __DIR__.'/apps.php';
|
||||
});
|
||||
|
||||
Route::middleware(['auth', 'verified'])->prefix('entra')->name('entra.')->group(function (): void {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user