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 2014/09/29 18:17:32 UTC

[01/27] git commit: [#7657] ticket:649 Add link to user profile

Repository: allura
Updated Branches:
  refs/heads/db/7657 [created] 69fe0fa7c


[#7657] ticket:649 Add link to user profile


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/143ce803
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/143ce803
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/143ce803

Branch: refs/heads/db/7657
Commit: 143ce803d1ce317aadbfeb1073422c54cc26041c
Parents: e754010
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Sep 12 11:52:25 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:43 2014 +0000

----------------------------------------------------------------------
 Allura/allura/templates/site_admin_user_details.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/143ce803/Allura/allura/templates/site_admin_user_details.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_user_details.html b/Allura/allura/templates/site_admin_user_details.html
index f2cc97d..f86e75c 100644
--- a/Allura/allura/templates/site_admin_user_details.html
+++ b/Allura/allura/templates/site_admin_user_details.html
@@ -29,7 +29,7 @@
       <fieldset>
         <legend>General</legend>
         <ul>
-          <li>Username: {{ user.username }}</li>
+          <li>Username: {{ user.username }} (<a href="{{ user.url() }}">Go to profile page</a>)</li>
           <li>Full name: {{ user.get_pref('display_name') }}</li>
           <li>Registered: {{ user.registration_date() }} ({{ h.ago(user.registration_date()) }})</li>
         </ul>


[18/27] git commit: [#7657] ticket:651 Set random password

Posted by br...@apache.org.
[#7657] ticket:651 Set random password


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/2a5d7de7
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/2a5d7de7
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/2a5d7de7

Branch: refs/heads/db/7657
Commit: 2a5d7de745cb496439714b1aa80451e3050b5563
Parents: c28cf1f
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Sep 17 11:23:57 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 18:30:46 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/site_admin.py              | 12 ++++++++++++
 Allura/allura/lib/helpers.py                         |  6 ++++++
 Allura/allura/templates/site_admin_user_details.html | 15 ++++++++++++---
 Allura/allura/tests/functional/test_site_admin.py    | 10 +++++++++-
 4 files changed, 39 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/2a5d7de7/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index 198207b..ef75906 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -525,6 +525,18 @@ class AdminUserDetailsController(object):
             flash('User disabled')
         redirect(request.referer)
 
+    @expose()
+    @require_post()
+    def set_random_password(self, username=None):
+        user = M.User.by_username(username)
+        if not user or user.is_anonymous():
+            raise HTTPNotFound()
+        pwd = h.random_password()
+        AuthenticationProvider.get(request).set_password(user, None, pwd)
+        h.auditlog_user('Set random password by %s', c.user.username, user=user)
+        flash('Password is set', 'ok')
+        redirect(request.referer)
+
     @h.vardec
     @expose()
     @require_post()

http://git-wip-us.apache.org/repos/asf/allura/blob/2a5d7de7/Allura/allura/lib/helpers.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/helpers.py b/Allura/allura/lib/helpers.py
index dbed410..45ff1fe 100644
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -26,6 +26,8 @@ import urllib2
 import re
 import json
 import logging
+import string
+import random
 import cPickle as pickle
 from hashlib import sha1
 from datetime import datetime, timedelta
@@ -350,6 +352,10 @@ def cryptographic_nonce(length=40):
     return hex_format % tuple(map(ord, os.urandom(length)))
 
 
+def random_password(length=20, chars=string.ascii_uppercase + string.digits):
+    return ''.join(random.choice(chars) for x in range(length))
+
+
 def ago(start_time, show_date_after=7):
     """
     Return time since starting time as a rounded, human readable string.

http://git-wip-us.apache.org/repos/asf/allura/blob/2a5d7de7/Allura/allura/templates/site_admin_user_details.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_user_details.html b/Allura/allura/templates/site_admin_user_details.html
index 0769c87..3f56efd 100644
--- a/Allura/allura/templates/site_admin_user_details.html
+++ b/Allura/allura/templates/site_admin_user_details.html
@@ -28,7 +28,7 @@
     <div class="grid-23">
       <fieldset>
         <legend>General</legend>
-        <div class="grid-19">
+        <div class="grid-17">
         <ul>
           <li>Username: {{ user.username }} (<a href="{{ user.url() }}">Go to profile page</a>)</li>
           <li>Full name: {{ user.get_pref('display_name') }}</li>
@@ -36,9 +36,9 @@
         </ul>
         </div>
 
-        <div class="grid-3">
+        <div class="grid-5">
         <form action='/nf/admin/user/set_status' method="POST">
-          <div class='grid-3'>
+          <div class='grid-5'>
             <label><input type="radio" name="status" value="enable"{% if not user.disabled %} checked="checked"{% endif %}>Enabled</label><br>
             <label><input type="radio" name="status" value="disable"{% if user.disabled %} checked="checked"{% endif %}>Disabled</label>
           </div>
@@ -46,6 +46,15 @@
           {{lib.csrf_token()}}
         </form>
         </div>
+
+        <div class="grid-17">&nbsp;</div>
+        <div class="grid-5">
+          <form action='/nf/admin/user/set_random_password' method="POST">
+            <input type="submit" value="Set random password">
+            <input type='hidden' name='username' value='{{ user.username }}'>
+            {{lib.csrf_token()}}
+          </form>
+        </div>
       </fieldset>
     </div>
   {% endblock general_info %}

http://git-wip-us.apache.org/repos/asf/allura/blob/2a5d7de7/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index 2dcfc18..ce8209b 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -20,7 +20,7 @@ import json
 import datetime as dt
 
 from mock import patch, MagicMock
-from nose.tools import assert_equal, assert_in, assert_not_in
+from nose.tools import assert_equal, assert_not_equal, assert_in, assert_not_in
 from ming.odm import ThreadLocalORMSession
 from pylons import tmpl_context as c
 from tg import config
@@ -471,6 +471,14 @@ class TestUserDetails(TestController):
         # test@example.com set as primary since test2@example.com is deleted
         assert_equal(user.get_pref('email_address'), 'test@example.com')
 
+    def test_set_random_password(self):
+        old_pwd = M.User.by_username('test-user').password
+        with td.audits('Set random password by test-admin', user=True):
+            r = self.app.post('/nf/admin/user/set_random_password', params={'username': 'test-user'})
+        assert_in('Password is set', self.webflash(r))
+        new_pwd = M.User.by_username('test-user').password
+        assert_not_equal(old_pwd, new_pwd)
+
 
 @task
 def test_task(*args, **kw):


[12/27] git commit: [#7657] ticket:651 Use email addresses widget on admin page

Posted by br...@apache.org.
[#7657] ticket:651 Use email addresses widget on admin page


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/239ad6b3
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/239ad6b3
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/239ad6b3

Branch: refs/heads/db/7657
Commit: 239ad6b31433ee5a034cfb08e5376e0f2fc583ee
Parents: 2c49164
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 16 13:47:01 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:45 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/auth.py               | 42 ++++-----
 Allura/allura/controllers/site_admin.py         | 12 ++-
 .../templates/site_admin_user_details.html      | 12 +++
 Allura/allura/templates/update_emails_form.html | 93 ++++++++++----------
 Allura/allura/templates/user_prefs.html         |  5 +-
 5 files changed, 94 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/239ad6b3/Allura/allura/controllers/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index 8d254bb..0e4b4c4 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -430,69 +430,69 @@ class PreferencesController(BaseController):
         c.upload_key_form = F.upload_key_form
         provider = plugin.AuthenticationProvider.get(request)
         menu = provider.account_navigation()
-        return dict(menu=menu)
+        return dict(menu=menu, user=c.user)
 
-    def _update_emails(self, **kw):
+    def _update_emails(self, user, admin=False, **kw):
         addr = kw.pop('addr', None)
         new_addr= kw.pop('new_addr', None)
         primary_addr = kw.pop('primary_addr', None)
         oid = kw.pop('oid', None)
         new_oid = kw.pop('new_oid', None)
         provider = plugin.AuthenticationProvider.get(request)
-        for i, (old_a, data) in enumerate(zip(c.user.email_addresses, addr or [])):
-            obj = c.user.address_object(old_a)
+        for i, (old_a, data) in enumerate(zip(user.email_addresses, addr or [])):
+            obj = user.address_object(old_a)
             if data.get('delete') or not obj:
-                if not kw.get('password') or not provider.validate_password(c.user, kw.get('password')):
+                if not admin and (not kw.get('password') or not provider.validate_password(user, kw.get('password'))):
                     flash('You must provide your current password to delete an email', 'error')
                     return
-                if primary_addr == c.user.email_addresses[i]:
-                    if select_new_primary_addr(c.user, ignore_emails=primary_addr) is None \
+                if primary_addr == user.email_addresses[i]:
+                    if select_new_primary_addr(user, ignore_emails=primary_addr) is None \
                             and asbool(config.get('auth.require_email_addr', False)):
                         flash('You must have at least one verified email address.', 'error')
                         return
                     else:
                         # clear it now, a new one will get set below
-                        c.user.set_pref('email_address', None)
+                        user.set_pref('email_address', None)
                         primary_addr = None
-                h.auditlog_user('Email address deleted: %s', c.user.email_addresses[i])
-                del c.user.email_addresses[i]
+                h.auditlog_user('Email address deleted: %s', user.email_addresses[i])
+                del user.email_addresses[i]
                 if obj:
                     obj.delete()
         if new_addr.get('claim') or new_addr.get('addr'):
-            if not kw.get('password') or not provider.validate_password(c.user, kw.get('password')):
+            if not admin and (not kw.get('password') or not provider.validate_password(user, kw.get('password'))):
                 flash('You must provide your current password to claim new email', 'error')
                 return
             if M.EmailAddress.query.get(email=new_addr['addr'], confirmed=True) \
-                    or M.EmailAddress.query.get(email=new_addr['addr'], claimed_by_user_id=c.user._id):
+                    or M.EmailAddress.query.get(email=new_addr['addr'], claimed_by_user_id=user._id):
                 flash('Email address already claimed', 'error')
             elif mail_util.isvalid(new_addr['addr']):
-                c.user.email_addresses.append(new_addr['addr'])
+                user.email_addresses.append(new_addr['addr'])
                 em = M.EmailAddress.create(new_addr['addr'])
-                em.claimed_by_user_id = c.user._id
+                em.claimed_by_user_id = user._id
                 em.send_verification_link()
                 h.auditlog_user('New email address: %s', new_addr['addr'])
                 flash('A verification email has been sent.  Please check your email and click to confirm.')
             else:
                 flash('Email address %s is invalid' % new_addr['addr'], 'error')
-        if not primary_addr and not c.user.get_pref('email_address') and c.user.email_addresses:
-            primary_addr = select_new_primary_addr(c.user)
+        if not primary_addr and not user.get_pref('email_address') and user.email_addresses:
+            primary_addr = select_new_primary_addr(user)
         if primary_addr:
-            if c.user.get_pref('email_address') != primary_addr:
-                if not kw.get('password') or not provider.validate_password(c.user, kw.get('password')):
+            if user.get_pref('email_address') != primary_addr:
+                if not admin and (not kw.get('password') or not provider.validate_password(user, kw.get('password'))):
                     flash('You must provide your current password to change primary address', 'error')
                     return
                 h.auditlog_user(
                     'Primary email changed: %s => %s',
-                    c.user.get_pref('email_address'),
+                    user.get_pref('email_address'),
                     primary_addr)
-            c.user.set_pref('email_address', primary_addr)
+            user.set_pref('email_address', primary_addr)
 
     @h.vardec
     @expose()
     @require_post()
     def update_emails(self, **kw):
         if asbool(config.get('auth.allow_edit_prefs', True)):
-            self._update_emails(**kw)
+            self._update_emails(c.user, **kw)
         redirect('.')
 
     @h.vardec

http://git-wip-us.apache.org/repos/asf/allura/blob/239ad6b3/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index bee6c8a..198207b 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -515,7 +515,7 @@ class AdminUserDetailsController(object):
     @require_post()
     def set_status(self, username=None, status=None):
         user = M.User.by_username(username)
-        if not user:
+        if not user or user.is_anonymous():
             raise HTTPNotFound()
         if status == 'enable' and user.disabled:
             AuthenticationProvider.get(request).enable_user(user)
@@ -525,6 +525,16 @@ class AdminUserDetailsController(object):
             flash('User disabled')
         redirect(request.referer)
 
+    @h.vardec
+    @expose()
+    @require_post()
+    def update_emails(self, username, **kw):
+        user = M.User.by_username(username)
+        if not user or user.is_anonymous():
+            raise HTTPNotFound()
+        allura.controllers.auth.PreferencesController()._update_emails(user, admin=True, **kw)
+        redirect(request.referer)
+
 
 class StatsSiteAdminExtension(SiteAdminExtension):
     controllers = {'stats': StatsController}

http://git-wip-us.apache.org/repos/asf/allura/blob/239ad6b3/Allura/allura/templates/site_admin_user_details.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_user_details.html b/Allura/allura/templates/site_admin_user_details.html
index e6eab2a..0769c87 100644
--- a/Allura/allura/templates/site_admin_user_details.html
+++ b/Allura/allura/templates/site_admin_user_details.html
@@ -50,6 +50,18 @@
     </div>
   {% endblock general_info %}
 
+  {% block emails %}
+    <div class="grid-23">
+      <fieldset>
+        <legend>Emails</legend>
+        <form action="update_emails" method="post">
+          {% include 'allura:templates/update_emails_form.html' %}
+          <input type='hidden' name='username' value='{{ user.username }}'>
+        </form>
+      </fieldset>
+    </div>
+  {% endblock emails %}
+
   {% block session_info %}
     <div class="grid-23">
       <fieldset>

http://git-wip-us.apache.org/repos/asf/allura/blob/239ad6b3/Allura/allura/templates/update_emails_form.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/update_emails_form.html b/Allura/allura/templates/update_emails_form.html
index 20aecce..3ac5502 100644
--- a/Allura/allura/templates/update_emails_form.html
+++ b/Allura/allura/templates/update_emails_form.html
@@ -16,53 +16,52 @@
        specific language governing permissions and limitations
        under the License.
 -#}
-<form action="update_emails" method="post" name="update-email">
-  {% for a in c.user.email_addresses %}
-    <input name="addr-{{loop.index0}}.ord" value="{{loop.index0}}" type="hidden"/>
-  {% endfor %}
-  <table class="grid-22">
-    <tr>
-      <th>Primary?</th>
-      <th>Address</th>
-      <th>Confirmed</th>
-      <th></th>
-    </tr>
-    {% for a in c.user.email_addresses %}
-    <tr>
-      {% set obj = c.user.address_object(a) %}
+{% import 'allura:templates/jinja_master/lib.html' as lib with context %}
+
+{% for a in user.email_addresses %}
+  <input name="addr-{{loop.index0}}.ord" value="{{loop.index0}}" type="hidden"/>
+{% endfor %}
+<table class="grid-22">
+  <tr>
+    <th>Primary?</th>
+    <th>Address</th>
+    <th>Confirmed</th>
+    <th></th>
+  </tr>
+  {% for a in user.email_addresses %}
+  <tr>
+    {% set obj = user.address_object(a) %}
+    {% if obj.confirmed %}
+      <td>{{lib.radio_button('primary_addr', None, a, user.preferences.email_address)}}</td>
+    {% else %}
+      <td> <input type="radio" disabled="disabled"></td>
+    {% endif %}
+    <td>{{a}}</td>
+    {% if obj %}
+    <td>
       {% if obj.confirmed %}
-        <td>{{lib.radio_button('primary_addr', None, a, c.user.preferences.email_address)}}</td>
+        yes
       {% else %}
-        <td> <input type="radio" disabled="disabled"></td>
+        no (<a href="/auth/send_verification_link?a={{a}}">verify</a>)
       {% endif %}
-      <td>{{a}}</td>
-      {% if obj %}
-      <td>
-        {% if obj.confirmed %}
-          yes
-        {% else %}
-          no (<a href="/auth/send_verification_link?a={{a}}">verify</a>)
-        {% endif %}
-      </td>
-      {% else %}
-        <td>Unknown addr obj {{a}}</td>
-      {% endif %}
-      <td>
-        <div class="addr-delete">
-          {{lib.submit_button('Delete', 'addr-%s.delete' % loop.index0)}}
-          {{lib.hidden_field('addr-%s.delete' % loop.index0, '')}}
-        </div>
-      </td>
-    </tr>
-    {% endfor %}
-    <tr>
-      <td colspan="2">{{lib.text_field('new_addr.addr', '')}}</td>
-      <td colspan="2">{{lib.submit_button('Claim New Address', name='new_addr.claim')}}</td>
-    </tr>
-  </table>
-  <div class="grid-22">
-    {{lib.submit_button('Save', name='addr-save')}}
-  </div>
-  {{lib.hidden_field('password', '')}}
-  {{lib.csrf_token()}}
-</form>
+    </td>
+    {% else %}
+      <td>Unknown addr obj {{a}}</td>
+    {% endif %}
+    <td>
+      <div class="addr-delete">
+        {{lib.submit_button('Delete', 'addr-%s.delete' % loop.index0)}}
+        {{lib.hidden_field('addr-%s.delete' % loop.index0, '')}}
+      </div>
+    </td>
+  </tr>
+  {% endfor %}
+  <tr>
+    <td colspan="2">{{lib.text_field('new_addr.addr', '')}}</td>
+    <td colspan="2">{{lib.submit_button('Claim New Address', name='new_addr.claim')}}</td>
+  </tr>
+</table>
+<div class="grid-22">
+  {{lib.submit_button('Save', name='addr-save')}}
+</div>
+{{lib.csrf_token()}}

http://git-wip-us.apache.org/repos/asf/allura/blob/239ad6b3/Allura/allura/templates/user_prefs.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/user_prefs.html b/Allura/allura/templates/user_prefs.html
index a19ae4e..bc8cf42 100644
--- a/Allura/allura/templates/user_prefs.html
+++ b/Allura/allura/templates/user_prefs.html
@@ -66,7 +66,10 @@
 
         <fieldset>
           <legend>Email addresses</legend>
-          {% include 'allura:templates/update_emails_form.html' %}
+          <form action="update_emails" method="post" name="update-email">
+            {% include 'allura:templates/update_emails_form.html' %}
+            {{lib.hidden_field('password', '')}}
+          </form>
         </fieldset>
 
       <!-- popup -->


[11/27] git commit: [#7657] ticket:651 Add ability for admin to enable/disable account

Posted by br...@apache.org.
[#7657] ticket:651 Add ability for admin to enable/disable account


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/6bcbdafa
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/6bcbdafa
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/6bcbdafa

Branch: refs/heads/db/7657
Commit: 6bcbdafa5f90557322ca9cb3e2e6f47cce885d91
Parents: dfbf4f7
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 16 10:41:51 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:45 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/site_admin.py         | 14 +++++++++++
 Allura/allura/lib/plugin.py                     | 20 +++++++++++++++-
 .../templates/site_admin_user_details.html      | 25 ++++++++++++++++++++
 3 files changed, 58 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/6bcbdafa/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index 13bbb6e..bee6c8a 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -511,6 +511,20 @@ class AdminUserDetailsController(object):
             flash('Can not add comment "%s" for user %s' % (comment, user))
         redirect(request.referer)
 
+    @expose()
+    @require_post()
+    def set_status(self, username=None, status=None):
+        user = M.User.by_username(username)
+        if not user:
+            raise HTTPNotFound()
+        if status == 'enable' and user.disabled:
+            AuthenticationProvider.get(request).enable_user(user)
+            flash('User enabled')
+        elif status == 'disable' and not user.disabled:
+            AuthenticationProvider.get(request).disable_user(user)
+            flash('User disabled')
+        redirect(request.referer)
+
 
 class StatsSiteAdminExtension(SiteAdminExtension):
     controllers = {'stats': StatsController}

http://git-wip-us.apache.org/repos/asf/allura/blob/6bcbdafa/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index 78aae92..1186cf6 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -179,6 +179,10 @@ class AuthenticationProvider(object):
         '''Disable user account'''
         raise NotImplementedError, 'disable_user'
 
+    def enable_user(self, user):
+        '''Enable user account'''
+        raise NotImplementedError, 'enable_user'
+
     def by_username(self, username):
         '''
         Find a user by username.
@@ -324,7 +328,18 @@ class LocalAuthenticationProvider(AuthenticationProvider):
     def disable_user(self, user):
         user.disabled = True
         session(user).flush(user)
-        h.auditlog_user('Account disabled', user=user)
+        suffix = u''
+        if user != c.user:
+            suffix = u' by %s' % c.user.username
+        h.auditlog_user(u'Account disabled' + suffix, user=user)
+
+    def enable_user(self, user):
+        user.disabled = False
+        session(user).flush(user)
+        suffix = u''
+        if user != c.user:
+            suffix = u' by %s' % c.user.username
+        h.auditlog_user(u'Account enabled' + suffix, user=user)
 
     def validate_password(self, user, password):
         return self._validate_password(user, password)
@@ -566,6 +581,9 @@ class LdapAuthenticationProvider(AuthenticationProvider):
     def disable_user(self, user):
         return LocalAuthenticationProvider(None).disable_user(user)
 
+    def enable_user(self, user):
+        return LocalAuthenticationProvider(None).enable_user(user)
+
     def get_last_password_updated(self, user):
         return LocalAuthenticationProvider(None).get_last_password_updated(user)
 

http://git-wip-us.apache.org/repos/asf/allura/blob/6bcbdafa/Allura/allura/templates/site_admin_user_details.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_user_details.html b/Allura/allura/templates/site_admin_user_details.html
index bfd17e7..e6eab2a 100644
--- a/Allura/allura/templates/site_admin_user_details.html
+++ b/Allura/allura/templates/site_admin_user_details.html
@@ -28,11 +28,24 @@
     <div class="grid-23">
       <fieldset>
         <legend>General</legend>
+        <div class="grid-19">
         <ul>
           <li>Username: {{ user.username }} (<a href="{{ user.url() }}">Go to profile page</a>)</li>
           <li>Full name: {{ user.get_pref('display_name') }}</li>
           <li>Registered: {{ user.registration_date() }} ({{ h.ago(user.registration_date()) }})</li>
         </ul>
+        </div>
+
+        <div class="grid-3">
+        <form action='/nf/admin/user/set_status' method="POST">
+          <div class='grid-3'>
+            <label><input type="radio" name="status" value="enable"{% if not user.disabled %} checked="checked"{% endif %}>Enabled</label><br>
+            <label><input type="radio" name="status" value="disable"{% if user.disabled %} checked="checked"{% endif %}>Disabled</label>
+          </div>
+          <input type='hidden' name='username' value='{{ user.username }}'>
+          {{lib.csrf_token()}}
+        </form>
+        </div>
       </fieldset>
     </div>
   {% endblock general_info %}
@@ -108,3 +121,15 @@
 }
 </style>
 {% endblock %}
+
+{% block extra_js %}
+{{ super() }}
+<script>
+$(document).ready(function() {
+  // enabled/disabled change
+  $('input[name="status"]').change(function(e) {
+    $(this).parents('form').submit();
+  });
+});
+</script>
+{% endblock %}


[20/27] git commit: [#7657] ticket:651 Fix random password test, remove old tests

Posted by br...@apache.org.
[#7657] ticket:651 Fix random password test, remove old tests


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/d90674aa
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/d90674aa
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/d90674aa

Branch: refs/heads/db/7657
Commit: d90674aa87e17dcc7a959ef7d08d7d21ca66e4d8
Parents: dab2b72
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 16 16:00:15 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 18:37:07 2014 +0000

----------------------------------------------------------------------
 .../allura/tests/functional/test_site_admin.py  | 72 ++++----------------
 1 file changed, 14 insertions(+), 58 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/d90674aa/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index ac018dc..37338d7 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -20,7 +20,7 @@ import json
 import datetime as dt
 
 from mock import patch, MagicMock
-from nose.tools import assert_equal, assert_not_equal, assert_in, assert_not_in
+from nose.tools import assert_equal, assert_in, assert_not_in
 from ming.odm import ThreadLocalORMSession
 from pylons import tmpl_context as c
 from tg import config
@@ -31,6 +31,7 @@ from allura.tests import TestController
 from allura.tests import decorators as td
 from allura.lib import helpers as h
 from allura.lib.decorators import task
+from allura.lib.plugin import LocalAuthenticationProvider
 
 
 class TestSiteAdmin(TestController):
@@ -168,50 +169,6 @@ class TestSiteAdmin(TestController):
             task_name='allura.tests.functional.test_site_admin.test_task'))
         assert json.loads(r.body)['doc'] == 'test_task doc string'
 
-    @patch('allura.model.auth.request')
-    @patch('allura.lib.helpers.request')
-    def test_users(self, req1, req2):
-        req1.url = req2.url = 'http://host.domain/path/'
-        c.user = M.User.by_username('test-user-1')
-        h.auditlog_user('test activity user 1')
-        h.auditlog_user('test activity user 2', user=M.User.by_username('test-user-2'))
-        r = self.app.get('/nf/admin/users')
-        assert_not_in('test activity', r)
-        r = self.app.get('/nf/admin/users?username=admin1')
-        assert_not_in('test activity', r)
-        r = self.app.get('/nf/admin/users?username=test-user-1')
-        assert_in('test activity user 1', r)
-        assert_not_in('test activity user 2', r)
-        r = self.app.get('/nf/admin/users?username=test-user-2')
-        assert_not_in('test activity user 1', r)
-        assert_in('test activity user 2', r)
-
-    def test_add_audit_trail_entry_access(self):
-        self.app.get('/nf/admin/add_audit_log_entry', status=404)  # GET is not allowed
-        r = self.app.post('/nf/admin/add_audit_log_entry',
-                          extra_environ={'username': '*anonymous'},
-                          status=302)
-        assert_equal(r.location, 'http://localhost/auth/')
-
-    def test_add_comment_on_users_trail_page(self):
-        r = self.app.get('/nf/admin/users')
-        assert_not_in('Add comment', r)
-        r = self.app.get('/nf/admin/users?username=fake-user')
-        assert_not_in('Add comment', r)
-        r = self.app.get('/nf/admin/users?username=test-user')
-        assert_in('Add comment', r)
-
-    def test_add_comment(self):
-        r = self.app.get('/nf/admin/users?username=test-user')
-        assert_not_in(u'Comment by test-admin: I was hêre!', r)
-        form = r.forms[1]
-        assert_equal(form['username'].value, 'test-user')
-        form['comment'] = u'I was hêre!'
-        r = form.submit()
-        assert_in(u'Comment added', self.webflash(r))
-        r = self.app.get('/nf/admin/users?username=test-user')
-        assert_in(u'Comment by test-admin: I was hêre!', r)
-
 
 class TestProjectsSearch(TestController):
 
@@ -390,29 +347,29 @@ class TestUserDetails(TestController):
         assert_in(u'Comment by test-admin: I was hêre!', r)
 
     def test_disable_user(self):
-        assert_equal(M.User.by_username('test-user').disabled, False)
-        r = self.app.get('/nf/admin/user/test-user')
+        assert_equal(M.User.by_username('test-user-3').disabled, False)
+        r = self.app.get('/nf/admin/user/test-user-3')
         form = r.forms[0]
-        assert_equal(form['username'].value, 'test-user')
+        assert_equal(form['username'].value, 'test-user-3')
         assert_equal(form['status'].value, 'enable')
         form['status'].value = 'disable'
         r = form.submit()
         assert_in(u'User disabled', self.webflash(r))
-        assert_equal(M.User.by_username('test-user').disabled, True)
+        assert_equal(M.User.by_username('test-user-3').disabled, True)
 
     def test_enable_user(self):
-        user = M.User.by_username('test-user')
+        user = M.User.by_username('test-user-3')
         user.disabled = True
         ThreadLocalORMSession.flush_all()
-        assert_equal(M.User.by_username('test-user').disabled, True)
-        r = self.app.get('/nf/admin/user/test-user')
+        assert_equal(M.User.by_username('test-user-3').disabled, True)
+        r = self.app.get('/nf/admin/user/test-user-3')
         form = r.forms[0]
-        assert_equal(form['username'].value, 'test-user')
+        assert_equal(form['username'].value, 'test-user-3')
         assert_equal(form['status'].value, 'disable')
         form['status'].value = 'enable'
         r = form.submit()
         assert_in(u'User enabled', self.webflash(r))
-        assert_equal(M.User.by_username('test-user').disabled, False)
+        assert_equal(M.User.by_username('test-user-3').disabled, False)
 
     def test_emails(self):
         # add test@example.com
@@ -471,13 +428,12 @@ class TestUserDetails(TestController):
         # test@example.com set as primary since test2@example.com is deleted
         assert_equal(user.get_pref('email_address'), 'test@example.com')
 
-    def test_set_random_password(self):
-        old_pwd = M.User.by_username('test-user').password
+    @patch.object(LocalAuthenticationProvider, 'set_password')
+    def test_set_random_password(self, set_password):
         with td.audits('Set random password by test-admin', user=True):
             r = self.app.post('/nf/admin/user/set_random_password', params={'username': 'test-user'})
         assert_in('Password is set', self.webflash(r))
-        new_pwd = M.User.by_username('test-user').password
-        assert_not_equal(old_pwd, new_pwd)
+        set_password.assert_called_once()
 
     @patch('allura.tasks.mail_tasks.sendsimplemail')
     @patch('allura.lib.helpers.gen_message_id')


[07/27] git commit: [#7657] ticket:650 Add comment form to audit log

Posted by br...@apache.org.
[#7657] ticket:650 Add comment form to audit log


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/fd8dec2a
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/fd8dec2a
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/fd8dec2a

Branch: refs/heads/db/7657
Commit: fd8dec2a094a428f6945f11212b197499baf1a0d
Parents: 90835eb
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Sep 12 15:08:20 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:44 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/site_admin.py              | 15 ++++++++++++++-
 Allura/allura/templates/site_admin_user_details.html | 13 +++++++++++++
 2 files changed, 27 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/fd8dec2a/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index 52b4d0f..ad224f4 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -507,7 +507,7 @@ class AdminUserDetailsController(object):
     @expose('jinja:allura:templates/site_admin_user_details.html')
     def _default(self, username, limit=25, page=0):
         user = M.User.by_username(username)
-        if not user:
+        if not user or user.is_anonymous():
             raise HTTPNotFound()
         projects = user.my_projects().all()
         audit_log = self._audit_log(user, limit, page)
@@ -541,6 +541,19 @@ class AdminUserDetailsController(object):
             page=page,
             count=count)
 
+    @expose()
+    @require_post()
+    def add_audit_trail_entry(self, **kw):
+        username = kw.get('username')
+        comment = kw.get('comment')
+        user = M.User.by_username(username)
+        if user and not user.is_anonymous() and comment:
+            M.AuditLog.comment_user(c.user, comment, user=user)
+            flash('Comment added', 'ok')
+        else:
+            flash('Can not add comment "%s" for user %s' % (comment, user))
+        redirect(request.referer)
+
 
 class StatsSiteAdminExtension(SiteAdminExtension):
     controllers = {'stats': StatsController}

http://git-wip-us.apache.org/repos/asf/allura/blob/fd8dec2a/Allura/allura/templates/site_admin_user_details.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_user_details.html b/Allura/allura/templates/site_admin_user_details.html
index 689e295..bfd17e7 100644
--- a/Allura/allura/templates/site_admin_user_details.html
+++ b/Allura/allura/templates/site_admin_user_details.html
@@ -65,6 +65,19 @@
       {% set al = audit_log %}
       <fieldset>
         <legend>Audit log</legend>
+        <form action='/nf/admin/user/add_audit_trail_entry' method='POST'>
+          <div class='grid-22'>
+            <label for='comment'>Comment:</label>
+          </div>
+          <div class='grid-22'>
+            <textarea name="comment" cols="38" rows="5"></textarea>
+          </div>
+          <div class='grid-5'>
+            <input type='hidden' name='username' value='{{ user.username }}'>
+            <input type='submit' value='Add comment'>
+            {{lib.csrf_token()}}
+          </div>
+        </form>
         {% if al['entries'] %}
           {{ c.audit_log_widget.display(entries=al['entries'], limit=al['limit'], page=al['page'], count=al['count'], grid='22') }}
         {% endif %}


[27/27] git commit: [#7657] log verified email addr instead of its _id

Posted by br...@apache.org.
[#7657] log verified email addr instead of its _id


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/69fe0fa7
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/69fe0fa7
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/69fe0fa7

Branch: refs/heads/db/7657
Commit: 69fe0fa7c323f6aa68e8f247e2db73aed7d49224
Parents: 4869d52
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Mon Sep 29 16:08:02 2014 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Sep 29 16:08:02 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/auth.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/69fe0fa7/Allura/allura/controllers/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index 4d2fb7c..d93e7a8 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -253,7 +253,7 @@ class AuthController(BaseController):
             })
 
             flash('Email address confirmed')
-            h.auditlog_user('Email address verified: %s', addr._id, user=addr.claimed_by_user())
+            h.auditlog_user('Email address verified: %s', addr.email, user=addr.claimed_by_user())
         else:
             flash('Unknown verification link', 'error')
 


[03/27] git commit: [#7657] ticket:649 Move fieldset/legend css to site_style

Posted by br...@apache.org.
[#7657] ticket:649 Move fieldset/legend css to site_style


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/5a4bc10f
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/5a4bc10f
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/5a4bc10f

Branch: refs/heads/db/7657
Commit: 5a4bc10fd799142c897106a9a66529435b6f29d2
Parents: f54be44
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Sep 12 11:39:55 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:43 2014 +0000

----------------------------------------------------------------------
 Allura/allura/nf/allura/css/site_style.css | 14 ++++++++++++++
 Allura/allura/templates/user_prefs.html    | 13 -------------
 2 files changed, 14 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/5a4bc10f/Allura/allura/nf/allura/css/site_style.css
----------------------------------------------------------------------
diff --git a/Allura/allura/nf/allura/css/site_style.css b/Allura/allura/nf/allura/css/site_style.css
index 8f8d5ab..963f071 100644
--- a/Allura/allura/nf/allura/css/site_style.css
+++ b/Allura/allura/nf/allura/css/site_style.css
@@ -3497,3 +3497,17 @@ ul.dropdown ul li a:hover {
     top: 10px;
 }
 .strikethrough { text-decoration: line-through; }
+
+fieldset {
+  margin-bottom: 2em;
+  border: 1px solid silver;
+  padding: 8px;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  border-radius: 4px;
+}
+legend {
+  margin: .2em;
+  padding: .2em;
+  font-size: 1.5em;
+}

http://git-wip-us.apache.org/repos/asf/allura/blob/5a4bc10f/Allura/allura/templates/user_prefs.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/user_prefs.html b/Allura/allura/templates/user_prefs.html
index 913bf8b..253174f 100644
--- a/Allura/allura/templates/user_prefs.html
+++ b/Allura/allura/templates/user_prefs.html
@@ -180,19 +180,6 @@
 {% block extra_css %}
 {{ super() }}
 <style>
-  fieldset {
-    margin-bottom: 2em;
-    border: 1px solid silver;
-    padding: 8px;
-    -webkit-border-radius: 4px;
-    -moz-border-radius: 4px;
-    border-radius: 4px;
-  }
-  legend {
-    margin: .2em;
-    padding: .2em;
-    font-size: 1.5em;
-  }
   .pad hr {
     margin: 15px 10px;
     width: 860px;


[16/27] git commit: [#7657] ticket:651 Autoverify emails if added via site admin UI nad fix audit logs

Posted by br...@apache.org.
[#7657] ticket:651 Autoverify emails if added via site admin UI nad fix audit logs


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/2e86cb1a
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/2e86cb1a
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/2e86cb1a

Branch: refs/heads/db/7657
Commit: 2e86cb1a4ee271e265d782c8aedf788173398561
Parents: 239ad6b
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 16 14:01:00 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:46 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/auth.py | 26 ++++++++++++++++----------
 1 file changed, 16 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/2e86cb1a/Allura/allura/controllers/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index 0e4b4c4..87908b8 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -233,10 +233,7 @@ class AuthController(BaseController):
             flash('No such address', 'error')
         redirect(request.referer)
 
-    @expose()
-    def verify_addr(self, a):
-        addr = M.EmailAddress.query.get(nonce=a)
-
+    def _verify_addr(self, addr):
         if addr:
             addr.confirmed = True
             # Remove other non-confirmed emails claimed by other users
@@ -256,9 +253,14 @@ class AuthController(BaseController):
             })
 
             flash('Email address confirmed')
-            h.auditlog_user('Email address verified: %s', addr._id)
+            h.auditlog_user('Email address verified: %s', addr._id, user=addr.claimed_by_user())
         else:
             flash('Unknown verification link', 'error')
+
+    @expose()
+    def verify_addr(self, a):
+        addr = M.EmailAddress.query.get(nonce=a)
+        self._verify_addr(addr)
         redirect('/auth/preferences/')
 
     @expose()
@@ -454,7 +456,7 @@ class PreferencesController(BaseController):
                         # clear it now, a new one will get set below
                         user.set_pref('email_address', None)
                         primary_addr = None
-                h.auditlog_user('Email address deleted: %s', user.email_addresses[i])
+                h.auditlog_user('Email address deleted: %s', user.email_addresses[i], user=user)
                 del user.email_addresses[i]
                 if obj:
                     obj.delete()
@@ -469,9 +471,12 @@ class PreferencesController(BaseController):
                 user.email_addresses.append(new_addr['addr'])
                 em = M.EmailAddress.create(new_addr['addr'])
                 em.claimed_by_user_id = user._id
-                em.send_verification_link()
-                h.auditlog_user('New email address: %s', new_addr['addr'])
-                flash('A verification email has been sent.  Please check your email and click to confirm.')
+                if not admin:
+                    em.send_verification_link()
+                    flash('A verification email has been sent.  Please check your email and click to confirm.')
+                else:
+                    AuthController()._verify_addr(em)
+                h.auditlog_user('New email address: %s', new_addr['addr'], user=user)
             else:
                 flash('Email address %s is invalid' % new_addr['addr'], 'error')
         if not primary_addr and not user.get_pref('email_address') and user.email_addresses:
@@ -484,7 +489,8 @@ class PreferencesController(BaseController):
                 h.auditlog_user(
                     'Primary email changed: %s => %s',
                     user.get_pref('email_address'),
-                    primary_addr)
+                    primary_addr,
+                    user=user)
             user.set_pref('email_address', primary_addr)
 
     @h.vardec


[10/27] git commit: [#7657] ticket:651 Refactor update emails controller/page to reuse in site admin UI

Posted by br...@apache.org.
[#7657] ticket:651 Refactor update emails controller/page to reuse in site admin UI


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/2c49164c
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/2c49164c
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/2c49164c

Branch: refs/heads/db/7657
Commit: 2c49164c26a15decfb71d94dbff1a5d9265c6288
Parents: 46cafe0
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 16 13:18:58 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:45 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/auth.py               | 120 ++++++++++---------
 Allura/allura/templates/update_emails_form.html |  68 +++++++++++
 Allura/allura/templates/user_prefs.html         | 102 +++++-----------
 3 files changed, 159 insertions(+), 131 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/2c49164c/Allura/allura/controllers/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index 1f8c8aa..8d254bb 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -432,73 +432,81 @@ class PreferencesController(BaseController):
         menu = provider.account_navigation()
         return dict(menu=menu)
 
+    def _update_emails(self, **kw):
+        addr = kw.pop('addr', None)
+        new_addr= kw.pop('new_addr', None)
+        primary_addr = kw.pop('primary_addr', None)
+        oid = kw.pop('oid', None)
+        new_oid = kw.pop('new_oid', None)
+        provider = plugin.AuthenticationProvider.get(request)
+        for i, (old_a, data) in enumerate(zip(c.user.email_addresses, addr or [])):
+            obj = c.user.address_object(old_a)
+            if data.get('delete') or not obj:
+                if not kw.get('password') or not provider.validate_password(c.user, kw.get('password')):
+                    flash('You must provide your current password to delete an email', 'error')
+                    return
+                if primary_addr == c.user.email_addresses[i]:
+                    if select_new_primary_addr(c.user, ignore_emails=primary_addr) is None \
+                            and asbool(config.get('auth.require_email_addr', False)):
+                        flash('You must have at least one verified email address.', 'error')
+                        return
+                    else:
+                        # clear it now, a new one will get set below
+                        c.user.set_pref('email_address', None)
+                        primary_addr = None
+                h.auditlog_user('Email address deleted: %s', c.user.email_addresses[i])
+                del c.user.email_addresses[i]
+                if obj:
+                    obj.delete()
+        if new_addr.get('claim') or new_addr.get('addr'):
+            if not kw.get('password') or not provider.validate_password(c.user, kw.get('password')):
+                flash('You must provide your current password to claim new email', 'error')
+                return
+            if M.EmailAddress.query.get(email=new_addr['addr'], confirmed=True) \
+                    or M.EmailAddress.query.get(email=new_addr['addr'], claimed_by_user_id=c.user._id):
+                flash('Email address already claimed', 'error')
+            elif mail_util.isvalid(new_addr['addr']):
+                c.user.email_addresses.append(new_addr['addr'])
+                em = M.EmailAddress.create(new_addr['addr'])
+                em.claimed_by_user_id = c.user._id
+                em.send_verification_link()
+                h.auditlog_user('New email address: %s', new_addr['addr'])
+                flash('A verification email has been sent.  Please check your email and click to confirm.')
+            else:
+                flash('Email address %s is invalid' % new_addr['addr'], 'error')
+        if not primary_addr and not c.user.get_pref('email_address') and c.user.email_addresses:
+            primary_addr = select_new_primary_addr(c.user)
+        if primary_addr:
+            if c.user.get_pref('email_address') != primary_addr:
+                if not kw.get('password') or not provider.validate_password(c.user, kw.get('password')):
+                    flash('You must provide your current password to change primary address', 'error')
+                    return
+                h.auditlog_user(
+                    'Primary email changed: %s => %s',
+                    c.user.get_pref('email_address'),
+                    primary_addr)
+            c.user.set_pref('email_address', primary_addr)
+
+    @h.vardec
+    @expose()
+    @require_post()
+    def update_emails(self, **kw):
+        if asbool(config.get('auth.allow_edit_prefs', True)):
+            self._update_emails(**kw)
+        redirect('.')
+
     @h.vardec
     @expose()
     @require_post()
-    def update(self,
-               addr=None,
-               new_addr=None,
-               primary_addr=None,
-               oid=None,
-               new_oid=None,
-               preferences=None,
-               **kw):
+    def update(self, preferences=None, **kw):
         if asbool(config.get('auth.allow_edit_prefs', True)):
             if not preferences.get('display_name'):
                 flash("Display Name cannot be empty.", 'error')
                 redirect('.')
-            provider = plugin.AuthenticationProvider.get(request)
             old = c.user.get_pref('display_name')
             c.user.set_pref('display_name', preferences['display_name'])
             if old != preferences['display_name']:
                 h.auditlog_user('Display Name changed %s => %s', old, preferences['display_name'])
-            for i, (old_a, data) in enumerate(zip(c.user.email_addresses, addr or [])):
-                obj = c.user.address_object(old_a)
-                if data.get('delete') or not obj:
-                    if not kw.get('password') or not provider.validate_password(c.user, kw.get('password')):
-                        flash('You must provide your current password to delete an email', 'error')
-                        redirect('.')
-                    if primary_addr == c.user.email_addresses[i]:
-                        if select_new_primary_addr(c.user, ignore_emails=primary_addr) is None \
-                                and asbool(config.get('auth.require_email_addr', False)):
-                            flash('You must have at least one verified email address.', 'error')
-                            redirect('.')
-                        else:
-                            # clear it now, a new one will get set below
-                            c.user.set_pref('email_address', None)
-                            primary_addr = None
-                    h.auditlog_user('Email address deleted: %s', c.user.email_addresses[i])
-                    del c.user.email_addresses[i]
-                    if obj:
-                        obj.delete()
-            if new_addr.get('claim') or new_addr.get('addr'):
-                if not kw.get('password') or not provider.validate_password(c.user, kw.get('password')):
-                    flash('You must provide your current password to claim new email', 'error')
-                    redirect('.')
-                if M.EmailAddress.query.get(email=new_addr['addr'], confirmed=True) \
-                        or M.EmailAddress.query.get(email=new_addr['addr'], claimed_by_user_id=c.user._id):
-                    flash('Email address already claimed', 'error')
-                elif mail_util.isvalid(new_addr['addr']):
-                    c.user.email_addresses.append(new_addr['addr'])
-                    em = M.EmailAddress.create(new_addr['addr'])
-                    em.claimed_by_user_id = c.user._id
-                    em.send_verification_link()
-                    h.auditlog_user('New email address: %s', new_addr['addr'])
-                    flash('A verification email has been sent.  Please check your email and click to confirm.')
-                else:
-                    flash('Email address %s is invalid' % new_addr['addr'], 'error')
-            if not primary_addr and not c.user.get_pref('email_address') and c.user.email_addresses:
-                primary_addr = select_new_primary_addr(c.user)
-            if primary_addr:
-                if c.user.get_pref('email_address') != primary_addr:
-                    if not kw.get('password') or not provider.validate_password(c.user, kw.get('password')):
-                        flash('You must provide your current password to change primary address', 'error')
-                        redirect('.')
-                    h.auditlog_user(
-                        'Primary email changed: %s => %s',
-                        c.user.get_pref('email_address'),
-                        primary_addr)
-                c.user.set_pref('email_address', primary_addr)
             for k, v in preferences.iteritems():
                 if k == 'results_per_page':
                     v = int(v)

http://git-wip-us.apache.org/repos/asf/allura/blob/2c49164c/Allura/allura/templates/update_emails_form.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/update_emails_form.html b/Allura/allura/templates/update_emails_form.html
new file mode 100644
index 0000000..20aecce
--- /dev/null
+++ b/Allura/allura/templates/update_emails_form.html
@@ -0,0 +1,68 @@
+{#-
+       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.
+-#}
+<form action="update_emails" method="post" name="update-email">
+  {% for a in c.user.email_addresses %}
+    <input name="addr-{{loop.index0}}.ord" value="{{loop.index0}}" type="hidden"/>
+  {% endfor %}
+  <table class="grid-22">
+    <tr>
+      <th>Primary?</th>
+      <th>Address</th>
+      <th>Confirmed</th>
+      <th></th>
+    </tr>
+    {% for a in c.user.email_addresses %}
+    <tr>
+      {% set obj = c.user.address_object(a) %}
+      {% if obj.confirmed %}
+        <td>{{lib.radio_button('primary_addr', None, a, c.user.preferences.email_address)}}</td>
+      {% else %}
+        <td> <input type="radio" disabled="disabled"></td>
+      {% endif %}
+      <td>{{a}}</td>
+      {% if obj %}
+      <td>
+        {% if obj.confirmed %}
+          yes
+        {% else %}
+          no (<a href="/auth/send_verification_link?a={{a}}">verify</a>)
+        {% endif %}
+      </td>
+      {% else %}
+        <td>Unknown addr obj {{a}}</td>
+      {% endif %}
+      <td>
+        <div class="addr-delete">
+          {{lib.submit_button('Delete', 'addr-%s.delete' % loop.index0)}}
+          {{lib.hidden_field('addr-%s.delete' % loop.index0, '')}}
+        </div>
+      </td>
+    </tr>
+    {% endfor %}
+    <tr>
+      <td colspan="2">{{lib.text_field('new_addr.addr', '')}}</td>
+      <td colspan="2">{{lib.submit_button('Claim New Address', name='new_addr.claim')}}</td>
+    </tr>
+  </table>
+  <div class="grid-22">
+    {{lib.submit_button('Save', name='addr-save')}}
+  </div>
+  {{lib.hidden_field('password', '')}}
+  {{lib.csrf_token()}}
+</form>

http://git-wip-us.apache.org/repos/asf/allura/blob/2c49164c/Allura/allura/templates/user_prefs.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/user_prefs.html b/Allura/allura/templates/user_prefs.html
index 253174f..a19ae4e 100644
--- a/Allura/allura/templates/user_prefs.html
+++ b/Allura/allura/templates/user_prefs.html
@@ -41,84 +41,33 @@
 
     {% block edit_prefs_form %}
       {% if h.asbool(tg.config.get('auth.allow_edit_prefs', True)) %}
-      <form action="update" method="post" name="update-email">
         <fieldset>
-          <legend>General and Email Settings</legend>
-          <label class="grid-4">Display Name</label>
-          <div class="grid-18">
-            <input name="preferences.display_name" value="{{c.user.display_name}}" type="text">
-          </div>
-          <label class="grid-4">Page Size</label>
-          <div class="grid-18">
-            <select name="preferences.results_per_page">
-              {% for per_page in [25, 50, 100, 250] %}
-                  <option {% if per_page == c.user.preferences.results_per_page %}selected="selected"{% endif %}
-                     value="{{per_page}}">{{per_page}}</option>
-              {% endfor %}
-            </select>
-          </div>
+          <legend>General Settings</legend>
+          <form action="update" method="POST">
+            <label class="grid-4">Display Name</label>
+            <div class="grid-18">
+              <input name="preferences.display_name" value="{{c.user.display_name}}" type="text">
+            </div>
+            <label class="grid-4">Page Size</label>
+            <div class="grid-18">
+              <select name="preferences.results_per_page">
+                {% for per_page in [25, 50, 100, 250] %}
+                    <option {% if per_page == c.user.preferences.results_per_page %}selected="selected"{% endif %}
+                       value="{{per_page}}">{{per_page}}</option>
+                {% endfor %}
+              </select>
+            </div>
+            <div class="grid-22">
+              {{lib.submit_button('Save')}}
+            </div>
+            {{lib.csrf_token()}}
+          </form>
+        </fieldset>
 
-          {% for a in c.user.email_addresses %}
-            <input name="addr-{{loop.index0}}.ord" value="{{loop.index0}}" type="hidden"/>
-          {% endfor %}
-          {#
-           # This is a hidden copy of a 'Save' submit button.
-           # We need this because form uses several submit buttons, and
-           # if user presses 'Enter' in one of the fields, browser chooses *first* submit button.
-           # In the case when user has at least one email address, first button is delete button
-           # for first email address. So user ends up deleting their first email address,
-           # instead of changing display name, for example.
-           #}
-          {{lib.submit_button('Save', style='display:none')}}
-          <hr>
-          <h3>Email Addresses</h3>
-          <table class="grid-22">
-            <tr>
-              <th>Primary?</th>
-              <th>Address</th>
-              <th>Confirmed</th>
-              <th></th>
-            </tr>
-            {% for a in c.user.email_addresses %}
-            <tr>
-              {% set obj = c.user.address_object(a) %}
-              {% if obj.confirmed %}
-                <td>{{lib.radio_button('primary_addr', None, a, c.user.preferences.email_address)}}</td>
-              {% else %}
-                <td> <input type="radio" disabled="disabled"></td>
-              {% endif %}
-              <td>{{a}}</td>
-              {% if obj %}
-              <td>
-                {% if obj.confirmed %}
-                  yes
-                {% else %}
-                  no (<a href="/auth/send_verification_link?a={{a}}">verify</a>)
-                {% endif %}
-              </td>
-              {% else %}
-                <td>Unknown addr obj {{a}}</td>
-              {% endif %}
-              <td>
-                <div class="addr-delete">
-                  {{lib.submit_button('Delete', 'addr-%s.delete' % loop.index0)}}
-                  {{lib.hidden_field('addr-%s.delete' % loop.index0, '')}}
-                </div>
-              </td>
-            </tr>
-            {% endfor %}
-            <tr>
-              <td colspan="2">{{lib.text_field('new_addr.addr', '')}}</td>
-              <td colspan="2">{{lib.submit_button('Claim New Address', name='new_addr.claim')}}</td>
-            </tr>
-          </table>
-          <div class="grid-22">
-            {{lib.submit_button('Save', name='addr-save')}}
-          </div>
-          {{lib.hidden_field('password', '')}}
-          {{lib.csrf_token()}}
+        <fieldset>
+          <legend>Email addresses</legend>
+          {% include 'allura:templates/update_emails_form.html' %}
         </fieldset>
-      </form>
 
       <!-- popup -->
       <form class="cur_password" style="display:none">
@@ -190,6 +139,8 @@
 {% endblock %}
 
 {% block extra_js %}
+  {% if h.asbool(tg.config.get('auth.allow_edit_prefs', True)) %}
+  {# js to ask for a current password on the email form #}
   <script type="text/javascript">
       $(function() {
 
@@ -238,4 +189,5 @@
         });
       });
   </script>
+  {% endif %}
  {% endblock %}


[05/27] git commit: [#7657] ticket:650 Fix tests

Posted by br...@apache.org.
[#7657] ticket:650 Fix tests


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/dfbf4f7b
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/dfbf4f7b
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/dfbf4f7b

Branch: refs/heads/db/7657
Commit: dfbf4f7bf9986572c4cd4dd5cd87d7d40370b362
Parents: 65f055f
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Sep 12 15:17:59 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:44 2014 +0000

----------------------------------------------------------------------
 .../allura/tests/functional/test_site_admin.py  | 34 ++++++++++++++++++++
 1 file changed, 34 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/dfbf4f7b/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index 8b8e55f..5ad00af 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -353,6 +353,40 @@ class TestUserDetails(TestController):
         assert_in('Test Project', projects)
         assert_in('Adobe project 1', projects)
 
+    @patch('allura.model.auth.request')
+    def test_audit_log(self, request):
+        request.url = 'http://host.domain/path/'
+        c.user = M.User.by_username('test-user-1')
+        h.auditlog_user('test activity user 1')
+        h.auditlog_user('test activity user 2', user=M.User.by_username('test-user-2'))
+        r = self.app.get('/nf/admin/user/test-admin')
+        assert_in('Add comment', r)
+        assert_not_in('test activity', r)
+        r = self.app.get('/nf/admin/user/test-user-1')
+        assert_in('test activity user 1', r)
+        assert_not_in('test activity user 2', r)
+        r = self.app.get('/nf/admin/user/test-user-2')
+        assert_not_in('test activity user 1', r)
+        assert_in('test activity user 2', r)
+
+    def test_add_audit_trail_entry_access(self):
+        self.app.get('/nf/admin/user/add_audit_log_entry', status=404)  # GET is not allowed
+        r = self.app.post('/nf/admin/user/add_audit_log_entry',
+                          extra_environ={'username': '*anonymous'},
+                          status=302)
+        assert_equal(r.location, 'http://localhost/auth/')
+
+    def test_add_comment(self):
+        r = self.app.get('/nf/admin/user/test-user')
+        assert_not_in(u'Comment by test-admin: I was hêre!', r)
+        form = r.forms[0]
+        assert_equal(form['username'].value, 'test-user')
+        form['comment'] = u'I was hêre!'
+        r = form.submit()
+        assert_in(u'Comment added', self.webflash(r))
+        r = self.app.get('/nf/admin/user/test-user')
+        assert_in(u'Comment by test-admin: I was hêre!', r)
+
 
 @task
 def test_task(*args, **kw):


[02/27] git commit: [#7657] ticket:649 Display dates better

Posted by br...@apache.org.
[#7657] ticket:649 Display dates better


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/e7540100
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/e7540100
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/e7540100

Branch: refs/heads/db/7657
Commit: e7540100e04caec7a9259ae7022033865027a647
Parents: 5a4bc10
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Sep 12 11:50:23 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:43 2014 +0000

----------------------------------------------------------------------
 Allura/allura/model/auth.py                          | 7 +++++--
 Allura/allura/templates/site_admin_user_details.html | 6 +++---
 2 files changed, 8 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/e7540100/Allura/allura/model/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index d110412..32f9b17 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -734,8 +734,11 @@ class User(MappedClass, ActivityNode, ActivityObject, SearchIndexable):
 
     def registration_date(self):
         p = plugin.AuthenticationProvider.get(request)
-        return p.user_registration_date(self)
-
+        d = p.user_registration_date(self)
+        # provider's user_registration_date returns aware datetime (in UTC)
+        # but we're using naive UTC time everywhere
+        d = datetime.utcfromtimestamp(calendar.timegm(d.utctimetuple()))
+        return d
 
 class OldProjectRole(MappedClass):
 

http://git-wip-us.apache.org/repos/asf/allura/blob/e7540100/Allura/allura/templates/site_admin_user_details.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_user_details.html b/Allura/allura/templates/site_admin_user_details.html
index 9aba434..f2cc97d 100644
--- a/Allura/allura/templates/site_admin_user_details.html
+++ b/Allura/allura/templates/site_admin_user_details.html
@@ -31,7 +31,7 @@
         <ul>
           <li>Username: {{ user.username }}</li>
           <li>Full name: {{ user.get_pref('display_name') }}</li>
-          <li>Registered: {{ user.registration_date() }}</li>
+          <li>Registered: {{ user.registration_date() }} ({{ h.ago(user.registration_date()) }})</li>
         </ul>
       </fieldset>
     </div>
@@ -43,13 +43,13 @@
         <legend>Session</legend>
         <h3>Last login</h3>
         <ul>
-          <li>Date: {{ user.last_access.login_date }}</li>
+          <li>Date: {{ user.last_access.login_date }} ({{ h.ago(user.last_access.login_date) }})</li>
           <li>IP: {{ user.last_access.login_ip }}</li>
           <li>UA: {{ user.last_access.login_ua }}</li>
         </ul>
         <h3>Last page access</h3>
         <ul>
-          <li>Date: {{ user.last_access.session_date }}</li>
+          <li>Date: {{ user.last_access.session_date }} ({{ h.ago(user.last_access.session_date) }})</li>
           <li>IP: {{ user.last_access.session_ip }}</li>
           <li>UA: {{ user.last_access.session_ua }}</li>
         </ul>


[19/27] git commit: [#7657] ticket:651 Send password reset link

Posted by br...@apache.org.
[#7657] ticket:651 Send password reset link


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/dab2b72b
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/dab2b72b
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/dab2b72b

Branch: refs/heads/db/7657
Commit: dab2b72b9c57802fe4d223a690c2d7677e8c81a8
Parents: 2a5d7de
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Sep 17 11:56:19 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 18:30:46 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/site_admin.py         | 15 +++++++++++-
 .../templates/site_admin_user_details.html      | 16 +++++++++----
 .../allura/tests/functional/test_site_admin.py  | 25 +++++++++++++++++++-
 3 files changed, 49 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/dab2b72b/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index ef75906..271e50b 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -28,7 +28,7 @@ from pylons import app_globals as g
 from pylons import tmpl_context as c
 from pylons import request
 from formencode import validators, Invalid
-from webob.exc import HTTPNotFound
+from webob.exc import HTTPNotFound, HTTPFound
 
 from allura.app import SitemapEntry
 from allura.lib import helpers as h
@@ -537,6 +537,19 @@ class AdminUserDetailsController(object):
         flash('Password is set', 'ok')
         redirect(request.referer)
 
+    @expose()
+    @require_post()
+    def send_password_reset_link(self, username=None):
+        user = M.User.by_username(username)
+        if not user or user.is_anonymous():
+            raise HTTPNotFound()
+        email = user.get_pref('email_address')
+        try:
+            allura.controllers.auth.AuthController().password_recovery_hash(email)
+        except HTTPFound:
+            pass  # catch redirect to '/'
+        redirect(request.referer)
+
     @h.vardec
     @expose()
     @require_post()

http://git-wip-us.apache.org/repos/asf/allura/blob/dab2b72b/Allura/allura/templates/site_admin_user_details.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_user_details.html b/Allura/allura/templates/site_admin_user_details.html
index 3f56efd..fbbb0f4 100644
--- a/Allura/allura/templates/site_admin_user_details.html
+++ b/Allura/allura/templates/site_admin_user_details.html
@@ -28,7 +28,7 @@
     <div class="grid-23">
       <fieldset>
         <legend>General</legend>
-        <div class="grid-17">
+        <div class="grid-16">
         <ul>
           <li>Username: {{ user.username }} (<a href="{{ user.url() }}">Go to profile page</a>)</li>
           <li>Full name: {{ user.get_pref('display_name') }}</li>
@@ -36,9 +36,9 @@
         </ul>
         </div>
 
-        <div class="grid-5">
+        <div class="grid-6">
         <form action='/nf/admin/user/set_status' method="POST">
-          <div class='grid-5'>
+          <div class='grid-6'>
             <label><input type="radio" name="status" value="enable"{% if not user.disabled %} checked="checked"{% endif %}>Enabled</label><br>
             <label><input type="radio" name="status" value="disable"{% if user.disabled %} checked="checked"{% endif %}>Disabled</label>
           </div>
@@ -47,13 +47,19 @@
         </form>
         </div>
 
-        <div class="grid-17">&nbsp;</div>
-        <div class="grid-5">
+        <div class="grid-16">&nbsp;</div>
+        <div class="grid-6">
           <form action='/nf/admin/user/set_random_password' method="POST">
             <input type="submit" value="Set random password">
             <input type='hidden' name='username' value='{{ user.username }}'>
             {{lib.csrf_token()}}
           </form>
+
+          <form action='/nf/admin/user/send_password_reset_link' method="POST">
+            <input type="submit" value="Send password reset link">
+            <input type='hidden' name='username' value='{{ user.username }}'>
+            {{lib.csrf_token()}}
+          </form>
         </div>
       </fieldset>
     </div>

http://git-wip-us.apache.org/repos/asf/allura/blob/dab2b72b/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index ce8209b..ac018dc 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -381,7 +381,7 @@ class TestUserDetails(TestController):
     def test_add_comment(self):
         r = self.app.get('/nf/admin/user/test-user')
         assert_not_in(u'Comment by test-admin: I was hêre!', r)
-        form = r.forms[2]
+        form = r.forms[4]
         assert_equal(form['username'].value, 'test-user')
         form['comment'] = u'I was hêre!'
         r = form.submit()
@@ -479,6 +479,29 @@ class TestUserDetails(TestController):
         new_pwd = M.User.by_username('test-user').password
         assert_not_equal(old_pwd, new_pwd)
 
+    @patch('allura.tasks.mail_tasks.sendsimplemail')
+    @patch('allura.lib.helpers.gen_message_id')
+    def test_send_password_reset_link(self, gen_message_id, sendmail):
+        user = M.User.by_username('test-user')
+        user.set_pref('email_address', 'test-user@example.org')
+        M.EmailAddress(email='test-user@example.org', confirmed=True, claimed_by_user_id=user._id)
+        ThreadLocalORMSession.flush_all()
+        with td.audits('Password recovery link sent to: test-user@example.org', user=True):
+            r = self.app.post('/nf/admin/user/send_password_reset_link', params={'username': 'test-user'})
+        hash = user.get_tool_data('AuthPasswordReset', 'hash')
+        text = '''Your username is test-user
+
+To reset your password on %s, please visit the following URL:
+
+%s/auth/forgotten_password/%s''' % (config['site_name'], config['base_url'], hash)
+        sendmail.post.assert_called_once_with(
+            toaddr='test-user@example.org',
+            fromaddr=config['forgemail.return_path'],
+            reply_to=config['forgemail.return_path'],
+            subject='Allura Password recovery',
+            message_id=gen_message_id(),
+            text=text)
+
 
 @task
 def test_task(*args, **kw):


[14/27] git commit: [#7657] ticket:651 Fix tests failures

Posted by br...@apache.org.
[#7657] ticket:651 Fix tests failures


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/420ef9c2
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/420ef9c2
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/420ef9c2

Branch: refs/heads/db/7657
Commit: 420ef9c2b15754a905b73a62066e7f2633a16ad4
Parents: 6bcbdaf
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 16 11:50:51 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:45 2014 +0000

----------------------------------------------------------------------
 Allura/allura/tests/functional/test_site_admin.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/420ef9c2/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index 5ad00af..aced210 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -354,8 +354,9 @@ class TestUserDetails(TestController):
         assert_in('Adobe project 1', projects)
 
     @patch('allura.model.auth.request')
-    def test_audit_log(self, request):
-        request.url = 'http://host.domain/path/'
+    @patch('allura.lib.helpers.request')
+    def test_audit_log(self, req1, req2):
+        req1.url = req2.url = 'http://host.domain/path/'
         c.user = M.User.by_username('test-user-1')
         h.auditlog_user('test activity user 1')
         h.auditlog_user('test activity user 2', user=M.User.by_username('test-user-2'))
@@ -379,7 +380,7 @@ class TestUserDetails(TestController):
     def test_add_comment(self):
         r = self.app.get('/nf/admin/user/test-user')
         assert_not_in(u'Comment by test-admin: I was hêre!', r)
-        form = r.forms[0]
+        form = r.forms[1]
         assert_equal(form['username'].value, 'test-user')
         form['comment'] = u'I was hêre!'
         r = form.submit()


[06/27] git commit: [#7657] ticket:650 Remove old audit log page

Posted by br...@apache.org.
[#7657] ticket:650 Remove old audit log page


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/65f055f8
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/65f055f8
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/65f055f8

Branch: refs/heads/db/7657
Commit: 65f055f85e14902836d4c5899f1d50ad350781b5
Parents: fd8dec2
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Sep 12 15:11:21 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:44 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/site_admin.py         | 43 ----------------
 .../templates/site_admin_users_audit.html       | 53 --------------------
 2 files changed, 96 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/65f055f8/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index ad224f4..13bbb6e 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -86,7 +86,6 @@ class SiteAdminController(object):
             SitemapEntry('New Projects', base_url + 'new_projects', ui_icon=g.icons['admin']),
             SitemapEntry('Reclone Repo', base_url + 'reclone_repo', ui_icon=g.icons['admin']),
             SitemapEntry('Task Manager', base_url + 'task_manager?state=busy', ui_icon=g.icons['stats']),
-            SitemapEntry('Users Audit Log', base_url + 'users', ui_icon=g.icons['admin']),
             SitemapEntry('Search Projects', base_url + 'search_projects', ui_icon=g.icons['search']),
             SitemapEntry('Search Users', base_url + 'search_users', ui_icon=g.icons['search']),
         ]
@@ -252,48 +251,6 @@ class SiteAdminController(object):
             mount_point = ''
         return dict(prefix=prefix, shortname=shortname, mount_point=mount_point)
 
-    @expose('jinja:allura:templates/site_admin_users_audit.html')
-    def users(self, username=None, limit=25, page=0, **kwargs):
-        user = M.User.by_username(username)
-        limit = int(limit)
-        page = int(page)
-        if user is None or user.is_anonymous():
-            return dict(
-                entries=[],
-                limit=limit,
-                page=page,
-                count=0,
-                username=username)
-        count = M.AuditLog.for_user(user).count()
-        q = M.AuditLog.for_user(user)
-        q = q.sort('timestamp', -1)
-        q = q.skip(page * limit)
-        if count > limit:
-            q = q.limit(limit)
-        else:
-            limit = count
-        c.widget = W.audit
-        return dict(
-            entries=q.all(),
-            limit=limit,
-            page=page,
-            count=count,
-            audit_user=user,
-            username=username)
-
-    @expose()
-    @require_post()
-    def add_audit_trail_entry(self, **kw):
-        username = kw.get('username')
-        comment = kw.get('comment')
-        user = M.User.by_username(username)
-        if user and not user.is_anonymous() and comment:
-            M.AuditLog.comment_user(c.user, comment, user=user)
-            flash('Comment added', 'ok')
-        else:
-            flash('Can not add comment "%s" for user %s' % (comment, user))
-        redirect(request.referer)
-
     def _search(self, model, fields, add_fields, q=None, f=None, page=0, limit=None, **kw):
         all_fields = fields + [(fld, fld) for fld in add_fields]
         c.search_form = W.admin_search_form(all_fields)

http://git-wip-us.apache.org/repos/asf/allura/blob/65f055f8/Allura/allura/templates/site_admin_users_audit.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_users_audit.html b/Allura/allura/templates/site_admin_users_audit.html
deleted file mode 100644
index 7a6859b..0000000
--- a/Allura/allura/templates/site_admin_users_audit.html
+++ /dev/null
@@ -1,53 +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.
--#}
-
-{% extends 'allura:templates/site_admin.html' %}
-{% import 'allura:templates/jinja_master/lib.html' as lib with context %}
-
-{% block content %}
-<div class='grid-19'>
-  <form action='' method='GET'>
-    <div class='grid-7'>
-      <label for='username'>Username:</label>
-      <input name='username' value='{{ username if username else ''}}'>
-    </div>
-    <div class='grid-5'>
-      <input type='submit' value='Show'>
-    </div>
-  </form>
-  {% if audit_user and not audit_user.is_anonymous() %}
-    <form action='/nf/admin/add_audit_trail_entry' method='POST'>
-      <div class='grid-19'>
-        <label for='comment'>Comment:</label>
-      </div>
-      <div class='grid-19'>
-        <textarea name="comment" cols="38" rows="5"></textarea>
-      </div>
-      <div class='grid-5'>
-        <input type='hidden' name='username' value='{{ audit_user.username }}'>
-        <input type='submit' value='Add comment'>
-        {{lib.csrf_token()}}
-      </div>
-    </form>
-  {% endif %}
-  {% if entries %}
-    {{ c.widget.display(entries=entries, limit=limit, page=page, count=count) }}
-  {% endif %}
-</div>
-{% endblock %}


[24/27] git commit: [#7657] more convenient links (profile link already in column 1)

Posted by br...@apache.org.
[#7657] more convenient links (profile link already in column 1)


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/4869d52d
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/4869d52d
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/4869d52d

Branch: refs/heads/db/7657
Commit: 4869d52dcce01aceaf7c656041e3eceb2f9283a5
Parents: a719b44
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Sep 26 21:57:21 2014 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 21:57:42 2014 +0000

----------------------------------------------------------------------
 Allura/allura/lib/plugin.py                          |  1 -
 Allura/allura/templates/site_admin_user_details.html | 10 +++++++++-
 Allura/allura/tests/functional/test_site_admin.py    |  6 +++---
 3 files changed, 12 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/4869d52d/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index 701f95b..52218d7 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -305,7 +305,6 @@ class AuthenticationProvider(object):
         Links will show up at admin user search page.
         '''
         return [
-           (user.url(), 'Public profile'),
            ('/nf/admin/user/%s' % user.username, 'Details/Edit'),
         ]
 

http://git-wip-us.apache.org/repos/asf/allura/blob/4869d52d/Allura/allura/templates/site_admin_user_details.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_user_details.html b/Allura/allura/templates/site_admin_user_details.html
index 18d2dff..0666b5c 100644
--- a/Allura/allura/templates/site_admin_user_details.html
+++ b/Allura/allura/templates/site_admin_user_details.html
@@ -24,6 +24,8 @@
 {% block header %}User Details{% endblock %}
 
 {% block content %}
+   <div class="grid-23"><p><a href="/nf/admin">Back to Site Admin Home</a></p></div>
+
   {% block general_info %}
     <div class="grid-23">
       <fieldset>
@@ -129,7 +131,10 @@
         <legend>Projects</legend>
         <ul>
           {% for p in projects %}
-            <li><a href="{{ p.url() }}">{{p.name}}</a></li>
+            <li><a href="{{ p.url() }}">{{p.name}}</a>
+                <span class="spacer">&ndash;</span>
+                <a href="{{ p.url() }}admin/">Admin</a>
+            </li>
           {% endfor %}
         </ul>
       </fieldset>
@@ -144,6 +149,9 @@
   width: 860px;
   margin-left: 0;
 }
+li .spacer {
+   margin: 0 0.5em;
+}
 </style>
 {% endblock %}
 

http://git-wip-us.apache.org/repos/asf/allura/blob/4869d52d/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index e24e734..197d058 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -307,9 +307,9 @@ class TestUserDetails(TestController):
         # list of projects
         projects = r.html.findAll('fieldset')[-1]
         projects = [e.getText() for e in projects.findAll('li')]
-        assert_in('Test 2', projects)
-        assert_in('Test Project', projects)
-        assert_in('Adobe project 1', projects)
+        assert_in('Test 2&ndash;Admin', projects)
+        assert_in('Test Project&ndash;Admin', projects)
+        assert_in('Adobe project 1&ndash;Admin', projects)
 
     @patch('allura.model.auth.request')
     @patch('allura.lib.helpers.request')


[22/27] git commit: [#7657] prevent users from crafting HTML form param to get admin mode of _update_emails

Posted by br...@apache.org.
[#7657] prevent users from crafting HTML form param to get admin mode of _update_emails


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/492bf8e4
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/492bf8e4
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/492bf8e4

Branch: refs/heads/db/7657
Commit: 492bf8e467dd2973804b3203aa0a81848acef0e1
Parents: ae1dc97
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Sep 26 18:46:31 2014 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 18:46:31 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/auth.py       | 6 ++++--
 Allura/allura/controllers/site_admin.py | 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/492bf8e4/Allura/allura/controllers/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index 87908b8..4d2fb7c 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -434,7 +434,9 @@ class PreferencesController(BaseController):
         menu = provider.account_navigation()
         return dict(menu=menu, user=c.user)
 
-    def _update_emails(self, user, admin=False, **kw):
+    def _update_emails(self, user, admin=False, form_params={}):
+        # not using **kw in method signature, to ensure 'admin' can't be passed in via a form submit
+        kw = form_params
         addr = kw.pop('addr', None)
         new_addr= kw.pop('new_addr', None)
         primary_addr = kw.pop('primary_addr', None)
@@ -498,7 +500,7 @@ class PreferencesController(BaseController):
     @require_post()
     def update_emails(self, **kw):
         if asbool(config.get('auth.allow_edit_prefs', True)):
-            self._update_emails(c.user, **kw)
+            self._update_emails(c.user, form_params=kw)
         redirect('.')
 
     @h.vardec

http://git-wip-us.apache.org/repos/asf/allura/blob/492bf8e4/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index 335a0e6..7090343 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -560,7 +560,7 @@ class AdminUserDetailsController(object):
         user = M.User.by_username(username)
         if not user or user.is_anonymous():
             raise HTTPNotFound()
-        allura.controllers.auth.PreferencesController()._update_emails(user, admin=True, **kw)
+        allura.controllers.auth.PreferencesController()._update_emails(user, admin=True, form_params=kw)
         redirect(request.referer)
 
 


[09/27] git commit: [#7657] ticket:649 Add simple test

Posted by br...@apache.org.
[#7657] ticket:649 Add simple test


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/75143b14
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/75143b14
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/75143b14

Branch: refs/heads/db/7657
Commit: 75143b1458f106ea5f485d201eb1bdee15919a9c
Parents: 143ce80
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Sep 12 12:37:56 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:44 2014 +0000

----------------------------------------------------------------------
 .../allura/tests/functional/test_site_admin.py  | 35 ++++++++++++++++++++
 1 file changed, 35 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/75143b14/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index 5b0a9b0..8b8e55f 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -17,6 +17,7 @@
 #       under the License.
 
 import json
+import datetime as dt
 
 from mock import patch, MagicMock
 from nose.tools import assert_equal, assert_in, assert_not_in
@@ -319,6 +320,40 @@ class TestUsersSearch(TestController):
                            'Disabled?', 'url', 'Details'])
 
 
+class TestUserDetails(TestController):
+
+    def test_404(self):
+        self.app.get('/nf/admin/user/does-not-exist/', status=404)
+
+    def test_general_info(self):
+        user = M.User.by_username('test-admin')
+        user.registration_date = lambda: dt.datetime(2014, 9, 1, 9, 9, 9)
+        user.last_access = {'login_date': dt.datetime(2014, 9, 2, 6, 6, 6),
+                            'login_ua': 'browser of the future 1.0',
+                            'login_ip': '8.8.8.8',
+                            'session_date': dt.datetime(2014, 9, 12, 6, 6, 6),
+                            'session_ua': 'browser of the future 1.1',
+                            'session_ip': '7.7.7.7'}
+        r = self.app.get('/nf/admin/user/test-admin/')
+        # general info
+        assert_in('Username: test-admin', r)
+        assert_in('Full name: Test Admin', r)
+        assert_in('Registered: 2014-09-01 09:09:09', r)
+        # session info
+        assert_in('Date: 2014-09-02 06:06:06', r)
+        assert_in('IP: 8.8.8.8', r)
+        assert_in('UA: browser of the future 1.0', r)
+        assert_in('Date: 2014-09-12 06:06:06', r)
+        assert_in('IP: 7.7.7.7', r)
+        assert_in('UA: browser of the future 1.1', r)
+        # list of projects
+        projects = r.html.findAll('fieldset')[-1]
+        projects = [e.getText() for e in projects.findAll('li')]
+        assert_in('Test 2', projects)
+        assert_in('Test Project', projects)
+        assert_in('Adobe project 1', projects)
+
+
 @task
 def test_task(*args, **kw):
     """test_task doc string"""


[17/27] git commit: [#7657] ticket:651 Add test form admin page email changes

Posted by br...@apache.org.
[#7657] ticket:651 Add test form admin page email changes


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/c28cf1fa
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/c28cf1fa
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/c28cf1fa

Branch: refs/heads/db/7657
Commit: c28cf1fadedea510536c582335c51289172bbd84
Parents: 8e583ea
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 16 17:07:23 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 18:30:43 2014 +0000

----------------------------------------------------------------------
 .../allura/tests/functional/test_site_admin.py  | 59 ++++++++++++++++++++
 1 file changed, 59 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/c28cf1fa/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index 02436eb..2dcfc18 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -28,6 +28,7 @@ from bson import ObjectId
 
 from allura import model as M
 from allura.tests import TestController
+from allura.tests import decorators as td
 from allura.lib import helpers as h
 from allura.lib.decorators import task
 
@@ -413,6 +414,64 @@ class TestUserDetails(TestController):
         assert_in(u'User enabled', self.webflash(r))
         assert_equal(M.User.by_username('test-user').disabled, False)
 
+    def test_emails(self):
+        # add test@example.com
+        with td.audits('New email address: test@example.com', user=True):
+            r = self.app.post('/nf/admin/user/update_emails', params={
+                'username': 'test-user',
+                'new_addr.addr': 'test@example.com',
+                'new_addr.claim': 'Claim Address',
+                'primary_addr': 'test@example.com'},
+                extra_environ=dict(username='test-admin'))
+        r = self.app.get('/nf/admin/user/test-user')
+        assert_in('test@example.com', r)
+        em = M.EmailAddress.query.get(email='test@example.com')
+        assert_equal(em.confirmed, True)
+        user = M.User.query.get(username='test-user')
+        assert_equal(user.get_pref('email_address'), 'test@example.com')
+
+        # add test2@example.com
+        with td.audits('New email address: test2@example.com', user=True):
+            r = self.app.post('/nf/admin/user/update_emails', params={
+                'username': 'test-user',
+                'new_addr.addr': 'test2@example.com',
+                'new_addr.claim': 'Claim Address',
+                'primary_addr': 'test@example.com'},
+                extra_environ=dict(username='test-admin'))
+        r = self.app.get('/nf/admin/user/test-user')
+        assert_in('test2@example.com', r)
+        em = M.EmailAddress.query.get(email='test2@example.com')
+        assert_equal(em.confirmed, True)
+        user = M.User.query.get(username='test-user')
+        assert_equal(user.get_pref('email_address'), 'test@example.com')
+
+        # change primary: test -> test2
+        with td.audits('Primary email changed: test@example.com => test2@example.com', user=True):
+            r = self.app.post('/nf/admin/user/update_emails', params={
+                'username': 'test-user',
+                'new_addr.addr': '',
+                'primary_addr': 'test2@example.com'},
+                extra_environ=dict(username='test-admin'))
+        r = self.app.get('/nf/admin/user/test-user')
+        user = M.User.query.get(username='test-user')
+        assert_equal(user.get_pref('email_address'), 'test2@example.com')
+
+        # remove test2@example.com
+        with td.audits('Email address deleted: test2@example.com', user=True):
+            r = self.app.post('/nf/admin/user/update_emails', params={
+                'username': 'test-user',
+                'addr-1.ord': '1',
+                'addr-2.ord': '2',
+                'addr-2.delete': 'on',
+                'new_addr.addr': '',
+                'primary_addr': 'test2@example.com'},
+                extra_environ=dict(username='test-admin'))
+        r = self.app.get('/nf/admin/user/test-user')
+        user = M.User.query.get(username='test-user')
+        # test@example.com set as primary since test2@example.com is deleted
+        assert_equal(user.get_pref('email_address'), 'test@example.com')
+
+
 @task
 def test_task(*args, **kw):
     """test_task doc string"""


[21/27] git commit: [#7657] Add an extension point for additional user details

Posted by br...@apache.org.
[#7657] Add an extension point for additional user details


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/ae1dc97c
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/ae1dc97c
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/ae1dc97c

Branch: refs/heads/db/7657
Commit: ae1dc97c70b42aff2022dd1125f29235e324985a
Parents: d90674a
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Sep 18 10:48:56 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 18:37:26 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/site_admin.py         |  5 +++-
 Allura/allura/lib/plugin.py                     |  7 ++++++
 .../templates/site_admin_user_details.html      | 24 +++++++++-----------
 3 files changed, 22 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/ae1dc97c/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index 271e50b..335a0e6 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -468,11 +468,14 @@ class AdminUserDetailsController(object):
             raise HTTPNotFound()
         projects = user.my_projects().all()
         audit_log = self._audit_log(user, limit, page)
-        return {
+        info = {
             'user': user,
             'projects': projects,
             'audit_log': audit_log,
         }
+        p = AuthenticationProvider.get(request)
+        info.update(p.user_details(user))
+        return info
 
     def _audit_log(self, user, limit, page):
         limit = int(limit)

http://git-wip-us.apache.org/repos/asf/allura/blob/ae1dc97c/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index 1186cf6..1ad4ef0 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -277,6 +277,13 @@ class AuthenticationProvider(object):
         '''
         raise NotImplementedError, 'get_last_password_updated'
 
+    def user_details(self, user):
+        '''Returns detailed information about user.
+
+        :param user: a :class:`User <allura.model.auth.User>`
+        '''
+        return {}
+
     def is_password_expired(self, user):
         days = asint(config.get('auth.pwdexpire.days', 0))
         before = asint(config.get('auth.pwdexpire.before', 0))

http://git-wip-us.apache.org/repos/asf/allura/blob/ae1dc97c/Allura/allura/templates/site_admin_user_details.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_user_details.html b/Allura/allura/templates/site_admin_user_details.html
index fbbb0f4..dd771cc 100644
--- a/Allura/allura/templates/site_admin_user_details.html
+++ b/Allura/allura/templates/site_admin_user_details.html
@@ -34,6 +34,7 @@
           <li>Full name: {{ user.get_pref('display_name') }}</li>
           <li>Registered: {{ user.registration_date() }} ({{ h.ago(user.registration_date()) }})</li>
         </ul>
+        {% block extra_general_info %}{% endblock %}
         </div>
 
         <div class="grid-6">
@@ -45,21 +46,18 @@
           <input type='hidden' name='username' value='{{ user.username }}'>
           {{lib.csrf_token()}}
         </form>
-        </div>
 
-        <div class="grid-16">&nbsp;</div>
-        <div class="grid-6">
-          <form action='/nf/admin/user/set_random_password' method="POST">
-            <input type="submit" value="Set random password">
-            <input type='hidden' name='username' value='{{ user.username }}'>
-            {{lib.csrf_token()}}
-          </form>
+        <form action='/nf/admin/user/set_random_password' method="POST">
+          <input type="submit" value="Set random password">
+          <input type='hidden' name='username' value='{{ user.username }}'>
+          {{lib.csrf_token()}}
+        </form>
 
-          <form action='/nf/admin/user/send_password_reset_link' method="POST">
-            <input type="submit" value="Send password reset link">
-            <input type='hidden' name='username' value='{{ user.username }}'>
-            {{lib.csrf_token()}}
-          </form>
+        <form action='/nf/admin/user/send_password_reset_link' method="POST">
+          <input type="submit" value="Send password reset link">
+          <input type='hidden' name='username' value='{{ user.username }}'>
+          {{lib.csrf_token()}}
+        </form>
         </div>
       </fieldset>
     </div>


[25/27] git commit: [#7657] show page access at day granularity, since thats how its tracked

Posted by br...@apache.org.
[#7657] show page access at day granularity, since thats how its tracked


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/2927ea55
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/2927ea55
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/2927ea55

Branch: refs/heads/db/7657
Commit: 2927ea55089c686cae4dc1daedcf0a6427e87c89
Parents: 492bf8e
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Sep 26 20:23:00 2014 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 21:57:42 2014 +0000

----------------------------------------------------------------------
 Allura/allura/templates/site_admin_user_details.html | 6 +++---
 Allura/allura/tests/functional/test_site_admin.py    | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/2927ea55/Allura/allura/templates/site_admin_user_details.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_user_details.html b/Allura/allura/templates/site_admin_user_details.html
index dd771cc..18d2dff 100644
--- a/Allura/allura/templates/site_admin_user_details.html
+++ b/Allura/allura/templates/site_admin_user_details.html
@@ -32,7 +32,7 @@
         <ul>
           <li>Username: {{ user.username }} (<a href="{{ user.url() }}">Go to profile page</a>)</li>
           <li>Full name: {{ user.get_pref('display_name') }}</li>
-          <li>Registered: {{ user.registration_date() }} ({{ h.ago(user.registration_date()) }})</li>
+          <li>Registered: {{ user.registration_date() }} ({{ h.ago(user.registration_date(), show_date_after=None) }})</li>
         </ul>
         {% block extra_general_info %}{% endblock %}
         </div>
@@ -81,13 +81,13 @@
         <legend>Session</legend>
         <h3>Last login</h3>
         <ul>
-          <li>Date: {{ user.last_access.login_date }} ({{ h.ago(user.last_access.login_date) }})</li>
+          <li>Date: {{ user.last_access.login_date }} ({{ h.ago(user.last_access.login_date, show_date_after=None) }})</li>
           <li>IP: {{ user.last_access.login_ip }}</li>
           <li>UA: {{ user.last_access.login_ua }}</li>
         </ul>
         <h3>Last page access</h3>
         <ul>
-          <li>Date: {{ user.last_access.session_date }} ({{ h.ago(user.last_access.session_date) }})</li>
+          <li>Date: {{ user.last_access.session_date and user.last_access.session_date.date() }}</li>
           <li>IP: {{ user.last_access.session_ip }}</li>
           <li>UA: {{ user.last_access.session_ua }}</li>
         </ul>

http://git-wip-us.apache.org/repos/asf/allura/blob/2927ea55/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index 37338d7..793f192 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -301,7 +301,7 @@ class TestUserDetails(TestController):
         assert_in('Date: 2014-09-02 06:06:06', r)
         assert_in('IP: 8.8.8.8', r)
         assert_in('UA: browser of the future 1.0', r)
-        assert_in('Date: 2014-09-12 06:06:06', r)
+        assert_in('Date: 2014-09-12', r)
         assert_in('IP: 7.7.7.7', r)
         assert_in('UA: browser of the future 1.1', r)
         # list of projects


[26/27] git commit: [#7657] skip *anonymous whose None id doesn't work as an ObjectId

Posted by br...@apache.org.
[#7657] skip *anonymous whose None id doesn't work as an ObjectId


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/a3d64fd1
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/a3d64fd1
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/a3d64fd1

Branch: refs/heads/db/7657
Commit: a3d64fd15e777cac2533cfa470f482c889ee8115
Parents: 2927ea5
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Sep 26 20:23:35 2014 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 21:57:42 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/site_admin.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/a3d64fd1/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index 7090343..9f6cc38 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -264,7 +264,8 @@ class SiteAdminController(object):
             if match:
                 count = match.hits
                 objects = match.docs
-                ids = [bson.ObjectId(obj['id'].split('#')[1]) for obj in objects]
+                ids = [obj['id'].split('#')[1] for obj in objects]
+                ids = [bson.ObjectId(_id) for _id in ids if _id != 'None']
                 mongo_objects = {}
                 for obj in model.query.find({'_id': {'$in': ids}}):
                     mongo_objects[str(obj._id)] = obj


[23/27] git commit: [#7657] automatically include user who made change in auditlog, if not same user

Posted by br...@apache.org.
[#7657] automatically include user who made change in auditlog, if not same user


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/a719b443
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/a719b443
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/a719b443

Branch: refs/heads/db/7657
Commit: a719b4439dae253a641c5fdcc62957b1a0efe2b0
Parents: a3d64fd
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Sep 26 21:16:06 2014 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 21:57:42 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/site_admin.py           |  2 +-
 Allura/allura/lib/helpers.py                      |  2 ++
 Allura/allura/lib/plugin.py                       | 10 ++--------
 Allura/allura/tests/decorators.py                 |  8 ++++++--
 Allura/allura/tests/functional/test_site_admin.py |  2 +-
 Allura/allura/tests/test_plugin.py                |  4 ++--
 6 files changed, 14 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/a719b443/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index 9f6cc38..5b84267 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -537,7 +537,7 @@ class AdminUserDetailsController(object):
             raise HTTPNotFound()
         pwd = h.random_password()
         AuthenticationProvider.get(request).set_password(user, None, pwd)
-        h.auditlog_user('Set random password by %s', c.user.username, user=user)
+        h.auditlog_user('Set random password', user=user)
         flash('Password is set', 'ok')
         redirect(request.referer)
 

http://git-wip-us.apache.org/repos/asf/allura/blob/a719b443/Allura/allura/lib/helpers.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/helpers.py b/Allura/allura/lib/helpers.py
index 45ff1fe..166146b 100644
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -1208,5 +1208,7 @@ def auditlog_user(message, *args, **kwargs):
     from allura import model as M
     ip_address = request.headers.get('X-Remote-Addr', request.remote_addr)
     message = 'IP Address: {}\n'.format(ip_address) + message
+    if kwargs.get('user') and kwargs['user'] != c.user:
+        message = 'Done by user: {}\n'.format(c.user.username) + message
     M.AuditLog.log_user(message, *args, **kwargs)
 

http://git-wip-us.apache.org/repos/asf/allura/blob/a719b443/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index 1ad4ef0..701f95b 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -335,18 +335,12 @@ class LocalAuthenticationProvider(AuthenticationProvider):
     def disable_user(self, user):
         user.disabled = True
         session(user).flush(user)
-        suffix = u''
-        if user != c.user:
-            suffix = u' by %s' % c.user.username
-        h.auditlog_user(u'Account disabled' + suffix, user=user)
+        h.auditlog_user(u'Account disabled', user=user)
 
     def enable_user(self, user):
         user.disabled = False
         session(user).flush(user)
-        suffix = u''
-        if user != c.user:
-            suffix = u' by %s' % c.user.username
-        h.auditlog_user(u'Account enabled' + suffix, user=user)
+        h.auditlog_user(u'Account enabled', user=user)
 
     def validate_password(self, user, password):
         return self._validate_password(user, password)

http://git-wip-us.apache.org/repos/asf/allura/blob/a719b443/Allura/allura/tests/decorators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/decorators.py b/Allura/allura/tests/decorators.py
index df0f0b9..46369d1 100644
--- a/Allura/allura/tests/decorators.py
+++ b/Allura/allura/tests/decorators.py
@@ -165,7 +165,9 @@ def audits(*messages, **kwargs):
     M.AuditLog.query.remove()
     yield
     if kwargs.get('user'):
-        preamble = 'IP Address: .*\n'
+        actor = kwargs.get('actor', '.*')
+        ip_addr = kwargs.get('ip_addr', '.*')
+        preamble = '(Done by user: {}\n)?IP Address: {}\n'.format(actor, ip_addr)
     else:
         preamble = ''
     for message in messages:
@@ -185,7 +187,9 @@ def out_audits(*messages, **kwargs):
     M.AuditLog.query.remove()
     yield
     if kwargs.get('user'):
-        preamble = 'IP Address: .*\n'
+        actor = kwargs.get('actor', '.*')
+        ip_addr = kwargs.get('ip_addr', '.*')
+        preamble = '(Done by user: {}\n)?IP Address: {}\n'.format(actor, ip_addr)
     else:
         preamble = ''
     for message in messages:

http://git-wip-us.apache.org/repos/asf/allura/blob/a719b443/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index 793f192..e24e734 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -430,7 +430,7 @@ class TestUserDetails(TestController):
 
     @patch.object(LocalAuthenticationProvider, 'set_password')
     def test_set_random_password(self, set_password):
-        with td.audits('Set random password by test-admin', user=True):
+        with td.audits('Set random password', user=True, actor='test-admin'):
             r = self.app.post('/nf/admin/user/set_random_password', params={'username': 'test-user'})
         assert_in('Password is set', self.webflash(r))
         set_password.assert_called_once()

http://git-wip-us.apache.org/repos/asf/allura/blob/a719b443/Allura/allura/tests/test_plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_plugin.py b/Allura/allura/tests/test_plugin.py
index 4e9e02c..b3073f3 100644
--- a/Allura/allura/tests/test_plugin.py
+++ b/Allura/allura/tests/test_plugin.py
@@ -259,7 +259,7 @@ class TestLocalAuthenticationProvider(object):
     def test_enable_user(self):
         user = Mock(disabled=True, __ming__=Mock(), is_anonymous=lambda: False, _id=ObjectId())
         c.user = Mock(username='test-admin')
-        with audits('Account enabled by test-admin', user=True):
+        with audits('Account enabled', user=True, actor='test-admin'):
             self.provider.enable_user(user)
             ThreadLocalORMSession.flush_all()
         assert_equal(user.disabled, False)
@@ -267,7 +267,7 @@ class TestLocalAuthenticationProvider(object):
     def test_disable_user(self):
         user = Mock(disabled=False, __ming__=Mock(), is_anonymous=lambda: False, _id=ObjectId())
         c.user = Mock(username='test-admin')
-        with audits('Account disabled by test-admin', user=True):
+        with audits('Account disabled', user=True, actor='test-admin'):
             self.provider.disable_user(user)
             ThreadLocalORMSession.flush_all()
         assert_equal(user.disabled, True)


[13/27] git commit: [#7657] ticket:651 Add tests for enable/disable user

Posted by br...@apache.org.
[#7657] ticket:651 Add tests for enable/disable user


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/46cafe00
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/46cafe00
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/46cafe00

Branch: refs/heads/db/7657
Commit: 46cafe008c6dd0a09df8c868247419b18ba8b3a7
Parents: 420ef9c
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 16 12:14:43 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:45 2014 +0000

----------------------------------------------------------------------
 .../allura/tests/functional/test_site_admin.py  | 24 ++++++++++++++++++++
 Allura/allura/tests/test_plugin.py              | 17 ++++++++++++++
 2 files changed, 41 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/46cafe00/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index aced210..d51ffc9 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -388,6 +388,30 @@ class TestUserDetails(TestController):
         r = self.app.get('/nf/admin/user/test-user')
         assert_in(u'Comment by test-admin: I was hêre!', r)
 
+    def test_disable_user(self):
+        assert_equal(M.User.by_username('test-user').disabled, False)
+        r = self.app.get('/nf/admin/user/test-user')
+        form = r.forms[0]
+        assert_equal(form['username'].value, 'test-user')
+        assert_equal(form['status'].value, 'enable')
+        form['status'].value = 'disable'
+        r = form.submit()
+        assert_in(u'User disabled', self.webflash(r))
+        assert_equal(M.User.by_username('test-user').disabled, True)
+
+    def test_enable_user(self):
+        user = M.User.by_username('test-user')
+        user.disabled = True
+        ThreadLocalORMSession.flush_all()
+        assert_equal(M.User.by_username('test-user').disabled, True)
+        r = self.app.get('/nf/admin/user/test-user')
+        form = r.forms[0]
+        assert_equal(form['username'].value, 'test-user')
+        assert_equal(form['status'].value, 'disable')
+        form['status'].value = 'enable'
+        r = form.submit()
+        assert_in(u'User enabled', self.webflash(r))
+        assert_equal(M.User.by_username('test-user').disabled, False)
 
 @task
 def test_task(*args, **kw):

http://git-wip-us.apache.org/repos/asf/allura/blob/46cafe00/Allura/allura/tests/test_plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_plugin.py b/Allura/allura/tests/test_plugin.py
index c19c931..4e9e02c 100644
--- a/Allura/allura/tests/test_plugin.py
+++ b/Allura/allura/tests/test_plugin.py
@@ -19,6 +19,7 @@ import datetime as dt
 import calendar
 
 import tg
+from pylons import tmpl_context as c
 from webob import Request, exc
 from bson import ObjectId
 from ming.orm.ormsession import ThreadLocalORMSession
@@ -41,6 +42,7 @@ from allura.lib.utils import TruthyCallable
 from allura.lib.plugin import ProjectRegistrationProvider
 from allura.lib.plugin import ThemeProvider
 from allura.lib.exceptions import ProjectConflict, ProjectShortnameInvalid
+from allura.tests.decorators import audits
 from alluratest.controller import setup_basic_test, setup_global_objects
 
 
@@ -254,6 +256,21 @@ class TestLocalAuthenticationProvider(object):
         upd = self.provider.get_last_password_updated(user)
         assert_equal(upd, user.last_password_updated)
 
+    def test_enable_user(self):
+        user = Mock(disabled=True, __ming__=Mock(), is_anonymous=lambda: False, _id=ObjectId())
+        c.user = Mock(username='test-admin')
+        with audits('Account enabled by test-admin', user=True):
+            self.provider.enable_user(user)
+            ThreadLocalORMSession.flush_all()
+        assert_equal(user.disabled, False)
+
+    def test_disable_user(self):
+        user = Mock(disabled=False, __ming__=Mock(), is_anonymous=lambda: False, _id=ObjectId())
+        c.user = Mock(username='test-admin')
+        with audits('Account disabled by test-admin', user=True):
+            self.provider.disable_user(user)
+            ThreadLocalORMSession.flush_all()
+        assert_equal(user.disabled, True)
 
 class TestAuthenticationProvider(object):
 


[15/27] git commit: [#7657] ticket:651 Fix tests failing due to splitting prefs update controllers

Posted by br...@apache.org.
[#7657] ticket:651 Fix tests failing due to splitting prefs update controllers


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/8e583ea7
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/8e583ea7
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/8e583ea7

Branch: refs/heads/db/7657
Commit: 8e583ea761be78e12d78073df8138d54e48cbb87
Parents: 2e86cb1
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 16 15:51:36 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:46 2014 +0000

----------------------------------------------------------------------
 Allura/allura/tests/functional/test_auth.py     | 36 ++++++++------------
 .../allura/tests/functional/test_site_admin.py  |  2 +-
 2 files changed, 15 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/8e583ea7/Allura/allura/tests/functional/test_auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_auth.py b/Allura/allura/tests/functional/test_auth.py
index 39f5794..c5be2d4 100644
--- a/Allura/allura/tests/functional/test_auth.py
+++ b/Allura/allura/tests/functional/test_auth.py
@@ -143,9 +143,8 @@ class TestAuth(TestController):
         email_address = 'test_abcd_123@domain.net'
         user = M.User.query.get(username='test-admin')
         addresses_number = len(user.email_addresses)
-        self.app.post('/auth/preferences/update',
+        self.app.post('/auth/preferences/update_emails',
                       params={
-                          'preferences.display_name': 'Test Admin',
                           'new_addr.addr': email_address,
                           'new_addr.claim': 'Claim Address',
                           'primary_addr': 'test-admin@users.localhost',
@@ -155,9 +154,8 @@ class TestAuth(TestController):
                       extra_environ=dict(username='test-admin'))
 
         assert M.EmailAddress.query.find(dict(email=email_address, claimed_by_user_id=user._id)).count() == 1
-        r = self.app.post('/auth/preferences/update',
+        r = self.app.post('/auth/preferences/update_emails',
                           params={
-                              'preferences.display_name': 'Test Admin',
                               'new_addr.addr': email_address,
                               'new_addr.claim': 'Claim Address',
                               'primary_addr': 'test-admin@users.localhost',
@@ -182,8 +180,7 @@ class TestAuth(TestController):
 
         # add test@example
         with td.audits('New email address: test@example.com', user=True):
-            r = self.app.post('/auth/preferences/update', params={
-                'preferences.display_name': 'Test Admin',
+            r = self.app.post('/auth/preferences/update_emails', params={
                 'new_addr.addr': 'test@example.com',
                 'new_addr.claim': 'Claim Address',
                 'primary_addr': 'test-admin@users.localhost',
@@ -197,8 +194,7 @@ class TestAuth(TestController):
 
         # remove test-admin@users.localhost
         with td.audits('Email address deleted: test-admin@users.localhost', user=True):
-            r = self.app.post('/auth/preferences/update', params={
-                'preferences.display_name': 'Test Admin',
+            r = self.app.post('/auth/preferences/update_emails', params={
                 'addr-1.ord': '1',
                 'addr-1.delete': 'on',
                 'addr-2.ord': '2',
@@ -215,75 +211,71 @@ class TestAuth(TestController):
 
         with td.audits('Display Name changed Test Admin => Admin', user=True):
             r = self.app.post('/auth/preferences/update', params={
-                'preferences.display_name': 'Admin',
-                'new_addr.addr': ''},
+                'preferences.display_name': 'Admin'},
                 extra_environ=dict(username='test-admin'))
 
     @td.with_user_project('test-admin')
     def test_email_prefs_change_requires_password(self):
         # Claim new email
         new_email_params = {
-            'preferences.display_name': 'Test Admin',
             'new_addr.addr': 'test@example.com',
             'new_addr.claim': 'Claim Address',
             'primary_addr': 'test-admin@users.localhost',
         }
-        r = self.app.post('/auth/preferences/update', params=new_email_params,
+        r = self.app.post('/auth/preferences/update_emails', params=new_email_params,
             extra_environ=dict(username='test-admin'))
         assert_in('You must provide your current password to claim new email', self.webflash(r))
         assert_not_in('test@example.com', r.follow())
         new_email_params['password'] = 'bad pass'
-        r = self.app.post('/auth/preferences/update', params=new_email_params,
+        r = self.app.post('/auth/preferences/update_emails', params=new_email_params,
             extra_environ=dict(username='test-admin'))
         assert_in('You must provide your current password to claim new email', self.webflash(r))
         assert_not_in('test@example.com', r.follow())
         new_email_params['password'] = 'foo'  # valid password
-        r = self.app.post('/auth/preferences/update', params=new_email_params,
+        r = self.app.post('/auth/preferences/update_emails', params=new_email_params,
             extra_environ=dict(username='test-admin'))
         assert_not_in('You must provide your current password to claim new email', self.webflash(r))
         assert_in('test@example.com', r.follow())
 
         # Change primary address
         change_primary_params = {
-            'preferences.display_name': 'Test Admin',
             'new_addr.addr': '',
             'primary_addr': 'test@example.com',
         }
-        r = self.app.post('/auth/preferences/update', params=change_primary_params,
+        r = self.app.post('/auth/preferences/update_emails', params=change_primary_params,
             extra_environ=dict(username='test-admin'))
         assert_in('You must provide your current password to change primary address', self.webflash(r))
         assert_equal(M.User.by_username('test-admin').get_pref('email_address'), 'test-admin@users.localhost')
         change_primary_params['password'] = 'bad pass'
-        r = self.app.post('/auth/preferences/update', params=change_primary_params,
+        r = self.app.post('/auth/preferences/update_emails', params=change_primary_params,
             extra_environ=dict(username='test-admin'))
         assert_in('You must provide your current password to change primary address', self.webflash(r))
         assert_equal(M.User.by_username('test-admin').get_pref('email_address'), 'test-admin@users.localhost')
         change_primary_params['password'] = 'foo'  # valid password
-        r = self.app.post('/auth/preferences/update', params=change_primary_params,
+        r = self.app.post('/auth/preferences/update_emails', params=change_primary_params,
             extra_environ=dict(username='test-admin'))
         assert_not_in('You must provide your current password to change primary address', self.webflash(r))
         assert_equal(M.User.by_username('test-admin').get_pref('email_address'), 'test@example.com')
 
         # Remove email
         remove_email_params = {
-            'preferences.display_name': 'Test Admin',
             'addr-1.ord': '1',
             'addr-2.ord': '2',
             'addr-2.delete': 'on',
             'new_addr.addr': '',
             'primary_addr': 'test-admin@users.localhost',
         }
-        r = self.app.post('/auth/preferences/update', params=remove_email_params,
+        r = self.app.post('/auth/preferences/update_emails', params=remove_email_params,
             extra_environ=dict(username='test-admin'))
         assert_in('You must provide your current password to delete an email', self.webflash(r))
         assert_in('test@example.com', r.follow())
         remove_email_params['password'] = 'bad pass'
-        r = self.app.post('/auth/preferences/update', params=remove_email_params,
+        r = self.app.post('/auth/preferences/update_emails', params=remove_email_params,
             extra_environ=dict(username='test-admin'))
         assert_in('You must provide your current password to delete an email', self.webflash(r))
         assert_in('test@example.com', r.follow())
         remove_email_params['password'] = 'foo'  # vallid password
-        r = self.app.post('/auth/preferences/update', params=remove_email_params,
+        r = self.app.post('/auth/preferences/update_emails', params=remove_email_params,
             extra_environ=dict(username='test-admin'))
         assert_not_in('You must provide your current password to delete an email', self.webflash(r))
         assert_not_in('test@example.com', r.follow())

http://git-wip-us.apache.org/repos/asf/allura/blob/8e583ea7/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index d51ffc9..02436eb 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -380,7 +380,7 @@ class TestUserDetails(TestController):
     def test_add_comment(self):
         r = self.app.get('/nf/admin/user/test-user')
         assert_not_in(u'Comment by test-admin: I was hêre!', r)
-        form = r.forms[1]
+        form = r.forms[2]
         assert_equal(form['username'].value, 'test-user')
         form['comment'] = u'I was hêre!'
         r = form.submit()


[04/27] git commit: [#7657] ticket:649 Display basic user info

Posted by br...@apache.org.
[#7657] ticket:649 Display basic user info


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/f54be441
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/f54be441
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/f54be441

Branch: refs/heads/db/7657
Commit: f54be4410cfd44723bd1550673549c8ac36f705b
Parents: da05789
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Sep 12 11:36:29 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:43 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/site_admin.py         | 15 ++++
 Allura/allura/model/auth.py                     |  5 ++
 .../templates/site_admin_user_details.html      | 75 ++++++++++++++++++++
 3 files changed, 95 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/f54be441/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index 8c56f9f..b61db8b 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -62,6 +62,7 @@ class SiteAdminController(object):
     def __init__(self):
         self.task_manager = TaskManagerController()
         c.site_admin_sidebar_menu = self.sidebar_menu()
+        self.user = AdminUserDetailsController()
 
     def _check_security(self):
         with h.push_context(config.get('site_admin_project', 'allura'),
@@ -501,6 +502,20 @@ class StatsController(object):
         return dict(neighborhoods=neighborhoods)
 
 
+class AdminUserDetailsController(object):
+
+    @expose('jinja:allura:templates/site_admin_user_details.html')
+    def _default(self, username):
+        user = M.User.by_username(username)
+        if not user:
+            raise HTTPNotFound()
+        projects = user.my_projects().all()
+        return {
+            'user': user,
+            'projects': projects,
+        }
+
+
 class StatsSiteAdminExtension(SiteAdminExtension):
     controllers = {'stats': StatsController}
 

http://git-wip-us.apache.org/repos/asf/allura/blob/f54be441/Allura/allura/model/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index 4460636..d110412 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -22,6 +22,7 @@ import logging
 import urllib
 import hmac
 import hashlib
+import calendar
 from urlparse import urlparse
 from email import header
 from hashlib import sha256
@@ -731,6 +732,10 @@ class User(MappedClass, ActivityNode, ActivityObject, SearchIndexable):
             url=h.absurl(self.url()),
         )
 
+    def registration_date(self):
+        p = plugin.AuthenticationProvider.get(request)
+        return p.user_registration_date(self)
+
 
 class OldProjectRole(MappedClass):
 

http://git-wip-us.apache.org/repos/asf/allura/blob/f54be441/Allura/allura/templates/site_admin_user_details.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_user_details.html b/Allura/allura/templates/site_admin_user_details.html
new file mode 100644
index 0000000..9aba434
--- /dev/null
+++ b/Allura/allura/templates/site_admin_user_details.html
@@ -0,0 +1,75 @@
+{#-
+       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.
+-#}
+{% set page = 'user_details' %}
+{% extends 'allura:templates/site_admin.html' %}
+{% set hide_left_bar=True %}
+
+{% block title %}User Details{% endblock %}
+{% block header %}User Details{% endblock %}
+
+{% block content %}
+  {% block general_info %}
+    <div class="grid-23">
+      <fieldset>
+        <legend>General</legend>
+        <ul>
+          <li>Username: {{ user.username }}</li>
+          <li>Full name: {{ user.get_pref('display_name') }}</li>
+          <li>Registered: {{ user.registration_date() }}</li>
+        </ul>
+      </fieldset>
+    </div>
+  {% endblock general_info %}
+
+  {% block session_info %}
+    <div class="grid-23">
+      <fieldset>
+        <legend>Session</legend>
+        <h3>Last login</h3>
+        <ul>
+          <li>Date: {{ user.last_access.login_date }}</li>
+          <li>IP: {{ user.last_access.login_ip }}</li>
+          <li>UA: {{ user.last_access.login_ua }}</li>
+        </ul>
+        <h3>Last page access</h3>
+        <ul>
+          <li>Date: {{ user.last_access.session_date }}</li>
+          <li>IP: {{ user.last_access.session_ip }}</li>
+          <li>UA: {{ user.last_access.session_ua }}</li>
+        </ul>
+      </fieldset>
+    </div>
+  {% endblock session_info %}
+
+  {% block extra_info %}
+  {% endblock extra_info %}
+
+  {% block user_projects %}
+    <div class="grid-23">
+      <fieldset>
+        <legend>Projects</legend>
+        <ul>
+          {% for p in projects %}
+            <li><a href="{{ p.url() }}">{{p.name}}</a></li>
+          {% endfor %}
+        </ul>
+      </fieldset>
+    </div>
+  {% endblock user_projects %}
+{% endblock %}


[08/27] git commit: [#7657] ticket:650 Add audit log to user details page

Posted by br...@apache.org.
[#7657] ticket:650 Add audit log to user details page


Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/90835ebc
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/90835ebc
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/90835ebc

Branch: refs/heads/db/7657
Commit: 90835ebccea26b5378a3eba247b376f80351bc26
Parents: 75143b1
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Sep 12 14:04:20 2014 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 26 16:39:44 2014 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/site_admin.py         | 28 +++++++++++++++++++-
 .../ext/admin/templates/widgets/audit.html      |  4 +--
 .../templates/site_admin_user_details.html      | 22 +++++++++++++++
 3 files changed, 51 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/90835ebc/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index b61db8b..52b4d0f 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -505,16 +505,42 @@ class StatsController(object):
 class AdminUserDetailsController(object):
 
     @expose('jinja:allura:templates/site_admin_user_details.html')
-    def _default(self, username):
+    def _default(self, username, limit=25, page=0):
         user = M.User.by_username(username)
         if not user:
             raise HTTPNotFound()
         projects = user.my_projects().all()
+        audit_log = self._audit_log(user, limit, page)
         return {
             'user': user,
             'projects': projects,
+            'audit_log': audit_log,
         }
 
+    def _audit_log(self, user, limit, page):
+        limit = int(limit)
+        page = int(page)
+        if user is None or user.is_anonymous():
+            return dict(
+                entries=[],
+                imit=limit,
+                page=page,
+                count=0)
+        q = M.AuditLog.for_user(user)
+        count = q.count()
+        q = q.sort('timestamp', -1)
+        q = q.skip(page * limit)
+        if count > limit:
+            q = q.limit(limit)
+        else:
+            limit = count
+        c.audit_log_widget = W.audit
+        return dict(
+            entries=q.all(),
+            limit=limit,
+            page=page,
+            count=count)
+
 
 class StatsSiteAdminExtension(SiteAdminExtension):
     controllers = {'stats': StatsController}

http://git-wip-us.apache.org/repos/asf/allura/blob/90835ebc/Allura/allura/ext/admin/templates/widgets/audit.html
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/templates/widgets/audit.html b/Allura/allura/ext/admin/templates/widgets/audit.html
index 0339be8..5c9dc68 100644
--- a/Allura/allura/ext/admin/templates/widgets/audit.html
+++ b/Allura/allura/ext/admin/templates/widgets/audit.html
@@ -16,7 +16,7 @@
        specific language governing permissions and limitations
        under the License.
 -#}
-<div class="grid-19">
+<div class="grid-{{ grid if grid else '19' }}">
   <div style="overflow:auto">
     <table>
       <thead>
@@ -44,7 +44,7 @@
     </table>
   </div>
 </div>
-<div class="grid-19" style="clear:both">
+<div class="grid-{{ grid if grid else '19' }}" style="clear:both">
   {{widget.fields.page_list.display(limit=limit, page=page, count=count)}}
   {{widget.fields.page_size.display(limit=limit, count=count)}}
 </div>

http://git-wip-us.apache.org/repos/asf/allura/blob/90835ebc/Allura/allura/templates/site_admin_user_details.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/site_admin_user_details.html b/Allura/allura/templates/site_admin_user_details.html
index f86e75c..689e295 100644
--- a/Allura/allura/templates/site_admin_user_details.html
+++ b/Allura/allura/templates/site_admin_user_details.html
@@ -60,6 +60,18 @@
   {% block extra_info %}
   {% endblock extra_info %}
 
+  {% block audit_log %}
+    <div class="grid-23">
+      {% set al = audit_log %}
+      <fieldset>
+        <legend>Audit log</legend>
+        {% if al['entries'] %}
+          {{ c.audit_log_widget.display(entries=al['entries'], limit=al['limit'], page=al['page'], count=al['count'], grid='22') }}
+        {% endif %}
+      </fieldset>
+    </div>
+  {% endblock audit_log %}
+
   {% block user_projects %}
     <div class="grid-23">
       <fieldset>
@@ -73,3 +85,13 @@
     </div>
   {% endblock user_projects %}
 {% endblock %}
+
+{% block extra_css %}
+{{ super() }}
+<style>
+.pad table {
+  width: 860px;
+  margin-left: 0;
+}
+</style>
+{% endblock %}