Credentials UI (#1828)
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import { getClient } from "@/api/AxiosClient";
|
||||
import { CredentialApiResponse } from "@/api/types";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
type Props = {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
};
|
||||
|
||||
function CredentialSelector({ value, onChange }: Props) {
|
||||
const credentialGetter = useCredentialGetter();
|
||||
|
||||
const { data: credentials, isFetching } = useQuery<
|
||||
Array<CredentialApiResponse>
|
||||
>({
|
||||
queryKey: ["credentials"],
|
||||
queryFn: async () => {
|
||||
const client = await getClient(credentialGetter);
|
||||
return await client.get("/credentials").then((res) => res.data);
|
||||
},
|
||||
});
|
||||
|
||||
if (isFetching) {
|
||||
return <Skeleton className="h-10 w-full" />;
|
||||
}
|
||||
|
||||
if (!credentials) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Select value={value} onValueChange={onChange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select a credential" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{credentials.map((credential) => (
|
||||
<SelectItem
|
||||
key={credential.credential_id}
|
||||
value={credential.credential_id}
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-medium">{credential.name}</p>
|
||||
<p className="text-xs text-slate-400">
|
||||
{credential.credential_type === "password"
|
||||
? "Password"
|
||||
: "Credit Card"}
|
||||
</p>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
||||
export { CredentialSelector };
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
BitwardenSensitiveInformationParameterYAML,
|
||||
BlockYAML,
|
||||
ContextParameterYAML,
|
||||
CredentialParameterYAML,
|
||||
ParameterYAML,
|
||||
WorkflowCreateYAMLRequest,
|
||||
WorkflowParameterYAML,
|
||||
@@ -91,6 +92,7 @@ function convertToParametersYAML(
|
||||
| ContextParameterYAML
|
||||
| BitwardenSensitiveInformationParameterYAML
|
||||
| BitwardenCreditCardDataParameterYAML
|
||||
| CredentialParameterYAML
|
||||
> {
|
||||
return parameters.map((parameter) => {
|
||||
if (parameter.parameterType === WorkflowEditorParameterTypes.Workflow) {
|
||||
@@ -143,6 +145,15 @@ function convertToParametersYAML(
|
||||
bitwarden_master_password_aws_secret_key:
|
||||
BITWARDEN_MASTER_PASSWORD_AWS_SECRET_KEY,
|
||||
};
|
||||
} else if (
|
||||
parameter.parameterType === WorkflowEditorParameterTypes.Credential
|
||||
) {
|
||||
return {
|
||||
parameter_type: WorkflowParameterTypes.Credential,
|
||||
key: parameter.key,
|
||||
description: parameter.description || null,
|
||||
credential_id: parameter.credentialId,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
parameter_type: WorkflowParameterTypes.Bitwarden_Login_Credential,
|
||||
@@ -170,7 +181,7 @@ export type ParametersState = Array<
|
||||
}
|
||||
| {
|
||||
key: string;
|
||||
parameterType: "credential";
|
||||
parameterType: "bitwardenLoginCredential";
|
||||
collectionId: string;
|
||||
urlParameterKey: string;
|
||||
description?: string | null;
|
||||
@@ -196,6 +207,12 @@ export type ParametersState = Array<
|
||||
collectionId: string;
|
||||
description?: string | null;
|
||||
}
|
||||
| {
|
||||
key: string;
|
||||
parameterType: "credential";
|
||||
credentialId: string;
|
||||
description?: string | null;
|
||||
}
|
||||
>;
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -75,27 +75,32 @@ function WorkflowEditor() {
|
||||
initialParameters={workflow.workflow_definition.parameters
|
||||
.filter((parameter) => isDisplayedInWorkflowEditor(parameter))
|
||||
.map((parameter) => {
|
||||
if (parameter.parameter_type === "workflow") {
|
||||
if (
|
||||
parameter.parameter_type === WorkflowParameterTypes.Workflow
|
||||
) {
|
||||
return {
|
||||
key: parameter.key,
|
||||
parameterType: "workflow",
|
||||
parameterType: WorkflowEditorParameterTypes.Workflow,
|
||||
dataType: parameter.workflow_parameter_type,
|
||||
defaultValue: parameter.default_value,
|
||||
description: parameter.description,
|
||||
};
|
||||
} else if (parameter.parameter_type === "context") {
|
||||
} else if (
|
||||
parameter.parameter_type === WorkflowParameterTypes.Context
|
||||
) {
|
||||
return {
|
||||
key: parameter.key,
|
||||
parameterType: "context",
|
||||
parameterType: WorkflowEditorParameterTypes.Context,
|
||||
sourceParameterKey: parameter.source.key,
|
||||
description: parameter.description,
|
||||
};
|
||||
} else if (
|
||||
parameter.parameter_type === "bitwarden_sensitive_information"
|
||||
parameter.parameter_type ===
|
||||
WorkflowParameterTypes.Bitwarden_Sensitive_Information
|
||||
) {
|
||||
return {
|
||||
key: parameter.key,
|
||||
parameterType: "secret",
|
||||
parameterType: WorkflowEditorParameterTypes.Secret,
|
||||
collectionId: parameter.bitwarden_collection_id,
|
||||
identityKey: parameter.bitwarden_identity_key,
|
||||
identityFields: parameter.bitwarden_identity_fields,
|
||||
@@ -112,10 +117,20 @@ function WorkflowEditor() {
|
||||
itemId: parameter.bitwarden_item_id,
|
||||
description: parameter.description,
|
||||
};
|
||||
} else if (
|
||||
parameter.parameter_type === WorkflowParameterTypes.Credential
|
||||
) {
|
||||
return {
|
||||
key: parameter.key,
|
||||
parameterType: WorkflowEditorParameterTypes.Credential,
|
||||
credentialId: parameter.credential_id,
|
||||
description: parameter.description,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
key: parameter.key,
|
||||
parameterType: "credential",
|
||||
parameterType:
|
||||
WorkflowEditorParameterTypes.BitwardenLoginCredential,
|
||||
collectionId: parameter.bitwarden_collection_id,
|
||||
urlParameterKey: parameter.url_parameter_key,
|
||||
description: parameter.description,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Cross2Icon } from "@radix-ui/react-icons";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useState } from "react";
|
||||
import { useContext, useState } from "react";
|
||||
import {
|
||||
WorkflowEditorParameterType,
|
||||
WorkflowParameterValueType,
|
||||
@@ -22,6 +22,8 @@ import { getDefaultValueForParameterType } from "../workflowEditorUtils";
|
||||
import { toast } from "@/components/ui/use-toast";
|
||||
import { SourceParameterKeySelector } from "../../components/SourceParameterKeySelector";
|
||||
import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area";
|
||||
import CloudContext from "@/store/CloudContext";
|
||||
import { CredentialSelector } from "../../components/CredentialSelector";
|
||||
|
||||
type Props = {
|
||||
type: WorkflowEditorParameterType;
|
||||
@@ -45,6 +47,9 @@ function header(type: WorkflowEditorParameterType) {
|
||||
if (type === "credential") {
|
||||
return "Add Credential Parameter";
|
||||
}
|
||||
if (type === "bitwardenLoginCredential") {
|
||||
return "Add Bitwarden Login Credential Parameter";
|
||||
}
|
||||
if (type === "secret") {
|
||||
return "Add Secret Parameter";
|
||||
}
|
||||
@@ -55,6 +60,7 @@ function header(type: WorkflowEditorParameterType) {
|
||||
}
|
||||
|
||||
function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
|
||||
const isCloud = useContext(CloudContext);
|
||||
const [key, setKey] = useState("");
|
||||
const [urlParameterKey, setUrlParameterKey] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
@@ -76,6 +82,8 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
|
||||
const [identityFields, setIdentityFields] = useState("");
|
||||
const [itemId, setItemId] = useState("");
|
||||
|
||||
const [credentialId, setCredentialId] = useState("");
|
||||
|
||||
return (
|
||||
<ScrollArea>
|
||||
<ScrollAreaViewport className="max-h-[500px]">
|
||||
@@ -181,7 +189,7 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{type === "credential" && (
|
||||
{type === "bitwardenLoginCredential" && (
|
||||
<>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-slate-300">
|
||||
@@ -255,6 +263,18 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{
|
||||
// temporarily cloud only
|
||||
type === "credential" && isCloud && (
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-slate-300">Credential</Label>
|
||||
<CredentialSelector
|
||||
value={credentialId}
|
||||
onChange={(value) => setCredentialId(value)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
onClick={() => {
|
||||
@@ -290,7 +310,7 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
|
||||
});
|
||||
}
|
||||
if (
|
||||
type === "credential" ||
|
||||
type === "bitwardenLoginCredential" ||
|
||||
type === "secret" ||
|
||||
type === "creditCardData"
|
||||
) {
|
||||
@@ -303,10 +323,10 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (type === "credential") {
|
||||
if (type === "bitwardenLoginCredential") {
|
||||
onSave({
|
||||
key,
|
||||
parameterType: "credential",
|
||||
parameterType: "bitwardenLoginCredential",
|
||||
collectionId,
|
||||
urlParameterKey,
|
||||
description,
|
||||
@@ -346,7 +366,23 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
|
||||
onSave({
|
||||
key,
|
||||
parameterType: "context",
|
||||
sourceParameterKey: sourceParameterKey,
|
||||
sourceParameterKey,
|
||||
description,
|
||||
});
|
||||
}
|
||||
if (type === "credential") {
|
||||
if (!credentialId) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Failed to add parameter",
|
||||
description: "Credential is required",
|
||||
});
|
||||
return;
|
||||
}
|
||||
onSave({
|
||||
key,
|
||||
parameterType: "credential",
|
||||
credentialId,
|
||||
description,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Cross2Icon } from "@radix-ui/react-icons";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { useState } from "react";
|
||||
import { useState, useContext } from "react";
|
||||
import {
|
||||
WorkflowEditorParameterType,
|
||||
WorkflowParameterValueType,
|
||||
@@ -22,6 +22,8 @@ import { WorkflowParameterInput } from "../../WorkflowParameterInput";
|
||||
import { toast } from "@/components/ui/use-toast";
|
||||
import { SourceParameterKeySelector } from "../../components/SourceParameterKeySelector";
|
||||
import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area";
|
||||
import CloudContext from "@/store/CloudContext";
|
||||
import { CredentialSelector } from "../../components/CredentialSelector";
|
||||
|
||||
type Props = {
|
||||
type: WorkflowEditorParameterType;
|
||||
@@ -49,6 +51,9 @@ function header(type: WorkflowEditorParameterType) {
|
||||
if (type === "secret") {
|
||||
return "Edit Secret Parameter";
|
||||
}
|
||||
if (type === "bitwardenLoginCredential") {
|
||||
return "Edit Bitwarden Login Credential Parameter";
|
||||
}
|
||||
if (type === "creditCardData") {
|
||||
return "Edit Credit Card Data Parameter";
|
||||
}
|
||||
@@ -61,9 +66,10 @@ function WorkflowParameterEditPanel({
|
||||
onSave,
|
||||
initialValues,
|
||||
}: Props) {
|
||||
const isCloud = useContext(CloudContext);
|
||||
const [key, setKey] = useState(initialValues.key);
|
||||
const [urlParameterKey, setUrlParameterKey] = useState(
|
||||
initialValues.parameterType === "credential"
|
||||
initialValues.parameterType === "bitwardenLoginCredential"
|
||||
? initialValues.urlParameterKey
|
||||
: "",
|
||||
);
|
||||
@@ -71,7 +77,7 @@ function WorkflowParameterEditPanel({
|
||||
initialValues.description ?? "",
|
||||
);
|
||||
const [collectionId, setCollectionId] = useState(
|
||||
initialValues.parameterType === "credential" ||
|
||||
initialValues.parameterType === "bitwardenLoginCredential" ||
|
||||
initialValues.parameterType === "secret" ||
|
||||
initialValues.parameterType === "creditCardData"
|
||||
? initialValues.collectionId
|
||||
@@ -123,6 +129,12 @@ function WorkflowParameterEditPanel({
|
||||
: "",
|
||||
);
|
||||
|
||||
const [credentialId, setCredentialId] = useState(
|
||||
initialValues.parameterType === "credential"
|
||||
? initialValues.credentialId
|
||||
: "",
|
||||
);
|
||||
|
||||
return (
|
||||
<ScrollArea>
|
||||
<ScrollAreaViewport className="max-h-[500px]">
|
||||
@@ -228,7 +240,7 @@ function WorkflowParameterEditPanel({
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{type === "credential" && (
|
||||
{type === "bitwardenLoginCredential" && (
|
||||
<>
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-slate-300">
|
||||
@@ -302,6 +314,18 @@ function WorkflowParameterEditPanel({
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{
|
||||
// temporarily cloud only
|
||||
type === "credential" && isCloud && (
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs text-slate-300">Credential</Label>
|
||||
<CredentialSelector
|
||||
value={credentialId}
|
||||
onChange={(value) => setCredentialId(value)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
onClick={() => {
|
||||
@@ -337,7 +361,7 @@ function WorkflowParameterEditPanel({
|
||||
});
|
||||
}
|
||||
if (
|
||||
type === "credential" ||
|
||||
type === "bitwardenLoginCredential" ||
|
||||
type === "secret" ||
|
||||
type === "creditCardData"
|
||||
) {
|
||||
@@ -350,10 +374,10 @@ function WorkflowParameterEditPanel({
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (type === "credential") {
|
||||
if (type === "bitwardenLoginCredential") {
|
||||
onSave({
|
||||
key,
|
||||
parameterType: "credential",
|
||||
parameterType: "bitwardenLoginCredential",
|
||||
urlParameterKey,
|
||||
collectionId,
|
||||
description,
|
||||
@@ -397,6 +421,22 @@ function WorkflowParameterEditPanel({
|
||||
description,
|
||||
});
|
||||
}
|
||||
if (type === "credential") {
|
||||
if (!credentialId) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: "Failed to save parameter",
|
||||
description: "Credential is required",
|
||||
});
|
||||
return;
|
||||
}
|
||||
onSave({
|
||||
key,
|
||||
parameterType: "credential",
|
||||
credentialId,
|
||||
description,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
Save
|
||||
|
||||
@@ -79,7 +79,7 @@ function WorkflowParametersPanel() {
|
||||
setOperationPanelState({
|
||||
active: true,
|
||||
operation: "add",
|
||||
type: "workflow",
|
||||
type: WorkflowEditorParameterTypes.Workflow,
|
||||
});
|
||||
}}
|
||||
>
|
||||
@@ -90,7 +90,7 @@ function WorkflowParametersPanel() {
|
||||
setOperationPanelState({
|
||||
active: true,
|
||||
operation: "add",
|
||||
type: "credential",
|
||||
type: WorkflowEditorParameterTypes.Credential,
|
||||
});
|
||||
}}
|
||||
>
|
||||
@@ -101,7 +101,18 @@ function WorkflowParametersPanel() {
|
||||
setOperationPanelState({
|
||||
active: true,
|
||||
operation: "add",
|
||||
type: "secret",
|
||||
type: WorkflowEditorParameterTypes.BitwardenLoginCredential,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Bitwarden Login Credential Parameter
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
setOperationPanelState({
|
||||
active: true,
|
||||
operation: "add",
|
||||
type: WorkflowEditorParameterTypes.Secret,
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1559,6 +1559,13 @@ function convertParametersToParameterYAML(
|
||||
default_value: parameter.default_value,
|
||||
};
|
||||
}
|
||||
case WorkflowParameterTypes.Credential: {
|
||||
return {
|
||||
...base,
|
||||
parameter_type: WorkflowParameterTypes.Credential,
|
||||
credential_id: parameter.credential_id,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -59,6 +59,16 @@ export type BitwardenCreditCardDataParameter = WorkflowParameterBase & {
|
||||
deleted_at: string | null;
|
||||
};
|
||||
|
||||
export type CredentialParameter = WorkflowParameterBase & {
|
||||
parameter_type: "credential";
|
||||
workflow_id: string;
|
||||
credential_parameter_id: string;
|
||||
credential_id: string;
|
||||
created_at: string;
|
||||
modified_at: string;
|
||||
deleted_at: string | null;
|
||||
};
|
||||
|
||||
export type WorkflowParameter = WorkflowParameterBase & {
|
||||
parameter_type: "workflow";
|
||||
workflow_id: string;
|
||||
@@ -105,6 +115,7 @@ export const WorkflowParameterTypes = {
|
||||
Bitwarden_Login_Credential: "bitwarden_login_credential",
|
||||
Bitwarden_Sensitive_Information: "bitwarden_sensitive_information",
|
||||
Bitwarden_Credit_Card_Data: "bitwarden_credit_card_data",
|
||||
Credential: "credential",
|
||||
} as const;
|
||||
|
||||
export type WorkflowParameterType =
|
||||
@@ -117,7 +128,8 @@ export function isDisplayedInWorkflowEditor(
|
||||
| ContextParameter
|
||||
| BitwardenCreditCardDataParameter
|
||||
| BitwardenLoginCredentialParameter
|
||||
| BitwardenSensitiveInformationParameter {
|
||||
| BitwardenSensitiveInformationParameter
|
||||
| CredentialParameter {
|
||||
return (
|
||||
parameter.parameter_type === WorkflowParameterTypes.Workflow ||
|
||||
parameter.parameter_type ===
|
||||
@@ -126,7 +138,8 @@ export function isDisplayedInWorkflowEditor(
|
||||
parameter.parameter_type ===
|
||||
WorkflowParameterTypes.Bitwarden_Sensitive_Information ||
|
||||
parameter.parameter_type ===
|
||||
WorkflowParameterTypes.Bitwarden_Credit_Card_Data
|
||||
WorkflowParameterTypes.Bitwarden_Credit_Card_Data ||
|
||||
parameter.parameter_type === WorkflowParameterTypes.Credential
|
||||
);
|
||||
}
|
||||
|
||||
@@ -137,7 +150,8 @@ export type Parameter =
|
||||
| BitwardenLoginCredentialParameter
|
||||
| BitwardenSensitiveInformationParameter
|
||||
| BitwardenCreditCardDataParameter
|
||||
| AWSSecretParameter;
|
||||
| AWSSecretParameter
|
||||
| CredentialParameter;
|
||||
|
||||
export type WorkflowBlock =
|
||||
| TaskBlock
|
||||
@@ -199,6 +213,7 @@ export type WorkflowBlockType =
|
||||
|
||||
export const WorkflowEditorParameterTypes = {
|
||||
Workflow: "workflow",
|
||||
BitwardenLoginCredential: "bitwardenLoginCredential",
|
||||
Credential: "credential",
|
||||
Secret: "secret",
|
||||
Context: "context",
|
||||
|
||||
@@ -20,6 +20,7 @@ export type ParameterYAML =
|
||||
| WorkflowParameterYAML
|
||||
| BitwardenLoginCredentialParameterYAML
|
||||
| AWSSecretParameterYAML
|
||||
| CredentialParameterYAML
|
||||
| ContextParameterYAML
|
||||
| OutputParameterYAML
|
||||
| BitwardenSensitiveInformationParameterYAML
|
||||
@@ -82,6 +83,11 @@ export type OutputParameterYAML = ParameterYAMLBase & {
|
||||
parameter_type: "output";
|
||||
};
|
||||
|
||||
export type CredentialParameterYAML = ParameterYAMLBase & {
|
||||
parameter_type: "credential";
|
||||
credential_id: string;
|
||||
};
|
||||
|
||||
export type BlockYAML =
|
||||
| TaskBlockYAML
|
||||
| CodeBlockYAML
|
||||
|
||||
Reference in New Issue
Block a user