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
14 changes: 14 additions & 0 deletions common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,29 @@ export const PLUGIN_NAME = 'security-dashboards-plugin';

export const APP_ID_LOGIN = 'login';
export const APP_ID_CUSTOMERROR = 'customerror';
export const OPENDISTRO_SECURITY_ANONYMOUS = 'opendistro_security_anonymous';

export const API_PREFIX = '/api/v1';
export const CONFIGURATION_API_PREFIX = 'configuration';
export const API_ENDPOINT_AUTHINFO = API_PREFIX + '/auth/authinfo';
export const API_ENDPOINT_AUTHTYPE = API_PREFIX + '/auth/type';
export const LOGIN_PAGE_URI = '/app/' + APP_ID_LOGIN;
export const CUSTOM_ERROR_PAGE_URI = '/app/' + APP_ID_CUSTOMERROR;
export const API_AUTH_LOGIN = '/auth/login';
export const API_AUTH_LOGOUT = '/auth/logout';
export const OPENID_AUTH_LOGIN = '/auth/openid/login';
export const SAML_AUTH_LOGIN = '/auth/saml/login';
export const ANONYMOUS_AUTH_LOGIN = '/auth/anonymous';
export const SAML_AUTH_LOGIN_WITH_FRAGMENT = '/auth/saml/captureUrlFragment?nextUrl=%2F';

export const OPENID_AUTH_LOGOUT = '/auth/openid/logout';
export const SAML_AUTH_LOGOUT = '/auth/saml/logout';
export const ANONYMOUS_AUTH_LOGOUT = '/auth/anonymous/logout';

export const ERROR_MISSING_ROLE_PATH = '/missing-role';
export const AUTH_HEADER_NAME = 'authorization';
export const AUTH_GRANT_TYPE = 'authorization_code';
export const AUTH_RESPONSE_TYPE = 'code';

export const GLOBAL_TENANT_SYMBOL = '';
export const PRIVATE_TENANT_SYMBOL = '__user__';
Expand All @@ -42,6 +55,7 @@ export enum AuthType {
JWT = 'jwt',
SAML = 'saml',
PROXY = 'proxy',
ANONYMOUS = 'anonymous',
}

/**
Expand Down
18 changes: 17 additions & 1 deletion public/apps/account/account-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ import { CoreStart } from 'opensearch-dashboards/public';
import { AccountNavButton } from './account-nav-button';
import { fetchAccountInfoSafe } from './utils';
import { ClientConfigType } from '../../types';
import { CUSTOM_ERROR_PAGE_URI, ERROR_MISSING_ROLE_PATH } from '../../../common';
import { AuthType, CUSTOM_ERROR_PAGE_URI, ERROR_MISSING_ROLE_PATH } from '../../../common';
import { fetchCurrentTenant, selectTenant } from '../configuration/utils/tenant-utils';
import {
getSavedTenant,
getShouldShowTenantPopup,
setShouldShowTenantPopup,
} from '../../utils/storage-utils';
import { constructErrorMessageAndLog } from '../error-utils';
import { fetchCurrentAuthType } from '../../utils/logout-utils';

function tenantSpecifiedInUrl() {
return (
Expand All @@ -36,6 +37,20 @@ function tenantSpecifiedInUrl() {
}

export async function setupTopNavButton(coreStart: CoreStart, config: ClientConfigType) {
const authType = config.auth?.type;
let currAuthType = '';
if (typeof authType === 'string') {
currAuthType = authType;
} else if (Array.isArray(authType) && authType.length === 1) {
currAuthType = authType[0];
} else {
try {
currAuthType = (await fetchCurrentAuthType(coreStart.http))?.currentAuthType;
} catch (e) {
currAuthType = AuthType.BASIC;
}
}

const accountInfo = (await fetchAccountInfoSafe(coreStart.http))?.data;
if (accountInfo) {
// Missing role error
Expand Down Expand Up @@ -94,6 +109,7 @@ export async function setupTopNavButton(coreStart: CoreStart, config: ClientConf
username={accountInfo.user_name}
tenant={tenant}
config={config}
currAuthType={currAuthType.toLowerCase()}
/>,
element
);
Expand Down
3 changes: 2 additions & 1 deletion public/apps/account/account-nav-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export function AccountNavButton(props: {
username: string;
tenant?: string;
config: ClientConfigType;
currAuthType: string;
}) {
const [isPopoverOpen, setPopoverOpen] = React.useState<boolean>(false);
const [modal, setModal] = React.useState<React.ReactNode>(null);
Expand Down Expand Up @@ -137,7 +138,7 @@ export function AccountNavButton(props: {
</>
)}
<LogoutButton
authType={props.config.auth.type}
authType={props.currAuthType}
http={props.coreStart.http}
divider={horizontalRule}
logoutUrl={props.config.auth.logout_url}
Expand Down
15 changes: 9 additions & 6 deletions public/apps/account/log-out-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,43 +16,46 @@
import React from 'react';
import { EuiButtonEmpty } from '@elastic/eui';
import { HttpStart } from 'opensearch-dashboards/public';
import { logout, samlLogout } from './utils';
import { externalLogout, logout } from './utils';
import { AuthType, OPENID_AUTH_LOGOUT, SAML_AUTH_LOGOUT } from '../../../common';
import { setShouldShowTenantPopup } from '../../utils/storage-utils';

export function LogoutButton(props: {
authType: string;
http: HttpStart;
divider: JSX.Element;
logoutUrl?: string;
}) {
if (props.authType === 'openid') {
if (props.authType === AuthType.OPEN_ID) {
return (
<div>
{props.divider}
<EuiButtonEmpty
data-test-subj="log-out-2"
color="danger"
size="xs"
href={`${props.http.basePath.serverBasePath}/auth/logout`}
onClick={() => externalLogout(props.http, OPENID_AUTH_LOGOUT)}
>
Log out
</EuiButtonEmpty>
</div>
);
} else if (props.authType === 'saml') {
} else if (props.authType === AuthType.SAML) {
return (
<div>
{props.divider}
<EuiButtonEmpty
data-test-subj="log-out-1"
color="danger"
size="xs"
onClick={() => samlLogout(props.http)}
onClick={() => externalLogout(props.http, SAML_AUTH_LOGOUT)}
>
Log out
</EuiButtonEmpty>
</div>
);
} else if (props.authType === 'proxy') {
} else if (props.authType === AuthType.PROXY) {
setShouldShowTenantPopup(null);
return <div />;
} else {
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,50 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Account menu - Log out button renders renders when auth type is MultiAuth: basicauth 1`] = `
<div>
<EuiButtonEmpty
color="danger"
data-test-subj="log-out-3"
onClick={[Function]}
size="xs"
>
Log out
</EuiButtonEmpty>
</div>
`;

exports[`Account menu - Log out button renders renders when auth type is MultiAuth: openid 1`] = `
<div>
<EuiButtonEmpty
color="danger"
data-test-subj="log-out-2"
onClick={[Function]}
size="xs"
>
Log out
</EuiButtonEmpty>
</div>
`;

exports[`Account menu - Log out button renders renders when auth type is MultiAuth: saml 1`] = `
<div>
<EuiButtonEmpty
color="danger"
data-test-subj="log-out-1"
onClick={[Function]}
size="xs"
>
Log out
</EuiButtonEmpty>
</div>
`;

exports[`Account menu - Log out button renders renders when auth type is OpenId 1`] = `
<div>
<EuiButtonEmpty
color="danger"
data-test-subj="log-out-2"
href="/auth/logout"
onClick={[Function]}
size="xs"
>
Log out
Expand Down
6 changes: 6 additions & 0 deletions public/apps/account/test/account-app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
getSavedTenant,
} from '../../../utils/storage-utils';
import { fetchAccountInfoSafe } from '../utils';
import { fetchCurrentAuthType } from '../../../utils/logout-utils';
import { fetchCurrentTenant, selectTenant } from '../../configuration/utils/tenant-utils';

jest.mock('../../../utils/storage-utils', () => ({
Expand All @@ -34,6 +35,10 @@ jest.mock('../utils', () => ({
fetchAccountInfoSafe: jest.fn(),
}));

jest.mock('../../../utils/logout-utils', () => ({
fetchCurrentAuthType: jest.fn(),
}));

jest.mock('../../configuration/utils/tenant-utils', () => ({
selectTenant: jest.fn(),
fetchCurrentTenant: jest.fn(),
Expand Down Expand Up @@ -66,6 +71,7 @@ describe('Account app', () => {

beforeAll(() => {
(fetchAccountInfoSafe as jest.Mock).mockResolvedValue(mockAccountInfo);
(fetchCurrentAuthType as jest.Mock).mockResolvedValue('dummy');
(fetchCurrentTenant as jest.Mock).mockResolvedValue(mockTenant);
});

Expand Down
3 changes: 3 additions & 0 deletions public/apps/account/test/account-nav-button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ describe('Account navigation button', () => {
username={userName}
tenant="tenant1"
config={config as any}
currAuthType={'dummy'}
/>
);
});
Expand All @@ -77,6 +78,7 @@ describe('Account navigation button', () => {
username={userName}
tenant="tenant1"
config={config as any}
currAuthType={'dummy'}
/>
);
expect(setState).toBeCalledTimes(1);
Expand Down Expand Up @@ -137,6 +139,7 @@ describe('Account navigation button, multitenancy disabled', () => {
isInternalUser={true}
username={userName}
config={config as any}
currAuthType={'dummy'}
/>
);
expect(setState).toBeCalledTimes(0);
Expand Down
23 changes: 23 additions & 0 deletions public/apps/account/test/log-out-button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,37 @@ describe('Account menu - Log out button', () => {
OpenId = 'openid',
SAML = 'saml',
Proxy = 'proxy',
Basic = 'basicauth',
}

const mockHttpStart = {
basePath: {
serverBasePath: '',
},
};
const mockDivider = <></>;
describe('renders', () => {
it('renders when auth type is MultiAuth: openid', () => {
const component = shallow(
<LogoutButton authType={authType.OpenId} http={mockHttpStart} divider={mockDivider} />
);
expect(component).toMatchSnapshot();
});

it('renders when auth type is MultiAuth: saml', () => {
const component = shallow(
<LogoutButton authType={authType.SAML} http={mockHttpStart} divider={mockDivider} />
);
expect(component).toMatchSnapshot();
});

it('renders when auth type is MultiAuth: basicauth', () => {
const component = shallow(
<LogoutButton authType={authType.Basic} http={mockHttpStart} divider={mockDivider} />
);
expect(component).toMatchSnapshot();
});

it('renders when auth type is OpenId', () => {
const component = shallow(
<LogoutButton authType={authType.OpenId} http={mockHttpStart} divider={mockDivider} />
Expand Down
23 changes: 21 additions & 2 deletions public/apps/account/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@
*/

import { HttpStart } from 'opensearch-dashboards/public';
import { API_AUTH_LOGOUT, LOGIN_PAGE_URI } from '../../../common';
import {
API_AUTH_LOGOUT,
LOGIN_PAGE_URI,
OPENID_AUTH_LOGOUT,
SAML_AUTH_LOGOUT,
} from '../../../common';
import { API_ENDPOINT_ACCOUNT_INFO } from './constants';
import { AccountInfo } from './types';
import { httpGet, httpGetWithIgnores, httpPost } from '../configuration/utils/request-utils';
Expand Down Expand Up @@ -43,7 +48,21 @@ export async function logout(http: HttpStart, logoutUrl?: string): Promise<void>
export async function samlLogout(http: HttpStart): Promise<void> {
// This will ensure tenancy is picked up from local storage in the next login.
setShouldShowTenantPopup(null);
window.location.href = `${http.basePath.serverBasePath}${API_AUTH_LOGOUT}`;
window.location.href = `${http.basePath.serverBasePath}${SAML_AUTH_LOGOUT}`;
}

export async function openidLogout(http: HttpStart): Promise<void> {
// This will ensure tenancy is picked up from local storage in the next login.
setShouldShowTenantPopup(null);
sessionStorage.clear();
window.location.href = `${http.basePath.serverBasePath}${OPENID_AUTH_LOGOUT}`;
}

export async function externalLogout(http: HttpStart, logoutEndpoint: string): Promise<void> {
// This will ensure tenancy is picked up from local storage in the next login.
setShouldShowTenantPopup(null);
sessionStorage.clear();
window.location.href = `${http.basePath.serverBasePath}${logoutEndpoint}`;
}

export async function updateNewPassword(
Expand Down
2 changes: 1 addition & 1 deletion public/apps/login/login-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { ClientConfigType } from '../../types';
export function renderApp(
coreStart: CoreStart,
params: AppMountParameters,
config: ClientConfigType['ui']['basicauth']['login']
config: ClientConfigType
) {
ReactDOM.render(<LoginPage http={coreStart.http} config={config} />, params.element);
return () => ReactDOM.unmountComponentAtNode(params.element);
Expand Down
Loading