diff options
author | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2012-03-11 00:18:28 -0300 |
---|---|---|
committer | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2012-03-11 00:18:28 -0300 |
commit | 81b775b6e292a943d609132985711eec17e8184d (patch) | |
tree | de6e4b2ea94654570a19a840ed30be524bcfc236 | |
parent | 41b6c6da9067efffa2b861b63d3c1dffccae17f3 (diff) | |
parent | 50a6f37885b0d489d6e29ee9f54895ab74173b9e (diff) | |
download | askbot-81b775b6e292a943d609132985711eec17e8184d.tar.gz askbot-81b775b6e292a943d609132985711eec17e8184d.tar.bz2 askbot-81b775b6e292a943d609132985711eec17e8184d.zip |
merged the LDAP login contribution by user monkut
-rw-r--r-- | askbot/conf/__init__.py | 1 | ||||
-rw-r--r-- | askbot/conf/external_keys.py | 35 | ||||
-rw-r--r-- | askbot/conf/ldap.py | 93 | ||||
-rw-r--r-- | askbot/deps/django_authopenid/backends.py | 100 | ||||
-rw-r--r-- | askbot/deps/django_authopenid/views.py | 38 | ||||
-rw-r--r-- | askbot/doc/source/changelog.rst | 1 | ||||
-rw-r--r-- | askbot/doc/source/contributors.rst | 1 | ||||
-rw-r--r-- | askbot/management/commands/initialize_ldap_logins.py | 68 |
8 files changed, 201 insertions, 136 deletions
diff --git a/askbot/conf/__init__.py b/askbot/conf/__init__.py index de1eeccc..dff91d8e 100644 --- a/askbot/conf/__init__.py +++ b/askbot/conf/__init__.py @@ -9,6 +9,7 @@ import askbot.conf.flatpages import askbot.conf.site_settings import askbot.conf.license import askbot.conf.external_keys +import askbot.conf.ldap import askbot.conf.skin_general_settings import askbot.conf.sidebar_main import askbot.conf.sidebar_question diff --git a/askbot/conf/external_keys.py b/askbot/conf/external_keys.py index a673534a..24a43265 100644 --- a/askbot/conf/external_keys.py +++ b/askbot/conf/external_keys.py @@ -53,6 +53,8 @@ settings.register( ) ) + + settings.register( livesettings.StringValue( EXTERNAL_KEYS, @@ -160,36 +162,3 @@ settings.register( description=_('ident.ca consumer secret'), ) ) - -settings.register( - livesettings.BooleanValue( - EXTERNAL_KEYS, - 'USE_LDAP_FOR_PASSWORD_LOGIN', - description=_('Use LDAP authentication for the password login'), - defaut=False - ) -) - -settings.register( - livesettings.StringValue( - EXTERNAL_KEYS, - 'LDAP_PROVIDER_NAME', - description=_('LDAP service provider name') - ) -) - -settings.register( - livesettings.StringValue( - EXTERNAL_KEYS, - 'LDAP_URL', - description=_('URL for the LDAP service') - ) -) - -settings.register( - livesettings.LongStringValue( - EXTERNAL_KEYS, - 'HOW_TO_CHANGE_LDAP_PASSWORD', - description=_('Explain how to change LDAP password') - ) -) diff --git a/askbot/conf/ldap.py b/askbot/conf/ldap.py new file mode 100644 index 00000000..077ff792 --- /dev/null +++ b/askbot/conf/ldap.py @@ -0,0 +1,93 @@ +"""Settings for LDAP login for Askbot""" +from askbot.conf.settings_wrapper import settings +from askbot.conf.super_groups import EXTERNAL_SERVICES +from askbot.deps import livesettings +from django.utils.translation import ugettext as _ + +LDAP_SETTINGS = livesettings.ConfigurationGroup( + 'LDAP_SETTINGS', + _('LDAP login configuration'), + super_group = EXTERNAL_SERVICES + ) + +settings.register( + livesettings.BooleanValue( + LDAP_SETTINGS, + 'USE_LDAP_FOR_PASSWORD_LOGIN', + description=_('Use LDAP authentication for the password login'), + defaut=False + ) +) + +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_URL', + description=_('LDAP URL'), + default="ldap://<host>:<port>" + ) +) + +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_BASEDN', + description=_('LDAP BASE DN') + ) +) + +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_SEARCH_SCOPE', + description=_('LDAP Search Scope'), + default="subs" + ) +) + +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_USERID_FIELD', + description=_('LDAP Server USERID field name'), + default="uid" + ) +) + +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_COMMONNAME_FIELD', + description=_('LDAP Server "Common Name" field name'), + default="cn" + ) +) + +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_EMAIL_FIELD', + description=_('LDAP Server EMAIL field name'), + default="mail" + ) +) + +# May be necessary, but not handled properly. +# --> Commenting out until handled properly in backends.ldap_authenticate() +#settings.register( +# livesettings.StringValue( +# LDAP_SETTINGS, +# 'LDAP_PROXYDN', +# description=_('LDAP PROXY DN'), +# default="" +# ) +#) +# +#settings.register( +# livesettings.StringValue( +# LDAP_SETTINGS, +# 'LDAP_PROXYDN_PASSWORD', +# description=_('LDAP PROXY DN Password'), +# defalut="", +# ) +#) diff --git a/askbot/deps/django_authopenid/backends.py b/askbot/deps/django_authopenid/backends.py index 9f8f1dfd..f3d8f64b 100644 --- a/askbot/deps/django_authopenid/backends.py +++ b/askbot/deps/django_authopenid/backends.py @@ -9,6 +9,84 @@ from django.core.exceptions import ImproperlyConfigured from django.utils.translation import ugettext as _ from askbot.deps.django_authopenid.models import UserAssociation from askbot.deps.django_authopenid import util +from askbot.conf import settings as askbot_settings + +log = logging.getLogger('configuration') + + +def ldap_authenticate(username, password): + """ + Authenticate using ldap + + python-ldap must be installed + http://pypi.python.org/pypi/python-ldap/2.4.6 + """ + import ldap + 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) + # 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 + 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) + 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.email = email + user.save() + except User.DoesNotExist: + # create new user in local db + user = User() + user.username = exact_username + user.set_password(password) + user.first_name = first_name + user.last_name = last_name + user.email = email + user.is_staff = False + user.is_superuser = False + user.is_active = True + user.save() + + log.info('Created New User : [{0}]'.format(exact_username)) + return user + else: + # Maybe a user created internally (django admin user) + try: + user = User.objects.get(username__exact=username) + if user.check_password(password): + return user + else: + return None + except User.DoesNotExist: + return None + + except ldap.INVALID_CREDENTIALS, e: + return None # Will fail login on return of None + except ldap.LDAPError, e: + log.error("LDAPError Exception") + log.exception(e) + return None + except Exception, e: + log.error("Unexpected Exception Occurred") + log.exception(e) + return None + class AuthBackend(object): """Authenticator's authentication backend class @@ -22,15 +100,14 @@ class AuthBackend(object): def authenticate( self, - username = None,#for 'password' - password = None,#for 'password' + username = None,#for 'password' and 'ldap' + password = None,#for 'password' and 'ldap' user_id = None,#for 'force' provider_name = None,#required with all except email_key openid_url = None, email_key = None, oauth_user_id = None,#used with oauth facebook_user_id = None,#user with facebook - ldap_user_id = None,#for ldap wordpress_url = None, # required for self hosted wordpress wp_user_id = None, # required for self hosted wordpress method = None,#requried parameter @@ -40,6 +117,7 @@ class AuthBackend(object): from the signature of the function call """ login_providers = util.get_enabled_login_providers() + assoc = None # UserAssociation not needed for ldap if method == 'password': if login_providers[provider_name]['type'] != 'password': raise ImproperlyConfigured('login provider must use password') @@ -156,14 +234,7 @@ class AuthBackend(object): return None elif method == 'ldap': - try: - assoc = UserAssociation.objects.get( - openid_url = ldap_user_id, - provider_name = provider_name - ) - user = assoc.user - except UserAssociation.DoesNotExist: - return None + user = ldap_authenticate(username, password) elif method == 'wordpress_site': try: @@ -180,9 +251,10 @@ class AuthBackend(object): else: raise TypeError('only openid and password supported') - #update last used time - assoc.last_used_timestamp = datetime.datetime.now() - assoc.save() + if assoc: + #update last used time + assoc.last_used_timestamp = datetime.datetime.now() + assoc.save() return user def get_user(self, user_id): diff --git a/askbot/deps/django_authopenid/views.py b/askbot/deps/django_authopenid/views.py index bb0b4986..22be8460 100644 --- a/askbot/deps/django_authopenid/views.py +++ b/askbot/deps/django_authopenid/views.py @@ -310,30 +310,26 @@ def signin(request): password_action = login_form.cleaned_data['password_action'] if askbot_settings.USE_LDAP_FOR_PASSWORD_LOGIN: assert(password_action == 'login') - ldap_provider_name = askbot_settings.LDAP_PROVIDER_NAME username = login_form.cleaned_data['username'] - if util.ldap_check_password( - username, - login_form.cleaned_data['password'] - ): - user = authenticate( - ldap_user_id = username, - provider_name = ldap_provider_name, - method = 'ldap' - ) - if user is not None: - login(request, user) - return HttpResponseRedirect(next_url) - else: - return finalize_generic_signin( - request = request, - user = user, - user_identifier = username, - login_provider_name = ldap_provider_name, - redirect_url = next_url + password = login_form.cleaned_data['password'] + # will be None if authentication fails + user = authenticate( + username=username, + password=password, + method = 'ldap' ) + if user is not None: + login(request, user) + return HttpResponseRedirect(next_url) else: - login_form.set_password_login_error() + return finalize_generic_signin( + request = request, + user = user, + user_identifier = username, + login_provider_name = ldap_provider_name, + redirect_url = next_url + ) + else: if password_action == 'login': user = authenticate( diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst index a8e695a6..967c1f6c 100644 --- a/askbot/doc/source/changelog.rst +++ b/askbot/doc/source/changelog.rst @@ -20,6 +20,7 @@ Development version (not released yet) * Added test on maximum length of title working for utf-8 text (Evgeny) * Added caching and invalidation to the question page (Evgeny) * Added a management command delete_contextless_activities (Evgeny) +* LDAP login configuration (github user `monkut <https://github.com/monkut>`_) 0.7.39 (Jan 11, 2012) --------------------- diff --git a/askbot/doc/source/contributors.rst b/askbot/doc/source/contributors.rst index a0b91518..eea61b31 100644 --- a/askbot/doc/source/contributors.rst +++ b/askbot/doc/source/contributors.rst @@ -35,6 +35,7 @@ Programming and documentation * `hjwp <https://github.com/hjwp>`_ * `Jacob Oscarson <http://www.aspektratio.net>`_ * `Radim Řehůřek <https://github.com/piskvorky>`_ +* `monkut <https://github.com/monkut>`_ Translations ------------ diff --git a/askbot/management/commands/initialize_ldap_logins.py b/askbot/management/commands/initialize_ldap_logins.py deleted file mode 100644 index 96ee74e5..00000000 --- a/askbot/management/commands/initialize_ldap_logins.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Management command to create LDAP login method for all users. -Please see description of the command in its ``help_text``. -""" -import datetime -from django.core.management.base import CommandError -from django.utils.translation import ugettext as _ -from askbot.management import NoArgsJob -from askbot import models -from askbot.deps.django_authopenid.models import UserAssociation -from askbot.conf import settings as askbot_settings - -def create_ldap_login_for_user(user): - """a unit job that creates LDAP account record for - the user, assuming that his or her LDAP user name - is the same as the user name on the forum site. - If the record already exists, LDAP provider name - will be updated according to the live setting, - otherwise a new record will be created. - Always returns ``True``. - """ - ldap_url = askbot_settings.LDAP_URL - ldap_provider_name = askbot_settings.LDAP_PROVIDER_NAME - if '' in (ldap_url, ldap_provider_name): - raise CommandError( - 'Please, first set up LDAP settings ' - 'at url /settings/EXTERNAL_KEYS,' - 'relative to the base url of your forum site' - ) - try: - assoc = UserAssociation.objects.get( - openid_url = user.username, - user = user - ) - except UserAssociation.DoesNotExist: - assoc = UserAssociation( - openid_url = user.username, - user = user - ) - assoc.provider_name = ldap_provider_name - assoc.last_used_timestamp = datetime.datetime.now() - assoc.save() - return True - -class Command(NoArgsJob): - """definition of the job that - runs through all users and creates LDAP login - methods, assuming that LDAP user ID's are the same - as values ``~askbot.User.username`` - """ - help = _( - 'This command may help you migrate to LDAP ' - 'password authentication by creating a record ' - 'for LDAP association with each user account. ' - 'There is an assumption that ldap user id\'s are ' - 'the same as user names registered at the site. ' - 'Before running this command it is necessary to ' - 'set up LDAP parameters in the "External keys" section ' - 'of the site settings.' - ) - def __init__(self, *args, **kwargs): - self.batches = ({ - 'title': 'Initializing LDAP logins for all users: ', - 'query_set': models.User.objects.all(), - 'function': create_ldap_login_for_user, - 'changed_count_message': 'Created LDAP logins for %d users', - 'nothing_changed_message': 'All users already have LDAP login methods' - },) - super(Command, self).__init__(*args, **kwargs) |