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/08/22 19:05:04 UTC
[1/2] allura git commit: [#8117] basic 2FA support
Repository: allura
Updated Branches:
refs/heads/db/8117 [created] 1848bd0ed
[#8117] basic 2FA support
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/1848bd0e
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/1848bd0e
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/1848bd0e
Branch: refs/heads/db/8117
Commit: 1848bd0eda47125948b9b9d5731e4bbe17df3e3f
Parents: f0b0d67
Author: Dave Brondsema <da...@brondsema.net>
Authored: Tue Aug 16 11:35:26 2016 -0400
Committer: Dave Brondsema <da...@brondsema.net>
Committed: Mon Aug 22 15:04:57 2016 -0400
----------------------------------------------------------------------
Allura/allura/controllers/auth.py | 86 +++++++++++++++++++++++-
Allura/allura/lib/app_globals.py | 1 +
Allura/allura/lib/helpers.py | 10 ++-
Allura/allura/lib/plugin.py | 97 ++++++++++++++++++++++++++++
Allura/allura/model/__init__.py | 3 +-
Allura/allura/model/auth.py | 5 +-
Allura/allura/model/multifactor.py | 41 ++++++++++++
Allura/allura/templates/user_prefs.html | 33 ++++++++++
Allura/allura/templates/user_totp.html | 48 ++++++++++++++
Allura/development.ini | 12 ++++
Allura/setup.py | 4 ++
Dockerfile | 2 +
requirements.txt | 10 +++
13 files changed, 348 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/1848bd0e/Allura/allura/controllers/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index 9ad0dea..931fcf5 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -19,6 +19,7 @@ import logging
import os
import datetime
import re
+from time import time
import bson
import tg
@@ -27,8 +28,9 @@ from tg.decorators import with_trailing_slash, without_trailing_slash
from pylons import tmpl_context as c, app_globals as g
from pylons import request, response
from webob import exc as wexc
-from paste.deploy.converters import asbool
+from paste.deploy.converters import asbool, asint
from urlparse import urlparse, urljoin
+from cryptography.hazmat.primitives.twofactor import InvalidToken
import allura.tasks.repo_tasks
from allura import model as M
@@ -452,6 +454,7 @@ class PreferencesController(BaseController):
def _check_security(self):
require_authenticated()
+
@with_trailing_slash
@expose('jinja:allura:templates/user_prefs.html')
def index(self, **kw):
@@ -604,6 +607,87 @@ class PreferencesController(BaseController):
c.user.set_pref('disable_user_messages', not allow_user_messages)
redirect(request.referer)
+ @expose('jinja:allura:templates/user_totp.html')
+ @without_trailing_slash
+ #@reconfirm_password
+ 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('.')
+ '''
+
+ # TODO: warn before that it will clear out previous
+
+ totp_storage = plugin.TotpStorage.get()
+ if 'totp_new_key' not in session:
+ # never been here yet
+ # get (or generate) new key
+ totp = totp_storage.get_totp(c.user)
+ # don't save to database until confirmed, just session for now
+ session['totp_new_key'] = totp.key
+ session.save()
+ else:
+ # use key from session, so we don't regenerate new keys on each page load
+ key = session['totp_new_key']
+ totp = totp_storage.Totp(key)
+
+ qr = totp_storage.get_qr_code(totp, c.user)
+ h.auditlog_user('Visited multifactor new TOTP page')
+ return dict(
+ qr=qr,
+ setup=True,
+ )
+
+
+ @expose('jinja:allura:templates/user_totp.html')
+ @without_trailing_slash
+ #@reconfirm_password
+ def totp_view(self, **kw):
+ totp_storage = plugin.TotpStorage.get()
+ totp = totp_storage.get_totp(c.user, generate_new=False)
+ qr = totp_storage.get_qr_code(totp, c.user)
+ h.auditlog_user('Viewed multifactor TOTP config page')
+ return dict(
+ qr=qr,
+ setup=False,
+ )
+
+ @expose()
+ @require_post()
+ # @reconfirm_password
+ def totp_set(self, pin, **kw):
+ key = session['totp_new_key']
+ totp_storage = plugin.TotpStorage.get()
+ totp = totp_storage.Totp(key)
+ pin = pin.replace(' ', '') # Google authenticator puts a space in their codes
+ pin = bytes(pin) # can't be unicode
+ try:
+ totp.verify(pin, time())
+ except InvalidToken:
+ h.auditlog_user('Failed to set up multifactor TOTP (wrong pin)')
+ tg.flash('Invalid PIN, please try again.')
+ redirect(request.referer)
+ else:
+ h.auditlog_user('Set up multifactor TOTP')
+ totp_storage.set_secret_key(c.user, key)
+ c.user.set_pref('multifactor', True)
+ del session['totp_new_key']
+ session.save()
+ tg.flash('Two factor authentication has now been set up.')
+ redirect('..')
+
+ @expose()
+ @require_post()
+ # @reconfirm_password
+ def totp_disable(self):
+ h.auditlog_user('Disabled multifactor TOTP')
+ totp_storage = plugin.TotpStorage.get()
+ totp_storage.set_secret_key(None)
+ c.user.set_pref('multifactor', False)
+
class UserInfoController(BaseController):
http://git-wip-us.apache.org/repos/asf/allura/blob/1848bd0e/Allura/allura/lib/app_globals.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/app_globals.py b/Allura/allura/lib/app_globals.py
index 33fa123..67995bd 100644
--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -306,6 +306,7 @@ class Globals(object):
# imported (after load, the ep itself is not used)
macros=_cache_eps('allura.macros'),
webhooks=_cache_eps('allura.webhooks'),
+ totp_storage=_cache_eps('allura.multifactor.totp_storage')
)
# Neighborhood cache
http://git-wip-us.apache.org/repos/asf/allura/blob/1848bd0e/Allura/allura/lib/helpers.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/helpers.py b/Allura/allura/lib/helpers.py
index 6e10208..890deb6 100644
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -16,7 +16,7 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
+import base64
import sys
import os
import os.path
@@ -35,6 +35,7 @@ from collections import defaultdict
import shlex
import socket
from functools import partial
+from cStringIO import StringIO
import tg
import genshi.template
@@ -1286,3 +1287,10 @@ def rate_limit(cfg_opt, artifact_count, start_date, exception=None):
artifact_count = artifact_count()
if artifact_count >= count:
raise exception()
+
+
+def base64uri(image, format='PNG', mimetype='image/png'):
+ output = StringIO()
+ image.save(output, format=format)
+ data = base64.b64encode(output.getvalue())
+ return 'data:{};base64,{}'.format(mimetype, data)
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/allura/blob/1848bd0e/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index f74dc62..413a772 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -35,6 +35,8 @@ from datetime import datetime, timedelta
import calendar
import json
+import bson
+
try:
import ldap
from ldap import modlist
@@ -46,6 +48,10 @@ from tg import config, request, redirect, response
from pylons import tmpl_context as c, app_globals as g
from webob import exc, Request
from paste.deploy.converters import asbool, asint
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.twofactor.totp import TOTP
+from cryptography.hazmat.primitives.hashes import SHA1
+import qrcode
from ming.utils import LazyProperty
from ming.orm import state
@@ -1489,6 +1495,97 @@ class LdapUserPreferencesProvider(UserPreferencesProvider):
return LocalUserPreferencesProvider().set_pref(user, pref_name, pref_value)
+class TotpStorage(object):
+
+ '''
+ An interface for storing multifact auth TOTP secret keys
+
+ To use a new provider, expose an entry point in setup.py::
+
+ [allura.multifactor.totp_storage]
+ mytotp = foo.bar:MyTotpStorage
+
+ Then in your .ini file, set auth.multifactor.totp.storage=mytotp
+ '''
+
+ @classmethod
+ def get(cls):
+ method = config.get('auth.multifactor.totp.storage', 'mongodb')
+ return g.entry_points['totp_storage'][method]()
+
+ def Totp(self, key):
+ # simple constructor helper
+ totp = TOTP(key,
+ asint(config.get('auth.multifactor.totp.length', 6)),
+ SHA1(),
+ asint(config.get('auth.multifactor.totp.time', 30)),
+ backend=default_backend())
+ totp.key = key # for convenience, else you have to use `totp._hotp._key`
+ return totp
+
+ def get_totp(self, user, generate_new=True):
+ '''
+ :param user: a :class:`User <allura.model.auth.User>`
+ :param bool generate_new: generate (but does not save) if one does not exist already
+ :return:
+ '''
+ key = self.get_secret_key(user)
+ if not key and generate_new:
+ key = os.urandom(20) # == 160 bytes which is recommended
+ return self.Totp(key)
+
+ def get_qr_code(self, totp, user, **qrcode_params):
+ qrcode_params.setdefault('box_size', 5)
+ uri = totp.get_provisioning_uri(user.username, config['site_name'])
+ qr = qrcode.make(uri, **qrcode_params)
+ return qr
+
+ def get_secret_key(self, user):
+ '''
+ :param user: a :class:`User <allura.model.auth.User>`
+ :return: key
+ '''
+ raise NotImplementedError('get_secret_key')
+
+ def set_secret_key(self, user, key):
+ '''
+ :param user: a :class:`User <allura.model.auth.User>`
+ :param bytes key: may be `None` to clear out a key
+ '''
+ raise NotImplementedError('set_secret_key')
+
+
+class MongodbTotpStorage(TotpStorage):
+
+ def get_secret_key(self, user):
+ from allura import model as M
+ if user.is_anonymous():
+ return None
+ record = M.TotpKey.query.get(user_id=user._id)
+ if record:
+ return record.key
+
+ def set_secret_key(self, user, key):
+ from allura import model as M
+ key = bson.binary.Binary(key)
+ M.TotpKey.query.update({'user_id': user._id},
+ {'user_id': user._id, 'key': key},
+ upsert=True)
+
+
+class FilesystemTotpStorage(TotpStorage):
+
+ @property
+ def basedir(self):
+ return config['auth.multifactor.totp.filesystem.basedir']
+
+ def get_key(self, user):
+ pass
+
+ def set_key(self, user, key):
+ pass
+
+
class AdminExtension(object):
"""
http://git-wip-us.apache.org/repos/asf/allura/blob/1848bd0e/Allura/allura/model/__init__.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/__init__.py b/Allura/allura/model/__init__.py
index a69e7c9..0b1bea2 100644
--- a/Allura/allura/model/__init__.py
+++ b/Allura/allura/model/__init__.py
@@ -36,6 +36,7 @@ from .stats import Stats
from .oauth import OAuthToken, OAuthConsumerToken, OAuthRequestToken, OAuthAccessToken
from .monq_model import MonQTask
from .webhook import Webhook
+from .multifactor import TotpKey
from .types import ACE, ACL, EVERYONE, ALL_PERMISSIONS, DENY_ALL, MarkdownCache
from .session import main_doc_session, main_orm_session
@@ -60,4 +61,4 @@ __all__ = [
'OAuthRequestToken', 'OAuthAccessToken', 'MonQTask', 'Webhook', 'ACE', 'ACL', 'EVERYONE', 'ALL_PERMISSIONS',
'DENY_ALL', 'MarkdownCache', 'main_doc_session', 'main_orm_session', 'project_doc_session', 'project_orm_session',
'artifact_orm_session', 'repository_orm_session', 'task_orm_session', 'ArtifactSessionExtension', 'repository',
- 'repo_refresh', 'SiteNotification']
+ 'repo_refresh', 'SiteNotification', 'TotpKey']
http://git-wip-us.apache.org/repos/asf/allura/blob/1848bd0e/Allura/allura/model/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index 7f6442c..fa26353 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -300,7 +300,10 @@ class User(MappedClass, ActivityNode, ActivityObject, SearchIndexable):
results_per_page=int,
email_address=str,
email_format=str,
- disable_user_messages=bool))
+ disable_user_messages=bool,
+ multifactor=bool,
+ #totp=S.Binary,
+ ))
# Additional top-level fields can/should be accessed with get/set_pref also
# Not sure why we didn't put them within the 'preferences' dictionary :(
display_name = FieldPropertyDisplayName(str)
http://git-wip-us.apache.org/repos/asf/allura/blob/1848bd0e/Allura/allura/model/multifactor.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/multifactor.py b/Allura/allura/model/multifactor.py
new file mode 100644
index 0000000..6006aad
--- /dev/null
+++ b/Allura/allura/model/multifactor.py
@@ -0,0 +1,41 @@
+# 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.
+
+import logging
+
+from ming import schema as S
+from ming.odm import FieldProperty
+from ming.odm.declarative import MappedClass
+
+from .session import main_orm_session
+
+log = logging.getLogger(__name__)
+
+
+class TotpKey(MappedClass):
+ '''
+ For use with "mongodb" storage option
+ '''
+
+ class __mongometa__:
+ session = main_orm_session
+ name = 'multifactor_totp'
+ unique_indexes = ['user_id']
+
+ _id = FieldProperty(S.ObjectId)
+ user_id = FieldProperty(S.ObjectId, required=True)
+ key = FieldProperty(str, required=True)
http://git-wip-us.apache.org/repos/asf/allura/blob/1848bd0e/Allura/allura/templates/user_prefs.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/user_prefs.html b/Allura/allura/templates/user_prefs.html
index 74d5c08..eda4c3f 100644
--- a/Allura/allura/templates/user_prefs.html
+++ b/Allura/allura/templates/user_prefs.html
@@ -101,6 +101,34 @@
{% endif %}
{% endblock %}
+ {% block multifactor %}
+ {% if h.asbool(tg.config.get('auth.multifactor.totp', False)) %}
+ <fieldset class="preferences multifactor">
+ <legend>Multifactor Authentication</legend>
+ <p>Multifactor authentication is currently
+ {% set user_multifactor = c.user.get_pref('multifactor') %}
+ {% if user_multifactor %}
+ <strong style="color:green">enabled</strong>
+ {%- else -%}
+ <strong style="color:red">disabled</strong>
+ {%- endif -%}
+ .
+ </p>
+ <p><b class="fa fa-cog"></b> <a href="totp_new">
+ {% if user_multifactor %}
+ Regenerate multifactor key (e.g. for a new phone).
+ {% else %}
+ Set up multifactor authentication.
+ {% endif %}
+ </a></p>
+ {% if user_multifactor %}
+ <p><b class="fa fa-qrcode"></b> <a href="totp_view">View existing configuration</a></p>
+ <p><b class="fa fa-trash"></b> <a href="totp_disable" class="post-link">Disable</a></p>
+ {% endif %}
+ </fieldset>
+ {% endif %}
+ {% endblock %}
+
{% block upload_key_form %}
{% if h.asbool(tg.config.get('auth.allow_upload_ssh_key', False)) %}
<fieldset class="preferences">
@@ -139,6 +167,11 @@
padding: 0;
border: 0;
}
+ .multifactor .fa {
+ font-size: 300%;
+ vertical-align: middle;
+ margin-right: 5px;
+ }
</style>
{% endblock %}
http://git-wip-us.apache.org/repos/asf/allura/blob/1848bd0e/Allura/allura/templates/user_totp.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/user_totp.html b/Allura/allura/templates/user_totp.html
new file mode 100644
index 0000000..27710ce
--- /dev/null
+++ b/Allura/allura/templates/user_totp.html
@@ -0,0 +1,48 @@
+{#-
+ 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 "allura:templates/user_account_base.html" %}
+
+{% block title %}{{c.user.username}} / Multifactor Authentication Setup{% endblock %}
+
+{% block header %}Multifactor Authentication Setup for {{c.user.username}}{% endblock %}
+
+{% block content %}
+ {{ super() }}
+ <div class='grid-20'>
+ {% if setup %}
+ <h2>Install App</h2>
+ <p>...</p>
+ {% endif %}
+
+ <h2>Scan this</h2>
+ <img src="{{ h.base64uri(qr) }}"/>
+
+ {% if setup %}
+ <h2>Enter the code</h2>
+ <p>
+ ...
+ <form method="POST" action="totp_set">
+ <input type="text" name="pin" autofocus/>
+ {{ lib.csrf_token() }}
+ </form>
+ </p>
+ {% endif %}
+ </div>
+{% endblock %}
http://git-wip-us.apache.org/repos/asf/allura/blob/1848bd0e/Allura/development.ini
----------------------------------------------------------------------
diff --git a/Allura/development.ini b/Allura/development.ini
index e4e0ffc..107ddd2 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -190,6 +190,18 @@ auth.upload_ssh_url = /auth/preferences/
; In seconds
auth.recovery_hash_expiry_period = 600
+; 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
+; Length of codes, 6 or 8 is recommended
+auth.multifactor.totp.length = 6
+; Time window codes are valid, in seconds. 30 is recommended
+auth.multifactor.totp.time = 30
+; secret key storage location. "filesystem" is another option (compatible with TOTP PAM plugins)
+auth.multifactor.totp.storage = mongodb
+; if using filesystem storage:
+; auth.multifactor.totp.filesystem.basedir = /var/lib/allura/totp-keys
+
user_prefs_storage.method = local
; user_prefs_storage.method = ldap
; If using ldap, you can specify which fields to use for a preference.
http://git-wip-us.apache.org/repos/asf/allura/blob/1848bd0e/Allura/setup.py
----------------------------------------------------------------------
diff --git a/Allura/setup.py b/Allura/setup.py
index a9607f8..0f69094 100644
--- a/Allura/setup.py
+++ b/Allura/setup.py
@@ -140,6 +140,10 @@ setup(
[allura.webhooks]
repo-push = allura.webhooks:RepoPushWebhookSender
+ [allura.multifactor.totp_storage]
+ mongodb = allura.lib.plugin:MongodbTotpStorage
+ filesystem = allura.lib.plugin:FilesystemTotpStorage
+
[paste.paster_command]
taskd = allura.command.taskd:TaskdCommand
taskd_cleanup = allura.command.taskd_cleanup:TaskdCleanupCommand
http://git-wip-us.apache.org/repos/asf/allura/blob/1848bd0e/Dockerfile
----------------------------------------------------------------------
diff --git a/Dockerfile b/Dockerfile
index f04b37b..7ac870c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -30,6 +30,8 @@ RUN apt-get update && apt-get install -y \
subversion \
python-svn \
curl
+ # build-essential libffi-dev
+# libffi-dev ? seemed to install ok without it
# up-to-date version of node & npm
RUN curl --silent --location https://deb.nodesource.com/setup_4.x | sudo bash - && \
http://git-wip-us.apache.org/repos/asf/allura/blob/1848bd0e/requirements.txt
----------------------------------------------------------------------
diff --git a/requirements.txt b/requirements.txt
index 4a8443f..4d93621 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -37,6 +37,7 @@ python-dateutil==1.5
python-magic==0.4.3
python-oembed==0.2.1
pytz==2014.10
+qrcode==5.3
requests==2.0.0
oauthlib==0.4.2
requests-oauthlib==0.4.0
@@ -53,6 +54,15 @@ WebOb==1.0.8
# part of the stdlib, but with a version number. see http://guide.python-distribute.org/pip.html#listing-installed-packages
wsgiref==0.1.2
+# dependencies for cryptography
+cryptography==1.4
+cffi==1.7.0
+pycparser==2.14
+enum34==1.1.6
+ipaddress==1.0.16
+idna==2.1
+pyasn1==0.1.9
+
# tg2 deps (not used directly)
Babel==1.3
Mako==0.3.2
[2/2] allura git commit: [#8117] post-link
Posted by br...@apache.org.
[#8117] post-link
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/f0b0d676
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/f0b0d676
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/f0b0d676
Branch: refs/heads/db/8117
Commit: f0b0d676a3de5d5a23780bf7cf58929c5e59e2a8
Parents: 74910c7
Author: Dave Brondsema <da...@brondsema.net>
Authored: Fri Aug 19 13:06:52 2016 -0400
Committer: Dave Brondsema <da...@brondsema.net>
Committed: Mon Aug 22 15:04:57 2016 -0400
----------------------------------------------------------------------
Allura/allura/public/nf/js/allura-base.js | 10 ++++++++++
ForgeTracker/forgetracker/templates/tracker/ticket.html | 7 -------
ForgeWiki/forgewiki/templates/wiki/master.html | 2 +-
ForgeWiki/forgewiki/templates/wiki/page_edit.html | 2 +-
ForgeWiki/forgewiki/templates/wiki/page_history.html | 2 +-
ForgeWiki/forgewiki/templates/wiki/page_view.html | 2 +-
ForgeWiki/forgewiki/wiki_main.py | 2 +-
7 files changed, 15 insertions(+), 12 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/f0b0d676/Allura/allura/public/nf/js/allura-base.js
----------------------------------------------------------------------
diff --git a/Allura/allura/public/nf/js/allura-base.js b/Allura/allura/public/nf/js/allura-base.js
index 7a1d731..93d269c 100644
--- a/Allura/allura/public/nf/js/allura-base.js
+++ b/Allura/allura/public/nf/js/allura-base.js
@@ -210,4 +210,14 @@ $(function(){
twemoji.size = '36x36';
twemoji.parse($('#content_base')[0]);
+
+ $('.post-link').click(function(evt) {
+ var cval = $.cookie('_session_id');
+ evt.preventDefault();
+ $.post(this.href,
+ {_session_id:cval},
+ function(val) { window.location = val.location; },
+ 'json'
+ );
+ });
});
http://git-wip-us.apache.org/repos/asf/allura/blob/f0b0d676/ForgeTracker/forgetracker/templates/tracker/ticket.html
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/templates/tracker/ticket.html b/ForgeTracker/forgetracker/templates/tracker/ticket.html
index afe0253..129c00e 100644
--- a/ForgeTracker/forgetracker/templates/tracker/ticket.html
+++ b/ForgeTracker/forgetracker/templates/tracker/ticket.html
@@ -233,13 +233,6 @@
$('a.edit_ticket').removeClass('btn_activate');
return false;
});
- $('.post-link').click(function(evt) {
- var cval = $.cookie('_session_id');
- evt.preventDefault();
- $.post(this.href, {_session_id:cval}, function(val)
- { window.location = val.location; },
- 'json');
- });
// delete attachments
$('div.attachment_thumb a.delete_attachment').click(function () {
var cval = $.cookie('_session_id');
http://git-wip-us.apache.org/repos/asf/allura/blob/f0b0d676/ForgeWiki/forgewiki/templates/wiki/master.html
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/templates/wiki/master.html b/ForgeWiki/forgewiki/templates/wiki/master.html
index ad03778..e976399 100644
--- a/ForgeWiki/forgewiki/templates/wiki/master.html
+++ b/ForgeWiki/forgewiki/templates/wiki/master.html
@@ -35,7 +35,7 @@
{% block extra_js %}
<script type="text/javascript">
- $('.post-link').click(function () {
+ $('.post-link-confirm').click(function () {
var dialog_text;
var version = $(this).data("dialog-id");
if (version) {
http://git-wip-us.apache.org/repos/asf/allura/blob/f0b0d676/ForgeWiki/forgewiki/templates/wiki/page_edit.html
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/templates/wiki/page_edit.html b/ForgeWiki/forgewiki/templates/wiki/page_edit.html
index 204b20c..935728f 100644
--- a/ForgeWiki/forgewiki/templates/wiki/page_edit.html
+++ b/ForgeWiki/forgewiki/templates/wiki/page_edit.html
@@ -34,7 +34,7 @@
{% block actions %}
{{ g.icons['eye'].render(href='.', title='View Page') }}
{% if page_exists and h.has_access(page, 'delete')() %}
- {{ g.icons['delete'].render(extra_css='post-link') }}
+ {{ g.icons['delete'].render(extra_css='post-link-confirm') }}
<div class="confirmation_dialog" style="display:none">
{{ g.icons['close'].render(tag='b', extra_css='close') }}
http://git-wip-us.apache.org/repos/asf/allura/blob/f0b0d676/ForgeWiki/forgewiki/templates/wiki/page_history.html
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/templates/wiki/page_history.html b/ForgeWiki/forgewiki/templates/wiki/page_history.html
index dc3027a..72ab9a8 100644
--- a/ForgeWiki/forgewiki/templates/wiki/page_history.html
+++ b/ForgeWiki/forgewiki/templates/wiki/page_history.html
@@ -53,7 +53,7 @@
<td class="tright">
{% if i != 0 and h.has_access(p, 'edit')() %}
{{ g.icons['revert'].render(
- extra_css='post-link',
+ extra_css='post-link-confirm',
title='Revert to version {}'.format(p.version),
**{'data-dialog-id': p.version}) }}
<div class="confirmation_dialog_{{p.version}}" style="display:none">
http://git-wip-us.apache.org/repos/asf/allura/blob/f0b0d676/ForgeWiki/forgewiki/templates/wiki/page_view.html
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/templates/wiki/page_view.html b/ForgeWiki/forgewiki/templates/wiki/page_view.html
index f770ccc..bc1d18a 100644
--- a/ForgeWiki/forgewiki/templates/wiki/page_view.html
+++ b/ForgeWiki/forgewiki/templates/wiki/page_view.html
@@ -46,7 +46,7 @@
{% endif %}
{{ g.icons['history'].render(href='history') }}
{% elif h.has_access(page, 'delete')() %}
- {{ g.icons['undelete'].render(extra_css='post-link') }}
+ {{ g.icons['undelete'].render(extra_css='post-link-confirm') }}
<div class="confirmation_dialog" style="display:none">
{{ g.icons['close'].render(tag='b', extra_css='close') }}
<h1>Confirm page restoration</h1>
http://git-wip-us.apache.org/repos/asf/allura/blob/f0b0d676/ForgeWiki/forgewiki/wiki_main.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index ace38b6..b6292e4 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -63,7 +63,7 @@ class W:
style='linear')
markdown_editor = ffw.MarkdownEdit()
confirmation = ffw.Lightbox(name='confirm',
- trigger='a.post-link',
+ trigger='a.post-link-confirm',
options="{ modalCSS: { minHeight: 0, width: 'inherit', top: '150px'}}")
label_edit = ffw.LabelEdit()
attachment_add = ffw.AttachmentAdd()