Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
390fd60
[Backport 1.3] Switch to new tenant after loading a copied long URL (…
DarshitChanpura Jul 19, 2023
cac8e3f
[Backport 1.3] Add the tenant into the short URL once the short URL i…
DarshitChanpura Jul 20, 2023
f44b2b9
Increment version to 1.3.12.0 (#1536)
opensearch-trigger-bot[bot] Aug 3, 2023
c3edcf4
Add release notes for 1.3.12.0 (#1543) (#1544)
opensearch-trigger-bot[bot] Aug 5, 2023
87dd14e
Fix a bad import path (#1498) (#1547)
opensearch-trigger-bot[bot] Aug 7, 2023
fba07b8
Increment version to 1.3.13.0 (#1556)
opensearch-trigger-bot[bot] Sep 12, 2023
01931ad
Backport cookie compression
jochen-kressin Oct 25, 2023
21e7a42
The cookie splitter should be able to use cookie values that have bee…
jochen-kressin Sep 11, 2023
1077d7d
[1.3] Increment version to 1.3.14.0 (#1659)
cwperks Nov 28, 2023
e2b1424
Check out latest OSD and re-run yarn install to address CVEs (#1669)
derek-ho Nov 28, 2023
f861f46
Bump debug and browserify-sign dependencies (#1674)
derek-ho Nov 29, 2023
f6f7a89
adding config as per 1x branch and craig p instructions
leanneeliatra Dec 6, 2023
9c3a8f6
Merge branch '1.x' into backport-cookie-splitter
DarshitChanpura Dec 7, 2023
307c7a0
[1.3] Add 1.3.14 release notes (#1690)
derek-ho Dec 8, 2023
9e629db
Stabilize SAML integ test on 1.3
cwperks Dec 11, 2023
09fac24
Merge branch 'stabilize-1.3' into backport-cookie-splitter
cwperks Dec 11, 2023
a31755d
WIP on init
cwperks Dec 18, 2023
763831b
Run yarn lint
cwperks Dec 18, 2023
efdfc18
Merge branch '1.3' into backport-cookie-splitter
cwperks Dec 18, 2023
b0fd19c
Checkout 1.3.14 tag
cwperks Dec 18, 2023
4e67bfc
Fix auth_handler_factory.test.ts
cwperks Dec 18, 2023
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
2 changes: 1 addition & 1 deletion .github/actions/install-dashboards/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ runs:
- id: branch-switch-if-possible
continue-on-error: true # Defaults onto main if the branch switch doesn't work
if: ${{ steps.osd-version.outputs.osd-version }}
run: git checkout ${{ steps.osd-version.outputs.osd-version }} || git checkout ${{ steps.osd-version.outputs.osd-x-version }}x
run: git checkout 1.3.14 || git checkout ${{ steps.osd-version.outputs.osd-version }} || git checkout ${{ steps.osd-version.outputs.osd-x-version }}x
working-directory: ./OpenSearch-Dashboards
shell: bash

Expand Down
2 changes: 2 additions & 0 deletions common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export const DEFAULT_TENANT = 'default';
export const GLOBAL_TENANT_RENDERING_TEXT = 'Global';
export const PRIVATE_TENANT_RENDERING_TEXT = 'Private';
export const globalTenantName = 'global_tenant';
export const MAX_LENGTH_OF_COOKIE_BYTES = 4000;
export const ESTIMATED_IRON_COOKIE_OVERHEAD = 1.5;

export enum AuthType {
BASIC = 'basicauth',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## 2023-12-08 Version 1.3.14.0

Compatible with OpenSearch-Dashboards 1.3.14

### Maintenance

* Update `yarn.lock` file ([#1669](https://github.com/opensearch-project/security-dashboards-plugin/pull/1669))
* Bump `debug` to `4.3.4` and `browserify-sign` to `4.2.2` to address CVEs ([#1674](https://github.com/opensearch-project/security-dashboards-plugin/pull/1674))
40 changes: 24 additions & 16 deletions server/auth/auth_handler_factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,35 @@ jest.mock('./types', () => {
return {
authHandler: () => {},
type: 'basicauth',
init: () => {},
};
}),
JwtAuthentication: jest.fn().mockImplementation(() => {
return {
authHandler: () => {},
type: 'jwt',
init: () => {},
};
}),
OpenIdAuthentication: jest.fn().mockImplementation(() => {
return {
authHandler: () => {},
type: 'openid',
init: () => {},
};
}),
ProxyAuthentication: jest.fn().mockImplementation(() => {
return {
authHandler: () => {},
type: 'proxy',
init: () => {},
};
}),
SamlAuthentication: jest.fn().mockImplementation(() => {
return {
authHandler: () => {},
type: 'saml',
init: () => {},
};
}),
};
Expand All @@ -69,8 +74,8 @@ describe('test authentication factory', () => {

beforeEach(() => {});

test('get basic auth', () => {
const auth = getAuthenticationHandler(
test('get basic auth', async () => {
const auth = await getAuthenticationHandler(
'basicauth',
router,
config,
Expand All @@ -82,8 +87,8 @@ describe('test authentication factory', () => {
expect(auth.type).toEqual('basicauth');
});

test('get basic auth with empty auth type', () => {
const auth = getAuthenticationHandler(
test('get basic auth with empty auth type', async () => {
const auth = await getAuthenticationHandler(
'',
router,
config,
Expand All @@ -95,8 +100,8 @@ describe('test authentication factory', () => {
expect(auth.type).toEqual('basicauth');
});

test('get jwt auth', () => {
const auth = getAuthenticationHandler(
test('get jwt auth', async () => {
const auth = await getAuthenticationHandler(
'jwt',
router,
config,
Expand All @@ -108,8 +113,8 @@ describe('test authentication factory', () => {
expect(auth.type).toEqual('jwt');
});

test('get openid auth', () => {
const auth = getAuthenticationHandler(
test('get openid auth', async () => {
const auth = await getAuthenticationHandler(
'openid',
router,
config,
Expand All @@ -121,8 +126,8 @@ describe('test authentication factory', () => {
expect(auth.type).toEqual('openid');
});

test('get proxy auth', () => {
const auth = getAuthenticationHandler(
test('get proxy auth', async () => {
const auth = await getAuthenticationHandler(
'proxy',
router,
config,
Expand All @@ -134,8 +139,8 @@ describe('test authentication factory', () => {
expect(auth.type).toEqual('proxy');
});

test('get saml auth', () => {
const auth = getAuthenticationHandler(
test('get saml auth', async () => {
const auth = await getAuthenticationHandler(
'saml',
router,
config,
Expand All @@ -147,9 +152,9 @@ describe('test authentication factory', () => {
expect(auth.type).toEqual('saml');
});

test('throws error for invalid auth type', () => {
expect(() => {
getAuthenticationHandler(
test('throws error for invalid auth type', async () => {
try {
await getAuthenticationHandler(
'invalid',
router,
config,
Expand All @@ -158,6 +163,9 @@ describe('test authentication factory', () => {
sessionStorageFactory,
logger
);
}).toThrow('Unsupported authentication type: invalid');
} catch (e) {
const targetError = 'Error: Unsupported authentication type: invalid';
expect(e.toString()).toEqual(targetError);
}
});
});
14 changes: 8 additions & 6 deletions server/auth/auth_handler_factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,27 +32,29 @@ import { SecuritySessionCookie } from '../session/security_cookie';
import { IAuthenticationType, IAuthHandlerConstructor } from './types/authentication_type';
import { SecurityPluginConfigType } from '..';

function createAuthentication(
async function createAuthentication(
ctor: IAuthHandlerConstructor,
config: SecurityPluginConfigType,
sessionStorageFactory: SessionStorageFactory<SecuritySessionCookie>,
router: IRouter,
esClient: ILegacyClusterClient,
coreSetup: CoreSetup,
logger: Logger
): IAuthenticationType {
return new ctor(config, sessionStorageFactory, router, esClient, coreSetup, logger);
): Promise<IAuthenticationType> {
const authHandler = new ctor(config, sessionStorageFactory, router, esClient, coreSetup, logger);
await authHandler.init();
return authHandler;
}

export function getAuthenticationHandler(
export async function getAuthenticationHandler(
authType: string,
router: IRouter,
config: SecurityPluginConfigType,
core: CoreSetup,
esClient: ILegacyClusterClient,
securitySessionStorageFactory: SessionStorageFactory<SecuritySessionCookie>,
logger: Logger
): IAuthenticationType {
): Promise<IAuthenticationType> {
let authHandlerType: IAuthHandlerConstructor;
switch (authType) {
case '':
Expand All @@ -74,7 +76,7 @@ export function getAuthenticationHandler(
default:
throw new Error(`Unsupported authentication type: ${authType}`);
}
const auth: IAuthenticationType = createAuthentication(
const auth: IAuthenticationType = await createAuthentication(
authHandlerType,
config,
securitySessionStorageFactory,
Expand Down
19 changes: 15 additions & 4 deletions server/auth/types/authentication_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { UnauthenticatedError } from '../../errors';
export interface IAuthenticationType {
type: string;
authHandler: AuthenticationHandler;
init: () => Promise<void>;
}

export type IAuthHandlerConstructor = new (
Expand Down Expand Up @@ -118,7 +119,7 @@ export abstract class AuthenticationType implements IAuthenticationType {
cookie = undefined;
}

if (!cookie || !(await this.isValidCookie(cookie))) {
if (!cookie || !(await this.isValidCookie(cookie, request))) {
// clear cookie
this.sessionStorageFactory.asScoped(request).clear();

Expand All @@ -140,7 +141,7 @@ export abstract class AuthenticationType implements IAuthenticationType {
}
// cookie is valid
// build auth header
const authHeadersFromCookie = this.buildAuthHeaderFromCookie(cookie!);
const authHeadersFromCookie = this.buildAuthHeaderFromCookie(cookie!, request);
Object.assign(authHeaders, authHeadersFromCookie);
const additonalAuthHeader = this.getAdditionalAuthHeader(request);
Object.assign(authHeaders, additonalAuthHeader);
Expand Down Expand Up @@ -236,11 +237,21 @@ export abstract class AuthenticationType implements IAuthenticationType {
request: OpenSearchDashboardsRequest,
authInfo: any
): SecuritySessionCookie;
protected abstract async isValidCookie(cookie: SecuritySessionCookie): Promise<boolean>;

public abstract isValidCookie(
cookie: SecuritySessionCookie,
request: OpenSearchDashboardsRequest
): Promise<boolean>;

protected abstract handleUnauthedRequest(
request: OpenSearchDashboardsRequest,
response: LifecycleResponseFactory,
toolkit: AuthToolkit
): IOpenSearchDashboardsResponse | AuthResult;
protected abstract buildAuthHeaderFromCookie(cookie: SecuritySessionCookie): any;

public abstract buildAuthHeaderFromCookie(
cookie: SecuritySessionCookie,
request: OpenSearchDashboardsRequest
): any;
public abstract init(): Promise<void>;
}
4 changes: 1 addition & 3 deletions server/auth/types/basic/basic_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,9 @@ export class BasicAuthentication extends AuthenticationType {
logger: Logger
) {
super(config, sessionStorageFactory, router, esClient, coreSetup, logger);

this.init();
}

private async init() {
public async init() {
const routes = new BasicAuthRoutes(
this.router,
this.config,
Expand Down
4 changes: 1 addition & 3 deletions server/auth/types/jwt/jwt_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,9 @@ export class JwtAuthentication extends AuthenticationType {
) {
super(config, sessionStorageFactory, router, esClient, coreSetup, logger);
this.authHeaderName = this.config.jwt?.header.toLowerCase() || 'authorization';

this.init();
}

private async init() {
public async init() {
const routes = new JwtAuthRoutes(this.router, this.sessionStorageFactory);
routes.setupRoutes();
}
Expand Down
120 changes: 120 additions & 0 deletions server/auth/types/openid/openid_auth.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright OpenSearch Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

import { httpServerMock } from '../../../../../../src/core/server/http/http_server.mocks';

import { OpenSearchDashboardsRequest } from '../../../../../../src/core/server/http/router';

import { OpenIdAuthentication } from './openid_auth';
import { SecurityPluginConfigType } from '../../../index';
import { SecuritySessionCookie } from '../../../session/security_cookie';
import { deflateValue } from '../../../utils/compression';
import {
IRouter,
CoreSetup,
ILegacyClusterClient,
Logger,
SessionStorageFactory,
} from '../../../../../../src/core/server';

describe('test OpenId authHeaderValue', () => {
let router: IRouter;
let core: CoreSetup;
let esClient: ILegacyClusterClient;
let sessionStorageFactory: SessionStorageFactory<SecuritySessionCookie>;
let logger: Logger;

// Consistent with auth_handler_factory.test.ts
beforeEach(() => {});

const config = ({
openid: {
header: 'authorization',
scope: [],
extra_storage: {
cookie_prefix: 'testcookie',
additional_cookies: 5,
},
},
} as unknown) as SecurityPluginConfigType;

test('make sure that cookies with authHeaderValue are still valid', async () => {
const openIdAuthentication = new OpenIdAuthentication(
config,
sessionStorageFactory,
router,
esClient,
core,
logger
);

const mockRequest = httpServerMock.createRawRequest();
const osRequest = OpenSearchDashboardsRequest.from(mockRequest);

const cookie: SecuritySessionCookie = {
credentials: {
authHeaderValue: 'Bearer eyToken',
},
};

const expectedHeaders = {
authorization: 'Bearer eyToken',
};

const headers = openIdAuthentication.buildAuthHeaderFromCookie(cookie, osRequest);

expect(headers).toEqual(expectedHeaders);
});

test('get authHeaderValue from split cookies', async () => {
const openIdAuthentication = new OpenIdAuthentication(
config,
sessionStorageFactory,
router,
esClient,
core,
logger
);

const testString = 'Bearer eyCombinedToken';
const testStringBuffer: Buffer = deflateValue(testString);
const cookieValue = testStringBuffer.toString('base64');
const cookiePrefix = config.openid!.extra_storage.cookie_prefix;
const splitValueAt = Math.ceil(
cookieValue.length / config.openid!.extra_storage.additional_cookies
);
const mockRequest = httpServerMock.createRawRequest({
state: {
[cookiePrefix + '1']: cookieValue.substring(0, splitValueAt),
[cookiePrefix + '2']: cookieValue.substring(splitValueAt),
},
});
const osRequest = OpenSearchDashboardsRequest.from(mockRequest);

const cookie: SecuritySessionCookie = {
credentials: {
authHeaderValueExtra: true,
},
};

const expectedHeaders = {
authorization: testString,
};

const headers = openIdAuthentication.buildAuthHeaderFromCookie(cookie, osRequest);

expect(headers).toEqual(expectedHeaders);
});
});
Loading