Skip to content

Commit 195ed9d

Browse files
SuZhou-Joewanglam
authored andcommitted
feat: address concerns on ensureRawRequest (#4)
* feat: address concerns on ensureRawRequest Signed-off-by: SuZhou-Joe <[email protected]> * feat: add check for empty array Signed-off-by: SuZhou-Joe <[email protected]> * feat: make find api backward compatible Signed-off-by: SuZhou-Joe <[email protected]> * feat: remove useless code Signed-off-by: SuZhou-Joe <[email protected]> --------- Signed-off-by: SuZhou-Joe <[email protected]>
1 parent 04f8602 commit 195ed9d

File tree

9 files changed

+85
-42
lines changed

9 files changed

+85
-42
lines changed

src/core/server/http/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ export {
7474
RouteValidationResultFactory,
7575
DestructiveRouteMethod,
7676
SafeRouteMethod,
77-
ensureRawRequest,
7877
} from './router';
7978
export { BasePathProxyServer } from './base_path_proxy_server';
8079
export { OnPreRoutingHandler, OnPreRoutingToolkit } from './lifecycle/on_pre_routing';

src/core/server/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,6 @@ export {
220220
SessionStorageFactory,
221221
DestructiveRouteMethod,
222222
SafeRouteMethod,
223-
ensureRawRequest,
224223
} from './http';
225224

226225
export {

src/core/server/saved_objects/service/lib/search_dsl/query_params.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,15 +287,15 @@ export function getQueryParams({
287287

288288
if (ACLSearchParams) {
289289
const shouldClause: any = [];
290-
if (ACLSearchParams.permissionModes && ACLSearchParams.principals) {
290+
if (ACLSearchParams.permissionModes?.length && ACLSearchParams.principals) {
291291
const permissionDSL = ACL.generateGetPermittedSavedObjectsQueryDSL(
292292
ACLSearchParams.permissionModes,
293293
ACLSearchParams.principals
294294
);
295295
shouldClause.push(permissionDSL.query);
296296
}
297297

298-
if (ACLSearchParams.workspaces) {
298+
if (ACLSearchParams.workspaces?.length) {
299299
shouldClause.push({
300300
terms: {
301301
workspaces: ACLSearchParams.workspaces,
@@ -306,7 +306,28 @@ export function getQueryParams({
306306
if (shouldClause.length) {
307307
bool.filter.push({
308308
bool: {
309-
should: shouldClause,
309+
should: [
310+
/**
311+
* Return those objects without workspaces field and permissions field to keep find find API backward compatible
312+
*/
313+
{
314+
bool: {
315+
must_not: [
316+
{
317+
exists: {
318+
field: 'workspaces',
319+
},
320+
},
321+
{
322+
exists: {
323+
field: 'permissions',
324+
},
325+
},
326+
],
327+
},
328+
},
329+
...shouldClause,
330+
],
310331
},
311332
});
312333
}

src/plugins/workspace/server/permission_control/client.test.ts

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,18 @@
55

66
import { loggerMock } from '@osd/logging/target/mocks';
77
import { SavedObjectsPermissionControl } from './client';
8-
import { httpServerMock, savedObjectsClientMock } from '../../../../core/server/mocks';
8+
import {
9+
httpServerMock,
10+
httpServiceMock,
11+
savedObjectsClientMock,
12+
} from '../../../../core/server/mocks';
13+
import * as utilsExports from '../utils';
914

10-
describe('workspace utils', () => {
15+
describe('PermissionControl', () => {
16+
jest.spyOn(utilsExports, 'getPrincipalsFromRequest').mockImplementation(() => ({
17+
users: ['bar'],
18+
}));
19+
const mockAuth = httpServiceMock.createAuth();
1120
it('should return principals when calling getPrincipalsOfObjects', async () => {
1221
const permissionControlClient = new SavedObjectsPermissionControl(loggerMock.create());
1322
const getScopedClient = jest.fn();
@@ -27,7 +36,7 @@ describe('workspace utils', () => {
2736
});
2837
return clientMock;
2938
});
30-
permissionControlClient.setup(getScopedClient);
39+
permissionControlClient.setup(getScopedClient, mockAuth);
3140
const result = await permissionControlClient.getPrincipalsOfObjects(
3241
httpServerMock.createOpenSearchDashboardsRequest(),
3342
[]
@@ -50,7 +59,7 @@ describe('workspace utils', () => {
5059
getScopedClient.mockImplementation((request) => {
5160
return clientMock;
5261
});
53-
permissionControlClient.setup(getScopedClient);
62+
permissionControlClient.setup(getScopedClient, mockAuth);
5463
clientMock.bulkGet.mockResolvedValue({
5564
saved_objects: [],
5665
});
@@ -69,7 +78,7 @@ describe('workspace utils', () => {
6978
getScopedClient.mockImplementation((request) => {
7079
return clientMock;
7180
});
72-
permissionControlClient.setup(getScopedClient);
81+
permissionControlClient.setup(getScopedClient, mockAuth);
7382

7483
clientMock.bulkGet.mockResolvedValue({
7584
saved_objects: [
@@ -102,7 +111,7 @@ describe('workspace utils', () => {
102111
getScopedClient.mockImplementation((request) => {
103112
return clientMock;
104113
});
105-
permissionControlClient.setup(getScopedClient);
114+
permissionControlClient.setup(getScopedClient, mockAuth);
106115

107116
clientMock.bulkGet.mockResolvedValue({
108117
saved_objects: [
@@ -126,19 +135,23 @@ describe('workspace utils', () => {
126135
],
127136
});
128137
const batchValidateResult = await permissionControlClient.batchValidate(
129-
httpServerMock.createOpenSearchDashboardsRequest({
130-
auth: {
131-
credentials: {
132-
authInfo: {
133-
user_name: 'bar',
134-
},
135-
},
136-
} as any,
137-
}),
138+
httpServerMock.createOpenSearchDashboardsRequest(),
138139
[],
139140
['read']
140141
);
141142
expect(batchValidateResult.success).toEqual(true);
142143
expect(batchValidateResult.result).toEqual(true);
143144
});
145+
146+
describe('getPrincipalsFromRequest', () => {
147+
const permissionControlClient = new SavedObjectsPermissionControl(loggerMock.create());
148+
const getScopedClient = jest.fn();
149+
permissionControlClient.setup(getScopedClient, mockAuth);
150+
151+
it('should return normally when calling getPrincipalsFromRequest', () => {
152+
const mockRequest = httpServerMock.createOpenSearchDashboardsRequest();
153+
const result = permissionControlClient.getPrincipalsFromRequest(mockRequest);
154+
expect(result.users).toEqual(['bar']);
155+
});
156+
});
144157
});

src/plugins/workspace/server/permission_control/client.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
SavedObject,
1616
WORKSPACE_TYPE,
1717
Permissions,
18+
HttpAuth,
1819
} from '../../../../core/server';
1920
import { WORKSPACE_SAVED_OBJECTS_CLIENT_WRAPPER_ID } from '../../common/constants';
2021
import { getPrincipalsFromRequest } from '../utils';
@@ -29,6 +30,7 @@ export type SavedObjectsPermissionModes = string[];
2930
export class SavedObjectsPermissionControl {
3031
private readonly logger: Logger;
3132
private _getScopedClient?: SavedObjectsServiceStart['getScopedClient'];
33+
private auth?: HttpAuth;
3234
private getScopedClient(request: OpenSearchDashboardsRequest) {
3335
return this._getScopedClient?.(request, {
3436
excludedWrappers: [WORKSPACE_SAVED_OBJECTS_CLIENT_WRAPPER_ID],
@@ -46,8 +48,9 @@ export class SavedObjectsPermissionControl {
4648
) {
4749
return (await this.getScopedClient?.(request)?.bulkGet(savedObjects))?.saved_objects || [];
4850
}
49-
public async setup(getScopedClient: SavedObjectsServiceStart['getScopedClient']) {
51+
public async setup(getScopedClient: SavedObjectsServiceStart['getScopedClient'], auth: HttpAuth) {
5052
this._getScopedClient = getScopedClient;
53+
this.auth = auth;
5154
}
5255
public async validate(
5356
request: OpenSearchDashboardsRequest,
@@ -76,6 +79,10 @@ export class SavedObjectsPermissionControl {
7679
);
7780
}
7881

82+
public getPrincipalsFromRequest(request: OpenSearchDashboardsRequest) {
83+
return getPrincipalsFromRequest(request, this.auth);
84+
}
85+
7986
public validateSavedObjectsACL(
8087
savedObjects: Array<Pick<SavedObject<unknown>, 'id' | 'type' | 'workspaces' | 'permissions'>>,
8188
principals: Principals,
@@ -137,7 +144,7 @@ export class SavedObjectsPermissionControl {
137144
};
138145
}
139146

140-
const principals = getPrincipalsFromRequest(request);
147+
const principals = this.getPrincipalsFromRequest(request);
141148
const deniedObjects: Array<
142149
Pick<SavedObjectsBulkGetObject, 'id' | 'type'> & {
143150
workspaces?: string[];

src/plugins/workspace/server/plugin.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export class WorkspacePlugin implements Plugin<{}, {}> {
9595
http: core.http,
9696
logger: this.logger,
9797
client: this.client as IWorkspaceClientImpl,
98+
permissionControlClient: this.permissionControl,
9899
});
99100

100101
core.capabilities.registerProvider(() => ({
@@ -111,7 +112,7 @@ export class WorkspacePlugin implements Plugin<{}, {}> {
111112

112113
public start(core: CoreStart) {
113114
this.logger.debug('Starting Workspace service');
114-
this.permissionControl?.setup(core.savedObjects.getScopedClient);
115+
this.permissionControl?.setup(core.savedObjects.getScopedClient, core.http.auth);
115116
this.client?.setSavedObjects(core.savedObjects);
116117
this.workspaceConflictControl?.setSerializer(core.savedObjects.createSerializer());
117118
this.workspaceSavedObjectsClientWrapper?.setScopedClient(core.savedObjects.getScopedClient);

src/plugins/workspace/server/routes/index.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
*/
55

66
import { schema } from '@osd/config-schema';
7-
import { CoreSetup, Logger, ensureRawRequest } from '../../../../core/server';
7+
import { CoreSetup, Logger } from '../../../../core/server';
88
import { WorkspacePermissionMode } from '../../common/constants';
9-
import { AuthInfo, IWorkspaceClientImpl, WorkspacePermissionItem } from '../types';
9+
import { IWorkspaceClientImpl, WorkspacePermissionItem } from '../types';
10+
import { SavedObjectsPermissionControlContract } from '../permission_control/client';
1011

1112
const WORKSPACES_API_BASE_URL = '/api/workspaces';
1213

@@ -44,10 +45,12 @@ export function registerRoutes({
4445
client,
4546
logger,
4647
http,
48+
permissionControlClient,
4749
}: {
4850
client: IWorkspaceClientImpl;
4951
logger: Logger;
5052
http: CoreSetup['http'];
53+
permissionControlClient?: SavedObjectsPermissionControlContract;
5154
}) {
5255
const router = http.createRouter();
5356
router.post(
@@ -124,8 +127,7 @@ export function registerRoutes({
124127
},
125128
router.handleLegacyErrors(async (context, req, res) => {
126129
const { attributes, permissions: permissionsInRequest } = req.body;
127-
const rawRequest = ensureRawRequest(req);
128-
const authInfo = rawRequest?.auth?.credentials?.authInfo as AuthInfo | null;
130+
const authInfo = permissionControlClient?.getPrincipalsFromRequest(req);
129131
let permissions: WorkspacePermissionItem[] = [];
130132
if (permissionsInRequest) {
131133
permissions = Array.isArray(permissionsInRequest)
@@ -134,10 +136,10 @@ export function registerRoutes({
134136
}
135137

136138
// Assign workspace owner to current user
137-
if (!!authInfo?.user_name) {
139+
if (!!authInfo?.users?.length) {
138140
permissions.push({
139141
type: 'user',
140-
userId: authInfo.user_name,
142+
userId: authInfo.users[0],
141143
modes: [WorkspacePermissionMode.LibraryWrite, WorkspacePermissionMode.Write],
142144
});
143145
}

src/plugins/workspace/server/saved_objects/workspace_saved_objects_client_wrapper.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import {
2828
SavedObjectsClientContract,
2929
} from '../../../../core/server';
3030
import { SavedObjectsPermissionControlContract } from '../permission_control/client';
31-
import { getPrincipalsFromRequest } from '../utils';
3231
import {
3332
WORKSPACE_SAVED_OBJECTS_CLIENT_WRAPPER_ID,
3433
WorkspacePermissionMode,
@@ -190,7 +189,7 @@ export class WorkspaceSavedObjectsClientWrapper {
190189
if (savedObject.permissions) {
191190
hasPermission = await this.permissionControl.validateSavedObjectsACL(
192191
[savedObject],
193-
getPrincipalsFromRequest(request),
192+
this.permissionControl.getPrincipalsFromRequest(request),
194193
objectPermissionModes
195194
);
196195
}
@@ -422,7 +421,7 @@ export class WorkspaceSavedObjectsClientWrapper {
422421
const findWithWorkspacePermissionControl = async <T = unknown>(
423422
options: SavedObjectsFindOptions
424423
) => {
425-
const principals = getPrincipalsFromRequest(wrapperOptions.request);
424+
const principals = this.permissionControl.getPrincipalsFromRequest(wrapperOptions.request);
426425
if (!options.ACLSearchParams) {
427426
options.ACLSearchParams = {};
428427
}
@@ -497,7 +496,6 @@ export class WorkspaceSavedObjectsClientWrapper {
497496
* Select all the docs that
498497
* 1. ACL matches read / write / user passed permission OR
499498
* 2. workspaces matches library_read or library_write OR
500-
* 3. Advanced settings
501499
*/
502500
options.workspaces = undefined;
503501
options.ACLSearchParams.workspaces = permittedWorkspaceIds;

src/plugins/workspace/server/utils.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
import crypto from 'crypto';
77
import {
8-
ensureRawRequest,
8+
AuthStatus,
9+
HttpAuth,
910
OpenSearchDashboardsRequest,
1011
Principals,
1112
PrincipalType,
@@ -19,25 +20,27 @@ export const generateRandomId = (size: number) => {
1920
return crypto.randomBytes(size).toString('base64url').slice(0, size);
2021
};
2122

22-
export const getPrincipalsFromRequest = (request: OpenSearchDashboardsRequest): Principals => {
23-
const rawRequest = ensureRawRequest(request);
24-
const authInfo = rawRequest?.auth?.credentials?.authInfo as AuthInfo | null;
23+
export const getPrincipalsFromRequest = (
24+
request: OpenSearchDashboardsRequest,
25+
auth?: HttpAuth
26+
): Principals => {
2527
const payload: Principals = {};
26-
if (!authInfo) {
28+
const authInfoResp = auth?.get(request);
29+
if (authInfoResp?.status === AuthStatus.unknown) {
2730
/**
2831
* Login user have access to all the workspaces when no authentication is presented.
29-
* The logic will be used when users create workspaces with authentication enabled but turn off authentication for any reason.
3032
*/
3133
return payload;
3234
}
33-
if (!authInfo?.backend_roles?.length && !authInfo.user_name) {
35+
36+
if (authInfoResp?.status === AuthStatus.unauthenticated) {
3437
/**
35-
* It means OSD can not recognize who the user is even if authentication is enabled,
36-
* use a fake user that won't be granted permission explicitly.
38+
* use a fake user that won't be granted permission explicitly when authenticated error.
3739
*/
3840
payload[PrincipalType.Users] = [`_user_fake_${Date.now()}_`];
3941
return payload;
4042
}
43+
const authInfo = authInfoResp?.state as AuthInfo | null;
4144
if (authInfo?.backend_roles) {
4245
payload[PrincipalType.Groups] = authInfo.backend_roles;
4346
}

0 commit comments

Comments
 (0)