Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SAML Integration Tests #1087

Closed
wants to merge 5 commits into from
Closed
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
1 change: 1 addition & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ plugins.security.allow_default_init_securityindex: true
plugins.security.authcz.admin_dn:
- CN=kirk,OU=client,O=client,L=test, C=de

plugins.security.unsupported.restapi.allow_securityconfig_modification: true
plugins.security.audit.type: internal_opensearch
plugins.security.enable_snapshot_restore_privilege: true
plugins.security.check_snapshot_restore_write_privileges: true
Expand Down
28 changes: 28 additions & 0 deletions idp-private-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC+eflDQihJ4zoZ
urwXuY3Nk9jRA3ms5Ge6jYuvId9mPjJ9ezndOdiVWnTgdqfmYUFMRsQ53iJJmprp
GHctBIPWpaz5Ao4oeco0D87CcySLA94tp8SG5ja2vasVYiNsaN9er1oWVo8uQP82
iLGF5jXOBDaN3iPub8W2w78oSfgV2Kw5fgmcEIxUGyf/Jr1a8X5TdbX1rS8cZ08D
crZ8DAQZ9ndLl6wJ6RKnp7dqtEys1BT2LXQVvGkFp7zrN74AnNl8+FUE269Wct3t
sPVkIKgGxURGj+ky6oHT9MbVp7vvE9+7dcwfQMDqbd5elOIFZsWad0wd4LgIFUGa
ATnaSyXrAgMBAAECggEAGiwOWPSFLqnfONvUrnnbhyzSdN3CYUQ9EIAUemrwFE9l
hqJg8AnbvaHhP1pudZRVcZKjscPY+D4bHP40meXt65d2Lfzw5ZTeeMrXQRarJcLR
S3qq4VJOoEttb5G9hk7eqlbzzO/12ogpySd9JQXdzMH9cP7d9ww6oYNCB9oqEW4o
QVjwF5RHuq9dB6FmhsNux9VbbP6gGVTXA7IpmNwFddW8JoDM2mj952POYjAwEy00
VQb0dzVrfuEw8DmpWZAOfyDt5QYO+7SC+L4eHbhrZrxeCzXr2hEhiCa1Nn3ieAHD
fF5FaguRMJcfGI3UEQZAw0++0SYeWPnjnwM1wE/nyQKBgQDyZA+6D1f3H/0fg4tT
ZrpGydnRPqqvMF+GBCtfa7Gjf6NdFQ+j0NzUXGzbIGy+0pJKEfEk4hE/2xFjCzru
LYJ5hSz/GXZH6qn4H39f0byl9oT6DYKDmrcXt1jRM4ioCU4Q1c58e2MqsvttVCnw
dVDQuTMb1xm6okHqcm3Fn8rUBwKBgQDJK7wbgrQcWriFxKDITdLvAivDcYAQ/p/e
XDw+nVdwq0p9Kpe+N5Lt6kvlTVyrS1aWfa9ucLVKMSKCbkjJBhesJsZU3nZgLa65
XT5+sP4640/gBoHWvauYBBjOy0iHHzhrIx+Kw0BXWzrYE0ol0aIWYZt1/I1LQtoB
E0+ojQLN/QKBgQDCtZhgiNTLwhmOSBgSffHizWC4klN/+SayvASvWQ5QXUa4jiOL
H0tVF42mFIzmWLaE45bHXwYmOm7kFfBXxZ0Kyu0TWrvGF35Dv+GM8ilNVBML3vBZ
kV3EolapbnE3MopQQb/mBSPq9+26rCIoc8TgdfTVR1v2rUKv9w2w86R13wKBgGh5
GQikeUscZiW6NtGvcPMFCptGb37j7Tx6ZCMUbVuq6VVVcFat39VEz0N3SMAAsSgY
f6n4SH4ORGC+S3hyfIq/3FIo8gsCznGflhwPaQhGEq5CUt2lxN5+ii+i7LiXoyIo
rHHQ8rIrQ8UBR4mac/XxnN3KWcqTHkpesAjVqnY1AoGAXoc9kr4v3WP0AG6lUhjB
P77AIrpCokbvnXfsAj9coPBJjK16XY534jjmnem4lwvxad/+PDWfU2i3wlP64YCi
BJkN8s9vJyJqHDFJjQ18zl6rdlFt+43x+YCt/zHsqazdcm47XN9kbXrunDYpVh2E
PZWa3aHt74aNhNMshynDIXI=
-----END PRIVATE KEY-----
20 changes: 20 additions & 0 deletions idp-public-cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDVjCCAj4CCQCgL9L+gaw8+jANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQGEwJV
UzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEQ
MA4GA1UECgwHSmFua3lDbzEfMB0GA1UEAwwWVGVzdCBJZGVudGl0eSBQcm92aWRl
cjAeFw0yMjA3MTUxNjU1MjRaFw00MjA3MTAxNjU1MjRaMG0xCzAJBgNVBAYTAlVT
MRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRAw
DgYDVQQKDAdKYW5reUNvMR8wHQYDVQQDDBZUZXN0IElkZW50aXR5IFByb3ZpZGVy
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvnn5Q0IoSeM6Gbq8F7mN
zZPY0QN5rORnuo2LryHfZj4yfXs53TnYlVp04Han5mFBTEbEOd4iSZqa6Rh3LQSD
1qWs+QKOKHnKNA/OwnMkiwPeLafEhuY2tr2rFWIjbGjfXq9aFlaPLkD/NoixheY1
zgQ2jd4j7m/FtsO/KEn4FdisOX4JnBCMVBsn/ya9WvF+U3W19a0vHGdPA3K2fAwE
GfZ3S5esCekSp6e3arRMrNQU9i10FbxpBae86ze+AJzZfPhVBNuvVnLd7bD1ZCCo
BsVERo/pMuqB0/TG1ae77xPfu3XMH0DA6m3eXpTiBWbFmndMHeC4CBVBmgE52ksl
6wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQADzGujWOBelV81BoxkS5vB+VGmEDAx
I67cZCZ7734vJsrf5c6QV45zz+NiOqyLkY2JEsHMh89Ns8n+1MsbIPn01tfjXFgL
arJLhLBRxFhqZr0H81E8DAzHwjAtx8Qmr/IQXcLPhJ0SMubIGC7EhCkYrphteTyd
2Rr5C9lCwF4Lb3xgoT2RsEO/IWDKb/CthcisQdDTw1XWLeAc+pJa76kOgDSkP93i
hHoZJMswOFU8KnLiXMaSxUZOXHLOYY7k4+xyh7dGqEkwKRYyY3TJ3mAULcJr5Ngz
UJvwmjmuEVCIgVNWqW45UsXJqkvdGFtUKj3UGfgyuvSV33daqXjkAims
-----END CERTIFICATE-----
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,20 @@
"lint:es": "node ../../scripts/eslint",
"lint:style": "node ../../scripts/stylelint",
"lint": "yarn run lint:es && yarn run lint:style",
"pretest:jest_server": "node ./test/jest_integration/runIdpServer.js &",
"test:jest_server": "node ./test/run_jest_tests.js --config ./test/jest.config.server.js",
"test:jest_ui": "node ./test/run_jest_tests.js --config ./test/jest.config.ui.js"
},
"devDependencies": {
"@elastic/eslint-import-resolver-kibana": "link:../../packages/osd-eslint-import-resolver-opensearch-dashboards",
"typescript": "4.0.2",
"gulp-rename": "2.0.0",
"@testing-library/react-hooks": "^7.0.2",
"@types/hapi__wreck": "^15.0.1"
"@types/hapi__wreck": "^15.0.1",
"selenium-webdriver": "^4.0.0-alpha.7",
"saml-idp": "^1.2.1"
},
"dependencies": {
"@hapi/wreck": "^17.1.0",
"@hapi/cryptiles": "5.0.0",
"@hapi/wreck": "^17.1.0",
"html-entities": "1.3.1"
}
}
8 changes: 6 additions & 2 deletions public/apps/account/account-nav-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,11 @@ export function AccountNavButton(props: {
<EuiListGroupItem
color="subdued"
key="tenant"
label={<EuiText size="xs">{resolveTenantName(props.tenant || '', username)}</EuiText>}
label={
<EuiText size="xs" id="tenantName">
{resolveTenantName(props.tenant || '', username)}
</EuiText>
}
/>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down Expand Up @@ -140,7 +144,7 @@ export function AccountNavButton(props: {
</div>
);
return (
<EuiHeaderSectionItemButton>
<EuiHeaderSectionItemButton id="user-icon-btn">
<EuiPopover
data-test-subj="account-popover"
id="actionsMenu"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Account navigation button renders 1`] = `
<EuiHeaderSectionItemButton>
<EuiHeaderSectionItemButton
id="user-icon-btn"
>
<EuiPopover
anchorPosition="downCenter"
button={
Expand Down Expand Up @@ -63,6 +65,7 @@ exports[`Account navigation button renders 1`] = `
key="tenant"
label={
<EuiText
id="tenantName"
size="xs"
>
tenant1
Expand Down Expand Up @@ -114,4 +117,4 @@ exports[`Account navigation button renders 1`] = `
</EuiContextMenuPanel>
</EuiPopover>
</EuiHeaderSectionItemButton>
`;
`;
33 changes: 12 additions & 21 deletions server/auth/types/saml/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,18 @@
*/

import { schema } from '@osd/config-schema';
import {
IRouter,
SessionStorageFactory,
OpenSearchDashboardsRequest,
} from '../../../../../../src/core/server';
import { IRouter, SessionStorageFactory } from '../../../../../../src/core/server';
import { SecuritySessionCookie } from '../../../session/security_cookie';
import { SecurityPluginConfigType } from '../../..';
import { SecurityClient } from '../../../backend/opensearch_security_client';
import { API_AUTH_LOGOUT } from '../../../../common';
import { CoreSetup } from '../../../../../../src/core/server';
import { validateNextUrl } from '../../../utils/next_url';
import { AuthType } from '../../../../common/index';

export class SamlAuthRoutes {
constructor(
private readonly router: IRouter,
// @ts-ignore: unused variable
private readonly config: SecurityPluginConfigType,
private readonly sessionStorageFactory: SessionStorageFactory<SecuritySessionCookie>,
private readonly securityClient: SecurityClient,
Expand All @@ -38,7 +35,7 @@ export class SamlAuthRoutes {
public setupRoutes() {
this.router.get(
{
path: `/auth/saml/login`,
path: '/auth/saml/login',
validate: {
query: schema.object({
nextUrl: schema.maybe(
Expand Down Expand Up @@ -86,7 +83,8 @@ export class SamlAuthRoutes {

this.router.post(
{
path: `/_opendistro/_security/saml/acs`,
// need to change to /_opendistro to execute the tests.
path: '/_opendistro/_security/saml/acs',
validate: {
body: schema.any(),
},
Expand Down Expand Up @@ -143,11 +141,12 @@ export class SamlAuthRoutes {
credentials: {
authHeaderValue: credentials.authorization,
},
authType: 'saml', // TODO: create constant
authType: AuthType.SAML,
expiryTime,
};
this.sessionStorageFactory.asScoped(request).set(cookie);
if (redirectHash) {
console.log('The server base path is : ' + this.coreSetup.http.basePath.serverBasePath);
return response.redirected({
headers: {
location: `${
Expand All @@ -174,7 +173,7 @@ export class SamlAuthRoutes {

this.router.post(
{
path: `/_opendistro/_security/saml/acs/idpinitiated`,
path: '/_plugins/_security/saml/acs/idpinitiated',
validate: {
body: schema.any(),
},
Expand All @@ -183,7 +182,7 @@ export class SamlAuthRoutes {
},
},
async (context, request, response) => {
const acsEndpoint = `${this.coreSetup.http.basePath.serverBasePath}/_opendistro/_security/saml/acs/idpinitiated`;
const acsEndpoint = `${this.coreSetup.http.basePath.serverBasePath}/_plugins/_security/saml/acs/idpinitiated`;
try {
const credentials = await this.securityClient.authToken(
undefined,
Expand Down Expand Up @@ -211,7 +210,7 @@ export class SamlAuthRoutes {
credentials: {
authHeaderValue: credentials.authorization,
},
authType: 'saml', // TODO: create constant
authType: AuthType.SAML,
expiryTime,
};
this.sessionStorageFactory.asScoped(request).set(cookie);
Expand All @@ -229,8 +228,6 @@ export class SamlAuthRoutes {
}
);

// captureUrlFragment is the first route that will be invoked in the SP initiated login.
// This route will execute the captureUrlFragment.js script.
this.coreSetup.http.resources.register(
{
path: '/auth/saml/captureUrlFragment',
Expand Down Expand Up @@ -261,7 +258,6 @@ export class SamlAuthRoutes {
}
);

// This script will store the URL Hash in browser's local storage.
this.coreSetup.http.resources.register(
{
path: '/auth/saml/captureUrlFragment.js',
Expand Down Expand Up @@ -291,9 +287,6 @@ export class SamlAuthRoutes {
}
);

// Once the User is authenticated via the '_opendistro/_security/saml/acs' route,
// the browser will be redirected to '/auth/saml/redirectUrlFragment' route,
// which will execute the redirectUrlFragment.js.
this.coreSetup.http.resources.register(
{
path: '/auth/saml/redirectUrlFragment',
Expand All @@ -319,8 +312,6 @@ export class SamlAuthRoutes {
}
);

// This script will pop the Hash from local storage if it exists.
// And forward the browser to the next url.
this.coreSetup.http.resources.register(
{
path: '/auth/saml/redirectUrlFragment.js',
Expand All @@ -344,7 +335,7 @@ export class SamlAuthRoutes {

this.router.get(
{
path: `/auth/logout`,
path: API_AUTH_LOGOUT,
validate: false,
},
async (context, request, response) => {
Expand Down
2 changes: 2 additions & 0 deletions server/auth/types/saml/saml_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export class SamlAuthentication extends AuthenticationType {
return escape(path);
}

// Check if we can get the previous tenant information from the expired cookie.
private redirectSAMlCapture = (request: OpenSearchDashboardsRequest, toolkit: AuthToolkit) => {
const nextUrl = this.generateNextUrl(request);
const clearOldVersionCookie = clearOldVersionCookieValue(this.config);
Expand Down Expand Up @@ -97,6 +98,7 @@ export class SamlAuthentication extends AuthenticationType {
};
}

// Can be improved to check if the token is expiring.
async isValidCookie(cookie: SecuritySessionCookie): Promise<boolean> {
return (
cookie.authType === this.type &&
Expand Down
2 changes: 1 addition & 1 deletion test/helper/cookie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { AUTHORIZATION_HEADER_NAME } from '../constant';

export function extractAuthCookie(response: Response) {
const setCookieHeaders = response.header['set-cookie'] as string[];
let securityAuthCookie: string;
let securityAuthCookie: string | null = null;
for (const setCookie of setCookieHeaders) {
if (setCookie.startsWith('security_authentication=')) {
securityAuthCookie = setCookie.split(';')[0];
Expand Down
22 changes: 22 additions & 0 deletions test/jest_integration/runIdpServer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* 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.
*/

const { runServer } = require('saml-idp');

// Create certificate pair on the fly and pass it to runServer
runServer({
acsUrl: 'http://localhost:5601/_opendistro/_security/saml/acs',
audience: 'https://localhost:9200',
});
Loading