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/05/11 20:11:34 UTC
(allura) branch cc/11102-part2a created (now 5d3ddcb56)
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a change to branch cc/11102-part2a
in repository https://gitbox.apache.org/repos/asf/allura.git
at 5d3ddcb56 [#8604] Update transient anonymous user property to not use encrypted field
This branch includes the following new commits:
new 1acb3055f [#8604] Add the display_name_encrypted field to the User class
new 02e3d1d53 [#8604] Implement field level encryption for User's display_name_field
new 8080ba0b2 Remove dead ProjectUserSelect code
new 5d3ddcb56 [#8604] Update transient anonymous user property to not use encrypted field
The 4 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/04: [#8604] Add the display_name_encrypted field to the User class
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/11102-part2a
in repository https://gitbox.apache.org/repos/asf/allura.git
commit 1acb3055fc0a5afb4fce8ca937c04567bd971edf
Author: Carlos Cruz <ca...@slashdotmedia.com>
AuthorDate: Fri Apr 24 11:21:54 2026 -0600
[#8604] Add the display_name_encrypted field to the User class
---
Allura/allura/model/auth.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index b3eee63fc..1f44966e5 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -294,6 +294,7 @@ class __mongometa__:
# Additional top-level fields can/should be accessed with get/set_pref also
# Not sure why we didn't put them within the 'preferences' dictionary :(
display_name: str = FieldPropertyDisplayName(str)
+ display_name_encrypted = FieldProperty(S.Binary, if_missing=None)
# Personal data
sex = FieldProperty(
S.OneOf('Male', 'Female', 'Other', 'Unknown',
(allura) 04/04: [#8604] Update transient anonymous user property to not use encrypted field
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/11102-part2a
in repository https://gitbox.apache.org/repos/asf/allura.git
commit 5d3ddcb56b331a323f9cd1c45dec09bb690afcd0
Author: Carlos Cruz <ca...@slashdotmedia.com>
AuthorDate: Mon May 11 13:02:39 2026 -0600
[#8604] Update transient anonymous user property to not use encrypted field
---
Allura/allura/model/auth.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index dcd2d151e..f72c3fb9e 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -600,6 +600,8 @@ def stats(self):
return None
def get_pref(self, pref_name):
+ if self.is_anonymous() and pref_name == 'display_name':
+ return 'Anonymous'
return plugin.UserPreferencesProvider.get().get_pref(self, pref_name)
def set_pref(self, pref_name, pref_value):
@@ -889,8 +891,7 @@ def anonymous(cls):
def anonymous_uncached(cls):
anon = cls(
_id=None,
- username='*anonymous',
- display_name='Anonymous')
+ username='*anonymous')
session(anon).expunge(anon) # don't save this transient Anon record
return anon
(allura) 02/04: [#8604] Implement field level encryption for User's display_name_field
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/11102-part2a
in repository https://gitbox.apache.org/repos/asf/allura.git
commit 02e3d1d530527e867225805e01e0a19277c35fbe
Author: Carlos Cruz <ca...@slashdotmedia.com>
AuthorDate: Fri Apr 24 11:23:07 2026 -0600
[#8604] Implement field level encryption for User's display_name_field
---
Allura/allura/lib/plugin.py | 7 +++----
Allura/allura/model/auth.py | 17 +++++++++++++----
Allura/allura/tests/model/test_auth.py | 31 +++++++++++++++++++++++++++++++
3 files changed, 47 insertions(+), 8 deletions(-)
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index 6cd02d63c..21abe14d9 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -1850,10 +1850,9 @@ def get_pref(self, user, pref_name):
if pref_name in user.preferences:
pref_value = user.preferences[pref_name]
elif pref_name == 'display_name':
- # get the value directly from ming's internals, bypassing
- # FieldPropertyDisplayName which always calls back to this get_pref
- # method (infinite recursion)
- pref_value = user.__dict__['__ming__'].state.document.display_name
+ # bypass FieldPropertyDisplayName.__get__, which always calls
+ # back to this get_pref method (infinite recursion)
+ pref_value = type(user).display_name.get_decrypted(user)
else:
pref_value = getattr(user, pref_name)
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index 1f44966e5..dcd2d151e 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -39,7 +39,7 @@
from tg import request
from ming import schema as S
from ming.odm import session, state
-from ming.odm import FieldProperty, RelationProperty, ForeignIdProperty
+from ming.odm import DecryptedProperty, FieldProperty, RelationProperty, ForeignIdProperty
from ming.odm.declarative import MappedClass
from ming.odm.odmsession import ThreadLocalODMSession
from ming.utils import LazyProperty
@@ -234,8 +234,8 @@ def get_next_uid(cls):
return g.next_uid
-class FieldPropertyDisplayName(FieldProperty):
- # display_name is mongo field but only for preference storage
+class FieldPropertyDisplayName(DecryptedProperty):
+ # display_name is stored encrypted in mongo but only for preference storage
# force all requests for this field to use the get_pref mechanism
# Cache it per user, since it may be re-used several times in a request
# and non-local preferences (ldap, database, etc) can be relatively expensive
@@ -249,6 +249,13 @@ def __get__(self, instance, cls=None):
display_name = instance._cache_display_name = instance.get_pref('display_name')
return display_name
+ def __set__(self, instance, value):
+ instance.__dict__.pop('_cache_display_name', None)
+ super().__set__(instance, value)
+
+ def get_decrypted(self, instance):
+ return super().__get__(instance, type(instance))
+
class User(MappedClass, ActivityNode, ActivityObject, SearchIndexable):
SALT_LEN = 8
@@ -293,7 +300,7 @@ class __mongometa__:
))
# Additional top-level fields can/should be accessed with get/set_pref also
# Not sure why we didn't put them within the 'preferences' dictionary :(
- display_name: str = FieldPropertyDisplayName(str)
+ display_name: str = FieldPropertyDisplayName(str, 'display_name_encrypted')
display_name_encrypted = FieldProperty(S.Binary, if_missing=None)
# Personal data
sex = FieldProperty(
@@ -597,6 +604,8 @@ def get_pref(self, pref_name):
def set_pref(self, pref_name, pref_value):
ret = plugin.UserPreferencesProvider.get().set_pref(self, pref_name, pref_value)
+ if pref_name == 'display_name':
+ self.__dict__.pop('_cache_display_name', None)
state(self).soil() # to make sure it gets reindexed in solr (even if pref is not part on the User ming model)
return ret
diff --git a/Allura/allura/tests/model/test_auth.py b/Allura/allura/tests/model/test_auth.py
index 2496c8ab1..4244feada 100644
--- a/Allura/allura/tests/model/test_auth.py
+++ b/Allura/allura/tests/model/test_auth.py
@@ -404,6 +404,37 @@ def test_user_index(self):
# provided bby auth provider
assert 'user_registration_date_dt' in idx
+ def test_display_name_is_stored_encrypted(self):
+ user = M.User(username='encrypted-display-name-test',
+ display_name='Encrypted Display Name')
+ ThreadLocalODMSession.flush_all()
+
+ assert user.display_name == 'Encrypted Display Name'
+ assert user.get_pref('display_name') == 'Encrypted Display Name'
+ assert user.display_name_encrypted
+ assert user.display_name_encrypted != 'Encrypted Display Name'
+ assert 'display_name' not in user.__dict__['__ming__'].state.document
+
+ user = M.User.query.get(
+ display_name_encrypted=M.User.encr('Encrypted Display Name'))
+ assert user.username == 'encrypted-display-name-test'
+
+ def test_set_display_name_pref_updates_encrypted_field_and_cache(self):
+ user = M.User(username='display-name-cache-test',
+ display_name='Original Display Name')
+ ThreadLocalODMSession.flush_all()
+ original_encrypted_display_name = user.display_name_encrypted
+
+ assert user.display_name == 'Original Display Name'
+ assert user._cache_display_name == 'Original Display Name'
+
+ user.set_pref('display_name', 'Updated Display Name')
+
+ assert not hasattr(user, '_cache_display_name')
+ assert user.display_name_encrypted != original_encrypted_display_name
+ assert user.display_name == 'Updated Display Name'
+ assert user.get_pref('display_name') == 'Updated Display Name'
+
def test_user_index_none_values(self):
c.user.email_addresses = [None]
c.user.set_pref('telnumbers', [None])
(allura) 03/04: Remove dead ProjectUserSelect code
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/11102-part2a
in repository https://gitbox.apache.org/repos/asf/allura.git
commit 8080ba0b2ce1ce5aa24b4a9a344b2a93fbe2cc6e
Author: Carlos Cruz <ca...@slashdotmedia.com>
AuthorDate: Fri Apr 24 12:37:36 2026 -0600
Remove dead ProjectUserSelect code
---
Allura/allura/controllers/project.py | 22 ------------
Allura/allura/lib/widgets/form_fields.py | 39 ----------------------
.../templates/widgets/project_user_select.html | 21 ------------
Allura/allura/tests/functional/test_home.py | 19 -----------
.../forgetracker/tests/functional/test_root.py | 2 +-
5 files changed, 1 insertion(+), 102 deletions(-)
diff --git a/Allura/allura/controllers/project.py b/Allura/allura/controllers/project.py
index cf521e59c..23d0d1e74 100644
--- a/Allura/allura/controllers/project.py
+++ b/Allura/allura/controllers/project.py
@@ -42,7 +42,6 @@
from allura.controllers.feed import FeedArgs, FeedController
from allura.controllers.rest import nbhd_lookup_first_path
from allura.lib.security import require_access
-from allura.lib.security import RoleCache
from allura.lib.widgets import forms as ff
from allura.lib.widgets import form_fields as ffw
from allura.lib.widgets import project_list as plw
@@ -448,27 +447,6 @@ def user_icon(self, **kw):
default_image_url = g.forge_static('images/user.png')
redirect(default_image_url)
- @expose('json:')
- def user_search(self, term='', **kw):
- if len(term) < 3:
- raise exc.HTTPBadRequest('"term" param must be at least length 3')
- named_roles = RoleCache(
- g.credentials,
- g.credentials.project_roles(project_id=c.project.root_project._id).named)
- users = M.User.query.find({
- '_id': {'$in': named_roles.userids_that_reach},
- 'display_name': re.compile(r'(?i)%s' % re.escape(term)),
- 'disabled': False,
- 'pending': False,
- }).sort('username').limit(10).all()
- return dict(
- users=[
- dict(
- label='{} ({})'.format(u.get_pref('display_name'), u.username),
- value=u.username,
- id=u.username)
- for u in users])
-
@expose('json:')
def users(self, **kw):
users = c.project.users()
diff --git a/Allura/allura/lib/widgets/form_fields.py b/Allura/allura/lib/widgets/form_fields.py
index 95d04311e..c4fa3cd29 100644
--- a/Allura/allura/lib/widgets/form_fields.py
+++ b/Allura/allura/lib/widgets/form_fields.py
@@ -102,45 +102,6 @@ def resources(self):
''' % dict(url=c.app.url))
-class ProjectUserSelect(ew.InputField):
- template = 'jinja:allura:templates/widgets/project_user_select.html'
- defaults = dict(
- ew.InputField.defaults,
- name=None,
- value=None,
- show_label=True,
- className=None)
-
- def __init__(self, **kw):
- super().__init__(**kw)
- if not isinstance(self.value, list):
- self.value = [self.value]
-
- def from_python(self, value, state=None):
- return value
-
- def resources(self):
- yield from super().resources()
- yield ew.JSLink('allura/js/jquery-ui.min.js', location='body_top_js')
- yield ew.CSSLink('css/autocomplete.css') # customized in [6b78ed] so we can't just use jquery-ui.min.css
- yield onready('''
- $('input.project_user_select').autocomplete({
- source: function (request, response) {
- $.ajax({
- url: "%suser_search",
- dataType: "json",
- data: {
- term: request.term
- },
- success: function (data) {
- response(data.users);
- }
- });
- },
- minLength: 2
- });''' % c.project.url())
-
-
class ProjectUserCombo(ew.SingleSelectField):
template = 'jinja:allura:templates/widgets/project_user_combo.html'
diff --git a/Allura/allura/templates/widgets/project_user_select.html b/Allura/allura/templates/widgets/project_user_select.html
deleted file mode 100644
index 23c434703..000000000
--- a/Allura/allura/templates/widgets/project_user_select.html
+++ /dev/null
@@ -1,21 +0,0 @@
-{#-
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied. See the License for the
- specific language governing permissions and limitations
- under the License.
--#}
-<div>
- <input type="text" id="{{id}}" name="{{name}}" class="ui-autocomplete-input project_user_select{{className and ' %s' % className or ''}}" value="{{value and value.username or value or ''}}"/>
-</div>
diff --git a/Allura/allura/tests/functional/test_home.py b/Allura/allura/tests/functional/test_home.py
index b98c5ba7d..ec3769b59 100644
--- a/Allura/allura/tests/functional/test_home.py
+++ b/Allura/allura/tests/functional/test_home.py
@@ -163,25 +163,6 @@ def test_user_icon(self):
r = self.app.get('/u/test-admin/user_icon')
assert r.content_type == 'image/png'
- def test_user_search(self):
- r = self.app.get('/p/test/user_search?term=test', status=200)
- j = json.loads(r.text)
- assert j['users'][0]['id'].startswith('test')
-
- def test_user_search_for_disabled_user(self):
- user = M.User.by_username('test-admin')
- user.disabled = True
- ThreadLocalODMSession.flush_all()
- r = self.app.get('/p/test/user_search?term=test', status=200)
- j = json.loads(r.text)
- assert j == {'users': []}
-
- def test_user_search_noparam(self):
- self.app.get('/p/test/user_search', status=400)
-
- def test_user_search_shortparam(self):
- self.app.get('/p/test/user_search?term=ad', status=400)
-
def test_users(self):
r = self.app.get('/p/test/users', status=200)
j = json.loads(r.text)
diff --git a/ForgeTracker/forgetracker/tests/functional/test_root.py b/ForgeTracker/forgetracker/tests/functional/test_root.py
index 92259e040..85fb70254 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_root.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_root.py
@@ -270,7 +270,7 @@ def test_deleted_ticket_visibility(self):
class TestFunctionalController(TrackerTestController):
def test_bad_ticket_number(self):
- self.app.get('/bugs/input.project_user_select', status=404)
+ self.app.get('/bugs/not-a-ticket', status=404)
def test_invalid_ticket(self):
self.app.get('/bugs/2/', status=404)