Skip to content

Commit cce7c40

Browse files
OctaveLaventuredelemaf
authored andcommitted
[backend] Add authorized members side effect (#14047)
1 parent 5e5962a commit cce7c40

4 files changed

Lines changed: 178 additions & 35 deletions

File tree

opencti-platform/opencti-front/src/schema/relay.schema.graphql

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9629,6 +9629,10 @@ type Query {
96299629
stixObjectOrStixRelationship(id: String!): StixObjectOrStixRelationship
96309630
stixObjectOrStixRelationships(first: Int, after: ID, search: String, filters: FilterGroup): StixObjectOrStixRelationshipConnection
96319631
stixCoreObjectOrStixCoreRelationship(id: String!): StixCoreObjectOrStixCoreRelationship
9632+
workflowDefinition(entityType: String!): WorkflowSchema
9633+
workflowInstance(entityId: String!): WorkflowInstance
9634+
allowedNextStatuses(entityId: String!): [Status!]!
9635+
allowedTransitions(entityId: String!): [WorkflowTransition!]!
96329636
csvMapperTest(configuration: String!, content: String!): CsvMapperTestResult @deprecated(reason: "[>=6.4 & <6.7]. Use `csvMapperTest mutation`.")
96339637
channel(id: String!): Channel
96349638
channels(first: Int, after: ID, orderBy: ChannelsOrdering, orderMode: OrderingMode, filters: FilterGroup, search: String): ChannelConnection
@@ -9793,11 +9797,6 @@ type Query {
97939797
Auth log history by provider id: internal_id for DB providers, or static id for singletons (Headers, Cert).
97949798
"""
97959799
authLogHistoryById(id: String!): [AuthLogEntry!]!
9796-
9797-
workflowDefinition(entityType: String!): WorkflowSchema
9798-
workflowInstance(entityId: String!): WorkflowInstance
9799-
allowedNextStatuses(entityId: String!): [Status!]!
9800-
allowedTransitions(entityId: String!): [WorkflowTransition!]!
98019800
}
98029801

98039802
type Subscription {
@@ -10457,6 +10456,9 @@ type Mutation {
1045710456
stixRefRelationshipEdit(id: ID!): StixRefRelationshipEditMutations
1045810457
stixSightingRelationshipAdd(input: StixSightingRelationshipAddInput!): StixSightingRelationship
1045910458
stixSightingRelationshipEdit(id: ID!): StixSightingRelationshipEditMutations
10459+
workflowDefinitionSet(entityType: String!, definition: String!): EntitySetting
10460+
workflowDefinitionDelete(entityType: String!): EntitySetting
10461+
triggerWorkflowEvent(entityId: String!, eventName: String!): WorkflowTriggerResult!
1046010462
channelAdd(input: ChannelAddInput!): Channel
1046110463
channelDelete(id: ID!): ID
1046210464
channelFieldPatch(id: ID!, input: [EditInput]!, commitMessage: String, references: [String]): Channel
@@ -10747,9 +10749,6 @@ type Mutation {
1074710749
ldapProviderAdd(input: LdapInput!): AuthenticationProvider
1074810750
ldapProviderEdit(id: ID!, input: LdapInput!): AuthenticationProvider
1074910751
ldapProviderDelete(id: ID!): ID
10750-
workflowDefinitionSet(entityType: String!, definition: String!): EntitySetting
10751-
workflowDefinitionDelete(entityType: String!): EntitySetting
10752-
triggerWorkflowEvent(entityId: String!, eventName: String!): WorkflowTriggerResult!
1075310752
}
1075410753

1075510754
enum WorkflowActionMode {
@@ -10837,6 +10836,30 @@ type WorkflowInstance {
1083710836
allowedTransitions: [WorkflowTransition!]!
1083810837
}
1083910838

10839+
type DraftWorkspace implements InternalObject & BasicObject {
10840+
workflowInstance: WorkflowInstance
10841+
id: ID!
10842+
entity_type: String!
10843+
standard_id: String!
10844+
parent_types: [String!]!
10845+
metrics: [Metric]
10846+
name: String!
10847+
createdBy: Identity
10848+
description: String
10849+
created_at: DateTime!
10850+
creators: [Creator!]
10851+
entity_id: String
10852+
objectAssignee: [Assignee!]
10853+
objectParticipant: [Participant!]
10854+
objectsCount: DraftObjectsCount!
10855+
draft_status: DraftStatus!
10856+
processingCount: Int!
10857+
works(first: Int): [Work!]
10858+
validationWork: Work
10859+
authorizedMembers: [MemberAccess!]!
10860+
currentUserAccessRight: String
10861+
}
10862+
1084010863
type Channel implements BasicObject & StixObject & StixCoreObject & StixDomainObject {
1084110864
id: ID!
1084210865
standard_id: String!
@@ -14984,30 +15007,6 @@ enum DraftStatus {
1498415007
validated
1498515008
}
1498615009

14987-
type DraftWorkspace implements InternalObject & BasicObject {
14988-
id: ID!
14989-
entity_type: String!
14990-
standard_id: String!
14991-
parent_types: [String!]!
14992-
metrics: [Metric]
14993-
name: String!
14994-
createdBy: Identity
14995-
description: String
14996-
created_at: DateTime!
14997-
creators: [Creator!]
14998-
entity_id: String
14999-
objectAssignee: [Assignee!]
15000-
objectParticipant: [Participant!]
15001-
objectsCount: DraftObjectsCount!
15002-
draft_status: DraftStatus!
15003-
processingCount: Int!
15004-
works(first: Int): [Work!]
15005-
validationWork: Work
15006-
authorizedMembers: [MemberAccess!]!
15007-
currentUserAccessRight: String
15008-
workflowInstance: WorkflowInstance
15009-
}
15010-
1501115010
type DraftObjectsCount {
1501215011
totalCount: Int!
1501315012
entitiesCount: Int!
@@ -16147,4 +16146,4 @@ type AuthenticationProviderMigrationResult {
1614716146
type AvailableSecretInfo {
1614816147
provider_name: String!
1614916148
secret_name: String!
16150-
}
16149+
}

opencti-platform/opencti-graphql/src/modules/entitySetting/entitySetting-utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export const defaultScale = JSON.stringify({
7777
export const availableSettings: Record<string, Array<string>> = {
7878
[ABSTRACT_STIX_DOMAIN_OBJECT]: ['attributes_configuration', 'platform_entity_files_ref', 'platform_hidden_type', 'enforce_reference', 'workflow_configuration'],
7979
[ABSTRACT_STIX_CORE_RELATIONSHIP]: ['attributes_configuration', 'enforce_reference', 'workflow_configuration'],
80-
[STIX_SIGHTING_RELATIONSHIP]: ['attributes_configuration', 'enforce_reference', 'platform_hidden_type', 'workflow_configuration'],
80+
[STIX_SIGHTING_RELATIONSHIP]: ['attributes_configuration', 'enforce_reference', 'platform_hidden_type', 'workflow_configuration', 'workflow_id'],
8181
[ABSTRACT_STIX_CYBER_OBSERVABLE]: ['platform_hidden_type'],
8282
[ENTITY_TYPE_EXTERNAL_REFERENCE]: ['platform_hidden_type'],
8383
// add templates for containers

opencti-platform/opencti-graphql/src/modules/workflow/registry/workflow-actions.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { logApp } from '../../../config/conf';
2-
import { validateDraftWorkspace } from '../../draftWorkspace/draftWorkspace-domain';
2+
import { draftWorkspaceEditAuthorizedMembers, validateDraftWorkspace } from '../../draftWorkspace/draftWorkspace-domain';
33
import type { Context } from '../types/workflow-types';
44

55
export type ActionFunction<TContext extends Context = Context> = (ctx: TContext, params?: any) => Promise<void> | void;
@@ -14,4 +14,8 @@ export const ActionRegistry: Record<string, ActionFunction> = {
1414
const { entity, user, context } = ctx;
1515
await validateDraftWorkspace(context, user, entity.id);
1616
},
17+
updateAuthorizedMembers: async (ctx, params) => {
18+
const { entity, user, context } = ctx;
19+
await draftWorkspaceEditAuthorizedMembers(context, user, entity.id, params?.authorized_members);
20+
},
1721
};
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import gql from 'graphql-tag';
2+
import { beforeAll, describe, expect, it } from 'vitest';
3+
import { adminQuery, ADMIN_USER, TEST_ORGANIZATION } from '../../utils/testQuery';
4+
5+
const WORKFLOW_DEFINITION_SET_MUTATION = gql`
6+
mutation WorkflowDefinitionSet($entityType: String!, $definition: String!) {
7+
workflowDefinitionSet(entityType: $entityType, definition: $definition) {
8+
id
9+
target_type
10+
workflow_id
11+
}
12+
}
13+
`;
14+
15+
const CREATE_DRAFT_WORKSPACE_QUERY = gql`
16+
mutation DraftWorkspaceAdd($input: DraftWorkspaceAddInput!) {
17+
draftWorkspaceAdd(input: $input) {
18+
id
19+
name
20+
draft_status
21+
}
22+
}
23+
`;
24+
25+
const TRIGGER_WORKFLOW_EVENT_MUTATION = gql`
26+
mutation TriggerWorkflowEvent($entityId: String!, $eventName: String!) {
27+
triggerWorkflowEvent(entityId: $entityId, eventName: $eventName) {
28+
success
29+
newState
30+
reason
31+
}
32+
}
33+
`;
34+
35+
const DRAFT_WORKSPACE_QUERY = gql`
36+
query DraftWorkspace($id: String!) {
37+
draftWorkspace(id: $id) {
38+
id
39+
name
40+
authorizedMembers {
41+
member_id
42+
access_right
43+
}
44+
}
45+
}
46+
`;
47+
48+
const WORKFLOW_DEFINITION_DELETE_MUTATION = gql`
49+
mutation WorkflowDefinitionDelete($entityType: String!) {
50+
workflowDefinitionDelete(entityType: $entityType) {
51+
id
52+
workflow_id
53+
}
54+
}
55+
`;
56+
57+
describe('Workflow Actions Resolver', () => {
58+
let draftWorkspaceId: string;
59+
60+
beforeAll(async () => {
61+
const result = await adminQuery({
62+
query: CREATE_DRAFT_WORKSPACE_QUERY,
63+
variables: {
64+
input: { name: 'Workflow Action Test Workspace' },
65+
},
66+
});
67+
draftWorkspaceId = result.data.draftWorkspaceAdd.id;
68+
});
69+
70+
it('should update authorized members on workflow event', async () => {
71+
// 1. Define a workflow with updateAuthorizedMembers action
72+
const authorizedMembersInput = [
73+
{ id: ADMIN_USER.id, access_right: 'admin' },
74+
{ id: TEST_ORGANIZATION.id, access_right: 'view' },
75+
];
76+
77+
const workflowDefinition = JSON.stringify({
78+
id: 'draft-workflow-with-action',
79+
name: 'Draft Workflow with Action',
80+
initialState: 'open',
81+
states: [
82+
{ name: 'open' },
83+
{
84+
name: 'restricted',
85+
onEnter: [
86+
{
87+
type: 'updateAuthorizedMembers',
88+
params: {
89+
authorized_members: authorizedMembersInput
90+
},
91+
},
92+
],
93+
},
94+
],
95+
transitions: [{ from: 'open', to: 'restricted', event: 'restrict_event' }],
96+
});
97+
98+
// 2. Set the workflow definition
99+
await adminQuery({
100+
query: WORKFLOW_DEFINITION_SET_MUTATION,
101+
variables: {
102+
entityType: 'DraftWorkspace',
103+
definition: workflowDefinition,
104+
},
105+
});
106+
107+
// 3. Trigger the event
108+
const triggerResult = await adminQuery({
109+
query: TRIGGER_WORKFLOW_EVENT_MUTATION,
110+
variables: {
111+
entityId: draftWorkspaceId,
112+
eventName: 'restrict_event',
113+
},
114+
});
115+
expect(triggerResult.data.triggerWorkflowEvent.success).toBe(true);
116+
expect(triggerResult.data.triggerWorkflowEvent.newState).toBe('restricted');
117+
118+
// 4. Verify authorized members
119+
const workspaceResult = await adminQuery({
120+
query: DRAFT_WORKSPACE_QUERY,
121+
variables: { id: draftWorkspaceId },
122+
});
123+
124+
const { authorizedMembers } = workspaceResult.data.draftWorkspace;
125+
expect(authorizedMembers.length).toBe(2);
126+
expect(authorizedMembers).toEqual(expect.arrayContaining([
127+
expect.objectContaining({ member_id: ADMIN_USER.id, access_right: 'admin' }),
128+
expect.objectContaining({ member_id: TEST_ORGANIZATION.id, access_right: 'view' }),
129+
]));
130+
});
131+
132+
it('should cleanup after tests', async () => {
133+
await adminQuery({
134+
query: WORKFLOW_DEFINITION_DELETE_MUTATION,
135+
variables: {
136+
entityType: 'DraftWorkspace',
137+
},
138+
});
139+
});
140+
});

0 commit comments

Comments
 (0)