Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[backend/frontend] fix multiple issues for Organisation Admins (#8101) #8459

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
Draft
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
12 changes: 10 additions & 2 deletions opencti-platform/opencti-graphql/src/domain/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,11 @@ export const roleEditContext = async (context, user, roleId, input) => {

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
const myAdministratedOrganizationsIds = user.administrated_organizations.map(({ id }) => id);
if (!myAdministratedOrganizationsIds.includes(organizationId)) {
throw ForbiddenAccess();
}
}
const targetUser = await findById(context, user, userId);
if (!targetUser) {
Expand Down Expand Up @@ -1004,7 +1008,11 @@ 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
const myAdministratedOrganizationsIds = user.administrated_organizations.map(({ id }) => id);
if (!myAdministratedOrganizationsIds.includes(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 @@ -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
}
}
`;
await adminQuery({
query: UPDATE_QUERY,
variables: { id: testOrganizationId, input: { key: 'grantable_groups', value: [] } },
});
for (let i = 0; i < organizationsIds.length; i += 1) {
// Delete admin to ORGANIZATION
await adminQuery({
query: ORGA_ADMIN_DELETE_QUERY,
variables: {
id: organizationsIds[i],
memberId: userEditorId,
},
});
// remove granted_groups to ORGANIZATION
await adminQuery({
query: UPDATE_QUERY,
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({
const organizationAdminAddQueryResult = await adminQueryWithSuccess({
query: ORGA_ADMIN_ADD_QUERY,
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,92 @@ 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);
organizationsIds.push(platformOrganizationId);
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 });

// Add Editor to PLATFORM_ORGANIZATION
const addEditorToOrgaQuery = await adminQueryWithSuccess({
query: ORGANIZATION_ADD_QUERY,
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,
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,
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,
variables: {
id: userInternalId,
organizationId: platformOrganizationId,
},
});
expect(queryResult.data.userEdit.organizationDelete.id).toEqual(userInternalId);
});
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(784);
expect(createEvents.length).toBe(786);
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(16);
expect(updateEventsByTypes['identity'].length).toBe(20);
expect(updateEventsByTypes['malware'].length).toBe(16);
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(161);
expect(updateEvents.length).toBe(165);
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(135);
expect(deleteEvents.length).toBe(136);
// 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 = 1088;
export const RAW_EVENTS_SIZE = 1095;
export const SYNC_LIVE_EVENTS_SIZE = 608;

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