Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #37987 - Filters dont inherit taxonomy on creation #10370

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions app/controllers/api/v2/filters_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ def show
param :filter, Hash, :action_aware => true, :required => true do
param :role_id, String, :required => true
param :search, String
param :override, :bool
param :permission_ids, Array
param :organization_ids, Array
param :location_ids, Array
end
end

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
module Foreman::Controller::Parameters::Filter
extend ActiveSupport::Concern
include Foreman::Controller::Parameters::Taxonomix

class_methods do
def filter_params_filter
Foreman::ParameterFilter.new(::Filter).tap do |filter|
filter.permit :resource_type,
:role_id, :role_name,
:search,
:taxonomy_search,
:unlimited,
:override,
:permissions => [], :permission_ids => [], :permission_names => []
add_taxonomix_params_filter(filter)
end
end
end
Expand Down
15 changes: 0 additions & 15 deletions app/controllers/filters_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,8 @@ def destroy
end
end

def disable_overriding
@filter = resource_base.find(params[:id])
@filter.disable_overriding!
process_success :success_msg => _('Filter overriding has been disabled')
end

private

def action_permission
case params[:action]
when 'disable_overriding'
'edit'
else
super
end
end

def find_role
@role = Role.find_by_id(role_id)
end
Expand Down
9 changes: 1 addition & 8 deletions app/controllers/roles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
class RolesController < ApplicationController
include Foreman::Controller::AutoCompleteSearch
include Foreman::Controller::Parameters::Role
before_action :find_resource, :only => [:clone, :edit, :update, :destroy, :disable_filters_overriding]
before_action :find_resource, :only => [:clone, :edit, :update, :destroy]

def index
params[:order] ||= 'name'
Expand Down Expand Up @@ -69,19 +69,12 @@ def destroy
end
end

def disable_filters_overriding
@role.disable_filters_overriding
process_success :success_msg => _('Filters overriding has been disabled')
end

private

def action_permission
case params[:action]
when 'clone'
'view'
when 'disable_filters_overriding'
'edit'
else
super
end
Expand Down
46 changes: 10 additions & 36 deletions app/models/filter.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
class Filter < ApplicationRecord
audited :associated_with => :role

include Taxonomix
Copy link
Member

@ofedoren ofedoren Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, we don't make filters non-taxonomable, but rather force them to be in line with the role they're assigned to disabling user override possibility.

If so, this should stay since this concern makes filter model taxonomable.

I think by disabling possibility to override taxonomies on filter level, there is no longer need to have taxonomy association, taxonomy_search can be created on the fly during before_save.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isnt that what before_save :enforce_inherited_taxonomies, do?

include Authorizable
include TopbarCacheExpiry

Expand Down Expand Up @@ -42,15 +41,14 @@ def ensure_taxonomies_not_escalated

scoped_search :on => :id, :complete_enabled => false, :only_explicit => true, :validator => ScopedSearch::Validators::INTEGER
scoped_search :on => :search, :complete_value => true
scoped_search :on => :override, :complete_value => { :true => true, :false => false }
scoped_search :on => :limited, :complete_value => { :true => true, :false => false }, :ext_method => :search_by_limited, :only_explicit => true
scoped_search :on => :unlimited, :complete_value => { :true => true, :false => false }, :ext_method => :search_by_unlimited, :only_explicit => true
scoped_search :relation => :role, :on => :id, :rename => :role_id, :complete_enabled => false, :only_explicit => true, :validator => ScopedSearch::Validators::INTEGER
scoped_search :relation => :role, :on => :name, :rename => :role
scoped_search :relation => :permissions, :on => :resource_type, :rename => :resource
scoped_search :relation => :permissions, :on => :name, :rename => :permission

before_validation :build_taxonomy_search, :nilify_empty_searches, :enforce_override_flag
before_validation :nilify_empty_searches
before_save :enforce_inherited_taxonomies, :nilify_empty_searches

validates :search, :presence => true, :unless => proc { |o| o.search.nil? }
Expand All @@ -60,7 +58,7 @@ def ensure_taxonomies_not_escalated
validate :role_not_locked
before_destroy :role_not_locked

validate :same_resource_type_permissions, :not_empty_permissions, :allowed_taxonomies
validate :same_resource_type_permissions, :not_empty_permissions

def self.allows_taxonomy_filtering?(_taxonomy)
false
Expand Down Expand Up @@ -153,39 +151,30 @@ def expire_topbar_cache
role.usergroups.each { |g| g.expire_topbar_cache }
end

def disable_overriding!
self.override = false
save!
end

def enforce_inherited_taxonomies
inherit_taxonomies! unless override?
end

def inherit_taxonomies!
self.organization_ids = role.organization_ids if resource_taxable_by_organization?
self.location_ids = role.location_ids if resource_taxable_by_location?
@organization_ids = role.organization_ids if resource_taxable_by_organization?
@location_ids = role.location_ids if resource_taxable_by_location?
build_taxonomy_search
end

private

def build_taxonomy_search
orgs = build_taxonomy_search_string('organization')
locs = build_taxonomy_search_string('location')
orgs = build_taxonomy_search_string_from_ids('organization')
locs = build_taxonomy_search_string_from_ids('location')

taxonomies = [orgs, locs].reject { |t| t.blank? }
self.taxonomy_search = taxonomies.join(' and ').presence
end

def build_taxonomy_search_string(name)
def build_taxonomy_search_string_from_ids(name)
return '' unless send("resource_taxable_by_#{name}?")
relation = send(name.pluralize).pluck(:id)
relation = name == "location" ? @location_ids : @organization_ids
return '' if relation.empty?

parenthesize("#{name}_id ^ (#{relation.join(',')})")
end

private

def nilify_empty_searches
self.search = nil if search.empty? || unlimited == '1'
self.taxonomy_search = nil if taxonomy_search.empty?
Expand Down Expand Up @@ -218,21 +207,6 @@ def not_empty_permissions
errors.add(:permissions, _('You must select at least one permission')) if permissions.blank? && filterings.blank?
end

def allowed_taxonomies
if organization_ids.present? && !resource_taxable_by_organization?
errors.add(:organization_ids, _('You can\'t assign organizations to this resource'))
end

if location_ids.present? && !resource_taxable_by_location?
errors.add(:location_ids, _('You can\'t assign locations to this resource'))
end
end

def enforce_override_flag
self.override = false unless resource_taxable?
true
end

def role_not_locked
errors.add(:role_id, _('is locked for user modifications.')) if role.locked? && !role.modify_locked
errors.empty?
Expand Down
6 changes: 1 addition & 5 deletions app/models/role.rb
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,6 @@ def remove_permissions!(*args)
find_for_permission_removal(args).map(&:destroy!)
end

def disable_filters_overriding
filters.where(:override => true).map { |filter| filter.disable_overriding! }
MariaAga marked this conversation as resolved.
Show resolved Hide resolved
end

def clone(role_params = {})
new_role = deep_clone(:except => [:name, :builtin, :origin],
:include => [:locations, :organizations, { :filters => :permissions }])
Expand Down Expand Up @@ -286,7 +282,7 @@ def self.override_search_operator(operator)
private

def sync_inheriting_filters
filters.where(:override => false).find_each do |f|
filters.find_each do |f|
unless f.save
errors.add :base, N_('One or more of the associated filters are invalid which prevented the role to be saved')
raise ActiveRecord::Rollback, N_("Unable to submit role: Problem with associated filter %s") % f.errors
Expand Down
5 changes: 4 additions & 1 deletion app/views/api/v2/filters/main.json.rabl
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ object @filter

extends "api/v2/filters/base"

attributes :search, :resource_type_label, :unlimited?, :created_at, :updated_at, :override?
attributes :search, :resource_type_label, :unlimited?, :created_at, :updated_at

child :role do
extends "api/v2/roles/base"
node do |role|
partial("api/v2/taxonomies/children_nodes", :object => role)
end
end

child :permissions do
Expand Down
4 changes: 0 additions & 4 deletions app/views/api/v2/filters/show.json.rabl
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
object @filter

extends "api/v2/filters/main"

node do |filter|
partial("api/v2/taxonomies/children_nodes", :object => filter)
end
4 changes: 0 additions & 4 deletions app/views/filters/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ end %>
<th><%= sort :resource, :as => s_("Filter|Resource"), :permitted => [:role_id] %></th>
<th><%= s_("Filter|Permissions") %></th>
<th><%= sort :search, :as => s_("Filter|Unlimited"), :permitted => [:role_id] %></th>
<th><%= sort :search, :as => s_("Filter|Override"), :permitted => [:role_id] %></th>
<th><%= sort :search, :as => s_("Filter|Search"), :permitted => [:role_id] %></th>
<% if @role && !@role.locked? %>
<th><%= _('Actions') %></th>
Expand All @@ -47,7 +46,6 @@ end %>
</td>
<td><%= filter.permissions.map(&:name).join(', ') %></td>
<td><%= checked_icon filter.unlimited? %></td>
<td><%= checked_icon filter.override? %></td>
<td>
<%= content_tag('span', link_to_unless_locked(filter.search || _('N/A'), @role,
hash_for_edit_filter_path(:id => filter, :role_id => @role).
Expand All @@ -58,8 +56,6 @@ end %>
<% buttons = [] %>
<% buttons.push display_link_if_authorized(_("Edit"), hash_for_edit_filter_path(:id => filter, :role_id => @role).
merge(:auth_object => filter, :authorizer => authorizer)) %>
<% buttons.push display_link_if_authorized(_("Disable overriding"), hash_for_disable_overriding_filter_path(:id => filter, :role_id => @role).
merge(:auth_object => filter, :authorizer => authorizer), :method => :patch) if filter.override? %>
<% buttons.push display_delete_if_authorized(hash_for_filter_path(:id => filter, :role_id => @role).
merge(:auth_object => filter, :authorizer => authorizer),
:data => { :confirm => (_("Delete filter?")) } ) %>
Expand Down
3 changes: 0 additions & 3 deletions app/views/roles/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
<%= hidden_field_tag :original_role_id, @role.cloned_from_id if @cloned_role %>

<% tax_help = N_("When the role's associated %{taxonomies} are changed,<br> the change will propagate to all inheriting filters.
Filters that are set to override <br> will remain untouched. Overriding of role filters can be easily disabled by <br> pressing the \"Disable overriding\" button.
Note that not all filters support <br>%{taxonomies}, so these always remain global.") %>
<% if show_location_tab? %>
<% loc_help = _(tax_help) % { :taxonomies => _('locations') }%>
Expand All @@ -52,8 +51,6 @@
<hr>
<%= link_to_if_authorized(_("New filter"), hash_for_new_filters_path(:role_id => @role),
{ :class => 'btn btn-success pull-right'} ) %>
<%= link_to_if_authorized(_('Disable all filters overriding'), hash_for_disable_filters_overriding_role_path(:id => @role),
:method => :patch, :class => 'btn btn-default pull-right') %>
<% end %>
</div>

Expand Down
4 changes: 2 additions & 2 deletions config/initializers/f_foreman_permissions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@
:'api/v2/filters' => [:index, :show]}
map.permission :create_filters, {:filters => [:new, :create],
:'api/v2/filters' => [:create]}
map.permission :edit_filters, {:filters => [:edit, :update, :disable_overriding], :permissions => [:index, :show_resource_types_with_translations],
map.permission :edit_filters, {:filters => [:edit, :update], :permissions => [:index, :show_resource_types_with_translations],
:'api/v2/filters' => [:update],
:'api/v2/permissions' => [:index, :show, :resource_types]}
map.permission :destroy_filters, {:filters => [:destroy],
Expand Down Expand Up @@ -453,7 +453,7 @@
:'api/v2/roles' => [:index, :show]}
map.permission :create_roles, {:roles => [:new, :create, :clone],
:'api/v2/roles' => [:create, :clone]}
map.permission :edit_roles, {:roles => [:edit, :update, :disable_filters_overriding],
map.permission :edit_roles, {:roles => [:edit, :update],
:'api/v2/roles' => [:update]}
map.permission :destroy_roles, {:roles => [:destroy],
:'api/v2/roles' => [:destroy]}
Expand Down
2 changes: 0 additions & 2 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,6 @@
resources :roles, except: [:show] do
member do
get 'clone'
patch 'disable_filters_overriding'
end
collection do
get 'auto_complete_search'
Expand All @@ -272,7 +271,6 @@

resources :filters, except: [:show, :new, :edit] do
member do
patch 'disable_overriding'
get 'edit', to: 'react#index'
end
collection do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class RemoveTaxonomyAndOverrideFromFilter < ActiveRecord::Migration[7.0]
def up
# remove_column :filters, :override
# filters = Filter.where(role_id: Role.where(origin: nil).or(Role.where(builtin: 2)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity: if we drop overriding anyway and force all the filters use inherited taxonomies, shouldn't we update all the filters?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this selects only the editable filters

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this selects only the editable filters

Aren't they all editable?..

Also, since filters were taxonomable and we're removing this, we should probably also clean related leftovers, such as TaxableTaxonomy.where(taxable_type: 'Filter').

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not the locked ones

  def locked?
    return false if modify_locked || self.class.modify_locked
    return false unless respond_to? :origin
    origin.present? && builtin != BUILTIN_DEFAULT_ROLE
  end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this is for role, not filter?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes but filters are a part of one role, and if its locked it cant be edited, not the role and not its filters, so it cant have override: true
Or do we just want to run it on the default roles anyway?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My apologies, you're right. It a role is locked, the filters can't be overriden and we also don't allow to lock roles.

I guess we could then search for overriden filters before we remove the column to update their taxonomy search to be based on the assigned role. And after that we could remove the column and the related taxonomy records.


# filters.each do |filter|
# filter.enforce_inherited_taxonomies
# filter.update_column(:taxonomy_search, filter.taxonomy_search)
# end
end

def down
# add_column :filters, :taxonomy_search, :text
# add_column :filters, :override, :boolean, :default => false, :null => false
# filters = Filter.where(role_id: Role.where(origin: nil).or(Role.where(builtin: 2))).where(override: false).where(taxonomy_search: nil)

# filters.each do |filter|
# filter.build_taxonomy_search
# filter.update_column(:taxonomy_search, filter.taxonomy_search)
# end
end
end
Loading
Loading