feat: Addition of FederatedIdentity Account Page

This commit is contained in:
giorgoslytos
2024-05-17 12:14:47 +03:00
parent 19ba0873f5
commit 346e3df009
6 changed files with 142 additions and 2 deletions

View File

@ -3,6 +3,7 @@ import type { PageProps } from "keycloakify/account/pages/PageProps";
import type { I18n } from "keycloakify/account/i18n"; import type { I18n } from "keycloakify/account/i18n";
import type { KcContext } from "./kcContext"; import type { KcContext } from "./kcContext";
import { assert, type Equals } from "tsafe/assert"; import { assert, type Equals } from "tsafe/assert";
import FederatedIdentity from "./pages/FederatedIdentity";
const Password = lazy(() => import("keycloakify/account/pages/Password")); const Password = lazy(() => import("keycloakify/account/pages/Password"));
const Account = lazy(() => import("keycloakify/account/pages/Account")); const Account = lazy(() => import("keycloakify/account/pages/Account"));
@ -30,6 +31,8 @@ export default function Fallback(props: PageProps<KcContext, I18n>) {
return <Applications kcContext={kcContext} {...rest} />; return <Applications kcContext={kcContext} {...rest} />;
case "log.ftl": case "log.ftl":
return <Log kcContext={kcContext} {...rest} />; return <Log kcContext={kcContext} {...rest} />;
case "federatedIdentity.ftl":
return <FederatedIdentity kcContext={kcContext} {...rest} />;
} }
assert<Equals<typeof kcContext, never>>(false); assert<Equals<typeof kcContext, never>>(false);
})()} })()}

View File

@ -3,7 +3,14 @@ import { assert } from "tsafe/assert";
import type { Equals } from "tsafe"; import type { Equals } from "tsafe";
import { type ThemeType } from "keycloakify/bin/constants"; import { type ThemeType } from "keycloakify/bin/constants";
export type KcContext = KcContext.Password | KcContext.Account | KcContext.Sessions | KcContext.Totp | KcContext.Applications | KcContext.Log; export type KcContext =
| KcContext.Password
| KcContext.Account
| KcContext.Sessions
| KcContext.Totp
| KcContext.Applications
| KcContext.Log
| KcContext.FederatedIdentity;
export declare namespace KcContext { export declare namespace KcContext {
export type Common = { export type Common = {
@ -264,6 +271,20 @@ export declare namespace KcContext {
}[]; }[];
}; };
}; };
export type FederatedIdentity = Common & {
pageId: "federatedIdentity.ftl";
stateChecker: string;
federatedIdentity: {
identities: {
providerId: string;
displayName: string;
userName: string;
connected: boolean;
}[];
removeLinkPossible: boolean;
};
};
} }
{ {

View File

@ -240,5 +240,21 @@ export const kcContextMocks: KcContext[] = [
} }
] ]
} }
}),
id<KcContext.FederatedIdentity>({
...kcContextCommonMock,
"stateChecker": "",
"pageId": "federatedIdentity.ftl",
"federatedIdentity": {
"identities": [
{
"providerId": "keycloak-oidc",
"displayName": "keycloak-oidc",
"userName": "John",
"connected": true
}
],
"removeLinkPossible": true
}
}) })
]; ];

View File

@ -0,0 +1,58 @@
import { PageProps } from "keycloakify/account";
import { I18n } from "keycloakify/account/i18n";
import { KcContext } from "keycloakify/account/kcContext";
export default function FederatedIdentity(props: PageProps<Extract<KcContext, { pageId: "federatedIdentity.ftl" }>, I18n>) {
const { kcContext, i18n, doUseDefaultCss, classes, Template } = props;
const { url, federatedIdentity, stateChecker } = kcContext;
const { msg } = i18n;
return (
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="federatedIdentity">
<div className="main-layout social">
<div className="row">
<div className="col-md-10">
<h2>{msg("federatedIdentitiesHtmlTitle")}</h2>
</div>
</div>
<div id="federated-identities">
{federatedIdentity.identities.map(identity => (
<div key={identity.providerId} className="row margin-bottom">
<div className="col-sm-2 col-md-2">
<label htmlFor={identity.providerId} className="control-label">
{identity.displayName}
</label>
</div>
<div className="col-sm-5 col-md-5">
<input disabled className="form-control" value={identity.userName} />
</div>
<div className="col-sm-5 col-md-5">
{identity.connected ? (
federatedIdentity.removeLinkPossible && (
<form action={url.socialUrl} method="post" className="form-inline">
<input type="hidden" name="stateChecker" value={stateChecker} />
<input type="hidden" name="action" value="remove" />
<input type="hidden" name="providerId" value={identity.providerId} />
<button id={`remove-link-${identity.providerId}`} className="btn btn-default">
{msg("doRemove")}
</button>
</form>
)
) : (
<form action={url.socialUrl} method="post" className="form-inline">
<input type="hidden" name="stateChecker" value={stateChecker} />
<input type="hidden" name="action" value="add" />
<input type="hidden" name="providerId" value={identity.providerId} />
<button id={`add-link-${identity.providerId}`} className="btn btn-default">
{msg("doAdd")}
</button>
</form>
)}
</div>
</div>
))}
</div>
</div>
</Template>
);
}

View File

@ -27,7 +27,15 @@ export const loginThemePageIds = [
"saml-post-form.ftl" "saml-post-form.ftl"
] as const; ] as const;
export const accountThemePageIds = ["password.ftl", "account.ftl", "sessions.ftl", "totp.ftl", "applications.ftl", "log.ftl"] as const; export const accountThemePageIds = [
"password.ftl",
"account.ftl",
"sessions.ftl",
"totp.ftl",
"applications.ftl",
"log.ftl",
"federatedIdentity.ftl"
] as const;
export type LoginThemePageId = (typeof loginThemePageIds)[number]; export type LoginThemePageId = (typeof loginThemePageIds)[number];
export type AccountThemePageId = (typeof accountThemePageIds)[number]; export type AccountThemePageId = (typeof accountThemePageIds)[number];

View File

@ -0,0 +1,34 @@
import React from "react";
import { Meta } from "@storybook/react";
import { createPageStory } from "../createPageStory";
const pageId = "federatedIdentity.ftl";
const { PageStory } = createPageStory({ pageId });
const meta = {
title: "account/FederatedIdentity",
component: PageStory
} satisfies Meta<typeof PageStory>;
export default meta;
export const Default = () => <PageStory />;
export const NotConnected = () => (
<PageStory
kcContext={{
pageId: "federatedIdentity.ftl",
federatedIdentity: {
identities: [
{
providerId: "google",
displayName: "keycloak-oidc",
connected: false
}
],
removeLinkPossible: true
}
}}
/>
);