You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by cc...@apache.org on 2026/07/07 20:09:47 UTC
(allura) 02/02: [#8613] Implement field level encryption for user preferences email address
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch cc/8613-part2
in repository https://gitbox.apache.org/repos/asf/allura.git
commit e13496f8d2ecad3bca5059832f761b31303e7116
Author: Carlos Cruz <ca...@slashdotmedia.com>
AuthorDate: Tue Jul 7 20:08:57 2026 +0000
[#8613] Implement field level encryption for user preferences email address
---
Allura/allura/model/auth.py | 26 +++-----------------------
Allura/allura/tests/model/test_auth.py | 8 ++++----
2 files changed, 7 insertions(+), 27 deletions(-)
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index 1ee866005..7cf8618d7 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -38,7 +38,8 @@
from tg import tmpl_context as c, app_globals as g
from tg import request
from ming import schema as S
-from ming.odm import session, state, MapperExtension
+from ming.encryption import NestedEncryptedProperty
+from ming.odm import session, state
from ming.odm import FieldProperty, RelationProperty, ForeignIdProperty, DecryptedProperty, DecryptedListProperty
from ming.odm.declarative import MappedClass
from ming.odm.odmsession import ThreadLocalODMSession
@@ -266,15 +267,6 @@ def get_decrypted(self, instance):
return super().__get__(instance, type(instance))
-class UserPreferencesMapperExtension(MapperExtension):
-
- def before_insert(self, obj, state, sess):
- obj.sync_preference_email_address_encrypted()
-
- def before_update(self, obj, state, sess):
- obj.sync_preference_email_address_encrypted()
-
-
class User(MappedClass, ActivityNode, ActivityObject, SearchIndexable):
SALT_LEN = 8
@@ -283,7 +275,6 @@ class __mongometa__:
session = main_orm_session
indexes = ['tool_data.AuthPasswordReset.hash']
unique_indexes = ['username']
- extensions = [UserPreferencesMapperExtension]
custom_indexes = [
dict(fields=('tool_data.phone_verification.number_hash',), sparse=True),
]
@@ -309,9 +300,8 @@ class __mongometa__:
pending = FieldProperty(bool, if_missing=False)
# Don't use these directly, use get/set_pref() instead
- preferences = FieldProperty(dict(
+ preferences = NestedEncryptedProperty(dict(
results_per_page=int,
- email_address=str,
email_address_encrypted=S.Binary,
email_format=str,
disable_user_messages=bool,
@@ -799,16 +789,6 @@ def set_tool_data(self, tool, **kw):
d.update(kw)
state(self).soil()
- @classmethod
- def encrypt_preference_email_address(cls, email_address):
- return cls.encr(email_address)
-
- def sync_preference_email_address_encrypted(self):
- preferences = dict(self.preferences or {})
- preferences['email_address_encrypted'] = type(self).encrypt_preference_email_address(
- preferences.get('email_address'))
- self.preferences = preferences
-
def address_object(self, addr):
return EmailAddress.get(email=addr, claimed_by_user_id=self._id)
diff --git a/Allura/allura/tests/model/test_auth.py b/Allura/allura/tests/model/test_auth.py
index 9ac8a0966..46816fc15 100644
--- a/Allura/allura/tests/model/test_auth.py
+++ b/Allura/allura/tests/model/test_auth.py
@@ -467,7 +467,7 @@ def test_email_addresses_encrypted_is_synced_on_update(self):
assert 'email_addresses' not in state(user).document
assert state(user).document['email_addresses_encrypted'] == expected_encrypted
- def test_preference_email_address_encrypted_is_populated_on_creation(self):
+ def test_preference_email_address_is_stored_encrypted_on_creation(self):
user = M.User(
username='preference-email-address-encrypted-create-test',
preferences={'email_address': 'primary@example.com'})
@@ -475,11 +475,11 @@ def test_preference_email_address_encrypted_is_populated_on_creation(self):
raw_preferences = state(user).document['preferences']
assert user.get_pref('email_address') == 'primary@example.com'
- assert raw_preferences['email_address'] == 'primary@example.com'
+ assert 'email_address' not in raw_preferences
assert raw_preferences['email_address_encrypted'] == M.User.encr('primary@example.com')
assert M.User.decr(raw_preferences['email_address_encrypted']) == 'primary@example.com'
- def test_preference_email_address_encrypted_is_synced_on_update(self):
+ def test_preference_email_address_updates_encrypted_field(self):
user = M.User(
username='preference-email-address-encrypted-update-test',
preferences={'email_address': 'old@example.com'})
@@ -490,7 +490,7 @@ def test_preference_email_address_encrypted_is_synced_on_update(self):
raw_preferences = state(user).document['preferences']
assert user.get_pref('email_address') == 'new@example.com'
- assert raw_preferences['email_address'] == 'new@example.com'
+ assert 'email_address' not in raw_preferences
assert raw_preferences['email_address_encrypted'] == M.User.encr('new@example.com')
assert M.User.decr(raw_preferences['email_address_encrypted']) == 'new@example.com'