Skip to content

Commit

Permalink
Add API Keys app to Management > Security. (elastic#45740)
Browse files Browse the repository at this point in the history
* Add API Keys app to Management > Security.
- For admins, list all API Keys created by the user: Name, Date Created, Expiration Date, Status, User, and Realm.
- For non-admins, list own API keys: Name, Date Created, Expiration Date, and Status.
- Surface admin status above table.
- Ability to search by Name and Revoke (invalidate) API keys, and filter by User and Realm.
- Surface feedback when API keys are disabled on Elasticsearch or when user lacks required permissions.
* Add `SectionLoading` component to `es_ui_shared` plugin.
  • Loading branch information
cjcenizal authored Oct 14, 2019
1 parent 7fbea42 commit e8df2fa
Show file tree
Hide file tree
Showing 28 changed files with 1,349 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export { SectionLoading } from './section_loading';
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React from 'react';

import {
EuiEmptyPrompt,
EuiLoadingSpinner,
EuiText,
EuiFlexGroup,
EuiFlexItem,
EuiTextColor,
} from '@elastic/eui';

interface Props {
inline?: boolean;
children: React.ReactNode;
[key: string]: any;
}

export const SectionLoading: React.FunctionComponent<Props> = ({ inline, children, ...rest }) => {
if (inline) {
return (
<EuiFlexGroup justifyContent="flexStart" alignItems="center" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="m" />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText {...rest}>
<EuiTextColor color="subdued">{children}</EuiTextColor>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);
}

return (
<EuiEmptyPrompt
title={<EuiLoadingSpinner size="xl" />}
body={<EuiText color="subdued">{children}</EuiText>}
data-test-subj="sectionLoading"
/>
);
};
1 change: 1 addition & 0 deletions x-pack/legacy/plugins/security/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export const GLOBAL_RESOURCE = '*';
export const IGNORED_TYPES = ['space'];
export const APPLICATION_PREFIX = 'kibana-';
export const RESERVED_PRIVILEGES_APPLICATION_WILDCARD = 'kibana-*';
export const INTERNAL_API_BASE_PATH = '/internal/security';
20 changes: 20 additions & 0 deletions x-pack/legacy/plugins/security/common/model/api_key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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 interface ApiKey {
id: string;
name: string;
username: string;
realm: string;
creation: number;
expiration: number;
invalidated: boolean;
}

export interface ApiKeyToInvalidate {
id: string;
name: string;
}
1 change: 1 addition & 0 deletions x-pack/legacy/plugins/security/common/model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { Role, RoleIndexPrivilege, RoleKibanaPrivilege } from './role';
export { FeaturesPrivileges } from './features_privileges';
export { RawKibanaPrivileges, RawKibanaFeaturePrivileges } from './raw_kibana_privileges';
export { KibanaPrivileges } from './kibana_privileges';
export { ApiKey } from './api_key';
export { User, EditUser, getUserDisplayName } from '../../../../../plugins/security/common/model';
export {
AuthenticatedUser,
Expand Down
2 changes: 2 additions & 0 deletions x-pack/legacy/plugins/security/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import { resolve } from 'path';
import { initAuthenticateApi } from './server/routes/api/v1/authenticate';
import { initUsersApi } from './server/routes/api/v1/users';
import { initApiKeysApi } from './server/routes/api/v1/api_keys';
import { initExternalRolesApi } from './server/routes/api/external/roles';
import { initPrivilegesApi } from './server/routes/api/external/privileges';
import { initIndicesApi } from './server/routes/api/v1/indices';
Expand Down Expand Up @@ -195,6 +196,7 @@ export const security = (kibana) => new kibana.Plugin({
initAPIAuthorization(server, authorization);
initAppAuthorization(server, xpackMainPlugin, authorization);
initUsersApi(securityPlugin, server);
initApiKeysApi(server);
initExternalRolesApi(server);
initIndicesApi(server);
initPrivilegesApi(server);
Expand Down
48 changes: 48 additions & 0 deletions x-pack/legacy/plugins/security/public/lib/api_keys_api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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 { kfetch } from 'ui/kfetch';
import { ApiKey, ApiKeyToInvalidate } from '../../common/model/api_key';
import { INTERNAL_API_BASE_PATH } from '../../common/constants';

interface CheckPrivilegesResponse {
areApiKeysEnabled: boolean;
isAdmin: boolean;
}

interface InvalidateApiKeysResponse {
itemsInvalidated: ApiKeyToInvalidate[];
errors: any[];
}

interface GetApiKeysResponse {
apiKeys: ApiKey[];
}

const apiKeysUrl = `${INTERNAL_API_BASE_PATH}/api_key`;

export class ApiKeysApi {
public static async checkPrivileges(): Promise<CheckPrivilegesResponse> {
return kfetch({ pathname: `${apiKeysUrl}/privileges` });
}

public static async getApiKeys(isAdmin: boolean = false): Promise<GetApiKeysResponse> {
const query = {
isAdmin,
};

return kfetch({ pathname: apiKeysUrl, query });
}

public static async invalidateApiKeys(
apiKeys: ApiKeyToInvalidate[],
isAdmin: boolean = false
): Promise<InvalidateApiKeysResponse> {
const pathname = `${apiKeysUrl}/invalidate`;
const body = JSON.stringify({ apiKeys, isAdmin });
return kfetch({ pathname, method: 'POST', body });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<kbn-management-app section="security/api_keys">
<div id="apiKeysGridReactRoot" />
</kbn-management-app>
Original file line number Diff line number Diff line change
@@ -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 from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import routes from 'ui/routes';
import template from './api_keys.html';
import { API_KEYS_PATH } from '../management_urls';
import { getApiKeysBreadcrumbs } from '../breadcrumbs';
import { I18nContext } from 'ui/i18n';
import { ApiKeysGridPage } from './components';

routes.when(API_KEYS_PATH, {
template,
k7Breadcrumbs: getApiKeysBreadcrumbs,
controller($scope) {
$scope.$$postDigest(() => {
const domNode = document.getElementById('apiKeysGridReactRoot');

render(
<I18nContext>
<ApiKeysGridPage />
</I18nContext>, domNode);

// unmount react on controller destroy
$scope.$on('$destroy', () => {
unmountComponentAtNode(domNode);
});
});
},
});
Loading

0 comments on commit e8df2fa

Please sign in to comment.