diff --git a/security/PermissionRole.php b/security/PermissionRole.php index 3f2dd0da1..3626ab478 100644 --- a/security/PermissionRole.php +++ b/security/PermissionRole.php @@ -62,4 +62,16 @@ class PermissionRole extends DataObject { $code->delete(); } } + + public function canCreate($member = null) { + return Permission::check('APPLY_ROLES', 'any', $member); + } + + public function canEdit($member = null) { + return Permission::check('APPLY_ROLES', 'any', $member); + } + + public function canDelete($member = null) { + return Permission::check('APPLY_ROLES', 'any', $member); + } } diff --git a/security/PermissionRoleCode.php b/security/PermissionRoleCode.php index 37a916df9..756a2622a 100644 --- a/security/PermissionRoleCode.php +++ b/security/PermissionRoleCode.php @@ -13,4 +13,38 @@ class PermissionRoleCode extends DataObject { static $has_one = array( "Role" => "PermissionRole", ); -} \ No newline at end of file + + protected function validate() { + $result = parent::validate(); + + // Check that new code doesn't increase privileges, unless an admin is editing. + $privilegedCodes = Permission::$privileged_permissions; + if( + $this->Code + && in_array($this->Code, $privilegedCodes) + && !Permission::check('ADMIN') + ) { + $result->error(sprintf( + _t( + 'PermissionRoleCode.PermsError', + 'Can\'t assign code "%s" with privileged permissions (requires ADMIN access)' + ), + $this->Code + )); + } + + return $result; + } + + public function canCreate($member = null) { + return Permission::check('APPLY_ROLES', 'any', $member); + } + + public function canEdit($member = null) { + return Permission::check('APPLY_ROLES', 'any', $member); + } + + public function canDelete($member = null) { + return Permission::check('APPLY_ROLES', 'any', $member); + } +} diff --git a/tests/security/PermissionRoleTest.php b/tests/security/PermissionRoleTest.php index 3c389078e..9d220c037 100644 --- a/tests/security/PermissionRoleTest.php +++ b/tests/security/PermissionRoleTest.php @@ -14,4 +14,35 @@ class PermissionRoleTest extends FunctionalTest { $this->assertNull(DataObject::get('PermissionRole', "\"ID\"={$role->ID}"), 'Role is removed'); $this->assertNull(DataObject::get('PermissionRoleCode',"\"RoleID\"={$role->ID}"), 'Permissions removed along with the role'); } + + public function testValidatesPrivilegedPermissions() { + $nonAdminCode = new PermissionRoleCode(array('Code' => 'CMS_ACCESS_CMSMain')); + $nonAdminValidateMethod = new ReflectionMethod($nonAdminCode, 'validate'); + $nonAdminValidateMethod->setAccessible(true); + + $adminCode = new PermissionRoleCode(array('Code' => 'ADMIN')); + $adminValidateMethod = new ReflectionMethod($adminCode, 'validate'); + $adminValidateMethod->setAccessible(true); + + $this->logInWithPermission('APPLY_ROLES'); + $result = $nonAdminValidateMethod->invoke($nonAdminCode); + $this->assertTrue( + $result->valid(), + 'Members with only APPLY_ROLES can create non-privileged permission role codes' + ); + + $this->logInWithPermission('APPLY_ROLES'); + $result = $adminValidateMethod->invoke($adminCode); + $this->assertFalse( + $result->valid(), + 'Members with only APPLY_ROLES can\'t create privileged permission role codes' + ); + + $this->logInWithPermission('ADMIN'); + $result = $adminValidateMethod->invoke($adminCode); + $this->assertTrue( + $result->valid(), + 'Members with ADMIN can create privileged permission role codes' + ); + } }