-
Notifications
You must be signed in to change notification settings - Fork 897
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
CustomButtonSet - make sure children follow button_order #18368
Conversation
Cc @ZitaNemeckova , with this change, you only need to send the button_order right. |
Yes. With this only |
app/models/custom_button_set.rb
Outdated
@@ -1,6 +1,11 @@ | |||
class CustomButtonSet < ApplicationRecord | |||
acts_as_miq_set | |||
|
|||
before_save :update_children | |||
def update_children | |||
replace_children Rbac.filtered(CustomButton.find(set_data[:button_order])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@himdel what about CustomButton.where(:id => ..)
to don't throw exception when IDs don't exists ? I also prefer where
because then we can pass just query (ActiveRecord::Relation
) to RBAC. (it is our long long term goal to pass only queries to RBAC)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, makes sense, fixed :)
.. actually, @lpichler I don't know... If we don't throw an exception on invalid ids, it means |
Added a second commit to explicitly |
app/models/custom_button_set.rb
Outdated
@@ -1,6 +1,13 @@ | |||
class CustomButtonSet < ApplicationRecord | |||
acts_as_miq_set | |||
|
|||
before_save :update_children | |||
def update_children | |||
replace_children Rbac.filtered(CustomButton.where(:id => set_data[:button_order])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's imagine a hypothetical scenario where you have 2 users (A and B). User A has access to the buttons x,y but not to z while user B has access to the buttons y, z but not to x. There's a custom button set already available with buttons x and z. What happens if either user A or user B tries to add to this set the button y? I think if user A touches this piece of code, the button set will contain x,y but not z even if it was originally in the set. Same goes for user B but then we will loose button x. Not sure if this situation is possible, if yes then we have a "loss of precision" here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, you're right. (I also have no idea how custom button sets act for different users, or indeed if rbac over custombuttons does anything. Anybody?)
Maybe we should just push RBAC filtering to the loading side, and skip it when saving?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The practical implication of the PR as it stands now (with Rbac.filtered
) is:
a user won't be able to edit a custom button set if it contains any buttons that user can't see
(because that throw(:abort)
should trigger)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not able to find any RBAC rule for CustomButton
model. Are you referencing to the rule
"User can see some CustomButtons" according to CustomButton#userid
?
if there is existence of such rule, it seems that it is not implemented in Rbac.filtered
call.
a user won't be able to edit a custom button set if it contains any buttons that user can't see
So should I implement it to the RBAC ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So should I implement it to the RBAC ?
No, it already does that (because the sets will not be the same after filtering, so if will abort).
If there are currently no rbac rules for custom buttons, that's fine, that probably means we don't have a problem at all, right @skateman ?
(Though it makes sense to make the solution correct for when rbac gets involved anyway.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@himdel yup, who knows when this can 💥 into our face when RBAC gets smarter 😕
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 so, which behaviour makes most sense then?
We can allow a user to edit sets where they can't access all the buttons, and not remove those buttons. But that means a user can theoretically add or remove buttons they don't have access to.
Or we can only allow a user to edit a set where that user has access to all the buttons. But then, it means they can potentially do less than they could do now. (Well, when there is a rule to make some buttons inaccessible to that user anyway.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@djberg96 is on PTO, but I'll talk to him on Tuesday about this, what else could we do on the API side.
But looks like some of my assumptions may be wrong, even though a
So, and I'm thinking...
EDIT: nope, looks like everything works with |
Updated to handle new, empty CustomButtonSets. The only remaining spec failures come from |
..And changed TaskHelpers::Import::CustomButtons to strip Should be green now :) |
@miq-bot remove_label wip |
app/models/custom_button_set.rb
Outdated
end | ||
|
||
children = Rbac.filtered(CustomButton.where(:id => set_data[:button_order])) | ||
throw(:abort) if children.pluck(:id).sort != set_data[:button_order].sort |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you want to use raise
here with an actual exception and its explanation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, as far as I can tell, throw :abort
is the only way for before_save
to abort that's compatible with rails 5.
(docs)
Or am I reading it wrong?
app/models/custom_button_set.rb
Outdated
@@ -1,6 +1,19 @@ | |||
class CustomButtonSet < ApplicationRecord | |||
acts_as_miq_set | |||
|
|||
before_save :update_children | |||
def update_children | |||
if set_data.nil? || set_data[:button_order].nil? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be a one-liner and you don't need the OR
at all if "you gotta get up and try
, and try
, and try
" 🤣
if set_data.nil? || set_data[:button_order].nil? | |
return remove_all_children if set_data.try(:[], :button_order).nil? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't be a one-liner, if remove_all_children
ever returns false, it will abort in Rails 4.
But 👍 , updating to use try
.
if klass.name == "CustomButtonSet" | ||
order = obj.fetch_path('attributes', 'set_data', :button_order) | ||
obj['attributes']['set_data'][:button_order] = nil if order.present? | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a red flag for @martinpovolny, but I understand that we need something like this 😕
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For some reason, none of CustomButtonSet === klass
, klass.is_a? CustomButtonSet
, klass.kind_of? ::CustomButtonSet
are true, only string comparison works.
And that "custom button" import deals with 3 different types of entities (CustomButton
, CustomButtonSet
, and ResourceAction
for some reason).
But yeah, I don't like it either :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems really weird. Have you inspected the klass
variable to see why that would happen? What does klass.class
show?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
> klass
=> CustomButtonSet(id: integer, name: string, description: string, set_type: string, created_on: datetime, updated_on: datetime, guid: string, read_only: boolean, set_data: text, mode: string, owner_type: string, owner_id: integer, userid: string, group_id: integer, href_slug: string, region_number: integer, region_description: string)
> klass.class
=> Class
> klass.name
=> "CustomButtonSet"
> CustomButtonSet
=> CustomButtonSet(id: integer, name: string, description: string, set_type: string, created_on: datetime, updated_on: datetime, guid: string, read_only: boolean, set_data: text, mode: string, owner_type: string, owner_id: integer, userid: string, group_id: integer, href_slug: string, region_number: integer, region_description: string)
> CustomButtonSet.class
=> Class
> CustomButtonSet.name
=> "CustomButtonSet"
> klass.is_a? CustomButtonSet
=> false
> CustomButtonSet.is_a? klass
=> false
¯\_(ツ)_/¯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fun :)
> klass.object_id
=> 47105247798240
> CustomButtonSet.object_id
=> 47105247798240
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this could be caused by FactoryGirl, I'm running this via be rspec spec/lib/task_helpers/imports/custom_buttons_spec.rb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean FactoryBot? ;)
Anyway, I've been burned a few times by FactoryBot creating new instances internally, causing allow
to fail. I'll have to look at the spec.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heheh, I actually don't think being a girl should be considered offensive :). But yeah, that.
Thanks! :)
app/models/custom_button_set.rb
Outdated
children = Rbac.filtered(CustomButton.where(:id => set_data[:button_order])) | ||
throw(:abort) if children.pluck(:id).sort != set_data[:button_order].sort | ||
|
||
replace_children Rbac.filtered(CustomButton.where(:id => set_data[:button_order])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it's wrong, but can't we use find
here, then you don't need the :id
at all and AFAIK it fails with invalid IDs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aand added a spec, and split into before_save/after_save after all, since we need the set to exist before adding children. |
@miq-bot remove_label wip Seriously :) |
Made changes so ManageIQ/manageiq-ui-classic#4913 doesn't need it so it doesn't need |
CustomButtonSet has currently 2 independent lists of children: - `set_data[:button_order]` is an array of ids with the right order, - `children` (or `members`) is a collection of custom buttons, in no order Currently, in the UI, the tree is reading button_order, the GTL is reading `members`, the SUI toolbar is using button_order, the OPS UI toolbar is using both. It's essential to keep the 2 lists the same, and there are 2 ways of achieving it: * adding methods to add items to specific position, that will handle both button_order and children, and expose those via the API * deciding `button_order` is the canonical collection, and we can always replace children based on the ids in button order. The first option would mean doing N+1 API requests when creating a button group with N assigned buttons, the second option means we only have to edit the custom button set, without any per-button requests. (We never create a new custom button as part of editing/creating a set.)
…rder don't exist without this, code like... ``` cbs.set_data[:button_order] = [1,2,3] cbs.save! ``` would suceed in saving a CustomButtonSet with that button order, even when some of the buttons don't exist. Since that would mean that children no longer match the button order, we need to throw. `throw(:abort)` is the only valid way of stopping the chain in Rails 5, and seems to work find in older rails too.
…ata nil no set_data => no children
…order instead adding a child won't actually affect any custom button toolbar that relies on the order (such as SUI)
…ing CustomButtonSet button_order is ignored for imports anyway, `custom_button_set_post` rewrites it. But having it set during `create!` causes `update_children` to fail because it can't find those nonexistent CustomButtons.
otherwise, creating a CustomButtonSet with nonempty button_order fails: ActiveRecord::RecordNotSaved: You cannot call create unless the parent is saved # ./app/models/mixins/relationship_mixin.rb:446:in `add_relationship'
Checked commits https://github.com/himdel/manageiq/compare/5069334e0f6f373f036f230e8ec0aa34bcf8c9e9~...1c4ea876a1dfb0a8a6ba3e0ac54c739a8484d1e8 with ruby 2.3.3, rubocop 0.52.1, haml-lint 0.20.0, and yamllint 1.10.0 spec/models/generic_object_definition_spec.rb
|
@himdel please ping me if you have time to discuss this 😉 |
I think this could be sorted out better with a rework of sets in our backend, but that's not the call of the UI team to make. So this is good to go from my side if it solves @ZitaNemeckova's problem with the API-driven generic object stuff. But I'm also pinging @kbrock, as far as I remember he said something about our sets being bad |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gtanzillo Yep, a bit janky, but let's go with it for now and refactor as needed. |
after ManageIQ/manageiq/pull/18368 the purpose of `set_data[:button_order]` has been changed, and an error is raised after going through an array, where the button does not exist anymore
This fixes https://bugzilla.redhat.com/show_bug.cgi?id=1737449, which is open for h, so we should backport (along with ManageIQ/manageiq-ui-classic#5319 and ManageIQ/manageiq-api#563 per conversation with @himdel.) |
@miq-bot add_label hammer/yes |
CustomButtonSet - make sure children follow button_order (cherry picked from commit 2c50993) https://bugzilla.redhat.com/show_bug.cgi?id=1745198
Hammer backport details:
|
CustomButtonSet has currently 2 independent lists of children:
set_data[:button_order]
is an array of ids with the right order,children
(ormembers
) is a collection of custom buttons, in no orderCurrently, in the UI, the tree is reading button_order, the GTL is reading
members
,the SUI toolbar is using button_order, the OPS UI toolbar is using both.
It's essential to keep the 2 lists the same, and there are 2 ways of achieving it:
button_order
is the canonical collection, and we can always replace children based on the ids in button order.The first option would mean doing N+1 API requests when creating a button group with N assigned buttons,
the second option means we only have to edit the custom button set, without any per-button requests.
(We never create a new custom button as part of editing/creating a set.)
Fixes ManageIQ/manageiq-api#539
(together with ManageIQ/manageiq-api#541 exposing the collection so that we can query it via the API)