Skip to content

Commit 697a0e0

Browse files
authored
feat: add functionality to hash data (#1677)
* feat: add functionality to hash data * change sensitive fields to private * update to sha512 * update docstring
1 parent 446c8e7 commit 697a0e0

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

google/auth/_helpers.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import calendar
1919
import datetime
2020
from email.message import Message
21+
import hashlib
2122
import sys
2223
import urllib
2324

@@ -27,6 +28,7 @@
2728
# expiry.
2829
REFRESH_THRESHOLD = datetime.timedelta(minutes=3, seconds=45)
2930

31+
_SENSITIVE_FIELDS = {"accessToken", "access_token", "id_token", "client_id", "refresh_token", "client_secret"}
3032

3133
def copy_docstring(source_class):
3234
"""Decorator that copies a method's docstring from another class.
@@ -271,3 +273,33 @@ def is_python_3():
271273
bool: True if the Python interpreter is Python 3 and False otherwise.
272274
"""
273275
return sys.version_info > (3, 0)
276+
277+
278+
def hash_sensitive_info(data: dict) -> dict:
279+
"""
280+
Hashes sensitive information within a dictionary.
281+
282+
Args:
283+
data: The dictionary containing data to be processed.
284+
285+
Returns:
286+
A new dictionary with sensitive values replaced by their SHA512 hashes.
287+
"""
288+
hashed_data = {}
289+
for key, value in data.items():
290+
if key in _SENSITIVE_FIELDS:
291+
hashed_data[key] = _hash_value(value, key)
292+
else:
293+
hashed_data[key] = value
294+
return hashed_data
295+
296+
297+
def _hash_value(value, field_name: str) -> str:
298+
"""Hashes a value and returns a formatted hash string."""
299+
if value is None:
300+
return None
301+
encoded_value = str(value).encode("utf-8")
302+
hash_object = hashlib.sha512()
303+
hash_object.update(encoded_value)
304+
hex_digest = hash_object.hexdigest()
305+
return f"hashed_{field_name}-{hex_digest}"

tests/test__helpers.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,3 +194,66 @@ def test_unpadded_urlsafe_b64encode():
194194

195195
for case, expected in cases:
196196
assert _helpers.unpadded_urlsafe_b64encode(case) == expected
197+
198+
def test_hash_sensitive_info_basic():
199+
test_data = {
200+
"expires_in": 3599,
201+
"access_token": "access-123",
202+
"scope": "https://www.googleapis.com/auth/test-api",
203+
"token_type": "Bearer",
204+
}
205+
hashed_data = _helpers.hash_sensitive_info(test_data)
206+
assert hashed_data["expires_in"] == 3599
207+
assert hashed_data["scope"] == "https://www.googleapis.com/auth/test-api"
208+
assert hashed_data["access_token"].startswith("hashed_access_token-")
209+
assert hashed_data["token_type"] == "Bearer"
210+
211+
def test_hash_sensitive_info_multiple_sensitive():
212+
test_data = {
213+
"access_token": "some_long_token",
214+
"id_token": "1234-5678-9012-3456",
215+
"expires_in": 3599,
216+
"token_type": "Bearer",
217+
}
218+
hashed_data = _helpers.hash_sensitive_info(test_data)
219+
assert hashed_data["expires_in"] == 3599
220+
assert hashed_data["token_type"] == "Bearer"
221+
assert hashed_data["access_token"].startswith("hashed_access_token-")
222+
assert hashed_data["id_token"].startswith("hashed_id_token-")
223+
224+
225+
def test_hash_sensitive_info_none_value():
226+
test_data = {"username": "user3", "secret": None, "normal_data": "abc"}
227+
hashed_data = _helpers.hash_sensitive_info(test_data)
228+
assert hashed_data["secret"] is None
229+
assert hashed_data["normal_data"] == "abc"
230+
231+
232+
def test_hash_sensitive_info_non_string_value():
233+
test_data = {"username": "user4", "access_token": 12345, "normal_data": "def"}
234+
hashed_data = _helpers.hash_sensitive_info(test_data)
235+
assert hashed_data["access_token"].startswith("hashed_access_token-")
236+
assert hashed_data["normal_data"] == "def"
237+
238+
def test_hash_sensitive_info_empty_dict():
239+
test_data = {}
240+
hashed_data = _helpers.hash_sensitive_info(test_data)
241+
assert hashed_data == {}
242+
243+
def test_hash_value_consistent_hashing():
244+
value = "test_value"
245+
field_name = "test_field"
246+
hash1 = _helpers._hash_value(value, field_name)
247+
hash2 = _helpers._hash_value(value, field_name)
248+
assert hash1 == hash2
249+
250+
def test_hash_value_different_hashing():
251+
value1 = "test_value1"
252+
value2 = "test_value2"
253+
field_name = "test_field"
254+
hash1 = _helpers._hash_value(value1, field_name)
255+
hash2 = _helpers._hash_value(value2, field_name)
256+
assert hash1 != hash2
257+
258+
def test_hash_value_none():
259+
assert _helpers._hash_value(None, "test") is None

0 commit comments

Comments
 (0)