From 8c6b527633c624b620eb5c26d4e4ef0c6c6b08a2 Mon Sep 17 00:00:00 2001 From: Colton Hurst Date: Fri, 15 Sep 2023 10:47:47 -0400 Subject: [PATCH] SM-874: Add a ProjectsClient and address PR comments --- .../BitwardenClient/bitwarden_client.py | 48 ++++++++++++++++++- languages/python/login.py | 35 ++++++++++++-- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/languages/python/BitwardenClient/bitwarden_client.py b/languages/python/BitwardenClient/bitwarden_client.py index 02045620c..db36876b7 100644 --- a/languages/python/BitwardenClient/bitwarden_client.py +++ b/languages/python/BitwardenClient/bitwarden_client.py @@ -2,7 +2,7 @@ from typing import Any, List, Optional from uuid import UUID import bitwarden_py -from .schemas import ClientSettings, Command, PasswordLoginRequest, PasswordLoginResponse, ResponseForPasswordLoginResponse, ResponseForSecretIdentifiersResponse, ResponseForSecretResponse, ResponseForSecretsDeleteResponse, ResponseForSyncResponse, ResponseForUserAPIKeyResponse, SecretCreateRequest, SecretGetRequest, SecretIdentifiersRequest, SecretIdentifiersResponse, SecretPutRequest, SecretResponse, SecretVerificationRequest, SecretsCommand, SecretsDeleteRequest, SecretsDeleteResponse, SyncRequest, SyncResponse, UserAPIKeyResponse, AccessTokenLoginRequest, AccessTokenLoginResponse, ResponseForAccessTokenLoginResponse +from .schemas import ClientSettings, Command, PasswordLoginRequest, PasswordLoginResponse, ResponseForPasswordLoginResponse, ResponseForSecretIdentifiersResponse, ResponseForSecretResponse, ResponseForSecretsDeleteResponse, ResponseForSyncResponse, ResponseForUserAPIKeyResponse, SecretCreateRequest, SecretGetRequest, SecretIdentifiersRequest, SecretIdentifiersResponse, SecretPutRequest, SecretResponse, SecretVerificationRequest, SecretsCommand, SecretsDeleteRequest, SecretsDeleteResponse, SyncRequest, SyncResponse, UserAPIKeyResponse, AccessTokenLoginRequest, AccessTokenLoginResponse, ResponseForAccessTokenLoginResponse, ResponseForProjectResponse, ProjectsCommand, ProjectCreateRequest, ProjectGetRequest, ProjectPutRequest, ProjectsListRequest, ResponseForProjectsResponse, ResponseForProjectsDeleteResponse, ProjectsDeleteRequest class BitwardenClient: def __init__(self, settings: ClientSettings = None): @@ -40,6 +40,9 @@ def sync(self, exclude_subdomains: bool = False) -> ResponseForSyncResponse: def secrets(self): return SecretsClient(self) + def projects(self): + return ProjectsClient(self) + def _run_command(self, command: Command) -> Any: response_json = self.inner.run_command(json.dumps(command.to_dict())) return json.loads(response_json) @@ -91,3 +94,46 @@ def delete(self, ids: List[str]) -> ResponseForSecretsDeleteResponse: Command(secrets=SecretsCommand(delete=SecretsDeleteRequest(ids))) ) return ResponseForSecretsDeleteResponse.from_dict(result) + +class ProjectsClient: + def __init__(self, client: BitwardenClient): + self.client = client + + def get(self, id: str) -> ResponseForProjectResponse: + result = self.client._run_command( + Command(projects=ProjectsCommand(get=ProjectGetRequest(id))) + ) + return ResponseForProjectResponse.from_dict(result) + + def create(self, + name: str, + organization_id: str, + ) -> ResponseForProjectResponse: + result = self.client._run_command( + Command(projects=ProjectsCommand( + create=ProjectCreateRequest(name, organization_id))) + ) + return ResponseForProjectResponse.from_dict(result) + + def list(self, organization_id: str) -> ResponseForProjectsResponse: + result = self.client._run_command( + Command(projects=ProjectsCommand( + list=ProjectsListRequest(organization_id))) + ) + return ResponseForProjectsResponse.from_dict(result) + + def update(self, id: str, + name: str, + organization_id: str, + ) -> ResponseForProjectResponse: + result = self.client._run_command( + Command(projects=ProjectsCommand(update=ProjectPutRequest( + id, name, organization_id))) + ) + return ResponseForProjectResponse.from_dict(result) + + def delete(self, ids: List[str]) -> ResponseForProjectsDeleteResponse: + result = self.client._run_command( + Command(projects=ProjectsCommand(delete=ProjectsDeleteRequest(ids))) + ) + return ResponseForProjectsDeleteResponse.from_dict(result) diff --git a/languages/python/login.py b/languages/python/login.py index 5c168de37..9cac60090 100644 --- a/languages/python/login.py +++ b/languages/python/login.py @@ -1,21 +1,46 @@ import json import logging from BitwardenClient.bitwarden_client import BitwardenClient -from BitwardenClient.schemas import client_settings_from_dict +from BitwardenClient.schemas import client_settings_from_dict, DeviceType +# Create the BitwardenClient, which is used to interact with the SDK client = BitwardenClient(client_settings_from_dict({ "apiUrl": "http://localhost:4000", - "deviceType": "SDK", + "deviceType": DeviceType.SDK, "identityUrl": "http://localhost:33656", "userAgent": "Python", })) +# Add some logging & set the org id logging.basicConfig(level=logging.DEBUG) +organization_id = "org_id_here" -result = client.access_token_login("access token here") +# Attempt to authenticate with the Secrets Manager Access Token +result = client.access_token_login("access_token_here") -secret = client.secrets().create("TEST_SECRET", "This is a test secret", "organization id here", "Secret1234!", ["project id here"]) +if result.success == False: + sys.exit(result.error_message) -input("Press Enter to delete the secret...") +# -- Example Project Commands -- + +project = client.projects().create("ProjectName", organization_id) +project2 = client.projects().create("Project - Don't Delete Me!", organization_id) +updated_project = client.projects().update(project.data.id, "Cool New Project Name", organization_id) +get_that_project = client.projects().get(project.data.id) + +input("Press Enter to delete the project...") +client.projects().delete([project.data.id]) + +print(client.projects().list(organization_id)) +# -- Example Secret Commands -- + +secret = client.secrets().create("TEST_SECRET", "This is a test secret", organization_id, "Secret1234!", [project2.data.id]) +secret2 = client.secrets().create("Secret - Don't Delete Me!", "This is a test secret that will stay", organization_id, "Secret1234!", [project2.data.id]) +secret_updated = client.secrets().update(secret.data.id, "TEST_SECRET_UPDATED", "This as an updated test secret", organization_id, "Secret1234!_updated", [project2.data.id]) +secret_retrieved = client.secrets().get([secret.data.id]) + +input("Press Enter to delete the secret...") client.secrets().delete([secret.data.id]) + +print(client.secrets().list(organization_id))