wip: Resellers and limits (see #105)

* Added support for migrations
* Big refactoring of the admin code

done: Support for SHA256 algorithm to store passwords (fix #195)
done: admin models refactoring (fix #196)
This commit is contained in:
Antoine Nguyen
2012-01-27 13:51:29 +01:00
parent c951541803
commit b7d77254ce
51 changed files with 849 additions and 604 deletions
+32 -8
View File
@@ -1,6 +1,4 @@
from django.contrib.auth.models import User
from modoboa.admin.models import Mailbox
from modoboa.auth.lib import _check_password
from modoboa.admin.models import User
class SimpleBackend:
@@ -13,11 +11,7 @@ class SimpleBackend:
user = User.objects.get(username=username)
except User.DoesNotExist:
return None
try:
mb = user.mailbox_set.all()[0]
except Exception:
return None
if not _check_password(password, mb.password):
if not user.check_password(password):
return None
return user
@@ -27,3 +21,33 @@ class SimpleBackend:
except User.DoesNotExist:
return None
try:
from django_auth_ldap.backend import LDAPBackend as orig_LDAPBackend, _LDAPUser
from modoboa.admin.models import User
class LDAPBackend(orig_LDAPBackend):
def get_or_create_user(self, username, ldap_user):
"""
This must return a (User, created) 2-tuple for the given LDAP user.
username is the Django-friendly username of the user. ldap_user.dn is
the user's DN and ldap_user.attrs contains all of their LDAP attributes.
"""
return User.objects.get_or_create(
username__iexact=username,
defaults={'username': username.lower()}
)
def get_user(self, user_id):
user = None
try:
user = User.objects.get(pk=user_id)
_LDAPUser(self, user=user) # This sets user.ldap_user
except User.DoesNotExist:
pass
return user
except ImportError:
pass
+22
View File
@@ -0,0 +1,22 @@
# coding: utf-8
"""
:mod:`decorators` --- common decorators
---------------------------------------
"""
from functools import wraps
from exceptions import NeedsMailboxException
def needs_mailbox():
"""Check if the current user owns at least one mailbox
Some applications (the webmail for example) need a mailbox to
work.
"""
def decorator(f):
@wraps(f)
def wrapped_f(request, *args, **kwargs):
if request.user.has_mailbox:
return f(request, *args, **kwargs)
raise NeedsMailboxException()
return wrapped_f
return decorator
+5 -3
View File
@@ -13,6 +13,8 @@ import inspect
import re
events = [
"SuperAdminPromotion",
"CanCreateDomain",
"CreateDomain",
"DeleteDomain",
@@ -87,8 +89,8 @@ class observe(object):
:param evtname: the event's name
"""
def __init__(self, evtname):
self.evtname = evtname
def __init__(self, *evtnames):
self.evtnames = evtnames
def __guess_extension_name(self, modname):
if modname.startswith('modoboa.extensions'):
@@ -112,7 +114,7 @@ class observe(object):
return []
return f(*args)
register(self.evtname, wrapped_f)
map(lambda evt: register(evt, wrapped_f), self.evtnames)
return wrapped_f
def unregister(event, callback):
+3
View File
@@ -16,3 +16,6 @@ class PermDeniedException(ModoboaException):
def __str__(self):
return _("Permission denied: %s" % self.msg)
class NeedsMailboxException(ModoboaException):
pass
+15 -7
View File
@@ -36,9 +36,9 @@ Extracted from `this blog
import sys
import ldap
import re
import base64
import base64, hashlib, string, crypt
from random import Random
from django.conf import settings
from modoboa.auth.lib import crypt_password
from modoboa.lib import parameters
class LDAPException(Exception): pass
@@ -81,7 +81,7 @@ class LDAPAuthBackend(object):
return None
return dn
def _crypt_password(self, clearpassword):
def _crypt_password(self, user, clearpassword):
"""Overidding of the crypt_password function (LDAP compliant)
The crypted password in base64 encoded and we prepend the used
@@ -91,10 +91,18 @@ class LDAPAuthBackend(object):
:return: the encrypted password
"""
scheme = parameters.get_admin("PASSWORD_SCHEME", app="admin")
if scheme == "clear":
if scheme == "crypt":
salt = ''.join(Random().sample(string.letters + string.digits, 2))
result = crypt.crypt(clearpassword, salt)
elif scheme == "md5":
obj = hashlib.md5(clearpassword)
result = obj.digest()
elif scheme == "sha256":
obj = hashlib.sha256(clearpassword)
result = obj.digest()
else:
return str(clearpassword)
crypted = crypt_password(clearpassword, False)
return str("{%s}%s" % (scheme.upper(), base64.encodestring(crypted)))
return str("{%s}%s" % (scheme.upper(), base64.b64encode(result)))
def update_user_password(self, user, password, newpassword):
try:
@@ -104,7 +112,7 @@ class LDAPAuthBackend(object):
newpassword = ('"%s"' % newpassword).encode('utf-16').lstrip('\377\376')
ldif = [(ldap.MOD_REPLACE,
self.pwd_attr,
self._crypt_password(newpassword))]
self._crypt_password(user, newpassword))]
conn.modify_s(dn, ldif)
except ldap.LDAPError, e:
raise LDAPException(str(e))
+1 -2
View File
@@ -155,7 +155,6 @@ def topredirection(request):
"""
from modoboa.lib import parameters
from modoboa.extensions import get_extension_infos
from modoboa.admin.lib import is_domain_admin
topredir = parameters.get_admin("DEFAULT_TOP_REDIRECTION", app="general")
if not topredir in ["admin", "userprefs"]:
@@ -165,7 +164,7 @@ def topredirection(request):
path = topredir
if topredir in ["admin", "stats"] and \
(not request.user.is_superuser and not is_domain_admin(request.user)):
request.user.belongs_to_group('SimpleUsers'):
path = "userprefs"
return HttpResponseRedirect(path)