Skip to content

Commit e6c4b7e

Browse files
committed
Update navbar to be displayed only if the user has permissions for it
1 parent 82e14eb commit e6c4b7e

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?: UserResponse
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
@@ -8,14 +8,16 @@ export const Language = {
88
successProfileUpdate: "Updated preferences.",
99
}
1010

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

2022
export interface AuthContext {
2123
getUserError?: Error | unknown
@@ -24,7 +26,7 @@ export interface AuthContext {
2426
updateProfileError?: Error | unknown
2527
me?: Types.UserResponse
2628
methods?: TypesGen.AuthMethods
27-
permissions?: TypesGen.UserPermissionCheckResponse
29+
permissions?: Permissions
2830
checkPermissionsError?: Error | unknown
2931
}
3032

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

0 commit comments

Comments
 (0)