Skip to content

Commit f721298

Browse files
[frontend] fix redux selector immutable return (#2793)
1 parent aa2644d commit f721298

File tree

79 files changed

+316
-320
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+316
-320
lines changed

openbas-front/src/actions/Schema.js

Lines changed: 56 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import { fromJS, List, Map } from 'immutable';
12
import { schema } from 'normalizr';
2-
import * as R from 'ramda';
33

44
import locale from '../utils/BrowserLanguage.js';
55

@@ -261,40 +261,18 @@ user.define({ user_organization: organization });
261261
const maps = (key, state) => state.referential.getIn(['entities', key]);
262262
const entities = (key, state) => maps(key, state).valueSeq();
263263
const entity = (id, key, state) => state.referential.getIn(['entities', key, id]);
264-
const me = (state) => {
265-
return state.referential.getIn(['entities', 'users', state.app.getIn(['logged', 'user'])]);
266-
};
267-
268-
const getInjectWithParsedInjectorContractContent = (i) => {
269-
if (!i) {
270-
return i;
271-
}
272-
return ({
273-
...i,
274-
inject_injector_contract: {
275-
...i.inject_injector_contract,
276-
injector_contract_content_parsed: i.inject_injector_contract?.injector_contract_content
277-
? JSON.parse(i.inject_injector_contract.injector_contract_content)
278-
: null,
279-
},
280-
});
281-
};
282-
const getInjectsWithParsedInjectorContractContent = (injects) => {
283-
if (R.isEmpty(injects)) {
284-
return injects;
285-
}
286-
return injects.map(getInjectWithParsedInjectorContractContent);
287-
};
264+
const me = state => state.referential.getIn(['entities', 'users', state.app.getIn(['logged', 'user'])]);
288265

289266
export const storeHelper = state => ({
290267
logged: () => state.app.get('logged'),
291268
getMe: () => me(state),
269+
getMeAdmin: () => me(state)?.get('user_admin') ?? false,
292270
getMeTokens: () => entities('tokens', state).filter(
293-
t => t.token_user === me(state)?.user_id,
271+
t => t.get('token_user') === me(state)?.get('user_id'),
294272
),
295273
getUserLang: () => {
296274
const rawPlatformLang = state.referential.getIn(['entities', 'platformParameters', 'parameters', 'platform_lang']) ?? 'auto';
297-
const rawUserLang = me(state)?.user_lang ?? 'auto';
275+
const rawUserLang = me(state)?.get('user_lang') ?? 'auto';
298276
const platformLang = rawPlatformLang !== 'auto' ? rawPlatformLang : locale;
299277
const userLang = rawUserLang !== 'auto' ? rawUserLang : platformLang;
300278
return userLang;
@@ -304,37 +282,38 @@ export const storeHelper = state => ({
304282
getExercises: () => entities('exercises', state),
305283
getExercisesMap: () => maps('exercises', state),
306284
getExercise: id => entity(id, 'exercises', state),
307-
getExerciseComchecks: id => entities('comchecks', state).filter(i => i.comcheck_exercise === id),
308-
getExerciseTeams: id => entities('teams', state).filter(i => i.team_exercises?.includes(id)),
309-
getExerciseVariables: id => entities('variables', state).filter(i => i.variable_exercise === id),
310-
getExerciseArticles: id => entities('articles', state).filter(i => i.article_exercise === id),
311-
getExerciseInjects: id => getInjectsWithParsedInjectorContractContent(entities('injects', state).filter(i => i.inject_exercise === id)),
285+
getExerciseComchecks: id => entities('comchecks', state).filter(i => i.get('comcheck_exercise') === id),
286+
getExerciseTeams: id => entities('teams', state).filter(i => i.get('team_exercises')?.includes(id)),
287+
getExerciseVariables: id => entities('variables', state).filter(i => i.get('variable_exercise') === id),
288+
getExerciseArticles: id => entities('articles', state).filter(i => i.get('article_exercise') === id),
289+
getExerciseInjects: id => entities('injects', state).filter(i => i.get('inject_exercise') === id),
312290
getExerciseCommunications: id => entities('communications', state).filter(
313-
i => i.communication_exercise === id,
291+
i => i.get('communication_exercise') === id,
314292
),
315-
getExerciseObjectives: id => entities('objectives', state).filter(o => o.objective_exercise === id),
316-
getExerciseLogs: id => entities('logs', state).filter(l => l.log_exercise === id),
293+
getExerciseObjectives: id => entities('objectives', state).filter(o => o.get('objective_exercise') === id),
294+
getExerciseLogs: id => entities('logs', state).filter(l => l.get('log_exercise') === id),
317295
getExerciseLessonsCategories: id => entities('lessonscategorys', state).filter(
318-
l => l.lessons_category_exercise === id,
296+
l => l.get('lessons_category_exercise') === id,
319297
),
320298
getExerciseLessonsQuestions: id => entities('lessonsquestions', state).filter(
321-
l => l.lessons_question_exercise === id,
299+
l => l.get('lessons_question_exercise') === id,
322300
),
323301
getExerciseLessonsAnswers: exerciseId => entities('lessonsanswers', state).filter(
324-
l => l.lessons_answer_exercise === exerciseId,
302+
l => l.get('lessons_answer_exercise') === exerciseId,
325303
),
326304
getExerciseUserLessonsAnswers: (exerciseId, userId) => entities('lessonsanswers', state).filter(
327-
l => l.lessons_answer_exercise === exerciseId
328-
&& l.lessons_answer_user === userId,
305+
l => l.get('lessons_answer_exercise') === exerciseId
306+
&& l.get('lessons_answer_user') === userId,
329307
),
330-
getExerciseReports: exerciseId => entities('reports', state).filter(l => l.report_exercise === exerciseId),
308+
isExercise: id => !maps('exercises', state)?.get(id)?.isEmpty(),
309+
getExerciseReports: exerciseId => entities('reports', state).filter(l => l.get('report_exercise') === exerciseId),
331310
// report
332311
getReport: id => entity(id, 'reports', state),
333312
// comcheck
334313
getComcheck: id => entity(id, 'comchecks', state),
335314
getComcheckStatus: id => entity(id, 'comcheckstatuses', state),
336315
getComcheckStatuses: id => entities('comcheckstatuses', state).filter(
337-
i => i.comcheckstatus_comcheck === id,
316+
i => i.get('comcheckstatus_comcheck') === id,
338317
),
339318
getChannelReader: id => entity(id, 'channelreaders', state),
340319
getChallengesReader: id => entity(id, 'challengesreaders', state),
@@ -346,52 +325,46 @@ export const storeHelper = state => ({
346325
getOrganizationsMap: () => maps('organizations', state),
347326
// objectives
348327
getObjective: id => entity(id, 'objectives', state),
349-
getObjectiveEvaluations: id => entities('evaluations', state).filter(e => e.evaluation_objective === id),
328+
getObjectiveEvaluations: id => entities('evaluations', state).filter(e => e.get('evaluation_objective') === id),
350329
// tags
351330
getTag: id => entity(id, 'tags', state),
352331
getTags: () => entities('tags', state),
353332
getTagsMap: () => maps('tags', state),
354333
// injects
355-
getInject: id => getInjectWithParsedInjectorContractContent(entity(id, 'injects', state)),
334+
getInject: id => entity(id, 'injects', state),
356335
getAtomicTesting: id => entity(id, 'atomics', state),
357336
getAtomicTestingDetail: id => entity(id, 'atomicdetails', state),
358337
getAtomicTestings: () => entities('atomics', state),
359-
getTargetResults: (id, injectId) => entities('targetresults', state).filter(r => (r.target_id === id) && (r.target_inject_id === injectId)),
360-
getInjectsMap: () => getInjectsWithParsedInjectorContractContent(entities('injects', state)).reduce((map, i) => {
361-
map[i.inject_id] = i;
362-
return map;
363-
}, {}),
364-
getNextInjects: () => {
365-
const sortFn = (a, b) => new Date(a.inject_date).getTime() - new Date(b.inject_date).getTime();
366-
const injects = entities('injects', state).filter(
367-
i => i.inject_date !== null && i.inject_status === null,
368-
);
369-
return R.take(6, R.sort(sortFn, injects));
370-
},
338+
getTargetResults: (id, injectId) => entities('targetresults', state).filter(r => (r.get('target_id') === id) && (r.get('target_inject_id') === injectId)),
339+
getInjectsMap: () => maps('injects', state),
371340
getInjectCommunications: id => entities('communications', state).filter(
372-
i => i.communication_inject === id,
341+
i => i.get('communication_inject') === id,
373342
),
374343
// injectexpectation
375344
getInjectExpectations: () => entities('injectexpectations', state),
376345
getExerciseInjectExpectations: id => entities('injectexpectations', state).filter(
377-
i => i.inject_expectation_exercise === id,
346+
i => i.get('inject_expectation_exercise') === id,
378347
),
379348
getInjectExpectationsMap: () => maps('injectexpectations', state),
380349
// documents
381350
getDocuments: () => entities('documents', state),
382351
getDocumentsMap: () => maps('documents', state),
383352
// teams
384353
getTeam: id => entity(id, 'teams', state),
385-
getTeamUsers: id => entities('users', state).filter(u => (entity(id, 'teams', state) || {}).team_users?.includes(
386-
u.user_id,
387-
)),
388-
getTeamExerciseInjects: id => entities('injects', state).filter(i => (entity(id, 'teams', state) || {}).team_exercise_injects?.includes(
389-
i.inject_id,
390-
)),
354+
getTeamUsers: (id) => {
355+
const team = entity(id, 'teams', state);
356+
if (!team) return List([]);
357+
return team.get('team_users').map(tu => entity(tu, 'users', state));
358+
},
359+
getTeamExerciseInjects: (id) => {
360+
const team = entity(id, 'teams', state);
361+
if (!team) return List([]);
362+
return team.get('team_exercise_injects').map(te => entity(te, 'injects', state));
363+
},
391364
getTeams: () => entities('teams', state),
392365
getTeamsMap: () => maps('teams', state),
393366
getPlatformSettings: () => {
394-
return state.referential.getIn(['entities', 'platformParameters', 'parameters']) || {};
367+
return state.referential.getIn(['entities', 'platformParameters', 'parameters']) || Map({});
395368
},
396369
getPlatformName: () => {
397370
return state.referential.getIn(['entities', 'platformParameters', 'parameters', 'platform_name']) || 'OpenBAS - Breach and Attack Simulation Platform';
@@ -415,13 +388,10 @@ export const storeHelper = state => ({
415388
// injector contracts
416389
getInjectorContract: (id) => {
417390
const i = entity(id, 'injector_contracts', state);
418-
if (!i) {
391+
if (i.isEmpty()) {
419392
return i;
420393
}
421-
return ({
422-
...i,
423-
...JSON.parse(i.injector_contract_content),
424-
});
394+
return i.merge(fromJS(JSON.parse(i.get('injector_contract_content'))));
425395
},
426396
getInjectorContracts: () => entities('injector_contracts', state),
427397
// collectors
@@ -446,19 +416,19 @@ export const storeHelper = state => ({
446416
getArticlesMap: () => maps('articles', state),
447417
// challenges
448418
getChallenges: () => entities('challenges', state),
449-
getExerciseChallenges: id => entities('challenges', state).filter(c => c.challenge_exercises.includes(id)),
419+
getExerciseChallenges: id => entities('challenges', state).filter(c => c.get('challenge_exercises').includes(id)),
450420
getChallengesMap: () => maps('challenges', state),
451421
// lessons templates
452422
getLessonsTemplate: id => entity(id, 'lessonstemplates', state),
453423
getLessonsTemplates: () => entities('lessonstemplates', state),
454424
getLessonsTemplatesMap: () => maps('lessonstemplates', state),
455425
getLessonsTemplateCategories: id => entities('lessonstemplatecategorys', state).filter(
456-
c => c.lessons_template_category_template === id,
426+
c => c.get('lessons_template_category_template') === id,
457427
),
458428
getLessonsTemplateQuestions: () => entities('lessonstemplatequestions', state),
459429
getLessonsTemplateQuestionsMap: () => maps('lessonstemplatequestions', state),
460430
getLessonsTemplateCategoryQuestions: id => entities('lessonstemplatequestions', state).filter(
461-
c => c.lessons_template_question_category === id,
431+
c => c.get('lessons_template_question_category') === id,
462432
),
463433
// assets
464434
getEndpoint: id => entity(id, 'endpoints', state),
@@ -476,19 +446,21 @@ export const storeHelper = state => ({
476446
getScenarios: () => entities('scenarios', state),
477447
getScenariosMap: () => maps('scenarios', state),
478448
getScenario: id => entity(id, 'scenarios', state),
479-
getScenarioTeams: id => entities('teams', state).filter(i => i.team_scenarios.includes(id)),
480-
getScenarioVariables: id => entities('variables', state).filter(i => i.variable_scenario === id),
481-
getScenarioArticles: id => entities('articles', state).filter(i => i.article_scenario === id),
482-
getScenarioChallenges: id => entities('challenges', state).filter(c => c.challenge_scenarios.includes(id)),
483-
getScenarioInjects: id => getInjectsWithParsedInjectorContractContent(entities('injects', state).filter(i => i.inject_scenario === id)),
484-
getTeamScenarioInjects: id => entities('injects', state).filter(i => (entity(id, 'teams', state) || {}).team_scenario_injects?.includes(
485-
i.inject_id,
486-
)),
487-
getScenarioObjectives: id => entities('objectives', state).filter(o => o.objective_scenario === id),
449+
getScenarioTeams: id => entities('teams', state).filter(i => i.get('team_scenarios').includes(id)),
450+
getScenarioVariables: id => entities('variables', state).filter(i => i.get('variable_scenario') === id),
451+
getScenarioArticles: id => entities('articles', state).filter(i => i.get('article_scenario') === id),
452+
getScenarioChallenges: id => entities('challenges', state).filter(c => c.get('challenge_scenarios').includes(id)),
453+
getScenarioInjects: id => entities('injects', state).filter(i => i.get('inject_scenario') === id),
454+
getTeamScenarioInjects: (id) => {
455+
const team = entity(id, 'teams', state);
456+
if (!team) return List([]);
457+
return team.get('team_scenario_injects').map(te => entity(te, 'injects', state));
458+
},
459+
getScenarioObjectives: id => entities('objectives', state).filter(o => o.get('objective_scenario') === id),
488460
getScenarioLessonsCategories: id => entities('lessonscategorys', state).filter(
489-
l => l.lessons_category_scenario === id,
461+
l => l.get('lessons_category_scenario') === id,
490462
),
491463
getScenarioLessonsQuestions: id => entities('lessonsquestions', state).filter(
492-
l => l.lessons_question_scenario === id,
464+
l => l.get('lessons_question_scenario') === id,
493465
),
494466
});

openbas-front/src/actions/exercises/exercise-helper.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { type Exercise, type ExerciseSimple, type InjectExpectation, type LessonsAnswer, type LessonsCategory, type LessonsQuestion, type Objective, type Team } from '../../utils/api-types';
22

33
export interface ExercisesHelper {
4+
isExercise: (exerciseId: string) => boolean;
45
getExercise: (exerciseId: string) => Exercise;
56
getExercises: () => ExerciseSimple[];
67
getExercisesMap: () => Record<string, Exercise>;

openbas-front/src/actions/helper.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { type Challenge, type Document, type Exercise, type Organization, type P
22

33
export interface UserHelper {
44
getMe: () => User;
5+
getMeAdmin: () => boolean;
56
getUsersMap: () => Record<string, User>;
67
}
78

openbas-front/src/actions/injector_contracts/InjectorContract.d.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,13 @@ export type InjectorContractConverted = Omit<InjectorContract, 'convertedContent
3838
convertedContent: {
3939
fields: ContractElement[];
4040
contract_id: string;
41-
config: { type: string };
42-
label: string;
41+
config: {
42+
type: string;
43+
color_dark: string;
44+
color_light: string;
45+
expose: boolean;
46+
label: Record<string, string>;
47+
};
48+
label: Record<string, string>;
4349
};
4450
};

openbas-front/src/actions/injects/Inject.d.ts

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,15 @@
11
import { type Inject, type InjectOutput } from '../../utils/api-types';
2+
import { type InjectorContractConverted } from '../injector_contracts/InjectorContract';
23

34
export type InjectStore = Omit<Inject, 'inject_content' | 'inject_injector_contract'> & {
4-
inject_content: {
5+
inject_content?: {
56
expectationScore: number;
67
challenges: string[] | undefined;
78
};
8-
inject_injector_contract: {
9-
// as we don't know the type of the content of a contract we need to put any here
10-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
11-
injector_contract_content_parsed: any;
12-
convertedContent: {
13-
label: Record<string, string>;
14-
config: { expose: boolean };
15-
};
16-
} & Inject['inject_injector_contract'];
9+
inject_injector_contract: Omit<InjectorContractConverted, 'convertedContent'> & { convertedContent: InjectorContractConverted['convertedContent'] };
1710
};
1811

19-
export type InjectorContractConvertedContent = {
20-
label: Record<string, string>;
21-
config: { expose: boolean };
22-
};
23-
24-
export type InjectOutputType = InjectOutput & {
25-
inject_injector_contract: {
26-
// as we don't know the type of the content of a contract we need to put any here
27-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
28-
injector_contract_content_parsed: any;
29-
convertedContent: InjectorContractConvertedContent;
30-
} & Inject['inject_injector_contract'];
31-
};
12+
export type InjectOutputType = InjectOutput & { inject_injector_contract: { convertedContent: InjectorContractConverted['convertedContent'] } & Inject['inject_injector_contract'] };
3213

3314
export interface ConditionElement {
3415
name: string;

openbas-front/src/admin/components/agents/ExecutorDocumentationLink.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const ExecutorDocumentationLink: FunctionComponent<Props> = ({ executor }) => {
1818
<Typography variant="body1">
1919
{t('To install the agent please follow the ')}
2020
<a target="_blank" href={executor.executor_doc} rel="noreferrer">
21-
{`${executor.executor_name} ${t('documentation')}`}
21+
{t('{executor_name} documentation', { executor_name: executor.executor_name })}
2222
</a>
2323
.
2424
</Typography>

openbas-front/src/admin/components/agents/InstructionSelector.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ SHA512: ca07dc1d0a5297e29327e483f4f35dadb254d96a16a5c33da5ad048e6965a3863d621518
341341
>
342342
{`${t('The agent runs in the background as a session and only executes when the user is logged in and active.')} ${t('For further details, refer to the')} `}
343343
<a target="_blank" href={selectedExecutor?.executor_doc} rel="noreferrer">
344-
{`${selectedExecutor?.executor_name} ${t('documentation.')}`}
344+
{t('{executor_name} documentation.', { executor_name: selectedExecutor?.executor_name })}
345345
</a>
346346
</Alert>
347347
<p>
@@ -362,7 +362,7 @@ SHA512: ca07dc1d0a5297e29327e483f4f35dadb254d96a16a5c33da5ad048e6965a3863d621518
362362
>
363363
{`${t('The agent runs in the background as a service and starts automatically when the machine powers on.')} ${t('For further details, refer to the')} `}
364364
<a target="_blank" href={selectedExecutor?.executor_doc} rel="noreferrer">
365-
{`${selectedExecutor?.executor_name} ${t('documentation.')}`}
365+
{t('{executor_name} documentation.', { executor_name: selectedExecutor?.executor_name })}
366366
</a>
367367
</Alert>
368368
<p>

openbas-front/src/admin/components/assets/asset_groups/AssetGroupManagement.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ const AssetGroupManagement: FunctionComponent<Props> = ({
6868
// Fetching data
6969
const { assetGroup, userAdmin } = useHelper((helper: AssetGroupsHelper & UserHelper) => ({
7070
assetGroup: helper.getAssetGroup(assetGroupId),
71-
userAdmin: helper.getMe()?.user_admin ?? false,
71+
userAdmin: helper.getMeAdmin(),
7272
}));
7373
useDataLoader(() => {
7474
dispatch(fetchAssetGroup(assetGroupId));

openbas-front/src/admin/components/assets/asset_groups/AssetGroups.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ const AssetGroups = () => {
128128
const [searchId] = searchParams.getAll('id');
129129

130130
// Fetching data
131-
const { userAdmin } = useHelper((helper: EndpointHelper & UserHelper & TagHelper) => ({ userAdmin: helper.getMe()?.user_admin ?? false }));
131+
const { userAdmin } = useHelper((helper: EndpointHelper & UserHelper & TagHelper) => ({ userAdmin: helper.getMeAdmin() }));
132132

133133
// Headers
134134
const headers: Header[] = useMemo(() => [

openbas-front/src/admin/components/assets/endpoints/Endpoints.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ import AssetStatus from '../AssetStatus';
3030
import AgentPrivilege from './AgentPrivilege';
3131
import EndpointPopover from './EndpointPopover';
3232

33-
const useStyles = makeStyles()(() => ({ itemHead: { textTransform: 'uppercase' } }));
33+
const useStyles = makeStyles()(() => ({
34+
itemHead: { textTransform: 'uppercase' },
35+
item: { height: 50 },
36+
}));
3437

3538
const inlineStyles: Record<string, CSSProperties> = {
3639
asset_name: { width: '25%' },
@@ -65,7 +68,7 @@ const Endpoints = () => {
6568

6669
// Fetching data
6770
const { userAdmin, executorsMap } = useHelper((helper: ExecutorHelper & UserHelper) => ({
68-
userAdmin: helper.getMe()?.user_admin ?? false,
71+
userAdmin: helper.getMeAdmin(),
6972
executorsMap: helper.getExecutorsMap(),
7073
}));
7174
useDataLoader(() => {
@@ -338,6 +341,7 @@ const Endpoints = () => {
338341
<ListItemButton
339342
component={Link}
340343
to={`/admin/assets/endpoints/${endpoint.asset_id}`}
344+
classes={{ root: classes.item }}
341345
>
342346
<ListItemIcon>
343347
<DevicesOtherOutlined color="primary" />

0 commit comments

Comments
 (0)