Skip to content

Commit 800b803

Browse files
SouadHadjiatmarieflorescontact
authored andcommitted
[backend] Check authorized members for element access (#4538)
1 parent 78b02b8 commit 800b803

File tree

1 file changed

+94
-73
lines changed
  • opencti-platform/opencti-graphql/src/utils

1 file changed

+94
-73
lines changed

opencti-platform/opencti-graphql/src/utils/access.ts

+94-73
Original file line numberDiff line numberDiff line change
@@ -400,39 +400,103 @@ export const isOnlyOrgaAdmin = (user: AuthUser) => {
400400
return !isUserHasCapability(user, SETTINGS_SET_ACCESSES) && isUserHasCapability(user, VIRTUAL_ORGANIZATION_ADMIN);
401401
};
402402

403-
export const isOrganizationAllowed = (element: BasicStoreCommon, user: AuthUser, settings:BasicStoreSettings) => {
404-
const elementOrganizations = element[RELATION_GRANTED_TO] ?? [];
403+
// returns all user member access ids : his id, his organizations ids (and parent organizations), his groups ids
404+
export const computeUserMemberAccessIds = (user: AuthUser) => {
405+
const memberAccessIds = [user.id];
406+
if (user.organizations) {
407+
const userOrganizationsIds = user.organizations.map((org) => org.internal_id);
408+
memberAccessIds.push(...userOrganizationsIds);
409+
}
410+
if (user.groups) {
411+
const userGroupsIds = user.groups.map((group) => group.internal_id);
412+
memberAccessIds.push(...userGroupsIds);
413+
}
414+
if (user.roles) {
415+
const userRolesIds = user.roles.map((role) => role.internal_id);
416+
memberAccessIds.push(...userRolesIds);
417+
}
418+
return memberAccessIds;
419+
};
405420

421+
// region entity access by user
422+
423+
export const getUserAccessRight = (user: AuthUser, element: any) => {
424+
if (!element.authorized_members || element.authorized_members.length === 0) { // no restricted user access on element
425+
return MEMBER_ACCESS_RIGHT_ADMIN;
426+
}
427+
const accessMembers = [...element.authorized_members];
428+
const userMemberAccessIds = computeUserMemberAccessIds(user);
429+
const foundAccessMembers = accessMembers.filter((u) => u.id === MEMBER_ACCESS_ALL || userMemberAccessIds.includes(u.id));
430+
// If user have extended capabilities, is an admin
431+
if ((element.authorized_authorities ?? []).some((c: string) => userMemberAccessIds.includes(c) || isUserHasCapability(user, c))) {
432+
return MEMBER_ACCESS_RIGHT_ADMIN;
433+
}
434+
// if user is bypass, user has admin access (needed for data management usage)
435+
if (isBypassUser(user)) {
436+
return MEMBER_ACCESS_RIGHT_ADMIN;
437+
}
438+
if (!foundAccessMembers.length) { // user has no access
439+
return null;
440+
}
441+
if (foundAccessMembers.some((m) => m.access_right === MEMBER_ACCESS_RIGHT_ADMIN)) {
442+
return MEMBER_ACCESS_RIGHT_ADMIN;
443+
}
444+
if (foundAccessMembers.some((m) => m.access_right === MEMBER_ACCESS_RIGHT_EDIT)) {
445+
return MEMBER_ACCESS_RIGHT_EDIT;
446+
}
447+
return MEMBER_ACCESS_RIGHT_VIEW;
448+
};
449+
export const hasAuthorizedMemberAccess = (user: AuthUser, element: { authorized_members?: AuthorizedMember[], authorized_authorities?: string[] }) => {
450+
const userAccessRight = getUserAccessRight(user, element);
451+
return !!userAccessRight;
452+
};
453+
454+
const isEntityOrganizationsAllowed = (
455+
entityInternalId: string,
456+
entityOrganizations: string[],
457+
user: AuthUser,
458+
settings: BasicStoreSettings,
459+
opts: { useStandardId?: boolean } = {}
460+
) => {
461+
const { useStandardId = false } = opts;
406462
// If platform organization is set
407463
if (settings.platform_organization) {
408-
const userOrganizations = user.organizations.map((o) => o.internal_id);
464+
const userOrganizations = user.organizations.map((o) => (useStandardId ? o.standard_id : o.internal_id));
409465

410466
// If user part of platform organization, is granted by default
411467
if (user.inside_platform_organization) {
412468
return true;
413469
}
414470
// Grant access to the user individual
415-
if (element.internal_id === user.individual_id) {
471+
if (entityInternalId === user.individual_id) {
416472
return true;
417473
}
418474
// If not, user is by design inside an organization
419475
// If element has no current sharing organization, it can be accessed (secure by default)
420476
// If element is shared, user must have a matching sharing organization
421-
return elementOrganizations.some((r) => userOrganizations.includes(r));
477+
return entityOrganizations.some((r) => userOrganizations.includes(r));
422478
}
423479
return true;
424480
};
425481

482+
export const isOrganizationAllowed = (element: BasicStoreCommon, user: AuthUser, settings: BasicStoreSettings) => {
483+
const elementOrganizations = element[RELATION_GRANTED_TO] ?? [];
484+
return isEntityOrganizationsAllowed(element.internal_id, elementOrganizations, user, settings);
485+
};
486+
487+
const isOrganizationUnrestrictedForEntityType = (entityType: string) => {
488+
const types = [entityType, ...getParentTypes(entityType)];
489+
if (STIX_ORGANIZATIONS_UNRESTRICTED.some((r) => types.includes(r))) {
490+
return true;
491+
}
492+
return false;
493+
};
426494
/**
427495
* Organization unrestricted mean that this element is visible whatever the organization the user belongs to.
428496
* @param element
429497
*/
430498
export const isOrganizationUnrestricted = (element: BasicStoreCommon) => {
431-
const types = [element.entity_type, ...getParentTypes(element.entity_type)];
432-
if (STIX_ORGANIZATIONS_UNRESTRICTED.some((r) => types.includes(r))) {
433-
return true;
434-
}
435-
return false;
499+
return isOrganizationUnrestrictedForEntityType(element.entity_type);
436500
};
437501

438502
export const isMarkingAllowed = (element: BasicStoreCommon, userAuthorizedMarkings: string[]) => {
@@ -469,13 +533,19 @@ export const userFilterStoreElements = async (context: AuthContext, user: AuthUs
469533
if (!isMarkingAllowed(element, authorizedMarkings)) {
470534
return false;
471535
}
472-
// 2. Check organizations
536+
// 2. check authorized members
537+
if (!hasAuthorizedMemberAccess(user, element)) {
538+
return false;
539+
}
540+
// 3. Check organizations
473541
// Allow unrestricted entities
474542
if (isOrganizationUnrestricted(element)) {
475543
return true;
476544
}
477545
// Check restricted elements
478-
return isOrganizationAllowed(element, user, settings);
546+
// either allowed by orga sharing or has authorized members access if authorized_members are defined (bypass orga sharing)
547+
return isOrganizationAllowed(element, user, settings)
548+
|| (element.authorized_members && element.authorized_members.length > 0 && hasAuthorizedMemberAccess(user, element));
479549
});
480550
};
481551
return telemetry(context, user, 'FILTERING store filter', {
@@ -503,52 +573,29 @@ export const isUserCanAccessStixElement = async (context: AuthContext, user: Aut
503573
return false;
504574
}
505575
}
506-
// 2. Check organizations
576+
const authorized_members = instance.extensions?.[STIX_EXT_OCTI]?.authorized_members ?? [];
577+
const authorizedMemberAllowed = hasAuthorizedMemberAccess(user, { authorized_members });
578+
// 2. check authorized members
579+
if (!authorizedMemberAllowed) {
580+
return false;
581+
}
582+
// 3. Check organizations
507583
// Allow unrestricted entities
508584
const entityType = instance.extensions?.[STIX_EXT_OCTI]?.type ?? generateInternalType(instance);
509-
const types = [entityType, ...getParentTypes(entityType)];
510-
if (STIX_ORGANIZATIONS_UNRESTRICTED.some((r) => types.includes(r))) {
585+
if (isOrganizationUnrestrictedForEntityType(entityType)) {
511586
return true;
512587
}
513588
// Check restricted elements
514589
const settings = await getEntityFromCache<BasicStoreSettings>(context, user, ENTITY_TYPE_SETTINGS);
515590
const elementOrganizations = instance.extensions?.[STIX_EXT_OCTI]?.granted_refs ?? [];
516-
const userOrganizations = user.organizations.map((o) => o.standard_id);
517-
// If platform organization is set
518-
if (settings.platform_organization) {
519-
// If user part of platform organization, is granted by default
520-
if (user.inside_platform_organization) {
521-
return true;
522-
}
523-
// If not, user is by design inside an organization
524-
// If element has no current sharing organization, it can be accessed (secure by default)
525-
// If element is shared, user must have a matching sharing organization
526-
return elementOrganizations.some((r) => userOrganizations.includes(r));
527-
}
528-
// If no platform organization is set, user can access
529-
return true;
591+
const organizationAllowed = isEntityOrganizationsAllowed(instance.id, elementOrganizations, user, settings, { useStandardId: true });
592+
// either allowed by organization or authorized members
593+
return organizationAllowed || (authorized_members.length > 0 && authorizedMemberAllowed);
530594
};
595+
// end region
531596

532597
// region member access
533598

534-
// returns all user member access ids : his id, his organizations ids (and parent organizations), his groups ids
535-
export const computeUserMemberAccessIds = (user: AuthUser) => {
536-
const memberAccessIds = [user.id];
537-
if (user.organizations) {
538-
const userOrganizationsIds = user.organizations.map((org) => org.internal_id);
539-
memberAccessIds.push(...userOrganizationsIds);
540-
}
541-
if (user.groups) {
542-
const userGroupsIds = user.groups.map((group) => group.internal_id);
543-
memberAccessIds.push(...userGroupsIds);
544-
}
545-
if (user.roles) {
546-
const userRolesIds = user.roles.map((role) => role.internal_id);
547-
memberAccessIds.push(...userRolesIds);
548-
}
549-
return memberAccessIds;
550-
};
551-
552599
// user access methods
553600
export const isDirectAdministrator = (user: AuthUser, element: any) => {
554601
const elementAccessIds = element.authorized_members
@@ -557,32 +604,6 @@ export const isDirectAdministrator = (user: AuthUser, element: any) => {
557604
const userMemberAccessIds = computeUserMemberAccessIds(user);
558605
return elementAccessIds.some((a: string) => userMemberAccessIds.includes(a));
559606
};
560-
export const getUserAccessRight = (user: AuthUser, element: any) => {
561-
if (!element.authorized_members || element.authorized_members.length === 0) { // no restricted user access on element
562-
return MEMBER_ACCESS_RIGHT_ADMIN;
563-
}
564-
const accessMembers = [...element.authorized_members];
565-
const userMemberAccessIds = computeUserMemberAccessIds(user);
566-
const foundAccessMembers = accessMembers.filter((u) => u.id === MEMBER_ACCESS_ALL || userMemberAccessIds.includes(u.id));
567-
// If user have extended capabilities, is an admin
568-
if ((element.authorized_authorities ?? []).some((c: string) => userMemberAccessIds.includes(c) || isUserHasCapability(user, c))) {
569-
return MEMBER_ACCESS_RIGHT_ADMIN;
570-
}
571-
// if user is bypass, user has admin access (needed for data management usage)
572-
if (isBypassUser(user)) {
573-
return MEMBER_ACCESS_RIGHT_ADMIN;
574-
}
575-
if (!foundAccessMembers.length) { // user has no access
576-
return null;
577-
}
578-
if (foundAccessMembers.some((m) => m.access_right === MEMBER_ACCESS_RIGHT_ADMIN)) {
579-
return MEMBER_ACCESS_RIGHT_ADMIN;
580-
}
581-
if (foundAccessMembers.some((m) => m.access_right === MEMBER_ACCESS_RIGHT_EDIT)) {
582-
return MEMBER_ACCESS_RIGHT_EDIT;
583-
}
584-
return MEMBER_ACCESS_RIGHT_VIEW;
585-
};
586607

587608
// ensure that user can access the element (operation: edit / delete / manage-access)
588609
export const validateUserAccessOperation = (user: AuthUser, element: any, operation: 'edit' | 'delete' | 'manage-access' | 'manage-authorities-access') => {

0 commit comments

Comments
 (0)