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

Patch/acl #231

Merged
merged 5 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 4 additions & 1 deletion src/core/server/saved_objects/serialization/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class SavedObjectsSerializer {
*/
public rawToSavedObject(doc: SavedObjectsRawDoc): SavedObjectSanitizedDoc {
const { _id, _source, _seq_no, _primary_term } = doc;
const { type, namespace, namespaces, originId, workspaces } = _source;
const { type, namespace, namespaces, originId, workspaces, permissions } = _source;

const version =
_seq_no != null || _primary_term != null
Expand All @@ -86,6 +86,7 @@ export class SavedObjectsSerializer {
...(namespace && this.registry.isSingleNamespace(type) && { namespace }),
...(namespaces && this.registry.isMultiNamespace(type) && { namespaces }),
...(originId && { originId }),
...(permissions && { permissions }),
attributes: _source[type],
references: _source.references || [],
...(_source.migrationVersion && { migrationVersion: _source.migrationVersion }),
Expand Down Expand Up @@ -114,6 +115,7 @@ export class SavedObjectsSerializer {
version,
references,
workspaces,
permissions,
} = savedObj;
const source = {
[type]: attributes,
Expand All @@ -125,6 +127,7 @@ export class SavedObjectsSerializer {
...(migrationVersion && { migrationVersion }),
...(updated_at && { updated_at }),
...(workspaces && { workspaces }),
...(permissions && { permissions }),
};

return {
Expand Down
2 changes: 2 additions & 0 deletions src/core/server/saved_objects/serialization/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
* under the License.
*/

import { Permissions } from '../permission_control/acl';
import { SavedObjectsMigrationVersion, SavedObjectReference } from '../types';

/**
Expand Down Expand Up @@ -71,6 +72,7 @@ interface SavedObjectDoc<T = unknown> {
updated_at?: string;
originId?: string;
workspaces?: string[];
permissions?: Permissions;
}

interface Referencable {
Expand Down
178 changes: 162 additions & 16 deletions src/core/server/saved_objects/service/lib/repository.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ describe('SavedObjectsRepository', () => {
});

const getMockGetResponse = (
{ type, id, references, namespace: objectNamespace, originId, workspaces },
{ type, id, references, namespace: objectNamespace, originId, workspaces, permissions },
namespace
) => {
const namespaceId = objectNamespace === 'default' ? undefined : objectNamespace ?? namespace;
Expand All @@ -189,6 +189,7 @@ describe('SavedObjectsRepository', () => {
...(registry.isMultiNamespace(type) && { namespaces: [namespaceId ?? 'default'] }),
workspaces,
...(originId && { originId }),
...(permissions && { permissions }),
type,
[type]: { title: 'Testing' },
references,
Expand Down Expand Up @@ -451,24 +452,35 @@ describe('SavedObjectsRepository', () => {
};
const namespace = 'foo-namespace';
const workspace = 'foo-workspace';
const permissions = {
read: {
users: ['user1'],
},
write: {
groups: ['groups1'],
},
};

const getMockBulkCreateResponse = (objects, namespace) => {
return {
items: objects.map(({ type, id, originId, attributes, references, migrationVersion }) => ({
create: {
_id: `${namespace ? `${namespace}:` : ''}${type}:${id}`,
_source: {
[type]: attributes,
type,
namespace,
...(originId && { originId }),
references,
...mockTimestampFields,
migrationVersion: migrationVersion || { [type]: '1.1.1' },
items: objects.map(
({ type, id, originId, attributes, references, migrationVersion, permissions }) => ({
create: {
_id: `${namespace ? `${namespace}:` : ''}${type}:${id}`,
_source: {
[type]: attributes,
type,
namespace,
...(originId && { originId }),
...(permissions && { permissions }),
references,
...mockTimestampFields,
migrationVersion: migrationVersion || { [type]: '1.1.1' },
},
...mockVersionProps,
},
...mockVersionProps,
},
})),
})
),
};
};

Expand Down Expand Up @@ -750,6 +762,18 @@ describe('SavedObjectsRepository', () => {
expect.anything()
);
});

it(`accepts permissions property when providing permissions info`, async () => {
const objects = [obj1, obj2].map((obj) => ({ ...obj, permissions: permissions }));
await bulkCreateSuccess(objects);
const expected = expect.objectContaining({ permissions });
const body = [expect.any(Object), expected, expect.any(Object), expected];
expect(client.bulk).toHaveBeenCalledWith(
expect.objectContaining({ body }),
expect.anything()
);
client.bulk.mockClear();
});
});

describe('errors', () => {
Expand Down Expand Up @@ -1088,6 +1112,17 @@ describe('SavedObjectsRepository', () => {
);
expect(result.saved_objects[1].id).toEqual(obj2.id);
});

it(`includes permissions property if present`, async () => {
const objects = [obj1, obj2].map((obj) => ({ ...obj, permissions: permissions }));
const result = await bulkCreateSuccess(objects);
expect(result).toEqual({
saved_objects: [
expect.objectContaining({ permissions }),
expect.objectContaining({ permissions }),
],
});
});
});
});

Expand Down Expand Up @@ -1307,6 +1342,22 @@ describe('SavedObjectsRepository', () => {
],
});
});

it(`includes permissions property if present`, async () => {
const permissions = {
read: {
users: ['user1'],
},
write: {
groups: ['groups1'],
},
};
const obj = { id: 'three', type: MULTI_NAMESPACE_TYPE, permissions: permissions };
const result = await bulkGetSuccess([obj]);
expect(result).toEqual({
saved_objects: [expect.objectContaining({ permissions: permissions })],
});
});
});
});

Expand All @@ -1324,6 +1375,14 @@ describe('SavedObjectsRepository', () => {
const references = [{ name: 'ref_0', type: 'test', id: '1' }];
const originId = 'some-origin-id';
const namespace = 'foo-namespace';
const permissions = {
read: {
users: ['user1'],
},
write: {
groups: ['groups1'],
},
};

const getMockBulkUpdateResponse = (objects, options, includeOriginId) => ({
items: objects.map(({ type, id }) => ({
Expand Down Expand Up @@ -1584,6 +1643,20 @@ describe('SavedObjectsRepository', () => {
await bulkUpdateSuccess([{ ..._obj2, namespace }]);
expectClientCallArgsAction([_obj2], { method: 'update', getId, overrides }, 2);
});

it(`accepts permissions property when providing permissions info`, async () => {
const objects = [obj1, obj2].map((obj) => ({ ...obj, permissions: permissions }));
await bulkUpdateSuccess(objects);
const doc = {
doc: expect.objectContaining({ permissions }),
};
const body = [expect.any(Object), doc, expect.any(Object), doc];
expect(client.bulk).toHaveBeenCalledWith(
expect.objectContaining({ body }),
expect.anything()
);
client.bulk.mockClear();
});
});

describe('errors', () => {
Expand Down Expand Up @@ -1776,6 +1849,14 @@ describe('SavedObjectsRepository', () => {
],
});
});

it(`includes permissions property if present`, async () => {
const obj = { type: MULTI_NAMESPACE_TYPE, id: 'three', permissions: permissions };
const result = await bulkUpdateSuccess([obj1, obj], {}, true);
expect(result).toEqual({
saved_objects: [expect.anything(), expect.objectContaining({ permissions })],
});
});
});
});

Expand Down Expand Up @@ -1965,6 +2046,14 @@ describe('SavedObjectsRepository', () => {
id: '123',
},
];
const permissions = {
read: {
users: ['user1'],
},
write: {
groups: ['groups1'],
},
};

const createSuccess = async (type, attributes, options) => {
const result = await savedObjectsRepository.create(type, attributes, options);
Expand Down Expand Up @@ -2189,6 +2278,15 @@ describe('SavedObjectsRepository', () => {
body: expect.objectContaining({
workspaces: ['foo'],
}),
})
);
});

it(`accepts permissions property`, async () => {
await createSuccess(type, attributes, { id, permissions });
expect(client.create).toHaveBeenCalledWith(
expect.objectContaining({
body: expect.objectContaining({ permissions }),
}),
expect.anything()
);
Expand Down Expand Up @@ -2288,6 +2386,11 @@ describe('SavedObjectsRepository', () => {
expect(serializer.savedObjectToRaw).toHaveBeenLastCalledWith(migratedDoc);
});

it(`adds permissions to body when providing permissions info`, async () => {
await createSuccess(type, attributes, { id, permissions });
expectMigrationArgs({ permissions });
});

it(`adds namespace to body when providing namespace for single-namespace type`, async () => {
await createSuccess(type, attributes, { id, namespace });
expectMigrationArgs({ namespace });
Expand Down Expand Up @@ -2334,11 +2437,13 @@ describe('SavedObjectsRepository', () => {
namespace,
references,
originId,
permissions,
});
expect(result).toEqual({
type,
id,
originId,
permissions,
...mockTimestampFields,
version: mockVersion,
attributes,
Expand Down Expand Up @@ -3208,14 +3313,15 @@ describe('SavedObjectsRepository', () => {
const namespace = 'foo-namespace';
const originId = 'some-origin-id';

const getSuccess = async (type, id, options, includeOriginId) => {
const getSuccess = async (type, id, options, includeOriginId, permissions) => {
const response = getMockGetResponse(
{
type,
id,
// "includeOriginId" is not an option for the operation; however, if the existing saved object contains an originId attribute, the
// operation will return it in the result. This flag is just used for test purposes to modify the mock cluster call response.
...(includeOriginId && { originId }),
...(permissions && { permissions }),
},
options?.namespace
);
Expand Down Expand Up @@ -3366,6 +3472,21 @@ describe('SavedObjectsRepository', () => {
const result = await getSuccess(type, id, {}, true);
expect(result).toMatchObject({ originId });
});

it(`includes permissions property if present`, async () => {
const permissions = {
read: {
users: ['user1'],
},
write: {
groups: ['groups1'],
},
};
const result = await getSuccess(type, id, { namespace }, undefined, permissions);
expect(result).toMatchObject({
permissions: permissions,
});
});
});
});

Expand Down Expand Up @@ -3967,6 +4088,14 @@ describe('SavedObjectsRepository', () => {
},
];
const originId = 'some-origin-id';
const permissions = {
read: {
users: ['user1'],
},
write: {
groups: ['groups1'],
},
};

const updateSuccess = async (type, id, attributes, options, includeOriginId) => {
if (registry.isMultiNamespace(type)) {
Expand Down Expand Up @@ -4143,6 +4272,18 @@ describe('SavedObjectsRepository', () => {
expect.anything()
);
});

it(`accepts permissions when providing permissions info`, async () => {
await updateSuccess(type, id, attributes, { permissions });
const expected = expect.objectContaining({ permissions });
const body = {
doc: expected,
};
expect(client.update).toHaveBeenCalledWith(
expect.objectContaining({ body }),
expect.anything()
);
});
});

describe('errors', () => {
Expand Down Expand Up @@ -4237,6 +4378,11 @@ describe('SavedObjectsRepository', () => {
const result = await updateSuccess(type, id, attributes, {}, true);
expect(result).toMatchObject({ originId });
});

it(`includes permissions property if present`, async () => {
const result = await updateSuccess(type, id, attributes, { permissions });
expect(result).toMatchObject({ permissions });
});
});
});
});
Loading
Loading