Skip to content

Commit

Permalink
[Remote clusters] Add new security model (#161836)
Browse files Browse the repository at this point in the history
  • Loading branch information
sabarasaba authored Aug 14, 2023
1 parent 56be6c6 commit 6e241a8
Show file tree
Hide file tree
Showing 25 changed files with 782 additions and 217 deletions.
3 changes: 3 additions & 0 deletions packages/kbn-doc-links/src/get_doc_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,9 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => {
remoteClusters: `${ELASTICSEARCH_DOCS}remote-clusters.html`,
remoteClustersProxy: `${ELASTICSEARCH_DOCS}remote-clusters.html#proxy-mode`,
remoteClusersProxySettings: `${ELASTICSEARCH_DOCS}remote-clusters-settings.html#remote-cluster-proxy-settings`,
remoteClustersOnPremSetupTrustWithCert: `${ELASTICSEARCH_DOCS}remote-clusters-cert.html`,
remoteClustersOnPremSetupTrustWithApiKey: `${ELASTICSEARCH_DOCS}remote-clusters-api-key.html`,
remoteClustersCloudSetupTrust: `${ELASTIC_WEBSITE_URL}guide/en/cloud/current/ec-enable-ccs.html`,
rrf: `${ELASTICSEARCH_DOCS}rrf.html`,
scriptParameters: `${ELASTICSEARCH_DOCS}modules-scripting-using.html#prefer-params`,
secureCluster: `${ELASTICSEARCH_DOCS}secure-cluster.html`,
Expand Down
8 changes: 8 additions & 0 deletions x-pack/plugins/remote_clusters/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Remote Clusters

## Setting up a remote cluster

* Run `yarn es snapshot --license=trial` and log into kibana
* Create a local copy of the ES snapshot with `cp -R .es/8.10.0 .es/8.10.0-2`
* Start your local copy of the ES snapshot .es/8.10.0-2/bin/elasticsearch -E cluster.name=europe -E transport.port=9400
* Create a remote cluster using `127.0.0.1:9400` as seed node and proceed with the wizard
* Verify that your newly created remote cluster is shown as connected in the remote clusters list

## About

This plugin helps users manage their [remote clusters](https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-remote-clusters.html), which enable cross-cluster search and cross-cluster replication.
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,63 @@ describe('Create Remote cluster', () => {
});
});

describe('Setup Trust', () => {
beforeEach(async () => {
await act(async () => {
({ actions, component } = await setup(httpSetup, {
canUseAPIKeyTrustModel: true,
}));
});

component.update();

actions.nameInput.setValue('remote_cluster_test');
actions.seedsInput.setValue('192.168.1.1:3000');

await actions.saveButton.click();
});

test('should contain two cards for setting up trust', () => {
// Cards exist
expect(actions.setupTrust.apiCardExist()).toBe(true);
expect(actions.setupTrust.certCardExist()).toBe(true);
// Each card has its doc link
expect(actions.setupTrust.apiCardDocsExist()).toBe(true);
expect(actions.setupTrust.certCardDocsExist()).toBe(true);
});

test('on submit should open confirm modal', async () => {
await actions.setupTrust.setupTrustConfirmClick();

expect(actions.setupTrust.isSubmitInConfirmDisabled()).toBe(true);
await actions.setupTrust.toggleConfirmSwitch();
expect(actions.setupTrust.isSubmitInConfirmDisabled()).toBe(false);
});

test('back button goes to first step', async () => {
await actions.setupTrust.backToFirstStepClick();
expect(actions.isOnFirstStep()).toBe(true);
});

test('shows only cert based config if API key trust model is not available', async () => {
await act(async () => {
({ actions, component } = await setup(httpSetup, {
canUseAPIKeyTrustModel: false,
}));
});

component.update();

actions.nameInput.setValue('remote_cluster_test');
actions.seedsInput.setValue('192.168.1.1:3000');

await actions.saveButton.click();

expect(actions.setupTrust.apiCardExist()).toBe(false);
expect(actions.setupTrust.certCardExist()).toBe(true);
});
});

describe('on prem', () => {
beforeEach(async () => {
await act(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,21 @@ export interface RemoteClustersActions {
getLabel: () => string;
exists: () => boolean;
};
isOnFirstStep: () => boolean;
saveButton: {
click: () => void;
isDisabled: () => boolean;
};
setupTrust: {
isSubmitInConfirmDisabled: () => boolean;
toggleConfirmSwitch: () => void;
setupTrustConfirmClick: () => void;
backToFirstStepClick: () => void;
apiCardExist: () => boolean;
certCardExist: () => boolean;
apiCardDocsExist: () => boolean;
certCardDocsExist: () => boolean;
};
getErrorMessages: () => string[];
globalErrorExists: () => boolean;
}
Expand Down Expand Up @@ -148,7 +159,7 @@ export const createRemoteClustersActions = (testBed: TestBed): RemoteClustersAct
};
};

const createSaveButtonActions = () => {
const createSetupTrustActions = () => {
const click = () => {
act(() => {
find('remoteClusterFormSaveButton').simulate('click');
Expand All @@ -157,7 +168,56 @@ export const createRemoteClustersActions = (testBed: TestBed): RemoteClustersAct
component.update();
};
const isDisabled = () => find('remoteClusterFormSaveButton').props().disabled;
return { saveButton: { click, isDisabled } };

const setupTrustConfirmClick = () => {
act(() => {
find('setupTrustDoneButton').simulate('click');
});

component.update();
};

const backToFirstStepClick = () => {
act(() => {
find('setupTrustBackButton').simulate('click');
});

component.update();
};

const isOnFirstStep = () => exists('remoteClusterFormNameInput');

const toggleConfirmSwitch = () => {
act(() => {
const $checkbox = find('remoteClusterTrustCheckbox');
const isChecked = $checkbox.props().checked;
$checkbox.simulate('change', { target: { checked: !isChecked } });
});

component.update();
};

const isSubmitInConfirmDisabled = () => find('remoteClusterTrustSubmitButton').props().disabled;

const apiCardExist = () => exists('setupTrustApiKeyCard');
const certCardExist = () => exists('setupTrustCertCard');
const apiCardDocsExist = () => exists('setupTrustApiKeyCardDocs');
const certCardDocsExist = () => exists('setupTrustCertCardDocs');

return {
isOnFirstStep,
saveButton: { click, isDisabled },
setupTrust: {
setupTrustConfirmClick,
isSubmitInConfirmDisabled,
toggleConfirmSwitch,
apiCardExist,
certCardExist,
apiCardDocsExist,
certCardDocsExist,
backToFirstStepClick,
},
};
};

const createServerNameActions = () => {
Expand Down Expand Up @@ -192,7 +252,7 @@ export const createRemoteClustersActions = (testBed: TestBed): RemoteClustersAct
...createCloudUrlInputActions(),
...createProxyAddressActions(),
...createServerNameActions(),
...createSaveButtonActions(),
...createSetupTrustActions(),
getErrorMessages: form.getErrorsMessages,
globalErrorExists,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const WithAppDependencies =
isCloudEnabled: !!isCloudEnabled,
cloudBaseUrl: 'test.com',
executionContext: executionContextServiceMock.createStartContract(),
canUseAPIKeyTrustModel: true,
...overrides,
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface Context {
isCloudEnabled: boolean;
cloudBaseUrl: string;
executionContext: ExecutionContextStart;
canUseAPIKeyTrustModel: boolean;
}

export const AppContext = createContext<Context>({} as any);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export declare const renderApp: (
isCloudEnabled: boolean;
cloudBaseUrl: string;
executionContext: ExecutionContextStart;
canUseAPIKeyTrustModel: boolean;
},
history: ScopedHistory,
theme$: Observable<CoreTheme>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

export { RemoteClusterSetupTrust } from './remote_cluster_setup_trust';
export { RemoteClusterForm } from './remote_cluster_form';
export { RemoteClusterPageTitle } from './remote_cluster_page_title';
export { ConfiguredByNodeWarning } from './configured_by_node_warning';
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,9 @@ import {
EuiFormRow,
EuiLink,
EuiLoadingLogo,
EuiLoadingSpinner,
EuiOverlayMask,
EuiSpacer,
EuiSwitch,
EuiText,
EuiTitle,
EuiDelayRender,
EuiScreenReaderOnly,
Expand Down Expand Up @@ -302,87 +300,67 @@ export class RemoteClusterForm extends Component<Props, State> {
}

renderActions() {
const { isSaving, cancel } = this.props;
const { isSaving, cancel, cluster: isEditMode } = this.props;
const { areErrorsVisible, isRequestVisible } = this.state;
const isSaveDisabled = (areErrorsVisible && this.hasErrors()) || isSaving;

if (isSaving) {
return (
<EuiFlexGroup justifyContent="flexStart" gutterSize="m">
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="l" />
</EuiFlexItem>

return (
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
{cancel && (
<EuiFlexItem grow={false}>
<EuiText>
<EuiButtonEmpty color="primary" onClick={cancel}>
<FormattedMessage
id="xpack.remoteClusters.remoteClusterForm.actions.savingText"
defaultMessage="Saving"
id="xpack.remoteClusters.remoteClusterForm.cancelButtonLabel"
defaultMessage="Cancel"
/>
</EuiText>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
);
}

let cancelButton;

if (cancel) {
cancelButton = (
<EuiFlexItem grow={false}>
<EuiButtonEmpty color="primary" onClick={cancel}>
<FormattedMessage
id="xpack.remoteClusters.remoteClusterForm.cancelButtonLabel"
defaultMessage="Cancel"
/>
</EuiButtonEmpty>
</EuiFlexItem>
);
}

const isSaveDisabled = areErrorsVisible && this.hasErrors();
)}

return (
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
<EuiFlexGroup alignItems="center" gutterSize="m">
<EuiFlexItem grow={false}>
<EuiButtonEmpty
onClick={this.toggleRequest}
data-test-subj="remoteClustersRequestButton"
>
{isRequestVisible ? (
<FormattedMessage
id="xpack.remoteClusters.remoteClusterForm.hideRequestButtonLabel"
defaultMessage="Hide request"
/>
) : (
<FormattedMessage
id="xpack.remoteClusters.remoteClusterForm.showRequestButtonLabel"
defaultMessage="Show request"
/>
)}
</EuiButtonEmpty>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="remoteClusterFormSaveButton"
color="success"
iconType="check"
color="primary"
onClick={this.save}
fill
isDisabled={isSaveDisabled}
isLoading={isSaving}
aria-describedby={`${this.generateId(ERROR_TITLE_ID)} ${this.generateId(
ERROR_LIST_ID
)}`}
>
<FormattedMessage
id="xpack.remoteClusters.remoteClusterForm.saveButtonLabel"
defaultMessage="Save"
id="xpack.remoteClusters.remoteClusterForm.nextButtonLabel"
defaultMessage="{isEditMode, select, true{Save} other{Next}}"
values={{
isEditMode: Boolean(isEditMode),
}}
/>
</EuiButton>
</EuiFlexItem>

{cancelButton}
</EuiFlexGroup>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={this.toggleRequest} data-test-subj="remoteClustersRequestButton">
{isRequestVisible ? (
<FormattedMessage
id="xpack.remoteClusters.remoteClusterForm.hideRequestButtonLabel"
defaultMessage="Hide request"
/>
) : (
<FormattedMessage
id="xpack.remoteClusters.remoteClusterForm.showRequestButtonLabel"
defaultMessage="Show request"
/>
)}
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
);
}
Expand Down Expand Up @@ -523,20 +501,20 @@ export class RemoteClusterForm extends Component<Props, State> {

return (
<Fragment>
<EuiSpacer size="m" data-test-subj="remoteClusterFormGlobalError" />
<EuiCallOut
title={
<h3 id={this.generateId(ERROR_TITLE_ID)}>
<span id={this.generateId(ERROR_TITLE_ID)}>
<FormattedMessage
id="xpack.remoteClusters.remoteClusterForm.errorTitle"
defaultMessage="Fix errors before continuing."
defaultMessage="Some fields require your attention."
/>
</h3>
</span>
}
color="danger"
iconType="cross"
iconType="error"
/>
<EuiDelayRender>{messagesToBeRendered}</EuiDelayRender>
<EuiSpacer size="m" data-test-subj="remoteClusterFormGlobalError" />
</Fragment>
);
};
Expand All @@ -549,6 +527,7 @@ export class RemoteClusterForm extends Component<Props, State> {
return (
<Fragment>
{this.renderSaveErrorFeedback()}
{this.renderErrors()}

<EuiForm data-test-subj="remoteClusterForm">
<EuiDescribedFormGroup
Expand Down Expand Up @@ -609,8 +588,6 @@ export class RemoteClusterForm extends Component<Props, State> {
{this.renderSkipUnavailable()}
</EuiForm>

{this.renderErrors()}

<EuiSpacer size="l" />

{this.renderActions()}
Expand Down
Loading

0 comments on commit 6e241a8

Please sign in to comment.