Skip to content
Snippets Groups Projects
Unverified Commit f225c927 authored by Raphael Krut-Landau's avatar Raphael Krut-Landau Committed by GitHub
Browse files

Allow license tokens that start with `airgap_` (#42733)

Accept license tokens with an `airgap_` prefix, besides the already accepted 64-character long tokens.

This weakens the validation in the FE. Rigorous validation of tokens beginning with `airgap_` occurs in the BE. See this [Slack message](https://metaboat.slack.com/archives/C064EB1UE5P/p1715813885703249?thread_ts=1715813614.437989&cid=C064EB1UE5P).
parent 430df7ee
Branches
Tags
No related merge requests found
import { t } from "ttag";
import * as Yup from "yup";
import FormSubmitButton from "metabase/core/components/FormSubmitButton";
import {
FormProvider,
Form,
FormTextInput,
FormErrorMessage,
FormProvider,
FormTextInput,
} from "metabase/forms";
import * as Errors from "metabase/lib/errors";
import { Box, Button, Flex } from "metabase/ui";
import { LICENSE_TOKEN_SCHEMA } from "./constants";
type LicenseTokenFormProps = {
onSubmit: (token: string) => Promise<void>;
onSkip: () => void;
initialValue?: string;
};
const LICENSE_TOKEN_SCHEMA = Yup.object({
license_token: Yup.string()
.length(64, Errors.exactLength)
.required(Errors.required),
});
export const LicenseTokenForm = ({
onSubmit,
onSkip,
......
import * as Yup from "yup";
import * as Errors from "metabase/lib/errors";
export const LICENSE_TOKEN_SCHEMA = Yup.object({
license_token: Yup.string()
.test("license-token-test", Errors.exactLength({ length: 64 }), value =>
Boolean(value?.length === 64 || value?.startsWith("airgap_")),
)
.required(Errors.required),
});
import * as Errors from "metabase/lib/errors";
import { LICENSE_TOKEN_SCHEMA } from "./constants";
describe("LICENSE_TOKEN_SCHEMA", () => {
it("should validate a valid license token with the correct length", async () => {
const validLicenseToken = "a".repeat(64);
await expect(
LICENSE_TOKEN_SCHEMA.validate({ license_token: validLicenseToken }),
).resolves.toEqual({ license_token: validLicenseToken });
});
it('should validate a valid license token that starts with "airgap_"', async () => {
const validLicenseTokenAirgap = "airgap_toucan";
await expect(
LICENSE_TOKEN_SCHEMA.validate({ license_token: validLicenseTokenAirgap }),
).resolves.toEqual({ license_token: validLicenseTokenAirgap });
});
it("should show a length error for an invalid length license token", async () => {
const invalidLicenseToken = "a".repeat(63); // One character too short and does not start with "airgap_"
await expect(
LICENSE_TOKEN_SCHEMA.validate({ license_token: invalidLicenseToken }),
).rejects.toThrow(Errors.exactLength({ length: 64 }));
});
it("should show the length error when license token is not provided", async () => {
await expect(LICENSE_TOKEN_SCHEMA.validate({})).rejects.toThrow(
Errors.exactLength({ length: 64 }),
);
});
it("should show an error when license token is undefined", async () => {
await expect(
LICENSE_TOKEN_SCHEMA.validate({ license_token: undefined }),
).rejects.toThrow(Errors.exactLength({ length: 64 }));
});
it('should not validate a license token with invalid length that does not start with "airgap_"', async () => {
const invalidLicenseToken = "b".repeat(65); // Too long and does not start with "airgap_"
await expect(
LICENSE_TOKEN_SCHEMA.validate({ license_token: invalidLicenseToken }),
).rejects.toThrow(Errors.exactLength({ length: 64 }));
});
});
......@@ -34,6 +34,7 @@ const setupEnterprise = (opts?: SetupOpts) => {
};
const sampleToken = "a".repeat(64);
const airgapToken = "airgap_toucan";
describe("setup (EE, no token)", () => {
beforeEach(() => {
......@@ -79,7 +80,7 @@ describe("setup (EE, no token)", () => {
expect(await errMsg()).toBeInTheDocument();
});
it("should have the Activate button disabled when the token is not 64 characters long", async () => {
it("should have the Activate button disabled when the token is not 64 characters long (unless the token begins with 'airgap_')", async () => {
await setupForLicenseStep();
await inputToken("a".repeat(63));
......@@ -90,6 +91,10 @@ describe("setup (EE, no token)", () => {
await userEvent.type(input(), "a"); //65 characters
expect(await submitBtn()).toBeDisabled();
await userEvent.clear(input());
await userEvent.type(input(), "airgap_");
expect(await submitBtn()).toBeEnabled();
});
it("should ignore whitespace around the token", async () => {
......@@ -106,7 +111,7 @@ describe("setup (EE, no token)", () => {
});
});
it("should go to the next step when activating a valid token", async () => {
it("should go to the next step when activating a typical, valid token", async () => {
await setupForLicenseStep();
setupForTokenCheckEndpoint({ valid: true });
......@@ -122,6 +127,22 @@ describe("setup (EE, no token)", () => {
);
});
it("should go to the next step when activating an airgap-specific token", async () => {
await setupForLicenseStep();
setupForTokenCheckEndpoint({ valid: true });
await inputToken(airgapToken);
await submit();
expect(trackLicenseTokenStepSubmitted).toHaveBeenCalledWith(true);
expect(getSection("Usage data preferences")).toHaveAttribute(
"aria-current",
"step",
);
});
it("should be possible to skip the step without a token", async () => {
await setupForLicenseStep();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment