diff --git a/app/assets/javascripts/app/controllers/_ui_element/permission.coffee b/app/assets/javascripts/app/controllers/_ui_element/permission.coffee
index 6c8b87543..96c996467 100644
--- a/app/assets/javascripts/app/controllers/_ui_element/permission.coffee
+++ b/app/assets/javascripts/app/controllers/_ui_element/permission.coffee
@@ -1,6 +1,6 @@
# coffeelint: disable=camel_case_classes
class App.UiElement.permission extends App.UiElement.ApplicationUiElement
- @render: (attribute, params) ->
+ @render: (attribute, params = {}) ->
permissions = App.Permission.search(sortBy: 'name')
diff --git a/app/assets/javascripts/app/controllers/clues.coffee b/app/assets/javascripts/app/controllers/clues.coffee
index 5e9e875b7..735a6329d 100644
--- a/app/assets/javascripts/app/controllers/clues.coffee
+++ b/app/assets/javascripts/app/controllers/clues.coffee
@@ -11,11 +11,11 @@ class Index extends App.Controller
App.Ajax.request(
id: 'preferences'
type: 'PUT'
- url: @apiPath + '/users/preferences'
+ url: "#{@apiPath}/users/preferences"
data: JSON.stringify({user:{intro:true}})
processData: true
)
@navigate '#'
)
-App.Config.set( 'clues', Index, 'Routes' )
+App.Config.set('clues', Index, 'Routes')
diff --git a/app/assets/javascripts/app/controllers/dashboard.coffee b/app/assets/javascripts/app/controllers/dashboard.coffee
index c87361840..7da78d6ff 100644
--- a/app/assets/javascripts/app/controllers/dashboard.coffee
+++ b/app/assets/javascripts/app/controllers/dashboard.coffee
@@ -7,7 +7,7 @@ class App.Dashboard extends App.Controller
constructor: ->
super
- if @permissionCheck('ticket.customer')
+ if !@permissionCheck('ticket.agent')
@clueAccess = false
return
diff --git a/app/assets/javascripts/app/controllers/manage.coffee b/app/assets/javascripts/app/controllers/manage.coffee
index 5ab127c7d..23b2cb5fb 100644
--- a/app/assets/javascripts/app/controllers/manage.coffee
+++ b/app/assets/javascripts/app/controllers/manage.coffee
@@ -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/:integration', IndexRouter, 'Routes')
-App.Config.set('Manage', { prio: 1000, name: 'Manage', target: '#manage', role: ['Admin'] }, 'NavBarAdmin')
-App.Config.set('Channels', { prio: 2500, name: 'Channels', target: '#channels', role: ['Admin'] }, 'NavBarAdmin')
-App.Config.set('Settings', { prio: 7000, name: 'Settings', target: '#settings', role: ['Admin'] }, 'NavBarAdmin')
-App.Config.set('System', { prio: 8000, name: 'System', target: '#system', 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', permission: ['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', permission: ['admin.*'] }, 'NavBarAdmin')
diff --git a/app/assets/javascripts/app/controllers/navigation.coffee b/app/assets/javascripts/app/controllers/navigation.coffee
index d951aab98..610c93e0b 100644
--- a/app/assets/javascripts/app/controllers/navigation.coffee
+++ b/app/assets/javascripts/app/controllers/navigation.coffee
@@ -406,7 +406,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
recentViewNavbarItemsRebuild: =>
# remove old views
- NavBarRight = @Config.get( 'NavBarRight' ) || {}
+ NavBarRight = @Config.get('NavBarRight') || {}
for key of NavBarRight
if NavBarRight[key].parent is '#current_user'
part = key.split '::'
diff --git a/app/assets/javascripts/app/controllers/role.coffee b/app/assets/javascripts/app/controllers/role.coffee
new file mode 100644
index 000000000..1c280f8a8
--- /dev/null
+++ b/app/assets/javascripts/app/controllers/role.coffee
@@ -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')
diff --git a/app/assets/javascripts/app/models/user.coffee b/app/assets/javascripts/app/models/user.coffee
index 30dd4662e..b5237eaa9 100644
--- a/app/assets/javascripts/app/models/user.coffee
+++ b/app/assets/javascripts/app/models/user.coffee
@@ -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('admin.*') # access if one sub key access exists
+
returns
true|false
@@ -190,9 +192,10 @@ class App.User extends App.Model
permissions = {}
for role_id in @role_ids
role = App.Role.find(role_id)
- for permission_id in role.permission_ids
- permission = App.Permission.find(permission_id)
- permissions[permission.name] = true
+ if role.active is true
+ for permission_id in role.permission_ids
+ permission = App.Permission.find(permission_id)
+ permissions[permission.name] = true
for localKey in keys
requiredPermissions = localKey.split('+')
@@ -200,12 +203,25 @@ class App.User extends App.Model
for requiredPermission in requiredPermissions
localAccess = false
partString = ''
- for part in requiredPermission.split('.')
- if partString isnt ''
- partString += '.'
- partString += part
- if permissions[partString]
- localAccess = true
+ 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 ''
+ partString += '.'
+ partString += part
+ if permissions[partString]
+ localAccess = true
+
if localAccess
access = true
else
diff --git a/app/assets/javascripts/app/views/generic/navbar_level2/navbar.jst.eco b/app/assets/javascripts/app/views/generic/navbar_level2/navbar.jst.eco
index 17ab75490..9e23a08c9 100644
--- a/app/assets/javascripts/app/views/generic/navbar_level2/navbar.jst.eco
+++ b/app/assets/javascripts/app/views/generic/navbar_level2/navbar.jst.eco
@@ -1,12 +1,14 @@
<% if @groups: %>
<% for group in @groups: %>
-
<%- @T(group.name) %>
-
-<% if group.items: %>
- <% for item in group.items: %>
- class="active"<% end %>><%- @T(item.name) %>
- <% end %>
-<% end %>
-
+ <% if !_.isEmpty(group.items): %>
+ <%- @T(group.name) %>
+
+ <% if group.items: %>
+ <% for item in group.items: %>
+ class="active"<% end %>><%- @T(item.name) %>
+ <% end %>
+ <% end %>
+
+ <% end %>
<% end %>
<% end %>
diff --git a/app/assets/javascripts/app/views/generic/permission.jst.eco b/app/assets/javascripts/app/views/generic/permission.jst.eco
index a8ebe892d..4ebbce9b3 100644
--- a/app/assets/javascripts/app/views/generic/permission.jst.eco
+++ b/app/assets/javascripts/app/views/generic/permission.jst.eco
@@ -2,7 +2,7 @@
<% for permission in @permissions: %>
<% if !permission.name.match(/\./): %>
- checked<% end %> <% if permission.preferences.disabled: %>disabled<% end %>/>
+ checked<% end %> <% if permission.preferences.disabled: %>disabled<% end %> data-permission-name="<%= permission.name %>"/>
<%- @Icon('checkbox', 'icon-unchecked') %>
<%- @Icon('checkbox-checked', 'icon-checked') %>
<%= permission.displayName() %> - <%- @T.apply(@, [permission.note].concat(permission.preferences.translations)) %>
@@ -10,7 +10,7 @@
<% else: %>
- checked<% end %> <% if permission.preferences.disabled: %>disabled<% end %>/>
+ checked<% end %> <% if permission.preferences.disabled: %>disabled<% end %> data-permission-name="<%= permission.name %>"/>
<%- @Icon('checkbox', 'icon-unchecked') %>
<%- @Icon('checkbox-checked', 'icon-checked') %>
<%= permission.displayName().replace(/^.+?\./, '') %> - <%- @T.apply(@, [permission.note].concat(permission.preferences.translations)) %>
diff --git a/app/models/user.rb b/app/models/user.rb
index 732dbbc59..1b1520f3f 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -374,10 +374,10 @@ returns
if local_key =~ /\.\*$/
local_key.sub!('.*', '.%')
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
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
list.each { |preferences|
next if preferences[:selectable] == false
diff --git a/test/browser/admin_role_test.rb b/test/browser/admin_role_test.rb
new file mode 100644
index 000000000..982d5cb8f
--- /dev/null
+++ b/test/browser/admin_role_test.rb
@@ -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
diff --git a/test/browser_test_helper.rb b/test/browser_test_helper.rb
index eed26a2ab..a851b71e6 100644
--- a/test/browser_test_helper.rb
+++ b/test/browser_test_helper.rb
@@ -2820,6 +2820,217 @@ wait untill text in selector disabppears
raise 'group 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_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
object_manager_attribute_create(
diff --git a/test/unit/permission_test.rb b/test/unit/permission_test.rb
index 5f0e1eab4..8f66b5826 100644
--- a/test/unit/permission_test.rb
+++ b/test/unit/permission_test.rb
@@ -47,4 +47,51 @@ class PermissionTest < ActiveSupport::TestCase
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