diff options
Diffstat (limited to 'mediagoblin/plugins/ldap')
-rw-r--r-- | mediagoblin/plugins/ldap/README.rst | 64 | ||||
-rw-r--r-- | mediagoblin/plugins/ldap/__init__.py | 59 | ||||
-rw-r--r-- | mediagoblin/plugins/ldap/forms.py | 40 | ||||
-rw-r--r-- | mediagoblin/plugins/ldap/templates/mediagoblin/plugins/ldap/create_account_link.html | 25 | ||||
-rw-r--r-- | mediagoblin/plugins/ldap/tools.py | 65 | ||||
-rw-r--r-- | mediagoblin/plugins/ldap/views.py | 104 |
6 files changed, 357 insertions, 0 deletions
diff --git a/mediagoblin/plugins/ldap/README.rst b/mediagoblin/plugins/ldap/README.rst new file mode 100644 index 00000000..ea9a34b3 --- /dev/null +++ b/mediagoblin/plugins/ldap/README.rst @@ -0,0 +1,64 @@ +.. MediaGoblin Documentation + + Written in 2012 by MediaGoblin contributors + + To the extent possible under law, the author(s) have dedicated all + copyright and related and neighboring rights to this software to + the public domain worldwide. This software is distributed without + any warranty. + + You should have received a copy of the CC0 Public Domain + Dedication along with this software. If not, see + <http://creativecommons.org/publicdomain/zero/1.0/>. + +.. _ldap-plugin: + +============= + ldap plugin +============= + +.. Warning:: + This plugin is not compatible with the other authentication plugins. + +This plugin allow your GNU Mediagoblin instance to authenticate against an +LDAP server. + +Set up the ldap plugin +====================== + +1. Install the ``python-ldap`` package. + +2. Add the following to your MediaGoblin .ini file in the ``[plugins]`` section:: + + [[mediagoblin.plugins.ldap]] + +Configuring the ldap plugin +=========================== + +This plugin allows you to use multiple ldap servers for authentication. + +In order to configure a server, add the following to you MediaGoblin .ini file +under the ldap plugin:: + + [[mediagoblin.plugins.ldap]] + [[[server1]]] + LDAP_SERVER_URI = 'ldap://ldap.testathon.net:389' + LDAP_USER_DN_TEMPLATE = 'cn={username},ou=users,dc=testathon,dc=net' + [[[server2]]] + ... + +Make any necessary changes to the above to work with your sever. Make sure +``{username}`` is where the username should be in LDAP_USER_DN_TEMPLATE. + +If you would like to fetch the users email from the ldap server upon account +registration, add ``LDAP_SEARCH_BASE = 'ou=users,dc=testathon,dc=net'`` and +``EMAIL_SEARCH_FIELD = 'mail'`` under you server configuration in your +MediaGoblin .ini file. + +.. Warning:: + By default, this plugin provides no encryption when communicating with the + ldap servers. If you would like to use an SSL connection, change + LDAP_SERVER_URI to use ``ldaps://`` and whichever port you use. Default ldap + port for SSL connections is 636. If you would like to use a TLS connection, + add ``LDAP_START_TLS = 'true'`` under your server configuration in your + MediaGoblin .ini file. diff --git a/mediagoblin/plugins/ldap/__init__.py b/mediagoblin/plugins/ldap/__init__.py new file mode 100644 index 00000000..4673acee --- /dev/null +++ b/mediagoblin/plugins/ldap/__init__.py @@ -0,0 +1,59 @@ +# 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 os + +from mediagoblin.auth.tools import create_basic_user +from mediagoblin.tools import pluginapi + +PLUGIN_DIR = os.path.dirname(__file__) + + +def setup_plugin(): + config = pluginapi.get_config('mediagoblin.plugins.ldap') + + routes = [ + ('mediagoblin.plugins.ldap.register', + '/auth/ldap/register/', + 'mediagoblin.plugins.ldap.views:register'), + ('mediagoblin.plugins.ldap.login', + '/auth/ldap/login/', + 'mediagoblin.plugins.ldap.views:login')] + + pluginapi.register_routes(routes) + pluginapi.register_template_path(os.path.join(PLUGIN_DIR, 'templates')) + + pluginapi.register_template_hooks( + {'create_account': 'mediagoblin/plugins/ldap/create_account_link.html'}) + + +def create_user(register_form): + if 'username' in register_form and 'password' not in register_form: + return create_basic_user(register_form) + + +def no_pass_redirect(): + return 'ldap' + + +def auth(): + return True + +hooks = { + 'setup': setup_plugin, + 'authentication': auth, + 'auth_no_pass_redirect': no_pass_redirect, + 'auth_create_user': create_user, +} diff --git a/mediagoblin/plugins/ldap/forms.py b/mediagoblin/plugins/ldap/forms.py new file mode 100644 index 00000000..7ec1479e --- /dev/null +++ b/mediagoblin/plugins/ldap/forms.py @@ -0,0 +1,40 @@ +# 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 RegisterForm(wtforms.Form): + username = wtforms.HiddenField( + '', + [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)]) + + +class LoginForm(wtforms.Form): + username = wtforms.TextField( + _('Username'), + [wtforms.validators.Required(), + normalize_user_or_email_field()]) + password = wtforms.PasswordField( + _('Password'), + [wtforms.validators.Required()]) diff --git a/mediagoblin/plugins/ldap/templates/mediagoblin/plugins/ldap/create_account_link.html b/mediagoblin/plugins/ldap/templates/mediagoblin/plugins/ldap/create_account_link.html new file mode 100644 index 00000000..947e4ae2 --- /dev/null +++ b/mediagoblin/plugins/ldap/templates/mediagoblin/plugins/ldap/create_account_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 create_account %} + {% if allow_registration %} + <p> + {% trans %}Sign in to create an account!{% endtrans %} + </p> + {% endif %} +{% endblock %} diff --git a/mediagoblin/plugins/ldap/tools.py b/mediagoblin/plugins/ldap/tools.py new file mode 100644 index 00000000..1c436792 --- /dev/null +++ b/mediagoblin/plugins/ldap/tools.py @@ -0,0 +1,65 @@ +# 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 ldap +import logging + +from mediagoblin.tools import pluginapi + +_log = logging.getLogger(__name__) + + +class LDAP(object): + def __init__(self): + self.ldap_settings = pluginapi.get_config('mediagoblin.plugins.ldap') + + def _connect(self, server): + _log.info('Connecting to {0}.'.format(server['LDAP_SERVER_URI'])) + self.conn = ldap.initialize(server['LDAP_SERVER_URI']) + + if server['LDAP_START_TLS'] == 'true': + _log.info('Initiating TLS') + self.conn.start_tls_s() + + def _get_email(self, server, username): + try: + results = self.conn.search_s(server['LDAP_SEARCH_BASE'], + ldap.SCOPE_SUBTREE, 'uid={0}' + .format(username), + [server['EMAIL_SEARCH_FIELD']]) + + email = results[0][1][server['EMAIL_SEARCH_FIELD']][0] + except KeyError: + email = None + + return email + + def login(self, username, password): + for k, v in self.ldap_settings.iteritems(): + try: + self._connect(v) + user_dn = v['LDAP_USER_DN_TEMPLATE'].format(username=username) + self.conn.simple_bind_s(user_dn, password.encode('utf8')) + email = self._get_email(v, username) + return username, email + + except ldap.LDAPError, e: + _log.info(e) + + finally: + _log.info('Unbinding {0}.'.format(v['LDAP_SERVER_URI'])) + self.conn.unbind() + + return False, None diff --git a/mediagoblin/plugins/ldap/views.py b/mediagoblin/plugins/ldap/views.py new file mode 100644 index 00000000..aef1bf56 --- /dev/null +++ b/mediagoblin/plugins/ldap/views.py @@ -0,0 +1,104 @@ +# 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 mediagoblin import mg_globals, messages +from mediagoblin.auth.tools import register_user +from mediagoblin.db.models import User +from mediagoblin.decorators import allow_registration, auth_enabled +from mediagoblin.plugins.ldap import forms +from mediagoblin.plugins.ldap.tools import LDAP +from mediagoblin.tools.translate import pass_to_ugettext as _ +from mediagoblin.tools.response import redirect, render_to_response + + +@auth_enabled +def login(request): + login_form = forms.LoginForm(request.form) + + login_failed = False + + if request.method == 'POST' and login_form.validate(): + l = LDAP() + username, email = l.login(login_form.username.data, + login_form.password.data) + + if username: + user = User.query.filter_by( + username=username).first() + + if user: + # set up login in session + request.session['user_id'] = unicode(user.id) + request.session.save() + + if request.form.get('next'): + return redirect(request, location=request.form['next']) + else: + 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.RegisterForm(username=username, + email=email) + + return render_to_response( + request, + 'mediagoblin/auth/register.html', + {'register_form': register_form, + 'post_url': request.urlgen('mediagoblin.plugins.ldap.register')}) + + login_failed = True + + return render_to_response( + request, + 'mediagoblin/auth/login.html', + {'login_form': login_form, + 'next': request.GET.get('next') or request.form.get('next'), + 'login_failed': login_failed, + 'post_url': request.urlgen('mediagoblin.plugins.ldap.login'), + 'allow_registration': mg_globals.app_config["allow_registration"]}) + + +@allow_registration +@auth_enabled +def register(request): + if request.method == 'GET': + return redirect( + request, + 'mediagoblin.plugins.ldap.login') + + register_form = forms.RegisterForm(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.ldap.register')}) |