Skip to content

Commit

Permalink
[backend/frontend] Fix playbook organization sharing schema (#8767)
Browse files Browse the repository at this point in the history
  • Loading branch information
SouadHadjiat authored Oct 31, 2024
1 parent 0eea615 commit 1302227
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Box from '@mui/material/Box';
import Alert from '@mui/material/Alert';
import Drawer from '../../common/drawer/Drawer';
import ObjectMembersField from '../../common/form/ObjectMembersField';
import ObjectOrganizationField from '../../common/form/ObjectOrganizationField';
import CreatedByField from '../../common/form/CreatedByField';
import Filters from '../../common/lists/Filters';
import FilterIconButton from '../../../../components/FilterIconButton';
Expand Down Expand Up @@ -458,6 +459,18 @@ const PlaybookAddComponentsContent = ({
/>
);
}
if (k === 'organizations') {
return (
<ObjectOrganizationField
key={k}
name="organizations"
style={{ marginTop: 20, width: '100%' }}
label={'Target organizations'}
multiple={true}
alert={false}
/>
);
}
if (k === 'filters') {
return (
<div key={k}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,14 @@ import {
import type { CyberObjectExtension, StixBundle, StixCoreObject, StixCyberObject, StixDomainObject, StixObject, StixOpenctiExtension } from '../../types/stix-common';
import { STIX_EXT_OCTI, STIX_EXT_OCTI_SCO } from '../../types/stix-extensions';
import { connectorsForPlaybook } from '../../database/repository';
import { listAllEntities, listAllRelations, storeLoadById } from '../../database/middleware-loader';
import type { BasicStoreEntityOrganization } from '../organization/organization-types';
import { internalFindByIds, listAllRelations, storeLoadById } from '../../database/middleware-loader';
import { ENTITY_TYPE_IDENTITY_ORGANIZATION } from '../organization/organization-types';
import { getEntitiesListFromCache, getEntitiesMapFromCache } from '../../database/cache';
import { createdBy, objectLabel, objectMarking } from '../../schema/stixRefRelationship';
import { logApp } from '../../config/conf';
import { FunctionalError } from '../../config/errors';
import { extractStixRepresentative } from '../../database/stix-representative';
import {
isEmptyField,
isNotEmptyField,
READ_ENTITIES_INDICES_WITHOUT_INFERRED,
READ_RELATIONSHIPS_INDICES,
READ_RELATIONSHIPS_INDICES_WITHOUT_INFERRED
} from '../../database/utils';
import { isEmptyField, isNotEmptyField, READ_RELATIONSHIPS_INDICES, READ_RELATIONSHIPS_INDICES_WITHOUT_INFERRED } from '../../database/utils';
import { schemaAttributesDefinition } from '../../schema/schema-attributes';
import { schemaRelationsRefDefinition } from '../../schema/schema-relationsRef';
import { stixLoadByIds } from '../../database/middleware';
Expand Down Expand Up @@ -496,8 +489,8 @@ const PLAYBOOK_CONTAINER_WRAPPER_COMPONENT: PlaybookComponent<ContainerWrapperCo
}
};

interface SharingConfiguration {
organizations: string[]
export interface SharingConfiguration {
organizations: string[] | { label: string, value: string }[]
}
const PLAYBOOK_SHARING_COMPONENT_SCHEMA: JSONSchemaType<SharingConfiguration> = {
type: 'object',
Expand All @@ -521,25 +514,20 @@ const PLAYBOOK_SHARING_COMPONENT: PlaybookComponent<SharingConfiguration> = {
is_internal: true,
ports: [{ id: 'out', type: 'out' }],
configuration_schema: PLAYBOOK_SHARING_COMPONENT_SCHEMA,
schema: async () => {
const context = executionContext('playbook_components');
const organizations = await listAllEntities(
context,
SYSTEM_USER,
[ENTITY_TYPE_IDENTITY_ORGANIZATION],
{ connectionFormat: false, indices: READ_ENTITIES_INDICES_WITHOUT_INFERRED }
);
const elements = organizations.map((c) => ({ const: c.id, title: c.name }));
const schemaElement = { properties: { organizations: { items: { oneOf: elements } } } };
return R.mergeDeepRight<JSONSchemaType<SharingConfiguration>, any>(PLAYBOOK_SHARING_COMPONENT_SCHEMA, schemaElement);
},
schema: async () => PLAYBOOK_SHARING_COMPONENT_SCHEMA,
executor: async ({ dataInstanceId, playbookNode, bundle }) => {
const context = executionContext('playbook_components');
const allOrganizations = await getEntitiesListFromCache<BasicStoreEntityOrganization>(context, SYSTEM_USER, ENTITY_TYPE_IDENTITY_ORGANIZATION);
const { organizations } = playbookNode.configuration;
const organizationIds = allOrganizations
.filter((o) => (organizations ?? []).includes(o.internal_id))
.map((o) => o.standard_id);
const organizationsValues = organizations.map((o) => (typeof o !== 'string' ? o.value : o));
const organizationsByIds = await internalFindByIds(context, SYSTEM_USER, organizationsValues, {
type: ENTITY_TYPE_IDENTITY_ORGANIZATION,
baseData: true,
baseFields: ['standard_id']
});
if (organizationsByIds.length === 0) {
return { output_port: 'out', bundle }; // nothing to do since organizations are empty
}
const organizationIds = organizationsByIds.map((o) => o.standard_id);
const baseData = bundle.objects.find((o) => o.id === dataInstanceId) as StixCoreObject;
baseData.extensions[STIX_EXT_OCTI].granted_refs = [...(baseData.extensions[STIX_EXT_OCTI].granted_refs ?? []), ...organizationIds];
return { output_port: 'out', bundle };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,18 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import { v4 as uuidv4 } from 'uuid';
import { BUS_TOPICS, logApp } from '../../config/conf';
import { createEntity, deleteElementById, patchAttribute, updateAttribute } from '../../database/middleware';
import { type EntityOptions, listAllEntities, listEntitiesPaginated, storeLoadById } from '../../database/middleware-loader';
import { type EntityOptions, internalFindByIds, listAllEntities, listEntitiesPaginated, storeLoadById } from '../../database/middleware-loader';
import { notify } from '../../database/redis';
import type { DomainFindById } from '../../domain/domainTypes';
import { ABSTRACT_INTERNAL_OBJECT } from '../../schema/general';
import type { AuthContext, AuthUser } from '../../types/user';
import type { EditInput, FilterGroup, PlaybookAddInput, PlaybookAddLinkInput, PlaybookAddNodeInput, PositionInput } from '../../generated/graphql';
import type { BasicStoreEntityPlaybook, ComponentDefinition, LinkDefinition, NodeDefinition } from './playbook-types';
import { ENTITY_TYPE_PLAYBOOK } from './playbook-types';
import { PLAYBOOK_COMPONENTS } from './playbook-components';
import { PLAYBOOK_COMPONENTS, type SharingConfiguration } from './playbook-components';
import { UnsupportedError } from '../../config/errors';
import { type BasicStoreEntityOrganization, ENTITY_TYPE_IDENTITY_ORGANIZATION } from '../organization/organization-types';
import { SYSTEM_USER } from '../../utils/access';
import { validateFilterGroupForStixMatch } from '../../utils/filtering/filtering-stix/stix-filtering';
import { registerConnectorQueues, unregisterConnector } from '../../database/rabbitmq';

Expand All @@ -45,6 +47,38 @@ export const availableComponents = () => {
return Object.values(PLAYBOOK_COMPONENTS);
};

export const getPlaybookDefinition = async (context: AuthContext, playbook: BasicStoreEntityPlaybook) => {
if (playbook.playbook_definition && playbook.playbook_definition.includes('PLAYBOOK_SHARING_COMPONENT')) {
// parse playbook definition in case there is a sharing with organization component, in order to parse organizations to get their label
const definition = JSON.parse(playbook.playbook_definition) as ComponentDefinition;
const sharingComponent = definition.nodes.find((n) => n.component_id === 'PLAYBOOK_SHARING_COMPONENT');
if (sharingComponent && sharingComponent.configuration) {
const sharingConfiguration = JSON.parse(sharingComponent.configuration) as SharingConfiguration;
const organizationsIds = sharingConfiguration.organizations.filter((o) => typeof o === 'string');
if (organizationsIds.length === 0) {
return playbook.playbook_definition; // nothing to map, already mapped
}
const organizationsByIds = await internalFindByIds(context, SYSTEM_USER, organizationsIds, {
type: ENTITY_TYPE_IDENTITY_ORGANIZATION,
baseData: true,
baseFields: ['internal_id', 'name'],
toMap: true,
}) as unknown as { [k: string]: BasicStoreEntityOrganization };
const organizationsWithNames = [];
for (let i = 0; i < organizationsIds.length; i += 1) {
const orgId = organizationsIds[i];
if (organizationsByIds[orgId]) {
organizationsWithNames.push({ label: organizationsByIds[orgId].name, value: orgId });
}
}
sharingConfiguration.organizations = organizationsWithNames;
sharingComponent.configuration = JSON.stringify(sharingConfiguration);
return JSON.stringify(definition);
}
}
return playbook.playbook_definition;
};

export const playbookAddNode = async (context: AuthContext, user: AuthUser, id: string, input: PlaybookAddNodeInput) => {
// our stix matching is currently limited, we need to validate the input filters
if (input.configuration) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
playbookDeleteNode,
playbookDeleteLink,
playbookUpdatePositions,
getPlaybookDefinition
} from './playbook-domain';
import { playbookStepExecution } from '../../manager/playbookManager';
import { getLastPlaybookExecutions } from '../../database/redis';
Expand All @@ -42,6 +43,7 @@ const playbookResolvers: Resolvers = {
playbookComponents: () => availableComponents(),
},
Playbook: {
playbook_definition: async (current, _, context) => getPlaybookDefinition(context, current),
last_executions: async (current) => getLastPlaybookExecutions(current.id),
queue_messages: async (current, _, context) => getConnectorQueueSize(context, context.user, current.id)
},
Expand Down

0 comments on commit 1302227

Please sign in to comment.