summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-03-11 00:18:28 -0300
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-03-11 00:18:28 -0300
commit81b775b6e292a943d609132985711eec17e8184d (patch)
treede6e4b2ea94654570a19a840ed30be524bcfc236
parent41b6c6da9067efffa2b861b63d3c1dffccae17f3 (diff)
parent50a6f37885b0d489d6e29ee9f54895ab74173b9e (diff)
downloadaskbot-81b775b6e292a943d609132985711eec17e8184d.tar.gz
askbot-81b775b6e292a943d609132985711eec17e8184d.tar.bz2
askbot-81b775b6e292a943d609132985711eec17e8184d.zip
merged the LDAP login contribution by user monkut
-rw-r--r--askbot/conf/__init__.py1
-rw-r--r--askbot/conf/external_keys.py35
-rw-r--r--askbot/conf/ldap.py93
-rw-r--r--askbot/deps/django_authopenid/backends.py100
-rw-r--r--askbot/deps/django_authopenid/views.py38
-rw-r--r--askbot/doc/source/changelog.rst1
-rw-r--r--askbot/doc/source/contributors.rst1
-rw-r--r--askbot/management/commands/initialize_ldap_logins.py68
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)