Skip to content

Commit

Permalink
Merge pull request #134 from creative-commoners/pulls/4/permission-model
Browse files Browse the repository at this point in the history
ENH Add permission methods based on owner
  • Loading branch information
sabina-talipova authored Dec 14, 2023
2 parents 7f65d4f + 5e35a84 commit 55208f8
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 0 deletions.
61 changes: 61 additions & 0 deletions src/Models/Link.php
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,67 @@ public function Owner(): ?DataObject
return $owner;
}

public function canView($member = null)
{
return $this->canPerformAction(__FUNCTION__, $member);
}

public function canEdit($member = null)
{
return $this->canPerformAction(__FUNCTION__, $member);
}

public function canDelete($member = null)
{
return $this->canPerformAction(__FUNCTION__, $member);
}

public function canCreate($member = null, $context = [])
{
// Allow extensions to override permission checks
$results = $this->extendedCan(__FUNCTION__, $member, $context);
if (isset($results)) {
return $results;
}

// Assume anyone can create a link by default - there's no way to determine
// what the "owner" record is going to be ahead of time, but if the user
// can't edit the owner then the linkfield will be read-only anyway, so we
// can rely on that to determine who can create links.
return true;
}

public function can($perm, $member = null, $context = [])
{
$check = ucfirst(strtolower($perm));
return match ($check) {
'View', 'Create', 'Edit', 'Delete' => $this->{"can$check"}($member, $context),
default => parent::can($perm, $member, $context)
};
}

private function canPerformAction(string $canMethod, $member, $context = [])
{
// Allow extensions to override permission checks
$results = $this->extendedCan($canMethod, $member, $context);
if (isset($results)) {
return $results;
}

// If we have an owner, rely on it to tell us what we can and can't do
$owner = $this->Owner();
if ($owner && $owner->exists()) {
// Can delete or create links if you can edit its owner.
if ($canMethod === 'canCreate' || $canMethod === 'canDelete') {
$canMethod = 'canEdit';
}
return $owner->$canMethod($member, $context);
}

// Default to DataObject's permission checks
return parent::$canMethod($member, $context);
}

/**
* Get all link types except the generic one
*
Expand Down
46 changes: 46 additions & 0 deletions tests/php/Models/LinkTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -515,4 +515,50 @@ public function testOwnerHasOne()
$link->flushCache(false);
$this->assertSame($owner->ID, $link->Owner()?->ID);
}

/**
* @dataProvider provideCanPermissions
*/
public function testCanPermissions(string $linkPermission, string $ownerPermission)
{
$link = $this->objFromFixture(SiteTreeLink::class, 'page-link-page-only');
$owner = $link->Owner();
$permissionName = substr($linkPermission, 3);

$this->assertTrue($owner?->exists());

$owner->$ownerPermission = true;
$this->assertTrue($link->$linkPermission());
$this->assertTrue($link->can($permissionName));

$owner->$ownerPermission = false;
$this->assertFalse($link->$linkPermission());
$this->assertFalse($link->can($permissionName));
}

public function provideCanPermissions()
{
return [
'canView' => [
'linkPermission' => 'canView',
'ownerPermission' => 'canView',
],
'canEdit' => [
'linkPermission' => 'canEdit',
'ownerPermission' => 'canEdit',
],
'canDelete' => [
'linkPermission' => 'canDelete',
'ownerPermission' => 'canEdit',
],
];
}

public function testCanCreate()
{
$link = $this->objFromFixture(SiteTreeLink::class, 'page-link-page-only');
$this->logOut();
$this->assertTrue($link->canCreate());
$this->assertTrue($link->can('Create'));
}
}
14 changes: 14 additions & 0 deletions tests/php/Models/LinkTest/LinkOwner.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,18 @@ class LinkOwner extends DataObject implements TestOnly
private static array $has_many = [
'LinkList' => Link::class . '.Owner',
];

// Allows us to toggle permissions easily within a unit test
public bool $canView = true;
public bool $canEdit = true;

public function canView($member = null)
{
return $this->canView;
}

public function canEdit($member = null)
{
return $this->canEdit;
}
}

0 comments on commit 55208f8

Please sign in to comment.