diff --git a/modules/backend/classes/AuthManager.php b/modules/backend/classes/AuthManager.php index d9f8cf89d..9fd62508d 100644 --- a/modules/backend/classes/AuthManager.php +++ b/modules/backend/classes/AuthManager.php @@ -31,6 +31,7 @@ class AuthManager extends RainAuthManager 'code' => null, 'label' => null, 'comment' => null, + 'roles' => null, 'order' => 500 ]; @@ -44,6 +45,11 @@ class AuthManager extends RainAuthManager */ protected $permissions = []; + /** + * @var array List of registered permission roles. + */ + protected $permissionRoles = false; + /** * @var array Cache of registered permissions. */ @@ -157,4 +163,40 @@ class AuthManager extends RainAuthManager return $tabs; } + + /** + * Returns an array of registered permissions belonging to a given role code + * @param string $role + * @return array + */ + public function listPermissionsForRole($role, $includeOrphans = true) + { + if ($this->permissionRoles === false) { + $this->permissionRoles = []; + + foreach ($this->listPermissions() as $permission) { + if ($permission->roles) { + foreach ((array) $permission->roles as $_role) { + $this->permissionRoles[$_role][$permission->code] = 1; + } + } + else { + $this->permissionRoles['*'][$permission->code] = 1; + } + } + } + + $result = $this->permissionRoles[$role] ?? []; + + if ($includeOrphans) { + $result += $this->permissionRoles['*'] ?? []; + } + + return $result; + } + + public function hasPermissionsForRole($role) + { + return !!$this->listPermissionsForRole($role, false); + } } diff --git a/modules/backend/controllers/UserRoles.php b/modules/backend/controllers/UserRoles.php index e6b9600f9..76587d1a2 100644 --- a/modules/backend/controllers/UserRoles.php +++ b/modules/backend/controllers/UserRoles.php @@ -1,5 +1,7 @@ bindEvent('page.beforeDisplay', function() { + if (!$this->user->isSuperUser()) { + return Response::make(View::make('backend::access_denied'), 403); + } + }); } /** diff --git a/modules/backend/controllers/Users.php b/modules/backend/controllers/Users.php index cc13b3c64..fbbdcc1d5 100644 --- a/modules/backend/controllers/Users.php +++ b/modules/backend/controllers/Users.php @@ -95,6 +95,7 @@ class Users extends Controller if (!$this->user->isSuperUser()) { $form->removeField('is_superuser'); + $form->removeField('role'); } /* diff --git a/modules/backend/controllers/usergroups/config_list.yaml b/modules/backend/controllers/usergroups/config_list.yaml index 00ba20707..e88b41715 100644 --- a/modules/backend/controllers/usergroups/config_list.yaml +++ b/modules/backend/controllers/usergroups/config_list.yaml @@ -7,7 +7,8 @@ list: ~/modules/backend/models/usergroup/columns.yaml modelClass: Backend\Models\UserGroup recordUrl: backend/usergroups/update/:id noRecordsMessage: backend::lang.list.no_records -recordsPerPage: 5 +recordsPerPage: 25 +showSetup: true toolbar: buttons: list_toolbar diff --git a/modules/backend/controllers/userroles/config_list.yaml b/modules/backend/controllers/userroles/config_list.yaml index 5871e58fd..520fb6e0c 100644 --- a/modules/backend/controllers/userroles/config_list.yaml +++ b/modules/backend/controllers/userroles/config_list.yaml @@ -7,7 +7,8 @@ list: ~/modules/backend/models/userrole/columns.yaml modelClass: Backend\Models\UserRole recordUrl: backend/userroles/update/:id noRecordsMessage: backend::lang.list.no_records -recordsPerPage: 5 +recordsPerPage: 25 +showSetup: true toolbar: buttons: list_toolbar diff --git a/modules/backend/controllers/users/_list_toolbar.htm b/modules/backend/controllers/users/_list_toolbar.htm index 096c4d68c..092dcd1e4 100644 --- a/modules/backend/controllers/users/_list_toolbar.htm +++ b/modules/backend/controllers/users/_list_toolbar.htm @@ -2,9 +2,11 @@ = e(trans('backend::lang.user.new')) ?> - - = e(trans('backend::lang.user.role.list_title')) ?> - + user->isSuperUser()): ?> + + = e(trans('backend::lang.user.role.list_title')) ?> + + = e(trans('backend::lang.user.group.list_title')) ?> diff --git a/modules/backend/database/migrations/2017_10_01_000010_Db_Backend_User_Roles.php b/modules/backend/database/migrations/2017_10_01_000010_Db_Backend_User_Roles.php index 4c49c3dd7..601893a45 100644 --- a/modules/backend/database/migrations/2017_10_01_000010_Db_Backend_User_Roles.php +++ b/modules/backend/database/migrations/2017_10_01_000010_Db_Backend_User_Roles.php @@ -1,5 +1,6 @@ string('code')->nullable()->index('role_code_index'); $table->text('description')->nullable(); $table->text('permissions')->nullable(); + $table->boolean('is_system')->default(0); $table->timestamps(); }); @@ -33,11 +35,12 @@ class DbBackendUserRoles extends Migration // Role not found in the users table, perform a complete migration. // Merging group permissions with the user and assigning the user // with the first available role. - if (Schema::hasColumn('backend_users', 'role_id')) { + if (!Schema::hasColumn('backend_users', 'role_id')) { Schema::table('backend_users', function (Blueprint $table) { $table->integer('role_id')->unsigned()->nullable()->index('admin_role_index'); }); + $this->createSystemUserRoles(); $this->migratePermissionsFromGroupsToRoles(); } @@ -49,6 +52,21 @@ class DbBackendUserRoles extends Migration } } + protected function createSystemUserRoles() + { + Db::table('backend_user_roles')->insert([ + 'name' => 'Publisher', + 'code' => UserRole::CODE_PUBLISHER, + 'description' => 'Site editor with access to publishing tools.', + ]); + + Db::table('backend_user_roles')->insert([ + 'name' => 'Developer', + 'code' => UserRole::CODE_DEVELOPER, + 'description' => 'Site administrator with access to developer tools.', + ]); + } + protected function migratePermissionsFromGroupsToRoles() { $groups = Db::table('backend_user_groups')->get(); @@ -66,6 +84,7 @@ class DbBackendUserRoles extends Migration try { $roles[$group->id] = Db::table('backend_user_roles')->insertGetId([ 'name' => $group->name, + 'description' => $group->description, 'permissions' => $group->permissions ?? null ]); } diff --git a/modules/backend/database/seeds/DatabaseSeeder.php b/modules/backend/database/seeds/DatabaseSeeder.php index ce9b9dfe1..7be5f9885 100644 --- a/modules/backend/database/seeds/DatabaseSeeder.php +++ b/modules/backend/database/seeds/DatabaseSeeder.php @@ -5,7 +5,6 @@ use Eloquent; class DatabaseSeeder extends Seeder { - /** * Run the database seeds. * diff --git a/modules/backend/database/seeds/SeedSetupAdmin.php b/modules/backend/database/seeds/SeedSetupAdmin.php index 64beb4be3..e161a41d8 100644 --- a/modules/backend/database/seeds/SeedSetupAdmin.php +++ b/modules/backend/database/seeds/SeedSetupAdmin.php @@ -2,11 +2,11 @@ use Seeder; use Backend\Models\User; +use Backend\Models\UserRole; use Backend\Models\UserGroup; class SeedSetupAdmin extends Seeder { - public static $email = 'admin@domain.tld'; public static $login = 'admin'; public static $password = 'admin'; @@ -25,9 +25,21 @@ class SeedSetupAdmin extends Seeder public function run() { + UserRole::create([ + 'name' => 'Publisher', + 'code' => UserRole::CODE_PUBLISHER, + 'description' => 'Site editor with access to publishing tools.', + ]); + + $role = UserRole::create([ + 'name' => 'Developer', + 'code' => UserRole::CODE_DEVELOPER, + 'description' => 'Site administrator with access to developer tools.', + ]); + $group = UserGroup::create([ 'name' => 'Owners', - 'code' => UserGroup::DEFAULT_CODE, + 'code' => UserGroup::CODE_OWNERS, 'description' => 'Default group for website owners.', 'is_new_user_default' => false ]); @@ -41,10 +53,10 @@ class SeedSetupAdmin extends Seeder 'last_name' => static::$lastName, 'permissions' => [], 'is_superuser' => true, - 'is_activated' => true + 'is_activated' => true, + 'role_id' => $role->id ]); $user->addGroup($group); } - } diff --git a/modules/backend/formwidgets/PermissionEditor.php b/modules/backend/formwidgets/PermissionEditor.php index 565ea3e68..51c66cf8e 100644 --- a/modules/backend/formwidgets/PermissionEditor.php +++ b/modules/backend/formwidgets/PermissionEditor.php @@ -12,6 +12,8 @@ use BackendAuth; */ class PermissionEditor extends FormWidgetBase { + protected $user; + public $mode; /** @@ -22,6 +24,8 @@ class PermissionEditor extends FormWidgetBase $this->fillFromConfig([ 'mode' ]); + + $this->user = BackendAuth::getUser(); } /** @@ -38,6 +42,10 @@ class PermissionEditor extends FormWidgetBase */ public function prepareVars() { + if ($this->formField->disabled) { + $this->previewMode = true; + } + $permissionsData = $this->formField->getValueFromData($this->model); if (!is_array($permissionsData)) { $permissionsData = []; @@ -54,6 +62,36 @@ class PermissionEditor extends FormWidgetBase * @inheritDoc */ public function getSaveValue($value) + { + if ($this->user->isSuperUser()) { + return is_array($value) ? $value : []; + } + + return $this->getSaveValueSecure($value); + } + + /** + * @inheritDoc + */ + protected function loadAssets() + { + $this->addCss('css/permissioneditor.css', 'core'); + $this->addJs('js/permissioneditor.js', 'core'); + } + + protected function getControlMode() + { + return strlen($this->mode) ? $this->mode : 'radio'; + } + + /** + * Returns a safely parsed set of permissions, ensuring the user cannot elevate + * their own permissions or permissions of another user above their own. + * + * @param string $value + * @return array + */ + protected function getSaveValueSecure($value) { $newPermissions = is_array($value) ? array_map('intval', $value) : []; @@ -80,20 +118,6 @@ class PermissionEditor extends FormWidgetBase return $newPermissions; } - /** - * @inheritDoc - */ - protected function loadAssets() - { - $this->addCss('css/permissioneditor.css', 'core'); - $this->addJs('js/permissioneditor.js', 'core'); - } - - protected function getControlMode() - { - return strlen($this->mode) ? $this->mode : 'radio'; - } - /** * Returns the available permissions; removing those that the logged-in user does not have access to * @@ -102,17 +126,22 @@ class PermissionEditor extends FormWidgetBase protected function getFilteredPermissions() { $permissions = BackendAuth::listTabbedPermissions(); - $user = BackendAuth::getUser(); + + if ($this->user->isSuperUser()) { + return $permissions; + } + foreach ($permissions as $tab => $permissionsArray) { foreach ($permissionsArray as $index => $permission) { - if (!$user->hasAccess($permission->code)) { + if (!$this->user->hasAccess($permission->code)) { unset($permissionsArray[$index]); } } if (empty($permissionsArray)) { unset($permissions[$tab]); - } else { + } + else { $permissions[$tab] = $permissionsArray; } } diff --git a/modules/backend/formwidgets/permissioneditor/partials/_permissioneditor.htm b/modules/backend/formwidgets/permissioneditor/partials/_permissioneditor.htm index cd28edaf7..4a6377650 100644 --- a/modules/backend/formwidgets/permissioneditor/partials/_permissioneditor.htm +++ b/modules/backend/formwidgets/permissioneditor/partials/_permissioneditor.htm @@ -1,34 +1,38 @@ -