From 7decd70de6138822ba8a5f8772bcc8c22fe0b2c8 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Sun, 24 Jun 2012 23:29:09 -0400 Subject: updated ldap protocol --- askbot/conf/ldap.py | 31 +++++++++++++++ askbot/deps/django_authopenid/backends.py | 64 ++++++++++++++++++++++++------- askbot/doc/source/changelog.rst | 2 + askbot/doc/source/debugging.rst | 35 +++++++++++++++++ askbot/doc/source/live-settings.rst | 37 ++++++++++++++++++ askbot/doc/source/optional-modules.rst | 54 ++++++++++++++++++-------- 6 files changed, 194 insertions(+), 29 deletions(-) diff --git a/askbot/conf/ldap.py b/askbot/conf/ldap.py index 077ff792..00c8a5fc 100644 --- a/askbot/conf/ldap.py +++ b/askbot/conf/ldap.py @@ -19,6 +19,25 @@ settings.register( ) ) +LDAP_PROTOCOL_VERSION_CHOICES = ( + ('3', _('Version 3')), + ('2', _('Version 2 (insecure and deprecated)!!!')) +) + +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_PROTOCOL_VERSION', + default = '3', + choices = LDAP_PROTOCOL_VERSION_CHOICES, + description = _('LDAP protocol version'), + help_text = _( + 'Note that Version 2 protocol is not secure!!! ' + 'Do not use it on unprotected network.' + ) + ) +) + settings.register( livesettings.StringValue( LDAP_SETTINGS, @@ -36,6 +55,18 @@ settings.register( ) ) +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_USER_FILTER_TEMPLATE', + description = _('User search filter template'), + default = '(%s=%s)', + help_text = _( + 'Python string format template, must have two string placeholders' + ) + ) +) + settings.register( livesettings.StringValue( LDAP_SETTINGS, diff --git a/askbot/deps/django_authopenid/backends.py b/askbot/deps/django_authopenid/backends.py index ed99e44f..515fbebe 100644 --- a/askbot/deps/django_authopenid/backends.py +++ b/askbot/deps/django_authopenid/backends.py @@ -6,6 +6,7 @@ import datetime import logging from django.contrib.auth.models import User from django.core.exceptions import ImproperlyConfigured +from django.conf import settings as django_settings from django.utils.translation import ugettext as _ from askbot.deps.django_authopenid.models import UserAssociation from askbot.deps.django_authopenid import util @@ -26,29 +27,66 @@ def ldap_authenticate(username, password): user_information = None try: ldap_session = ldap.initialize(askbot_settings.LDAP_URL) - ldap_session.protocol_version = ldap.VERSION3 - user_filter = "({0}={1})".format(askbot_settings.LDAP_USERID_FIELD, - username) + + #set protocol version + if askbot_settings.LDAP_PROTOCOL_VERSION == '2': + ldap_session.protocol_version = ldap.VERSION2 + elif askbot_settings.LDAP_PROTOCOL_VERSION == '3': + ldap_session.protocol_version = ldap.VERSION3 + else: + raise NotImplementedError('unsupported version of ldap protocol') + + #set extra ldap options, if given + if hasattr(django_settings, 'LDAP_EXTRA_OPTIONS'): + options = django_settings.LDAP_EXTRA_OPTIONS + for key, value in options: + if key.startswith('OPT_'): + ldap_key = getattr(ldap, key) + ldap.set_option(ldap_key, value) + else: + raise ValueError('Invalid LDAP option %s' % key) + + #add optional "master" LDAP authentication, if required + master_username = getattr(django_settings, 'LDAP_USER', None) + master_password = getattr(django_settings, 'LDAP_PASSWORD', None) + if master_username and master_password: + ldap_session.simple_bind_s(master_username, master_password) + + user_filter = askbot_settings.LDAP_USERNAME_FILTER_TEMPLATE % ( + askbot_settings.LDAP_USERID_FIELD, + username + ) + + get_attrs = ( + askbot_settings.LDAP_EMAIL_FIELD, + #askbot_settings.LDAP_SCREEN_NAME_FIELD + #todo: here we have a chance to get more data from LDAP + #maybe a point for some plugin + ) + # search ldap directory for user - res = ldap_session.search_s(askbot_settings.LDAP_BASEDN, ldap.SCOPE_SUBTREE, user_filter, None) - if res: # User found in LDAP Directory - user_dn = res[0][0] - user_information = res[0][1] - ldap_session.simple_bind_s(user_dn, password) # <-- will throw ldap.INVALID_CREDENTIALS if fails + user_search_result = ldap_session.search_s( + askbot_settings.LDAP_BASEDN, ldap.SCOPE_SUBTREE, user_filter, get_attrs + ) + if user_search_result: # User found in LDAP Directory + user_dn = user_search_result[0][0] + user_information = user_search_result[0][1] + ldap_session.simple_bind_s(user_dn, password) #raises INVALID_CREDENTIALS ldap_session.unbind_s() exact_username = user_information[askbot_settings.LDAP_USERID_FIELD][0] # Assuming last, first order # --> may be different - last_name, first_name = user_information[askbot_settings.LDAP_COMMONNAME_FIELD][0].rsplit(" ", 1) + #last_name, first_name = user_information[askbot_settings.LDAP_COMMONNAME_FIELD][0].rsplit(" ", 1) + email = user_information[askbot_settings.LDAP_EMAIL_FIELD][0] try: user = User.objects.get(username__exact=exact_username) # always update user profile to synchronize with ldap server user.set_password(password) - user.first_name = first_name - user.last_name = last_name + #user.first_name = first_name + #user.last_name = last_name user.email = email user.save() except User.DoesNotExist: @@ -56,8 +94,8 @@ def ldap_authenticate(username, password): user = User() user.username = exact_username user.set_password(password) - user.first_name = first_name - user.last_name = last_name + #user.first_name = first_name + #user.last_name = last_name user.email = email user.is_staff = False user.is_superuser = False diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst index 89290fb6..e1008390 100644 --- a/askbot/doc/source/changelog.rst +++ b/askbot/doc/source/changelog.rst @@ -3,6 +3,8 @@ Changes in Askbot Development version ------------------- +* Updated LDAP configuration: allow protocol change, master login and + adding "extra options" to the ldap session (Evgeny) * Simple tag moderation via email (Evgeny) * Editable optional three level category selector for the tags (Evgeny) * Tag editor adding tags as they are typed (Evgeny) diff --git a/askbot/doc/source/debugging.rst b/askbot/doc/source/debugging.rst index 4b46ee58..af865b12 100644 --- a/askbot/doc/source/debugging.rst +++ b/askbot/doc/source/debugging.rst @@ -7,6 +7,41 @@ Debugging Askbot (and other Django applications) This document describes techniques that can be used to debug Askbot and other Django projects If you discover new debugging techniques, please add here. +.. _runserver: +Use development server for debugging +------------------------------------ + +Django comes with a handy development webserver that can be started with the command:: + + python manage.py runserver + +With the combination of runserver, +the :ref:`python debugger `, +and even inserted "print" statements directly in the code +it is possible to "look into" the program as it runs. + +Inspect the log file +-------------------- + +By default askbot will log errors into file `log/askbot.log` within the +project directory. See what's inside that file. + +Note that in the production setups there are many log files - for the +production webserver, database, etc. + +.. _pdb: +Use Python debugger +------------------- + +In the problematic portion of the code, insert lines:: + + import pdb + pdb.set_trace() + +Then fire up the :ref:`runserver ` and step through the program. +When you see prompt starting with `(pdb)` +type `help` and see what options there are. + Use logging in code --------------------- diff --git a/askbot/doc/source/live-settings.rst b/askbot/doc/source/live-settings.rst index e154a257..12546e6c 100644 --- a/askbot/doc/source/live-settings.rst +++ b/askbot/doc/source/live-settings.rst @@ -19,3 +19,40 @@ No-one but the site administrators can change those settings. At the moment this command is not available from the web-interface but this will be fixed in the future. +.. _live-settings-options: +Entering live settings in settings.py file +========================================== + +You might want to bypass live settings and enter them directly +in the ``settings.py`` file in the ``LIVESETTINGS_OPTIONS`` dictionary. + +Having live settings overridden from the ``settings.py`` file may +somewhat speed up your site +and +decrease a chance that the values could be accessed +by an unauthorized person. + +Please see an example below:: + + LIVESETTINGS_OPTIONS = { + 1: { + 'DB' : True, + 'SETTINGS': { + 'EMAIL': { + 'REPLY_BY_EMAIL': True + } + } + } + +Firstly, the number "1" is site id. Most +likely the number should be the same as the value of ``SITE_ID`` setting. + +The value for the site id key is a nested dictionary with two keys: +``'DB'`` (if True - then the rest of settings will be taken from the database) +and ``'SETTINGS'`` - a dictionary with the actual settings. +In this example ``'EMAIL'`` is the settings group +and +``'REPLY_BY_EMAIL'`` is the setting name, with ``True`` being the value. + +Setting group names and setting names can be looked up in files within +``askbot/conf`` directory. diff --git a/askbot/doc/source/optional-modules.rst b/askbot/doc/source/optional-modules.rst index 25bb5cc8..54043c1e 100644 --- a/askbot/doc/source/optional-modules.rst +++ b/askbot/doc/source/optional-modules.rst @@ -83,24 +83,46 @@ To enable authentication via LDAP pip install python-ldap -After that, add configuration parameters in :ref:`live settings `, section -"Keys to connect the site with external services ..." -(url ``/settings/EXTERNAL_KEYS``, relative to the domain name) +After that, add configuration parameters in :ref:`live settings `, +section "LDAP settings" +(url ``/settings/LDAP_SETTINGS``, relative to the forum base url) .. note:: - Location of these parameters is likely to change in the future. - When that happens, an update notice will appear in the documentation. - -The parameters are: - -* "Use LDAP authentication for the password login" - enable/disable the feature. - When enabled, the user name and password will be routed to use the LDAP protocol. - Default system password authentication will be overridden. -* "LDAP service provider name" - any string - just come up with a name for the provider service. -* "URL fro the LDAP service" - a correct url to access the service. -* "Explain how to change the LDAP password" - - askbot does not provide a method to change LDAP passwords - , therefore - use this field to explain users how they can change their passwords. + While it is possible to configure LDAP via web interface, + it is actually more safe to add them in your ``settings.py`` file in the + :ref:`LIVESETTINGS_OPTIONS ` dictionary. + Consider that a breach in security of your forum might open + malicious access into your LDAP directory. + +The parameters are (note that some have pre-set defaults that might work for you):: + +* enable/disable LDAP for password login +* protocol version (``LDAP_PROTOCOL_VERSION``) (version 2 is insecure and deprecated) +* ldap url (``LDAP_URL``) +* base distinguished name, 'dn' in LDAP parlance (``LDAP_BASEDN``) +* user id field name (``LDAP_USERID_FIELD``) +* email field name (``LDAP_EMAIL_FIELD``) +* user name filter template (``LDAP_USERNAME_FILTER_TEMPLATE``) +* user name filter template - must have two string placeholders. + +There are three more optional parameters that must go to the ``settings.py`` file:: + +* ``LDAP_USER`` +* ``LDAP_PASSWORD`` +* ``LDAP_EXTRA_OPTIONS``, a list of two-item tuples - of names and values of + the options. Option names must be upper case strings all starting with ``OPT_`` + as described in the `python ldap library documentation `_. An often used option is (`OPT_REFERRALS`, 0). + +Use these when you have the "directory master passsword" - +for a specific user who can access the rest of the directory, +these were not added to the live settings due to security concerns. + +``LDAP_USER`` and ``LDAP_PASSWORD`` will be used only if both are provided! + +Since LDAP authentication requires so many parameters, +you might need to :ref:`debug ` the settings. +The function to look at is `askbot.deps.django_authopenid.backends.ldap_authenticate`. +If you have problems with LDAP please contact us at support@askbot.com. Uploaded avatars ================ -- cgit v1.2.3-1-g7c22