Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions public/assets/users_icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/i18n/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
"TITLE_BACKUPS": "Backups",
"TITLE_WORKLOAD": "Workload details",
"TITLE_WORKLOAD_VALUES": "Workload values",
"TITLE_USERS": "Users",
"TITLE_USER": "User details",
"TITLE_PROJECTS": "Projects",
"TITLE_PROJECT": "Project details",
"DELETE_PROJECT_WARNING": "Deleting this project will permanently remove all associated collections (build, workload, service) and their data. This action cannot be undone.",
Expand All @@ -85,6 +87,8 @@
"Backups": "Backups",
"Workload_plural": "Workloads",
"Backup_plural": "Backups",
"User": "User",
"User_plural": "Users",
"Project_plural": "Projects",
"Build_plural": "Builds",
"WELCOME_DASHBOARD": "Team <1>{{teamName}}</1> dashboard",
Expand Down
63 changes: 47 additions & 16 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@ import Netpol from 'pages/Netpol'
import Build from 'pages/Build'
import LoadingScreen from 'components/LoadingScreen'
import Dashboard from 'pages/Dashboard'
import Users from 'pages/Users'
import Projects from 'pages/Projects'
import { CollapseDrawerProvider } from 'contexts/CollapseDrawerContext'
import { ShellDrawerProvider } from 'contexts/ShellDrawerContext'
import { SettingsProvider } from 'contexts/SettingsContext'
import { getSettings } from 'utils/getSettings'
import ThemeColorPresets from 'components/ThemeColorPresets'
import User from 'pages/User'
import Project from 'pages/Project'
import Policy from 'pages/Policy'
import Maintenance from 'pages/Maintenance'
Expand Down Expand Up @@ -94,29 +96,52 @@ function App() {
<Route path='/' component={Dashboard} exact />
<PrivateRoute path='/apps/:teamId' component={Apps} exact />
<PrivateRoute path='/apps/:teamId/:appId' component={OtomiApp} exact />
<PrivateRoute path='/backups' component={Backups} adminRoute exact />
<PrivateRoute path='/clusters/:clusterId' component={Cluster} adminRoute exact />
<PrivateRoute path='/clusters' component={Clusters} adminRoute exact />
<PrivateRoute path='/create-team' component={Team} adminRoute exact />
<PrivateRoute path='/netpols' component={Netpols} adminRoute exact />
<PrivateRoute path='/policies' component={Policies} adminRoute exact />
<PrivateRoute path='/policies/:policyId' component={Policy} adminRoute exact />
<PrivateRoute path='/backups' component={Backups} platformAdminRoute exact />
<PrivateRoute
path='/clusters/:clusterId'
component={Cluster}
platformAdminRoute
exact
/>
<PrivateRoute path='/clusters' component={Clusters} platformAdminRoute exact />
<PrivateRoute path='/create-team' component={Team} platformAdminRoute exact />
<PrivateRoute path='/netpols' component={Netpols} platformAdminRoute exact />
<PrivateRoute path='/policies' component={Policies} platformAdminRoute exact />
<PrivateRoute
path='/policies/:policyId'
component={Policy}
platformAdminRoute
exact
/>
<PrivateRoute path='/catalogs/:teamId' component={Catalogs} exact />
<PrivateRoute path='/catalogs/:teamId/:catalogName' component={Catalog} exact />
<PrivateRoute
path='/catalogs/:teamId/:catalogName/:workloadId'
component={Catalog}
exact
/>
<PrivateRoute path='/services' component={Services} adminRoute exact />
<PrivateRoute path='/secrets' component={Secrets} adminRoute exact />
<PrivateRoute path='/sealed-secrets' component={SealedSecrets} adminRoute exact />
<PrivateRoute path='/workloads' component={Workloads} adminRoute exact />
<PrivateRoute path='/settings' component={SettingsOverview} adminRoute exact />
<PrivateRoute path='/projects' component={Projects} adminRoute exact />
<PrivateRoute path='/builds' component={Builds} adminRoute exact />
<PrivateRoute path='/services' component={Services} platformAdminRoute exact />
<PrivateRoute path='/secrets' component={Secrets} platformAdminRoute exact />
<PrivateRoute
path='/sealed-secrets'
component={SealedSecrets}
platformAdminRoute
exact
/>
<PrivateRoute path='/workloads' component={Workloads} platformAdminRoute exact />
<PrivateRoute
path='/settings'
component={SettingsOverview}
platformAdminRoute
exact
/>
<PrivateRoute path='/users' component={Users} platformAdminRoute exact />
<PrivateRoute path='/users/:userId' component={User} platformAdminRoute exact />
<PrivateRoute path='/teams/:teamId/users' component={Users} teamAdminRoute exact />
<PrivateRoute path='/projects' component={Projects} platformAdminRoute exact />
<PrivateRoute path='/builds' component={Builds} platformAdminRoute exact />
<PrivateRoute path='/settings/:settingId' component={Setting} exact />
<PrivateRoute path='/teams' component={Teams} adminRoute exact />
<PrivateRoute path='/teams' component={Teams} platformAdminRoute exact />
<PrivateRoute path='/teams/:teamId' component={Team} exact />
<PrivateRoute path='/teams/:teamId/create-backup' component={Backup} exact />
<PrivateRoute path='/teams/:teamId/create-netpol' component={Netpol} exact />
Expand All @@ -127,6 +152,7 @@ function App() {
exact
/>
<PrivateRoute path='/teams/:teamId/create-service' component={Service} exact />
<PrivateRoute path='/teams/:teamId/create-user' component={User} exact />
<PrivateRoute path='/teams/:teamId/create-project' component={Project} exact />
<PrivateRoute path='/teams/:teamId/create-build' component={Build} exact />
<PrivateRoute path='/teams/:teamId/secrets' component={Secrets} exact />
Expand Down Expand Up @@ -155,7 +181,12 @@ function App() {
<PrivateRoute path='/teams/:teamId/workloads' component={Workloads} exact />
<PrivateRoute path='/teams/:teamId/services' component={Services} exact />
<PrivateRoute path='/teams/:teamId/services/:serviceId' component={Service} exact />
<PrivateRoute path='/maintenance' component={Maintenance} adminRoute exact />
<PrivateRoute
path='/maintenance'
component={Maintenance}
platformAdminRoute
exact
/>
<Route path='*'>
<Error error={new HttpErrorBadRequest()} />
</Route>
Expand Down
2 changes: 1 addition & 1 deletion src/common/api-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const applyAclToUiSchema = (
teamId: string,
schemaName: string,
): void => {
if (user.isAdmin) return
if (user.isPlatformAdmin) return

get(user, `authz.${teamId}.deniedAttributes.${schemaName}`, []).forEach((path) => {
set(uiSchema, `${path}.ui:readonly`, true)
Expand Down
2 changes: 1 addition & 1 deletion src/components/AccountPopover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export default function AccountPopover({ email }: Props) {
}
const handleLogout = async () => {
await del({
body: { teamId: oboTeamId, domain, emailNoSymbols, isAdmin: user.isAdmin, userTeams },
body: { teamId: oboTeamId, domain, emailNoSymbols, isAdmin: user.isPlatformAdmin, userTeams },
})
clearLocalStorage('oboTeamId')
window.location.href = '/logout-otomi'
Expand Down
10 changes: 6 additions & 4 deletions src/components/AuthzRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { useSession } from 'providers/Session'
import React from 'react'
import { Route } from 'react-router-dom'

function PrivateRoute({ component: Component, adminRoute, ...rest }: any) {
function PrivateRoute({ component: Component, platformAdminRoute, teamAdminRoute, ...rest }: any) {
const session = useSession()
const {
user: { isAdmin },
user: { isPlatformAdmin, isTeamAdmin },
oboTeamId,
} = session
const isAuthenticated = (props) => {
Expand All @@ -15,8 +15,10 @@ function PrivateRoute({ component: Component, adminRoute, ...rest }: any) {
params: { teamId },
},
} = props
if ((adminRoute && !isAdmin) || (!isAdmin && teamId && teamId !== oboTeamId)) return false
return true
if (isPlatformAdmin) return true
if (isTeamAdmin && teamAdminRoute) return true
if (!platformAdminRoute && !teamAdminRoute && teamId && teamId === oboTeamId) return true
return false
}
return (
<Route
Expand Down
11 changes: 1 addition & 10 deletions src/components/Backups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,7 @@ interface Props {
}

export default function ({ backups, teamId }: Props): React.ReactElement {
// const {
// oboTeamId,
// user: { isAdmin },
// } = useSession()
const {
appsEnabled,
settings: {
cluster: { domainSuffix },
},
} = useSession()
const { appsEnabled } = useSession()

const { t } = useTranslation()
// END HOOKS
Expand Down
4 changes: 2 additions & 2 deletions src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,8 @@ export default function Dashboard({ team, inventory }: Props): React.ReactElemen
}, [themeView])
// reset themeView to team if user is not admin
React.useEffect(() => {
const { isAdmin } = user
if (!isAdmin) onChangeView({ target: { value: 'team' } } as React.ChangeEvent<HTMLInputElement>)
const { isPlatformAdmin } = user
if (!isPlatformAdmin) onChangeView({ target: { value: 'team' } } as React.ChangeEvent<HTMLInputElement>)
}, [])

// platform view base iframe urls
Expand Down
2 changes: 1 addition & 1 deletion src/components/Error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const useStyles = makeStyles()((theme) => ({
interface Props {
error?: ApiError
}
const apiCodes = [403, 504, 666]
const apiCodes = [403, 409, 504, 666]

export default function ({ error }: Props): React.ReactElement {
const { classes } = useStyles()
Expand Down
8 changes: 4 additions & 4 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export default function Header({ onOpenSidebar, isCollapse = false, verticalLayo
cluster,
otomi: { additionalClusters = [] },
},
user: { email, teams: userTeams, isAdmin },
user: { email, teams: userTeams, isPlatformAdmin },
oboTeamId,
setOboTeamId,
} = useSession()
Expand All @@ -72,7 +72,7 @@ export default function Header({ onOpenSidebar, isCollapse = false, verticalLayo
// END HOOKs
let teams: GetTeamsApiResponse
const allClusters = [...additionalClusters, cluster]
if (isAdmin) {
if (isPlatformAdmin) {
teams = ((allTeams as any) || []).map(({ id }) => ({
id,
}))
Expand Down Expand Up @@ -136,7 +136,7 @@ export default function Header({ onOpenSidebar, isCollapse = false, verticalLayo
)}
<Box sx={{ flexGrow: 1 }} />
<Stack direction='row' alignItems='center' spacing={{ xs: 0.5, sm: 1.5 }}>
{isAdmin && (
{isPlatformAdmin && (
<>
<Typography>view:</Typography>
<Select
Expand Down Expand Up @@ -177,7 +177,7 @@ export default function Header({ onOpenSidebar, isCollapse = false, verticalLayo
onChange={handleChangeTeam}
data-cy='select-oboteam'
>
{isAdmin && (
{isPlatformAdmin && (
<MenuItem value='admin' data-cy='select-oboteam-admin'>
{t('admin')}
</MenuItem>
Expand Down
7 changes: 5 additions & 2 deletions src/components/ListTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface ListTableProps extends EnhancedTableProps {
idKey?: string
collection?: string
to?: string
customButton?: React.ReactElement
}
export default function ({
teamId,
Expand All @@ -26,10 +27,11 @@ export default function ({
noCrud = false,
idKey = 'id',
to,
customButton = null,
...other
}: ListTableProps): React.ReactElement {
const {
user: { isAdmin },
user: { isPlatformAdmin },
oboTeamId,
} = useSession()
const { t } = useTranslation()
Expand All @@ -48,7 +50,7 @@ export default function ({
<Box sx={{ flexGrow: 1 }}>
<HeaderTitle title={inTitle || title} resourceType={resourceType} />
</Box>
{(isAdmin || oboTeamId) && !noCrud && (
{(isPlatformAdmin || oboTeamId) && !noCrud && (
<Box mb={1}>
<Button
variant='contained'
Expand All @@ -61,6 +63,7 @@ export default function ({
</Button>
</Box>
)}
{customButton && <Box mb={1}>{customButton}</Box>}
</Box>
</Box>

Expand Down
19 changes: 18 additions & 1 deletion src/components/NavConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ const getIcon = (name: string) => <SvgIconStyle src={`/assets/${name}`} sx={{ wi
// it's SVG format.

export default function NavConfig() {
const { ca, appsEnabled, oboTeamId, user } = useSession()
const {
ca,
appsEnabled,
oboTeamId,
user,
settings: {
otomi: { hasExternalIDP },
},
} = useSession()
const downloadOpts = {
data: ca ?? '',
title: 'Click to download the custom root CA used to generate the browser certs.',
Expand All @@ -19,6 +27,13 @@ export default function NavConfig() {
const anchor = ca ? generateDownloadLink(downloadOpts) : ''
const dashboard =
oboTeamId !== 'admin' ? [{ title: 'Dashboard', path: '/', icon: getIcon('dashboard_icon.svg') }] : []
const platformUserManagement = !hasExternalIDP
? [{ title: 'User Management', path: '/users', icon: getIcon('users_icon.svg') }]
: []
const teamUserManagement =
!hasExternalIDP && (user.isPlatformAdmin || user.isTeamAdmin)
? [{ title: 'User Management', path: `/teams/${oboTeamId}/users`, icon: getIcon('users_icon.svg') }]
: []

return [
{
Expand All @@ -35,6 +50,7 @@ export default function NavConfig() {
{ title: 'Apps', path: '/apps/admin', icon: getIcon('apps_icon.svg') },
// { title: 'Policies', path: '/policies', icon: getIcon('policies_icon.svg') },
{ title: 'Teams', path: '/teams', icon: getIcon('teams_icon.svg') },
...platformUserManagement,
{ title: 'Projects', path: '/projects', icon: getIcon('projects_icon.svg') },
{ title: 'Builds', path: '/builds', icon: getIcon('builds_icon.svg') },
{ title: 'Workloads', path: '/workloads', icon: getIcon('workloads_icon.svg') },
Expand Down Expand Up @@ -70,6 +86,7 @@ export default function NavConfig() {
{ title: 'Network Policies', path: `/teams/${oboTeamId}/netpols/`, icon: getIcon('policies_icon.svg') },
{ title: 'Services', path: `/teams/${oboTeamId}/services`, icon: getIcon('services_icon.svg') },
{ title: 'Security Policies', path: `/teams/${oboTeamId}/policies`, icon: getIcon('security_icon.svg') },
...teamUserManagement,
{
title: 'Settings',
path: `/teams/${oboTeamId}`,
Expand Down
4 changes: 2 additions & 2 deletions src/components/Netpols.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ interface Props {
export default function ({ netpols, teamId }: Props): React.ReactElement {
const {
oboTeamId,
user: { isAdmin },
user: { isPlatformAdmin },
} = useSession()
const { t } = useTranslation()
// END HOOKS
const headCells: HeadCell[] = [
{
id: 'name',
label: t('Name'),
renderer: getNetpolLink(isAdmin, oboTeamId),
renderer: getNetpolLink(isPlatformAdmin, oboTeamId),
},
{
id: 'type',
Expand Down
4 changes: 2 additions & 2 deletions src/components/Project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,12 +304,12 @@ export default function ({
body,
})
if (res.error) return
history.push(user.isAdmin ? `/projects` : `/teams/${teamId}/projects`)
history.push(user.isPlatformAdmin ? `/projects` : `/teams/${teamId}/projects`)
}

const handleDeleteProject = () => {
onDelete({ teamId, projectId })
history.push(user.isAdmin ? `/projects` : `/teams/${teamId}/projects`)
history.push(user.isPlatformAdmin ? `/projects` : `/teams/${teamId}/projects`)
}

const handleNext = async () => {
Expand Down
6 changes: 3 additions & 3 deletions src/components/SealedSecrets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ interface Props {
export default function ({ secrets, teamId }: Props): React.ReactElement {
const {
oboTeamId,
user: { isAdmin },
user: { isPlatformAdmin },
} = useSession()
const { t } = useTranslation()
const status = useStatus()
Expand All @@ -44,7 +44,7 @@ export default function ({ secrets, teamId }: Props): React.ReactElement {
{
id: 'name',
label: t('Name'),
renderer: getSecretLink(isAdmin, oboTeamId),
renderer: getSecretLink(isPlatformAdmin, oboTeamId),
},
{
id: 'type',
Expand All @@ -67,7 +67,7 @@ export default function ({ secrets, teamId }: Props): React.ReactElement {

return (
<Box>
{isAdmin && (
{isPlatformAdmin && (
<InformationBanner message='Please make sure to download encryption keys for the disaster recovery purpose.'>
<MuiLink href='/api/v1/sealedsecretskeys' sx={{ ml: '8px' }}>
Download Keys
Expand Down
Loading