Parse environment information as JSON (#851)
* Parse environment information as JSON * Pass 'isRunningAsTheme' from injected environment * Update comment to use index.ftl instead of html Co-authored-by: Stan Silvert <ssilvert@redhat.com> * Update realm param comment * Update version param * add more sensible defaults Co-authored-by: Stan Silvert <ssilvert@redhat.com>
This commit is contained in:
parent
d67b37e48e
commit
8236528a07
9 changed files with 100 additions and 62 deletions
|
@ -68,18 +68,23 @@
|
||||||
<value>href="${resourceUrl}/</value>
|
<value>href="${resourceUrl}/</value>
|
||||||
</replacement>
|
</replacement>
|
||||||
<replacement>
|
<replacement>
|
||||||
<token><head></token>
|
<token><![CDATA[</body>]]></token>
|
||||||
<value>
|
<value xml:space="preserve">
|
||||||
<head>
|
<![CDATA[
|
||||||
<script type="text/javascript">
|
<script id="environment" type="application/json">
|
||||||
var loginRealm = "${loginRealm}";
|
{
|
||||||
var authServerUrl = "${authServerUrl}";
|
"loginRealm": "${loginRealm}",
|
||||||
var authUrl = "${authUrl}";
|
"authServerUrl": "${authServerUrl}",
|
||||||
var consoleBaseUrl = "${consoleBaseUrl}";
|
"authUrl": "${authUrl}",
|
||||||
var resourceUrl = "${resourceUrl}";
|
"consoleBaseUrl": "${consoleBaseUrl}",
|
||||||
var masterRealm = "${masterRealm}";
|
"resourceUrl": "${resourceUrl}",
|
||||||
var resourceVersion = "${resourceVersion}";
|
"masterRealm": "${masterRealm}",
|
||||||
</script>
|
"resourceVersion": "${resourceVersion}",
|
||||||
|
"isRunningAsTheme": true
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
]]>
|
||||||
</value>
|
</value>
|
||||||
</replacement>
|
</replacement>
|
||||||
</replacements>
|
</replacements>
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { WhoAmIContext } from "./context/whoami/WhoAmI";
|
||||||
import { HelpContext, HelpHeader } from "./components/help-enabler/HelpHeader";
|
import { HelpContext, HelpHeader } from "./components/help-enabler/HelpHeader";
|
||||||
import { Link, useHistory } from "react-router-dom";
|
import { Link, useHistory } from "react-router-dom";
|
||||||
import { useAdminClient } from "./context/auth/AdminClient";
|
import { useAdminClient } from "./context/auth/AdminClient";
|
||||||
import { resourceUri } from "./util";
|
import environment from "./environment";
|
||||||
|
|
||||||
export const Header = () => {
|
export const Header = () => {
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
|
@ -127,7 +127,10 @@ export const Header = () => {
|
||||||
<UserDropdown />
|
<UserDropdown />
|
||||||
</PageHeaderToolsItem>
|
</PageHeaderToolsItem>
|
||||||
</PageHeaderToolsGroup>
|
</PageHeaderToolsGroup>
|
||||||
<Avatar src={resourceUri + "/img_avatar.svg"} alt="Avatar image" />
|
<Avatar
|
||||||
|
src={environment.resourceUrl + "/img_avatar.svg"}
|
||||||
|
alt="Avatar image"
|
||||||
|
/>
|
||||||
</PageHeaderTools>
|
</PageHeaderTools>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -181,7 +184,7 @@ export const Header = () => {
|
||||||
logo={
|
logo={
|
||||||
<Link to="/">
|
<Link to="/">
|
||||||
<Brand
|
<Brand
|
||||||
src={resourceUri + "/logo.svg"}
|
src={environment.resourceUrl + "/logo.svg"}
|
||||||
id="masthead-logo"
|
id="masthead-logo"
|
||||||
alt="Logo"
|
alt="Logo"
|
||||||
className="keycloak__pageheader_brand"
|
className="keycloak__pageheader_brand"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import KcAdminClient from "keycloak-admin";
|
import KcAdminClient from "keycloak-admin";
|
||||||
import { homeRealm, isDevMode, authUri } from "../../util";
|
import environment from "../../environment";
|
||||||
|
|
||||||
export default async function (): Promise<KcAdminClient> {
|
export default async function (): Promise<KcAdminClient> {
|
||||||
const kcAdminClient = new KcAdminClient();
|
const kcAdminClient = new KcAdminClient();
|
||||||
|
@ -7,16 +7,16 @@ export default async function (): Promise<KcAdminClient> {
|
||||||
await kcAdminClient.init(
|
await kcAdminClient.init(
|
||||||
{ onLoad: "check-sso", pkceMethod: "S256" },
|
{ onLoad: "check-sso", pkceMethod: "S256" },
|
||||||
{
|
{
|
||||||
url: authUri(),
|
url: environment.authUrl,
|
||||||
realm: homeRealm(),
|
realm: environment.loginRealm,
|
||||||
clientId: isDevMode
|
clientId: environment.isRunningAsTheme
|
||||||
? "security-admin-console-v2"
|
? "security-admin-console"
|
||||||
: "security-admin-console",
|
: "security-admin-console-v2",
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
kcAdminClient.setConfig({ realmName: homeRealm() });
|
kcAdminClient.setConfig({ realmName: environment.loginRealm });
|
||||||
|
|
||||||
kcAdminClient.baseUrl = authUri();
|
kcAdminClient.baseUrl = environment.authUrl;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert("failed to initialize keycloak");
|
alert("failed to initialize keycloak");
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import _ from "lodash";
|
||||||
import type RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation";
|
import type RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation";
|
||||||
import { RecentUsed } from "../../components/realm-selector/recent-used";
|
import { RecentUsed } from "../../components/realm-selector/recent-used";
|
||||||
import { useAdminClient, useFetch } from "../auth/AdminClient";
|
import { useAdminClient, useFetch } from "../auth/AdminClient";
|
||||||
import { WhoAmIContext } from "../whoami/WhoAmI";
|
import environment from "../../environment";
|
||||||
|
|
||||||
type RealmContextType = {
|
type RealmContextType = {
|
||||||
realm: string;
|
realm: string;
|
||||||
|
@ -25,8 +25,7 @@ type RealmContextProviderProps = { children: React.ReactNode };
|
||||||
export const RealmContextProvider = ({
|
export const RealmContextProvider = ({
|
||||||
children,
|
children,
|
||||||
}: RealmContextProviderProps) => {
|
}: RealmContextProviderProps) => {
|
||||||
const { whoAmI } = useContext(WhoAmIContext);
|
const [realm, setRealm] = useState(environment.loginRealm);
|
||||||
const [realm, setRealm] = useState(whoAmI.getHomeRealm());
|
|
||||||
const [realms, setRealms] = useState<RealmRepresentation[]>([]);
|
const [realms, setRealms] = useState<RealmRepresentation[]>([]);
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
const recentUsed = new RecentUsed();
|
const recentUsed = new RecentUsed();
|
||||||
|
|
|
@ -4,7 +4,6 @@ import i18n from "../../i18n";
|
||||||
import type WhoAmIRepresentation from "keycloak-admin/lib/defs/whoAmIRepresentation";
|
import type WhoAmIRepresentation from "keycloak-admin/lib/defs/whoAmIRepresentation";
|
||||||
import type { AccessType } from "keycloak-admin/lib/defs/whoAmIRepresentation";
|
import type { AccessType } from "keycloak-admin/lib/defs/whoAmIRepresentation";
|
||||||
import { useAdminClient, useFetch } from "../auth/AdminClient";
|
import { useAdminClient, useFetch } from "../auth/AdminClient";
|
||||||
import { homeRealm } from "../../util";
|
|
||||||
|
|
||||||
export class WhoAmI {
|
export class WhoAmI {
|
||||||
constructor(private me?: WhoAmIRepresentation) {
|
constructor(private me?: WhoAmIRepresentation) {
|
||||||
|
@ -27,13 +26,6 @@ export class WhoAmI {
|
||||||
return this.me.userId;
|
return this.me.userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the realm I am signed in to.
|
|
||||||
*/
|
|
||||||
public getHomeRealm(): string {
|
|
||||||
return homeRealm();
|
|
||||||
}
|
|
||||||
|
|
||||||
public canCreateRealm(): boolean {
|
public canCreateRealm(): boolean {
|
||||||
return this.me !== undefined && this.me.createRealm;
|
return this.me !== undefined && this.me.createRealm;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,6 @@ test("returns display name", () => {
|
||||||
expect(whoami.getDisplayName()).toEqual("Stan Silvert");
|
expect(whoami.getDisplayName()).toEqual("Stan Silvert");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("returns correct home realm in dev mode", () => {
|
|
||||||
const whoami = new WhoAmI(whoamiMock as WhoAmIRepresentation);
|
|
||||||
expect(whoami.getHomeRealm()).toEqual("master");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("can not create realm", () => {
|
test("can not create realm", () => {
|
||||||
const whoami = new WhoAmI(whoamiMock as WhoAmIRepresentation);
|
const whoami = new WhoAmI(whoamiMock as WhoAmIRepresentation);
|
||||||
expect(whoami.canCreateRealm()).toEqual(false);
|
expect(whoami.canCreateRealm()).toEqual(false);
|
||||||
|
|
|
@ -28,8 +28,9 @@ import { useRealm } from "../context/realm-context/RealmContext";
|
||||||
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
||||||
|
|
||||||
import "./dashboard.css";
|
import "./dashboard.css";
|
||||||
import { toUpperCase, resourceUri } from "../util";
|
import { toUpperCase } from "../util";
|
||||||
import { HelpItem } from "../components/help-enabler/HelpItem";
|
import { HelpItem } from "../components/help-enabler/HelpItem";
|
||||||
|
import environment from "../environment";
|
||||||
|
|
||||||
const EmptyDashboard = () => {
|
const EmptyDashboard = () => {
|
||||||
const { t } = useTranslation("dashboard");
|
const { t } = useTranslation("dashboard");
|
||||||
|
@ -38,7 +39,7 @@ const EmptyDashboard = () => {
|
||||||
<PageSection variant="light">
|
<PageSection variant="light">
|
||||||
<EmptyState variant="large">
|
<EmptyState variant="large">
|
||||||
<Brand
|
<Brand
|
||||||
src={resourceUri + "/icon.svg"}
|
src={environment.resourceUrl + "/icon.svg"}
|
||||||
alt="Keycloak icon"
|
alt="Keycloak icon"
|
||||||
className="keycloak__dashboard_icon"
|
className="keycloak__dashboard_icon"
|
||||||
/>
|
/>
|
||||||
|
|
64
src/environment.ts
Normal file
64
src/environment.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
export type Environment = {
|
||||||
|
/** The realm which should be used when signing into the application. */
|
||||||
|
loginRealm: string;
|
||||||
|
/** The URL to the root of the auth server. */
|
||||||
|
authServerUrl: string;
|
||||||
|
/** The URL to the path of the auth server where client requests can be sent. */
|
||||||
|
authUrl: string;
|
||||||
|
/** The URL to the base of the admin console. */
|
||||||
|
consoleBaseUrl: string;
|
||||||
|
resourceUrl: string;
|
||||||
|
/** The name of the master realm. */
|
||||||
|
masterRealm: string;
|
||||||
|
/** The version hash of the auth sever. */
|
||||||
|
resourceVersion: string;
|
||||||
|
/** Indicates if the application is running as a Keycloak theme. */
|
||||||
|
isRunningAsTheme: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
// During development the realm can be passed as a query parameter when redirecting back from Keycloak.
|
||||||
|
const realm =
|
||||||
|
new URLSearchParams(window.location.search).get("realm") ?? "master";
|
||||||
|
|
||||||
|
// The default environment, used during development.
|
||||||
|
const defaultEnvironment: Environment = {
|
||||||
|
loginRealm: realm,
|
||||||
|
authServerUrl: "http://localhost:8180/auth",
|
||||||
|
authUrl: "http://localhost:8180/auth",
|
||||||
|
consoleBaseUrl: "/auth/admin/master/console/",
|
||||||
|
resourceUrl: ".",
|
||||||
|
masterRealm: "master",
|
||||||
|
resourceVersion: "unknown",
|
||||||
|
isRunningAsTheme: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Merge the default and injected environment variables together.
|
||||||
|
const environment: Environment = {
|
||||||
|
...defaultEnvironment,
|
||||||
|
...getInjectedEnvironment(),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the environment variables that are passed if the application is running as a Keycloak theme.
|
||||||
|
* These variables are injected by Keycloak into the `index.ftl` as a script tag, the contents of which can be parsed as JSON.
|
||||||
|
*/
|
||||||
|
function getInjectedEnvironment(): Record<string, string | number | boolean> {
|
||||||
|
const element = document.getElementById("environment");
|
||||||
|
|
||||||
|
// If the element cannot be found, return an empty record.
|
||||||
|
if (!element?.textContent) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to parse the contents as JSON and return its value.
|
||||||
|
try {
|
||||||
|
return JSON.parse(element.textContent);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Unable to parse environment variables.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, return an empty record.
|
||||||
|
return {};
|
||||||
|
}
|
21
src/util.ts
21
src/util.ts
|
@ -5,27 +5,6 @@ import type ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentat
|
||||||
import type { ProviderRepresentation } from "keycloak-admin/lib/defs/serverInfoRepesentation";
|
import type { ProviderRepresentation } from "keycloak-admin/lib/defs/serverInfoRepesentation";
|
||||||
import type KeycloakAdminClient from "keycloak-admin";
|
import type KeycloakAdminClient from "keycloak-admin";
|
||||||
|
|
||||||
// if we are running on Keycloak server, resourceUrl will be passed from index.ftl
|
|
||||||
declare const resourceUrl: string;
|
|
||||||
export const isDevMode = typeof resourceUrl === "undefined";
|
|
||||||
export const resourceUri = isDevMode ? "." : resourceUrl;
|
|
||||||
|
|
||||||
// if we are running on Keycloak server, loginRealm will be passed from index.ftl
|
|
||||||
declare const loginRealm: string;
|
|
||||||
export const homeRealm = () => {
|
|
||||||
if (typeof loginRealm !== "undefined") return loginRealm;
|
|
||||||
|
|
||||||
return new URLSearchParams(window.location.search).get("realm") || "master";
|
|
||||||
};
|
|
||||||
|
|
||||||
// if we are running on Keycloak server, authUrl will be passed from index.ftl
|
|
||||||
declare const authUrl: string;
|
|
||||||
export const authUri = () => {
|
|
||||||
if (typeof authUrl !== "undefined") return authUrl;
|
|
||||||
|
|
||||||
return "http://localhost:8180/auth";
|
|
||||||
};
|
|
||||||
|
|
||||||
export const sortProviders = (providers: {
|
export const sortProviders = (providers: {
|
||||||
[index: string]: ProviderRepresentation;
|
[index: string]: ProviderRepresentation;
|
||||||
}) => {
|
}) => {
|
||||||
|
|
Loading…
Reference in a new issue