-
Notifications
You must be signed in to change notification settings - Fork 162
Toggle group ownership on projects #4792
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
base: master
Are you sure you want to change the base?
Conversation
apps/dashboard/app/models/project.rb
Outdated
| else | ||
| true | ||
| end | ||
| 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.
Even though this could be condensed by combining the 'None' case and the unset case, I left them separate so that we can replace true in the ternary with the ownership removal later. Otherwise we could simplify it to just
new_group = attributes.fetch(:group_owner, 'None')
(new_group == 'None') ? true : set_group_owner(new_group)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 don't see where this is being called from. Also - None value is likely dangerous if it's ever used. It should default to the users Primary group as that will always be a valid fallback.
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 already removed the 'None' option from the select, as that was only necessary when we were detecting the 'possible groups', which could have been an empty set. Now that we are detecting the user's groups directly, there is no way to submit 'None'. However I see how this could create issues, maybe just sticking with nil as the default is a better idea. It should still cause set_group_owner to rescue, but ideally without any unintended effects
apps/dashboard/app/models/project.rb
Outdated
| @directory = File.expand_path(@directory) unless @directory.blank? | ||
| @template = attributes[:template] | ||
|
|
||
| @group_owner = get_group_owner |
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.
Although this instance variable is not explicitly used, it is responsible for setting the initial value in the form
|
I don't think I'm a fan of modifying the directory after it's been created. I'd think this is needed in the project creation - but editing or changing it after the fact, I'm not so sure. I don't think changing the group/account/charge-back code after the fact is something that'll ever need to happen. This is because funds ($) are tied to this group/account/charge-back code. A new/different group/account/charge-back code is an entirely new project. Specifying it during project creation? Sure, I think that's needed. |
|
If we specify it during creation, we don't get to do the automagic detection of which groups it is 'shareable' with, since that depends on the location of the project root (unknown before initial creation). Of course this is all an alternative to specifying in the group level directory the proper default group ownership, but that moves the responsibility from the project owner to the center admin.
Isn't that the only way we can toggle projects from private to shared and vice versa? If charge schemes prevent us from modifying group ownership then we could not allow for mistakes to be made during project creation (which seems user-error prone) |
|
I am also trying to account for a universal shared directory where you want to limit your project to a specific group (but multiple groups have access to the parent). Is that something we should be accounting for or not? The use case I had in mind for this was the following interaction when working with non-primary groups This seems like a common enough occurrence that we should find a good solution to it, or putting this responsibility on the admin who creates the |
I don't think there is such a thing as a "universal shared directory" outside of maybe
If we're accounting for a mistake - then maybe - but Changing the ownership after the fact I think just sets us up for a lot of edge case failures. You'd want to
Not sure where you're getting the admin from here. If a user creates |
|
Ok the group selection option has now been enabled for the new page only, and the group is only shown on projects outside the user's home directory (potentially shared projects). We can return to group owner modification later, when we have a way to account for edge cases or have a clearer idea of what that should look like. |
| end | ||
|
|
||
| def user_groups | ||
| CurrentUser.groups.map(&:name) |
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.
CurrentUser already has this api.
ondemand/apps/dashboard/lib/current_user.rb
Lines 39 to 41 in c4d8536
| def group_names | |
| @group_names ||= groups.map(&:name) | |
| end |
apps/dashboard/app/models/project.rb
Outdated
| if new_record? | ||
| set_group_owner(@group_owner) | ||
| return | ||
| 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.
This should likely go into the save method. I don't think we should chmod during object creation as it may never actually get saved.
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.
Yeah that seems like a good idea
| end | ||
|
|
||
| def private? | ||
| project_dataroot.to_s.start_with?(CurrentUser.home) |
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'm getting tripped up on project_dataroot. This was supposed to be the root directory for all the default projects - i.e., ~/ondemand/data/sys/dashboard/projects
But it appears to be used throughout this file as if it's @directory which seems erroneous to me. Not sure how this got so corrupted, but I think it's wrong to be using this all over the place as we are.
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.
Yes looking through the git blame this is a mistake to use. We're confusing Project.dataroot as a static directory for all projects to default to and @directory the specific directory that this project lives in.
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.
Yeah I had noticed that as well but was sticking to the (confusing) precedent within this class. I think the reason that we don't access @directory directly is that this is a string instead of a pathname, but I would be in favor of the idea of renaming project_dataroot to something like directory_path
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.
Yes all of this works a little bit by coincidence.
Joining two paths that have a top level root (i.e., starts with /) - as this method does -
ondemand/apps/dashboard/app/models/project.rb
Lines 220 to 222 in c4d8536
| def project_dataroot | |
| Project.dataroot.join(directory.to_s) | |
| end |
Actually just chooses the operand given in join.
Consider this simple example:
Pathname.new('/a').join('/b').to_sThe outtput is not /a/b or /b/a - but is in fact just simply /b.
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.
Yeah I had noticed that as well but was sticking to the (confusing) precedent within this class. I think the reason that we don't access @Directory directly is that this is a string instead of a pathname, but I would be in favor of the idea of renaming project_dataroot to something like directory_path
project_dataroot should not even exist. If we want to cast directory to a Pathname, then fine we can do that in an initializer and/or a getter/setter method. We can use Project.dataroot as a default, but this should never be bleed into a member variable.
Let's have 1 single variable and use it.
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.
It seems like the advantage over casting is it handles a relative directory path, so that project_dataroot is guaranteed to be absolute. (If @directory is relative we assume it is relative to Project.dataroot). So it seems like a good guardrail to have, just should be a better name IMO.
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.
Obviously a relative directory should never happen, and the path selector should discourage it. To make it more confusing the variable is set straight from textbox value with
File.expand_path(@directory)
Which actually interprets relative paths relative to the working directory of the code, which seems like a very unpredictable default
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.
🤦♂️Glad we discussed this, because I realized that all these quirks made me accidentally change the ownership of my personal project root to my non-primary account. Since before a project directory exists project_dataroot == Project.dataroot. I've added some protection against that, both by ensuring that we never set the group ownership of private? projects, and that get_group_owner never gets the owner of Project.dataroot by mistake. I mean that could be a way to ascertain the user's primary group, but certainly not the ideal way.
The flow now should be
- set the @group_owner variable every time we initialize, ensuring that forms are populated correctly.
- on save, run set_group_owner to make sure the actual owner matches @group_owner (which comes from a parameter). We run it directly after we make the project root directory to ensure that the subsequent files are created with the correct group (the setgid method will go here as well)
- If the project directory hasn't been initialized,
@group_ownershould benil, andset_group_ownerhas no effect (no risk of repeating the mistake above)
Fixes #4780 by adding a form item to edit forms allowing you to set the group owner. This has a few parts to it:
Eventually I would like to show the group owner in the show page, but this is a proof of concept that gets the hard part out of the way. I also think we may want to move the permissions settings to their own modal on the show page (which would more elegantly handle the fact that these can only be set after project creation), but since there is a good deal of frontend work and usage planning that would have to go into that, placing it in the form is a quicker way to get the same functionality.
The one piece this is currently missing is the ability to remove group ownership once it is given. The only way I can think to do this currently is to find a group inside the intersection mentioned above so that only the owner can access, but I don't know if that is guaranteed to exist. That being said, we are eventually going to add options to change facl permissions, so maybe "setting the group owner to 'none'" is actually best implemented by removing group access altogether, though interpreting that correctly (like in
get_group_ownercould get complicated.The last special case here is that you can't use this to target intersections of groups. So if I (member of
group1andgroup2) create a project in a directory only accessible togroup2, I can't change the project owner togroup1knowing that a certain member ofgroup2is also a member ofgroup1(effectively sharing it just with them). In a specialized permissions pane, it would be nice to have a toggle that would ignore the "who can access the parent" filter, but I don't know if this is possible inside a form without getting too complicated.