Skip to content

Commit

Permalink
add permission control service for saved objects and workspace saved …
Browse files Browse the repository at this point in the history
…objects client wrapper (opensearch-project#230)

* feat: add basic workspace saved objects client wrapper

Signed-off-by: Lin Wang <wonglam@amazon.com>

* feat: add unit test (#2)

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: update client wrapper

Signed-off-by: tygao <tygao@amazon.com>

* feat: init permission control in workspace plugin

Signed-off-by: Lin Wang <wonglam@amazon.com>

* Support disable permission check on workspace (opensearch-project#228)

* support disable permission check for workspace

Signed-off-by: Hailong Cui <ihailong@amazon.com>

* fix typos

Signed-off-by: Hailong Cui <ihailong@amazon.com>

---------

Signed-off-by: Hailong Cui <ihailong@amazon.com>

* feat: add ACLSearchParams consumer in repository (opensearch-project#3)

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* fix: ACLSearchParams missing in search dsl

Signed-off-by: Lin Wang <wonglam@amazon.com>

* test: add integration test for workspace saved objects client wrapper

Signed-off-by: Lin Wang <wonglam@amazon.com>

* style: add empty line under license

Signed-off-by: Lin Wang <wonglam@amazon.com>

* test: enable workspace permission control for integration tests

Signed-off-by: Lin Wang <wonglam@amazon.com>

* feat: add workspace into includeHiddenTypes (opensearch-project#249)

* feat: add workspace into includeHiddenTypes of client wrapper and permission control client

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* fix: hiddenType side effect

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

---------

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* fix workspace client wrapper integration tests

Signed-off-by: Lin Wang <wonglam@amazon.com>

* add permissions fields to workspace CRUD APIs

Signed-off-by: Lin Wang <wonglam@amazon.com>

* Move WorkspacePermissionMode inside workspace plugin

Signed-off-by: Lin Wang <wonglam@amazon.com>

* Address pr comments

Signed-off-by: Lin Wang <wonglam@amazon.com>

* Remove ACLSearchParams in public SavedObjectsFindOptions

Signed-off-by: Lin Wang <wonglam@amazon.com>

* Remove lodash and Add default permissionModes

Signed-off-by: Lin Wang <wonglam@amazon.com>

* feat: address concerns on ensureRawRequest (opensearch-project#4)

* feat: address concerns on ensureRawRequest

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: add check for empty array

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: make find api backward compatible

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: remove useless code

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

---------

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* Update annotations and  error

Signed-off-by: Lin Wang <wonglam@amazon.com>

* Add unit tests for worksapce saved objects client wrapper

Signed-off-by: Lin Wang <wonglam@amazon.com>

* Remove getPrincipalsOfObjects in permission

Signed-off-by: Lin Wang <wonglam@amazon.com>

* Fix permissionEnabled flag missed in workspace plugin setup test

Signed-off-by: Lin Wang <wonglam@amazon.com>

* Change back to Not Authorized error

Signed-off-by: Lin Wang <wonglam@amazon.com>

* Fix unit tests for query_params and plugin setup

Signed-off-by: Lin Wang <wonglam@amazon.com>

* Fix unittests in workspace server utils

Signed-off-by: Lin Wang <wonglam@amazon.com>

* feat: add workspacesSearchOperators to decouple ACLSearchParams

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: update test cases

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: optimize test cases

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: optimize comment

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: omit defaultSearchOperator in public savedobjetcs client

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

* feat: omit workspacesSearchOperator field

Signed-off-by: SuZhou-Joe <suzhou@amazon.com>

---------

Signed-off-by: Lin Wang <wonglam@amazon.com>
Signed-off-by: SuZhou-Joe <suzhou@amazon.com>
Signed-off-by: tygao <tygao@amazon.com>
Signed-off-by: Hailong Cui <ihailong@amazon.com>
Co-authored-by: Lin Wang <wonglam@amazon.com>
Co-authored-by: SuZhou-Joe <suzhou@amazon.com>
Co-authored-by: Hailong Cui <ihailong@amazon.com>
  • Loading branch information
4 people authored Mar 6, 2024
1 parent 3ddbf99 commit 370dbc6
Show file tree
Hide file tree
Showing 28 changed files with 2,615 additions and 34 deletions.
5 changes: 4 additions & 1 deletion config/opensearch_dashboards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,7 @@
# vis_augmenter.pluginAugmentationEnabled: true

# Set the value to true to enable workspace feature
# workspace.enabled: false
# workspace.enabled: false
# Set the value to false to disable permission check on workspace
# Permission check depends on OpenSearch Dashboards has authentication enabled, set it to false if no authentication is configured
# workspace.permission.enabled: true
1 change: 1 addition & 0 deletions src/core/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export {
StringValidationRegex,
StringValidationRegexString,
WorkspaceObject,
WorkspaceAttribute,
} from '../types';

export {
Expand Down
25 changes: 13 additions & 12 deletions src/core/public/saved_objects/saved_objects_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ import { HttpFetchOptions, HttpSetup } from '../http';

type SavedObjectsFindOptions = Omit<
SavedObjectFindOptionsServer,
'sortOrder' | 'rootSearchFields' | 'typeToNamespacesMap'
| 'sortOrder'
| 'rootSearchFields'
| 'typeToNamespacesMap'
| 'ACLSearchParams'
| 'workspacesSearchOperator'
>;

type PromiseType<T extends Promise<any>> = T extends Promise<infer U> ? U : never;
Expand Down Expand Up @@ -392,17 +396,14 @@ export class SavedObjectsClient {
finalWorkspaces = Array.from(new Set([currentWorkspaceId]));
}

const renamedQuery = renameKeys<Omit<SavedObjectsFindOptions, 'ACLSearchParams'>, any>(
renameMap,
{
...options,
...(finalWorkspaces
? {
workspaces: finalWorkspaces,
}
: {}),
}
);
const renamedQuery = renameKeys<SavedObjectsFindOptions, any>(renameMap, {
...options,
...(finalWorkspaces
? {
workspaces: finalWorkspaces,
}
: {}),
});
const query = pick.apply(null, [renamedQuery, ...Object.values<string>(renameMap)]) as Partial<
Record<string, any>
>;
Expand Down
5 changes: 5 additions & 0 deletions src/core/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,11 @@ export {
importSavedObjectsFromStream,
resolveSavedObjectsImportErrors,
SavedObjectsDeleteByWorkspaceOptions,
ACL,
Principals,
TransformedPermission,
PrincipalType,
Permissions,
} from './saved_objects';

export {
Expand Down
8 changes: 8 additions & 0 deletions src/core/server/saved_objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,11 @@ export {

export { savedObjectsConfig, savedObjectsMigrationConfig } from './saved_objects_config';
export { SavedObjectTypeRegistry, ISavedObjectTypeRegistry } from './saved_objects_type_registry';

export {
Permissions,
ACL,
Principals,
TransformedPermission,
PrincipalType,
} from './permission_control/acl';
4 changes: 4 additions & 0 deletions src/core/server/saved_objects/service/lib/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,8 @@ export class SavedObjectsRepository {
filter,
preference,
workspaces,
workspacesSearchOperator,
ACLSearchParams,
} = options;

if (!type && !typeToNamespacesMap) {
Expand Down Expand Up @@ -897,6 +899,8 @@ export class SavedObjectsRepository {
hasReference,
kueryNode,
workspaces,
workspacesSearchOperator,
ACLSearchParams,
}),
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,131 @@ describe('#getQueryParams', () => {
});
});
});

describe('when using ACLSearchParams search', () => {
it('no ACLSearchParams provided', () => {
const result: Result = getQueryParams({
registry,
ACLSearchParams: {},
});
expect(result.query.bool.filter[1]).toEqual(undefined);
});

it('workspacesSearchOperator prvided as "OR"', () => {
const result: Result = getQueryParams({
registry,
workspaces: ['foo'],
workspacesSearchOperator: 'OR',
});
expect(result.query.bool.filter[1]).toEqual({
bool: {
should: [
{
bool: {
must_not: [
{
exists: {
field: 'workspaces',
},
},
{
exists: {
field: 'permissions',
},
},
],
},
},
{
bool: {
minimum_should_match: 1,
should: [
{
bool: {
must: [
{
term: {
workspaces: 'foo',
},
},
],
},
},
],
},
},
],
},
});
});

it('principals and permissionModes provided in ACLSearchParams', () => {
const result: Result = getQueryParams({
registry,
ACLSearchParams: {
principals: {
users: ['user-foo'],
groups: ['group-foo'],
},
permissionModes: ['read'],
},
});
expect(result.query.bool.filter[1]).toEqual({
bool: {
should: [
{
bool: {
must_not: [
{
exists: {
field: 'workspaces',
},
},
{
exists: {
field: 'permissions',
},
},
],
},
},
{
bool: {
filter: [
{
bool: {
should: [
{
terms: {
'permissions.read.users': ['user-foo'],
},
},
{
term: {
'permissions.read.users': '*',
},
},
{
terms: {
'permissions.read.groups': ['group-foo'],
},
},
{
term: {
'permissions.read.groups': '*',
},
},
],
},
},
],
},
},
],
},
});
});
});
});

describe('namespaces property', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type KueryNode = any;

import { ISavedObjectTypeRegistry } from '../../../saved_objects_type_registry';
import { ALL_NAMESPACES_STRING, DEFAULT_NAMESPACE_STRING } from '../utils';
import { SavedObjectsFindOptions } from '../../../types';
import { ACL } from '../../../permission_control/acl';

/**
* Gets the types based on the type. Uses mappings to support
Expand Down Expand Up @@ -166,6 +168,8 @@ interface QueryParams {
hasReference?: HasReferenceQueryParams;
kueryNode?: KueryNode;
workspaces?: string[];
workspacesSearchOperator?: 'AND' | 'OR';
ACLSearchParams?: SavedObjectsFindOptions['ACLSearchParams'];
}

export function getClauseForReference(reference: HasReferenceQueryParams) {
Expand Down Expand Up @@ -223,6 +227,8 @@ export function getQueryParams({
hasReference,
kueryNode,
workspaces,
workspacesSearchOperator = 'AND',
ACLSearchParams,
}: QueryParams) {
const types = getTypes(
registry,
Expand All @@ -247,17 +253,6 @@ export function getQueryParams({
],
};

if (workspaces) {
bool.filter.push({
bool: {
should: workspaces.map((workspace) => {
return getClauseForWorkspace(workspace);
}),
minimum_should_match: 1,
},
});
}

if (search) {
const useMatchPhrasePrefix = shouldUseMatchPhrasePrefix(search);
const simpleQueryStringClause = getSimpleQueryStringClause({
Expand All @@ -279,6 +274,69 @@ export function getQueryParams({
}
}

const ACLSearchParamsShouldClause: any = [];

if (ACLSearchParams) {
if (ACLSearchParams.permissionModes?.length && ACLSearchParams.principals) {
const permissionDSL = ACL.generateGetPermittedSavedObjectsQueryDSL(
ACLSearchParams.permissionModes,
ACLSearchParams.principals
);
ACLSearchParamsShouldClause.push(permissionDSL.query);
}
}

if (workspaces?.length) {
if (workspacesSearchOperator === 'OR') {
ACLSearchParamsShouldClause.push({
bool: {
should: workspaces.map((workspace) => {
return getClauseForWorkspace(workspace);
}),
minimum_should_match: 1,
},
});
} else {
bool.filter.push({
bool: {
should: workspaces.map((workspace) => {
return getClauseForWorkspace(workspace);
}),
minimum_should_match: 1,
},
});
}
}

if (ACLSearchParamsShouldClause.length) {
bool.filter.push({
bool: {
should: [
/**
* Return those objects without workspaces field and permissions field to keep find API backward compatible
*/
{
bool: {
must_not: [
{
exists: {
field: 'workspaces',
},
},
{
exists: {
field: 'permissions',
},
},
],
},
},
...ACLSearchParamsShouldClause,
],
},
});
}

return { query: { bool } };
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { IndexMapping } from '../../../mappings';
import { getQueryParams } from './query_params';
import { getSortingParams } from './sorting_params';
import { ISavedObjectTypeRegistry } from '../../../saved_objects_type_registry';
import { SavedObjectsFindOptions } from '../../../types';

type KueryNode = any;

Expand All @@ -53,6 +54,8 @@ interface GetSearchDslOptions {
};
kueryNode?: KueryNode;
workspaces?: string[];
workspacesSearchOperator?: 'AND' | 'OR';
ACLSearchParams?: SavedObjectsFindOptions['ACLSearchParams'];
}

export function getSearchDsl(
Expand All @@ -73,6 +76,8 @@ export function getSearchDsl(
hasReference,
kueryNode,
workspaces,
workspacesSearchOperator,
ACLSearchParams,
} = options;

if (!type) {
Expand All @@ -96,6 +101,8 @@ export function getSearchDsl(
hasReference,
kueryNode,
workspaces,
workspacesSearchOperator,
ACLSearchParams,
}),
...getSortingParams(mappings, type, sortField, sortOrder),
};
Expand Down
Loading

0 comments on commit 370dbc6

Please sign in to comment.