diff --git a/app/controllers/access_permissions/projects_controller.rb b/app/controllers/access_permissions/projects_controller.rb index 70c5c3921..976af9f97 100644 --- a/app/controllers/access_permissions/projects_controller.rb +++ b/app/controllers/access_permissions/projects_controller.rb @@ -37,6 +37,14 @@ module AccessPermissions user_id: permitted_update_params[:user_id], team: current_team ) + + # prevent role change if it would result in no users having the user management permission + new_user_role = UserRole.find(permitted_update_params[:user_role_id]) + if !new_user_role.has_permission?(ProjectPermissions::USERS_MANAGE) && + @user_assignment.last_with_permission?(ProjectPermissions::USERS_MANAGE) + raise ActiveRecord::RecordInvalid + end + @user_assignment.update!(permitted_update_params) log_activity(:change_user_role_on_project, @user_assignment) @@ -103,6 +111,9 @@ module AccessPermissions user = @project.assigned_users.find(params[:user_id]) user_assignment = @project.user_assignments.find_by(user: user, team: current_team) + # prevent deletion of last user that can manage users + raise ActiveRecord::RecordInvalid if user_assignment.last_with_permission?(ProjectPermissions::USERS_MANAGE) + if @project.visible? user_assignment.update!( user_role: @project.default_public_user_role, diff --git a/app/controllers/access_permissions/protocols_controller.rb b/app/controllers/access_permissions/protocols_controller.rb index bd7399605..debd8d4ba 100644 --- a/app/controllers/access_permissions/protocols_controller.rb +++ b/app/controllers/access_permissions/protocols_controller.rb @@ -35,6 +35,14 @@ module AccessPermissions user_id: permitted_update_params[:user_id], team: current_team ) + + # prevent role change if it would result in no users having the user management permission + new_user_role = UserRole.find(permitted_update_params[:user_role_id]) + if !new_user_role.has_permission?(ProtocolPermissions::USERS_MANAGE) && + @user_assignment.last_with_permission?(ProtocolPermissions::USERS_MANAGE) + raise ActiveRecord::RecordInvalid + end + @user_assignment.update!(permitted_update_params) log_activity(:protocol_template_access_changed, @user_assignment) @@ -88,6 +96,9 @@ module AccessPermissions user = @protocol.assigned_users.find(params[:user_id]) user_assignment = @protocol.user_assignments.find_by(user: user, team: current_team) + # prevent deletion of last user that can manage users + raise ActiveRecord::RecordInvalid if user_assignment.last_with_permission?(ProtocolPermissions::USERS_MANAGE) + Protocol.transaction do if @protocol.visible? user_assignment.update!( diff --git a/app/models/user_assignment.rb b/app/models/user_assignment.rb index 9b54a5479..ccd6a35cf 100644 --- a/app/models/user_assignment.rb +++ b/app/models/user_assignment.rb @@ -24,6 +24,15 @@ class UserAssignment < ApplicationRecord assignable_owners.count == 1 && user_role.owner? end + def last_with_permission?(permission) + return false if user_role.permissions.exclude?(permission) + + assignable.user_assignments.joins(:user_role) + .where.not(user: user) + .where('? = ANY(user_roles.permissions)', permission) + .none? + end + private def call_user_assignment_changed_hook diff --git a/app/models/user_role.rb b/app/models/user_role.rb index f96ab16a2..836f20592 100644 --- a/app/models/user_role.rb +++ b/app/models/user_role.rb @@ -61,6 +61,10 @@ class UserRole < ApplicationRecord predefined.find_by(name: UserRole.public_send('viewer_role').name) end + def has_permission?(permission) + permissions.include?(permission) + end + def owner? predefined? && name == I18n.t('user_roles.predefined.owner') end