mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
196 lines
6.7 KiB
Ruby
196 lines
6.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
#-- copyright
|
|
# OpenProject is an open source project management software.
|
|
# Copyright (C) the OpenProject GmbH
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License version 3.
|
|
#
|
|
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
|
|
# Copyright (C) 2006-2013 Jean-Philippe Lang
|
|
# Copyright (C) 2010-2013 the ChiliProject Team
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# See COPYRIGHT and LICENSE files for more details.
|
|
#++
|
|
|
|
module Authorization
|
|
class UserPermissibleService
|
|
attr_accessor :user
|
|
|
|
def initialize(user)
|
|
@user = user
|
|
end
|
|
|
|
def allowed_globally?(permission)
|
|
perms = contextual_permissions(permission, :global)
|
|
return false unless authorizable_user?
|
|
return true if admin_and_all_granted_to_admin?(perms)
|
|
|
|
cached_permissions(nil).intersect?(perms.map(&:name))
|
|
end
|
|
|
|
def allowed_in_project?(permission, projects_to_check)
|
|
permissions = contextual_permissions(permission, :project)
|
|
return false if projects_to_check.blank?
|
|
return false unless authorizable_user?
|
|
|
|
Array(projects_to_check).all? do |project|
|
|
allowed_in_single_project?(permissions, project)
|
|
end
|
|
end
|
|
|
|
def allowed_in_any_project?(permission)
|
|
permissions = contextual_permissions(permission, :project)
|
|
return false unless authorizable_user?
|
|
|
|
cached_in_any_project?(permissions)
|
|
end
|
|
|
|
def allowed_in_entity?(permission, entities_to_check, entity_class)
|
|
return false if entities_to_check.blank?
|
|
return false unless authorizable_user?
|
|
|
|
permissions = contextual_permissions(permission, context_name(entity_class))
|
|
|
|
entities = Array(entities_to_check)
|
|
|
|
entities.all? do |entity|
|
|
allowed_in_single_entity?(permissions, entity, entity_class)
|
|
end
|
|
end
|
|
|
|
def allowed_in_any_entity?(permission, entity_class, in_project: nil)
|
|
perms = contextual_permissions(permission, context_name(entity_class))
|
|
return false unless authorizable_user?
|
|
return false if in_project && !(in_project.active? || in_project.being_archived?)
|
|
|
|
if entity_is_project_scoped?(entity_class)
|
|
allowed_in_any_project_scoped_entity?(perms, entity_class, in_project:)
|
|
else
|
|
allowed_in_any_standalone_entity?(perms, entity_class)
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def cached_permissions(context)
|
|
@cached_permissions ||= Hash.new do |hash, context_key|
|
|
hash[context_key] = user.all_permissions_for(context_key)
|
|
end
|
|
|
|
@cached_permissions[context]
|
|
end
|
|
|
|
def cached_in_any_project?(permissions)
|
|
@any_project_cache ||= Hash.new do |hash, perm|
|
|
hash[perm] = Project.allowed_to(user, perm).exists?
|
|
end
|
|
|
|
permissions.any? { |permission| @any_project_cache[permission] }
|
|
end
|
|
|
|
def allowed_in_single_project?(permissions, project)
|
|
return false if project.nil?
|
|
return false unless project.active? || project.being_archived?
|
|
|
|
permissions_filtered_for_project = permissions_by_enabled_project_modules(project, permissions)
|
|
|
|
return false if permissions_filtered_for_project.empty?
|
|
return true if admin_and_all_granted_to_admin?(permissions)
|
|
|
|
cached_permissions(project).intersect?(permissions_filtered_for_project)
|
|
end
|
|
|
|
def allowed_in_single_entity?(permissions, entity, entity_class)
|
|
if entity_is_project_scoped?(entity_class)
|
|
allowed_in_single_project_scoped_entity?(permissions, entity)
|
|
else
|
|
allowed_in_single_standalone_entity?(permissions, entity)
|
|
end
|
|
end
|
|
|
|
def allowed_in_single_project_scoped_entity?(permissions, entity)
|
|
return false if entity.nil?
|
|
return false if entity.project.nil?
|
|
return false unless entity.project.active? || entity.project.being_archived?
|
|
|
|
permissions_filtered_for_project = permissions_by_enabled_project_modules(entity.project, permissions)
|
|
|
|
return false if permissions_filtered_for_project.empty?
|
|
|
|
# The combination of this is better then doing
|
|
# EntityClass.allowed_to(user, permission).exists?.
|
|
# Because this way, all permissions for that context are fetched and cached.
|
|
allowed_in_single_project?(permissions, entity.project) ||
|
|
cached_permissions(entity).intersect?(permissions_filtered_for_project)
|
|
end
|
|
|
|
def allowed_in_single_standalone_entity?(permissions, entity)
|
|
return false if entity.nil?
|
|
return true if admin_and_all_granted_to_admin?(permissions)
|
|
|
|
permission_names = permissions.map { |perm| perm.name.to_sym }
|
|
|
|
cached_permissions(entity).intersect?(permission_names)
|
|
end
|
|
|
|
def allowed_in_any_project_scoped_entity?(permissions, entity_class, in_project:)
|
|
# entity_class.allowed_to will also check whether the user has the permission via a membership in the project.
|
|
# ^-- still a problem in some cases
|
|
allowed_scope = entity_class.allowed_to(user, permissions)
|
|
|
|
if in_project
|
|
allowed_in_single_project?(permissions, in_project) || allowed_scope.exists?(project: in_project)
|
|
else
|
|
allowed_in_any_project?(permissions) || allowed_scope.exists?
|
|
end
|
|
end
|
|
|
|
def allowed_in_any_standalone_entity?(permissions, entity_class)
|
|
entity_class.allowed_to(user, permissions).exists?
|
|
end
|
|
|
|
def admin_and_all_granted_to_admin?(permissions)
|
|
user.active_admin? && permissions.all?(&:grant_to_admin?)
|
|
end
|
|
|
|
def authorizable_user?
|
|
!(user.locked? || user.deleted?) || user.is_a?(SystemUser)
|
|
end
|
|
|
|
def permissions_by_enabled_project_modules(project, permissions)
|
|
project
|
|
.allowed_permissions
|
|
.intersection(permissions.map(&:name))
|
|
.map { |perm| perm.name.to_sym }
|
|
end
|
|
|
|
def contextual_permissions(permission, context)
|
|
Authorization.contextual_permissions(permission, context, raise_on_unknown: true)
|
|
end
|
|
|
|
def context_name(entity_class)
|
|
entity_class.model_name.element.to_sym
|
|
end
|
|
|
|
def entity_is_project_scoped?(entity_class)
|
|
entity_class.reflect_on_association(:project).present?
|
|
end
|
|
end
|
|
end
|