Support for LDAP password modification (user level only for now). I need a working environment to check if postfix/dovecot accept the way I encrypt passwords.

see #192
This commit is contained in:
Antoine Nguyen
2011-12-28 13:30:01 +01:00
parent 880880a374
commit 96dfb877ae
5 changed files with 82 additions and 39 deletions
+4
View File
@@ -5,6 +5,10 @@ from django.utils.translation import ugettext as _
from django.db.utils import DatabaseError
from models import Extension
parameters.register_admin("AUTHENTICATION_TYPE", type="list", deflt="local",
values=[('local', _("Local")),
('ldap', "LDAP")],
help=_("The backend used for authentication"))
parameters.register_admin("CREATE_DIRECTORIES", type="list_yesno", deflt="yes",
help=_("Modoboa will handle mailbox creation on filesystem"))
parameters.register_admin("STORAGE_PATH", type="string", deflt="/var/vmail",
+59 -30
View File
@@ -6,6 +6,7 @@ from django.conf import settings
from modoboa.lib import parameters, events
from modoboa.lib.sysutils import exec_cmd, exec_as_vuser
from modoboa.lib.emailutils import split_mailbox
from modoboa.lib.ldaputils import *
from modoboa.auth.lib import crypt_password
import os
import pwd
@@ -291,6 +292,26 @@ class Mailbox(DatesAware):
pass
super(Mailbox, self).save(*args, **kwargs)
def set_password(self, curvalue, newvalue):
"""Password update
Update the current mailbox's password with the given clear
value. This value is encrypted according to the defined method
before it is saved.
:param value: the new password's value
"""
if parameters.get_admin("AUTHENTICATION_TYPE") == "local":
self.password = crypt_password(value)
self.save()
return
ab = LDAPAuthBackend()
try:
ab.update_user_password(self.user.username, curvalue, newvalue)
except LDAPException, e:
raise AdminError(_("Failed to update password: %s" % str(e)))
def save_from_user(self, localpart, domain, user):
"""Simple save method called for automatic creations
@@ -307,16 +328,16 @@ class Mailbox(DatesAware):
# If we ever had numerical uid, don't resolve it
v_uid = parameters.get_admin("VIRTUAL_UID");
if v_uid.isdigit():
self.uid = v_uid
self.uid = v_uid
else:
self.uid = pwd.getpwnam(parameters.get_admin("VIRTUAL_UID")).pw_uid
self.uid = pwd.getpwnam(parameters.get_admin("VIRTUAL_UID")).pw_uid
# Just the same for gid
v_gid = parameters.get_admin("VIRTUAL_GID")
if v_gid.isdigit:
self.gid = v_gid
if v_gid.isdigit():
self.gid = v_gid
else:
self.gid = pwd.getpwnam(parameters.get_admin("VIRTUAL_GID")).pw_gid
self.quota = self.domain.quota
self.gid = pwd.getpwnam(parameters.get_admin("VIRTUAL_GID")).pw_gid
self.quota = self.domain.quota
super(Mailbox, self).save()
def create_from_csv(self, line):
@@ -489,31 +510,39 @@ class Extension(models.Model):
events.raiseEvent("ExtDisabled", ext=self)
#
# Optional callback to execute if django-auth-ldap is enabled.
#
def populate_callback(sender, user=None, **kwargs):
"""Populate signal callback
If the LDAP authentication backend is in use, this callback will
be called each time a new user authenticates succesfuly to
Modoboa. This function is in charge of creating the mailbox
associated to the provided ``User`` object.
:param sender: ??
:param user: a ``User`` instance
"""
if parameters.get_admin("AUTHENTICATION_TYPE") != "ldap":
return
if user is None:
return
localpart, domname = split_mailbox(user.username)
try:
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
domain = Domain()
domain.name = domname
domain.enabled = True
domain.quota = 0
domain.save()
try:
mb = Mailbox.objects.get(domain=domain, address=localpart)
except Mailbox.DoesNotExist:
mb = Mailbox()
mb.save_from_user(localpart, domain, user)
try:
from django_auth_ldap.backend import populate_user
def populate_callback(sender, user=None, **kwargs):
if user is None:
return
localpart, domname = split_mailbox(user.username)
try:
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
domain = Domain()
domain.name = domname
domain.enabled = True
domain.quota = 0
domain.save()
try:
mb = Mailbox.objects.get(domain=domain, address=localpart)
except Mailbox.DoesNotExist:
mb = Mailbox()
mb.save_from_user(localpart, domain, user)
populate_user.connect(populate_callback, dispatch_uid="myuid")
except ImportError, inst:
pass
else:
populate_user.connect(populate_callback, dispatch_uid="myuid")
+3 -2
View File
@@ -34,11 +34,12 @@ def _check_password(password, crypted):
return hashlib.md5(password).hexdigest() == crypted
return password
def crypt_password(password):
def crypt_password(password, hexdigest=True):
scheme = parameters.get_admin("PASSWORD_SCHEME", app="admin")
if scheme == "crypt":
salt = ''.join(Random().sample(string.letters + string.digits, 2))
return crypt.crypt(password, salt)
if scheme == "md5":
return hashlib.md5(password).hexdigest()
obj = hashlib.md5(password)
return obj.hexdigest() if hexdigest else obj.digest()
return password
+4
View File
@@ -4,6 +4,7 @@ from modoboa.auth.lib import _check_password
from django.contrib.auth.models import check_password
from django.utils.translation import ugettext as _, ugettext_noop
from modoboa.lib.emailutils import split_mailbox
from modoboa.lib import parameters
class BadDestination(Exception):
pass
@@ -21,6 +22,9 @@ class ChangePasswordForm(forms.Form):
self.target = target
def clean_oldpassword(self):
if parameters.get_admin("AUTHENTICATION_TYPE", app="admin") != "local":
return self.cleaned_data["oldpassword"]
if not isinstance(self.target, Mailbox):
func = check_password
else:
+12 -7
View File
@@ -8,9 +8,9 @@ from django.utils.translation import ugettext as _
from modoboa.lib import parameters, events
from modoboa.lib.webutils import _render, _render_error, ajax_response
from forms import *
from modoboa.admin.models import Mailbox, Alias
from modoboa.admin.models import Mailbox, Alias, AdminError
from modoboa.admin.lib import is_domain_admin
from modoboa.auth.lib import encrypt, crypt_password
from modoboa.auth.lib import encrypt
@login_required
def index(request):
@@ -31,13 +31,18 @@ def changepassword(request, tplname="userprefs/chpassword.html"):
if request.method == "POST":
form = ChangePasswordForm(target, request.POST)
if form.is_valid():
if request.user.id != 1:
target.password = crypt_password(request.POST["confirmation"])
if request.user.id != 1:
try:
target.set_password(form.cleaned_data["oldpassword"],
form.cleaned_data["confirmation"])
except AdminError, e:
error = str(e)
request.session["password"] = encrypt(request.POST["confirmation"])
else:
target.set_password(request.POST["confirmation"])
request.session["password"] = encrypt(request.POST["confirmation"])
target.save()
return ajax_response(request, respmsg=_("Password changed"))
if error is None:
return ajax_response(request, respmsg=_("Password changed"))
return ajax_response(request, status="ko", template=tplname,
form=form, error=error)