Implement BitwardenSensitiveInformationParameter (#589)

This commit is contained in:
Kerem Yilmaz
2024-07-11 09:48:14 -07:00
committed by GitHub
parent 87d6e71768
commit 6f88ae31a0
10 changed files with 422 additions and 41 deletions

View File

@@ -0,0 +1,71 @@
"""Create bitwarden identity parameter table
Revision ID: ac679ea03578
Revises: bea545cb21b4
Create Date: 2024-07-11 16:44:54.145819+00:00
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision: str = "ac679ea03578"
down_revision: Union[str, None] = "bea545cb21b4"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"bitwarden_sensitive_information_parameters",
sa.Column("bitwarden_sensitive_information_parameter_id", sa.String(), nullable=False),
sa.Column("workflow_id", sa.String(), nullable=False),
sa.Column("key", sa.String(), nullable=False),
sa.Column("description", sa.String(), nullable=True),
sa.Column("bitwarden_client_id_aws_secret_key", sa.String(), nullable=False),
sa.Column("bitwarden_client_secret_aws_secret_key", sa.String(), nullable=False),
sa.Column("bitwarden_master_password_aws_secret_key", sa.String(), nullable=False),
sa.Column("bitwarden_collection_id", sa.String(), nullable=False),
sa.Column("bitwarden_identity_key", sa.String(), nullable=False),
sa.Column("bitwarden_identity_fields", sa.JSON(), nullable=False),
sa.Column("created_at", sa.DateTime(), nullable=False),
sa.Column("modified_at", sa.DateTime(), nullable=False),
sa.Column("deleted_at", sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(
["workflow_id"],
["workflows.workflow_id"],
),
sa.PrimaryKeyConstraint("bitwarden_sensitive_information_parameter_id"),
)
op.create_index(
op.f("ix_bitwarden_sensitive_information_parameters_bitwarden_sensitive_information_parameter_id"),
"bitwarden_sensitive_information_parameters",
["bitwarden_sensitive_information_parameter_id"],
unique=False,
)
op.create_index(
op.f("ix_bitwarden_sensitive_information_parameters_workflow_id"),
"bitwarden_sensitive_information_parameters",
["workflow_id"],
unique=False,
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(
op.f("ix_bitwarden_sensitive_information_parameters_workflow_id"),
table_name="bitwarden_sensitive_information_parameters",
)
op.drop_index(
op.f("ix_bitwarden_sensitive_information_parameters_bitwarden_sensitive_information_parameter_id"),
table_name="bitwarden_sensitive_information_parameters",
)
op.drop_table("bitwarden_sensitive_information_parameters")
# ### end Alembic commands ###

View File

@@ -14,6 +14,7 @@ from skyvern.forge.sdk.db.models import (
ArtifactModel,
AWSSecretParameterModel,
BitwardenLoginCredentialParameterModel,
BitwardenSensitiveInformationParameterModel,
OrganizationAuthTokenModel,
OrganizationModel,
OutputParameterModel,
@@ -31,6 +32,7 @@ from skyvern.forge.sdk.db.utils import (
convert_to_artifact,
convert_to_aws_secret_parameter,
convert_to_bitwarden_login_credential_parameter,
convert_to_bitwarden_sensitive_information_parameter,
convert_to_organization,
convert_to_organization_auth_token,
convert_to_output_parameter,
@@ -48,6 +50,7 @@ from skyvern.forge.sdk.schemas.tasks import ProxyLocation, Task, TaskStatus
from skyvern.forge.sdk.workflow.models.parameter import (
AWSSecretParameter,
BitwardenLoginCredentialParameter,
BitwardenSensitiveInformationParameter,
OutputParameter,
WorkflowParameter,
WorkflowParameterType,
@@ -1138,6 +1141,35 @@ class AgentDB:
await session.refresh(bitwarden_login_credential_parameter)
return convert_to_bitwarden_login_credential_parameter(bitwarden_login_credential_parameter)
async def create_bitwarden_sensitive_information_parameter(
self,
workflow_id: str,
bitwarden_client_id_aws_secret_key: str,
bitwarden_client_secret_aws_secret_key: str,
bitwarden_master_password_aws_secret_key: str,
bitwarden_collection_id: str,
bitwarden_identity_key: str,
bitwarden_identity_fields: list[str],
key: str,
description: str | None = None,
) -> BitwardenSensitiveInformationParameter:
async with self.Session() as session:
bitwarden_sensitive_information_parameter = BitwardenSensitiveInformationParameterModel(
workflow_id=workflow_id,
bitwarden_client_id_aws_secret_key=bitwarden_client_id_aws_secret_key,
bitwarden_client_secret_aws_secret_key=bitwarden_client_secret_aws_secret_key,
bitwarden_master_password_aws_secret_key=bitwarden_master_password_aws_secret_key,
bitwarden_collection_id=bitwarden_collection_id,
bitwarden_identity_key=bitwarden_identity_key,
bitwarden_identity_fields=bitwarden_identity_fields,
key=key,
description=description,
)
session.add(bitwarden_sensitive_information_parameter)
await session.commit()
await session.refresh(bitwarden_sensitive_information_parameter)
return convert_to_bitwarden_sensitive_information_parameter(bitwarden_sensitive_information_parameter)
async def create_output_parameter(
self,
workflow_id: str,

View File

@@ -40,6 +40,7 @@ WORKFLOW_PARAMETER_PREFIX = "wp"
AWS_SECRET_PARAMETER_PREFIX = "asp"
OUTPUT_PARAMETER_PREFIX = "op"
BITWARDEN_LOGIN_CREDENTIAL_PARAMETER_PREFIX = "blc"
BITWARDEN_SENSITIVE_INFORMATION_PARAMETER_PREFIX = "bsi"
TASK_GENERATION_PREFIX = "tg"
@@ -78,6 +79,11 @@ def generate_bitwarden_login_credential_parameter_id() -> str:
return f"{BITWARDEN_LOGIN_CREDENTIAL_PARAMETER_PREFIX}_{int_id}"
def generate_bitwarden_sensitive_information_parameter_id() -> str:
int_id = generate_id()
return f"{BITWARDEN_SENSITIVE_INFORMATION_PARAMETER_PREFIX}_{int_id}"
def generate_organization_auth_token_id() -> str:
int_id = generate_id()
return f"{ORGANIZATION_AUTH_TOKEN_PREFIX}_{int_id}"

View File

@@ -22,6 +22,7 @@ from skyvern.forge.sdk.db.id import (
generate_artifact_id,
generate_aws_secret_parameter_id,
generate_bitwarden_login_credential_parameter_id,
generate_bitwarden_sensitive_information_parameter_id,
generate_org_id,
generate_organization_auth_token_id,
generate_output_parameter_id,
@@ -293,6 +294,35 @@ class BitwardenLoginCredentialParameterModel(Base):
deleted_at = Column(DateTime, nullable=True)
class BitwardenSensitiveInformationParameterModel(Base):
__tablename__ = "bitwarden_sensitive_information_parameters"
bitwarden_sensitive_information_parameter_id = Column(
String,
primary_key=True,
index=True,
default=generate_bitwarden_sensitive_information_parameter_id,
)
workflow_id = Column(String, ForeignKey("workflows.workflow_id"), index=True, nullable=False)
key = Column(String, nullable=False)
description = Column(String, nullable=True)
bitwarden_client_id_aws_secret_key = Column(String, nullable=False)
bitwarden_client_secret_aws_secret_key = Column(String, nullable=False)
bitwarden_master_password_aws_secret_key = Column(String, nullable=False)
bitwarden_collection_id = Column(String, nullable=False)
bitwarden_identity_key = Column(String, nullable=False)
# This is a list of fields to extract from the Bitwarden Identity.
bitwarden_identity_fields = Column(JSON, nullable=False)
created_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
modified_at = Column(
DateTime,
default=datetime.datetime.utcnow,
onupdate=datetime.datetime.utcnow,
nullable=False,
)
deleted_at = Column(DateTime, nullable=True)
class WorkflowRunParameterModel(Base):
__tablename__ = "workflow_run_parameters"

View File

@@ -10,6 +10,7 @@ from skyvern.forge.sdk.db.models import (
ArtifactModel,
AWSSecretParameterModel,
BitwardenLoginCredentialParameterModel,
BitwardenSensitiveInformationParameterModel,
OrganizationAuthTokenModel,
OrganizationModel,
OutputParameterModel,
@@ -26,6 +27,7 @@ from skyvern.forge.sdk.schemas.tasks import ProxyLocation, Task, TaskStatus
from skyvern.forge.sdk.workflow.models.parameter import (
AWSSecretParameter,
BitwardenLoginCredentialParameter,
BitwardenSensitiveInformationParameter,
OutputParameter,
WorkflowParameter,
WorkflowParameterType,
@@ -263,6 +265,33 @@ def convert_to_bitwarden_login_credential_parameter(
)
def convert_to_bitwarden_sensitive_information_parameter(
bitwarden_sensitive_information_parameter_model: BitwardenSensitiveInformationParameterModel,
debug_enabled: bool = False,
) -> BitwardenSensitiveInformationParameter:
if debug_enabled:
LOG.debug(
"Converting BitwardenSensitiveInformationParameterModel to BitwardenSensitiveInformationParameter",
bitwarden_sensitive_information_parameter_id=bitwarden_sensitive_information_parameter_model.bitwarden_sensitive_information_parameter_id,
)
return BitwardenSensitiveInformationParameter(
bitwarden_sensitive_information_parameter_id=bitwarden_sensitive_information_parameter_model.bitwarden_sensitive_information_parameter_id,
workflow_id=bitwarden_sensitive_information_parameter_model.workflow_id,
key=bitwarden_sensitive_information_parameter_model.key,
description=bitwarden_sensitive_information_parameter_model.description,
bitwarden_client_id_aws_secret_key=bitwarden_sensitive_information_parameter_model.bitwarden_client_id_aws_secret_key,
bitwarden_client_secret_aws_secret_key=bitwarden_sensitive_information_parameter_model.bitwarden_client_secret_aws_secret_key,
bitwarden_master_password_aws_secret_key=bitwarden_sensitive_information_parameter_model.bitwarden_master_password_aws_secret_key,
bitwarden_collection_id=bitwarden_sensitive_information_parameter_model.bitwarden_collection_id,
bitwarden_identity_key=bitwarden_sensitive_information_parameter_model.bitwarden_identity_key,
bitwarden_identity_fields=bitwarden_sensitive_information_parameter_model.bitwarden_identity_fields,
created_at=bitwarden_sensitive_information_parameter_model.created_at,
modified_at=bitwarden_sensitive_information_parameter_model.modified_at,
deleted_at=bitwarden_sensitive_information_parameter_model.deleted_at,
)
def convert_to_output_parameter(
output_parameter_model: OutputParameterModel, debug_enabled: bool = False
) -> OutputParameter:

View File

@@ -29,6 +29,7 @@ class BitwardenConstants(StrEnum):
MASTER_PASSWORD = "BW_MASTER_PASSWORD"
URL = "BW_URL"
BW_COLLECTION_ID = "BW_COLLECTION_ID"
IDENTITY_KEY = "BW_IDENTITY_KEY"
USERNAME = "BW_USERNAME"
PASSWORD = "BW_PASSWORD"
@@ -75,49 +76,10 @@ class BitwardenService:
"""
Get the secret value from the Bitwarden CLI.
"""
# Step 1: Set up environment variables and log in
try:
env = {
"BW_CLIENTID": client_id,
"BW_CLIENTSECRET": client_secret,
"BW_PASSWORD": master_password,
}
login_command = ["bw", "login", "--apikey"]
login_result = BitwardenService.run_command(login_command, env)
BitwardenService.login(client_id, client_secret)
session_key = BitwardenService.unlock(master_password)
# Validate the login result
if login_result.stdout and "You are logged in!" not in login_result.stdout:
raise BitwardenLoginError(
f"Failed to log in. stdout: {login_result.stdout} stderr: {login_result.stderr}"
)
if login_result.stderr and "You are already logged in as" not in login_result.stderr:
raise BitwardenLoginError(
f"Failed to log in. stdout: {login_result.stdout} stderr: {login_result.stderr}"
)
LOG.info("Bitwarden login successful")
# Step 2: Unlock the vault
unlock_command = ["bw", "unlock", "--passwordenv", "BW_PASSWORD"]
unlock_result = BitwardenService.run_command(unlock_command, env)
# Validate the unlock result
if unlock_result.stdout and "Your vault is now unlocked!" not in unlock_result.stdout:
raise BitwardenUnlockError(
f"Failed to unlock vault. stdout: {unlock_result.stdout} stderr: {unlock_result.stderr}"
)
# Extract session key
try:
session_key = BitwardenService._extract_session_key(unlock_result.stdout)
except Exception as e:
raise BitwardenUnlockError(f"Unable to extract session key: {str(e)}")
if not session_key:
raise BitwardenUnlockError("Session key is empty.")
# Step 3: Retrieve the items
# Extract the domain from the URL and search for items in Bitwarden with that domain
domain = tldextract.extract(url).domain
list_command = [
@@ -187,6 +149,123 @@ class BitwardenService:
# Step 4: Log out
BitwardenService.logout()
@staticmethod
def get_sensitive_information_from_identity(
client_id: str,
client_secret: str,
master_password: str,
collection_id: str,
identity_key: str,
identity_fields: list[str],
) -> dict[str, str]:
"""
Get the sensitive information from the Bitwarden CLI.
"""
try:
BitwardenService.login(client_id, client_secret)
session_key = BitwardenService.unlock(master_password)
# Step 3: Retrieve the items
list_command = [
"bw",
"list",
"items",
"--search",
identity_key,
"--session",
session_key,
"--collectionid",
collection_id,
]
items_result = BitwardenService.run_command(list_command)
# Parse the items and extract sensitive information
try:
items = json.loads(items_result.stdout)
except json.JSONDecodeError:
raise BitwardenListItemsError("Failed to parse items JSON. Output: " + items_result.stdout)
if not items:
raise BitwardenListItemsError(
f"No items found in Bitwarden for identity key: {identity_key} in collection with ID: {collection_id}"
)
# Filter the identity items
# https://bitwarden.com/help/cli/#create lists the type of the identity items as 4
identity_items = [item for item in items if item["type"] == 4]
if len(identity_items) != 1:
raise BitwardenListItemsError(
f"Expected exactly one identity item, but found {len(identity_items)} items for identity key: {identity_key} in collection with ID: {collection_id}"
)
identity_item = identity_items[0]
sensitive_information: dict[str, str] = {}
for field in identity_fields:
# The identity item may store sensitive information in custom fields or default fields
# Custom fields are prioritized over default fields
# TODO (kerem): Make this case insensitive?
if field in identity_item["fields"]:
sensitive_information[field] = identity_item["fields"][field]["value"]
elif field in identity_item["identity"]:
sensitive_information[field] = identity_item["identity"][field]
return sensitive_information
finally:
# Step 4: Log out
BitwardenService.logout()
@staticmethod
def login(client_id: str, client_secret: str) -> None:
"""
Log in to the Bitwarden CLI.
"""
env = {
"BW_CLIENTID": client_id,
"BW_CLIENTSECRET": client_secret,
}
login_command = ["bw", "login", "--apikey"]
login_result = BitwardenService.run_command(login_command, env)
# Validate the login result
if login_result.stdout and "You are logged in!" not in login_result.stdout:
raise BitwardenLoginError(f"Failed to log in. stdout: {login_result.stdout} stderr: {login_result.stderr}")
if login_result.stderr and "You are already logged in as" not in login_result.stderr:
raise BitwardenLoginError(f"Failed to log in. stdout: {login_result.stdout} stderr: {login_result.stderr}")
LOG.info("Bitwarden login successful")
@staticmethod
def unlock(master_password: str) -> str:
"""
Unlock the Bitwarden CLI.
"""
env = {
"BW_PASSWORD": master_password,
}
unlock_command = ["bw", "unlock", "--passwordenv", "BW_PASSWORD"]
unlock_result = BitwardenService.run_command(unlock_command, env)
# Validate the unlock result
if unlock_result.stdout and "Your vault is now unlocked!" not in unlock_result.stdout:
raise BitwardenUnlockError(
f"Failed to unlock vault. stdout: {unlock_result.stdout} stderr: {unlock_result.stderr}"
)
# Extract session key
try:
session_key = BitwardenService._extract_session_key(unlock_result.stdout)
except Exception as e:
raise BitwardenUnlockError(f"Unable to extract session key: {str(e)}")
if not session_key:
raise BitwardenUnlockError("Session key is empty.")
return session_key
@staticmethod
def logout() -> None:
"""

View File

@@ -194,6 +194,55 @@ class WorkflowRunContext:
except BitwardenBaseError as e:
LOG.error(f"Failed to get secret from Bitwarden. Error: {e}")
raise e
# TODO (kerem): Implement this
elif parameter.parameter_type == ParameterType.BITWARDEN_SENSITIVE_INFORMATION:
try:
# Get the Bitwarden login credentials from AWS secrets
client_id = await aws_client.get_secret(parameter.bitwarden_client_id_aws_secret_key)
client_secret = await aws_client.get_secret(parameter.bitwarden_client_secret_aws_secret_key)
master_password = await aws_client.get_secret(parameter.bitwarden_master_password_aws_secret_key)
except Exception as e:
LOG.error(f"Failed to get Bitwarden login credentials from AWS secrets. Error: {e}")
raise e
bitwarden_identity_key = parameter.bitwarden_identity_key
if self.has_parameter(parameter.bitwarden_identity_key) and self.has_value(
parameter.bitwarden_identity_key
):
bitwarden_identity_key = self.values[parameter.bitwarden_identity_key]
collection_id = parameter.bitwarden_collection_id
if self.has_parameter(parameter.bitwarden_collection_id) and self.has_value(
parameter.bitwarden_collection_id
):
collection_id = self.values[parameter.bitwarden_collection_id]
try:
sensitive_values = BitwardenService.get_sensitive_information_from_identity(
client_id,
client_secret,
master_password,
collection_id,
bitwarden_identity_key,
parameter.bitwarden_identity_fields,
)
if sensitive_values:
self.secrets[BitwardenConstants.IDENTITY_KEY] = bitwarden_identity_key
self.secrets[BitwardenConstants.CLIENT_SECRET] = client_secret
self.secrets[BitwardenConstants.CLIENT_ID] = client_id
self.secrets[BitwardenConstants.MASTER_PASSWORD] = master_password
self.secrets[BitwardenConstants.BW_COLLECTION_ID] = collection_id
self.values[parameter.key] = {}
for key, value in sensitive_values.items():
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
except BitwardenBaseError as e:
LOG.error(f"Failed to get sensitive information from Bitwarden. Error: {e}")
raise e
elif isinstance(parameter, ContextParameter):
if isinstance(parameter.source, WorkflowParameter):
# TODO (kerem): set this while initializing the context manager

View File

@@ -12,6 +12,7 @@ class ParameterType(StrEnum):
CONTEXT = "context"
AWS_SECRET = "aws_secret"
BITWARDEN_LOGIN_CREDENTIAL = "bitwarden_login_credential"
BITWARDEN_SENSITIVE_INFORMATION = "bitwarden_sensitive_information"
OUTPUT = "output"
@@ -61,6 +62,30 @@ class BitwardenLoginCredentialParameter(Parameter):
deleted_at: datetime | None = None
class BitwardenSensitiveInformationParameter(Parameter):
parameter_type: Literal[ParameterType.BITWARDEN_SENSITIVE_INFORMATION] = (
ParameterType.BITWARDEN_SENSITIVE_INFORMATION
)
# parameter fields
bitwarden_sensitive_information_parameter_id: str
workflow_id: str
# bitwarden cli required fields
bitwarden_client_id_aws_secret_key: str
bitwarden_client_secret_aws_secret_key: str
bitwarden_master_password_aws_secret_key: str
# bitwarden collection id to filter the Bitwarden Identity from
bitwarden_collection_id: str
# unique key to identify the Bitwarden Identity in the collection
# this has to be in the identity's name
bitwarden_identity_key: str
# fields to extract from the Bitwarden Identity. Custom fields are prioritized over default identity fields
bitwarden_identity_fields: list[str]
created_at: datetime
modified_at: datetime
deleted_at: datetime | None = None
class WorkflowParameterType(StrEnum):
STRING = "string"
INTEGER = "integer"
@@ -124,6 +149,7 @@ ParameterSubclasses = Union[
ContextParameter,
AWSSecretParameter,
BitwardenLoginCredentialParameter,
BitwardenSensitiveInformationParameter,
OutputParameter,
]
PARAMETER_TYPE = Annotated[ParameterSubclasses, Field(discriminator="parameter_type")]

View File

@@ -41,6 +41,28 @@ class BitwardenLoginCredentialParameterYAML(ParameterYAML):
bitwarden_collection_id: str | None = None
class BitwardenSensitiveInformationParameterYAML(ParameterYAML):
# 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"
# This pattern already works in block.py but since the ParameterType is not defined in this file, mypy is not able
# to infer the type of the parameter_type attribute.
parameter_type: Literal[ParameterType.BITWARDEN_SENSITIVE_INFORMATION] = (
ParameterType.BITWARDEN_SENSITIVE_INFORMATION
) # type: ignore
# bitwarden cli required fields
bitwarden_client_id_aws_secret_key: str
bitwarden_client_secret_aws_secret_key: str
bitwarden_master_password_aws_secret_key: str
# bitwarden collection id to filter the Bitwarden Identity from
bitwarden_collection_id: str
# unique key to identify the Bitwarden Identity in the collection
# this has to be in the identity's name
bitwarden_identity_key: str
# fields to extract from the Bitwarden Identity. Custom fields are prioritized over default identity fields
bitwarden_identity_fields: list[str]
class WorkflowParameterYAML(ParameterYAML):
# 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"
@@ -172,6 +194,7 @@ class FileParserBlockYAML(BlockYAML):
PARAMETER_YAML_SUBCLASSES = (
AWSSecretParameterYAML
| BitwardenLoginCredentialParameterYAML
| BitwardenSensitiveInformationParameterYAML
| WorkflowParameterYAML
| ContextParameterYAML
| OutputParameterYAML

View File

@@ -489,6 +489,30 @@ class WorkflowService:
bitwarden_collection_id=bitwarden_collection_id,
)
async def create_bitwarden_sensitive_information_parameter(
self,
workflow_id: str,
bitwarden_client_id_aws_secret_key: str,
bitwarden_client_secret_aws_secret_key: str,
bitwarden_master_password_aws_secret_key: str,
bitwarden_collection_id: str,
bitwarden_identity_key: str,
bitwarden_identity_fields: list[str],
key: str,
description: str | None = None,
) -> Parameter:
return await app.DATABASE.create_bitwarden_sensitive_information_parameter(
workflow_id=workflow_id,
bitwarden_client_id_aws_secret_key=bitwarden_client_id_aws_secret_key,
bitwarden_client_secret_aws_secret_key=bitwarden_client_secret_aws_secret_key,
bitwarden_master_password_aws_secret_key=bitwarden_master_password_aws_secret_key,
bitwarden_collection_id=bitwarden_collection_id,
bitwarden_identity_key=bitwarden_identity_key,
bitwarden_identity_fields=bitwarden_identity_fields,
key=key,
description=description,
)
async def create_output_parameter(
self, workflow_id: str, key: str, description: str | None = None
) -> OutputParameter:
@@ -865,6 +889,18 @@ class WorkflowService:
description=parameter.description,
bitwarden_collection_id=parameter.bitwarden_collection_id,
)
elif parameter.parameter_type == ParameterType.BITWARDEN_SENSITIVE_INFORMATION:
parameters[parameter.key] = await self.create_bitwarden_sensitive_information_parameter(
workflow_id=workflow.workflow_id,
bitwarden_client_id_aws_secret_key=parameter.bitwarden_client_id_aws_secret_key,
bitwarden_client_secret_aws_secret_key=parameter.bitwarden_client_secret_aws_secret_key,
bitwarden_master_password_aws_secret_key=parameter.bitwarden_master_password_aws_secret_key,
bitwarden_collection_id=parameter.bitwarden_collection_id,
bitwarden_identity_key=parameter.bitwarden_identity_key,
bitwarden_identity_fields=parameter.bitwarden_identity_fields,
key=parameter.key,
description=parameter.description,
)
elif parameter.parameter_type == ParameterType.WORKFLOW:
parameters[parameter.key] = await self.create_workflow_parameter(
workflow_id=workflow.workflow_id,