Support credit cards in 1Password credential parameters (#3746)

This commit is contained in:
Stanislav Novosad
2025-10-17 10:13:47 -06:00
committed by GitHub
parent e3ecc4b657
commit 75ce98e841
3 changed files with 87 additions and 12 deletions

View File

@@ -25,6 +25,7 @@ import { WorkflowParameterInput } from "../../WorkflowParameterInput";
import { ParametersState } from "../types";
import { getDefaultValueForParameterType } from "../workflowEditorUtils";
import { validateBitwardenLoginCredential } from "./util";
import { HelpTooltip } from "@/components/HelpTooltip";
type Props = {
type: WorkflowEditorParameterType;
@@ -273,19 +274,32 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
{type === "credential" && credentialType === "onepassword" && (
<>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Vault ID</Label>
<div className="flex gap-2">
<Label className="text-xs text-slate-300">Vault ID</Label>
<HelpTooltip content="You can find the Vault ID and Item ID in the URL when viewing the item in 1Password on the web." />
</div>
<Input
value={vaultId}
onChange={(e) => setVaultId(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Item ID</Label>
<div className="flex gap-2">
<Label className="text-xs text-slate-300">Item ID</Label>
<HelpTooltip content="Supports all 1Password item types: Logins, Passwords, Credit Cards, Secure Notes, and more." />
</div>
<Input
value={itemId}
onChange={(e) => setItemId(e.target.value)}
/>
</div>
<div className="rounded-md bg-slate-800 p-2">
<div className="space-y-1 text-xs text-slate-400">
* Credit Cards: Due to a 1Password limitation, add the
expiration date as a separate text field named "Expire Date"
in the format MM/YYYY (e.g. 09/2027).
</div>
</div>
</>
)}
{type === "credential" && credentialType === "azurevault" && (

View File

@@ -31,6 +31,7 @@ import {
} from "../types";
import { getDefaultValueForParameterType } from "../workflowEditorUtils";
import { validateBitwardenLoginCredential } from "./util";
import { HelpTooltip } from "@/components/HelpTooltip";
type Props = {
type: WorkflowEditorParameterType;
@@ -357,19 +358,32 @@ function WorkflowParameterEditPanel({
{type === "credential" && credentialType === "onepassword" && (
<>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Vault ID</Label>
<div className="flex gap-2">
<Label className="text-xs text-slate-300">Vault ID</Label>
<HelpTooltip content="You can find the Vault ID and Item ID in the URL when viewing the item in 1Password on the web." />
</div>
<Input
value={vaultId}
onChange={(e) => setVaultId(e.target.value)}
/>
</div>
<div className="space-y-1">
<Label className="text-xs text-slate-300">Item ID</Label>
<div className="flex gap-2">
<Label className="text-xs text-slate-300">Item ID</Label>
<HelpTooltip content="Supports all 1Password item types: Logins, Passwords, Credit Cards, Secure Notes, and more." />
</div>
<Input
value={opItemId}
onChange={(e) => setOpItemId(e.target.value)}
/>
</div>
<div className="rounded-md bg-slate-800 p-2">
<div className="space-y-1 text-xs text-slate-400">
Credit Cards: Due to a 1Password limitation, add the
expiration date as a separate text field named Expire Date
in the format MM/YYYY (e.g. 09/2027).
</div>
</div>
</>
)}
{type === "credential" && credentialType === "azurevault" && (

View File

@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Any, Self
import structlog
from jinja2.sandbox import SandboxedEnvironment
from onepassword import ItemFieldType
from onepassword.client import Client as OnePasswordClient
from skyvern.config import settings
@@ -452,24 +453,61 @@ class WorkflowRunContext:
"context": "These values are placeholders. When you type this in, the real value gets inserted (For security reasons)",
}
# Process all fields
# Process all fields generically so it covers passwords and credit cards
for field in item.fields:
if field.value is None:
if not field.value or field.field_type == ItemFieldType.UNSUPPORTED:
continue
random_secret_id = self.generate_random_secret_id()
# ignore irrelevant fields to avoid confusing AI
if field.id in ["validFrom", "interest", "issuenumber"]:
continue
field_type = field.field_type.value.lower()
if field_type == "totp":
random_secret_id = self.generate_random_secret_id()
totp_secret_id = f"{random_secret_id}_totp"
self.secrets[totp_secret_id] = OnePasswordConstants.TOTP
totp_secret_value = self.totp_secret_value_key(totp_secret_id)
self.secrets[totp_secret_value] = parse_totp_secret(field.value)
self.values[parameter.key]["totp"] = totp_secret_id
elif field.title and field.title.lower() in ["expire date", "expiry date", "expiration date"]:
parts = [part.strip() for part in field.value.strip().split("/")]
if len(parts) == 2:
month, year_part = parts
month = month.zfill(2) # ensure '5' becomes '05'
if len(year_part) == 4:
year = year_part[2:] # 2025 -> 25
else:
year = year_part
self._add_secret_parameter_value(parameter, "card_exp_month", month)
self._add_secret_parameter_value(parameter, "card_exp_year", year)
if len(year) == 2:
self._add_secret_parameter_value(parameter, "card_exp_mmyy", f"{month}/{year}")
self._add_secret_parameter_value(parameter, "card_exp_mmyyyy", f"{month}/20{year}")
else:
# store the 1password-provided value additionally
self._add_secret_parameter_value(parameter, "card_exp", field.value)
else:
# fallback on the 1password-provided value
self._add_secret_parameter_value(parameter, "card_exp", field.value)
else:
# this will be the username or password or other field
key = field.id.replace(" ", "_")
secret_id = f"{random_secret_id}_{key}"
self.secrets[secret_id] = field.value
self.values[parameter.key][key] = secret_id
# using more descriptive keys than 1password provides by default
if field.id == "ccnum":
self._add_secret_parameter_value(parameter, "card_number", field.value)
elif field.id == "cardholder":
self._add_secret_parameter_value(parameter, "card_holder_name", field.value)
elif field.id == "cvv":
self._add_secret_parameter_value(parameter, "card_cvv", field.value)
else:
# this will be the username, password or other fields
self._add_secret_parameter_value(parameter, field.id.replace(" ", "_"), field.value)
# Secure Note support
if item.notes:
self._add_secret_parameter_value(parameter, "notes", item.notes)
async def register_bitwarden_login_credential_parameter_value(
self,
@@ -981,6 +1019,15 @@ class WorkflowRunContext:
azure_vault_client = AsyncAzureVaultClient.create_default()
return azure_vault_client
def _add_secret_parameter_value(self, parameter: Parameter, key: str, value: str) -> None:
if parameter.key not in self.values:
raise ValueError(f"{parameter.key} is missing")
random_secret_id = self.generate_random_secret_id()
secret_id = f"{random_secret_id}_{key}"
self.secrets[secret_id] = value
self.values[parameter.key][key] = secret_id
class WorkflowContextManager:
aws_client: AsyncAWSClient