diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb index 8e29fcf22..ffee83cfe 100644 --- a/app/controllers/settings_controller.rb +++ b/app/controllers/settings_controller.rb @@ -5,29 +5,38 @@ class SettingsController < ApplicationController # GET /settings def index - model_index_render(Setting, params) + list = [] + Setting.all.each { |setting| + next if setting.preferences[:permission] && !current_user.permissions?(setting.preferences[:permission]) + list.push setting + } + render json: list, status: :ok end # GET /settings/1 def show + check_access('read') model_show_render(Setting, params) end # POST /settings def create - model_create_render(Setting, params) + raise Exceptions::NotAuthorized, 'Not authorized (feature not possible)' end # PUT /settings/1 def update - check_access - model_update_render(Setting, params) + check_access('write') + clean_params = keep_certain_attributes + model_update_render(Setting, clean_params) end # PUT /settings/image/:id def update_image + check_access('write') + clean_params = keep_certain_attributes - if !params[:logo] + if !clean_params[:logo] render json: { result: 'invalid', message: 'Need logo param', @@ -36,7 +45,7 @@ class SettingsController < ApplicationController end # validate image - if params[:logo] !~ /^data:image/i + if clean_params[:logo] !~ /^data:image/i render json: { result: 'invalid', message: 'Invalid payload, need data:image in logo param', @@ -45,7 +54,7 @@ class SettingsController < ApplicationController end # process image - file = StaticAssets.data_url_attributes(params[:logo]) + file = StaticAssets.data_url_attributes(clean_params[:logo]) if !file[:content] || !file[:mime_type] render json: { result: 'invalid', @@ -58,7 +67,7 @@ class SettingsController < ApplicationController StaticAssets.store_raw(file[:content], file[:mime_type]) # store resized image 1:1 - setting = Setting.find_by(name: 'product_logo') + setting = Setting.lookup(name: 'product_logo') if params[:logo_resize] && params[:logo_resize] =~ /^data:image/i # data:image/png;base64 @@ -66,7 +75,7 @@ class SettingsController < ApplicationController # store image 1:1 setting.state = StaticAssets.store(file[:content], file[:mime_type]) - setting.save + setting.save! end render json: { @@ -77,16 +86,36 @@ class SettingsController < ApplicationController # DELETE /settings/1 def destroy - check_access - model_destory_render(Setting, params) + raise Exceptions::NotAuthorized, 'Not authorized (feature not possible)' end private - def check_access - return true if !Setting.get('system_online_service') + def keep_certain_attributes setting = Setting.find(params[:id]) - return true if setting.preferences && !setting.preferences[:online_service_disable] - raise Exceptions::NotAuthorized + [:name, :area, :state_initial, :frontend, :options].each { |key| + params.delete(key) + } + [:online_service_disable, :permission, :render].each { |key| + params[:preferences].delete(key) + } + params[:preferences].merge!(setting.preferences) + params + end + + def check_access(type) + setting = Setting.lookup(id: params[:id]) + + if setting.preferences[:permission] && !current_user.permissions?(setting.preferences[:permission]) + raise Exceptions::NotAuthorized, "Not authorized (required #{setting.preferences[:permission].inspect})" + end + + if type == 'write' + return true if !Setting.get('system_online_service') + if setting.preferences && setting.preferences[:online_service_disable] + raise Exceptions::NotAuthorized, 'Not authorized (service disabled)' + end + end + true end end diff --git a/app/models/user.rb b/app/models/user.rb index 257ff3f77..78ca876ec 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -350,6 +350,8 @@ true or false for permission user.permissions?('permission') # access to all sub keys + user.permissions?('permission.*') # access if one sub key access exists + returns true|false @@ -368,8 +370,16 @@ returns cache = Cache.get(cache_key) return cache if cache end - permissions = Object.const_get('Permission').with_parents(local_key) - Object.const_get('Permission').joins(:roles).where('roles.id IN (?) AND permissions.name IN (?)', role_ids, permissions).each { |permission| + list = [] + if local_key =~ /\.\*$/ + local_key.sub!('.*', '.%') + permissions = Object.const_get('Permission').with_parents(local_key) + list = Object.const_get('Permission').joins(:roles).where('roles.id IN (?) AND (permissions.name IN (?) OR permissions.name LIKE ?)', role_ids, permissions, local_key) + else + permissions = Object.const_get('Permission').with_parents(local_key) + list = Object.const_get('Permission').joins(:roles).where('roles.id IN (?) AND permissions.name IN (?)', role_ids, permissions) + end + list.each { |permission| next if permission.preferences[:selectable] == false Cache.write(key, true, expires_in: 20.seconds) return true diff --git a/db/migrate/20160826000001_update_setting_permission.rb b/db/migrate/20160826000001_update_setting_permission.rb new file mode 100644 index 000000000..da875fc79 --- /dev/null +++ b/db/migrate/20160826000001_update_setting_permission.rb @@ -0,0 +1,462 @@ +class UpdateSettingPermission < ActiveRecord::Migration + def up + # return if it's a new setup + return if !Setting.find_by(name: 'system_init_done') + + updates = [ + { + name: 'maintenance_mode', + preferences: { + permission: ['admin.maintenance'] + }, + }, + { + name: 'maintenance_login', + preferences: { + permission: ['admin.maintenance'] + }, + }, + { + name: 'maintenance_login_message', + preferences: { + permission: ['admin.maintenance'] + }, + }, + { + name: 'product_name', + preferences: { + permission: ['admin.branding'] + }, + }, + { + name: 'product_logo', + preferences: { + permission: ['admin.branding'] + }, + }, + { + name: 'system_id', + preferences: { + permission: ['admin.system'] + }, + }, + { + name: 'fqdn', + preferences: { + permission: ['admin.system'] + }, + }, + { + name: 'http_type', + preferences: { + permission: ['admin.system'] + }, + }, + { + name: 'storage', + preferences: { + permission: ['admin.system'] + }, + }, + { + name: 'image_backend', + preferences: { + permission: ['admin.system'] + }, + }, + { + name: 'geo_ip_backend', + preferences: { + permission: ['admin.system'] + }, + }, + { + name: 'geo_location_backend', + preferences: { + permission: ['admin.system'] + }, + }, + { + name: 'geo_calendar_backend', + preferences: { + permission: ['admin.system'] + }, + }, + { + name: 'ui_send_client_stats', + preferences: { + permission: ['admin.system'] + }, + }, + { + name: 'ui_client_storage', + preferences: { + permission: ['admin.system'] + }, + }, + { + name: 'user_create_account', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'user_lost_password', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_ldap', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_twitter', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_twitter_credentials', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_facebook', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_facebook_credentials', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_google_oauth2', + preferences: { + permission: ['admin.maintenance'] + }, + }, + { + name: 'maintenance_mode', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_google_oauth2_credentials', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_linkedin', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_linkedin_credentials', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_github', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_github_credentials', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_gitlab', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_gitlab_credentials', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_oauth2', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'auth_oauth2_credentials', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'password_min_size', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'password_min_2_lower_2_upper_characters', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'password_need_digit', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'password_max_login_failed', + preferences: { + permission: ['admin.security'] + }, + }, + { + name: 'ticket_hook', + preferences: { + permission: ['admin.ticket'] + }, + }, + { + name: 'ticket_hook_divider', + preferences: { + permission: ['admin.ticket'] + }, + }, + { + name: 'ticket_hook_position', + preferences: { + permission: ['admin.ticket'] + }, + }, + { + name: 'ticket_number', + preferences: { + permission: ['admin.ticket'] + }, + }, + { + name: 'ticket_number_increment', + preferences: { + permission: ['admin.ticket'] + }, + }, + { + name: 'ticket_number_date', + preferences: { + permission: ['admin.ticket'] + }, + }, + { + name: 'customer_ticket_create', + preferences: { + permission: ['admin.channel_web'] + }, + }, + { + name: 'customer_ticket_create_group_ids', + preferences: { + permission: ['admin.channel_web'] + }, + }, + { + name: 'customer_ticket_view', + preferences: { + permission: ['admin.channel_web'] + }, + }, + { + name: 'form_ticket_create', + preferences: { + permission: ['admin.channel_formular'] + }, + }, + { + name: 'ticket_subject_size', + preferences: { + permission: ['admin.channel_email'] + }, + }, + { + name: 'ticket_subject_re', + preferences: { + permission: ['admin.channel_email'] + }, + }, + { + name: 'ticket_define_email_from', + preferences: { + permission: ['admin.channel_email'] + }, + }, + { + name: 'ticket_define_email_from_seperator', + preferences: { + permission: ['admin.channel_email'] + }, + }, + { + name: 'postmaster_max_size', + preferences: { + permission: ['admin.channel_email'] + }, + }, + { + name: 'postmaster_follow_up_search_in', + preferences: { + permission: ['admin.channel_email'] + }, + }, + { + name: 'notification_sender', + preferences: { + permission: ['admin.channel_email'] + }, + }, + { + name: 'send_no_auto_response_reg_exp', + preferences: { + permission: ['admin.channel_email'] + }, + }, + { + name: 'api_token_access', + preferences: { + permission: ['admin.api'] + }, + }, + { + name: 'api_password_access', + preferences: { + permission: ['admin.api'] + }, + }, + { + name: 'chat', + preferences: { + permission: ['admin.channel_chat'] + }, + }, + { + name: 'chat_agent_idle_timeout', + preferences: { + permission: ['admin.channel_chat'] + }, + }, + { + name: 'tag_new', + preferences: { + permission: ['admin.tag'] + }, + }, + { + name: 'icinga_integration', + preferences: { + permission: ['admin.integration'] + }, + }, + { + name: 'icinga_sender', + preferences: { + permission: ['admin.integration'] + }, + }, + { + name: 'icinga_auto_close', + preferences: { + permission: ['admin.integration'] + }, + }, + { + name: 'icinga_auto_close_state_id', + preferences: { + permission: ['admin.integration'] + }, + }, + { + name: 'nagios_integration', + preferences: { + permission: ['admin.integration'] + }, + }, + { + name: 'nagios_sender', + preferences: { + permission: ['admin.integration'] + }, + }, + { + name: 'nagios_auto_close', + preferences: { + permission: ['admin.integration'] + }, + }, + { + name: 'nagios_auto_close_state_id', + preferences: { + permission: ['admin.integration'] + }, + }, + { + name: 'slack_integration', + preferences: { + permission: ['admin.integration'] + }, + }, + { + name: 'slack_config', + preferences: { + permission: ['admin.integration'] + }, + }, + { + name: 'sipgate_integration', + preferences: { + permission: ['admin.integration'] + }, + }, + { + name: 'sipgate_config', + preferences: { + permission: ['admin.integration'] + }, + }, + { + name: 'clearbit_integration', + preferences: { + permission: ['admin.integration'] + }, + }, + { + name: 'clearbit_config', + preferences: { + permission: ['admin.integration'] + }, + }, + ] + + updates.each { |item| + setting = Setting.find_by(name: item[:name]) + item[:preferences].each { |key, value| + setting.preferences[key] = value + } + setting.save! + } + + end +end diff --git a/db/seeds.rb b/db/seeds.rb index 990524999..8f9fb8e95 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -33,7 +33,9 @@ Setting.create_if_not_exists( description: 'Enable or disable the maintenance mode of Zammad. If enabled, all non-administrators get logged out and only administrators can start a new session.', options: {}, state: false, - preferences: {}, + preferences: { + permission: ['admin.maintenance'], + }, frontend: true ) Setting.create_if_not_exists( @@ -43,7 +45,9 @@ Setting.create_if_not_exists( description: 'Put a message on the login page. To change it, click on the text area below and change it inline.', options: {}, state: false, - preferences: {}, + preferences: { + permission: ['admin.maintenance'], + }, frontend: true ) Setting.create_if_not_exists( @@ -53,7 +57,9 @@ Setting.create_if_not_exists( description: 'Message for login page.', options: {}, state: 'Something about to share. Click here to change.', - preferences: {}, + preferences: { + permission: ['admin.maintenance'], + }, frontend: true ) Setting.create_if_not_exists( @@ -91,7 +97,13 @@ Setting.create_if_not_exists( }, ], }, - preferences: { render: true, session_check: true, prio: 1, placeholder: true }, + preferences: { + render: true, + session_check: true, + prio: 1, + placeholder: true, + permission: ['admin.branding'], + }, state: 'Zammad Helpdesk', frontend: true ) @@ -113,6 +125,7 @@ Setting.create_if_not_exists( preferences: { prio: 3, controller: 'SettingsAreaLogo', + permission: ['admin.branding'], }, state: 'logo.svg', frontend: true @@ -133,7 +146,11 @@ Setting.create_if_not_exists( ], }, state: '', - preferences: { prio: 2, placeholder: true }, + preferences: { + prio: 2, + placeholder: true, + permission: ['admin.branding'], + }, frontend: true ) options = {} @@ -162,6 +179,7 @@ Setting.create_if_not_exists( online_service_disable: true, placeholder: true, authentication: true, + permission: ['admin.system'], }, frontend: true ) @@ -181,7 +199,11 @@ Setting.create_if_not_exists( ], }, state: 'zammad.example.com', - preferences: { online_service_disable: true, placeholder: true }, + preferences: { + online_service_disable: true, + placeholder: true, + permission: ['admin.system'], + }, frontend: true ) Setting.create_if_not_exists( @@ -223,7 +245,11 @@ Setting.create_if_not_exists( ], }, state: 'http', - preferences: { online_service_disable: true, placeholder: true }, + preferences: { + online_service_disable: true, + placeholder: true, + permission: ['admin.system'], + }, frontend: true ) @@ -247,7 +273,10 @@ Setting.create_if_not_exists( ], }, state: 'DB', - preferences: { online_service_disable: true }, + preferences: { + online_service_disable: true, + permission: ['admin.system'], + }, frontend: false ) @@ -271,7 +300,10 @@ Setting.create_if_not_exists( ], }, state: 'Service::Image::Zammad', - preferences: { prio: 1 }, + preferences: { + prio: 1, + permission: ['admin.system'], + }, frontend: false ) @@ -295,7 +327,10 @@ Setting.create_if_not_exists( ], }, state: 'Service::GeoIp::Zammad', - preferences: { prio: 2 }, + preferences: { + prio: 2, + permission: ['admin.system'], + }, frontend: false ) @@ -319,7 +354,10 @@ Setting.create_if_not_exists( ], }, state: 'Service::GeoLocation::Gmaps', - preferences: { prio: 3 }, + preferences: { + prio: 3, + permission: ['admin.system'], + }, frontend: false ) @@ -343,7 +381,10 @@ Setting.create_if_not_exists( ], }, state: 'Service::GeoCalendar::Zammad', - preferences: { prio: 2 }, + preferences: { + prio: 2, + permission: ['admin.system'], + }, frontend: false ) @@ -367,7 +408,10 @@ Setting.create_if_not_exists( ], }, state: true, - preferences: { prio: 1 }, + preferences: { + prio: 1, + permission: ['admin.system'], + }, frontend: true ) Setting.create_if_not_exists( @@ -390,7 +434,10 @@ Setting.create_if_not_exists( ], }, state: false, - preferences: { prio: 2 }, + preferences: { + prio: 2, + permission: ['admin.system'], + }, frontend: true ) @@ -414,6 +461,9 @@ Setting.create_if_not_exists( ], }, state: true, + preferences: { + permission: ['admin.security'], + }, frontend: true ) Setting.create_if_not_exists( @@ -436,6 +486,9 @@ Setting.create_if_not_exists( ], }, state: true, + preferences: { + permission: ['admin.security'], + }, frontend: true ) Setting.create_if_not_exists( @@ -445,7 +498,8 @@ Setting.create_if_not_exists( description: 'Enables user authentication via %s.', preferences: { title_i18n: ['LDAP'], - description_i18n: ['LDAP'] + description_i18n: ['LDAP'], + permission: ['admin.security'], }, state: { adapter: 'Auth::Ldap', @@ -490,7 +544,8 @@ Setting.create_if_not_exists( controller: 'SettingsAreaSwitch', sub: ['auth_twitter_credentials'], title_i18n: ['Twitter'], - description_i18n: ['Twitter', 'Twitter Developer Site', 'https://dev.twitter.com/apps'] + description_i18n: ['Twitter', 'Twitter Developer Site', 'https://dev.twitter.com/apps'], + permission: ['admin.security'], }, state: false, frontend: true @@ -517,6 +572,9 @@ Setting.create_if_not_exists( ], }, state: {}, + preferences: { + permission: ['admin.security'], + }, frontend: false ) Setting.create_if_not_exists( @@ -542,7 +600,8 @@ Setting.create_if_not_exists( controller: 'SettingsAreaSwitch', sub: ['auth_facebook_credentials'], title_i18n: ['Facebook'], - description_i18n: ['Facebook', 'Facebook Developer Site', 'https://developers.facebook.com/apps/'] + description_i18n: ['Facebook', 'Facebook Developer Site', 'https://developers.facebook.com/apps/'], + permission: ['admin.security'], }, state: false, frontend: true @@ -570,6 +629,9 @@ Setting.create_if_not_exists( ], }, state: {}, + preferences: { + permission: ['admin.security'], + }, frontend: false ) @@ -596,7 +658,8 @@ Setting.create_if_not_exists( controller: 'SettingsAreaSwitch', sub: ['auth_google_oauth2_credentials'], title_i18n: ['Google'], - description_i18n: ['Google', 'Google API Console Site', 'https://console.developers.google.com/apis/credentials'] + description_i18n: ['Google', 'Google API Console Site', 'https://console.developers.google.com/apis/credentials'], + permission: ['admin.security'], }, state: false, frontend: true @@ -623,6 +686,9 @@ Setting.create_if_not_exists( ], }, state: {}, + preferences: { + permission: ['admin.security'], + }, frontend: false ) @@ -649,7 +715,8 @@ Setting.create_if_not_exists( controller: 'SettingsAreaSwitch', sub: ['auth_linkedin_credentials'], title_i18n: ['LinkedIn'], - description_i18n: ['LinkedIn', 'Linkedin Developer Site', 'https://www.linkedin.com/developer/apps'] + description_i18n: ['LinkedIn', 'Linkedin Developer Site', 'https://www.linkedin.com/developer/apps'], + permission: ['admin.security'], }, state: false, frontend: true @@ -676,6 +743,9 @@ Setting.create_if_not_exists( ], }, state: {}, + preferences: { + permission: ['admin.security'], + }, frontend: false ) @@ -702,7 +772,8 @@ Setting.create_if_not_exists( controller: 'SettingsAreaSwitch', sub: ['auth_github_credentials'], title_i18n: ['Github'], - description_i18n: ['Github', 'Github OAuth Applications', 'https://github.com/settings/applications'] + description_i18n: ['Github', 'Github OAuth Applications', 'https://github.com/settings/applications'], + permission: ['admin.security'], }, state: false, frontend: true @@ -729,6 +800,9 @@ Setting.create_if_not_exists( ], }, state: {}, + preferences: { + permission: ['admin.security'], + }, frontend: false ) @@ -755,7 +829,8 @@ Setting.create_if_not_exists( controller: 'SettingsAreaSwitch', sub: ['auth_gitlab_credentials'], title_i18n: ['Gitlab'], - description_i18n: ['Gitlab', 'Gitlab Applications', 'https://your-gitlab-host/admin/applications'] + description_i18n: ['Gitlab', 'Gitlab Applications', 'https://your-gitlab-host/admin/applications'], + permission: ['admin.security'], }, state: false, frontend: true @@ -789,6 +864,9 @@ Setting.create_if_not_exists( ], }, state: {}, + preferences: { + permission: ['admin.security'], + }, frontend: false ) @@ -815,6 +893,7 @@ Setting.create_if_not_exists( controller: 'SettingsAreaSwitch', sub: ['auth_oauth2_credentials'], title_i18n: ['Generic OAuth2'], + permission: ['admin.security'], }, state: false, frontend: true @@ -869,6 +948,9 @@ Setting.create_if_not_exists( ], }, state: {}, + preferences: { + permission: ['admin.security'], + }, frontend: false ) @@ -907,6 +989,9 @@ Setting.create_if_not_exists( ], }, state: 6, + preferences: { + permission: ['admin.security'], + }, frontend: false ) Setting.create_if_not_exists( @@ -929,6 +1014,9 @@ Setting.create_if_not_exists( ], }, state: 0, + preferences: { + permission: ['admin.security'], + }, frontend: false ) Setting.create_if_not_exists( @@ -951,6 +1039,9 @@ Setting.create_if_not_exists( ], }, state: 1, + preferences: { + permission: ['admin.security'], + }, frontend: false ) Setting.create_if_not_exists( @@ -987,6 +1078,9 @@ Setting.create_if_not_exists( ], }, state: 10, + preferences: { + permission: ['admin.security'], + }, frontend: false ) @@ -1009,6 +1103,7 @@ Setting.create_if_not_exists( render: true, placeholder: true, authentication: true, + permission: ['admin.ticket'], }, state: 'Ticket#', frontend: true @@ -1029,6 +1124,9 @@ Setting.create_if_not_exists( ], }, state: '', + preferences: { + permission: ['admin.ticket'], + }, frontend: false ) Setting.create_if_not_exists( @@ -1055,6 +1153,9 @@ Setting.create_if_not_exists( ], }, state: 'right', + preferences: { + permission: ['admin.ticket'], + }, frontend: false ) Setting.create_if_not_exists( @@ -1081,6 +1182,9 @@ Setting.create_if_not_exists( ], }, state: 'Ticket::Number::Increment', + preferences: { + permission: ['admin.ticket'], + }, frontend: false ) Setting.create_if_not_exists( @@ -1134,6 +1238,9 @@ Setting.create_if_not_exists( checksum: false, min_size: 5, }, + preferences: { + permission: ['admin.ticket'], + }, frontend: false ) Setting.create_if_not_exists( @@ -1158,6 +1265,9 @@ Setting.create_if_not_exists( state: { checksum: false, }, + preferences: { + permission: ['admin.ticket'], + }, frontend: false ) @@ -1183,6 +1293,7 @@ Setting.create_if_not_exists( state: true, preferences: { authentication: true, + permission: ['admin.channel_web'], }, frontend: true ) @@ -1208,6 +1319,7 @@ Setting.create_if_not_exists( state: '', preferences: { authentication: true, + permission: ['admin.channel_web'], }, frontend: true ) @@ -1234,6 +1346,7 @@ Setting.create_if_not_exists( state: true, preferences: { authentication: true, + permission: ['admin.channel_web'], }, frontend: true ) @@ -1258,6 +1371,9 @@ Setting.create_if_not_exists( ], }, state: false, + preferences: { + permission: ['admin.channel_formular'], + }, frontend: false, ) @@ -1277,6 +1393,9 @@ Setting.create_if_not_exists( ], }, state: '110', + preferences: { + permission: ['admin.channel_email'], + }, frontend: false ) Setting.create_if_not_exists( @@ -1295,6 +1414,9 @@ Setting.create_if_not_exists( ], }, state: 'RE', + preferences: { + permission: ['admin.channel_email'], + }, frontend: false ) @@ -1318,6 +1440,9 @@ Setting.create_if_not_exists( ], }, state: 'AgentNameSystemAddressName', + preferences: { + permission: ['admin.channel_email'], + }, frontend: false ) @@ -1337,6 +1462,9 @@ Setting.create_if_not_exists( ], }, state: 'via', + preferences: { + permission: ['admin.channel_email'], + }, frontend: false ) @@ -1383,7 +1511,10 @@ Setting.create_if_not_exists( ], }, state: 10, - preferences: { online_service_disable: true }, + preferences: { + online_service_disable: true, + permission: ['admin.channel_email'], + }, frontend: false ) @@ -1408,6 +1539,9 @@ Setting.create_if_not_exists( ], }, state: [], + preferences: { + permission: ['admin.channel_email'], + }, frontend: false ) @@ -1427,7 +1561,10 @@ Setting.create_if_not_exists( ], }, state: 'Notification Master ', - preferences: { online_service_disable: true }, + preferences: { + online_service_disable: true, + permission: ['admin.channel_email'], + }, frontend: false ) @@ -1447,7 +1584,10 @@ Setting.create_if_not_exists( ], }, state: '(mailer-daemon|postmaster|abuse|root|noreply|noreply.+?|no-reply|no-reply.+?)@.+?\..+?', - preferences: { online_service_disable: true }, + preferences: { + online_service_disable: true, + permission: ['admin.channel_email'], + }, frontend: false ) @@ -1471,6 +1611,9 @@ Setting.create_or_update( ], }, state: true, + preferences: { + permission: ['admin.api'], + }, frontend: false ) Setting.create_or_update( @@ -1493,6 +1636,9 @@ Setting.create_or_update( ], }, state: true, + preferences: { + permission: ['admin.api'], + }, frontend: false ) @@ -1516,7 +1662,8 @@ Setting.create_if_not_exists( ], }, preferences: { - trigger: ['menu:render', 'chat:rerender'] + trigger: ['menu:render', 'chat:rerender'], + permission: ['admin.channel_chat'], }, state: false, frontend: true @@ -1538,6 +1685,9 @@ Setting.create_if_not_exists( ], }, state: '120', + preferences: { + permission: ['admin.channel_chat'], + }, frontend: true ) @@ -1825,6 +1975,7 @@ Setting.create_if_not_exists( }, preferences: { authentication: true, + permission: ['admin.tag'], }, state: true, frontend: true @@ -1976,7 +2127,10 @@ Setting.create_if_not_exists( ], }, state: false, - preferences: { prio: 1 }, + preferences: { + prio: 1, + permission: ['admin.integration'], + }, frontend: false ) Setting.create_if_not_exists( @@ -1996,8 +2150,11 @@ Setting.create_if_not_exists( ], }, state: 'icinga@monitoring.example.com', + preferences: { + prio: 2, + permission: ['admin.integration'], + }, frontend: false, - preferences: { prio: 2 }, ) Setting.create_if_not_exists( title: 'Auto close', @@ -2019,7 +2176,10 @@ Setting.create_if_not_exists( ], }, state: true, - preferences: { prio: 3 }, + preferences: { + prio: 3, + permission: ['admin.integration'], + }, frontend: false ) Setting.create_if_not_exists( @@ -2039,7 +2199,10 @@ Setting.create_if_not_exists( ], }, state: 4, - preferences: { prio: 4 }, + preferences: { + prio: 4, + permission: ['admin.integration'], + }, frontend: false ) Setting.create_if_not_exists( @@ -2062,7 +2225,10 @@ Setting.create_if_not_exists( ], }, state: false, - preferences: { prio: 1 }, + preferences: { + prio: 1, + permission: ['admin.integration'], + }, frontend: false ) Setting.create_if_not_exists( @@ -2082,8 +2248,11 @@ Setting.create_if_not_exists( ], }, state: 'nagios@monitoring.example.com', + preferences: { + prio: 2, + permission: ['admin.integration'], + }, frontend: false, - preferences: { prio: 2 }, ) Setting.create_if_not_exists( title: 'Auto close', @@ -2105,7 +2274,10 @@ Setting.create_if_not_exists( ], }, state: true, - preferences: { prio: 3 }, + preferences: { + prio: 3, + permission: ['admin.integration'], + }, frontend: false ) Setting.create_if_not_exists( @@ -2125,7 +2297,10 @@ Setting.create_if_not_exists( ], }, state: 4, - preferences: { prio: 4 }, + preferences: { + prio: 4, + permission: ['admin.integration'], + }, frontend: false ) Setting.create_if_not_exists( @@ -2184,7 +2359,10 @@ Setting.create_if_not_exists( ], }, state: false, - preferences: { prio: 1 }, + preferences: { + prio: 1, + permission: ['admin.integration'], + }, frontend: false ) Setting.create_if_not_exists( @@ -2196,8 +2374,11 @@ Setting.create_if_not_exists( state: { items: [] }, + preferences: { + prio: 2, + permission: ['admin.integration'], + }, frontend: false, - preferences: { prio: 2 }, ) Setting.create_if_not_exists( title: 'sipgate.io integration', @@ -2223,6 +2404,7 @@ Setting.create_if_not_exists( prio: 1, trigger: ['menu:render', 'cti:reload'], authentication: true, + permission: ['admin.integration'], }, frontend: true ) @@ -2233,8 +2415,11 @@ Setting.create_if_not_exists( description: 'Define the sipgate.io config.', options: {}, state: {}, + preferences: { + prio: 2, + permission: ['admin.integration'], + }, frontend: false, - preferences: { prio: 2 }, ) Setting.create_if_not_exists( title: 'Clearbit integration', @@ -2256,7 +2441,10 @@ Setting.create_if_not_exists( ], }, state: false, - preferences: { prio: 1 }, + preferences: { + prio: 1, + permission: ['admin.integration'], + }, frontend: false ) Setting.create_if_not_exists( @@ -2267,7 +2455,10 @@ Setting.create_if_not_exists( options: {}, state: {}, frontend: false, - preferences: { prio: 2 }, + preferences: { + prio: 2, + permission: ['admin.integration'], + }, ) Setting.create_if_not_exists( title: 'Define transaction backend.', @@ -2395,17 +2586,6 @@ Role.create_if_not_exists( updated_by_id: 1, created_by_id: 1 ) -Role.create_if_not_exists( - id: 4, - name: 'Report', - note: 'Access the report area.', - preferences: { - not: ['Customer'], - }, - default_at_signup: false, - created_by_id: 1, - updated_by_id: 1, -) Permission.create_if_not_exists( name: 'admin', diff --git a/test/controllers/settings_controller_test.rb b/test/controllers/settings_controller_test.rb index bd55a5a62..d729c13e2 100644 --- a/test/controllers/settings_controller_test.rb +++ b/test/controllers/settings_controller_test.rb @@ -12,7 +12,7 @@ class SettingsControllerTest < ActionDispatch::IntegrationTest groups = Group.all UserInfo.current_user_id = 1 - @admin = User.create_or_update( + @admin_full = User.create_or_update( login: 'setting-admin', firstname: 'Setting', lastname: 'Admin', @@ -23,6 +23,28 @@ class SettingsControllerTest < ActionDispatch::IntegrationTest groups: groups, ) + role_api = Role.create_or_update( + name: 'AdminApi', + note: 'To configure your api.', + preferences: { + not: ['Customer'], + }, + default_at_signup: false, + updated_by_id: 1, + created_by_id: 1 + ) + role_api.permission_grand('admin.api') + @admin_api = User.create_or_update( + login: 'setting-admin-api', + firstname: 'Setting', + lastname: 'Admin Api', + email: 'setting-admin-api@example.com', + password: 'adminpw', + active: true, + roles: [role_api], + groups: groups, + ) + # create agent roles = Role.where(name: 'Agent') @agent = User.create_or_update( @@ -58,6 +80,13 @@ class SettingsControllerTest < ActionDispatch::IntegrationTest result = JSON.parse(@response.body) assert_equal(Hash, result.class) assert_not(result['settings']) + + # show + setting = Setting.find_by(name: 'product_name') + get "/api/v1/settings/#{setting.id}", {} + assert_response(401) + result = JSON.parse(@response.body) + assert_equal('Not authorized', result['error']) end test 'settings index with admin' do @@ -70,6 +99,157 @@ class SettingsControllerTest < ActionDispatch::IntegrationTest result = JSON.parse(@response.body) assert_equal(Array, result.class) assert(result) + hit_api = false + hit_product_name = false + result.each { |setting| + if setting['name'] == 'api_token_access' + hit_api = true + end + if setting['name'] == 'product_name' + hit_product_name = true + end + } + assert_equal(true, hit_api) + assert_equal(true, hit_product_name) + + # show + setting = Setting.find_by(name: 'product_name') + get "/api/v1/settings/#{setting.id}", {}, @headers.merge('Authorization' => credentials) + assert_response(200) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('product_name', result['name']) + + setting = Setting.find_by(name: 'api_token_access') + get "/api/v1/settings/#{setting.id}", {}, @headers.merge('Authorization' => credentials) + assert_response(200) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('api_token_access', result['name']) + + # update + setting = Setting.find_by(name: 'product_name') + params = { + id: setting.id, + name: 'some_new_name', + preferences: { + permission: ['admin.branding', 'admin.some_new_permission'], + some_new_key: true, + } + } + put "/api/v1/settings/#{setting.id}", params.to_json, @headers.merge('Authorization' => credentials) + assert_response(200) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('product_name', result['name']) + assert_equal(1, result['preferences']['permission'].length) + assert_equal('admin.branding', result['preferences']['permission'][0]) + assert_equal(true, result['preferences']['some_new_key']) + + # update + setting = Setting.find_by(name: 'api_token_access') + params = { + id: setting.id, + name: 'some_new_name', + preferences: { + permission: ['admin.branding', 'admin.some_new_permission'], + some_new_key: true, + } + } + put "/api/v1/settings/#{setting.id}", params.to_json, @headers.merge('Authorization' => credentials) + assert_response(200) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('api_token_access', result['name']) + assert_equal(1, result['preferences']['permission'].length) + assert_equal('admin.api', result['preferences']['permission'][0]) + assert_equal(true, result['preferences']['some_new_key']) + + # delete + setting = Setting.find_by(name: 'product_name') + delete "/api/v1/settings/#{setting.id}", {}.to_json, @headers.merge('Authorization' => credentials) + assert_response(401) + result = JSON.parse(@response.body) + assert_equal('Not authorized (feature not possible)', result['error']) + end + + test 'settings index with admin-api' do + + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('setting-admin-api@example.com', 'adminpw') + + # index + get '/api/v1/settings', {}, @headers.merge('Authorization' => credentials) + assert_response(200) + result = JSON.parse(@response.body) + assert_equal(Array, result.class) + assert(result) + hit_api = false + hit_product_name = false + result.each { |setting| + if setting['name'] == 'api_token_access' + hit_api = true + end + if setting['name'] == 'product_name' + hit_product_name = true + end + } + assert_equal(true, hit_api) + assert_equal(false, hit_product_name) + + # show + setting = Setting.find_by(name: 'product_name') + get "/api/v1/settings/#{setting.id}", {}, @headers.merge('Authorization' => credentials) + assert_response(401) + result = JSON.parse(@response.body) + assert_equal('Not authorized (required ["admin.branding"])', result['error']) + + setting = Setting.find_by(name: 'api_token_access') + get "/api/v1/settings/#{setting.id}", {}, @headers.merge('Authorization' => credentials) + assert_response(200) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('api_token_access', result['name']) + + # update + setting = Setting.find_by(name: 'product_name') + params = { + id: setting.id, + name: 'some_new_name', + preferences: { + permission: ['admin.branding', 'admin.some_new_permission'], + some_new_key: true, + } + } + put "/api/v1/settings/#{setting.id}", params.to_json, @headers.merge('Authorization' => credentials) + assert_response(401) + result = JSON.parse(@response.body) + assert_equal('Not authorized (required ["admin.branding"])', result['error']) + + # update + setting = Setting.find_by(name: 'api_token_access') + params = { + id: setting.id, + name: 'some_new_name', + preferences: { + permission: ['admin.branding', 'admin.some_new_permission'], + some_new_key: true, + } + } + put "/api/v1/settings/#{setting.id}", params.to_json, @headers.merge('Authorization' => credentials) + assert_response(200) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('api_token_access', result['name']) + assert_equal(1, result['preferences']['permission'].length) + assert_equal('admin.api', result['preferences']['permission'][0]) + assert_equal(true, result['preferences']['some_new_key']) + + # delete + setting = Setting.find_by(name: 'product_name') + delete "/api/v1/settings/#{setting.id}", {}.to_json, @headers.merge('Authorization' => credentials) + assert_response(401) + result = JSON.parse(@response.body) + assert_equal('Not authorized (feature not possible)', result['error']) end test 'settings index with agent' do @@ -83,6 +263,13 @@ class SettingsControllerTest < ActionDispatch::IntegrationTest assert_equal(Hash, result.class) assert_not(result['settings']) assert_equal('Not authorized (user)!', result['error']) + + # show + setting = Setting.find_by(name: 'product_name') + get "/api/v1/settings/#{setting.id}", {}, @headers.merge('Authorization' => credentials) + assert_response(401) + result = JSON.parse(@response.body) + assert_equal('Not authorized (user)', result['error']) end test 'settings index with customer' do @@ -96,6 +283,20 @@ class SettingsControllerTest < ActionDispatch::IntegrationTest assert_equal(Hash, result.class) assert_not(result['settings']) assert_equal('Not authorized (user)!', result['error']) + + # show + setting = Setting.find_by(name: 'product_name') + get "/api/v1/settings/#{setting.id}", {}, @headers.merge('Authorization' => credentials) + assert_response(401) + result = JSON.parse(@response.body) + assert_equal('Not authorized (user)', result['error']) + + # delete + setting = Setting.find_by(name: 'product_name') + delete "/api/v1/settings/#{setting.id}", {}.to_json, @headers.merge('Authorization' => credentials) + assert_response(401) + result = JSON.parse(@response.body) + assert_equal('Not authorized (feature not possible)', result['error']) end end diff --git a/test/integration/zendesk_import_test.rb b/test/integration/zendesk_import_test.rb index d90cfa138..2db21aeeb 100644 --- a/test/integration/zendesk_import_test.rb +++ b/test/integration/zendesk_import_test.rb @@ -46,7 +46,7 @@ class ZendeskImportTest < ActiveSupport::TestCase test 'check counts' do assert_equal(143, User.count, 'users') assert_equal(3, Group.count, 'groups') - assert_equal(4, Role.count, 'roles') + assert_equal(3, Role.count, 'roles') assert_equal(2, Organization.count, 'organizations') assert_equal(143, Ticket.count, 'tickets') assert_equal(151, Ticket::Article.count, 'ticket articles') diff --git a/test/unit/permission_test.rb b/test/unit/permission_test.rb index fc9fc340c..5f0e1eab4 100644 --- a/test/unit/permission_test.rb +++ b/test/unit/permission_test.rb @@ -10,4 +10,41 @@ class PermissionTest < ActiveSupport::TestCase assert_equal(2, permissions.count) end + test 'user permission' do + + Permission.create_if_not_exists( + name: 'admin.permission1', + note: 'Admin Interface', + preferences: {}, + ) + role_permission1 = Role.create_or_update( + name: 'AdminPermission1', + note: 'To configure your permission1.', + preferences: { + not: ['Customer'], + }, + default_at_signup: false, + updated_by_id: 1, + created_by_id: 1, + ) + role_permission1.permission_grand('admin.permission1') + user_with_permission1 = User.create_or_update( + login: 'setting-permission1', + firstname: 'Setting', + lastname: 'Admin Permission1', + email: 'setting-admin-permission1@example.com', + password: 'some_pw', + active: true, + roles: [role_permission1], + updated_by_id: 1, + created_by_id: 1, + ) + assert_equal(true, user_with_permission1.permissions?('admin.permission1')) + assert_equal(true, user_with_permission1.permissions?('admin.*')) + assert_equal(false, user_with_permission1.permissions?('admi.*')) + assert_equal(false, user_with_permission1.permissions?('admin.permission2')) + assert_equal(false, user_with_permission1.permissions?('admin')) + + end + end