8000 Format Terms and Conditions accepted timestamp by palasht75 · Pull Request #49031 · keycloak/keycloak · GitHub
[go: up one dir, main page]

Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion js/apps/admin-ui/src/user/UserForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ import { useNavigate } from "react-router-dom";
import { CopyToClipboardButton } from "../components/copy-to-clipboard-button/CopyToClipboardButton";
import { GroupResourceContext } from "../context/group-resource/GroupResourceContext";

const TERMS_AND_CONDITIONS_ATTRIBUTE = "terms_and_conditions";

export type BruteForced = {
isBruteForceProtected?: boolean;
isLocked?: boolean;
Expand Down Expand Up @@ -86,6 +88,10 @@ export const UserForm = ({
const canViewFederationLink = hasAccess("view-realm");
const { whoAmI } = useWhoAmI();

const termsAndConditionsAcceptedDate = toTermsAndConditionsAcceptedDate(
user?.attributes?.[TERMS_AND_CONDITIONS_ATTRIBUTE],
);

const { handleSubmit, setValue, control, reset, formState } = form;
const { errors } = formState;

Expand Down Expand Up @@ -270,6 +276,19 @@ export const UserForm = ({
label={t("emailVerified")}
labelIcon={t("emailVerifiedHelp")}
/>
{termsAndConditionsAcceptedDate && (
<FormGroup
label={t("termsAndConditionsUserAttribute")}
fieldId={TERMS_AND_CONDITIONS_ATTRIBUTE}
>
<span
id={TERMS_AND_CONDITIONS_ATTRIBUTE}
data-testid={TERMS_AND_CONDITIONS_ATTRIBUTE}
>
{formatDate(termsAndConditionsAcceptedDate)}
</span>
</FormGroup>
)}
{user?.attributes?.["kc.email.pending"] && (
<Alert
variant={AlertVariant.warning}
Expand Down Expand Up @@ -298,7 +317,11 @@ export const UserForm = ({
...userProfileMetadata,
attributes: userProfileMetadata.attributes?.filter(
(attribute: UserProfileAttributeMetadata) => {
return attribute.name !== "kc.email.pending";
return (
attribute.name !== "kc.email.pending" &&
(attribute.name !== TERMS_AND_CONDITIONS_ATTRIBUTE ||
!termsAndConditionsAcceptedDate)
);
},
),
}}
Expand Down Expand Up @@ -430,3 +453,15 @@ export const UserForm = ({
</FormAccess>
);
};

function toTermsAndConditionsAcceptedDate(value: unknown): Date | undefined {
const timestamp = Number(Array.isArray(value) ? value[0] : value);

if (!Number.isFinite(timestamp) || timestamp <= 0) {
return undefined;
}

const date = new Date(timestamp * 1000);

return Number.isNaN(date.getTime()) ? undefined : date;
}
73 changes: 73 additions & 0 deletions js/apps/admin-ui/test/users/main.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,79 @@ test.describe("Existing users", () => {
await assertNotificationMessage(page, "The user has been saved");
});

test("formats Terms and Conditions accepted timestamp", async ({ page }) => {
const termsAcceptedTimestamp = "1700000000";
const email = "existing-user@example.com";
const firstName = "Existing";
const lastName = "User";

await using testBed = await createTestBed({
attributes: {
userProfileEnabled: "true",
},
requiredActions: [
{
alias: "TERMS_AND_CONDITIONS",
name: "Terms and Conditions",
providerId: "TERMS_AND_CONDITIONS",
enabled: true,
defaultAction: false,
priority: 20,
config: {},
},
],
users: [
{
username: existingUserName,
email,
firstName,
lastName,
attributes: {
terms_and_conditions: [termsAcceptedTimestamp],
},
},
],
});
const user = await adminClient.findUserByUsername(
testBed.realm,
existingUserName,
);

await login(page, {
to: toUser({ realm: testBed.realm, id: user.id!, tab: "settings" }),
});

const termsAcceptedField = page.getByTestId("terms_and_conditions");
await expect(termsAcceptedField).toBeVisible();
await expect(termsAcceptedField).not.toHaveText(termsAcceptedTimestamp);
await expect(
page.getByText(termsAcceptedTimestamp, { exact: true }),
).toHaveCount(0);
await expect(
page.locator(`input[value="${termsAcceptedTimestamp}"]`),
).toHaveCount(0);

const displayedTimestamp = await termsAcceptedField.innerText();
expect(displayedTimestamp).toContain("2023");
expect(displayedTimestamp).toMatch(/\D/);
expect(displayedTimestamp).toMatch(/\d{1,2}:\d{2}/);

await expect(page.getByTestId("email")).toHaveValue(email);
await expect(page.getByTestId("firstName")).toHaveValue(firstName);
await expect(page.getByTestId("lastName")).toHaveValue(lastName);

await clickSaveButton(page);
await assertNotificationMessage(page, "The user has been saved");

const updatedUser = await adminClient.findUserByUsername(
testBed.realm,
existingUserName,
);
expect(updatedUser.attributes?.terms_and_conditions).toEqual([
termsAcceptedTimestamp,
]);
});

test("shows validation error for empty required attribute", async ({
page,
}) => {
Expand Down
Loading
0