[manual] Support authentication and profile prefilling via LDAP

The latter doesn't depend on the former; we can still fill in your full
name even if you didn't authenticate via LDAP.

This commit requires django_auth_ldap to be installed. On Debian
systems, you can do so via APT:
    sudo apt-get install python-django-auth-ldap

On OS X, use your favourite package manager. For pip, I believe this
will work:
    pip install django_auth_ldap

django_auth_ldap depends on the "ldap" Python package, which should be
installed automatically on your system.

(imported from commit 43967754285990b06b5a920abe95b8bce44e2053)
This commit is contained in:
Luke Faraone 2013-11-20 19:30:20 -05:00
parent e711b8160a
commit af02e45a17
5 changed files with 82 additions and 2 deletions

View File

@ -57,6 +57,8 @@ class zulip::app_frontend {
"python-apns-client",
# Needed for avatar image resizing
"python-imaging",
# Needed for LDAP support
"python-django-auth-ldap",
]
define safepackage ( $ensure = present ) {
if !defined(Package[$title]) {

View File

@ -1,7 +1,7 @@
from __future__ import absolute_import
from django.conf import settings
from django.contrib.auth import authenticate, login
from django.contrib.auth import authenticate, login, get_backends
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, HttpResponseForbidden, HttpResponse
@ -46,6 +46,7 @@ from zerver.forms import RegistrationForm, HomepageForm, ToSForm, \
CreateBotForm, is_inactive
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django_openid_auth.views import default_render_failure, login_complete
from django_auth_ldap.backend import LDAPBackend, _LDAPUser
from openid.consumer.consumer import SUCCESS as openid_SUCCESS
from openid.extensions import ax
from zerver.lib import bugdown
@ -251,8 +252,19 @@ def accounts_register(request):
hesiod_name = compute_mit_user_fullname(email)
form = RegistrationForm(
initial={'full_name': hesiod_name if "@" not in hesiod_name else ""})
elif settings.POPULATE_PROFILE_VIA_LDAP:
for backend in get_backends():
if isinstance(backend, LDAPBackend):
ldap_attrs = _LDAPUser(backend, backend.django_to_ldap_username(email)).attrs
# TODO: check if ldap_attributes are none
form = RegistrationForm(
initial={'full_name': ldap_attrs[
settings.AUTH_LDAP_USER_ATTR_MAP['full_name']
][0]})
break
else:
form = RegistrationForm()
else:
form = RegistrationForm(request.POST)
if form.is_valid():

View File

@ -1,10 +1,13 @@
from __future__ import absolute_import
from django.contrib.auth.backends import RemoteUserBackend
from django.conf import settings
import django.contrib.auth
from django_auth_ldap.backend import LDAPBackend
from zerver.models import UserProfile, get_user_profile_by_id, \
get_user_profile_by_email, remote_user_to_email
get_user_profile_by_email, remote_user_to_email, email_to_username
from openid.consumer.consumer import SUCCESS
@ -77,3 +80,26 @@ class ZulipRemoteUserBackend(RemoteUserBackend):
return get_user_profile_by_email(email)
except UserProfile.DoesNotExist:
return None
class ZulipLDAPAuthBackend(ZulipAuthMixin, LDAPBackend):
def django_to_ldap_username(self, username):
if settings.LDAP_APPEND_DOMAIN is not None:
return email_to_username(username)
return username
def ldap_to_django_username(self, username):
if settings.LDAP_APPEND_DOMAIN is not None:
return username + settings.LDAP_APPEND_DOMAIN
return username
def get_or_create_user(self, username, ldap_user):
try:
return get_user_profile_by_email(username), False
except UserProfile.DoesNotExist:
return UserProfile(), False
class ZulipLDAPUserPopulator(ZulipLDAPAuthBackend):
# Just like ZulipLDAPAuthBackend, but doesn't let you log in.
def authenticate(self, username, password):
return None

View File

@ -22,6 +22,7 @@ DEPLOYMENT_ROLE_KEY = ''
AUTHENTICATION_BACKENDS = (
# 'zproject.backends.EmailAuthBackend', # Email and password
# 'zproject.backends.ZulipRemoteUserBackend', # Local SSO
# 'zproject.backends.ZulipLDAPAuthBackend', # LDAP authentication
# 'zproject.backends.GoogleBackend', # Google Apps
)
@ -136,6 +137,36 @@ EMAIL_GATEWAY_IMAP_PORT = 993
# must be delivered to this folder
EMAIL_GATEWAY_IMAP_FOLDER = "INBOX"
### LDAP integration configuration
# Zulip supports retrieving information about users via LDAP, and optionally
# using LDAP as an authentication mechanism.
import ldap
from django_auth_ldap.config import LDAPSearch, GroupOfNamesType
# URI of your LDAP server. If set, LDAP is used to prepopulate a user's name in
# Zulip. Example: "ldaps://ldap.example.com"
AUTH_LDAP_SERVER_URI = ""
# This DN and password will be used to bind to your server. If unset, anonymous
# binds are performed.
AUTH_LDAP_BIND_DN = ""
AUTH_LDAP_BIND_PASSWORD = ""
# Specify the search base and the property to filter on that corrisponds to the
# username.
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
ldap.SCOPE_SUBTREE, "(uid=%(user)s)")
# If the value of a user's "uid" (or similar) property is not their email
# address, specify the domain to append here.
LDAP_APPEND_DOMAIN = SSO_APPEND_DOMAIN
AUTH_LDAP_USER_ATTR_MAP = {
# Populate the Django user's name from the LDAP directory.
"full_name": "cn",
}
# The following secrets are randomly generated during the install
# process, are used for security purposes, and should not be shared
# with anyone.

View File

@ -267,6 +267,7 @@ DEFAULT_SETTINGS = {'TWITTER_CONSUMER_KEY': '',
'ENABLE_FEEDBACK': True,
'ENABLE_GRAVATAR': True,
'DEFAULT_AVATAR_URI': '/static/images/default-avatar.png',
'AUTH_LDAP_BIND_DN': "",
}
for setting_name, setting_val in DEFAULT_SETTINGS.iteritems():
@ -733,6 +734,14 @@ else:
ONLY_SSO = False
AUTHENTICATION_BACKENDS += ('guardian.backends.ObjectPermissionBackend',)
POPULATE_PROFILE_VIA_LDAP = bool(AUTH_LDAP_BIND_DN)
if POPULATE_PROFILE_VIA_LDAP and \
not 'zproject.backends.ZulipLDAPAuthBackend' in AUTHENTICATION_BACKENDS:
AUTHENTICATION_BACKENDS += ('zproject.backends.ZulipLDAPUserPopulator',)
else:
POPULATE_PROFILE_VIA_LDAP = 'zproject.backends.ZulipLDAPAuthBackend' in AUTHENTICATION_BACKENDS or POPULATE_PROFILE_VIA_LDAP
if DEPLOYED:
FULL_NAVBAR = False
else: