Skip to content

Commit

Permalink
[backend] fix capability check on feeds (#9573)
Browse files Browse the repository at this point in the history
  • Loading branch information
lndrtrbn authored Jan 14, 2025
1 parent bc9fc0f commit f7549fc
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 18 deletions.
9 changes: 2 additions & 7 deletions opencti-platform/opencti-graphql/src/http/httpRollingFeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import nconf from 'nconf';
import { authenticateUserFromRequest, TAXIIAPI } from '../domain/user';
import { basePath } from '../config/conf';
import { ForbiddenAccess } from '../config/errors';
import { BYPASS, executionContext, SYSTEM_USER } from '../utils/access';
import { executionContext, isUserHasCapability, SYSTEM_USER } from '../utils/access';
import { findById as findFeed } from '../domain/feed';
import type { AuthUser } from '../types/user';
import { listAllThings } from '../database/middleware';
import { minutesAgo } from '../utils/format';
import { isNotEmptyField } from '../database/utils';
Expand All @@ -26,10 +25,6 @@ const errorConverter = (e: any) => {
details,
};
};
const userHaveAccess = (user: AuthUser) => {
const capabilities = user.capabilities.map((c) => c.name);
return capabilities.includes(BYPASS) || capabilities.includes(TAXIIAPI);
};

const dataFormat = (separator: string, data: string) => {
if (data.includes(separator) || data.includes('"')) {
Expand Down Expand Up @@ -58,7 +53,7 @@ const initHttpRollingFeeds = (app: Express.Application) => {
// If feed is not public, we need to ensure the user access
if (!feed.feed_public) {
const userFeed = await findFeed(context, authUser, id);
if (!userHaveAccess(authUser) || !userFeed) {
if (!isUserHasCapability(authUser, TAXIIAPI) || !userFeed) {
throw ForbiddenAccess();
}
}
Expand Down
9 changes: 3 additions & 6 deletions opencti-platform/opencti-graphql/src/http/httpTaxii.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { basePath, getBaseUrl } from '../config/conf';
import { AuthRequired, error, ForbiddenAccess, UNSUPPORTED_ERROR, UnsupportedError } from '../config/errors';
import { STIX_EXT_OCTI } from '../types/stix-extensions';
import { findById, restAllCollections, restBuildCollection, restCollectionManifest, restCollectionStix } from '../domain/taxii';
import { BYPASS, executionContext, SYSTEM_USER } from '../utils/access';
import { executionContext, isUserHasCapability, SYSTEM_USER } from '../utils/access';
import { findById as findTaxiiCollection } from '../modules/ingestion/ingestion-taxii-collection-domain';
import { handleConfidenceToScoreTransformation, pushBundleToConnectorQueue } from '../manager/ingestionManager';
import { now } from '../utils/format';
Expand All @@ -35,18 +35,15 @@ const errorConverter = (e) => {
http_status: e.extensions.data?.http_status || 500,
};
};
const userHaveAccess = (user) => {
const capabilities = user.capabilities.map((c) => c.name);
return capabilities.includes(BYPASS) || capabilities.includes(TAXIIAPI);
};

const extractUserFromRequest = async (context, req, res) => {
// noinspection UnnecessaryLocalVariableJS
const user = await authenticateUserFromRequest(context, req, res);
if (!user) {
res.setHeader('WWW-Authenticate', 'Basic, Bearer');
throw AuthRequired();
}
if (!userHaveAccess(user)) throw ForbiddenAccess();
if (!isUserHasCapability(user, TAXIIAPI)) throw ForbiddenAccess();
return user;
};
const rebuildParamsForObject = (id, req) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ describe('Elasticsearch pagination', () => {
let data = await elPaginate(testContext, ADMIN_USER, READ_RELATIONSHIPS_INDICES, { includeAuthorities: true });
expect(data).not.toBeNull();
const groupByIndices = R.groupBy((e) => e.node._index, data.edges);
expect(groupByIndices[`${ES_INDEX_PREFIX}_internal_relationships-000001`].length).toEqual(106);
expect(groupByIndices[`${ES_INDEX_PREFIX}_internal_relationships-000001`].length).toEqual(107);
expect(groupByIndices[`${ES_INDEX_PREFIX}_stix_core_relationships-000001`].length).toEqual(24);
expect(groupByIndices[`${ES_INDEX_PREFIX}_stix_meta_relationships-000001`].length).toEqual(129);
expect(groupByIndices[`${ES_INDEX_PREFIX}_stix_sighting_relationships-000001`].length).toEqual(2);
Expand All @@ -692,14 +692,14 @@ describe('Elasticsearch pagination', () => {
expect(metaByEntityType['external-reference'].length).toEqual(7);
expect(metaByEntityType['object-marking'].length).toEqual(28);
expect(metaByEntityType['kill-chain-phase'].length).toEqual(3);
expect(data.edges.length).toEqual(261);
expect(data.edges.length).toEqual(262);
let filterBaseTypes = R.uniq(R.map((e) => e.node.base_type, data.edges));
expect(filterBaseTypes.length).toEqual(1);
expect(R.head(filterBaseTypes)).toEqual('RELATION');
// Same query with no pagination
data = await elPaginate(testContext, ADMIN_USER, READ_RELATIONSHIPS_INDICES, { connectionFormat: false });
expect(data).not.toBeNull();
expect(data.length).toEqual(261);
expect(data.length).toEqual(262);
filterBaseTypes = R.uniq(R.map((e) => e.base_type, data));
expect(filterBaseTypes.length).toEqual(1);
expect(R.head(filterBaseTypes)).toEqual('RELATION');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { expect, it, describe } from 'vitest';
import gql from 'graphql-tag';
import { editorQuery, queryAsAdmin } from '../../utils/testQuery';

describe('CSV Feed resolver standard behavior', () => {
let internalFeedId: string;

it('should create CSV Feed', async () => {
const CREATE_FEED = gql(`
mutation FeedAdd($input: FeedAddInput!) {
feedAdd(input: $input) {
id
name
feed_types
}
}
`);
const feed = await queryAsAdmin({
query: CREATE_FEED,
variables: {
input: {
name: 'List of created cities',
separator: ';',
rolling_time: 60,
include_header: true,
feed_types: ['City'],
feed_date_attribute: 'created_at',
feed_attributes: [{
attribute: 'A',
mappings: [{ type: 'City', attribute: 'name' }]
}]
}
},
});
expect(feed).not.toBeNull();
expect(feed.data?.feedAdd.name).toEqual('List of created cities');
internalFeedId = feed.data?.feedAdd.id;
});

it('should access feed if user has capa to manage feeds', async () => {
const QUERY_FEED = gql(`
query QueryFeed($id: String!) {
feed(id: $id) {
id
name
}
}
`);
const feed = await editorQuery({
query: QUERY_FEED,
variables: { id: internalFeedId }
});
expect(feed).not.toBeNull();
expect(feed.data?.feed.name).toEqual('List of created cities');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -851,7 +851,7 @@ describe('User has no settings capability and is organization admin query behavi
const editorUserQueryResult = await adminQuery({ query: READ_QUERY, variables: { id: userEditorId } });
expect(editorUserQueryResult).not.toBeNull();
expect(editorUserQueryResult.data.user).not.toBeNull();
expect(editorUserQueryResult.data.user.capabilities.length).toEqual(5);
expect(editorUserQueryResult.data.user.capabilities.length).toEqual(6);
const { capabilities } = editorUserQueryResult.data.user;
expect(capabilities.some((capa: Capability) => capa.name === VIRTUAL_ORGANIZATION_ADMIN)).toEqual(true);
});
Expand Down
7 changes: 6 additions & 1 deletion opencti-platform/opencti-graphql/tests/utils/testQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,12 @@ export const ROLE_EDITOR: Role = {
id: generateStandardId(ENTITY_TYPE_ROLE, { name: 'Access knowledge/exploration and edit/delete' }),
name: 'Access knowledge/exploration and edit/delete',
description: 'Knowledge/exploration edit/delete',
capabilities: ['KNOWLEDGE_KNUPDATE_KNDELETE', 'EXPLORE_EXUPDATE_EXDELETE', 'EXPLORE_EXUPDATE_PUBLISH']
capabilities: [
'KNOWLEDGE_KNUPDATE_KNDELETE',
'EXPLORE_EXUPDATE_EXDELETE',
'EXPLORE_EXUPDATE_PUBLISH',
'TAXIIAPI_SETCOLLECTIONS'
]
};
TESTING_ROLES.push(ROLE_EDITOR);

Expand Down

0 comments on commit f7549fc

Please sign in to comment.