From 3b90225c5a648aac336d223ca535f7d67150d3d6 Mon Sep 17 00:00:00 2001 From: Oleksii Kriuchykhin Date: Tue, 9 Feb 2021 10:45:12 +0100 Subject: [PATCH] Add new permission checking logic [SCI-5436] --- .../concerns/permission_checkable_model.rb | 29 +++++++++ app/models/experiment.rb | 6 +- app/models/my_module.rb | 5 ++ app/models/project.rb | 7 +++ app/permissions/experiment.rb | 11 ++-- app/permissions/project.rb | 20 +++--- .../extends/permission_extends.rb | 62 +++++++++++++++++++ 7 files changed, 124 insertions(+), 16 deletions(-) create mode 100644 app/models/concerns/permission_checkable_model.rb create mode 100644 config/initializers/extends/permission_extends.rb diff --git a/app/models/concerns/permission_checkable_model.rb b/app/models/concerns/permission_checkable_model.rb new file mode 100644 index 000000000..420dd7e89 --- /dev/null +++ b/app/models/concerns/permission_checkable_model.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module PermissionCheckableModel + extend ActiveSupport::Concern + + def permission_granted?(user, permission) + user_role_permissions = load_user_role_permissions(user) + return false if user_role_permissions.blank? + + user_role_permissions.include?(permission) + end + + private + + def load_user_role_permissions(user) + user_role_permissions = + if user_assignments.loaded? + user_assignments.detect { |user_assignment| user_assignment.user == user }&.user_role&.permissions + else + user_assignments.find_by(user: user)&.user_role&.permissions + end + + if user_role_permissions.blank? && permission_parent.present? + user_role_permissions = permission_parent.load_user_role_permissions(user) + end + + user_role_permissions + end +end diff --git a/app/models/experiment.rb b/app/models/experiment.rb index fa02d9cf9..27a5a64bf 100644 --- a/app/models/experiment.rb +++ b/app/models/experiment.rb @@ -2,6 +2,7 @@ class Experiment < ApplicationRecord include ArchivableModel include SearchableModel include SearchableByNameModel + include PermissionCheckableModel belongs_to :project, inverse_of: :experiments, touch: true belongs_to :created_by, @@ -24,9 +25,10 @@ class Experiment < ApplicationRecord has_many :activities, inverse_of: :experiment has_many :user_assignments, as: :assignable, dependent: :destroy has_many :users, through: :user_assignments - has_one_attached :workflowimg + alias_attribute :project, :permission_parent + auto_strip_attributes :name, :description, nullify: false validates :name, length: { minimum: Constants::NAME_MIN_LENGTH, maximum: Constants::NAME_MAX_LENGTH } validates :description, length: { maximum: Constants::TEXT_MAX_LENGTH } @@ -36,6 +38,8 @@ class Experiment < ApplicationRecord validates :uuid, uniqueness: { scope: :project }, unless: proc { |e| e.uuid.blank? } + default_scope { includes(user_assignments: :user_role) } + scope :is_archived, lambda { |is_archived| if is_archived joins(:project).where('experiments.archived = TRUE OR projects.archived = TRUE') diff --git a/app/models/my_module.rb b/app/models/my_module.rb index 183b5e194..6cbeeb1bc 100644 --- a/app/models/my_module.rb +++ b/app/models/my_module.rb @@ -5,6 +5,7 @@ class MyModule < ApplicationRecord include SearchableModel include SearchableByNameModel include TinyMceImages + include PermissionCheckableModel enum state: Extends::TASKS_STATES @@ -58,6 +59,10 @@ class MyModule < ApplicationRecord # Associations for old activity type has_many :activities, inverse_of: :my_module + alias_attribute :experiment, :permission_parent + + default_scope { includes(user_assignments: :user_role) } + scope :overdue, -> { where('my_modules.due_date < ?', Time.current.utc) } scope :without_group, -> { active.where(my_module_group: nil) } scope :one_day_prior, (lambda do diff --git a/app/models/project.rb b/app/models/project.rb index 63e9241cb..ab81e6bd8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -3,6 +3,7 @@ class Project < ApplicationRecord include SearchableModel include SearchableByNameModel include ViewableModel + include PermissionCheckableModel enum visibility: { hidden: 0, visible: 1 } @@ -46,6 +47,8 @@ class Project < ApplicationRecord has_many :reports, inverse_of: :project, dependent: :destroy has_many :report_elements, inverse_of: :project, dependent: :destroy + default_scope { includes(user_assignments: :user_role) } + scope :visible_to, (lambda do |user, team| unless user.is_admin_of_team?(team) left_outer_joins(:user_projects) @@ -152,6 +155,10 @@ class Project < ApplicationRecord .distinct end + def permission_parent + nil + end + def default_view_state { experiments: { diff --git a/app/permissions/experiment.rb b/app/permissions/experiment.rb index 9cf01cf12..a68eb54b1 100644 --- a/app/permissions/experiment.rb +++ b/app/permissions/experiment.rb @@ -16,7 +16,7 @@ Canaid::Permissions.register_for(Experiment) do # module: read (read users, read comments, read archive) # result: read (read comments) can :read_experiment do |user, experiment| - can_read_project?(user, experiment.project) + experiment.permission_granted?(user, ExperimentPermissions::READ) end # experiment: create/update/delete @@ -24,7 +24,7 @@ Canaid::Permissions.register_for(Experiment) do # module: create, copy, reposition, create/update/delete connection, # assign/reassign/unassign tags can :manage_experiment do |user, experiment| - user.is_user_or_higher_of_project?(experiment.project) && + experiment.permission_granted?(user, ExperimentPermissions::MANAGE) && MyModule.joins(:experiment) .where(experiment: experiment) .preload(my_module_status: :my_module_status_implications) @@ -39,7 +39,7 @@ Canaid::Permissions.register_for(Experiment) do # experiment: archive can :archive_experiment do |user, experiment| - can_manage_experiment?(user, experiment) + experiment.permission_granted?(user, ExperimentPermissions::ARCHIVE) end # NOTE: Must not be dependent on canaid parmision for which we check if it's @@ -47,15 +47,14 @@ Canaid::Permissions.register_for(Experiment) do # experiment: restore can :restore_experiment do |user, experiment| project = experiment.project - user.is_user_or_higher_of_project?(project) && + experiment.permission_granted?(user, ExperimentPermissions::RESTORE) && experiment.archived? && project.active? end # experiment: copy can :clone_experiment do |user, experiment| - user.is_user_or_higher_of_project?(experiment.project) && - user.is_normal_user_or_admin_of_team?(experiment.project.team) + experiment.permission_granted?(user, ExperimentPermissions::CLONE) end # experiment: move diff --git a/app/permissions/project.rb b/app/permissions/project.rb index 84bd0243b..a42bdf89f 100644 --- a/app/permissions/project.rb +++ b/app/permissions/project.rb @@ -1,4 +1,8 @@ +# frozen_string_literal: true + Canaid::Permissions.register_for(Project) do + include PermissionExtends + # Project must be active for all the specified permissions %i(manage_project archive_project @@ -15,9 +19,7 @@ Canaid::Permissions.register_for(Project) do export_project) .each do |perm| can perm do |user, project| - user.is_member_of_project?(project) || - user.is_admin_of_team?(project.team) || - (project.visible? && user.is_member_of_team?(project.team)) + project.permission_granted?(user, ProjectPermissions::READ) end end # project: read, read activities, read comments, read users, read archive, @@ -36,7 +38,7 @@ Canaid::Permissions.register_for(Project) do # project: update/delete, assign/reassign/unassign users can :manage_project do |user, project| - user.is_owner_of_project?(project) && + project.permission_granted?(user, ProjectPermissions::MANAGE) && MyModule.joins(experiment: :project) .where(experiments: { project: project }) .preload(my_module_status: :my_module_status_implications) @@ -51,24 +53,24 @@ Canaid::Permissions.register_for(Project) do # project: archive can :archive_project do |user, project| - can_manage_project?(user, project) + project.permission_granted?(user, ProjectPermissions::ARCHIVE) end # NOTE: Must not be dependent on canaid parmision for which we check if it's # active # project: restore can :restore_project do |user, project| - user.is_owner_of_project?(project) && project.archived? + project.archived? && project.permission_granted?(user, ProjectPermissions::RESTORE) end # experiment: create can :create_experiments do |user, project| - user.is_user_or_higher_of_project?(project) + project.permission_granted?(user, ProjectPermissions::CREATE_EXPERIMENTS) end # project: create comment can :create_comments_in_project do |user, project| - user.is_technician_or_higher_of_project?(project) + project.permission_granted?(user, ProjectPermissions::CREATE_COMMENTS) end # project: create/update/delete tag @@ -90,6 +92,6 @@ Canaid::Permissions.register_for(ProjectComment) do # project: update/delete comment can :manage_comment_in_project do |user, project_comment| project_comment.project.present? && (project_comment.user == user || - user.is_owner_of_project?(project_comment.project)) + project.permission_granted?(user, ProjectPermissions::EDIT_COMMENTS)) end end diff --git a/config/initializers/extends/permission_extends.rb b/config/initializers/extends/permission_extends.rb new file mode 100644 index 000000000..687adc167 --- /dev/null +++ b/config/initializers/extends/permission_extends.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module PermissionExtends + module ProjectPermissions + %w( + READ + EXPORT + MANAGE + ARCHIVE + RESTORE + CREATE_EXPERIMENTS + CREATE_COMMENTS + EDIT_COMMENTS + DELETE_COMMENTS + MANAGE_TAGS + ).each { |permission| const_set(permission, permission.underscore) } + end + + module ExperimentPermissions + %w( + READ + MANAGE + ARCHIVE + RESTORE + CLONE + MOVE + ).each { |permission| const_set(permission, permission.underscore) } + end + + module MyModulePermissions + %w( + MANAGE + ARCHIVE + RESTORE + MOVE + MANAGE_USERS + ASSIGN_REPOSITORY_ROWS + CHANGE_FLOW_STATUS + CREATE_COMMENTS + CREATE_REPOSITORY_SNAPSHOT + MANAGE_REPOSITORY_SNAPSHOT + ).each { |permission| const_set(permission, permission.underscore) } + end + + module RepositoryPermissions + %w( + READ + MANAGE + ARCHIVE + RESTORE + SHARE + CREATE_SNAPSHOT + DELETE_SNAPSHOT + CREATE_ROW + UPDATE_ROW + DELETE_ROW + CREATE_COLUMN + UPDATE_COLUMN + DELETE_COLUMN + ).each { |permission| const_set(permission, permission.underscore) } + end +end