Project

General

Profile

« Previous | Next » 

Revision 2db78de0

Added by Ondřej Pražák over 5 years ago

Fixes #24259 - Add canned admin role

View differences:

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