Revision 2db78de0
Added by Ondřej Pražák over 5 years ago
app/controllers/api/v2/settings_controller.rb | ||
---|---|---|
module Api
|
||
module V2
|
||
class SettingsController < V2::BaseController
|
||
before_action :require_admin
|
||
before_action :find_resource, :only => %w{show update}
|
||
|
||
api :GET, "/settings/", N_("List all settings")
|
app/controllers/filters_controller.rb | ||
---|---|---|
include Foreman::Controller::AutoCompleteSearch
|
||
include Foreman::Controller::Parameters::Filter
|
||
|
||
before_action :find_role
|
||
before_action :find_role, :except => :edit
|
||
before_action :setup_search_options, :only => :index
|
||
|
||
def index
|
app/controllers/settings_controller.rb | ||
---|---|---|
class SettingsController < ApplicationController
|
||
include Foreman::Controller::AutoCompleteSearch
|
||
|
||
before_action :require_admin
|
||
helper_method :xeditable?
|
||
|
||
# This can happen in development when removing a plugin
|
||
... | ... | |
end
|
||
|
||
def xeditable?(object = nil, permission = nil)
|
||
# The current user is required to be admin
|
||
current_user.admin?
|
||
current_user.can? :edit_settings
|
||
end
|
||
end
|
app/models/filter.rb | ||
---|---|---|
false
|
||
end
|
||
|
||
# allow creating filters for non-taxable resources when user is not admin
|
||
def ensure_taxonomies_not_escalated
|
||
super if skip_taxonomy_escalation_check?
|
||
end
|
||
|
||
def skip_taxonomy_escalation_check?
|
||
if self.resource_class.present?
|
||
!self.resource_class.included_modules.include?(Taxonomix)
|
||
else
|
||
true
|
||
end
|
||
end
|
||
|
||
belongs_to :role
|
app/models/role.rb | ||
---|---|---|
MANAGER = 'Manager'
|
||
ORG_ADMIN = 'Organization admin'
|
||
VIEWER = 'Viewer'
|
||
SYSTEM_ADMIN = 'System admin'
|
||
|
||
has_associated_audits
|
||
scope :givable, -> { where(:builtin => 0).order(:name) }
|
||
scope :for_current_user, -> { User.current.admin? ? where('0 = 0') : where(:id => User.current.role_ids) }
|
||
scope :for_current_user, -> { User.current.can_escalate? ? givable : givable.where(:id => User.current.cached_role_ids) }
|
||
scope :builtin, lambda { |*args|
|
||
compare = 'not' if args.first
|
||
where("#{compare} builtin = 0")
|
||
... | ... | |
permission_names - current_names
|
||
end
|
||
|
||
def add_permissions!(*args)
|
||
add_permissions(*args)
|
||
def add_permissions!(permissions, opts = {})
|
||
add_permissions(permissions, opts.merge(:save! => true))
|
||
save!
|
||
end
|
||
|
||
... | ... | |
def permission_records(permissions)
|
||
perms = permissions.flatten
|
||
collection = Permission.where(:name => perms).all
|
||
raise ::Foreman::PermissionMissingException.new(N_('some permissions were not found')) if collection.size != perms.size
|
||
if collection.size != perms.size
|
||
raise ::Foreman::PermissionMissingException.new(N_("some permissions were not found: %s"),
|
||
not_found_permissions(collection.pluck(:name), perms))
|
||
end
|
||
collection
|
||
end
|
||
|
||
def not_found_permissions(first, second)
|
||
(first - second) | (second - first)
|
||
end
|
||
end
|
app/models/user.rb | ||
---|---|---|
end
|
||
|
||
# user must be assigned all given roles in order to delegate them
|
||
def can_assign?(roles)
|
||
can_change_admin_flag? || roles.all? { |r| self.role_ids_was.include?(r) }
|
||
# or have :escalate_roles permission
|
||
def can_assign?(role_ids)
|
||
can_escalate? || role_ids.all? { |r| self.role_ids_was.include?(r) }
|
||
end
|
||
|
||
def can_escalate?
|
||
self.admin? || self.can?(:escalate_roles)
|
||
end
|
||
|
||
# only admin can change admin flag
|
app/registries/foreman/access_permissions.rb | ||
---|---|---|
map.permission :revoke_personal_access_tokens,
|
||
:"api/v2/personal_access_tokens" => [:destroy]
|
||
end
|
||
|
||
permission_set.security_block :settings do |map|
|
||
map.permission :view_settings, { :settings => [:index, :show, :auto_complete_search],
|
||
:'api/v2/settings' => [:index, :show] }
|
||
map.permission :edit_settings, { :settings => [:update],
|
||
:'api/v2/settings' => [:update] }
|
||
end
|
||
end
|
app/registries/foreman/plugin/rbac_support.rb | ||
---|---|---|
class Plugin
|
||
class RbacSupport
|
||
# These plugins can be extended by plugins through plugin API
|
||
AUTO_EXTENDED_ROLES = [ Role::VIEWER, Role::MANAGER, Role::ORG_ADMIN ]
|
||
AUTO_EXTENDED_ROLES = [ Role::VIEWER, Role::MANAGER, Role::ORG_ADMIN, Role::SYSTEM_ADMIN ]
|
||
|
||
def add_all_permissions_to_default_roles(all_permissions)
|
||
view_permissions = all_permissions.where("name LIKE :name", :name => "view_%")
|
||
... | ... | |
Role.without_auditing do
|
||
existing_permissions = this_role.permissions.where(:name => permission_names).pluck(:name)
|
||
to_add = permission_names.select { |name| !existing_permissions.include?(name) }
|
||
this_role.add_permissions! to_add if to_add.any?
|
||
Role.skip_permission_check do
|
||
this_role.add_permissions! to_add if to_add.any?
|
||
end
|
||
end
|
||
end
|
||
end
|
app/views/usergroups/_form.html.erb | ||
---|---|---|
</div>
|
||
<div class="tab-pane" id="roles">
|
||
<%= checkbox_f f, :admin if User.current.can_change_admin_flag? %>
|
||
<%= multiple_checkboxes f, :roles, @usergroup, Role.givable.for_current_user, {:label => _('Roles')} %>
|
||
<%= multiple_checkboxes f, :roles, @usergroup, Role.for_current_user, {:label => _('Roles')} %>
|
||
</div>
|
||
<% if AuthSource.non_internal.present? %>
|
||
<div class="tab-pane" id="external">
|
app/views/users/_form.html.erb | ||
---|---|---|
|
||
<div class='tab-pane' id='roles'>
|
||
<%= checkbox_f f, :admin if User.current.can_change_admin_flag? %>
|
||
<%= multiple_checkboxes f, :roles, @user, Role.givable.for_current_user,
|
||
{ :label => _('Roles')}, {:disabled => @editing_self ? Role.givable.for_current_user.pluck(:id) : false } %>
|
||
<%= multiple_checkboxes f, :roles, @user, Role.for_current_user,
|
||
{ :label => _('Roles')}, {:disabled => @editing_self ? Role.for_current_user.pluck(:id) : false } %>
|
||
<% usergroups = @user.cached_usergroups.includes(:roles).distinct %>
|
||
<% if usergroups.any? %>
|
||
<div class="form-group" id="inherited-roles">
|
db/seeds.d/020-permissions_list.rb | ||
---|---|---|
['Role', 'create_roles'],
|
||
['Role', 'edit_roles'],
|
||
['Role', 'destroy_roles'],
|
||
[nil, 'escalate_roles'],
|
||
['Setting', 'view_settings'],
|
||
['Setting', 'edit_settings'],
|
||
['SmartProxy', 'view_smart_proxies'],
|
||
['SmartProxy', 'create_smart_proxies'],
|
||
['SmartProxy', 'edit_smart_proxies'],
|
db/seeds.d/020-roles_list.rb | ||
---|---|---|
class << self
|
||
def seeded_roles
|
||
{
|
||
Role::MANAGER => { :permissions => base_manage_permissions + view_permissions + manage_organizations_permissions,
|
||
Role::MANAGER => { :permissions => base_manage_permissions + view_permissions + manage_organizations_permissions + settings_permissions,
|
||
:description => 'Role granting all available permissions. With this role, user is able to do everything that admin can except for changing settings.' },
|
||
Role::ORG_ADMIN => { :permissions => base_manage_permissions + view_permissions,
|
||
:description => 'Role granting all permissions except for managing organizations. It can be used to delegate administration of specific organization to a user. In order to create such role, clone this role and assign desired organizations' },
|
||
Role::SYSTEM_ADMIN => { :permissions => (settings_permissions + manage_organizations_permissions + system_admin_extra_permissions + escalate_roles_permission),
|
||
:description => 'Role granting permissions for managing organizations, locations, users, usergroups, auth sources, roles, filters and settings. This is a very powerful role that can potentially gain access to all resources.' },
|
||
|
||
'Edit partition tables' => { :permissions => [:view_ptables, :create_ptables, :edit_ptables, :destroy_ptables], :description => 'Role granting permissions required for managing partition tables' },
|
||
'View hosts' => { :permissions => [:view_hosts],
|
||
:description => 'Role granting permission only to view hosts' },
|
||
... | ... | |
end
|
||
|
||
def base_manage_permissions
|
||
PermissionsList.permissions.reject { |resource, name| name.start_with?('view_') }.map { |p| p.last.to_sym } - manage_organizations_permissions - role_managements_permissions
|
||
PermissionsList.permissions.reject { |resource, name| name.start_with?('view_') }
|
||
.map { |p| p.last.to_sym } - manage_organizations_permissions - role_managements_permissions - settings_permissions - escalate_roles_permission
|
||
end
|
||
|
||
def manage_organizations_permissions
|
||
... | ... | |
]
|
||
end
|
||
|
||
def escalate_roles_permission
|
||
[:escalate_roles]
|
||
end
|
||
|
||
def system_admin_extra_permissions
|
||
[
|
||
:view_organizations, :edit_organizations, :assign_organizations,
|
||
:view_locations, :edit_locations, :assign_locations, :create_locations, :destroy_locations,
|
||
:view_users, :create_users, :edit_users, :destroy_users,
|
||
:view_usergroups, :create_usergroups, :edit_usergroups, :destroy_usergroups,
|
||
:view_roles, :create_roles, :edit_roles, :destroy_roles,
|
||
:view_authenticators, :create_authenticators, :edit_authenticators, :destroy_authenticators,
|
||
:view_filters, :create_filters, :edit_filters, :destroy_filters
|
||
]
|
||
end
|
||
|
||
def role_managements_permissions
|
||
[
|
||
:create_roles, :edit_roles, :destroy_roles,
|
||
... | ... | |
end
|
||
|
||
def view_permissions
|
||
PermissionsList.permissions.select { |resource, name| name.start_with?('view_') }.map { |p| p.last.to_sym }
|
||
PermissionsList.permissions.select { |resource, name| name.start_with?('view_') && name != 'view_settings' }.map { |p| p.last.to_sym }
|
||
end
|
||
|
||
def settings_permissions
|
||
[:view_settings, :edit_settings]
|
||
end
|
||
end
|
||
end
|
test/controllers/api/v2/settings_controller_test.rb | ||
---|---|---|
response = ActiveSupport::JSON.decode(@response.body)
|
||
assert response["results"][0].key?("full_name")
|
||
end
|
||
|
||
test "should update setting as system admin" do
|
||
user = user_one_as_system_admin
|
||
setting_id = Setting.where(:settings_type => 'integer').first.id
|
||
as_user user do
|
||
put :update, params: { :id => setting_id, :setting => { :value => "100" } }
|
||
end
|
||
assert_response :success
|
||
end
|
||
|
||
test "should view setting as system admin" do
|
||
user = user_one_as_system_admin
|
||
setting_id = Setting.first.id
|
||
as_user user do
|
||
get :show, params: { :id => setting_id }
|
||
end
|
||
assert_response :success
|
||
end
|
||
|
||
private
|
||
|
||
def user_one_as_system_admin
|
||
user = users(:one)
|
||
user.roles = [Role.default, Role.find_by_name('System admin')]
|
||
user
|
||
end
|
||
end
|
test/controllers/api/v2/users_controller_test.rb | ||
---|---|---|
assert_response :success
|
||
assert_equal JSON.parse(@response.body)['roles'].map { |role| role["id"] }, roles.map { |role| role.id }, "Can't update user with valid roles #{roles}"
|
||
end
|
||
|
||
test "should create user with escalated roles as system admin" do
|
||
roles = [Role.find_by_name('Manager')]
|
||
org = FactoryBot.create(:organization)
|
||
loc = FactoryBot.create(:location)
|
||
system_admin = FactoryBot.create :user, :login => 'ca',
|
||
:role_ids => [roles(:system_admin).id],
|
||
:organization_ids => [org.id],
|
||
:location_ids => [loc.id]
|
||
as_user system_admin do
|
||
post :create, params: { :user => min_valid_attrs.clone.update(:role_ids => roles.map { |role| role.id },
|
||
:organization_ids => [org.id],
|
||
:location_ids => [loc.id]) }
|
||
assert_response :success
|
||
end
|
||
end
|
||
end
|
test/fixtures/filterings.yml | ||
---|---|---|
crud_hosts_1_3:
|
||
filter: crud_hosts_1
|
||
permission: view_hosts
|
||
system_admin_1_0:
|
||
filter: system_admin_1
|
||
permission: view_organizations
|
||
system_admin_1_1:
|
||
filter: system_admin_1
|
||
permission: edit_organizations
|
||
system_admin_1_2:
|
||
filter: system_admin_1
|
||
permission: assign_organizations
|
||
system_admin_1_3:
|
||
filter: system_admin_1
|
||
permission: create_organizations
|
||
system_admin_1_4:
|
||
filter: system_admin_1
|
||
permission: destroy_organizations
|
||
system_admin_2_0:
|
||
filter: system_admin_2
|
||
permission: view_settings
|
||
system_admin_2_1:
|
||
filter: system_admin_2
|
||
permission: edit_settings
|
||
system_admin_3_0:
|
||
filter: system_admin_3
|
||
permission: escalate_roles
|
||
system_admin_3_1:
|
||
filter: system_admin_3
|
||
permission: view_roles
|
||
system_admin_4_0:
|
||
filter: system_admin_4
|
||
permission: create_filters
|
||
system_admin_5_0:
|
||
filter: system_admin_5
|
||
permission: view_users
|
||
system_admin_5_1:
|
||
filter: system_admin_5
|
||
permission: create_users
|
||
system_admin_6_0:
|
||
filter: system_admin_6
|
||
permission: view_locations
|
||
system_admin_6_1:
|
||
filter: system_admin_6
|
||
permission: edit_locations
|
||
system_admin_6_2:
|
||
filter: system_admin_6
|
||
permission: assign_locations
|
||
system_admin_6_3:
|
||
filter: system_admin_6
|
||
permission: create_locations
|
||
system_admin_6_4:
|
||
filter: system_admin_6
|
||
permission: destroy_locations
|
||
|
||
<% Foreman::Plugin.all.map(&:default_roles).inject({}, :merge).each do |role,permissions| %>
|
||
<% role_id = role.tr(' ', '_').scan(/[a-z_]/i).join %>
|
||
<% permissions.each do |permission| %>
|
test/fixtures/filters.yml | ||
---|---|---|
role_id: 11
|
||
crud_hosts_1:
|
||
role_id: 12
|
||
system_admin_1:
|
||
role_id: 14
|
||
system_admin_2:
|
||
role_id: 14
|
||
system_admin_3:
|
||
role_id: 14
|
||
system_admin_4:
|
||
role_id: 14
|
||
system_admin_5:
|
||
role_id: 14
|
||
system_admin_6:
|
||
role_id: 14
|
||
|
||
<% Foreman::Plugin.all.map(&:default_roles).inject({}, :merge).each do |role, permissions| %>
|
||
<% role_id = role.tr(' ', '_').scan(/[a-z_]/i).join %>
|
||
# This line assumes permissions come in the form of a symbol 'action_resource'
|
test/fixtures/permissions.yml | ||
---|---|---|
resource_type: ReportTemplate
|
||
created_at: "2014-01-07 15:09:32.783442"
|
||
updated_at: "2014-01-07 15:09:32.783442"
|
||
view_settings:
|
||
name: view_settings
|
||
resource_type: Setting
|
||
created_at: "2014-01-07 15:09:32.783442"
|
||
updated_at: "2014-01-07 15:09:32.783442"
|
||
edit_settings:
|
||
name: edit_settings
|
||
resource_type: Setting
|
||
created_at: "2014-01-07 15:09:32.783442"
|
||
updated_at: "2014-01-07 15:09:32.783442"
|
||
escalate_roles:
|
||
name: escalate_roles
|
||
resource_type: Role
|
||
created_at: "2014-01-07 15:09:32.783442"
|
||
updated_at: "2014-01-07 15:09:32.783442"
|
||
|
||
<% Foreman::Plugin.all.map(&:permissions).inject({}, :merge).each do |permission,attrs| %>
|
||
<%= permission %>:
|
||
name: <%= permission %>
|
test/fixtures/roles.yml | ||
---|---|---|
builtin: "0"
|
||
origin: foreman
|
||
|
||
system_admin:
|
||
name: System admin
|
||
id: "14"
|
||
builtin: "0"
|
||
origin: foreman
|
||
|
||
<% Foreman::Plugin.all.map do |plugin| %>
|
||
<% plugin.default_roles.each do |role_name, permissions| %>
|
||
<%= role_name.tr(' ', '_').scan(/[a-z_]/i).join %>:
|
test/fixtures/user_roles.yml | ||
---|---|---|
user_restricted_manage_compute_resources:
|
||
owner: restricted
|
||
role_id: 11
|
||
|
||
user_view_hosts_view_hosts:
|
||
owner: view_hosts
|
||
role_id: 3
|
||
|
||
user_system_admin_system_admin:
|
||
owner: system_admin
|
||
role_id: 14
|
test/fixtures/users.yml | ||
---|---|---|
auth_source: internal
|
||
password_hash: 02d7ff9921071af778ff4f8608579dcd6d80dfba
|
||
password_salt: 80a167f1effbd82c2485ed81c3cfd68b11bc40dc
|
||
|
||
view_hosts:
|
||
login: view_hosts
|
||
lower_login: view_hosts
|
||
firstname: view_hosts
|
||
lastname: User
|
||
mail: view_hosts@someware.com
|
||
admin: false
|
||
last_login_on: 2009-10-12 21:50:04
|
||
auth_source: internal
|
||
password_hash: 02d7ff9921071af778ff4f8608579dcd6d80dfba
|
||
password_salt: 80a167f1effbd82c2485ed81c3cfd68b11bc40dc
|
||
|
||
system_admin:
|
||
login: system_admin
|
||
lower_login: system_admin
|
||
firstname: system_admin
|
||
lastname: User
|
||
mail: system_admin@anywhere.com
|
||
admin: false
|
||
last_login_on: 2009-10-12 21:50:04
|
||
auth_source: internal
|
||
password_hash: 02d7ff9921071af778ff4f8608579dcd6d80dfba
|
||
password_salt: 80a167f1effbd82c2485ed81c3cfd68b11bc40dc
|
test/models/filter_test.rb | ||
---|---|---|
assert_valid f
|
||
end
|
||
|
||
test 'filter for taxable resource does not skip escalation check' do
|
||
f = FactoryBot.build_stubbed(:filter, :resource_type => 'Domain')
|
||
refute f.skip_taxonomy_escalation_check?
|
||
end
|
||
|
||
test 'filter for nontaxable resource skips escalation check' do
|
||
f = FactoryBot.build_stubbed(:filter, :resource_type => 'Architecture')
|
||
assert f.skip_taxonomy_escalation_check?
|
||
end
|
||
|
||
test 'filter without resource always skips escalation check' do
|
||
f = FactoryBot.build_stubbed(:filter, :resource_type => nil)
|
||
assert f.skip_taxonomy_escalation_check?
|
||
end
|
||
|
||
test 'disable overriding recalculates taxonomies' do
|
||
f = FactoryBot.build(:filter, :resource_type => 'Domain')
|
||
f.role = FactoryBot.build(:role, :organizations => [ FactoryBot.build(:organization) ])
|
test/models/role_test.rb | ||
---|---|---|
it { subject.wont_include(second) }
|
||
end
|
||
|
||
context "when current user is admin for_current_user should return all roles" do
|
||
setup do
|
||
User.current = users(:admin)
|
||
context "when current user is admin for_current_user should return all givable roles" do
|
||
test "Admin user should query Role model with no restrictions" do
|
||
assert_include Role.for_current_user.to_sql, 'WHERE "roles"."builtin" = 0'
|
||
end
|
||
end
|
||
|
||
test "Admin user should query Role model with no restrictions" do
|
||
assert_include Role.for_current_user.to_sql, 'WHERE (0 = 0)'
|
||
context "when current user is not admin" do
|
||
test "should not allow to escalate when missing escalation permission" do
|
||
role = Role.create(:name => 'Not owned')
|
||
User.current = users(:view_hosts)
|
||
|
||
refute_includes Role.for_current_user, role
|
||
end
|
||
|
||
test "should allow to escalate for canned admin" do
|
||
role = Role.create(:name => 'Not owned by canned admin')
|
||
User.current = users(:system_admin)
|
||
|
||
refute_includes Role.for_current_user, role
|
||
end
|
||
end
|
||
end
|
||
... | ... | |
end
|
||
end
|
||
end
|
||
|
||
describe "#for_current_user" do
|
||
setup do
|
||
@roles = [Role.find_by_name('Manager')]
|
||
@user = FactoryBot.create(:user, :admin => false)
|
||
@role = FactoryBot.create(:role)
|
||
|
||
FactoryBot.create(:user_role, :owner => @user, :role => @role)
|
||
FactoryBot.create(:filter, :role => @role, :permissions => [permissions(:escalate_roles)])
|
||
end
|
||
|
||
it "should display roles" do
|
||
as_user @user do
|
||
refute_empty Role.for_current_user
|
||
end
|
||
end
|
||
end
|
||
end
|
test/models/user_test.rb | ||
---|---|---|
assert user.save
|
||
end
|
||
|
||
test "use that can change admin flag #can_assign? any role" do
|
||
user = users(:one)
|
||
test "user that is admin #can_assign? any role" do
|
||
user = users(:one)
|
||
extra_role = Role.where(:name => "foobar").first_or_create
|
||
user.stub :can_change_admin_flag?, true do
|
||
user.stub :admin?, true do
|
||
assert user.can_assign?([extra_role.id])
|
||
end
|
||
end
|
||
... | ... | |
end
|
||
|
||
test "non admin user #can_assign? only his assigned roles" do
|
||
user = users(:one)
|
||
user = users(:one)
|
||
foobar = Role.where(:name => "foobar").first_or_create
|
||
barfoo = Role.where(:name => "barfoo").first_or_create
|
||
user.roles << foobar
|
test/unit/tasks/seeds_test.rb | ||
---|---|---|
# Check all access control have a matching seeded permission
|
||
assert_equal [], access_permissions - seeded_permissions
|
||
# Check all seeded permissions have a matching access control
|
||
assert_equal [], seeded_permissions - access_permissions
|
||
# except for 'escalate_roles' as it is not tied to a controller action
|
||
assert_equal [], seeded_permissions - access_permissions - ['escalate_roles']
|
||
end
|
||
|
||
test "viewer role contains all view permissions" do
|
||
test "viewer role contains all view permissions except for settings" do
|
||
seed('020-permissions_list.rb', '030-permissions.rb', '020-roles_list.rb', '040-roles.rb')
|
||
view_permissions = Permission.all.select { |permission| permission.name.match(/view/) }
|
||
view_permissions = Permission.all.select { |permission| permission.name.match(/view/) && permission.name != 'view_settings' }
|
||
assert_equal [], view_permissions - Role.unscoped.find_by_name('Viewer').permissions
|
||
end
|
||
end
|
Also available in: Unified diff
Fixes #24259 - Add canned admin role