Skip to content

Commit

Permalink
use policy authorization for client-side server endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
ericwang401 committed Nov 28, 2023
1 parent 948c659 commit 375e338
Show file tree
Hide file tree
Showing 30 changed files with 317 additions and 180 deletions.
42 changes: 23 additions & 19 deletions app/Http/Controllers/Client/Servers/BackupController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Convoy\Http\Controllers\Client\Servers;

use Convoy\Http\Requests\Client\Servers\Backups\DeleteBackupRequest;
use Convoy\Http\Requests\Client\Servers\Backups\RestoreBackupRequest;
use Convoy\Models\Backup;
use Convoy\Models\Server;
use Illuminate\Http\Request;
Expand All @@ -19,48 +21,50 @@
class BackupController extends ApplicationApiController
{
public function __construct(
private BackupCreationService $backupCreationService,
private BackupDeletionService $backupDeletionService,
private BackupCreationService $backupCreationService,
private BackupDeletionService $backupDeletionService,
private RestoreFromBackupService $restoreFromBackupService,
private BackupRepository $backupRepository,
) {
private BackupRepository $backupRepository,
)
{
}

public function index(Server $server, Request $request)
public function index(Request $request, Server $server)
{
$backups = QueryBuilder::for(Backup::query())
->where('backups.server_id', $server->id)
->allowedFilters(['name'])
->defaultSort('-created_at')
->allowedSorts('created_at', 'completed_at')
->paginate(min($request->query('per_page') ?? 20, 50));
->where('backups.server_id', $server->id)
->allowedFilters(['name'])
->defaultSort('-created_at')
->allowedSorts('created_at', 'completed_at')
->paginate(min($request->query('per_page') ?? 20, 50));

return fractal($backups, new BackupTransformer)->addMeta([
return fractal($backups, new BackupTransformer())->addMeta([
'backup_count' => $this->backupRepository->getNonFailedBackups($server)->count(),
])->respond();
}

public function store(Server $server, StoreBackupRequest $request)
public function store(StoreBackupRequest $request, Server $server)
{
$backup = $this->backupCreationService
->create(server: $server,
name: $request->name,
mode: $request->enum('mode', BackupMode::class),
->create(
server : $server,
name : $request->name,
mode : $request->enum('mode', BackupMode::class),
compressionType: $request->enum('compression_type', BackupCompressionType::class),
isLocked: $request->input('locked', false)
isLocked : $request->input('locked', false),
);

return fractal($backup, new BackupTransformer)->respond();
return fractal($backup, new BackupTransformer())->respond();
}

public function restore(Server $server, Backup $backup)
public function restore(RestoreBackupRequest $request, Server $server, Backup $backup)
{
$this->restoreFromBackupService->handle($server, $backup);

return $this->returnNoContent();
}

public function destroy(Server $server, Backup $backup)
public function destroy(DeleteBackupRequest $request, Server $server, Backup $backup)
{
$this->backupDeletionService->handle($backup);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
use Illuminate\Support\Arr;
use Convoy\Models\AddressPool;
use Illuminate\Validation\Validator;
use Convoy\Http\Requests\FormRequest;
use Convoy\Http\Requests\BaseApiRequest;
use Convoy\Enums\Network\AddressType;
use Convoy\Validation\ValidateAddressType;
use Convoy\Validation\ValidateAddressUniqueness;

class StoreAddressRequest extends FormRequest
class StoreAddressRequest extends BaseApiRequest
{
public function rules(): array
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
use Illuminate\Support\Arr;
use Convoy\Models\AddressPool;
use Illuminate\Validation\Validator;
use Convoy\Http\Requests\FormRequest;
use Convoy\Http\Requests\BaseApiRequest;
use Convoy\Enums\Network\AddressType;
use Convoy\Validation\ValidateAddressType;
use Convoy\Validation\ValidateAddressUniqueness;

class UpdateAddressRequest extends FormRequest
class UpdateAddressRequest extends BaseApiRequest
{
public function rules(): array
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
use Convoy\Models\Node;
use Convoy\Models\AddressPool;
use Illuminate\Validation\Validator;
use Convoy\Http\Requests\FormRequest;
use Convoy\Http\Requests\BaseApiRequest;

class UpdateAddressPoolRequest extends FormRequest
class UpdateAddressPoolRequest extends BaseApiRequest
{
public function rules(): array
{
Expand Down
4 changes: 2 additions & 2 deletions app/Http/Requests/Admin/Nodes/Isos/StoreIsoRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
use Convoy\Models\ISO;
use Convoy\Models\Node;
use Illuminate\Validation\Validator;
use Convoy\Http\Requests\FormRequest;
use Convoy\Http\Requests\BaseApiRequest;
use Illuminate\Validation\Rules\Enum;
use Convoy\Services\Nodes\Isos\IsoService;
use Convoy\Enums\Helpers\ChecksumAlgorithm;

class StoreIsoRequest extends FormRequest
class StoreIsoRequest extends BaseApiRequest
{
public function rules(): array
{
Expand Down
4 changes: 2 additions & 2 deletions app/Http/Requests/Admin/Nodes/Isos/UpdateIsoRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
namespace Convoy\Http\Requests\Admin\Nodes\Isos;

use Convoy\Models\ISO;
use Convoy\Http\Requests\FormRequest;
use Convoy\Http\Requests\BaseApiRequest;

class UpdateIsoRequest extends FormRequest
class UpdateIsoRequest extends BaseApiRequest
{
public function rules(): array
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
namespace Convoy\Http\Requests\Admin\Nodes\Settings;

use Illuminate\Validation\Rule;
use Convoy\Http\Requests\FormRequest;
use Convoy\Http\Requests\BaseApiRequest;

class UpdateCotermRequest extends FormRequest
class UpdateCotermRequest extends BaseApiRequest
{
/**
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
Expand Down
4 changes: 2 additions & 2 deletions app/Http/Requests/Admin/Nodes/UpdateNodeRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
use Convoy\Models\Node;
use Illuminate\Support\Arr;
use Illuminate\Validation\Validator;
use Convoy\Http\Requests\FormRequest;
use Convoy\Http\Requests\BaseApiRequest;

class UpdateNodeRequest extends FormRequest
class UpdateNodeRequest extends BaseApiRequest
{
public function rules(): array
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
use Convoy\Models\Server;
use Convoy\Models\Address;
use Illuminate\Validation\Validator;
use Convoy\Http\Requests\FormRequest;
use Convoy\Http\Requests\BaseApiRequest;

class UpdateBuildRequest extends FormRequest
class UpdateBuildRequest extends BaseApiRequest
{
public function rules(): array
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
namespace Convoy\Http\Requests\Admin\Servers\Settings;

use Convoy\Models\Server;
use Convoy\Http\Requests\FormRequest;
use Convoy\Http\Requests\BaseApiRequest;

class UpdateDetailsRequest extends FormRequest
class UpdateDetailsRequest extends BaseApiRequest
{
/**
* Determine if the user is authorized to make this request.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

use Convoy\Models\Server;
use Convoy\Rules\Hostname;
use Convoy\Http\Requests\FormRequest;
use Convoy\Http\Requests\BaseApiRequest;

class UpdateGeneralInfoRequest extends FormRequest
class UpdateGeneralInfoRequest extends BaseApiRequest
{
public function rules(): array
{
Expand Down
4 changes: 2 additions & 2 deletions app/Http/Requests/Admin/Servers/StoreServerRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
use Convoy\Models\Address;
use Convoy\Rules\Password;
use Illuminate\Validation\Validator;
use Convoy\Http\Requests\FormRequest;
use Convoy\Http\Requests\BaseApiRequest;
use Convoy\Rules\EnglishKeyboardCharacters;

/**
* @property mixed $type
*/
class StoreServerRequest extends FormRequest
class StoreServerRequest extends BaseApiRequest
{
/**
* Get the validation rules that apply to the request.
Expand Down
4 changes: 2 additions & 2 deletions app/Http/Requests/Admin/Users/UpdateUserRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
namespace Convoy\Http\Requests\Admin\Users;

use Convoy\Models\User;
use Convoy\Http\Requests\FormRequest;
use Convoy\Http\Requests\BaseApiRequest;
use Illuminate\Validation\Rules\Password;

class UpdateUserRequest extends FormRequest
class UpdateUserRequest extends BaseApiRequest
{
/**
* Determine if the user is authorized to make this request.
Expand Down
109 changes: 109 additions & 0 deletions app/Http/Requests/BaseApiRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php

namespace Convoy\Http\Requests;

use Illuminate\Auth\Access\AuthorizationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Webmozart\Assert\Assert;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Http\FormRequest;

abstract class BaseApiRequest extends FormRequest
{
/**
* Tracks if the request has been validated internally or not to avoid
* making duplicate validation calls.
*/
private bool $hasValidated = false;

public function authorize(): bool
{
return $this->user()->root_admin;
}

/**
* Validate that the resource exists and can be accessed prior to booting
* the validator and attempting to use the data.
*
* @throws AuthorizationException
*/
protected function prepareForValidation(): void
{
if (!$this->passesAuthorization()) {
$this->failedAuthorization();
}

$this->hasValidated = true;
}

/*
* Determine if the request passes the authorization check as well
* as the exists check.
*
* @return bool
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
protected function passesAuthorization(): bool
{
// If we have already validated we do not need to call this function
// again. This is needed to work around Laravel's normal auth validation
// that occurs after validating the request params since we are doing auth
// validation in the prepareForValidation() function.
if ($this->hasValidated) {
return true;
}

if (!parent::passesAuthorization()) {
return false;
}

// Only let the user know that a resource does not exist if they are
// authenticated to access the endpoint. This avoids exposing that
// an item exists (or does not exist) to the user until they can prove
// that they have permission to know about it.
if ($this->attributes->get('is_missing_model', false)) {
throw new NotFoundHttpException(trans('exceptions.api.resource_not_found'));
}

return true;
}

public function requiredToOptional(array $rules): array
{
foreach ($rules as &$rule) {
if (is_string($rule)) {
$rule = str_replace('required', 'sometimes', $rule);
}

if (is_array($rule)) {
$rule = $this->requiredToOptional($rule);
}
}

return $rules;
}

/**
* Returns the named route parameter and asserts that it is a real model that
* exists in the database.
*
* @template T of Model
*
* @param class-string<T> $expect
* @return T
*
* @noinspection PhpDocSignatureInspection
*/
public function parameter(string $key, string $expect)
{
$value = $this->route()->parameter($key);

Assert::isInstanceOf($value, $expect);
Assert::isInstanceOf($value, Model::class);
Assert::true($value->exists);

/* @var T $value */
return $value;
}
}
18 changes: 18 additions & 0 deletions app/Http/Requests/Client/Servers/Backups/DeleteBackupRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Convoy\Http\Requests\Client\Servers\Backups;

use Convoy\Http\Requests\BaseApiRequest;
use Convoy\Models\Backup;
use Convoy\Models\Server;

class DeleteBackupRequest extends BaseApiRequest
{
public function authorize(): bool
{
$server = $this->parameter('server', Server::class);
$backup = $this->parameter('backup', Backup::class);

return $this->user()->can('delete', [$backup, $server]);
}
}
18 changes: 18 additions & 0 deletions app/Http/Requests/Client/Servers/Backups/RestoreBackupRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Convoy\Http\Requests\Client\Servers\Backups;

use Convoy\Http\Requests\BaseApiRequest;
use Convoy\Models\Backup;
use Convoy\Models\Server;

class RestoreBackupRequest extends BaseApiRequest
{
public function authorize(): bool
{
$server = $this->parameter('server', Server::class);
$backup = $this->parameter('backup', Backup::class);

return $this->user()->can('restore', [$backup, $server]);
}
}
Loading

0 comments on commit 375e338

Please sign in to comment.