Skip to content

Commit

Permalink
fix: allow signed post policy v4 with service account and token (#1356)
Browse files Browse the repository at this point in the history
  • Loading branch information
cojenco authored Oct 4, 2024
1 parent cea20e2 commit 8ec02c0
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 2 deletions.
7 changes: 5 additions & 2 deletions google/cloud/storage/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1724,13 +1724,16 @@ def generate_signed_post_policy_v4(
)

credentials = self._credentials if credentials is None else credentials
ensure_signed_credentials(credentials)
client_email = service_account_email
if not access_token or not service_account_email:
ensure_signed_credentials(credentials)
client_email = credentials.signer_email

# prepare policy conditions and fields
timestamp, datestamp = get_v4_now_dtstamps()

x_goog_credential = "{email}/{datestamp}/auto/storage/goog4_request".format(
email=credentials.signer_email, datestamp=datestamp
email=client_email, datestamp=datestamp
)
required_conditions = [
{"bucket": bucket_name},
Expand Down
49 changes: 49 additions & 0 deletions tests/system/test__signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,55 @@ def test_generate_signed_post_policy_v4(
assert blob.download_as_bytes() == payload


@pytest.mark.skipif(
_helpers.is_api_endpoint_override,
reason="Test does not yet support endpoint override",
)
def test_generate_signed_post_policy_v4_access_token_sa_email(
storage_client, signing_bucket, blobs_to_delete, service_account, no_mtls
):
client = iam_credentials_v1.IAMCredentialsClient()
service_account_email = service_account.service_account_email
name = path_template.expand(
"projects/{project}/serviceAccounts/{service_account}",
project="-",
service_account=service_account_email,
)
scope = [
"https://www.googleapis.com/auth/devstorage.read_write",
"https://www.googleapis.com/auth/iam",
]
response = client.generate_access_token(name=name, scope=scope)

now = _NOW(_UTC).replace(tzinfo=None)
blob_name = "post_policy_obj_email2.txt"
payload = b"DEADBEEF"
with open(blob_name, "wb") as f:
f.write(payload)
policy = storage_client.generate_signed_post_policy_v4(
signing_bucket.name,
blob_name,
conditions=[
{"bucket": signing_bucket.name},
["starts-with", "$Content-Type", "text/pla"],
],
expiration=now + datetime.timedelta(hours=1),
fields={"content-type": "text/plain"},
service_account_email=service_account_email,
access_token=response.access_token,
)
with open(blob_name, "r") as f:
files = {"file": (blob_name, f)}
response = requests.post(policy["url"], data=policy["fields"], files=files)

os.remove(blob_name)
assert response.status_code == 204

blob = signing_bucket.get_blob(blob_name)
blobs_to_delete.append(blob)
assert blob.download_as_bytes() == payload


def test_generate_signed_post_policy_v4_invalid_field(
storage_client, buckets_to_delete, blobs_to_delete, service_account, no_mtls
):
Expand Down
44 changes: 44 additions & 0 deletions tests/unit/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2849,6 +2849,50 @@ def test_get_signed_policy_v4_with_access_token(self):
self.assertEqual(fields["x-goog-signature"], EXPECTED_SIGN)
self.assertEqual(fields["policy"], EXPECTED_POLICY)

def test_get_signed_policy_v4_with_access_token_sa_email(self):
import datetime

BUCKET_NAME = "bucket-name"
BLOB_NAME = "object-name"
EXPECTED_SIGN = "0c4003044105"
EXPECTED_POLICY = "eyJjb25kaXRpb25zIjpbeyJidWNrZXQiOiJidWNrZXQtbmFtZSJ9LHsiYWNsIjoicHJpdmF0ZSJ9LFsic3RhcnRzLXdpdGgiLCIkQ29udGVudC1UeXBlIiwidGV4dC9wbGFpbiJdLHsiYnVja2V0IjoiYnVja2V0LW5hbWUifSx7ImtleSI6Im9iamVjdC1uYW1lIn0seyJ4LWdvb2ctZGF0ZSI6IjIwMjAwMzEyVDExNDcxNloifSx7IngtZ29vZy1jcmVkZW50aWFsIjoidGVzdEBtYWlsLmNvbS8yMDIwMDMxMi9hdXRvL3N0b3JhZ2UvZ29vZzRfcmVxdWVzdCJ9LHsieC1nb29nLWFsZ29yaXRobSI6IkdPT0c0LVJTQS1TSEEyNTYifV0sImV4cGlyYXRpb24iOiIyMDIwLTAzLTI2VDAwOjAwOjEwWiJ9"

project = "PROJECT"
credentials = _make_credentials(project=project)
client = self._make_one(credentials=credentials)

dtstamps_patch, now_patch, expire_secs_patch = _time_functions_patches()
with dtstamps_patch, now_patch, expire_secs_patch:
with mock.patch(
"google.cloud.storage.client._sign_message", return_value=b"DEADBEEF"
):
policy = client.generate_signed_post_policy_v4(
BUCKET_NAME,
BLOB_NAME,
expiration=datetime.datetime(2020, 3, 12),
conditions=[
{"bucket": BUCKET_NAME},
{"acl": "private"},
["starts-with", "$Content-Type", "text/plain"],
],
service_account_email="test@mail.com",
access_token="token",
)
self.assertEqual(
policy["url"], "https://storage.googleapis.com/" + BUCKET_NAME + "/"
)
fields = policy["fields"]

self.assertEqual(fields["key"], BLOB_NAME)
self.assertEqual(fields["x-goog-algorithm"], "GOOG4-RSA-SHA256")
self.assertEqual(fields["x-goog-date"], "20200312T114716Z")
self.assertEqual(
fields["x-goog-credential"],
"test@mail.com/20200312/auto/storage/goog4_request",
)
self.assertEqual(fields["x-goog-signature"], EXPECTED_SIGN)
self.assertEqual(fields["policy"], EXPECTED_POLICY)


class Test__item_to_bucket(unittest.TestCase):
def _call_fut(self, iterator, item):
Expand Down

0 comments on commit 8ec02c0

Please sign in to comment.