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] fix capability check on feeds (#9573) #9574

Merged
merged 4 commits into from
Jan 14, 2025
Merged
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
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 { 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';

Check warning on line 8 in opencti-platform/opencti-graphql/src/http/httpRollingFeed.ts

View check run for this annotation

Codecov / codecov/patch

opencti-platform/opencti-graphql/src/http/httpRollingFeed.ts#L8

Added line #L8 was not covered by tests
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 @@
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 @@
// 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) {

Check warning on line 56 in opencti-platform/opencti-graphql/src/http/httpRollingFeed.ts

View check run for this annotation

Codecov / codecov/patch

opencti-platform/opencti-graphql/src/http/httpRollingFeed.ts#L56

Added line #L56 was not covered by tests
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 { 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';

Check warning on line 14 in opencti-platform/opencti-graphql/src/http/httpTaxii.js

View check run for this annotation

Codecov / codecov/patch

opencti-platform/opencti-graphql/src/http/httpTaxii.js#L14

Added line #L14 was not covered by tests
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 @@
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);
};

Check warning on line 38 in opencti-platform/opencti-graphql/src/http/httpTaxii.js

View check run for this annotation

Codecov / codecov/patch

opencti-platform/opencti-graphql/src/http/httpTaxii.js#L38

Added line #L38 was not covered by tests
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();

Check warning on line 46 in opencti-platform/opencti-graphql/src/http/httpTaxii.js

View check run for this annotation

Codecov / codecov/patch

opencti-platform/opencti-graphql/src/http/httpTaxii.js#L46

Added line #L46 was not covered by tests
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
Loading