From a9caae9a5a4c73194229afbaab56bc72188d920d Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 10:48:11 -0400 Subject: [PATCH] Fix the bug of capabilities request not supporting carrying authinfo (#2014) (#2017) * capabilities api support authinfo Signed-off-by: yubonluo * optimize the annotation Signed-off-by: yubonluo * optimize the code Signed-off-by: yubonluo --------- Signed-off-by: yubonluo (cherry picked from commit 293490dce6f0d769d8f36b0d5fd93b99fe176d51) Co-authored-by: yuboluo <15242088755@163.com> --- server/auth/types/authentication_type.test.ts | 54 +++++++++++++++++++ server/auth/types/authentication_type.ts | 19 +++++-- 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/server/auth/types/authentication_type.test.ts b/server/auth/types/authentication_type.test.ts index fdf0397cf..0274251dc 100644 --- a/server/auth/types/authentication_type.test.ts +++ b/server/auth/types/authentication_type.test.ts @@ -111,3 +111,57 @@ describe('test tenant header', () => { expect(result.requestHeaders.securitytenant).toEqual('dummy_tenant'); }); }); + +describe('test capabilities request authinfo', () => { + const config = { + auth: { + unauthenticated_routes: [] as string[], + }, + session: { + keepalive: false, + }, + } as SecurityPluginConfigType; + const sessionStorageFactory = { + asScoped: jest.fn(() => { + return { + clear: jest.fn(), + get: jest.fn().mockResolvedValue({}), + }; + }), + }; + const router = jest.fn(); + const esClient = { + asScoped: jest.fn().mockImplementation(() => { + return { + callAsCurrentUser: jest.fn().mockImplementation(() => { + return { username: 'capabilities-username' }; + }), + }; + }), + }; + const coreSetup = jest.fn(); + const logger = { + error: jest.fn(), + }; + + const dummyAuthType = new DummyAuthType( + config, + sessionStorageFactory, + router, + esClient, + coreSetup, + logger + ); + + it(`Capabilities API includes authinfo`, async () => { + const request = httpServerMock.createOpenSearchDashboardsRequest({ + path: '/api/core/capabilities', + }); + const response = jest.fn(); + const toolkit = { + authenticated: jest.fn((value) => value), + }; + const result = await dummyAuthType.authHandler(request, response, toolkit); + expect(result.state.authInfo.username).toEqual('capabilities-username'); + }); +}); diff --git a/server/auth/types/authentication_type.ts b/server/auth/types/authentication_type.ts index 6dd3db59d..783bdd142 100755 --- a/server/auth/types/authentication_type.ts +++ b/server/auth/types/authentication_type.ts @@ -73,10 +73,8 @@ export interface OpenSearchDashboardsAuthState { } export abstract class AuthenticationType implements IAuthenticationType { - protected static readonly ROUTES_TO_IGNORE: string[] = [ - '/api/core/capabilities', // FIXME: need to figure out how to bypass this API call - '/app/login', - ]; + protected static readonly ROUTES_TO_IGNORE: string[] = ['/app/login']; + protected static readonly ROUTES_AUTH_OPTIONAL: string[] = ['/api/core/capabilities']; protected static readonly REST_API_CALL_HEADER = 'osd-xsrf'; @@ -153,6 +151,11 @@ export abstract class AuthenticationType implements IAuthenticationType { return toolkit.notHandled(); } + // allow optional authentication + if (this.authOptional(request)) { + return toolkit.authenticated(); + } + // send to auth workflow return this.handleUnauthedRequest(request, response, toolkit); } @@ -236,6 +239,14 @@ export abstract class AuthenticationType implements IAuthenticationType { return false; } + authOptional(request: OpenSearchDashboardsRequest): boolean { + const pathname = request.url.pathname; + if (!pathname) { + return false; + } + return AuthenticationType.ROUTES_AUTH_OPTIONAL.includes(pathname!); + } + async resolveTenant( request: OpenSearchDashboardsRequest, cookie: SecuritySessionCookie,