Skip to content

Commit

Permalink
move types and run license check
Browse files Browse the repository at this point in the history
  • Loading branch information
maffkipp committed Feb 12, 2024
1 parent 20b2708 commit 546c222
Show file tree
Hide file tree
Showing 8 changed files with 327 additions and 61 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 18 additions & 1 deletion cmd/api/src/test/integration/harnesses/esc3harness3.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 18 additions & 1 deletion cmd/api/src/test/integration/harnesses/sessionharness.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
90 changes: 90 additions & 0 deletions cmd/ui/src/hooks/usePermissions/usePermissions.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// Copyright 2024 Specter Ops, Inc.
//
// Licensed under the Apache License, Version 2.0
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

import { PermissionsAuthority, PermissionsName, PermissionsSpec } from 'bh-shared-ui';
import { renderHook } from 'src/test-utils';
import usePermissions from './usePermissions';

describe('usePermissions', () => {
const checkPermissions = (permissions: { has: PermissionsSpec[]; needs: PermissionsSpec[] }) => {
return renderHook(() => usePermissions(permissions.needs), {
initialState: {
auth: {
user: {
roles: [
{
permissions: permissions.has,
},
],
},
},
},
}).result.current;
};

const manageClientsPermission = {
authority: PermissionsAuthority.CLIENTS,
name: PermissionsName.MANAGE_CLIENTS,
};

const createTokenPermission = {
authority: PermissionsAuthority.AUTH,
name: PermissionsName.CREATE_TOKEN,
};

const manageAppConfigPermission = {
authority: PermissionsAuthority.APP,
name: PermissionsName.MANAGE_APP_CONFIG,
};

const allPermissions = [manageClientsPermission, createTokenPermission, manageAppConfigPermission];

it('returns true if the user has a required permission', () => {
const hasPermissions = checkPermissions({
has: [manageClientsPermission],
needs: [manageClientsPermission],
});

expect(hasPermissions).toBe(true);
});

it('returns true if the user has multiple required permissions', () => {
const hasPermissions = checkPermissions({
has: allPermissions,
needs: allPermissions,
});

expect(hasPermissions).toBe(true);
});

it('returns false if the user does not have a matching permission', () => {
const hasPermissions = checkPermissions({
has: [manageClientsPermission],
needs: [createTokenPermission],
});

expect(hasPermissions).toBe(false);
});

it('returns false if the user is missing one of many required permissions', () => {
const hasPermissions = checkPermissions({
has: [manageClientsPermission, createTokenPermission],
needs: allPermissions,
});

expect(hasPermissions).toBe(false);
});
});
46 changes: 46 additions & 0 deletions cmd/ui/src/hooks/usePermissions/usePermissions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright 2024 Specter Ops, Inc.
//
// Licensed under the Apache License, Version 2.0
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

import { useCallback, useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { getSelfResponse } from 'src/ducks/auth/types';
import { PermissionsSpec } from 'bh-shared-ui';

const usePermissions: (permissions: PermissionsSpec[]) => boolean = (permissions) => {
const [hasAllPermissions, setHasAllPermissions] = useState<boolean>(false);
const auth: { user: getSelfResponse } = useSelector((state: any) => state.auth);

const doesUserHavePermissions = useCallback(
(user: getSelfResponse): boolean => {
const userPermissions = user.roles.map((role) => role.permissions).flat();

return permissions.every((permission) => {
return userPermissions.some((userPermission) => {
return userPermission.authority === permission.authority && userPermission.name === permission.name;
});
});
},
[permissions]
);

useEffect(() => {
setHasAllPermissions(auth.user && doesUserHavePermissions(auth.user));
}, [auth, doesUserHavePermissions]);

return hasAllPermissions;
};

export default usePermissions;
144 changes: 86 additions & 58 deletions cmd/ui/src/test-utils.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2023 Specter Ops, Inc.
// Copyright 2024 Specter Ops, Inc.
//
// Licensed under the Apache License, Version 2.0
// you may not use this file except in compliance with the License.
Expand All @@ -17,7 +17,7 @@
import { createTheme } from '@mui/material/styles';
import { CssBaseline, StyledEngineProvider, ThemeProvider } from '@mui/material';
import { configureStore } from '@reduxjs/toolkit';
import { render } from '@testing-library/react';
import { render, renderHook } from '@testing-library/react';
import { createMemoryHistory } from 'history';
import { SnackbarProvider } from 'notistack';
import { QueryClient, QueryClientProvider } from 'react-query';
Expand All @@ -27,75 +27,103 @@ import createSagaMiddleware from 'redux-saga';
import { rootReducer } from 'src/store';
import { NotificationsProvider } from 'bh-shared-ui';

const defaultTheme = {
palette: {
primary: {
main: '#406f8e',
light: '#709dbe',
dark: '#064460',
contrastText: '#ffffff',
},
neutral: {
main: '#e0e0e0',
light: '#ffffff',
dark: '#cccccc',
contrastText: '#000000',
},
background: {
paper: '#fafafa',
default: '#e4e9eb',
},
low: 'rgb(255, 195, 15)',
moderate: 'rgb(255, 97, 66)',
high: 'rgb(205, 0, 117)',
critical: 'rgb(76, 29, 143)',
},
}

const createDefaultQueryClient = () => {
return new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
})
}

const createDefaultStore = (state) => {
return configureStore({
reducer: rootReducer,
preloadedState: state,
middleware: (getDefaultMiddleware) => {
return [...getDefaultMiddleware({ serializableCheck: false }), createSagaMiddleware()];
},
})
}

const createProviders = ({ queryClient, history, theme, store, children }) => {
return (
<Provider store={store}>
<QueryClientProvider client={queryClient}>
<StyledEngineProvider injectFirst>
<ThemeProvider theme={theme}>
<NotificationsProvider>
<CssBaseline />
<Router location={history.location} navigator={history}>
<SnackbarProvider>{children}</SnackbarProvider>
</Router>
</NotificationsProvider>
</ThemeProvider>
</StyledEngineProvider>
</QueryClientProvider>
</Provider>
)
}

const customRender = (
ui,
{
initialState = {},
queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
}),
queryClient = createDefaultQueryClient(),
history = createMemoryHistory(),
theme = createTheme({
palette: {
primary: {
main: '#406f8e',
light: '#709dbe',
dark: '#064460',
contrastText: '#ffffff',
},
neutral: {
main: '#e0e0e0',
light: '#ffffff',
dark: '#cccccc',
contrastText: '#000000',
},
background: {
paper: '#fafafa',
default: '#e4e9eb',
},
low: 'rgb(255, 195, 15)',
moderate: 'rgb(255, 97, 66)',
high: 'rgb(205, 0, 117)',
critical: 'rgb(76, 29, 143)',
},
}),
store = configureStore({
reducer: rootReducer,
preloadedState: initialState,
middleware: (getDefaultMiddleware) => {
return [...getDefaultMiddleware({ serializableCheck: false }), createSagaMiddleware()];
},
}),
theme = createTheme(defaultTheme),
store = createDefaultStore(initialState),
...renderOptions
} = {}
) => {
const AllTheProviders = ({ children }) => {
return (
<Provider store={store}>
<QueryClientProvider client={queryClient}>
<StyledEngineProvider injectFirst>
<ThemeProvider theme={theme}>
<NotificationsProvider>
<CssBaseline />
<Router location={history.location} navigator={history}>
<SnackbarProvider>{children}</SnackbarProvider>
</Router>
</NotificationsProvider>
</ThemeProvider>
</StyledEngineProvider>
</QueryClientProvider>
</Provider>
);
};
const AllTheProviders = ({ children }) => createProviders({ queryClient, history, theme, store, children });
return render(ui, { wrapper: AllTheProviders, ...renderOptions });
};

const customRenderHook = (
hook,
{
initialState = {},
queryClient = createDefaultQueryClient(),
history = createMemoryHistory(),
theme = createTheme(defaultTheme),
store = createDefaultStore(initialState),
...renderOptions
} = {}
) => {
const AllTheProviders = ({ children }) => createProviders({ queryClient, history, theme, store, children });
return renderHook(hook, { wrapper: AllTheProviders, ...renderOptions });
};

// re-export everything
// eslint-disable-next-line react-refresh/only-export-components
export * from '@testing-library/react';
// override render method
export { customRender as render };
export { customRenderHook as renderHook };
1 change: 1 addition & 0 deletions packages/javascript/bh-shared-ui/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export * from './entityInfoDisplay';
export * from './passwd';
export * from './user';
export * from './icons';
export * from './permissions';
50 changes: 50 additions & 0 deletions packages/javascript/bh-shared-ui/src/utils/permissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2024 Specter Ops, Inc.
//
// Licensed under the Apache License, Version 2.0
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

export enum PermissionsAuthority {
APP = 'app',
RISKS = 'risks',
AUTH = 'auth',
CLIENTS = 'clients',
COLLECTION = 'collection',
GRAPHDB = 'graphdb',
SAVED_QUERIES = 'saved_queries',
}

export enum PermissionsName {
READ_APP_CONFIG = 'ReadAppConfig',
WRITE_APP_CONFIG = 'WriteAppConfig',
GENERATE_REPORT = 'GenerateReport',
MANAGE_RISKS = 'ManageRisks',
CREATE_TOKEN = 'CreateToken',
MANAGE_APP_CONFIG = 'ManageAppConfig',
MANAGE_PROVIDERS = 'ManageProviders',
MANAGE_SELF = 'ManageSelf',
MANAGE_USERS = 'ManageUsers',
MANAGE_CLIENTS = 'Manage',
READ_CLIENTS = 'Read',
CLIENT_TASKING = 'Tasking',
MANAGE_COLLECTION_JOBS = 'ManageJobs',
READ_GRAPHDB = 'Read',
WRITE_GRAPHDB = 'Write',
READ_SAVED_QUERIES = 'Read',
WRITE_SAVED_QUERIES = 'Write',
}

export type PermissionsSpec = {
authority: PermissionsAuthority;
name: PermissionsName;
};

0 comments on commit 546c222

Please sign in to comment.