From 976a055441538a13859db7789179cfd4f0f2bcdc Mon Sep 17 00:00:00 2001 From: Eric Jolibois Date: Wed, 18 Oct 2023 18:42:56 +0200 Subject: [PATCH] feat(google credentials): `private_key_id` and `private_key` are secrets --- tests/test_google_credentials.py | 12 +++++++++++- toucan_connectors/google_credentials.py | 17 +++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/tests/test_google_credentials.py b/tests/test_google_credentials.py index b6c2fc910..60b32c799 100644 --- a/tests/test_google_credentials.py +++ b/tests/test_google_credentials.py @@ -1,3 +1,5 @@ +import json + from pytest_mock import MockFixture from toucan_connectors.google_credentials import GoogleCredentials, get_google_oauth2_credentials @@ -17,6 +19,14 @@ def test_google_credentials(mocker: MockFixture): 'client_x509_cert_url': 'https://www.googleapis.com/robot/v1/metadata/x509/xxx.iam.gserviceaccount.com', # noqa: E501 } credentials = GoogleCredentials(**conf) + # Ensure `private_key_id` and `private_key` are masked + assert credentials.json() == json.dumps( + { + **conf, + 'private_key_id': '**********', + 'private_key': '**********', + } + ) # Ensure `Credentials` is called with the right values of secrets mock_credentials = mocker.patch('toucan_connectors.google_credentials.Credentials') get_google_oauth2_credentials(credentials) @@ -38,7 +48,7 @@ def test_unespace_break_lines(): } credentials = GoogleCredentials(**conf) assert ( - credentials.private_key == '-----BEGIN PRIVATE KEY-----\n' + credentials.private_key.get_secret_value() == '-----BEGIN PRIVATE KEY-----\n' 'aaa\n' 'bbb\n' '-----END PRIVATE KEY-----\n' diff --git a/toucan_connectors/google_credentials.py b/toucan_connectors/google_credentials.py index fc160b662..429f40eac 100644 --- a/toucan_connectors/google_credentials.py +++ b/toucan_connectors/google_credentials.py @@ -1,5 +1,5 @@ from google.oauth2.service_account import Credentials -from pydantic import BaseModel, Field, HttpUrl, validator +from pydantic import BaseModel, Field, HttpUrl, SecretStr, validator CREDENTIALS_INFO_MESSAGE = ( 'This information is provided in your ' @@ -13,8 +13,10 @@ class GoogleCredentials(BaseModel): 'service_account', title='Service account', description=CREDENTIALS_INFO_MESSAGE ) project_id: str = Field(..., title='Project ID', description=CREDENTIALS_INFO_MESSAGE) - private_key_id: str = Field(..., title='Private Key ID', description=CREDENTIALS_INFO_MESSAGE) - private_key: str = Field( + private_key_id: SecretStr = Field( + ..., title='Private Key ID', description=CREDENTIALS_INFO_MESSAGE + ) + private_key: SecretStr = Field( ..., title='Private Key', description=f'A private key in the form ' @@ -44,15 +46,18 @@ class GoogleCredentials(BaseModel): ) @validator('private_key') - def unescape_break_lines(cls, v): + def unescape_break_lines(cls, v: SecretStr) -> SecretStr: """ `private_key` is a long string like '-----BEGIN PRIVATE KEY-----\nxxx...zzz\n-----END PRIVATE KEY-----\n As the breaking line are often escaped by the client, we need to be sure it's unescaped """ - return v.replace('\\n', '\n') + return SecretStr(v.get_secret_value().replace('\\n', '\n')) def get_google_oauth2_credentials(google_credentials: GoogleCredentials) -> Credentials: - return Credentials.from_service_account_info(google_credentials.dict()) + creds = google_credentials.dict() + for secret_field in ('private_key_id', 'private_key'): + creds[secret_field] = creds[secret_field].get_secret_value() + return Credentials.from_service_account_info(creds)