diff options
Diffstat (limited to 'mediagoblin/plugins')
22 files changed, 720 insertions, 35 deletions
diff --git a/mediagoblin/plugins/api/tools.py b/mediagoblin/plugins/api/tools.py index 92411f4b..d1b3ebb1 100644 --- a/mediagoblin/plugins/api/tools.py +++ b/mediagoblin/plugins/api/tools.py @@ -51,30 +51,6 @@ class Auth(object): def __call__(self, request, *args, **kw): raise NotImplemented() - -def json_response(serializable, _disable_cors=False, *args, **kw): - ''' - Serializes a json objects and returns a werkzeug Response object with the - serialized value as the response body and Content-Type: application/json. - - :param serializable: A json-serializable object - - Any extra arguments and keyword arguments are passed to the - Response.__init__ method. - ''' - response = Response(json.dumps(serializable), *args, content_type='application/json', **kw) - - if not _disable_cors: - cors_headers = { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS', - 'Access-Control-Allow-Headers': 'Content-Type, X-Requested-With'} - for key, value in cors_headers.iteritems(): - response.headers.set(key, value) - - return response - - def get_entry_serializable(entry, urlgen): ''' Returns a serializable dict() of a MediaEntry instance. diff --git a/mediagoblin/plugins/api/views.py b/mediagoblin/plugins/api/views.py index 9159fe65..b7e74799 100644 --- a/mediagoblin/plugins/api/views.py +++ b/mediagoblin/plugins/api/views.py @@ -21,11 +21,11 @@ from os.path import splitext from werkzeug.exceptions import BadRequest, Forbidden from werkzeug.wrappers import Response +from mediagoblin.tools.response import json_response from mediagoblin.decorators import require_active_login from mediagoblin.meddleware.csrf import csrf_exempt from mediagoblin.media_types import sniff_media -from mediagoblin.plugins.api.tools import api_auth, get_entry_serializable, \ - json_response +from mediagoblin.plugins.api.tools import api_auth, get_entry_serializable from mediagoblin.submit.lib import check_file_field, prepare_queue_task, \ run_process_media, new_upload_entry diff --git a/mediagoblin/plugins/basic_auth/README.rst b/mediagoblin/plugins/basic_auth/README.rst new file mode 100644 index 00000000..82f247ed --- /dev/null +++ b/mediagoblin/plugins/basic_auth/README.rst @@ -0,0 +1,24 @@ +.. _basic_auth-chapter: + +=================== + basic_auth plugin +=================== + +The basic_auth plugin is enabled by default in mediagoblin.ini. This plugin +provides basic username and password authentication for GNU Mediagoblin. + +This plugin can be enabled alongside :ref:`openid-chapter` and +:ref:`persona-chapter`. + +Set up the basic_auth plugin +============================ + +1. Add the following to your MediaGoblin .ini file in the ``[plugins]`` section:: + + [[mediagoblin.plugins.basic_auth]] + +2. Run:: + + gmg assetlink + + in order to link basic_auth's static assets diff --git a/mediagoblin/plugins/oauth/__init__.py b/mediagoblin/plugins/oauth/__init__.py index 5762379d..82c1f380 100644 --- a/mediagoblin/plugins/oauth/__init__.py +++ b/mediagoblin/plugins/oauth/__init__.py @@ -35,22 +35,22 @@ def setup_plugin(): routes = [ ('mediagoblin.plugins.oauth.authorize', - '/oauth/authorize', + '/oauth-2/authorize', 'mediagoblin.plugins.oauth.views:authorize'), ('mediagoblin.plugins.oauth.authorize_client', - '/oauth/client/authorize', + '/oauth-2/client/authorize', 'mediagoblin.plugins.oauth.views:authorize_client'), ('mediagoblin.plugins.oauth.access_token', - '/oauth/access_token', + '/oauth-2/access_token', 'mediagoblin.plugins.oauth.views:access_token'), ('mediagoblin.plugins.oauth.list_connections', - '/oauth/client/connections', + '/oauth-2/client/connections', 'mediagoblin.plugins.oauth.views:list_connections'), ('mediagoblin.plugins.oauth.register_client', - '/oauth/client/register', + '/oauth-2/client/register', 'mediagoblin.plugins.oauth.views:register_client'), ('mediagoblin.plugins.oauth.list_clients', - '/oauth/client/list', + '/oauth-2/client/list', 'mediagoblin.plugins.oauth.views:list_clients')] pluginapi.register_routes(routes) diff --git a/mediagoblin/plugins/oauth/tools.py b/mediagoblin/plugins/oauth/tools.py index 27ff32b4..af0a3305 100644 --- a/mediagoblin/plugins/oauth/tools.py +++ b/mediagoblin/plugins/oauth/tools.py @@ -23,7 +23,7 @@ from datetime import datetime from functools import wraps -from mediagoblin.plugins.api.tools import json_response +from mediagoblin.tools.response import json_response def require_client_auth(controller): diff --git a/mediagoblin/plugins/oauth/views.py b/mediagoblin/plugins/oauth/views.py index d6fd314f..de637d6b 100644 --- a/mediagoblin/plugins/oauth/views.py +++ b/mediagoblin/plugins/oauth/views.py @@ -21,7 +21,7 @@ from urllib import urlencode from werkzeug.exceptions import BadRequest -from mediagoblin.tools.response import render_to_response, redirect +from mediagoblin.tools.response import render_to_response, redirect, json_response from mediagoblin.decorators import require_active_login from mediagoblin.messages import add_message, SUCCESS from mediagoblin.tools.translate import pass_to_ugettext as _ @@ -31,7 +31,6 @@ from mediagoblin.plugins.oauth.forms import ClientRegistrationForm, \ AuthorizationForm from mediagoblin.plugins.oauth.tools import require_client_auth, \ create_token -from mediagoblin.plugins.api.tools import json_response _log = logging.getLogger(__name__) diff --git a/mediagoblin/plugins/openid/README.rst b/mediagoblin/plugins/openid/README.rst new file mode 100644 index 00000000..870a2b58 --- /dev/null +++ b/mediagoblin/plugins/openid/README.rst @@ -0,0 +1,34 @@ +.. _openid-chapter: + +=================== + openid plugin +=================== + +The openid plugin allows user to login to your GNU Mediagoblin instance using +their openid url. + +This plugin can be enabled alongside :ref:`basic_auth-chapter` and +:ref:`persona-chapter`. + +.. note:: + When :ref:`basic_auth-chapter` is enabled alongside this openid plugin, and + a user creates an account using their openid. If they would like to add a + password to their account, they can use the forgot password feature to do + so. + + +Set up the openid plugin +============================ + +1. Install the ``python-openid`` package. + +2. Add the following to your MediaGoblin .ini file in the ``[plugins]`` section:: + + [[mediagoblin.plugins.openid]] + +3. Run:: + + gmg dbupdate + + in order to create and apply migrations to any database tables that the + plugin requires. diff --git a/mediagoblin/plugins/openid/__init__.py b/mediagoblin/plugins/openid/__init__.py index ee88808c..ca17a7e8 100644 --- a/mediagoblin/plugins/openid/__init__.py +++ b/mediagoblin/plugins/openid/__init__.py @@ -120,4 +120,6 @@ hooks = { 'auth_no_pass_redirect': no_pass_redirect, ('mediagoblin.auth.register', 'mediagoblin/auth/register.html'): add_to_form_context, + ('mediagoblin.auth.login', + 'mediagoblin/auth/login.html'): add_to_form_context } diff --git a/mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html b/mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html index 33df7200..8d74c2b9 100644 --- a/mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html +++ b/mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login.html @@ -44,6 +44,7 @@ {% trans %}Log in to create an account!{% endtrans %} </p> {% endif %} + {% template_hook('login_link') %} {% if pass_auth is defined %} <p> <a href="{{ request.urlgen('mediagoblin.auth.login') }}?{{ request.query_string }}"> diff --git a/mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login_link.html b/mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login_link.html index e5e77d01..fa4d5e85 100644 --- a/mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login_link.html +++ b/mediagoblin/plugins/openid/templates/mediagoblin/plugins/openid/login_link.html @@ -17,9 +17,11 @@ #} {% block openid_login_link %} + {% if openid_link is defined %} <p> <a href="{{ request.urlgen('mediagoblin.plugins.openid.login') }}?{{ request.query_string }}"> {%- trans %}Or login with OpenID!{% endtrans %} </a> </p> + {% endif %} {% endblock %} diff --git a/mediagoblin/plugins/persona/README.rst b/mediagoblin/plugins/persona/README.rst new file mode 100644 index 00000000..ef19ac5d --- /dev/null +++ b/mediagoblin/plugins/persona/README.rst @@ -0,0 +1,41 @@ +.. _persona-chapter: + +================ + persona plugin +================ + +The persona plugin allows users to login to you GNU MediaGoblin instance using +`Mozilla Persona`_. + +This plugin can be enabled alongside :ref:`openid-chapter` and +:ref:`basic_auth-chapter`. + +.. note:: + When :ref:`basic_auth-chapter` is enabled alongside this persona plugin, and + a user creates an account using their persona. If they would like to add a + password to their account, they can use the forgot password feature to do + so. + +.. _Mozilla Persona: https://www.mozilla.org/en-US/persona/ + +Set up the persona plugin +========================= + +1. Install the ``requests`` package. + +2. Add the following to your MediaGoblin .ini file in the ``[plugins]`` section:: + + [[mediagoblin.plugins.persona]] + +3. Run:: + + gmg dbupdate + + in order to create and apply migrations to any database tables that the + plugin requires. + +4. Run:: + + gmg assetlink + + in order to persona's static assets. diff --git a/mediagoblin/plugins/persona/__init__.py b/mediagoblin/plugins/persona/__init__.py new file mode 100644 index 00000000..700c18e2 --- /dev/null +++ b/mediagoblin/plugins/persona/__init__.py @@ -0,0 +1,116 @@ +# 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 <http://www.gnu.org/licenses/>. +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_end': 'mediagoblin/plugins/persona/persona_js_end.html', + 'persona_form': 'mediagoblin/plugins/persona/persona.html', + 'edit_link': 'mediagoblin/plugins/persona/edit_link.html', + 'login_link': 'mediagoblin/plugins/persona/login_link.html', + 'register_link': 'mediagoblin/plugins/persona/register_link.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 <http://www.gnu.org/licenses/>. +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 <http://www.gnu.org/licenses/>. +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..a6def398 --- /dev/null +++ b/mediagoblin/plugins/persona/static/js/persona.js @@ -0,0 +1,51 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + */ + +$(document).ready(function () { + var signinLink = document.getElementById('persona_login'); + if (signinLink) { + signinLink.onclick = function() { navigator.id.request(); }; + } + + var signinLink1 = document.getElementById('persona_login1'); + if (signinLink1) { + signinLink1.onclick = function() { navigator.id.request(); }; + } + + var signoutLink = document.getElementById('logout'); + if (signoutLink) { + signoutLink.onclick = function() { navigator.id.logout(); }; + } + + var logout_url = document.getElementById('_logout_url').value; + + navigator.id.watch({ + onlogin: function(assertion) { + document.getElementById('_assertion').value = assertion; + document.getElementById('_persona_login').submit() + }, + onlogout: function() { + $.ajax({ + type: 'GET', + url: logout_url, + 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 <http://www.gnu.org/licenses/>. +#} +{% extends "mediagoblin/base.html" %} + +{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %} + +{% block title -%} + {% trans %}Add an OpenID{% endtrans %} — {{ super() }} +{%- endblock %} + +{% block mediagoblin_content %} + <form action="{{ request.urlgen('mediagoblin.plugins.persona.edit') }}" + method="POST" enctype="multipart/form-data"> + {{ csrf_token }} + <div class="form_box"> + <h1>{% trans %}Delete a Persona email address{% endtrans %}</h1> + <p> + <a href="javascript:;" id="persona_login"> + {% trans %}Add a Persona email address{% endtrans %} + </a> + </p> + {{ wtforms_util.render_divs(form, True) }} + <div class="form_submit_buttons"> + <input type="submit" value="{% trans %}Delete{% endtrans %}" class="button_form"/> + </div> + </div> + </form> +{% endblock %} diff --git a/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit_link.html b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit_link.html new file mode 100644 index 00000000..08879da5 --- /dev/null +++ b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/edit_link.html @@ -0,0 +1,25 @@ +{# +# 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 <http://www.gnu.org/licenses/>. +#} + +{% block persona_edit_link %} + <p> + <a href="{{ request.urlgen('mediagoblin.plugins.persona.edit') }}"> + {% trans %}Edit your Persona email addresses{% endtrans %} + </a> + </p> +{% endblock %} diff --git a/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/login_link.html b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/login_link.html new file mode 100644 index 00000000..975683da --- /dev/null +++ b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/login_link.html @@ -0,0 +1,25 @@ +{# +# 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 <http://www.gnu.org/licenses/>. +#} + +{% block person_login_link %} + <p> + <a href="javascript:;" id="persona_login"> + {% trans %}Or login with Persona!{% endtrans %} + </a> + </p> +{% 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..372bd246 --- /dev/null +++ b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/persona.html @@ -0,0 +1,32 @@ +{# +# 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 <http://www.gnu.org/licenses/>. +#} +{% block persona %} + <form id="_persona_login" + action= + {%- if edit_persona is defined -%} + "{{ request.urlgen('mediagoblin.plugins.persona.add') }}" + {%- else -%} + "{{ request.urlgen('mediagoblin.plugins.persona.login') }}" + {%- endif %} + method="POST"> + {{ csrf_token }} + <input type="hidden" name="assertion" type="text" id="_assertion"/> + <input type="hidden" name="_logout_url" type="text" id="_logout_url" + value="{{ request.urlgen('mediagoblin.auth.logout') }}"/> + </form> +{% endblock %} diff --git a/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/persona_js_end.html b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/persona_js_end.html new file mode 100644 index 00000000..8c0d72d5 --- /dev/null +++ b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/persona_js_end.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 <http://www.gnu.org/licenses/>. +#} + +<script src="https://login.persona.org/include.js"></script> +<script type="text/javascript" + src="{{ request.staticdirect('/js/persona.js', 'coreplugin_persona') }}"></script> diff --git a/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/register_link.html b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/register_link.html new file mode 100644 index 00000000..bcd9ae2b --- /dev/null +++ b/mediagoblin/plugins/persona/templates/mediagoblin/plugins/persona/register_link.html @@ -0,0 +1,25 @@ +{# +# 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 <http://www.gnu.org/licenses/>. +#} + +{% block persona_register_link %} + <p> + <a href="javascript:;" id="persona_login"> + {% trans %}Or register with Persona!{% endtrans %} + </a> + </p> +{% endblock %} 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 <http://www.gnu.org/licenses/>. +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') |