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:
Jon Koops 2021-07-15 11:50:01 +02:00 committed by GitHub
parent d67b37e48e
commit 8236528a07
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 100 additions and 62 deletions

View file

@ -68,18 +68,23 @@
<value>href="${resourceUrl}/</value>
</replacement>
<replacement>
<token>&lt;head&gt;</token>
<value>
&lt;head&gt;
&lt;script type="text/javascript"&gt;
var loginRealm = "${loginRealm}";
var authServerUrl = "${authServerUrl}";
var authUrl = "${authUrl}";
var consoleBaseUrl = "${consoleBaseUrl}";
var resourceUrl = "${resourceUrl}";
var masterRealm = "${masterRealm}";
var resourceVersion = "${resourceVersion}";
&lt;/script&gt;
<token><![CDATA[</body>]]></token>
<value xml:space="preserve">
<![CDATA[
<script id="environment" type="application/json">
{
"loginRealm": "${loginRealm}",
"authServerUrl": "${authServerUrl}",
"authUrl": "${authUrl}",
"consoleBaseUrl": "${consoleBaseUrl}",
"resourceUrl": "${resourceUrl}",
"masterRealm": "${masterRealm}",
"resourceVersion": "${resourceVersion}",
"isRunningAsTheme": true
}
</script>
</body>
]]>
</value>
</replacement>
</replacements>

View file

@ -18,7 +18,7 @@ import { WhoAmIContext } from "./context/whoami/WhoAmI";
import { HelpContext, HelpHeader } from "./components/help-enabler/HelpHeader";
import { Link, useHistory } from "react-router-dom";
import { useAdminClient } from "./context/auth/AdminClient";
import { resourceUri } from "./util";
import environment from "./environment";
export const Header = () => {
const adminClient = useAdminClient();
@ -127,7 +127,10 @@ export const Header = () => {
<UserDropdown />
</PageHeaderToolsItem>
</PageHeaderToolsGroup>
<Avatar src={resourceUri + "/img_avatar.svg"} alt="Avatar image" />
<Avatar
src={environment.resourceUrl + "/img_avatar.svg"}
alt="Avatar image"
/>
</PageHeaderTools>
);
};
@ -181,7 +184,7 @@ export const Header = () => {
logo={
<Link to="/">
<Brand
src={resourceUri + "/logo.svg"}
src={environment.resourceUrl + "/logo.svg"}
id="masthead-logo"
alt="Logo"
className="keycloak__pageheader_brand"

View file

@ -1,5 +1,5 @@
import KcAdminClient from "keycloak-admin";
import { homeRealm, isDevMode, authUri } from "../../util";
import environment from "../../environment";
export default async function (): Promise<KcAdminClient> {
const kcAdminClient = new KcAdminClient();
@ -7,16 +7,16 @@ export default async function (): Promise<KcAdminClient> {
await kcAdminClient.init(
{ onLoad: "check-sso", pkceMethod: "S256" },
{
url: authUri(),
realm: homeRealm(),
clientId: isDevMode
? "security-admin-console-v2"
: "security-admin-console",
url: environment.authUrl,
realm: environment.loginRealm,
clientId: environment.isRunningAsTheme
? "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) {
alert("failed to initialize keycloak");
}

View file

@ -4,7 +4,7 @@ import _ from "lodash";
import type RealmRepresentation from "keycloak-admin/lib/defs/realmRepresentation";
import { RecentUsed } from "../../components/realm-selector/recent-used";
import { useAdminClient, useFetch } from "../auth/AdminClient";
import { WhoAmIContext } from "../whoami/WhoAmI";
import environment from "../../environment";
type RealmContextType = {
realm: string;
@ -25,8 +25,7 @@ type RealmContextProviderProps = { children: React.ReactNode };
export const RealmContextProvider = ({
children,
}: RealmContextProviderProps) => {
const { whoAmI } = useContext(WhoAmIContext);
const [realm, setRealm] = useState(whoAmI.getHomeRealm());
const [realm, setRealm] = useState(environment.loginRealm);
const [realms, setRealms] = useState<RealmRepresentation[]>([]);
const adminClient = useAdminClient();
const recentUsed = new RecentUsed();

View file

@ -4,7 +4,6 @@ import i18n from "../../i18n";
import type WhoAmIRepresentation from "keycloak-admin/lib/defs/whoAmIRepresentation";
import type { AccessType } from "keycloak-admin/lib/defs/whoAmIRepresentation";
import { useAdminClient, useFetch } from "../auth/AdminClient";
import { homeRealm } from "../../util";
export class WhoAmI {
constructor(private me?: WhoAmIRepresentation) {
@ -27,13 +26,6 @@ export class WhoAmI {
return this.me.userId;
}
/**
* Return the realm I am signed in to.
*/
public getHomeRealm(): string {
return homeRealm();
}
public canCreateRealm(): boolean {
return this.me !== undefined && this.me.createRealm;
}

View file

@ -7,11 +7,6 @@ test("returns display name", () => {
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", () => {
const whoami = new WhoAmI(whoamiMock as WhoAmIRepresentation);
expect(whoami.canCreateRealm()).toEqual(false);

View file

@ -28,8 +28,9 @@ import { useRealm } from "../context/realm-context/RealmContext";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import "./dashboard.css";
import { toUpperCase, resourceUri } from "../util";
import { toUpperCase } from "../util";
import { HelpItem } from "../components/help-enabler/HelpItem";
import environment from "../environment";
const EmptyDashboard = () => {
const { t } = useTranslation("dashboard");
@ -38,7 +39,7 @@ const EmptyDashboard = () => {
<PageSection variant="light">
<EmptyState variant="large">
<Brand
src={resourceUri + "/icon.svg"}
src={environment.resourceUrl + "/icon.svg"}
alt="Keycloak icon"
className="keycloak__dashboard_icon"
/>

64
src/environment.ts Normal file
View 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 {};
}

View file

@ -5,27 +5,6 @@ import type ClientRepresentation from "keycloak-admin/lib/defs/clientRepresentat
import type { ProviderRepresentation } from "keycloak-admin/lib/defs/serverInfoRepesentation";
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: {
[index: string]: ProviderRepresentation;
}) => {