mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Change workflow view function to update. Also handle authorization.
Implements #43041 Implements #43450 Signed-off-by: Stan Silvert <ssilvert@redhat.com>
This commit is contained in:
parent
b287543f6c
commit
398cf1afed
@ -3607,11 +3607,15 @@ workflowDeleteConfirmDialog=This action will permanently delete the workflow. Th
|
||||
workflowNameRequired=Workflow name is required.
|
||||
workflowDeletedSuccess=The workflow has been deleted.
|
||||
workflowDeleteError=Could not delete the workflow\: {{error}}
|
||||
viewWorkflow=View workflow
|
||||
updateWorkflow=Update workflow
|
||||
copyWorkflow=Copy workflow
|
||||
workflowDetails=Workflow details
|
||||
viewWorkflowDetails=Workflows can not be edited except to change enabled/disabled. You can copy the workflow and edit the copy.
|
||||
copyWorkflowDetails=You are about to create a new workflow based on an existing one. You can change the name and edit the JSON of the new workflow.
|
||||
updateWorkflowDetails=Currently, workflows can not be edited except to change the name and enabled/disabled. You can copy the workflow and edit the copy.
|
||||
copyWorkflowDetails=You are about to create a new workflow based on an existing one.
|
||||
createWorkflowDetails=Create a new workflow by providing its JSON representation.
|
||||
changeStatus=Change Status
|
||||
changeStatusTooltip=Enable or disable this workflow
|
||||
workflowEnabled=Workflow enabled
|
||||
workflowDisabled=Workflow disabled
|
||||
workflowUpdated=Workflow updated successfully
|
||||
workflowUpdateError=Could not update the workflow\: {{error}}
|
||||
@ -65,7 +65,7 @@ const LeftNav = ({ title, path, id }: LeftNavProps) => {
|
||||
export const PageNav = () => {
|
||||
const { t } = useTranslation();
|
||||
const { environment } = useEnvironment<Environment>();
|
||||
const { hasSomeAccess } = useAccess();
|
||||
const { hasAccess, hasSomeAccess } = useAccess();
|
||||
const { componentTypes } = useServerInfo();
|
||||
const isFeatureEnabled = useIsFeatureEnabled();
|
||||
const pages =
|
||||
@ -99,6 +99,9 @@ export const PageNav = () => {
|
||||
"view-identity-providers",
|
||||
);
|
||||
|
||||
const showWorkflows =
|
||||
hasAccess("manage-realm") && isFeatureEnabled(Feature.Workflows);
|
||||
|
||||
const showManageRealm = environment.masterRealm === environment.realm;
|
||||
|
||||
return (
|
||||
@ -145,9 +148,7 @@ export const PageNav = () => {
|
||||
)}
|
||||
<LeftNav title="identityProviders" path="/identity-providers" />
|
||||
<LeftNav title="userFederation" path="/user-federation" />
|
||||
{isFeatureEnabled(Feature.Workflows) && (
|
||||
<LeftNav title="workflows" path="/workflows" />
|
||||
)}
|
||||
{showWorkflows && <LeftNav title="workflows" path="/workflows" />}
|
||||
{isFeatureEnabled(Feature.DeclarativeUI) &&
|
||||
pages?.map((p) => (
|
||||
<LeftNav
|
||||
|
||||
@ -31,6 +31,7 @@ import {
|
||||
toWorkflowDetail,
|
||||
} from "./routes/WorkflowDetail";
|
||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||
import WorkflowRepresentation from "libs/keycloak-admin-client/lib/defs/workflowRepresentation";
|
||||
|
||||
type AttributeForm = {
|
||||
workflowJSON?: string;
|
||||
@ -48,7 +49,6 @@ export default function WorkflowDetailForm() {
|
||||
const { addAlert, addError } = useAlerts();
|
||||
const { mode, id } = useParams<WorkflowDetailParams>();
|
||||
const [workflowJSON, setWorkflowJSON] = useState("");
|
||||
const [enabled, setEnabled] = useState(true);
|
||||
|
||||
useFetch(
|
||||
async () => {
|
||||
@ -70,53 +70,33 @@ export default function WorkflowDetailForm() {
|
||||
}
|
||||
|
||||
setWorkflowJSON(JSON.stringify(workflow, null, 2));
|
||||
setEnabled(workflow?.enabled ?? true);
|
||||
},
|
||||
[mode, id],
|
||||
);
|
||||
|
||||
const onSubmit: SubmitHandler<AttributeForm> = async () => {
|
||||
if (mode === "view") {
|
||||
navigate(toWorkflowDetail({ realm, mode: "copy", id: id! }));
|
||||
return;
|
||||
const validateWorkflowJSON = (): WorkflowRepresentation => {
|
||||
const json = JSON.parse(workflowJSON);
|
||||
if (!json.name) {
|
||||
throw new Error(t("workflowNameRequired"));
|
||||
}
|
||||
return json;
|
||||
};
|
||||
|
||||
const onUpdate: SubmitHandler<AttributeForm> = async () => {
|
||||
try {
|
||||
const json = JSON.parse(workflowJSON);
|
||||
if (!json.name) {
|
||||
throw new Error(t("workflowNameRequired"));
|
||||
}
|
||||
|
||||
const payload = {
|
||||
realm,
|
||||
...json,
|
||||
};
|
||||
await adminClient.workflows.create(payload);
|
||||
|
||||
addAlert(t("workflowCreated"), AlertVariant.success);
|
||||
navigate(toWorkflows({ realm }));
|
||||
const json = validateWorkflowJSON();
|
||||
await adminClient.workflows.update({ id: json.id! }, json);
|
||||
addAlert(t("workflowUpdated"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addError("workflowCreateError", error);
|
||||
addError("workflowUpdateError", error);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleEnabled = async () => {
|
||||
const json = JSON.parse(workflowJSON);
|
||||
json.enabled = !enabled;
|
||||
|
||||
const onCreate: SubmitHandler<AttributeForm> = async () => {
|
||||
try {
|
||||
const payload = {
|
||||
realm,
|
||||
...json,
|
||||
};
|
||||
await adminClient.workflows.update({ id: json.id }, payload);
|
||||
|
||||
setWorkflowJSON(JSON.stringify(json, null, 2));
|
||||
setEnabled(!enabled);
|
||||
addAlert(
|
||||
enabled ? t("workflowDisabled") : t("workflowEnabled"),
|
||||
AlertVariant.success,
|
||||
);
|
||||
await adminClient.workflows.create(validateWorkflowJSON());
|
||||
addAlert(t("workflowCreated"), AlertVariant.success);
|
||||
navigate(toWorkflows({ realm }));
|
||||
} catch (error) {
|
||||
addError("workflowCreateError", error);
|
||||
}
|
||||
@ -125,32 +105,31 @@ export default function WorkflowDetailForm() {
|
||||
const titlekeyMap: Record<WorkflowDetailParams["mode"], string> = {
|
||||
copy: "copyWorkflow",
|
||||
create: "createWorkflow",
|
||||
view: "viewWorkflow",
|
||||
update: "updateWorkflow",
|
||||
};
|
||||
|
||||
const subkeyMap: Record<WorkflowDetailParams["mode"], string> = {
|
||||
copy: "copyWorkflowDetails",
|
||||
create: "createWorkflowDetails",
|
||||
view: "viewWorkflowDetails",
|
||||
update: "updateWorkflowDetails",
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewHeader
|
||||
titleKey={titlekeyMap[mode]}
|
||||
subKey={subkeyMap[mode]}
|
||||
isEnabled={enabled}
|
||||
onToggle={mode === "view" ? toggleEnabled : undefined}
|
||||
/>
|
||||
<ViewHeader titleKey={titlekeyMap[mode]} subKey={subkeyMap[mode]} />
|
||||
|
||||
<FormProvider {...form}>
|
||||
<PageSection variant="light">
|
||||
<FormAccess
|
||||
isHorizontal
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
onSubmit={
|
||||
mode === "update"
|
||||
? handleSubmit(onUpdate)
|
||||
: handleSubmit(onCreate)
|
||||
}
|
||||
role={"manage-realm"}
|
||||
className="pf-v5-u-mt-lg"
|
||||
fineGrainedAccess={true} // TODO: Set this properly
|
||||
fineGrainedAccess={true}
|
||||
>
|
||||
<FormGroup
|
||||
label={t("workflowJSON")}
|
||||
@ -171,7 +150,6 @@ export default function WorkflowDetailForm() {
|
||||
<CodeEditor
|
||||
id="workflowJSON"
|
||||
data-testid="workflowJSON"
|
||||
readOnly={mode === "view"}
|
||||
value={workflowJSON}
|
||||
onChange={(value) => setWorkflowJSON(value ?? "")}
|
||||
language="json"
|
||||
@ -181,25 +159,27 @@ export default function WorkflowDetailForm() {
|
||||
/>
|
||||
</FormGroup>
|
||||
<ActionGroup>
|
||||
{mode !== "view" && (
|
||||
<FormSubmitButton
|
||||
formState={form.formState}
|
||||
data-testid="save"
|
||||
allowInvalid
|
||||
allowNonDirty
|
||||
>
|
||||
{t("save")}
|
||||
</FormSubmitButton>
|
||||
)}
|
||||
{mode === "view" && (
|
||||
<FormSubmitButton
|
||||
formState={form.formState}
|
||||
<FormSubmitButton
|
||||
formState={form.formState}
|
||||
data-testid="save"
|
||||
allowInvalid
|
||||
allowNonDirty
|
||||
>
|
||||
{mode === "update" ? t("save") : t("create")}
|
||||
</FormSubmitButton>
|
||||
{mode === "update" && (
|
||||
<Button
|
||||
data-testid="copy"
|
||||
allowInvalid
|
||||
allowNonDirty
|
||||
variant="link"
|
||||
component={(props) => (
|
||||
<Link
|
||||
{...props}
|
||||
to={toWorkflowDetail({ realm, mode: "copy", id: id! })}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
{t("copy")}
|
||||
</FormSubmitButton>
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
data-testid="cancel"
|
||||
|
||||
@ -16,7 +16,6 @@ import { useTranslation } from "react-i18next";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { useAdminClient } from "../admin-client";
|
||||
import { ViewHeader } from "../components/view-header/ViewHeader";
|
||||
//import { useAccess } from "../context/access/Access";
|
||||
import { useRealm } from "../context/realm-context/RealmContext";
|
||||
import helpUrls from "../help-urls";
|
||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||
@ -30,10 +29,6 @@ export default function WorkflowsSection() {
|
||||
const navigate = useNavigate();
|
||||
const { addAlert, addError } = useAlerts();
|
||||
|
||||
// TODO: handle role-based access
|
||||
//const { hasAccess } = useAccess();
|
||||
//const isManager = hasAccess("manage-realm");
|
||||
|
||||
const [key, setKey] = useState(0);
|
||||
const refresh = () => setKey(key + 1);
|
||||
|
||||
@ -51,6 +46,25 @@ export default function WorkflowsSection() {
|
||||
);
|
||||
};
|
||||
|
||||
const toggleEnabled = async (workflowJSON: WorkflowRepresentation) => {
|
||||
workflowJSON.enabled = !(workflowJSON.enabled ?? true);
|
||||
|
||||
try {
|
||||
await adminClient.workflows.update(
|
||||
{ id: workflowJSON.id! },
|
||||
workflowJSON,
|
||||
);
|
||||
|
||||
addAlert(
|
||||
workflowJSON.enabled ? t("workflowEnabled") : t("workflowDisabled"),
|
||||
AlertVariant.success,
|
||||
);
|
||||
refresh();
|
||||
} catch (error) {
|
||||
addError("workflowUpdateError", error);
|
||||
}
|
||||
};
|
||||
|
||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||
titleKey: "workflowDeleteConfirm",
|
||||
messageKey: t("workflowDeleteConfirmDialog", {
|
||||
@ -100,7 +114,7 @@ export default function WorkflowsSection() {
|
||||
displayKey: "name",
|
||||
cellRenderer: (row: WorkflowRepresentation) => (
|
||||
<Link
|
||||
to={toWorkflowDetail({ realm, mode: "view", id: row.id! })}
|
||||
to={toWorkflowDetail({ realm, mode: "update", id: row.id! })}
|
||||
>
|
||||
{row.name}
|
||||
</Link>
|
||||
@ -111,8 +125,8 @@ export default function WorkflowsSection() {
|
||||
displayKey: "id",
|
||||
},
|
||||
{
|
||||
name: "enabled",
|
||||
displayKey: "enabled",
|
||||
name: "status",
|
||||
displayKey: "status",
|
||||
cellRenderer: (row: WorkflowRepresentation) => {
|
||||
return (row.enabled ?? true) ? t("enabled") : t("disabled");
|
||||
},
|
||||
@ -135,6 +149,16 @@ export default function WorkflowsSection() {
|
||||
);
|
||||
},
|
||||
} as Action<WorkflowRepresentation>,
|
||||
{
|
||||
title: t("changeStatus"),
|
||||
tooltipProps: {
|
||||
content: t("changeStatusTooltip"),
|
||||
},
|
||||
onRowClick: (workflow) => {
|
||||
setSelectedWorkflow(workflow);
|
||||
void toggleEnabled(workflow);
|
||||
},
|
||||
} as Action<WorkflowRepresentation>,
|
||||
]}
|
||||
loader={loader}
|
||||
ariaLabelKey="workflows"
|
||||
|
||||
@ -6,7 +6,7 @@ import type { AppRouteObject } from "../../routes";
|
||||
export type WorkflowDetailParams = {
|
||||
realm: string;
|
||||
id: string;
|
||||
mode: "view" | "copy" | "create";
|
||||
mode: "update" | "copy" | "create";
|
||||
};
|
||||
|
||||
const WorkflowDetailForm = lazy(() => import("../WorkflowDetailForm"));
|
||||
@ -16,7 +16,7 @@ export const WorkflowDetailRoute: AppRouteObject = {
|
||||
element: <WorkflowDetailForm />,
|
||||
breadcrumb: (t) => t("workflowDetails"),
|
||||
handle: {
|
||||
access: "anyone", // TODO: update access when view permission is added
|
||||
access: "manage-realm",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ export const WorkflowsRoute: AppRouteObject = {
|
||||
element: <WorkflowsSection />,
|
||||
breadcrumb: (t) => t("workflows"),
|
||||
handle: {
|
||||
access: "view-realm",
|
||||
access: "manage-realm",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user