Skip to content

Commit

Permalink
GrantResult: Allow to deny, but to show the form
Browse files Browse the repository at this point in the history
  • Loading branch information
Dominic Tubach committed Oct 26, 2023
1 parent 601ab7b commit 4f80bc3
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public function getFields(RemoteGetFieldsAction $action): Result {
*/
public function getCreateForm(RemoteGetCreateFormAction $action): array {
$grantResult = $this->profile->isCreateGranted($action->getArguments(), $action->getResolvedContactId());
if (!$grantResult->granted) {
if (!$grantResult->showForm) {
throw new UnauthorizedException($grantResult->message ?? E::ts('Permission to create entity is missing'));
}

Expand All @@ -133,7 +133,7 @@ public function getCreateForm(RemoteGetCreateFormAction $action): array {
public function getUpdateForm(RemoteGetUpdateFormAction $action): array {
$entityValues = $this->getEntityById($action->getId(), 'update', $action->getResolvedContactId());
$grantResult = $this->profile->isUpdateGranted($entityValues, $action->getResolvedContactId());
if (NULL === $entityValues || !$grantResult->granted) {
if (NULL === $entityValues || !$grantResult->showForm) {
throw new UnauthorizedException($grantResult->message ?? E::ts('Permission to update entity is missing'));
}

Expand Down
17 changes: 16 additions & 1 deletion Civi/RemoteTools/EntityProfile/Authorization/GrantResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ final class GrantResult {

public bool $granted;

public bool $showForm;

public ?string $message;

private function __construct(bool $granted, ?string $message) {
private function __construct(bool $granted, ?string $message, ?bool $showForm = NULL) {
$this->granted = $granted;
$this->message = $message;
$this->showForm = $showForm ?? $granted;
}

public static function newPermitted(): self {
Expand All @@ -45,4 +48,16 @@ public static function newDenied(?string $message = NULL): self {
return new self(FALSE, $message);
}

/**
* Performing the actual action will be denied, but the form will still be
* delivered. This can be used to provide a "form" with only custom markup
* (no fields).
*
* @param string|null $message
* If no message is specified, a generic message might be used.
*/
public static function newDeniedWithForm(?string $message = NULL): self {
return new self(FALSE, $message, TRUE);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,41 @@ public function testGetCreateForm(): void {
static::assertSame(['form' => 'Test'], $this->handler->getCreateForm($actionMock));
}

public function testGetCreateFormNotAllowed(): void {
public function testGetCreateFormDeiniedWithForm(): void {
$actionMock = $this->createActionMock(RemoteGetCreateFormAction::class);
$arguments = ['key' => 'value'];
$actionMock->setArguments($arguments);

$this->profileMock->method('isCreateGranted')
->with($arguments, self::RESOLVED_CONTACT_ID)
->willReturn(GrantResult::newDeniedWithForm());
$this->profileMock->method('isFormSpecNeedsFieldOptions')->willReturn(FALSE);

$entityFields = [
'foo' => ['name' => 'foo'],
'bar' => ['name' => 'bar'],
];
$this->api4Mock->method('execute')
->with('Entity', 'getFields', [
'loadOptions' => FALSE,
'values' => [],
'checkPermissions' => FALSE,
])
->willReturn(new Result(array_values($entityFields)));

$formSpec = new FormSpec('Title');
$this->profileMock->method('getCreateFormSpec')
->with($arguments, $entityFields, self::RESOLVED_CONTACT_ID)
->willReturn($formSpec);

$this->handler->method('convertToGetFormActionResult')
->with($formSpec)
->willReturn(['form' => 'Test']);

static::assertSame(['form' => 'Test'], $this->handler->getCreateForm($actionMock));
}

public function testGetCreateFormDenied(): void {
$actionMock = $this->createActionMock(RemoteGetCreateFormAction::class);
$arguments = ['key' => 'value'];
$actionMock->setArguments($arguments);
Expand Down Expand Up @@ -277,7 +311,62 @@ public function testGetUpdateForm(): void {
static::assertSame(['form' => 'Test'], $this->handler->getUpdateForm($actionMock));
}

public function testGetUpdateFormNotAllowed(): void {
public function testGetUpdateFormDeniedWithForm(): void {
$actionMock = $this->createActionMock(RemoteGetUpdateFormAction::class);
$actionMock->setId(12);

$entityValues = ['foo' => 'f'];
$entityFields = [
'foo' => ['name' => 'foo'],
'bar' => ['name' => 'bar'],
];

$this->profileMock->method('getSelectFieldNames')
->with(['*'], 'update', [], self::RESOLVED_CONTACT_ID)
->willReturn(['foo']);
$this->profileMock->method('isUpdateGranted')
->with($entityValues, self::RESOLVED_CONTACT_ID)
->willReturn(GrantResult::newDeniedWithForm());
$this->profileMock->method('isFormSpecNeedsFieldOptions')->willReturn(TRUE);

$valueMap = [
[
'Entity',
'get',
[
'select' => ['foo'],
'where' => [['id', '=', 12]],
'checkPermissions' => FALSE,
],
new Result([$entityValues]),
],
[
'Entity',
'getFields',
[
'loadOptions' => TRUE,
'values' => ['id' => 12],
'checkPermissions' => FALSE,
],
new Result(array_values($entityFields)),
],
];
$this->api4Mock->method('execute')
->willReturnMap($valueMap);

$formSpec = new FormSpec('Title');
$this->profileMock->method('getUpdateFormSpec')
->with($entityValues, $entityFields, self::RESOLVED_CONTACT_ID)
->willReturn($formSpec);

$this->handler->method('convertToGetFormActionResult')
->with($formSpec)
->willReturn(['form' => 'Test']);

static::assertSame(['form' => 'Test'], $this->handler->getUpdateForm($actionMock));
}

public function testGetUpdateFormDenied(): void {
$actionMock = $this->createActionMock(RemoteGetUpdateFormAction::class);
$actionMock->setId(12);

Expand Down

0 comments on commit 4f80bc3

Please sign in to comment.