Skip to content

Commit

Permalink
[backend/frontend] fix multiple issues for Organisation Admins (OpenC…
Browse files Browse the repository at this point in the history
  • Loading branch information
marieflorescontact authored Oct 18, 2024
1 parent f65806d commit b04b787
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ export const convertTypeToStixType = (type: string): string => {
if (isStixCoreRelationship(type)) {
return 'relationship';
}
if (isInternalRelationship(type)) {
return 'internal-relationship';
}
if (isStixSightingRelationship(type)) {
return 'sighting';
}
Expand Down
14 changes: 12 additions & 2 deletions opencti-platform/opencti-graphql/src/domain/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,9 +418,16 @@ export const roleEditContext = async (context, user, roleId, input) => {
});
};

const isUserAdministratingOrga = (user, organizationId) => {
return user.administrated_organizations.some(({ id }) => id === organizationId);
};

export const assignOrganizationToUser = async (context, user, userId, organizationId) => {
if (isOnlyOrgaAdmin(user)) {
throw ForbiddenAccess();
// When user is organization admin, we make sure she is also admin of organization added
if (!isUserAdministratingOrga(user, organizationId)) {
throw ForbiddenAccess();
}
}
const targetUser = await findById(context, user, userId);
if (!targetUser) {
Expand Down Expand Up @@ -1006,7 +1013,10 @@ export const userIdDeleteRelation = async (context, user, userId, toId, relation

export const userDeleteOrganizationRelation = async (context, user, userId, toId) => {
if (isOnlyOrgaAdmin(user)) {
throw ForbiddenAccess();
// When user is organization admin, we make sure she is also admin of organization removed
if (!isUserAdministratingOrga(user, toId)) {
throw ForbiddenAccess();
}
}
const targetUser = await findById(context, user, userId);
if (!targetUser) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ def _process_message(self, msg):
logging.info("%s", f"Processing event {msg.id}")
self.count_number += 1
data = json.loads(msg.data)
type = data["data"]["type"]
if type == "internal-relationship":
return
if msg.event == "create":
bundle = {
"type": "bundle",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
getGroupIdByName,
getOrganizationIdByName,
getUserIdByEmail,
PLATFORM_ORGANIZATION,
queryAsAdmin,
TEST_ORGANIZATION,
testContext,
Expand All @@ -20,6 +21,7 @@ import {
} from '../../utils/testQuery';
import { ENTITY_TYPE_IDENTITY_ORGANIZATION } from '../../../src/modules/organization/organization-types';
import { VIRTUAL_ORGANIZATION_ADMIN } from '../../../src/utils/access';
import { adminQueryWithSuccess, queryAsUserIsExpectedForbidden, queryAsUserWithSuccess } from '../../utils/testQueryHelper';

const LIST_QUERY = gql`
query users(
Expand Down Expand Up @@ -705,6 +707,44 @@ describe('User has no settings capability and is organization admin query behavi
let userEditorId;
let testOrganizationId;
let amberGroupId;
let platformOrganizationId;
const organizationsIds = [];

const ORGA_ADMIN_ADD_QUERY = gql`
mutation OrganizationAdminAdd($id: ID!, $memberId: String!) {
organizationAdminAdd(id: $id, memberId: $memberId) {
id
standard_id
}
}
`;

const ORGANIZATION_ADD_QUERY = gql`
mutation UserOrganizationAddMutation(
$id: ID!
$organizationId: ID!
) {
userEdit(id: $id) {
organizationAdd(organizationId: $organizationId) {
id
}
}
}
`;

const ORGANIZATION_DELETE_QUERY = gql`
mutation UserOrganizationDeleteMutation(
$id: ID!
$organizationId: ID!
) {
userEdit(id: $id) {
organizationDelete(organizationId: $organizationId) {
id
}
}
}
`;

afterAll(async () => {
// remove the capability to administrate the Organization
const ORGA_ADMIN_DELETE_QUERY = gql`
Expand All @@ -714,15 +754,7 @@ describe('User has no settings capability and is organization admin query behavi
}
}
`;
await adminQuery({
query: ORGA_ADMIN_DELETE_QUERY,
variables: {
id: testOrganizationId,
memberId: userEditorId,
},
});

// remove granted_groups to TEST_ORGANIZATION
const UPDATE_QUERY = gql`
mutation OrganizationEdit($id: ID!, $input: [EditInput]!) {
organizationFieldPatch(id: $id, input: $input) {
Expand All @@ -734,31 +766,33 @@ describe('User has no settings capability and is organization admin query behavi
}
}
`;
// Delete admin to ORGANIZATION
await adminQuery({
query: UPDATE_QUERY,
variables: { id: testOrganizationId, input: { key: 'grantable_groups', value: [] } },
query: ORGA_ADMIN_DELETE_QUERY, // +1 update organization
variables: {
id: testOrganizationId,
memberId: userEditorId,
},
});
for (let i = 0; i < organizationsIds.length; i += 1) {
// remove granted_groups to ORGANIZATION
await adminQuery({
query: UPDATE_QUERY, // +1 update organization for each (+2 total)
variables: { id: organizationsIds[i], input: { key: 'grantable_groups', value: [] } },
});
}
});
it('should has the capability to administrate the Organization', async () => {
const ORGA_ADMIN_ADD_QUERY = gql`
mutation OrganizationAdminAdd($id: ID!, $memberId: String!) {
organizationAdminAdd(id: $id, memberId: $memberId) {
id
standard_id
}
}
`;
userEditorId = await getUserIdByEmail(USER_EDITOR.email); // USER_EDITOR is perfect because she has no settings capabilities and is part of TEST_ORGANIZATION
const queryResult = await adminQuery({
query: ORGA_ADMIN_ADD_QUERY,
const organizationAdminAddQueryResult = await adminQueryWithSuccess({
query: ORGA_ADMIN_ADD_QUERY, // +1 update event of organization
variables: {
id: TEST_ORGANIZATION.id,
memberId: userEditorId,
},
});
expect(queryResult).not.toBeNull();
expect(queryResult.data.organizationAdminAdd).not.toBeNull();
expect(queryResult.data.organizationAdminAdd.standard_id).toEqual(TEST_ORGANIZATION.id);
expect(organizationAdminAddQueryResult.data.organizationAdminAdd).not.toBeNull();
expect(organizationAdminAddQueryResult.data.organizationAdminAdd.standard_id).toEqual(TEST_ORGANIZATION.id);

// Check that USER_EDITOR is Organization administrator
const editorUserQueryResult = await adminQuery({ query: READ_QUERY, variables: { id: userEditorId } });
Expand All @@ -769,9 +803,10 @@ describe('User has no settings capability and is organization admin query behavi
expect(capabilities.some((capa) => capa.name === VIRTUAL_ORGANIZATION_ADMIN)).toEqual(true);
});
it('should user created', async () => {
// Create the user
testOrganizationId = await getOrganizationIdByName(TEST_ORGANIZATION.name);
organizationsIds.push(testOrganizationId);
amberGroupId = await getGroupIdByName(AMBER_GROUP.name);

const USER_TO_CREATE = {
input: {
name: 'User',
Expand Down Expand Up @@ -811,9 +846,8 @@ describe('User has no settings capability and is organization admin query behavi
});
expect(user).not.toBeNull();
expect(user.data.userAdd).not.toBeNull();
userInternalId = user.data.userAdd.id;

expect(user.data.userAdd.name).toEqual('User');
userInternalId = user.data.userAdd.id;
});
it('should update user from its own organization', async () => {
const UPDATE_QUERY = gql`
Expand All @@ -825,21 +859,102 @@ describe('User has no settings capability and is organization admin query behavi
}
}
`;
const queryResult = await editorQuery({
const queryResult = await queryAsUserWithSuccess(USER_EDITOR.client, {
query: UPDATE_QUERY,
variables: { id: userInternalId, input: { key: 'account_status', value: ['Inactive'] } },
});
expect(queryResult.data.userEdit.fieldPatch.account_status).toEqual('Inactive');
});
it('should not add organization to user if not admin', async () => {
platformOrganizationId = await getOrganizationIdByName(PLATFORM_ORGANIZATION.name);
await queryAsUserIsExpectedForbidden(USER_EDITOR.client, {
query: ORGANIZATION_ADD_QUERY,
variables: {
id: userInternalId,
organizationId: platformOrganizationId,
},
});
});
it('should administrate more than 1 organization', async () => {
// Need to add granted_groups to PLATFORM_ORGANIZATION because of line 533 in domain/user.js
const UPDATE_QUERY = gql`
mutation OrganizationEdit($id: ID!, $input: [EditInput]!) {
organizationFieldPatch(id: $id, input: $input) {
id
name
grantable_groups {
id
}
}
}
`;
const grantableGroupQueryResult = await adminQuery({
query: UPDATE_QUERY,
variables: { id: platformOrganizationId, input: { key: 'grantable_groups', value: [amberGroupId] } },
});
expect(grantableGroupQueryResult.data.organizationFieldPatch.grantable_groups.length).toEqual(1);
expect(grantableGroupQueryResult.data.organizationFieldPatch.grantable_groups[0]).toEqual({ id: amberGroupId });
organizationsIds.push(platformOrganizationId);

// Add Editor to PLATFORM_ORGANIZATION
const addEditorToOrgaQuery = await adminQueryWithSuccess({
query: ORGANIZATION_ADD_QUERY, // +1 create of relation between orga & user
variables: {
id: userEditorId,
organizationId: platformOrganizationId,
},
});
expect(addEditorToOrgaQuery.data.userEdit.organizationAdd.id).toEqual(userEditorId);

// Editor administrate PLATFORM_ORGANIZATION
const queryResult = await adminQueryWithSuccess({
query: ORGA_ADMIN_ADD_QUERY, // +1 update event of organization
variables: {
id: PLATFORM_ORGANIZATION.id,
memberId: userEditorId,
},
});
expect(queryResult.data.organizationAdminAdd).not.toBeNull();
expect(queryResult.data.organizationAdminAdd.standard_id).toEqual(PLATFORM_ORGANIZATION.id);
});
it('should add 2nd organization to user if admin', async () => {
const queryResult = await queryAsUserWithSuccess(USER_EDITOR.client, {
query: ORGANIZATION_ADD_QUERY, // +1 create of relation between orga & user
variables: {
id: userInternalId,
organizationId: platformOrganizationId,
},
});
expect(queryResult.data.userEdit.organizationAdd.id).toEqual(userInternalId);
});
it('should delete 2nd organization to user if admin', async () => {
const queryResult = await queryAsUserWithSuccess(USER_EDITOR.client, {
query: ORGANIZATION_DELETE_QUERY, // +1 delete of relation between orga & user
variables: {
id: userInternalId,
organizationId: platformOrganizationId,
},
});
expect(queryResult.data.userEdit.organizationDelete.id).toEqual(userInternalId);
});
it('should remove Editor from PLATFORM_ORGANIZATION', async () => {
const queryResult = await adminQueryWithSuccess({
query: ORGANIZATION_DELETE_QUERY, // +1 delete event (delete relation) +1 update event
variables: {
id: userEditorId,
organizationId: platformOrganizationId,
},
});
expect(queryResult.data.userEdit.organizationDelete.id).toEqual(userEditorId);
});
it('should user deleted', async () => {
// Delete user
await editorQuery({
query: DELETE_QUERY,
variables: { id: userInternalId },
});
// Verify is no longer found
const queryResult = await adminQuery({ query: READ_QUERY, variables: { id: userInternalId } });
expect(queryResult).not.toBeNull();
const queryResult = await adminQueryWithSuccess({ query: READ_QUERY, variables: { id: userInternalId } });
expect(queryResult.data.user).toBeNull();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('Raw streams tests', () => {
expect(createEventsByTypes.tool.length).toBe(2);
expect(createEventsByTypes.vocabulary.length).toBe(342); // 328 created at init + 2 created in tests + 5 vocabulary organizations types + 7 persona
expect(createEventsByTypes.vulnerability.length).toBe(7);
expect(createEvents.length).toBe(793);
expect(createEvents.length).toBe(795);
for (let createIndex = 0; createIndex < createEvents.length; createIndex += 1) {
const { data: insideData, origin, type } = createEvents[createIndex];
expect(origin).toBeDefined();
Expand All @@ -58,7 +58,7 @@ describe('Raw streams tests', () => {
expect(updateEventsByTypes['marking-definition'].length).toBe(2);
expect(updateEventsByTypes['campaign'].length).toBe(7);
expect(updateEventsByTypes['relationship'].length).toBe(8);
expect(updateEventsByTypes['identity'].length).toBe(18);
expect(updateEventsByTypes['identity'].length).toBe(22);
expect(updateEventsByTypes['malware'].length).toBe(17);
expect(updateEventsByTypes['intrusion-set'].length).toBe(4);
expect(updateEventsByTypes['data-component'].length).toBe(4);
Expand All @@ -82,7 +82,7 @@ describe('Raw streams tests', () => {
expect(updateEventsByTypes['threat-actor'].length).toBe(17);
expect(updateEventsByTypes['vocabulary'].length).toBe(3);
expect(updateEventsByTypes['vulnerability'].length).toBe(3);
expect(updateEvents.length).toBe(169);
expect(updateEvents.length).toBe(173);
for (let updateIndex = 0; updateIndex < updateEvents.length; updateIndex += 1) {
const event = updateEvents[updateIndex];
const { data: insideData, origin, type } = event;
Expand All @@ -95,7 +95,7 @@ describe('Raw streams tests', () => {
}
// 03 - CHECK DELETE EVENTS
const deleteEvents = events.filter((e) => e.type === EVENT_TYPE_DELETE);
expect(deleteEvents.length).toBe(144);
expect(deleteEvents.length).toBe(146);
// const deleteEventsByTypes = R.groupBy((e) => e.data.data.type, deleteEvents);
for (let delIndex = 0; delIndex < deleteEvents.length; delIndex += 1) {
const { data: insideData, origin, type } = deleteEvents[delIndex];
Expand Down
2 changes: 1 addition & 1 deletion opencti-platform/opencti-graphql/tests/utils/testQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const SYNC_LIVE_START_REMOTE_URI = conf.get('app:sync_live_start_remote_u
export const SYNC_DIRECT_START_REMOTE_URI = conf.get('app:sync_direct_start_remote_uri');
export const SYNC_RESTORE_START_REMOTE_URI = conf.get('app:sync_restore_start_remote_uri');
export const SYNC_TEST_REMOTE_URI = `http://api-tests:${PORT}`;
export const RAW_EVENTS_SIZE = 1114;
export const RAW_EVENTS_SIZE = 1122;
export const SYNC_LIVE_EVENTS_SIZE = 608;

export const PYTHON_PATH = './src/python/testing';
Expand Down

0 comments on commit b04b787

Please sign in to comment.