Repository navigation
feat(header): link the hub account console in the avatar menu #429
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
Changes from all commits
7c5ef1b
2ff3347
47f5eeb
f8aa3d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ import Button from "primevue/button"; | |
| import { useRuntimeConfig } from "nuxt/app"; | ||
| import { useToast } from "primevue/usetoast"; | ||
| import CleanupDialog from "~/components/header/CleanupDialog.vue"; | ||
| import { buildHubAccountUrl } from "~/utils/hub-console-url"; | ||
|
|
||
| const { signIn, signOut } = useAuth(); | ||
| const { status: authStatus, data: authData } = useAuthState(); | ||
|
|
@@ -13,9 +14,15 @@ const menu = ref(); | |
| const showCleanupDialog = ref(false); | ||
|
|
||
| const config = useRuntimeConfig(); | ||
| const baseUrl = new URL(config.public.baseUrl as string).origin; | ||
| const idpProvider = config.public.idpProvider; | ||
| const victoriaLogsUrl = config.public.victoriaLogsUrl as string; | ||
| const hubAccountUrl = | ||
| idpProvider === "hub" | ||
| ? buildHubAccountUrl( | ||
| config.public.idpIssuer as string | undefined, | ||
| window.location.origin, | ||
| ) | ||
| : undefined; | ||
|
|
||
| const isAuthenticated = computed(() => authStatus.value === "authenticated"); | ||
|
|
||
|
|
@@ -42,10 +49,11 @@ const menuItems = computed(() => [ | |
| }, | ||
| }, | ||
| { | ||
| label: "Node Keycloak Admin", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This link needs to be kept since admins may still need to reach the keycloak used by the node services for internal auth. But this can be renamed to "Service Admin" to avoid confusion with a tooltip
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would rather drop it. The keycloak is internal to the node services, and an operator should not create or edit its clients by hand. A missing analysis client is a failure the pod orchestrator should report to the node-ui, not something to fix in the keycloak admin console. |
||
| icon: "pi pi-external-link", | ||
| url: `${baseUrl}/keycloak/admin`, | ||
| label: "Account", | ||
| icon: "pi pi-user-edit", | ||
| url: hubAccountUrl, | ||
| target: "_blank", | ||
| visible: !!hubAccountUrl, | ||
| }, | ||
| { | ||
| label: "VictoriaLogs", | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this module necessary? The
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /** | ||
| * Derive the Hub's Authup account console link from the OIDC issuer the node UI signs in | ||
| * with, e.g. "https://auth.privateaim.net/realms/my-realm". | ||
| * | ||
| * The issuer carries both the Authup base URL and the realm. Authup only lets a user log | ||
| * in through a client of the user's own realm, so the issuer realm is also the realm of | ||
| * the node's Hub client. The account console accepts the realm (id or name) as a | ||
| * `realmId` hint and skips its realm chooser; the node UI origin rides along as `ref`, | ||
| * which the account console renders as a back link when the origin is trusted. | ||
| * | ||
| * Returns undefined when the issuer is not an http(s) URL with the Authup realm shape. | ||
| */ | ||
| export function buildHubAccountUrl( | ||
| issuer: string | undefined, | ||
| origin?: string, | ||
| ): string | undefined { | ||
| if (!issuer) return undefined; | ||
|
|
||
| let url: URL; | ||
| try { | ||
| url = new URL(issuer); | ||
| } catch { | ||
| return undefined; | ||
| } | ||
|
|
||
| if (url.protocol !== "http:" && url.protocol !== "https:") return undefined; | ||
|
|
||
| const match = url.pathname.match(/^(.*)\/realms\/([^/]+)\/?$/); | ||
| if (!match) return undefined; | ||
|
tada5hi marked this conversation as resolved.
|
||
|
|
||
| let realm: string; | ||
| try { | ||
| realm = decodeURIComponent(match[2] as string); | ||
| } catch { | ||
| return undefined; | ||
| } | ||
|
|
||
| const query = new URLSearchParams({ realmId: realm }); | ||
| if (origin) query.set("ref", origin); | ||
|
|
||
| return `${url.origin}${match[1]}/console/account?${query.toString()}`; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import { describe, expect, test } from "vitest"; | ||
| import { buildHubAccountUrl } from "~/utils/hub-console-url"; | ||
|
|
||
| describe("buildHubAccountUrl", () => { | ||
| test("derives the account console link and realm from the issuer", () => { | ||
| expect( | ||
| buildHubAccountUrl( | ||
| "https://auth.privateaim.net/realms/my-realm", | ||
| "https://node.example.org", | ||
| ), | ||
| ).toBe( | ||
| "https://auth.privateaim.net/console/account?realmId=my-realm&ref=https%3A%2F%2Fnode.example.org", | ||
| ); | ||
| }); | ||
|
|
||
| test("keeps a sub-path deployment and a trailing slash", () => { | ||
| expect(buildHubAccountUrl("https://example.org/auth/realms/master/")).toBe( | ||
| "https://example.org/auth/console/account?realmId=master", | ||
| ); | ||
| }); | ||
|
|
||
| test.each([ | ||
| undefined, | ||
| "", | ||
| "not a url", | ||
| "https://auth.privateaim.net", | ||
| "javascript:/realms/master", | ||
| "https://auth.privateaim.net/realms/%ZZ", | ||
| ])( | ||
| "returns undefined for %s", | ||
| (issuer) => { | ||
| expect(buildHubAccountUrl(issuer)).toBeUndefined(); | ||
| }, | ||
| ); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be simplified to:
the origin can also be added here too as well, but as mentioned in another comment, it does not seem to affect access to the console page
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The regex form drops
realmId, which non-master realms need (see the other thread). The helper also returnsundefinedfor a malformed issuer instead of throwing or building a broken link; the two earlier review threads asked for exactly that.