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 2016/09/06 14:50:36 UTC
[03/10] allura git commit: [#8117] Reconfirm auth on security pages
[#8117] Reconfirm auth on security pages
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/bced8935
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/bced8935
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/bced8935
Branch: refs/heads/master
Commit: bced893549b00775774c23fc928bfb71c31f9f22
Parents: 208b99b
Author: Dave Brondsema <da...@brondsema.net>
Authored: Thu Aug 25 13:15:38 2016 -0400
Committer: Dave Brondsema <da...@brondsema.net>
Committed: Tue Sep 6 10:38:51 2016 -0400
----------------------------------------------------------------------
Allura/allura/controllers/auth.py | 28 ++++++---------
Allura/allura/lib/decorators.py | 36 ++++++++++++++++++-
Allura/allura/templates/reconfirm_auth.html | 44 ++++++++++++++++++++++++
Allura/allura/templates/user_prefs.html | 20 +++++++++--
Allura/development.ini | 3 ++
5 files changed, 109 insertions(+), 22 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/bced8935/Allura/allura/controllers/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index d326397..93a0875 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -17,7 +17,7 @@
import logging
import os
-import datetime
+from datetime import datetime, timedelta
import re
from urlparse import urlparse, urljoin
@@ -37,7 +37,7 @@ from allura.lib import validators as V
from allura.lib.security import require_authenticated, has_access
from allura.lib import helpers as h
from allura.lib import plugin
-from allura.lib.decorators import require_post
+from allura.lib.decorators import require_post, reconfirm_auth
from allura.lib.repository import RepositoryApp
from allura.lib.widgets import (
SubscriptionForm,
@@ -141,7 +141,7 @@ class AuthController(BaseController):
redirect(login_url)
hash_expiry = user_record.get_tool_data(
'AuthPasswordReset', 'hash_expiry')
- if not hash_expiry or hash_expiry < datetime.datetime.utcnow():
+ if not hash_expiry or hash_expiry < datetime.utcnow():
log.info('Reset hash expired: {} {}'.format(hash, hash_expiry))
flash('Unable to process reset, please try again')
redirect(login_url)
@@ -203,8 +203,8 @@ class AuthController(BaseController):
hash = h.nonce(42)
user_record.set_tool_data('AuthPasswordReset',
hash=hash,
- hash_expiry=datetime.datetime.utcnow() +
- datetime.timedelta(seconds=int(config.get('auth.recovery_hash_expiry_period', 600))))
+ hash_expiry=datetime.utcnow() +
+ timedelta(seconds=int(config.get('auth.recovery_hash_expiry_period', 600))))
log.info('Sending password recovery link to %s', email_record.email)
subject = '%s Password recovery' % config['site_name']
@@ -646,16 +646,8 @@ class PreferencesController(BaseController):
@expose('jinja:allura:templates/user_totp.html')
@without_trailing_slash
- #@reconfirm_password
+ @reconfirm_auth
def totp_new(self, **kw):
- '''
- auth_provider = plugin.AuthenticationProvider.get(request)
- # TODO: don't require it every single time
- if not kw.get('password') or not auth_provider.validate_password(c.user, kw.get('password')):
- flash('You must provide your current password to set up multifactor authentication', 'error')
- redirect('.')
- '''
-
totp_service = TotpService.get()
if 'totp_new_key' not in session:
# never been here yet
@@ -678,7 +670,7 @@ class PreferencesController(BaseController):
@expose('jinja:allura:templates/user_totp.html')
@without_trailing_slash
- #@reconfirm_password
+ @reconfirm_auth
def totp_view(self, **kw):
totp_service = TotpService.get()
totp = totp_service.get_totp(c.user)
@@ -690,8 +682,8 @@ class PreferencesController(BaseController):
)
@expose('jinja:allura:templates/user_totp.html')
+ @reconfirm_auth
@require_post()
- # @reconfirm_password
def totp_set(self, code, **kw):
# TODO: email notification
key = session['totp_new_key']
@@ -714,8 +706,8 @@ class PreferencesController(BaseController):
@expose()
@require_post()
- # @reconfirm_password
- def multifactor_disable(self):
+ @reconfirm_auth
+ def multifactor_disable(self, **kw):
# TODO: email notification
h.auditlog_user('Disabled multifactor TOTP')
totp_service = TotpService.get()
http://git-wip-us.apache.org/repos/asf/allura/blob/bced8935/Allura/allura/lib/decorators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/decorators.py b/Allura/allura/lib/decorators.py
index cb5cfc5..dc4e1c3 100644
--- a/Allura/allura/lib/decorators.py
+++ b/Allura/allura/lib/decorators.py
@@ -22,9 +22,14 @@ import logging
from collections import defaultdict
from urllib import unquote
+from datetime import datetime
+
+from datetime import timedelta
from decorator import decorator
+from paste.deploy.converters import asint
from tg.decorators import before_validate
-from tg import request, redirect
+from tg import request, redirect, session, config
+from tg.render import render
from webob import exc
from pylons import tmpl_context as c
@@ -32,6 +37,9 @@ from allura.lib import helpers as h
from allura.lib import utils
+log = logging.getLogger(__name__)
+
+
def task(*args, **kw):
"""Decorator that adds a ``.post()`` function to the decorated callable.
@@ -84,6 +92,10 @@ class event_handler(object):
class require_post(object):
+ '''
+ A decorator to require controllers by accessed with a POST only. Use whenever data will be modified by a
+ controller, since that's what POST is good for. We have CSRF protection middleware on POSTs, too.
+ '''
def __init__(self, redir=None):
self.redir = redir
@@ -98,6 +110,28 @@ class require_post(object):
return func
+@decorator
+def reconfirm_auth(func, *args, **kwargs):
+ '''
+ A decorator to require the user to reconfirm their login. Useful for sensitive pages.
+ '''
+ from allura.lib.plugin import AuthenticationProvider
+
+ if request.POST.get('password'):
+ if AuthenticationProvider.get(request).validate_password(c.user, request.POST['password']):
+ session['auth-reconfirmed'] = datetime.utcnow()
+ session.save()
+ else:
+ c.form_errors['password'] = 'Invalid password.'
+
+ allowed_timedelta = timedelta(seconds=asint(config.get('auth.reconfirm.seconds', 60)))
+ last_reconfirm = session.get('auth-reconfirmed', datetime.min)
+ if datetime.utcnow() - last_reconfirm <= allowed_timedelta:
+ return func(*args, **kwargs)
+ else:
+ return render({}, 'jinja', "allura:templates/reconfirm_auth.html")
+
+
class log_action(object): # pragma no cover
def __init__(self,
http://git-wip-us.apache.org/repos/asf/allura/blob/bced8935/Allura/allura/templates/reconfirm_auth.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/reconfirm_auth.html b/Allura/allura/templates/reconfirm_auth.html
new file mode 100644
index 0000000..f44be2d
--- /dev/null
+++ b/Allura/allura/templates/reconfirm_auth.html
@@ -0,0 +1,44 @@
+{#-
+ 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 hide_left_bar = True %}
+{% extends g.theme.master %}
+
+{% block title %}{{ config['site_name'] }} - Reconfirm Password{% endblock %}
+
+{% block header %}Reconfirm Your Password{% endblock %}
+
+{% block content %}
+<form method="post">
+ <h2>Password Confirmation</h2>
+ <p>To access this account security page, you must reconfirm your password:</p>
+ <div class="fielderror">{{ c.form_errors['password'] }}</div>
+ <input type="password" name="password" autofocus>
+ <br>
+ <input type="submit" value="Submit">
+
+ {# include any post params again, so that their original form submit can go through successfully #}
+ {% for key, val in request.POST.iteritems() %}
+ {% if key != 'password' %}
+ <input type="hidden" name="{{ key }}" value="{{ val }}">
+ {% endif %}
+ {% endfor %}
+
+ {{ lib.csrf_token() }}
+</form>
+{% endblock %}
http://git-wip-us.apache.org/repos/asf/allura/blob/bced8935/Allura/allura/templates/user_prefs.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/user_prefs.html b/Allura/allura/templates/user_prefs.html
index f2cd5fc..66450e5 100644
--- a/Allura/allura/templates/user_prefs.html
+++ b/Allura/allura/templates/user_prefs.html
@@ -123,9 +123,12 @@
</a></p>
{% if user_multifactor %}
<p><b class="fa fa-qrcode"></b> <a href="totp_view">View existing configuration</a></p>
-
- {# FIXME, need confirmation #}
- <p><b class="fa fa-trash"></b> <a href="multifactor_disable" class="post-link">Disable</a></p>
+ <form action="multifactor_disable" method="post">
+ <p>
+ <b class="fa fa-trash"></b> <a href="#" class="disable">Disable</a>
+ </p>
+ {{ lib.csrf_token() }}
+ </form>
{% endif %}
</fieldset>
{% endif %}
@@ -178,6 +181,17 @@
{% endblock %}
{% block extra_js %}
+ <script type="text/javascript">
+ $(function() {
+ $('.multifactor .disable').click(function(e){
+ var ok = confirm('Are you sure you want to disable multifactor authentication?');
+ if(ok) {
+ $(this).closest('form').submit();
+ }
+ e.preventDefault();
+ });
+ });
+ </script>
{% 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">
http://git-wip-us.apache.org/repos/asf/allura/blob/bced8935/Allura/development.ini
----------------------------------------------------------------------
diff --git a/Allura/development.ini b/Allura/development.ini
index 24ab3be..a72f9fd 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -190,6 +190,9 @@ auth.upload_ssh_url = /auth/preferences/
; In seconds
auth.recovery_hash_expiry_period = 600
+; Some pages require users to reconfirm their password. This controls how long that lasts for
+auth.reconfirm.seconds = 60
+
; TOTP stands for Time-based One Time Password
; it is the most common two-factor auth protocol, used with Google Authenticator and other phone apps
auth.multifactor.totp = true