2017-05-11 11:07:27 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace SilverStripe\Security\Tests;
|
|
|
|
|
|
|
|
use SilverStripe\Core\Injector\Injector;
|
|
|
|
use SilverStripe\Dev\SapphireTest;
|
|
|
|
use SilverStripe\Security\Group;
|
|
|
|
use SilverStripe\Security\InheritedPermissions;
|
|
|
|
use SilverStripe\Security\Member;
|
|
|
|
use SilverStripe\Security\PermissionChecker;
|
|
|
|
use SilverStripe\Security\Test\InheritedPermissionsTest\TestPermissionNode;
|
|
|
|
use SilverStripe\Security\Test\InheritedPermissionsTest\TestDefaultPermissionChecker;
|
2018-02-19 23:37:27 +01:00
|
|
|
use SilverStripe\Security\Test\InheritedPermissionsTest\UnstagedNode;
|
2017-05-11 11:07:27 +02:00
|
|
|
use SilverStripe\Versioned\Versioned;
|
|
|
|
|
|
|
|
class InheritedPermissionsTest extends SapphireTest
|
|
|
|
{
|
|
|
|
protected static $fixture_file = 'InheritedPermissionsTest.yml';
|
|
|
|
|
|
|
|
protected static $extra_dataobjects = [
|
|
|
|
TestPermissionNode::class,
|
2018-02-19 23:37:27 +01:00
|
|
|
UnstagedNode::class,
|
2017-05-11 11:07:27 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var TestDefaultPermissionChecker
|
|
|
|
*/
|
|
|
|
protected $rootPermissions = null;
|
|
|
|
|
|
|
|
protected function setUp()
|
|
|
|
{
|
2018-02-19 23:37:27 +01:00
|
|
|
$this->rootPermissions = new TestDefaultPermissionChecker();
|
2017-05-11 11:07:27 +02:00
|
|
|
|
|
|
|
// Register root permissions
|
2018-02-19 23:37:27 +01:00
|
|
|
$permission1 = InheritedPermissions::create(TestPermissionNode::class)
|
2017-05-11 11:07:27 +02:00
|
|
|
->setGlobalEditPermissions(['TEST_NODE_ACCESS'])
|
2018-02-19 23:37:27 +01:00
|
|
|
->setDefaultPermissions($this->rootPermissions);
|
2017-05-11 11:07:27 +02:00
|
|
|
Injector::inst()->registerService(
|
2018-02-19 23:37:27 +01:00
|
|
|
$permission1,
|
2018-01-16 19:39:30 +01:00
|
|
|
PermissionChecker::class . '.testpermissions'
|
2017-05-11 11:07:27 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
// Reset root permission
|
2018-02-19 23:37:27 +01:00
|
|
|
$permission2 = InheritedPermissions::create(UnstagedNode::class)
|
|
|
|
->setGlobalEditPermissions(['TEST_NODE_ACCESS'])
|
|
|
|
->setDefaultPermissions($this->rootPermissions);
|
|
|
|
Injector::inst()->registerService(
|
|
|
|
$permission2,
|
|
|
|
PermissionChecker::class . '.unstagedpermissions'
|
|
|
|
);
|
|
|
|
|
|
|
|
parent::setUp();
|
|
|
|
|
|
|
|
$permission1->clearCache();
|
|
|
|
$permission2->clearCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function tearDown()
|
|
|
|
{
|
|
|
|
Injector::inst()->unregisterNamedObject(PermissionChecker::class . '.testpermissions');
|
|
|
|
Injector::inst()->unregisterNamedObject(PermissionChecker::class . '.unstagedpermissions');
|
|
|
|
$this->rootPermissions = null;
|
|
|
|
parent::tearDown();
|
2017-05-11 11:07:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function testEditPermissions()
|
|
|
|
{
|
|
|
|
$editor = $this->objFromFixture(Member::class, 'editor');
|
|
|
|
|
|
|
|
$about = $this->objFromFixture(TestPermissionNode::class, 'about');
|
|
|
|
$aboutStaff = $this->objFromFixture(TestPermissionNode::class, 'about-staff');
|
|
|
|
$history = $this->objFromFixture(TestPermissionNode::class, 'history');
|
|
|
|
$products = $this->objFromFixture(TestPermissionNode::class, 'products');
|
|
|
|
$product1 = $this->objFromFixture(TestPermissionNode::class, 'products-product1');
|
|
|
|
$product4 = $this->objFromFixture(TestPermissionNode::class, 'products-product4');
|
|
|
|
|
|
|
|
// Test logged out users cannot edit
|
|
|
|
Member::actAs(null, function () use ($aboutStaff) {
|
|
|
|
$this->assertFalse($aboutStaff->canEdit());
|
|
|
|
});
|
|
|
|
|
|
|
|
// Can't edit a page that is locked to admins
|
|
|
|
$this->assertFalse($about->canEdit($editor));
|
|
|
|
|
|
|
|
// Can edit a page that is locked to editors
|
|
|
|
$this->assertTrue($products->canEdit($editor));
|
|
|
|
|
|
|
|
// Can edit a child of that page that inherits
|
|
|
|
$this->assertTrue($product1->canEdit($editor));
|
|
|
|
|
|
|
|
// Can't edit a child of that page that has its permissions overridden
|
|
|
|
$this->assertFalse($product4->canEdit($editor));
|
|
|
|
|
|
|
|
// Test that root node respects root permissions
|
|
|
|
$this->assertTrue($history->canEdit($editor));
|
|
|
|
|
|
|
|
TestPermissionNode::getInheritedPermissions()->clearCache();
|
|
|
|
$this->rootPermissions->setCanEdit(false);
|
|
|
|
|
|
|
|
// With root edit false, permissions are now denied for CanEditType = Inherit
|
|
|
|
$this->assertFalse($history->canEdit($editor));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testDeletePermissions()
|
|
|
|
{
|
|
|
|
$editor = $this->objFromFixture(Member::class, 'editor');
|
|
|
|
|
|
|
|
$about = $this->objFromFixture(TestPermissionNode::class, 'about');
|
|
|
|
$aboutStaff = $this->objFromFixture(TestPermissionNode::class, 'about-staff');
|
|
|
|
$history = $this->objFromFixture(TestPermissionNode::class, 'history');
|
|
|
|
$products = $this->objFromFixture(TestPermissionNode::class, 'products');
|
|
|
|
$product1 = $this->objFromFixture(TestPermissionNode::class, 'products-product1');
|
|
|
|
$product4 = $this->objFromFixture(TestPermissionNode::class, 'products-product4');
|
|
|
|
|
|
|
|
// Test logged out users cannot edit
|
|
|
|
Member::actAs(null, function () use ($aboutStaff) {
|
|
|
|
$this->assertFalse($aboutStaff->canDelete());
|
|
|
|
});
|
|
|
|
|
|
|
|
// Can't edit a page that is locked to admins
|
|
|
|
$this->assertFalse($about->canDelete($editor));
|
|
|
|
|
|
|
|
// Can't delete a page if a child (product4) is un-deletable
|
|
|
|
$this->assertFalse($products->canDelete($editor));
|
|
|
|
|
|
|
|
// Can edit a child of that page that inherits
|
|
|
|
$this->assertTrue($product1->canDelete($editor));
|
|
|
|
|
|
|
|
// Can't edit a child of that page that has its permissions overridden
|
|
|
|
$this->assertFalse($product4->canDelete($editor));
|
|
|
|
|
|
|
|
// Test that root node respects root permissions
|
|
|
|
$this->assertTrue($history->canDelete($editor));
|
|
|
|
|
|
|
|
TestPermissionNode::getInheritedPermissions()->clearCache();
|
|
|
|
$this->rootPermissions->setCanEdit(false);
|
|
|
|
|
|
|
|
// With root edit false, permissions are now denied for CanEditType = Inherit
|
|
|
|
$this->assertFalse($history->canDelete($editor));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testViewPermissions()
|
|
|
|
{
|
|
|
|
$history = $this->objFromFixture(TestPermissionNode::class, 'history');
|
|
|
|
$contact = $this->objFromFixture(TestPermissionNode::class, 'contact');
|
|
|
|
$contactForm = $this->objFromFixture(TestPermissionNode::class, 'contact-form');
|
|
|
|
$secret = $this->objFromFixture(TestPermissionNode::class, 'secret');
|
|
|
|
$secretNested = $this->objFromFixture(TestPermissionNode::class, 'secret-nested');
|
|
|
|
$protected = $this->objFromFixture(TestPermissionNode::class, 'protected');
|
|
|
|
$protectedChild = $this->objFromFixture(TestPermissionNode::class, 'protected-child');
|
|
|
|
$editor = $this->objFromFixture(Member::class, 'editor');
|
|
|
|
|
|
|
|
// Not logged in user can only access Inherit or Anyone pages
|
|
|
|
Member::actAs(
|
|
|
|
null,
|
|
|
|
function () use ($protectedChild, $secretNested, $protected, $secret, $history, $contact, $contactForm) {
|
|
|
|
$this->assertTrue($history->canView());
|
|
|
|
$this->assertTrue($contact->canView());
|
|
|
|
$this->assertTrue($contactForm->canView());
|
|
|
|
// Protected
|
|
|
|
$this->assertFalse($secret->canView());
|
|
|
|
$this->assertFalse($secretNested->canView());
|
|
|
|
$this->assertFalse($protected->canView());
|
|
|
|
$this->assertFalse($protectedChild->canView());
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Editor can view pages restricted to logged in users
|
|
|
|
$this->assertTrue($secret->canView($editor));
|
|
|
|
$this->assertTrue($secretNested->canView($editor));
|
|
|
|
|
|
|
|
// Cannot read admin-only pages
|
|
|
|
$this->assertFalse($protected->canView($editor));
|
|
|
|
$this->assertFalse($protectedChild->canView($editor));
|
|
|
|
|
|
|
|
// Check root permissions
|
|
|
|
$this->assertTrue($history->canView($editor));
|
|
|
|
|
|
|
|
TestPermissionNode::getInheritedPermissions()->clearCache();
|
|
|
|
$this->rootPermissions->setCanView(false);
|
|
|
|
|
|
|
|
$this->assertFalse($history->canView($editor));
|
|
|
|
}
|
|
|
|
|
2018-02-19 23:37:27 +01:00
|
|
|
public function testUnstagedViewPermissions()
|
|
|
|
{
|
|
|
|
$history = $this->objFromFixture(UnstagedNode::class, 'history');
|
|
|
|
$contact = $this->objFromFixture(UnstagedNode::class, 'contact');
|
|
|
|
$contactForm = $this->objFromFixture(UnstagedNode::class, 'contact-form');
|
|
|
|
$secret = $this->objFromFixture(UnstagedNode::class, 'secret');
|
|
|
|
$secretNested = $this->objFromFixture(UnstagedNode::class, 'secret-nested');
|
|
|
|
$protected = $this->objFromFixture(UnstagedNode::class, 'protected');
|
|
|
|
$protectedChild = $this->objFromFixture(UnstagedNode::class, 'protected-child');
|
|
|
|
$editor = $this->objFromFixture(Member::class, 'editor');
|
|
|
|
|
|
|
|
// Not logged in user can only access Inherit or Anyone pages
|
|
|
|
Member::actAs(
|
|
|
|
null,
|
|
|
|
function () use ($protectedChild, $secretNested, $protected, $secret, $history, $contact, $contactForm) {
|
|
|
|
$this->assertTrue($history->canView());
|
|
|
|
$this->assertTrue($contact->canView());
|
|
|
|
$this->assertTrue($contactForm->canView());
|
|
|
|
// Protected
|
|
|
|
$this->assertFalse($secret->canView());
|
|
|
|
$this->assertFalse($secretNested->canView());
|
|
|
|
$this->assertFalse($protected->canView());
|
|
|
|
$this->assertFalse($protectedChild->canView());
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
// Editor can view pages restricted to logged in users
|
|
|
|
$this->assertTrue($secret->canView($editor));
|
|
|
|
$this->assertTrue($secretNested->canView($editor));
|
|
|
|
|
|
|
|
// Cannot read admin-only pages
|
|
|
|
$this->assertFalse($protected->canView($editor));
|
|
|
|
$this->assertFalse($protectedChild->canView($editor));
|
|
|
|
|
|
|
|
// Check root permissions
|
|
|
|
$this->assertTrue($history->canView($editor));
|
|
|
|
|
|
|
|
UnstagedNode::getInheritedPermissions()->clearCache();
|
|
|
|
$this->rootPermissions->setCanView(false);
|
|
|
|
|
|
|
|
$this->assertFalse($history->canView($editor));
|
|
|
|
}
|
|
|
|
|
2017-05-11 11:07:27 +02:00
|
|
|
/**
|
|
|
|
* Test that draft permissions deny unrestricted live permissions
|
|
|
|
*/
|
|
|
|
public function testRestrictedDraftUnrestrictedLive()
|
|
|
|
{
|
|
|
|
Versioned::set_stage(Versioned::DRAFT);
|
|
|
|
|
|
|
|
// Should be editable by non-admin editor
|
|
|
|
/** @var TestPermissionNode $products */
|
|
|
|
$products = $this->objFromFixture(TestPermissionNode::class, 'products');
|
|
|
|
/** @var TestPermissionNode $products1 */
|
|
|
|
$products1 = $this->objFromFixture(TestPermissionNode::class, 'products-product1');
|
|
|
|
$editor = $this->objFromFixture(Member::class, 'editor');
|
|
|
|
|
|
|
|
// Ensure the editor can edit
|
|
|
|
$this->assertTrue($products->canEdit($editor));
|
|
|
|
$this->assertTrue($products1->canEdit($editor));
|
|
|
|
|
|
|
|
// Write current version to live
|
|
|
|
$products->writeToStage(Versioned::LIVE);
|
|
|
|
$products1->writeToStage(Versioned::LIVE);
|
|
|
|
|
|
|
|
// Draft version restrict to admins
|
|
|
|
$products->EditorGroups()->setByIDList([
|
|
|
|
$this->idFromFixture(Group::class, 'admins')
|
|
|
|
]);
|
|
|
|
$products->write();
|
|
|
|
|
|
|
|
// Ensure editor can no longer edit
|
|
|
|
TestPermissionNode::getInheritedPermissions()->clearCache();
|
|
|
|
$this->assertFalse($products->canEdit($editor));
|
|
|
|
$this->assertFalse($products1->canEdit($editor));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that draft permissions permit access over live permissions
|
|
|
|
*/
|
|
|
|
public function testUnrestrictedDraftOverridesLive()
|
|
|
|
{
|
|
|
|
Versioned::set_stage(Versioned::DRAFT);
|
|
|
|
|
|
|
|
// Should be editable by non-admin editor
|
|
|
|
/** @var TestPermissionNode $about */
|
|
|
|
$about = $this->objFromFixture(TestPermissionNode::class, 'about');
|
|
|
|
/** @var TestPermissionNode $aboutStaff */
|
|
|
|
$aboutStaff = $this->objFromFixture(TestPermissionNode::class, 'about-staff');
|
|
|
|
$editor = $this->objFromFixture(Member::class, 'editor');
|
|
|
|
|
|
|
|
// Ensure the editor can't edit
|
|
|
|
$this->assertFalse($about->canEdit($editor));
|
|
|
|
$this->assertFalse($aboutStaff->canEdit($editor));
|
|
|
|
|
|
|
|
// Write current version to live
|
|
|
|
$about->writeToStage(Versioned::LIVE);
|
|
|
|
$aboutStaff->writeToStage(Versioned::LIVE);
|
|
|
|
|
|
|
|
// Unrestrict draft
|
|
|
|
$about->CanEditType = InheritedPermissions::LOGGED_IN_USERS;
|
|
|
|
$about->write();
|
|
|
|
|
|
|
|
// Ensure editor can no longer edit
|
|
|
|
TestPermissionNode::getInheritedPermissions()->clearCache();
|
|
|
|
$this->assertTrue($about->canEdit($editor));
|
|
|
|
$this->assertTrue($aboutStaff->canEdit($editor));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ensure that flipping parent / child relationship on live doesn't
|
|
|
|
* cause infinite loop
|
|
|
|
*/
|
|
|
|
public function testMobiusHierarchy()
|
|
|
|
{
|
|
|
|
Versioned::set_stage(Versioned::DRAFT);
|
|
|
|
|
|
|
|
/** @var TestPermissionNode $history */
|
|
|
|
$history = $this->objFromFixture(TestPermissionNode::class, 'history');
|
|
|
|
/** @var TestPermissionNode $historyGallery */
|
|
|
|
$historyGallery = $this->objFromFixture(TestPermissionNode::class, 'history-gallery');
|
|
|
|
|
|
|
|
// Publish current state to live
|
|
|
|
$history->writeToStage(Versioned::LIVE);
|
|
|
|
$historyGallery->writeToStage(Versioned::LIVE);
|
|
|
|
|
|
|
|
// Flip relation
|
|
|
|
$historyGallery->ParentID = 0;
|
|
|
|
$historyGallery->write();
|
|
|
|
$history->ParentID = $historyGallery->ID;
|
|
|
|
$history->write();
|
|
|
|
|
|
|
|
// Test viewability (not logged in users)
|
|
|
|
Member::actAs(null, function () use ($history, $historyGallery) {
|
|
|
|
$this->assertTrue($history->canView());
|
|
|
|
$this->assertTrue($historyGallery->canView());
|
|
|
|
});
|
|
|
|
|
|
|
|
// Change permission on draft root and ensure it affects both
|
|
|
|
$historyGallery->CanViewType = InheritedPermissions::LOGGED_IN_USERS;
|
|
|
|
$historyGallery->write();
|
|
|
|
TestPermissionNode::getInheritedPermissions()->clearCache();
|
|
|
|
|
|
|
|
// Test viewability (not logged in users)
|
|
|
|
Member::actAs(null, function () use ($history, $historyGallery) {
|
|
|
|
$this->assertFalse($historyGallery->canView());
|
|
|
|
$this->assertFalse($history->canView());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|