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
+7 -7
View File
@@ -22,9 +22,11 @@ parameters.register_admin("MAILBOX_TYPE", type="list", deflt="maildir",
help=_("Mailboxes storage format"))
parameters.register_admin("MAILDIR_ROOT", type="string", deflt=".maildir",
help=_("Sub-directory (inside the mailbox) where messages are stored when using the maildir format"))
parameters.register_admin("PASSWORD_SCHEME", type="list", deflt="crypt",
values=[("crypt", "crypt"), ("md5", "md5"), ("clear", "clear")],
help=_("Scheme used to crypt mailbox passwords"))
parameters.register_admin(
"PASSWORD_SCHEME", type="list", deflt="crypt",
values=[("crypt", "crypt"), ("md5", "md5"), ("sha256", "sha256"), ("clear", "clear")],
help=_("Scheme used to crypt mailbox passwords")
)
parameters.register_admin("ITEMS_PER_PAGE", type="int", deflt=30,
help=_("Number of displayed items per page"))
@@ -51,6 +53,7 @@ parameters.register_admin("DEFAULT_TOP_REDIRECTION", type="list", deflt="admin",
values=enabled_applications(),
help=_("The default redirection used when no application is specified"))
@events.observe("ExtDisabled")
def unset_default_topredirection(extension):
"""
Simple callback to change the default redirection if the
@@ -60,6 +63,7 @@ def unset_default_topredirection(extension):
if topredirection == extension.name:
parameters.save_admin("DEFAULT_TOP_REDIRECTION", "userprefs", app="general")
@events.observe("ExtDisabled", "ExtEnabled")
def update_available_applications(extension):
"""Simple callback to update the list of available applications
@@ -67,7 +71,3 @@ def update_available_applications(extension):
"""
parameters.update_admin("DEFAULT_TOP_REDIRECTION", app="general",
values=enabled_applications())
events.register("ExtDisabled", unset_default_topredirection)
events.register("ExtDisabled", update_available_applications)
events.register("ExtEnabled", update_available_applications)
+15
View File
@@ -0,0 +1,15 @@
# coding: utf-8
"""
:mod:`exceptions` --- Custom exceptions of the admin. application
-----------------------------------------------------------------
"""
from modoboa.lib.exceptions import ModoboaException
class AdminError(ModoboaException):
"""
Just a Custom exception to identify errors coming from the admin
application.
"""
pass
+1 -1
View File
@@ -1 +1 @@
[{"pk": 1, "model": "auth.group", "fields": {"name": "DomainAdmins", "permissions": [["add_mailbox", "admin", "mailbox"], ["change_mailbox", "admin", "mailbox"], ["delete_mailbox", "admin", "mailbox"], ["add_alias", "admin", "alias"], ["change_alias", "admin", "alias"], ["delete_alias", "admin", "alias"], ["view_domain", "admin", "domain"], ["view_mailboxes", "admin", "mailbox"], ["view_aliases", "admin", "alias"], ["view_domaliases", "admin", "domainalias"], ["add_domainalias", "admin", "domainalias"], ["change_domainalias", "admin", "domainalias"], ["delete_domainalias", "admin", "domainalias"]]}},{"pk": 2, "model": "auth.group", "fields": {"name": "SimpleUsers", "permissions": []}}]
[{"pk": 1, "model": "auth.group", "fields": {"name": "DomainAdmins", "permissions": [["add_mailbox", "admin", "mailbox"], ["change_mailbox", "admin", "mailbox"], ["delete_mailbox", "admin", "mailbox"], ["add_alias", "admin", "alias"], ["change_alias", "admin", "alias"], ["delete_alias", "admin", "alias"], ["view_domain", "admin", "domain"], ["view_mailboxes", "admin", "mailbox"], ["view_aliases", "admin", "alias"], ["view_domaliases", "admin", "domainalias"], ["add_domainalias", "admin", "domainalias"], ["change_domainalias", "admin", "domainalias"], ["delete_domainalias", "admin", "domainalias"]]}},{"pk": 2, "model": "auth.group", "fields": {"name": "SimpleUsers", "permissions": []}},{"pk": 1, "model": "auth.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "last_login": "2012-01-26 15:19:25", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26 15:19:25"}}]
+44 -31
View File
@@ -1,13 +1,10 @@
# coding: utf-8
from django import forms
from modoboa.admin.models import *
from modoboa.admin.lib import is_domain_admin
from django.utils.translation import ugettext as _, ugettext_noop
from django.contrib.auth.models import User
from modoboa.admin.templatetags.admin_extras import gender
from modoboa.lib import tables
from modoboa.lib.emailutils import split_mailbox
from lib import get_user_domains, get_user_domains_qs
class DomainForm(forms.ModelForm):
class Meta:
@@ -34,7 +31,7 @@ class ProxyForm(forms.ModelForm):
super(ProxyForm, self).__init__(*args, **kwargs)
if not user.is_superuser:
self._domains = get_user_domains_qs(user)
self._domains = user.get_domains()
else:
self._domains = None
@@ -50,25 +47,23 @@ class DomainAliasForm(ProxyForm):
self.fields["target"].queryset = self._domains
class MailboxForm(ProxyForm):
name = forms.CharField(
label=ugettext_noop("Name"), max_length=100,
help_text=ugettext_noop("First name and last name of mailbox owner")
)
quota = forms.IntegerField(label=ugettext_noop("Quota"), required=False,
help_text=ugettext_noop("Mailbox quota in MB (default to domain quota if blank)"))
password1 = forms.CharField(label=ugettext_noop("Password"),
widget=forms.PasswordInput(render_value=True),
help_text=ugettext_noop("Password used to log in"))
password2 = forms.CharField(label=ugettext_noop("Confirmation"),
widget=forms.PasswordInput(render_value=True),
help_text=ugettext_noop("Password confirmation"))
enabled = forms.BooleanField(label=gender("Enabled", "f"), required=False,
initial=True,
help_text=ugettext_noop("Check to activate this mailbox"))
class Meta:
model = Mailbox
fields = ('domain', 'name', 'address')
fields = ('domain', 'address')
def __init__(self, *args, **kwargs):
super(MailboxForm, self).__init__(*args, **kwargs)
for f in ['name', 'address', 'quota', 'password1', 'password2']:
for f in ['name', 'address', 'quota']:
self.fields[f].widget.attrs['size'] = 14
if self._domains is not None:
self.fields["domain"].queryset = self._domains
@@ -76,25 +71,21 @@ class MailboxForm(ProxyForm):
#self.fields["domain"].initial = self._domain
if kwargs.has_key("instance"):
self.fields['quota'].initial = kwargs["instance"].quota
self.fields['enabled'].initial = kwargs["instance"].user.is_active
self.fields['password1'].initial = "é"
self.fields['password2'].initial = "é"
mb = kwargs["instance"]
self.fields['name'].initial = unicode(mb.user)
self.fields['quota'].initial = mb.quota
self.fields['enabled'].initial = mb.user.is_active
def clean_address(self):
if self.cleaned_data["address"].find('@') != -1:
return self.cleaned_data["address"].rsplit("@", 1)[0]
return self.cleaned_data["address"]
def clean_password2(self):
if self.cleaned_data["password1"] != self.cleaned_data["password2"]:
raise forms.ValidationError(_("Passwords mismatch"))
return self.cleaned_data["password2"]
def commit_save(self, mb):
mb.save(enabled=self.cleaned_data["enabled"],
password=self.cleaned_data["password1"],
quota=self.cleaned_data["quota"])
def commit_save(self, mb, **kwargs):
mb.save(name=self.cleaned_data["name"],
enabled=self.cleaned_data["enabled"],
quota=self.cleaned_data["quota"],
**kwargs)
def save(self, force_insert=False, force_update=False, commit=True):
m = super(MailboxForm, self).save(commit=False)
@@ -102,6 +93,31 @@ class MailboxForm(ProxyForm):
self.commit_save(m)
return m
class MailboxWithPasswordForm(MailboxForm):
password1 = forms.CharField(label=ugettext_noop("Password"),
widget=forms.PasswordInput(render_value=True),
help_text=ugettext_noop("Password used to log in"))
password2 = forms.CharField(label=ugettext_noop("Confirmation"),
widget=forms.PasswordInput(render_value=True),
help_text=ugettext_noop("Password confirmation"))
def __init__(self, *args, **kwargs):
super(MailboxWithPasswordForm, self).__init__(*args, **kwargs)
for f in ['password1', 'password2']:
self.fields[f].widget.attrs['size'] = 14
self.fields.keyOrder = ['name', 'domain', 'address', 'quota',
'password1', 'password2', 'enabled']
def clean_password2(self):
if self.cleaned_data["password1"] != self.cleaned_data["password2"]:
raise forms.ValidationError(_("Passwords mismatch"))
return self.cleaned_data["password2"]
def commit_save(self, mb):
super(MailboxWithPasswordForm, self).commit_save(
mb, password=self.cleaned_data["password1"]
)
class AliasForm(ProxyForm):
targets = forms.CharField(label=ugettext_noop("Target(s)"), required=False,
help_text=ugettext_noop("Mailbox(es) this alias will point to"))
@@ -152,8 +168,6 @@ class AliasForm(ProxyForm):
We make a difference between 'local' targets (the ones hosted
by Modoboa) and 'external targets.
"""
from modoboa.admin.lib import is_object_owner
self.ext_targets = []
self.int_targets = []
@@ -168,7 +182,7 @@ class AliasForm(ProxyForm):
except Domain.DoesNotExist:
domain = None
if domain:
if not is_object_owner(user, domain):
if not user.is_owner(domain):
raise PermDeniedException(addr)
try:
mb = Mailbox.objects.get(domain=domain, address=local_part)
@@ -274,9 +288,8 @@ class AssignDomainsForm(forms.Form):
def __init__(self, user, domadmin, *args, **kwargs):
super(AssignDomainsForm, self).__init__(*args, **kwargs)
self.fields["domains"].queryset = get_user_domains_qs(user)
self.fields["domains"].initial = get_user_domains_qs(domadmin)
self.fields["domains"].queryset = user.get_domains()
self.fields["domains"].initial = domadmin.get_domains()
class ImportDataForm(forms.Form):
sourcefile = forms.FileField(label=_("Select a file"))
+2 -207
View File
@@ -1,82 +1,12 @@
# coding: utf-8
from functools import wraps
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.models import User, Group
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext as _
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from models import *
from modoboa.lib.exceptions import ModoboaException
from modoboa.lib.webutils import _render_error, _render
from modoboa.admin.tables import *
class AdminError(ModoboaException):
"""
Just a Custom exception to identify errors coming from the admin
application.
"""
pass
def is_not_localadmin(errortpl="error"):
def dec(f):
def wrapped_f(request, *args, **kwargs):
if request.user.id == 1:
return _render_error(request, errortpl, {
"error" : _("Invalid action, %(user)s is a local user" \
% {"user" : request.user.username})
})
return f(request, *args, **kwargs)
wrapped_f.__name__ = f.__name__
wrapped_f.__dict__ = f.__dict__
wrapped_f.__doc__ = f.__doc__
wrapped_f.__module__ = f.__module__
return wrapped_f
return dec
def is_domain_admin(user):
"""Tell if a user is administrator for a domain of not
Whatever the domain, we just want to know if the given user is
declared as a domain administrator.
:param user: a User object
:return: True if the user is a domain administrator, False otherwise.
"""
grp = Group.objects.get(name="DomainAdmins")
return grp in user.groups.all()
def good_domain(f):
"""Custom permission decorator
Check if a domain administrator is accessing the good domain.
:param f: the original called function
"""
def dec(request, **kwargs):
if request.user.is_superuser:
return f(request, **kwargs)
if is_domain_admin(request.user):
mb = Mailbox.objects.get(user=request.user.id)
access = True
if request.GET.has_key("domid"):
dom_id = int(request.GET["domid"])
if dom_id != mb.domain.id:
access = False
else:
q = request.GET.copy()
q["domid"] = mb.domain.id
request.GET = q
if access:
return f(request, **kwargs)
from django.conf import settings
from django.utils.http import urlquote
path = urlquote(request.get_full_path())
login_url = settings.LOGIN_URL
return HttpResponseRedirect("%s?next=%s" % (login_url, path))
return dec
from exceptions import *
def render_listing(request, objtype, tplname="admin/listing.html",
**kwargs):
@@ -162,53 +92,6 @@ def ungrant_access_to_object(obj, user=None):
else:
ObjectAccess.objects.filter(content_type=ct, object_id=obj.id).delete()
def can_access_object(user, obj):
"""Check if a user can access a specific object
This function is recursive : if the given user hasn't got direct
access to this object and if he has got access other ``User``
objects, we check if one of those users owns the object.
:param user: a ``User`` object
:param obj: a admin object
:return: a boolean
"""
if user.is_superuser:
return True
ct = ContentType.objects.get_for_model(obj)
try:
ooentry = user.objectaccess_set.get(content_type=ct, object_id=obj.id)
except ObjectAccess.DoesNotExist:
pass
else:
return True
if ct.model == "user":
return False
if directowner:
return False
ct = ContentType.objects.get_for_model(User)
qs = user.objectaccess_set.filter(content_type=ct)
for ooentry in qs.all():
if is_object_owner(ooentry.content_object, obj):
return True
return False
def is_object_owner(user, obj):
"""Tell is the user is the unique owner of this object
:param user: a ``User`` object
:param obj: an object inheriting from ``models.Model``
:return: a boolean
"""
ct = ContentType.objects.get_for_model(obj)
try:
ooentry = user.objectaccess_set.get(content_type=ct, object_id=obj.id)
except ObjectAccess.DoesNotExist:
return False
return ooentry.is_owner
def get_object_owner(obj):
"""Return the unique owner of this object
@@ -242,96 +125,8 @@ def check_domain_access(f):
mbid = request.GET.get("mbid", None)
if mbid:
domain = Mailbox.objects.get(pk=mbid).domain
if domain and not can_access_object(request.user, domain):
if domain and not request.user.can_access(domain):
raise PermDeniedError(_("You can't access this domain"))
return f(request, **kwargs)
return dec
def get_user_domains(user):
if user.is_superuser:
return Domain.objects.all()
domct = ContentType.objects.get_for_model(Domain)
qs = user.objectaccess_set.filter(content_type=domct)
domains = []
for entry in qs.all():
domains += [entry.content_object]
userct = ContentType.objects.get_for_model(User)
qs = user.objectaccess_set.filter(content_type=userct)
for entry in qs:
if not is_domain_admin(entry.content_object):
continue
domains += get_user_domains(entry.content_object)
return domains
def get_user_domains_qs(user):
"""Return the domains belonging to this user
The result is a ``QuerySet`` object, so this function can be used
to fill ``ModelChoiceField`` objects.
:param user: a ``User`` object
"""
if user.is_superuser:
return Domain.objects.all()
result = Domain.objects.filter(owners__user=user).distinct()
userct = ContentType.objects.get_for_model(User)
for entry in user.objectaccess_set.filter(content_type=userct):
if not is_domain_admin(entry.content_object):
continue
result |= get_user_domains_qs(entry.content_object).distinct()
return result
def get_user_domaliases(user):
"""Return the domain aliases that belong to this user
The result will contain the domain aliases defined for each domain
that user can see.
:param user: a ``User`` object
:return: a list of ``DomainAlias`` objects
"""
if user.is_superuser:
return DomainAlias.objects.all()
domains = get_user_domains_qs(user)
domaliases = []
for dom in domains:
domaliases += dom.domainalias_set.all()
return domaliases
def get_user_mailboxes(user):
"""Return the mailboxes that belong to this user
The result will contain the mailboxes defined for each domain that
user can see.
:param user: a ``User`` object
:return: a list of ``Mailbox`` objects
"""
if user.is_superuser:
return Mailbox.objects.all()
mboxes = []
domains = get_user_domains_qs(user)
for dom in domains:
mboxes += dom.mailbox_set.all()
return mboxes
def get_user_mbaliases(user):
"""Return the mailbox aliases that belong to this user
The result will contain the mailbox aliases defined for each
domain that user can see.
:param user: a ``User`` object
:return: a list of ``Alias`` objects
"""
if user.is_superuser:
return Alias.objects.all()
domains = get_user_domains_qs(user)
mbaliases = []
for dom in domains:
mbaliases += dom.alias_set.all()
return mbaliases
+116
View File
@@ -0,0 +1,116 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
from modoboa.lib.models import Parameter
class Migration(DataMigration):
def forwards(self, orm):
"Write your forwards methods here."
try:
s = Parameter.objects.get(name='PASSWORD_SCHEME').value
except Parameter.DoesNotExist:
s = "crypt"
for mb in orm.Mailbox.objects.all():
mb.user.password = '{%s}%s' % (s.upper(), mb.password)
mb.user.save()
def backwards(self, orm):
"Write your backwards methods here."
raise RuntimeError("Cannot reverse this migration.")
models = {
'admin.alias': {
'Meta': {'object_name': 'Alias'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '254'}),
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'extmboxes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'mboxes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['admin.Mailbox']", 'symmetrical': 'False'})
},
'admin.domain': {
'Meta': {'object_name': 'Domain'},
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'quota': ('django.db.models.fields.IntegerField', [], {})
},
'admin.domainalias': {
'Meta': {'object_name': 'DomainAlias'},
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'target': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"})
},
'admin.extension': {
'Meta': {'object_name': 'Extension'},
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '150'})
},
'admin.mailbox': {
'Meta': {'object_name': 'Mailbox'},
'address': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'dates': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.ObjectDates']"}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}),
'gid': ('django.db.models.fields.IntegerField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'path': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'quota': ('django.db.models.fields.IntegerField', [], {}),
'uid': ('django.db.models.fields.IntegerField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'admin.objectdates': {
'Meta': {'object_name': 'ObjectDates'},
'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_modification': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
}
}
complete_apps = ['admin']
@@ -21,6 +21,12 @@ class Migration(SchemaMigration):
# Adding unique constraint on 'ObjectAccess', fields ['user', 'content_type', 'object_id']
db.create_unique('admin_objectaccess', ['user_id', 'content_type_id', 'object_id'])
# Deleting field 'Mailbox.password'
db.delete_column('admin_mailbox', 'password')
# Deleting field 'Mailbox.name'
db.delete_column('admin_mailbox', 'name')
def backwards(self, orm):
@@ -30,6 +36,12 @@ class Migration(SchemaMigration):
# Deleting model 'ObjectAccess'
db.delete_table('admin_objectaccess')
# User chose to not deal with backwards NULL issues for 'Mailbox.password'
raise RuntimeError("Cannot reverse this migration. 'Mailbox.password' and its values cannot be restored.")
# User chose to not deal with backwards NULL issues for 'Mailbox.name'
raise RuntimeError("Cannot reverse this migration. 'Mailbox.name' and its values cannot be restored.")
models = {
'admin.alias': {
@@ -71,8 +83,6 @@ class Migration(SchemaMigration):
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['admin.Domain']"}),
'gid': ('django.db.models.fields.IntegerField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'path': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'quota': ('django.db.models.fields.IntegerField', [], {}),
'uid': ('django.db.models.fields.IntegerField', [], {}),
@@ -92,6 +102,9 @@ class Migration(SchemaMigration):
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_modification': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
'admin.user': {
'Meta': {'object_name': 'User', 'db_table': "'auth_user'", '_ormbases': ['auth.User'], 'proxy': 'True'}
},
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+223 -50
View File
@@ -2,15 +2,20 @@
from django.db import models, IntegrityError
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.contrib.auth.models import User, Group
from django.contrib.auth.models import User as DUser, UserManager, Group
from django.utils.translation import ugettext as _, ugettext_noop
from django.utils.crypto import constant_time_compare
from django.conf import settings
from modoboa.lib import parameters, events
from modoboa.lib.exceptions import PermDeniedException
from modoboa.lib.sysutils import exec_cmd, exec_as_vuser
from modoboa.lib.emailutils import split_mailbox
from modoboa.auth.lib import crypt_password
from exceptions import *
import os
import pwd
import re
import crypt, hashlib, string, base64
from random import Random
try:
from modoboa.lib.ldaputils import *
@@ -18,6 +23,205 @@ try:
except ImportError:
ldap_available = False
class User(DUser):
"""Proxy for the ``User`` model.
It overloads the way passwords are stored into the database. The
main reason to change this mechanism is to ensure the
compatibility with the way Dovecot stores passwords.
It also adds new attributes and methods.
"""
class Meta:
proxy = True
password_expr = re.compile(r'\{(\w+)\}(.+)')
def __unicode__(self):
return u"%s %s" % (self.first_name, self.last_name)
def _crypt_password(self, raw_value):
scheme = parameters.get_admin("PASSWORD_SCHEME")
if scheme == "crypt":
salt = ''.join(Random().sample(string.letters + string.digits, 2))
result = crypt.crypt(raw_value, salt)
elif scheme == "md5":
obj = hashlib.md5(raw_value)
result = obj.hexdigest()
elif scheme == "sha256":
obj = hashlib.sha256(raw_value)
result = base64.b64encode(obj.digest())
else:
scheme = "plain"
result = raw_value
return "{%s}%s" % (scheme.upper(), result)
def set_password(self, raw_value, curvalue=None):
"""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 raw_value: the new password's value
:param curvalue: the current password (for LDAP authentication)
"""
if parameters.get_admin("AUTHENTICATION_TYPE") == "local":
self.password = self._crypt_password(raw_value)
return
if not ldap_available:
raise AdminError(_("Failed to update password: LDAP module not installed"))
ab = LDAPAuthBackend()
try:
ab.update_user_password(self.username, curvalue, raw_value)
except LDAPException, e:
raise AdminError(_("Failed to update password: %s" % str(e)))
def check_password(self, raw_value):
m = self.password_expr.match(self.password)
if m is None:
return False
scheme = m.group(1).lower()
val2 = m.group(2)
if scheme == "crypt":
val1 = crypt.crypt(raw_value, m.group(2))
elif scheme == "md5":
val1 = hashlib.md5(raw_value).hexdigest()
elif scheme == "sha256":
val1 = base64.b64encode(hashlib.sha256(raw_value).digest())
else:
val1 = raw_value
return constant_time_compare(val1, val2)
@property
def has_mailbox(self):
return len(self.mailbox_set.all()) != 0
def belongs_to_group(self, name):
"""Simple shortcut to check if this user is a member of a
specific group.
:param name: the group's name
:return: a boolean
"""
try:
self.groups.get(name=name)
except Group.DoesNotExist:
return False
return True
def get_domains(self):
"""Return the domains belonging to this user
The result is a ``QuerySet`` object, so this function can be used
to fill ``ModelChoiceField`` objects.
:param user: a ``User`` object
"""
if self.is_superuser:
return Domain.objects.all()
result = Domain.objects.filter(owners__user=self).distinct()
userct = ContentType.objects.get_for_model(User)
for entry in self.objectaccess_set.filter(content_type=userct):
if not entry.content_object.belongs_to_group('DomainAdmins'):
continue
result |= entry.content_object.get_domains().distinct()
return result
def get_domaliases(self):
"""Return the domain aliases that belong to this user
The result will contain the domain aliases defined for each domain
that user can see.
:return: a list of ``DomainAlias`` objects
"""
if self.is_superuser:
return DomainAlias.objects.all()
domains = self.get_domains()
domaliases = []
for dom in domains:
domaliases += dom.domainalias_set.all()
return domaliases
def get_mailboxes(self):
"""Return the mailboxes that belong to this user
The result will contain the mailboxes defined for each domain that
user can see.
:return: a list of ``Mailbox`` objects
"""
if self.is_superuser:
return Mailbox.objects.all()
mboxes = []
domains = self.get_domains()
for dom in domains:
mboxes += dom.mailbox_set.all()
return mboxes
def get_mbaliases(self):
"""Return the mailbox aliases that belong to this user
The result will contain the mailbox aliases defined for each
domain that user can see.
:return: a list of ``Alias`` objects
"""
if self.is_superuser:
return Alias.objects.all()
domains = self.get_domains()
mbaliases = []
for dom in domains:
mbaliases += dom.alias_set.all()
return mbaliases
def is_owner(self, obj):
"""Tell is the user is the unique owner of this object
:param obj: an object inheriting from ``models.Model``
:return: a boolean
"""
ct = ContentType.objects.get_for_model(obj)
try:
ooentry = self.objectaccess_set.get(content_type=ct, object_id=obj.id)
except ObjectAccess.DoesNotExist:
return False
return ooentry.is_owner
def can_access(self, obj):
"""Check if the user can access a specific object
This function is recursive : if the given user hasn't got direct
access to this object and if he has got access other ``User``
objects, we check if one of those users owns the object.
:param obj: a admin object
:return: a boolean
"""
if self.is_superuser:
return True
ct = ContentType.objects.get_for_model(obj)
try:
ooentry = self.objectaccess_set.get(content_type=ct, object_id=obj.id)
except ObjectAccess.DoesNotExist:
pass
else:
return True
if ct.model == "user":
return False
ct = ContentType.objects.get_for_model(User)
qs = self.objectaccess_set.filter(content_type=ct)
for ooentry in qs.all():
if ooentry.content_object.is_owner(obj):
return True
return False
class ObjectAccess(models.Model):
user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType)
@@ -27,7 +231,7 @@ class ObjectAccess(models.Model):
class Meta:
unique_together = (("user", "content_type", "object_id"),)
class ObjectDates(models.Model):
"""Dates recording for admin objects
@@ -154,11 +358,10 @@ class DomainAlias(DatesAware):
)
class Mailbox(DatesAware):
name = models.CharField(ugettext_noop('name'), max_length=100,
help_text=ugettext_noop("First name and last name of mailbox owner"))
address = models.CharField(ugettext_noop('address'), max_length=100,
help_text=ugettext_noop("Mailbox address (without the @domain.tld part)"))
password = models.CharField(ugettext_noop('password'), max_length=100)
address = models.CharField(
ugettext_noop('address'), max_length=100,
help_text=ugettext_noop("Mailbox address (without the @domain.tld part)")
)
quota = models.IntegerField()
uid = models.IntegerField()
gid = models.IntegerField()
@@ -251,16 +454,17 @@ class Mailbox(DatesAware):
except User.DoesNotExist:
user = User()
user.username = user.email = "%s@%s" % (self.address, self.domain.name)
user.set_unusable_password()
if kwargs.has_key("enabled"):
user.is_active = kwargs["enabled"]
try:
fname, lname = self.name.split()
fname, lname = kwargs["name"].split()
except ValueError:
fname = self.name
fname = kwargs["name"]
lname = ""
user.first_name = fname
user.last_name = lname
if kwargs.has_key("password"):
user.set_password(kwargs["password"])
try:
user.save()
except IntegrityError:
@@ -271,8 +475,6 @@ class Mailbox(DatesAware):
user.groups.add(Group.objects.get(name="SimpleUsers"))
user.save()
if kwargs.has_key("password") and kwargs["password"] != u"é":
self.password = crypt_password(kwargs["password"])
# If we ever had numerical uid, don't resolve it
v_uid = parameters.get_admin("VIRTUAL_UID");
if v_uid.isdigit():
@@ -291,35 +493,13 @@ class Mailbox(DatesAware):
self.quota = kwargs["quota"]
else:
self.quota = self.domain.quota
for kw in ["enabled", "password", "quota"]:
for kw in ["name", "enabled", "password", "quota"]:
try:
del kwargs[kw]
except KeyError:
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(newvalue)
self.save()
return
if not ldap_available:
raise AdminError(_("Failed to update password: LDAP module not installed"))
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
@@ -348,7 +528,7 @@ class Mailbox(DatesAware):
self.quota = self.domain.quota
super(Mailbox, self).save()
def create_from_csv(self, line):
def create_from_csv(self, user, line):
"""Create a new mailbox from a CSV file entry
The expected order is the following::
@@ -365,8 +545,11 @@ class Mailbox(DatesAware):
self.domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
raise AdminError(_("Cannot import this mailbox because the associated domain does not exist"))
self.name = "%s %s" % (line[2], line[3])
self.save(password=line[1], enabled=True)
if not user.is_owner(self.domain):
raise PermDeniedException(_("You don't have access to this domain"))
name = "%s %s" % (line[2], line[3])
self.save(name=name, password=line[1], enabled=True)
def delete(self, *args, **kwargs):
keepdir = False
@@ -382,14 +565,6 @@ class Mailbox(DatesAware):
if not keepdir:
self.delete_dir()
def tohash(self):
return {
"id" : self.id,
"domain" : self.domain.name,
"full_name" : self.name,
"date_joined" : self.user.date_joined,
"enabled" : self.user.is_active
}
class Alias(DatesAware):
address = models.CharField(ugettext_noop('address'), max_length=254,
@@ -464,12 +639,10 @@ class Alias(DatesAware):
return "<br/>".join(self.extmboxes.split(","))
def ui_disabled(self, user):
from modoboa.admin.lib import is_object_owner
if user.is_superuser:
return False
for mb in self.mboxes.all():
if not is_object_owner(user, mb.domain):
if not user.is_owner(mb.domain):
return True
return False
+4 -22
View File
@@ -2,10 +2,11 @@
import datetime
from django.utils.translation import ugettext as _, ugettext_noop
from django.contrib.auth.models import User, Group
from django.contrib.auth.models import Group
from modoboa.lib.webutils import _render, getctx, _render_to_string
from modoboa.lib import events
from forms import *
from models import User
from tables import SuperAdminsTable, DomainAdminsTable
from lib import grant_access_to_object, ungrant_access_to_object
@@ -50,6 +51,7 @@ class SuperAdminsPerms(Permissions):
user = User.objects.get(pk=request.POST["user"])
user.is_superuser = True
user.date_joined = datetime.datetime.now()
events.raiseEvent("SuperAdminPromotion", user)
user.groups.clear()
user.save()
return True, None
@@ -61,7 +63,6 @@ class SuperAdminsPerms(Permissions):
for s in selection:
user = User.objects.get(pk=int(s))
if user.username == "admin":
#messages.error(request, _("admin is intouchable!!"), fail_silently=True)
continue
user.is_superuser = False
user.save()
@@ -89,22 +90,6 @@ class DomainAdminsPerms(Permissions):
def get_add_form(self, request):
form = DomainAdminForm()
return self._render_form(request, form)
def add(self, request):
form = DomainAdminForm(request.POST)
if request.POST.get("user", "") != "":
mboxid = request.POST['user']
mb = Mailbox.objects.get(pk=mboxid)
form.fields["user"].choices = [(mboxid, mb),]
if form.is_valid():
mb.user.is_superuser = False
mb.user.date_joined = datetime.datetime.now()
mb.user.groups.add(Group.objects.get(name="DomainAdmins"))
mb.user.save()
#set_object_ownership(mb.user, mb.domain)
return True, None
content = self._render_form(request, form, True)
return False, getctx("ko", content=content)
def delete(self, selection):
dagrp = Group.objects.get(name="DomainAdmins")
@@ -121,11 +106,8 @@ class DomainAdminsPerms(Permissions):
u.groups.add(sugrp)
def get(self, request):
#domadmins = Mailbox.objects.filter(user__groups__name="DomainAdmins")
domainadmins = User.objects.filter(groups__name="DomainAdmins")
# domadmins_list = []
# for admin in domadmins:
# domadmins_list += [admin.tohash()]
domainadmins = filter(lambda da: request.user.is_owner(da), domainadmins)
return DomainAdminsTable(request, domainadmins).render()
def get_perms_class(user, role):
+4 -1
View File
@@ -68,7 +68,7 @@ class MailboxesTable(tables.Table):
idkey = "id"
address = tables.Column("address", label=ugettext_noop("Address"))
name = tables.Column("name", label=ugettext_noop("Name"))
name = tables.Column("user", label=ugettext_noop("Name"))
creation = tables.Column("creation", label=ugettext_noop("Created"),
width="160px")
modified = tables.Column("last_modification", label=ugettext_noop("Last modified"),
@@ -95,6 +95,9 @@ class MailboxesTable(tables.Table):
def parse_enabled(self, value):
return _("yes") if value else _("no")
def parse_name(self, user):
return "%s %s" % (user.first_name, user.last_name)
class MbaliasesTable(tables.Table):
tableid = "objects_table"
idkey = "id"
+2 -1
View File
@@ -2,7 +2,6 @@ from django import template
from django.template.loader import render_to_string
from django.utils.translation import ugettext as _, ugettext_noop
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from modoboa import admin
from modoboa.lib import events
from modoboa.lib.webutils import static_url, render_actions
@@ -202,6 +201,8 @@ def mailbox_actions(user, mboxid):
@register.simple_tag
def domain_admin_actions(user, daid):
from modoboa.admin.models import User
actions = [
{"name" : "editdomainadmin",
"url" : reverse(admin.views.edit_domain_admin, args=[daid]),
-2
View File
@@ -25,8 +25,6 @@ urlpatterns = patterns(
(r'^mbaliases/edit/(?P<alias_id>\d+)/$', 'editmbalias'),
(r'^mbaliases/delete/$', 'delmbalias'),
(r'^domains/(?P<dom_id>\d+)/rawmboxes/$', 'mailboxes_raw'),
(r'^settings/$', 'viewparameters'),
(r'^settings/parameters/$', 'viewparameters'),
(r'^settings/parameters/save/$', 'saveparameters'),
+33 -42
View File
@@ -6,7 +6,7 @@ from django.core.urlresolvers import reverse
from django.contrib.auth.decorators \
import login_required, permission_required, user_passes_test
from django.db.models import Q
from django.contrib.auth.models import User, Group
from django.contrib.auth.models import Group
from django.contrib import messages
from django.db import transaction, IntegrityError, transaction
@@ -17,7 +17,6 @@ from modoboa.admin.permissions import *
from modoboa.admin.tables import *
from modoboa.lib import events, parameters
from modoboa.lib.exceptions import *
from modoboa.auth.lib import crypt_password
from modoboa.lib.webutils \
import _render, ajax_response, ajax_simple_response, getctx
from modoboa.lib.emailutils import split_mailbox
@@ -32,7 +31,7 @@ def domains(request):
return HttpResponseRedirect(reverse(userprefs.views.preferences))
domains = get_user_domains_qs(request.user)
domains = request.user.get_domains()
for dom in domains:
dom.mbalias_counter = len(Alias.objects.filter(domain=dom.id))
deloptions = {"keepdir" : _("Do not delete domain directory")}
@@ -93,7 +92,7 @@ def newdomain(request, tplname="admin/adminform.html"):
@permission_required("admin.change_domain")
def editdomain(request, dom_id, tplname="admin/adminform.html"):
domain = Domain.objects.get(pk=dom_id)
if not is_object_owner(request.user, domain):
if not request.user.is_owner(domain):
raise PermDeniedException(_("You can't edit this domain"))
commonctx = {"title" : _("Domain editing"),
@@ -121,7 +120,7 @@ def deldomain(request):
mb = None
for dom in Domain.objects.filter(id__in=selection):
if not is_object_owner(request.user, dom):
if not request.user.is_owner(dom):
raise PermDeniedException(_("You can't delete this domain"))
if mb and mb.domain == dom:
raise AdminError(_("You can't delete your own domain"))
@@ -143,7 +142,7 @@ def domaliases(request):
domaliases = DomainAlias.objects.filter(target=request.GET["domid"])
else:
domain = None
domaliases = get_user_domaliases(request.user)
domaliases = request.user.get_domaliases()
return render_listing(request, "domaliases",
title=_("Domain aliases"),
rel="310 190",
@@ -190,7 +189,7 @@ def newdomalias(request, tplname="admin/adminform.html"):
@permission_required("admin.change_domainalias")
def editdomalias(request, alias_id, tplname="admin/adminform.html"):
domalias = DomainAlias.objects.get(pk=alias_id)
if not is_object_owner(request.user, domalias.target):
if not request.user.is_owner(domalias.target):
raise PermDeniedException(_("You do not have access to this domain"))
commonctx = {"title" : _("Domain alias editing"),
@@ -227,7 +226,7 @@ def deldomalias(request):
selection = request.GET["selection"].split(",")
for daid in selection:
domalias = DomainAlias.objects.get(pk=daid)
if not is_object_owner(request.user, domalias.target):
if not request.user.is_owner(domalias.target):
raise PermDeniedException(_("You don't have acces to this domain"))
events.raiseEvent("DomainAliasDeleted", domalias)
ungrant_access_to_object(domalias)
@@ -246,7 +245,7 @@ def mailboxes(request):
mboxes = Mailbox.objects.filter(domain=domain.id)
title += " (%s)" % domain.name
else:
mboxes = get_user_mailboxes(request.user)
mboxes = request.user.get_mailboxes()
domain = None
deloptions = {"keepdir" : _("Do not delete mailbox directory")}
return render_listing(request, "mailboxes",
@@ -254,20 +253,6 @@ def mailboxes(request):
rel="310 320",
objects=mboxes, domain=domain, deloptions=deloptions)
@login_required
@good_domain
@permission_required("admin.view_mailboxes")
def mailboxes_raw(request, dom_id=None):
target = request.GET.has_key("target") and request.GET["target"] or "permissions"
if target == "permissions":
mailboxes = Mailbox.objects.filter(domain=dom_id).exclude(user__id=request.user.id)
else:
mailboxes = Mailbox.objects.filter(domain=dom_id)
return _render(request, 'admin/mailboxes_raw.html', {
"mailboxes" : mailboxes,
"target" : target
})
@login_required
@permission_required("admin.view_mailboxes")
@check_domain_access
@@ -281,7 +266,7 @@ def mailboxes_search(request):
if request.GET.has_key("domid"):
query = Q(domain=request.GET["domid"]) & query
elif not request.user.is_superuser:
query = Q(domain__in=get_user_domains(request.user)) & query
query = Q(domain__in=request.user.get_domains()) & query
mboxes = Mailbox.objects.filter(query)
result = map(lambda mb: mb.full_address, mboxes)
return ajax_simple_response(result)
@@ -296,11 +281,11 @@ def newmailbox(request, tplname="admin/adminform.html"):
"action" : reverse(newmailbox),
"formid" : "mboxform"}
if request.method == "POST":
form = MailboxForm(request.user, request.POST)
form = MailboxWithPasswordForm(request.user, request.POST)
error = None
if form.is_valid():
if not request.user.is_superuser:
if not can_access_object(request.user, form.cleaned_data["domain"]):
if not request.user.can_access(form.cleaned_data["domain"]):
raise PermDeniedException(_("You do not have access to this domain"))
try:
@@ -319,7 +304,7 @@ def newmailbox(request, tplname="admin/adminform.html"):
commonctx["error"] = error
return ajax_response(request, status="ko", template=tplname, **commonctx)
form = MailboxForm(request.user)
form = MailboxWithPasswordForm(request.user)
commonctx["form"] = form
return _render(request, tplname, commonctx)
@@ -327,7 +312,7 @@ def newmailbox(request, tplname="admin/adminform.html"):
@permission_required("admin.change_mailbox")
def editmailbox(request, mbox_id=None, tplname="admin/adminform.html"):
mb = Mailbox.objects.get(pk=mbox_id)
if not is_object_owner(request.user, mb.domain):
if not request.user.is_owner(mb.domain):
raise PermDeniedException(_("You do not have access to this domain"))
commonctx = {"title" : _("Mailbox editing"),
@@ -335,8 +320,13 @@ def editmailbox(request, mbox_id=None, tplname="admin/adminform.html"):
"action" : reverse(editmailbox, args=[mbox_id]),
"formid" : "mboxform"}
if request.method == "POST":
if request.POST.get("password1", "") != "" or \
request.POST.get("password2", "") != "":
formclass = MailboxWithPasswordForm
else:
formclass = MailboxForm
oldmb = copy.deepcopy(mb)
form = MailboxForm(request.user, request.POST, instance=mb)
form = formclass(request.user, request.POST, instance=mb)
error = None
if form.is_valid():
try:
@@ -356,7 +346,7 @@ def editmailbox(request, mbox_id=None, tplname="admin/adminform.html"):
commonctx["error"] = error
return ajax_response(request, status="ko", template=tplname, **commonctx)
form = MailboxForm(request.user, instance=mb)
form = MailboxWithPasswordForm(request.user, instance=mb)
commonctx["form"] = form
return _render(request, tplname, commonctx)
@@ -371,7 +361,7 @@ def delmailbox(request):
if len(request.user.mailbox_set.all()) else None
for mb in Mailbox.objects.filter(id__in=selection):
if not can_access_object(request.user, mb.domain):
if not request.user.can_access(mb.domain):
raise PermDeniedException(_("You don't have access to this domain"))
if usermb and usermb == mb:
raise AdminError(_("You can't delete your own mailbox"))
@@ -393,7 +383,7 @@ def mbaliases(request):
elif request.GET.has_key("mbid"):
aliases = Alias.objects.filter(mboxes__id=request.GET["mbid"]).distinct()
else:
aliases = get_user_mbaliases(request.user)
aliases = request.user.get_mbaliases()
return render_listing(request, "mbaliases",
title=_("Mailbox aliases"),
@@ -456,7 +446,7 @@ def newmbalias(request, tplname="admin/mbaliasform.html"):
@permission_required("admin.change_alias")
def editmbalias(request, alias_id, tplname="admin/mbaliasform.html"):
alias = Alias.objects.get(pk=alias_id)
if not is_object_owner(request.user, alias.domain):
if not request.user.is_owner(alias.domain):
raise PermDeniedException(_("You do not have access to this domain"))
commonctx = {"title" : _("Mailbox alias editing"),
"submit_label" : _("Update"),
@@ -479,7 +469,7 @@ def delmbalias(request):
selection = request.GET["selection"].split(",")
for mbaid in selection:
mbalias = Alias.objects.get(pk=mbaid)
if not is_object_owner(request.user, mbalias.domain):
if not request.user.is_owner(mbalias.domain):
raise PermDeniedException(_("You do not have access to this domain"))
events.raiseEvent("MailboxAliasDeleted", mbalias)
ungrant_access_to_object(mbalias)
@@ -590,7 +580,7 @@ def domain_admin_promotion(request):
u = User.objects.get(username=form.cleaned_data["name"])
u.groups.add(Group.objects.get(name="DomainAdmins"))
u.save()
if not can_access_object(request.user, u):
if not request.user.can_access(u):
# The only case where we need to grant access to the
# object is when ``u`` has been create by a super
# admin
@@ -615,7 +605,7 @@ def assign_domains_to_admin(request, da_id, tplname="admin/adminform.html"):
if request.method == "POST":
form = AssignDomainsForm(request.user, da, request.POST)
if form.is_valid():
current_domains = get_user_domains(da)
current_domains = da.get_domains()
for domain in form.cleaned_data["domains"]:
if not domain in current_domains:
grant_access_to_object(da, domain)
@@ -744,7 +734,8 @@ def saveextensions(request):
return HttpResponseRedirect(reverse(admin.views.viewextensions))
@login_required
@user_passes_test(lambda u: u.is_superuser)
@permission_required("admin.add_mailbox")
@transaction.commit_on_success
def importdata(request, tplname="admin/import.html"):
if request.method == "POST":
error = None
@@ -753,19 +744,19 @@ def importdata(request, tplname="admin/import.html"):
import csv
reader = csv.reader(request.FILES['sourcefile'],
delimiter=form.cleaned_data['sepcar'])
cpt = 0
try:
cpt = 0
for row in reader:
mb = Mailbox()
mb.create_from_csv(row)
mb.create_from_csv(request.user, row)
events.raiseEvent("CreateMailbox", request.user, mb)
cpt += 1
except AdminError, e:
error = str(e)
else:
messages.info(request, _("%d mailboxes imported successfully" % cpt))
return _render(request, "admin/import_done.html", {
"status" : "ok", "msg" : ""
})
except ModoboaException, e:
error = str(e)
return _render(request, "admin/import_done.html", {
"status" : "ko", "msg" : error