Skip to content

Commit

Permalink
Adds saml auth header to differentiate saml requests
Browse files Browse the repository at this point in the history
Signed-off-by: Darshit Chanpura <dchanp@amazon.com>
  • Loading branch information
DarshitChanpura committed Mar 20, 2024
1 parent d682701 commit ca8b4ef
Show file tree
Hide file tree
Showing 9 changed files with 32 additions and 36 deletions.
7 changes: 3 additions & 4 deletions common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,9 @@ export const OPENID_AUTH_LOGIN_WITH_FRAGMENT = '/auth/openid/captureUrlFragment'
export const SAML_AUTH_LOGIN = '/auth/saml/login';
export const SAML_AUTH_LOGIN_WITH_FRAGMENT = '/auth/saml/captureUrlFragment';
export const ANONYMOUS_AUTH_LOGIN = '/auth/anonymous';
const ANONYMOUS_AUTH_USER: string = 'opendistro_security_anonymous';
const RANDOM_PASS: string = randomString(12);
const ANONYMOUS_USER_PASS: string = `${ANONYMOUS_AUTH_USER}:${RANDOM_PASS}`;
export const ANONYMOUS_AUTH_HEADER = `Basic ${Buffer.from(ANONYMOUS_USER_PASS).toString('base64')}`;
export const AUTH_REQUEST_TYPE_HEADER = 'auth_request_type';
export const SAML_AUTH_REQUEST_TYPE = 'saml';


export const OPENID_AUTH_LOGOUT = '/auth/openid/logout';
export const SAML_AUTH_LOGOUT = '/auth/saml/logout';
Expand Down
11 changes: 1 addition & 10 deletions server/auth/types/authentication_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { SecuritySessionCookie } from '../../session/security_cookie';
import { SecurityClient } from '../../backend/opensearch_security_client';
import { resolveTenant, isValidTenant } from '../../multitenancy/tenant_resolver';
import { UnauthenticatedError } from '../../errors';
import { ANONYMOUS_AUTH_HEADER, GLOBAL_TENANT_SYMBOL } from '../../../common';
import { GLOBAL_TENANT_SYMBOL } from '../../../common';

export interface IAuthenticationType {
type: string;
Expand Down Expand Up @@ -114,15 +114,6 @@ export abstract class AuthenticationType implements IAuthenticationType {
let cookie: SecuritySessionCookie | null | undefined;
let authInfo: any | undefined;

// Adds a basic auth credentials headers to requests originated as anonymous user
if (
this.config.auth.anonymous_auth_enabled &&
!request.headers.hasOwnProperty('authorization')
) {
const anonymousAuthHeaders = { authorization: ANONYMOUS_AUTH_HEADER };
Object.assign(authHeaders, anonymousAuthHeaders);
}

// if this is an REST API call, suppose the request includes necessary auth header
// see https://www.elastic.co/guide/en/opensearch-dashboards/master/using-api.html
if (this.requestIncludesAuthInfo(request)) {
Expand Down
4 changes: 2 additions & 2 deletions server/auth/types/basic/basic_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { SecurityPluginConfigType } from '../../..';
import { SecuritySessionCookie } from '../../../session/security_cookie';
import { BasicAuthRoutes } from './routes';
import { AuthenticationType } from '../authentication_type';
import { LOGIN_PAGE_URI, ANONYMOUS_AUTH_HEADER } from '../../../../common';
import { LOGIN_PAGE_URI } from '../../../../common';
import { composeNextUrlQueryParam } from '../../../utils/next_url';
import { AUTH_HEADER_NAME, AuthType, OPENDISTRO_SECURITY_ANONYMOUS } from '../../../../common';

Expand Down Expand Up @@ -130,7 +130,7 @@ export class BasicAuthentication extends AuthenticationType {
request: OpenSearchDashboardsRequest
): any {
if (this.config.auth.anonymous_auth_enabled && cookie.isAnonymousAuth) {
return { authorization: ANONYMOUS_AUTH_HEADER };
return { };
}
const headers: any = {};
Object.assign(headers, { authorization: cookie.credentials?.authHeaderValue });
Expand Down
5 changes: 1 addition & 4 deletions server/auth/types/basic/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import { SecurityPluginConfigType } from '../../..';
import { User } from '../../user';
import { SecurityClient } from '../../../backend/opensearch_security_client';
import {
ANONYMOUS_AUTH_HEADER,
ANONYMOUS_AUTH_LOGIN,
API_AUTH_LOGIN,
API_AUTH_LOGOUT,
Expand Down Expand Up @@ -187,9 +186,7 @@ export class BasicAuthRoutes {
}
context.security_plugin.logger.info('The Redirect Path is ' + redirectUrl);
try {
user = await this.securityClient.authenticateWithHeaders(request, {
authorization: ANONYMOUS_AUTH_HEADER,
});
user = await this.securityClient.authenticateWithHeaders(request, { });
} catch (error) {
context.security_plugin.logger.error(
`Failed authentication: ${error}. Redirecting to Login Page`
Expand Down
3 changes: 2 additions & 1 deletion server/auth/types/openid/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ export class OpenIdAuthRoutes {
const user = await this.securityClient.authenticateWithHeader(
request,
this.openIdAuthConfig.authHeaderName as string,
`Bearer ${tokenResponse.idToken}`
`Bearer ${tokenResponse.idToken}`,
undefined
);

// set to cookie
Expand Down
15 changes: 10 additions & 5 deletions server/auth/types/saml/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { SecurityPluginConfigType } from '../../..';
import { SecurityClient } from '../../../backend/opensearch_security_client';
import { CoreSetup } from '../../../../../../src/core/server';
import { validateNextUrl } from '../../../utils/next_url';
import { AuthType, SAML_AUTH_LOGIN, SAML_AUTH_LOGOUT } from '../../../../common';
import { AuthType, SAML_AUTH_LOGIN, SAML_AUTH_LOGOUT, SAML_AUTH_REQUEST_TYPE } from '../../../../common';

import {
clearSplitCookies,
Expand Down Expand Up @@ -120,6 +120,7 @@ export class SamlAuthRoutes {
`${this.coreSetup.http.basePath.serverBasePath}/app/opensearch-dashboards`;
redirectHash = cookie.saml?.redirectHash || false;
}
console.log(requestId);
if (!requestId) {
return response.badRequest({
body: 'Invalid requestId',
Expand All @@ -134,12 +135,14 @@ export class SamlAuthRoutes {
const credentials = await this.securityClient.authToken(
requestId,
request.body.SAMLResponse,
undefined
undefined,
SAML_AUTH_REQUEST_TYPE
);
const user = await this.securityClient.authenticateWithHeader(
request,
'authorization',
credentials.authorization
credentials.authorization,
SAML_AUTH_REQUEST_TYPE
);

let expiryTime = Date.now() + this.config.session.ttl;
Expand Down Expand Up @@ -211,12 +214,14 @@ export class SamlAuthRoutes {
const credentials = await this.securityClient.authToken(
undefined,
request.body.SAMLResponse,
acsEndpoint
acsEndpoint,
SAML_AUTH_REQUEST_TYPE
);
const user = await this.securityClient.authenticateWithHeader(
request,
'authorization',
credentials.authorization
credentials.authorization,
SAML_AUTH_REQUEST_TYPE
);

let expiryTime = Date.now() + this.config.session.ttl;
Expand Down
15 changes: 11 additions & 4 deletions server/backend/opensearch_security_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@

import { ILegacyClusterClient, OpenSearchDashboardsRequest } from '../../../../src/core/server';
import { User } from '../auth/user';
import { getAuthInfo } from '../../public/utils/auth-info-utils';
import { TenancyConfigSettings } from '../../public/apps/configuration/panels/tenancy-config/types';
import { AUTH_REQUEST_TYPE_HEADER, SAML_AUTH_REQUEST_TYPE } from '../../common';


export class SecurityClient {
constructor(private readonly esClient: ILegacyClusterClient) {}
Expand Down Expand Up @@ -52,8 +53,9 @@ export class SecurityClient {
request: OpenSearchDashboardsRequest,
headerName: string,
headerValue: string,
authRequestType: string | undefined,
whitelistedHeadersAndValues: any = {},
additionalAuthHeaders: any = {}
additionalAuthHeaders: any = {},
): Promise<User> {
try {
const credentials: any = {
Expand All @@ -64,6 +66,7 @@ export class SecurityClient {
if (headerValue) {
headers[headerName] = headerValue;
}
headers.AUTH_REQUEST_TYPE_HEADER = authRequestType;

// cannot get config elasticsearch.requestHeadersWhitelist from kibana.yml file in new platfrom
// meanwhile, do we really need to save all headers in cookie?
Expand Down Expand Up @@ -183,7 +186,9 @@ export class SecurityClient {
public async getSamlHeader(request: OpenSearchDashboardsRequest) {
try {
// response is expected to be an error
await this.esClient.asScoped(request).callAsCurrentUser('opensearch_security.authinfo');
await this.esClient.asScoped(request).callAsCurrentUser('opensearch_security.authinfo', {
[AUTH_REQUEST_TYPE_HEADER]: SAML_AUTH_REQUEST_TYPE,
});
} catch (error: any) {
// the error looks like
// wwwAuthenticateDirective:
Expand Down Expand Up @@ -221,7 +226,8 @@ export class SecurityClient {
public async authToken(
requestId: string | undefined,
samlResponse: any,
acsEndpoint: any | undefined = undefined
acsEndpoint: any | undefined = undefined,
authRequestType: string | undefined
) {
const body = {
RequestId: requestId,
Expand All @@ -231,6 +237,7 @@ export class SecurityClient {
try {
return await this.esClient.asScoped().callAsCurrentUser('opensearch_security.authtoken', {
body,
[AUTH_REQUEST_TYPE_HEADER]: authRequestType,
});
} catch (error: any) {
console.log(error);
Expand Down
1 change: 0 additions & 1 deletion server/readonly/readonly_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
isPrivateTenant,
LOGIN_PAGE_URI,
CUSTOM_ERROR_PAGE_URI,
ANONYMOUS_AUTH_HEADER,
} from '../../common';
import { SecurityClient } from '../backend/opensearch_security_client';
import { IAuthenticationType, OpenSearchAuthInfo } from '../auth/types/authentication_type';
Expand Down
7 changes: 2 additions & 5 deletions test/jest_integration/basic_auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import {
} from '../constant';
import { getAuthCookie, extractAuthCookie } from '../helper/cookie';
import wreck from '@hapi/wreck';
import { ANONYMOUS_AUTH_HEADER } from '../../common';

describe('start OpenSearch Dashboards server', () => {
let root: Root;
Expand Down Expand Up @@ -141,8 +140,7 @@ describe('start OpenSearch Dashboards server', () => {

it('can access home page as anonymous user', async () => {
const response = await osdTestServer.request
.get(root, '/app/home#/')
.set(AUTHORIZATION_HEADER_NAME, ANONYMOUS_AUTH_HEADER);
.get(root, '/app/home#/');
expect(response.status).toEqual(200);
});

Expand All @@ -163,8 +161,7 @@ describe('start OpenSearch Dashboards server', () => {

it('call authinfo API as anonymous user', async () => {
const response = await osdTestServer.request
.get(root, '/api/v1/auth/authinfo')
.set(AUTHORIZATION_HEADER_NAME, ANONYMOUS_AUTH_HEADER);
.get(root, '/api/v1/auth/authinfo');
expect(response.status).toEqual(200);
});

Expand Down

0 comments on commit ca8b4ef

Please sign in to comment.