mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
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:
+7
-7
@@ -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)
|
||||
|
||||
@@ -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 @@
|
||||
[{"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
@@ -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
@@ -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
|
||||
|
||||
@@ -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']
|
||||
+15
-2
@@ -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
@@ -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
@@ -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
@@ -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,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]),
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
-18
@@ -25,21 +25,3 @@ def decrypt(ciph):
|
||||
|
||||
def get_password(request):
|
||||
return decrypt(request.session["password"])
|
||||
|
||||
def _check_password(password, crypted):
|
||||
scheme = parameters.get_admin("PASSWORD_SCHEME", app="admin")
|
||||
if scheme == "crypt":
|
||||
return crypt.crypt(password, crypted) == crypted
|
||||
if scheme == "md5":
|
||||
return hashlib.md5(password).hexdigest() == crypted
|
||||
return 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":
|
||||
obj = hashlib.md5(password)
|
||||
return obj.hexdigest() if hexdigest else obj.digest()
|
||||
return password
|
||||
|
||||
+6
-4
@@ -6,10 +6,12 @@ from django.template.loader import render_to_string
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.webutils import static_url
|
||||
|
||||
def menu(**kwargs):
|
||||
def menu(target, user):
|
||||
import views
|
||||
|
||||
if kwargs["target"] != "top_menu_middle":
|
||||
if target != "top_menu_middle":
|
||||
return []
|
||||
if not user.has_mailbox:
|
||||
return []
|
||||
return [
|
||||
{"name" : "demo",
|
||||
@@ -29,8 +31,8 @@ def menu(**kwargs):
|
||||
}
|
||||
]
|
||||
|
||||
def announcement(**kwargs):
|
||||
if kwargs["target"] == "loginpage":
|
||||
def announcement(target):
|
||||
if target == "loginpage":
|
||||
txt = render_to_string("demo/login_announcement.html")
|
||||
return [txt]
|
||||
return ""
|
||||
|
||||
+3
-3
@@ -2,12 +2,12 @@
|
||||
import os
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from modoboa.admin.lib import is_not_localadmin
|
||||
from modoboa.lib.webutils import ajax_response
|
||||
from modoboa.lib.emailutils import sendmail_simple, sendmail_fromfile
|
||||
from modoboa.lib.decorators import needs_mailbox
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def send_virus(request):
|
||||
status, error = sendmail_fromfile("virus@example.net", request.user.username,
|
||||
os.path.join(settings.MODOBOA_DIR,
|
||||
@@ -17,7 +17,7 @@ def send_virus(request):
|
||||
return ajax_response(request, "ko", respmsg=error)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def send_spam(request):
|
||||
status, error = sendmail_simple("spam@example.net", request.user.username, """
|
||||
This is the GTUBE, the
|
||||
|
||||
@@ -20,7 +20,7 @@ if __name__ == "__main__":
|
||||
l.setLevel(logging.DEBUG)
|
||||
l.addHandler(logging.StreamHandler())
|
||||
|
||||
main.init()
|
||||
main.load()
|
||||
|
||||
max_messages_age = int(parameters.get_admin("MAX_MESSAGES_AGE",
|
||||
app="amavis_quarantine"))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# coding: utf-8
|
||||
|
||||
"""
|
||||
"""
|
||||
@@ -16,13 +16,16 @@ def infos():
|
||||
"url" : "quarantine"
|
||||
}
|
||||
|
||||
def init():
|
||||
events.register("UserMenuDisplay", menu)
|
||||
parameters.register_admin("MAX_MESSAGES_AGE", type="int",
|
||||
deflt=14,
|
||||
help=_("Quarantine messages maximum age (in days) before deletion"))
|
||||
parameters.register_admin("RELEASED_MSGS_CLEANUP", type="list_yesno", deflt="no",
|
||||
help=_("Remove messages marked as released while cleaning up the database"))
|
||||
def load():
|
||||
parameters.register_admin(
|
||||
"MAX_MESSAGES_AGE", type="int",
|
||||
deflt=14,
|
||||
help=_("Quarantine messages maximum age (in days) before deletion")
|
||||
)
|
||||
parameters.register_admin(
|
||||
"RELEASED_MSGS_CLEANUP", type="list_yesno", deflt="no",
|
||||
help=_("Remove messages marked as released while cleaning up the database")
|
||||
)
|
||||
parameters.register_admin("AM_PDP_MODE", type="list",
|
||||
deflt="unix",
|
||||
values=[("inet", "inet"), ("unix", "unix")],
|
||||
@@ -37,9 +40,11 @@ def init():
|
||||
deflt=_("/var/amavis/amavisd.sock"),
|
||||
help=_("Path to the PDP server socket (if unix mode)"))
|
||||
|
||||
parameters.register_user("MESSAGES_PER_PAGE", type="int", deflt=40,
|
||||
label="Number of displayed emails per page",
|
||||
help=_("Sets the maximum number of messages displayed in a page"))
|
||||
parameters.register_user(
|
||||
"MESSAGES_PER_PAGE", type="int", deflt=40,
|
||||
label="Number of displayed emails per page",
|
||||
help=_("Sets the maximum number of messages displayed in a page")
|
||||
)
|
||||
|
||||
def destroy():
|
||||
events.unregister("UserMenuDisplay", menu)
|
||||
@@ -49,6 +54,7 @@ def urls(prefix):
|
||||
return (r'^%squarantine/' % prefix,
|
||||
include('modoboa.extensions.amavis_quarantine.urls'))
|
||||
|
||||
@events.observe("UserMenuDisplay")
|
||||
def menu(target, menu):
|
||||
import views
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ from modoboa.lib import tables
|
||||
from modoboa.lib.webutils import static_url
|
||||
from modoboa.lib.email_listing import MBconnector, EmailListing
|
||||
from modoboa.lib.emailutils import *
|
||||
from modoboa.admin.lib import is_domain_admin
|
||||
from models import *
|
||||
|
||||
class Qtable(tables.Table):
|
||||
@@ -84,7 +83,7 @@ class SQLlisting(EmailListing):
|
||||
reset_wm_url = True
|
||||
|
||||
def __init__(self, user, msgs, filter, **kwargs):
|
||||
if not user.is_superuser and not is_domain_admin(user):
|
||||
if user.belongs_to_group('SimpleUsers'):
|
||||
Qtable.cols_order = ['type', 'rstatus', 'from_', 'subject', 'time']
|
||||
else:
|
||||
Qtable.cols_order = ['type', 'rstatus', 'to', 'from_', 'subject', 'time']
|
||||
|
||||
@@ -5,7 +5,6 @@ from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from modoboa.lib.webutils import static_url
|
||||
from modoboa.extensions import amavis_quarantine
|
||||
from modoboa.admin.lib import is_domain_admin
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@@ -90,7 +89,7 @@ def quar_menu(selection, user):
|
||||
|
||||
menu = render_to_string('common/menu.html',
|
||||
{"selection" : selection, "entries" : entries})
|
||||
if user.is_superuser or is_domain_admin(user):
|
||||
if not user.belongs_to_group('SimpleUsers'):
|
||||
extraopts = [{"name" : "to", "label" : _("To")}]
|
||||
else:
|
||||
extraopts = []
|
||||
|
||||
@@ -16,7 +16,6 @@ from lib import AMrelease
|
||||
from templatetags.amextras import *
|
||||
from modoboa.lib.email_listing import parse_search_parameters
|
||||
from sql_listing import *
|
||||
from modoboa.admin.lib import is_domain_admin
|
||||
|
||||
def __get_current_url(request):
|
||||
if request.session.has_key("page"):
|
||||
@@ -28,7 +27,13 @@ def __get_current_url(request):
|
||||
if params != "":
|
||||
res += "?%s" % (params)
|
||||
return res
|
||||
|
||||
|
||||
def empty_quarantine(request):
|
||||
content = "<div class='info'>%s</div>" % _("Empty quarantine")
|
||||
ctx = getctx("ok", level=2, listing=content, navbar="",
|
||||
menu=quar_menu("", request.user))
|
||||
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
|
||||
|
||||
@login_required
|
||||
def _listing(request):
|
||||
filter = None
|
||||
@@ -59,12 +64,16 @@ def _listing(request):
|
||||
|
||||
q = ~Q(rs='D')
|
||||
if not request.user.is_superuser:
|
||||
mb = Mailbox.objects.get(user=request.user.id)
|
||||
if is_domain_admin(request.user):
|
||||
q &= Q(rid__email__contains=mb.domain.name)
|
||||
if not request.user.belongs_to_group('SimpleUsers'):
|
||||
doms = request.user.get_domains()
|
||||
if not len(doms):
|
||||
return empty_quarantine(request)
|
||||
regexp = "(%s)" % '|'.join(map(lambda dom: dom.name, doms))
|
||||
q &= Q(rid__email__regex=regexp)
|
||||
else:
|
||||
q &= Q(rid__email=mb.full_address)
|
||||
if (request.user.is_superuser or is_domain_admin(request.user)) \
|
||||
q &= Q(rid__email=request.user.email)
|
||||
|
||||
if not request.user.belongs_to_group('SimpleUsers') \
|
||||
and rcptfilter is not None:
|
||||
q &= Q(rid__email__contains=rcptfilter)
|
||||
msgs = Msgrcpt.objects.filter(q).values("mail_id")
|
||||
@@ -82,12 +91,11 @@ def _listing(request):
|
||||
elems_per_page=int(parameters.get_user(request.user,
|
||||
"MESSAGES_PER_PAGE")))
|
||||
page = lst.paginator.getpage(pageid)
|
||||
if page:
|
||||
content = lst.fetch(request, page.id_start, page.id_stop)
|
||||
navbar = lst.render_navbar(page)
|
||||
else:
|
||||
content = "<div class='info'>%s</div>" % _("Empty quarantine")
|
||||
navbar = ""
|
||||
if not page:
|
||||
return empty_quarantine(request)
|
||||
|
||||
content = lst.fetch(request, page.id_start, page.id_stop)
|
||||
navbar = lst.render_navbar(page)
|
||||
ctx = getctx("ok", listing=content, navbar=navbar,
|
||||
menu=quar_menu("", request.user))
|
||||
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
|
||||
@@ -115,7 +123,7 @@ def getmailcontent(request, mail_id):
|
||||
|
||||
@login_required
|
||||
def viewmail(request, mail_id):
|
||||
if request.user.is_superuser or is_domain_admin(request.user):
|
||||
if not request.user.belongs_to_group('SimpleUsers'):
|
||||
rcpt = request.GET["rcpt"]
|
||||
else:
|
||||
mb = Mailbox.objects.get(user=request.user)
|
||||
@@ -158,7 +166,7 @@ def check_mail_id(request, mail_id):
|
||||
@login_required
|
||||
def delete(request, mail_id):
|
||||
mail_id = check_mail_id(request, mail_id)
|
||||
if not request.user.is_superuser and not is_domain_admin(request.user):
|
||||
if request.user.belongs_to_group('SimpleUsers'):
|
||||
mb = Mailbox.objects.get(user=request.user)
|
||||
msgrcpts = Msgrcpt.objects.filter(mail__in=mail_id, rid__email=mb.full_address)
|
||||
msgrcpts.update(rs='D')
|
||||
@@ -178,7 +186,7 @@ def delete(request, mail_id):
|
||||
@login_required
|
||||
def release(request, mail_id):
|
||||
mail_id = check_mail_id(request, mail_id)
|
||||
if not request.user.is_superuser and not is_domain_admin(request.user):
|
||||
if request.user.belongs_to_group('SimpleUsers'):
|
||||
mb = Mailbox.objects.get(user=request.user)
|
||||
msgrcpts = Msgrcpt.objects.filter(mail__in=mail_id, rid__email=mb.full_address)
|
||||
else:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"""
|
||||
from modoboa.admin.models import Mailbox
|
||||
from modoboa.admin.lib import \
|
||||
get_object_owner, grant_access_to_object, is_object_owner, \
|
||||
get_object_owner, grant_access_to_object, \
|
||||
ungrant_access_to_object
|
||||
from modoboa.lib import events
|
||||
from lib import *
|
||||
@@ -104,20 +104,20 @@ def create_pool(user):
|
||||
p.save()
|
||||
p.create_limits()
|
||||
|
||||
@events.observe("DomainAdminDeleted")
|
||||
def move_pool_resource(domadmin):
|
||||
owner = get_object_owner(domadmin)
|
||||
@events.observe("DomainAdminDeleted", "SuperAdminPromotion")
|
||||
def move_pool_resource(user):
|
||||
owner = get_object_owner(user)
|
||||
if not owner.is_superuser:
|
||||
for ooentry in domadmin.objectaccess_set.all():
|
||||
for ooentry in user.objectaccess_set.all():
|
||||
if ooentry.is_owner:
|
||||
grant_access_to_object(owner, ooentry.content_object, True)
|
||||
ooentry.delete()
|
||||
|
||||
for lname in reseller_limits_tpl:
|
||||
l = domadmin.limitspool.get_limit(lname)
|
||||
l = user.limitspool.get_limit(lname)
|
||||
ol = owner.limitspool.get_limit(lname)
|
||||
ol.curvalue += l.curvalue
|
||||
ol.maxvalue += l.maxvalue
|
||||
ol.save()
|
||||
|
||||
domadmin.limitspool.delete()
|
||||
user.limitspool.delete()
|
||||
|
||||
@@ -6,7 +6,8 @@ The *limits* extension
|
||||
|
||||
|
||||
"""
|
||||
from django.contrib.auth.models import Permission, Group
|
||||
from django.contrib.auth.models import Permission, User, Group
|
||||
from django.db import IntegrityError
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.translation import ugettext_noop as _, ugettext
|
||||
from django.core.urlresolvers import reverse
|
||||
@@ -28,6 +29,12 @@ def init():
|
||||
grp.permissions.add(perm)
|
||||
grp.save()
|
||||
|
||||
for user in User.objects.filter(groups__name='DomainAdmins'):
|
||||
try:
|
||||
controls.create_pool(user)
|
||||
except IntegrityError:
|
||||
pass
|
||||
|
||||
def destroy():
|
||||
ct = ContentType.objects.get(app_label="admin", model="domain")
|
||||
grp = Group.objects.get(name="DomainAdmins")
|
||||
@@ -50,6 +57,8 @@ def urls(prefix):
|
||||
|
||||
@events.observe('AdminFooterDisplay')
|
||||
def display_pool_usage(user, objtype):
|
||||
if user.id == 1:
|
||||
return []
|
||||
if objtype == "domaliases":
|
||||
objtype = "domain_aliases"
|
||||
elif objtype == "mbaliases":
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# encoding: utf-8
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
|
||||
# Adding model 'LimitsPool'
|
||||
db.create_table('limits_limitspool', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['admin.User'], unique=True)),
|
||||
))
|
||||
db.send_create_signal('limits', ['LimitsPool'])
|
||||
|
||||
# Adding model 'Limit'
|
||||
db.create_table('limits_limit', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
||||
('curvalue', self.gf('django.db.models.fields.IntegerField')(default=0)),
|
||||
('maxvalue', self.gf('django.db.models.fields.IntegerField')(default=-2)),
|
||||
('pool', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['limits.LimitsPool'])),
|
||||
))
|
||||
db.send_create_signal('limits', ['Limit'])
|
||||
|
||||
# Adding unique constraint on 'Limit', fields ['name', 'pool']
|
||||
db.create_unique('limits_limit', ['name', 'pool_id'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
|
||||
# Removing unique constraint on 'Limit', fields ['name', 'pool']
|
||||
db.delete_unique('limits_limit', ['name', 'pool_id'])
|
||||
|
||||
# Deleting model 'LimitsPool'
|
||||
db.delete_table('limits_limitspool')
|
||||
|
||||
# Deleting model 'Limit'
|
||||
db.delete_table('limits_limit')
|
||||
|
||||
|
||||
models = {
|
||||
'admin.user': {
|
||||
'Meta': {'object_name': 'User', '_ormbases': ['auth.User']},
|
||||
'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': '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'})
|
||||
},
|
||||
'limits.limit': {
|
||||
'Meta': {'unique_together': "(('name', 'pool'),)", 'object_name': 'Limit'},
|
||||
'curvalue': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'maxvalue': ('django.db.models.fields.IntegerField', [], {'default': '-2'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'pool': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['limits.LimitsPool']"})
|
||||
},
|
||||
'limits.limitspool': {
|
||||
'Meta': {'object_name': 'LimitsPool'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['admin.User']", 'unique': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['limits']
|
||||
@@ -3,8 +3,8 @@
|
||||
from django.db import models
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.contenttypes import generic
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext as _, ugettext_noop
|
||||
from modoboa.admin.models import User
|
||||
|
||||
reseller_limits_tpl = [
|
||||
"domains_limit",
|
||||
|
||||
@@ -54,7 +54,6 @@ def get_resellers_table(request):
|
||||
|
||||
@events.observe("PermsGetClass")
|
||||
def get_perms_class(role):
|
||||
print role
|
||||
if role != "resellers":
|
||||
return []
|
||||
return [ResellersPerms]
|
||||
|
||||
@@ -22,17 +22,12 @@ def infos():
|
||||
"url" : "postfix_autoreply"
|
||||
}
|
||||
|
||||
def init():
|
||||
events.register("CreateDomain", onCreateDomain)
|
||||
events.register("DeleteDomain", onDeleteDomain)
|
||||
events.register("CreateMailbox", onCreateMailbox)
|
||||
events.register("DeleteMailbox", onDeleteMailbox)
|
||||
events.register("ModifyMailbox", onModifyMailbox)
|
||||
events.register("UserMenuDisplay", menu)
|
||||
|
||||
parameters.register_admin("AUTOREPLIES_TIMEOUT",
|
||||
type="int", deflt=86400,
|
||||
help=_("Timeout in seconds between two auto-replies to the same recipient"))
|
||||
def load():
|
||||
parameters.register_admin(
|
||||
"AUTOREPLIES_TIMEOUT",
|
||||
type="int", deflt=86400,
|
||||
help=_("Timeout in seconds between two auto-replies to the same recipient")
|
||||
)
|
||||
|
||||
def destroy():
|
||||
events.unregister("CreateDomain", onCreateDomain)
|
||||
@@ -47,11 +42,14 @@ def urls(prefix):
|
||||
return (r'^%spostfix_autoreply/' % prefix,
|
||||
include('modoboa.extensions.postfix_autoreply.urls'))
|
||||
|
||||
@events.observe("UserMenuDisplay")
|
||||
def menu(target, user):
|
||||
import views
|
||||
|
||||
if target != "uprefs_menu":
|
||||
return []
|
||||
if not user.has_mailbox:
|
||||
return []
|
||||
return [
|
||||
{"name" : "autoreply",
|
||||
"url" : reverse(views.autoreply),
|
||||
@@ -61,41 +59,40 @@ def menu(target, user):
|
||||
"label" : ugettext("Auto-reply message")}
|
||||
]
|
||||
|
||||
def onCreateDomain(**kwargs):
|
||||
dom = kwargs["dom"]
|
||||
@events.observe("CreateDomain")
|
||||
def onCreateDomain(user, domain):
|
||||
transport = Transport()
|
||||
transport.domain = "autoreply.%s" % dom.name
|
||||
transport.domain = "autoreply.%s" % domain.name
|
||||
transport.method = "autoreply:"
|
||||
transport.save()
|
||||
|
||||
def onDeleteDomain(**kwargs):
|
||||
dom = kwargs["dom"]
|
||||
trans = Transport.objects.get(domain="autoreply.%s" % dom.name)
|
||||
@events.observe("DeleteDomain")
|
||||
def onDeleteDomain(domain):
|
||||
trans = Transport.objects.get(domain="autoreply.%s" % domain.name)
|
||||
trans.delete()
|
||||
|
||||
def onCreateMailbox(**kwargs):
|
||||
mbox = kwargs["mbox"]
|
||||
@events.observe("CreateMailbox")
|
||||
def onCreateMailbox(user, mailbox):
|
||||
alias = Alias()
|
||||
alias.full_address = mbox.full_address
|
||||
alias.full_address = mailbox.full_address
|
||||
alias.autoreply_address = \
|
||||
"%s@autoreply.%s" % (mbox.full_address, mbox.domain.name)
|
||||
"%s@autoreply.%s" % (mailbox.full_address, mailbox.domain.name)
|
||||
alias.save()
|
||||
|
||||
def onDeleteMailbox(**kwargs):
|
||||
mbox = kwargs["mbox"]
|
||||
@events.observe("DeleteMailbox")
|
||||
def onDeleteMailbox(mailbox):
|
||||
try:
|
||||
alias = Alias.objects.get(full_address=mbox.full_address)
|
||||
alias = Alias.objects.get(full_address=mailbox.full_address)
|
||||
alias.delete()
|
||||
except Alias.DoesNotExist:
|
||||
pass
|
||||
|
||||
def onModifyMailbox(**kwargs):
|
||||
mbox = kwargs["mbox"]
|
||||
oldmbox = kwargs["oldmbox"]
|
||||
if oldmbox.full_address == mbox.full_address:
|
||||
@events.observe("ModifyMailbox")
|
||||
def onModifyMailbox(mailbox, oldmailbox):
|
||||
if oldmailbox.full_address == mailbox.full_address:
|
||||
return
|
||||
alias = Alias.objects.get(full_address=oldmbox.full_address)
|
||||
alias.full_address = mbox.full_address
|
||||
alias = Alias.objects.get(full_address=oldmailbox.full_address)
|
||||
alias.full_address = mailbox.full_address
|
||||
alias.autoreply_address = \
|
||||
"%s@autoreply.%s" % (mbox.full_address, mbox.domain.name)
|
||||
"%s@autoreply.%s" % (mailbox.full_address, mailbox.domain.name)
|
||||
alias.save()
|
||||
|
||||
@@ -20,7 +20,7 @@ def send_autoreply(sender, mailbox, armessage):
|
||||
|
||||
try:
|
||||
lastar = ARhistoric.objects.get(armessage=armessage.id, sender=sender)
|
||||
main.init()
|
||||
main.load()
|
||||
timeout = parameters.get_admin("AUTOREPLIES_TIMEOUT",
|
||||
app="postfix_autoreply")
|
||||
delta = datetime.timedelta(seconds=int(timeout))
|
||||
|
||||
@@ -13,7 +13,7 @@ if __name__ == "__main__":
|
||||
import modoboa.extensions.postfix_autoreply.main as par
|
||||
from modoboa.extensions.postfix_autoreply.models import Transport, Alias
|
||||
|
||||
par.init()
|
||||
par.load()
|
||||
doms = Domain.objects.all()
|
||||
for dom in doms:
|
||||
try:
|
||||
|
||||
@@ -8,12 +8,12 @@ from django.contrib import messages
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from modoboa.lib.webutils import _render, ajax_response
|
||||
from modoboa.admin.lib import is_not_localadmin
|
||||
from modoboa.lib.decorators import needs_mailbox
|
||||
from forms import *
|
||||
from models import *
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin('boxerror')
|
||||
@needs_mailbox()
|
||||
def autoreply(request, tplname="postfix_autoreply/autoreply.html"):
|
||||
mb = Mailbox.objects.get(user=request.user.id)
|
||||
try:
|
||||
|
||||
@@ -6,10 +6,7 @@ from modoboa.lib import events, parameters
|
||||
from modoboa.lib.webutils import static_url
|
||||
from sievelib.managesieve import SUPPORTED_AUTH_MECHS
|
||||
|
||||
def init():
|
||||
events.register("UserMenuDisplay", menu)
|
||||
events.register("UserLogout", userlogout)
|
||||
|
||||
def load():
|
||||
parameters.register_admin("SERVER", type="string",
|
||||
deflt="127.0.0.1",
|
||||
help=_("Address of your MANAGESIEVE server"))
|
||||
@@ -47,11 +44,14 @@ def urls(prefix):
|
||||
return (r'^%ssfilters/' % prefix,
|
||||
include('modoboa.extensions.sievefilters.urls'))
|
||||
|
||||
@events.observe("UserMenuDisplay")
|
||||
def menu(target, user):
|
||||
import views
|
||||
|
||||
if target != "options_menu":
|
||||
return []
|
||||
if not user.has_mailbox:
|
||||
return []
|
||||
return [
|
||||
{"name" : "sievefilters",
|
||||
"label" : ugettext("Message filters"),
|
||||
@@ -59,14 +59,15 @@ def menu(target, user):
|
||||
"img" : static_url("pics/filters.png")}
|
||||
]
|
||||
|
||||
def userlogout(**kwargs):
|
||||
@events.observe("Userlogout")
|
||||
def userlogout(request):
|
||||
from lib import SieveClient
|
||||
|
||||
if kwargs["request"].user.id == 1:
|
||||
if not request.user.has_mailbox:
|
||||
return
|
||||
try:
|
||||
sc = SieveClient(user=kwargs["request"].user.username,
|
||||
password=kwargs["request"].session["password"])
|
||||
sc = SieveClient(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
except Exception, e:
|
||||
pass
|
||||
else:
|
||||
|
||||
@@ -7,14 +7,14 @@ from django.core.urlresolvers import reverse
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.webutils import _render, _render_to_string, _render_error, \
|
||||
ajax_response, ajax_simple_response
|
||||
from modoboa.admin.lib import is_not_localadmin
|
||||
from modoboa.lib.decorators import needs_mailbox
|
||||
from modoboa.lib.connections import ConnectionError
|
||||
from modoboa.auth.lib import get_password
|
||||
from lib import *
|
||||
from forms import *
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def index(request, tplname="sievefilters/index.html"):
|
||||
try:
|
||||
sc = SieveClient(user=request.user.username,
|
||||
@@ -38,14 +38,14 @@ def index(request, tplname="sievefilters/index.html"):
|
||||
})
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def get_templates(request, ftype):
|
||||
if ftype == "condition":
|
||||
return ajax_simple_response(FilterForm([], [], request).cond_templates)
|
||||
return ajax_simple_response(FilterForm([], [], request).action_templates)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def get_user_folders(request):
|
||||
from modoboa.extensions.webmail.lib import IMAPconnector
|
||||
|
||||
@@ -55,7 +55,7 @@ def get_user_folders(request):
|
||||
return ajax_simple_response(ret)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def getfs(request, name):
|
||||
sc = SieveClient(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
@@ -109,7 +109,7 @@ def submitfilter(request, setname, okmsg, tplname, tplctx, update=False, sc=None
|
||||
return ajax_response(request, status="ko", template=tplname, **tplctx)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def newfilter(request, setname, tplname="sievefilters/filter.html"):
|
||||
ctx = {"title" : _("New filter"),
|
||||
"actionurl" : reverse(newfilter, args=[setname])}
|
||||
@@ -123,7 +123,7 @@ def newfilter(request, setname, tplname="sievefilters/filter.html"):
|
||||
return _render(request, tplname, ctx)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def editfilter(request, setname, fname, tplname="sievefilters/filter.html"):
|
||||
ctx = {"title" : _("Edit filter"),
|
||||
"actionurl" : reverse(editfilter, args=[setname, fname])}
|
||||
@@ -143,7 +143,7 @@ def editfilter(request, setname, fname, tplname="sievefilters/filter.html"):
|
||||
return _render(request, tplname, ctx)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def removefilter(request, setname, fname):
|
||||
sc = SieveClient(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
@@ -154,7 +154,7 @@ def removefilter(request, setname, fname):
|
||||
return ajax_response(request, "ko", respmsg=_("Failed to remove filter"))
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def savefs(request, name):
|
||||
if not request.POST.has_key("scriptcontent"):
|
||||
return
|
||||
@@ -168,7 +168,7 @@ def savefs(request, name):
|
||||
return ajax_response(request, respmsg=_("Filters set saved"))
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def new_filters_set(request, tplname="sievefilters/newfiltersset.html"):
|
||||
ctx = {"title" : _("Create a new filters set"),
|
||||
"fname" : "newfiltersset",
|
||||
@@ -199,7 +199,7 @@ def new_filters_set(request, tplname="sievefilters/newfiltersset.html"):
|
||||
return _render(request, tplname, ctx)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def remove_filters_set(request, name):
|
||||
sc = SieveClient(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
@@ -210,7 +210,7 @@ def remove_filters_set(request, name):
|
||||
return ajax_response(request, respmsg=_("Filters set deleted"))
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def activate_filters_set(request, name):
|
||||
sc = SieveClient(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
@@ -221,7 +221,7 @@ def activate_filters_set(request, name):
|
||||
return ajax_response(request, respmsg=_("Filters set activated"))
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def download_filters_set(request, name):
|
||||
sc = SieveClient(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
@@ -237,7 +237,7 @@ def download_filters_set(request, name):
|
||||
return resp
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def toggle_filter_state(request, setname, fname):
|
||||
sc = SieveClient(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
@@ -275,11 +275,11 @@ def move_filter(request, setname, fname, direction):
|
||||
return ajax_response(request, template="sievefilters/filters.html", fs=fset)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def move_filter_up(request, setname, fname):
|
||||
return move_filter(request, setname, fname, "up")
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def move_filter_down(request, setname, fname):
|
||||
return move_filter(request, setname, fname, "down")
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import os
|
||||
import time
|
||||
import datetime
|
||||
import rrdtool
|
||||
#import rrdtool
|
||||
from django.utils.translation import ugettext as _
|
||||
from modoboa.lib import parameters
|
||||
|
||||
|
||||
@@ -21,8 +21,7 @@ def infos():
|
||||
"url" : "stats"
|
||||
}
|
||||
|
||||
def init():
|
||||
events.register("AdminMenuDisplay", menu)
|
||||
def load():
|
||||
parameters.register_admin("LOGFILE", type="string",
|
||||
deflt="/var/log/mail.log",
|
||||
help=_("Path to log file used to collect statistics"))
|
||||
@@ -40,6 +39,7 @@ def urls(prefix):
|
||||
return (r'^%sstats/' % prefix,
|
||||
include('modoboa.extensions.stats.urls'))
|
||||
|
||||
@events.observe("AdminMenuDisplay")
|
||||
def menu(target, user):
|
||||
if target == "admin_menu_box":
|
||||
return [
|
||||
|
||||
@@ -259,7 +259,7 @@ class LogParser(object):
|
||||
if __name__ == "__main__":
|
||||
from modoboa.extensions.stats import main
|
||||
|
||||
main.init()
|
||||
main.load()
|
||||
|
||||
log_file = parameters.get_admin("LOGFILE", app="stats")
|
||||
rrd_rootdir = parameters.get_admin("RRD_ROOTDIR", app="stats")
|
||||
|
||||
@@ -58,11 +58,7 @@ window.addEvent("domready", function() {
|
||||
};
|
||||
|
||||
get_graph = function(evt) {
|
||||
{% if user.is_superuser %}
|
||||
var url = "{% url getgraph_root %}" + $("domain").value + "/";
|
||||
{% else %}
|
||||
var url = "{% url getgraph domain.id %}";
|
||||
{% endif %}
|
||||
var p = get_period();
|
||||
|
||||
if (p == "") {
|
||||
@@ -72,9 +68,7 @@ window.addEvent("domready", function() {
|
||||
onSuccess : getgraph_callback}).get();
|
||||
};
|
||||
|
||||
{% if user.is_superuser %}
|
||||
$("domain").addEvent("change", get_graph);
|
||||
{% endif %}
|
||||
$("period").addEvent("change", get_graph);
|
||||
$("customsend").addEvent("click", get_graph);
|
||||
});
|
||||
@@ -84,17 +78,18 @@ window.addEvent("domready", function() {
|
||||
{% block pagetitle %}{% trans "Statistics" %}{% endblock %}
|
||||
{% block subcontent %}
|
||||
<div id="selectors">
|
||||
{% if user.is_superuser %}
|
||||
<div id="domainbox">
|
||||
<label for="domain">{% trans "Domain:" %}</label>
|
||||
<select id="domain" name="domain">
|
||||
{% if user.is_superuser %}
|
||||
<option value="global" {% ifequal domain "global" %}selected{% endifequal %}>{% trans "Global" %}</option>
|
||||
{% endif %}
|
||||
{% for d in domains %}
|
||||
<option value="{{ d.id }}" {% ifequal d.name domain %}selected{% endifequal %}>{{ d.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div id="periodbox">
|
||||
<label for="period">{% trans "Period:" %}</label>
|
||||
<select id="period" name="period">
|
||||
|
||||
+15
-15
@@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _, ugettext_noop
|
||||
from django.utils import simplejson
|
||||
from modoboa.lib.webutils import _render, _render_to_string, getctx
|
||||
from modoboa.admin.views import good_domain
|
||||
from modoboa.admin.lib import check_domain_access
|
||||
from modoboa.admin.models import Domain, Mailbox
|
||||
from django.contrib.auth.decorators \
|
||||
import login_required, user_passes_test, permission_required
|
||||
@@ -22,21 +22,17 @@ periods = [{"name" : "day", "label" : ugettext_noop("Day")},
|
||||
{"name" : "custom", "label" : ugettext_noop("Custom")}]
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.view_mailboxes")
|
||||
@check_domain_access
|
||||
def index(request):
|
||||
domains = None
|
||||
period = request.GET.has_key("period") and request.GET["period"] or "day"
|
||||
domid = request.GET.has_key("domid") and request.GET["domid"] or ""
|
||||
if request.user.is_superuser:
|
||||
domains = Domain.objects.all()
|
||||
domain = "global"
|
||||
else:
|
||||
domain = Domain.objects.get(pk=domid)
|
||||
period = request.GET.get("period", "day")
|
||||
domid = request.GET.get("domid", "")
|
||||
|
||||
domains = request.user.get_domains()
|
||||
|
||||
return _render(request, 'stats/index.html', {
|
||||
"domains" : domains,
|
||||
"domain" : domain,
|
||||
"graphs" : graph_list,
|
||||
"periods" : periods,
|
||||
"period" : period,
|
||||
@@ -45,16 +41,20 @@ def index(request):
|
||||
})
|
||||
|
||||
@login_required
|
||||
@good_domain
|
||||
@permission_required("admin.view_mailboxes")
|
||||
def getgraph(request, dom_id):
|
||||
period = request.GET.has_key("period") and request.GET["period"] or "day"
|
||||
period = request.GET.get("period", "day")
|
||||
if dom_id == "global":
|
||||
if not request.user.is_superuser:
|
||||
raise PermDeniedError(_("you're not allowed to see those statistics"))
|
||||
domain = dom_id
|
||||
else:
|
||||
domain = Domain.objects.get(pk=dom_id).name
|
||||
domain = Domain.objects.get(pk=dom_id)
|
||||
if not request.user.can_access(domain):
|
||||
raise PermDeniedError(_("You don't have access to this domain"))
|
||||
|
||||
ctx = None
|
||||
tplvars = {"graphs" : graph_list, "period" : period, "domain" : domain}
|
||||
tplvars = {"graphs" : graph_list, "period" : period, "domain" : domain.name}
|
||||
if period == "custom":
|
||||
if not request.GET.has_key("start") or not request.GET.has_key("end"):
|
||||
ctx = getctx("ko", error=_("Bad custom period"))
|
||||
@@ -64,7 +64,7 @@ def getgraph(request, dom_id):
|
||||
G = Grapher()
|
||||
period_name = "%s_%s" % (start.replace('-',''), end.replace('-',''))
|
||||
for tpl_name in graph_list:
|
||||
G.process(domain,
|
||||
G.process(domain.name,
|
||||
period_name,
|
||||
str2Time(*start.split('-')),
|
||||
str2Time(*end.split('-')),
|
||||
|
||||
@@ -735,7 +735,6 @@ def send_mail(request, origmsg=None, posturl=None):
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from forms import ComposeMailForm
|
||||
from modoboa.lib.webutils import getctx, ajax_simple_response
|
||||
from modoboa.admin.models import Mailbox
|
||||
from modoboa.auth.lib import get_password
|
||||
|
||||
form = ComposeMailForm(request.POST)
|
||||
@@ -772,11 +771,11 @@ def send_mail(request, origmsg=None, posturl=None):
|
||||
msg.attach(create_mail_attachment(attdef))
|
||||
|
||||
msg["Subject"] = form.cleaned_data["subject"]
|
||||
address, domain = split_mailbox(form.cleaned_data["from_"])
|
||||
try:
|
||||
mb = Mailbox.objects.get(address=address, domain__name=domain)
|
||||
msg["From"] = "%s <%s>" % (mb.name, form.cleaned_data["from_"])
|
||||
except Mailbox.DoesNotExist:
|
||||
if request.user.first_name != "" or request.user.last_name != "":
|
||||
msg["From"] = "%s %s <%s>" % (request.user.first_name,
|
||||
request.user.last_name,
|
||||
form.cleaned_data["from_"])
|
||||
else:
|
||||
msg["From"] = form.cleaned_data["from_"]
|
||||
msg["To"] = form.cleaned_data["to"]
|
||||
msg["Message-ID"] = make_msgid()
|
||||
|
||||
@@ -5,10 +5,7 @@ from django.conf.urls.defaults import include
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.lib.webutils import static_url
|
||||
|
||||
def init():
|
||||
events.register("UserMenuDisplay", menu)
|
||||
events.register("UserLogout", userlogout)
|
||||
|
||||
def load():
|
||||
parameters.register_admin("IMAP_SERVER", type="string",
|
||||
deflt="127.0.0.1",
|
||||
help=_("Address of your IMAP server"))
|
||||
@@ -81,11 +78,14 @@ def urls(prefix):
|
||||
return (r'^%swebmail/' % prefix,
|
||||
include('modoboa.extensions.webmail.urls'))
|
||||
|
||||
@events.observe("UserMenuDisplay")
|
||||
def menu(target, user):
|
||||
import views
|
||||
|
||||
if target != "top_menu":
|
||||
return []
|
||||
if not user.has_mailbox:
|
||||
return []
|
||||
return [
|
||||
{"name" : "webmail",
|
||||
"label" : _("Webmail"),
|
||||
@@ -93,14 +93,15 @@ def menu(target, user):
|
||||
"img" : static_url("pics/webmail.png")}
|
||||
]
|
||||
|
||||
def userlogout(**kwargs):
|
||||
@events.observe("Userlogout")
|
||||
def userlogout(request):
|
||||
from lib import IMAPconnector
|
||||
|
||||
if kwargs["request"].user.id == 1:
|
||||
if not request.user.has_mailbox:
|
||||
return
|
||||
try:
|
||||
m = IMAPconnector(user=kwargs["request"].user.username,
|
||||
password=kwargs["request"].session["password"])
|
||||
m = IMAPconnector(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
except Exception, e:
|
||||
pass
|
||||
else:
|
||||
|
||||
+19
-20
@@ -9,13 +9,12 @@ from django.utils.translation import ugettext as _
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
from modoboa.admin.models import Mailbox
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.webutils import _render, _render_error, \
|
||||
getctx, _render_to_string, ajax_response
|
||||
from modoboa.lib.emailutils import split_mailbox
|
||||
from modoboa.lib.email_listing import parse_search_parameters, Paginator
|
||||
from modoboa.admin.lib import is_not_localadmin
|
||||
from modoboa.lib.decorators import needs_mailbox
|
||||
from modoboa.auth.lib import *
|
||||
from lib import *
|
||||
from forms import *
|
||||
@@ -78,7 +77,7 @@ def __render_common_components(request, folder_name, lst=None, content=None, men
|
||||
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def folder(request, name, updatenav=True):
|
||||
if not name:
|
||||
name = "INBOX"
|
||||
@@ -112,7 +111,7 @@ def folder(request, name, updatenav=True):
|
||||
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def index(request):
|
||||
try:
|
||||
navp = request.session.has_key("navparams") \
|
||||
@@ -134,7 +133,7 @@ def fetchmail(request, folder, mail_id, all=False):
|
||||
return None
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def viewmail(request, folder, mail_id=None):
|
||||
from templatetags.webextras import viewm_menu
|
||||
|
||||
@@ -155,7 +154,7 @@ def viewmail(request, folder, mail_id=None):
|
||||
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def getmailcontent(request, folder, mail_id):
|
||||
msg = fetchmail(request, folder, mail_id, True)
|
||||
if "class" in msg.keys() and msg["class"] == "unseen":
|
||||
@@ -172,7 +171,7 @@ def getmailcontent(request, folder, mail_id):
|
||||
})
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def getattachment(request, folder, mail_id):
|
||||
if request.GET.has_key("partnumber"):
|
||||
headers = {"Content-Type" : "text/plain",
|
||||
@@ -207,7 +206,7 @@ def getattachment(request, folder, mail_id):
|
||||
raise Http404
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def move(request):
|
||||
for arg in ["msgset", "to"]:
|
||||
if not request.GET.has_key(arg):
|
||||
@@ -218,7 +217,7 @@ def move(request):
|
||||
return folder(request, request.session["folder"], False)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def delete(request, fdname, mail_id):
|
||||
mbc = IMAPconnector(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
@@ -227,7 +226,7 @@ def delete(request, fdname, mail_id):
|
||||
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def mark(request, name):
|
||||
if not request.GET.has_key("status") or not request.GET.has_key("ids"):
|
||||
return
|
||||
@@ -240,7 +239,7 @@ def mark(request, name):
|
||||
return folder(request, name, False)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def empty(request, name):
|
||||
if name == parameters.get_user(request.user, "TRASH_FOLDER"):
|
||||
mbc = IMAPconnector(user=request.user.username,
|
||||
@@ -249,7 +248,7 @@ def empty(request, name):
|
||||
return folder(request, name, False)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def compact(request, name):
|
||||
mbc = IMAPconnector(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
@@ -301,7 +300,7 @@ def render_compose(request, form, posturl, email=None, insert_signature=False):
|
||||
return HttpResponse(simplejson.dumps(ctx), mimetype="application/json")
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def compose(request):
|
||||
if request.method == "POST":
|
||||
status, resp = send_mail(request, posturl=reverse(compose))
|
||||
@@ -312,7 +311,7 @@ def compose(request):
|
||||
return render_compose(request, form, reverse(compose), insert_signature=True)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def reply(request, folder, mail_id):
|
||||
msg = fetchmail(request, folder, mail_id, True)
|
||||
if request.method == "POST":
|
||||
@@ -331,7 +330,7 @@ def reply(request, folder, mail_id):
|
||||
email)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def forward(request, folder, mail_id):
|
||||
if request.method == "POST":
|
||||
status, response = send_mail(request,
|
||||
@@ -362,7 +361,7 @@ def separate_folder(fullname, sep="."):
|
||||
return fullname, None
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def newfolder(request, tplname="webmail/folder.html"):
|
||||
mbc = IMAPconnector(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
@@ -389,7 +388,7 @@ def newfolder(request, tplname="webmail/folder.html"):
|
||||
return _render(request, tplname, ctx)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def editfolder(request, tplname="webmail/folder.html"):
|
||||
mbc = IMAPconnector(user=request.user.username,
|
||||
password=request.session["password"])
|
||||
@@ -430,7 +429,7 @@ def editfolder(request, tplname="webmail/folder.html"):
|
||||
return _render(request, tplname, ctx)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def delfolder(request):
|
||||
if not request.GET.has_key("name") or request.GET["name"] == "":
|
||||
return
|
||||
@@ -440,7 +439,7 @@ def delfolder(request):
|
||||
return ajax_response(request)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def attachments(request, tplname="webmail/attachments.html"):
|
||||
if request.method == "POST":
|
||||
csuploader = AttachmentUploadHandler()
|
||||
@@ -475,7 +474,7 @@ def attachments(request, tplname="webmail/attachments.html"):
|
||||
return _render(request, tplname, ctx)
|
||||
|
||||
@login_required
|
||||
@is_not_localadmin()
|
||||
@needs_mailbox()
|
||||
def delattachment(request):
|
||||
if not request.session.has_key("compose_mail") \
|
||||
or not request.GET.has_key("name") \
|
||||
|
||||
+32
-8
@@ -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
|
||||
|
||||
@@ -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
@@ -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):
|
||||
|
||||
@@ -16,3 +16,6 @@ class PermDeniedException(ModoboaException):
|
||||
def __str__(self):
|
||||
return _("Permission denied: %s" % self.msg)
|
||||
|
||||
class NeedsMailboxException(ModoboaException):
|
||||
pass
|
||||
|
||||
|
||||
+15
-7
@@ -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
@@ -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)
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
{% extends "layout_simple.html" %}
|
||||
{% block html_head %}
|
||||
<script src="{{ MEDIA_URL }}js/form.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
window.addEvent("domready", function() {
|
||||
var form = new Form();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% load i18n %}
|
||||
|
||||
+1
-8
@@ -1,7 +1,5 @@
|
||||
from django import forms
|
||||
from modoboa.admin.models import Domain, Mailbox
|
||||
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
|
||||
@@ -25,12 +23,7 @@ class ChangePasswordForm(forms.Form):
|
||||
if parameters.get_admin("AUTHENTICATION_TYPE", app="admin") != "local":
|
||||
return self.cleaned_data["oldpassword"]
|
||||
|
||||
if not isinstance(self.target, Mailbox):
|
||||
func = check_password
|
||||
else:
|
||||
func = _check_password
|
||||
if not func(self.cleaned_data["oldpassword"],
|
||||
self.target.password):
|
||||
if not self.target.check_password(self.cleaned_data["oldpassword"]):
|
||||
raise forms.ValidationError(_("Old password mismatchs"))
|
||||
return self.cleaned_data["oldpassword"]
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ from django.core.urlresolvers import reverse
|
||||
from modoboa import userprefs
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.webutils import static_url
|
||||
from modoboa.admin.lib import is_domain_admin
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@@ -31,7 +30,7 @@ def options_menu(user):
|
||||
]
|
||||
},
|
||||
]
|
||||
if not user.is_superuser and not is_domain_admin(user):
|
||||
if user.belongs_to_group('SimpleUsers'):
|
||||
entries[0]["menu"] += [{
|
||||
"name" : "setforwards",
|
||||
"url" : reverse(userprefs.views.setforward),
|
||||
@@ -41,7 +40,7 @@ def options_menu(user):
|
||||
"rel" : "{handler:'iframe',size:{x:360,y:350},closeBtn:true}"
|
||||
}]
|
||||
|
||||
entries[0]["menu"] += events.raiseQueryEvent("UserMenuDisplay", "options_menu")
|
||||
entries[0]["menu"] += events.raiseQueryEvent("UserMenuDisplay", "options_menu", user)
|
||||
|
||||
return render_to_string('common/menulist.html',
|
||||
{"entries" : entries, "user" : user})
|
||||
@@ -49,7 +48,7 @@ def options_menu(user):
|
||||
@register.simple_tag
|
||||
def uprefs_menu(user):
|
||||
entries = []
|
||||
entries += events.raiseQueryEvent("UserMenuDisplay", "uprefs_menu")
|
||||
entries += events.raiseQueryEvent("UserMenuDisplay", "uprefs_menu", user)
|
||||
|
||||
return render_to_string('common/menulist.html',
|
||||
{"entries" : entries, "user" : user})
|
||||
|
||||
+13
-19
@@ -9,7 +9,7 @@ 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.lib import is_domain_admin, AdminError
|
||||
from modoboa.admin.lib import AdminError
|
||||
from modoboa.auth.lib import encrypt
|
||||
|
||||
@login_required
|
||||
@@ -18,42 +18,36 @@ def index(request):
|
||||
|
||||
@login_required
|
||||
def changepassword(request, tplname="userprefs/chpassword.html"):
|
||||
if request.user.id == 1:
|
||||
target = request.user
|
||||
else:
|
||||
target = Mailbox.objects.get(user=request.user.id)
|
||||
res = events.raiseQueryEvent("PasswordChange", user=request.user)
|
||||
res = events.raiseQueryEvent("PasswordChange", request.user)
|
||||
if True in res:
|
||||
ctx = dict(error=_("Password change is disabled for this user"))
|
||||
return _render_error(request, errortpl="error_simple", user_context=ctx)
|
||||
|
||||
error = None
|
||||
if request.method == "POST":
|
||||
form = ChangePasswordForm(target, request.POST)
|
||||
form = ChangePasswordForm(request.user, request.POST)
|
||||
if form.is_valid():
|
||||
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"])
|
||||
try:
|
||||
request.user.set_password(form.cleaned_data["confirmation"],
|
||||
form.cleaned_data["oldpassword"])
|
||||
except AdminError, e:
|
||||
error = str(e)
|
||||
else:
|
||||
target.set_password(request.POST["confirmation"])
|
||||
target.save()
|
||||
|
||||
request.session["password"] = encrypt(request.POST["confirmation"])
|
||||
request.user.save()
|
||||
if error is None:
|
||||
return ajax_response(request, respmsg=_("Password changed"))
|
||||
|
||||
return ajax_response(request, status="ko", template=tplname,
|
||||
form=form, error=error)
|
||||
|
||||
form = ChangePasswordForm(target)
|
||||
form = ChangePasswordForm(request.user)
|
||||
return _render(request, 'userprefs/chpassword.html', {
|
||||
"form" : form
|
||||
})
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: not u.is_superuser and not is_domain_admin(u))
|
||||
@user_passes_test(lambda u: u.belongs_to_group('SimpleUsers'))
|
||||
def setforward(request, tplname="userprefs/setforward.html"):
|
||||
mb = Mailbox.objects.get(user=request.user.id)
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user