Added role management.

This commit is contained in:
Martin Edenhofer 2016-09-07 08:47:31 +02:00
parent b61068a13a
commit e5999fe71d
13 changed files with 459 additions and 30 deletions

View file

@ -1,6 +1,6 @@
# coffeelint: disable=camel_case_classes # coffeelint: disable=camel_case_classes
class App.UiElement.permission extends App.UiElement.ApplicationUiElement class App.UiElement.permission extends App.UiElement.ApplicationUiElement
@render: (attribute, params) -> @render: (attribute, params = {}) ->
permissions = App.Permission.search(sortBy: 'name') permissions = App.Permission.search(sortBy: 'name')

View file

@ -11,7 +11,7 @@ class Index extends App.Controller
App.Ajax.request( App.Ajax.request(
id: 'preferences' id: 'preferences'
type: 'PUT' type: 'PUT'
url: @apiPath + '/users/preferences' url: "#{@apiPath}/users/preferences"
data: JSON.stringify({user:{intro:true}}) data: JSON.stringify({user:{intro:true}})
processData: true processData: true
) )

View file

@ -7,7 +7,7 @@ class App.Dashboard extends App.Controller
constructor: -> constructor: ->
super super
if @permissionCheck('ticket.customer') if !@permissionCheck('ticket.agent')
@clueAccess = false @clueAccess = false
return return

View file

@ -10,7 +10,7 @@ App.Config.set('channels/:target/:channel_id', IndexRouter, 'Routes')
App.Config.set('system/:target', IndexRouter, 'Routes') App.Config.set('system/:target', IndexRouter, 'Routes')
App.Config.set('system/:target/:integration', IndexRouter, 'Routes') App.Config.set('system/:target/:integration', IndexRouter, 'Routes')
App.Config.set('Manage', { prio: 1000, name: 'Manage', target: '#manage', role: ['Admin'] }, 'NavBarAdmin') App.Config.set('Manage', { prio: 1000, name: 'Manage', target: '#manage', permission: ['admin.*'] }, 'NavBarAdmin')
App.Config.set('Channels', { prio: 2500, name: 'Channels', target: '#channels', role: ['Admin'] }, 'NavBarAdmin') App.Config.set('Channels', { prio: 2500, name: 'Channels', target: '#channels', permission: ['admin.*'] }, 'NavBarAdmin')
App.Config.set('Settings', { prio: 7000, name: 'Settings', target: '#settings', role: ['Admin'] }, 'NavBarAdmin') App.Config.set('Settings', { prio: 7000, name: 'Settings', target: '#settings', permission: ['admin.*'] }, 'NavBarAdmin')
App.Config.set('System', { prio: 8000, name: 'System', target: '#system', role: ['Admin'] }, 'NavBarAdmin') App.Config.set('System', { prio: 8000, name: 'System', target: '#system', permission: ['admin.*'] }, 'NavBarAdmin')

View file

@ -0,0 +1,25 @@
class Index extends App.ControllerContent
requiredPermission: 'admin.role'
constructor: ->
super
new App.ControllerGenericIndex(
el: @el
id: @id
genericObject: 'Role'
pageData:
title: 'Roles'
home: 'roles'
object: 'Role'
objects: 'Roles'
navupdate: '#roles'
notes: [
'Roles are ...'
]
buttons: [
{ name: 'New Role', 'data-type': 'new', class: 'btn--success' }
]
container: @el.closest('.content')
)
App.Config.set('Role', { prio: 1600, name: 'Roles', parent: '#manage', target: '#manage/roles', controller: Index, permission: ['admin.role'] }, 'NavBarAdmin')

View file

@ -172,6 +172,8 @@ class App.User extends App.Model
result = user.permission('user_preferences.calendar+ticket.agent') # access must have two permission keys result = user.permission('user_preferences.calendar+ticket.agent') # access must have two permission keys
result = user.permission('admin.*') # access if one sub key access exists
returns returns
true|false true|false
@ -190,6 +192,7 @@ class App.User extends App.Model
permissions = {} permissions = {}
for role_id in @role_ids for role_id in @role_ids
role = App.Role.find(role_id) role = App.Role.find(role_id)
if role.active is true
for permission_id in role.permission_ids for permission_id in role.permission_ids
permission = App.Permission.find(permission_id) permission = App.Permission.find(permission_id)
permissions[permission.name] = true permissions[permission.name] = true
@ -200,12 +203,25 @@ class App.User extends App.Model
for requiredPermission in requiredPermissions for requiredPermission in requiredPermissions
localAccess = false localAccess = false
partString = '' partString = ''
for part in requiredPermission.split('.') parts = requiredPermission.split('.')
# verify name.* permissions
if parts[parts.length - 1] is '*'
for permission_key, permission_value of permissions
if permission_value is true
length = requiredPermission.length - 1
if permission_key.substr(0, length) is requiredPermission.substr(0, length)
localAccess = true
# verify name.explicite permissions
if !localAccess
for part in parts
if partString isnt '' if partString isnt ''
partString += '.' partString += '.'
partString += part partString += part
if permissions[partString] if permissions[partString]
localAccess = true localAccess = true
if localAccess if localAccess
access = true access = true
else else

View file

@ -1,5 +1,6 @@
<% if @groups: %> <% if @groups: %>
<% for group in @groups: %> <% for group in @groups: %>
<% if !_.isEmpty(group.items): %>
<h2><%- @T(group.name) %></h2> <h2><%- @T(group.name) %></h2>
<ul class="nav nav-pills nav-stacked"> <ul class="nav nav-pills nav-stacked">
<% if group.items: %> <% if group.items: %>
@ -10,3 +11,4 @@
</ul> </ul>
<% end %> <% end %>
<% end %> <% end %>
<% end %>

View file

@ -2,7 +2,7 @@
<% for permission in @permissions: %> <% for permission in @permissions: %>
<% if !permission.name.match(/\./): %> <% if !permission.name.match(/\./): %>
<label class="inline-label checkbox-replacement"> <label class="inline-label checkbox-replacement">
<input type="checkbox" value="<%= permission.id %>" name="permission_ids" <% if _.contains(@params.permission_ids, permission.id): %>checked<% end %> <% if permission.preferences.disabled: %>disabled<% end %>/> <input type="checkbox" value="<%= permission.id %>" name="permission_ids" <% if _.contains(@params.permission_ids, permission.id): %>checked<% end %> <% if permission.preferences.disabled: %>disabled<% end %> data-permission-name="<%= permission.name %>"/>
<%- @Icon('checkbox', 'icon-unchecked') %> <%- @Icon('checkbox', 'icon-unchecked') %>
<%- @Icon('checkbox-checked', 'icon-checked') %> <%- @Icon('checkbox-checked', 'icon-checked') %>
<span class="label-text"><%= permission.displayName() %> - <span class="help-text"><%- @T.apply(@, [permission.note].concat(permission.preferences.translations)) %></span></span> <span class="label-text"><%= permission.displayName() %> - <span class="help-text"><%- @T.apply(@, [permission.note].concat(permission.preferences.translations)) %></span></span>
@ -10,7 +10,7 @@
<% else: %> <% else: %>
<div style="padding-left: 20px;" class="js-subPermissionList"> <div style="padding-left: 20px;" class="js-subPermissionList">
<label class="inline-label checkbox-replacement"> <label class="inline-label checkbox-replacement">
<input type="checkbox" value="<%= permission.id %>" name="permission_ids" <% if _.contains(@params.permission_ids, permission.id): %>checked<% end %> <% if permission.preferences.disabled: %>disabled<% end %>/> <input type="checkbox" value="<%= permission.id %>" name="permission_ids" <% if _.contains(@params.permission_ids, permission.id): %>checked<% end %> <% if permission.preferences.disabled: %>disabled<% end %> data-permission-name="<%= permission.name %>"/>
<%- @Icon('checkbox', 'icon-unchecked') %> <%- @Icon('checkbox', 'icon-unchecked') %>
<%- @Icon('checkbox-checked', 'icon-checked') %> <%- @Icon('checkbox-checked', 'icon-checked') %>
<span class="label-text"><%= permission.displayName().replace(/^.+?\./, '') %> - <span class="help-text"><%- @T.apply(@, [permission.note].concat(permission.preferences.translations)) %></span></span> <span class="label-text"><%= permission.displayName().replace(/^.+?\./, '') %> - <span class="help-text"><%- @T.apply(@, [permission.note].concat(permission.preferences.translations)) %></span></span>

View file

@ -374,10 +374,10 @@ returns
if local_key =~ /\.\*$/ if local_key =~ /\.\*$/
local_key.sub!('.*', '.%') local_key.sub!('.*', '.%')
permissions = Object.const_get('Permission').with_parents(local_key) permissions = Object.const_get('Permission').with_parents(local_key)
list = Object.const_get('Permission').select('preferences').joins(:roles).where('roles.id IN (?) AND (permissions.name IN (?) OR permissions.name LIKE ?)', role_ids, permissions, local_key).pluck(:preferences) list = Object.const_get('Permission').select('preferences').joins(:roles).where('roles.id IN (?) AND roles.active = ? AND (permissions.name IN (?) OR permissions.name LIKE ?)', role_ids, true, permissions, local_key).pluck(:preferences)
else else
permissions = Object.const_get('Permission').with_parents(local_key) permissions = Object.const_get('Permission').with_parents(local_key)
list = Object.const_get('Permission').select('preferences').joins(:roles).where('roles.id IN (?) AND permissions.name IN (?)', role_ids, permissions).pluck(:preferences) list = Object.const_get('Permission').select('preferences').joins(:roles).where('roles.id IN (?) AND roles.active = ? AND permissions.name IN (?)', role_ids, true, permissions).pluck(:preferences)
end end
list.each { |preferences| list.each { |preferences|
next if preferences[:selectable] == false next if preferences[:selectable] == false

View file

@ -0,0 +1,128 @@
# encoding: utf-8
require 'browser_test_helper'
class AdminRoleTest < TestCase
def test_role
name = "some role #{rand(99_999_999)}"
@browser = browser_instance
login(
username: 'master@example.com',
password: 'test',
url: browser_url,
)
tasks_close_all()
rand = rand(99_999_999).to_s
login = 'agent-role-' + rand
firstname = 'Role' + rand
lastname = 'Module' + rand
email = 'agent-role-' + rand + '@example.com'
password = 'agentpw'
user_create(
data: {
login: login,
firstname: firstname,
lastname: lastname,
email: email,
password: password,
},
)
name = "somerole#{rand}"
role_create(
data: {
name: name,
default_at_signup: false,
permission: [
'admin.group',
'user_preferences.device',
],
member: [login],
}
)
logout()
login(
username: email,
password: password,
url: browser_url,
)
tasks_close_all()
click(css: 'a[href="#current_user"]')
click(css: 'a[href="#profile"]')
match(
css: '.content .NavBarProfile',
value: 'Password',
)
match(
css: '.content .NavBarProfile',
value: 'Language',
)
match_not(
css: '.content .NavBarProfile',
value: 'Notifications',
)
match_not(
css: '.content .NavBarProfile',
value: 'Calendar',
)
match_not(
css: '.content .NavBarProfile',
value: 'Token Access',
)
match(
css: '.content .NavBarProfile',
value: 'Devices',
)
logout()
login(
username: 'master@example.com',
password: 'test',
url: browser_url,
)
role_edit(
data: {
name: name,
active: false,
}
)
logout()
login(
username: email,
password: password,
url: browser_url,
)
tasks_close_all()
click(css: 'a[href="#current_user"]')
click(css: 'a[href="#profile"]')
match(
css: '.content .NavBarProfile',
value: 'Password',
)
match(
css: '.content .NavBarProfile',
value: 'Language',
)
match_not(
css: '.content .NavBarProfile',
value: 'Notifications',
)
match_not(
css: '.content .NavBarProfile',
value: 'Calendar',
)
match_not(
css: '.content .NavBarProfile',
value: 'Token Access',
)
match_not(
css: '.content .NavBarProfile',
value: 'Devices',
)
end
end

View file

@ -2820,6 +2820,217 @@ wait untill text in selector disabppears
raise 'group creation failed' raise 'group creation failed'
end end
=begin
role_create(
browser: browser2,
data: {
name: 'some role' + random,
default_at_signup: false,
permission: [
'admin.group',
'preferences.password',
],
member: [
'some_user_login',
],
},
)
=end
def role_create(params = {})
switch_window_focus(params)
log('role_create', params)
instance = params[:browser] || @browser
data = params[:data]
click(
browser: instance,
css: 'a[href="#manage"]',
mute_log: true,
)
click(
browser: instance,
css: 'a[href="#manage/roles"]',
mute_log: true,
)
click(
browser: instance,
css: 'a[data-type="new"]',
mute_log: true,
)
modal_ready(browser: instance)
element = instance.find_elements(css: '.modal input[name=name]')[0]
element.clear
element.send_keys(data[:name])
if data.key?(:default_at_signup)
element = instance.find_elements(css: '.modal select[name="default_at_signup"]')[0]
dropdown = Selenium::WebDriver::Support::Select.new(element)
if data[:default_at_signup] == true
dropdown.select_by(:text, 'yes')
else
dropdown.select_by(:text, 'no')
end
end
if data.key?(:permission)
data[:permission].each { |permission_name|
check(
browser: instance,
css: ".modal [data-permission-name=\"#{permission_name}\"]",
)
}
end
instance.find_elements(css: '.modal button.js-submit')[0].click
modal_disappear(browser: instance)
11.times {
element = instance.find_elements(css: 'body')[0]
text = element.text
if text =~ /#{Regexp.quote(data[:name])}/
assert(true, 'role created')
modal_disappear(browser: instance) # wait until modal has gone
# add member
if data[:member]
data[:member].each { |login|
instance.find_elements(css: 'a[href="#manage"]')[0].click
sleep 1
instance.find_elements(css: 'a[href="#manage/users"]')[0].click
sleep 3
element = instance.find_elements(css: '#content [name="search"]')[0]
element.clear
element.send_keys(login)
sleep 3
#instance.find_elements(:css => '#content table [data-id]')[0].click
instance.execute_script('$("#content table [data-id] td").first().click()')
sleep 3
#instance.find_elements(:css => 'label:contains(" ' + action[:name] + '")')[0].click
instance.execute_script('$(\'label:contains(" ' + data[:name] + '")\').first().click()')
instance.find_elements(css: '.modal button.js-submit')[0].click
modal_disappear(browser: instance)
}
end
end
sleep 1
return true
}
screenshot(browser: instance, comment: 'role_create_failed')
raise 'role creation failed'
end
=begin
role_create(
browser: browser2,
data: {
name: 'some role' + random,
default_at_signup: false,
permission: [
'admin.group',
'preferences.password',
],
member: [
'some_user_login',
],
},
)
=end
def role_edit(params = {})
switch_window_focus(params)
log('role_edit', params)
instance = params[:browser] || @browser
data = params[:data]
click(
browser: instance,
css: 'a[href="#manage"]',
mute_log: true,
)
click(
browser: instance,
css: 'a[href="#manage/roles"]',
mute_log: true,
)
instance.execute_script('$(\'#content table tr td:contains(" ' + data[:name] + '")\').first().click()')
modal_ready(browser: instance)
element = instance.find_elements(css: '.modal input[name=name]')[0]
element.clear
element.send_keys(data[:name])
if data.key?(:default_at_signup)
element = instance.find_elements(css: '.modal select[name="default_at_signup"]')[0]
dropdown = Selenium::WebDriver::Support::Select.new(element)
if data[:default_at_signup] == true
dropdown.select_by(:text, 'yes')
else
dropdown.select_by(:text, 'no')
end
end
if data.key?(:permission)
data[:permission].each { |permission_name|
check(
browser: instance,
css: ".modal [data-permission-name=\"#{permission_name}\"]",
)
}
end
if data.key?(:active)
element = instance.find_elements(css: '.modal select[name="active"]')[0]
dropdown = Selenium::WebDriver::Support::Select.new(element)
if data[:active] == true
dropdown.select_by(:text, 'active')
else
dropdown.select_by(:text, 'inactive')
end
end
instance.find_elements(css: '.modal button.js-submit')[0].click
modal_disappear(browser: instance)
11.times {
element = instance.find_elements(css: 'body')[0]
text = element.text
if text =~ /#{Regexp.quote(data[:name])}/
assert(true, 'role created')
modal_disappear(browser: instance) # wait until modal has gone
# add member
if data[:member]
data[:member].each { |login|
instance.find_elements(css: 'a[href="#manage"]')[0].click
sleep 1
instance.find_elements(css: 'a[href="#manage/users"]')[0].click
sleep 3
element = instance.find_elements(css: '#content [name="search"]')[0]
element.clear
element.send_keys(login)
sleep 3
#instance.find_elements(:css => '#content table [data-id]')[0].click
instance.execute_script('$("#content table [data-id] td").first().click()')
sleep 3
#instance.find_elements(:css => 'label:contains(" ' + action[:name] + '")')[0].click
instance.execute_script('$(\'label:contains(" ' + data[:name] + '")\').first().click()')
instance.find_elements(css: '.modal button.js-submit')[0].click
modal_disappear(browser: instance)
}
end
end
sleep 1
return true
}
screenshot(browser: instance, comment: 'role_edit_failed')
raise 'role edit failed'
end
=begin =begin
object_manager_attribute_create( object_manager_attribute_create(

View file

@ -47,4 +47,51 @@ class PermissionTest < ActiveSupport::TestCase
end end
test 'user permission with invalid role' do
Permission.create_if_not_exists(
name: 'admin.permission2',
note: 'Admin Interface',
preferences: {},
)
role_permission2 = Role.create_or_update(
name: 'AdminPermission2',
note: 'To configure your permission2.',
preferences: {
not: ['Customer'],
},
default_at_signup: false,
active: true,
updated_by_id: 1,
created_by_id: 1,
)
role_permission2.permission_grand('admin.permission2')
user_with_permission2 = User.create_or_update(
login: 'setting-permission2',
firstname: 'Setting',
lastname: 'Admin Permission2',
email: 'setting-admin-permission2@example.com',
password: 'some_pw',
active: true,
roles: [role_permission2],
updated_by_id: 1,
created_by_id: 1,
)
assert_equal(true, user_with_permission2.permissions?('admin.permission2'))
assert_equal(true, user_with_permission2.permissions?('admin.*'))
assert_equal(false, user_with_permission2.permissions?('admi.*'))
assert_equal(false, user_with_permission2.permissions?('admin.permission3'))
assert_equal(false, user_with_permission2.permissions?('admin'))
role_permission2.active = false
role_permission2.save
user_with_permission2.reload
assert_equal(false, user_with_permission2.permissions?('admin.permission2'))
assert_equal(false, user_with_permission2.permissions?('admin.*'))
assert_equal(false, user_with_permission2.permissions?('admi.*'))
assert_equal(false, user_with_permission2.permissions?('admin.permission3'))
assert_equal(false, user_with_permission2.permissions?('admin'))
end
end end