Skip to content
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
140 changes: 56 additions & 84 deletions openbas-front/src/actions/Schema.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { fromJS, List, Map } from 'immutable';
import { schema } from 'normalizr';
import * as R from 'ramda';

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

Expand Down Expand Up @@ -261,40 +261,18 @@ user.define({ user_organization: organization });
const maps = (key, state) => state.referential.getIn(['entities', key]);
const entities = (key, state) => maps(key, state).valueSeq();
const entity = (id, key, state) => state.referential.getIn(['entities', key, id]);
const me = (state) => {
return state.referential.getIn(['entities', 'users', state.app.getIn(['logged', 'user'])]);
};

const getInjectWithParsedInjectorContractContent = (i) => {
if (!i) {
return i;
}
return ({
...i,
inject_injector_contract: {
...i.inject_injector_contract,
injector_contract_content_parsed: i.inject_injector_contract?.injector_contract_content
? JSON.parse(i.inject_injector_contract.injector_contract_content)
: null,
},
});
};
const getInjectsWithParsedInjectorContractContent = (injects) => {
if (R.isEmpty(injects)) {
return injects;
}
return injects.map(getInjectWithParsedInjectorContractContent);
};
const me = state => state.referential.getIn(['entities', 'users', state.app.getIn(['logged', 'user'])]);

export const storeHelper = state => ({
logged: () => state.app.get('logged'),
getMe: () => me(state),
getMeAdmin: () => me(state)?.get('user_admin') ?? false,
getMeTokens: () => entities('tokens', state).filter(
t => t.token_user === me(state)?.user_id,
t => t.get('token_user') === me(state)?.get('user_id'),
),
getUserLang: () => {
const rawPlatformLang = state.referential.getIn(['entities', 'platformParameters', 'parameters', 'platform_lang']) ?? 'auto';
const rawUserLang = me(state)?.user_lang ?? 'auto';
const rawUserLang = me(state)?.get('user_lang') ?? 'auto';
const platformLang = rawPlatformLang !== 'auto' ? rawPlatformLang : locale;
const userLang = rawUserLang !== 'auto' ? rawUserLang : platformLang;
return userLang;
Expand All @@ -304,37 +282,38 @@ export const storeHelper = state => ({
getExercises: () => entities('exercises', state),
getExercisesMap: () => maps('exercises', state),
getExercise: id => entity(id, 'exercises', state),
getExerciseComchecks: id => entities('comchecks', state).filter(i => i.comcheck_exercise === id),
getExerciseTeams: id => entities('teams', state).filter(i => i.team_exercises?.includes(id)),
getExerciseVariables: id => entities('variables', state).filter(i => i.variable_exercise === id),
getExerciseArticles: id => entities('articles', state).filter(i => i.article_exercise === id),
getExerciseInjects: id => getInjectsWithParsedInjectorContractContent(entities('injects', state).filter(i => i.inject_exercise === id)),
getExerciseComchecks: id => entities('comchecks', state).filter(i => i.get('comcheck_exercise') === id),
getExerciseTeams: id => entities('teams', state).filter(i => i.get('team_exercises')?.includes(id)),
getExerciseVariables: id => entities('variables', state).filter(i => i.get('variable_exercise') === id),
getExerciseArticles: id => entities('articles', state).filter(i => i.get('article_exercise') === id),
getExerciseInjects: id => entities('injects', state).filter(i => i.get('inject_exercise') === id),
getExerciseCommunications: id => entities('communications', state).filter(
i => i.communication_exercise === id,
i => i.get('communication_exercise') === id,
),
getExerciseObjectives: id => entities('objectives', state).filter(o => o.objective_exercise === id),
getExerciseLogs: id => entities('logs', state).filter(l => l.log_exercise === id),
getExerciseObjectives: id => entities('objectives', state).filter(o => o.get('objective_exercise') === id),
getExerciseLogs: id => entities('logs', state).filter(l => l.get('log_exercise') === id),
getExerciseLessonsCategories: id => entities('lessonscategorys', state).filter(
l => l.lessons_category_exercise === id,
l => l.get('lessons_category_exercise') === id,
),
getExerciseLessonsQuestions: id => entities('lessonsquestions', state).filter(
l => l.lessons_question_exercise === id,
l => l.get('lessons_question_exercise') === id,
),
getExerciseLessonsAnswers: exerciseId => entities('lessonsanswers', state).filter(
l => l.lessons_answer_exercise === exerciseId,
l => l.get('lessons_answer_exercise') === exerciseId,
),
getExerciseUserLessonsAnswers: (exerciseId, userId) => entities('lessonsanswers', state).filter(
l => l.lessons_answer_exercise === exerciseId
&& l.lessons_answer_user === userId,
l => l.get('lessons_answer_exercise') === exerciseId
&& l.get('lessons_answer_user') === userId,
),
getExerciseReports: exerciseId => entities('reports', state).filter(l => l.report_exercise === exerciseId),
isExercise: id => !maps('exercises', state)?.get(id)?.isEmpty(),
getExerciseReports: exerciseId => entities('reports', state).filter(l => l.get('report_exercise') === exerciseId),
// report
getReport: id => entity(id, 'reports', state),
// comcheck
getComcheck: id => entity(id, 'comchecks', state),
getComcheckStatus: id => entity(id, 'comcheckstatuses', state),
getComcheckStatuses: id => entities('comcheckstatuses', state).filter(
i => i.comcheckstatus_comcheck === id,
i => i.get('comcheckstatus_comcheck') === id,
),
getChannelReader: id => entity(id, 'channelreaders', state),
getChallengesReader: id => entity(id, 'challengesreaders', state),
Expand All @@ -346,52 +325,46 @@ export const storeHelper = state => ({
getOrganizationsMap: () => maps('organizations', state),
// objectives
getObjective: id => entity(id, 'objectives', state),
getObjectiveEvaluations: id => entities('evaluations', state).filter(e => e.evaluation_objective === id),
getObjectiveEvaluations: id => entities('evaluations', state).filter(e => e.get('evaluation_objective') === id),
// tags
getTag: id => entity(id, 'tags', state),
getTags: () => entities('tags', state),
getTagsMap: () => maps('tags', state),
// injects
getInject: id => getInjectWithParsedInjectorContractContent(entity(id, 'injects', state)),
getInject: id => entity(id, 'injects', state),
getAtomicTesting: id => entity(id, 'atomics', state),
getAtomicTestingDetail: id => entity(id, 'atomicdetails', state),
getAtomicTestings: () => entities('atomics', state),
getTargetResults: (id, injectId) => entities('targetresults', state).filter(r => (r.target_id === id) && (r.target_inject_id === injectId)),
getInjectsMap: () => getInjectsWithParsedInjectorContractContent(entities('injects', state)).reduce((map, i) => {
map[i.inject_id] = i;
return map;
}, {}),
getNextInjects: () => {
const sortFn = (a, b) => new Date(a.inject_date).getTime() - new Date(b.inject_date).getTime();
const injects = entities('injects', state).filter(
i => i.inject_date !== null && i.inject_status === null,
);
return R.take(6, R.sort(sortFn, injects));
},
getTargetResults: (id, injectId) => entities('targetresults', state).filter(r => (r.get('target_id') === id) && (r.get('target_inject_id') === injectId)),
getInjectsMap: () => maps('injects', state),
getInjectCommunications: id => entities('communications', state).filter(
i => i.communication_inject === id,
i => i.get('communication_inject') === id,
),
// injectexpectation
getInjectExpectations: () => entities('injectexpectations', state),
getExerciseInjectExpectations: id => entities('injectexpectations', state).filter(
i => i.inject_expectation_exercise === id,
i => i.get('inject_expectation_exercise') === id,
),
getInjectExpectationsMap: () => maps('injectexpectations', state),
// documents
getDocuments: () => entities('documents', state),
getDocumentsMap: () => maps('documents', state),
// teams
getTeam: id => entity(id, 'teams', state),
getTeamUsers: id => entities('users', state).filter(u => (entity(id, 'teams', state) || {}).team_users?.includes(
u.user_id,
)),
getTeamExerciseInjects: id => entities('injects', state).filter(i => (entity(id, 'teams', state) || {}).team_exercise_injects?.includes(
i.inject_id,
)),
getTeamUsers: (id) => {
const team = entity(id, 'teams', state);
if (!team) return List([]);
return team.get('team_users').map(tu => entity(tu, 'users', state));
},
getTeamExerciseInjects: (id) => {
const team = entity(id, 'teams', state);
if (!team) return List([]);
return team.get('team_exercise_injects').map(te => entity(te, 'injects', state));
},
getTeams: () => entities('teams', state),
getTeamsMap: () => maps('teams', state),
getPlatformSettings: () => {
return state.referential.getIn(['entities', 'platformParameters', 'parameters']) || {};
return state.referential.getIn(['entities', 'platformParameters', 'parameters']) || Map({});
},
getPlatformName: () => {
return state.referential.getIn(['entities', 'platformParameters', 'parameters', 'platform_name']) || 'OpenBAS - Breach and Attack Simulation Platform';
Expand All @@ -415,13 +388,10 @@ export const storeHelper = state => ({
// injector contracts
getInjectorContract: (id) => {
const i = entity(id, 'injector_contracts', state);
if (!i) {
if (i.isEmpty()) {
return i;
}
return ({
...i,
...JSON.parse(i.injector_contract_content),
});
return i.merge(fromJS(JSON.parse(i.get('injector_contract_content'))));
},
getInjectorContracts: () => entities('injector_contracts', state),
// collectors
Expand All @@ -446,19 +416,19 @@ export const storeHelper = state => ({
getArticlesMap: () => maps('articles', state),
// challenges
getChallenges: () => entities('challenges', state),
getExerciseChallenges: id => entities('challenges', state).filter(c => c.challenge_exercises.includes(id)),
getExerciseChallenges: id => entities('challenges', state).filter(c => c.get('challenge_exercises').includes(id)),
getChallengesMap: () => maps('challenges', state),
// lessons templates
getLessonsTemplate: id => entity(id, 'lessonstemplates', state),
getLessonsTemplates: () => entities('lessonstemplates', state),
getLessonsTemplatesMap: () => maps('lessonstemplates', state),
getLessonsTemplateCategories: id => entities('lessonstemplatecategorys', state).filter(
c => c.lessons_template_category_template === id,
c => c.get('lessons_template_category_template') === id,
),
getLessonsTemplateQuestions: () => entities('lessonstemplatequestions', state),
getLessonsTemplateQuestionsMap: () => maps('lessonstemplatequestions', state),
getLessonsTemplateCategoryQuestions: id => entities('lessonstemplatequestions', state).filter(
c => c.lessons_template_question_category === id,
c => c.get('lessons_template_question_category') === id,
),
// assets
getEndpoint: id => entity(id, 'endpoints', state),
Expand All @@ -476,19 +446,21 @@ export const storeHelper = state => ({
getScenarios: () => entities('scenarios', state),
getScenariosMap: () => maps('scenarios', state),
getScenario: id => entity(id, 'scenarios', state),
getScenarioTeams: id => entities('teams', state).filter(i => i.team_scenarios.includes(id)),
getScenarioVariables: id => entities('variables', state).filter(i => i.variable_scenario === id),
getScenarioArticles: id => entities('articles', state).filter(i => i.article_scenario === id),
getScenarioChallenges: id => entities('challenges', state).filter(c => c.challenge_scenarios.includes(id)),
getScenarioInjects: id => getInjectsWithParsedInjectorContractContent(entities('injects', state).filter(i => i.inject_scenario === id)),
getTeamScenarioInjects: id => entities('injects', state).filter(i => (entity(id, 'teams', state) || {}).team_scenario_injects?.includes(
i.inject_id,
)),
getScenarioObjectives: id => entities('objectives', state).filter(o => o.objective_scenario === id),
getScenarioTeams: id => entities('teams', state).filter(i => i.get('team_scenarios').includes(id)),
getScenarioVariables: id => entities('variables', state).filter(i => i.get('variable_scenario') === id),
getScenarioArticles: id => entities('articles', state).filter(i => i.get('article_scenario') === id),
getScenarioChallenges: id => entities('challenges', state).filter(c => c.get('challenge_scenarios').includes(id)),
getScenarioInjects: id => entities('injects', state).filter(i => i.get('inject_scenario') === id),
getTeamScenarioInjects: (id) => {
const team = entity(id, 'teams', state);
if (!team) return List([]);
return team.get('team_scenario_injects').map(te => entity(te, 'injects', state));
},
getScenarioObjectives: id => entities('objectives', state).filter(o => o.get('objective_scenario') === id),
getScenarioLessonsCategories: id => entities('lessonscategorys', state).filter(
l => l.lessons_category_scenario === id,
l => l.get('lessons_category_scenario') === id,
),
getScenarioLessonsQuestions: id => entities('lessonsquestions', state).filter(
l => l.lessons_question_scenario === id,
l => l.get('lessons_question_scenario') === id,
),
});
1 change: 1 addition & 0 deletions openbas-front/src/actions/exercises/exercise-helper.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type Exercise, type ExerciseSimple, type InjectExpectation, type LessonsAnswer, type LessonsCategory, type LessonsQuestion, type Objective, type Team } from '../../utils/api-types';

export interface ExercisesHelper {
isExercise: (exerciseId: string) => boolean;
getExercise: (exerciseId: string) => Exercise;
getExercises: () => ExerciseSimple[];
getExercisesMap: () => Record<string, Exercise>;
Expand Down
1 change: 1 addition & 0 deletions openbas-front/src/actions/helper.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { type Challenge, type Document, type Exercise, type Organization, type P

export interface UserHelper {
getMe: () => User;
getMeAdmin: () => boolean;
getUsersMap: () => Record<string, User>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ export type InjectorContractConverted = Omit<InjectorContract, 'convertedContent
convertedContent: {
fields: ContractElement[];
contract_id: string;
config: { type: string };
label: string;
config: {
type: string;
color_dark: string;
color_light: string;
expose: boolean;
label: Record<string, string>;
};
label: Record<string, string>;
};
};
27 changes: 4 additions & 23 deletions openbas-front/src/actions/injects/Inject.d.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,15 @@
import { type Inject, type InjectOutput } from '../../utils/api-types';
import { type InjectorContractConverted } from '../injector_contracts/InjectorContract';

export type InjectStore = Omit<Inject, 'inject_content' | 'inject_injector_contract'> & {
inject_content: {
inject_content?: {
expectationScore: number;
challenges: string[] | undefined;
};
inject_injector_contract: {
// as we don't know the type of the content of a contract we need to put any here
// eslint-disable-next-line @typescript-eslint/no-explicit-any
injector_contract_content_parsed: any;
convertedContent: {
label: Record<string, string>;
config: { expose: boolean };
};
} & Inject['inject_injector_contract'];
inject_injector_contract: Omit<InjectorContractConverted, 'convertedContent'> & { convertedContent: InjectorContractConverted['convertedContent'] };
};

export type InjectorContractConvertedContent = {
label: Record<string, string>;
config: { expose: boolean };
};

export type InjectOutputType = InjectOutput & {
inject_injector_contract: {
// as we don't know the type of the content of a contract we need to put any here
// eslint-disable-next-line @typescript-eslint/no-explicit-any
injector_contract_content_parsed: any;
convertedContent: InjectorContractConvertedContent;
} & Inject['inject_injector_contract'];
};
export type InjectOutputType = InjectOutput & { inject_injector_contract: { convertedContent: InjectorContractConverted['convertedContent'] } & Inject['inject_injector_contract'] };

export interface ConditionElement {
name: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const ExecutorDocumentationLink: FunctionComponent<Props> = ({ executor }) => {
<Typography variant="body1">
{t('To install the agent please follow the ')}
<a target="_blank" href={executor.executor_doc} rel="noreferrer">
{`${executor.executor_name} ${t('documentation')}`}
{t('{executor_name} documentation', { executor_name: executor.executor_name })}
</a>
.
</Typography>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ SHA512: ca07dc1d0a5297e29327e483f4f35dadb254d96a16a5c33da5ad048e6965a3863d621518
>
{`${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')} `}
<a target="_blank" href={selectedExecutor?.executor_doc} rel="noreferrer">
{`${selectedExecutor?.executor_name} ${t('documentation.')}`}
{t('{executor_name} documentation.', { executor_name: selectedExecutor?.executor_name })}
</a>
</Alert>
<p>
Expand All @@ -362,7 +362,7 @@ SHA512: ca07dc1d0a5297e29327e483f4f35dadb254d96a16a5c33da5ad048e6965a3863d621518
>
{`${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')} `}
<a target="_blank" href={selectedExecutor?.executor_doc} rel="noreferrer">
{`${selectedExecutor?.executor_name} ${t('documentation.')}`}
{t('{executor_name} documentation.', { executor_name: selectedExecutor?.executor_name })}
</a>
</Alert>
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const AssetGroupManagement: FunctionComponent<Props> = ({
// Fetching data
const { assetGroup, userAdmin } = useHelper((helper: AssetGroupsHelper & UserHelper) => ({
assetGroup: helper.getAssetGroup(assetGroupId),
userAdmin: helper.getMe()?.user_admin ?? false,
userAdmin: helper.getMeAdmin(),
}));
useDataLoader(() => {
dispatch(fetchAssetGroup(assetGroupId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ const AssetGroups = () => {
const [searchId] = searchParams.getAll('id');

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

// Headers
const headers: Header[] = useMemo(() => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ import AssetStatus from '../AssetStatus';
import AgentPrivilege from './AgentPrivilege';
import EndpointPopover from './EndpointPopover';

const useStyles = makeStyles()(() => ({ itemHead: { textTransform: 'uppercase' } }));
const useStyles = makeStyles()(() => ({
itemHead: { textTransform: 'uppercase' },
item: { height: 50 },
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sur we want this.

Copy link
Copy Markdown
Member Author

@guillaumejparis guillaumejparis Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On prerealease :
image

On my branch :
image

Others screens where you canclick on the line :
image
image
image

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sorry but, what's the differences ?

}));

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

// Fetching data
const { userAdmin, executorsMap } = useHelper((helper: ExecutorHelper & UserHelper) => ({
userAdmin: helper.getMe()?.user_admin ?? false,
userAdmin: helper.getMeAdmin(),
executorsMap: helper.getExecutorsMap(),
}));
useDataLoader(() => {
Expand Down Expand Up @@ -338,6 +341,7 @@ const Endpoints = () => {
<ListItemButton
component={Link}
to={`/admin/assets/endpoints/${endpoint.asset_id}`}
classes={{ root: classes.item }}
>
<ListItemIcon>
<DevicesOtherOutlined color="primary" />
Expand Down
Loading