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/08/25 16:03:05 UTC
(allura) branch cc/8617-part2 created (now 9e018da0b)
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a change to branch cc/8617-part2
in repository https://gitbox.apache.org/repos/asf/allura.git
at 9e018da0b [#8617] Implement field level encryption for tool data fields
This branch includes the following new commits:
new 9e018da0b [#8617] Implement field level encryption for tool data fields
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(allura) 01/01: [#8617] Implement field level encryption for tool data fields
Posted by cc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch cc/8617-part2
in repository https://gitbox.apache.org/repos/asf/allura.git
commit 9e018da0b58c3be6034715108c4ed6b7c716455a
Author: Carlos Cruz <ca...@slashdotmedia.com>
AuthorDate: Tue Aug 25 10:02:50 2026 -0600
[#8617] Implement field level encryption for tool data fields
---
Allura/allura/model/auth.py | 17 +++++++-----
Allura/allura/tests/model/test_auth.py | 25 ++++++++++++++----
.../tests/scripts/test_convert_encrypted_field.py | 30 ++++++++++++++++++++++
3 files changed, 61 insertions(+), 11 deletions(-)
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index cdc1ba90e..a980cbefb 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -786,7 +786,12 @@ def by_username(cls, name):
return plugin.AuthenticationProvider.get(request).by_username(name)
def get_tool_data(self, tool, key, default=None):
- result = self.tool_data.get(tool, {}).get(key, default)
+ tool_data = self.tool_data.get(tool, {})
+ encrypted_key = f'{key}_encrypted'
+ if encrypted_key in tool_data:
+ result = self.decr(tool_data[encrypted_key])
+ else:
+ result = tool_data.get(key, default)
if hasattr(result, '_deinstrument'):
return result._deinstrument()
else:
@@ -794,12 +799,12 @@ def get_tool_data(self, tool, key, default=None):
def set_tool_data(self, tool, *, encrypt=False, **kw):
d = self.tool_data.setdefault(tool, {})
- d.update(kw)
if encrypt:
- d.update({
- f'{key}_encrypted': self.encr(value)
- for key, value in kw.items()
- })
+ for key, value in kw.items():
+ d.pop(key, None)
+ d[f'{key}_encrypted'] = self.encr(value)
+ else:
+ d.update(kw)
state(self).soil()
def address_object(self, addr):
diff --git a/Allura/allura/tests/model/test_auth.py b/Allura/allura/tests/model/test_auth.py
index e11f7418d..6668811ec 100644
--- a/Allura/allura/tests/model/test_auth.py
+++ b/Allura/allura/tests/model/test_auth.py
@@ -494,7 +494,7 @@ def test_preference_email_address_updates_encrypted_field(self):
assert raw_preferences['email_address_encrypted'] == M.User.encr('new@example.com')
assert M.User.decr(raw_preferences['email_address_encrypted']) == 'new@example.com'
- def test_set_tool_data_can_dual_write_encrypted_values(self):
+ def test_set_tool_data_stores_only_encrypted_values(self):
user = M.User(
username='tool-data-encrypted-setter-test',
tool_data={'sfx': {'unrelated': 'keep'}},
@@ -511,18 +511,33 @@ def test_set_tool_data_can_dual_write_encrypted_values(self):
ThreadLocalODMSession.flush_all()
tool_data = state(user).document['tool_data']
- assert tool_data['sfx']['registration_ip'] == '198.51.100.12'
+ assert user.get_tool_data('sfx', 'registration_ip') == '198.51.100.12'
+ assert user.get_tool_data('sfx', 'registration_rdns') is None
+ assert user.get_tool_data('sfx', 'normalized_email') == ''
+ assert user.get_tool_data('temp_reg_fields', 'phone_ext') == '104'
+ assert 'registration_ip' not in tool_data['sfx']
assert tool_data['sfx']['registration_ip_encrypted'] == M.User.encr('198.51.100.12')
- assert tool_data['sfx']['registration_rdns'] is None
+ assert 'registration_rdns' not in tool_data['sfx']
assert tool_data['sfx']['registration_rdns_encrypted'] is None
- assert tool_data['sfx']['normalized_email'] == ''
+ assert 'normalized_email' not in tool_data['sfx']
assert tool_data['sfx']['normalized_email_encrypted'] == M.User.encr('')
- assert tool_data['temp_reg_fields']['phone_ext'] == '104'
+ assert 'phone_ext' not in tool_data['temp_reg_fields']
assert tool_data['temp_reg_fields']['phone_ext_encrypted'] == M.User.encr('104')
assert tool_data['sfx']['registration_ua'] == 'TestBrowser/1.0'
assert 'registration_ua_encrypted' not in tool_data['sfx']
assert tool_data['sfx']['unrelated'] == 'keep'
+ def test_get_tool_data_prefers_encrypted_value_over_stale_plaintext(self):
+ user = M.User(
+ username='tool-data-encrypted-getter-test',
+ tool_data={'sfx': {
+ 'registration_ip': '192.0.2.1',
+ 'registration_ip_encrypted': M.User.encr('198.51.100.12'),
+ }},
+ )
+
+ assert user.get_tool_data('sfx', 'registration_ip') == '198.51.100.12'
+
def test_personal_data_fields_are_stored_encrypted_on_creation(self):
user = M.User(
username='personal-data-encrypted-create-test',
diff --git a/Allura/allura/tests/scripts/test_convert_encrypted_field.py b/Allura/allura/tests/scripts/test_convert_encrypted_field.py
index 1af6d4e88..f9811aca4 100644
--- a/Allura/allura/tests/scripts/test_convert_encrypted_field.py
+++ b/Allura/allura/tests/scripts/test_convert_encrypted_field.py
@@ -20,11 +20,15 @@
from ming import schema as S
from ming.encryption import NestedEncryptedProperty
from ming.odm import FieldProperty
+from ming.odm.odmsession import ThreadLocalODMSession
+from allura import model as M
+from alluratest.controller import setup_basic_test
from scripts.convert_encrypted_field import (
_encryption_schema_info,
_encrypt_nested_array_value,
_remove_nested_array_value,
+ main,
)
@@ -92,6 +96,32 @@ def test_encryption_schema_info_rejects_dynamic_field_without_opt_in():
)
+def test_remove_unencrypted_dynamic_field_preserves_ciphertext():
+ setup_basic_test()
+ user = M.User(
+ username='dynamic-field-conversion-test',
+ tool_data={'sfx': {
+ 'registration_ip': '198.51.100.12',
+ 'registration_ip_encrypted': M.User.encr('198.51.100.12'),
+ }},
+ )
+ ThreadLocalODMSession.flush_all()
+ user_id = user._id
+
+ main(
+ 'allura.model.auth.User',
+ 'tool_data.sfx.registration_ip',
+ remove_unencrypted=True,
+ encrypt_dynamic_field=True,
+ )
+ ThreadLocalODMSession.close_all()
+
+ user = M.User.query.get(_id=user_id)
+ raw_sfx = user.tool_data.sfx
+ assert 'registration_ip' not in raw_sfx
+ assert raw_sfx.registration_ip_encrypted == M.User.encr('198.51.100.12')
+
+
def test_encrypt_nested_array_value_updates_every_item():
socialnetworks = [
{'socialnetwork': 'Mastodon', 'accounturl': 'https://example.com/@user'},