Skip to content
This repository was archived by the owner on May 13, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions src/components/Misc/RestrictedView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Stack, Text } from '@mantine/core';

const RestrictedView = () => {
return (
<Stack
style={{
flex: 1,
height: '100%',
width: '100%',
alignItems: 'center',
justifyContent: 'center',
}}>
<Stack>
<Text ta="center" c='gray.6'>Access restricted, Please contact your administrator.</Text>
</Stack>
</Stack>
);
};

export default RestrictedView;
11 changes: 7 additions & 4 deletions src/components/Navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import { signOutHandler } from '@/utils';
import { appStoreReducers, useAppStore } from '@/layouts/MainLayout/providers/AppProvider';
import _ from 'lodash';

const { setUserRoles, setUserSpecificStreams, setUserAccessMap, changeStream } = appStoreReducers;
const { setUserRoles, setUserSpecificStreams, setUserAccessMap, changeStream, setStreamSpecificUserAccess } =
appStoreReducers;

const navItems = [
{
Expand Down Expand Up @@ -132,8 +133,10 @@ const Navbar: FC = () => {
setAppStore((store) => setUserSpecificStreams(store, null));
}
}
setAppStore((store) => setUserAccessMap(store, getStreamsSepcificAccess(getUserRolesData?.data)));
}, [getUserRolesData?.data, getLogStreamListData?.data]);
const streamSpecificAccess = getStreamsSepcificAccess(getUserRolesData?.data, streamName);
setAppStore((store) => setStreamSpecificUserAccess(store, streamSpecificAccess));
setAppStore((store) => setUserAccessMap(store, streamSpecificAccess));
}, [getUserRolesData?.data, getLogStreamListData?.data, streamName]);

useEffect(() => {
getUserRolesMutation({ userName: username ? username : '' });
Expand Down Expand Up @@ -174,7 +177,7 @@ const Navbar: FC = () => {
{previlagedActions.map((navItem, index) => {
if (isStandAloneMode === null) return null;
if (navItem.route === USERS_MANAGEMENT_ROUTE && !userAccessMap.hasUserAccess) return null;
if (navItem.route === CLUSTER_ROUTE && (!userAccessMap.hasUserAccess || isStandAloneMode)) return null;
if (navItem.route === CLUSTER_ROUTE && (!userAccessMap.hasClusterAccess || isStandAloneMode)) return null;

const isActiveItem = navItem.route === currentRoute;
return (
Expand Down
29 changes: 27 additions & 2 deletions src/components/Navbar/rolesHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const adminAccess = [
'Ingest',
'Query',
'CreateStream',
'DeleteStream',
'ListStream',
'GetSchema',
'GetStats',
Expand All @@ -19,18 +20,26 @@ const adminAccess = [
'PutRoles',
'GetRole',
'Cluster',
'Dashboard',
'Alerts',
'Users',
'StreamSettings', // retention & hot-tier
];
const editorAccess = [
'Ingest',
'Query',
'CreateStream',
'DeleteStream',
'ListStream',
'GetSchema',
'GetStats',
'GetRetention',
'PutRetention',
'PutAlert',
'GetAlert',
'Dashboard',
'Alerts',
'StreamSettings', // retention & hot-tier
];
const writerAccess = [
'Ingest',
Expand All @@ -42,11 +51,27 @@ const writerAccess = [
'PutAlert',
'GetAlert',
'GetLiveTail',
'Dashboard',
'Alerts',
'StreamSettings', // retention & hot-tier
];
const readerAccess = [
'Query',
'ListStream',
'GetSchema',
'GetStats',
'GetRetention',
'GetAlert',
'GetLiveTail',
'Dashboard',
];
const readerAccess = ['Query', 'ListStream', 'GetSchema', 'GetStats', 'GetRetention', 'GetAlert', 'GetLiveTail'];
const ingestorAccess = ['Ingest'];

const getStreamsSepcificAccess = (rolesWithRoleName: UserRoles, stream?: string) => {
const getStreamsSepcificAccess = (rolesWithRoleName: UserRoles | null, stream?: string): string[] | null => {
if (!rolesWithRoleName) {
return null;
}

let access: string[] = [];
let roles: any[] = [];
for (var prop in rolesWithRoleName) {
Expand Down
6 changes: 3 additions & 3 deletions src/hooks/useAlertsEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import { AxiosError, isAxiosError } from 'axios';
import { useStreamStore, streamStoreReducers } from '@/pages/Stream/providers/StreamProvider';
const { setAlertsConfig } = streamStoreReducers;

const useAlertsQuery = (streamName: string) => {
const useAlertsQuery = (streamName: string, hasAlertsAccess: boolean) => {
const [, setStreamStore] = useStreamStore((_store) => null);
const { data, isError, isSuccess, isLoading, refetch } = useQuery(
['fetch-log-stream-alert', streamName],
['fetch-log-stream-alert', streamName, hasAlertsAccess],
() => getLogStreamAlerts(streamName),
{
retry: false,
enabled: streamName !== '',
enabled: streamName !== '' && hasAlertsAccess,
refetchOnWindowFocus: false,
onSuccess: (data) => {
setStreamStore((store) => setAlertsConfig(store, data));
Expand Down
9 changes: 8 additions & 1 deletion src/hooks/useGetStreamMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { LogStreamRetention, LogStreamStat } from '@/@types/parseable/api/stream';
import { getLogStreamRetention, getLogStreamStats } from '@/api/logStream';
import { getStreamsSepcificAccess } from '@/components/Navbar/rolesHandler';
import { useAppStore } from '@/layouts/MainLayout/providers/AppProvider';
import _ from 'lodash';
import { useCallback, useState } from 'react';

type MetaData = {
Expand All @@ -14,6 +17,7 @@ export const useGetStreamMetadata = () => {
const [isLoading, setLoading] = useState<Boolean>(false);
const [error, setError] = useState<Boolean>(false);
const [metaData, setMetadata] = useState<MetaData | null>(null);
const [userRoles] = useAppStore((store) => store.userRoles);

const getStreamMetadata = useCallback(async (streams: string[]) => {
setLoading(true);
Expand All @@ -23,7 +27,10 @@ export const useGetStreamMetadata = () => {
const allStatsRes = await Promise.all(allStatsReqs);

// retention
const allretentionReqs = streams.map((stream) => getLogStreamRetention(stream));
const streamsWithSettingsAccess = _.filter(streams, (stream) =>
_.includes(getStreamsSepcificAccess(userRoles, stream), 'StreamSettings'),
);
const allretentionReqs = streamsWithSettingsAccess.map((stream) => getLogStreamRetention(stream));
const allretentionRes = await Promise.all(allretentionReqs);

const metadata = streams.reduce((acc, stream, index) => {
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useHotTier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import { AxiosError, isAxiosError } from 'axios';

const { setHotTier } = streamStoreReducers;

export const useHotTier = (streamName: string) => {
export const useHotTier = (streamName: string, hasSettingsAccess: boolean) => {
const [, setStreamStore] = useStreamStore((_store) => null);
const {
refetch: refetchHotTierInfo,
isError: getHotTierInfoError,
isLoading: getHotTierInfoLoading,
} = useQuery(['fetch-hot-tier-info', streamName], () => getHotTierInfo(streamName), {
retry: false,
enabled: streamName !== '',
enabled: streamName !== '' && hasSettingsAccess,
refetchOnWindowFocus: false,
onSuccess: (data) => {
setStreamStore((store) => setHotTier(store, data.data));
Expand Down
19 changes: 18 additions & 1 deletion src/hooks/useLoginForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { useId } from '@mantine/hooks';
import { useEffect } from 'react';
import Cookies from 'js-cookie';
import { getQueryParam } from '@/utils';
import { isAxiosError } from 'axios';
import _ from 'lodash';

export const useLoginForm = () => {
const notificationId = useId();
Expand Down Expand Up @@ -78,7 +80,22 @@ export const useLoginForm = () => {
}
}
} catch (err) {
notifyError({ message: 'Something went wrong' });
if (isAxiosError(err)) {
const errStatus = err.response?.status;
if (errStatus === 401) {
setError('Unauthorized User');
notifyError({ message: 'The request failed with a status code of 401' });
} else {
const errMsg = _.isString(err.response?.data)
? err.response?.data || 'Something went wrong'
: 'Something went wrong';
setError(errMsg);
notifyError({ message: errMsg });
}
} else {
setError('Request Failed!');
notifyError({ message: 'Something went wrong' });
}
} finally {
setLoading(false);
}
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useRetentionEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import _ from 'lodash';

const { setRetention } = streamStoreReducers;

export const useRetentionQuery = (streamName: string) => {
export const useRetentionQuery = (streamName: string, hasSettingsAccess: boolean) => {
const [, setStreamStore] = useStreamStore((_store) => null);
const {
data: getLogRetentionData,
Expand All @@ -29,7 +29,7 @@ export const useRetentionQuery = (streamName: string) => {
}
},
retry: false,
enabled: streamName !== '',
enabled: streamName !== '' && hasSettingsAccess,
refetchOnWindowFocus: false,
});

Expand Down
19 changes: 8 additions & 11 deletions src/layouts/MainLayout/providers/AppProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { LogStreamData } from '@/@types/parseable/api/stream';
import { getStreamsSepcificAccess } from '@/components/Navbar/rolesHandler';
import initContext from '@/utils/initContext';
import { AboutData } from '@/@types/parseable/api/about';
import _ from 'lodash';
Expand Down Expand Up @@ -40,7 +39,7 @@ type AppStoreReducers = {
setUserRoles: (store: AppStore, roles: UserRoles | null) => ReducerOutput;
setUserSpecificStreams: (store: AppStore, userSpecficStreams: LogStreamData | null) => ReducerOutput;
setUserAccessMap: (store: AppStore, accessRoles: string[] | null) => ReducerOutput;
setStreamSpecificUserAccess: (store: AppStore) => ReducerOutput;
setStreamSpecificUserAccess: (store: AppStore, streamSpecificUserAccess: string[] | null) => ReducerOutput;
setInstanceConfig: (store: AppStore, instanceConfig: AboutData) => ReducerOutput;
toggleCreateStreamModal: (store: AppStore, val?: boolean) => ReducerOutput;
setSavedFilters: (store: AppStore, savedFilters: AxiosResponse<SavedFilterType[]>) => ReducerOutput;
Expand All @@ -65,11 +64,13 @@ const { Provider: AppProvider, useStore: useAppStore } = initContext(initialStat

// helpers
const accessKeyMap: { [key: string]: string } = {
hasUserAccess: 'ListUser',
hasUserAccess: 'Users',
hasDeleteAccess: 'DeleteStream',
hasUpdateAlertAccess: 'PutAlert',
hasGetAlertAccess: 'GetAlert',
hasCreateStreamAccess: 'CreateStream',
hasDeleteStreamAccess: 'DeleteStream',
hasClusterAccess: 'Cluster',
hasAlertsAccess: 'Alerts',
hasSettingsAccess: 'StreamSettings',
};

const generateUserAcccessMap = (accessRoles: string[] | null) => {
Expand Down Expand Up @@ -113,12 +114,8 @@ const setUserAccessMap = (_store: AppStore, accessRoles: string[] | null) => {
return { userAccessMap: generateUserAcccessMap(accessRoles) };
};

const setStreamSpecificUserAccess = (store: AppStore) => {
if (store.userRoles && store.currentStream) {
return { streamSpecificUserAccess: getStreamsSepcificAccess(store.userRoles, store.currentStream) };
} else {
return store;
}
const setStreamSpecificUserAccess = (_store: AppStore, streamSpecificUserAccess: string[] | null) => {
return { streamSpecificUserAccess };
};

const setInstanceConfig = (_store: AppStore, instanceConfig: AboutData | null) => {
Expand Down
17 changes: 15 additions & 2 deletions src/pages/Home/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import cardStyles from './styles/Card.module.css';
import homeStyles from './styles/Home.module.css';
import CreateStreamModal from './CreateStreamModal';
import { useAppStore, appStoreReducers } from '@/layouts/MainLayout/providers/AppProvider';
import { getStreamsSepcificAccess } from '@/components/Navbar/rolesHandler';
import _ from 'lodash';

const { changeStream, toggleCreateStreamModal } = appStoreReducers;

Expand Down Expand Up @@ -40,6 +42,7 @@ const Home: FC = () => {
const navigate = useNavigate();
const { getStreamMetadata, metaData } = useGetStreamMetadata();
const [userSpecificStreams, setAppStore] = useAppStore((store) => store.userSpecificStreams);
const [userRoles] = useAppStore((store) => store.userRoles);
const [userAccessMap] = useAppStore((store) => store.userAccessMap);

useEffect(() => {
Expand Down Expand Up @@ -89,7 +92,16 @@ const Home: FC = () => {
) : (
<Group style={{ marginRight: '1rem', marginLeft: '1rem', gap: '1rem' }}>
{Object.entries(metaData || {}).map(([stream, data]) => {
return <StreamInfo key={stream} stream={stream} data={data} navigateToStream={navigateToStream} />;
const hasSettingsAccess = _.includes(getStreamsSepcificAccess(userRoles, stream), 'StreamSettings');
return (
<StreamInfo
key={stream}
stream={stream}
data={data}
navigateToStream={navigateToStream}
hasSettingsAccess={hasSettingsAccess}
/>
);
})}
</Group>
)}
Expand Down Expand Up @@ -131,6 +143,7 @@ type StreamInfoProps = {
retention: LogStreamRetention | [];
};
navigateToStream: (stream: string) => void;
hasSettingsAccess: boolean;
};

const StreamInfo: FC<StreamInfoProps> = (props) => {
Expand Down Expand Up @@ -163,7 +176,7 @@ const StreamInfo: FC<StreamInfoProps> = (props) => {
<BigNumber label={'Ingestion'} value={sanitizeBytes(ingestionSize)} />
<BigNumber label={'Storage'} value={sanitizeBytes(storageSize)} />
<BigNumber label={'Compression'} value={calcCompressionRate(storageSize, ingestionSize)} />
<BigNumber label={'Retention'} value={streamRetention} />
{props.hasSettingsAccess && <BigNumber label={'Retention'} value={streamRetention} />}
<Stack style={{ justifyContent: 'flex-end', flex: 1, alignItems: 'flex-end' }}>
<Box style={{ width: '15%' }}>
<ActionIcon variant="transparent" color="black" size={50}>
Expand Down
11 changes: 7 additions & 4 deletions src/pages/Stream/Views/Manage/Alerts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { IconEdit, IconInfoCircleFilled, IconPlus, IconTrash } from '@tabler/ico
import { UseFormReturnType, useForm } from '@mantine/form';
import { useStreamStore, streamStoreReducers } from '../../providers/StreamProvider';
import ErrorView from './ErrorView';
import RestrictedView from '@/components/Misc/RestrictedView';

const defaultColumnTypeConfig = { column: '', operator: '=', value: '', repeats: 1, ignoreCase: false };
const defaultColumnTypeRule = { type: 'column' as 'column', config: defaultColumnTypeConfig };
Expand Down Expand Up @@ -573,16 +574,15 @@ const AlertsModal = (props: {
);
};

const Header = (props: { selectAlert: selectAlert; isLoading: boolean }) => {
const Header = (props: { selectAlert: selectAlert; isLoading: boolean, showCreateBtn: boolean }) => {
return (
<Stack className={classes.headerContainer} style={{ minHeight: '3rem', maxHeight: '3rem' }}>
<Text className={classes.title}>Alerts</Text>
{!props.isLoading && (
{!props.isLoading && props.showCreateBtn && (
<Box>
<Button
variant="outline"
onClick={() => props.selectAlert('')}
// h={'2rem'}
leftSection={<IconPlus stroke={2} size={'1rem'} />}>
New Alert
</Button>
Expand Down Expand Up @@ -659,6 +659,7 @@ const Alerts = (props: {
isLoading: boolean;
schemaLoading: boolean;
isError: boolean;
hasAlertsAccess: boolean;
updateAlerts: ({ config, onSuccess }: { config: any; onSuccess?: () => void }) => void;
}) => {
const [alertName, setAlertName] = useState<string>('');
Expand All @@ -676,9 +677,11 @@ const Alerts = (props: {
return (
<Stack className={classes.sectionContainer} gap={0} style={{ flex: 1 }}>
<AlertsModal open={alertModalOpen} alertName={alertName} onClose={closeModal} updateAlerts={props.updateAlerts} />
<Header selectAlert={selectAlert} isLoading={props.isLoading} />
<Header selectAlert={selectAlert} isLoading={props.isLoading} showCreateBtn={props.hasAlertsAccess}/>
{props.isError ? (
<ErrorView />
) : !props.hasAlertsAccess ? (
<RestrictedView />
) : (
<AlertList
selectAlert={selectAlert}
Expand Down
Loading
Loading