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
1 change: 1 addition & 0 deletions external-ui/src/auth/session-bootstrap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ function toAuthenticatedSession(response: AuthSessionResponse): AuthenticatedSes
n8nUser: response.n8nUser,
permissions: response.permissions,
tenantRoles: response.tenantRoles ?? [],
tenantGroups: response.tenantGroups ?? [],
};
}

Expand Down
19 changes: 15 additions & 4 deletions external-ui/src/components/action-handlers/show-form-handler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import type { WilActionItem } from '../../services/backend/wil';
import { postWilChefsToken, postWilCallback } from '../../services/backend/wil';
import { getStoredAppToken } from '../../services/backend/axios';
import { useSessionSnapshot } from '../../state/session';
import { useSessionSnapshot, useTenantGroupsById, useTenantRolesById } from '../../state/session';
import { ChefsFormPanel } from '../chefs/chefs-form-panel';
import type { ChefsFormPanelInitData } from '../chefs/chefs-form-panel';
import { buildTokenObject, buildUserObject, buildUserProfile } from '../chefs/user-claims-utils';
Expand All @@ -23,6 +23,8 @@ async function initializeForm(params: {
actionId: string;
payload: Record<string, unknown>;
claims: Record<string, unknown>;
tenantRoles: readonly string[];
tenantGroups: readonly string[];
}): Promise<ChefsFormPanelInitData> {
const tokenResponse = await postWilChefsToken({ tenantId: params.tenantId, actionId: params.actionId });
const formPreFillData = (params.payload.formPreFillData as Record<string, unknown>) ?? {};
Expand All @@ -36,14 +38,16 @@ async function initializeForm(params: {
submissionId,
prefillData: { ...formPreFillData, ...buildUserProfile(params.claims) },
token: buildTokenObject(params.claims),
user: buildUserObject(params.claims),
user: buildUserObject(params.claims, { roles: params.tenantRoles, groups: params.tenantGroups }),
headers: userToken ? { Authorization: `Bearer ${userToken}` } : {},
};
}

/** Renders a CHEFS form for a `showform` action and posts the submission to the WIL callback API. */
export function ShowFormHandler({ action, tenantId, onInteractionSuccess, onRefresh }: Readonly<ShowFormHandlerProps>) {
const { session } = useSessionSnapshot();
const tenantRoles = useTenantRolesById(tenantId);
const tenantGroups = useTenantGroupsById(tenantId);
const queryClient = useQueryClient();
const onInteractionSuccessRef = useRef(onInteractionSuccess);
const { claimError, setClaimError } = useClaimVerification({
Expand All @@ -66,11 +70,18 @@ export function ShowFormHandler({ action, tenantId, onInteractionSuccess, onRefr
// Re-initialize whenever the action or session claims change
useEffect(() => {
if (!session?.oidc.claims) return;
initMutation.mutate({ tenantId, actionId: action.id, payload: action.payload, claims: session.oidc.claims });
initMutation.mutate({
tenantId,
actionId: action.id,
payload: action.payload,
claims: session.oidc.claims,
tenantRoles,
tenantGroups,
});
callbackMutation.reset();
setClaimError(null);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [action.id, action.payload, session?.oidc.claims, tenantId]);
}, [action.id, action.payload, session?.oidc.claims, tenantId, tenantRoles, tenantGroups]);

function handleRefresh() {
queryClient.invalidateQueries({ queryKey: ['wil-actions'] });
Expand Down
7 changes: 6 additions & 1 deletion external-ui/src/components/chefs/user-claims-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@ export function buildTokenObject(claims: Record<string, unknown>): Record<string
};
}

export function buildUserObject(claims: Record<string, unknown>): Record<string, unknown> {
export function buildUserObject(
claims: Record<string, unknown>,
tenant?: { roles: readonly string[]; groups: readonly string[] },
): Record<string, unknown> {
return {
name: claims.display_name,
firstName: claims.given_name,
lastName: claims.family_name,
email: claims.email,
username: claims.idir_username ?? claims.preferred_username,
idp: claims.identity_provider,
tenantRoles: tenant?.roles ?? [],
tenantGroups: tenant?.groups ?? [],
};
}

Expand Down
18 changes: 14 additions & 4 deletions external-ui/src/components/wil/trigger/trigger-chefs-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useMutation } from '@tanstack/react-query';
import type { Trigger } from '../../../services/backend/trigger-types';
import { getTriggerChefsToken, callbackTrigger } from '../../../services/backend/triggers';
import { getStoredAppToken } from '../../../services/backend/axios';
import { useSessionSnapshot } from '../../../state/session';
import { useSessionSnapshot, useTenantGroupsById, useTenantRolesById } from '../../../state/session';
import { ChefsFormPanel } from '../../chefs/chefs-form-panel';
import type { ChefsFormPanelInitData } from '../../chefs/chefs-form-panel';
import { buildTokenObject, buildUserObject } from '../../chefs/user-claims-utils';
Expand All @@ -13,6 +13,8 @@ async function initializeForm(params: {
tenantId: string;
triggerId: string;
claims: Record<string, unknown>;
tenantRoles: readonly string[];
tenantGroups: readonly string[];
}): Promise<ChefsFormPanelInitData> {
const tokenResponse = await getTriggerChefsToken({ tenantId: params.tenantId, triggerId: params.triggerId });
const userToken = getStoredAppToken();
Expand All @@ -21,7 +23,7 @@ async function initializeForm(params: {
formId: tokenResponse.formId,
baseUrl: tokenResponse.baseUrl,
token: buildTokenObject(params.claims),
user: buildUserObject(params.claims),
user: buildUserObject(params.claims, { roles: params.tenantRoles, groups: params.tenantGroups }),
headers: userToken ? { Authorization: `Bearer ${userToken}` } : {},
};
}
Expand All @@ -33,6 +35,8 @@ interface TriggerChefsPreviewProps {

export function TriggerChefsPreview({ trigger, tenantId }: Readonly<TriggerChefsPreviewProps>) {
const { session } = useSessionSnapshot();
const tenantRoles = useTenantRolesById(tenantId);
const tenantGroups = useTenantGroupsById(tenantId);

const initMutation = useMutation({ mutationFn: initializeForm });

Expand All @@ -54,13 +58,19 @@ export function TriggerChefsPreview({ trigger, tenantId }: Readonly<TriggerChefs

useEffect(() => {
if (!session?.oidc.claims) return;
initMutation.mutate({ tenantId, triggerId: trigger.id, claims: session.oidc.claims });
initMutation.mutate({
tenantId,
triggerId: trigger.id,
claims: session.oidc.claims,
tenantRoles,
tenantGroups,
});
// Reset submission state when trigger or session changes
setSubmitStatus('idle');
setSubmitError(null);
callbackMutation.reset();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [trigger.id, tenantId, session?.oidc.claims]);
}, [trigger.id, tenantId, session?.oidc.claims, tenantRoles, tenantGroups]);

// callbackMutation.mutate is stable across renders (TanStack Query wraps it in useCallback
// with a ref internally), so it is safe as a useCallback dep without causing churn.
Expand Down
8 changes: 8 additions & 0 deletions external-ui/src/services/backend/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type AuthSessionResponse = {
n8nUser: AuthSessionN8nUser | null;
permissions: Permissions | null;
tenantRoles: TenantRole[] | null;
tenantGroups: TenantGroup[] | null;
};

export type AuthenticatedSession = {
Expand All @@ -43,6 +44,7 @@ export type AuthenticatedSession = {
n8nUser: AuthSessionN8nUser;
permissions: Permissions;
tenantRoles: TenantRole[];
tenantGroups: TenantGroup[];
};

export type Permissions = {
Expand All @@ -63,6 +65,12 @@ export type TenantRole = {
roles: readonly string[];
};

export type TenantGroup = {
tenantId: string;
tenantName: string;
groups: readonly string[];
};

export type WhoamiResponse = {
userAgent: string | null;
oidc: {
Expand Down
18 changes: 17 additions & 1 deletion external-ui/src/state/session.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { proxy, useSnapshot } from 'valtio';
import type { AuthenticatedSession, TenantRole } from '../services/backend/auth';
import type { AuthenticatedSession, TenantGroup, TenantRole } from '../services/backend/auth';

type SessionState = {
session: AuthenticatedSession | null;
Expand Down Expand Up @@ -65,6 +65,22 @@ export function useTenantRolesById(tenantId: string): readonly string[] {
return useTenantRoles().find((t) => t.tenantId === tenantId)?.roles ?? [];
}

/**
* Hook to get the current authenticated user's tenants with groups.
* Returns an empty array if the user is not logged in or has no tenant groups.
*/
export function useTenantGroups(): readonly TenantGroup[] {
return useSnapshot(sessionState).session?.tenantGroups ?? [];
}

/**
* Hook to get the current authenticated user's groups for a specific tenant.
* Returns an empty array if the user is not logged in or has no groups for the specified tenant.
*/
export function useTenantGroupsById(tenantId: string): readonly string[] {
return useTenantGroups().find((t) => t.tenantId === tenantId)?.groups ?? [];
}

/**
* Hook to check if the current authenticated user has a specific role in any tenant.
* Returns false if the user is not logged in or does not have the specified role.
Expand Down
2 changes: 1 addition & 1 deletion helm/main/values-c89a45-dev-gold.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ n8n:
UI_OIDC_REDIS_PREFIX: "chwf:ui-oidc:dev:"
CHEFS_BASE_URL: https://chefs-dev.apps.silver.devops.gov.bc.ca
FEATURES_ENABLED: "wil, project, workflow-share, tenant-project-sync"
CSTAR_BASE_URL: https://dev-workflow-mock.apps.gold.devops.gov.bc.ca
CSTAR_BASE_URL: https://dev.connect.digital.gov.bc.ca

route:
enabled: true
Expand Down
2 changes: 1 addition & 1 deletion helm/main/values-c89a45-dev-golddr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ n8n:
CHEFS_GATEWAY_URL: https://chefs-dev.apps.silver.devops.gov.bc.ca/app/gateway/v1
CHEFS_BASE_URL: https://chefs-dev.apps.silver.devops.gov.bc.ca
FEATURES_ENABLED: "wil, project, workflow-share, tenant-project-sync"
CSTAR_BASE_URL: https://dev-workflow-mock.apps.gold.devops.gov.bc.ca
CSTAR_BASE_URL: https://dev.connect.digital.gov.bc.ca

extraExcludedNodes:
- "n8n-nodes-base.scheduleTrigger"
Expand Down