Skip to content

Conversation

david-crespo
Copy link
Collaborator

@david-crespo david-crespo commented Oct 14, 2022

Whether the user is shown /system/* routes is governed by whether they have viewer perms (or better) on the fleet. The natural place to look for that is the fleet policy, but if the user doesn't have fleet read, they will get a 403 from that endpoint. So we simply check whether that endpoint 200s or not to determine whether the user is a fleet viewer.

The real trick here is doing the check for this in the loader, which means

  1. It happens in parallel with requests that are already happening, so it doesn't slow anything down
  2. The request is complete before we render anything, which means we don't have UI popping in, and in the case of the 404, we can go straight to 404 without flashing anything else
  • Silo/system picker is only shown if user has fleet viewer perms
  • System routes 404 if user only has fleet viewer perms
  • Finally figured out how to handle the top bar pickers in the layouts
  • Tests

Writing tests for both sides of this is hard because we don't have a way of logging as a less-privileged user in the mock server yet.

@vercel
Copy link

vercel bot commented Oct 14, 2022

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
console-ui-storybook ✅ Ready (Inspect) Visit Preview Oct 16, 2022 at 4:28AM (UTC)

// TODO: make sure 404 is the desired behavior. This situation should be
// pretty unlikely.
if (!isFleetViewer) throw trigger404
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

doing this here is awesome because it means we don't have to do any janky "is this a system route?" detection. This layout wraps all the system routes in the route config. If we ever want to change the scope of this check to include some other routes, all we have to do is add another <Route> that wraps this one and the other one and move this loader to there.

const { data: user, error } = useApiQuery(
'sessionMe',
{},
{ cacheTime: 0, refetchOnWindowFocus: false }
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved refetchOnWindowFocus: false to the global config. I am less nervous about making a move on #1178 now that the big demo is done.

Copy link
Contributor

Choose a reason for hiding this comment

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

Which is a reminder @benjaminleonard that we should think more holistically about this from a design perspective. Having things pop out from under you is a bad experience but having to refresh over and over to see changes is also not great. I think the key here is giving explicit control in a way that's intuitive.

I think it'll be a big enough part of the experience that we need to communicate how to tweak this to users during on boarding.

const isFleetViewer = !!systemPolicy

const loggedIn = user && !error
const isSystem = pb.isSystemPath(useLocation().pathname)

This comment was marked as resolved.

Comment on lines +30 to +32
// toArray filters out nulls, which is essential because the silo/system
// picker is going to come in null when the user isn't supposed to see it
const [cornerPicker, ...otherPickers] = React.Children.toArray(children)
Copy link
Contributor

Choose a reason for hiding this comment

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

Love to see this cleaned up!

<PageContainer>
<TopBar />
<TopBar>
{useSiloSystemPicker('silo')}
Copy link
Contributor

Choose a reason for hiding this comment

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

A nit but it seems strange to me to see a hook inlined in the JSX.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, I had it pulled out initially but I moved it inline because I could. I do like that it feels like calling a component, which is what I wish it was.

Comment on lines +27 to +28
* error. We're being a little cavalier here with the error. If it's something
* other than a 403, that would be strange and we would want to know.
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps add a TODO?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Problem is I have no idea how we’d track it, but we could totally have an API endpoint for analytics.

Comment on lines +31 to +34
// Need to prefetch this because every layout hits it when deciding whether
// to show the silo/system picker. It's also fetched by the SystemLayout
// loader to figure out whether to 404, but RQ dedupes the request.
apiQueryClient.prefetchQuery('systemPolicyView', {}),
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the comments. The query being spread across multiple loaders and a hook doesn't feel great to me, but I get why it's needed and the benefit of this approach. Prefetched here, fetched in the system loader (which could be from cache), and queried via useSystemSiloPicker which should also be from cache. From my understanding if we had it on /session/me all that would do is remove it from the SystemLayout loader. I still suspect that may be the better way to approach it long term, but this seems good enough for where we're at.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Right, it feels like a lot but also necessary. I’m thinking about how to test that these things are hooked up correctly, but it’s pretty tough to be thorough. Like we can obviously assert that the silo/system picker is there or not depending on the current user (at least, we can when we add some form of authz to the mock server) but testing whether there’s pop-in is pretty hard because the timings are tight. Working on it.

icon: <Success16Icon />,
})
navigate(searchParams.get('state') || '/')
navigate(searchParams.get('state') || pb.orgs())
Copy link
Contributor

Choose a reason for hiding this comment

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

Good catch. Need a lint rule for this.

// }
// // if any of them showed up, we want to see that all of them did
// expect(handles.every((h) => h != null)).toBe(true)
// }
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

left in the first version to show how appalling it is

export async function expectSimultaneous(page: Page, sel1: string, sel2: string) {
const [t1, t2] = await Promise.all([timeToAppear(page, sel1), timeToAppear(page, sel2)])
expect(Math.abs(t1 - t2)).toBeLessThan(20)
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this is my greatest creation

'role=button[name="Switch between system and silo"]',
'role=button[name="Switch organization"]',
])
})
Copy link
Collaborator Author

@david-crespo david-crespo Oct 16, 2022

Choose a reason for hiding this comment

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

It works surprisingly well. First 3 times are with the prefetch commented out, second 3 are with it in (the expect is commented out so it wouldn't print 100 lines of failure stuff).

Screen Shot 2022-10-15 at 10 30 20 PM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants