Skip to content

Commit

Permalink
Merge branch 'main' into look&feel
Browse files Browse the repository at this point in the history
  • Loading branch information
derek-ho authored Aug 22, 2024
2 parents 2f42104 + b1148fb commit 1a8658b
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @cliu123 @cwperks @DarshitChanpura @derek-ho @RyanL1997 @scrawfor99
* @cwperks @DarshitChanpura @derek-ho @RyanL1997 @stephen-crawford
5 changes: 3 additions & 2 deletions MAINTAINERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ This document contains a list of maintainers in this repo. See [opensearch-proje

| Maintainer | GitHub ID | Affiliation |
| ---------------- | ----------------------------------------------------- | ----------- |
| Chang Liu | [cliu123](https://github.com/cliu123) | Amazon |
| Darshit Chanpura | [DarshitChanpura](https://github.com/DarshitChanpura) | Amazon |
| Craig Perkins | [cwperks](https://github.com/cwperks) | Amazon |
| Ryan Liang | [RyanL1997](https://github.com/RyanL1997) | Amazon |
| Stephen Crawford | [scrawfor99](https://github.com/scrawfor99) | Amazon |
| Stephen Crawford | [scrawfor99](https://github.com/stephen-crawford) | Amazon |
| Derek Ho | [derek-ho](https://github.com/derek-ho) | Amazon |

## Emeritus
Expand All @@ -22,3 +21,5 @@ This document contains a list of maintainers in this repo. See [opensearch-proje
| Tianle Huang | [tianleh](https://github.com/tianleh) | Amazon |
| Dave Lago | [davidlago](https://github.com/davidlago) | Contributor |
| Peter Nied | [peternied](https://github.com/peternied) | Amazon |
| Chang Liu | [cliu123](https://github.com/cliu123) | Amazon |

8 changes: 7 additions & 1 deletion public/apps/login/login-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,10 @@ export function LoginPage(props: LoginPageDeps) {
);
}

if (authOpts.length > 1) {
if (
authOpts.length > 1 &&
!(authOpts.includes(AuthType.PROXY) && authOpts.length === 2)
) {
formBody.push(<EuiSpacer size="xs" />);
formBody.push(<EuiHorizontalRule size="full" margin="xl" />);
formBody.push(<EuiSpacer size="xs" />);
Expand All @@ -258,6 +261,9 @@ export function LoginPage(props: LoginPageDeps) {
formBodyOp.push(renderLoginButton(AuthType.SAML, samlAuthLoginUrl, samlConfig));
break;
}
case AuthType.PROXY: {
break;
}
default: {
setloginFailed(true);
setloginError(
Expand Down
22 changes: 20 additions & 2 deletions server/auth/types/multiple/multi_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ import { AuthType, LOGIN_PAGE_URI } from '../../../../common';
import { composeNextUrlQueryParam } from '../../../utils/next_url';
import { MultiAuthRoutes } from './routes';
import { SecuritySessionCookie } from '../../../session/security_cookie';
import { BasicAuthentication, OpenIdAuthentication, SamlAuthentication } from '../../types';
import {
BasicAuthentication,
OpenIdAuthentication,
ProxyAuthentication,
SamlAuthentication,
} from '../../types';

export class MultipleAuthentication extends AuthenticationType {
private authTypes: string | string[];
Expand Down Expand Up @@ -93,6 +98,19 @@ export class MultipleAuthentication extends AuthenticationType {
this.authHandlers.set(AuthType.SAML, SamlAuth);
break;
}
case AuthType.PROXY: {
const ProxyAuth = new ProxyAuthentication(
this.config,
this.sessionStorageFactory,
this.router,
this.esClient,
this.coreSetup,
this.logger
);
await ProxyAuth.init();
this.authHandlers.set(AuthType.PROXY, ProxyAuth);
break;
}
default: {
throw new Error(`Unsupported authentication type: ${this.authTypes[i]}`);
}
Expand All @@ -115,7 +133,7 @@ export class MultipleAuthentication extends AuthenticationType {
async getAdditionalAuthHeader(
request: OpenSearchDashboardsRequest<unknown, unknown, unknown, any>
): Promise<any> {
// To Do: refactor this method to improve the effiency to get cookie, get cookie from input parameter
// To Do: refactor this method to improve the efficiency to get cookie, get cookie from input parameter
const cookie = await this.sessionStorageFactory.asScoped(request).get();
const reqAuthType = cookie?.authType?.toLowerCase();

Expand Down
5 changes: 5 additions & 0 deletions server/utils/next_url.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ describe('test validateNextUrl', () => {
expect(validateNextUrl(url, '')).toEqual(undefined);
});

test('allow basePath', () => {
const url = '/osd';
expect(validateNextUrl(url, '/osd')).toEqual(undefined);
});

test('allow dashboard url', () => {
const url =
'/_plugin/opensearch-dashboards/app/opensearch-dashboards#dashbard/dashboard-id?_g=(param=a&p=b)';
Expand Down
2 changes: 1 addition & 1 deletion server/utils/next_url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export function validateNextUrl(
}
const pathMinusBase = path.replace(bp, '');
if (
!pathMinusBase.startsWith('/') ||
(pathMinusBase && !pathMinusBase.startsWith('/')) ||
(pathMinusBase.length >= 2 && !/^\/[a-zA-Z_][\/a-zA-Z0-9-_]+$/.test(pathMinusBase))
) {
return INVALID_NEXT_URL_PARAMETER_MESSAGE;
Expand Down
4 changes: 4 additions & 0 deletions test/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ export const ADMIN_PASSWORD: string = process.env.ADMIN_PASSWORD || 'admin';
const ADMIN_USER_PASS: string = `${ADMIN_USER}:${ADMIN_PASSWORD}`;
export const ADMIN_CREDENTIALS: string = `Basic ${Buffer.from(ADMIN_USER_PASS).toString('base64')}`;
export const AUTHORIZATION_HEADER_NAME: string = 'Authorization';

export const PROXY_USER: string = 'x-proxy-user';
export const PROXY_ROLE: string = 'x-proxy-roles';
export const PROXY_ADMIN_ROLE: string = 'admin';
209 changes: 209 additions & 0 deletions test/jest_integration/proxy_multiauth.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/*
* 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 * as osdTestServer from '../../../../src/core/test_helpers/osd_server';
import { Root } from '../../../../src/core/server/root';
import { resolve } from 'path';
import { describe, it, beforeAll, afterAll } from '@jest/globals';
import {
ADMIN_CREDENTIALS,
OPENSEARCH_DASHBOARDS_SERVER_USER,
OPENSEARCH_DASHBOARDS_SERVER_PASSWORD,
ADMIN_USER,
PROXY_ADMIN_ROLE,
} from '../constant';
import wreck from '@hapi/wreck';

describe('start OpenSearch Dashboards server', () => {
let root: Root;
let config;

beforeAll(async () => {
root = osdTestServer.createRootWithSettings(
{
plugins: {
scanDirs: [resolve(__dirname, '../..')],
},
home: { disableWelcomeScreen: true },
server: {
host: 'localhost',
port: 5601,
},
logging: {
silent: true,
verbose: false,
},
opensearch: {
hosts: ['https://localhost:9200'],
ignoreVersionMismatch: true,
ssl: { verificationMode: 'none' },
username: OPENSEARCH_DASHBOARDS_SERVER_USER,
password: OPENSEARCH_DASHBOARDS_SERVER_PASSWORD,
requestHeadersAllowlist: [
'securitytenant',
'Authorization',
'x-forwarded-for',
'x-proxy-user',
'x-proxy-roles',
],
},
opensearch_security: {
auth: {
anonymous_auth_enabled: false,
type: ['basicauth', 'proxy'],
multiple_auth_enabled: true,
},
proxycache: {
user_header: 'x-proxy-user',
roles_header: 'x-proxy-roles',
},
multitenancy: {
enabled: true,
tenants: {
enable_global: true,
enable_private: true,
preferred: ['Private', 'Global'],
},
},
},
},
{
// to make ignoreVersionMismatch setting work
// can be removed when we have corresponding ES version
dev: true,
}
);

console.log('Starting OpenSearchDashboards server..');
await root.setup();
await root.start();

console.log('Starting to Download Flights Sample Data');
await wreck.post('http://localhost:5601/api/sample_data/flights', {
payload: {},
rejectUnauthorized: false,
headers: {
'Content-Type': 'application/json',
authorization: ADMIN_CREDENTIALS,
security_tenant: 'global',
},
});
console.log('Downloaded Sample Data');
const getConfigResponse = await wreck.get(
'https://localhost:9200/_plugins/_security/api/securityconfig',
{
rejectUnauthorized: false,
headers: {
authorization: ADMIN_CREDENTIALS,
},
}
);
const responseBody = (getConfigResponse.payload as Buffer).toString();
config = JSON.parse(responseBody).config;
const proxyConfig = {
http_enabled: true,
transport_enabled: true,
order: 0,
http_authenticator: {
challenge: false,
type: 'proxy',
config: {
user_header: 'x-proxy-user',
roles_header: 'x-proxy-roles',
},
},
authentication_backend: {
type: 'noop',
config: {},
},
};
try {
config.dynamic!.authc!.proxy_auth_domain = proxyConfig;
config.dynamic!.authc!.basic_internal_auth_domain.http_authenticator.challenge = false;
config.dynamic!.http!.anonymous_auth_enabled = false;
await wreck.put('https://localhost:9200/_plugins/_security/api/securityconfig/config', {
payload: config,
rejectUnauthorized: false,
headers: {
'Content-Type': 'application/json',
authorization: ADMIN_CREDENTIALS,
},
});
} catch (error) {
console.log('Got an error while updating security config!!', error.stack);
fail(error);
}
});

afterAll(async () => {
console.log('Remove the Sample Data');
await wreck
.delete('http://localhost:5601/api/sample_data/flights', {
rejectUnauthorized: false,
headers: {
'Content-Type': 'application/json',
authorization: ADMIN_CREDENTIALS,
},
})
.then((value) => {
Promise.resolve(value);
})
.catch((value) => {
Promise.resolve(value);
});
console.log('Remove the Security Config');
await wreck
.patch('https://localhost:9200/_plugins/_security/api/securityconfig', {
payload: [
{
op: 'remove',
path: '/config/dynamic/authc/proxy_auth_domain',
},
],
rejectUnauthorized: false,
headers: {
'Content-Type': 'application/json',
authorization: ADMIN_CREDENTIALS,
},
})
.then((value) => {
Promise.resolve(value);
})
.catch((value) => {
Promise.resolve(value);
});
// shutdown OpenSearchDashboards server
await root.shutdown();
});

it('Verify Proxy access to dashboards', async () => {
console.log('Wreck access home page');
await wreck
.get('http://localhost:5601/app/home#', {
rejectUnauthorized: true,
headers: {
'Content-Type': 'application/json',
PROXY_USER: ADMIN_USER,
PROXY_ROLE: PROXY_ADMIN_ROLE,
},
})
.then((value) => {
Promise.resolve(value);
})
.catch((value) => {
Promise.resolve(value);
});
});
});

0 comments on commit 1a8658b

Please sign in to comment.