Fixed issue #1794 - Admin users can be disabled by Agents.
This commit is contained in:
parent
46ed7a053b
commit
d52634701d
28 changed files with 1782 additions and 146 deletions
2
Gemfile
2
Gemfile
|
@ -128,7 +128,7 @@ group :development, :test do
|
||||||
gem 'simplecov-rcov'
|
gem 'simplecov-rcov'
|
||||||
|
|
||||||
# UI tests w/ Selenium
|
# UI tests w/ Selenium
|
||||||
gem 'selenium-webdriver', '2.53.4'
|
gem 'selenium-webdriver'
|
||||||
|
|
||||||
# livereload on template changes (html, js, css)
|
# livereload on template changes (html, js, css)
|
||||||
gem 'guard', require: false
|
gem 'guard', require: false
|
||||||
|
|
12
Gemfile.lock
12
Gemfile.lock
|
@ -77,7 +77,7 @@ GEM
|
||||||
browser (2.5.2)
|
browser (2.5.2)
|
||||||
buftok (0.2.0)
|
buftok (0.2.0)
|
||||||
builder (3.2.3)
|
builder (3.2.3)
|
||||||
childprocess (0.8.0)
|
childprocess (0.9.0)
|
||||||
ffi (~> 1.0, >= 1.0.11)
|
ffi (~> 1.0, >= 1.0.11)
|
||||||
clavius (1.0.3)
|
clavius (1.0.3)
|
||||||
clearbit (0.2.8)
|
clearbit (0.2.8)
|
||||||
|
@ -141,7 +141,7 @@ GEM
|
||||||
multipart-post (>= 1.2, < 3)
|
multipart-post (>= 1.2, < 3)
|
||||||
faraday-http-cache (2.0.0)
|
faraday-http-cache (2.0.0)
|
||||||
faraday (~> 0.8)
|
faraday (~> 0.8)
|
||||||
ffi (1.9.18)
|
ffi (1.9.23)
|
||||||
ffi-compiler (0.1.3)
|
ffi-compiler (0.1.3)
|
||||||
ffi (>= 1.0.0)
|
ffi (>= 1.0.0)
|
||||||
rake
|
rake
|
||||||
|
@ -383,10 +383,9 @@ GEM
|
||||||
sawyer (0.8.1)
|
sawyer (0.8.1)
|
||||||
addressable (>= 2.3.5, < 2.6)
|
addressable (>= 2.3.5, < 2.6)
|
||||||
faraday (~> 0.8, < 1.0)
|
faraday (~> 0.8, < 1.0)
|
||||||
selenium-webdriver (2.53.4)
|
selenium-webdriver (3.11.0)
|
||||||
childprocess (~> 0.5)
|
childprocess (~> 0.5)
|
||||||
rubyzip (~> 1.0)
|
rubyzip (~> 1.2)
|
||||||
websocket (~> 1.0)
|
|
||||||
shellany (0.0.1)
|
shellany (0.0.1)
|
||||||
simple_oauth (0.3.1)
|
simple_oauth (0.3.1)
|
||||||
simplecov (0.15.1)
|
simplecov (0.15.1)
|
||||||
|
@ -452,7 +451,6 @@ GEM
|
||||||
addressable (>= 2.3.6)
|
addressable (>= 2.3.6)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
hashdiff
|
hashdiff
|
||||||
websocket (1.2.5)
|
|
||||||
websocket-driver (0.6.5)
|
websocket-driver (0.6.5)
|
||||||
websocket-extensions (>= 0.1.0)
|
websocket-extensions (>= 0.1.0)
|
||||||
websocket-extensions (0.1.3)
|
websocket-extensions (0.1.3)
|
||||||
|
@ -528,7 +526,7 @@ DEPENDENCIES
|
||||||
rubocop
|
rubocop
|
||||||
rubyntlm!
|
rubyntlm!
|
||||||
sass-rails
|
sass-rails
|
||||||
selenium-webdriver (= 2.53.4)
|
selenium-webdriver
|
||||||
simplecov
|
simplecov
|
||||||
simplecov-rcov
|
simplecov-rcov
|
||||||
slack-notifier
|
slack-notifier
|
||||||
|
|
|
@ -1258,7 +1258,7 @@ class App.ObserverActionRow extends App.ObserverController
|
||||||
|
|
||||||
render: (object) =>
|
render: (object) =>
|
||||||
return if _.isEmpty(object)
|
return if _.isEmpty(object)
|
||||||
actions = @actions()
|
actions = @actions(object)
|
||||||
@html App.view('generic/actions')(
|
@html App.view('generic/actions')(
|
||||||
items: actions
|
items: actions
|
||||||
type: @type
|
type: @type
|
||||||
|
|
|
@ -7,14 +7,18 @@ class SidebarCustomer extends App.Controller
|
||||||
badgeCallback: @badgeRender
|
badgeCallback: @badgeRender
|
||||||
sidebarHead: 'Customer'
|
sidebarHead: 'Customer'
|
||||||
sidebarCallback: @showCustomer
|
sidebarCallback: @showCustomer
|
||||||
sidebarActions: [
|
sidebarActions: []
|
||||||
{
|
}
|
||||||
|
if App.User.exists(@params.customer_id)
|
||||||
|
customer = App.User.find(@params.customer_id)
|
||||||
|
currentUser = App.User.find(App.Session.get('id'))
|
||||||
|
if customer.isAccessibleBy(currentUser, 'change')
|
||||||
|
@item.sidebarActions.push {
|
||||||
title: 'Edit Customer'
|
title: 'Edit Customer'
|
||||||
name: 'customer-edit'
|
name: 'customer-edit'
|
||||||
callback: @editCustomer
|
callback: @editCustomer
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@item
|
||||||
|
|
||||||
metaBadge: (user) =>
|
metaBadge: (user) =>
|
||||||
counter = ''
|
counter = ''
|
||||||
|
|
|
@ -182,7 +182,7 @@ class App.TicketZoom extends App.Controller
|
||||||
@formMeta = data.form_meta
|
@formMeta = data.form_meta
|
||||||
|
|
||||||
# load assets
|
# load assets
|
||||||
App.Collection.loadAssets(data.assets)
|
App.Collection.loadAssets(data.assets, targetModel: 'Ticket')
|
||||||
|
|
||||||
# get data
|
# get data
|
||||||
@ticket = App.Ticket.fullLocal(@ticket_id)
|
@ticket = App.Ticket.fullLocal(@ticket_id)
|
||||||
|
|
|
@ -15,6 +15,10 @@ class SidebarCustomer extends App.Controller
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
return @item if @ticket && @ticket.customer_id == 1
|
return @item if @ticket && @ticket.customer_id == 1
|
||||||
|
currentUser = App.User.find(App.Session.get('id'))
|
||||||
|
if @ticket.customer_id && App.User.exists(@ticket.customer_id)
|
||||||
|
customer = App.User.find(@ticket.customer_id)
|
||||||
|
if customer.isAccessibleBy(currentUser, 'change')
|
||||||
@item.sidebarActions.push {
|
@item.sidebarActions.push {
|
||||||
title: 'Edit Customer'
|
title: 'Edit Customer'
|
||||||
name: 'customer-edit'
|
name: 'customer-edit'
|
||||||
|
|
|
@ -106,13 +106,10 @@ class ActionRow extends App.ObserverActionRow
|
||||||
newTicket: (user) =>
|
newTicket: (user) =>
|
||||||
@navigate("ticket/create/customer/#{user.id}")
|
@navigate("ticket/create/customer/#{user.id}")
|
||||||
|
|
||||||
actions: =>
|
actions: (user) =>
|
||||||
[
|
currentUser = App.User.find(App.Session.get('id'))
|
||||||
{
|
|
||||||
name: 'edit'
|
actions = [
|
||||||
title: 'Edit'
|
|
||||||
callback: @editUser
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
name: 'history'
|
name: 'history'
|
||||||
title: 'History'
|
title: 'History'
|
||||||
|
@ -125,6 +122,15 @@ class ActionRow extends App.ObserverActionRow
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if user.isAccessibleBy(currentUser, 'change')
|
||||||
|
actions.unshift {
|
||||||
|
name: 'edit'
|
||||||
|
title: 'Edit'
|
||||||
|
callback: @editUser
|
||||||
|
}
|
||||||
|
|
||||||
|
actions
|
||||||
|
|
||||||
class Object extends App.ObserverController
|
class Object extends App.ObserverController
|
||||||
model: 'User'
|
model: 'User'
|
||||||
observeNot:
|
observeNot:
|
||||||
|
|
|
@ -308,7 +308,7 @@ set new attributes of model (remove already available attributes)
|
||||||
|
|
||||||
# full / load assets
|
# full / load assets
|
||||||
if data.assets
|
if data.assets
|
||||||
App.Collection.loadAssets(data.assets)
|
App.Collection.loadAssets(data.assets, targetModel: @className)
|
||||||
|
|
||||||
# find / load object
|
# find / load object
|
||||||
else
|
else
|
||||||
|
|
|
@ -285,3 +285,51 @@ class App.User extends App.Model
|
||||||
outOfOfficeText: ->
|
outOfOfficeText: ->
|
||||||
return @preferences.out_of_office_text if !_.isEmpty(@preferences.out_of_office_text)
|
return @preferences.out_of_office_text if !_.isEmpty(@preferences.out_of_office_text)
|
||||||
App.User.outOfOfficeTextPlaceholder()
|
App.User.outOfOfficeTextPlaceholder()
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
Checks if requester has given access level on requested.
|
||||||
|
Possible access levels are: read, update and delete
|
||||||
|
See backend method User#access?
|
||||||
|
|
||||||
|
requester = App.User.find(1)
|
||||||
|
requested = App.User.find(3)
|
||||||
|
result = requested.isAccessibleBy(requester, 'read')
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
true|false
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
isAccessibleBy: (requester, access) ->
|
||||||
|
return true if requester.permission('admin')
|
||||||
|
|
||||||
|
capitalized = access.charAt(0).toUpperCase() + access.slice(1)
|
||||||
|
accessMethod = 'is' + capitalized + 'ableBy'
|
||||||
|
@[accessMethod](requester)
|
||||||
|
|
||||||
|
isReadableBy: (requester) ->
|
||||||
|
return true if @ownAccount(requester)
|
||||||
|
return true if requester.permission('admin.*')
|
||||||
|
return true if requester.permission('ticket.agent')
|
||||||
|
# check same organization for customers
|
||||||
|
return false if !requester.permission('ticket.customer')
|
||||||
|
@sameOrganization?(requester)
|
||||||
|
|
||||||
|
isChangeableBy: (requester) ->
|
||||||
|
return true if requester.permission('admin.user')
|
||||||
|
# allow agents to change customers
|
||||||
|
return false if !requester.permission('ticket.agent')
|
||||||
|
@permission('ticket.customer')
|
||||||
|
|
||||||
|
isDeleteableBy: (requester) ->
|
||||||
|
requester.permission('admin.user')
|
||||||
|
|
||||||
|
ownAccount: (requester) ->
|
||||||
|
@id is requester.id
|
||||||
|
|
||||||
|
sameOrganization: (requester) ->
|
||||||
|
return false if @organization_id is null
|
||||||
|
return false if requester.organization_id is null
|
||||||
|
@organization_id == requester.organization_id
|
||||||
|
|
|
@ -4,35 +4,32 @@ module ChecksUserAttributesByCurrentUserPermission
|
||||||
private
|
private
|
||||||
|
|
||||||
def check_attributes_by_current_user_permission(params)
|
def check_attributes_by_current_user_permission(params)
|
||||||
|
# admins can do whatever they want
|
||||||
return true if current_user.permissions?('admin.user')
|
return true if current_user.permissions?('admin.user')
|
||||||
|
|
||||||
%i[role_ids roles].each do |key|
|
# non-agents (customers) can't set anything
|
||||||
next if !params[key]
|
response_access_deny if !current_user.permissions?('ticket.agent')
|
||||||
if current_user.permissions?('ticket.agent')
|
|
||||||
params.delete(key)
|
# regular agents are not allowed to set Groups and Roles
|
||||||
else
|
%w[Role Group].each do |model|
|
||||||
logger.info "Role assignment is only allowed by admin! current_user_id: #{current_user.id} assigned to #{params[key].inspect}"
|
|
||||||
raise Exceptions::NotAuthorized, 'This role assignment is only allowed by admin!'
|
%w[_ids s].each do |suffix|
|
||||||
|
attribute = "#{model.downcase}#{suffix}"
|
||||||
|
values = params[attribute]
|
||||||
|
|
||||||
|
next if values.nil?
|
||||||
|
|
||||||
|
logger.warn "#{model} assignment is only allowed by admin! User with ID #{current_user.id} tried to assign #{values.inspect}"
|
||||||
|
params.delete(attribute)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if current_user.permissions?('ticket.agent') && !params[:role_ids] && !params[:roles] && params[:id].blank?
|
|
||||||
|
# check for create requests and set
|
||||||
|
# signup roles if no other roles are given
|
||||||
|
return true if params[:id].present?
|
||||||
|
return true if params[:role_ids]
|
||||||
|
return true if params[:roles]
|
||||||
params[:role_ids] = Role.signup_role_ids
|
params[:role_ids] = Role.signup_role_ids
|
||||||
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
%i[group_ids groups].each do |key|
|
|
||||||
next if !params[key]
|
|
||||||
if current_user.permissions?('ticket.agent')
|
|
||||||
params.delete(key)
|
|
||||||
else
|
|
||||||
logger.info "Group relation assignment is only allowed by admin! current_user_id: #{current_user.id} assigned to #{params[key].inspect}"
|
|
||||||
raise Exceptions::NotAuthorized, 'Group relation is only allowed by admin!'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true if current_user.permissions?('ticket.agent')
|
|
||||||
|
|
||||||
response_access_deny
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -116,7 +116,7 @@ class UsersController < ApplicationController
|
||||||
|
|
||||||
# check if it's first user, the admin user
|
# check if it's first user, the admin user
|
||||||
# inital admin account
|
# inital admin account
|
||||||
count = User.all.count()
|
count = User.all.count
|
||||||
admin_account_exists = true
|
admin_account_exists = true
|
||||||
if count <= 2
|
if count <= 2
|
||||||
admin_account_exists = false
|
admin_account_exists = false
|
||||||
|
@ -156,7 +156,7 @@ class UsersController < ApplicationController
|
||||||
Role.where(name: %w[Admin Agent]).each do |role|
|
Role.where(name: %w[Admin Agent]).each do |role|
|
||||||
role_ids.push role.id
|
role_ids.push role.id
|
||||||
end
|
end
|
||||||
Group.all().each do |group|
|
Group.all.each do |group|
|
||||||
group_ids.push group.id
|
group_ids.push group.id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -207,6 +207,7 @@ class UsersController < ApplicationController
|
||||||
|
|
||||||
# send inviteation if needed / only if session exists
|
# send inviteation if needed / only if session exists
|
||||||
if params[:invite].present? && current_user
|
if params[:invite].present? && current_user
|
||||||
|
sleep 5 if ENV['REMOTE_URL'].present?
|
||||||
token = Token.create(action: 'PasswordReset', user_id: user.id)
|
token = Token.create(action: 'PasswordReset', user_id: user.id)
|
||||||
NotificationFactory::Mailer.notification(
|
NotificationFactory::Mailer.notification(
|
||||||
template: 'user_invite',
|
template: 'user_invite',
|
||||||
|
@ -261,8 +262,6 @@ class UsersController < ApplicationController
|
||||||
# @response_message 200 [User] Updated User record.
|
# @response_message 200 [User] Updated User record.
|
||||||
# @response_message 401 Invalid session.
|
# @response_message 401 Invalid session.
|
||||||
def update
|
def update
|
||||||
check_attributes_by_current_user_permission(params)
|
|
||||||
|
|
||||||
user = User.find(params[:id])
|
user = User.find(params[:id])
|
||||||
access!(user, 'change')
|
access!(user, 'change')
|
||||||
|
|
||||||
|
@ -273,19 +272,11 @@ class UsersController < ApplicationController
|
||||||
clean_params = User.param_cleanup(clean_params, true)
|
clean_params = User.param_cleanup(clean_params, true)
|
||||||
user.update!(clean_params)
|
user.update!(clean_params)
|
||||||
|
|
||||||
# only allow Admin's
|
# presence and permissions were checked via `check_attributes_by_current_user_permission`
|
||||||
if current_user.permissions?('admin.user') && (params[:role_ids] || params[:roles])
|
privileged_attributes = params.slice(:role_ids, :roles, :group_ids, :groups, :organization_ids, :organizations)
|
||||||
user.associations_from_param(role_ids: params[:role_ids], roles: params[:roles])
|
|
||||||
end
|
|
||||||
|
|
||||||
# only allow Admin's
|
if privileged_attributes.present?
|
||||||
if current_user.permissions?('admin.user') && (params[:group_ids] || params[:groups])
|
user.associations_from_param(privileged_attributes)
|
||||||
user.associations_from_param(group_ids: params[:group_ids], groups: params[:groups])
|
|
||||||
end
|
|
||||||
|
|
||||||
# only allow Admin's and Agent's
|
|
||||||
if current_user.permissions?(['admin.user', 'ticket.agent']) && (params[:organization_ids] || params[:organizations])
|
|
||||||
user.associations_from_param(organization_ids: params[:organization_ids], organizations: params[:organizations])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1118,5 +1109,4 @@ curl http://localhost/api/v1/users/avatar -v -u #{login}:#{password} -H "Content
|
||||||
end
|
end
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -136,18 +136,13 @@ returns
|
||||||
private_class_method
|
private_class_method
|
||||||
|
|
||||||
def self.permission_ids_by_name(keys)
|
def self.permission_ids_by_name(keys)
|
||||||
if keys.class != Array
|
Array(keys).each_with_object([]) do |key, result|
|
||||||
keys = [keys]
|
|
||||||
end
|
|
||||||
permission_ids = []
|
|
||||||
keys.each do |key|
|
|
||||||
::Permission.with_parents(key).each do |local_key|
|
::Permission.with_parents(key).each do |local_key|
|
||||||
permission = ::Permission.lookup(name: local_key)
|
permission = ::Permission.lookup(name: local_key)
|
||||||
next if !permission
|
next if !permission
|
||||||
permission_ids.push permission.id
|
result.push permission.id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
permission_ids
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -13,19 +13,10 @@ class User
|
||||||
# #=> true
|
# #=> true
|
||||||
#
|
#
|
||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
def access?(user, _access)
|
def access?(requester, access)
|
||||||
|
# full admins can do whatever they want
|
||||||
# check agent
|
return true if requester.permissions?('admin')
|
||||||
return true if user.permissions?('admin.user')
|
send("#{access}able_by?".to_sym, requester)
|
||||||
return true if user.permissions?('ticket.agent')
|
|
||||||
|
|
||||||
# check customer
|
|
||||||
if user.permissions?('ticket.customer')
|
|
||||||
# access ok if its own user
|
|
||||||
return id == user.id
|
|
||||||
end
|
|
||||||
|
|
||||||
false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks the given access of a given user for another user and fails with an exception.
|
# Checks the given access of a given user for another user and fails with an exception.
|
||||||
|
@ -42,5 +33,37 @@ class User
|
||||||
return if access?(user, access)
|
return if access?(user, access)
|
||||||
raise Exceptions::NotAuthorized
|
raise Exceptions::NotAuthorized
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def readable_by?(requester)
|
||||||
|
return true if own_account?(requester)
|
||||||
|
return true if requester.permissions?('admin.*')
|
||||||
|
return true if requester.permissions?('ticket.agent')
|
||||||
|
# check same organization for customers
|
||||||
|
return false if !requester.permissions?('ticket.customer')
|
||||||
|
same_organization?(requester)
|
||||||
|
end
|
||||||
|
|
||||||
|
def changeable_by?(requester)
|
||||||
|
return true if requester.permissions?('admin.user')
|
||||||
|
# allow agents to change customers
|
||||||
|
return false if !requester.permissions?('ticket.agent')
|
||||||
|
permissions?('ticket.customer')
|
||||||
|
end
|
||||||
|
|
||||||
|
def deleteable_by?(requester)
|
||||||
|
requester.permissions?('admin.user')
|
||||||
|
end
|
||||||
|
|
||||||
|
def own_account?(requester)
|
||||||
|
id == requester.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def same_organization?(requester)
|
||||||
|
return false if organization_id.blank?
|
||||||
|
return false if requester.organization_id.blank?
|
||||||
|
organization_id == requester.organization_id
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,6 +19,7 @@ if [ "$LEVEL" == '1' ]; then
|
||||||
rm test/browser/admin_overview_test.rb
|
rm test/browser/admin_overview_test.rb
|
||||||
rm test/browser/admin_role_test.rb
|
rm test/browser/admin_role_test.rb
|
||||||
# test/browser/agent_navigation_and_title_test.rb
|
# test/browser/agent_navigation_and_title_test.rb
|
||||||
|
# test/browser/agent_organization_profile_test.rb
|
||||||
rm test/browser/agent_ticket_attachment_test.rb
|
rm test/browser/agent_ticket_attachment_test.rb
|
||||||
rm test/browser/agent_ticket_auto_assignment_test.rb
|
rm test/browser/agent_ticket_auto_assignment_test.rb
|
||||||
rm test/browser/agent_ticket_create_reset_customer_selection_test.rb
|
rm test/browser/agent_ticket_create_reset_customer_selection_test.rb
|
||||||
|
@ -63,6 +64,7 @@ if [ "$LEVEL" == '1' ]; then
|
||||||
# test/browser/taskbar_session_test.rb
|
# test/browser/taskbar_session_test.rb
|
||||||
# test/browser/taskbar_task_test.rb
|
# test/browser/taskbar_task_test.rb
|
||||||
# test/browser/translation_test.rb
|
# test/browser/translation_test.rb
|
||||||
|
rm test/browser/user_access_permissions_test.rb
|
||||||
rm test/browser/user_switch_cache_test.rb
|
rm test/browser/user_switch_cache_test.rb
|
||||||
|
|
||||||
elif [ "$LEVEL" == '2' ]; then
|
elif [ "$LEVEL" == '2' ]; then
|
||||||
|
@ -124,7 +126,8 @@ elif [ "$LEVEL" == '2' ]; then
|
||||||
rm test/browser/taskbar_session_test.rb
|
rm test/browser/taskbar_session_test.rb
|
||||||
rm test/browser/taskbar_task_test.rb
|
rm test/browser/taskbar_task_test.rb
|
||||||
rm test/browser/translation_test.rb
|
rm test/browser/translation_test.rb
|
||||||
#rm test/browser/user_switch_cache_test.rb
|
# test/browser/user_access_permissions_test.rb
|
||||||
|
# test/browser/user_switch_cache_test.rb
|
||||||
|
|
||||||
elif [ "$LEVEL" == '3' ]; then
|
elif [ "$LEVEL" == '3' ]; then
|
||||||
echo "slicing level 3"
|
echo "slicing level 3"
|
||||||
|
@ -185,6 +188,7 @@ elif [ "$LEVEL" == '3' ]; then
|
||||||
rm test/browser/taskbar_session_test.rb
|
rm test/browser/taskbar_session_test.rb
|
||||||
rm test/browser/taskbar_task_test.rb
|
rm test/browser/taskbar_task_test.rb
|
||||||
rm test/browser/translation_test.rb
|
rm test/browser/translation_test.rb
|
||||||
|
rm test/browser/user_access_permissions_test.rb
|
||||||
rm test/browser/user_switch_cache_test.rb
|
rm test/browser/user_switch_cache_test.rb
|
||||||
|
|
||||||
elif [ "$LEVEL" == '4' ]; then
|
elif [ "$LEVEL" == '4' ]; then
|
||||||
|
@ -246,6 +250,7 @@ elif [ "$LEVEL" == '4' ]; then
|
||||||
rm test/browser/taskbar_session_test.rb
|
rm test/browser/taskbar_session_test.rb
|
||||||
rm test/browser/taskbar_task_test.rb
|
rm test/browser/taskbar_task_test.rb
|
||||||
rm test/browser/translation_test.rb
|
rm test/browser/translation_test.rb
|
||||||
|
rm test/browser/user_access_permissions_test.rb
|
||||||
rm test/browser/user_switch_cache_test.rb
|
rm test/browser/user_switch_cache_test.rb
|
||||||
|
|
||||||
elif [ "$LEVEL" == '5' ]; then
|
elif [ "$LEVEL" == '5' ]; then
|
||||||
|
@ -261,7 +266,7 @@ elif [ "$LEVEL" == '5' ]; then
|
||||||
# test/browser/admin_overview_test.rb
|
# test/browser/admin_overview_test.rb
|
||||||
rm test/browser/admin_role_test.rb
|
rm test/browser/admin_role_test.rb
|
||||||
rm test/browser/agent_navigation_and_title_test.rb
|
rm test/browser/agent_navigation_and_title_test.rb
|
||||||
# test/browser/agent_organization_profile_test.rb
|
rm test/browser/agent_organization_profile_test.rb
|
||||||
rm test/browser/agent_ticket_attachment_test.rb
|
rm test/browser/agent_ticket_attachment_test.rb
|
||||||
rm test/browser/agent_ticket_auto_assignment_test.rb
|
rm test/browser/agent_ticket_auto_assignment_test.rb
|
||||||
rm test/browser/agent_ticket_create_reset_customer_selection_test.rb
|
rm test/browser/agent_ticket_create_reset_customer_selection_test.rb
|
||||||
|
@ -306,6 +311,7 @@ elif [ "$LEVEL" == '5' ]; then
|
||||||
rm test/browser/taskbar_session_test.rb
|
rm test/browser/taskbar_session_test.rb
|
||||||
rm test/browser/taskbar_task_test.rb
|
rm test/browser/taskbar_task_test.rb
|
||||||
rm test/browser/translation_test.rb
|
rm test/browser/translation_test.rb
|
||||||
|
rm test/browser/user_access_permissions_test.rb
|
||||||
rm test/browser/user_switch_cache_test.rb
|
rm test/browser/user_switch_cache_test.rb
|
||||||
|
|
||||||
elif [ "$LEVEL" == '6' ]; then
|
elif [ "$LEVEL" == '6' ]; then
|
||||||
|
@ -369,6 +375,7 @@ elif [ "$LEVEL" == '6' ]; then
|
||||||
rm test/browser/taskbar_session_test.rb
|
rm test/browser/taskbar_session_test.rb
|
||||||
rm test/browser/taskbar_task_test.rb
|
rm test/browser/taskbar_task_test.rb
|
||||||
rm test/browser/translation_test.rb
|
rm test/browser/translation_test.rb
|
||||||
|
rm test/browser/user_access_permissions_test.rb
|
||||||
rm test/browser/user_switch_cache_test.rb
|
rm test/browser/user_switch_cache_test.rb
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
676
spec/controllers/users_controller_spec.rb
Normal file
676
spec/controllers/users_controller_spec.rb
Normal file
|
@ -0,0 +1,676 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe UsersController, type: :controller 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 'POST #create' do
|
||||||
|
|
||||||
|
let(:attributes) { attributes_params_for(:user) }
|
||||||
|
|
||||||
|
it 'responds unauthorized for customer' do
|
||||||
|
requester = create(:customer_user)
|
||||||
|
authenticated_as(requester)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
post :create, params: attributes
|
||||||
|
end.to not_change {
|
||||||
|
User.count
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
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 :create, 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 unauthorized for sub admin without admin.user' do
|
||||||
|
authenticated_as(admin_without_admin_user_permissions)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
post :create, params: payload
|
||||||
|
end.to not_change {
|
||||||
|
User.count
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'responds successful for agent but removes assignment' do
|
||||||
|
requester = create(:agent_user)
|
||||||
|
authenticated_as(requester)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
post :create, 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.map { |g| [g.name, ['full']] }.to_h
|
||||||
|
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.map { |g| [g.id, ['full']] }.to_h
|
||||||
|
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 :create, 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 unauthorized for sub admin without admin.user' do
|
||||||
|
authenticated_as(admin_without_admin_user_permissions)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
post :create, params: payload
|
||||||
|
end.to not_change {
|
||||||
|
User.count
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'responds successful for agent but removes assignment' do
|
||||||
|
requester = create(:agent_user)
|
||||||
|
authenticated_as(requester)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
post :create, 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 'PUT #update' do
|
||||||
|
|
||||||
|
def authorized_update_request(requester:, requested:)
|
||||||
|
authenticated_as(requester)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
put :update, params: cleaned_params_for(requested).merge(firstname: 'Changed')
|
||||||
|
end.to change {
|
||||||
|
requested.reload.firstname
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unauthorized_update_request(requester:, requested:)
|
||||||
|
authenticated_as(requester)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
put :update, params: cleaned_params_for(requested).merge(firstname: 'Changed')
|
||||||
|
end.to not_change {
|
||||||
|
requested.reload.attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
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_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is successful for agent' do
|
||||||
|
authorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:agent_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is successful for customer' do
|
||||||
|
authorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:customer_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'request by sub admin without admin.user' do
|
||||||
|
|
||||||
|
let(:requester) { admin_without_admin_user_permissions }
|
||||||
|
|
||||||
|
it 'is unauthorized for same admin' do
|
||||||
|
unauthorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: requester,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for other admin' do
|
||||||
|
unauthorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:admin_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for agent' do
|
||||||
|
unauthorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:agent_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for customer' do
|
||||||
|
unauthorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:customer_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'request by agent' do
|
||||||
|
|
||||||
|
let(:requester) { create(:agent_user) }
|
||||||
|
|
||||||
|
it 'is unauthorized for admin' do
|
||||||
|
unauthorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:admin_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized same agent' do
|
||||||
|
unauthorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: requester,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for other agent' do
|
||||||
|
unauthorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:agent_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is successful for customer' do
|
||||||
|
authorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:customer_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'request by customer' do
|
||||||
|
|
||||||
|
let(:requester) { create(:customer_user) }
|
||||||
|
|
||||||
|
it 'is unauthorized for admin' do
|
||||||
|
unauthorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:admin_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for agent' do
|
||||||
|
unauthorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:agent_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for same customer' do
|
||||||
|
unauthorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: requester,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for other customer' do
|
||||||
|
unauthorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:customer_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for same organization' do
|
||||||
|
same_organization = create(:organization)
|
||||||
|
|
||||||
|
requester.update!(organization: same_organization)
|
||||||
|
|
||||||
|
unauthorized_update_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:customer_user, 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 :update, params: payload
|
||||||
|
end.to change {
|
||||||
|
value_of_attribute
|
||||||
|
}
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'responds unauthorized for sub admin without admin.user' do
|
||||||
|
authenticated_as(admin_without_admin_user_permissions)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
put :update, params: payload
|
||||||
|
end.to not_change {
|
||||||
|
value_of_attribute
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'permitted agent update' do
|
||||||
|
|
||||||
|
it 'responds successful for agent but removes assignment' do
|
||||||
|
requester = create(:agent_user)
|
||||||
|
authenticated_as(requester)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
put :update, 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_user)
|
||||||
|
authenticated_as(requester)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
put :update, 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.map { |g| [g.name, ['full']] }.to_h
|
||||||
|
}
|
||||||
|
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.map { |g| [g.id, ['full']] }.to_h
|
||||||
|
}
|
||||||
|
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
|
||||||
|
|
||||||
|
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 'DELETE #destroy' do
|
||||||
|
|
||||||
|
def authorized_destroy_request(requester:, requested:)
|
||||||
|
authenticated_as(requester)
|
||||||
|
|
||||||
|
delete :destroy, params: { id: requested.id }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(requested).not_to exist_in_database
|
||||||
|
end
|
||||||
|
|
||||||
|
def unauthorized_destroy_request(requester:, requested:)
|
||||||
|
authenticated_as(requester)
|
||||||
|
|
||||||
|
delete :destroy, params: { id: requested.id }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unauthorized)
|
||||||
|
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_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is successful for agent' do
|
||||||
|
authorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:agent_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is successful for customer' do
|
||||||
|
authorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:customer_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'request by sub admin without admin.user' do
|
||||||
|
|
||||||
|
let(:requester) { admin_without_admin_user_permissions }
|
||||||
|
|
||||||
|
it 'is unauthorized for same admin' do
|
||||||
|
unauthorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: requester,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for other admin' do
|
||||||
|
unauthorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:admin_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for agent' do
|
||||||
|
unauthorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:agent_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for customer' do
|
||||||
|
unauthorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:customer_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'request by agent' do
|
||||||
|
|
||||||
|
let(:requester) { create(:agent_user) }
|
||||||
|
|
||||||
|
it 'is unauthorized for admin' do
|
||||||
|
unauthorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:admin_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized same agent' do
|
||||||
|
unauthorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: requester,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for other agent' do
|
||||||
|
unauthorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:agent_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for customer' do
|
||||||
|
unauthorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:customer_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'request by customer' do
|
||||||
|
|
||||||
|
let(:requester) { create(:customer_user) }
|
||||||
|
|
||||||
|
it 'is unauthorized for admin' do
|
||||||
|
unauthorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:admin_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for agent' do
|
||||||
|
unauthorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:agent_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for same customer' do
|
||||||
|
unauthorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: requester,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for other customer' do
|
||||||
|
unauthorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:customer_user),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized for same organization' do
|
||||||
|
same_organization = create(:organization)
|
||||||
|
|
||||||
|
requester.update!(organization: same_organization)
|
||||||
|
|
||||||
|
unauthorized_destroy_request(
|
||||||
|
requester: requester,
|
||||||
|
requested: create(:customer_user, organization: same_organization),
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -11,7 +11,7 @@ FactoryBot.define do
|
||||||
firstname 'Nicole'
|
firstname 'Nicole'
|
||||||
lastname 'Braun'
|
lastname 'Braun'
|
||||||
email { generate(:email) }
|
email { generate(:email) }
|
||||||
password 'zammad'
|
password nil
|
||||||
active true
|
active true
|
||||||
login_failed 0
|
login_failed 0
|
||||||
updated_by_id 1
|
updated_by_id 1
|
||||||
|
|
|
@ -3,14 +3,15 @@ require 'lib/auth/backend_examples'
|
||||||
|
|
||||||
RSpec.describe Auth::Internal do
|
RSpec.describe Auth::Internal do
|
||||||
|
|
||||||
let(:user) { create(:user) }
|
let(:password) { 'zammad' }
|
||||||
|
let(:user) { create(:user, password: password) }
|
||||||
let(:instance) { described_class.new({ adapter: described_class.name }) }
|
let(:instance) { described_class.new({ adapter: described_class.name }) }
|
||||||
|
|
||||||
context '#valid?' do
|
context '#valid?' do
|
||||||
it_behaves_like 'Auth backend'
|
it_behaves_like 'Auth backend'
|
||||||
|
|
||||||
it 'authenticates via password' do
|
it 'authenticates via password' do
|
||||||
result = instance.valid?(user, 'zammad')
|
result = instance.valid?(user, password)
|
||||||
expect(result).to be true
|
expect(result).to be true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -21,17 +22,16 @@ RSpec.describe Auth::Internal do
|
||||||
|
|
||||||
it 'converts legacy sha2 passwords' do
|
it 'converts legacy sha2 passwords' do
|
||||||
|
|
||||||
pw_plain = 'zammad'
|
sha2 = PasswordHash.sha2(password)
|
||||||
sha2_pw = PasswordHash.sha2(pw_plain)
|
user = create(:user, password: sha2)
|
||||||
user = create(:user, password: sha2_pw)
|
|
||||||
|
|
||||||
expect(PasswordHash.crypted?(user.password)).to be true
|
expect(PasswordHash.crypted?(user.password)).to be true
|
||||||
expect(PasswordHash.legacy?(user.password, pw_plain)).to be true
|
expect(PasswordHash.legacy?(user.password, password)).to be true
|
||||||
|
|
||||||
result = instance.valid?(user, pw_plain)
|
result = instance.valid?(user, password)
|
||||||
expect(result).to be true
|
expect(result).to be true
|
||||||
|
|
||||||
expect(PasswordHash.legacy?(user.password, pw_plain)).to be false
|
expect(PasswordHash.legacy?(user.password, password)).to be false
|
||||||
expect(PasswordHash.crypted?(user.password)).to be true
|
expect(PasswordHash.crypted?(user.password)).to be true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -40,8 +40,9 @@ RSpec.describe Auth do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'authenticates users' do
|
it 'authenticates users' do
|
||||||
user = create(:user)
|
password = 'zammad'
|
||||||
result = described_class.valid?(user, 'zammad')
|
user = create(:user, password: password)
|
||||||
|
result = described_class.valid?(user, password)
|
||||||
expect(result).to be true
|
expect(result).to be true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -95,8 +95,9 @@ RSpec.describe User do
|
||||||
context '.authenticate' do
|
context '.authenticate' do
|
||||||
|
|
||||||
it 'authenticates by username and password' do
|
it 'authenticates by username and password' do
|
||||||
user = create(:user)
|
password = 'zammad'
|
||||||
result = described_class.authenticate(user.login, 'zammad')
|
user = create(:user, password: password)
|
||||||
|
result = described_class.authenticate(user.login, password)
|
||||||
expect(result).to be_an(User)
|
expect(result).to be_an(User)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -158,7 +159,7 @@ RSpec.describe User do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context '.password_reset_via_token' do
|
context '#password_reset_via_token' do
|
||||||
|
|
||||||
it 'changes the password of the token user and destroys the token' do
|
it 'changes the password of the token user and destroys the token' do
|
||||||
token = create(:token_password_reset)
|
token = create(:token_password_reset)
|
||||||
|
@ -191,4 +192,342 @@ RSpec.describe User do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context '.access?' 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]) }
|
||||||
|
|
||||||
|
context 'read' do
|
||||||
|
|
||||||
|
context 'admin' do
|
||||||
|
|
||||||
|
let(:requested) { create(:admin_user) }
|
||||||
|
|
||||||
|
it 'is possible for admin.user' do
|
||||||
|
requester = admin_with_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'read')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is possible for sub admin without admin.user' do
|
||||||
|
requester = admin_without_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'read')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is possible for agent' do
|
||||||
|
requester = create(:agent_user)
|
||||||
|
access = requested.access?(requester, 'read')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for customer' do
|
||||||
|
requester = create(:customer_user)
|
||||||
|
access = requested.access?(requester, 'read')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'agent' do
|
||||||
|
|
||||||
|
let(:requested) { create(:agent_user) }
|
||||||
|
|
||||||
|
it 'is possible for admin.user' do
|
||||||
|
requester = admin_with_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'read')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is possible for sub admin without admin.user' do
|
||||||
|
requester = admin_without_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'read')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is possible for agent' do
|
||||||
|
requester = create(:agent_user)
|
||||||
|
access = requested.access?(requester, 'read')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for customer' do
|
||||||
|
requester = create(:customer_user)
|
||||||
|
access = requested.access?(requester, 'read')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'customer' do
|
||||||
|
|
||||||
|
let(:requested) { create(:customer_user) }
|
||||||
|
|
||||||
|
it 'is possible for admin.user' do
|
||||||
|
requester = admin_with_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'read')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is possible for sub admin without admin.user' do
|
||||||
|
requester = admin_without_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'read')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is possible for agent' do
|
||||||
|
requester = create(:agent_user)
|
||||||
|
access = requested.access?(requester, 'read')
|
||||||
|
expect(access).to be(true)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is possible for same customer' do
|
||||||
|
access = requested.access?(requested, 'read')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is possible for same organization' do
|
||||||
|
organization = create(:organization)
|
||||||
|
requester = create(:customer_user, organization: organization)
|
||||||
|
requested.update!(organization: organization)
|
||||||
|
access = requested.access?(requester, 'read')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for different customer' do
|
||||||
|
requester = create(:customer_user)
|
||||||
|
access = requested.access?(requester, 'read')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'change' do
|
||||||
|
|
||||||
|
context 'admin' do
|
||||||
|
|
||||||
|
let(:requested) { create(:admin_user) }
|
||||||
|
|
||||||
|
it 'is possible for admin.user' do
|
||||||
|
requester = admin_with_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'change')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for sub admin without admin.user' do
|
||||||
|
requester = admin_without_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'change')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for same for sub admin without admin.user' do
|
||||||
|
access = admin_without_admin_user_permissions.access?(admin_without_admin_user_permissions, 'change')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for agent' do
|
||||||
|
requester = create(:agent_user)
|
||||||
|
access = requested.access?(requester, 'change')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for customer' do
|
||||||
|
requester = create(:customer_user)
|
||||||
|
access = requested.access?(requester, 'change')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'agent' do
|
||||||
|
|
||||||
|
let(:requested) { create(:agent_user) }
|
||||||
|
|
||||||
|
it 'is possible for admin.user' do
|
||||||
|
requester = admin_with_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'change')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for sub admin without admin.user' do
|
||||||
|
requester = admin_without_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'change')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for same agent' do
|
||||||
|
access = requested.access?(requested, 'change')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for other agent' do
|
||||||
|
requester = create(:agent_user)
|
||||||
|
access = requested.access?(requester, 'change')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for customer' do
|
||||||
|
requester = create(:customer_user)
|
||||||
|
access = requested.access?(requester, 'change')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'customer' do
|
||||||
|
|
||||||
|
let(:requested) { create(:customer_user) }
|
||||||
|
|
||||||
|
it 'is possible for admin.user' do
|
||||||
|
requester = admin_with_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'change')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for sub admin without admin.user' do
|
||||||
|
requester = admin_without_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'change')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is possible for agent' do
|
||||||
|
requester = create(:agent_user)
|
||||||
|
access = requested.access?(requester, 'change')
|
||||||
|
expect(access).to be(true)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for same customer' do
|
||||||
|
access = requested.access?(requested, 'change')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for same organization' do
|
||||||
|
organization = create(:organization)
|
||||||
|
requester = create(:customer_user, organization: organization)
|
||||||
|
requested.update!(organization: organization)
|
||||||
|
access = requested.access?(requester, 'change')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for different customer' do
|
||||||
|
requester = create(:customer_user)
|
||||||
|
access = requested.access?(requester, 'change')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'delete' do
|
||||||
|
|
||||||
|
context 'admin' do
|
||||||
|
|
||||||
|
let(:requested) { create(:admin_user) }
|
||||||
|
|
||||||
|
it 'is possible for admin.user' do
|
||||||
|
requester = admin_with_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'delete')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for sub admin without admin.user' do
|
||||||
|
requester = admin_without_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'delete')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for agent' do
|
||||||
|
requester = create(:agent_user)
|
||||||
|
access = requested.access?(requester, 'delete')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for customer' do
|
||||||
|
requester = create(:customer_user)
|
||||||
|
access = requested.access?(requester, 'delete')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'agent' do
|
||||||
|
|
||||||
|
let(:requested) { create(:agent_user) }
|
||||||
|
|
||||||
|
it 'is possible for admin.user' do
|
||||||
|
requester = admin_with_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'delete')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for sub admin without admin.user' do
|
||||||
|
requester = admin_without_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'delete')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for agent' do
|
||||||
|
requester = create(:agent_user)
|
||||||
|
access = requested.access?(requester, 'delete')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for customer' do
|
||||||
|
requester = create(:customer_user)
|
||||||
|
access = requested.access?(requester, 'delete')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'customer' do
|
||||||
|
|
||||||
|
let(:requested) { create(:customer_user) }
|
||||||
|
|
||||||
|
it 'is possible for admin.user' do
|
||||||
|
requester = admin_with_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'delete')
|
||||||
|
expect(access).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for sub admin without admin.user' do
|
||||||
|
requester = admin_without_admin_user_permissions
|
||||||
|
access = requested.access?(requester, 'delete')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for agent' do
|
||||||
|
requester = create(:agent_user)
|
||||||
|
access = requested.access?(requester, 'delete')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for same customer' do
|
||||||
|
access = requested.access?(requested, 'delete')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for same organization' do
|
||||||
|
organization = create(:organization)
|
||||||
|
requester = create(:customer_user, organization: organization)
|
||||||
|
requested.update!(organization: organization)
|
||||||
|
access = requested.access?(requester, 'delete')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is not possible for different customer' do
|
||||||
|
requester = create(:customer_user)
|
||||||
|
access = requested.access?(requester, 'delete')
|
||||||
|
expect(access).to be(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
26
spec/support/avatar_check.rb
Normal file
26
spec/support/avatar_check.rb
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
module ZammadSpecSupportAvatarCheck
|
||||||
|
|
||||||
|
def self.included(base)
|
||||||
|
|
||||||
|
# Execute in RSpec class context
|
||||||
|
base.class_exec do
|
||||||
|
|
||||||
|
# This method disables the avatar for email check for all examples.
|
||||||
|
# It's possible to re-enable the check by adding the
|
||||||
|
# meta tag `perform_avatar_for_email_check` to the needing example:
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# it 'does stuff with avatar check', perform_avatar_for_email_check: true do
|
||||||
|
#
|
||||||
|
before(:each) do |example|
|
||||||
|
if !example.metadata[:perform_avatar_for_email_check]
|
||||||
|
allow(Avatar).to receive(:auto_detection).and_return(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.include ZammadSpecSupportAvatarCheck
|
||||||
|
end
|
89
spec/support/controller.rb
Normal file
89
spec/support/controller.rb
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
module ZammadSpecSupportController
|
||||||
|
|
||||||
|
# Authenticates all requests of the current example as the given user.
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# authenticated_as(some_admin_user)
|
||||||
|
#
|
||||||
|
# @return nil
|
||||||
|
def authenticated_as(user)
|
||||||
|
session[:user_id] = user.id
|
||||||
|
end
|
||||||
|
|
||||||
|
# Provides a Hash of attributes for the given FactoryBot
|
||||||
|
# factory parameters which can be used as the params payload.
|
||||||
|
# Note that the attributes are "cleaned" so no created_by_id etc.
|
||||||
|
# is present.
|
||||||
|
#
|
||||||
|
# @see FactoryBot#attributes_for
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# attributes_params_for(:admin_user, email: 'custom@example.com')
|
||||||
|
# # => {firstname: 'Nicole', email: 'custom@example.com', ...}
|
||||||
|
#
|
||||||
|
# @return [Hash{Symbol => <String, Array, Hash>}] request cleaned attributes
|
||||||
|
def attributes_params_for(*args)
|
||||||
|
filter_unused_params(attributes_for(*args))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Provides a Hash of attributes for the given Model instance which can
|
||||||
|
# be used as the params payload.
|
||||||
|
# Note that the attributes are "cleaned" so no created_by_id etc.
|
||||||
|
# is present.
|
||||||
|
#
|
||||||
|
# @param [Hash] instance An ActiveRecord instance
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# cleaned_params_for(some_admin_user)
|
||||||
|
# # => {firstname: 'Nicole', email: 'admin@example.com', ...}
|
||||||
|
#
|
||||||
|
# @return [Hash{Symbol => <String, Array, Hash>}] request cleaned attributes
|
||||||
|
def cleaned_params_for(instance)
|
||||||
|
filter_unused_params(instance.attributes)
|
||||||
|
end
|
||||||
|
|
||||||
|
# This is a self explaining internal method.
|
||||||
|
#
|
||||||
|
# @see ApplicationModel#filter_unused_params
|
||||||
|
def filter_unused_params(unfiltered)
|
||||||
|
# let's get private
|
||||||
|
ApplicationModel.send(:filter_unused_params, unfiltered)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.included(base)
|
||||||
|
|
||||||
|
# Execute in RSpec class context
|
||||||
|
base.class_exec do
|
||||||
|
|
||||||
|
# This method disables the CSRF token validation for all controller
|
||||||
|
# examples. It's possible to re-enable the check by adding the
|
||||||
|
# meta tag `verify_csrf_token` to the needing example:
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# it 'does stuff with verified CSRF token', verify_csrf_token: true do
|
||||||
|
#
|
||||||
|
before(:each) do |example|
|
||||||
|
if !example.metadata[:verify_csrf_token]
|
||||||
|
allow(controller).to receive(:verify_csrf_token).and_return(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# This method disables the user device check for all controller
|
||||||
|
# examples. It's possible to re-enable the check by adding the
|
||||||
|
# meta tag `perform_user_device_check` to the needing example:
|
||||||
|
#
|
||||||
|
# @example
|
||||||
|
# it 'does stuff with user device check', perform_user_device_check: true do
|
||||||
|
#
|
||||||
|
before(:each) do |example|
|
||||||
|
if !example.metadata[:perform_user_device_check]
|
||||||
|
session[:user_device_updated_at] = Time.zone.now
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.include ZammadSpecSupportController, type: :controller
|
||||||
|
end
|
7
spec/support/custom_matchers.rb
Normal file
7
spec/support/custom_matchers.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# taken from https://makandracards.com/makandra/1080-rspec-matcher-to-check-if-an-activerecord-exists-in-the-database
|
||||||
|
RSpec::Matchers.define :exist_in_database do
|
||||||
|
|
||||||
|
match do |actual|
|
||||||
|
actual.class.exists?(actual.id)
|
||||||
|
end
|
||||||
|
end
|
28
spec/support/user_info.rb
Normal file
28
spec/support/user_info.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# This module registers a before and after each hook callback that
|
||||||
|
# resets the stored current_user_id in the UserInfo which will otherwise
|
||||||
|
# persists across multiple examples.
|
||||||
|
# This can lead to issues where actions were performed by a user created
|
||||||
|
# via a FactoryBot factory which will get removed after the example is
|
||||||
|
# completed. The UserInfo.current_user_id will persist which leads to e.g.
|
||||||
|
# DB ForeignKey violation errors.
|
||||||
|
module ZammadSpecSupportUserInfo
|
||||||
|
|
||||||
|
def self.included(base)
|
||||||
|
|
||||||
|
# Execute in RSpec class context
|
||||||
|
base.class_exec do
|
||||||
|
|
||||||
|
before(:each) do |_example|
|
||||||
|
UserInfo.current_user_id = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:each) do |_example|
|
||||||
|
UserInfo.current_user_id = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.include ZammadSpecSupportUserInfo
|
||||||
|
end
|
|
@ -38,6 +38,10 @@ class AgentTicketAttachmentTest < TestCase
|
||||||
#alert.accept()
|
#alert.accept()
|
||||||
#alert = alert.text
|
#alert = alert.text
|
||||||
|
|
||||||
|
# since selenium webdriver with firefox is not able to upload files, skipp here
|
||||||
|
# https://github.com/w3c/webdriver/issues/1230
|
||||||
|
return if browser == 'firefox'
|
||||||
|
|
||||||
# add attachment, attachment check should quiet
|
# add attachment, attachment check should quiet
|
||||||
file_upload(
|
file_upload(
|
||||||
css: '.content.active .attachmentPlaceholder-inputHolder input',
|
css: '.content.active .attachmentPlaceholder-inputHolder input',
|
||||||
|
@ -112,7 +116,7 @@ class AgentTicketAttachmentTest < TestCase
|
||||||
)
|
)
|
||||||
|
|
||||||
# check content and edit screen in instance 1
|
# check content and edit screen in instance 1
|
||||||
match(
|
watch_for(
|
||||||
css: '.content.active div.ticket-article',
|
css: '.content.active div.ticket-article',
|
||||||
value: 'test 6 - ticket 1-1',
|
value: 'test 6 - ticket 1-1',
|
||||||
)
|
)
|
||||||
|
|
|
@ -138,7 +138,7 @@ class ChatTest < TestCase
|
||||||
# init chat
|
# init chat
|
||||||
click(
|
click(
|
||||||
browser: customer,
|
browser: customer,
|
||||||
css: '.js-chat-open',
|
css: '.zammad-chat .js-chat-open',
|
||||||
)
|
)
|
||||||
exists(
|
exists(
|
||||||
browser: customer,
|
browser: customer,
|
||||||
|
@ -156,7 +156,7 @@ class ChatTest < TestCase
|
||||||
)
|
)
|
||||||
click(
|
click(
|
||||||
browser: customer,
|
browser: customer,
|
||||||
css: '.js-chat-toggle',
|
css: '.zammad-chat .js-chat-toggle .zammad-chat-header-icon',
|
||||||
)
|
)
|
||||||
watch_for_disappear(
|
watch_for_disappear(
|
||||||
browser: customer,
|
browser: customer,
|
||||||
|
@ -274,7 +274,7 @@ class ChatTest < TestCase
|
||||||
)
|
)
|
||||||
click(
|
click(
|
||||||
browser: customer,
|
browser: customer,
|
||||||
css: '.js-chat-toggle',
|
css: '.js-chat-toggle .zammad-chat-header-icon',
|
||||||
)
|
)
|
||||||
watch_for(
|
watch_for(
|
||||||
browser: agent,
|
browser: agent,
|
||||||
|
@ -401,7 +401,7 @@ class ChatTest < TestCase
|
||||||
)
|
)
|
||||||
click(
|
click(
|
||||||
browser: customer,
|
browser: customer,
|
||||||
css: '.zammad-chat .js-chat-toggle',
|
css: '.zammad-chat .js-chat-toggle .zammad-chat-header-icon',
|
||||||
)
|
)
|
||||||
watch_for_disappear(
|
watch_for_disappear(
|
||||||
browser: customer,
|
browser: customer,
|
||||||
|
@ -412,7 +412,7 @@ class ChatTest < TestCase
|
||||||
sleep 2
|
sleep 2
|
||||||
click(
|
click(
|
||||||
browser: customer,
|
browser: customer,
|
||||||
css: '.js-chat-open',
|
css: '.zammad-chat .js-chat-open',
|
||||||
)
|
)
|
||||||
exists(
|
exists(
|
||||||
browser: customer,
|
browser: customer,
|
||||||
|
@ -581,7 +581,7 @@ class ChatTest < TestCase
|
||||||
)
|
)
|
||||||
click(
|
click(
|
||||||
browser: customer,
|
browser: customer,
|
||||||
css: '.js-chat-toggle',
|
css: '.zammad-chat .js-chat-toggle .zammad-chat-header-icon',
|
||||||
)
|
)
|
||||||
watch_for(
|
watch_for(
|
||||||
browser: agent,
|
browser: agent,
|
||||||
|
|
|
@ -20,7 +20,8 @@ class SwitchToUserTest < TestCase
|
||||||
)
|
)
|
||||||
sleep 3
|
sleep 3
|
||||||
|
|
||||||
@browser.mouse.move_to(@browser.find_elements({ css: '.content.active .table-overview tbody tr:first-child' } )[0])
|
@browser.action.move_to(@browser.find_elements({ css: '.content.active .table-overview tbody tr:first-child' } )[0]).release.perform
|
||||||
|
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
click(
|
click(
|
||||||
css: '.content.active .icon-switchView',
|
css: '.content.active .icon-switchView',
|
||||||
|
|
376
test/browser/user_access_permissions_test.rb
Normal file
376
test/browser/user_access_permissions_test.rb
Normal file
|
@ -0,0 +1,376 @@
|
||||||
|
require 'browser_test_helper'
|
||||||
|
|
||||||
|
class AgentProfilePermissionsTest < TestCase
|
||||||
|
def test_agent_to_edit_customer_profile
|
||||||
|
@browser = browser_instance
|
||||||
|
login(
|
||||||
|
username: 'agent1@example.com',
|
||||||
|
password: 'test',
|
||||||
|
url: browser_url,
|
||||||
|
)
|
||||||
|
tasks_close_all()
|
||||||
|
|
||||||
|
# search and open user
|
||||||
|
user_open_by_search(value: 'Braun')
|
||||||
|
|
||||||
|
verify_task(
|
||||||
|
data: {
|
||||||
|
title: 'Nicole Braun',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .profile-window',
|
||||||
|
value: 'note',
|
||||||
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .profile-window',
|
||||||
|
value: 'email',
|
||||||
|
)
|
||||||
|
|
||||||
|
# update note
|
||||||
|
set(
|
||||||
|
css: '.content.active [data-name="note"]',
|
||||||
|
value: 'some note 123',
|
||||||
|
)
|
||||||
|
empty_search()
|
||||||
|
|
||||||
|
# check and change note again in edit screen
|
||||||
|
click(css: '.content.active .js-action .icon-arrow-down', fast: true)
|
||||||
|
click(css: '.content.active .js-action [data-type="edit"]')
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .modal',
|
||||||
|
value: 'note',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .modal',
|
||||||
|
value: 'some note 123',
|
||||||
|
)
|
||||||
|
|
||||||
|
set(
|
||||||
|
css: '.modal [name="lastname"]',
|
||||||
|
value: 'B2',
|
||||||
|
)
|
||||||
|
set(
|
||||||
|
css: '.modal [data-name="note"]',
|
||||||
|
value: 'some note abc',
|
||||||
|
)
|
||||||
|
click(css: '.content.active .modal button.js-submit')
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .profile-window',
|
||||||
|
value: 'some note abc',
|
||||||
|
)
|
||||||
|
|
||||||
|
verify_task(
|
||||||
|
data: {
|
||||||
|
title: 'Nicole B2',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# change lastname back
|
||||||
|
click(css: '.content.active .js-action .icon-arrow-down', fast: true)
|
||||||
|
click(css: '.content.active .js-action [data-type="edit"]')
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .modal',
|
||||||
|
value: 'note',
|
||||||
|
)
|
||||||
|
set(
|
||||||
|
css: '.modal [name="lastname"]',
|
||||||
|
value: 'Braun',
|
||||||
|
)
|
||||||
|
click(css: '.content.active .modal button.js-submit')
|
||||||
|
|
||||||
|
verify_task(
|
||||||
|
data: {
|
||||||
|
title: 'Nicole Braun',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_agent_edit_admin_profile
|
||||||
|
@browser = browser_instance
|
||||||
|
login(
|
||||||
|
username: 'agent1@example.com',
|
||||||
|
password: 'test',
|
||||||
|
url: browser_url,
|
||||||
|
)
|
||||||
|
tasks_close_all()
|
||||||
|
|
||||||
|
# search and open user
|
||||||
|
user_open_by_search(value: 'Test Master')
|
||||||
|
|
||||||
|
verify_task(
|
||||||
|
data: {
|
||||||
|
title: 'Test Master Agent',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .profile-window',
|
||||||
|
value: 'note',
|
||||||
|
)
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .profile-window',
|
||||||
|
value: 'email',
|
||||||
|
)
|
||||||
|
|
||||||
|
empty_search()
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
click(css: '.content.active .js-action .icon-arrow-down', fast: true)
|
||||||
|
|
||||||
|
exists_not(
|
||||||
|
css: '.content.active .js-action [data-type="edit"]'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_agent_to_edit_admin_ticket_user_details
|
||||||
|
@browser = browser_instance
|
||||||
|
login(
|
||||||
|
username: 'master@example.com',
|
||||||
|
password: 'test',
|
||||||
|
url: browser_url,
|
||||||
|
)
|
||||||
|
tasks_close_all()
|
||||||
|
|
||||||
|
ticket1 = ticket_create(
|
||||||
|
data: {
|
||||||
|
customer: 'master',
|
||||||
|
group: 'Users',
|
||||||
|
title: 'test_auto_assignment_1 - ticket 1',
|
||||||
|
body: 'test_auto_assignment_1 - ticket 1 - no auto assignment',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
tasks_close_all()
|
||||||
|
|
||||||
|
logout()
|
||||||
|
|
||||||
|
login(
|
||||||
|
username: 'agent1@example.com',
|
||||||
|
password: 'test',
|
||||||
|
url: browser_url,
|
||||||
|
)
|
||||||
|
tasks_close_all()
|
||||||
|
|
||||||
|
# open ticket#1
|
||||||
|
ticket_open_by_search(
|
||||||
|
number: ticket1[:number],
|
||||||
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .tabsSidebar-holder',
|
||||||
|
value: ticket1[:title],
|
||||||
|
)
|
||||||
|
|
||||||
|
click(css: '.content.active .tabsSidebar .tabsSidebar-tab[data-tab="customer"]')
|
||||||
|
click(css: '.content.active .sidebar[data-tab="customer"] .js-actions .dropdown-toggle')
|
||||||
|
exists_not(css: '.content.active .sidebar[data-tab="customer"] .js-actions [data-type="customer-edit"]')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_agent_to_edit_customer_ticket
|
||||||
|
@browser = browser_instance
|
||||||
|
|
||||||
|
login(
|
||||||
|
username: 'agent1@example.com',
|
||||||
|
password: 'test',
|
||||||
|
url: browser_url,
|
||||||
|
)
|
||||||
|
tasks_close_all()
|
||||||
|
|
||||||
|
ticket1 = ticket_create(
|
||||||
|
data: {
|
||||||
|
customer: 'nico',
|
||||||
|
group: 'Users',
|
||||||
|
title: 'test_auto_assignment_2 - ticket 2',
|
||||||
|
body: 'test_auto_assignment_2 - ticket 2 - no auto assignment',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# open ticket#1
|
||||||
|
ticket_open_by_search(
|
||||||
|
number: ticket1[:number],
|
||||||
|
)
|
||||||
|
|
||||||
|
click(css: '.content.active .tabsSidebar .tabsSidebar-tab[data-tab="customer"]')
|
||||||
|
click(css: '.content.active .sidebar[data-tab="customer"] .js-actions .dropdown-toggle')
|
||||||
|
click(css: '.content.active .sidebar[data-tab="customer"] .js-actions [data-type="customer-edit"]')
|
||||||
|
|
||||||
|
set(
|
||||||
|
css: '.modal [name="lastname"]',
|
||||||
|
value: 'B2',
|
||||||
|
)
|
||||||
|
|
||||||
|
set(
|
||||||
|
css: '.modal [data-name="note"]',
|
||||||
|
value: 'some note abc',
|
||||||
|
)
|
||||||
|
|
||||||
|
click(css: '.content.active .modal button.js-submit')
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .sidebar[data-tab="customer"] .sidebar-block [data-name="note"]',
|
||||||
|
value: 'some note abc',
|
||||||
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .sidebar[data-tab="customer"] .sidebar-block h3[title="Name"]',
|
||||||
|
value: 'Nicole B2',
|
||||||
|
)
|
||||||
|
|
||||||
|
sleep 2
|
||||||
|
# change lastname back
|
||||||
|
click(css: '.content.active .sidebar[data-tab="customer"] .js-actions')
|
||||||
|
click(css: 'li[data-type="customer-edit"]')
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .modal',
|
||||||
|
value: 'note',
|
||||||
|
)
|
||||||
|
|
||||||
|
set(
|
||||||
|
css: '.modal [name="lastname"]',
|
||||||
|
value: 'Braun',
|
||||||
|
)
|
||||||
|
set(
|
||||||
|
css: '.modal [data-name="note"]',
|
||||||
|
value: 'some note abc',
|
||||||
|
)
|
||||||
|
click(css: '.content.active .modal button.js-submit')
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .sidebar[data-tab="customer"] .sidebar-block [data-name="note"]',
|
||||||
|
value: 'some note abc',
|
||||||
|
)
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .sidebar[data-tab="customer"] .sidebar-block [title="Name"]',
|
||||||
|
value: 'Nicole Braun',
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_agent_to_edit_customer_ticket_details
|
||||||
|
@browser = browser_instance
|
||||||
|
|
||||||
|
login(
|
||||||
|
username: 'agent1@example.com',
|
||||||
|
password: 'test',
|
||||||
|
url: browser_url,
|
||||||
|
)
|
||||||
|
tasks_close_all()
|
||||||
|
|
||||||
|
ticket1 = ticket_create(
|
||||||
|
data: {
|
||||||
|
customer: 'nico',
|
||||||
|
group: 'Users',
|
||||||
|
title: 'test_auto_assignment_2 - ticket 2',
|
||||||
|
body: 'test_auto_assignment_2 - ticket 2 - no auto assignment',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# open ticket#1
|
||||||
|
ticket_open_by_search(
|
||||||
|
number: ticket1[:number],
|
||||||
|
)
|
||||||
|
|
||||||
|
exists(css: '.content.active .tabsSidebar .tabsSidebar-tab[data-tab="customer"]')
|
||||||
|
exists(css: '.content.active .sidebar[data-tab="customer"] .js-actions .dropdown-toggle')
|
||||||
|
exists(css: '.content.active .sidebar[data-tab="customer"] .js-actions [data-type="customer-edit"]')
|
||||||
|
|
||||||
|
click(css: '.content.active .tabsSidebar-holder .js-avatar')
|
||||||
|
|
||||||
|
# check and change note again in edit screen
|
||||||
|
click(css: '.content.active .js-action .dropdown-toggle')
|
||||||
|
click(css: '.content.active .js-action [data-type="edit"]')
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .modal',
|
||||||
|
value: 'note',
|
||||||
|
)
|
||||||
|
|
||||||
|
set(
|
||||||
|
css: '.modal [name="lastname"]',
|
||||||
|
value: 'B2',
|
||||||
|
)
|
||||||
|
set(
|
||||||
|
css: '.modal [data-name="note"]',
|
||||||
|
value: 'some note abc',
|
||||||
|
)
|
||||||
|
click(css: '.content.active .modal button.js-submit')
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .profile-window',
|
||||||
|
value: 'some note abc',
|
||||||
|
)
|
||||||
|
|
||||||
|
verify_task(
|
||||||
|
data: {
|
||||||
|
title: 'Nicole B2',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# change lastname back
|
||||||
|
click(css: '.content.active .js-action .dropdown-toggle')
|
||||||
|
click(css: '.content.active .js-action [data-type="edit"]')
|
||||||
|
|
||||||
|
watch_for(
|
||||||
|
css: '.content.active .modal',
|
||||||
|
value: 'note',
|
||||||
|
)
|
||||||
|
set(
|
||||||
|
css: '.modal [name="lastname"]',
|
||||||
|
value: 'Braun',
|
||||||
|
)
|
||||||
|
set(
|
||||||
|
css: '.modal [data-name="note"]',
|
||||||
|
value: 'note',
|
||||||
|
)
|
||||||
|
click(css: '.content.active .modal button.js-submit')
|
||||||
|
|
||||||
|
verify_task(
|
||||||
|
data: {
|
||||||
|
title: 'Nicole Braun',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_agent_to_edit_admin_ticket_details
|
||||||
|
@browser = browser_instance
|
||||||
|
|
||||||
|
login(
|
||||||
|
username: 'agent1@example.com',
|
||||||
|
password: 'test',
|
||||||
|
url: browser_url,
|
||||||
|
)
|
||||||
|
tasks_close_all()
|
||||||
|
|
||||||
|
ticket1 = ticket_create(
|
||||||
|
data: {
|
||||||
|
customer: 'master',
|
||||||
|
group: 'Users',
|
||||||
|
title: 'test_auto_assignment_2 - ticket 2',
|
||||||
|
body: 'test_auto_assignment_2 - ticket 2 - no auto assignment',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# open ticket#1
|
||||||
|
ticket_open_by_search(
|
||||||
|
number: ticket1[:number],
|
||||||
|
)
|
||||||
|
|
||||||
|
exists(css: '.content.active .tabsSidebar .tabsSidebar-tab[data-tab="customer"]')
|
||||||
|
exists(css: '.content.active .sidebar[data-tab="customer"] .js-actions .dropdown-toggle')
|
||||||
|
exists_not(css: '.content.active .sidebar[data-tab="customer"] .js-actions [data-type="customer-edit"]')
|
||||||
|
|
||||||
|
click(css: '.content.active .tabsSidebar-holder .js-avatar')
|
||||||
|
|
||||||
|
# check and change note again in edit screen
|
||||||
|
click(css: '.content.active .js-action .icon-arrow-down', fast: true)
|
||||||
|
exists_not(
|
||||||
|
css: '.content.active .js-action [data-type="edit"]'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -422,7 +422,7 @@ class TestCase < Test::Unit::TestCase
|
||||||
element = instance.find_elements(css: params[:css])[0]
|
element = instance.find_elements(css: params[:css])[0]
|
||||||
return if !element && params[:only_if_exists] == true
|
return if !element && params[:only_if_exists] == true
|
||||||
#if element
|
#if element
|
||||||
# instance.mouse.move_to(element)
|
# instance.action.move_to(element).release.perform
|
||||||
#end
|
#end
|
||||||
element.click
|
element.click
|
||||||
rescue => e
|
rescue => e
|
||||||
|
@ -433,8 +433,9 @@ class TestCase < Test::Unit::TestCase
|
||||||
element = instance.find_elements(css: params[:css])[0]
|
element = instance.find_elements(css: params[:css])[0]
|
||||||
return if !element && params[:only_if_exists] == true
|
return if !element && params[:only_if_exists] == true
|
||||||
#if element
|
#if element
|
||||||
# instance.mouse.move_to(element)
|
# instance.action.move_to(element).release.perform
|
||||||
#end
|
#end
|
||||||
|
raise "No such element '#{params[:css]}'" if !element
|
||||||
element.click
|
element.click
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -447,7 +448,9 @@ class TestCase < Test::Unit::TestCase
|
||||||
|
|
||||||
# just try again
|
# just try again
|
||||||
log('click', { rescure: true })
|
log('click', { rescure: true })
|
||||||
instance.find_elements(partial_link_text: params[:text])[0].click
|
element = instance.find_elements(partial_link_text: params[:text])[0]
|
||||||
|
raise "No such element '#{params[:text]}'" if !element
|
||||||
|
element.click
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
sleep 0.2 if !params[:fast]
|
sleep 0.2 if !params[:fast]
|
||||||
|
@ -632,6 +635,7 @@ class TestCase < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
element.clear
|
element.clear
|
||||||
|
|
||||||
|
begin
|
||||||
if !params[:slow]
|
if !params[:slow]
|
||||||
element.send_keys(params[:value])
|
element.send_keys(params[:value])
|
||||||
else
|
else
|
||||||
|
@ -641,17 +645,28 @@ class TestCase < Test::Unit::TestCase
|
||||||
instance.action.send_keys(key).perform
|
instance.action.send_keys(key).perform
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
rescue => e
|
||||||
|
sleep 0.5
|
||||||
|
|
||||||
|
# just try again
|
||||||
|
log('set', { rescure: true })
|
||||||
|
element = instance.find_elements(css: params[:css])[0]
|
||||||
|
raise "No such element '#{params[:css]}'" if !element
|
||||||
|
if !params[:slow]
|
||||||
|
element.send_keys(params[:value])
|
||||||
|
else
|
||||||
|
element.send_keys('')
|
||||||
|
keys = params[:value].to_s.split('')
|
||||||
|
keys.each do |key|
|
||||||
|
instance.action.send_keys(key).perform
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# it's not working stable with ff via selenium, use js
|
# it's not working stable with ff via selenium, use js
|
||||||
if browser =~ /firefox/i && params[:css] =~ /\[data-name=/
|
if browser =~ /firefox/i && params[:css] =~ /\[data-name=/
|
||||||
log('set_ff_check', params)
|
log('set_ff_trigger_workaround', params)
|
||||||
value = instance.find_elements(css: params[:css])[0].text
|
instance.execute_script("$('#{params[:css]}').trigger('focusout')")
|
||||||
if value != params[:value]
|
|
||||||
log('set_ff_check_failed_use_js', params)
|
|
||||||
value_quoted = quote(params[:value])
|
|
||||||
puts "DEBUG $('#{params[:css]}').html('#{value_quoted}').trigger('focusout')"
|
|
||||||
instance.execute_script("$('#{params[:css]}').html('#{value_quoted}').trigger('focusout')")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if params[:blur]
|
if params[:blur]
|
||||||
|
@ -1159,12 +1174,15 @@ set type of task (closeTab, closeNextInOverview, stayOnTab)
|
||||||
instance = params[:browser] || @browser
|
instance = params[:browser] || @browser
|
||||||
data = params[:data]
|
data = params[:data]
|
||||||
|
|
||||||
element = instance.find_elements(partial_link_text: data[:title])[0]
|
element = instance.find_element(css: '#navigation').find_element(partial_link_text: data[:title])
|
||||||
if !element
|
if !element
|
||||||
screenshot(browser: instance, comment: 'open_task_failed')
|
screenshot(browser: instance, comment: 'open_task_failed')
|
||||||
raise "no task with title '#{data[:title]}' found"
|
raise "no task with title '#{data[:title]}' found"
|
||||||
end
|
end
|
||||||
element.click
|
# firefix/marionette issue with Selenium::WebDriver::Error::ElementNotInteractableError: could not be scrolled into view
|
||||||
|
# use js workaround instead of native click
|
||||||
|
instance.execute_script("$('#navigation .tasks .task:contains(\"#{data[:title]}\") .nav-tab-name').click()")
|
||||||
|
#element.click
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1187,15 +1205,15 @@ set type of task (closeTab, closeNextInOverview, stayOnTab)
|
||||||
instance = params[:browser] || @browser
|
instance = params[:browser] || @browser
|
||||||
data = params[:data]
|
data = params[:data]
|
||||||
|
|
||||||
element = instance.find_elements(partial_link_text: data[:title])[0]
|
element = instance.find_element(css: '#navigation').find_element(partial_link_text: data[:title])
|
||||||
if !element
|
if !element
|
||||||
screenshot(browser: instance, comment: 'close_task_failed')
|
screenshot(browser: instance, comment: 'close_task_failed')
|
||||||
raise "no task with title '#{data[:title]}' found"
|
raise "no task with title '#{data[:title]}' found"
|
||||||
end
|
end
|
||||||
|
|
||||||
instance.mouse.move_to(element)
|
instance.action.move_to(element).release.perform
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
instance.execute_script("$('.navigation .tasks .task:contains(\"#{data[:title]}\") .js-close').click()")
|
instance.execute_script("$('#navigation .tasks .task:contains(\"#{data[:title]}\") .js-close').click()")
|
||||||
|
|
||||||
# accept task close warning
|
# accept task close warning
|
||||||
if params[:discard_changes]
|
if params[:discard_changes]
|
||||||
|
@ -1405,10 +1423,9 @@ wait untill text in selector disabppears
|
||||||
99.times do
|
99.times do
|
||||||
#sleep 0.5
|
#sleep 0.5
|
||||||
begin
|
begin
|
||||||
if instance.find_elements(css: '.navigation .tasks .task:first-child')[0]
|
if instance.find_elements(css: '#navigation .tasks .task:first-child')[0]
|
||||||
instance.mouse.move_to(instance.find_elements(css: '.navigation .tasks .task:first-child')[0])
|
instance.action.move_to(instance.find_elements(css: '#navigation .tasks .task:first-child')[0]).release.perform
|
||||||
sleep 0.1
|
click_element = instance.find_elements(css: '#navigation .tasks .task:first-child .js-close')[0]
|
||||||
click_element = instance.find_elements(css: '.navigation .tasks .task:first-child .js-close')[0]
|
|
||||||
if click_element
|
if click_element
|
||||||
click_element.click
|
click_element.click
|
||||||
|
|
||||||
|
@ -1453,7 +1470,7 @@ wait untill text in selector disabppears
|
||||||
screenshot(browser: instance, comment: 'close_online_notitifcation')
|
screenshot(browser: instance, comment: 'close_online_notitifcation')
|
||||||
raise "no online notification with title '#{data[:title]}' found"
|
raise "no online notification with title '#{data[:title]}' found"
|
||||||
end
|
end
|
||||||
instance.mouse.move_to(element)
|
instance.action.move_to(element).release.perform
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
instance.execute_script("$('.js-notificationsContainer .js-items .js-item .activity-text:contains(\"#{data[:title]}\") .js-remove').first().click()")
|
instance.execute_script("$('.js-notificationsContainer .js-items .js-item .activity-text:contains(\"#{data[:title]}\") .js-remove').first().click()")
|
||||||
|
|
||||||
|
@ -1465,7 +1482,7 @@ wait untill text in selector disabppears
|
||||||
raise "no online notification with postion '#{css}' found"
|
raise "no online notification with postion '#{css}' found"
|
||||||
end
|
end
|
||||||
|
|
||||||
instance.mouse.move_to(element)
|
instance.action.move_to(element).release.perform
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
instance.find_elements(css: "#{css} .js-remove")[0].click
|
instance.find_elements(css: "#{css} .js-remove")[0].click
|
||||||
end
|
end
|
||||||
|
@ -1491,7 +1508,7 @@ wait untill text in selector disabppears
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
begin
|
begin
|
||||||
if instance.find_elements(css: '.js-notificationsContainer .js-item:first-child')[0]
|
if instance.find_elements(css: '.js-notificationsContainer .js-item:first-child')[0]
|
||||||
instance.action.move_to(instance.find_elements(css: '.js-notificationsContainer .js-item:first-child')[0])
|
instance.action.move_to(instance.find_elements(css: '.js-notificationsContainer .js-item:first-child')[0]).perform
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
click_element = instance.find_elements(css: '.js-notificationsContainer .js-item:first-child .js-remove')[0]
|
click_element = instance.find_elements(css: '.js-notificationsContainer .js-item:first-child .js-remove')[0]
|
||||||
click_element&.click
|
click_element&.click
|
||||||
|
@ -2361,7 +2378,7 @@ wait untill text in selector disabppears
|
||||||
)
|
)
|
||||||
screenshot(browser: instance, comment: 'ticket_open_by_overview_search')
|
screenshot(browser: instance, comment: 'ticket_open_by_overview_search')
|
||||||
if params[:title]
|
if params[:title]
|
||||||
element = instance.find_elements(partial_link_text: params[:title])[0]
|
element = instance.find_element(css: '.content.active').find_element(partial_link_text: params[:title])
|
||||||
if !element
|
if !element
|
||||||
screenshot(browser: instance, comment: 'ticket_open_by_overview_no_ticket_failed')
|
screenshot(browser: instance, comment: 'ticket_open_by_overview_no_ticket_failed')
|
||||||
raise "unable to find ticket #{params[:title]} in overview #{params[:link]}!"
|
raise "unable to find ticket #{params[:title]} in overview #{params[:link]}!"
|
||||||
|
|
Loading…
Reference in a new issue