feat(auth): refine authorization checks for S3 storage and service management
This commit is contained in:
@@ -29,7 +29,7 @@ class S3StoragePolicy
|
|||||||
*/
|
*/
|
||||||
public function create(User $user): bool
|
public function create(User $user): bool
|
||||||
{
|
{
|
||||||
return true;
|
return $user->isAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -28,7 +28,7 @@ class ServicePolicy
|
|||||||
*/
|
*/
|
||||||
public function create(User $user): bool
|
public function create(User $user): bool
|
||||||
{
|
{
|
||||||
return true;
|
return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $service->team()->first()->id) !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,7 +36,7 @@ class ServicePolicy
|
|||||||
*/
|
*/
|
||||||
public function update(User $user, Service $service): bool
|
public function update(User $user, Service $service): bool
|
||||||
{
|
{
|
||||||
return true;
|
return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $service->team()->first()->id) !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,10 +73,22 @@ class ServicePolicy
|
|||||||
|
|
||||||
public function stop(User $user, Service $service): bool
|
public function stop(User $user, Service $service): bool
|
||||||
{
|
{
|
||||||
if ($user->isAdmin()) {
|
return $user->teams()->get()->firstWhere('id', $service->team()->first()->id) !== null;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
/**
|
||||||
|
* Determine whether the user can manage environment variables.
|
||||||
|
*/
|
||||||
|
public function manageEnvironment(User $user, Service $service): bool
|
||||||
|
{
|
||||||
|
return $user->isAdmin() && $user->teams()->get()->firstWhere('id', $service->team()->first()->id) !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the user can deploy the service.
|
||||||
|
*/
|
||||||
|
public function deploy(User $user, Service $service): bool
|
||||||
|
{
|
||||||
|
return $user->teams()->get()->firstWhere('id', $service->team()->first()->id) !== null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,15 @@ class AuthServiceProvider extends ServiceProvider
|
|||||||
\App\Models\Application::class => \App\Policies\ApplicationPolicy::class,
|
\App\Models\Application::class => \App\Policies\ApplicationPolicy::class,
|
||||||
\App\Models\ApplicationPreview::class => \App\Policies\ApplicationPreviewPolicy::class,
|
\App\Models\ApplicationPreview::class => \App\Policies\ApplicationPreviewPolicy::class,
|
||||||
\App\Models\ApplicationSetting::class => \App\Policies\ApplicationSettingPolicy::class,
|
\App\Models\ApplicationSetting::class => \App\Policies\ApplicationSettingPolicy::class,
|
||||||
|
// Database policies - all use the shared DatabasePolicy
|
||||||
|
\App\Models\StandalonePostgresql::class => \App\Policies\DatabasePolicy::class,
|
||||||
|
\App\Models\StandaloneMysql::class => \App\Policies\DatabasePolicy::class,
|
||||||
|
\App\Models\StandaloneMariadb::class => \App\Policies\DatabasePolicy::class,
|
||||||
|
\App\Models\StandaloneMongodb::class => \App\Policies\DatabasePolicy::class,
|
||||||
|
\App\Models\StandaloneRedis::class => \App\Policies\DatabasePolicy::class,
|
||||||
|
\App\Models\StandaloneKeydb::class => \App\Policies\DatabasePolicy::class,
|
||||||
|
\App\Models\StandaloneDragonfly::class => \App\Policies\DatabasePolicy::class,
|
||||||
|
\App\Models\StandaloneClickhouse::class => \App\Policies\DatabasePolicy::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -2,9 +2,11 @@
|
|||||||
<form wire:submit='submit' class="flex flex-col pb-32">
|
<form wire:submit='submit' class="flex flex-col pb-32">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<h2>General</h2>
|
<h2>General</h2>
|
||||||
|
@can('update', $application)
|
||||||
<x-forms.button type="submit">
|
<x-forms.button type="submit">
|
||||||
Save
|
Save
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
|
@endcan
|
||||||
|
|
||||||
{{-- <x-forms.button wire:click="downloadConfig">
|
{{-- <x-forms.button wire:click="downloadConfig">
|
||||||
Download Config
|
Download Config
|
||||||
@@ -24,6 +26,7 @@
|
|||||||
@if (!$application->dockerfile && $application->build_pack !== 'dockerimage')
|
@if (!$application->dockerfile && $application->build_pack !== 'dockerimage')
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
|
@can('update', $application)
|
||||||
<x-forms.select x-bind:disabled="initLoadingCompose" wire:model.live="application.build_pack"
|
<x-forms.select x-bind:disabled="initLoadingCompose" wire:model.live="application.build_pack"
|
||||||
label="Build Pack" required>
|
label="Build Pack" required>
|
||||||
<option value="nixpacks">Nixpacks</option>
|
<option value="nixpacks">Nixpacks</option>
|
||||||
@@ -31,6 +34,16 @@
|
|||||||
<option value="dockerfile">Dockerfile</option>
|
<option value="dockerfile">Dockerfile</option>
|
||||||
<option value="dockercompose">Docker Compose</option>
|
<option value="dockercompose">Docker Compose</option>
|
||||||
</x-forms.select>
|
</x-forms.select>
|
||||||
|
@else
|
||||||
|
<x-forms.select disabled label="Build Pack" required>
|
||||||
|
<option value="nixpacks" @if ($application->build_pack === 'nixpacks') selected @endif>Nixpacks</option>
|
||||||
|
<option value="static" @if ($application->build_pack === 'static') selected @endif>Static</option>
|
||||||
|
<option value="dockerfile" @if ($application->build_pack === 'dockerfile') selected @endif>Dockerfile
|
||||||
|
</option>
|
||||||
|
<option value="dockercompose" @if ($application->build_pack === 'dockercompose') selected @endif>Docker
|
||||||
|
Compose</option>
|
||||||
|
</x-forms.select>
|
||||||
|
@endcan
|
||||||
@if ($application->settings->is_static || $application->build_pack === 'static')
|
@if ($application->settings->is_static || $application->build_pack === 'static')
|
||||||
<x-forms.select id="application.static_image" label="Static Image" required>
|
<x-forms.select id="application.static_image" label="Static Image" required>
|
||||||
<option value="nginx:alpine">nginx:alpine</option>
|
<option value="nginx:alpine">nginx:alpine</option>
|
||||||
@@ -52,8 +65,10 @@
|
|||||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io,https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "
|
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io,https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "
|
||||||
label="Domains for {{ str($serviceName)->headline() }}"
|
label="Domains for {{ str($serviceName)->headline() }}"
|
||||||
id="parsedServiceDomains.{{ str($serviceName)->slug('_') }}.domain"></x-forms.input>
|
id="parsedServiceDomains.{{ str($serviceName)->slug('_') }}.domain"></x-forms.input>
|
||||||
|
@can('update', $application)
|
||||||
<x-forms.button wire:click="generateDomain('{{ $serviceName }}')">Generate
|
<x-forms.button wire:click="generateDomain('{{ $serviceName }}')">Generate
|
||||||
Domain</x-forms.button>
|
Domain</x-forms.button>
|
||||||
|
@endcan
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@endforeach
|
@endforeach
|
||||||
@@ -66,8 +81,10 @@
|
|||||||
<x-forms.textarea id="application.custom_nginx_configuration"
|
<x-forms.textarea id="application.custom_nginx_configuration"
|
||||||
placeholder="Empty means default configuration will be used." label="Custom Nginx Configuration"
|
placeholder="Empty means default configuration will be used." label="Custom Nginx Configuration"
|
||||||
helper="You can add custom Nginx configuration here." />
|
helper="You can add custom Nginx configuration here." />
|
||||||
|
@can('update', $application)
|
||||||
<x-forms.button wire:click="generateNginxConfiguration">Generate Default Nginx
|
<x-forms.button wire:click="generateNginxConfiguration">Generate Default Nginx
|
||||||
Configuration</x-forms.button>
|
Configuration</x-forms.button>
|
||||||
|
@endcan
|
||||||
@endif
|
@endif
|
||||||
<div class="w-96 pb-6">
|
<div class="w-96 pb-6">
|
||||||
@if ($application->could_set_build_commands())
|
@if ($application->could_set_build_commands())
|
||||||
@@ -90,8 +107,10 @@
|
|||||||
<x-forms.input placeholder="https://coolify.io" wire:model.blur-sm="application.fqdn"
|
<x-forms.input placeholder="https://coolify.io" wire:model.blur-sm="application.fqdn"
|
||||||
label="Domains"
|
label="Domains"
|
||||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io,https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. " />
|
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io,https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. " />
|
||||||
|
@can('update', $application)
|
||||||
<x-forms.button wire:click="getWildcardDomain">Generate Domain
|
<x-forms.button wire:click="getWildcardDomain">Generate Domain
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
|
@endcan
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-end gap-2">
|
<div class="flex items-end gap-2">
|
||||||
@@ -204,14 +223,18 @@
|
|||||||
@endif
|
@endif
|
||||||
<div class="flex flex-col gap-2 pt-6 pb-10">
|
<div class="flex flex-col gap-2 pt-6 pb-10">
|
||||||
@if ($application->build_pack === 'dockercompose')
|
@if ($application->build_pack === 'dockercompose')
|
||||||
|
@can('update', $application)
|
||||||
<div class="flex flex-col gap-2" x-init="$wire.dispatch('loadCompose', true)">
|
<div class="flex flex-col gap-2" x-init="$wire.dispatch('loadCompose', true)">
|
||||||
|
@else
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
@endcan
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<x-forms.input x-bind:disabled="initLoadingCompose" placeholder="/"
|
<x-forms.input x-bind:disabled="initLoadingCompose" placeholder="/"
|
||||||
id="application.base_directory" label="Base Directory"
|
id="application.base_directory" label="Base Directory"
|
||||||
helper="Directory to use as root. Useful for monorepos." />
|
helper="Directory to use as root. Useful for monorepos." />
|
||||||
<x-forms.input x-bind:disabled="initLoadingCompose"
|
<x-forms.input x-bind:disabled="initLoadingCompose"
|
||||||
placeholder="/docker-compose.yaml" id="application.docker_compose_location"
|
placeholder="/docker-compose.yaml"
|
||||||
label="Docker Compose Location"
|
id="application.docker_compose_location" label="Docker Compose Location"
|
||||||
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }}</span>" />
|
helper="It is calculated together with the Base Directory:<br><span class='dark:text-warning'>{{ Str::start($application->base_directory . $application->docker_compose_location, '/') }}</span>" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-96">
|
<div class="w-96">
|
||||||
@@ -240,7 +263,8 @@
|
|||||||
</div>
|
</div>
|
||||||
@else
|
@else
|
||||||
<div class="flex flex-col gap-2 xl:flex-row">
|
<div class="flex flex-col gap-2 xl:flex-row">
|
||||||
<x-forms.input placeholder="/" id="application.base_directory" label="Base Directory"
|
<x-forms.input placeholder="/" id="application.base_directory"
|
||||||
|
label="Base Directory"
|
||||||
helper="Directory to use as root. Useful for monorepos." />
|
helper="Directory to use as root. Useful for monorepos." />
|
||||||
@if ($application->build_pack === 'dockerfile' && !$application->dockerfile)
|
@if ($application->build_pack === 'dockerfile' && !$application->dockerfile)
|
||||||
<x-forms.input placeholder="/Dockerfile" id="application.dockerfile_location"
|
<x-forms.input placeholder="/Dockerfile" id="application.dockerfile_location"
|
||||||
@@ -419,6 +443,7 @@
|
|||||||
@script
|
@script
|
||||||
<script>
|
<script>
|
||||||
$wire.$on('loadCompose', (isInit = true) => {
|
$wire.$on('loadCompose', (isInit = true) => {
|
||||||
|
// Only load compose file if user has permission (this event should only be dispatched when authorized)
|
||||||
$wire.initLoadingCompose = true;
|
$wire.initLoadingCompose = true;
|
||||||
$wire.loadComposeFile(isInit);
|
$wire.loadComposeFile(isInit);
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user