Implement public user grouping, deletion and updates to manually assigned [SCI-6228] (#3701)

* Implement public user grouping, deletion and updates to manually assigned [SCI-6228]

* PR fixes [SCI-6228]

* Permission check improvement [SCI-6228]
This commit is contained in:
artoscinote 2021-12-01 16:23:24 +01:00 committed by GitHub
parent 62d4d8e36e
commit 78ab094bf2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 110 additions and 26 deletions

View file

@ -49,6 +49,7 @@
.user-assignment-remove {
margin-left: 1em;
min-width: 7em;
}
a,

View file

@ -4,10 +4,16 @@ module AccessPermissions
class ProjectsController < ApplicationController
before_action :set_project
before_action :check_read_permissions, only: %i(show)
before_action :check_manage_permissions, only: %i(new create edit update destroy)
before_action :check_manage_permissions, except: %i(show)
def new
available_users = current_team.users.where.not(id: @project.users.pluck(:id))
# automatically assigned or not assigned to project
available_users = current_team.users.where(
id: @project.user_assignments.automatically_assigned.select(:user_id)
).or(
current_team.users.where.not(id: @project.users.select(:id))
)
@form = AccessPermissions::NewUserProjectForm.new(
current_user,
@project,
@ -75,8 +81,17 @@ module AccessPermissions
end
end
def update_default_public_user_role
@project.update!(permitted_default_public_user_role_params)
UserAssignments::GroupAssignmentJob.perform_later(current_team, @project, current_user)
end
private
def permitted_default_public_user_role_params
params.require(:project).permit(:default_public_user_role_id)
end
def permitted_update_params
params.require(:project_member)
.permit(%i(user_role_id user_id))

View file

@ -44,7 +44,7 @@ module Api
project_member = ProjectMember.new(user, @project, current_user)
project_member.assign = true
project_member.user_role_id = user_project_params[:user_role_id]
project_member.create
project_member.save
render jsonapi: project_member.user_assignment.reload,
serializer: UserAssignmentSerializer,

View file

@ -20,7 +20,7 @@ module AccessPermissions
if @error
false
else
@resource_members.map(&:create)
@resource_members.map(&:save)
true
end
end

View file

@ -9,14 +9,19 @@ module UserAssignments
@assigned_by = assigned_by
ActiveRecord::Base.transaction do
team.users.where.not(id: project.users.pluck(:id)).where.not(id: assigned_by.id).find_each do |user|
UserAssignment.create!(
team.users.where.not(id: assigned_by.id).find_each do |user|
user_assignment = UserAssignment.find_or_initialize_by(
user: user,
assignable: project,
user_role: project.default_public_user_role,
assigned_by: @assigned_by,
assigned: :automatically
assignable: project
)
next if user_assignment.manually_assigned?
user_assignment.update!(
user_role: project.default_public_user_role,
assigned_by: @assigned_by
)
# make sure all related experiments and my modules are assigned
UserAssignments::PropagateAssignmentJob.perform_later(
project,

View file

@ -59,7 +59,20 @@ module UserAssignments
# also destroy user designations if it's a MyModule
object.user_my_modules.where(user: @user).destroy_all if object.is_a?(MyModule)
UserAssignment.where(user: @user, assignable: object).destroy_all
user_assignment = UserAssignment.find_by!(user: @user, assignable: object)
if object.project.visible?
# if project is public, the assignment
# will reset to the default public role
user_assignment.update!(
user_role_id: object.project.default_public_user_role_id,
assigned: :automatically,
assigned_by: @assigned_by
)
else
user_assignment.destroy!
end
end
end
end

View file

@ -36,6 +36,10 @@ module Assignable
user_assignments.find_by(user: user)&.user_role
end
def manually_assigned_users
User.joins(:user_assignments).where(user_assignments: { assigned: :manually, assignable: self })
end
private
def create_users_assignments

View file

@ -40,6 +40,7 @@ class MyModule < ApplicationRecord
belongs_to :archived_by, foreign_key: 'archived_by_id', class_name: 'User', optional: true
belongs_to :restored_by, foreign_key: 'restored_by_id', class_name: 'User', optional: true
belongs_to :experiment, inverse_of: :my_modules, touch: true
has_one :project, through: :experiment, autosave: false
belongs_to :my_module_group, inverse_of: :my_modules, optional: true
belongs_to :my_module_status, optional: true
belongs_to :changing_from_my_module_status, optional: true, class_name: 'MyModuleStatus'

View file

@ -11,7 +11,6 @@ class ProjectMember
validates :user, :project, presence: true, if: -> { assign }
validates :user_role_id, presence: true, if: -> { assign }
validate :validate_role_presence, if: -> { assign }
validate :validate_user_assignment_presence, if: -> { assign }
def initialize(user, project, current_user = nil)
@user = user
@ -20,17 +19,21 @@ class ProjectMember
@user_assignment = UserAssignment.find_by(assignable: @project, user: @user)
end
def create
def save
return unless assign
ActiveRecord::Base.transaction do
@user_assignment = UserAssignment.create!(
@user_assignment = UserAssignment.find_or_initialize_by(
assignable: @project,
user: @user,
user: @user
)
@user_assignment.update!(
user_role_id: user_role_id,
assigned_by: current_user,
assigned: :manually
)
log_activity(:assign_user_to_project)
UserAssignments::PropagateAssignmentJob.perform_later(
@ -66,9 +69,17 @@ class ProjectMember
return false if last_project_owner?
ActiveRecord::Base.transaction do
user_assignment.destroy!
user_project&.destroy!
log_activity(:unassign_user_from_project)
# if project is public, the assignment
# will reset to the default public role
if @project.visible?
user_assignment.update!(
user_role: @project.default_public_user_role,
assigned: :automatically
)
else
user_assignment.destroy!
user_project&.destroy!
end
UserAssignments::PropagateAssignmentJob.perform_later(
@project,
@ -77,6 +88,8 @@ class ProjectMember
current_user,
destroy: true
)
log_activity(:unassign_user_from_project)
end
end
@ -106,12 +119,6 @@ class ProjectMember
errors.add(:user_role_id, :not_found) if UserRole.find_by(id: user_role_id).nil?
end
def validate_user_assignment_presence
return if UserAssignment.find_by(assignable: @project, user: @user).nil?
errors.add(:user_role_id, :already_present)
end
def project_owners
@project_owners ||= @project.user_assignments
.includes(:user_role)

View file

@ -0,0 +1,24 @@
<%= form_with(model: project, url: update_default_public_user_role_access_permissions_project_path(project), method: :put, remote: true, html: { class: 'row member-item', id: 'public_assignments', data: { action: 'replace-form autosave-form', object_type: :project } }) do |f| %>
<div class="user-assignment-info">
<div class="global-avatar-container">
<%= image_tag "icon/team.png", class: 'img-circle pull-left' %>
</div>
<div>
<%= t('access_permissions.everyone_else', team_name: f.object.team.name) %>
<br>
<small class="text-muted">
<%= f.object.default_public_user_role.name %>
<span class="permission-object-tag" title="<%= t("access_permissions.partials.project_tooltip") %>"">
<%= t("access_permissions.partials.project") %>
</span>
</small>
</div>
</div>
<div class="user-assignment-controls">
<div class="user-assignment-role">
<%= f.select :default_public_user_role_id, options_for_select(user_roles_collection, selected: f.object.default_public_user_role_id), {}, class: 'form-control selectpicker', title: t('user_assignment.change_project_role'), data: { 'selected-text-format' => 'static' } %>
</div>
<div class="user-assignment-remove">
</div>
</div>
<% end %>

View file

@ -6,10 +6,12 @@
<h4 class="modal-title"><%= t '.title', resource_name: project.name %></h4>
</div>
<div class="modal-body">
<% project.users.each do |user| %>
<%= render('access_permissions/partials/project_member_field.html.erb', user: user, project: project, update_path: update_path) %>
<% project.manually_assigned_users.each do |user| %>
<%= render('access_permissions/partials/project_member_field', user: user, project: project, update_path: update_path) %>
<% end %>
<%= render('access_permissions/partials/default_public_user_role_form', project: project) if project.visible? %>
</div>
<div class="modal-footer">
<%= link_to new_resource_path, class: 'btn btn-default pull-left', data: { action: 'swap-remote-container', target: '#user_assignments_modal' } do %>
<i class="fas fa-plus"></i>

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
json.form controller.render_to_string(
partial: 'access_permissions/partials/default_public_user_role_form',
formats: [:html],
locals: {
project: @project
},
layout: false
)

View file

@ -2527,6 +2527,7 @@ en:
general_error: "Something went wrong"
access_permissions:
everyone_else: "Everyone else at %{team_name}"
create:
success:
one: "You have successfully granted access to %{count} member to the project."

View file

@ -261,6 +261,7 @@ Rails.application.routes.draw do
namespace :access_permissions do
resources :projects, defaults: { format: 'json' } do
put :update_default_public_user_role, on: :member
resources :experiments, only: %i(show update edit) do
resources :my_modules, only: %i(show update edit)
end