Implement BitwardenSensitiveInformationParameter (#589)
This commit is contained in:
@@ -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 ###
|
||||
@@ -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,
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user