Skip to content

Commit 3619a31

Browse files
authored
feat: use cryptographically secure random for new document ids (#1162)
1 parent bda9865 commit 3619a31

File tree

2 files changed

+22
-3
lines changed

2 files changed

+22
-3
lines changed

packages/google-cloud-firestore/google/cloud/firestore_v1/base_collection.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import datetime
6161

6262
_AUTO_ID_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
63+
system_random = random.SystemRandom()
6364

6465

6566
class BaseCollectionReference(Generic[QueryType]):
@@ -623,8 +624,11 @@ def _auto_id() -> str:
623624
str: A 20 character string composed of digits, uppercase and
624625
lowercase and letters.
625626
"""
626-
627-
return "".join(random.choice(_AUTO_ID_CHARS) for _ in range(20))
627+
try:
628+
return "".join(system_random.choice(_AUTO_ID_CHARS) for _ in range(20))
629+
# Very old Unix systems don't have os.urandom (/dev/urandom), in which case use random.choice
630+
except NotImplementedError:
631+
return "".join(random.choice(_AUTO_ID_CHARS) for _ in range(20))
628632

629633

630634
def _item_to_document_ref(collection_reference, item):

packages/google-cloud-firestore/tests/unit/v1/test_base_collection.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ def test_basecollectionreference_pipeline(mock_query):
437437
assert pipeline == mock_query._build_pipeline.return_value
438438

439439

440-
@mock.patch("random.choice")
440+
@mock.patch("random.SystemRandom.choice")
441441
def test__auto_id(mock_rand_choice):
442442
from google.cloud.firestore_v1.base_collection import _AUTO_ID_CHARS, _auto_id
443443

@@ -450,6 +450,21 @@ def test__auto_id(mock_rand_choice):
450450
assert mock_rand_choice.mock_calls == mock_calls
451451

452452

453+
@mock.patch("random.choice")
454+
@mock.patch("random.SystemRandom.choice")
455+
def test__auto_id_fallback_to_random(mock_system_rand_choice, mock_rand_choice):
456+
from google.cloud.firestore_v1.base_collection import _AUTO_ID_CHARS, _auto_id
457+
458+
mock_system_rand_choice.side_effect = NotImplementedError
459+
mock_result = "0123456789abcdefghij"
460+
mock_rand_choice.side_effect = list(mock_result)
461+
result = _auto_id()
462+
assert result == mock_result
463+
464+
mock_calls = [mock.call(_AUTO_ID_CHARS)] * 20
465+
assert mock_rand_choice.mock_calls == mock_calls
466+
467+
453468
def _make_credentials():
454469
import google.auth.credentials
455470

0 commit comments

Comments
 (0)