Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

migrated to use ui-shared #27849

Merged
merged 3 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,8 @@ describe("Realm settings events tab tests", () => {
it("Realm header settings", () => {
sidebarPage.goToRealmSettings();
cy.findByTestId("rs-security-defenses-tab").click();
cy.findByTestId("headers-form-tab-save").should("be.disabled");
cy.get("#xFrameOptions").clear().type("DENY");
cy.findByTestId("browserSecurityHeaders.xFrameOptions").clear();
cy.findByTestId("browserSecurityHeaders.xFrameOptions").type("DENY");
cy.findByTestId("headers-form-tab-save").should("be.enabled").click();

masthead.checkNotificationMessage("Realm successfully updated");
Expand All @@ -249,8 +249,6 @@ describe("Realm settings events tab tests", () => {
cy.findAllByTestId("rs-security-defenses-tab").click();
cy.get("#pf-tab-20-bruteForce").click();

cy.findByTestId("brute-force-tab-save").should("be.disabled");

cy.get("#kc-brute-force-mode").click();
cy.findByTestId("select-brute-force-mode")
.contains("Lockout temporarily")
Expand Down
39 changes: 30 additions & 9 deletions js/apps/admin-ui/cypress/e2e/realm_settings_tabs_test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,22 +136,43 @@ describe("Realm settings tabs tests", () => {
it("Realm header settings- update single input", () => {
sidebarPage.goToRealmSettings();
realmSettingsPage.goToSecurityDefensesTab();
cy.get("#xFrameOptions").clear().type("DENY");
cy.findByTestId("browserSecurityHeaders.xFrameOptions").clear();
cy.findByTestId("browserSecurityHeaders.xFrameOptions").type("DENY");
realmSettingsPage.saveSecurityDefensesHeaders();
masthead.checkNotificationMessage("Realm successfully updated");
});

it("Realm header settings- update all inputs", () => {
sidebarPage.goToRealmSettings();
realmSettingsPage.goToSecurityDefensesTab();
cy.get("#xFrameOptions").clear().type("SAMEORIGIN");
cy.get("#contentSecurityPolicy").clear().type("default-src 'self'");
cy.get("#strictTransportSecurity").clear().type("max-age=31536000");
cy.get("#xContentTypeOptions").clear().type("nosniff");
cy.get("#xRobotsTag").clear().type("none");
cy.get("#xXSSProtection").clear().type("1; mode=block");
cy.get("#strictTransportSecurity").clear().type("max-age=31537000");
cy.get("#referrerPolicy").clear().type("referrer");
cy.findByTestId("browserSecurityHeaders.xFrameOptions").clear();
cy.findByTestId("browserSecurityHeaders.xFrameOptions").type(
"SAMEORIGIN",
);
cy.findByTestId("browserSecurityHeaders.contentSecurityPolicy").clear();
cy.findByTestId("browserSecurityHeaders.contentSecurityPolicy").type(
"default-src 'self'",
);
cy.findByTestId("browserSecurityHeaders.strictTransportSecurity").clear();
cy.findByTestId("browserSecurityHeaders.strictTransportSecurity").type(
"max-age=31536000",
);
cy.findByTestId("browserSecurityHeaders.xContentTypeOptions").clear();
cy.findByTestId("browserSecurityHeaders.xContentTypeOptions").type(
"nosniff",
);
cy.findByTestId("browserSecurityHeaders.xRobotsTag").clear();
cy.findByTestId("browserSecurityHeaders.xRobotsTag").type("none");
cy.findByTestId("browserSecurityHeaders.xXSSProtection").clear();
cy.findByTestId("browserSecurityHeaders.xXSSProtection").type(
"1; mode=block",
);
cy.findByTestId("browserSecurityHeaders.strictTransportSecurity").clear();
cy.findByTestId("browserSecurityHeaders.strictTransportSecurity").type(
"max-age=31537000",
);
cy.findByTestId("browserSecurityHeaders.referrerPolicy").clear();
cy.findByTestId("browserSecurityHeaders.referrerPolicy").type("referrer");
realmSettingsPage.saveSecurityDefensesHeaders();
masthead.checkNotificationMessage("Realm successfully updated");
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@ import {
ActionGroup,
Button,
FormGroup,
NumberInput,
Select,
SelectOption,
SelectVariant,
} from "@patternfly/react-core";
import { useEffect, useState } from "react";
import { Controller, FormProvider, useForm } from "react-hook-form";
import { FormProvider, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";

import { HelpItem, NumberControl } from "ui-shared";
import { FormAccess } from "../../components/form/FormAccess";
import { HelpItem } from "ui-shared";
import { convertToFormValues } from "../../util";
import { Time } from "./Time";

Expand All @@ -31,7 +29,6 @@ export const BruteForceDetection = ({
const {
setValue,
handleSubmit,
control,
formState: { isDirty },
} = form;

Expand Down Expand Up @@ -131,74 +128,26 @@ export const BruteForceDetection = ({
</FormGroup>
{bruteForceMode !== BruteForceMode.Disabled && (
<>
<FormGroup
<NumberControl
name="failureFactor"
label={t("failureFactor")}
labelIcon={
<HelpItem
helpText={t("failureFactorHelp")}
fieldLabelId="failureFactor"
/>
}
fieldId="failureFactor"
>
<Controller
name="failureFactor"
defaultValue={0}
control={control}
rules={{ required: true }}
render={({ field }) => (
<NumberInput
type="text"
id="failureFactor"
value={field.value}
onPlus={() => field.onChange(field.value + 1)}
onMinus={() => field.onChange(field.value - 1)}
onChange={(event) =>
field.onChange(
Number((event.target as HTMLInputElement).value),
)
}
/>
)}
/>
</FormGroup>

labelIcon={t("failureFactorHelp")}
controller={{
defaultValue: 0,
rules: { required: t("required") },
}}
/>
{bruteForceMode ===
BruteForceMode.PermanentAfterTemporaryLockout && (
<FormGroup
<NumberControl
name="maxTemporaryLockouts"
label={t("maxTemporaryLockouts")}
labelIcon={
<HelpItem
helpText={t("maxTemporaryLockoutsHelp")}
fieldLabelId="maxTemporaryLockouts"
/>
}
fieldId="maxTemporaryLockouts"
hasNoPaddingTop
>
<Controller
name="maxTemporaryLockouts"
defaultValue={0}
control={control}
render={({ field }) => (
<NumberInput
type="text"
id="maxTemporaryLockouts"
value={field.value}
onPlus={() => field.onChange(field.value + 1)}
onMinus={() => field.onChange(field.value - 1)}
onChange={(event) =>
field.onChange(
Number((event.target as HTMLInputElement).value),
)
}
aria-label={t("maxTemporaryLockouts")}
/>
)}
/>
</FormGroup>
labelIcon={t("maxTemporaryLockoutsHelp")}
controller={{
defaultValue: 0,
}}
/>
)}

{(bruteForceMode === BruteForceMode.TemporaryLockout ||
bruteForceMode ===
BruteForceMode.PermanentAfterTemporaryLockout) && (
Expand All @@ -208,38 +157,14 @@ export const BruteForceDetection = ({
<Time name="maxDeltaTimeSeconds" />
</>
)}

<FormGroup
<NumberControl
name="quickLoginCheckMilliSeconds"
label={t("quickLoginCheckMilliSeconds")}
labelIcon={
<HelpItem
helpText={t("quickLoginCheckMilliSecondsHelp")}
fieldLabelId="quickLoginCheckMilliSeconds"
/>
}
fieldId="quickLoginCheckMilliSeconds"
>
<Controller
name="quickLoginCheckMilliSeconds"
defaultValue={0}
control={control}
render={({ field }) => (
<NumberInput
type="text"
id="quickLoginCheckMilliSeconds"
value={field.value}
onPlus={() => field.onChange(field.value + 1)}
onMinus={() => field.onChange(field.value - 1)}
onChange={(event) =>
field.onChange(
Number((event.target as HTMLInputElement).value),
)
}
/>
)}
/>
</FormGroup>

labelIcon={t("quickLoginCheckMilliSecondsHelp")}
controller={{
defaultValue: 0,
}}
/>
<Time name="minimumQuickLoginWaitSeconds" />
</>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { FormGroup } from "@patternfly/react-core";
import { useFormContext } from "react-hook-form";
import { Trans, useTranslation } from "react-i18next";

import { TextControl } from "ui-shared";
import { FormattedLink } from "../../components/external-link/FormattedLink";
import { HelpItem } from "ui-shared";
import { KeycloakTextInput } from "../../components/keycloak-text-input/KeycloakTextInput";

type HelpLinkTextInputProps = {
fieldName: string;
Expand All @@ -16,25 +12,17 @@ export const HelpLinkTextInput = ({
url,
}: HelpLinkTextInputProps) => {
const { t } = useTranslation();
const { register } = useFormContext();
const name = fieldName.substr(fieldName.indexOf(".") + 1);
return (
<FormGroup
<TextControl
name={fieldName}
label={t(name)}
fieldId={name}
labelIcon={
<HelpItem
helpText={
<Trans i18nKey={`${name}Help`}>
Default value prevents pages from being included
<FormattedLink href={url} title={t("learnMore")} />
</Trans>
}
fieldLabelId={name}
/>
<Trans i18nKey={`${name}Help`}>
Default value prevents pages from being included
<FormattedLink href={url} title={t("learnMore")} />
</Trans>
}
>
<KeycloakTextInput id={name} {...register(fieldName)} />
</FormGroup>
/>
);
};
42 changes: 9 additions & 33 deletions js/apps/admin-ui/src/realm-settings/security-defences/Time.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { FormGroup, ValidatedOptions } from "@patternfly/react-core";
import { CSSProperties } from "react";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";

import { HelpItem } from "ui-shared";
import { TimeSelector } from "../../components/time-selector/TimeSelector";
import { TimeSelectorControl } from "../../components/time-selector/TimeSelectorControl";

export const Time = ({
name,
Expand All @@ -14,37 +11,16 @@ export const Time = ({
style?: CSSProperties;
}) => {
const { t } = useTranslation();
const {
control,
formState: { errors },
} = useFormContext();
return (
<FormGroup
<TimeSelectorControl
name={name}
style={style}
label={t(name)}
fieldId={name}
labelIcon={<HelpItem helpText={t(`${name}Help`)} fieldLabelId={name} />}
validated={
errors[name] ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("required")}
>
<Controller
name={name}
defaultValue=""
control={control}
rules={{ required: true }}
render={({ field }) => (
<TimeSelector
data-testid={name}
value={field.value}
onChange={field.onChange}
validated={
errors[name] ? ValidatedOptions.error : ValidatedOptions.default
}
/>
)}
/>
</FormGroup>
labelIcon={t(`${name}Help`)}
controller={{
defaultValue: "",
rules: { required: t("required") },
}}
/>
);
};
4 changes: 2 additions & 2 deletions js/libs/ui-shared/src/controls/FormLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import {
FormGroupProps,
ValidatedOptions,
} from "@patternfly/react-core";
import { PropsWithChildren } from "react";
import { PropsWithChildren, ReactNode } from "react";
import { FieldError, FieldValues, Merge } from "react-hook-form";
import { HelpItem } from "./HelpItem";

export type FieldProps<T extends FieldValues = FieldValues> = {
label?: string;
name: string;
labelIcon?: string;
labelIcon?: string | ReactNode;
error?: FieldError | Merge<FieldError, T>;
isRequired: boolean;
};
Expand Down
3 changes: 2 additions & 1 deletion js/libs/ui-shared/src/controls/TextControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import {

import { KeycloakTextInput } from "../keycloak-text-input/KeycloakTextInput";
import { FormLabel } from "./FormLabel";
import { ReactNode } from "react";

export type TextControlProps<
T extends FieldValues,
P extends FieldPath<T> = FieldPath<T>,
> = UseControllerProps<T, P> &
Omit<TextInputProps, "name" | "isRequired" | "required"> & {
label: string;
labelIcon?: string;
labelIcon?: string | ReactNode;
isDisabled?: boolean;
helperText?: string;
};
Expand Down