Skip to content

Commit d0ff321

Browse files
committed
Update navbar to be displayed only if the user has permissions for it
1 parent dbd9b30 commit d0ff321

File tree

5 files changed

+45
-11
lines changed

5 files changed

+45
-11
lines changed

site/src/components/Navbar/Navbar.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
import { useActor } from "@xstate/react"
1+
import { useActor, useSelector } from "@xstate/react"
22
import React, { useContext } from "react"
3+
import { selectPermissions } from "../../xServices/auth/authSelectors"
34
import { XServiceContext } from "../../xServices/StateContext"
45
import { NavbarView } from "../NavbarView/NavbarView"
56

67
export const Navbar: React.FC = () => {
78
const xServices = useContext(XServiceContext)
89
const [authState, authSend] = useActor(xServices.authXService)
910
const { me } = authState.context
11+
const permissions = useSelector(xServices.authXService, selectPermissions)
12+
// When we have more options in the admin dropdown we may want to check this
13+
// for more permissions
14+
const displayAdminDropdown = !!permissions?.readAllUsers
1015
const onSignOut = () => authSend("SIGN_OUT")
1116

12-
return <NavbarView user={me} onSignOut={onSignOut} />
17+
return <NavbarView user={me} onSignOut={onSignOut} displayAdminDropdown={displayAdminDropdown} />
1318
}

site/src/components/NavbarView/NavbarView.stories.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,33 @@ const Template: Story<NavbarViewProps> = (args: NavbarViewProps) => <NavbarView
1414

1515
export const ForAdmin = Template.bind({})
1616
ForAdmin.args = {
17-
user: { id: "1", username: "Administrator", email: "admin@coder.com", created_at: "dawn" },
17+
user: {
18+
id: "1",
19+
username: "Administrator",
20+
email: "admin@coder.com",
21+
created_at: "dawn",
22+
status: "active",
23+
organization_ids: [],
24+
roles: [],
25+
},
26+
displayAdminDropdown: true,
1827
onSignOut: () => {
1928
return Promise.resolve()
2029
},
2130
}
2231

2332
export const ForMember = Template.bind({})
2433
ForMember.args = {
25-
user: { id: "1", username: "CathyCoder", email: "cathy@coder.com", created_at: "dawn" },
34+
user: {
35+
id: "1",
36+
username: "CathyCoder",
37+
email: "cathy@coder.com",
38+
created_at: "dawn",
39+
status: "active",
40+
organization_ids: [],
41+
roles: [],
42+
},
43+
displayAdminDropdown: false,
2644
onSignOut: () => {
2745
return Promise.resolve()
2846
},

site/src/components/NavbarView/NavbarView.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import { UserDropdown } from "../UserDropdown/UsersDropdown"
1212
export interface NavbarViewProps {
1313
user?: TypesGen.User
1414
onSignOut: () => void
15+
displayAdminDropdown: boolean
1516
}
1617

17-
export const NavbarView: React.FC<NavbarViewProps> = ({ user, onSignOut }) => {
18+
export const NavbarView: React.FC<NavbarViewProps> = ({ user, onSignOut, displayAdminDropdown }) => {
1819
const styles = useStyles()
1920
return (
2021
<nav className={styles.root}>
@@ -31,7 +32,7 @@ export const NavbarView: React.FC<NavbarViewProps> = ({ user, onSignOut }) => {
3132
</ListItem>
3233
</List>
3334
<div className={styles.fullWidth} />
34-
{user && user.email === "admin@coder.com" && <AdminDropdown />}
35+
{displayAdminDropdown && <AdminDropdown />}
3536
<div className={styles.fixed}>{user && <UserDropdown user={user} onSignOut={onSignOut} />}</div>
3637
</nav>
3738
)
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { State } from "xstate"
22
import { AuthContext, AuthEvent } from "./authXService"
33

4-
export const selectOrgId = (state: State<AuthContext, AuthEvent>): string | undefined => {
4+
type AuthState = State<AuthContext, AuthEvent>
5+
6+
export const selectOrgId = (state: AuthState): string | undefined => {
57
return state.context.me?.organization_ids[0]
68
}
9+
10+
export const selectPermissions = (state: AuthState): AuthContext["permissions"] => {
11+
return state.context.permissions
12+
}

site/src/xServices/auth/authXService.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@ export const Language = {
77
successProfileUpdate: "Updated preferences.",
88
}
99

10-
const permissionsToCheck: Record<string, TypesGen.UserPermissionCheck> = {
10+
const permissionsToCheck = {
1111
readAllUsers: {
1212
object: {
1313
resource_type: "user",
1414
},
1515
action: "read",
1616
},
17-
}
17+
} as const
18+
19+
type Permissions = Record<keyof typeof permissionsToCheck, boolean>
1820

1921
export interface AuthContext {
2022
getUserError?: Error | unknown
@@ -23,7 +25,7 @@ export interface AuthContext {
2325
updateProfileError?: Error | unknown
2426
me?: TypesGen.User
2527
methods?: TypesGen.AuthMethods
26-
permissions?: TypesGen.UserPermissionCheckResponse
28+
permissions?: Permissions
2729
checkPermissionsError?: Error | unknown
2830
}
2931

@@ -286,7 +288,9 @@ export const authMachine =
286288
updateProfileError: (_) => undefined,
287289
}),
288290
assignPermissions: assign({
289-
permissions: (_, event) => event.data,
291+
// Setting event.data as Permissions to be more stricted. So we know
292+
// what permissions we asked for.
293+
permissions: (_, event) => event.data as Permissions,
290294
}),
291295
assignGetPermissionsError: assign({
292296
checkPermissionsError: (_, event) => event.data,

0 commit comments

Comments
 (0)