[Remote Clusters] Show what type of security model each cluster is using (#173489)

This commit is contained in:
Ignacio Rivas 2024-01-02 16:23:01 +01:00 committed by GitHub
parent 76854d450d
commit 8b3e1d3893
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 121 additions and 4 deletions

View file

@ -208,6 +208,7 @@ describe('<RemoteClusterList />', () => {
hasDeprecatedProxySetting: true,
seeds: null,
connectedNodesCount: null,
securityModel: 'api_keys',
});
const remoteClusters = [remoteCluster1, remoteCluster2, remoteCluster3];
@ -246,6 +247,7 @@ describe('<RemoteClusterList />', () => {
'Connected',
'default',
remoteCluster1.seeds.join(', '),
'CertificateInfo',
remoteCluster1.connectedNodesCount.toString(),
'', // Empty because the last column is for the "actions" on the resource
],
@ -255,6 +257,7 @@ describe('<RemoteClusterList />', () => {
'Not connected',
PROXY_MODE,
remoteCluster2.proxyAddress,
'CertificateInfo',
remoteCluster2.connectedSocketsCount.toString(),
'',
],
@ -264,6 +267,7 @@ describe('<RemoteClusterList />', () => {
'Not connected',
PROXY_MODE,
remoteCluster2.proxyAddress,
'api_keysInfo',
remoteCluster2.connectedSocketsCount.toString(),
'',
],
@ -278,13 +282,18 @@ describe('<RemoteClusterList />', () => {
});
test('should have a tooltip to indicate that the cluster has a deprecated setting', () => {
const secondRow = rows[2].reactWrapper; // The third cluster has been defined with deprecated setting
const thirdRow = rows[2].reactWrapper; // The third cluster has been defined with deprecated setting
expect(
findTestSubject(secondRow, 'remoteClustersTableListClusterWithDeprecatedSettingTooltip')
findTestSubject(thirdRow, 'remoteClustersTableListClusterWithDeprecatedSettingTooltip')
.length
).toBe(1);
});
test('should have a tooltip to indicate that the cluster is using an old security model', () => {
const secondRow = rows[1].reactWrapper;
expect(findTestSubject(secondRow, 'authenticationTypeWarning').length).toBe(1);
});
describe('bulk delete button', () => {
test('should be visible when a remote cluster is selected', () => {
expect(exists('remoteClusterBulkDeleteButton')).toBe(false);
@ -442,6 +451,11 @@ describe('<RemoteClusterList />', () => {
actions.clickRemoteClusterAt(1); // the remoteCluster2 has been configured by node
expect(exists('remoteClusterConfiguredByNodeWarning')).toBe(true);
});
test('Should display authentication type', () => {
actions.clickRemoteClusterAt(2);
expect(exists('remoteClusterDetailAuthType')).toBe(true);
});
});
});
});

View file

@ -26,3 +26,19 @@ export const API_BASE_PATH = '/api/remote_clusters';
export const SNIFF_MODE = 'sniff';
export const PROXY_MODE = 'proxy';
export const getSecurityModel = (type: string) => {
if (type === 'certificate') {
return i18n.translate('xpack.remoteClusters.securityModelCert', {
defaultMessage: 'Certificate',
});
}
if (type === 'api_key') {
return i18n.translate('xpack.remoteClusters.securityModelApiKey', {
defaultMessage: 'API key',
});
}
return type;
};

View file

@ -19,6 +19,7 @@ export const getRemoteClusterMock = ({
mode = SNIFF_MODE,
proxyAddress,
hasDeprecatedProxySetting = false,
securityModel = 'certificate',
} = {}) => ({
name,
seeds,
@ -32,4 +33,5 @@ export const getRemoteClusterMock = ({
connectedSocketsCount,
proxyAddress,
hasDeprecatedProxySetting,
securityModel,
});

View file

@ -7,3 +7,4 @@
export { ConnectionStatus } from './connection_status';
export { RemoveClusterButtonProvider } from './remove_cluster_button_provider';
export { SecurityModel } from './security_model';

View file

@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export { SecurityModel } from './security_model';

View file

@ -0,0 +1,40 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiText, EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui';
import { getSecurityModel } from '../../../../../../common/constants';
import { Cluster } from '../../../../../../common/lib/cluster_serialization';
export function SecurityModel({ securityModel }: { securityModel: Cluster['securityModel'] }) {
return (
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiText data-test-subj="authenticationTypeLabel" size="s">
{getSecurityModel(securityModel)}
</EuiText>
</EuiFlexItem>
{securityModel !== 'api_key' && (
<EuiFlexItem grow={false} data-test-subj="authenticationTypeWarning">
<EuiIconTip
type="iInCircle"
color="subdued"
content={
<FormattedMessage
id="xpack.remoteClusters.remoteClusterList.table.authTypeWarningMessage"
defaultMessage="If you need more fine-grained permissions on your remote cluster, you can now use API keys instead of certificates as authentication mechanism."
/>
}
/>
</EuiFlexItem>
)}
</EuiFlexGroup>
);
}

View file

@ -34,7 +34,7 @@ import {
import { reactRouterNavigate } from '@kbn/kibana-react-plugin/public';
import { PROXY_MODE } from '../../../../../common/constants';
import { ConfiguredByNodeWarning } from '../../components';
import { ConnectionStatus, RemoveClusterButtonProvider } from '../components';
import { ConnectionStatus, RemoveClusterButtonProvider, SecurityModel } from '../components';
import { getRouter } from '../../../services';
import { proxyModeUrl } from '../../../services/documentation';
@ -181,6 +181,7 @@ export class DetailPanel extends Component {
maxConnectionsPerCluster,
initialConnectTimeout,
mode,
securityModel,
}) {
return (
<EuiFlexGroup data-test-subj="remoteClusterDetailPanelStatusValues">
@ -198,6 +199,18 @@ export class DetailPanel extends Component {
<ConnectionStatus isConnected={isConnected} mode={mode} />
</EuiDescriptionListDescription>
<EuiDescriptionListTitle>
<EuiTitle size="xs">
<FormattedMessage
id="xpack.remoteClusters.detailPanel.securityModel"
defaultMessage="Authentication type"
/>
</EuiTitle>
</EuiDescriptionListTitle>
<EuiDescriptionListDescription data-test-subj="remoteClusterDetailAuthType">
<SecurityModel securityModel={securityModel} />
</EuiDescriptionListDescription>
<EuiDescriptionListTitle>
<EuiTitle size="xs">
<FormattedMessage
@ -280,6 +293,7 @@ export class DetailPanel extends Component {
connectedSocketsCount,
mode,
serverName,
securityModel,
}) {
return (
<EuiFlexGroup>
@ -297,6 +311,18 @@ export class DetailPanel extends Component {
<ConnectionStatus isConnected={isConnected} mode={mode} />
</EuiDescriptionListDescription>
<EuiDescriptionListTitle>
<EuiTitle size="xs">
<FormattedMessage
id="xpack.remoteClusters.detailPanel.securityModel"
defaultMessage="Authentication type"
/>
</EuiTitle>
</EuiDescriptionListTitle>
<EuiDescriptionListDescription data-test-subj="remoteClusterDetailAuthType">
<SecurityModel securityModel={securityModel} />
</EuiDescriptionListDescription>
<EuiDescriptionListTitle>
<EuiTitle size="xs">
<FormattedMessage

View file

@ -25,7 +25,7 @@ import { reactRouterNavigate } from '@kbn/kibana-react-plugin/public';
import { UIM_SHOW_DETAILS_CLICK } from '../../../constants';
import { PROXY_MODE } from '../../../../../common/constants';
import { trackUiMetric, METRIC_TYPE, getRouter } from '../../../services';
import { ConnectionStatus, RemoveClusterButtonProvider } from '../components';
import { ConnectionStatus, RemoveClusterButtonProvider, SecurityModel } from '../components';
const getFilteredClusters = (clusters, queryText) => {
if (queryText) {
@ -249,6 +249,16 @@ export class RemoteClusterTable extends Component {
return connectionMode;
},
},
{
field: 'securityModel',
name: i18n.translate('xpack.remoteClusters.remoteClusterList.table.authTypeColumnTitle', {
defaultMessage: 'Authentication type',
}),
sortable: true,
render: (securityModel) => {
return <SecurityModel securityModel={securityModel} />;
},
},
{
field: 'mode',
name: i18n.translate(