Skip to content

Commit 84d43ad

Browse files
author
Jon Wayne Parrott
authored
Refactor cloud client storage samples. (#421)
1 parent 848b086 commit 84d43ad

File tree

6 files changed

+251
-147
lines changed

6 files changed

+251
-147
lines changed

storage/cloud-client/customer_supplied_keys.py

Lines changed: 0 additions & 107 deletions
This file was deleted.

storage/cloud-client/customer_supplied_keys_test.py

Lines changed: 0 additions & 24 deletions
This file was deleted.

storage/cloud-client/encryption.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2016 Google, Inc.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
"""This application demonstrates how to upload and download encrypted blobs
18+
(objects) in Google Cloud Storage.
19+
20+
Use `generate-encryption-key` to generate an example key:
21+
22+
python encryption.py generate-encryption-key
23+
24+
Then use the key to upload and download files encrypted with a custom key.
25+
26+
For more information, see the README.md under /storage and the documentation
27+
at https://cloud.google.com/storage/docs/encryption.
28+
"""
29+
30+
import argparse
31+
import base64
32+
import os
33+
34+
from gcloud import storage
35+
36+
37+
def generate_encryption_key():
38+
"""Generates a 256 bit (32 byte) AES encryption key and prints the
39+
base64 representation.
40+
41+
This is included for demonstration purposes. You should generate your own
42+
key. Please remember that encryption keys should be handled with a
43+
comprehensive security policy.
44+
"""
45+
key = os.urandom(32)
46+
encoded_key = base64.b64encode(key).decode('utf-8')
47+
print('Base 64 encoded encryption key: {}'.format(encoded_key))
48+
49+
50+
def upload_encrypted_blob(bucket_name, source_file_name,
51+
destination_blob_name, base64_encryption_key):
52+
"""Uploads a file to a Google Cloud Storage bucket using a custom
53+
encryption key.
54+
55+
The file will be encrypted by Google Cloud Storage and only
56+
retrievable using the provided encryption key.
57+
"""
58+
storage_client = storage.Client()
59+
bucket = storage_client.get_bucket(bucket_name)
60+
blob = bucket.blob(destination_blob_name)
61+
62+
# Encryption key must be an AES256 key represented as a bytestring with
63+
# 32 bytes. Since it's passed in as a base64 encoded string, it needs
64+
# to be decoded.
65+
encryption_key = base64.b64decode(base64_encryption_key)
66+
67+
blob.upload_from_filename(
68+
source_file_name, encryption_key=encryption_key)
69+
70+
print('File {} uploaded to {}.'.format(
71+
source_file_name,
72+
destination_blob_name))
73+
74+
75+
def download_encrypted_blob(bucket_name, source_blob_name,
76+
destination_file_name, base64_encryption_key):
77+
"""Downloads a previously-encrypted blob from Google Cloud Storage.
78+
79+
The encryption key provided must be the same key provided when uploading
80+
the blob.
81+
"""
82+
storage_client = storage.Client()
83+
bucket = storage_client.get_bucket(bucket_name)
84+
blob = bucket.blob(source_blob_name)
85+
86+
# Encryption key must be an AES256 key represented as a bytestring with
87+
# 32 bytes. Since it's passed in as a base64 encoded string, it needs
88+
# to be decoded.
89+
encryption_key = base64.b64decode(base64_encryption_key)
90+
91+
blob.download_to_filename(
92+
destination_file_name, encryption_key=encryption_key)
93+
94+
print('Blob {} downloaded to {}.'.format(
95+
source_blob_name,
96+
destination_file_name))
97+
98+
99+
def rotate_encryption_key(bucket_name, blob_name, base64_encryption_key,
100+
base64_new_encryption_key):
101+
"""Performs a key rotation by re-writing an encrypted blob with a new
102+
encryption key."""
103+
raise NotImplementedError(
104+
'This is currently not available using the Cloud Client Library.')
105+
106+
107+
if __name__ == '__main__':
108+
parser = argparse.ArgumentParser(
109+
description=__doc__,
110+
formatter_class=argparse.RawDescriptionHelpFormatter)
111+
subparsers = parser.add_subparsers(dest='command')
112+
113+
subparsers.add_parser(
114+
'generate-encryption-key', help=generate_encryption_key.__doc__)
115+
116+
upload_parser = subparsers.add_parser(
117+
'upload', help=upload_encrypted_blob.__doc__)
118+
upload_parser.add_argument(
119+
'bucket_name', help='Your cloud storage bucket.')
120+
upload_parser.add_argument('source_file_name')
121+
upload_parser.add_argument('destination_blob_name')
122+
upload_parser.add_argument('base64_encryption_key')
123+
124+
download_parser = subparsers.add_parser(
125+
'download', help=download_encrypted_blob.__doc__)
126+
download_parser.add_argument(
127+
'bucket_name', help='Your cloud storage bucket.')
128+
download_parser.add_argument('source_blob_name')
129+
download_parser.add_argument('destination_file_name')
130+
download_parser.add_argument('base64_encryption_key')
131+
132+
rotate_parser = subparsers.add_parser(
133+
'rotate', help=rotate_encryption_key.__doc__)
134+
rotate_parser.add_argument(
135+
'bucket_name', help='Your cloud storage bucket.')
136+
download_parser.add_argument('blob_name')
137+
download_parser.add_argument('base64_encryption_key')
138+
download_parser.add_argument('base64_new_encryption_key')
139+
140+
args = parser.parse_args()
141+
142+
if args.command == 'generate-encryption-key':
143+
generate_encryption_key()
144+
elif args.command == 'upload':
145+
upload_encrypted_blob(
146+
args.bucket_name,
147+
args.source_file_name,
148+
args.destination_blob_name,
149+
args.base64_encryption_key)
150+
elif args.command == 'download':
151+
download_encrypted_blob(
152+
args.bucket_name,
153+
args.source_blob_name,
154+
args.destination_file_name,
155+
args.base64_encryption_key)
156+
elif args.command == 'rotate':
157+
rotate_encryption_key(
158+
args.bucket_name,
159+
args.blob_name,
160+
args.base64_encryption_key,
161+
args.base64_new_encryption_key)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Copyright 2016 Google, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import base64
16+
import tempfile
17+
18+
import encryption
19+
from gcloud import storage
20+
import pytest
21+
22+
TEST_ENCRYPTION_KEY = 'brtJUWneL92g5q0N2gyDSnlPSYAiIVZ/cWgjyZNeMy0='
23+
TEST_ENCRYPTION_KEY_DECODED = base64.b64decode(TEST_ENCRYPTION_KEY)
24+
25+
26+
def test_generate_encryption_key(capsys):
27+
encryption.generate_encryption_key()
28+
out, _ = capsys.readouterr()
29+
encoded_key = out.split(':', 1).pop().strip()
30+
key = base64.b64decode(encoded_key)
31+
assert len(key) == 32, 'Returned key should be 32 bytes'
32+
33+
34+
def test_upload_encrypted_blob(cloud_config):
35+
with tempfile.NamedTemporaryFile() as source_file:
36+
source_file.write(b'test')
37+
38+
encryption.upload_encrypted_blob(
39+
cloud_config.storage_bucket,
40+
source_file.name,
41+
'test_encrypted_upload_blob',
42+
TEST_ENCRYPTION_KEY)
43+
44+
45+
@pytest.fixture
46+
def test_blob(cloud_config):
47+
"""Provides a pre-existing blob in the test bucket."""
48+
bucket = storage.Client().bucket(cloud_config.storage_bucket)
49+
blob = bucket.blob('encrption_test_sigil')
50+
content = 'Hello, is it me you\'re looking for?'
51+
blob.upload_from_string(
52+
content,
53+
encryption_key=TEST_ENCRYPTION_KEY_DECODED)
54+
return blob.name, content
55+
56+
57+
def test_download_blob(test_blob, cloud_config):
58+
test_blob_name, test_blob_content = test_blob
59+
with tempfile.NamedTemporaryFile() as dest_file:
60+
encryption.download_encrypted_blob(
61+
cloud_config.storage_bucket,
62+
test_blob_name,
63+
dest_file.name,
64+
TEST_ENCRYPTION_KEY)
65+
66+
downloaded_content = dest_file.read().decode('utf-8')
67+
assert downloaded_content == test_blob_content

0 commit comments

Comments
 (0)