From bbea19ffb3bc97173475e3cc3c72c280d01dffa1 Mon Sep 17 00:00:00 2001
From: Thom Heymann
Date: Wed, 28 Oct 2020 20:59:14 +0000
Subject: [PATCH 01/15] Account Management: Create API keys
---
package.json | 2 +-
x-pack/package.json | 2 +-
.../account_details_page.tsx | 33 ++
.../change_password.tsx | 0
.../index.ts | 2 +
.../personal_info.tsx | 37 +-
.../account_management_app.ts | 54 --
.../account_management_app.tsx | 168 ++++++
.../account_management_page.test.tsx | 140 -----
.../account_management_page.tsx | 69 ---
.../api_keys_page/api_keys_empty_prompt.tsx | 109 ++++
.../api_keys_page/api_keys_page.tsx | 156 ++++++
.../api_keys_page/api_keys_table.tsx | 139 +++++
.../api_keys_page/create_api_key_flyout.tsx | 273 ++++++++++
.../account_management/api_keys_page/index.ts | 11 +
.../invalidate_api_key_modal.tsx | 100 ++++
.../components/breadcrumb.tsx | 109 ++++
.../components/confirm_modal.tsx | 75 +++
.../field_text_with_copy_button.tsx | 34 ++
.../components/form_flyout.tsx | 82 +++
.../components/tabbed_routes.tsx | 43 ++
.../account_management/components/use_form.ts | 235 ++++++++
.../account_management/personal_info/index.ts | 7 -
.../api_keys/api_keys_api_client.ts | 41 +-
.../security/server/routes/api_keys/create.ts | 42 ++
.../security/server/routes/api_keys/index.ts | 2 +
.../server/routes/views/account_management.ts | 8 +
yarn.lock | 507 +++++++++++++++---
28 files changed, 2097 insertions(+), 383 deletions(-)
create mode 100644 x-pack/plugins/security/public/account_management/account_details_page/account_details_page.tsx
rename x-pack/plugins/security/public/account_management/{change_password => account_details_page}/change_password.tsx (100%)
rename x-pack/plugins/security/public/account_management/{change_password => account_details_page}/index.ts (63%)
rename x-pack/plugins/security/public/account_management/{personal_info => account_details_page}/personal_info.tsx (59%)
delete mode 100644 x-pack/plugins/security/public/account_management/account_management_app.ts
create mode 100644 x-pack/plugins/security/public/account_management/account_management_app.tsx
delete mode 100644 x-pack/plugins/security/public/account_management/account_management_page.test.tsx
delete mode 100644 x-pack/plugins/security/public/account_management/account_management_page.tsx
create mode 100644 x-pack/plugins/security/public/account_management/api_keys_page/api_keys_empty_prompt.tsx
create mode 100644 x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.tsx
create mode 100644 x-pack/plugins/security/public/account_management/api_keys_page/api_keys_table.tsx
create mode 100644 x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
create mode 100644 x-pack/plugins/security/public/account_management/api_keys_page/index.ts
create mode 100644 x-pack/plugins/security/public/account_management/api_keys_page/invalidate_api_key_modal.tsx
create mode 100644 x-pack/plugins/security/public/account_management/components/breadcrumb.tsx
create mode 100644 x-pack/plugins/security/public/account_management/components/confirm_modal.tsx
create mode 100644 x-pack/plugins/security/public/account_management/components/field_text_with_copy_button.tsx
create mode 100644 x-pack/plugins/security/public/account_management/components/form_flyout.tsx
create mode 100644 x-pack/plugins/security/public/account_management/components/tabbed_routes.tsx
create mode 100644 x-pack/plugins/security/public/account_management/components/use_form.ts
delete mode 100644 x-pack/plugins/security/public/account_management/personal_info/index.ts
create mode 100644 x-pack/plugins/security/server/routes/api_keys/create.ts
diff --git a/package.json b/package.json
index 3a2d13fd5ef3bc..7c0b073fda0f23 100644
--- a/package.json
+++ b/package.json
@@ -199,7 +199,7 @@
"react-dom": "^16.12.0",
"react-input-range": "^1.3.0",
"react-router": "^5.2.0",
- "react-use": "^13.27.0",
+ "react-use": "^15.3.4",
"redux-actions": "^2.6.5",
"redux-thunk": "^2.3.0",
"regenerator-runtime": "^0.13.3",
diff --git a/x-pack/package.json b/x-pack/package.json
index ec4388c0b8b7d0..778d69854489cf 100644
--- a/x-pack/package.json
+++ b/x-pack/package.json
@@ -228,7 +228,7 @@
"react-syntax-highlighter": "^5.7.0",
"react-test-renderer": "^16.12.0",
"react-tiny-virtual-list": "^2.2.0",
- "react-use": "^13.27.0",
+ "react-use": "^15.3.4",
"react-virtualized": "^9.21.2",
"react-vis": "^1.8.1",
"react-visibility-sensor": "^5.1.1",
diff --git a/x-pack/plugins/security/public/account_management/account_details_page/account_details_page.tsx b/x-pack/plugins/security/public/account_management/account_details_page/account_details_page.tsx
new file mode 100644
index 00000000000000..0d446a631a37df
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/account_details_page/account_details_page.tsx
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent } from 'react';
+import { ChangePassword } from './change_password';
+import { PersonalInfo } from './personal_info';
+import { AuthenticatedUser } from '../../../common/model';
+import { UserAPIClient } from '../../management';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
+import { NotificationsSetup } from '../../../../../../src/core/public';
+
+export interface AccountDetailsPageProps {
+ user: AuthenticatedUser;
+ notifications: NotificationsSetup;
+}
+
+export const AccountDetailsPage: FunctionComponent = ({
+ user,
+ notifications,
+}) => {
+ const { services } = useKibana();
+ const userAPIClient = new UserAPIClient(services.http!);
+
+ return (
+ <>
+
+
+ >
+ );
+};
diff --git a/x-pack/plugins/security/public/account_management/change_password/change_password.tsx b/x-pack/plugins/security/public/account_management/account_details_page/change_password.tsx
similarity index 100%
rename from x-pack/plugins/security/public/account_management/change_password/change_password.tsx
rename to x-pack/plugins/security/public/account_management/account_details_page/change_password.tsx
diff --git a/x-pack/plugins/security/public/account_management/change_password/index.ts b/x-pack/plugins/security/public/account_management/account_details_page/index.ts
similarity index 63%
rename from x-pack/plugins/security/public/account_management/change_password/index.ts
rename to x-pack/plugins/security/public/account_management/account_details_page/index.ts
index ccd810bb814c00..a1b31254e87cb2 100644
--- a/x-pack/plugins/security/public/account_management/change_password/index.ts
+++ b/x-pack/plugins/security/public/account_management/account_details_page/index.ts
@@ -4,4 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
+export { AccountDetailsPage as default } from './account_details_page'; // eslint-disable-line import/no-default-export
export { ChangePassword } from './change_password';
+export { PersonalInfo } from './personal_info';
diff --git a/x-pack/plugins/security/public/account_management/personal_info/personal_info.tsx b/x-pack/plugins/security/public/account_management/account_details_page/personal_info.tsx
similarity index 59%
rename from x-pack/plugins/security/public/account_management/personal_info/personal_info.tsx
rename to x-pack/plugins/security/public/account_management/account_details_page/personal_info.tsx
index 9cbbc242e8400e..a46b10ea997b8f 100644
--- a/x-pack/plugins/security/public/account_management/personal_info/personal_info.tsx
+++ b/x-pack/plugins/security/public/account_management/account_details_page/personal_info.tsx
@@ -4,7 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
-import { EuiDescribedFormGroup, EuiFormRow, EuiText } from '@elastic/eui';
+import {
+ EuiDescribedFormGroup,
+ EuiDescriptionList,
+ EuiDescriptionListTitle,
+ EuiDescriptionListDescription,
+} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { AuthenticatedUser } from '../../../common/model';
@@ -31,23 +36,19 @@ export const PersonalInfo = (props: Props) => {
/>
}
>
-
-
-
- -
- {props.user.username}
-
- -
- {props.user.email || (
-
- )}
-
-
-
-
+
+
+ {props.user.username}
+
+
+ {props.user.email || (
+
+ )}
+
+
);
};
diff --git a/x-pack/plugins/security/public/account_management/account_management_app.ts b/x-pack/plugins/security/public/account_management/account_management_app.ts
deleted file mode 100644
index 0bb7785259c0ea..00000000000000
--- a/x-pack/plugins/security/public/account_management/account_management_app.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { i18n } from '@kbn/i18n';
-import {
- ApplicationSetup,
- AppMountParameters,
- AppNavLinkStatus,
- StartServicesAccessor,
-} from '../../../../../src/core/public';
-import { AuthenticationServiceSetup } from '../authentication';
-
-interface CreateDeps {
- application: ApplicationSetup;
- authc: AuthenticationServiceSetup;
- getStartServices: StartServicesAccessor;
-}
-
-export const accountManagementApp = Object.freeze({
- id: 'security_account',
- create({ application, authc, getStartServices }: CreateDeps) {
- const title = i18n.translate('xpack.security.account.breadcrumb', {
- defaultMessage: 'Account Management',
- });
- application.register({
- id: this.id,
- title,
- navLinkStatus: AppNavLinkStatus.hidden,
- appRoute: '/security/account',
- async mount({ element }: AppMountParameters) {
- const [
- [coreStart],
- { renderAccountManagementPage },
- { UserAPIClient },
- ] = await Promise.all([
- getStartServices(),
- import('./account_management_page'),
- import('../management'),
- ]);
-
- coreStart.chrome.setBreadcrumbs([{ text: title }]);
-
- return renderAccountManagementPage(coreStart.i18n, element, {
- authc,
- notifications: coreStart.notifications,
- userAPIClient: new UserAPIClient(coreStart.http),
- });
- },
- });
- },
-});
diff --git a/x-pack/plugins/security/public/account_management/account_management_app.tsx b/x-pack/plugins/security/public/account_management/account_management_app.tsx
new file mode 100644
index 00000000000000..03b7f7be1e7ab9
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/account_management_app.tsx
@@ -0,0 +1,168 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { lazy, Suspense, FunctionComponent } from 'react';
+import ReactDOM from 'react-dom';
+import { Router, useHistory } from 'react-router-dom';
+import { useAsync } from 'react-use';
+import { i18n } from '@kbn/i18n';
+import {
+ EuiAvatar,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiLoadingSpinner,
+ EuiPage,
+ EuiPageBody,
+ EuiPageContent,
+ EuiPageContentBody,
+ EuiPageContentHeader,
+ EuiPageContentHeaderSection,
+ EuiText,
+ EuiTitle,
+} from '@elastic/eui';
+import {
+ ApplicationSetup,
+ AppMountParameters,
+ AppNavLinkStatus,
+ StartServicesAccessor,
+ NotificationsSetup,
+} from '../../../../../src/core/public';
+import {
+ createKibanaReactContext,
+ reactRouterNavigate,
+} from '../../../../../src/plugins/kibana_react/public';
+import { getUserDisplayName } from '../../common/model';
+import { AuthenticationServiceSetup } from '../authentication';
+import { TabbedRoutes } from './components/tabbed_routes';
+import { Breadcrumb } from './components/breadcrumb';
+
+interface CreateDeps {
+ application: ApplicationSetup;
+ authc: AuthenticationServiceSetup;
+ getStartServices: StartServicesAccessor;
+}
+
+export const accountManagementApp = Object.freeze({
+ id: 'security_account',
+ create({ application, authc, getStartServices }: CreateDeps) {
+ const title = i18n.translate('xpack.security.account.breadcrumb', {
+ defaultMessage: 'Account Management',
+ });
+
+ application.register({
+ id: this.id,
+ title,
+ navLinkStatus: AppNavLinkStatus.hidden,
+ appRoute: '/security/account',
+ async mount({ element, history }: AppMountParameters) {
+ const [coreStart] = await getStartServices();
+
+ const { Provider: KibanaProvider } = createKibanaReactContext(coreStart);
+ const { Context: IntlProvider } = coreStart.i18n;
+
+ ReactDOM.render(
+
+
+
+
+
+
+
+
+ ,
+ element
+ );
+
+ return () => ReactDOM.unmountComponentAtNode(element);
+ },
+ });
+ },
+});
+
+export interface AccountManagementProps {
+ authc: AuthenticationServiceSetup;
+ notifications: NotificationsSetup;
+}
+
+export const AccountManagement: FunctionComponent = ({
+ authc,
+ notifications,
+}) => {
+ const history = useHistory();
+ const userState = useAsync(authc.getCurrentUser, [authc]);
+ const AccountDetailsPage = lazy(() => import('./account_details_page'));
+ const ApiKeysPage = lazy(() => import('./api_keys_page'));
+
+ if (!userState.value) {
+ return null;
+ }
+
+ const displayName = getUserDisplayName(userState.value);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {displayName}
+
+ {userState.value.email}
+
+
+
+
+
+ }>
+
+
+ ),
+ },
+ {
+ id: 'api-keys',
+ path: '/api-keys',
+ to: 'api-keys',
+ name: i18n.translate('xpack.security.accountManagement.ApiKeysTab', {
+ defaultMessage: 'API Keys',
+ }),
+ content: (
+
+ }>
+
+
+
+ ),
+ },
+ ]}
+ />
+
+
+
+
+ );
+};
diff --git a/x-pack/plugins/security/public/account_management/account_management_page.test.tsx b/x-pack/plugins/security/public/account_management/account_management_page.test.tsx
deleted file mode 100644
index b677f6a1fe8bba..00000000000000
--- a/x-pack/plugins/security/public/account_management/account_management_page.test.tsx
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-import React from 'react';
-import { act } from '@testing-library/react';
-import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers';
-import { AuthenticatedUser } from '../../common/model';
-import { AccountManagementPage } from './account_management_page';
-import { coreMock } from 'src/core/public/mocks';
-import { mockAuthenticatedUser } from '../../common/model/authenticated_user.mock';
-import { securityMock } from '../mocks';
-import { userAPIClientMock } from '../management/users/index.mock';
-
-interface Options {
- withFullName?: boolean;
- withEmail?: boolean;
- realm?: string;
-}
-const createUser = ({ withFullName = true, withEmail = true, realm = 'native' }: Options = {}) => {
- return mockAuthenticatedUser({
- full_name: withFullName ? 'Casey Smith' : '',
- username: 'csmith',
- email: withEmail ? 'csmith@domain.com' : '',
- roles: [],
- authentication_realm: {
- type: realm,
- name: realm,
- },
- lookup_realm: {
- type: realm,
- name: realm,
- },
- });
-};
-
-function getSecuritySetupMock({ currentUser }: { currentUser: AuthenticatedUser }) {
- const securitySetupMock = securityMock.createSetup();
- securitySetupMock.authc.getCurrentUser.mockResolvedValue(currentUser);
- return securitySetupMock;
-}
-
-describe('', () => {
- it(`displays users full name, username, and email address`, async () => {
- const user = createUser();
- const wrapper = mountWithIntl(
-
- );
-
- await act(async () => {
- await nextTick();
- wrapper.update();
- });
-
- expect(wrapper.find('EuiText[data-test-subj="userDisplayName"]').text()).toEqual(
- user.full_name
- );
- expect(wrapper.find('[data-test-subj="username"]').text()).toEqual(user.username);
- expect(wrapper.find('[data-test-subj="email"]').text()).toEqual(user.email);
- });
-
- it(`displays username when full_name is not provided`, async () => {
- const user = createUser({ withFullName: false });
- const wrapper = mountWithIntl(
-
- );
-
- await act(async () => {
- await nextTick();
- wrapper.update();
- });
-
- expect(wrapper.find('EuiText[data-test-subj="userDisplayName"]').text()).toEqual(user.username);
- });
-
- it(`displays a placeholder when no email address is provided`, async () => {
- const user = createUser({ withEmail: false });
- const wrapper = mountWithIntl(
-
- );
-
- await act(async () => {
- await nextTick();
- wrapper.update();
- });
-
- expect(wrapper.find('[data-test-subj="email"]').text()).toEqual('no email address');
- });
-
- it(`displays change password form for users in the native realm`, async () => {
- const user = createUser();
- const wrapper = mountWithIntl(
-
- );
-
- await act(async () => {
- await nextTick();
- wrapper.update();
- });
-
- expect(wrapper.find('EuiFieldPassword[data-test-subj="currentPassword"]')).toHaveLength(1);
- expect(wrapper.find('EuiFieldPassword[data-test-subj="newPassword"]')).toHaveLength(1);
- });
-
- it(`does not display change password form for users in the saml realm`, async () => {
- const user = createUser({ realm: 'saml' });
- const wrapper = mountWithIntl(
-
- );
-
- await act(async () => {
- await nextTick();
- wrapper.update();
- });
-
- expect(wrapper.find('EuiFieldText[data-test-subj="currentPassword"]')).toHaveLength(0);
- expect(wrapper.find('EuiFieldText[data-test-subj="newPassword"]')).toHaveLength(0);
- });
-});
diff --git a/x-pack/plugins/security/public/account_management/account_management_page.tsx b/x-pack/plugins/security/public/account_management/account_management_page.tsx
deleted file mode 100644
index 2c870bf788ceb7..00000000000000
--- a/x-pack/plugins/security/public/account_management/account_management_page.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-import React, { useEffect, useState } from 'react';
-import ReactDOM from 'react-dom';
-import { EuiPage, EuiPageBody, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui';
-import type { PublicMethodsOf } from '@kbn/utility-types';
-import { CoreStart, NotificationsStart } from 'src/core/public';
-import { getUserDisplayName, AuthenticatedUser } from '../../common/model';
-import { AuthenticationServiceSetup } from '../authentication';
-import { UserAPIClient } from '../management';
-import { ChangePassword } from './change_password';
-import { PersonalInfo } from './personal_info';
-
-interface Props {
- authc: AuthenticationServiceSetup;
- userAPIClient: PublicMethodsOf;
- notifications: NotificationsStart;
-}
-
-export const AccountManagementPage = ({ userAPIClient, authc, notifications }: Props) => {
- const [currentUser, setCurrentUser] = useState(null);
- useEffect(() => {
- authc.getCurrentUser().then(setCurrentUser);
- }, [authc]);
-
- if (!currentUser) {
- return null;
- }
-
- return (
-
-
-
-
- {getUserDisplayName(currentUser)}
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export function renderAccountManagementPage(
- i18nStart: CoreStart['i18n'],
- element: Element,
- props: Props
-) {
- ReactDOM.render(
-
-
- ,
- element
- );
-
- return () => ReactDOM.unmountComponentAtNode(element);
-}
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_empty_prompt.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_empty_prompt.tsx
new file mode 100644
index 00000000000000..085121cf9e127b
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_empty_prompt.tsx
@@ -0,0 +1,109 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent } from 'react';
+import { EuiLink, EuiEmptyPrompt } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
+
+export interface ApiKeysEmptyPromptProps {
+ error?: Error;
+}
+
+export const ApiKeysEmptyPrompt: FunctionComponent = ({
+ error,
+ children,
+}) => {
+ const { services } = useKibana();
+
+ if (error) {
+ const { statusCode, message = '' } = (error as any).body ?? {};
+
+ if (statusCode === 400 && message.indexOf('[feature_not_enabled_exception]') !== -1) {
+ return (
+
+
+
+
+
+
+
+
+
+ >
+ }
+ />
+ );
+ }
+
+ if (statusCode === 403) {
+ return (
+
+
+
+ }
+ />
+ );
+ }
+
+ return (
+
+
+
+ }
+ actions={children}
+ />
+ );
+ }
+
+ return (
+
+
+
+ }
+ body={
+
+
+
+ }
+ actions={children}
+ />
+ );
+};
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.tsx
new file mode 100644
index 00000000000000..5a364a2c3a353d
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.tsx
@@ -0,0 +1,156 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent, useState, useEffect } from 'react';
+import { Route, useHistory } from 'react-router-dom';
+import { useAsyncFn } from 'react-use';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiButton, EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
+import { reactRouterNavigate, useKibana } from '../../../../../../src/plugins/kibana_react/public';
+import { SectionLoading } from '../../../../../../src/plugins/es_ui_shared/public';
+import { ApiKey } from '../../../common/model';
+import {
+ APIKeysAPIClient,
+ CreateApiKeyResponse,
+} from '../../management/api_keys/api_keys_api_client';
+import { FieldTextWithCopyButton } from '../components/field_text_with_copy_button';
+import { Breadcrumb } from '../components/breadcrumb';
+import { CreateApiKeyFlyout } from './create_api_key_flyout';
+import { InvalidateApiKeyModal } from './invalidate_api_key_modal';
+import { ApiKeysTable } from './api_keys_table';
+import { ApiKeysEmptyPrompt } from './api_keys_empty_prompt';
+
+export const ApiKeysPage: FunctionComponent = () => {
+ const history = useHistory();
+ const { services } = useKibana();
+ const apiKeysApiClient = new APIKeysAPIClient(services.http!);
+ const [state, getApiKeys] = useAsyncFn(apiKeysApiClient.getApiKeys, [services.http]);
+ const [createdApiKey, setCreatedApiKey] = useState();
+ const [apiKeyToInvalidate, setApiKeyToInvalidate] = useState();
+
+ useEffect(() => {
+ getApiKeys();
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
+
+ if (!state.value) {
+ if (state.error && !state.loading) {
+ return (
+
+ getApiKeys()}>
+
+
+
+ );
+ }
+ return (
+
+
+
+ );
+ }
+
+ return (
+ <>
+
+
+ history.push({ pathname: '/api-keys' })}
+ onSuccess={(apiKey) => {
+ history.push({ pathname: '/api-keys' });
+ setCreatedApiKey(apiKey);
+ getApiKeys();
+ }}
+ />
+
+
+
+ {apiKeyToInvalidate && (
+ setApiKeyToInvalidate(undefined)}
+ onSuccess={() => {
+ setApiKeyToInvalidate(undefined);
+ setCreatedApiKey(undefined);
+ getApiKeys();
+ }}
+ />
+ )}
+
+ {!state.loading && state.value.apiKeys.length === 0 ? (
+
+
+
+
+
+ ) : (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ {createdApiKey && !state.loading && (
+ <>
+
+
+
+
+
+
+
+ >
+ )}
+
+
+
+ >
+ )}
+ >
+ );
+};
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_table.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_table.tsx
new file mode 100644
index 00000000000000..cff43273800e2d
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_table.tsx
@@ -0,0 +1,139 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent } from 'react';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+// @ts-ignore
+import { formatDate } from '@elastic/eui/lib/services/format';
+import {
+ EuiBadge,
+ EuiButtonEmpty,
+ EuiHideFor,
+ EuiInMemoryTable,
+ EuiInMemoryTableProps,
+ EuiShowFor,
+ EuiText,
+ EuiButtonIcon,
+} from '@elastic/eui';
+import { ApiKey } from '../../../common/model';
+
+export type ApiKeysTableProps = Omit, 'columns'> & {
+ createdItemId?: ApiKey['id'];
+ onInvalidateItem(item: ApiKey): void;
+};
+
+export const ApiKeysTable: FunctionComponent = ({
+ createdItemId,
+ onInvalidateItem,
+ ...props
+}) => {
+ const actions = [
+ {
+ render: (item: ApiKey) => (
+ <>
+
+ {
+ onInvalidateItem(item);
+ }}
+ />
+
+
+ {
+ onInvalidateItem(item);
+ }}
+ >
+
+
+
+ >
+ ),
+ },
+ ];
+
+ return (
+
+ !expiration ? (
+
+
+
+ ) : (
+ formatDate(expiration)
+ ),
+ width: '25%',
+ mobileOptions: { show: false },
+ },
+ {
+ field: 'creation',
+ name: i18n.translate('xpack.security.accountManagement.apiKeys.createdHeader', {
+ defaultMessage: 'Created',
+ }),
+ render: (creation: number, item: ApiKey) =>
+ item.id === createdItemId ? (
+
+
+
+ ) : (
+ formatDate(creation)
+ ),
+ width: '25%',
+ },
+ {
+ actions,
+ width: '25%',
+ },
+ ]}
+ sorting={{
+ sort: {
+ field: 'creation',
+ direction: 'desc',
+ },
+ }}
+ pagination={true}
+ />
+ );
+};
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
new file mode 100644
index 00000000000000..faafc6809e349c
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
@@ -0,0 +1,273 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useRef, FunctionComponent } from 'react';
+import {
+ EuiCallOut,
+ EuiCodeEditor,
+ EuiFieldNumber,
+ EuiFieldText,
+ EuiForm,
+ EuiFormRow,
+ EuiSpacer,
+ EuiSwitch,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import {
+ APIKeysAPIClient,
+ CreateApiKeyRequest,
+ CreateApiKeyResponse,
+} from '../../management/api_keys/api_keys_api_client';
+import { useForm, ValidationErrors } from '../components/use_form';
+import { FormFlyout, FormFlyoutProps } from '../components/form_flyout';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
+
+export interface FormValues {
+ name: string;
+ expiration: string;
+ customExpiration: boolean;
+ customPriviliges: boolean;
+ role_descriptors: string;
+}
+
+export interface CreateApiKeyFlyoutProps {
+ defaultValues?: Partial;
+ onSuccess?: (apiKey: CreateApiKeyResponse) => void;
+ onError?: (error: Error) => void;
+ onClose: FormFlyoutProps['onClose'];
+}
+
+export const CreateApiKeyFlyout: FunctionComponent = ({
+ onSuccess,
+ onError,
+ onClose,
+ defaultValues,
+}) => {
+ const { services } = useKibana();
+ const [form, eventHandlers] = useForm(
+ {
+ onSubmit: (values) => new APIKeysAPIClient(services.http!).createApiKey(mapValues(values)),
+ onSubmitSuccess: onSuccess,
+ onSubmitError: onError,
+ validate,
+ defaultValues,
+ },
+ [services.http]
+ );
+ const nameInput = useRef(null);
+
+ return (
+
+ {form.submitError && (
+ <>
+
+ {(form.submitError as any).body?.message || form.submitError.message}
+
+
+ >
+ )}
+
+
+
+
+
+
+ form.setValue('customPriviliges', e.target.checked)}
+ />
+ {form.values.customPriviliges && (
+ <>
+
+
+ form.setValue('role_descriptors', value)}
+ onBlur={() => form.trigger('role_descriptors')}
+ value={form.values.role_descriptors}
+ height="300px"
+ mode="json"
+ />
+
+ >
+ )}
+
+
+ form.setValue('customExpiration', e.target.checked)}
+ />
+ {form.values.customExpiration && (
+ <>
+
+
+
+
+ >
+ )}
+
+
+ );
+};
+
+CreateApiKeyFlyout.defaultProps = {
+ defaultValues: {
+ customExpiration: false,
+ customPriviliges: false,
+ role_descriptors: JSON.stringify(
+ {
+ 'role-a': {
+ cluster: ['all'],
+ index: [
+ {
+ names: ['index-a*'],
+ privileges: ['read'],
+ },
+ ],
+ },
+ 'role-b': {
+ cluster: ['all'],
+ index: [
+ {
+ names: ['index-b*'],
+ privileges: ['all'],
+ },
+ ],
+ },
+ },
+ null,
+ 2
+ ),
+ },
+};
+
+export function validate(v: Partial) {
+ const errors: ValidationErrors = {};
+
+ if (!v.name) {
+ errors.name = 'Enter a name.';
+ }
+
+ if (v.customExpiration && !v.expiration) {
+ errors.expiration = i18n.translate(
+ 'xpack.security.management.apiKeys.createApiKey.successNotification',
+ {
+ defaultMessage: 'Enter a duration or disable the option.',
+ }
+ );
+ }
+
+ if (v.customPriviliges) {
+ if (!v.role_descriptors) {
+ errors.role_descriptors = i18n.translate(
+ 'xpack.security.management.apiKeys.createApiKey.successNotification',
+ {
+ defaultMessage: 'Enter role descriptors or disable the option.',
+ }
+ );
+ } else {
+ try {
+ JSON.parse(v.role_descriptors);
+ } catch (e) {
+ errors.role_descriptors = i18n.translate(
+ 'xpack.security.management.apiKeys.createApiKey.successNotification',
+ {
+ defaultMessage: 'Enter valid JSON.',
+ }
+ );
+ }
+ }
+ }
+
+ return errors;
+}
+
+export function mapValues(values: FormValues): CreateApiKeyRequest {
+ return {
+ name: values.name,
+ expiration: values.customExpiration && values.expiration ? `${values.expiration}d` : undefined,
+ role_descriptors:
+ values.customPriviliges && values.role_descriptors
+ ? JSON.parse(values.role_descriptors)
+ : undefined,
+ };
+}
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/index.ts b/x-pack/plugins/security/public/account_management/api_keys_page/index.ts
new file mode 100644
index 00000000000000..d4821de33b201d
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/index.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+export { ApiKeysEmptyPrompt } from './api_keys_empty_prompt';
+export { ApiKeysPage as default } from './api_keys_page'; // eslint-disable-line import/no-default-export
+export { ApiKeysTable } from './api_keys_table';
+export { CreateApiKeyFlyout } from './create_api_key_flyout';
+export { InvalidateApiKeyModal } from './invalidate_api_key_modal';
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/invalidate_api_key_modal.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/invalidate_api_key_modal.tsx
new file mode 100644
index 00000000000000..11c16cea33b3b8
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/invalidate_api_key_modal.tsx
@@ -0,0 +1,100 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent } from 'react';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiText } from '@elastic/eui';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
+import { ApiKeyToInvalidate } from '../../../common/model';
+import {
+ APIKeysAPIClient,
+ InvalidateApiKeysResponse,
+} from '../../management/api_keys/api_keys_api_client';
+import { useSubmitHandler } from '../components/use_form';
+import { ConfirmModal, ConfirmModalProps } from '../components/confirm_modal';
+
+export interface InvalidateApiKeyModalProps {
+ onCancel: ConfirmModalProps['onCancel'];
+ onSuccess?(result: InvalidateApiKeysResponse): any;
+ onError?(error: Error): any;
+ apiKey: ApiKeyToInvalidate;
+}
+
+export const InvalidateApiKeyModal: FunctionComponent = ({
+ onCancel,
+ onSuccess,
+ onError,
+ apiKey,
+}) => {
+ const { services, notifications } = useKibana();
+ const [state, invalidateApiKeys] = useSubmitHandler(
+ {
+ onSubmit: () =>
+ new APIKeysAPIClient(services.http!).invalidateApiKeys([
+ { id: apiKey.id, name: apiKey.name },
+ ]),
+ onSubmitSuccess: (values) => {
+ notifications.toasts.success({
+ iconType: 'check',
+ title: i18n.translate(
+ 'xpack.security.accountManagement.invalidateApiKey.successMessage',
+ {
+ defaultMessage: 'Invalidated API key “{name}”',
+ values: { name: apiKey.name },
+ }
+ ),
+ toastLifeTimeMs: 3000,
+ });
+ onSuccess?.(values);
+ },
+ onSubmitError: (error) => {
+ notifications.toasts.danger({
+ iconType: 'alert',
+ title: i18n.translate('xpack.security.accountManagement.invalidateApiKey.errorMessage', {
+ defaultMessage: 'Could not invalidate API key “{name}”',
+ values: { name: apiKey.name },
+ }),
+ body: (error as any).body?.message || error.message,
+ toastLifeTimeMs: 1500,
+ });
+ onError?.(error);
+ },
+ },
+ [services.http]
+ );
+
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/x-pack/plugins/security/public/account_management/components/breadcrumb.tsx b/x-pack/plugins/security/public/account_management/components/breadcrumb.tsx
new file mode 100644
index 00000000000000..52cd721b658c35
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/components/breadcrumb.tsx
@@ -0,0 +1,109 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { createContext, useEffect, useRef, FunctionComponent, ReactNode } from 'react';
+import { EuiBreadcrumb } from '@elastic/eui';
+import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
+
+/**
+ * Component that sets breadcrumbs and doc title based on the render tree.
+ *
+ * @example
+ * ```typescript
+ *
+ *
+ * {showForm && (
+ *
+ *
+ *
+ * )}
+ *
+ * ```
+ */
+export const Breadcrumb: FunctionComponent = ({ children, ...breadcrumb }) => (
+
+ {(value) =>
+ value ? (
+
+ {children}
+
+ ) : (
+ {children}
+ )
+ }
+
+);
+
+interface BreadcrumbContext {
+ parents: BreadcrumbProps[];
+ onMount(breadcrumbs: BreadcrumbProps[]): void;
+ onUnmount(breadcrumbs: BreadcrumbProps[]): void;
+}
+
+const { Provider, Consumer } = createContext(undefined);
+
+export interface RootProviderProps {
+ breadcrumb: BreadcrumbProps;
+ children: ReactNode;
+}
+
+export const RootProvider: FunctionComponent = ({ breadcrumb, children }) => {
+ const { services } = useKibana();
+ const breadcrumbsRef = useRef([]);
+
+ const setBreadcrumbs = (breadcrumbs: BreadcrumbProps[]) => {
+ breadcrumbsRef.current = breadcrumbs;
+ services.chrome?.setBreadcrumbs(breadcrumbs);
+ services.chrome?.docTitle.change(getDocTitle(breadcrumbs));
+ };
+
+ return (
+ {
+ if (breadcrumbs.length > breadcrumbsRef.current.length) {
+ setBreadcrumbs(breadcrumbs);
+ }
+ }}
+ onUnmount={(breadcrumbs) => {
+ if (breadcrumbs.length < breadcrumbsRef.current.length) {
+ setBreadcrumbs(breadcrumbs);
+ }
+ }}
+ >
+ {children}
+
+ );
+};
+
+export function getDocTitle(breadcrumbs: BreadcrumbProps[]) {
+ return breadcrumbs
+ .slice()
+ .reverse()
+ .map(({ text }) => text);
+}
+
+export const NestedProvider: FunctionComponent = ({
+ parents,
+ onMount,
+ onUnmount,
+ breadcrumb,
+ children,
+}) => {
+ const nextParents = [...parents, breadcrumb];
+
+ useEffect(() => {
+ onMount(nextParents);
+ return () => onUnmount(parents);
+ }, [breadcrumb.text, breadcrumb.href]); // eslint-disable-line react-hooks/exhaustive-deps
+
+ return {children};
+};
+
+export interface BreadcrumbProps extends EuiBreadcrumb {
+ text: string;
+}
diff --git a/x-pack/plugins/security/public/account_management/components/confirm_modal.tsx b/x-pack/plugins/security/public/account_management/components/confirm_modal.tsx
new file mode 100644
index 00000000000000..ece2dd000490ea
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/components/confirm_modal.tsx
@@ -0,0 +1,75 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent } from 'react';
+import {
+ EuiButton,
+ EuiButtonProps,
+ EuiButtonEmpty,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiModal,
+ EuiModalBody,
+ EuiModalFooter,
+ EuiModalHeader,
+ EuiModalHeaderTitle,
+ EuiModalProps,
+ EuiOverlayMask,
+} from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+
+export interface ConfirmModalProps extends Omit {
+ confirmButtonColor?: EuiButtonProps['color'];
+ confirmButtonText: string;
+ isLoading?: EuiButtonProps['isLoading'];
+ onCancel(): void;
+ onConfirm(): void;
+ ownFocus?: boolean;
+}
+
+export const ConfirmModal: FunctionComponent = ({
+ children,
+ confirmButtonColor,
+ confirmButtonText,
+ isLoading,
+ onCancel,
+ onConfirm,
+ ownFocus,
+ title,
+ ...rest
+}) => {
+ const modal = (
+
+
+ {title}
+
+ {children}
+
+
+
+
+
+
+
+
+
+ {confirmButtonText}
+
+
+
+
+
+ );
+
+ return ownFocus ? {modal} : modal;
+};
+
+ConfirmModal.defaultProps = {
+ ownFocus: true,
+};
diff --git a/x-pack/plugins/security/public/account_management/components/field_text_with_copy_button.tsx b/x-pack/plugins/security/public/account_management/components/field_text_with_copy_button.tsx
new file mode 100644
index 00000000000000..5c32b74f5af6bf
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/components/field_text_with_copy_button.tsx
@@ -0,0 +1,34 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent } from 'react';
+import { EuiButtonIcon, EuiCopy, EuiFieldText, EuiFieldTextProps } from '@elastic/eui';
+
+export interface FieldTextWithCopyButtonProps extends Omit {
+ value: string;
+}
+
+export const FieldTextWithCopyButton: FunctionComponent = (props) => (
+
+ {(copyText) => (
+
+ )}
+
+ }
+ />
+);
+
+FieldTextWithCopyButton.defaultProps = {
+ readOnly: true,
+};
diff --git a/x-pack/plugins/security/public/account_management/components/form_flyout.tsx b/x-pack/plugins/security/public/account_management/components/form_flyout.tsx
new file mode 100644
index 00000000000000..92d282bc972ccf
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/components/form_flyout.tsx
@@ -0,0 +1,82 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { useEffect, FunctionComponent, MouseEventHandler, RefObject } from 'react';
+import { FormattedMessage } from '@kbn/i18n/react';
+import {
+ EuiTitle,
+ EuiFlyout,
+ EuiFlyoutProps,
+ EuiFlyoutHeader,
+ EuiFlyoutBody,
+ EuiFlyoutFooter,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiButton,
+ EuiButtonProps,
+ EuiButtonEmpty,
+ EuiPortal,
+} from '@elastic/eui';
+
+export interface FormFlyoutProps extends Omit {
+ title: string;
+ isLoading?: EuiButtonProps['isLoading'];
+ initialFocus?: RefObject;
+ onSubmit: MouseEventHandler;
+ submitButtonText: string;
+ submitButtonColor?: EuiButtonProps['color'];
+}
+
+export const FormFlyout: FunctionComponent = ({
+ title,
+ submitButtonText,
+ submitButtonColor,
+ onSubmit,
+ isLoading,
+ children,
+ initialFocus,
+ ...rest
+}) => {
+ useEffect(() => {
+ if (initialFocus && initialFocus.current) {
+ initialFocus.current.focus();
+ }
+ }, [initialFocus]);
+
+ const flyout = (
+
+
+
+ {title}
+
+
+ {children}
+
+
+
+
+
+
+
+
+
+ {submitButtonText}
+
+
+
+
+
+ );
+
+ return rest.ownFocus ? {flyout} : flyout;
+};
+
+FormFlyout.defaultProps = {
+ ownFocus: true,
+};
diff --git a/x-pack/plugins/security/public/account_management/components/tabbed_routes.tsx b/x-pack/plugins/security/public/account_management/components/tabbed_routes.tsx
new file mode 100644
index 00000000000000..0acbbd08ba68eb
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/components/tabbed_routes.tsx
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React, { FunctionComponent } from 'react';
+import { EuiTabs, EuiTab, EuiTabbedContentTab, EuiSpacer } from '@elastic/eui';
+import { Switch, Route, RouteProps, RedirectProps } from 'react-router-dom';
+import { reactRouterNavigate } from '../../../../../../src/plugins/kibana_react/public';
+
+export type TabbedRoutesTab = EuiTabbedContentTab &
+ Pick &
+ Pick;
+
+export interface TabbedRoutesProps {
+ routes: TabbedRoutesTab[];
+}
+export const TabbedRoutes: FunctionComponent = ({ routes }) => {
+ return (
+ <>
+
+ {routes.map((route) => (
+
+ {({ match, history }) => (
+
+ {route.name}
+
+ )}
+
+ ))}
+
+
+
+ {routes.map((route) => (
+
+ {route.content}
+
+ ))}
+
+ >
+ );
+};
diff --git a/x-pack/plugins/security/public/account_management/components/use_form.ts b/x-pack/plugins/security/public/account_management/components/use_form.ts
new file mode 100644
index 00000000000000..80021b3a03d16f
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/components/use_form.ts
@@ -0,0 +1,235 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import {
+ useState,
+ useEffect,
+ DependencyList,
+ FocusEventHandler,
+ ChangeEventHandler,
+ ReactEventHandler,
+} from 'react';
+import { useAsyncFn } from 'react-use';
+
+export interface FormOptions extends SubmitHandlerOptions {
+ validate: ValidateFunction;
+ defaultValues?: Partial;
+}
+
+export interface FormProps {
+ onSubmit: ReactEventHandler;
+ onChange: ChangeEventHandler;
+ onBlur: FocusEventHandler;
+ noValidate: boolean;
+}
+
+export type FormReturnTuple = [
+ ValidationState & SubmitState,
+ FormProps
+];
+
+/**
+ * Returns state and {@link HTMLFormElement} event handlers useful for creating
+ * forms with inline validation.
+ *
+ * @see {@link useInlineValidation} if you don't want to use {@link HTMLFormElement}.
+ * @see {@link useSubmitHandler} if you don't require inline validation.
+ *
+ * @example
+ * ```typescript
+ * const [form, eventHandlers] = useForm({
+ * onSubmit: (values) => apiClient.create(values),
+ * validate: (values) => !values.email ? { email: 'Required' } : {}
+ * });
+ *
+ *
+ *
+ * Submit
+ *
+ * ```
+ */
+export function useForm(
+ options: FormOptions,
+ deps?: DependencyList
+): FormReturnTuple {
+ const [submitState, submit] = useSubmitHandler(options, deps);
+ const validationState = useInlineValidation(options.validate, options.defaultValues);
+
+ const eventHandlers: FormProps = {
+ onSubmit: (event) => {
+ event.preventDefault();
+ validationState.handleSubmit(submit);
+ },
+ onChange: (event) => {
+ const { name, type, checked, value } = event.target;
+ validationState.setValue(name, type === 'checkbox' ? checked : value);
+ },
+ onBlur: (event) => {
+ validationState.trigger(event.target.name);
+ },
+ noValidate: true, // Native browser validation gets in the way of EUI
+ };
+
+ return [{ ...validationState, ...submitState }, eventHandlers];
+}
+
+export type ValidateFunction = (values: Partial) => ValidationErrors;
+export type ValidationErrors = Partial>;
+export type DirtyFields = Partial>;
+export type SubmitCallback = (values: Values) => Promise;
+
+export interface ValidationState {
+ setValue(name: string, value: any): void;
+ setError(name: string, message: string): void;
+ trigger(name: string): void;
+ handleSubmit(onSubmit: SubmitCallback): Promise;
+ values: Partial;
+ errors: ValidationErrors;
+ isInvalid: boolean;
+ isSubmitted: boolean;
+}
+
+/**
+ * Returns state useful for creating forms with inline validation.
+ *
+ * @example
+ * ```typescript
+ * const form = useInlineValidation((values) => !values.toggle ? { toggle: 'Required' } : {});
+ *
+ * form.setValue('toggle', e.target.checked)}
+ * onBlur={() => form.trigger('toggle')}
+ * isInvalid={!!form.errors.toggle}
+ * />
+ * apiClient.create(values))}>
+ * Submit
+ *
+ * ```
+ */
+export function useInlineValidation(
+ validate: ValidateFunction,
+ defaultValues: Partial = {}
+): ValidationState {
+ const [values, setValues] = useState>(defaultValues);
+ const [errors, setErrors] = useState>({});
+ const [dirty, setDirty] = useState>({});
+ const [submitCount, setSubmitCount] = useState(0);
+
+ const isSubmitted = submitCount > 0;
+ const isInvalid = Object.keys(errors).length > 0;
+
+ const inlineValidation = (nextValues: Partial, nextDirty: DirtyFields) => {
+ const nextErrors = getIntersection(validate(nextValues), nextDirty);
+ setErrors(nextErrors);
+ if (Object.keys(nextErrors).length === 0) {
+ setSubmitCount(0);
+ }
+ };
+
+ return {
+ setValue: (name, value) => {
+ const nextValues = { ...values, [name]: value };
+ setValues(nextValues);
+ inlineValidation(nextValues, dirty);
+ },
+ setError: (name, message) => {
+ setErrors({ ...errors, [name]: message });
+ setDirty({ ...dirty, [name]: true });
+ },
+ trigger: (name) => {
+ const nextDirty = { ...dirty, [name]: true };
+ setDirty(nextDirty);
+ inlineValidation(values, nextDirty);
+ },
+ handleSubmit: async (onSubmit) => {
+ const nextErrors = validate(values);
+ setDirty({ ...dirty, ...nextErrors });
+ setErrors(nextErrors);
+ setSubmitCount(submitCount + 1);
+ if (Object.keys(nextErrors).length === 0) {
+ return await onSubmit(values as Values);
+ }
+ },
+ values,
+ errors,
+ isInvalid,
+ isSubmitted,
+ };
+}
+
+export function getIntersection(
+ errors: ValidationErrors,
+ dirty: DirtyFields
+) {
+ const names = Object.keys(errors) as Array;
+ return names.reduce>((acc, name) => {
+ if (dirty[name]) {
+ acc[name] = errors[name];
+ }
+ return acc;
+ }, {});
+}
+
+export type AsyncFunction = (...args: any[]) => Promise;
+
+export interface SubmitHandlerOptions {
+ onSubmit: AsyncFunction;
+ onSubmitSuccess?: (result: Result) => void;
+ onSubmitError?: (error: Error) => void;
+}
+
+export interface SubmitState {
+ isSubmitting: boolean;
+ submitError?: Error;
+ submitResult?: Result;
+}
+
+export type SubmitHandlerReturnTuple = [SubmitState, AsyncFunction];
+
+/**
+ * Tracks state of an async function and triggers callbacks with the outcome.
+ *
+ * @example
+ * ```typescript
+ * const [state, deleteUser] = useSubmitHandler({
+ * onSubmit: () => apiClient.deleteUser(),
+ * onSubmitSuccess: (result) => toasts.addSuccess('Deleted user'),
+ * onSubmitError: (error) => toasts.addError('Could not delete user'),
+ * });
+ *
+ *
+ * Delete user
+ *
+ * ```
+ */
+export function useSubmitHandler(
+ { onSubmit, onSubmitSuccess, onSubmitError }: SubmitHandlerOptions,
+ deps?: DependencyList
+): SubmitHandlerReturnTuple {
+ const [state, callback] = useAsyncFn(onSubmit, deps);
+
+ useEffect(() => {
+ if (state.value && onSubmitSuccess) {
+ onSubmitSuccess(state.value);
+ }
+ }, [state.value]); // eslint-disable-line react-hooks/exhaustive-deps
+
+ useEffect(() => {
+ if (state.error && onSubmitError) {
+ onSubmitError(state.error);
+ }
+ }, [state.error]); // eslint-disable-line react-hooks/exhaustive-deps
+
+ return [
+ {
+ isSubmitting: state.loading,
+ submitError: state.loading ? undefined : state.error,
+ submitResult: state.value,
+ },
+ callback,
+ ];
+}
diff --git a/x-pack/plugins/security/public/account_management/personal_info/index.ts b/x-pack/plugins/security/public/account_management/personal_info/index.ts
deleted file mode 100644
index 5980157f5b76e2..00000000000000
--- a/x-pack/plugins/security/public/account_management/personal_info/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export { PersonalInfo } from './personal_info';
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts
index a127379d972413..ac8655a7e49a50 100644
--- a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts
@@ -5,39 +5,60 @@
*/
import { HttpStart } from 'src/core/public';
-import { ApiKey, ApiKeyToInvalidate } from '../../../common/model';
+import { ApiKey, ApiKeyToInvalidate, Role } from '../../../common/model';
-interface CheckPrivilegesResponse {
+export interface CheckPrivilegesResponse {
areApiKeysEnabled: boolean;
isAdmin: boolean;
canManage: boolean;
}
-interface InvalidateApiKeysResponse {
+export interface InvalidateApiKeysResponse {
itemsInvalidated: ApiKeyToInvalidate[];
errors: any[];
}
-interface GetApiKeysResponse {
+export interface GetApiKeysResponse {
apiKeys: ApiKey[];
}
+export interface CreateApiKeyRequest {
+ name: string;
+ expiration?: string;
+ role_descriptors?: {
+ [key in string]: Role['elasticsearch'];
+ };
+}
+
+export interface CreateApiKeyResponse {
+ id: string;
+ name: string;
+ expiration: number;
+ api_key: string;
+}
+
const apiKeysUrl = '/internal/security/api_key';
export class APIKeysAPIClient {
constructor(private readonly http: HttpStart) {}
- public async checkPrivileges() {
+ public checkPrivileges = async () => {
return await this.http.get(`${apiKeysUrl}/privileges`);
- }
+ };
- public async getApiKeys(isAdmin = false) {
+ public getApiKeys = async (isAdmin = false) => {
return await this.http.get(apiKeysUrl, { query: { isAdmin } });
- }
+ };
- public async invalidateApiKeys(apiKeys: ApiKeyToInvalidate[], isAdmin = false) {
+ public invalidateApiKeys = async (apiKeys: ApiKeyToInvalidate[], isAdmin = false) => {
return await this.http.post(`${apiKeysUrl}/invalidate`, {
body: JSON.stringify({ apiKeys, isAdmin }),
});
- }
+ };
+
+ public createApiKey = async (apiKey: CreateApiKeyRequest) => {
+ return await this.http.post(apiKeysUrl, {
+ body: JSON.stringify(apiKey),
+ });
+ };
}
diff --git a/x-pack/plugins/security/server/routes/api_keys/create.ts b/x-pack/plugins/security/server/routes/api_keys/create.ts
new file mode 100644
index 00000000000000..dfef44e9b6d92f
--- /dev/null
+++ b/x-pack/plugins/security/server/routes/api_keys/create.ts
@@ -0,0 +1,42 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { schema } from '@kbn/config-schema';
+import { createLicensedRouteHandler } from '../licensed_route_handler';
+import { ApiKey } from '../../../common/model';
+import { wrapError, wrapIntoCustomErrorResponse } from '../../errors';
+import { RouteDefinitionParams } from '..';
+
+interface ResponseType {
+ itemsInvalidated: Array>;
+ errors: Array & { error: Error }>;
+}
+
+export function defineCreateApiKeyRoutes({ router, clusterClient }: RouteDefinitionParams) {
+ router.post(
+ {
+ path: '/internal/security/api_key',
+ validate: {
+ body: schema.object({
+ name: schema.string(),
+ expiration: schema.maybe(schema.string()),
+ }),
+ },
+ },
+ createLicensedRouteHandler(async (context, request, response) => {
+ const scopedClusterClient = clusterClient.asScoped(request);
+ try {
+ const apiKey = await scopedClusterClient.callAsCurrentUser('shield.createAPIKey', {
+ body: request.body,
+ });
+
+ return response.ok({ body: apiKey });
+ } catch (error) {
+ return response.customError(wrapIntoCustomErrorResponse(error));
+ }
+ })
+ );
+}
diff --git a/x-pack/plugins/security/server/routes/api_keys/index.ts b/x-pack/plugins/security/server/routes/api_keys/index.ts
index 7ac37bbead6136..aa148732e78708 100644
--- a/x-pack/plugins/security/server/routes/api_keys/index.ts
+++ b/x-pack/plugins/security/server/routes/api_keys/index.ts
@@ -8,6 +8,7 @@ import { defineGetApiKeysRoutes } from './get';
import { defineCheckPrivilegesRoutes } from './privileges';
import { defineInvalidateApiKeysRoutes } from './invalidate';
import { defineEnabledApiKeysRoutes } from './enabled';
+import { defineCreateApiKeyRoutes } from './create';
import { RouteDefinitionParams } from '..';
export function defineApiKeysRoutes(params: RouteDefinitionParams) {
@@ -15,4 +16,5 @@ export function defineApiKeysRoutes(params: RouteDefinitionParams) {
defineGetApiKeysRoutes(params);
defineCheckPrivilegesRoutes(params);
defineInvalidateApiKeysRoutes(params);
+ defineCreateApiKeyRoutes(params);
}
diff --git a/x-pack/plugins/security/server/routes/views/account_management.ts b/x-pack/plugins/security/server/routes/views/account_management.ts
index 696a5e12b64c13..2ccca46ea3418f 100644
--- a/x-pack/plugins/security/server/routes/views/account_management.ts
+++ b/x-pack/plugins/security/server/routes/views/account_management.ts
@@ -13,4 +13,12 @@ export function defineAccountManagementRoutes({ httpResources }: RouteDefinition
httpResources.register({ path: '/security/account', validate: false }, (context, req, res) =>
res.renderCoreApp()
);
+ httpResources.register(
+ { path: '/security/account/api-keys', validate: false },
+ (context, req, res) => res.renderCoreApp()
+ );
+ httpResources.register(
+ { path: '/security/account/api-keys/create', validate: false },
+ (context, req, res) => res.renderCoreApp()
+ );
}
diff --git a/yarn.lock b/yarn.lock
index 8de8e0a8c0eb20..bd64b8cdd07e14 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1074,13 +1074,20 @@
dependencies:
regenerator-runtime "^0.12.0"
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.11.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.3", "@babel/runtime@^7.11.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.11.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
dependencies:
regenerator-runtime "^0.13.4"
+"@babel/runtime@^7.1.2":
+ version "7.12.1"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.1.tgz#b4116a6b6711d010b2dad3b7b6e43bf1b9954740"
+ integrity sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/template@^7.10.4", "@babel/template@^7.3.3":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
@@ -4241,10 +4248,10 @@
dependencies:
"@types/sizzle" "*"
-"@types/js-cookie@2.2.5":
- version "2.2.5"
- resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.5.tgz#38dfaacae8623b37cc0b0d27398e574e3fc28b1e"
- integrity sha512-cpmwBRcHJmmZx0OGU7aPVwGWGbs4iKwVYchk9iuMtxNCA2zorwdaTz4GkLgs2WGxiRZRFKnV1k6tRUHX7tBMxg==
+"@types/js-cookie@2.2.6":
+ version "2.2.6"
+ resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.6.tgz#f1a1cb35aff47bc5cfb05cb0c441ca91e914c26f"
+ integrity sha512-+oY0FDTO2GYKEV0YPvSshGq9t7YozVkgvXLty7zogQNuCxBhT9/3INX9Q7H1aRZ4SUDRXAKlJuA4EA5nTt7SNw==
"@types/js-search@^1.4.0":
version "1.4.0"
@@ -5492,10 +5499,10 @@
dependencies:
tslib "^1.9.3"
-"@xobotyi/scrollbar-width@1.9.4":
- version "1.9.4"
- resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.4.tgz#a7dce20b7465bcad29cd6bbb557695e4ea7863cb"
- integrity sha512-o12FCQt/X5n3pgKEWGpt0f/7Eg4mfv3uRwPUrctiOT8ZuxbH3cNLGWfH/8y6KxVJg4L2885ucuXQ6XECZzUiJA==
+"@xobotyi/scrollbar-width@1.9.5":
+ version "1.9.5"
+ resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d"
+ integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
@@ -5754,7 +5761,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da"
integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==
-ajv@^4.7.0:
+ajv@^4.7.0, ajv@^4.9.1:
version "4.11.8"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=
@@ -5762,6 +5769,16 @@ ajv@^4.7.0:
co "^4.6.0"
json-stable-stringify "^1.0.1"
+ajv@^5.0.0:
+ version "5.5.2"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
+ integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=
+ dependencies:
+ co "^4.6.0"
+ fast-deep-equal "^1.0.0"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.3.0"
+
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.5.5, ajv@^6.9.1:
version "6.12.4"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234"
@@ -5772,6 +5789,16 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.5.5, ajv@
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
+ajv@^6.12.3:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
align-text@^0.1.1, align-text@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
@@ -6560,6 +6587,11 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
+assert-plus@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
+ integrity sha1-104bh+ev/A24qttwIfP+SBAasjQ=
+
assert@^1.1.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
@@ -6763,11 +6795,21 @@ await-event@^2.1.0:
resolved "https://registry.yarnpkg.com/await-event/-/await-event-2.1.0.tgz#78e9f92684bae4022f9fa0b5f314a11550f9aa76"
integrity sha1-eOn5JoS65AIvn6C18xShFVD5qnY=
+aws-sign2@~0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
+ integrity sha1-FDQt0428yU0OW4fXY81jYSwOeU8=
+
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
+aws4@^1.2.1:
+ version "1.10.1"
+ resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428"
+ integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==
+
aws4@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
@@ -7557,6 +7599,13 @@ boolbase@^1.0.0, boolbase@~1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
+boom@2.x.x:
+ version "2.10.1"
+ resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
+ integrity sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=
+ dependencies:
+ hoek "2.x.x"
+
boom@7.x.x, boom@^7.1.0, boom@^7.2.0:
version "7.2.2"
resolved "https://registry.yarnpkg.com/boom/-/boom-7.2.2.tgz#ac92101451aa5cea901aed07d881dd32b4f08345"
@@ -8973,7 +9022,7 @@ colorspace@1.1.x:
color "3.0.x"
text-hex "1.0.x"
-combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
+combined-stream@^1.0.5, combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.5, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
@@ -9308,13 +9357,20 @@ copy-props@^2.0.1:
each-props "^1.3.0"
is-plain-object "^2.0.1"
-copy-to-clipboard@^3.0.8, copy-to-clipboard@^3.2.0:
+copy-to-clipboard@^3.0.8:
version "3.2.0"
resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.2.0.tgz#d2724a3ccbfed89706fac8a894872c979ac74467"
integrity sha512-eOZERzvCmxS8HWzugj4Uxl8OJxa7T2k1Gi0X5qavwydHIfuSHq2dTD09LOg/XyGq4Zpb5IsR/2OJ5lbOegz78w==
dependencies:
toggle-selection "^1.0.6"
+copy-to-clipboard@^3.2.0:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae"
+ integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==
+ dependencies:
+ toggle-selection "^1.0.6"
+
copy-webpack-plugin@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.0.2.tgz#10efc6ad219a61acbf2f5fb50af83da38431bc34"
@@ -9571,6 +9627,13 @@ crypt@~0.0.1:
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
+cryptiles@2.x.x:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
+ integrity sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=
+ dependencies:
+ boom "2.x.x"
+
cryptiles@4.x.x:
version "4.1.3"
resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-4.1.3.tgz#2461d3390ea0b82c643a6ba79f0ed491b0934c25"
@@ -9702,7 +9765,7 @@ css-to-react-native@^3.0.0:
css-color-keywords "^1.0.0"
postcss-value-parser "^4.0.2"
-css-tree@1.0.0-alpha.37, css-tree@^1.0.0-alpha.28:
+css-tree@1.0.0-alpha.37:
version "1.0.0-alpha.37"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22"
integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==
@@ -9710,6 +9773,14 @@ css-tree@1.0.0-alpha.37, css-tree@^1.0.0-alpha.28:
mdn-data "2.0.4"
source-map "^0.6.1"
+css-tree@^1.0.0-alpha.28:
+ version "1.0.0-alpha.39"
+ resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb"
+ integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==
+ dependencies:
+ mdn-data "2.0.6"
+ source-map "^0.6.1"
+
css-what@2.1, css-what@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
@@ -9785,11 +9856,16 @@ cssstyle@^2.2.0:
dependencies:
cssom "~0.3.6"
-csstype@^2.2.0, csstype@^2.5.5, csstype@^2.5.7, csstype@^2.6.7:
+csstype@^2.2.0, csstype@^2.5.7, csstype@^2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.7.tgz#20b0024c20b6718f4eda3853a1f5a1cce7f5e4a5"
integrity sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==
+csstype@^2.5.5:
+ version "2.6.13"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.13.tgz#a6893015b90e84dd6e85d0e3b442a1e84f2dbe0f"
+ integrity sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A==
+
currently-unhandled@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
@@ -10389,7 +10465,12 @@ deep-object-diff@^1.1.0:
resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.0.tgz#d6fabf476c2ed1751fc94d5ca693d2ed8c18bc5a"
integrity sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw==
-deepmerge@3.2.0, deepmerge@^4.0.0, deepmerge@^4.2.2:
+deepmerge@3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.2.0.tgz#58ef463a57c08d376547f8869fdc5bcee957f44e"
+ integrity sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow==
+
+deepmerge@^4.0.0, deepmerge@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
@@ -11330,6 +11411,13 @@ encodeurl@^1.0.2, encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
+encoding@^0.1.11:
+ version "0.1.13"
+ resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
+ integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
+ dependencies:
+ iconv-lite "^0.6.2"
+
end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1, end-of-stream@^1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@@ -11502,7 +11590,7 @@ error-stack-parser@^1.3.5:
dependencies:
stackframe "^0.3.1"
-error-stack-parser@^2.0.4, error-stack-parser@^2.0.6:
+error-stack-parser@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8"
integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==
@@ -12390,7 +12478,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
assign-symbols "^1.0.0"
is-extendable "^1.0.1"
-extend@^3.0.0, extend@~3.0.2:
+extend@^3.0.0, extend@~3.0.0, extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
@@ -12490,11 +12578,21 @@ fancy-log@^1.3.2:
color-support "^1.1.3"
time-stamp "^1.0.0"
-fast-deep-equal@^3.1.1, fast-deep-equal@~3.1.3:
+fast-deep-equal@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
+ integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=
+
+fast-deep-equal@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
+fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
fast-diff@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
@@ -13169,6 +13267,15 @@ form-data@^3.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
+form-data@~2.1.1:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
+ integrity sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.5"
+ mime-types "^2.1.12"
+
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@@ -14698,11 +14805,32 @@ hapi@^17.5.3:
teamwork "3.x.x"
topo "3.x.x"
+har-schema@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
+ integrity sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=
+
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
+har-validator@~4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
+ integrity sha1-M0gdDxu/9gDdID11gSpqX7oALio=
+ dependencies:
+ ajv "^4.9.1"
+ har-schema "^1.0.5"
+
+har-validator@~5.1.0:
+ version "5.1.5"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
+ integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
+ dependencies:
+ ajv "^6.12.3"
+ har-schema "^2.0.0"
+
har-validator@~5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
@@ -15004,6 +15132,16 @@ hat@0.0.3:
resolved "https://registry.yarnpkg.com/hat/-/hat-0.0.3.tgz#bb014a9e64b3788aed8005917413d4ff3d502d8a"
integrity sha1-uwFKnmSzeIrtgAWRdBPU/z1QLYo=
+hawk@~3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
+ integrity sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=
+ dependencies:
+ boom "2.x.x"
+ cryptiles "2.x.x"
+ hoek "2.x.x"
+ sntp "1.x.x"
+
he@1.2.0, he@1.2.x, he@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
@@ -15072,6 +15210,11 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
+hoek@2.x.x:
+ version "2.16.3"
+ resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
+ integrity sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=
+
hoek@5.x.x, hoek@^5.0.4:
version "5.0.4"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-5.0.4.tgz#0f7fa270a1cafeb364a4b2ddfaa33f864e4157da"
@@ -15082,7 +15225,12 @@ hoek@6.x.x:
resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.0.3.tgz#7884360426d927865a0a1251fc9c59313af5b798"
integrity sha512-TU6RyZ/XaQCTWRLrdqZZtZqwxUVr6PDMfi6MlWNURZ7A6czanQqX4pFE1mdOUQR9FdPCsZ0UzL8jI/izZ+eBSQ==
-hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0, hoist-non-react-statics@^2.5.5, hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
+hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0, hoist-non-react-statics@^2.5.5:
+ version "2.5.5"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
+ integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==
+
+hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@@ -15337,6 +15485,15 @@ http-request-to-url@^1.0.0:
await-event "^2.1.0"
socket-location "^1.0.0"
+http-signature@~1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
+ integrity sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=
+ dependencies:
+ assert-plus "^0.2.0"
+ jsprim "^1.2.2"
+ sshpk "^1.7.0"
+
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
@@ -15400,9 +15557,9 @@ hyperlinker@^1.0.0:
integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==
hyphenate-style-name@^1.0.2:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48"
- integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ==
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d"
+ integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==
i18n-iso-countries@^4.3.1:
version "4.3.1"
@@ -15430,6 +15587,13 @@ iconv-lite@^0.5.0, iconv-lite@^0.5.1:
dependencies:
safer-buffer ">= 2.1.2 < 3"
+iconv-lite@^0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01"
+ integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3.0.0"
+
icss-utils@^4.0.0, icss-utils@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467"
@@ -16496,7 +16660,7 @@ is-ssh@^1.3.0:
dependencies:
protocols "^1.1.0"
-is-stream@^1.0.0, is-stream@^1.1.0:
+is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
@@ -17576,6 +17740,11 @@ json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
+json-schema-traverse@^0.3.0:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
+ integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=
+
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@@ -18583,7 +18752,17 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
-lodash@4.17.11, lodash@4.17.15, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5:
+lodash@4.17.11:
+ version "4.17.11"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
+ integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
+
+lodash@4.17.15:
+ version "4.17.15"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
+ integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
+
+lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5:
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
@@ -19039,6 +19218,11 @@ mdn-data@2.0.4:
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
+mdn-data@2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978"
+ integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==
+
mdurl@^1.0.0, mdurl@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
@@ -19298,7 +19482,7 @@ mime-db@1.44.0, mime-db@1.x.x, "mime-db@>= 1.40.0 < 2":
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
-mime-types@^2.1.12, mime-types@^2.1.25, mime-types@^2.1.26, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24:
+mime-types@^2.1.12, mime-types@^2.1.25, mime-types@^2.1.26, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.7:
version "2.1.27"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
@@ -19399,7 +19583,22 @@ minimist-options@^4.0.2:
is-plain-obj "^1.1.0"
kind-of "^6.0.3"
-minimist@0.0.8, minimist@1.1.x, minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.0:
+minimist@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+ integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
+
+minimist@1.1.x:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.3.tgz#3bedfd91a92d39016fcfaa1c681e8faa1a1efda8"
+ integrity sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=
+
+minimist@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+ integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
+
+minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.0:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
@@ -19851,9 +20050,9 @@ nan@^2.12.1, nan@^2.13.2, nan@^2.14.0, nan@^2.14.1:
integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
nano-css@^5.2.1:
- version "5.2.1"
- resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.2.1.tgz#73b8470fa40b028a134d3393ae36bbb34b9fa332"
- integrity sha512-T54okxMAha0+de+W8o3qFtuWhTxYvqQh2ku1cYEqTTP9mR62nWV2lLK9qRuAGWmoaYWhU7K4evT9Lc1iF65wuw==
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.3.0.tgz#9d3cd29788d48b6a07f52aa4aec7cf4da427b6b5"
+ integrity sha512-uM/9NGK9/E9/sTpbIZ/bQ9xOLOIHZwrrb/CRlbDHBU/GFS7Gshl24v/WJhwsVViWkpOXUmiZ66XO7fSB4Wd92Q==
dependencies:
css-tree "^1.0.0-alpha.28"
csstype "^2.5.5"
@@ -20026,7 +20225,20 @@ node-environment-flags@1.0.6:
object.getownpropertydescriptors "^2.0.3"
semver "^5.7.0"
-node-fetch@2.1.2, node-fetch@^1.0.1, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1:
+node-fetch@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5"
+ integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=
+
+node-fetch@^1.0.1:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
+ integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
+ dependencies:
+ encoding "^0.1.11"
+ is-stream "^1.0.1"
+
+node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
@@ -20036,11 +20248,16 @@ node-forge@0.9.0:
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==
-node-forge@^0.10.0, node-forge@^0.7.6:
+node-forge@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
+node-forge@^0.7.6:
+ version "0.7.6"
+ resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac"
+ integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==
+
node-gyp-build@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
@@ -20464,6 +20681,11 @@ nyc@^15.0.1:
test-exclude "^6.0.0"
yargs "^15.0.2"
+oauth-sign@~0.8.1:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
+ integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=
+
oauth-sign@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
@@ -22197,6 +22419,11 @@ pseudomap@^1.0.2:
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
+psl@^1.1.24:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
+ integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
+
psl@^1.1.28:
version "1.4.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2"
@@ -22258,7 +22485,7 @@ punycode@2.x.x, punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
-punycode@^1.2.4:
+punycode@^1.2.4, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
@@ -22310,6 +22537,11 @@ qs@6.7.0, qs@^6.4.0, qs@^6.5.1, qs@^6.6.0:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
+qs@~6.4.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
+ integrity sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=
+
qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
@@ -23120,24 +23352,30 @@ react-transition-group@^4.3.0:
loose-envify "^1.4.0"
prop-types "^15.6.2"
-react-use@^13.27.0:
- version "13.27.0"
- resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.27.0.tgz#53a619dc9213e2cbe65d6262e8b0e76641ade4aa"
- integrity sha512-2lyTyqJWyvnaP/woVtDcFS4B5pUYz0FQWI9pVHk/6TBWom2x3/ziJthkEn/LbCA9Twv39xSQU7Dn0zdIWfsNTQ==
+react-universal-interface@^0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b"
+ integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==
+
+react-use@^15.3.4:
+ version "15.3.4"
+ resolved "https://registry.yarnpkg.com/react-use/-/react-use-15.3.4.tgz#f853d310bd71f75b38900a8caa3db93f6dc6e872"
+ integrity sha512-cHq1dELW6122oi1+xX7lwNyE/ugZs5L902BuO8eFJCfn2api1KeuPVG1M/GJouVARoUf54S2dYFMKo5nQXdTag==
dependencies:
- "@types/js-cookie" "2.2.5"
- "@xobotyi/scrollbar-width" "1.9.4"
+ "@types/js-cookie" "2.2.6"
+ "@xobotyi/scrollbar-width" "1.9.5"
copy-to-clipboard "^3.2.0"
- fast-deep-equal "^3.1.1"
+ fast-deep-equal "^3.1.3"
fast-shallow-equal "^1.0.0"
js-cookie "^2.2.1"
nano-css "^5.2.1"
+ react-universal-interface "^0.6.2"
resize-observer-polyfill "^1.5.1"
screenfull "^5.0.0"
set-harmonic-interval "^1.0.1"
throttle-debounce "^2.1.0"
ts-easing "^0.2.0"
- tslib "^1.10.0"
+ tslib "^2.0.0"
react-virtualized-auto-sizer@^1.0.2:
version "1.0.2"
@@ -23605,11 +23843,16 @@ regenerator-runtime@^0.12.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
-regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4:
+regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.3:
version "0.13.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
+regenerator-runtime@^0.13.4:
+ version "0.13.7"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
+ integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
+
regenerator-transform@^0.14.2:
version "0.14.4"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7"
@@ -24004,7 +24247,61 @@ request-promise@^4.2.2:
stealthy-require "^1.1.1"
tough-cookie "^2.3.3"
-request@2.81.0, request@2.88.0, request@^2.74.0, request@^2.87.0, request@^2.88.0, request@^2.88.2:
+request@2.81.0:
+ version "2.81.0"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
+ integrity sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=
+ dependencies:
+ aws-sign2 "~0.6.0"
+ aws4 "^1.2.1"
+ caseless "~0.12.0"
+ combined-stream "~1.0.5"
+ extend "~3.0.0"
+ forever-agent "~0.6.1"
+ form-data "~2.1.1"
+ har-validator "~4.2.1"
+ hawk "~3.1.3"
+ http-signature "~1.1.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.7"
+ oauth-sign "~0.8.1"
+ performance-now "^0.2.0"
+ qs "~6.4.0"
+ safe-buffer "^5.0.1"
+ stringstream "~0.0.4"
+ tough-cookie "~2.3.0"
+ tunnel-agent "^0.6.0"
+ uuid "^3.0.0"
+
+request@2.88.0:
+ version "2.88.0"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
+ integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
+ dependencies:
+ aws-sign2 "~0.7.0"
+ aws4 "^1.8.0"
+ caseless "~0.12.0"
+ combined-stream "~1.0.6"
+ extend "~3.0.2"
+ forever-agent "~0.6.1"
+ form-data "~2.3.2"
+ har-validator "~5.1.0"
+ http-signature "~1.2.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.19"
+ oauth-sign "~0.9.0"
+ performance-now "^2.1.0"
+ qs "~6.5.2"
+ safe-buffer "^5.1.2"
+ tough-cookie "~2.4.3"
+ tunnel-agent "^0.6.0"
+ uuid "^3.3.2"
+
+request@^2.74.0, request@^2.87.0, request@^2.88.0, request@^2.88.2:
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
@@ -24387,9 +24684,9 @@ rsvp@^4.8.4:
integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
rtl-css-js@^1.9.0:
- version "1.13.1"
- resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.13.1.tgz#80deabf6e8f36d6767d495cd3eb60fecb20c67e1"
- integrity sha512-jgkIDj6Xi25kAEm5oYM3ZMFiOQhpLEcXi2LY/6bVr91cVz73hciHKneL5AMVPxOcks/JuizSaaNsvNRkeAWe3w==
+ version "1.14.0"
+ resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.14.0.tgz#daa4f192a92509e292a0519f4b255e6e3c076b7d"
+ integrity sha512-Dl5xDTeN3e7scU1cWX8c9b6/Nqz3u/HgR4gePc1kWXYiQWVQbKCEyK6+Hxve9LbcJ5EieHy1J9nJCN3grTtGwg==
dependencies:
"@babel/runtime" "^7.1.2"
@@ -24495,7 +24792,7 @@ safefs@^4.1.0:
editions "^1.1.1"
graceful-fs "^4.1.4"
-"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
+"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -24600,14 +24897,12 @@ scheduler@^0.18.0:
loose-envify "^1.1.0"
object-assign "^4.1.1"
-schema-utils@1.0.0, schema-utils@^0.3.0, schema-utils@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
- integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==
+schema-utils@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf"
+ integrity sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=
dependencies:
- ajv "^6.1.0"
- ajv-errors "^1.0.0"
- ajv-keywords "^3.1.0"
+ ajv "^5.0.0"
schema-utils@^0.4.5:
version "0.4.7"
@@ -24617,6 +24912,15 @@ schema-utils@^0.4.5:
ajv "^6.1.0"
ajv-keywords "^3.1.0"
+schema-utils@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
+ integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==
+ dependencies:
+ ajv "^6.1.0"
+ ajv-errors "^1.0.0"
+ ajv-keywords "^3.1.0"
+
schema-utils@^2.0.0, schema-utils@^2.0.1, schema-utils@^2.5.0, schema-utils@^2.6.1, schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"
@@ -24645,9 +24949,9 @@ scoped-regex@^1.0.0:
integrity sha1-o0a7Gs1CB65wvXwMfKnlZra63bg=
screenfull@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.0.0.tgz#5c2010c0e84fd4157bf852877698f90b8cbe96f6"
- integrity sha512-yShzhaIoE9OtOhWVyBBffA6V98CDCoyHTsp8228blmqYy1Z5bddzE/4FPiJKlr8DVR4VBiiUyfPzIQPIYDkeMA==
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.0.2.tgz#b9acdcf1ec676a948674df5cd0ff66b902b0bed7"
+ integrity sha512-cCF2b+L/mnEiORLN5xSAz6H3t18i2oHh9BA8+CQlAh5DRw2+NFAGQJOSYbcGw8B2k04g/lVvFcfZ83b3ysH5UQ==
scss-tokenizer@^0.2.3:
version "0.2.3"
@@ -25116,6 +25420,13 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^2.0.0"
+sntp@1.x.x:
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
+ integrity sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=
+ dependencies:
+ hoek "2.x.x"
+
socket-location@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/socket-location/-/socket-location-1.0.0.tgz#6f0c6f891c9a61c9a750265c14921d12196d266f"
@@ -25245,9 +25556,9 @@ source-map@^0.7.3:
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
sourcemap-codec@^1.4.1:
- version "1.4.6"
- resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz#e30a74f0402bad09807640d39e971090a08ce1e9"
- integrity sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==
+ version "1.4.8"
+ resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
+ integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
space-separated-tokens@^1.0.0:
version "1.1.2"
@@ -25450,12 +25761,12 @@ stable@^0.1.8:
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
-stack-generator@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.4.tgz#027513eab2b195bbb43b9c8360ba2dd0ab54de09"
- integrity sha512-ha1gosTNcgxwzo9uKTQ8zZ49aUp5FIUW58YHFxCqaAHtE0XqBg0chGFYA1MfmW//x1KWq3F4G7Ug7bJh4RiRtg==
+stack-generator@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.5.tgz#fb00e5b4ee97de603e0773ea78ce944d81596c36"
+ integrity sha512-/t1ebrbHkrLrDuNMdeAcsvynWgoH/i4o8EGGfX7dEYDoTXOYVAkEpFdtshlvabzc6JlJ8Kf9YdFEoz7JkzGN9Q==
dependencies:
- stackframe "^1.1.0"
+ stackframe "^1.1.1"
stack-trace@0.0.10, stack-trace@0.0.x:
version "0.0.10"
@@ -25479,10 +25790,10 @@ stackframe@^0.3.1:
resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-0.3.1.tgz#33aa84f1177a5548c8935533cbfeb3420975f5a4"
integrity sha1-M6qE8Rd6VUjIk1Uzy/6zQgl19aQ=
-stackframe@^1.1.0, stackframe@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.1.1.tgz#ffef0a3318b1b60c3b58564989aca5660729ec71"
- integrity sha512-0PlYhdKh6AfFxRyK/v+6/k+/mMfyiEBbTM5L94D0ZytQnJ166wuwoTYLHFWGbs2dpA8Rgq763KGWmN1EQEYHRQ==
+stackframe@^1.1.1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303"
+ integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==
stackman@^4.0.1:
version "4.0.1"
@@ -25495,22 +25806,22 @@ stackman@^4.0.1:
error-callsites "^2.0.3"
load-source-map "^1.0.0"
-stacktrace-gps@^3.0.3:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.0.3.tgz#b89f84cc13bb925b96607e737b617c8715facf57"
- integrity sha512-51Rr7dXkyFUKNmhY/vqZWK+EvdsfFSRiQVtgHTFlAdNIYaDD7bVh21yBHXaNWAvTD+w+QSjxHg7/v6Tz4veExA==
+stacktrace-gps@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.0.4.tgz#7688dc2fc09ffb3a13165ebe0dbcaf41bcf0c69a"
+ integrity sha512-qIr8x41yZVSldqdqe6jciXEaSCKw1U8XTXpjDuy0ki/apyTn/r3w9hDAAQOhZdxvsC93H+WwwEu5cq5VemzYeg==
dependencies:
source-map "0.5.6"
- stackframe "^1.1.0"
+ stackframe "^1.1.1"
stacktrace-js@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.1.tgz#ebdb0e9a16e6f171f96ca7878404e7f15c3d42ba"
- integrity sha512-13oDNgBSeWtdGa4/2BycNyKqe+VktCoJ8VLx4pDoJkwGGJVtiHdfMOAj3aW9xTi8oR2v34z9IcvfCvT6XNdNAw==
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b"
+ integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==
dependencies:
- error-stack-parser "^2.0.4"
- stack-generator "^2.0.4"
- stacktrace-gps "^3.0.3"
+ error-stack-parser "^2.0.6"
+ stack-generator "^2.0.5"
+ stacktrace-gps "^3.0.4"
state-toggle@^1.0.0:
version "1.0.0"
@@ -25819,6 +26130,11 @@ stringify-object@^3.2.1:
is-obj "^1.0.1"
is-regexp "^1.0.0"
+stringstream@~0.0.4:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.6.tgz#7880225b0d4ad10e30927d167a1d6f2fd3b33a72"
+ integrity sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==
+
strip-ansi@*, strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
@@ -26522,9 +26838,9 @@ throat@^5.0.0:
integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==
throttle-debounce@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.1.0.tgz#257e648f0a56bd9e54fe0f132c4ab8611df4e1d5"
- integrity sha512-AOvyNahXQuU7NN+VVvOOX+uW6FPaWdAOdRP5HfwYxAfCzXTFKRMoIMk+n+po318+ktcChx+F1Dd91G3YHeMKyg==
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.3.0.tgz#fd31865e66502071e411817e241465b3e9c372e2"
+ integrity sha512-H7oLPV0P7+jgvrk+6mwwwBDmxTaxnu9HMXmloNLXwnNO0ZxZ31Orah2n8lU1eMPvsaowP2CX+USCgyovXfdOFQ==
throttleit@^1.0.0:
version "1.0.0"
@@ -26866,6 +27182,21 @@ tough-cookie@^3.0.1:
psl "^1.1.28"
punycode "^2.1.1"
+tough-cookie@~2.3.0:
+ version "2.3.4"
+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655"
+ integrity sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==
+ dependencies:
+ punycode "^1.4.1"
+
+tough-cookie@~2.4.3:
+ version "2.4.3"
+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
+ integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
+ dependencies:
+ psl "^1.1.24"
+ punycode "^1.4.1"
+
tr46@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
@@ -27199,11 +27530,21 @@ typescript-tuple@^2.2.1:
dependencies:
typescript-compare "^0.0.2"
-typescript@4.0.2, typescript@^3.0.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.4.5, typescript@~3.7.2:
+typescript@4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2"
integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==
+typescript@^3.0.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.4.5:
+ version "3.9.7"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
+ integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
+
+typescript@~3.7.2:
+ version "3.7.5"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
+ integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
+
ua-parser-js@^0.7.18:
version "0.7.22"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.22.tgz#960df60a5f911ea8f1c818f3747b99c6e177eae3"
From fc4dce598a6cb0e584d440e04b5dd4d3bfcddcdd Mon Sep 17 00:00:00 2001
From: Thom Heymann <190132+thomheymann@users.noreply.github.com>
Date: Fri, 30 Oct 2020 16:04:57 +0000
Subject: [PATCH 02/15] Apply suggestions from code review
Co-authored-by: Larry Gregory
---
.../api_keys_page/api_keys_empty_prompt.tsx | 2 +-
.../account_management/api_keys_page/api_keys_table.tsx | 5 ++++-
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_empty_prompt.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_empty_prompt.tsx
index 085121cf9e127b..49f8dd72fcb527 100644
--- a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_empty_prompt.tsx
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_empty_prompt.tsx
@@ -60,7 +60,7 @@ export const ApiKeysEmptyPrompt: FunctionComponent = ({
}
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_table.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_table.tsx
index cff43273800e2d..9a9999554c4806 100644
--- a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_table.tsx
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_table.tsx
@@ -70,6 +70,9 @@ export const ApiKeysTable: FunctionComponent = ({
= ({
},
{
field: 'expiration',
- name: i18n.translate('xpack.security.accountManagement.apiKeys.espiresHeader', {
+ name: i18n.translate('xpack.security.accountManagement.apiKeys.expiresHeader', {
defaultMessage: 'Expires',
}),
render: (expiration: number) =>
From 19e038084f2e9b1311e5d378fbbb960e921f0732 Mon Sep 17 00:00:00 2001
From: Thom Heymann
Date: Mon, 2 Nov 2020 10:56:21 +0000
Subject: [PATCH 03/15] Added suggestions from code review
---
.../create_notifications.test.tsx | 11 ---
.../notifications/create_notifications.tsx | 14 +--
.../account_management_app.test.ts | 74 ---------------
.../account_management_app.test.tsx | 65 ++++++++++++++
.../account_management_app.tsx | 34 ++++---
.../api_keys_page/api_keys_empty_prompt.tsx | 31 +++----
.../api_keys_page/api_keys_page.test.tsx | 90 +++++++++++++++++++
.../api_keys_page/api_keys_page.tsx | 17 ++--
.../api_keys_page/api_keys_table.tsx | 14 +--
.../api_keys_page/create_api_key_flyout.tsx | 86 ++++++++++++------
.../invalidate_api_key_modal.tsx | 2 -
.../components/doc_link.tsx | 39 ++++++++
.../api_keys/api_keys_api_client.mock.ts | 6 +-
.../api_keys/api_keys_api_client.ts | 16 ++--
.../nav_control/nav_control_component.tsx | 14 ++-
.../nav_control/nav_control_service.tsx | 7 +-
.../security/server/routes/api_keys/create.ts | 10 +--
.../authorization/roles/model/put_payload.ts | 2 +-
18 files changed, 344 insertions(+), 188 deletions(-)
delete mode 100644 x-pack/plugins/security/public/account_management/account_management_app.test.ts
create mode 100644 x-pack/plugins/security/public/account_management/account_management_app.test.tsx
create mode 100644 x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.test.tsx
create mode 100644 x-pack/plugins/security/public/account_management/components/doc_link.tsx
diff --git a/src/plugins/kibana_react/public/notifications/create_notifications.test.tsx b/src/plugins/kibana_react/public/notifications/create_notifications.test.tsx
index 4f64a2b95f512f..23500e8480ebb8 100644
--- a/src/plugins/kibana_react/public/notifications/create_notifications.test.tsx
+++ b/src/plugins/kibana_react/public/notifications/create_notifications.test.tsx
@@ -54,16 +54,12 @@ test('can display string element as title', () => {
expect(notifications.toasts.add).toHaveBeenCalledTimes(1);
expect(notifications.toasts.add.mock.calls[0][0]).toMatchInlineSnapshot(`
Object {
- "color": undefined,
- "iconType": undefined,
- "onClose": undefined,
"text": MountPoint {
"reactNode": ,
},
"title": MountPoint {
"reactNode": "foo",
},
- "toastLifeTimeMs": undefined,
}
`);
});
@@ -120,7 +116,6 @@ test('can set toast properties', () => {
Object {
"color": "danger",
"iconType": "foo",
- "onClose": undefined,
"text": MountPoint {
"reactNode":
1
@@ -147,42 +142,36 @@ test('can display success, warning and danger toasts', () => {
Object {
"color": "success",
"iconType": "check",
- "onClose": undefined,
"text": MountPoint {
"reactNode": ,
},
"title": MountPoint {
"reactNode": "1",
},
- "toastLifeTimeMs": undefined,
}
`);
expect(notifications.toasts.add.mock.calls[1][0]).toMatchInlineSnapshot(`
Object {
"color": "warning",
"iconType": "help",
- "onClose": undefined,
"text": MountPoint {
"reactNode": ,
},
"title": MountPoint {
"reactNode": "2",
},
- "toastLifeTimeMs": undefined,
}
`);
expect(notifications.toasts.add.mock.calls[2][0]).toMatchInlineSnapshot(`
Object {
"color": "danger",
"iconType": "alert",
- "onClose": undefined,
"text": MountPoint {
"reactNode": ,
},
"title": MountPoint {
"reactNode": "3",
},
- "toastLifeTimeMs": undefined,
}
`);
});
diff --git a/src/plugins/kibana_react/public/notifications/create_notifications.tsx b/src/plugins/kibana_react/public/notifications/create_notifications.tsx
index 826435ec5b587a..dc6430e2f18a95 100644
--- a/src/plugins/kibana_react/public/notifications/create_notifications.tsx
+++ b/src/plugins/kibana_react/public/notifications/create_notifications.tsx
@@ -23,24 +23,14 @@ import { KibanaReactNotifications } from './types';
import { toMountPoint } from '../util';
export const createNotifications = (services: KibanaServices): KibanaReactNotifications => {
- const show: KibanaReactNotifications['toasts']['show'] = ({
- title,
- body,
- color,
- iconType,
- toastLifeTimeMs,
- onClose,
- }) => {
+ const show: KibanaReactNotifications['toasts']['show'] = ({ title, body, ...rest }) => {
if (!services.notifications) {
throw new TypeError('Could not show notification as notifications service is not available.');
}
services.notifications!.toasts.add({
title: toMountPoint(title),
text: toMountPoint(<>{body || null}>),
- color,
- iconType,
- toastLifeTimeMs,
- onClose,
+ ...rest,
});
};
diff --git a/x-pack/plugins/security/public/account_management/account_management_app.test.ts b/x-pack/plugins/security/public/account_management/account_management_app.test.ts
deleted file mode 100644
index c41bd43872beed..00000000000000
--- a/x-pack/plugins/security/public/account_management/account_management_app.test.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-jest.mock('./account_management_page');
-
-import { AppMount, AppNavLinkStatus } from 'src/core/public';
-import { UserAPIClient } from '../management';
-import { accountManagementApp } from './account_management_app';
-
-import { coreMock, scopedHistoryMock } from '../../../../../src/core/public/mocks';
-import { securityMock } from '../mocks';
-
-describe('accountManagementApp', () => {
- it('properly registers application', () => {
- const coreSetupMock = coreMock.createSetup();
-
- accountManagementApp.create({
- application: coreSetupMock.application,
- getStartServices: coreSetupMock.getStartServices,
- authc: securityMock.createSetup().authc,
- });
-
- expect(coreSetupMock.application.register).toHaveBeenCalledTimes(1);
-
- const [[appRegistration]] = coreSetupMock.application.register.mock.calls;
- expect(appRegistration).toEqual({
- id: 'security_account',
- appRoute: '/security/account',
- navLinkStatus: AppNavLinkStatus.hidden,
- title: 'Account Management',
- mount: expect.any(Function),
- });
- });
-
- it('properly sets breadcrumbs and renders application', async () => {
- const coreSetupMock = coreMock.createSetup();
- const coreStartMock = coreMock.createStart();
- coreSetupMock.getStartServices.mockResolvedValue([coreStartMock, {}, {}]);
-
- const authcMock = securityMock.createSetup().authc;
- const containerMock = document.createElement('div');
-
- accountManagementApp.create({
- application: coreSetupMock.application,
- getStartServices: coreSetupMock.getStartServices,
- authc: authcMock,
- });
-
- const [[{ mount }]] = coreSetupMock.application.register.mock.calls;
- await (mount as AppMount)({
- element: containerMock,
- appBasePath: '',
- onAppLeave: jest.fn(),
- setHeaderActionMenu: jest.fn(),
- history: scopedHistoryMock.create(),
- });
-
- expect(coreStartMock.chrome.setBreadcrumbs).toHaveBeenCalledTimes(1);
- expect(coreStartMock.chrome.setBreadcrumbs).toHaveBeenCalledWith([
- { text: 'Account Management' },
- ]);
-
- const mockRenderApp = jest.requireMock('./account_management_page').renderAccountManagementPage;
- expect(mockRenderApp).toHaveBeenCalledTimes(1);
- expect(mockRenderApp).toHaveBeenCalledWith(coreStartMock.i18n, containerMock, {
- userAPIClient: expect.any(UserAPIClient),
- authc: authcMock,
- notifications: coreStartMock.notifications,
- });
- });
-});
diff --git a/x-pack/plugins/security/public/account_management/account_management_app.test.tsx b/x-pack/plugins/security/public/account_management/account_management_app.test.tsx
new file mode 100644
index 00000000000000..c5e7784e46fa43
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/account_management_app.test.tsx
@@ -0,0 +1,65 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import ReactDOM from 'react-dom';
+import { act, screen, fireEvent } from '@testing-library/react';
+import { AppMount, AppNavLinkStatus, ScopedHistory } from '../../../../../src/core/public';
+import { coreMock } from '../../../../../src/core/public/mocks';
+import { mockAuthenticatedUser } from '../../common/model/authenticated_user.mock';
+import { securityMock } from '../mocks';
+import { accountManagementApp } from './account_management_app';
+import { createMemoryHistory } from 'history';
+
+describe('accountManagementApp', () => {
+ it('registers application', () => {
+ const { application, getStartServices } = coreMock.createSetup();
+ const { authc } = securityMock.createSetup();
+ accountManagementApp.create({ application, getStartServices, authc });
+ expect(application.register).toHaveBeenLastCalledWith({
+ id: 'security_account',
+ appRoute: '/security/account',
+ navLinkStatus: AppNavLinkStatus.hidden,
+ title: 'Account Management',
+ mount: expect.any(Function),
+ });
+ });
+
+ it('renders application and sets breadcrumbs', async () => {
+ const { application, getStartServices } = coreMock.createSetup();
+ const coreStartMock = coreMock.createStart();
+ getStartServices.mockResolvedValue([coreStartMock, {}, {}]);
+ const { authc } = securityMock.createSetup();
+ authc.getCurrentUser.mockResolvedValue(
+ mockAuthenticatedUser({ username: 'some-user', full_name: undefined })
+ );
+ accountManagementApp.create({ application, getStartServices, authc });
+ const [[{ mount }]] = application.register.mock.calls;
+ const element = document.body.appendChild(document.createElement('div'));
+
+ await act(async () => {
+ await (mount as AppMount)({
+ element,
+ appBasePath: '',
+ onAppLeave: jest.fn(),
+ setHeaderActionMenu: jest.fn(),
+ history: (createMemoryHistory() as unknown) as ScopedHistory,
+ });
+ });
+ expect(coreStartMock.chrome.setBreadcrumbs).toHaveBeenLastCalledWith([
+ expect.objectContaining({ text: 'Account Management' }),
+ ]);
+
+ fireEvent.click(screen.getByRole('tab', { name: 'API Keys' }));
+ expect(coreStartMock.chrome.setBreadcrumbs).toHaveBeenLastCalledWith([
+ expect.objectContaining({ text: 'Account Management' }),
+ expect.objectContaining({ text: 'API Keys' }),
+ ]);
+
+ // Need to cleanup manually since `mount` renders the app straight to the DOM
+ ReactDOM.unmountComponentAtNode(element);
+ document.body.removeChild(element);
+ });
+});
diff --git a/x-pack/plugins/security/public/account_management/account_management_app.tsx b/x-pack/plugins/security/public/account_management/account_management_app.tsx
index 03b7f7be1e7ab9..cf7ce75938e0ed 100644
--- a/x-pack/plugins/security/public/account_management/account_management_app.tsx
+++ b/x-pack/plugins/security/public/account_management/account_management_app.tsx
@@ -7,6 +7,7 @@
import React, { lazy, Suspense, FunctionComponent } from 'react';
import ReactDOM from 'react-dom';
import { Router, useHistory } from 'react-router-dom';
+import { History } from 'history';
import { useAsync } from 'react-use';
import { i18n } from '@kbn/i18n';
import {
@@ -29,9 +30,10 @@ import {
AppNavLinkStatus,
StartServicesAccessor,
NotificationsSetup,
+ CoreStart,
} from '../../../../../src/core/public';
import {
- createKibanaReactContext,
+ KibanaContextProvider,
reactRouterNavigate,
} from '../../../../../src/plugins/kibana_react/public';
import { getUserDisplayName } from '../../common/model';
@@ -60,19 +62,12 @@ export const accountManagementApp = Object.freeze({
async mount({ element, history }: AppMountParameters) {
const [coreStart] = await getStartServices();
- const { Provider: KibanaProvider } = createKibanaReactContext(coreStart);
- const { Context: IntlProvider } = coreStart.i18n;
-
ReactDOM.render(
-
-
-
-
-
-
-
-
- ,
+
+
+
+
+ ,
element
);
@@ -82,6 +77,19 @@ export const accountManagementApp = Object.freeze({
},
});
+export interface ProvidersProps {
+ services: CoreStart;
+ history: History;
+}
+
+export const Providers: FunctionComponent = ({ services, history, children }) => (
+
+
+ {children}
+
+
+);
+
export interface AccountManagementProps {
authc: AuthenticationServiceSetup;
notifications: NotificationsSetup;
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_empty_prompt.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_empty_prompt.tsx
index 085121cf9e127b..f9e9dd0af1441d 100644
--- a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_empty_prompt.tsx
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_empty_prompt.tsx
@@ -5,9 +5,9 @@
*/
import React, { FunctionComponent } from 'react';
-import { EuiLink, EuiEmptyPrompt } from '@elastic/eui';
+import { EuiEmptyPrompt } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
-import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
+import { DocLink } from '../components/doc_link';
export interface ApiKeysEmptyPromptProps {
error?: Error;
@@ -17,15 +17,11 @@ export const ApiKeysEmptyPrompt: FunctionComponent = ({
error,
children,
}) => {
- const { services } = useKibana();
-
if (error) {
- const { statusCode, message = '' } = (error as any).body ?? {};
-
- if (statusCode === 400 && message.indexOf('[feature_not_enabled_exception]') !== -1) {
+ if (doesErrorIndicateAPIKeysAreDisabled(error)) {
return (
@@ -35,16 +31,12 @@ export const ApiKeysEmptyPrompt: FunctionComponent = ({
/>
-
+
-
+
>
}
@@ -52,7 +44,7 @@ export const ApiKeysEmptyPrompt: FunctionComponent = ({
);
}
- if (statusCode === 403) {
+ if (doesErrorIndicateUserHasNoPermissionsToManageAPIKeys(error)) {
return (
= ({
/>
);
};
+
+function doesErrorIndicateAPIKeysAreDisabled(error: Record) {
+ const message = error.body?.message || '';
+ return message.indexOf('disabled.feature="api_keys"') !== -1;
+}
+
+function doesErrorIndicateUserHasNoPermissionsToManageAPIKeys(error: Record) {
+ return error.body?.statusCode === 403;
+}
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.test.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.test.tsx
new file mode 100644
index 00000000000000..44728f4d674977
--- /dev/null
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.test.tsx
@@ -0,0 +1,90 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import React from 'react';
+import { render, fireEvent, within, waitForElementToBeRemoved } from '@testing-library/react';
+import { createMemoryHistory } from 'history';
+import { coreMock } from '../../../../../../src/core/public/mocks';
+import { Providers } from '../account_management_app';
+import { ApiKeysPage } from './api_keys_page';
+
+const getApiKeysResponse = {
+ apiKeys: [
+ {
+ creation: 1571322182082,
+ expiration: 1571408582082,
+ id: '0QQZ2m0BO2XZwgJFuWTT',
+ invalidated: false,
+ name: 'my-api-key',
+ realm: 'reserved',
+ username: 'elastic',
+ },
+ ],
+};
+
+describe('ApiKeysPage', () => {
+ it('fetches API keys on mount', async () => {
+ const coreStart = coreMock.createStart();
+ const history = createMemoryHistory();
+ coreStart.http.get.mockResolvedValue(getApiKeysResponse);
+
+ const { queryByText } = render(
+
+
+
+ );
+
+ expect(coreStart.http.get).toHaveBeenLastCalledWith('/internal/security/api_key', {
+ query: { isAdmin: false },
+ });
+
+ await waitForElementToBeRemoved(queryByText(/Loading API keys/i));
+ });
+
+ it('creates API key on form submit', async () => {
+ const coreStart = coreMock.createStart();
+ const history = createMemoryHistory({ initialEntries: ['/api-keys/create'] });
+ coreStart.http.get.mockResolvedValue(getApiKeysResponse);
+
+ const { findByRole } = render(
+
+
+
+ );
+
+ const flyout = await findByRole('dialog');
+ fireEvent.change(within(flyout).getByLabelText(/Name/i), { target: { value: 'Test Key' } });
+ fireEvent.click(within(flyout).getByRole('button', { name: /Create API key/i }));
+
+ expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/api_key', {
+ body: '{"name":"Test Key"}',
+ });
+
+ await waitForElementToBeRemoved(() => within(flyout).queryByText(/Creating API key/i));
+ });
+
+ it('invalidates API key after confirmation', async () => {
+ const coreStart = coreMock.createStart();
+ const history = createMemoryHistory();
+ coreStart.http.get.mockResolvedValue(getApiKeysResponse);
+
+ const { findByRole, getByRole, queryByText } = render(
+
+
+
+ );
+
+ const table = await findByRole('table');
+ fireEvent.click(within(table).getByRole('button', { name: /Invalidate/i }));
+ fireEvent.click(getByRole('button', { name: /Invalidate this key/i }));
+
+ expect(coreStart.http.post).toHaveBeenLastCalledWith('/internal/security/api_key/invalidate', {
+ body: '{"apiKeys":[{"id":"0QQZ2m0BO2XZwgJFuWTT","name":"my-api-key"}],"isAdmin":false}',
+ });
+
+ await waitForElementToBeRemoved(() => queryByText(/Invalidating API key/i));
+ });
+});
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.tsx
index 5a364a2c3a353d..7f70ab228cb0f9 100644
--- a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.tsx
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.tsx
@@ -4,9 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { FunctionComponent, useState, useEffect } from 'react';
+import React, { FunctionComponent, useState } from 'react';
import { Route, useHistory } from 'react-router-dom';
-import { useAsyncFn } from 'react-use';
+import { useAsyncFn, useMount } from 'react-use';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiButton, EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
@@ -27,20 +27,19 @@ import { ApiKeysEmptyPrompt } from './api_keys_empty_prompt';
export const ApiKeysPage: FunctionComponent = () => {
const history = useHistory();
const { services } = useKibana();
- const apiKeysApiClient = new APIKeysAPIClient(services.http!);
- const [state, getApiKeys] = useAsyncFn(apiKeysApiClient.getApiKeys, [services.http]);
+ const [state, getApiKeys] = useAsyncFn(() => new APIKeysAPIClient(services.http!).getApiKeys(), [
+ services.http,
+ ]);
const [createdApiKey, setCreatedApiKey] = useState();
const [apiKeyToInvalidate, setApiKeyToInvalidate] = useState();
- useEffect(() => {
- getApiKeys();
- }, []); // eslint-disable-line react-hooks/exhaustive-deps
+ useMount(getApiKeys);
if (!state.value) {
if (state.error && !state.loading) {
return (
- getApiKeys()}>
+
{
= ({
{
- onInvalidateItem(item);
- }}
+ onClick={() => onInvalidateItem(item)}
/>
@@ -51,9 +55,7 @@ export const ApiKeysTable: FunctionComponent = ({
color="danger"
flush="right"
disabled={props.loading}
- onClick={() => {
- onInvalidateItem(item);
- }}
+ onClick={() => onInvalidateItem(item)}
>
= ({
initialFocus={nameInput}
onSubmit={eventHandlers.onSubmit}
onClose={onClose}
+ size="s"
>
{form.submitError && (
<>
@@ -100,7 +103,9 @@ export const CreateApiKeyFlyout: FunctionComponent = ({
label={i18n.translate('xpack.security.accountManagement.createApiKey.nameLabel', {
defaultMessage: 'Name',
})}
- helpText="What is the API key used for?"
+ helpText={i18n.translate('xpack.security.accountManagement.createApiKey.nameHelpText', {
+ defaultMessage: 'What is the API key used for?',
+ })}
error={form.errors.name}
isInvalid={!!form.errors.name}
>
@@ -109,6 +114,7 @@ export const CreateApiKeyFlyout: FunctionComponent = ({
defaultValue={form.values.name}
isInvalid={!!form.errors.name}
inputRef={nameInput}
+ fullWidth
/>
@@ -121,10 +127,10 @@ export const CreateApiKeyFlyout: FunctionComponent = ({
defaultMessage: 'Grant subset of your permissions',
}
)}
- checked={!!form.values.customPriviliges}
- onChange={(e) => form.setValue('customPriviliges', e.target.checked)}
+ checked={!!form.values.customPrivileges}
+ onChange={(e) => form.setValue('customPrivileges', e.target.checked)}
/>
- {form.values.customPriviliges && (
+ {form.values.customPrivileges && (
<>
= ({
defaultMessage: 'Role Descriptors',
}
)}
- helpText="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html#security-api-create-api-key-request-body"
+ helpText={
+
+
+
+ }
error={form.errors.role_descriptors}
isInvalid={!!form.errors.role_descriptors}
>
- form.setValue('role_descriptors', value)}
- onBlur={() => form.trigger('role_descriptors')}
- value={form.values.role_descriptors}
- height="300px"
- mode="json"
- />
+
+ form.setValue('role_descriptors', value)}
+ width="100%"
+ height="300px"
+ languageId="xjson"
+ options={{
+ fixedOverflowWidgets: true,
+ folding: false,
+ lineNumbers: 'off',
+ scrollBeyondLastLine: false,
+ minimap: {
+ enabled: false,
+ },
+ scrollbar: {
+ useShadows: false,
+ },
+ wordBasedSuggestions: false,
+ wordWrap: 'on',
+ wrappingIndent: 'indent',
+ }}
+ />
+
>
)}
@@ -167,7 +200,7 @@ export const CreateApiKeyFlyout: FunctionComponent = ({
= ({
name="expiration"
defaultValue={form.values.expiration}
isInvalid={!!form.errors.expiration}
+ fullWidth
/>
>
@@ -192,12 +226,12 @@ export const CreateApiKeyFlyout: FunctionComponent = ({
CreateApiKeyFlyout.defaultProps = {
defaultValues: {
customExpiration: false,
- customPriviliges: false,
+ customPrivileges: false,
role_descriptors: JSON.stringify(
{
'role-a': {
cluster: ['all'],
- index: [
+ indices: [
{
names: ['index-a*'],
privileges: ['read'],
@@ -206,7 +240,7 @@ CreateApiKeyFlyout.defaultProps = {
},
'role-b': {
cluster: ['all'],
- index: [
+ indices: [
{
names: ['index-b*'],
privileges: ['all'],
@@ -224,22 +258,24 @@ export function validate(v: Partial) {
const errors: ValidationErrors = {};
if (!v.name) {
- errors.name = 'Enter a name.';
+ errors.name = i18n.translate('xpack.security.management.apiKeys.createApiKey.nameRequired', {
+ defaultMessage: 'Enter a name.',
+ });
}
if (v.customExpiration && !v.expiration) {
errors.expiration = i18n.translate(
- 'xpack.security.management.apiKeys.createApiKey.successNotification',
+ 'xpack.security.management.apiKeys.createApiKey.expirationRequired',
{
defaultMessage: 'Enter a duration or disable the option.',
}
);
}
- if (v.customPriviliges) {
+ if (v.customPrivileges) {
if (!v.role_descriptors) {
errors.role_descriptors = i18n.translate(
- 'xpack.security.management.apiKeys.createApiKey.successNotification',
+ 'xpack.security.management.apiKeys.createApiKey.roleDescriptorsRequired',
{
defaultMessage: 'Enter role descriptors or disable the option.',
}
@@ -249,7 +285,7 @@ export function validate(v: Partial) {
JSON.parse(v.role_descriptors);
} catch (e) {
errors.role_descriptors = i18n.translate(
- 'xpack.security.management.apiKeys.createApiKey.successNotification',
+ 'xpack.security.management.apiKeys.createApiKey.invalidJsonError',
{
defaultMessage: 'Enter valid JSON.',
}
@@ -266,7 +302,7 @@ export function mapValues(values: FormValues): CreateApiKeyRequest {
name: values.name,
expiration: values.customExpiration && values.expiration ? `${values.expiration}d` : undefined,
role_descriptors:
- values.customPriviliges && values.role_descriptors
+ values.customPrivileges && values.role_descriptors
? JSON.parse(values.role_descriptors)
: undefined,
};
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/invalidate_api_key_modal.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/invalidate_api_key_modal.tsx
index 11c16cea33b3b8..2150826c53d724 100644
--- a/x-pack/plugins/security/public/account_management/api_keys_page/invalidate_api_key_modal.tsx
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/invalidate_api_key_modal.tsx
@@ -47,7 +47,6 @@ export const InvalidateApiKeyModal: FunctionComponent string;
+
+export function useDocLinks(): [DocLinks, GetDocLinkFunction] {
+ const { services } = useKibana();
+ const { links, ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = services.docLinks!;
+ const getDocLink = useCallback(
+ (app, doc) => {
+ return `${ELASTIC_WEBSITE_URL}guide/en/${app}/reference/${DOC_LINK_VERSION}/${doc}`;
+ },
+ [ELASTIC_WEBSITE_URL, DOC_LINK_VERSION]
+ );
+ return [links, getDocLink];
+}
+
+export interface DocLinkProps {
+ app: string;
+ doc: string;
+}
+
+export const DocLink: FunctionComponent = ({ app, doc, children }) => {
+ const [, getDocLink] = useDocLinks();
+ return (
+
+ {children}
+
+ );
+};
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.mock.ts b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.mock.ts
index 2a45d497029f41..46551cae2bdc46 100644
--- a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.mock.ts
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.mock.ts
@@ -4,10 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import type { PublicMethodsOf } from '@kbn/utility-types';
+import { APIKeysAPIClient } from './api_keys_api_client';
+
export const apiKeysAPIClientMock = {
- create: () => ({
+ create: (): PublicMethodsOf => ({
checkPrivileges: jest.fn(),
getApiKeys: jest.fn(),
invalidateApiKeys: jest.fn(),
+ createApiKey: jest.fn(),
}),
};
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts
index ac8655a7e49a50..7d78a5ae5e9aa8 100644
--- a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.ts
@@ -42,23 +42,23 @@ const apiKeysUrl = '/internal/security/api_key';
export class APIKeysAPIClient {
constructor(private readonly http: HttpStart) {}
- public checkPrivileges = async () => {
+ public async checkPrivileges() {
return await this.http.get(`${apiKeysUrl}/privileges`);
- };
+ }
- public getApiKeys = async (isAdmin = false) => {
+ public async getApiKeys(isAdmin = false) {
return await this.http.get(apiKeysUrl, { query: { isAdmin } });
- };
+ }
- public invalidateApiKeys = async (apiKeys: ApiKeyToInvalidate[], isAdmin = false) => {
+ public async invalidateApiKeys(apiKeys: ApiKeyToInvalidate[], isAdmin = false) {
return await this.http.post(`${apiKeysUrl}/invalidate`, {
body: JSON.stringify({ apiKeys, isAdmin }),
});
- };
+ }
- public createApiKey = async (apiKey: CreateApiKeyRequest) => {
+ public async createApiKey(apiKey: CreateApiKeyRequest) {
return await this.http.post(apiKeysUrl, {
body: JSON.stringify(apiKey),
});
- };
+ }
}
diff --git a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx
index 3ddabb0dc55f8c..b9a0d5ec79a9f4 100644
--- a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx
+++ b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx
@@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
+/* eslint-disable @elastic/eui/href-or-on-click */
+
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { Component } from 'react';
@@ -120,7 +122,11 @@ export class SecurityNavControl extends Component {
-
+
{
-
+
-
+
+
+
,
el
);
diff --git a/x-pack/plugins/security/server/routes/api_keys/create.ts b/x-pack/plugins/security/server/routes/api_keys/create.ts
index dfef44e9b6d92f..25e3fdc1b76900 100644
--- a/x-pack/plugins/security/server/routes/api_keys/create.ts
+++ b/x-pack/plugins/security/server/routes/api_keys/create.ts
@@ -6,15 +6,10 @@
import { schema } from '@kbn/config-schema';
import { createLicensedRouteHandler } from '../licensed_route_handler';
-import { ApiKey } from '../../../common/model';
-import { wrapError, wrapIntoCustomErrorResponse } from '../../errors';
+import { wrapIntoCustomErrorResponse } from '../../errors';
+import { elasticsearchRoleSchema } from '../authorization/roles/model/put_payload';
import { RouteDefinitionParams } from '..';
-interface ResponseType {
- itemsInvalidated: Array>;
- errors: Array & { error: Error }>;
-}
-
export function defineCreateApiKeyRoutes({ router, clusterClient }: RouteDefinitionParams) {
router.post(
{
@@ -23,6 +18,7 @@ export function defineCreateApiKeyRoutes({ router, clusterClient }: RouteDefinit
body: schema.object({
name: schema.string(),
expiration: schema.maybe(schema.string()),
+ role_descriptors: schema.maybe(schema.recordOf(schema.string(), elasticsearchRoleSchema)),
}),
},
},
diff --git a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts
index e0af14f90d01c5..5660dfe5057582 100644
--- a/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts
+++ b/x-pack/plugins/security/server/routes/authorization/roles/model/put_payload.ts
@@ -15,7 +15,7 @@ import { ElasticsearchRole } from './elasticsearch_role';
* Elasticsearch specific portion of the role definition.
* See more details at https://www.elastic.co/guide/en/elasticsearch/reference/master/security-api.html#security-role-apis.
*/
-const elasticsearchRoleSchema = schema.object({
+export const elasticsearchRoleSchema = schema.object({
/**
* An optional list of cluster privileges. These privileges define the cluster level actions that
* users with this role are able to execute
From 1fc64ab9c97b1fa062c6f0ea123ae3ccc9638e10 Mon Sep 17 00:00:00 2001
From: Thom Heymann <190132+thomheymann@users.noreply.github.com>
Date: Mon, 2 Nov 2020 10:56:33 +0000
Subject: [PATCH 04/15] Apply suggestions from code review
Co-authored-by: Larry Gregory
---
.../account_management/api_keys_page/create_api_key_flyout.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
index faafc6809e349c..9af6d0eac750db 100644
--- a/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
@@ -29,7 +29,7 @@ export interface FormValues {
name: string;
expiration: string;
customExpiration: boolean;
- customPriviliges: boolean;
+ customPrivileges: boolean;
role_descriptors: string;
}
From ad3907eeaa18cb1a5d95184b748cebf59f19c1df Mon Sep 17 00:00:00 2001
From: Thom Heymann
Date: Mon, 2 Nov 2020 11:54:58 +0000
Subject: [PATCH 05/15] Updated lock file and create route handler
---
.../security/server/routes/api_keys/create.ts | 15 +-
yarn.lock | 344 ++----------------
2 files changed, 32 insertions(+), 327 deletions(-)
diff --git a/x-pack/plugins/security/server/routes/api_keys/create.ts b/x-pack/plugins/security/server/routes/api_keys/create.ts
index 25e3fdc1b76900..81b4ac2cbf505c 100644
--- a/x-pack/plugins/security/server/routes/api_keys/create.ts
+++ b/x-pack/plugins/security/server/routes/api_keys/create.ts
@@ -10,7 +10,7 @@ import { wrapIntoCustomErrorResponse } from '../../errors';
import { elasticsearchRoleSchema } from '../authorization/roles/model/put_payload';
import { RouteDefinitionParams } from '..';
-export function defineCreateApiKeyRoutes({ router, clusterClient }: RouteDefinitionParams) {
+export function defineCreateApiKeyRoutes({ router, authc }: RouteDefinitionParams) {
router.post(
{
path: '/internal/security/api_key',
@@ -18,16 +18,19 @@ export function defineCreateApiKeyRoutes({ router, clusterClient }: RouteDefinit
body: schema.object({
name: schema.string(),
expiration: schema.maybe(schema.string()),
- role_descriptors: schema.maybe(schema.recordOf(schema.string(), elasticsearchRoleSchema)),
+ role_descriptors: schema.recordOf(schema.string(), elasticsearchRoleSchema, {
+ defaultValue: {},
+ }),
}),
},
},
createLicensedRouteHandler(async (context, request, response) => {
- const scopedClusterClient = clusterClient.asScoped(request);
try {
- const apiKey = await scopedClusterClient.callAsCurrentUser('shield.createAPIKey', {
- body: request.body,
- });
+ const apiKey = await authc.createAPIKey(request, request.body);
+
+ if (!apiKey) {
+ return response.badRequest({ body: { message: `API Keys are not available` } });
+ }
return response.ok({ body: apiKey });
} catch (error) {
diff --git a/yarn.lock b/yarn.lock
index bd64b8cdd07e14..0e44dbfebb06e4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5761,7 +5761,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da"
integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==
-ajv@^4.7.0, ajv@^4.9.1:
+ajv@^4.7.0:
version "4.11.8"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=
@@ -5769,16 +5769,6 @@ ajv@^4.7.0, ajv@^4.9.1:
co "^4.6.0"
json-stable-stringify "^1.0.1"
-ajv@^5.0.0:
- version "5.5.2"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
- integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=
- dependencies:
- co "^4.6.0"
- fast-deep-equal "^1.0.0"
- fast-json-stable-stringify "^2.0.0"
- json-schema-traverse "^0.3.0"
-
ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.5.5, ajv@^6.9.1:
version "6.12.4"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234"
@@ -5789,16 +5779,6 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.5.5, ajv@
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
-ajv@^6.12.3:
- version "6.12.6"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
- integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
- dependencies:
- fast-deep-equal "^3.1.1"
- fast-json-stable-stringify "^2.0.0"
- json-schema-traverse "^0.4.1"
- uri-js "^4.2.2"
-
align-text@^0.1.1, align-text@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
@@ -6587,11 +6567,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
-assert-plus@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
- integrity sha1-104bh+ev/A24qttwIfP+SBAasjQ=
-
assert@^1.1.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
@@ -6795,21 +6770,11 @@ await-event@^2.1.0:
resolved "https://registry.yarnpkg.com/await-event/-/await-event-2.1.0.tgz#78e9f92684bae4022f9fa0b5f314a11550f9aa76"
integrity sha1-eOn5JoS65AIvn6C18xShFVD5qnY=
-aws-sign2@~0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
- integrity sha1-FDQt0428yU0OW4fXY81jYSwOeU8=
-
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
-aws4@^1.2.1:
- version "1.10.1"
- resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428"
- integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==
-
aws4@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
@@ -7599,13 +7564,6 @@ boolbase@^1.0.0, boolbase@~1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
-boom@2.x.x:
- version "2.10.1"
- resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f"
- integrity sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=
- dependencies:
- hoek "2.x.x"
-
boom@7.x.x, boom@^7.1.0, boom@^7.2.0:
version "7.2.2"
resolved "https://registry.yarnpkg.com/boom/-/boom-7.2.2.tgz#ac92101451aa5cea901aed07d881dd32b4f08345"
@@ -9022,7 +8980,7 @@ colorspace@1.1.x:
color "3.0.x"
text-hex "1.0.x"
-combined-stream@^1.0.5, combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.5, combined-stream@~1.0.6:
+combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
@@ -9627,13 +9585,6 @@ crypt@~0.0.1:
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
-cryptiles@2.x.x:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
- integrity sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=
- dependencies:
- boom "2.x.x"
-
cryptiles@4.x.x:
version "4.1.3"
resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-4.1.3.tgz#2461d3390ea0b82c643a6ba79f0ed491b0934c25"
@@ -10465,12 +10416,7 @@ deep-object-diff@^1.1.0:
resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.0.tgz#d6fabf476c2ed1751fc94d5ca693d2ed8c18bc5a"
integrity sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw==
-deepmerge@3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.2.0.tgz#58ef463a57c08d376547f8869fdc5bcee957f44e"
- integrity sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow==
-
-deepmerge@^4.0.0, deepmerge@^4.2.2:
+deepmerge@3.2.0, deepmerge@^4.0.0, deepmerge@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
@@ -11411,13 +11357,6 @@ encodeurl@^1.0.2, encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
-encoding@^0.1.11:
- version "0.1.13"
- resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
- integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
- dependencies:
- iconv-lite "^0.6.2"
-
end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1, end-of-stream@^1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@@ -12478,7 +12417,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2:
assign-symbols "^1.0.0"
is-extendable "^1.0.1"
-extend@^3.0.0, extend@~3.0.0, extend@~3.0.2:
+extend@^3.0.0, extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
@@ -12578,21 +12517,11 @@ fancy-log@^1.3.2:
color-support "^1.1.3"
time-stamp "^1.0.0"
-fast-deep-equal@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
- integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=
-
-fast-deep-equal@^3.1.1:
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3:
version "3.1.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
-fast-deep-equal@^3.1.3, fast-deep-equal@~3.1.3:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
- integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
-
fast-diff@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
@@ -13267,15 +13196,6 @@ form-data@^3.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
-form-data@~2.1.1:
- version "2.1.4"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
- integrity sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=
- dependencies:
- asynckit "^0.4.0"
- combined-stream "^1.0.5"
- mime-types "^2.1.12"
-
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@@ -14805,32 +14725,11 @@ hapi@^17.5.3:
teamwork "3.x.x"
topo "3.x.x"
-har-schema@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
- integrity sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=
-
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
-har-validator@~4.2.1:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
- integrity sha1-M0gdDxu/9gDdID11gSpqX7oALio=
- dependencies:
- ajv "^4.9.1"
- har-schema "^1.0.5"
-
-har-validator@~5.1.0:
- version "5.1.5"
- resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
- integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
- dependencies:
- ajv "^6.12.3"
- har-schema "^2.0.0"
-
har-validator@~5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
@@ -15132,16 +15031,6 @@ hat@0.0.3:
resolved "https://registry.yarnpkg.com/hat/-/hat-0.0.3.tgz#bb014a9e64b3788aed8005917413d4ff3d502d8a"
integrity sha1-uwFKnmSzeIrtgAWRdBPU/z1QLYo=
-hawk@~3.1.3:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
- integrity sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=
- dependencies:
- boom "2.x.x"
- cryptiles "2.x.x"
- hoek "2.x.x"
- sntp "1.x.x"
-
he@1.2.0, he@1.2.x, he@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
@@ -15210,11 +15099,6 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
-hoek@2.x.x:
- version "2.16.3"
- resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
- integrity sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=
-
hoek@5.x.x, hoek@^5.0.4:
version "5.0.4"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-5.0.4.tgz#0f7fa270a1cafeb364a4b2ddfaa33f864e4157da"
@@ -15225,12 +15109,7 @@ hoek@6.x.x:
resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.0.3.tgz#7884360426d927865a0a1251fc9c59313af5b798"
integrity sha512-TU6RyZ/XaQCTWRLrdqZZtZqwxUVr6PDMfi6MlWNURZ7A6czanQqX4pFE1mdOUQR9FdPCsZ0UzL8jI/izZ+eBSQ==
-hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0, hoist-non-react-statics@^2.5.5:
- version "2.5.5"
- resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
- integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==
-
-hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
+hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0, hoist-non-react-statics@^2.5.5, hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@@ -15485,15 +15364,6 @@ http-request-to-url@^1.0.0:
await-event "^2.1.0"
socket-location "^1.0.0"
-http-signature@~1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
- integrity sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=
- dependencies:
- assert-plus "^0.2.0"
- jsprim "^1.2.2"
- sshpk "^1.7.0"
-
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
@@ -15587,13 +15457,6 @@ iconv-lite@^0.5.0, iconv-lite@^0.5.1:
dependencies:
safer-buffer ">= 2.1.2 < 3"
-iconv-lite@^0.6.2:
- version "0.6.2"
- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01"
- integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==
- dependencies:
- safer-buffer ">= 2.1.2 < 3.0.0"
-
icss-utils@^4.0.0, icss-utils@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467"
@@ -16660,7 +16523,7 @@ is-ssh@^1.3.0:
dependencies:
protocols "^1.1.0"
-is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
+is-stream@^1.0.0, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
@@ -17740,11 +17603,6 @@ json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
-json-schema-traverse@^0.3.0:
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
- integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=
-
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
@@ -18752,17 +18610,7 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
-lodash@4.17.11:
- version "4.17.11"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
- integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
-
-lodash@4.17.15:
- version "4.17.15"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
- integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
-
-lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5:
+lodash@4.17.11, lodash@4.17.15, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5:
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
@@ -19482,7 +19330,7 @@ mime-db@1.44.0, mime-db@1.x.x, "mime-db@>= 1.40.0 < 2":
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
-mime-types@^2.1.12, mime-types@^2.1.25, mime-types@^2.1.26, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.7:
+mime-types@^2.1.12, mime-types@^2.1.25, mime-types@^2.1.26, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24:
version "2.1.27"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
@@ -19583,22 +19431,7 @@ minimist-options@^4.0.2:
is-plain-obj "^1.1.0"
kind-of "^6.0.3"
-minimist@0.0.8:
- version "0.0.8"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
- integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
-
-minimist@1.1.x:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.3.tgz#3bedfd91a92d39016fcfaa1c681e8faa1a1efda8"
- integrity sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=
-
-minimist@1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
- integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
-
-minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.0:
+minimist@0.0.8, minimist@1.1.x, minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@~1.2.0:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
@@ -20225,20 +20058,7 @@ node-environment-flags@1.0.6:
object.getownpropertydescriptors "^2.0.3"
semver "^5.7.0"
-node-fetch@2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5"
- integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=
-
-node-fetch@^1.0.1:
- version "1.7.3"
- resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
- integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
- dependencies:
- encoding "^0.1.11"
- is-stream "^1.0.1"
-
-node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1:
+node-fetch@2.1.2, node-fetch@^1.0.1, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
@@ -20248,16 +20068,11 @@ node-forge@0.9.0:
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==
-node-forge@^0.10.0:
+node-forge@^0.10.0, node-forge@^0.7.6:
version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
-node-forge@^0.7.6:
- version "0.7.6"
- resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac"
- integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==
-
node-gyp-build@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
@@ -20681,11 +20496,6 @@ nyc@^15.0.1:
test-exclude "^6.0.0"
yargs "^15.0.2"
-oauth-sign@~0.8.1:
- version "0.8.2"
- resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
- integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=
-
oauth-sign@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
@@ -22419,11 +22229,6 @@ pseudomap@^1.0.2:
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
-psl@^1.1.24:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
- integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
-
psl@^1.1.28:
version "1.4.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2"
@@ -22485,7 +22290,7 @@ punycode@2.x.x, punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
-punycode@^1.2.4, punycode@^1.4.1:
+punycode@^1.2.4:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
@@ -22537,11 +22342,6 @@ qs@6.7.0, qs@^6.4.0, qs@^6.5.1, qs@^6.6.0:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
-qs@~6.4.0:
- version "6.4.0"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
- integrity sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=
-
qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
@@ -24247,61 +24047,7 @@ request-promise@^4.2.2:
stealthy-require "^1.1.1"
tough-cookie "^2.3.3"
-request@2.81.0:
- version "2.81.0"
- resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
- integrity sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=
- dependencies:
- aws-sign2 "~0.6.0"
- aws4 "^1.2.1"
- caseless "~0.12.0"
- combined-stream "~1.0.5"
- extend "~3.0.0"
- forever-agent "~0.6.1"
- form-data "~2.1.1"
- har-validator "~4.2.1"
- hawk "~3.1.3"
- http-signature "~1.1.0"
- is-typedarray "~1.0.0"
- isstream "~0.1.2"
- json-stringify-safe "~5.0.1"
- mime-types "~2.1.7"
- oauth-sign "~0.8.1"
- performance-now "^0.2.0"
- qs "~6.4.0"
- safe-buffer "^5.0.1"
- stringstream "~0.0.4"
- tough-cookie "~2.3.0"
- tunnel-agent "^0.6.0"
- uuid "^3.0.0"
-
-request@2.88.0:
- version "2.88.0"
- resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
- integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
- dependencies:
- aws-sign2 "~0.7.0"
- aws4 "^1.8.0"
- caseless "~0.12.0"
- combined-stream "~1.0.6"
- extend "~3.0.2"
- forever-agent "~0.6.1"
- form-data "~2.3.2"
- har-validator "~5.1.0"
- http-signature "~1.2.0"
- is-typedarray "~1.0.0"
- isstream "~0.1.2"
- json-stringify-safe "~5.0.1"
- mime-types "~2.1.19"
- oauth-sign "~0.9.0"
- performance-now "^2.1.0"
- qs "~6.5.2"
- safe-buffer "^5.1.2"
- tough-cookie "~2.4.3"
- tunnel-agent "^0.6.0"
- uuid "^3.3.2"
-
-request@^2.74.0, request@^2.87.0, request@^2.88.0, request@^2.88.2:
+request@2.81.0, request@2.88.0, request@^2.74.0, request@^2.87.0, request@^2.88.0, request@^2.88.2:
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
@@ -24792,7 +24538,7 @@ safefs@^4.1.0:
editions "^1.1.1"
graceful-fs "^4.1.4"
-"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
+"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -24897,12 +24643,14 @@ scheduler@^0.18.0:
loose-envify "^1.1.0"
object-assign "^4.1.1"
-schema-utils@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf"
- integrity sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=
+schema-utils@1.0.0, schema-utils@^0.3.0, schema-utils@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
+ integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==
dependencies:
- ajv "^5.0.0"
+ ajv "^6.1.0"
+ ajv-errors "^1.0.0"
+ ajv-keywords "^3.1.0"
schema-utils@^0.4.5:
version "0.4.7"
@@ -24912,15 +24660,6 @@ schema-utils@^0.4.5:
ajv "^6.1.0"
ajv-keywords "^3.1.0"
-schema-utils@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
- integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==
- dependencies:
- ajv "^6.1.0"
- ajv-errors "^1.0.0"
- ajv-keywords "^3.1.0"
-
schema-utils@^2.0.0, schema-utils@^2.0.1, schema-utils@^2.5.0, schema-utils@^2.6.1, schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"
@@ -25420,13 +25159,6 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^2.0.0"
-sntp@1.x.x:
- version "1.0.9"
- resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
- integrity sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=
- dependencies:
- hoek "2.x.x"
-
socket-location@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/socket-location/-/socket-location-1.0.0.tgz#6f0c6f891c9a61c9a750265c14921d12196d266f"
@@ -26130,11 +25862,6 @@ stringify-object@^3.2.1:
is-obj "^1.0.1"
is-regexp "^1.0.0"
-stringstream@~0.0.4:
- version "0.0.6"
- resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.6.tgz#7880225b0d4ad10e30927d167a1d6f2fd3b33a72"
- integrity sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==
-
strip-ansi@*, strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
@@ -27182,21 +26909,6 @@ tough-cookie@^3.0.1:
psl "^1.1.28"
punycode "^2.1.1"
-tough-cookie@~2.3.0:
- version "2.3.4"
- resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655"
- integrity sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==
- dependencies:
- punycode "^1.4.1"
-
-tough-cookie@~2.4.3:
- version "2.4.3"
- resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
- integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
- dependencies:
- psl "^1.1.24"
- punycode "^1.4.1"
-
tr46@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
@@ -27530,21 +27242,11 @@ typescript-tuple@^2.2.1:
dependencies:
typescript-compare "^0.0.2"
-typescript@4.0.2:
+typescript@4.0.2, typescript@^3.0.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.4.5, typescript@~3.7.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2"
integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==
-typescript@^3.0.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.4.5:
- version "3.9.7"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
- integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
-
-typescript@~3.7.2:
- version "3.7.5"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
- integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
-
ua-parser-js@^0.7.18:
version "0.7.22"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.22.tgz#960df60a5f911ea8f1c818f3747b99c6e177eae3"
From d1906c16d4b8f96f8d866c6c6d2b08ef3c50882f Mon Sep 17 00:00:00 2001
From: Thom Heymann
Date: Mon, 2 Nov 2020 14:05:36 +0000
Subject: [PATCH 06/15] Fixed test failures
---
.../api_keys_page/create_api_key_flyout.tsx | 2 +-
.../management/api_keys/api_keys_api_client.mock.ts | 2 +-
x-pack/plugins/security/server/routes/views/index.test.ts | 8 ++++++++
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
index 994e6d9a2b73c2..69dd188824a129 100644
--- a/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
@@ -146,7 +146,7 @@ export const CreateApiKeyFlyout: FunctionComponent = ({
doc="security-api-create-api-key.html#security-api-create-api-key-request-body"
>
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.mock.ts b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.mock.ts
index 46551cae2bdc46..fac8f2e1b5b24a 100644
--- a/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.mock.ts
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_api_client.mock.ts
@@ -8,7 +8,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types';
import { APIKeysAPIClient } from './api_keys_api_client';
export const apiKeysAPIClientMock = {
- create: (): PublicMethodsOf => ({
+ create: (): jest.Mocked> => ({
checkPrivileges: jest.fn(),
getApiKeys: jest.fn(),
invalidateApiKeys: jest.fn(),
diff --git a/x-pack/plugins/security/server/routes/views/index.test.ts b/x-pack/plugins/security/server/routes/views/index.test.ts
index fa2088a80b1833..1be0a8e11a0fe8 100644
--- a/x-pack/plugins/security/server/routes/views/index.test.ts
+++ b/x-pack/plugins/security/server/routes/views/index.test.ts
@@ -22,6 +22,8 @@ describe('View routes', () => {
Array [
"/security/access_agreement",
"/security/account",
+ "/security/account/api-keys",
+ "/security/account/api-keys/create",
"/security/logged_out",
"/logout",
"/security/overwritten_session",
@@ -49,6 +51,8 @@ describe('View routes', () => {
"/login",
"/security/access_agreement",
"/security/account",
+ "/security/account/api-keys",
+ "/security/account/api-keys/create",
"/security/logged_out",
"/logout",
"/security/overwritten_session",
@@ -77,6 +81,8 @@ describe('View routes', () => {
"/login",
"/security/access_agreement",
"/security/account",
+ "/security/account/api-keys",
+ "/security/account/api-keys/create",
"/security/logged_out",
"/logout",
"/security/overwritten_session",
@@ -105,6 +111,8 @@ describe('View routes', () => {
"/login",
"/security/access_agreement",
"/security/account",
+ "/security/account/api-keys",
+ "/security/account/api-keys/create",
"/security/logged_out",
"/logout",
"/security/overwritten_session",
From e5e2d766dfa9fcdeff4095c7fa1c06d8207e0f21 Mon Sep 17 00:00:00 2001
From: Thom Heymann
Date: Mon, 2 Nov 2020 16:26:52 +0000
Subject: [PATCH 07/15] Fixed unit test and prettier errors
---
.../api_keys_page/api_keys_table.tsx | 4 +-
.../nav_control/nav_control_service.test.ts | 48 ++++++++++---------
2 files changed, 28 insertions(+), 24 deletions(-)
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_table.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_table.tsx
index 3a6fa8e70daf32..324f5d1e4d241a 100644
--- a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_table.tsx
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_table.tsx
@@ -73,8 +73,8 @@ export const ApiKeysTable: FunctionComponent = ({
{...props}
items={props.loading && !createdItemId ? [] : props.items}
tableCaption={i18n.translate('xpack.security.accountManagement.apiKeys.tableCaption', {
- defaultMessage: 'Below is a table of your API Keys.',
- })}
+ defaultMessage: 'Below is a table of your API Keys.',
+ })}
message={
props.loading
? i18n.translate('xpack.security.accountManagement.apiKeys.loadingMessage', {
diff --git a/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts b/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts
index acf62f3376b8b1..c7c64ccc67f4ce 100644
--- a/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts
+++ b/x-pack/plugins/security/public/nav_control/nav_control_service.test.ts
@@ -56,34 +56,38 @@ describe('SecurityNavControlService', () => {
expect(target).toMatchInlineSnapshot(`
From 325a21cbc3fc0775ddf5e139fdae10074c4f115b Mon Sep 17 00:00:00 2001
From: Thom Heymann
Date: Thu, 5 Nov 2020 19:52:12 +0000
Subject: [PATCH 08/15] Added suggestions from code review
---
.../account_management_app.tsx | 2 +-
.../api_keys_page/api_keys_page.tsx | 3 +-
.../api_keys_page/create_api_key_flyout.tsx | 12 ++-
.../components/form_flyout.tsx | 20 ++--
.../account_management/components/use_form.ts | 2 +-
.../server/routes/api_keys/create.test.ts | 99 +++++++++++++++++++
.../security/server/routes/api_keys/create.ts | 11 ++-
7 files changed, 130 insertions(+), 19 deletions(-)
create mode 100644 x-pack/plugins/security/server/routes/api_keys/create.test.ts
diff --git a/x-pack/plugins/security/public/account_management/account_management_app.tsx b/x-pack/plugins/security/public/account_management/account_management_app.tsx
index cf7ce75938e0ed..e4bab6bddf3224 100644
--- a/x-pack/plugins/security/public/account_management/account_management_app.tsx
+++ b/x-pack/plugins/security/public/account_management/account_management_app.tsx
@@ -8,7 +8,7 @@ import React, { lazy, Suspense, FunctionComponent } from 'react';
import ReactDOM from 'react-dom';
import { Router, useHistory } from 'react-router-dom';
import { History } from 'history';
-import { useAsync } from 'react-use';
+import useAsync from 'react-use/lib/useAsync';
import { i18n } from '@kbn/i18n';
import {
EuiAvatar,
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.tsx
index 7f70ab228cb0f9..81ebf21ba97db8 100644
--- a/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.tsx
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/api_keys_page.tsx
@@ -6,7 +6,8 @@
import React, { FunctionComponent, useState } from 'react';
import { Route, useHistory } from 'react-router-dom';
-import { useAsyncFn, useMount } from 'react-use';
+import useAsyncFn from 'react-use/lib/useAsyncFn';
+import useMount from 'react-use/lib/useMount';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiButton, EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
index 69dd188824a129..0df5788777e688 100644
--- a/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useRef, FunctionComponent } from 'react';
+import React, { FunctionComponent } from 'react';
import {
EuiCallOut,
EuiFieldNumber,
@@ -59,7 +59,6 @@ export const CreateApiKeyFlyout: FunctionComponent = ({
},
[services.http]
);
- const nameInput = useRef(null);
return (
= ({
}
)}
isLoading={form.isSubmitting}
- initialFocus={nameInput}
onSubmit={eventHandlers.onSubmit}
onClose={onClose}
size="s"
@@ -113,7 +111,6 @@ export const CreateApiKeyFlyout: FunctionComponent = ({
name="name"
defaultValue={form.values.name}
isInvalid={!!form.errors.name}
- inputRef={nameInput}
fullWidth
/>
@@ -209,7 +206,12 @@ export const CreateApiKeyFlyout: FunctionComponent = ({
isInvalid={!!form.errors.expiration}
>
{
title: string;
isLoading?: EuiButtonProps['isLoading'];
- initialFocus?: RefObject;
onSubmit: MouseEventHandler;
submitButtonText: string;
submitButtonColor?: EuiButtonProps['color'];
@@ -37,14 +36,15 @@ export const FormFlyout: FunctionComponent = ({
onSubmit,
isLoading,
children,
- initialFocus,
...rest
}) => {
+ const submitButton = useRef(null);
+
useEffect(() => {
- if (initialFocus && initialFocus.current) {
- initialFocus.current.focus();
+ if (submitButton.current) {
+ submitButton.current.focus();
}
- }, [initialFocus]);
+ }, []);
const flyout = (
@@ -65,7 +65,13 @@ export const FormFlyout: FunctionComponent = ({
-
+
{submitButtonText}
diff --git a/x-pack/plugins/security/public/account_management/components/use_form.ts b/x-pack/plugins/security/public/account_management/components/use_form.ts
index 80021b3a03d16f..1b9cadfba46809 100644
--- a/x-pack/plugins/security/public/account_management/components/use_form.ts
+++ b/x-pack/plugins/security/public/account_management/components/use_form.ts
@@ -12,7 +12,7 @@ import {
ChangeEventHandler,
ReactEventHandler,
} from 'react';
-import { useAsyncFn } from 'react-use';
+import useAsyncFn from 'react-use/lib/useAsyncFn';
export interface FormOptions extends SubmitHandlerOptions {
validate: ValidateFunction;
diff --git a/x-pack/plugins/security/server/routes/api_keys/create.test.ts b/x-pack/plugins/security/server/routes/api_keys/create.test.ts
new file mode 100644
index 00000000000000..d72d40f93bbb6d
--- /dev/null
+++ b/x-pack/plugins/security/server/routes/api_keys/create.test.ts
@@ -0,0 +1,99 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { kibanaResponseFactory } from '../../../../../../src/core/server';
+import { httpServerMock } from '../../../../../../src/core/server/mocks';
+import { createLicensedRouteHandler } from '../licensed_route_handler';
+import { routeDefinitionParamsMock } from '../index.mock';
+import { defineCreateApiKeyRoutes } from './create';
+
+jest.mock('../licensed_route_handler');
+
+const createLicensedRouteHandlerMock = createLicensedRouteHandler as jest.Mock;
+createLicensedRouteHandlerMock.mockImplementation((handler) => handler);
+
+describe('defineCreateApiKeyRoutes', () => {
+ beforeEach(() => {
+ createLicensedRouteHandlerMock.mockClear();
+ });
+
+ test('creates licensed route handler', () => {
+ const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
+ defineCreateApiKeyRoutes(mockRouteDefinitionParams);
+ expect(createLicensedRouteHandlerMock).toHaveBeenCalledTimes(1);
+ });
+
+ test('validates request body', () => {
+ const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
+ defineCreateApiKeyRoutes(mockRouteDefinitionParams);
+ const [[route]] = mockRouteDefinitionParams.router.post.mock.calls;
+ expect(() => (route.validate as any).body.validate({})).toThrow(
+ '[name]: expected value of type [string] but got [undefined]'
+ );
+ });
+
+ test('creates API key', async () => {
+ const createAPIKeyResult = {
+ id: 'ID',
+ name: 'NAME',
+ api_key: 'API_KEY',
+ };
+ const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
+ mockRouteDefinitionParams.authc.createAPIKey.mockResolvedValue(createAPIKeyResult);
+ defineCreateApiKeyRoutes(mockRouteDefinitionParams);
+ const [[, handler]] = mockRouteDefinitionParams.router.post.mock.calls;
+
+ const mockRequest = httpServerMock.createKibanaRequest({
+ method: 'post',
+ path: '/internal/security/api_key',
+ body: {},
+ });
+ const response = await handler(undefined, mockRequest, kibanaResponseFactory);
+ expect(mockRouteDefinitionParams.authc.createAPIKey).toHaveBeenCalledWith(
+ mockRequest,
+ mockRequest.body
+ );
+ expect(response.status).toBe(200);
+ expect(response.payload).toEqual(createAPIKeyResult);
+ });
+
+ test('returns bad request if api keys are not available', async () => {
+ const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
+ mockRouteDefinitionParams.authc.createAPIKey.mockResolvedValue(undefined);
+ defineCreateApiKeyRoutes(mockRouteDefinitionParams);
+ const [[, handler]] = mockRouteDefinitionParams.router.post.mock.calls;
+
+ const response = await handler(
+ undefined,
+ httpServerMock.createKibanaRequest({
+ method: 'post',
+ path: '/internal/security/api_key',
+ body: {},
+ }),
+ kibanaResponseFactory
+ );
+ expect(response.status).toBe(400);
+ expect(response.payload).toEqual({ message: 'API Keys are not available' });
+ });
+
+ test('returns bad request if api key could not be created', async () => {
+ const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
+ mockRouteDefinitionParams.authc.createAPIKey.mockRejectedValue(new Error('Error'));
+ defineCreateApiKeyRoutes(mockRouteDefinitionParams);
+ const [[, handler]] = mockRouteDefinitionParams.router.post.mock.calls;
+
+ const response = await handler(
+ undefined,
+ httpServerMock.createKibanaRequest({
+ method: 'post',
+ path: '/internal/security/api_key',
+ body: {},
+ }),
+ kibanaResponseFactory
+ );
+ expect(response.status).toBe(500);
+ });
+});
diff --git a/x-pack/plugins/security/server/routes/api_keys/create.ts b/x-pack/plugins/security/server/routes/api_keys/create.ts
index 81b4ac2cbf505c..2bb6c077d16c29 100644
--- a/x-pack/plugins/security/server/routes/api_keys/create.ts
+++ b/x-pack/plugins/security/server/routes/api_keys/create.ts
@@ -7,7 +7,6 @@
import { schema } from '@kbn/config-schema';
import { createLicensedRouteHandler } from '../licensed_route_handler';
import { wrapIntoCustomErrorResponse } from '../../errors';
-import { elasticsearchRoleSchema } from '../authorization/roles/model/put_payload';
import { RouteDefinitionParams } from '..';
export function defineCreateApiKeyRoutes({ router, authc }: RouteDefinitionParams) {
@@ -18,9 +17,13 @@ export function defineCreateApiKeyRoutes({ router, authc }: RouteDefinitionParam
body: schema.object({
name: schema.string(),
expiration: schema.maybe(schema.string()),
- role_descriptors: schema.recordOf(schema.string(), elasticsearchRoleSchema, {
- defaultValue: {},
- }),
+ role_descriptors: schema.recordOf(
+ schema.string(),
+ schema.object({}, { unknowns: 'allow' }),
+ {
+ defaultValue: {},
+ }
+ ),
}),
},
},
From ef4b6e4179c5a399e2d4ca2280f6b97d79d90791 Mon Sep 17 00:00:00 2001
From: Thom Heymann
Date: Fri, 6 Nov 2020 09:16:10 +0000
Subject: [PATCH 09/15] Fixed types in tests
---
.../security/server/routes/api_keys/create.test.ts | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/x-pack/plugins/security/server/routes/api_keys/create.test.ts b/x-pack/plugins/security/server/routes/api_keys/create.test.ts
index d72d40f93bbb6d..6f4c2a9b85c48b 100644
--- a/x-pack/plugins/security/server/routes/api_keys/create.test.ts
+++ b/x-pack/plugins/security/server/routes/api_keys/create.test.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { kibanaResponseFactory } from '../../../../../../src/core/server';
+import { kibanaResponseFactory, RequestHandlerContext } from '../../../../../../src/core/server';
import { httpServerMock } from '../../../../../../src/core/server/mocks';
import { createLicensedRouteHandler } from '../licensed_route_handler';
import { routeDefinitionParamsMock } from '../index.mock';
@@ -15,6 +15,8 @@ jest.mock('../licensed_route_handler');
const createLicensedRouteHandlerMock = createLicensedRouteHandler as jest.Mock;
createLicensedRouteHandlerMock.mockImplementation((handler) => handler);
+const contextMock = ({} as unknown) as RequestHandlerContext;
+
describe('defineCreateApiKeyRoutes', () => {
beforeEach(() => {
createLicensedRouteHandlerMock.mockClear();
@@ -51,7 +53,7 @@ describe('defineCreateApiKeyRoutes', () => {
path: '/internal/security/api_key',
body: {},
});
- const response = await handler(undefined, mockRequest, kibanaResponseFactory);
+ const response = await handler(contextMock, mockRequest, kibanaResponseFactory);
expect(mockRouteDefinitionParams.authc.createAPIKey).toHaveBeenCalledWith(
mockRequest,
mockRequest.body
@@ -62,12 +64,12 @@ describe('defineCreateApiKeyRoutes', () => {
test('returns bad request if api keys are not available', async () => {
const mockRouteDefinitionParams = routeDefinitionParamsMock.create();
- mockRouteDefinitionParams.authc.createAPIKey.mockResolvedValue(undefined);
+ mockRouteDefinitionParams.authc.createAPIKey.mockResolvedValue(null);
defineCreateApiKeyRoutes(mockRouteDefinitionParams);
const [[, handler]] = mockRouteDefinitionParams.router.post.mock.calls;
const response = await handler(
- undefined,
+ contextMock,
httpServerMock.createKibanaRequest({
method: 'post',
path: '/internal/security/api_key',
@@ -86,7 +88,7 @@ describe('defineCreateApiKeyRoutes', () => {
const [[, handler]] = mockRouteDefinitionParams.router.post.mock.calls;
const response = await handler(
- undefined,
+ contextMock,
httpServerMock.createKibanaRequest({
method: 'post',
path: '/internal/security/api_key',
From 6dd9fa14ee687a992c4372d76143213b14989f96 Mon Sep 17 00:00:00 2001
From: Thom Heymann
Date: Fri, 6 Nov 2020 11:30:38 +0000
Subject: [PATCH 10/15] Fix test failure
---
.../__tests__/step_screenshot_display.test.tsx | 14 ++++++++------
.../monitor/synthetics/step_screenshot_display.tsx | 2 +-
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/step_screenshot_display.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/step_screenshot_display.test.tsx
index 16db430dbd73a1..e4ab6c64461606 100644
--- a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/step_screenshot_display.test.tsx
+++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/step_screenshot_display.test.tsx
@@ -6,15 +6,17 @@
import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers';
import React from 'react';
-import * as reactUse from 'react-use';
+import useIntersection from 'react-use/lib/useIntersection';
import { StepScreenshotDisplay } from '../step_screenshot_display';
-describe('StepScreenshotDisplayProps', () => {
- // @ts-ignore missing fields don't matter in this test, the component in question only relies on `isIntersecting`
- jest.spyOn(reactUse, 'useIntersection').mockImplementation(() => ({
- isIntersecting: true,
- }));
+jest.mock('react-use/lib/useIntersection');
+
+const useIntersectionMock = useIntersection as jest.Mock;
+useIntersectionMock.mockImplementation(() => ({
+ isIntersecting: true,
+}));
+describe('StepScreenshotDisplayProps', () => {
it('displays screenshot thumbnail when present', () => {
const wrapper = mountWithIntl(
Date: Mon, 9 Nov 2020 12:29:54 +0000
Subject: [PATCH 11/15] Added e2e tests
---
.../api_keys_page/create_api_key_flyout.tsx | 5 +-
.../components/form_flyout.tsx | 20 +++-----
.../account_management.config.ts | 51 +++++++++++++++++++
.../tests/account_management/api_keys_page.ts | 43 ++++++++++++++++
.../tests/account_management/index.ts | 15 ++++++
5 files changed, 120 insertions(+), 14 deletions(-)
create mode 100644 x-pack/test/security_functional/account_management.config.ts
create mode 100644 x-pack/test/security_functional/tests/account_management/api_keys_page.ts
create mode 100644 x-pack/test/security_functional/tests/account_management/index.ts
diff --git a/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx b/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
index 0df5788777e688..d876ea75c4282a 100644
--- a/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
+++ b/x-pack/plugins/security/public/account_management/api_keys_page/create_api_key_flyout.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { FunctionComponent } from 'react';
+import React, { useRef, FunctionComponent } from 'react';
import {
EuiCallOut,
EuiFieldNumber,
@@ -59,6 +59,7 @@ export const CreateApiKeyFlyout: FunctionComponent = ({
},
[services.http]
);
+ const nameInput = useRef(null);
return (
= ({
}
)}
isLoading={form.isSubmitting}
+ initialFocus={nameInput}
onSubmit={eventHandlers.onSubmit}
onClose={onClose}
size="s"
@@ -111,6 +113,7 @@ export const CreateApiKeyFlyout: FunctionComponent = ({
name="name"
defaultValue={form.values.name}
isInvalid={!!form.errors.name}
+ inputRef={nameInput}
fullWidth
/>
diff --git a/x-pack/plugins/security/public/account_management/components/form_flyout.tsx b/x-pack/plugins/security/public/account_management/components/form_flyout.tsx
index 6da8e7b638faac..63be4381285670 100644
--- a/x-pack/plugins/security/public/account_management/components/form_flyout.tsx
+++ b/x-pack/plugins/security/public/account_management/components/form_flyout.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useEffect, useRef, FunctionComponent, MouseEventHandler } from 'react';
+import React, { useEffect, useRef, FunctionComponent, MouseEventHandler, RefObject } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiTitle,
@@ -24,6 +24,7 @@ import {
export interface FormFlyoutProps extends Omit {
title: string;
isLoading?: EuiButtonProps['isLoading'];
+ initialFocus?: RefObject;
onSubmit: MouseEventHandler;
submitButtonText: string;
submitButtonColor?: EuiButtonProps['color'];
@@ -36,15 +37,14 @@ export const FormFlyout: FunctionComponent = ({
onSubmit,
isLoading,
children,
+ initialFocus,
...rest
}) => {
- const submitButton = useRef(null);
-
useEffect(() => {
- if (submitButton.current) {
- submitButton.current.focus();
+ if (initialFocus && initialFocus.current) {
+ initialFocus.current.focus();
}
- }, []);
+ }, [initialFocus]);
const flyout = (
@@ -65,13 +65,7 @@ export const FormFlyout: FunctionComponent = ({
-
+
{submitButtonText}
diff --git a/x-pack/test/security_functional/account_management.config.ts b/x-pack/test/security_functional/account_management.config.ts
new file mode 100644
index 00000000000000..9c336fc0b833b2
--- /dev/null
+++ b/x-pack/test/security_functional/account_management.config.ts
@@ -0,0 +1,51 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { resolve } from 'path';
+import { FtrConfigProviderContext } from '@kbn/test/types/ftr';
+import { services } from '../functional/services';
+import { pageObjects } from '../functional/page_objects';
+
+// the default export of config files must be a config provider
+// that returns an object with the projects config values
+export default async function ({ readConfigFile }: FtrConfigProviderContext) {
+ const kibanaCommonConfig = await readConfigFile(
+ require.resolve('../../../test/common/config.js')
+ );
+ const kibanaFunctionalConfig = await readConfigFile(
+ require.resolve('../../../test/functional/config.js')
+ );
+
+ return {
+ testFiles: [resolve(__dirname, './tests/account_management')],
+ services,
+ pageObjects,
+ servers: kibanaFunctionalConfig.get('servers'),
+ esTestCluster: {
+ ...kibanaCommonConfig.get('esTestCluster'),
+ license: 'trial',
+ serverArgs: [
+ ...kibanaCommonConfig.get('esTestCluster.serverArgs'),
+ 'xpack.security.authc.api_key.enabled=true',
+ ],
+ },
+ kbnTestServer: kibanaCommonConfig.get('kbnTestServer'),
+ apps: {
+ ...kibanaFunctionalConfig.get('apps'),
+ accountManagement: {
+ pathname: '/security/account',
+ },
+ accountManagementApiKeys: {
+ pathname: '/security/account/api-keys',
+ },
+ },
+ esArchiver: { directory: resolve(__dirname, 'es_archives') },
+ screenshots: { directory: resolve(__dirname, 'screenshots') },
+ junit: {
+ reportName: 'Chrome X-Pack Security Functional Tests (Account Management)',
+ },
+ };
+}
diff --git a/x-pack/test/security_functional/tests/account_management/api_keys_page.ts b/x-pack/test/security_functional/tests/account_management/api_keys_page.ts
new file mode 100644
index 00000000000000..90f1e3fc222853
--- /dev/null
+++ b/x-pack/test/security_functional/tests/account_management/api_keys_page.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function ({ getService, getPageObjects }: FtrProviderContext) {
+ const pageObjects = getPageObjects(['common']);
+ const log = getService('log');
+ const find = getService('find');
+ const security = getService('security');
+
+ describe('API keys page', function () {
+ before(async () => {
+ await security.role.create('allow_manage_own_api_key_role', {
+ elasticsearch: {
+ cluster: ['manage_own_api_key'],
+ },
+ });
+ await security.testUser.setRoles(['allow_manage_own_api_key_role']);
+ await pageObjects.common.navigateToApp('accountManagementApiKeys');
+ });
+
+ after(async () => {
+ await security.testUser.restoreDefaults();
+ });
+
+ it('creates API key', async () => {
+ log.debug('Checking for section header');
+
+ await find.clickByLinkText('Create API key');
+
+ const nameInput = await find.byName('name');
+ await nameInput.type('test');
+
+ await find.clickByButtonText('Create API key');
+
+ await find.byCssSelector('.euiCallOut--success');
+ });
+ });
+}
diff --git a/x-pack/test/security_functional/tests/account_management/index.ts b/x-pack/test/security_functional/tests/account_management/index.ts
new file mode 100644
index 00000000000000..a39109faf6b89c
--- /dev/null
+++ b/x-pack/test/security_functional/tests/account_management/index.ts
@@ -0,0 +1,15 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function ({ loadTestFile }: FtrProviderContext) {
+ describe('security app - Account Management', function () {
+ this.tags('ciGroup4');
+
+ loadTestFile(require.resolve('./api_keys_page'));
+ });
+}
From dad4664a6da6bd7452c1db8a5c3090de8a0a812f Mon Sep 17 00:00:00 2001
From: Thom Heymann
Date: Mon, 9 Nov 2020 15:53:39 +0000
Subject: [PATCH 12/15] Fix linting errors
---
.../public/account_management/components/form_flyout.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/x-pack/plugins/security/public/account_management/components/form_flyout.tsx b/x-pack/plugins/security/public/account_management/components/form_flyout.tsx
index 63be4381285670..92d282bc972ccf 100644
--- a/x-pack/plugins/security/public/account_management/components/form_flyout.tsx
+++ b/x-pack/plugins/security/public/account_management/components/form_flyout.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useEffect, useRef, FunctionComponent, MouseEventHandler, RefObject } from 'react';
+import React, { useEffect, FunctionComponent, MouseEventHandler, RefObject } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiTitle,
From aeb06623e4c5b6cd67d8b18706c6ad6f052796db Mon Sep 17 00:00:00 2001
From: Thom Heymann
Date: Tue, 10 Nov 2020 09:12:02 +0000
Subject: [PATCH 13/15] Added link to account management
---
.../empty_prompt/empty_prompt.tsx | 18 ++++--------------
.../test/functional/apps/api_keys/home_page.ts | 4 ++--
.../functional/page_objects/api_keys_page.ts | 4 ++--
3 files changed, 8 insertions(+), 18 deletions(-)
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx
index 9b2ccfcb99ef30..17e2ed1406c67e 100644
--- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx
@@ -44,17 +44,7 @@ export const EmptyPrompt: React.FunctionComponent = ({
-
-
- ),
- }}
+ defaultMessage="You can create an API key from your account."
/>
@@ -62,12 +52,12 @@ export const EmptyPrompt: React.FunctionComponent = ({
actions={
navigateToApp('dev_tools')}
- data-test-subj="goToConsoleButton"
+ onClick={() => navigateToApp('security_account', { path: 'api-keys' })}
+ data-test-subj="goToAccountButton"
>
}
diff --git a/x-pack/test/functional/apps/api_keys/home_page.ts b/x-pack/test/functional/apps/api_keys/home_page.ts
index 39d8449218ffad..52355aba3a5c7b 100644
--- a/x-pack/test/functional/apps/api_keys/home_page.ts
+++ b/x-pack/test/functional/apps/api_keys/home_page.ts
@@ -34,8 +34,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
const headers = await testSubjects.findAll('noApiKeysHeader');
if (headers.length > 0) {
expect(await headers[0].getVisibleText()).to.be('No API keys');
- const goToConsoleButton = await pageObjects.apiKeys.getGoToConsoleButton();
- expect(await goToConsoleButton.isDisplayed()).to.be(true);
+ const goToAccountButton = await pageObjects.apiKeys.getGoToAccountButton();
+ expect(await goToAccountButton.isDisplayed()).to.be(true);
} else {
// page may already contain EiTable with data, then check API Key Admin text
const description = await pageObjects.apiKeys.getApiKeyAdminDesc();
diff --git a/x-pack/test/functional/page_objects/api_keys_page.ts b/x-pack/test/functional/page_objects/api_keys_page.ts
index fa10c5a574c095..d1aeda653f8a0b 100644
--- a/x-pack/test/functional/page_objects/api_keys_page.ts
+++ b/x-pack/test/functional/page_objects/api_keys_page.ts
@@ -18,8 +18,8 @@ export function ApiKeysPageProvider({ getService }: FtrProviderContext) {
return await testSubjects.getVisibleText('apiKeyAdminDescriptionCallOut');
},
- async getGoToConsoleButton() {
- return await testSubjects.find('goToConsoleButton');
+ async getGoToAccountButton() {
+ return await testSubjects.find('goToAccountButton');
},
async apiKeysPermissionDeniedMessage() {
From fe64fd99d0c8186d7d22c5e94e695ceb8261160b Mon Sep 17 00:00:00 2001
From: Thom Heymann
Date: Tue, 10 Nov 2020 10:22:23 +0000
Subject: [PATCH 14/15] Fixed translations
---
.../api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx | 4 ++--
x-pack/plugins/translations/translations/ja-JP.json | 3 ---
x-pack/plugins/translations/translations/zh-CN.json | 3 ---
3 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx
index 17e2ed1406c67e..330637087d9d8c 100644
--- a/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx
+++ b/x-pack/plugins/security/public/management/api_keys/api_keys_grid/empty_prompt/empty_prompt.tsx
@@ -7,7 +7,7 @@
import React, { Fragment } from 'react';
import { ApplicationStart } from 'kibana/public';
-import { EuiEmptyPrompt, EuiButton, EuiLink } from '@elastic/eui';
+import { EuiEmptyPrompt, EuiButton } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { DocumentationLinksService } from '../../documentation_links';
@@ -56,7 +56,7 @@ export const EmptyPrompt: React.FunctionComponent = ({
data-test-subj="goToAccountButton"
>
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 485b24dced3463..f9404d7882e953 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -15917,9 +15917,6 @@
"xpack.security.management.apiKeys.table.apiKeysTitle": "API キー",
"xpack.security.management.apiKeys.table.creationDateColumnName": "作成済み",
"xpack.security.management.apiKeys.table.emptyPromptAdminTitle": "API キーがありません",
- "xpack.security.management.apiKeys.table.emptyPromptConsoleButtonMessage": "コンソールに移動してください",
- "xpack.security.management.apiKeys.table.emptyPromptDescription": "コンソールで {link} を作成できます。",
- "xpack.security.management.apiKeys.table.emptyPromptDocsLinkMessage": "API キー",
"xpack.security.management.apiKeys.table.emptyPromptNonAdminTitle": "まだ API キーがありません",
"xpack.security.management.apiKeys.table.expirationDateColumnName": "有効期限",
"xpack.security.management.apiKeys.table.expirationDateNeverMessage": "無し",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 98d13011d3306f..58d2d048d76cdf 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -15935,9 +15935,6 @@
"xpack.security.management.apiKeys.table.apiKeysTitle": "API 密钥",
"xpack.security.management.apiKeys.table.creationDateColumnName": "创建时间",
"xpack.security.management.apiKeys.table.emptyPromptAdminTitle": "无 API 密钥",
- "xpack.security.management.apiKeys.table.emptyPromptConsoleButtonMessage": "前往 Console",
- "xpack.security.management.apiKeys.table.emptyPromptDescription": "您可以从 Console 创建 {link}。",
- "xpack.security.management.apiKeys.table.emptyPromptDocsLinkMessage": "API 密钥",
"xpack.security.management.apiKeys.table.emptyPromptNonAdminTitle": "您未有任何 API 密钥",
"xpack.security.management.apiKeys.table.expirationDateColumnName": "过期",
"xpack.security.management.apiKeys.table.expirationDateNeverMessage": "永远不",
From 16848fb39fdaa67aea9a5ee03f218c5e751e67b2 Mon Sep 17 00:00:00 2001
From: Thom Heymann
Date: Wed, 11 Nov 2020 09:56:37 +0000
Subject: [PATCH 15/15] Remove unused eslint disable
---
.../security/public/nav_control/nav_control_component.tsx | 2 --
1 file changed, 2 deletions(-)
diff --git a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx
index ff676313832f30..ee90eb1379d0ab 100644
--- a/x-pack/plugins/security/public/nav_control/nav_control_component.tsx
+++ b/x-pack/plugins/security/public/nav_control/nav_control_component.tsx
@@ -4,8 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-/* eslint-disable @elastic/eui/href-or-on-click */
-
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { Component } from 'react';