Floater mode: tightened superuser gate#19232
Conversation
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| Complexity | 1 medium |
🟢 Metrics 19 complexity
Metric Results Complexity 19
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
|
Here’s the use-case I want to support: I work at a company that has a bunch of sub-companies, and I’m in HR, in the “on-boarding” department. I have to on-board people across any of the associated sub-companies. So we have floater mode enabled, and I am listed as a floater. I only have I think I should be able to create a user who is also a floater - that’s well within the type of responsibilities my company might want me to have, right? So I think a Floater who is not a super-user should be able to create or edit users who are also floaters if they have that edit or create permission. |
uberbrady
left a comment
There was a problem hiding this comment.
I think this looks great! I have one comment I left as a standalone thing, and one question here that I’m curious about (and am worried might be a bug).
But this is very nice, succinct code to do a lot of important work - I think that’s great!
| // per-target path, which left actors able to see users they | ||
| // couldn't then edit. One check, one query, same logic as the list. | ||
| if ($companyable instanceof User) { | ||
| return User::where('users.id', $companyable->id)->exists(); |
There was a problem hiding this comment.
Shouldn’t this be looking at $companyable->companies()->where(‘id’,Auth::user()->id)->exists() or something like that?
There was a problem hiding this comment.
(I’d look at the toSql() of that before trusting me directly on that :P )
There was a problem hiding this comment.
The reason for going through the model query rather than an explicit pivot intersection is to avoid duplicating the visibility logic.
User::where('users.id', $id)->exists() runs through CompanyableScope, which already knows about every edge case that governs whether the actor can see this target in the user list:
- companied actor, companied target -> pivot intersection
- empty-pivot actor + floater mode off -> can see other empty-pivot targets only
- empty-pivot actor + floater mode on -> can see everyone
- companied actor + floater mode -> can also see null-pivot targets
- superuser short-circuits (skips the scope entirely)
- the
null_company_is_floatersetting changes a couple of these rules in non-obvious ways
A direct pivot intersection like the suggestion would only cover the first row. The bug we just fixed (#19187) was specifically that the per-target check was doing its own narrower intersection, so actors could see users in the list via the scope's null-pivot branch but then got 403'd at the edit screen. The two checks had drifted apart. By having both list visibility and per-target access go through the same scope, they can't drift again, and a future tweak to floater-mode semantics doesn't require remembering to update this logic branch too.
It's also the same number of queries (one indexed EXISTS either way), and the scope's query is the same shape the listing already runs, so it's not introducing a new query pattern.
|
@uberbrady I just pushed up some more changes that loosens the gates for your use-case. Now under floater mode, you can grant floater status if you're a superuser OR you yourself are uncompanied. The only blocked case is the actual escalation: a companied non-superuser trying to elevate someone to floater. The floater-HR workflow you described now works as expected. |
|
I should also note, these gates deliberately don't cover "trusted sources" like LDAP, SCIM, cli tools, etc |
uberbrady
left a comment
There was a problem hiding this comment.
This is looking great! I think if we can fix that Auth::check() thing that I mentioned in the code, I'll feel better. Thanks for taking this on - I think it's really going to help a lot of people using a lot of different workflows. I had a couple of bits where I wasn't sure about a few things - if you could see if those are real concerns, or let me know that the things I'm worried about might actually belong elsewhere, then that would be great. Can't wait to get this one going!
| public function withValidator(Validator $validator): void | ||
| { | ||
| $validator->after(function (Validator $validator) { | ||
| if (! auth()->check() || auth()->user()->canGrantFloaterStatus()) { |
There was a problem hiding this comment.
Doesn't this say "if the user isn't logged-in" OR they can grant floater status, then pass them along? I'm assuming it's because of CLI usage, where we don't necessarily have a user ID we can refer to.
That first clause feels confusing. Yes, via the CLI, you're automatically "assumed" to be a superuser (for the most part). But I would hope there could be a clearer way of determining whether or not this is CLI, which might be clearer to read and understand, and safer. (There might be some contexts where a not-logged-in user, in the future, might be able to do some things - like acceptances or something)
There was a problem hiding this comment.
It looks like there's a app()->runningInConsole() thing you can do in Laravel - would it be okay if we switched over to that instead?
There was a problem hiding this comment.
Yes, but PHPUnit runs in CLI too, so a bare app()->runningInConsole() would short-circuit the gate during every test (including the existing tests that expect the rejection). I think the way Laravel wants "actual console, not the test runner" is app()->runningInConsole() && ! app()->runningUnitTests().
There was a problem hiding this comment.
Isn't this only true when floater mode is enabled? In non-floater mode, null company stuff can only be checked out to null-company people.
This cleans up a few rough edges around how the company-assignment field on the user edit form behaves when Full Multiple Company Support is enabled.
What changed
users.company_idscalar can lag behind the pivot (LDAP sync, observer order, etc.) so falling back to it sometimes showed stale selections.FMCS ON + Floater ON,
user.edit- formFMCS ON + Floater ON,
user.edit- errorFMCS ON + Floater OFF,
user.editWhat to test