diff --git a/admin/__init__.py b/admin/__init__.py index c886038f4..2d8a8ff17 100644 --- a/admin/__init__.py +++ b/admin/__init__.py @@ -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", diff --git a/admin/models.py b/admin/models.py index f0895b644..bf3e764ef 100644 --- a/admin/models.py +++ b/admin/models.py @@ -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") diff --git a/auth/lib.py b/auth/lib.py index 160cd67e7..33b541601 100644 --- a/auth/lib.py +++ b/auth/lib.py @@ -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 diff --git a/userprefs/forms.py b/userprefs/forms.py index 4bdd5ccd1..0d8a061b3 100644 --- a/userprefs/forms.py +++ b/userprefs/forms.py @@ -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: diff --git a/userprefs/views.py b/userprefs/views.py index 6debcf45f..7a23cf883 100644 --- a/userprefs/views.py +++ b/userprefs/views.py @@ -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)