123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- require 'rails_helper'
- RSpec.describe 'User endpoint', type: :request do
- let(:role_with_admin_user_permissions) do
- create(:role).tap do |role|
- role.permission_grant('admin.user')
- end
- end
- let(:admin_with_admin_user_permissions) { create(:user, roles: [role_with_admin_user_permissions]) }
- let(:role_without_admin_user_permissions) do
- create(:role).tap do |role|
- role.permission_grant('admin.tag')
- end
- end
- let(:admin_without_admin_user_permissions) { create(:user, roles: [role_without_admin_user_permissions]) }
- describe 'User creation' do
- let(:attributes) { attributes_params_for(:user) }
- it 'responds forbidden for customer' do
- requester = create(:customer)
- authenticated_as(requester)
- expect do
- post api_v1_users_path, params: attributes
- end.to not_change {
- User.count
- }
- expect(response).to have_http_status(:forbidden)
- end
- context 'privileged attributes' do
- context 'group assignment' do
- # group access assignment is in general only valid for agents
- # see HasGroups.groups_access_permission?
- let(:agent_attributes) do
- attributes.merge(
- roles: Role.where(name: 'Agent').map(&:name),
- )
- end
- shared_examples 'group assignment' do |map_method_id|
- it 'responds success for admin.user' do
- authenticated_as(admin_with_admin_user_permissions)
- expect do
- post api_v1_users_path, params: payload
- end.to change(User, :count).by(1)
- expect(response).to have_http_status(:success)
- expect(User.last.send(map_method_id)).to eq(send(map_method_id))
- end
- it 'responds forbidden for sub admin without admin.user' do
- authenticated_as(admin_without_admin_user_permissions)
- expect do
- post api_v1_users_path, params: payload
- end.to not_change {
- User.count
- }
- expect(response).to have_http_status(:forbidden)
- end
- it 'responds successful for agent but removes assignment' do
- requester = create(:agent)
- authenticated_as(requester)
- expect do
- post api_v1_users_path, params: payload
- end.to change(User, :count).by(1)
- expect(response).to have_http_status(:success)
- expect(User.last.send(map_method_id)).to be_blank
- end
- end
- context 'parameter groups' do
- let(:group_names_access_map) do
- Group.all.to_h { |g| [g.name, ['full']] }
- end
- let(:payload) do
- agent_attributes.merge(
- groups: group_names_access_map,
- )
- end
- it_behaves_like 'group assignment', :group_names_access_map
- end
- context 'parameter group_ids' do
- let(:group_ids_access_map) do
- Group.all.to_h { |g| [g.id, ['full']] }
- end
- let(:payload) do
- agent_attributes.merge(
- group_ids: group_ids_access_map,
- )
- end
- it_behaves_like 'group assignment', :group_ids_access_map
- end
- end
- context 'role assignment' do
- shared_examples 'role assignment' do
- let(:privileged) { Role.where(name: 'Admin') }
- it 'responds success for admin.user' do
- authenticated_as(admin_with_admin_user_permissions)
- expect do
- post api_v1_users_path, params: payload
- end.to change(User, :count).by(1)
- expect(response).to have_http_status(:success)
- expect(User.last.roles).to eq(privileged)
- end
- it 'responds forbidden for sub admin without admin.user' do
- authenticated_as(admin_without_admin_user_permissions)
- expect do
- post api_v1_users_path, params: payload
- end.to not_change {
- User.count
- }
- expect(response).to have_http_status(:forbidden)
- end
- it 'responds successful for agent but removes assignment' do
- requester = create(:agent)
- authenticated_as(requester)
- expect do
- post api_v1_users_path, params: payload
- end.to change(User, :count).by(1)
- expect(response).to have_http_status(:success)
- expect(User.last.roles).to eq(Role.signup_roles)
- end
- end
- context 'parameter roles' do
- let(:payload) do
- attributes.merge(
- roles: privileged.map(&:name),
- )
- end
- it_behaves_like 'role assignment'
- end
- context 'parameter role_ids' do
- let(:payload) do
- attributes.merge(
- role_ids: privileged.map(&:id),
- )
- end
- it_behaves_like 'role assignment'
- end
- end
- end
- end
- describe 'User update' do
- def authorized_update_request(requester:, requested:)
- authenticated_as(requester)
- expect do
- put api_v1_update_user_path(requested), params: cleaned_params_for(requested).merge(firstname: 'Changed')
- end.to change {
- requested.reload.firstname
- }
- expect(response).to have_http_status(:success)
- end
- def forbidden_update_request(requester:, requested:)
- authenticated_as(requester)
- expect do
- put api_v1_update_user_path(requested), params: cleaned_params_for(requested).merge(firstname: 'Changed')
- end.to not_change {
- requested.reload.attributes.tap do |attributes|
- # take attributes as they are for different users
- next if requester != requested
- # last_login and updated_at change every time
- # even if the record itself should not change
- attributes.delete_if do |key, _value|
- %w[last_login updated_at].include?(key)
- end
- end
- }
- expect(response).to have_http_status(:forbidden)
- end
- context 'request by admin.user' do
- let(:requester) { admin_with_admin_user_permissions }
- it 'is successful for same admin' do
- authorized_update_request(
- requester: requester,
- requested: requester,
- )
- end
- it 'is successful for other admin' do
- authorized_update_request(
- requester: requester,
- requested: create(:admin),
- )
- end
- it 'is successful for agent' do
- authorized_update_request(
- requester: requester,
- requested: create(:agent),
- )
- end
- it 'is successful for customer' do
- authorized_update_request(
- requester: requester,
- requested: create(:customer),
- )
- end
- end
- context 'request by sub admin without admin.user' do
- let(:requester) { admin_without_admin_user_permissions }
- it 'is forbidden for same admin' do
- forbidden_update_request(
- requester: requester,
- requested: requester,
- )
- end
- it 'is forbidden for other admin' do
- forbidden_update_request(
- requester: requester,
- requested: create(:admin),
- )
- end
- it 'is forbidden for agent' do
- forbidden_update_request(
- requester: requester,
- requested: create(:agent),
- )
- end
- it 'is forbidden for customer' do
- forbidden_update_request(
- requester: requester,
- requested: create(:customer),
- )
- end
- end
- context 'request by agent' do
- let(:requester) { create(:agent) }
- it 'is forbidden for admin' do
- forbidden_update_request(
- requester: requester,
- requested: create(:admin),
- )
- end
- it 'is forbidden same agent' do
- forbidden_update_request(
- requester: requester,
- requested: requester,
- )
- end
- it 'is forbidden for other agent' do
- forbidden_update_request(
- requester: requester,
- requested: create(:agent),
- )
- end
- it 'is successful for customer' do
- authorized_update_request(
- requester: requester,
- requested: create(:customer),
- )
- end
- end
- context 'request by customer' do
- let(:requester) { create(:customer) }
- it 'is forbidden for admin' do
- forbidden_update_request(
- requester: requester,
- requested: create(:admin),
- )
- end
- it 'is forbidden for agent' do
- forbidden_update_request(
- requester: requester,
- requested: create(:agent),
- )
- end
- it 'is forbidden for same customer' do
- forbidden_update_request(
- requester: requester,
- requested: requester,
- )
- end
- it 'is forbidden for other customer' do
- forbidden_update_request(
- requester: requester,
- requested: create(:customer),
- )
- end
- it 'is forbidden for same organization' do
- same_organization = create(:organization)
- requester.update!(organization: same_organization)
- forbidden_update_request(
- requester: requester,
- requested: create(:customer, organization: same_organization),
- )
- end
- end
- context 'privileged attributes' do
- let(:requested) { create(:user) }
- let(:attribute) { privileged.keys.first }
- let(:payload) { cleaned_params_for(requested).merge(privileged) }
- def value_of_attribute
- # we need to call .to_a otherwise Rails will load the
- # ActiveRecord::Associations::CollectionProxy
- # on comparsion which is to late
- requested.reload.public_send(attribute).to_a
- end
- shared_examples 'admin types requests' do
- it 'responds success for admin.user' do
- authenticated_as(admin_with_admin_user_permissions)
- expect do
- put api_v1_update_user_path(requested), params: payload
- end.to change {
- value_of_attribute
- }
- expect(response).to have_http_status(:success)
- end
- it 'responds forbidden for sub admin without admin.user' do
- authenticated_as(admin_without_admin_user_permissions)
- expect do
- put api_v1_update_user_path(requested), params: payload
- end.to not_change {
- value_of_attribute
- }
- expect(response).to have_http_status(:forbidden)
- end
- end
- shared_examples 'permitted agent update' do
- it 'responds successful for agent but removes assignment' do
- requester = create(:agent)
- authenticated_as(requester)
- expect do
- put api_v1_update_user_path(requested), params: payload
- end.to change {
- value_of_attribute
- }
- expect(response).to have_http_status(:success)
- end
- end
- shared_examples 'forbidden agent update' do
- it 'responds successful for agent but removes assignment' do
- requester = create(:agent)
- authenticated_as(requester)
- expect do
- put api_v1_update_user_path(requested), params: payload
- end.to not_change {
- value_of_attribute
- }
- expect(response).to have_http_status(:success)
- end
- end
- context 'group assignment' do
- context 'parameter groups' do
- let(:privileged) do
- {
- groups: Group.all.to_h { |g| [g.name, ['full']] }
- }
- end
- it_behaves_like 'admin types requests'
- it_behaves_like 'forbidden agent update'
- end
- context 'parameter group_ids' do
- let(:privileged) do
- {
- group_ids: Group.all.to_h { |g| [g.id, ['full']] }
- }
- end
- it_behaves_like 'admin types requests'
- it_behaves_like 'forbidden agent update'
- end
- end
- context 'role assignment' do
- let(:admin_role) { Role.where(name: 'Admin') }
- context 'parameter roles' do
- let(:privileged) do
- {
- roles: admin_role.map(&:name),
- }
- end
- it_behaves_like 'admin types requests'
- it_behaves_like 'forbidden agent update'
- end
- context 'parameter role_ids' do
- let(:privileged) do
- {
- role_ids: admin_role.map(&:id),
- }
- end
- it_behaves_like 'admin types requests'
- it_behaves_like 'forbidden agent update'
- end
- end
- context 'organization assignment' do
- # We need a primary org to be able to assign secondary orgs.
- let(:requested) { create(:user, organization: create(:organization)) }
- let(:new_organizations) { create_list(:organization, 2) }
- context 'parameter organizations' do
- let(:privileged) do
- {
- organizations: new_organizations.map(&:name),
- }
- end
- it_behaves_like 'admin types requests'
- it_behaves_like 'permitted agent update'
- end
- context 'parameter organization_ids' do
- let(:privileged) do
- {
- organization_ids: new_organizations.map(&:id),
- }
- end
- it_behaves_like 'admin types requests'
- it_behaves_like 'permitted agent update'
- end
- end
- end
- end
- describe 'User deletion' do
- def authorized_destroy_request(requester:, requested:)
- authenticated_as(requester)
- delete api_v1_delete_user_path(requested)
- expect(response).to have_http_status(:success)
- expect(requested).not_to exist_in_database
- end
- def forbidden_destroy_request(requester:, requested:)
- authenticated_as(requester)
- delete api_v1_delete_user_path(requested)
- expect(response).to have_http_status(:forbidden)
- expect(requested).to exist_in_database
- end
- context 'request by admin.user' do
- let(:requester) { admin_with_admin_user_permissions }
- it 'is successful for same admin' do
- authorized_destroy_request(
- requester: requester,
- requested: requester,
- )
- end
- it 'is successful for other admin' do
- authorized_destroy_request(
- requester: requester,
- requested: create(:admin),
- )
- end
- it 'is successful for agent' do
- authorized_destroy_request(
- requester: requester,
- requested: create(:agent),
- )
- end
- it 'is successful for customer' do
- authorized_destroy_request(
- requester: requester,
- requested: create(:customer),
- )
- end
- end
- context 'request by sub admin without admin.user' do
- let(:requester) { admin_without_admin_user_permissions }
- it 'is forbidden for same admin' do
- forbidden_destroy_request(
- requester: requester,
- requested: requester,
- )
- end
- it 'is forbidden for other admin' do
- forbidden_destroy_request(
- requester: requester,
- requested: create(:admin),
- )
- end
- it 'is forbidden for agent' do
- forbidden_destroy_request(
- requester: requester,
- requested: create(:agent),
- )
- end
- it 'is forbidden for customer' do
- forbidden_destroy_request(
- requester: requester,
- requested: create(:customer),
- )
- end
- end
- context 'request by agent' do
- let(:requester) { create(:agent) }
- it 'is forbidden for admin' do
- forbidden_destroy_request(
- requester: requester,
- requested: create(:admin),
- )
- end
- it 'is forbidden same agent' do
- forbidden_destroy_request(
- requester: requester,
- requested: requester,
- )
- end
- it 'is forbidden for other agent' do
- forbidden_destroy_request(
- requester: requester,
- requested: create(:agent),
- )
- end
- it 'is forbidden for customer' do
- forbidden_destroy_request(
- requester: requester,
- requested: create(:customer),
- )
- end
- end
- context 'request by customer' do
- let(:requester) { create(:customer) }
- it 'is forbidden for admin' do
- forbidden_destroy_request(
- requester: requester,
- requested: create(:admin),
- )
- end
- it 'is forbidden for agent' do
- forbidden_destroy_request(
- requester: requester,
- requested: create(:agent),
- )
- end
- it 'is forbidden for same customer' do
- forbidden_destroy_request(
- requester: requester,
- requested: requester,
- )
- end
- it 'is forbidden for other customer' do
- forbidden_destroy_request(
- requester: requester,
- requested: create(:customer),
- )
- end
- it 'is forbidden for same organization' do
- same_organization = create(:organization)
- requester.update!(organization: same_organization)
- forbidden_destroy_request(
- requester: requester,
- requested: create(:customer, organization: same_organization),
- )
- end
- end
- end
- end
|