From 4f8f0a4e1f2d5a1eb64f47e62958d77bd12379f3 Mon Sep 17 00:00:00 2001
From: Rodney Ewing
Date: Wed, 26 Jun 2013 12:04:45 -0700
Subject: [PATCH 01/45] merge --squash persona branch to take care of a false
merge commit in the basic_auth branch that persona is forked from
Conflicts:
mediagoblin/templates/mediagoblin/auth/login.html
mediagoblin/templates/mediagoblin/auth/register.html
mediagoblin/templates/mediagoblin/edit/edit_account.html
These are commit messages from the squashed persona stuff:
- added tests and fixed minor errors
- fixed a redirect loop when only persona is enabled and accessing /auth/login
- moved persona.js to plugin's static dir
- fixes for add/remove persona emails
- add and remove personas
- working with multiple plugins
- working version
- switched to hidden form instead of ajax
- beginings
---
mediagoblin/auth/views.py | 14 +-
.../mediagoblin/plugins/openid/login.html | 7 +
mediagoblin/plugins/persona/__init__.py | 113 ++++++++++
mediagoblin/plugins/persona/forms.py | 41 ++++
mediagoblin/plugins/persona/models.py | 36 +++
.../plugins/persona/static/js/persona.js | 44 ++++
.../mediagoblin/plugins/persona/edit.html | 43 ++++
.../mediagoblin/plugins/persona/persona.html | 30 +++
.../plugins/persona/persona_js_head.html | 21 ++
mediagoblin/plugins/persona/views.py | 191 ++++++++++++++++
.../templates/mediagoblin/auth/login.html | 7 +
.../templates/mediagoblin/auth/register.html | 8 +
mediagoblin/templates/mediagoblin/base.html | 33 ++-
.../mediagoblin/bits/frontpage_welcome.html | 10 +-
.../mediagoblin/edit/edit_account.html | 8 +
.../tests/auth_configs/persona_appconfig.ini | 42 ++++
mediagoblin/tests/test_persona.py | 210 ++++++++++++++++++
17 files changed, 848 insertions(+), 10 deletions(-)
create mode 100644 mediagoblin/plugins/persona/__init__.py
create mode 100644 mediagoblin/plugins/persona/forms.py
create mode 100644 mediagoblin/plugins/persona/models.py
create mode 100644 mediagoblin/plugins/persona/static/js/persona.js
create mode 100644 mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit.html
create mode 100644 mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/persona.html
create mode 100644 mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/persona_js_head.html
create mode 100644 mediagoblin/plugins/persona/views.py
create mode 100644 mediagoblin/tests/auth_configs/persona_appconfig.ini
create mode 100644 mediagoblin/tests/test_persona.py
diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py
index 1cff8dcc..560080e4 100644
--- a/mediagoblin/auth/views.py
+++ b/mediagoblin/auth/views.py
@@ -41,8 +41,11 @@ def register(request):
"""
if 'pass_auth' not in request.template_env.globals:
redirect_name = hook_handle('auth_no_pass_redirect')
- return redirect(request, 'mediagoblin.plugins.{0}.register'.format(
- redirect_name))
+ if redirect_name:
+ return redirect(request, 'mediagoblin.plugins.{0}.register'.format(
+ redirect_name))
+ else:
+ return redirect(request, 'index')
register_form = hook_handle("auth_get_registration_form", request)
@@ -73,8 +76,11 @@ def login(request):
"""
if 'pass_auth' not in request.template_env.globals:
redirect_name = hook_handle('auth_no_pass_redirect')
- return redirect(request, 'mediagoblin.plugins.{0}.login'.format(
- redirect_name))
+ if redirect_name:
+ return redirect(request, 'mediagoblin.plugins.{0}.login'.format(
+ redirect_name))
+ else:
+ return redirect(request, 'index')
login_form = hook_handle("auth_get_login_form", request)
diff --git a/mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html b/mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html
index 33df7200..604c4bec 100644
--- a/mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html
+++ b/mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html
@@ -44,6 +44,13 @@
{% trans %}Log in to create an account!{% endtrans %}
{% endif %}
+ {% if persona is defined %}
+
+
+ {% trans %}Or login with Persona!{% endtrans %}
+
+
+ {% endif %}
{% if pass_auth is defined %}
diff --git a/mediagoblin/plugins/persona/__init__.py b/mediagoblin/plugins/persona/__init__.py
new file mode 100644
index 00000000..20cbbbf9
--- /dev/null
+++ b/mediagoblin/plugins/persona/__init__.py
@@ -0,0 +1,113 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+from pkg_resources import resource_filename
+import os
+
+from sqlalchemy import or_
+
+from mediagoblin.auth.tools import create_basic_user
+from mediagoblin.db.models import User
+from mediagoblin.plugins.persona.models import PersonaUserEmails
+from mediagoblin.tools import pluginapi
+from mediagoblin.tools.staticdirect import PluginStatic
+from mediagoblin.tools.translate import pass_to_ugettext as _
+
+PLUGIN_DIR = os.path.dirname(__file__)
+
+
+def setup_plugin():
+ config = pluginapi.get_config('mediagoblin.plugins.persona')
+
+ routes = [
+ ('mediagoblin.plugins.persona.login',
+ '/auth/persona/login/',
+ 'mediagoblin.plugins.persona.views:login'),
+ ('mediagoblin.plugins.persona.register',
+ '/auth/persona/register/',
+ 'mediagoblin.plugins.persona.views:register'),
+ ('mediagoblin.plugins.persona.edit',
+ '/edit/persona/',
+ 'mediagoblin.plugins.persona.views:edit'),
+ ('mediagoblin.plugins.persona.add',
+ '/edit/persona/add/',
+ 'mediagoblin.plugins.persona.views:add')]
+
+ pluginapi.register_routes(routes)
+ pluginapi.register_template_path(os.path.join(PLUGIN_DIR, 'templates'))
+ pluginapi.register_template_hooks(
+ {'persona_head': 'mediagoblin/plugins/persona/persona_js_head.html',
+ 'persona_form': 'mediagoblin/plugins/persona/persona.html'})
+
+
+def create_user(register_form):
+ if 'persona_email' in register_form:
+ username = register_form.username.data
+ user = User.query.filter(
+ or_(
+ User.username == username,
+ User.email == username,
+ )).first()
+
+ if not user:
+ user = create_basic_user(register_form)
+
+ new_entry = PersonaUserEmails()
+ new_entry.persona_email = register_form.persona_email.data
+ new_entry.user_id = user.id
+ new_entry.save()
+
+ return user
+
+
+def extra_validation(register_form):
+ persona_email = register_form.persona_email.data if 'persona_email' in \
+ register_form else None
+ if persona_email:
+ persona_email_exists = PersonaUserEmails.query.filter_by(
+ persona_email=persona_email
+ ).count()
+
+ extra_validation_passes = True
+
+ if persona_email_exists:
+ register_form.persona_email.errors.append(
+ _('Sorry, an account is already registered to that Persona'
+ ' email.'))
+ extra_validation_passes = False
+
+ return extra_validation_passes
+
+
+def Auth():
+ return True
+
+
+def add_to_global_context(context):
+ if len(pluginapi.hook_runall('authentication')) == 1:
+ context['persona_auth'] = True
+ context['persona'] = True
+ return context
+
+hooks = {
+ 'setup': setup_plugin,
+ 'authentication': Auth,
+ 'auth_extra_validation': extra_validation,
+ 'auth_create_user': create_user,
+ 'template_global_context': add_to_global_context,
+ 'static_setup': lambda: PluginStatic(
+ 'coreplugin_persona',
+ resource_filename('mediagoblin.plugins.persona', 'static'))
+}
diff --git a/mediagoblin/plugins/persona/forms.py b/mediagoblin/plugins/persona/forms.py
new file mode 100644
index 00000000..608be0c7
--- /dev/null
+++ b/mediagoblin/plugins/persona/forms.py
@@ -0,0 +1,41 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+import wtforms
+
+from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
+from mediagoblin.auth.tools import normalize_user_or_email_field
+
+
+class RegistrationForm(wtforms.Form):
+ username = wtforms.TextField(
+ _('Username'),
+ [wtforms.validators.Required(),
+ normalize_user_or_email_field(allow_email=False)])
+ email = wtforms.TextField(
+ _('Email address'),
+ [wtforms.validators.Required(),
+ normalize_user_or_email_field(allow_user=False)])
+ persona_email = wtforms.HiddenField(
+ '',
+ [wtforms.validators.Required(),
+ normalize_user_or_email_field(allow_user=False)])
+
+
+class EditForm(wtforms.Form):
+ email = wtforms.TextField(
+ _('Email address'),
+ [wtforms.validators.Required(),
+ normalize_user_or_email_field(allow_user=False)])
diff --git a/mediagoblin/plugins/persona/models.py b/mediagoblin/plugins/persona/models.py
new file mode 100644
index 00000000..ff3c525a
--- /dev/null
+++ b/mediagoblin/plugins/persona/models.py
@@ -0,0 +1,36 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+from sqlalchemy import Column, Integer, Unicode, ForeignKey
+from sqlalchemy.orm import relationship, backref
+
+from mediagoblin.db.models import User
+from mediagoblin.db.base import Base
+
+
+class PersonaUserEmails(Base):
+ __tablename__ = "persona__user_emails"
+
+ id = Column(Integer, primary_key=True)
+ persona_email = Column(Unicode, nullable=False)
+ user_id = Column(Integer, ForeignKey(User.id), nullable=False)
+
+ # Persona's are owned by their user, so do the full thing.
+ user = relationship(User, backref=backref('persona_emails',
+ cascade='all, delete-orphan'))
+
+MODELS = [
+ PersonaUserEmails
+]
diff --git a/mediagoblin/plugins/persona/static/js/persona.js b/mediagoblin/plugins/persona/static/js/persona.js
new file mode 100644
index 00000000..034f2574
--- /dev/null
+++ b/mediagoblin/plugins/persona/static/js/persona.js
@@ -0,0 +1,44 @@
+/**
+ * GNU MediaGoblin -- federated, autonomous media hosting
+ * Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+$(document).ready(function () {
+ var signinLink = document.getElementById('persona_login');
+ if (signinLink) {
+ signinLink.onclick = function() { navigator.id.request(); };
+ }
+
+ var signoutLink = document.getElementById('logout');
+ if (signoutLink) {
+ signoutLink.onclick = function() { navigator.id.logout(); };
+ }
+
+ navigator.id.watch({
+ onlogin: function(assertion) {
+ document.getElementById('_assertion').value = assertion;
+ document.getElementById('_persona_login').submit()
+ },
+ onlogout: function() {
+ $.ajax({
+ type: 'POST',
+ url: '/auth/logout',
+ success: function(res, status, xhr) { window.location.reload(); },
+ error: function(xhr, status, err) { alert("Logout failure: " + err); }
+ });
+ }
+ });
+});
diff --git a/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit.html b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit.html
new file mode 100644
index 00000000..be62b8cc
--- /dev/null
+++ b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit.html
@@ -0,0 +1,43 @@
+{#
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#}
+{% extends "mediagoblin/base.html" %}
+
+{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
+
+{% block title -%}
+ {% trans %}Add an OpenID{% endtrans %} — {{ super() }}
+{%- endblock %}
+
+{% block mediagoblin_content %}
+
+{% endblock %}
diff --git a/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/persona.html b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/persona.html
new file mode 100644
index 00000000..c953f393
--- /dev/null
+++ b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/persona.html
@@ -0,0 +1,30 @@
+{#
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#}
+{% block persona %}
+
+{% endblock %}
diff --git a/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/persona_js_head.html b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/persona_js_head.html
new file mode 100644
index 00000000..8c0d72d5
--- /dev/null
+++ b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/persona_js_head.html
@@ -0,0 +1,21 @@
+{#
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#}
+
+
+
diff --git a/mediagoblin/plugins/persona/views.py b/mediagoblin/plugins/persona/views.py
new file mode 100644
index 00000000..f3aff38d
--- /dev/null
+++ b/mediagoblin/plugins/persona/views.py
@@ -0,0 +1,191 @@
+# GNU MediaGoblin -- federated, autonomous media hosting
+# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+import json
+import logging
+import requests
+
+from werkzeug.exceptions import BadRequest
+
+from mediagoblin import messages, mg_globals
+from mediagoblin.auth.tools import register_user
+from mediagoblin.decorators import (auth_enabled, allow_registration,
+ require_active_login)
+from mediagoblin.tools.response import render_to_response, redirect
+from mediagoblin.tools.translate import pass_to_ugettext as _
+from mediagoblin.plugins.persona import forms
+from mediagoblin.plugins.persona.models import PersonaUserEmails
+
+_log = logging.getLogger(__name__)
+
+
+def _get_response(request):
+ if 'assertion' not in request.form:
+ _log.debug('assertion not in request.form')
+ raise BadRequest()
+
+ data = {'assertion': request.form['assertion'],
+ 'audience': request.urlgen('index', qualified=True)}
+ resp = requests.post('https://verifier.login.persona.org/verify',
+ data=data, verify=True)
+
+ if resp.ok:
+ verification_data = json.loads(resp.content)
+
+ if verification_data['status'] == 'okay':
+ return verification_data['email']
+
+ return None
+
+
+@auth_enabled
+def login(request):
+ if request.method == 'GET':
+ return redirect(request, 'mediagoblin.auth.login')
+
+ email = _get_response(request)
+ if email:
+ query = PersonaUserEmails.query.filter_by(
+ persona_email=email
+ ).first()
+ user = query.user if query else None
+
+ if user:
+ request.session['user_id'] = unicode(user.id)
+ request.session.save()
+
+ return redirect(request, "index")
+
+ else:
+ if not mg_globals.app.auth:
+ messages.add_message(
+ request,
+ messages.WARNING,
+ _('Sorry, authentication is disabled on this instance.'))
+
+ return redirect(request, 'index')
+
+ register_form = forms.RegistrationForm(email=email,
+ persona_email=email)
+ return render_to_response(
+ request,
+ 'mediagoblin/auth/register.html',
+ {'register_form': register_form,
+ 'post_url': request.urlgen(
+ 'mediagoblin.plugins.persona.register')})
+
+ return redirect(request, 'mediagoblin.auth.login')
+
+
+@allow_registration
+@auth_enabled
+def register(request):
+ if request.method == 'GET':
+ # Need to connect to persona before registering a user. If method is
+ # 'GET', then this page was acessed without logging in first.
+ return redirect(request, 'mediagoblin.auth.login')
+ register_form = forms.RegistrationForm(request.form)
+
+ if register_form.validate():
+ user = register_user(request, register_form)
+
+ if user:
+ # redirect the user to their homepage... there will be a
+ # message waiting for them to verify their email
+ return redirect(
+ request, 'mediagoblin.user_pages.user_home',
+ user=user.username)
+
+ return render_to_response(
+ request,
+ 'mediagoblin/auth/register.html',
+ {'register_form': register_form,
+ 'post_url': request.urlgen('mediagoblin.plugins.persona.register')})
+
+
+@require_active_login
+def edit(request):
+ form = forms.EditForm(request.form)
+
+ if request.method == 'POST' and form.validate():
+ query = PersonaUserEmails.query.filter_by(
+ persona_email=form.email.data)
+ user = query.first().user if query.first() else None
+
+ if user and user.id == int(request.user.id):
+ count = len(user.persona_emails)
+
+ if count > 1 or user.pw_hash:
+ # User has more then one Persona email or also has a password.
+ query.first().delete()
+
+ messages.add_message(
+ request,
+ messages.SUCCESS,
+ _('The Persona email address was successfully removed.'))
+
+ return redirect(request, 'mediagoblin.edit.account')
+
+ elif not count > 1:
+ form.email.errors.append(
+ _("You can't delete your only Persona email address unless"
+ " you have a password set."))
+
+ else:
+ form.email.errors.append(
+ _('That Persona email address is not registered to this'
+ ' account.'))
+
+ return render_to_response(
+ request,
+ 'mediagoblin/plugins/persona/edit.html',
+ {'form': form,
+ 'edit_persona': True})
+
+
+@require_active_login
+def add(request):
+ if request.method == 'GET':
+ return redirect(request, 'mediagoblin.plugins.persona.edit')
+
+ email = _get_response(request)
+
+ if email:
+ query = PersonaUserEmails.query.filter_by(
+ persona_email=email
+ ).first()
+ user_exists = query.user if query else None
+
+ if user_exists:
+ messages.add_message(
+ request,
+ messages.WARNING,
+ _('Sorry, an account is already registered with that Persona'
+ ' email address.'))
+ return redirect(request, 'mediagoblin.plugins.persona.edit')
+
+ else:
+ # Save the Persona Email to the user
+ new_entry = PersonaUserEmails()
+ new_entry.persona_email = email
+ new_entry.user_id = request.user.id
+ new_entry.save()
+
+ messages.add_message(
+ request,
+ messages.SUCCESS,
+ _('Your Person email address was saved successfully.'))
+
+ return redirect(request, 'mediagoblin.edit.account')
diff --git a/mediagoblin/templates/mediagoblin/auth/login.html b/mediagoblin/templates/mediagoblin/auth/login.html
index 3329b5d0..af8c61b5 100644
--- a/mediagoblin/templates/mediagoblin/auth/login.html
+++ b/mediagoblin/templates/mediagoblin/auth/login.html
@@ -47,6 +47,13 @@
{% endif %}
{% template_hook("login_link") %}
+ {% if persona is defined %}
+
+
+ {% trans %}Or login with Persona!{% endtrans %}
+
+
+ {% endif %}
{{ wtforms_util.render_divs(login_form, True) }}
{% if pass_auth %}
diff --git a/mediagoblin/templates/mediagoblin/auth/register.html b/mediagoblin/templates/mediagoblin/auth/register.html
index a7b8033f..8ed389a0 100644
--- a/mediagoblin/templates/mediagoblin/auth/register.html
+++ b/mediagoblin/templates/mediagoblin/auth/register.html
@@ -35,6 +35,14 @@
diff --git a/mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html b/mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html
index 9ef28a4d..dbc23a1f 100644
--- a/mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html
+++ b/mediagoblin/templates/mediagoblin/bits/frontpage_welcome.html
@@ -26,8 +26,14 @@
{% trans %}To add your own media, place comments, and more, you can log in with your MediaGoblin account.{% endtrans %}
{% if allow_registration %}
{% trans %}Don't have one yet? It's easy!{% endtrans %}
- {% trans register_url=request.urlgen('mediagoblin.auth.register') -%}
- Create an account at this site
+ Create an account at this site
or
{%- endtrans %}
{% endif %}
diff --git a/mediagoblin/templates/mediagoblin/edit/edit_account.html b/mediagoblin/templates/mediagoblin/edit/edit_account.html
index 51293acb..9bd488d7 100644
--- a/mediagoblin/templates/mediagoblin/edit/edit_account.html
+++ b/mediagoblin/templates/mediagoblin/edit/edit_account.html
@@ -49,6 +49,14 @@
{% endif %}
{% template_hook("edit_link") %}
+ {% template_hook("openid_edit_link") %}
+ {% if persona is defined %}
+
+
+ {% trans %}Edit your Persona email addresses{% endtrans %}
+
+
+ {% endif %}
{{ wtforms_util.render_divs(form, True) }}