Skip to content

Commit

Permalink
feat(vault): Add vault setup instructions to settings (#4498)
Browse files Browse the repository at this point in the history
  • Loading branch information
ndv99 authored Oct 19, 2022
1 parent 0672557 commit ce46036
Show file tree
Hide file tree
Showing 7 changed files with 405 additions and 5 deletions.
3 changes: 3 additions & 0 deletions src/app/base/docsUrls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ const docsUrls = {
"https://maas.io/docs/how-to-work-with-tags#heading--kernel-options",
tagsXpathExpressions:
"https://maas.io/docs/how-to-work-with-tags#heading--xpath-expressions",
vaultIntegration:
// "https://maas.io/docs/how-to-integrate-with-vault#heading--enabling-vault", <-- TODO: Enable this link when the page is ready
"https://maas.io/docs",
} as const;

export default docsUrls;
58 changes: 58 additions & 0 deletions src/app/settings/views/Configuration/Security/Security.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import {
rootState as rootStateFactory,
tlsCertificate as tlsCertificateFactory,
tlsCertificateState as tlsCertificateStateFactory,
controller as controllerFactory,
controllerState as controllerStateFactory,
vaultEnabledState as vaultEnabledStateFactory,
} from "testing/factories";
import { renderWithBrowserRouter } from "testing/utils";

Expand All @@ -16,6 +19,30 @@ it("displays loading text if TLS certificate has not loaded", () => {
tlsCertificate: tlsCertificateStateFactory({
data: null,
loaded: false,
loading: true,
}),
vaultEnabled: vaultEnabledStateFactory({
data: false,
loaded: true,
}),
}),
});
renderWithBrowserRouter(<Security />, { wrapperProps: { state } });

expect(screen.getByText(/Loading.../)).toBeInTheDocument();
});

it("displays loading text if Vault Status has not loaded", () => {
const state = rootStateFactory({
general: generalStateFactory({
tlsCertificate: tlsCertificateStateFactory({
data: null,
loaded: true,
}),
vaultEnabled: vaultEnabledStateFactory({
data: false,
loaded: false,
loading: true,
}),
}),
});
Expand All @@ -31,6 +58,10 @@ it("renders TLS disabled section if no TLS certificate is present", () => {
data: null,
loaded: true,
}),
vaultEnabled: vaultEnabledStateFactory({
data: false,
loaded: true,
}),
}),
});
renderWithBrowserRouter(<Security />, { wrapperProps: { state } });
Expand All @@ -46,10 +77,37 @@ it("renders TLS enabled section if TLS certificate is present", () => {
data: tlsCertificateFactory(),
loaded: true,
}),
vaultEnabled: vaultEnabledStateFactory({
data: false,
loaded: true,
}),
}),
});
renderWithBrowserRouter(<Security />, { wrapperProps: { state } });

expect(screen.getByText(/TLS enabled/)).toBeInTheDocument();
expect(screen.queryByText(/TLS disabled/)).not.toBeInTheDocument();
});

it("renders the Vault section", () => {
const state = rootStateFactory({
general: generalStateFactory({
tlsCertificate: tlsCertificateStateFactory({
data: tlsCertificateFactory(),
loaded: true,
}),
vaultEnabled: vaultEnabledStateFactory({
data: false,
loaded: true,
}),
}),
controller: controllerStateFactory({
loaded: true,
items: [controllerFactory({ vault_configured: false })],
}),
});

renderWithBrowserRouter(<Security />, { wrapperProps: { state } });

expect(screen.getByText(/Integrate with Vault/)).toBeInTheDocument();
});
33 changes: 28 additions & 5 deletions src/app/settings/views/Configuration/Security/Security.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,52 @@ import { useDispatch, useSelector } from "react-redux";

import TLSDisabled from "./TLSDisabled";
import TLSEnabled from "./TLSEnabled";
import VaultSettings from "./VaultSettings";

import { useWindowTitle } from "app/base/hooks";
import { actions as generalActions } from "app/store/general";
import { tlsCertificate as tlsCertificateSelectors } from "app/store/general/selectors";
import {
tlsCertificate as tlsCertificateSelectors,
vaultEnabled as vaultEnabledSelectors,
} from "app/store/general/selectors";

const Security = (): JSX.Element => {
const dispatch = useDispatch();
const tlsCertificate = useSelector(tlsCertificateSelectors.get);
const tlsCertificateLoaded = useSelector(tlsCertificateSelectors.loaded);
const vaultEnabledLoaded = useSelector(vaultEnabledSelectors.loaded);
useWindowTitle("Security");

useEffect(() => {
dispatch(generalActions.fetchTlsCertificate());
dispatch(generalActions.fetchVaultEnabled());
}, [dispatch]);

if (!tlsCertificateLoaded) {
if (!tlsCertificateLoaded && !vaultEnabledLoaded) {
return <Spinner text="Loading..." />;
}

return (
<Row>
<Col size={6}>{tlsCertificate ? <TLSEnabled /> : <TLSDisabled />}</Col>
</Row>
<>
<Row>
<Col size={6}>
<h4>Security protocols</h4>
{!tlsCertificateLoaded ? (
<Spinner text="Loading..." />
) : tlsCertificate ? (
<TLSEnabled />
) : (
<TLSDisabled />
)}
</Col>
</Row>
<Row>
<Col size={6}>
<h4>Secret storage</h4>
<VaultSettings />
</Col>
</Row>
</>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { screen } from "@testing-library/react";

import VaultSettings, { Labels as VaultSettingsLabels } from "./VaultSettings";

import type { Controller } from "app/store/controller/types";
import type { RootState } from "app/store/root/types";
import { NodeType } from "app/store/types/node";
import {
generalState as generalStateFactory,
controller as controllerFactory,
controllerState as controllerStateFactory,
rootState as rootStateFactory,
vaultEnabledState as vaultEnabledStateFactory,
} from "testing/factories";
import { renderWithMockStore } from "testing/utils";

describe("VaultSettings", () => {
let controllers: Controller[];
let state: RootState;
beforeEach(() => {
controllers = [
controllerFactory({
fqdn: "testcontroller1",
node_type: NodeType.REGION_AND_RACK_CONTROLLER,
system_id: "abc123",
vault_configured: false,
}),
controllerFactory({
fqdn: "testcontroller2",
node_type: NodeType.REGION_CONTROLLER,
system_id: "def456",
vault_configured: false,
}),
];
state = rootStateFactory({
controller: controllerStateFactory({
loaded: true,
loading: false,
items: controllers,
}),
general: generalStateFactory({
vaultEnabled: vaultEnabledStateFactory({
data: false,
loaded: true,
}),
}),
});
});

it("displays a spinner while loading", () => {
state.controller.loaded = false;
state.controller.loading = true;
state.general.vaultEnabled.loaded = false;

renderWithMockStore(<VaultSettings />, { state });

expect(screen.getByText(VaultSettingsLabels.Loading)).toBeInTheDocument();
});

it("displays the vault setup instructions if Vault is not configured on any controllers", () => {
renderWithMockStore(<VaultSettings />, { state });

expect(
screen.getByText(VaultSettingsLabels.IntegrateWithVault)
).toBeInTheDocument();
expect(
screen.getByLabelText(VaultSettingsLabels.IntegrateWithVault)
).toBeInTheDocument();
});

it("displays the vault setup instructions a warning if vault is not configured on all controllers", () => {
controllers[0].vault_configured = true;
state.controller.items = controllers;

renderWithMockStore(<VaultSettings />, { state });

expect(
screen.getByText(
/Incomplete Vault integration, configure 1 other controller with Vault to complete this operation./
)
);
expect(
screen.getByLabelText(
/Incomplete Vault integration, configure 1 other controller with Vault to complete this operation./
)
).toBeInTheDocument();
});

it("displays only the secret migration instruction if all controllers are set up but secrets are not migrated", () => {
controllers[0].vault_configured = true;
controllers[1].vault_configured = true;
state.controller.items = controllers;

renderWithMockStore(<VaultSettings />, { state });

expect(
screen.getByText(VaultSettingsLabels.SecretMigrationInstructions)
).toBeInTheDocument();
expect(
screen.getByLabelText(VaultSettingsLabels.SecretMigrationInstructions)
).toBeInTheDocument();
});

it("displays 'Vault enabled' and hides setup instructions if Vault is configured on all controllers", () => {
controllers[0].vault_configured = true;
controllers[1].vault_configured = true;
state.controller.items = controllers;
state.general.vaultEnabled.data = true;

renderWithMockStore(<VaultSettings />, { state });

expect(
screen.getByText(VaultSettingsLabels.VaultEnabled)
).toBeInTheDocument();
expect(
screen.queryByLabelText(VaultSettingsLabels.SetupInstructions)
).not.toBeInTheDocument();
});
});
Loading

0 comments on commit ce46036

Please sign in to comment.