Merge pull request #10244 from opf/feature/41086-work-package-assigned-capability

Feature/41086 work package assigned capability
pull/10274/head
Oliver Günther 3 years ago committed by GitHub
commit 6ae0926fb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/contracts/roles/base_contract.rb
  2. 14
      app/models/actions/scopes/default.rb
  3. 10
      app/models/capabilities/scopes/default.rb
  4. 10
      app/models/members/scopes/assignable.rb
  5. 2
      app/models/queries/capabilities/filters/action_filter.rb
  6. 2
      app/models/role.rb
  7. 22
      app/services/authorization/project_query.rb
  8. 6
      app/services/authorization/user_allowed_query.rb
  9. 11
      app/services/authorization/user_allowed_service.rb
  10. 6
      app/views/roles/_form.html.erb
  11. 3
      app/views/roles/_member_attributes.html.erb
  12. 13
      app/views/roles/_permissions.html.erb
  13. 25
      config/initializers/permissions.rb
  14. 3
      config/locales/en.yml
  15. 30
      db/migrate/20220302123642_assignable_to_permission.rb
  16. 3
      frontend/src/global_styles/content/_forms.sass
  17. 19
      lib/open_project/access_control.rb
  18. 5
      lib/open_project/access_control/mapper.rb
  19. 47
      lib/open_project/access_control/permission.rb
  20. 4
      lib/redmine/plugin.rb
  21. 3
      modules/backlogs/spec/features/impediments_spec.rb
  22. 3
      modules/backlogs/spec/features/tasks_on_taskboard_spec.rb
  23. 2
      modules/backlogs/spec/services/impediments/create_services_spec.rb
  24. 4
      modules/bim/spec/requests/api/bcf/v2_1/project_extensions_api_spec.rb
  25. 2
      modules/bim/spec/requests/api/bcf/v2_1/topics_api_spec.rb
  26. 2
      modules/boards/spec/features/action_boards/assignee_board_spec.rb
  27. 2
      modules/budgets/spec/features/budgets/add_budget_spec.rb
  28. 8
      modules/budgets/spec/features/budgets/update_budget_spec.rb
  29. 14
      modules/costs/spec/features/cost_entries/add_cost_entry_spec.rb
  30. 8
      modules/costs/spec/features/cost_entries/add_entry_without_rate_permission_spec.rb
  31. 4
      modules/dashboards/lib/dashboards/engine.rb
  32. 2
      modules/github_integration/lib/open_project/github_integration/engine.rb
  33. 9
      modules/my_page/spec/features/my/assigned_to_me_spec.rb
  34. 2
      modules/overviews/lib/overviews/engine.rb
  35. 138
      modules/overviews/spec/requests/api/v3/grids/grids_resource_spec.rb
  36. 17
      modules/reporting/lib/widget/settings.rb
  37. 58
      modules/reporting/lib/widget/settings_patch.rb
  38. 2
      modules/reporting/spec/features/update_entries_spec.rb
  39. 1
      modules/team_planner/spec/features/shared_context.rb
  40. 2
      modules/team_planner/spec/features/team_planner_user_interaction_spec.rb
  41. 1
      spec/contracts/roles/create_contract_spec.rb
  42. 1
      spec/contracts/roles/shared_contract_examples.rb
  43. 4
      spec/contracts/roles/update_contract_spec.rb
  44. 3
      spec/controllers/work_packages/bulk_controller_spec.rb
  45. 3
      spec/controllers/work_packages/moves_controller_spec.rb
  46. 8
      spec/factories/role_factory.rb
  47. 2
      spec/features/global_roles/global_role_crud_spec.rb
  48. 5
      spec/features/global_roles/mock_global_permissions.rb
  49. 7
      spec/features/projects/copy_spec.rb
  50. 10
      spec/features/users/invite_user_modal/invite_user_modal_spec.rb
  51. 2
      spec/features/users/invite_user_modal/subproject_invite_spec.rb
  52. 2
      spec/features/work_packages/copy_spec.rb
  53. 2
      spec/features/work_packages/custom_actions/custom_actions_spec.rb
  54. 3
      spec/features/work_packages/edit_work_package_spec.rb
  55. 2
      spec/features/work_packages/new/attributes_from_filter_spec.rb
  56. 2
      spec/features/work_packages/new/new_work_package_spec.rb
  57. 2
      spec/features/work_packages/table/queries/me_filter_spec.rb
  58. 32
      spec/lib/open_project/access_control/permission_spec.rb
  59. 50
      spec/lib/open_project/access_control_spec.rb
  60. 2
      spec/models/actions/scopes/default_spec.rb
  61. 27
      spec/models/capabilities/scopes/default_spec.rb
  62. 4
      spec/models/mail_handler_spec.rb
  63. 2
      spec/models/principals/scopes/possible_assignee_spec.rb
  64. 109
      spec/models/users/allowed_scope_spec.rb
  65. 155
      spec/models/users/allowed_to_spec.rb
  66. 29
      spec/requests/api/v3/action_resource_spec.rb
  67. 8
      spec/requests/api/v3/work_packages/form/work_package_form_resource_spec.rb
  68. 6
      spec/requests/api/v3/work_packages/update_resource_spec.rb
  69. 5
      spec/services/members/cleanup_service_integration_spec.rb
  70. 2
      spec/services/projects/copy_service_integration_spec.rb
  71. 27
      spec/support/api/v3/shared_available_principals_examples.rb
  72. 4
      spec_legacy/fixtures/role_permissions.yml
  73. 2
      spec_legacy/unit/mail_handler_spec.rb

@ -29,7 +29,6 @@
module Roles
class BaseContract < ::ModelContract
attribute :name
attribute :assignable
validate :check_permission_prerequisites

@ -32,12 +32,13 @@ module Actions::Scopes
class_methods do
def default
capabilities_sql = <<~SQL
(SELECT id, permission, global, module FROM (VALUES #{action_map}) AS t(id, permission, global, module)) actions
actions_sql = <<~SQL.squish
(SELECT id, permission, global, module, grant_to_admin
FROM (VALUES #{action_map}) AS t(id, permission, global, module, grant_to_admin)) actions
SQL
select('actions.*')
.from(capabilities_sql)
.from(actions_sql)
end
private
@ -45,18 +46,19 @@ module Actions::Scopes
def action_map
OpenProject::AccessControl
.contract_actions_map
.map { |permission, v| map_actions(permission, v[:actions], v[:global], v[:module]) }
.map { |permission, v| map_actions(permission, **v) }
.flatten
.join(', ')
end
def map_actions(permission, actions, global, module_name)
def map_actions(permission, actions:, global:, module_name:, grant_to_admin:)
actions.map do |namespace, actions|
actions.map do |action|
values = [quote_string("#{action_v3_name(namespace)}/#{action}"),
quote_string(permission),
global,
module_name ? quote_string(module_name) : 'NULL'].join(', ')
module_name ? quote_string(module_name) : 'NULL',
grant_to_admin].join(', ')
"(#{values})"
end

@ -36,7 +36,7 @@ module Capabilities::Scopes
# Currently, this does not reflect the behaviour present in the backend that every permission in at least one project
# leads to having that permission in the global context as well. Hopefully, this is not necessary to be added.
def default
capabilities_sql = <<~SQL
capabilities_sql = <<~SQL.squish
(
#{default_sql_by_member}
UNION
@ -53,7 +53,7 @@ module Capabilities::Scopes
private
def default_sql_by_member
<<~SQL
<<~SQL.squish
SELECT DISTINCT
actions.id "action",
users.id principal_id,
@ -76,14 +76,14 @@ module Capabilities::Scopes
end
def default_sql_by_admin
<<~SQL
<<~SQL.squish
SELECT DISTINCT
actions.id "action",
users.id principal_id,
projects.id context_id
FROM (#{Action.default.to_sql}) actions
JOIN (#{Principal.visible.not_builtin.not_locked.to_sql}) users
ON "users".admin = true
ON "users".admin = true AND actions.grant_to_admin = true
LEFT OUTER JOIN "projects"
ON "projects".active = true
AND NOT "actions".global
@ -95,7 +95,7 @@ module Capabilities::Scopes
end
def default_sql_by_non_member
<<~SQL
<<~SQL.squish
SELECT DISTINCT
actions.id "action",
users.id principal_id,

@ -39,7 +39,15 @@ module Members::Scopes
not_locked
.includes(:roles)
.references(:roles)
.where(roles: { assignable: true })
.where(assignable_permission_exists)
end
def assignable_permission_exists
RolePermission
.where('role_permissions.role_id = roles.id')
.where(permission: 'work_package_assigned')
.arel
.exists
end
end
end

@ -33,7 +33,7 @@ class Queries::Capabilities::Filters::ActionFilter < Queries::Capabilities::Filt
def split_values
values.map do |value|
if (matches = value.match(/\A([a-z]+\/[a-z]+)\z/))
if (matches = value.match(/\A([a-z_]+\/[a-z_]+)\z/))
{
action: matches[1]
}

@ -50,7 +50,7 @@ class Role < ApplicationRecord
has_many :member_roles, dependent: :destroy
has_many :members, through: :member_roles
has_many :role_permissions
has_many :role_permissions, dependent: :destroy
default_scope -> {
includes(:role_permissions)

@ -147,9 +147,13 @@ class Authorization::ProjectQuery < Authorization::AbstractQuery
permissions(action).all?(&:public?)
end
def self.granted_to_admin?(user, action)
user.admin? && OpenProject::AccessControl.grant_to_admin?(action)
end
transformations.register :all,
:members_join do |statement, user|
if user.admin?
:members_join do |statement, user, action|
if granted_to_admin?(user, action)
statement
else
statement
@ -172,7 +176,7 @@ class Authorization::ProjectQuery < Authorization::AbstractQuery
transformations.register :all,
:role_permissions_join,
after: [:enabled_modules_join] do |statement, user, action|
if action_public?(action) || user.admin?
if action_public?(action) || granted_to_admin?(user, action)
statement
else
statement.join(role_permissions_table)
@ -182,8 +186,8 @@ class Authorization::ProjectQuery < Authorization::AbstractQuery
transformations.register :all,
:members_member_roles_join,
after: [:members_join] do |statement, user|
if user.admin?
after: [:members_join] do |statement, user, action|
if granted_to_admin?(user, action)
statement
else
statement.outer_join(member_roles_table)
@ -194,7 +198,7 @@ class Authorization::ProjectQuery < Authorization::AbstractQuery
transformations.register :all,
:permission_roles_join,
after: [:role_permissions_join] do |statement, user, action|
if action_public?(action) || user.admin?
if action_public?(action) || granted_to_admin?(user, action)
statement
else
statement.join(permission_roles_table)
@ -206,7 +210,7 @@ class Authorization::ProjectQuery < Authorization::AbstractQuery
:assigned_roles_join,
after: %i[permission_roles_join
members_member_roles_join] do |statement, user, action|
if user.admin?
if granted_to_admin?(user, action)
statement
else
statement.outer_join(assigned_roles_table)
@ -215,8 +219,8 @@ class Authorization::ProjectQuery < Authorization::AbstractQuery
end
transformations.register :all,
:assigned_role_exists_condition do |statement, user|
if user.admin?
:assigned_role_exists_condition do |statement, user, action|
if granted_to_admin?(user, action)
statement.where(project_active_condition)
else
statement.where(assigned_roles_table[:id].not_eq(nil))

@ -53,7 +53,11 @@ class Authorization::UserAllowedQuery < Authorization::AbstractUserQuery
has_role.and(has_permission)
end
is_admin = users_table[:admin].eq(true)
is_admin = if OpenProject::AccessControl.grant_to_admin?(action)
users_table[:admin].eq(true)
else
Arel::Nodes::Equality.new(1, 0)
end
statement.where(has_role_and_permission.or(is_admin))
else

@ -93,7 +93,8 @@ class Authorization::UserAllowedService
# Inactive users are never authorized
return false unless authorizable_user?
# Admin users are authorized for anything else
return true if user.admin?
# unless the permission is explicitly flagged not to be granted to admins.
return true if granted_to_admin?(action)
has_authorized_role?(action, project)
end
@ -104,7 +105,7 @@ class Authorization::UserAllowedService
# Inactive users are never authorized
return false unless authorizable_user?
# Admin users are always authorized
return true if user.admin?
return true if granted_to_admin?(action)
has_authorized_role?(action)
end
@ -116,6 +117,12 @@ class Authorization::UserAllowedService
!user.locked? || user.is_a?(SystemUser)
end
# Admin users are granted every permission unless the
# permission explicitly disables it.
def granted_to_admin?(action)
user.admin? && OpenProject::AccessControl.grant_to_admin?(action)
end
def has_authorized_role?(action, project = nil)
project_role_cache
.fetch(project)

@ -58,11 +58,13 @@ See COPYRIGHT and LICENSE files for more details.
<% if role.new_record? || role.is_a?(GlobalRole) %>
<div id="global_permissions" style=<%= role.new_record? && !role.is_a?(GlobalRole) ? "display:none" : ""%> >
<%= render partial: "permissions", locals: {permissions: grouped_setable_permissions(GlobalRole.new), role: role, showGlobalRole: true }%>
<%= render partial: "permissions",
locals: { permissions: grouped_setable_permissions(GlobalRole.new), role: role, show_global_role: true }%>
</div>
<% end %>
<% if role.new_record? || !role.is_a?(GlobalRole) %>
<div id="member_permissions" style=<%= role.new_record? && role.is_a?(GlobalRole) ? "display:none" : ""%>>
<%= render partial: "permissions", locals: {permissions: grouped_setable_permissions(role), role: role, showGlobalRole: false }%>
<%= render partial: "permissions",
locals: { permissions: grouped_setable_permissions(role), role: role, show_global_role: false }%>
</div>
<% end %>

@ -27,9 +27,6 @@ See COPYRIGHT and LICENSE files for more details.
++#%>
<div class="form--field">
<%= f.check_box :assignable %>
</div>
<% if role.new_record? && roles.any? %>
<div class="form--field">
<%= styled_label_tag :copy_workflow_from, t(:label_copy_workflow_from) %>

@ -28,12 +28,12 @@ See COPYRIGHT and LICENSE files for more details.
++#%>
<% permissions.each do |mod, mod_permissions| %>
<% global_prefix = showGlobalRole ? 'fieldset--global--' : 'fieldset--' %>
<% global_prefix = show_global_role ? 'fieldset--global--' : 'fieldset--' %>
<% module_name = mod.blank? ? 'fieldset--global--' + Project.model_name.human.downcase.gsub(' ', '_') : global_prefix + l_or_humanize(mod, prefix: 'project_module_').downcase.gsub(' ', '_') %>
<fieldset class="form--fieldset -collapsible" id="<%= module_name %>">
<legend class="form--fieldset-legend">
<% if mod.blank? %>
<%= showGlobalRole ? t(:label_global) : Project.model_name.human %>
<%= show_global_role ? t(:label_global) : Project.model_name.human %>
<% else %>
<%= l_or_humanize(mod, prefix: 'project_module_') %>
<% end %>
@ -48,10 +48,15 @@ See COPYRIGHT and LICENSE files for more details.
<div class="form--field">
<div class="form--field-container">
<label class="form--label-with-check-box">
<%= styled_check_box_tag 'role[permissions][]', permission.name, (role.permissions && role.permissions.include?(permission.name)) %>
<%= l_or_humanize(permission.name, prefix: 'permission_') %>
<%= styled_check_box_tag 'role[permissions][]',
permission.name,
(role.permissions && role.permissions.include?(permission.name)) %>
<%= t(:"permission_#{permission.name}") %>
</label>
</div>
<div class="form--field-instructions -labeled-checkbox">
<%= t(:"permission_#{permission.name}_explanation", default: nil) %>
</div>
</div>
<% end %>
</div>

@ -230,6 +230,15 @@ OpenProject::AccessControl.map do |map|
wpt.permission :assign_versions,
{},
dependencies: :view_work_packages
# A user having the following permission can become assignee and/or responsible of a work package.
# This is a passive permission in the sense that a user having the permission isn't eligible to perform
# actions but rather to have actions taken together with him/her.
wpt.permission :work_package_assigned,
{},
require: :member,
contract_actions: { work_packages: %i[assigned] },
grant_to_admin: false
end
map.project_module :news do |news|
@ -245,12 +254,12 @@ OpenProject::AccessControl.map do |map|
require: :member
news.permission :comment_news,
'news/comments': :create
{ 'news/comments': :create }
end
map.project_module :wiki do |wiki|
wiki.permission :view_wiki_pages,
wiki: %i[index show special date_index]
{ wiki: %i[index show special date_index] }
wiki.permission :list_attachments,
{ wiki: :list_attachments },
@ -277,13 +286,13 @@ OpenProject::AccessControl.map do |map|
require: :member
wiki.permission :export_wiki_pages,
wiki: [:export]
{ wiki: [:export] }
wiki.permission :view_wiki_edits,
wiki: %i[history diff annotate]
{ wiki: %i[history diff annotate] }
wiki.permission :edit_wiki_pages,
wiki: %i[edit update preview add_attachment new new_child create]
{ wiki: %i[edit update preview add_attachment new new_child create] }
wiki.permission :delete_wiki_pages_attachments,
{}
@ -295,7 +304,7 @@ OpenProject::AccessControl.map do |map|
map.project_module :repository do |repo|
repo.permission :browse_repository,
repositories: %i[show browse entry annotate changes diff stats graph]
{ repositories: %i[show browse entry annotate changes diff stats graph] }
repo.permission :commit_access,
{}
@ -308,7 +317,7 @@ OpenProject::AccessControl.map do |map|
require: :member
repo.permission :view_changesets,
repositories: %i[show revisions revision]
{ repositories: %i[show revisions revision] }
repo.permission :view_commit_author_statistics,
{}
@ -325,7 +334,7 @@ OpenProject::AccessControl.map do |map|
public: true
forum.permission :add_messages,
messages: %i[new create reply quote preview]
{ messages: %i[new create reply quote preview] }
forum.permission :edit_messages,
{ messages: %i[edit update preview] },

@ -529,7 +529,6 @@ en:
repository:
url: "URL"
role:
assignable: "Work packages can be assigned to users and groups in possession of this role in the respective project"
permissions: "Permissions"
time_entry:
activity: "Activity"
@ -2288,6 +2287,8 @@ en:
permission_view_timelines: "View timelines"
permission_view_wiki_edits: "View wiki history"
permission_view_wiki_pages: "View wiki"
permission_work_package_assigned: "Become assignee/responsible"
permission_work_package_assigned_explanation: "Work packages can be assigned to users and groups in possession of this role in the respective project"
placeholders:
default: "-"

@ -0,0 +1,30 @@
class AssignableToPermission < ActiveRecord::Migration[6.1]
def up
# Because of a missing dependent: :destroy, some role_permission
# for removed roles exist.
execute <<~SQL.squish
DELETE FROM role_permissions WHERE role_id IS NULL
SQL
execute <<~SQL.squish
INSERT INTO role_permissions (role_id, permission, created_at, updated_at)
SELECT id role_id, 'work_package_assigned' permission, NOW() created_at, NOW() updated_at FROM roles
WHERE assignable AND type = 'Role' AND builtin = 0
SQL
remove_column :roles, :assignable
end
def down
add_column :roles, :assignable, :boolean, default: true
execute <<~SQL.squish
UPDATE roles
SET assignable = EXISTS(SELECT 1 from role_permissions WHERE permission = 'work_package_assigned' and role_id = roles.id)
SQL
execute <<~SQL.squish
DELETE FROM role_permissions where permission = 'work_package_assigned'
SQL
end
end

@ -484,6 +484,9 @@ fieldset.form--fieldset
&.-no-italic
font-style: normal
&.-labeled-checkbox
margin-left: 25px
.form--inline-instructions
font-style: italic
display: inline

@ -64,8 +64,12 @@ module OpenProject
# Returns the permission of given name or nil if it wasn't found
# Argument should be a symbol
def permission(name)
permissions.detect { |p| p.name == name }
def permission(action)
if action.is_a?(Hash)
permissions.detect { |p| p.controller_actions.include?("#{action[:controller]}/#{action[:action]}") }
else
permissions.detect { |p| p.name == action }
end
end
# Returns the actions that are allowed by the permission of given name
@ -119,10 +123,19 @@ module OpenProject
@contract_actions_map ||= permissions.each_with_object({}) do |p, hash|
next unless p.contract_actions.any?
hash[p.name] = { actions: p.contract_actions, global: p.global?, module: p.project_module }
hash[p.name] = { actions: p.contract_actions,
global: p.global?,
module_name: p.project_module,
grant_to_admin: p.grant_to_admin? }
end
end
def grant_to_admin?(permission_name)
# Parts of the application currently rely on granting not defined permissions,
# e.g. :edit_attribute_help_texts to administrators.
permission(permission_name).nil? || permission(permission_name).grant_to_admin?
end
def remove_modules_permissions(module_name)
permissions = @permissions

@ -31,9 +31,8 @@
module OpenProject
module AccessControl
class Mapper
def permission(name, hash, options = {})
options[:project_module] = @project_module
mapped_permissions << Permission.new(name, hash, options)
def permission(name, hash, **options)
mapped_permissions << Permission.new(name, hash, project_module: @project_module, **options)
end
def project_module(name, options = {})

@ -37,24 +37,33 @@ module OpenProject
:project_module,
:dependencies
def initialize(name, hash, options)
def initialize(name,
hash,
public: false,
require: nil,
global: false,
enabled: true,
project_module: nil,
contract_actions: [],
grant_to_admin: true,
dependencies: nil)
@name = name
@controller_actions = []
@public = options[:public] || false
@require = options[:require]
@global = options[:global] || false
@enabled = options.include?(:enabled) ? options[:enabled] : true
@dependencies = Array(options[:dependencies]) || []
@project_module = options[:project_module]
@contract_actions = options[:contract_actions] || []
hash.each do |controller, actions|
@controller_actions << if actions.is_a? Array
actions.map { |action| "#{controller}/#{action}" }
else
"#{controller}/#{actions}"
end
end
@controller_actions.flatten!
@public = public
@require = require
@global = global
@enabled = enabled
@project_module = project_module
@contract_actions = contract_actions
@grant_to_admin = grant_to_admin
@dependencies = Array(dependencies)
@controller_actions = hash.map do |controller, actions|
if actions.is_a? Array
actions.map { |action| "#{controller}/#{action}" }
else
"#{controller}/#{actions}"
end
end.flatten
end
def public?
@ -65,6 +74,10 @@ module OpenProject
@global
end
def grant_to_admin?
@grant_to_admin
end
def require_member?
@require && @require == :member
end

@ -315,11 +315,11 @@ module Redmine #:nodoc:
mod, mod_options = @project_scope
OpenProject::AccessControl.map do |map|
map.project_module(mod, mod_options) do |map|
map.permission(name, actions, options)
map.permission(name, actions, **options)
end
end
else
OpenProject::AccessControl.map { |map| map.permission(name, actions, options) }
OpenProject::AccessControl.map { |map| map.permission(name, actions, **options) }
end
end

@ -60,7 +60,8 @@ describe 'Impediments on taskboard',
view_work_packages
edit_work_packages
manage_subtasks
assign_versions))
assign_versions
work_package_assigned))
end
let!(:current_user) do
create(:user,

@ -57,7 +57,8 @@ describe 'Tasks on taskboard',
view_work_packages
edit_work_packages
manage_subtasks
assign_versions))
assign_versions
work_package_assigned))
end
let!(:current_user) do
create(:user,

@ -32,7 +32,7 @@ describe Impediments::CreateService do
let(:instance) { described_class.new(user: user) }
let(:user) { create(:user) }
let(:role) { create(:role, permissions: %i(add_work_packages assign_versions)) }
let(:role) { create(:role, permissions: %i(add_work_packages assign_versions work_package_assigned)) }
let(:type_feature) { create(:type_feature) }
let(:type_task) { create(:type_task) }
let(:priority) { create(:priority, is_default: true) }

@ -75,13 +75,13 @@ describe 'BCF 2.1 project extensions resource', type: :request, content_type: :j
let(:current_user) do
create(:user,
member_in_project: project,
member_with_permissions: %i[view_project edit_project manage_bcf view_members])
member_with_permissions: %i[view_project edit_project manage_bcf view_members work_package_assigned])
end
let(:other_user) do
create(:user,
member_in_project: project,
member_with_permissions: [:view_project])
member_with_permissions: %i[view_project work_package_assigned])
end
before do

@ -38,7 +38,7 @@ describe 'BCF 2.1 topics resource', type: :request, content_type: :json, with_ma
let(:view_only_user) do
create(:user,
member_in_project: project,
member_with_permissions: %i[view_linked_issues view_work_packages])
member_with_permissions: %i[view_linked_issues view_work_packages work_package_assigned])
end
let(:only_member_user) do
create(:user,

@ -51,7 +51,7 @@ describe 'Assignee action board',
let(:permissions) do
%i[show_board_views manage_board_views add_work_packages
edit_work_packages view_work_packages manage_public_queries]
edit_work_packages view_work_packages manage_public_queries work_package_assigned]
end
let!(:priority) { create :default_priority }

@ -97,7 +97,7 @@ describe 'adding a new budget', type: :feature, js: true do
let(:new_budget_page) { Pages::NewBudget.new project.identifier }
let(:budget_page) { Pages::EditBudget.new Budget.last }
let(:project_members) { { user => create(:role) } }
let(:project_members) { { user => create(:role, permissions: %i[work_package_assigned]) } }
before do
create :cost_rate, cost_type: cost_type, rate: 50.0

@ -31,15 +31,13 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
describe 'updating a budget', type: :feature, js: true do
let(:project) do
create :project_with_types,
enabled_module_names: %i[budgets costs],
members: { user => create(:role) }
enabled_module_names: %i[budgets costs work_package_tracking],
members: { user => create(:role, permissions: %i[work_package_assigned]) }
end
let(:user) { create :admin }
let(:budget) { create :budget, author: user, project: project }
before do
login_as(user)
end
current_user { user }
describe 'with new cost items' do
let(:cost_type) do

@ -36,12 +36,14 @@ describe 'Work Package cost fields', type: :feature, js: true do
create(:project, types: [type_task])
end
shared_let(:role) do
create :role, permissions: %i[view_work_packages
delete_work_packages
log_costs
view_cost_rates
edit_cost_entries
view_cost_entries]
create :role,
permissions: %i[view_work_packages
delete_work_packages
log_costs
view_cost_rates
edit_cost_entries
view_cost_entries
work_package_assigned]
end
shared_let(:user) do
create :user,

@ -36,9 +36,11 @@ describe 'Create cost entry without rate permissions', type: :feature, js: true
create(:project, types: [type_task])
end
shared_let(:role) do
create :role, permissions: %i[view_work_packages
log_costs
view_cost_entries]
create :role,
permissions: %i[view_work_packages
log_costs
view_cost_entries
work_package_assigned]
end
shared_let(:user) do
create :user,

@ -25,8 +25,8 @@ module Dashboards
OpenProject::AccessControl.map do |ac_map|
ac_map.project_module(:dashboards) do |pm_map|
pm_map.permission(:view_dashboards, 'dashboards/dashboards': ['show'])
pm_map.permission(:manage_dashboards, 'dashboards/dashboards': ['show'])
pm_map.permission(:view_dashboards, { 'dashboards/dashboards': ['show'] })
pm_map.permission(:manage_dashboards, { 'dashboards/dashboards': ['show'] })
end
end
end

@ -61,7 +61,7 @@ module OpenProject::GithubIntegration
initializer 'github.permissions' do
OpenProject::AccessControl.map do |ac_map|
ac_map.project_module(:github, dependencies: :work_package_tracking) do |pm_map|
pm_map.permission(:show_github_content, {}, {})
pm_map.permission(:show_github_content, {})
end
end
end

@ -63,7 +63,10 @@ describe 'Assigned to me embedded query on my page', type: :feature, js: true do
create(:user)
end
let(:role) { create(:role, permissions: %i[view_work_packages add_work_packages edit_work_packages save_queries]) }
let(:role) do
create(:role,
permissions: %i[view_work_packages add_work_packages edit_work_packages save_queries work_package_assigned])
end
let(:user) do
create(:user,
@ -78,9 +81,7 @@ describe 'Assigned to me embedded query on my page', type: :feature, js: true do
let(:embedded_table) { Pages::EmbeddedWorkPackagesTable.new(assigned_area.area) }
let(:hierarchies) { ::Components::WorkPackages::Hierarchies.new }
before do
login_as user
end
current_user { user }
context 'with parent work package' do
let!(:assigned_work_package_child) do

@ -22,7 +22,7 @@ module Overviews
OpenProject::AccessControl.map do |ac_map|
ac_map.project_module nil do |map|
map.permission :manage_overview,
'overviews/overviews': ['show'],
{ 'overviews/overviews': ['show'] },
public: true
end
end

@ -33,7 +33,7 @@ describe 'API v3 Grids resource', type: :request, content_type: :json do
include Rack::Test::Methods
include API::V3::Utilities::PathHelper
let(:current_user) do
current_user do
create(:user,
member_in_project: project,
member_with_permissions: permissions)
@ -58,21 +58,13 @@ describe 'API v3 Grids resource', type: :request, content_type: :json do
end
let(:custom_text) { "Some text a user wrote" }
before do
login_as(current_user)
end
subject(:response) { last_response }
describe '#get api/v3/grids/:id' do
let(:path) { api_v3_paths.grid(grid.id) }
let(:stored_grids) do
grid
end
before do
stored_grids
grid
get path
end
@ -107,19 +99,36 @@ describe 'API v3 Grids resource', type: :request, content_type: :json do
let(:path) { api_v3_paths.grid(grid.id + 1) }
it 'responds with 404 NOT FOUND' do
expect(subject.status).to eql 404
expect(subject.status).to be 404
end
end
end
shared_examples_for 'creates a grid resource' do
it 'responds with 201 CREATED' do
expect(subject.status).to eq(201)
end
it 'returns the created grid block' do
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql(params['rowCount'].to_json)
.at_path('rowCount')
end
it 'persists the grid' do
expect(Grids::Grid.count)
.to be(1)
end
end
describe '#post api/v3/grids' do
let(:path) { api_v3_paths.grids }
let(:permissions) { %i[manage_overview] }
let(:stored_grids) do
end
let(:params) do
{
"rowCount": 10,
@ -133,115 +142,32 @@ describe 'API v3 Grids resource', type: :request, content_type: :json do
end
before do
stored_grids
post path, params.to_json
end
it 'responds with 201 CREATED' do
expect(subject.status).to eq(201)
end
it 'returns the created grid block' do
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql(params['rowCount'].to_json)
.at_path('rowCount')
end
it 'persists the grid' do
expect(Grids::Grid.count)
.to eql(1)
end
it_behaves_like 'creates a grid resource'
context 'if lacking the manage_overview permission' do
# Since manage_overview is a public permission, being a member in the project will grant the permission
# which is why this test case is the same as the one above
let(:permissions) { %i[] }
let(:params) do
{
"_links": {
"scope": {
"href": project_overview_path(project)
}
}
}.with_indifferent_access
end
it 'responds with 201 CREATED' do
expect(subject.status).to eq(201)
end
it 'returns the created grid block' do
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql(Overviews::GridRegistration.defaults[:row_count].to_json)
.at_path('rowCount')
end
it 'persists the grid' do
expect(Grids::Grid.count)
.to eql(1)
end
context 'if deviating from the default page parameters' do
let(:params) do
{
"_links": {
"scope": {
"href": project_overview_path(project)
}
}
}.with_indifferent_access
end
it 'responds with 201 CREATED' do
expect(subject.status).to eq(201)
end
it 'returns the created grid block' do
expect(subject.body)
.to be_json_eql('Grid'.to_json)
.at_path('_type')
expect(subject.body)
.to be_json_eql(Overviews::GridRegistration.defaults[:row_count].to_json)
.at_path('rowCount')
end
it 'persists the grid' do
expect(Grids::Grid.count)
.to eql(1)
end
end
it_behaves_like 'creates a grid resource'
end
context 'if lacking the manage_overview permission and deviating from the default page' do
let(:permissions) { %i[] }
let(:stored_grids) do
end
let(:params) do
{
"rowCount": Overviews::GridRegistration.defaults[:row_count] + 1,
"_links": {
"scope": {
"href": project_overview_path(project)
}
}
}.with_indifferent_access
context 'if not being a member in the project' do
current_user do
create(:user)
end
it 'responds with 422' do
expect(subject.status).to eq(422)
end
it 'does not persists the grid' do
it 'persists no grid' do
expect(Grids::Grid.count)
.to eql(0)
.to be(0)
end
end
end

@ -43,6 +43,14 @@ class Widget::Settings < Widget::Base
end
end
def render_cost_types_settings
render_widget Widget::Settings::Fieldset, @subject, type: "units" do
render_widget Widget::CostTypes,
@cost_types,
selected_type_id: @selected_type_id
end
end
def render_controls_settings
content_tag :div, class: 'form--buttons -with-button-form hide-when-print' do
widgets = ''.html_safe
@ -75,7 +83,14 @@ class Widget::Settings < Widget::Base
end)
end
def render_with_options(options, &block)
@cost_types = options.delete(:cost_types)
@selected_type_id = options.delete(:selected_type_id)
super(options, &block)
end
def settings_to_render
@settings_to_render ||= %i[filter group_by controls]
@settings_to_render ||= %i[filter group_by cost_types controls]
end
end

@ -1,58 +0,0 @@
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2022 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 Widget::SettingsPatch
extend ActiveSupport::Concern
included do
prepend InstanceMethods
end
module InstanceMethods
def settings_to_render
super.insert(-2, :cost_types)
end
def render_cost_types_settings
render_widget Widget::Settings::Fieldset, @subject, type: "units" do
render_widget Widget::CostTypes,
@cost_types,
selected_type_id: @selected_type_id
end
end
def render_with_options(options, &block)
@cost_types = options.delete(:cost_types)
@selected_type_id = options.delete(:selected_type_id)
super(options, &block)
end
end
end
Widget::Settings.include Widget::SettingsPatch

@ -32,7 +32,7 @@ require_relative 'support/components/cost_reports_base_table'
describe 'Updating entries within the cost report', type: :feature, js: true do
let(:project) { create :project }
let(:user) { create :admin }
let(:user) { create :admin, member_in_project: project, member_with_permissions: %i[work_package_assigned] }
let(:work_package) { create :work_package, project: project }
let!(:time_entry_user) do

@ -43,6 +43,7 @@ shared_context 'with team planner full access' do
view_work_packages edit_work_packages add_work_packages
view_team_planner manage_team_planner
save_queries manage_public_queries
work_package_assigned
]
end

@ -43,7 +43,7 @@ describe 'Team planner drag&dop and resizing', type: :feature, js: true do
firstname: 'Bernd',
member_in_project: project,
member_with_permissions: %w[
view_work_packages view_team_planner
view_work_packages view_team_planner work_package_assigned
]
end

@ -34,7 +34,6 @@ describe Roles::CreateContract do
let(:role) do
Role.new.tap do |r|
r.name = role_name
r.assignable = role_assignable
r.permissions = role_permissions
end
end

@ -34,7 +34,6 @@ shared_examples_for 'roles contract' do
end
let(:role_instance) { Role.new }
let(:role_name) { 'A role name' }
let(:role_assignable) { true }
let(:role_permissions) { [:view_work_packages] }
def expect_valid(valid, symbols = {})

@ -33,10 +33,8 @@ describe Roles::UpdateContract do
it_behaves_like 'roles contract' do
let(:role) do
build_stubbed(:role,
name: 'Some name',
assignable: !role_assignable).tap do |r|
name: 'Some name').tap do |r|
r.name = role_name
r.assignable = role_assignable
r.permissions = role_permissions
end
end

@ -58,7 +58,8 @@ describe WorkPackages::BulkController, type: :controller, with_settings: { journ
permissions: %i[edit_work_packages
view_work_packages
manage_subtasks
assign_versions])
assign_versions
work_package_assigned])
end
let(:member1_p1) do
create(:member,

@ -37,7 +37,8 @@ describe WorkPackages::MovesController, type: :controller, with_settings: { jour
add_work_packages
edit_work_packages
assign_versions
manage_subtasks)
manage_subtasks
work_package_assigned)
end
let(:type) { create :type }
let(:type_2) { create :type }

@ -32,25 +32,21 @@ FactoryBot.define do
factory :role do
permissions { [] }
sequence(:name) { |n| "role_#{n}" }
assignable { true }
factory :non_member do
name { 'Non member' }
builtin { Role::BUILTIN_NON_MEMBER }
assignable { false }
initialize_with { Role.where(name: name).first_or_initialize }
end
factory :anonymous_role do
name { 'Anonymous' }
builtin { Role::BUILTIN_ANONYMOUS }
assignable { false }
initialize_with { Role.where(name: name).first_or_initialize }
end
factory :existing_role do
name { 'Role ' + Digest::MD5.hexdigest(permissions.map(&:to_s).join('/'))[0..4] }
assignable { true }
name { "Role #{Digest::MD5.hexdigest(permissions.map(&:to_s).join('/'))[0..4]}" }
permissions { [] }
initialize_with do
@ -58,7 +54,7 @@ FactoryBot.define do
if Role.where(name: name).exists?
Role.find_by(name: name)
else
Role.create name: name, assignable: assignable
Role.create name: name
end
role.add_permission!(*permissions.reject { |p| role.permissions.include?(p) })

@ -52,7 +52,7 @@ describe 'Global role: Global role CRUD', type: :feature, js: true do
# And I should see "Global group"
expect(page).to have_text 'GLOBAL GROUP'
# And I should see "Glob test"
expect(page).to have_text 'Glob test'
expect(page).to have_text 'Permission Glob Test'
# And I should not see "Issues can be assigned to this role"
expect(page).to have_no_text 'Issues can be assigned to this role'
# When I fill in "Name" with "Manager"

@ -22,6 +22,9 @@ def mock_permissions(name, options = {})
::OpenProject::AccessControl::Permission.new(
name,
{ does_not: :matter },
{ project_module: 'Foo', public: false, global: false }.merge(options)
project_module: 'Foo',
public: false,
global: false,
**options
)
end

@ -76,7 +76,9 @@ describe 'Projects copy',
create(:role,
permissions: permissions)
end
let(:permissions) { %i(copy_projects edit_project add_subprojects manage_types view_work_packages select_custom_fields) }
let(:permissions) do
%i(copy_projects edit_project add_subprojects manage_types view_work_packages select_custom_fields work_package_assigned)
end
let(:wp_user) do
user = create(:user)
@ -151,7 +153,8 @@ describe 'Projects copy',
expect(page).to have_text 'The job has been queued and will be processed shortly.'
# ensure all jobs are run especially emails which might be sent later on
while perform_enqueued_jobs > 0 do end
while perform_enqueued_jobs > 0
end
copied_project = Project.find_by(name: 'Copied project')

@ -32,7 +32,7 @@ describe 'Invite user modal', type: :feature, js: true do
shared_let(:project) { create :project }
shared_let(:work_package) { create :work_package, project: project }
let(:permissions) { %i[view_work_packages edit_work_packages manage_members] }
let(:permissions) { %i[view_work_packages edit_work_packages manage_members work_package_assigned] }
let(:global_permissions) { %i[] }
let(:modal) do
::Components::Users::InviteUserModal.new project: project,
@ -127,7 +127,7 @@ describe 'Invite user modal', type: :feature, js: true do
let(:principal) { build :invited_user }
context 'when the current user has permissions to create a user' do
let(:permissions) { %i[view_work_packages edit_work_packages manage_members] }
let(:permissions) { %i[view_work_packages edit_work_packages manage_members work_package_assigned] }
let(:global_permissions) { %i[manage_user] }
it_behaves_like 'invites the principal to the project' do
@ -139,6 +139,7 @@ describe 'Invite user modal', type: :feature, js: true do
context 'when the current user does not have permissions to invite a user to the instance by email' do
let(:permissions) { %i[view_work_packages edit_work_packages manage_members] }
it 'does not show the invite user option' do
modal.project_step
ngselect = modal.open_select_in_step principal.mail
@ -186,9 +187,10 @@ describe 'Invite user modal', type: :feature, js: true do
let(:principal) { build :placeholder_user, name: 'MY NEW PLACEHOLDER' }
context 'an enterprise system', with_ee: %i[placeholder_users] do
let(:permissions) { %i[view_work_packages edit_work_packages manage_members work_package_assigned] }
describe 'create a new placeholder' do
context 'with permissions to manage placeholders' do
let(:permissions) { %i[view_work_packages edit_work_packages manage_members] }
let(:global_permissions) { %i[manage_placeholder_user] }
it_behaves_like 'invites the principal to the project' do
@ -199,7 +201,6 @@ describe 'Invite user modal', type: :feature, js: true do
end
context 'without permissions to manage placeholders' do
let(:permissions) { %i[view_work_packages edit_work_packages manage_members] }
it 'does not allow to invite a new placeholder' do
modal.project_step
@ -213,7 +214,6 @@ describe 'Invite user modal', type: :feature, js: true do
context 'with an existing placeholder' do
let(:principal) { create :placeholder_user, name: 'EXISTING PLACEHOLDER' }
let(:permissions) { %i[view_work_packages edit_work_packages manage_members] }
let(:global_permissions) { %i[] }
it_behaves_like 'invites the principal to the project' do

@ -34,7 +34,7 @@ describe 'Invite user modal subprojects', type: :feature, js: true do
shared_let(:work_package) { create :work_package, project: subproject }
shared_let(:invitable_user) { create :user, firstname: 'Invitable', lastname: 'User' }
let(:permissions) { %i[view_work_packages edit_work_packages manage_members] }
let(:permissions) { %i[view_work_packages edit_work_packages manage_members work_package_assigned] }
let(:global_permissions) { %i[] }
let(:modal) do
::Components::Users::InviteUserModal.new project: subproject,

@ -61,7 +61,7 @@ RSpec.feature 'Work package copy', js: true, selenium: true do
type: type,
author: author)
end
let(:role) { build(:role, permissions: [:view_work_packages]) }
let(:role) { build(:role, permissions: %i[view_work_packages work_package_assigned]) }
let(:assignee) do
create(:user,
firstname: 'An',

@ -31,7 +31,7 @@ require 'spec_helper'
describe 'Custom actions', type: :feature, js: true do
shared_let(:admin) { create :admin }
let(:permissions) { %i(view_work_packages edit_work_packages move_work_packages) }
let(:permissions) { %i(view_work_packages edit_work_packages move_work_packages work_package_assigned) }
let(:role) { create(:role, permissions: permissions) }
let!(:other_role) { create(:role, permissions: permissions) }
let(:user) do

@ -17,7 +17,8 @@ describe 'edit work package', js: true do
let(:manager_role) do
create :role,
permissions: %i[view_work_packages
edit_work_packages]
edit_work_packages
work_package_assigned]
end
let(:manager) do
create :admin,

@ -41,7 +41,7 @@ RSpec.feature 'Work package create uses attributes from filters', js: true, sele
let(:wp_table) { ::Pages::WorkPackagesTable.new(project) }
let(:split_view_create) { ::Pages::SplitWorkPackageCreate.new(project: project) }
let(:role) { create :existing_role, permissions: [:view_work_packages] }
let(:role) { create :existing_role, permissions: %i[view_work_packages work_package_assigned] }
let!(:query) do
build(:query, project: project, user: user).tap do |query|

@ -13,7 +13,7 @@ describe 'new work package', js: true do
create(:project, types: types)
end
let(:permissions) { %i[view_work_packages add_work_packages edit_work_packages] }
let(:permissions) { %i[view_work_packages add_work_packages edit_work_packages work_package_assigned] }
let(:user) do
create(:user,
member_in_project: project,

@ -36,7 +36,7 @@ describe 'filter me value', js: true do
public: true,
members: project_members
end
let(:role) { create :existing_role, permissions: [:view_work_packages] }
let(:role) { create :existing_role, permissions: %i[view_work_packages work_package_assigned] }
let(:admin) { create :admin }
let(:user) { create :user }
let(:wp_table) { ::Pages::WorkPackagesTable.new(project) }

@ -51,25 +51,45 @@ describe OpenProject::AccessControl::Permission do
describe '#global?' do
describe 'setting global permission' do
before { @permission = OpenProject::AccessControl::Permission.new(:perm, { cont: [:action] }, { global: true }) }
let(:permission) { described_class.new(:perm, { cont: [:action] }, global: true) }
it { expect(@permission.global?).to be_truthy }
it { expect(permission).to be_global }
end
describe 'setting non global permission' do
before { @permission = OpenProject::AccessControl::Permission.new :perm, { cont: [:action] }, { global: false } }
let(:permission) { described_class.new :perm, { cont: [:action] }, global: false }
it 'is false' do
expect(@permission.global?).to be_falsey
expect(permission).not_to be_global
end
end
describe 'not specifying -> default' do
before { @permission = OpenProject::AccessControl::Permission.new :perm, { cont: [:action] }, {} }
let(:permission) { described_class.new :perm, { cont: [:action] } }
it 'is false' do
expect(@permission.global?).to be_falsey
expect(permission).not_to be_global
end
end
end
describe '#grant_to_admin?' do
context 'if explicitly specified' do
let(:permission) { described_class.new(:perm, {}, grant_to_admin: true) }
it { expect(permission).to be_grant_to_admin }
end
context 'as a default' do
let(:permission) { described_class.new(:perm, {}) }
it { expect(permission).to be_grant_to_admin }
end
context 'if explicitly specified not to' do
let(:permission) { described_class.new(:perm, {}, grant_to_admin: false) }
it { expect(permission).not_to be_grant_to_admin }
end
end
end

@ -55,12 +55,12 @@ describe OpenProject::AccessControl do
end
map.project_module :mixed_module do |mod|
mod.permission :proj3, { dont: :care }
mod.permission :proj3, { dont: :care }, grant_to_admin: true
mod.permission :global2, { dont: :care }, global: true, contract_actions: { baz: %i[destroy] }
end
map.project_module :dependent_module, dependencies: :project_module do |mod|
mod.permission :proj4, { dont: :care }
mod.permission :proj4, { dont: :care }, grant_to_admin: false
end
end
end
@ -243,9 +243,49 @@ describe OpenProject::AccessControl do
it 'contains all contract actions grouped by the permission' do
expect(subject.contract_actions_map)
.to eql(global2: { actions: { baz: [:destroy] }, global: true, module: :mixed_module },
proj0: { actions: { foo: :create }, global: false, module: nil },
proj2: { actions: { bar: %i[create read] }, global: false, module: :project_module })
.to eql(global2: { actions: { baz: [:destroy] }, global: true, module_name: :mixed_module, grant_to_admin: true },
proj0: { actions: { foo: :create }, global: false, module_name: nil, grant_to_admin: true },
proj2: { actions: { bar: %i[create read] }, global: false, module_name: :project_module, grant_to_admin: true })
end
end
describe '.grant_to_admin?' do
before do
stash_access_control_permissions
setup_global_permissions
end
after do
restore_access_control_permissions
end
context 'for a granted permission (default)' do
it 'is granted' do
expect(described_class)
.to be_grant_to_admin(:proj0)
end
end
context 'for a non granted permission' do
it 'is granted' do
expect(described_class)
.not_to be_grant_to_admin(:proj4)
end
end
context 'for an explicitly granted permission' do
it 'is granted' do
expect(described_class)
.to be_grant_to_admin(:proj3)
end
end
context 'for a non existing permission' do
it 'is granted' do
expect(described_class)
.to be_grant_to_admin(:not_existing)
end
end
end
end

@ -44,7 +44,7 @@ describe Actions::Scopes::Default, type: :model do
OpenProject::AccessControl
.contract_actions_map
.map do |permission, v|
v[:actions].map { |vk, vv| vv.map { |vvv| item.call(permission, vk, vvv, v[:global], v[:module]) } }
v[:actions].map { |vk, vv| vv.map { |vvv| item.call(permission, vk, vvv, v[:global], v[:module_name]) } }
end.flatten(2)
end

@ -28,6 +28,7 @@
require 'spec_helper'
# rubocop:disable RSpec/MultipleMemoizedHelpers
describe Capabilities::Scopes::Default, type: :model do
# we focus on the non current user capabilities to make the tests easier to understand
subject(:scope) { Capability.default.where(principal_id: user.id) }
@ -143,9 +144,9 @@ describe Capabilities::Scopes::Default, type: :model do
end
end
context 'with a lgobal member with an action permission and the user being locked' do
let(:permissions) { %i[manage_members] }
let(:members) { [member] }
context 'with a global member with an action permission and the user being locked' do
let(:permissions) { %i[manage_user] }
let(:members) { [global_member] }
let(:user_status) { Principal.statuses[:locked] }
it_behaves_like 'is empty'
@ -235,7 +236,8 @@ describe Capabilities::Scopes::Default, type: :model do
OpenProject::AccessControl
.contract_actions_map
.map { |_, v| v[:actions].map { |vk, vv| vv.map { |vvv| item.call(vk, vvv, v[:global], v[:module]) } } }
.select { |_, v| v[:grant_to_admin] }
.map { |_, v| v[:actions].map { |vk, vv| vv.map { |vvv| item.call(vk, vvv, v[:global], v[:module_name]) } } }
.flatten(2)
.compact
end
@ -262,7 +264,8 @@ describe Capabilities::Scopes::Default, type: :model do
OpenProject::AccessControl
.contract_actions_map
.map { |_, v| v[:actions].map { |vk, vv| vv.map { |vvv| item.call(vk, vvv, v[:global], v[:module]) } } }
.select { |_, v| v[:grant_to_admin] }
.map { |_, v| v[:actions].map { |vk, vv| vv.map { |vvv| item.call(vk, vvv, v[:global], v[:module_name]) } } }
.flatten(2)
.compact
end
@ -305,6 +308,19 @@ describe Capabilities::Scopes::Default, type: :model do
end
end
context 'with a member with an action permission that is not granted to admin' do
let(:permissions) { %i[work_package_assigned] }
let(:members) { [member] }
it_behaves_like 'consists of contract actions' do
let(:expected) do
[
['work_packages/assigned', user.id, project.id]
]
end
end
end
context 'with a member with an action permission and the project being archived' do
let(:permissions) { %i[manage_members] }
let(:members) { [member] }
@ -314,3 +330,4 @@ describe Capabilities::Scopes::Default, type: :model do
end
end
end
# rubocop:enable RSpec/MultipleMemoizedHelpers

@ -47,7 +47,7 @@ describe MailHandler, type: :model do
end
shared_context 'wp_on_given_project' do
let(:permissions) { %i[add_work_packages assign_versions] }
let(:permissions) { %i[add_work_packages assign_versions work_package_assigned] }
let!(:user) do
create(:user,
mail: 'JSmith@somenet.foo',
@ -151,7 +151,7 @@ describe MailHandler, type: :model do
end
shared_context 'with a reply to a wp mention with attributes' do
let(:permissions) { %i[add_work_package_notes view_work_packages edit_work_packages] }
let(:permissions) { %i[add_work_package_notes view_work_packages edit_work_packages work_package_assigned] }
let(:role) do
create(:role, permissions: permissions)
end

@ -34,7 +34,7 @@ describe Principals::Scopes::PossibleAssignee, type: :model do
let(:project) { create(:project) }
let(:other_project) { create(:project) }
let(:role_assignable) { true }
let(:role) { create(:role, assignable: role_assignable) }
let(:role) { create(:role, permissions: (role_assignable ? [:work_package_assigned] : [])) }
let(:user_status) { :active }
let!(:member_user) do
create(:user,

@ -62,8 +62,8 @@ describe User, 'allowed scope' do
member.save!
end
it 'should return the user' do
expect(User.allowed(action, project).where(id: user.id)).to match_array [user]
it 'returns the user' do
expect(described_class.allowed(action, project).where(id: user.id)).to match_array [user]
end
end
@ -75,8 +75,24 @@ describe User, 'allowed scope' do
user.update_attribute(:admin, true)
end
it 'should return the user' do
expect(User.allowed(action, project).where(id: user.id)).to match_array [user]
it 'returns the user' do
expect(described_class.allowed(action, project).where(id: user.id)).to match_array [user]
end
end
context 'w/ the context being a project
w/o the project being public
w/o the user being member in the project
w/ the user being admin
w/ the action not granted to admins' do
let(:action) { :work_package_assigned }
before do
user.update_attribute(:admin, true)
end
it 'is empty' do
expect(described_class.allowed(action, project).where(id: user.id)).to be_empty
end
end
@ -89,8 +105,8 @@ describe User, 'allowed scope' do
member.save!
end
it 'should be empty' do
expect(User.allowed(action, project).where(id: user.id)).to be_empty
it 'is empty' do
expect(described_class.allowed(action, project).where(id: user.id)).to be_empty
end
end
@ -102,8 +118,8 @@ describe User, 'allowed scope' do
role.add_permission! action
end
it 'should return the user' do
expect(User.allowed(action, project).where(id: user.id)).to be_empty
it 'returns the user' do
expect(described_class.allowed(action, project).where(id: user.id)).to be_empty
end
end
@ -119,8 +135,8 @@ describe User, 'allowed scope' do
member.save!
end
it 'should be empty' do
expect(User.allowed(action, project).where(id: user.id)).to be_empty
it 'is empty' do
expect(described_class.allowed(action, project).where(id: user.id)).to be_empty
end
end
@ -138,8 +154,8 @@ describe User, 'allowed scope' do
member.save!
end
it 'should be empty' do
expect(User.allowed(action, project).where(id: user.id)).to be_empty
it 'is empty' do
expect(described_class.allowed(action, project).where(id: user.id)).to be_empty
end
end
@ -156,8 +172,8 @@ describe User, 'allowed scope' do
project.save!
end
it 'should return the user' do
expect(User.allowed(action, project).where(id: user.id)).to match_array [user]
it 'returns the user' do
expect(described_class.allowed(action, project).where(id: user.id)).to match_array [user]
end
end
@ -174,8 +190,8 @@ describe User, 'allowed scope' do
project.save!
end
it 'should return the anonymous user' do
expect(User.allowed(action, project).where(id: [user.id, anonymous.id])).to match_array([anonymous])
it 'returns the anonymous user' do
expect(described_class.allowed(action, project).where(id: [user.id, anonymous.id])).to match_array([anonymous])
end
end
@ -192,8 +208,8 @@ describe User, 'allowed scope' do
project.save!
end
it 'should be empty' do
expect(User.allowed(action, project).where(id: user.id)).to be_empty
it 'is empty' do
expect(described_class.allowed(action, project).where(id: user.id)).to be_empty
end
end
@ -208,8 +224,8 @@ describe User, 'allowed scope' do
project.save!
end
it 'should be empty' do
expect(User.allowed(action, project).where(id: user.id)).to be_empty
it 'is empty' do
expect(described_class.allowed(action, project).where(id: user.id)).to be_empty
end
end
@ -229,8 +245,8 @@ describe User, 'allowed scope' do
non_member.add_permission! action
end
it 'should be empty' do
expect(User.allowed(action, project).where(id: user.id)).to be_empty
it 'is empty' do
expect(described_class.allowed(action, project).where(id: user.id)).to be_empty
end
end
@ -243,8 +259,8 @@ describe User, 'allowed scope' do
member.save!
end
it 'should return the user' do
expect(User.allowed(public_action, project).where(id: user.id)).to match_array [user]
it 'returns the user' do
expect(described_class.allowed(public_action, project).where(id: user.id)).to match_array [user]
end
end
@ -258,8 +274,8 @@ describe User, 'allowed scope' do
project.save
end
it 'should return the user and anonymous' do
expect(User.allowed(public_action, project).where(id: [user.id, anonymous.id])).to match_array [user, anonymous]
it 'returns the user and anonymous' do
expect(described_class.allowed(public_action, project).where(id: [user.id, anonymous.id])).to match_array [user, anonymous]
end
end
@ -279,8 +295,8 @@ describe User, 'allowed scope' do
member.save!
end
it 'should be empty' do
expect(User.allowed(permission.name, project).where(id: user.id)).to eq []
it 'is empty' do
expect(described_class.allowed(permission.name, project).where(id: user.id)).to eq []
end
end
@ -300,8 +316,8 @@ describe User, 'allowed scope' do
member.save!
end
it 'should return the user' do
expect(User.allowed(permission.name, project).where(id: user.id)).to eq [user]
it 'returns the user' do
expect(described_class.allowed(permission.name, project).where(id: user.id)).to eq [user]
end
end
@ -317,21 +333,8 @@ describe User, 'allowed scope' do
project.update(active: false)
end
it 'should be empty' do
expect(User.allowed(action, project)).to eq []
end
end
context 'w/ only asking for members
w/o the project being public
w/o the user being member in the project
w/ the user being admin' do
before do
user.update_attribute(:admin, true)
end
it 'should return the user' do
expect(User.allowed_members(action, project).where(id: user.id)).to be_empty
it 'is empty' do
expect(described_class.allowed(action, project)).to eq []
end
end
@ -345,8 +348,8 @@ describe User, 'allowed scope' do
member.save!
end
it 'should return the user' do
expect(User.allowed_members(action, project).where(id: user.id)).to match_array [user]
it 'returns the user' do
expect(described_class.allowed_members(action, project).where(id: user.id)).to match_array [user]
end
end
@ -358,8 +361,8 @@ describe User, 'allowed scope' do
user.update_attribute(:admin, true)
end
it 'should return the user' do
expect(User.allowed_members(action, project).where(id: user.id)).to be_empty
it 'returns the user' do
expect(described_class.allowed_members(action, project).where(id: user.id)).to be_empty
end
end
@ -375,8 +378,8 @@ describe User, 'allowed scope' do
member.save!
end
it 'should return the user' do
expect(User.allowed_members(action, project).where(id: user.id)).to match_array [user]
it 'returns the user' do
expect(described_class.allowed_members(action, project).where(id: user.id)).to match_array [user]
end
end
@ -390,8 +393,8 @@ describe User, 'allowed scope' do
role.add_permission! action
end
it 'should return the user' do
expect(User.allowed_members(action, project).where(id: user.id)).to be_empty
it 'returns the user' do
expect(described_class.allowed_members(action, project).where(id: user.id)).to be_empty
end
end
end

@ -73,8 +73,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(user.allowed_to?(permission, project)).to be_truthy
it 'is true' do
expect(user).to be_allowed_to(permission, project)
end
end
@ -87,8 +87,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be false' do
expect(user.allowed_to?(permission, project)).to be_falsey
it 'is false' do
expect(user).not_to be_allowed_to(permission, project)
end
end
@ -101,8 +101,25 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be false' do
expect(user.allowed_to?(permission, project)).to be_falsey
it 'is false' do
expect(user).not_to be_allowed_to(permission, project)
end
end
context 'w/ the user being admin
w/ the permission not automatically granted to admins' do
let(:permission) { :work_package_assigned }
before do
user.update(admin: true)
project.save
final_setup_step
end
it 'is false' do
expect(user).not_to be_allowed_to(permission, project)
end
end
@ -114,8 +131,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be false' do
expect(user.allowed_to?(permission, project)).to be_falsey
it 'is false' do
expect(user).not_to be_allowed_to(permission, project)
end
end
@ -129,8 +146,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(user.allowed_to?(permission, project)).to be_truthy
it 'is true' do
expect(user).to be_allowed_to(permission, project)
end
end
@ -148,8 +165,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be false' do
expect(user.allowed_to?(permission, project)).to be_falsey
it 'is false' do
expect(user).not_to be_allowed_to(permission, project)
end
end
@ -168,8 +185,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be false' do
expect(user.allowed_to?(permission, project)).to be_falsey
it 'is false' do
expect(user).not_to be_allowed_to(permission, project)
end
end
@ -187,8 +204,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be false' do
expect(user.allowed_to?(permission, project)).to be_falsey
it 'is false' do
expect(user).not_to be_allowed_to(permission, project)
end
end
@ -205,8 +222,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(user.allowed_to?(permission, project)).to be_truthy
it 'is true' do
expect(user).to be_allowed_to(permission, project)
end
end
@ -224,8 +241,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be false' do
expect(user.allowed_to?(permission, project)).to be_falsey
it 'is false' do
expect(user).not_to be_allowed_to(permission, project)
end
end
@ -243,8 +260,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(user.allowed_to?(permission, project)).to be_truthy
it 'is true' do
expect(user).to be_allowed_to(permission, project)
end
end
@ -264,8 +281,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be false' do
expect(user.allowed_to?(permission, project)).to be_falsey
it 'is false' do
expect(user).not_to be_allowed_to(permission, project)
end
end
@ -281,8 +298,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(anonymous.allowed_to?(permission, project)).to be_truthy
it 'is true' do
expect(anonymous).to be_allowed_to(permission, project)
end
end
@ -300,8 +317,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(anonymous.allowed_to?(permission, project)).to be_truthy
it 'is true' do
expect(anonymous).to be_allowed_to(permission, project)
end
end
@ -320,9 +337,9 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(anonymous.allowed_to?(permission, project))
.to be_truthy
it 'is true' do
expect(anonymous)
.to be_allowed_to(permission, project)
end
end
@ -336,8 +353,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be false' do
expect(anonymous.allowed_to?(permission, project)).to be_falsey
it 'is false' do
expect(anonymous).not_to be_allowed_to(permission, project)
end
end
@ -353,8 +370,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(user.allowed_to?(permission, [project, project2])).to be_truthy
it 'is true' do
expect(user).to be_allowed_to(permission, [project, project2])
end
end
@ -369,8 +386,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be false' do
expect(user.allowed_to?(permission, [project, project2])).to be_falsey
it 'is false' do
expect(user).not_to be_allowed_to(permission, [project, project2])
end
end
@ -387,8 +404,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(user.allowed_to?(permission, [project, project2])).to be_truthy
it 'is true' do
expect(user).to be_allowed_to(permission, [project, project2])
end
end
@ -405,8 +422,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be false' do
expect(user.allowed_to?(permission, [project, project2])).to be_falsey
it 'is false' do
expect(user).not_to be_allowed_to(permission, [project, project2])
end
end
@ -422,7 +439,7 @@ describe User, 'allowed_to?' do
end
it 'is false' do
expect(user.allowed_to?(global_permission.name, project)).to be_falsey
expect(user).not_to be_allowed_to(global_permission.name, project)
end
end
@ -438,9 +455,9 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(user.allowed_to?({ controller: 'wiki', action: 'show' }, project))
.to be_truthy
it 'is true' do
expect(user)
.to be_allowed_to({ controller: 'wiki', action: 'show' }, project)
end
end
@ -456,9 +473,9 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(user.allowed_to?({ controller: 'projects', action: 'show' }, project))
.to be_truthy
it 'is true' do
expect(user)
.to be_allowed_to({ controller: 'projects', action: 'show' }, project)
end
end
end
@ -475,8 +492,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(user.allowed_to?(permission, nil, global: true)).to be_truthy
it 'is true' do
expect(user).to be_allowed_to(permission, nil, global: true)
end
end
@ -488,8 +505,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be false' do
expect(user.allowed_to?(permission, nil, global: true)).to be_falsey
it 'is false' do
expect(user).not_to be_allowed_to(permission, nil, global: true)
end
end
@ -503,8 +520,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(user.allowed_to?(permission, nil, global: true)).to be_truthy
it 'is true' do
expect(user).to be_allowed_to(permission, nil, global: true)
end
end
@ -521,9 +538,9 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(user.allowed_to?({ controller: 'wiki', action: 'show' }, nil, global: true))
.to be_truthy
it 'is true' do
expect(user)
.to be_allowed_to({ controller: 'wiki', action: 'show' }, nil, global: true)
end
end
@ -539,8 +556,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(user.allowed_to?(permission, nil, global: true)).to be_truthy
it 'is true' do
expect(user).to be_allowed_to(permission, nil, global: true)
end
end
@ -553,8 +570,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(user.allowed_to?(permission, nil, global: true)).to be_truthy
it 'is true' do
expect(user).to be_allowed_to(permission, nil, global: true)
end
end
@ -568,7 +585,7 @@ describe User, 'allowed_to?' do
end
it 'is true' do
expect(user.allowed_to?(global_permission.name, nil, global: true)).to be_truthy
expect(user).to be_allowed_to(global_permission.name, nil, global: true)
end
end
@ -583,7 +600,7 @@ describe User, 'allowed_to?' do
end
it 'is false' do
expect(user.allowed_to?(global_permission.name, nil, global: true)).to be_falsey
expect(user).not_to be_allowed_to(global_permission.name, nil, global: true)
end
end
@ -595,7 +612,7 @@ describe User, 'allowed_to?' do
end
it 'is false' do
expect(user.allowed_to?(global_permission.name, nil, global: true)).to be_falsey
expect(user).not_to be_allowed_to(global_permission.name, nil, global: true)
end
end
@ -607,8 +624,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
expect(anonymous.allowed_to?(permission, nil, global: true)).to be_truthy
it 'is true' do
expect(anonymous).to be_allowed_to(permission, nil, global: true)
end
end
@ -627,7 +644,7 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be true' do
it 'is true' do
expect(user)
.to be_allowed_to({ controller: '/projects/settings/categories', action: 'show' }, nil, global: true)
end
@ -639,8 +656,8 @@ describe User, 'allowed_to?' do
final_setup_step
end
it 'should be false' do
expect(anonymous.allowed_to?(permission, nil, global: true)).to be_falsey
it 'is false' do
expect(anonymous).not_to be_allowed_to(permission, nil, global: true)
end
end
end

@ -46,18 +46,9 @@ describe 'API v3 action resource', type: :request, content_type: :json do
get path
end
it 'returns a collection of actions' do
expect(subject.body)
.to be_json_eql(Action.count.to_json)
.at_path('total')
expect(subject.body)
.to be_json_eql(Action.order(id: :asc).first.id.to_json)
.at_path('_embedded/elements/0/id')
expect(subject.body)
.to be_json_eql(Action.order(id: :asc).first.id.to_json)
.at_path("_embedded/elements/0/id")
# 20 because this is the standard pagination size
it_behaves_like 'API V3 collection response', Action.count, 20, 'Action' do
let(:elements) { Action.order(id: :asc).limit(20).to_a }
end
end
@ -70,7 +61,7 @@ describe 'API v3 action resource', type: :request, content_type: :json do
it 'returns 200 OK' do
expect(subject.status)
.to eql(200)
.to be(200)
end
it 'returns the action' do
@ -88,7 +79,7 @@ describe 'API v3 action resource', type: :request, content_type: :json do
it 'returns 200 OK' do
expect(subject.status)
.to eql(200)
.to be(200)
end
it 'returns the action' do
@ -105,19 +96,13 @@ describe 'API v3 action resource', type: :request, content_type: :json do
context 'if querying a non existing action' do
let(:path) { api_v3_paths.action("foo/bar") }
it 'returns 404 NOT FOUND' do
expect(subject.status)
.to be 404
end
it_behaves_like 'not found'
end
context 'if querying with malformed id' do
let(:path) { api_v3_paths.action("foobar") }
it 'returns 404 NOT FOUND' do
expect(subject.status)
.to be 404
end
it_behaves_like 'not found'
end
end
end

@ -404,7 +404,8 @@ describe 'API v3 Work package form resource', type: :request, with_mail: false d
let(:path) { "_embedded/payload/_links/#{property}/href" }
let(:visible_user) do
create(:user,
member_in_project: project)
member_in_project: project,
member_with_permissions: [:work_package_assigned])
end
let(:user_parameter) { { _links: { property => { href: user_link } } } }
let(:params) { valid_params.merge(user_parameter) }
@ -441,7 +442,7 @@ describe 'API v3 Work package form resource', type: :request, with_mail: false d
context 'existing group' do
let(:user_link) { api_v3_paths.group group.id }
let(:group) { create(:group) }
let(:role) { create(:role) }
let(:role) { create(:role, permissions: %i[work_package_assigned]) }
let(:group_member) do
create(:member,
principal: group,
@ -458,11 +459,10 @@ describe 'API v3 Work package form resource', type: :request, with_mail: false d
context 'existing placeholder_user' do
let(:user_link) { api_v3_paths.placeholder_user placeholder_user.id }
let(:role) { create(:role) }
let(:placeholder_user) do
create(:placeholder_user,
member_in_project: project,
member_through_role: role)
member_with_permissions: %i[work_package_assigned])
end
it_behaves_like 'valid user assignment'

@ -45,7 +45,7 @@ describe 'API v3 Work package resource',
create(:project, identifier: 'test_project', public: false)
end
let(:role) { create(:role, permissions: permissions) }
let(:permissions) { %i[view_work_packages edit_work_packages assign_versions] }
let(:permissions) { %i[view_work_packages edit_work_packages assign_versions work_package_assigned] }
let(:type) { create(:type) }
current_user do
@ -442,7 +442,7 @@ describe 'API v3 Work package resource',
end
context 'assignee and responsible' do
let(:user) { create(:user, member_in_project: project) }
let(:user) { create(:user, member_in_project: project, member_with_permissions: %i[work_package_assigned]) }
let(:params) { valid_params.merge(user_parameter) }
let(:work_package) do
create(:work_package,
@ -455,7 +455,7 @@ describe 'API v3 Work package resource',
shared_context 'setup group membership' do
let(:group) { create(:group) }
let(:group_role) { create(:role) }
let(:group_role) { create(:role, permissions: %i[work_package_assigned]) }
let!(:group_member) do
create(:member,
principal: group,

@ -58,7 +58,7 @@ describe Members::CleanupService, 'integration', type: :model do
create(:member,
principal: user,
project: project,
roles: [create(:role, assignable: true)])
roles: [create(:role, permissions: %i[work_package_assigned])])
end
it 'keeps assigned_to to the user' do
@ -74,7 +74,8 @@ describe Members::CleanupService, 'integration', type: :model do
create(:member,
principal: user,
project: project,
roles: [create(:role, assignable: false)])
# Lacking work_package_assigned
roles: [create(:role, permissions: [])])
end
it 'sets assigned_to to nil' do

@ -51,7 +51,7 @@ describe Projects::CopyService, 'integration', type: :model do
member_in_project: source,
member_through_role: role)
end
let(:role) { create :role, permissions: %i[copy_projects view_work_packages] }
let(:role) { create :role, permissions: %i[copy_projects view_work_packages work_package_assigned] }
shared_let(:new_project_role) { create :role, permissions: %i[] }
let(:instance) do
described_class.new(source: source, user: current_user)

@ -39,21 +39,23 @@ shared_examples_for 'available principals' do |principals|
let(:other_user) do
create(:user,
member_in_project: project,
member_through_role: role)
member_through_role: assignable_role)
end
let(:role) { create(:role, permissions: permissions) }
let(:assignable_role) { create(:role, permissions: assignable_permissions) }
let(:project) { create(:project) }
let(:group) do
create(:group,
member_in_project: project,
member_through_role: role)
member_through_role: assignable_role)
end
let(:placeholder_user) do
create(:placeholder_user,
member_in_project: project,
member_through_role: role)
member_through_role: assignable_role)
end
let(:permissions) { [:view_work_packages] }
let(:assignable_permissions) { [:work_package_assigned] }
shared_context "request available #{principals}" do
before { get href }
@ -67,13 +69,15 @@ shared_examples_for 'available principals' do |principals|
end
describe 'users' do
context 'single user' do
let(:permissions) { %i[view_work_packages work_package_assigned] }
context 'for a single user' do
# The current user
it_behaves_like "returns available #{principals}", 1, 1, 'User'
end
context 'multiple users' do
context 'for multiple users' do
before do
other_user
# and the current user
@ -81,26 +85,31 @@ shared_examples_for 'available principals' do |principals|
it_behaves_like "returns available #{principals}", 2, 2, 'User'
end
context 'if the user lacks the assignable permission' do
let(:permissions) { %i[view_work_packages] }
it_behaves_like "returns available #{principals}", 0, 0, 'User'
end
end
describe 'groups' do
let!(:users) { [group] }
# current user and group
it_behaves_like "returns available #{principals}", 2, 2, 'Group'
it_behaves_like "returns available #{principals}", 1, 1, 'Group'
end
describe 'placeholder users' do
let!(:users) { [placeholder_user] }
# current user and placeholder user
it_behaves_like "returns available #{principals}", 2, 2, 'PlaceholderUser'
it_behaves_like "returns available #{principals}", 1, 1, 'PlaceholderUser'
end
end
describe 'if not allowed' do
include Rack::Test::Methods
let(:permissions) { [] }
before { get href }
it_behaves_like 'unauthorized access'

@ -340,6 +340,10 @@ role_permission132:
id: 132
role_id: 2
permission: 'assign_versions'
role_permission133:
id: 133
role_id: 2
permission: 'work_package_assigned'
role_permission200:
id: 200

@ -114,7 +114,7 @@ describe MailHandler, type: :model do
it 'should add work package should match assignee on display name' do # added from redmine - not sure if it is ok here
user = create(:user, firstname: 'Foo', lastname: 'Bar')
role = create(:role, name: 'Superhero')
role = create(:role, name: 'Superhero', permissions: ['work_package_assigned'])
create(:member, user: user, project: Project.find(2), role_ids: [role.id])
issue = submit_email('ticket_on_given_project.eml') do |email|
email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar')

Loading…
Cancel
Save