code block support credential (#3656)
This commit is contained in:
@@ -14,11 +14,13 @@ from collections import defaultdict
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from email.message import EmailMessage
|
from email.message import EmailMessage
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from types import SimpleNamespace
|
||||||
from typing import Annotated, Any, Awaitable, Callable, Literal, Union
|
from typing import Annotated, Any, Awaitable, Callable, Literal, Union
|
||||||
from urllib.parse import quote, urlparse
|
from urllib.parse import quote, urlparse
|
||||||
|
|
||||||
import filetype
|
import filetype
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
import pyotp
|
||||||
import structlog
|
import structlog
|
||||||
from email_validator import EmailNotValidError, validate_email
|
from email_validator import EmailNotValidError, validate_email
|
||||||
from jinja2.sandbox import SandboxedEnvironment
|
from jinja2.sandbox import SandboxedEnvironment
|
||||||
@@ -61,6 +63,8 @@ from skyvern.forge.sdk.db.enums import TaskType
|
|||||||
from skyvern.forge.sdk.schemas.files import FileInfo
|
from skyvern.forge.sdk.schemas.files import FileInfo
|
||||||
from skyvern.forge.sdk.schemas.task_v2 import TaskV2Status
|
from skyvern.forge.sdk.schemas.task_v2 import TaskV2Status
|
||||||
from skyvern.forge.sdk.schemas.tasks import Task, TaskOutput, TaskStatus
|
from skyvern.forge.sdk.schemas.tasks import Task, TaskOutput, TaskStatus
|
||||||
|
from skyvern.forge.sdk.services.bitwarden import BitwardenConstants
|
||||||
|
from skyvern.forge.sdk.services.credentials import AzureVaultConstants, OnePasswordConstants
|
||||||
from skyvern.forge.sdk.trace import TraceManager
|
from skyvern.forge.sdk.trace import TraceManager
|
||||||
from skyvern.forge.sdk.workflow.context_manager import BlockMetadata, WorkflowRunContext
|
from skyvern.forge.sdk.workflow.context_manager import BlockMetadata, WorkflowRunContext
|
||||||
from skyvern.forge.sdk.workflow.exceptions import (
|
from skyvern.forge.sdk.workflow.exceptions import (
|
||||||
@@ -1447,6 +1451,10 @@ class ForLoopBlock(Block):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Credential(SimpleNamespace):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CodeBlock(Block):
|
class CodeBlock(Block):
|
||||||
# There is a mypy bug with Literal. Without the type: ignore, mypy will raise an error:
|
# There is a mypy bug with Literal. Without the type: ignore, mypy will raise an error:
|
||||||
# Parameter 1 of Literal[...] cannot be of type "Any"
|
# Parameter 1 of Literal[...] cannot be of type "Any"
|
||||||
@@ -1573,11 +1581,38 @@ async def wrapper():
|
|||||||
parameter_values = {}
|
parameter_values = {}
|
||||||
for parameter in self.parameters:
|
for parameter in self.parameters:
|
||||||
value = workflow_run_context.get_value(parameter.key)
|
value = workflow_run_context.get_value(parameter.key)
|
||||||
secret_value = workflow_run_context.get_original_secret_value_or_none(value)
|
if not parameter.parameter_type.is_secret_or_credential():
|
||||||
if secret_value is not None:
|
|
||||||
parameter_values[parameter.key] = secret_value
|
|
||||||
else:
|
|
||||||
parameter_values[parameter.key] = value
|
parameter_values[parameter.key] = value
|
||||||
|
continue
|
||||||
|
if isinstance(value, dict):
|
||||||
|
real_secret_values = {}
|
||||||
|
for credential_field, credential_place_holder in value.items():
|
||||||
|
# "context" is a skyvern-defined field to reduce LLM hallucination
|
||||||
|
if credential_field == "context":
|
||||||
|
continue
|
||||||
|
secret_value = workflow_run_context.get_original_secret_value_or_none(credential_place_holder)
|
||||||
|
if (
|
||||||
|
secret_value == BitwardenConstants.TOTP
|
||||||
|
or secret_value == OnePasswordConstants.TOTP
|
||||||
|
or secret_value == AzureVaultConstants.TOTP
|
||||||
|
):
|
||||||
|
totp_secret_key = workflow_run_context.totp_secret_value_key(credential_place_holder)
|
||||||
|
totp_secret = workflow_run_context.get_original_secret_value_or_none(totp_secret_key)
|
||||||
|
if totp_secret:
|
||||||
|
secret_value = pyotp.TOTP(totp_secret).now()
|
||||||
|
else:
|
||||||
|
LOG.warning(
|
||||||
|
"No TOTP secret found, returning the parameter value as is",
|
||||||
|
parameter=credential_place_holder,
|
||||||
|
)
|
||||||
|
|
||||||
|
real_secret_value = secret_value if secret_value is not None else credential_place_holder
|
||||||
|
parameter_values[credential_field] = real_secret_value
|
||||||
|
real_secret_values[credential_field] = real_secret_value
|
||||||
|
parameter_values[parameter.key] = Credential(**real_secret_values)
|
||||||
|
else:
|
||||||
|
secret_value = workflow_run_context.get_original_secret_value_or_none(value)
|
||||||
|
parameter_values[parameter.key] = secret_value if secret_value is not None else value
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.is_safe_code(self.code)
|
self.is_safe_code(self.code)
|
||||||
|
|||||||
@@ -32,6 +32,18 @@ class ParameterType(StrEnum):
|
|||||||
CREDENTIAL = "credential"
|
CREDENTIAL = "credential"
|
||||||
AZURE_SECRET = "azure_secret"
|
AZURE_SECRET = "azure_secret"
|
||||||
|
|
||||||
|
def is_secret_or_credential(self) -> bool:
|
||||||
|
return self in [
|
||||||
|
ParameterType.AWS_SECRET,
|
||||||
|
ParameterType.BITWARDEN_LOGIN_CREDENTIAL,
|
||||||
|
ParameterType.BITWARDEN_SENSITIVE_INFORMATION,
|
||||||
|
ParameterType.BITWARDEN_CREDIT_CARD_DATA,
|
||||||
|
ParameterType.ONEPASSWORD,
|
||||||
|
ParameterType.AZURE_VAULT_CREDENTIAL,
|
||||||
|
ParameterType.CREDENTIAL,
|
||||||
|
ParameterType.AZURE_SECRET,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class Parameter(BaseModel, abc.ABC):
|
class Parameter(BaseModel, abc.ABC):
|
||||||
# TODO (kerem): Should we also have organization_id here?
|
# TODO (kerem): Should we also have organization_id here?
|
||||||
|
|||||||
Reference in New Issue
Block a user