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;
|
namespace App\Enums;
|
||||||
|
|
||||||
use App\Concerns\EnumValuesAsArray;
|
use App\Concerns\EnumValuesAsArray;
|
||||||
|
use App\Contracts\EnumMorphsToOptionsContract;
|
||||||
|
|
||||||
enum ConnectionProtocolEnum: string
|
enum ConnectionProtocolEnum: string implements EnumMorphsToOptionsContract
|
||||||
{
|
{
|
||||||
use EnumValuesAsArray;
|
use EnumValuesAsArray;
|
||||||
|
|
||||||
case OIDC = 'oidc';
|
case OIDC = 'oidc';
|
||||||
case SAML = 'saml';
|
case SAML = 'saml';
|
||||||
case OAUTH2 = 'oauth2';
|
|
||||||
case URL = 'url';
|
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\Concerns\HandlesOperations;
|
||||||
use App\Data\Ui\ConnectedApps\ConnectedAppData;
|
use App\Data\Ui\ConnectedApps\ConnectedAppData;
|
||||||
use App\Data\Ui\TableHeader;
|
use App\Data\Ui\TableHeader;
|
||||||
|
use App\Enums\ApplicationTypeEnum;
|
||||||
|
use App\Enums\ConnectionProtocolEnum;
|
||||||
|
use App\Livewire\Forms\CreateAppSelectionForm;
|
||||||
use App\Services\ConnectedAppService;
|
use App\Services\ConnectedAppService;
|
||||||
use Livewire\Attributes\Computed;
|
use Livewire\Attributes\Computed;
|
||||||
use Livewire\Attributes\Title;
|
use Livewire\Attributes\Title;
|
||||||
@ -19,6 +22,8 @@ class extends Component {
|
|||||||
use WithPagination, HandlesOperations, Confirmation;
|
use WithPagination, HandlesOperations, Confirmation;
|
||||||
|
|
||||||
protected ConnectedAppService $service;
|
protected ConnectedAppService $service;
|
||||||
|
public bool $showAppCreationModal = false;
|
||||||
|
public CreateAppSelectionForm $appSelectionForm;
|
||||||
|
|
||||||
#[DataCollectionOf(TableHeader::class)]
|
#[DataCollectionOf(TableHeader::class)]
|
||||||
public DataCollection $headers;
|
public DataCollection $headers;
|
||||||
@ -64,13 +69,22 @@ public function delete(int $appId): void
|
|||||||
successMessage: 'The app has been deleted !'
|
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>
|
<div>
|
||||||
<x-shared.card title="Connected Apps" class="pb-2">
|
<x-shared.card title="Connected Apps" class="pb-2">
|
||||||
<x-slot:actions>
|
<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-mary-button>
|
||||||
</x-slot:actions>
|
</x-slot:actions>
|
||||||
<x-mary-table
|
<x-mary-table
|
||||||
@ -85,6 +99,7 @@ public function delete(int $appId): void
|
|||||||
@scope('cell_status', $row)
|
@scope('cell_status', $row)
|
||||||
<x-shared.connection-status :status="$row->status"/>
|
<x-shared.connection-status :status="$row->status"/>
|
||||||
@endscope
|
@endscope
|
||||||
|
|
||||||
@scope('actions', $row)
|
@scope('actions', $row)
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<x-mary-button icon="lucide.edit" wire:click="edit({{$row->id}})" spinner="edit({{$row->id}})"
|
<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
|
@endscope
|
||||||
</x-mary-table>
|
</x-mary-table>
|
||||||
</x-shared.card>
|
</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>
|
</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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use App\Enums\DefaultUserRole;
|
use App\Enums\DefaultUserRole;
|
||||||
use App\Enums\Permissions\{AppPermissionEnum,
|
use App\Enums\Permissions\{PermissionPermissionEnum, RoleAndAppsPermissionEnum, UserPermissionEnum};
|
||||||
ConnectionProviderPermissionEnum,
|
|
||||||
PermissionPermissionEnum,
|
|
||||||
RoleAndAppsPermissionEnum,
|
|
||||||
UserPermissionEnum};
|
|
||||||
use App\Http\Controllers\{EntraController, KekaController};
|
use App\Http\Controllers\{EntraController, KekaController};
|
||||||
use App\Http\Controllers\KekaOutlookController;
|
use App\Http\Controllers\KekaOutlookController;
|
||||||
use App\Http\Controllers\{ResolveDashboardRouteController, SamlIdpController};
|
use App\Http\Controllers\{ResolveDashboardRouteController, SamlIdpController};
|
||||||
@ -62,25 +58,7 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::prefix('apps')->name('apps.')->group(function (): void {
|
require __DIR__.'/apps.php';
|
||||||
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');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::middleware(['auth', 'verified'])->prefix('entra')->name('entra.')->group(function (): void {
|
Route::middleware(['auth', 'verified'])->prefix('entra')->name('entra.')->group(function (): void {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user