From fedf702808be8f1a115cdc4a9c8f0253e12f8c7e Mon Sep 17 00:00:00 2001 From: Joseph Snyder Date: Thu, 9 Jan 2025 14:51:28 -0500 Subject: [PATCH] Start creating new permissions for registration Introduce a new setting that determines what registration forms are shown as the pages are accessed: PUBLIC (or not set) : All registration forms are available and non-users can register an account PROJECTADMIN: The public form is hidden and users must be added via the "Manage project users" or "Manage users" by someone with Project Administrator priviliges or highter. ADMIN: Only the Site administrators may register a new account via the two available forms. DISABLED: All registration forms are disabled and registration must occur through a different means (via OAuth/LDAP). --- .env.example | 7 + app/Enums/RegistrationPermissionsLevel.php | 11 ++ .../ManageProjectRolesController.php | 6 +- app/Policies/UserPolicy.php | 35 ++++ config/auth.php | 7 +- phpstan-baseline.neon | 10 + resources/views/admin/manage-users.blade.php | 176 +++++++++--------- resources/views/components/header.blade.php | 7 +- tests/Environments/.env.ADMIN | 21 +++ tests/Environments/.env.DISABLED | 21 +++ tests/Environments/.env.PROJECT_ADMIN | 21 +++ tests/Environments/.env.PUBLIC | 21 +++ tests/cypress/e2e/CMakeLists.txt | 3 + .../cypress/e2e/registration-permission.cy.js | 94 ++++++++++ 14 files changed, 344 insertions(+), 96 deletions(-) create mode 100644 app/Enums/RegistrationPermissionsLevel.php create mode 100644 app/Policies/UserPolicy.php create mode 100644 tests/Environments/.env.ADMIN create mode 100644 tests/Environments/.env.DISABLED create mode 100644 tests/Environments/.env.PROJECT_ADMIN create mode 100644 tests/Environments/.env.PUBLIC create mode 100644 tests/cypress/e2e/registration-permission.cy.js diff --git a/.env.example b/.env.example index ab43017eba..f7dfc780cd 100755 --- a/.env.example +++ b/.env.example @@ -237,6 +237,13 @@ MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" # Whether or not a Project administrator can register a user # PROJECT_ADMIN_REGISTRATION_FORM_ENABLED=true +# The minimum permission level able to register a user. +# Leaving this value blank will disable registration forms, unless a value can be +# generated from USER_REGISTRATION_FORM_ENABLED and PROJECT_ADMIN_REGISTRATION_FORM_ENABLED + +# Options: PUBLIC, PROJECT_ADMIN, ADMIN, DISABLED +# USER_REGISTRATION_ACCESS_LEVEL_REQUIRED=PUBLIC + # Require all new projects to use authenticated submissions. # Instance administrators can override this, and this setting has no effect on # existing projects. diff --git a/app/Enums/RegistrationPermissionsLevel.php b/app/Enums/RegistrationPermissionsLevel.php new file mode 100644 index 0000000000..0a2ef605ef --- /dev/null +++ b/app/Enums/RegistrationPermissionsLevel.php @@ -0,0 +1,11 @@ +admin) { + if ($current_user->can('create', [User::class, EloquentProject::find($projectid)])) { $xml .= add_XML_value('canRegister', '1'); } $xml .= ''; @@ -426,7 +428,7 @@ private static function find_site_maintainers(int $projectid): array private function register_user($projectid, $email, $firstName, $lastName, $repositoryCredential) { - if (config('auth.project_admin_registration_form_enabled') === false) { + if (Gate::authorize('create', [User::class, EloquentProject::findOrFail($projectid)])->denied()) { return 'Users cannot be registered via this form at the current time.'; } diff --git a/app/Policies/UserPolicy.php b/app/Policies/UserPolicy.php new file mode 100644 index 0000000000..41171a5079 --- /dev/null +++ b/app/Policies/UserPolicy.php @@ -0,0 +1,35 @@ +value : ((bool) env('PROJECT_ADMIN_REGISTRATION_FORM_ENABLED', true) ? RegistrationPermissionsLevel::PROJECT_ADMIN->value : RegistrationPermissionsLevel::ADMIN->value); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + $user_permission_level = Project::whereRelation('administrators', 'users.id', request()->user()?->id)->exists() ? 1 : 0; + $user_permission_level = $user->admin ? 2 : $user_permission_level; + $registration_permission_level_required = match (Str::upper(config('auth.user_registration_access_level_required'))) { + 'PUBLIC' => RegistrationPermissionsLevel::PUBLIC->value, + 'PROJECT_ADMIN' => RegistrationPermissionsLevel::PROJECT_ADMIN->value, + 'ADMIN' => RegistrationPermissionsLevel::ADMIN->value, + 'DISABLED' => RegistrationPermissionsLevel::DISABLED->value, + default => $this->AttemptValueBuild(), + }; + + // Fail if the caller is requesting a value that the setting disallows + return $user_permission_level >= $registration_permission_level_required; + } +} diff --git a/config/auth.php b/config/auth.php index 17ebbf1a5a..0d636e0157 100755 --- a/config/auth.php +++ b/config/auth.php @@ -6,10 +6,9 @@ return [ // Whether or not "normal" username+password authentication is enabled 'username_password_authentication_enabled' => env('USERNAME_PASSWORD_AUTHENTICATION_ENABLED', true), - // Whether or not "normal" username+password authentication is enabled - 'user_registration_form_enabled' => env('USER_REGISTRATION_FORM_ENABLED', true), - // Whether or not a Project administrator can register a user - 'project_admin_registration_form_enabled' => env('PROJECT_ADMIN_REGISTRATION_FORM_ENABLED', true), + // Which level of permissions can register a user + // supported: PUBLIC,PROJECT_ADMIN,ADMIN,"" + 'user_registration_access_level_required' => env('USER_REGISTRATION_ACCESS_LEVEL_REQUIRED', (bool) env('USER_REGISTRATION_FORM_ENABLED', true) ? 'PUBLIC' : ((bool) env('PROJECT_ADMIN_REGISTRATION_FORM_ENABLED', true) ? 'PROJECT_ADMIN' : 'ADMIN')), /* |-------------------------------------------------------------------------- diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 581e368616..516a5b5b97 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -3719,6 +3719,16 @@ parameters: count: 2 path: app/Policies/ProjectPolicy.php + - + message: "#^Dynamic call to static method Illuminate\\\\Database\\\\Eloquent\\\\Builder\\\\:\\:exists\\(\\)\\.$#" + count: 1 + path: app/Policies/UserPolicy.php + + - + message: "#^Parameter \\#1 \\$value of static method Illuminate\\\\Support\\\\Str\\:\\:upper\\(\\) expects string, mixed given\\.$#" + count: 1 + path: app/Policies/UserPolicy.php + - message: "#^Dynamic call to static method Illuminate\\\\Foundation\\\\Application\\:\\:isProduction\\(\\)\\.$#" count: 1 diff --git a/resources/views/admin/manage-users.blade.php b/resources/views/admin/manage-users.blade.php index 5e4bee0a6c..2ea77c84f4 100644 --- a/resources/views/admin/manage-users.blade.php +++ b/resources/views/admin/manage-users.blade.php @@ -40,93 +40,95 @@ > - - - -
- - - - - - Add new user - - - - -
First Name:
- - - - - - - -
Last Name:
- - - - - - - -
Email:
- - - - - - - -
Password:
- - - - - - - - - -
Confirm Password:
- - - - - - - -
Institution:
- - - - - - - - - - (password will be displayed in clear text upon addition) - - + @can("create", App\Models\User::class) + + + +
+ + + + + + Add new user + + + + +
First Name:
+ + + + + + + +
Last Name:
+ + + + + + + +
Email:
+ + + + + + + +
Password:
+ + + + + + + + + +
Confirm Password:
+ + + + + + + +
Institution:
+ + + + + + + + + + (password will be displayed in clear text upon addition) + + + @endcan diff --git a/resources/views/components/header.blade.php b/resources/views/components/header.blade.php index 6e1ba51993..725028e3ae 100755 --- a/resources/views/components/header.blade.php +++ b/resources/views/components/header.blade.php @@ -2,9 +2,10 @@ if (isset($project)) { $logoid = $project->ImageId; } -$hideRegistration = config('auth.user_registration_form_enabled') === false; -@endphp + use Illuminate\Support\Str; + $canRegister = Str::upper(config('auth.user_registration_access_level_required')) === "PUBLIC"; +@endphp