You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2020/07/29 20:27:23 UTC
[allura] 01/01: [#8372] misc site admin improvements:
This is an automated email from the ASF dual-hosted git repository.
brondsem pushed a commit to branch db/8372
in repository https://gitbox.apache.org/repos/asf/allura.git
commit 9facc398ddb839b7d3db22f464061c2ba65a77be
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Wed Jul 29 16:24:25 2020 -0400
[#8372] misc site admin improvements:
* admin search by usernames & project shortnames should handle uppercase
* on user-profile page, show site admin link
* allow site admins to view disabled user-profile pages
* more notes on user admin details pages
---
Allura/allura/controllers/rest.py | 7 +++++--
Allura/allura/controllers/site_admin.py | 3 +++
.../allura/ext/user_profile/templates/user_index.html | 14 ++++++++++++++
Allura/allura/lib/helpers.py | 2 +-
Allura/allura/lib/plugin.py | 17 +++++++++++------
Allura/allura/lib/security.py | 7 +++++++
Allura/allura/model/project.py | 12 ++++++++++--
Allura/allura/templates/site_admin.html | 2 +-
Allura/allura/templates/site_admin_new_projects.html | 1 +
Allura/allura/templates/site_admin_user_details.html | 16 +++++++++++++++-
.../override/allura/templates/site_admin.html | 2 +-
Allura/allura/tests/functional/test_neighborhood.py | 4 +++-
.../forgeactivity/tests/functional/test_root.py | 5 ++---
13 files changed, 74 insertions(+), 18 deletions(-)
diff --git a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
index 6072c6f..2686a54 100644
--- a/Allura/allura/controllers/rest.py
+++ b/Allura/allura/controllers/rest.py
@@ -331,8 +331,11 @@ def nbhd_lookup_first_path(nbhd, name, current_user, remainder, api=False):
return project, (pname,) + remainder # include pname in new remainder, it is actually the nbhd tool path
if project and prefix == 'u/':
# make sure user-projects are associated with an enabled user
- user = project.user_project_of
- if not user or user.disabled or user.pending:
+ is_site_admin = h.is_site_admin(c.user)
+ user = project.get_userproject_user(include_disabled=is_site_admin)
+ if not user or user.pending:
+ raise exc.HTTPNotFound
+ if user.disabled and not is_site_admin:
raise exc.HTTPNotFound
if not api and user.url() != '/{}{}/'.format(prefix, pname):
# might be different URL than the URL requested
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index 7def94e..d70e7c6 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -272,6 +272,9 @@ class SiteAdminController(object):
objects = []
limit, page, start = g.handle_paging(limit, page, default=25)
if q:
+ if f in ('username', 'shortname'):
+ # these are always lowercase, so search by lowercase
+ q = q.lower()
match = search.site_admin_search(model, q, f, rows=limit, start=start)
if match:
count = match.hits
diff --git a/Allura/allura/ext/user_profile/templates/user_index.html b/Allura/allura/ext/user_profile/templates/user_index.html
index 9d448c7..17db505 100644
--- a/Allura/allura/ext/user_profile/templates/user_index.html
+++ b/Allura/allura/ext/user_profile/templates/user_index.html
@@ -47,6 +47,20 @@
{% block top_nav %}{# disabled #}{% endblock %}
{% block content_base %}
+ {% if h.is_site_admin(c.user) %}
+ {# maybe could be a regular user profile "section" but would need a way to make it conditionally display #}
+ <div class="profile-section tools">
+ <h3 style="background: darkorange">Site Admin</h3>
+ <div class="section-body">
+ <ul>
+ <li><a href="/nf/admin/user/{{ user.username }}">Admin details page for {{ user.username }}</a></li>
+ {% if user.disabled %}
+ <li>User is <strong>DISABLED</strong>. <span style="color:darkorange">This page is only visible because you are a site admin. Everyone else gets a 404 error page.</span></li>
+ {% endif %}
+ </ul>
+ </div>
+ </div>
+ {% endif %}
{% for section in sections %}
{{ section.display() }}
{% endfor %}
diff --git a/Allura/allura/lib/helpers.py b/Allura/allura/lib/helpers.py
index b664dcf..6b29822 100644
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -72,7 +72,7 @@ from allura.lib import exceptions as exc
from allura.lib import utils
# import to make available to templates, don't delete:
-from .security import has_access, is_allowed_by_role
+from .security import has_access, is_allowed_by_role, is_site_admin
log = logging.getLogger(__name__)
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index bcf6527..2a804f0 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -363,7 +363,7 @@ class AuthenticationProvider(object):
# default implementation for any providers that haven't implemented this newer method yet
return '/{}/'.format(self.user_project_shortname(user))
- def user_by_project_shortname(self, shortname):
+ def user_by_project_shortname(self, shortname, include_disabled=False):
'''
:param str: shortname
:rtype: user: a :class:`User <allura.model.auth.User>`
@@ -592,12 +592,17 @@ class LocalAuthenticationProvider(AuthenticationProvider):
# (nbhd_lookup_first_path will figure it out)
return '/u/{}/'.format(user.username)
- def user_by_project_shortname(self, shortname):
+ def user_by_project_shortname(self, shortname, include_disabled=False):
from allura import model as M
- user = M.User.query.get(username=shortname, disabled=False, pending=False)
+ filters = {
+ 'pending': False,
+ }
+ if not include_disabled:
+ filters['disabled'] = False
+ user = M.User.query.get(username=shortname, **filters)
if not user and '-' in shortname:
# try alternate version in case username & user-project shortname differ - see user_project_shortname()
- user = M.User.query.get(username=shortname.replace('-', '_'), disabled=False, pending=False)
+ user = M.User.query.get(username=shortname.replace('-', '_'), **filters)
return user
def update_notifications(self, user):
@@ -797,8 +802,8 @@ class LdapAuthenticationProvider(AuthenticationProvider):
def user_project_shortname(self, user):
return LocalAuthenticationProvider(None).user_project_shortname(user)
- def user_by_project_shortname(self, shortname):
- return LocalAuthenticationProvider(None).user_by_project_shortname(shortname)
+ def user_by_project_shortname(self, shortname, **kwargs):
+ return LocalAuthenticationProvider(None).user_by_project_shortname(shortname, **kwargs)
def user_registration_date(self, user):
# could read this from an LDAP field?
diff --git a/Allura/allura/lib/security.py b/Allura/allura/lib/security.py
index 3969ea8..4ff62f7 100644
--- a/Allura/allura/lib/security.py
+++ b/Allura/allura/lib/security.py
@@ -498,8 +498,15 @@ def require_authenticated():
def is_site_admin(user):
+ '''
+ :param allura.model.User user:
+ :rtype: bool
+ '''
from allura.lib import helpers as h
+ if user.is_anonymous():
+ return False
+
with h.push_context(tg.config.get('site_admin_project', 'allura'),
neighborhood=tg.config.get('site_admin_project_nbhd', 'Projects')):
return has_access(c.project, 'admin', user=user)
diff --git a/Allura/allura/model/project.py b/Allura/allura/model/project.py
index 5c8ca1b..c073186 100644
--- a/Allura/allura/model/project.py
+++ b/Allura/allura/model/project.py
@@ -464,10 +464,18 @@ class Project(SearchIndexable, MappedClass, ActivityNode, ActivityObject):
'''
If this is a user-project, return the User, else None
'''
+ return self.get_userproject_user()
+
+ def get_userproject_user(self, include_disabled=False):
+ '''
+ If this is a user-project, return the User, else None
+ '''
user = None
if self.is_user_project:
- user = plugin.AuthenticationProvider.get(
- request).user_by_project_shortname(self.shortname[2:])
+ user = plugin.AuthenticationProvider.get(request).user_by_project_shortname(
+ self.shortname[2:], # strip leading u/ prefix
+ include_disabled=include_disabled,
+ )
return user
@LazyProperty
diff --git a/Allura/allura/templates/site_admin.html b/Allura/allura/templates/site_admin.html
index 3670285..1a4dbb5 100644
--- a/Allura/allura/templates/site_admin.html
+++ b/Allura/allura/templates/site_admin.html
@@ -25,7 +25,7 @@
{% block header %}Forge Site Admin{% endblock %}
{% block nav_menu %}
- <h1 class="project_title">Site Admin</h1>
+ <h1 class="project_title"><a href="/nf/admin/">Site Admin</a></h1>
{% endblock %}
{% block sidebar_menu %}
diff --git a/Allura/allura/templates/site_admin_new_projects.html b/Allura/allura/templates/site_admin_new_projects.html
index aa366fa..eb2d931 100644
--- a/Allura/allura/templates/site_admin_new_projects.html
+++ b/Allura/allura/templates/site_admin_new_projects.html
@@ -22,6 +22,7 @@
{% extends 'allura:templates/site_admin.html' %}
{% block title %}New Projects{% endblock %}
+{% block header %}New Projects{% endblock %}
{% macro _paging() %}
<div class="paging">
diff --git a/Allura/allura/templates/site_admin_user_details.html b/Allura/allura/templates/site_admin_user_details.html
index d491307..0f4d34a 100644
--- a/Allura/allura/templates/site_admin_user_details.html
+++ b/Allura/allura/templates/site_admin_user_details.html
@@ -44,7 +44,14 @@
<form action='/nf/admin/user/set_status' method="POST">
Account Status:<br>
<label><input type="radio" name="status" value="enable"{% if status == 'enabled' %} checked="checked"{% endif %}>Enabled</label><br>
- <label><input type="radio" name="status" value="disable"{% if status == 'disabled' %} checked="checked"{% endif %}>Disabled</label><br>
+ <label><input type="radio" name="status" value="disable"{% if status == 'disabled' %} checked="checked"{% endif %}>
+ {%- if status == 'disabled' -%}
+ <strong>Disabled</strong>
+ (see Audit Log below)
+ {%- else -%}
+ Disabled
+ {%- endif -%}
+ </label><br>
<label><input type="radio" name="status" value="pending"{% if status == 'pending' %} checked="checked"{% endif %}>Pending</label>
<input type='hidden' name='username' value='{{ user.username }}'>
{{lib.csrf_token()}}
@@ -152,6 +159,13 @@
<li><a href="{{ p.url() }}">{{p.name}}</a>
<span class="spacer">–</span>
<a href="{{ p.url() }}admin/">Admin</a>
+ {% if not p.is_root %}
+ (subproject)
+ {% elif p.is_user_project %}
+ (user profile project)
+ {% elif p.is_nbhd_project %}
+ (neighborhood)
+ {% endif %}
</li>
{% endfor %}
</ul>
diff --git a/Allura/allura/templates_responsive/override/allura/templates/site_admin.html b/Allura/allura/templates_responsive/override/allura/templates/site_admin.html
index 3d13608..926e7f3 100644
--- a/Allura/allura/templates_responsive/override/allura/templates/site_admin.html
+++ b/Allura/allura/templates_responsive/override/allura/templates/site_admin.html
@@ -30,7 +30,7 @@
{% block header %}Forge Site Admin{% endblock %}
{% block nav_menu %}
- <h1 class="project_title">Site Admin</h1>
+ <h1 class="project_title"><a href="/nf/admin/">Site Admin</a></h1>
{% endblock %}
{% block sidebar_menu %}
diff --git a/Allura/allura/tests/functional/test_neighborhood.py b/Allura/allura/tests/functional/test_neighborhood.py
index 1f7034c..a302ec1 100644
--- a/Allura/allura/tests/functional/test_neighborhood.py
+++ b/Allura/allura/tests/functional/test_neighborhood.py
@@ -951,7 +951,9 @@ class TestNeighborhood(TestController):
self.app.get('/u/donald-duck/') # assert it's there
M.User.query.update(dict(username='donald-duck'),
{'$set': {'disabled': True}})
- self.app.get('/u/donald-duck/', status=404)
+ self.app.get('/u/donald-duck/', status=404, extra_environ={'username': str('*anonymous')})
+ self.app.get('/u/donald-duck/', status=404, extra_environ={'username': str('test-user')})
+ self.app.get('/u/donald-duck/', status=302, extra_environ={'username': str('test-admin')}) # site admin user
def test_more_projects_link(self):
r = self.app.get('/adobe/adobe-1/admin/')
diff --git a/ForgeActivity/forgeactivity/tests/functional/test_root.py b/ForgeActivity/forgeactivity/tests/functional/test_root.py
index 7567004..1213ca7 100644
--- a/ForgeActivity/forgeactivity/tests/functional/test_root.py
+++ b/ForgeActivity/forgeactivity/tests/functional/test_root.py
@@ -58,9 +58,8 @@ class TestActivityController(TestController):
def test_anon_read(self):
r = self.app.get('/u/test-user-1',
extra_environ={'username': str('*anonymous')}).follow().follow()
- assert r.html.find('div', 'profile-section tools').find('a',
- dict(href='/u/test-user-1/activity/')), \
- 'No Activity tool in top nav'
+ assert r.html.select('div.profile-section.tools a[href="/u/test-user-1/activity/"]'),\
+ 'No Activity tool in top nav'
@td.with_tool('test', 'activity')
@patch('forgeactivity.main.g.director')