Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
plugin-version: ${{ env.PLUGIN_VERSION }}

- name: Run Opensearch with A Single Plugin
uses: opensearch-project/security/.github/actions/start-opensearch-with-one-plugin@main
uses: opensearch-project/security/.github/actions/start-opensearch-with-one-plugin@1.3
with:
opensearch-version: ${{ env.OPENSEARCH_VERSION }}
plugin-name: ${{ env.PLUGIN_NAME }}
Expand Down
3 changes: 2 additions & 1 deletion common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +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',
OPEN_ID = 'openid',
Expand Down
4 changes: 2 additions & 2 deletions opensearch_dashboards.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "securityDashboards",
"version": "1.3.14.0",
"opensearchDashboardsVersion": "1.3.14",
"version": "1.3.15.0",
"opensearchDashboardsVersion": "1.3.15",
"configPath": [
"opensearch_security"
],
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "opensearch-security-dashboards",
"version": "1.3.14.0",
"version": "1.3.15.0",
"main": "target/plugins/opensearch_security_dashboards",
"opensearchDashboards": {
"version": "1.3.14",
"templateVersion": "1.3.14"
"version": "1.3.15",
"templateVersion": "1.3.15"
},
"license": "Apache-2.0",
"homepage": "https://github.com/opensearch-project/security-dashboards-plugin",
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))
18 changes: 14 additions & 4 deletions server/auth/types/authentication_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,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 +140,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 +236,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>;
}
158 changes: 158 additions & 0 deletions server/auth/types/openid/openid_auth.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* 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 esClient: ILegacyClusterClient;
let logger: Logger;

const router: Partial<IRouter> = {
get: jest.fn(),
post: jest.fn(),
};
const core = ({
http: {
basePath: {
serverBasePath: '/',
},
resources: {
register: jest.fn(),
},
},
} as unknown) as CoreSetup;

const sessionStorageFactory: SessionStorageFactory<SecuritySessionCookie> = {
asScoped: jest.fn().mockImplementation(() => {
return {
server: {
states: {
add: jest.fn(),
},
},
};
}),
};

// Consistent with auth_handler_factory.test.ts
beforeEach(() => {
// @ts-ignore
jest.spyOn(OpenIdAuthentication.prototype, 'init').mockImplementation(async () => {});
});

const config = ({
cookie: {
secure: false,
},
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 as IRouter,
esClient,
core,
logger
);

// The init method has a spyOn and is not executed, so we call createExtraStorage separately.
// This is not really needed for the test, but may help in spotting errors.
openIdAuthentication.createExtraStorage();

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 as IRouter,
esClient,
core,
logger
);

// The init method has a spyOn and is not executed, so we call createExtraStorage separately.
// This is not really needed for the test, but may help in spotting errors.
openIdAuthentication.createExtraStorage();

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