mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
Merge pull request #954 from tonioo/refactor/parameters
Refactor/parameters
This commit is contained in:
+1
-1
@@ -38,7 +38,7 @@ before_script:
|
||||
script:
|
||||
- python ./tests.py
|
||||
- cd test_project
|
||||
- coverage run --source ../modoboa manage.py test modoboa.core modoboa.lib modoboa.admin.tests modoboa.limits modoboa.relaydomains
|
||||
- coverage run --source ../modoboa manage.py test modoboa.core modoboa.admin.tests modoboa.limits modoboa.relaydomains
|
||||
|
||||
after_success:
|
||||
- codecov
|
||||
|
||||
@@ -563,20 +563,6 @@ Must return a list of set. Each set must contain at least three entries::
|
||||
|
||||
[('<limit_name>', '<limit label>', '<limit help text>')]
|
||||
|
||||
GetExtraParameters
|
||||
==================
|
||||
|
||||
Raised to request extra parameters for a given parameters form.
|
||||
|
||||
*Callback prototype*::
|
||||
|
||||
def callback(application, level): pass
|
||||
|
||||
* ``application`` is the name of the form's application (ie. admin, amavis, etc.)
|
||||
* ``level`` is the form's level: ``A`` for admin or ``U`` for user
|
||||
|
||||
Must return a dictionary. Each entry must be a valid Django form field.
|
||||
|
||||
.. _getextrarolepermissions:
|
||||
|
||||
GetExtraRolePermissions
|
||||
|
||||
@@ -6,11 +6,11 @@ from django import forms
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from ..lib import events
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.form_utils import YesNoField, SeparatorField
|
||||
from modoboa.lib.sysutils import exec_cmd
|
||||
from modoboa.parameters import forms as param_forms
|
||||
|
||||
from ..lib import events
|
||||
|
||||
ADMIN_EVENTS = [
|
||||
"DomainCreated",
|
||||
@@ -48,14 +48,14 @@ ADMIN_EVENTS = [
|
||||
]
|
||||
|
||||
|
||||
class AdminParametersForm(parameters.AdminParametersForm):
|
||||
class AdminParametersForm(param_forms.AdminParametersForm):
|
||||
app = "admin"
|
||||
|
||||
dom_sep = SeparatorField(label=ugettext_lazy("Domains"))
|
||||
|
||||
enable_mx_checks = YesNoField(
|
||||
label=ugettext_lazy("Enable MX checks"),
|
||||
initial="yes",
|
||||
initial=True,
|
||||
help_text=ugettext_lazy(
|
||||
"Check that every domain has a valid MX record"
|
||||
)
|
||||
@@ -75,7 +75,7 @@ class AdminParametersForm(parameters.AdminParametersForm):
|
||||
|
||||
enable_dnsbl_checks = YesNoField(
|
||||
label=ugettext_lazy("Enable DNSBL checks"),
|
||||
initial="yes",
|
||||
initial=True,
|
||||
help_text=ugettext_lazy(
|
||||
"Check every domain against major DNSBL providers"
|
||||
)
|
||||
@@ -85,7 +85,7 @@ class AdminParametersForm(parameters.AdminParametersForm):
|
||||
|
||||
handle_mailboxes = YesNoField(
|
||||
label=ugettext_lazy("Handle mailboxes on filesystem"),
|
||||
initial="no",
|
||||
initial=False,
|
||||
help_text=ugettext_lazy(
|
||||
"Rename or remove mailboxes on the filesystem when they get"
|
||||
" renamed or removed within Modoboa"
|
||||
@@ -111,14 +111,14 @@ class AdminParametersForm(parameters.AdminParametersForm):
|
||||
|
||||
auto_account_removal = YesNoField(
|
||||
label=ugettext_lazy("Automatic account removal"),
|
||||
initial="no",
|
||||
initial=False,
|
||||
help_text=ugettext_lazy(
|
||||
"When a mailbox is removed, also remove the associated account")
|
||||
)
|
||||
|
||||
auto_create_domain_and_mailbox = YesNoField(
|
||||
label=ugettext_lazy("Automatic domain/mailbox creation"),
|
||||
initial="yes",
|
||||
initial=True,
|
||||
help_text=ugettext_lazy(
|
||||
"Create a domain and a mailbox when an account is automatically "
|
||||
"created."
|
||||
@@ -127,8 +127,8 @@ class AdminParametersForm(parameters.AdminParametersForm):
|
||||
|
||||
# Visibility rules
|
||||
visibility_rules = {
|
||||
"valid_mxs": "enable_mx_checks=yes",
|
||||
"mailboxes_owner": "handle_mailboxes=yes",
|
||||
"valid_mxs": "enable_mx_checks=True",
|
||||
"mailboxes_owner": "handle_mailboxes=True",
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -174,9 +174,9 @@ class AdminParametersForm(parameters.AdminParametersForm):
|
||||
|
||||
def load_admin_settings():
|
||||
"""Load admin settings."""
|
||||
from .app_settings import AdminParametersForm
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
parameters.register(
|
||||
AdminParametersForm, ugettext_lazy("Administration"))
|
||||
param_tools.registry.add(
|
||||
"global", AdminParametersForm, ugettext_lazy("Administration"))
|
||||
events.declare(ADMIN_EVENTS)
|
||||
from . import callbacks
|
||||
|
||||
@@ -4,9 +4,10 @@ from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from modoboa.core import signals as core_signals
|
||||
from modoboa.lib import parameters, events
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.email_utils import split_mailbox
|
||||
from modoboa.lib.exceptions import PermDeniedException, BadRequest, Conflict
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from .models import (
|
||||
Domain, DomainAlias, Mailbox, Alias
|
||||
@@ -154,7 +155,7 @@ def account_auto_created(user):
|
||||
from modoboa.lib.permissions import grant_access_to_object
|
||||
from .lib import check_if_domain_exists
|
||||
|
||||
if parameters.get_admin("AUTO_CREATE_DOMAIN_AND_MAILBOX") == "no":
|
||||
if not param_tools.get_global_parameter("auto_create_domain_and_mailbox"):
|
||||
return
|
||||
localpart, domname = split_mailbox(user.username)
|
||||
if user.role != 'SimpleUsers' and domname is None:
|
||||
|
||||
@@ -9,9 +9,9 @@ from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from passwords.fields import PasswordField
|
||||
|
||||
from modoboa.core.models import User
|
||||
from modoboa.core import signals as core_signals
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.email_utils import split_mailbox
|
||||
from modoboa.lib import exceptions as lib_exceptions
|
||||
from modoboa.lib import fields as lib_fields
|
||||
@@ -21,6 +21,7 @@ from modoboa.lib.form_utils import (
|
||||
from modoboa.lib.permissions import get_account_roles
|
||||
from modoboa.lib.validators import validate_utf8_email
|
||||
from modoboa.lib.web_utils import render_to_json_response
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from .. import models
|
||||
|
||||
@@ -111,8 +112,8 @@ class AccountFormGeneral(forms.ModelForm):
|
||||
self.fields["role"].initial = self.instance.role
|
||||
condition = (
|
||||
not self.instance.is_local and
|
||||
parameters.get_admin(
|
||||
"LDAP_AUTH_METHOD", app="core") == "directbind")
|
||||
param_tools.get_global_parameter(
|
||||
"ldap_auth_method", app="core") == "directbind")
|
||||
if condition:
|
||||
del self.fields["password1"]
|
||||
del self.fields["password2"]
|
||||
|
||||
@@ -5,15 +5,16 @@ from django.core.urlresolvers import reverse
|
||||
from django.http import QueryDict
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from modoboa.core.models import User
|
||||
from modoboa.core import signals as core_signals
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.exceptions import Conflict
|
||||
from modoboa.lib.fields import DomainNameField
|
||||
from modoboa.lib.form_utils import (
|
||||
YesNoField, WizardForm, WizardStep, DynamicForm, TabForms
|
||||
)
|
||||
from modoboa.lib.web_utils import render_to_json_response
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from ..lib import check_if_domain_exists
|
||||
from ..models import (
|
||||
@@ -75,11 +76,12 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
self._create_field(forms.CharField, name, dalias.name, 3)
|
||||
|
||||
def clean_quota(self):
|
||||
if self.cleaned_data['quota'] is None:
|
||||
return int(parameters.get_admin('DEFAULT_DOMAIN_QUOTA'))
|
||||
if self.cleaned_data['quota'] < 0:
|
||||
"""Check quota value."""
|
||||
if self.cleaned_data["quota"] is None:
|
||||
return param_tools.get_global_parameter("default_domain_quota")
|
||||
if self.cleaned_data["quota"] < 0:
|
||||
raise forms.ValidationError(_("Must be a positive integer"))
|
||||
return self.cleaned_data['quota']
|
||||
return self.cleaned_data["quota"]
|
||||
|
||||
def clean(self):
|
||||
"""Custom fields validation.
|
||||
@@ -164,7 +166,7 @@ class DomainFormOptions(forms.Form):
|
||||
|
||||
create_dom_admin = YesNoField(
|
||||
label=ugettext_lazy("Create a domain administrator"),
|
||||
initial="no",
|
||||
initial=False,
|
||||
help_text=ugettext_lazy(
|
||||
"Automatically create an administrator for this domain"
|
||||
)
|
||||
@@ -182,7 +184,7 @@ class DomainFormOptions(forms.Form):
|
||||
|
||||
create_aliases = YesNoField(
|
||||
label=ugettext_lazy("Create aliases"),
|
||||
initial="yes",
|
||||
initial=True,
|
||||
help_text=ugettext_lazy(
|
||||
"Automatically create standard aliases for this domain"
|
||||
),
|
||||
@@ -191,25 +193,33 @@ class DomainFormOptions(forms.Form):
|
||||
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
super(DomainFormOptions, self).__init__(*args, **kwargs)
|
||||
result = events.raiseQueryEvent('UserCanSetRole', user, 'DomainAdmins')
|
||||
result = events.raiseQueryEvent("UserCanSetRole", user, "DomainAdmins")
|
||||
if False in result:
|
||||
self.fields = {}
|
||||
return
|
||||
if args:
|
||||
if args[0].get("create_dom_admin", "no") == "yes":
|
||||
self.fields["dom_admin_username"].required = True
|
||||
self.fields["create_aliases"].required = True
|
||||
|
||||
def clean_dom_admin_username(self):
|
||||
"""Ensure admin username is an email address."""
|
||||
if '@' in self.cleaned_data["dom_admin_username"]:
|
||||
if "@" in self.cleaned_data["dom_admin_username"]:
|
||||
raise forms.ValidationError(_("Invalid format"))
|
||||
return self.cleaned_data["dom_admin_username"]
|
||||
|
||||
def clean(self):
|
||||
"""Check required values."""
|
||||
cleaned_data = super(DomainFormOptions, self).clean()
|
||||
if cleaned_data.get("create_dom_admin"):
|
||||
if not cleaned_data.get("dom_admin_username"):
|
||||
self.add_error(
|
||||
"dom_admin_username", _("This field is required."))
|
||||
if "create_aliases" not in cleaned_data:
|
||||
self.add_error(
|
||||
"create_aliases", _("This field is required."))
|
||||
return cleaned_data
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.fields:
|
||||
return
|
||||
if self.cleaned_data["create_dom_admin"] == "no":
|
||||
if not self.cleaned_data["create_dom_admin"]:
|
||||
return
|
||||
user = kwargs.pop("user")
|
||||
domain = kwargs.pop("domain")
|
||||
@@ -237,8 +247,7 @@ class DomainFormOptions(forms.Form):
|
||||
override_rules=user.has_perm("admin.change_domain"))
|
||||
mb.save(creator=user)
|
||||
|
||||
if domain.type == "domain" and \
|
||||
self.cleaned_data["create_aliases"] == "yes":
|
||||
if domain.type == "domain" and self.cleaned_data["create_aliases"]:
|
||||
core_signals.can_create_object.send(
|
||||
self.__class__, context=user, object_type="mailbox_aliases")
|
||||
alias = Alias(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"""A management command to apply mailbox operations."""
|
||||
|
||||
import logging
|
||||
from optparse import make_option
|
||||
@@ -5,7 +6,7 @@ import os
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from modoboa.lib import parameters
|
||||
from param_tools import tools as param_tools
|
||||
from modoboa.lib.sysutils import exec_cmd
|
||||
from modoboa.lib.exceptions import InternalError
|
||||
|
||||
@@ -14,22 +15,26 @@ from ...models import MailboxOperation
|
||||
|
||||
|
||||
class OperationError(Exception):
|
||||
"""Custom exception."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Handles rename and delete operations on mailboxes'
|
||||
"""Command definition"""
|
||||
|
||||
help = "Handles rename and delete operations on mailboxes"
|
||||
|
||||
option_list = BaseCommand.option_list + (
|
||||
make_option(
|
||||
'--pidfile', default='/tmp/handle_mailbox_operations.pid',
|
||||
help='Path to the file that will contain the PID of this process'
|
||||
"--pidfile", default="/tmp/handle_mailbox_operations.pid",
|
||||
help="Path to the file that will contain the PID of this process"
|
||||
),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Command, self).__init__(*args, **kwargs)
|
||||
self.logger = logging.getLogger('modoboa.admin')
|
||||
self.logger = logging.getLogger("modoboa.admin")
|
||||
|
||||
def rename_mailbox(self, operation):
|
||||
if not os.path.exists(operation.argument):
|
||||
@@ -75,22 +80,23 @@ class Command(BaseCommand):
|
||||
return True
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""Command entry point."""
|
||||
load_admin_settings()
|
||||
if parameters.get_admin("HANDLE_MAILBOXES") == 'no':
|
||||
if not param_tools.get_global_parameter("handle_mailboxes"):
|
||||
return
|
||||
if not self.check_pidfile(options['pidfile']):
|
||||
if not self.check_pidfile(options["pidfile"]):
|
||||
return
|
||||
for ope in MailboxOperation.objects.all():
|
||||
try:
|
||||
f = getattr(self, '%s_mailbox' % ope.type)
|
||||
f = getattr(self, "%s_mailbox" % ope.type)
|
||||
except AttributeError:
|
||||
continue
|
||||
try:
|
||||
f(ope)
|
||||
except (OperationError, InternalError) as e:
|
||||
self.logger.critical('%s failed (reason: %s)',
|
||||
ope, str(e).encode('utf-8'))
|
||||
self.logger.critical("%s failed (reason: %s)",
|
||||
ope, str(e).encode("utf-8"))
|
||||
else:
|
||||
self.logger.info('%s succeed', ope)
|
||||
self.logger.info("%s succeed", ope)
|
||||
ope.delete()
|
||||
os.unlink(options['pidfile'])
|
||||
os.unlink(options["pidfile"])
|
||||
|
||||
@@ -17,8 +17,8 @@ from django.utils.translation import ugettext as _
|
||||
|
||||
from modoboa.admin import constants
|
||||
from modoboa.admin import models
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib import email_utils
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
|
||||
class CheckMXRecords(BaseCommand):
|
||||
@@ -36,12 +36,12 @@ class CheckMXRecords(BaseCommand):
|
||||
@cached_property
|
||||
def sender(self):
|
||||
"""Return sender address for notifications."""
|
||||
return parameters.get_admin("SENDER_ADDRESS", app="core")
|
||||
return param_tools.get_global_parameter("sender_address", app="core")
|
||||
|
||||
@cached_property
|
||||
def valid_mxs(self):
|
||||
"""Return valid MXs set in admin."""
|
||||
valid_mxs = parameters.get_admin("VALID_MXS", app="admin")
|
||||
valid_mxs = param_tools.get_global_parameter("valid_mxs")
|
||||
return [ipaddress.ip_network(u"{}".format(v.strip()))
|
||||
for v in valid_mxs.split() if v.strip()]
|
||||
|
||||
@@ -217,11 +217,11 @@ class CheckMXRecords(BaseCommand):
|
||||
"""Check specified domain."""
|
||||
mx_list = list(self.get_mx_records_for_domain(domain, ttl=ttl))
|
||||
|
||||
if parameters.get_admin("ENABLE_MX_CHECKS") != "no":
|
||||
if param_tools.get_global_parameter("enable_mx_checks"):
|
||||
self.check_valid_mx(domain, mx_list, **options)
|
||||
|
||||
condition = (
|
||||
parameters.get_admin("ENABLE_DNSBL_CHECKS") == "no" or
|
||||
not param_tools.get_global_parameter("enable_dnsbl_checks") or
|
||||
options["no_dnsbl"] is True)
|
||||
if condition or not mx_list:
|
||||
return
|
||||
|
||||
@@ -13,10 +13,11 @@ from django.contrib.contenttypes.fields import GenericRelation
|
||||
|
||||
from reversion import revisions as reversion
|
||||
|
||||
from modoboa.core.models import User, ObjectAccess
|
||||
from modoboa.core import signals as core_signals
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.core.models import User, ObjectAccess
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.exceptions import BadRequest, Conflict
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from .base import AdminObject
|
||||
|
||||
@@ -185,11 +186,11 @@ class Domain(AdminObject):
|
||||
if self.alias_set.count():
|
||||
events.raiseEvent("MailboxAliasDeleted", self.alias_set.all())
|
||||
ungrant_access_to_objects(self.alias_set.all())
|
||||
if parameters.get_admin("AUTO_ACCOUNT_REMOVAL") == "yes":
|
||||
if param_tools.get_global_parameter("auto_account_removal"):
|
||||
for account in User.objects.filter(mailbox__domain=self):
|
||||
account.delete(fromuser, keepdir)
|
||||
elif self.mailbox_set.count():
|
||||
Quota.objects.filter(username__contains='@%s' % self.name).delete()
|
||||
Quota.objects.filter(username__contains="@%s" % self.name).delete()
|
||||
events.raiseEvent("MailboxDeleted", self.mailbox_set.all())
|
||||
ungrant_access_to_objects(self.mailbox_set.all())
|
||||
super(Domain, self).delete()
|
||||
@@ -242,7 +243,7 @@ class MXQuerySet(models.QuerySet):
|
||||
|
||||
def has_valids(self):
|
||||
"""Return managed results."""
|
||||
if parameters.get_admin("VALID_MXS", app="admin").strip():
|
||||
if param_tools.get_global_parameter("valid_mxs").strip():
|
||||
return self.filter(managed=True).exists()
|
||||
return self.exists()
|
||||
|
||||
@@ -259,12 +260,12 @@ class MXRecord(models.Model):
|
||||
objects = models.Manager.from_queryset(MXQuerySet)()
|
||||
|
||||
def is_managed(self):
|
||||
if not parameters.get_admin("ENABLE_MX_CHECKS"):
|
||||
if not param_tools.get_global_parameter("enable_mx_checks"):
|
||||
return False
|
||||
return bool(parameters.get_admin("VALID_MXS", app="admin").strip())
|
||||
return bool(param_tools.get_global_parameter("valid_mxs").strip())
|
||||
|
||||
def __unicode__(self):
|
||||
return u'{0.name} ({0.address}) for {0.domain} '.format(self)
|
||||
return u"{0.name} ({0.address}) for {0.domain} ".format(self)
|
||||
|
||||
|
||||
class DNSBLQuerySet(models.QuerySet):
|
||||
|
||||
@@ -12,13 +12,15 @@ from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from reversion import revisions as reversion
|
||||
|
||||
from .base import AdminObject
|
||||
from .domain import Domain
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib import exceptions as lib_exceptions
|
||||
from modoboa.lib.email_utils import split_mailbox
|
||||
from modoboa.lib.sysutils import exec_cmd
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from .base import AdminObject
|
||||
from .domain import Domain
|
||||
|
||||
|
||||
class Quota(models.Model):
|
||||
@@ -131,15 +133,15 @@ class Mailbox(AdminObject):
|
||||
several patterns to understand and we don't want to implement
|
||||
them.
|
||||
"""
|
||||
hm = parameters.get_admin("HANDLE_MAILBOXES", raise_error=False)
|
||||
if hm is None or hm == "no":
|
||||
admin_params = dict(param_tools.get_global_parameters("admin"))
|
||||
if not admin_params.get("handle_mailboxes"):
|
||||
return None
|
||||
if self.__mail_home is None:
|
||||
curuser = pwd.getpwuid(os.getuid()).pw_name
|
||||
mbowner = parameters.get_admin("MAILBOXES_OWNER")
|
||||
mbowner = admin_params["mailboxes_owner"]
|
||||
options = {}
|
||||
if curuser != mbowner:
|
||||
options['sudo_user'] = mbowner
|
||||
options["sudo_user"] = mbowner
|
||||
code, output = exec_cmd(
|
||||
"doveadm user %s -f home" % self.full_address, **options
|
||||
)
|
||||
@@ -180,8 +182,9 @@ class Mailbox(AdminObject):
|
||||
|
||||
def rename_dir(self, old_mail_home):
|
||||
"""Rename local directory if needed."""
|
||||
hm = parameters.get_admin("HANDLE_MAILBOXES", raise_error=False)
|
||||
if hm is None or hm == "no":
|
||||
hm = param_tools.get_global_parameter(
|
||||
"handle_mailboxes", raise_exception=False)
|
||||
if not hm:
|
||||
return
|
||||
MailboxOperation.objects.create(
|
||||
mailbox=self, type="rename", argument=old_mail_home
|
||||
@@ -210,10 +213,11 @@ class Mailbox(AdminObject):
|
||||
self.rename_dir(old_mail_home)
|
||||
|
||||
def delete_dir(self):
|
||||
hm = parameters.get_admin("HANDLE_MAILBOXES", raise_error=False)
|
||||
if hm is None or hm == "no":
|
||||
hm = param_tools.get_global_parameter(
|
||||
"handle_mailboxes", raise_exception=False)
|
||||
if not hm:
|
||||
return
|
||||
MailboxOperation.objects.create(type='delete', argument=self.mail_home)
|
||||
MailboxOperation.objects.create(type="delete", argument=self.mail_home)
|
||||
|
||||
def set_quota(self, value=None, override_rules=False):
|
||||
"""Set or update quota's value for this mailbox.
|
||||
|
||||
@@ -146,7 +146,7 @@ Domains.prototype = {
|
||||
* @param {Object} data - options
|
||||
*/
|
||||
init_domain_links: function(data) {
|
||||
var deloptions = (data.handle_mailboxes === "yes") ?
|
||||
var deloptions = (data.handle_mailboxes) ?
|
||||
{keepdir: gettext("Do not delete domain directory")} : {};
|
||||
var warnmsg = (data.auto_account_removal && data.auto_account_removal === "yes")
|
||||
? gettext("This operation will remove ALL data associated to this domain.")
|
||||
@@ -329,7 +329,7 @@ Identities.prototype = {
|
||||
init_identity_links: function(data) {
|
||||
var deloptions = {};
|
||||
|
||||
if (data.handle_mailboxes === "yes") {
|
||||
if (data.handle_mailboxes) {
|
||||
deloptions = {keepdir: gettext("Do not delete mailbox directory")};
|
||||
}
|
||||
|
||||
|
||||
@@ -21,16 +21,16 @@
|
||||
<h3 class="panel-title">{% trans "Summary" %}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{% if enable_mx_checks == "yes" or enable_dnsbl_checks == "yes" %}
|
||||
{% if enable_mx_checks or enable_dnsbl_checks %}
|
||||
<div class="row">
|
||||
<span class="title">{% trans "DNS status" %}</span>
|
||||
{% if domain.awaiting_checks %}
|
||||
<span class="label label-mini label-info">{% trans "Awaiting checks" %}</span>
|
||||
{% else %}
|
||||
{% if enable_mx_checks == "yes" %}
|
||||
{% if enable_mx_checks %}
|
||||
<span class="label label-mini label-{% if not domain.mxrecord_set.has_valids %}danger{% else %}success{% endif %}"><a href="{% url 'admin:mx_domain_detail' domain.pk %}" data-toggle="ajaxmodal">MX</a></span>
|
||||
{% endif %}
|
||||
{% if enable_dnsbl_checks == "yes" %}
|
||||
{% if enable_dnsbl_checks %}
|
||||
<span class="label label-mini label-{{ domain.dnsbl_status_color }}"><a href="{% url 'admin:dnsbl_domain_detail' domain.pk %}" data-toggle="ajaxmodal">DNSBL</a></span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@@ -34,7 +34,7 @@ $(document).ready(function() {
|
||||
<th id="name" class="sortable" data-sort_order="name" width="">{% trans "Name" %}</th>
|
||||
<th id="aliases" width="">{% trans "Alias(es)" %}</th>
|
||||
<th id="tags" class="sortable" data-sort_order="tags" width="">{% trans "Tags" %}</th>
|
||||
{% if enable_mx_checks == "yes" or enable_dnsbl_checks == "yes" %}
|
||||
{% if enable_mx_checks or enable_dnsbl_checks %}
|
||||
<th id="dns" width="">{% trans "DNS status" %}</th>
|
||||
{% endif %}
|
||||
<th id="actions" width="">{% trans "Actions" %}</th>
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
{% if domain.awaiting_checks %}
|
||||
<span class="label label-mini label-info">{% trans "Awaiting checks" %}</span>
|
||||
{% else %}
|
||||
{% if enable_mx_checks == "yes" %}
|
||||
{% if enable_mx_checks %}
|
||||
<span class="label label-mini label-{% if not domain.mxrecord_set.has_valids %}danger{% else %}success{% endif %}"><a href="{% url 'admin:mx_domain_detail' domain.pk %}" data-toggle="ajaxmodal">MX</a></span>
|
||||
{% endif %}
|
||||
{% if enable_dnsbl_checks == "yes" %}
|
||||
{% if enable_dnsbl_checks %}
|
||||
<span class="label label-mini label-{{ domain.dnsbl_status_color }}"><a href="{% url 'admin:dnsbl_domain_detail' domain.pk %}" data-toggle="ajaxmodal">DNSBL</a></span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@@ -8,9 +8,9 @@ from django.test import override_settings
|
||||
from modoboa.core import factories as core_factories
|
||||
from modoboa.core.tests import test_core
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
from modoboa.lib.tests import NO_LDAP
|
||||
from modoboa.limits import utils as limits_utils
|
||||
|
||||
from .. import factories
|
||||
from .. import models
|
||||
@@ -201,8 +201,7 @@ class AccountTestCase(ModoTestCase):
|
||||
|
||||
def test_account_detail_view(self):
|
||||
"""Test account detail view."""
|
||||
parameters.save_admin(
|
||||
"ENABLE_ADMIN_LIMITS", "no", app="limits")
|
||||
self.set_global_parameter("enable_admin_limits", False, app="limits")
|
||||
account = User.objects.get(username="admin@test.com")
|
||||
url = reverse("admin:account_detail", args=[account.pk])
|
||||
response = self.client.get(url)
|
||||
@@ -210,8 +209,7 @@ class AccountTestCase(ModoTestCase):
|
||||
self.assertIn("Summary", response.content)
|
||||
self.assertIn("Administered domains", response.content)
|
||||
self.assertNotIn("Resources usage", response.content)
|
||||
parameters.save_admin(
|
||||
"ENABLE_ADMIN_LIMITS", "yes", app="limits")
|
||||
self.set_global_parameter("enable_admin_limits", True, app="limits")
|
||||
response = self.client.get(url)
|
||||
self.assertIn("Resources usage", response.content)
|
||||
|
||||
@@ -228,7 +226,7 @@ class LDAPAccountTestCase(test_core.LDAPTestCaseMixin, ModoTestCase):
|
||||
"""Check if objects are not created as expected."""
|
||||
self.activate_ldap_authentication()
|
||||
self.searchbind_mode()
|
||||
parameters.save_admin("AUTO_CREATE_DOMAIN_AND_MAILBOX", "no")
|
||||
self.set_global_parameter("auto_create_domain_and_mailbox", False)
|
||||
username = "testuser@example.com"
|
||||
self.authenticate(username, "test")
|
||||
self.assertFalse(
|
||||
@@ -242,15 +240,12 @@ class PermissionsTestCase(ModoTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Create test data."""
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.limits import utils as limits_utils
|
||||
|
||||
super(PermissionsTestCase, cls).setUpTestData()
|
||||
parameters = {}
|
||||
for name, tpl in limits_utils.get_user_limit_templates():
|
||||
parameters.save_admin(
|
||||
"DEFLT_USER_{0}_LIMIT".format(name.upper()), 2,
|
||||
app="limits"
|
||||
)
|
||||
parameters["deflt_user_{0}_limit".format(name)] = 2
|
||||
cls.localconfig.parameters.set_values(parameters, app="limits")
|
||||
cls.localconfig.save()
|
||||
factories.populate_database()
|
||||
cls.reseller = core_factories.UserFactory(username="reseller")
|
||||
cls.reseller.role = "Resellers"
|
||||
|
||||
@@ -10,7 +10,6 @@ from rest_framework.authtoken.models import Token
|
||||
|
||||
from modoboa.admin import models as admin_models
|
||||
from modoboa.core import models as core_models
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.tests import ModoAPITestCase
|
||||
|
||||
from .. import factories
|
||||
@@ -196,8 +195,10 @@ class AccountAPITestCase(ModoAPITestCase):
|
||||
def setUp(self):
|
||||
"""Test setup."""
|
||||
super(AccountAPITestCase, self).setUp()
|
||||
parameters.save_admin("ENABLE_ADMIN_LIMITS", "no", app="limits")
|
||||
parameters.save_admin("ENABLE_DOMAIN_LIMITS", "no", app="limits")
|
||||
self.set_global_parameters({
|
||||
"enable_admin_limits": False,
|
||||
"enable_domain_limits": False
|
||||
}, app="limits")
|
||||
|
||||
def test_get_accounts(self):
|
||||
"""Retrieve a list of accounts."""
|
||||
@@ -461,7 +462,9 @@ class AliasAPITestCase(ModoAPITestCase):
|
||||
def setUpTestData(cls):
|
||||
"""Create test data."""
|
||||
super(AliasAPITestCase, cls).setUpTestData()
|
||||
parameters.save_admin("ENABLE_ADMIN_LIMITS", "no", app="limits")
|
||||
cls.localconfig.parameters.set_value(
|
||||
"enable_admin_limits", False, app="limits")
|
||||
cls.localconfig.save()
|
||||
factories.populate_database()
|
||||
cls.da_token = Token.objects.create(
|
||||
user=core_models.User.objects.get(username="admin@test.com"))
|
||||
|
||||
@@ -7,7 +7,6 @@ import json
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
|
||||
from .. import factories
|
||||
@@ -15,7 +14,6 @@ from ..models import Domain, Alias
|
||||
|
||||
|
||||
class DomainTestCase(ModoTestCase):
|
||||
|
||||
"""Test case for Domain."""
|
||||
|
||||
@classmethod
|
||||
@@ -27,7 +25,7 @@ class DomainTestCase(ModoTestCase):
|
||||
def test_create(self):
|
||||
"""Test the creation of a domain."""
|
||||
values = {
|
||||
"name": "pouet.com", "quota": 100, "create_dom_admin": "no",
|
||||
"name": "pouet.com", "quota": 100, "create_dom_admin": False,
|
||||
"type": "domain", "stepid": "step3"
|
||||
}
|
||||
self.ajax_post(reverse("admin:domain_add"), values)
|
||||
@@ -40,7 +38,7 @@ class DomainTestCase(ModoTestCase):
|
||||
def test_create_utf8(self):
|
||||
"""Test the creation of a domain with non-ASCII characters."""
|
||||
values = {
|
||||
"name": "pouét.com", "quota": 100, "create_dom_admin": "no",
|
||||
"name": "pouét.com", "quota": 100, "create_dom_admin": False,
|
||||
"type": "domain", "stepid": "step3"
|
||||
}
|
||||
self.ajax_post(reverse("admin:domain_add"), values)
|
||||
@@ -51,12 +49,10 @@ class DomainTestCase(ModoTestCase):
|
||||
self.assertFalse(dom.admins)
|
||||
|
||||
def test_create_with_template(self):
|
||||
"""Test the creation of a domain with a template
|
||||
|
||||
"""
|
||||
"""Test the creation of a domain with a template."""
|
||||
values = {
|
||||
"name": "pouet.com", "quota": 100, "create_dom_admin": "yes",
|
||||
"dom_admin_username": "toto", "create_aliases": "yes",
|
||||
"name": "pouet.com", "quota": 100, "create_dom_admin": True,
|
||||
"dom_admin_username": "toto", "create_aliases": True,
|
||||
"type": "domain", "stepid": 'step3'
|
||||
}
|
||||
self.ajax_post(
|
||||
@@ -76,8 +72,8 @@ class DomainTestCase(ModoTestCase):
|
||||
def test_create_with_template_and_empty_quota(self):
|
||||
"""Test the creation of a domain with a template and no quota"""
|
||||
values = {
|
||||
"name": "pouet.com", "quota": 0, "create_dom_admin": "yes",
|
||||
"dom_admin_username": "toto", "create_aliases": "yes",
|
||||
"name": "pouet.com", "quota": 0, "create_dom_admin": True,
|
||||
"dom_admin_username": "toto", "create_aliases": True,
|
||||
"type": "domain", "stepid": 'step3'
|
||||
}
|
||||
self.ajax_post(
|
||||
@@ -95,10 +91,10 @@ class DomainTestCase(ModoTestCase):
|
||||
self.assertTrue(da.can_access(al))
|
||||
|
||||
def test_create_using_default_quota(self):
|
||||
parameters.save_admin('DEFAULT_DOMAIN_QUOTA', 50, app='admin')
|
||||
self.set_global_parameter("default_domain_quota", 50)
|
||||
values = {
|
||||
"name": "pouet.com", "create_dom_admin": "yes",
|
||||
"dom_admin_username": "toto", "create_aliases": "yes",
|
||||
"name": "pouet.com", "create_dom_admin": True,
|
||||
"dom_admin_username": "toto", "create_aliases": True,
|
||||
"type": "domain", "stepid": 'step3'
|
||||
}
|
||||
self.ajax_post(
|
||||
@@ -149,8 +145,7 @@ class DomainTestCase(ModoTestCase):
|
||||
|
||||
def test_domain_detail_view(self):
|
||||
"""Test Domain detail view."""
|
||||
parameters.save_admin(
|
||||
"ENABLE_DOMAIN_LIMITS", "no", app="limits")
|
||||
self.set_global_parameter("enable_domain_limits", False, app="limits")
|
||||
domain = Domain.objects.get(name="test.com")
|
||||
url = reverse("admin:domain_detail", args=[domain.pk])
|
||||
response = self.client.get(url)
|
||||
@@ -158,8 +153,7 @@ class DomainTestCase(ModoTestCase):
|
||||
self.assertIn("Summary", response.content)
|
||||
self.assertIn("Administrators", response.content)
|
||||
self.assertNotIn("Resources usage", response.content)
|
||||
parameters.save_admin(
|
||||
"ENABLE_DOMAIN_LIMITS", "yes", app="limits")
|
||||
self.set_global_parameter("enable_domain_limits", True, app="limits")
|
||||
response = self.client.get(url)
|
||||
self.assertIn("Resources usage", response.content)
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ from django.core.files.base import ContentFile
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
|
||||
from .. import factories
|
||||
@@ -16,7 +15,9 @@ class ImportTestCase(ModoTestCase):
|
||||
def setUpTestData(cls):
|
||||
"""Create test data."""
|
||||
super(ImportTestCase, cls).setUpTestData()
|
||||
parameters.save_admin("ENABLE_ADMIN_LIMITS", "no", app="limits")
|
||||
cls.localconfig.parameters.set_value(
|
||||
"enable_admin_limits", False, app="limits")
|
||||
cls.localconfig.save()
|
||||
factories.populate_database()
|
||||
|
||||
def test_domains_import(self):
|
||||
|
||||
@@ -5,7 +5,6 @@ from django.core.urlresolvers import reverse
|
||||
from django.test import override_settings
|
||||
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
from modoboa.lib import parameters
|
||||
|
||||
from .. import factories
|
||||
from .. import models
|
||||
@@ -21,7 +20,8 @@ class MXTestCase(ModoTestCase):
|
||||
cls.domain = factories.DomainFactory(name="modoboa.org")
|
||||
# should not exist
|
||||
cls.bad_domain = factories.DomainFactory(name="pouet.com")
|
||||
parameters.save_admin("VALID_MXS", "127.0.0.1")
|
||||
cls.localconfig.parameters.set_value("valid_mxs", "127.0.0.1")
|
||||
cls.localconfig.save()
|
||||
models.MXRecord.objects.all().delete()
|
||||
|
||||
def test_management_command(self):
|
||||
|
||||
@@ -2,7 +2,6 @@ from django.core.urlresolvers import reverse
|
||||
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
from modoboa.lib import parameters
|
||||
|
||||
from .. import factories
|
||||
|
||||
@@ -28,7 +27,7 @@ class PasswordSchemesTestCase(ModoTestCase):
|
||||
)
|
||||
|
||||
def _test_scheme(self, name, startpattern):
|
||||
parameters.save_admin('PASSWORD_SCHEME', name, app='core')
|
||||
self.set_global_parameter("password_scheme", name, app="core")
|
||||
self._create_account()
|
||||
account = User.objects.get(username='tester@test.com')
|
||||
self.assertTrue(account.password.startswith(startpattern))
|
||||
|
||||
@@ -16,7 +16,7 @@ from reversion import revisions as reversion
|
||||
|
||||
from modoboa.core.models import User
|
||||
from modoboa.core import signals as core_signals
|
||||
from modoboa.lib import parameters, events
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.web_utils import (
|
||||
_render_to_string, render_to_json_response
|
||||
)
|
||||
@@ -61,20 +61,22 @@ def _domains(request):
|
||||
domainlist = sorted(domainlist, key=lambda d: d.tags[0],
|
||||
reverse=sort_dir == '-')
|
||||
context = {
|
||||
"handle_mailboxes": parameters.get_admin(
|
||||
"HANDLE_MAILBOXES", raise_error=False),
|
||||
"auto_account_removal": parameters.get_admin("AUTO_ACCOUNT_REMOVAL"),
|
||||
"handle_mailboxes": request.localconfig.parameters.get_value(
|
||||
"handle_mailboxes", raise_exception=False),
|
||||
"auto_account_removal": request.localconfig.parameters.get_value(
|
||||
"auto_account_removal"),
|
||||
}
|
||||
page = get_listing_page(domainlist, request.GET.get("page", 1))
|
||||
if page is None:
|
||||
context["length"] = 0
|
||||
else:
|
||||
parameters = request.localconfig.parameters
|
||||
context["rows"] = _render_to_string(
|
||||
request, "admin/domains_table.html", {
|
||||
"domains": page.object_list,
|
||||
"enable_mx_checks": parameters.get_admin("ENABLE_MX_CHECKS"),
|
||||
"enable_mx_checks": parameters.get_value("enable_mx_checks"),
|
||||
"enable_dnsbl_checks": (
|
||||
parameters.get_admin("ENABLE_DNSBL_CHECKS")),
|
||||
parameters.get_value("enable_dnsbl_checks"))
|
||||
}
|
||||
)
|
||||
context["pages"] = [page.number]
|
||||
@@ -90,10 +92,11 @@ def domains(request, tplname="admin/domains.html"):
|
||||
reverse("admin:identity_list")
|
||||
)
|
||||
return HttpResponseRedirect(reverse("core:user_index"))
|
||||
parameters = request.localconfig.parameters
|
||||
return render(request, tplname, {
|
||||
"selection": "domains",
|
||||
"enable_mx_checks": parameters.get_admin("ENABLE_MX_CHECKS"),
|
||||
"enable_dnsbl_checks": parameters.get_admin("ENABLE_DNSBL_CHECKS")
|
||||
"enable_mx_checks": parameters.get_value("enable_mx_checks"),
|
||||
"enable_dnsbl_checks": parameters.get_value("enable_dnsbl_checks")
|
||||
})
|
||||
|
||||
|
||||
@@ -189,10 +192,11 @@ class DomainDetailView(
|
||||
context = super(DomainDetailView, self).get_context_data(**kwargs)
|
||||
result = signals.extra_domain_dashboard_widgets.send(
|
||||
self.__class__, user=self.request.user, domain=self.object)
|
||||
parameters = self.request.localconfig.parameters
|
||||
context.update({
|
||||
"templates": {"left": [], "right": []},
|
||||
"enable_mx_checks": parameters.get_admin("ENABLE_MX_CHECKS"),
|
||||
"enable_dnsbl_checks": parameters.get_admin("ENABLE_DNSBL_CHECKS"),
|
||||
"enable_mx_checks": parameters.get_value("enable_mx_checks"),
|
||||
"enable_dnsbl_checks": parameters.get_value("enable_dnsbl_checks"),
|
||||
})
|
||||
for receiver, widgets in result:
|
||||
for widget in widgets:
|
||||
|
||||
@@ -13,7 +13,7 @@ from django.contrib.auth.decorators import (
|
||||
from reversion import revisions as reversion
|
||||
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import parameters, events
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.exceptions import (
|
||||
PermDeniedException, BadRequest
|
||||
)
|
||||
@@ -49,8 +49,8 @@ def _identities(request):
|
||||
objects = sorted(idents_list, key=lambda o: o.tags[0],
|
||||
reverse=sort_dir == '-')
|
||||
context = {
|
||||
"handle_mailboxes": parameters.get_admin(
|
||||
"HANDLE_MAILBOXES", raise_error=False)
|
||||
"handle_mailboxes": request.localconfig.parameters.get_value(
|
||||
"handle_mailboxes", raise_exception=False)
|
||||
}
|
||||
page = get_listing_page(objects, request.GET.get("page", 1))
|
||||
if page is None:
|
||||
|
||||
@@ -3,25 +3,19 @@ from django import forms
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib import fields as lib_fields
|
||||
from modoboa.lib.cryptutils import random_key
|
||||
from modoboa.lib.form_utils import (
|
||||
YesNoField, SeparatorField, InlineRadioSelect
|
||||
)
|
||||
from modoboa.parameters import forms as param_forms
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from . import constants
|
||||
|
||||
|
||||
def enabled_applications():
|
||||
"""Return the list of installed extensions.
|
||||
|
||||
We check if the table exists before trying to fetch activated
|
||||
extensions because the admin module is always imported by Django,
|
||||
even before the database exists (example: the first ``syncdb``).
|
||||
|
||||
:return: a list
|
||||
"""
|
||||
"""Return the list of installed extensions."""
|
||||
from modoboa.core.extensions import exts_pool
|
||||
|
||||
result = [("user", "user")]
|
||||
@@ -30,8 +24,7 @@ def enabled_applications():
|
||||
return sorted(result, key=lambda e: e[0])
|
||||
|
||||
|
||||
class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
|
||||
class GeneralParametersForm(param_forms.AdminParametersForm):
|
||||
"""General parameters."""
|
||||
|
||||
app = "core"
|
||||
@@ -99,7 +92,7 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
|
||||
ldap_secured = YesNoField(
|
||||
label=ugettext_lazy("Use a secured connection"),
|
||||
initial="no",
|
||||
initial=False,
|
||||
help_text=ugettext_lazy(
|
||||
"Use an SSL/TLS connection to access the LDAP server")
|
||||
)
|
||||
@@ -177,7 +170,7 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
|
||||
ldap_is_active_directory = YesNoField(
|
||||
label=ugettext_lazy("Active Directory"),
|
||||
initial="no",
|
||||
initial=False,
|
||||
help_text=ugettext_lazy(
|
||||
"Tell if the LDAP server is an Active Directory one")
|
||||
)
|
||||
@@ -224,21 +217,21 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
|
||||
enable_api_communication = YesNoField(
|
||||
label=ugettext_lazy("Enable communication"),
|
||||
initial="yes",
|
||||
initial=True,
|
||||
help_text=ugettext_lazy(
|
||||
"Enable communication with Modoboa public API")
|
||||
)
|
||||
|
||||
check_new_versions = YesNoField(
|
||||
label=ugettext_lazy("Check new versions"),
|
||||
initial="yes",
|
||||
initial=True,
|
||||
help_text=ugettext_lazy(
|
||||
"Automatically checks if a newer version is available")
|
||||
)
|
||||
|
||||
send_statistics = YesNoField(
|
||||
label=ugettext_lazy("Send statistics"),
|
||||
initial="yes",
|
||||
initial=True,
|
||||
help_text=ugettext_lazy(
|
||||
"Send statistics to Modoboa public API "
|
||||
"(counters and used extensions)")
|
||||
@@ -296,8 +289,8 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
"ldap_admin_groups": "authentication_type=ldap",
|
||||
"ldap_group_type": "authentication_type=ldap",
|
||||
"ldap_groups_search_base": "authentication_type=ldap",
|
||||
"check_new_versions": "enable_api_communication=yes",
|
||||
"send_statistics": "enable_api_communication=yes",
|
||||
"check_new_versions": "enable_api_communication=True",
|
||||
"send_statistics": "enable_api_communication=True",
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -348,7 +341,7 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
return cleaned_data
|
||||
|
||||
def to_django_settings(self):
|
||||
"""Apply LDAP related parameters to Django settings
|
||||
"""Apply LDAP related parameters to Django settings.
|
||||
|
||||
Doing so, we can use the django_auth_ldap module.
|
||||
"""
|
||||
@@ -360,7 +353,7 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
except ImportError:
|
||||
ldap_available = False
|
||||
|
||||
values = self.get_current_values()
|
||||
values = dict(param_tools.get_global_parameters("core"))
|
||||
if not ldap_available or values["authentication_type"] != "ldap":
|
||||
return
|
||||
if not hasattr(settings, "AUTH_LDAP_USER_ATTR_MAP"):
|
||||
@@ -369,7 +362,7 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
"email": "mail",
|
||||
"last_name": "sn"
|
||||
})
|
||||
ldap_uri = 'ldaps://' if values["ldap_secured"] == "yes" else "ldap://"
|
||||
ldap_uri = "ldaps://" if values["ldap_secured"] else "ldap://"
|
||||
ldap_uri += "%s:%s" % (
|
||||
values["ldap_server_address"], values["ldap_server_port"])
|
||||
setattr(settings, "AUTH_LDAP_SERVER_URI", ldap_uri)
|
||||
@@ -400,7 +393,7 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
settings, "AUTH_LDAP_USER_DN_TEMPLATE",
|
||||
values["ldap_user_dn_template"]
|
||||
)
|
||||
if values["ldap_is_active_directory"] == "yes":
|
||||
if values["ldap_is_active_directory"]:
|
||||
if not hasattr(settings, "AUTH_LDAP_GLOBAL_OPTIONS"):
|
||||
setattr(settings, "AUTH_LDAP_GLOBAL_OPTIONS", {
|
||||
ldap.OPT_REFERRALS: False
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.db.models import signals
|
||||
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
BASE_EVENTS = [
|
||||
"AccountCreated",
|
||||
@@ -36,7 +36,6 @@ BASE_EVENTS = [
|
||||
"ExtraUprefsRoutes",
|
||||
"ExtraUprefsJS",
|
||||
|
||||
"GetExtraParameters",
|
||||
"ExtraFormFields",
|
||||
"SaveExtraFormFields",
|
||||
]
|
||||
@@ -48,11 +47,12 @@ def load_core_settings():
|
||||
This function must be manually called (see :file:`urls.py`) in
|
||||
order to load base settings.
|
||||
"""
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from modoboa.lib import parameters, events
|
||||
from modoboa.lib import events
|
||||
from modoboa.parameters import tools as param_tools
|
||||
from .app_settings import GeneralParametersForm
|
||||
|
||||
parameters.register(GeneralParametersForm, ugettext_lazy("General"))
|
||||
param_tools.registry.add(
|
||||
"global", GeneralParametersForm, ugettext_lazy("General"))
|
||||
events.declare(BASE_EVENTS)
|
||||
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ MODOBOA_APPS = (
|
||||
'modoboa.admin',
|
||||
'modoboa.relaydomains',
|
||||
'modoboa.limits',
|
||||
'modoboa.parameters',
|
||||
# Modoboa extensions here.
|
||||
{% for extension in extensions %} '{{ extension }}',
|
||||
{% endfor %}
|
||||
@@ -81,6 +82,7 @@ MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'modoboa.core.middleware.LocalConfigMiddleware',
|
||||
'modoboa.lib.middleware.AjaxLoginRedirect',
|
||||
'modoboa.lib.middleware.CommonExceptionCatcher',
|
||||
'modoboa.lib.middleware.RequestCatcherMiddleware',
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
"""
|
||||
Custom context processors.
|
||||
"""
|
||||
from modoboa.lib import events, parameters
|
||||
"""Custom context processors."""
|
||||
|
||||
from modoboa.lib import events
|
||||
|
||||
|
||||
def top_notifications(request):
|
||||
"""
|
||||
A context processor to include top notifications.
|
||||
"""
|
||||
"""A context processor to include top notifications."""
|
||||
if request.user.is_anonymous():
|
||||
return {}
|
||||
interval = request.localconfig.parameters.get_value(
|
||||
"top_notifications_check_interval")
|
||||
return {
|
||||
"notifications_check_interval":
|
||||
int(parameters.get_admin("TOP_NOTIFICATIONS_CHECK_INTERVAL")) * 1000,
|
||||
"notifications_check_interval": interval * 1000,
|
||||
"top_notifications": events.raiseQueryEvent(
|
||||
"TopNotifications", request, False)
|
||||
}
|
||||
|
||||
@@ -8,11 +8,10 @@ from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from passwords.fields import PasswordField
|
||||
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
|
||||
class LoginForm(forms.Form):
|
||||
|
||||
"""User login form."""
|
||||
|
||||
username = forms.CharField(
|
||||
@@ -30,7 +29,6 @@ class LoginForm(forms.Form):
|
||||
|
||||
|
||||
class ProfileForm(forms.ModelForm):
|
||||
|
||||
"""Form to update User profile."""
|
||||
|
||||
oldpassword = forms.CharField(
|
||||
@@ -51,8 +49,8 @@ class ProfileForm(forms.ModelForm):
|
||||
fields = ("first_name", "last_name", "language",
|
||||
"phone_number", "secondary_email")
|
||||
widgets = {
|
||||
'first_name': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'last_name': forms.TextInput(attrs={'class': 'form-control'})
|
||||
"first_name": forms.TextInput(attrs={"class": "form-control"}),
|
||||
"last_name": forms.TextInput(attrs={"class": "form-control"})
|
||||
}
|
||||
|
||||
def __init__(self, update_password, *args, **kwargs):
|
||||
@@ -66,7 +64,7 @@ class ProfileForm(forms.ModelForm):
|
||||
if self.cleaned_data["oldpassword"] == "":
|
||||
return self.cleaned_data["oldpassword"]
|
||||
|
||||
if parameters.get_admin("AUTHENTICATION_TYPE") != "local":
|
||||
if param_tools.get_global_parameter("authentication_type") != "local":
|
||||
return self.cleaned_data["oldpassword"]
|
||||
|
||||
if not self.instance.check_password(self.cleaned_data["oldpassword"]):
|
||||
@@ -93,7 +91,6 @@ class ProfileForm(forms.ModelForm):
|
||||
|
||||
|
||||
class APIAccessForm(forms.Form):
|
||||
|
||||
"""Form to control API access."""
|
||||
|
||||
enable_api_access = forms.BooleanField(
|
||||
|
||||
@@ -7,7 +7,8 @@ from django.core.management.base import BaseCommand
|
||||
from django.utils import timezone
|
||||
|
||||
from modoboa.core.models import Log
|
||||
from modoboa.lib import parameters
|
||||
|
||||
from ... import parameters
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
@@ -38,7 +39,7 @@ class Command(BaseCommand):
|
||||
l.addHandler(logging.StreamHandler())
|
||||
self.verbose = options["verbose"]
|
||||
|
||||
log_maximum_age = int(parameters.get_admin("LOG_MAXIMUM_AGE"))
|
||||
log_maximum_age = parameters.get_global_parameter("log_maximum_age")
|
||||
self.__vprint("Deleting logs older than %d days..." % log_maximum_age)
|
||||
limit = timezone.now() - datetime.timedelta(log_maximum_age)
|
||||
Log.objects.filter(date_created__lt=limit).delete()
|
||||
|
||||
@@ -4,7 +4,7 @@ from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
from modoboa.admin import models as admin_models
|
||||
from modoboa.core.extensions import exts_pool
|
||||
from modoboa.lib import api_client, parameters
|
||||
from modoboa.lib import api_client
|
||||
|
||||
from ... import models
|
||||
|
||||
@@ -16,18 +16,18 @@ class Command(BaseCommand):
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""Command entry point."""
|
||||
if parameters.get_admin("ENABLE_API_COMMUNICATION") == "no":
|
||||
return
|
||||
self.client = api_client.ModoAPIClient()
|
||||
local_config = (
|
||||
models.LocalConfig.objects.select_related("site").first())
|
||||
if not local_config.parameters.get_value("enable_api_communication"):
|
||||
return
|
||||
self.client = api_client.ModoAPIClient()
|
||||
if not local_config.api_pk:
|
||||
pk = self.client.register_instance(local_config.site.domain)
|
||||
if pk is None:
|
||||
raise CommandError("Instance registration failed.")
|
||||
local_config.api_pk = pk
|
||||
|
||||
if parameters.get_admin("CHECK_NEW_VERSIONS") == "yes":
|
||||
if local_config.parameters.get_value("check_new_versions"):
|
||||
versions = self.client.versions()
|
||||
if versions is None:
|
||||
raise CommandError("Failed to retrieve versions from the API.")
|
||||
@@ -35,7 +35,7 @@ class Command(BaseCommand):
|
||||
|
||||
local_config.save()
|
||||
|
||||
if parameters.get_admin("SEND_STATISTICS") == "no":
|
||||
if not local_config.parameters.get_value("send_statistics"):
|
||||
return
|
||||
extensions = [ext["name"] for ext in exts_pool.list_all()]
|
||||
data = {
|
||||
|
||||
@@ -10,13 +10,13 @@ from django.core.management.base import BaseCommand
|
||||
|
||||
from modoboa.core import PERMISSIONS
|
||||
from modoboa.core.extensions import exts_pool
|
||||
from modoboa.core.models import User, ObjectAccess
|
||||
from modoboa.lib.cryptutils import random_key
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib import models as lib_models
|
||||
from modoboa.lib.permissions import add_permissions_to_group
|
||||
import modoboa.relaydomains.models as relay_models
|
||||
|
||||
from ... import models
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
||||
@@ -37,25 +37,26 @@ class Command(BaseCommand):
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""Command entry point."""
|
||||
if not User.objects.filter(is_superuser=True).count():
|
||||
admin = User(username=options["admin_username"], is_superuser=True)
|
||||
if not models.User.objects.filter(is_superuser=True).count():
|
||||
admin = models.User(
|
||||
username=options["admin_username"], is_superuser=True)
|
||||
admin.set_password("password")
|
||||
admin.save()
|
||||
ObjectAccess.objects.create(
|
||||
models.ObjectAccess.objects.create(
|
||||
user=admin, content_object=admin, is_owner=True)
|
||||
|
||||
param_name = "core.SECRET_KEY"
|
||||
qset = lib_models.Parameter.objects.filter(name=param_name)
|
||||
if not qset.exists():
|
||||
lib_models.Parameter.objects.create(
|
||||
name=param_name, value=random_key())
|
||||
lc = models.LocalConfig.objects.first()
|
||||
secret_key = lc.parameters.get_value("secret_key")
|
||||
if not secret_key:
|
||||
lc.parameters.set_value("secret_key", random_key())
|
||||
lc.save()
|
||||
|
||||
for service_name in ['relay', 'smtp']:
|
||||
for service_name in ["relay", "smtp"]:
|
||||
relay_models.Service.objects.get_or_create(name=service_name)
|
||||
|
||||
exts_pool.load_all()
|
||||
|
||||
superadmin = User.objects.filter(is_superuser=True).first()
|
||||
superadmin = models.User.objects.filter(is_superuser=True).first()
|
||||
groups = PERMISSIONS.keys() + [
|
||||
role[0] for role
|
||||
in events.raiseQueryEvent("GetExtraRoles", superadmin, None)
|
||||
@@ -75,6 +76,6 @@ class Command(BaseCommand):
|
||||
extension.load_initial_data()
|
||||
events.raiseEvent("InitialDataLoaded", extname)
|
||||
|
||||
if options['extra_fixtures']:
|
||||
if options["extra_fixtures"]:
|
||||
from modoboa.admin import factories
|
||||
factories.populate_database()
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
"""Core middlewares."""
|
||||
|
||||
from . import models
|
||||
|
||||
|
||||
class LocalConfigMiddleware(object):
|
||||
"""A middleware to inject LocalConfig into request."""
|
||||
|
||||
def process_request(self, request):
|
||||
"""Inject LocalConfig instance to request."""
|
||||
request.localconfig = models.LocalConfig.objects.first()
|
||||
@@ -0,0 +1,67 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.5 on 2016-10-26 08:11
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
import jsonfield.fields
|
||||
|
||||
|
||||
def set_parameter(store, parameter):
|
||||
"""Add parameter to the specified store."""
|
||||
app, name = parameter.name.split(".")
|
||||
if app not in store:
|
||||
store[app] = {}
|
||||
# decode value
|
||||
value = parameter.value.decode("unicode_escape")
|
||||
if value == "yes":
|
||||
value = True
|
||||
elif value == "no":
|
||||
value = False
|
||||
elif value.isdigit():
|
||||
value = int(value)
|
||||
store[app][name.lower()] = value
|
||||
|
||||
|
||||
def move_parameters(apps, schema_editor):
|
||||
"""Move global and user parameters."""
|
||||
Parameter = apps.get_model("lib", "Parameter")
|
||||
LocalConfig = apps.get_model("core", "LocalConfig")
|
||||
parameters = {}
|
||||
for parameter in Parameter.objects.all():
|
||||
set_parameter(parameters, parameter)
|
||||
LocalConfig.objects.all().update(_parameters=parameters)
|
||||
|
||||
User = apps.get_model("core", "User")
|
||||
for user in User.objects.prefetch_related("userparameter_set"):
|
||||
for parameter in user.userparameter_set.all():
|
||||
set_parameter(user._parameters, parameter)
|
||||
user.save()
|
||||
|
||||
|
||||
def clear_parameters(apps, schema_editor):
|
||||
"""Reverse operation."""
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0009_auto_20161026_1003'),
|
||||
('lib', '0005_auto_20160416_1449'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='localconfig',
|
||||
name='_parameters',
|
||||
field=jsonfield.fields.JSONField(default={}),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='_parameters',
|
||||
field=jsonfield.fields.JSONField(default={}),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.RunPython(move_parameters, clear_parameters)
|
||||
]
|
||||
+22
-7
@@ -20,10 +20,11 @@ import jsonfield
|
||||
from reversion import revisions as reversion
|
||||
|
||||
from modoboa.core.password_hashers import get_password_hasher
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.exceptions import (
|
||||
PermDeniedException, InternalError, BadRequest, Conflict
|
||||
)
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from . import constants
|
||||
|
||||
@@ -67,7 +68,8 @@ class User(PermissionsMixin):
|
||||
ugettext_lazy('last login'), blank=True, null=True
|
||||
)
|
||||
|
||||
language = models.CharField(ugettext_lazy("language"),
|
||||
language = models.CharField(
|
||||
ugettext_lazy("language"),
|
||||
max_length=10, default="en", choices=constants.LANGUAGES,
|
||||
help_text=ugettext_lazy(
|
||||
"Prefered language to display pages."
|
||||
@@ -81,6 +83,7 @@ class User(PermissionsMixin):
|
||||
help_text=ugettext_lazy(
|
||||
"An alternative e-mail address, can be used for recovery needs.")
|
||||
)
|
||||
_parameters = jsonfield.JSONField(default={})
|
||||
|
||||
objects = UserManager()
|
||||
|
||||
@@ -95,6 +98,11 @@ class User(PermissionsMixin):
|
||||
|
||||
password_expr = re.compile(r'\{([\w\-]+)\}(.+)')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Load parameter manager."""
|
||||
super(User, self).__init__(*args, **kwargs)
|
||||
self.parameters = param_tools.Manager("user", self._parameters)
|
||||
|
||||
def delete(self, fromuser, *args, **kwargs):
|
||||
"""Custom delete method
|
||||
|
||||
@@ -138,13 +146,13 @@ class User(PermissionsMixin):
|
||||
one more time.
|
||||
|
||||
"""
|
||||
try:
|
||||
scheme = parameters.get_admin("PASSWORD_SCHEME")
|
||||
except parameters.NotDefined:
|
||||
scheme = param_tools.get_global_parameter(
|
||||
"password_scheme", raise_exception=False)
|
||||
if scheme is None:
|
||||
from modoboa.core.apps import load_core_settings
|
||||
load_core_settings()
|
||||
scheme = parameters.get_admin("PASSWORD_SCHEME")
|
||||
|
||||
scheme = param_tools.get_global_parameter(
|
||||
"password_scheme", raise_exception=False)
|
||||
if isinstance(raw_value, unicode):
|
||||
raw_value = raw_value.encode("utf-8")
|
||||
return get_password_hasher(scheme.upper())().encrypt(raw_value)
|
||||
@@ -505,3 +513,10 @@ class LocalConfig(models.Model):
|
||||
|
||||
# API results cache
|
||||
api_versions = jsonfield.JSONField()
|
||||
|
||||
_parameters = jsonfield.JSONField(default={})
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Load parameter manager."""
|
||||
super(LocalConfig, self).__init__(*args, **kwargs)
|
||||
self.parameters = param_tools.Manager("global", self._parameters)
|
||||
|
||||
@@ -3,9 +3,12 @@ Advanced (ie. stronger) password hashers.
|
||||
|
||||
This module relies on `passlib` to provide more secure hashers.
|
||||
"""
|
||||
|
||||
from passlib.hash import bcrypt, md5_crypt, sha256_crypt, sha512_crypt
|
||||
from modoboa.core.password_hashers.base import PasswordHasher
|
||||
from modoboa.lib import parameters
|
||||
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from .base import PasswordHasher
|
||||
|
||||
|
||||
class BLFCRYPTHasher(PasswordHasher):
|
||||
@@ -26,7 +29,7 @@ class BLFCRYPTHasher(PasswordHasher):
|
||||
# Using the parameter for rounds will generate the error
|
||||
# ValueError: rounds too high (bcrypt requires <= 31 rounds)
|
||||
# when using the bcrypt hasher.
|
||||
# rounds = int(parameters.get_admin('ROUNDS_NUMBER'))
|
||||
# rounds = parameters.get_global_parameter("rounds_number")
|
||||
# To get around this, I use the default of 12.
|
||||
rounds = 12
|
||||
return bcrypt.encrypt(clearvalue, rounds=rounds)
|
||||
@@ -72,7 +75,7 @@ class SHA256CRYPTHasher(PasswordHasher):
|
||||
return pwhash
|
||||
|
||||
def _encrypt(self, clearvalue, salt=None):
|
||||
rounds = int(parameters.get_admin('ROUNDS_NUMBER'))
|
||||
rounds = param_tools.get_global_parameter("rounds_number")
|
||||
return sha256_crypt.encrypt(clearvalue, rounds=rounds)
|
||||
|
||||
def verify(self, clearvalue, hashed_value):
|
||||
@@ -95,7 +98,7 @@ class SHA512CRYPTHasher(PasswordHasher):
|
||||
return pwhash
|
||||
|
||||
def _encrypt(self, clearvalue, salt=None):
|
||||
rounds = int(parameters.get_admin('ROUNDS_NUMBER'))
|
||||
rounds = param_tools.get_global_parameter("rounds_number")
|
||||
return sha512_crypt.encrypt(clearvalue, rounds=rounds)
|
||||
|
||||
def verify(self, clearvalue, hashed_value):
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
* @constructor
|
||||
* @param {Object} options - instance options
|
||||
*/
|
||||
/* global TwocolsNav */
|
||||
|
||||
var Settings = function(options) {
|
||||
TwocolsNav.call(this, options);
|
||||
};
|
||||
@@ -40,7 +42,7 @@ Settings.prototype = {
|
||||
* Navigation callback: extensions.
|
||||
*/
|
||||
extensions_callback: function(data) {
|
||||
this.select_left_menu();
|
||||
this.select_left_menu();
|
||||
if (data.content) {
|
||||
$('#' + this.options.divid).html(data.content);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
{% load i18n lib_tags %}
|
||||
<h2>{% trans "Parameters" %} <small>{% trans "Configure Modoboa" %}</small></h2><hr />
|
||||
<form id="settings_form" method="POST" action="{% url 'core:parameter_save' %}" class="form-horizontal">{% csrf_token %}
|
||||
{% include "common/parameters.html" %}
|
||||
<form id="parameters_form" method="POST" action="{% url 'core:parameters' %}" class="form-horizontal">{% csrf_token %}
|
||||
{% include "parameters/base_form.html" %}
|
||||
<hr>
|
||||
<button type="submit" class="btn btn-primary col-xs-offset-4 col-xs-4" id="update" name="update">{% trans 'Save' %}</button>
|
||||
<button type="submit" class="btn btn-primary col-xs-offset-4 col-xs-4" name="update">{% trans 'Save' %}</button>
|
||||
</form>
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<hr>
|
||||
{% if forms %}
|
||||
<form class="form-horizontal" method="POST" action="{% url 'core:user_preferences' %}"
|
||||
id="uprefs_form">
|
||||
id="parameters_form">
|
||||
{% csrf_token %}
|
||||
{% include "common/parameters.html" %}
|
||||
{% include "parameters/base_form.html" %}
|
||||
<hr>
|
||||
<button type="submit" class="btn btn-primary col-xs-offset-4 col-xs-4" id="update" name="update">{% trans 'Save' %}</button>
|
||||
<button type="submit" class="btn btn-primary col-xs-offset-4 col-xs-4" name="update">{% trans 'Save' %}</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
|
||||
@@ -7,9 +7,9 @@ import httmock
|
||||
from django.core import management
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import override_settings, TestCase
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
from modoboa.lib import exceptions
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
from modoboa.lib.tests import NO_LDAP
|
||||
|
||||
@@ -58,62 +58,28 @@ class ChangeDefaultAdminTestCase(TestCase):
|
||||
self.client.login(username="modoadmin", password="password"))
|
||||
|
||||
|
||||
class DashboardTestCase(ModoTestCase):
|
||||
"""Dashboard tests."""
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Create some data."""
|
||||
super(DashboardTestCase, cls).setUpTestData()
|
||||
cls.dadmin = factories.UserFactory(
|
||||
username="admin@test.com", groups=("DomainAdmins",)
|
||||
)
|
||||
cls.user = factories.UserFactory(
|
||||
username="user@test.com", groups=("SimpleUsers",)
|
||||
)
|
||||
|
||||
def test_access(self):
|
||||
"""Load dashboard."""
|
||||
url = reverse("core:dashboard")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn("Latest news", response.content)
|
||||
self.client.logout()
|
||||
self.client.login(username=self.dadmin.username, password="toto")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn("Latest news", response.content)
|
||||
self.client.logout()
|
||||
self.client.login(username=self.user.username, password="toto")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_root_dispatch(self):
|
||||
"""Check root dispatching."""
|
||||
url = reverse("core:root")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertTrue(response.url.endswith(reverse("core:dashboard")))
|
||||
|
||||
|
||||
@skipIf(NO_LDAP, 'No ldap module installed')
|
||||
class LDAPTestCaseMixin(object):
|
||||
"""Set of methods used to test LDAP features."""
|
||||
|
||||
@cached_property
|
||||
def ldapauthbackend(self):
|
||||
"""Return LDAPAuthBackend instance."""
|
||||
from modoboa.lib.ldap_utils import LDAPAuthBackend
|
||||
return LDAPAuthBackend()
|
||||
|
||||
def activate_ldap_authentication(self):
|
||||
"""Modify settings."""
|
||||
parameters.save_admin(
|
||||
"AUTHENTICATION_TYPE", "ldap")
|
||||
parameters.save_admin("LDAP_SERVER_PORT", "3389")
|
||||
self.set_global_parameters({
|
||||
"authentication_type": "ldap",
|
||||
"ldap_server_port": 3389
|
||||
})
|
||||
|
||||
def restore_user_password(self, username, new_password):
|
||||
"""Restore user password to its initial state."""
|
||||
from modoboa.lib.ldap_utils import LDAPAuthBackend
|
||||
|
||||
backend = LDAPAuthBackend()
|
||||
for password in ["Toto1234", "test"]:
|
||||
try:
|
||||
backend.update_user_password(
|
||||
self.ldapauthbackend.update_user_password(
|
||||
username, password, new_password)
|
||||
except exceptions.InternalError:
|
||||
pass
|
||||
@@ -131,16 +97,19 @@ class LDAPTestCaseMixin(object):
|
||||
|
||||
def searchbind_mode(self):
|
||||
"""Apply settings required by the searchbind mode."""
|
||||
parameters.save_admin("LDAP_AUTH_METHOD", "searchbind")
|
||||
parameters.save_admin("LDAP_BIND_DN", "cn=admin,dc=example,dc=com")
|
||||
parameters.save_admin("LDAP_BIND_PASSWORD", "test")
|
||||
parameters.save_admin("LDAP_SEARCH_BASE", "ou=users,dc=example,dc=com")
|
||||
self.set_global_parameters({
|
||||
"ldap_auth_method": "searchbind",
|
||||
"ldap_bind_dn": "cn=admin,dc=example,dc=com",
|
||||
"ldap_bind_password": "test",
|
||||
"ldap_search_base": "ou=users,dc=example,dc=com"
|
||||
})
|
||||
|
||||
def directbind_mode(self):
|
||||
"""Apply settings required by the directbind mode."""
|
||||
parameters.save_admin("LDAP_AUTH_METHOD", "directbind")
|
||||
parameters.save_admin(
|
||||
"LDAP_USER_DN_TEMPLATE", "cn=%(user)s,ou=users,dc=example,dc=com")
|
||||
self.set_global_parameters({
|
||||
"ldap_auth_method": "directbind",
|
||||
"ldap_user_dn_template": "cn=%(user)s,ou=users,dc=example,dc=com"
|
||||
})
|
||||
|
||||
|
||||
@override_settings(AUTHENTICATION_BACKENDS=(
|
||||
@@ -148,7 +117,6 @@ class LDAPTestCaseMixin(object):
|
||||
'modoboa.lib.authbackends.SimpleBackend',
|
||||
))
|
||||
class LDAPAuthenticationTestCase(LDAPTestCaseMixin, ModoTestCase):
|
||||
|
||||
"""Validate LDAP authentication scenarios."""
|
||||
|
||||
def setUp(self):
|
||||
@@ -172,11 +140,11 @@ class LDAPAuthenticationTestCase(LDAPTestCaseMixin, ModoTestCase):
|
||||
username = "testuser@example.com"
|
||||
self.authenticate(username, "test")
|
||||
self.check_created_user(username)
|
||||
self.client.logout()
|
||||
|
||||
parameters.save_admin("LDAP_ADMIN_GROUPS", "admins")
|
||||
parameters.save_admin(
|
||||
"LDAP_GROUPS_SEARCH_BASE", "ou=groups,dc=example,dc=com")
|
||||
self.set_global_parameters({
|
||||
"ldap_admin_groups": "admins",
|
||||
"ldap_groups_search_base": "ou=groups,dc=example,dc=com"
|
||||
})
|
||||
username = "mailadmin@example.com"
|
||||
self.authenticate(username, "test", False)
|
||||
self.check_created_user(username, "DomainAdmins")
|
||||
@@ -194,9 +162,10 @@ class LDAPAuthenticationTestCase(LDAPTestCaseMixin, ModoTestCase):
|
||||
|
||||
# 1: must work because usernames of domain admins are not
|
||||
# always email addresses
|
||||
parameters.save_admin("LDAP_ADMIN_GROUPS", "admins")
|
||||
parameters.save_admin(
|
||||
"LDAP_GROUPS_SEARCH_BASE", "ou=groups,dc=example,dc=com")
|
||||
self.set_global_parameters({
|
||||
"ldap_admin_groups": "admins",
|
||||
"ldap_groups_search_base": "ou=groups,dc=example,dc=com"
|
||||
})
|
||||
username = "mailadmin"
|
||||
self.authenticate(username, "test", False)
|
||||
self.check_created_user(username, "DomainAdmins", False)
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
"""Core views test cases."""
|
||||
|
||||
from testfixtures import compare
|
||||
|
||||
from django import forms
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
from modoboa.parameters import forms as param_forms
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from .. import factories
|
||||
from .. import models
|
||||
|
||||
|
||||
class DashboardTestCase(ModoTestCase):
|
||||
"""Dashboard tests."""
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Create some data."""
|
||||
super(DashboardTestCase, cls).setUpTestData()
|
||||
cls.dadmin = factories.UserFactory(
|
||||
username="admin@test.com", groups=("DomainAdmins",)
|
||||
)
|
||||
cls.user = factories.UserFactory(
|
||||
username="user@test.com", groups=("SimpleUsers",)
|
||||
)
|
||||
|
||||
def test_access(self):
|
||||
"""Load dashboard."""
|
||||
url = reverse("core:dashboard")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn("Latest news", response.content)
|
||||
self.client.logout()
|
||||
self.client.login(username=self.dadmin.username, password="toto")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn("Latest news", response.content)
|
||||
self.client.logout()
|
||||
self.client.login(username=self.user.username, password="toto")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_root_dispatch(self):
|
||||
"""Check root dispatching."""
|
||||
url = reverse("core:root")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertTrue(response.url.endswith(reverse("core:dashboard")))
|
||||
|
||||
|
||||
class SettingsTestCase(ModoTestCase):
|
||||
"""Settings tests."""
|
||||
|
||||
settings_sample = {
|
||||
"core-check_new_versions": "True",
|
||||
"core-log_maximum_age": "365",
|
||||
"core-ldap_auth_method": "searchbind",
|
||||
"limits-deflt_domain_mailbox_aliases_limit": "0",
|
||||
"core-send_statistics": "True",
|
||||
"core-default_top_redirection": "user",
|
||||
"core-sender_address": "noreply@modoboa.org",
|
||||
"admin-auto_account_removal": "False",
|
||||
"core-ldap_server_port": "389",
|
||||
"core-rounds_number": "70000",
|
||||
"core-ldap_bind_password": "",
|
||||
"limits-deflt_domain_domain_aliases_limit": "0",
|
||||
"core-secret_key": ":?j3]QPWo!.'_c4n",
|
||||
"relaydomains-master_cf_path": "/etc/postfix/master.cf",
|
||||
"limits-deflt_domain_domain_admins_limit": "0",
|
||||
"limits-enable_admin_limits": "True",
|
||||
"core-ldap_bind_dn": "",
|
||||
"core-enable_api_communication": "True",
|
||||
"limits-deflt_user_domain_admins_limit": "0",
|
||||
"limits-enable_domain_limits": "False",
|
||||
"csrfmiddlewaretoken": "SGgMVZsA4TPqoiV786TMST6xgOlhAf4F",
|
||||
"limits-deflt_user_mailboxes_limit": "0",
|
||||
"core-password_scheme": "sha512crypt",
|
||||
"core-items_per_page": "30",
|
||||
"limits-deflt_user_mailbox_aliases_limit": "0",
|
||||
"limits-deflt_domain_mailboxes_limit": "0",
|
||||
"core-ldap_is_active_directory": "False",
|
||||
"core-ldap_group_type": "posixgroup",
|
||||
"limits-deflt_user_domain_aliases_limit": "0",
|
||||
"core-top_notifications_check_interval": "30",
|
||||
"core-ldap_password_attribute": "userPassword",
|
||||
"admin-auto_create_domain_and_mailbox": "True",
|
||||
"admin-enable_dnsbl_checks": "True",
|
||||
"admin-default_domain_quota": "0",
|
||||
"core-ldap_server_address": "localhost",
|
||||
"core-authentication_type": "local",
|
||||
"core-ldap_admin_groups": "",
|
||||
"core-ldap_groups_search_base": "",
|
||||
"admin-enable_mx_checks": "True",
|
||||
"core-ldap_search_base": "",
|
||||
"admin-valid_mxs": "",
|
||||
"limits-deflt_user_domains_limit": "0",
|
||||
"core-ldap_search_filter": "(mail=%(user)s)",
|
||||
"core-ldap_secured": "False",
|
||||
"core-ldap_user_dn_template": ""
|
||||
}
|
||||
|
||||
def test_get_settings(self):
|
||||
"""Test settings display."""
|
||||
url = reverse("core:parameters")
|
||||
response = self.ajax_get(url)
|
||||
for app in ["core", "admin", "limits", "relaydomains"]:
|
||||
self.assertIn('data-app="{}"'.format(app), response["content"])
|
||||
|
||||
def test_save_settings(self):
|
||||
"""Test settings save."""
|
||||
url = reverse("core:parameters")
|
||||
response = self.client.post(url, self.settings_sample, format="json")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.settings_sample["core-rounds_number"] = ""
|
||||
response = self.client.post(url, self.settings_sample, format="json")
|
||||
self.assertEqual(response.status_code, 400)
|
||||
compare(response.json(), {
|
||||
"form_errors": {"rounds_number": ["This field is required."]},
|
||||
"prefix": "core"
|
||||
})
|
||||
|
||||
|
||||
class UserSettings(param_forms.UserParametersForm):
|
||||
"""Stupid user settings form."""
|
||||
|
||||
app = "core"
|
||||
|
||||
test = forms.CharField()
|
||||
|
||||
|
||||
class PreferencesTestCase(ModoTestCase):
|
||||
"""Test user preferences."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Register user form."""
|
||||
super(PreferencesTestCase, cls).setUpClass()
|
||||
param_tools.registry.add("user", UserSettings, "Test")
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""Remove user class."""
|
||||
super(PreferencesTestCase, cls).tearDownClass()
|
||||
del param_tools.registry._registry["user"]["core"]
|
||||
|
||||
def test_get_preferences(self):
|
||||
"""Test preferences display."""
|
||||
url = reverse("core:user_preferences")
|
||||
self.ajax_get(url)
|
||||
|
||||
def test_save_preferences(self):
|
||||
"""Test preferences save."""
|
||||
url = reverse("core:user_preferences")
|
||||
response = self.client.post(url, {"core-test": "toto"}, format="json")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.json(), "Preferences saved")
|
||||
|
||||
admin = models.User.objects.get(username="admin")
|
||||
self.assertEqual(admin.parameters.get_value("test"), "toto")
|
||||
@@ -17,10 +17,8 @@ urlpatterns = patterns(
|
||||
|
||||
url(r'^core/$', 'modoboa.core.views.admin.viewsettings',
|
||||
name="index"),
|
||||
url(r'^core/parameters/$', 'modoboa.core.views.admin.viewparameters',
|
||||
name="parameter_listl"),
|
||||
url(r'^core/parameters/save/$', 'modoboa.core.views.admin.saveparameters',
|
||||
name="parameter_save"),
|
||||
url(r'^core/parameters/$', 'modoboa.core.views.admin.parameters',
|
||||
name="parameters"),
|
||||
url(r'^core/info/$', 'modoboa.core.views.admin.information',
|
||||
name="information"),
|
||||
url(r'^core/logs/$', 'modoboa.core.views.admin.logs', name="log_list"),
|
||||
|
||||
+20
-18
@@ -10,11 +10,12 @@ from django.utils.translation import ugettext as _
|
||||
|
||||
from modoboa.core.models import Log
|
||||
from modoboa.core.utils import check_for_updates
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.listing import get_sort_order, get_listing_page
|
||||
from modoboa.lib.web_utils import (
|
||||
_render_to_string, render_to_json_response
|
||||
)
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
|
||||
@login_required
|
||||
@@ -27,30 +28,31 @@ def viewsettings(request, tplname='core/settings_header.html'):
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def viewparameters(request, tplname='core/parameters.html'):
|
||||
def parameters(request, tplname="core/parameters.html"):
|
||||
"""View to display and save global parameters."""
|
||||
if request.method == "POST":
|
||||
forms = param_tools.registry.get_forms(
|
||||
"global", request.POST, localconfig=request.localconfig)
|
||||
for formdef in forms:
|
||||
form = formdef["form"]
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
form.to_django_settings()
|
||||
continue
|
||||
return render_to_json_response(
|
||||
{"form_errors": form.errors, "prefix": form.app}, status=400
|
||||
)
|
||||
request.localconfig.save()
|
||||
return render_to_json_response(_("Parameters saved"))
|
||||
return render_to_json_response({
|
||||
"left_selection": "parameters",
|
||||
"content": _render_to_string(request, tplname, {
|
||||
"forms": parameters.get_admin_forms
|
||||
"forms": param_tools.registry.get_forms(
|
||||
"global", localconfig=request.localconfig)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def saveparameters(request):
|
||||
for formdef in parameters.get_admin_forms(request.POST):
|
||||
form = formdef["form"]
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
form.to_django_settings()
|
||||
continue
|
||||
return render_to_json_response(
|
||||
{'form_errors': form.errors, 'prefix': form.app}, status=400
|
||||
)
|
||||
return render_to_json_response(_("Parameters saved"))
|
||||
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def information(request, tplname="core/information.html"):
|
||||
|
||||
@@ -5,8 +5,6 @@ from django.views import generic
|
||||
|
||||
from django.contrib.auth import mixins as auth_mixins
|
||||
|
||||
from modoboa.lib import parameters
|
||||
|
||||
from ..extensions import exts_pool
|
||||
|
||||
|
||||
@@ -18,7 +16,8 @@ def find_nextlocation(request, user):
|
||||
nextlocation = request.POST.get("next", None)
|
||||
if nextlocation is None or nextlocation == "None":
|
||||
if request.user.role == "SimpleUsers":
|
||||
topredir = parameters.get_admin("DEFAULT_TOP_REDIRECTION")
|
||||
topredir = request.localconfig.parameters.get_value(
|
||||
"default_top_redirection")
|
||||
if topredir != "user":
|
||||
infos = exts_pool.get_extension_infos(topredir)
|
||||
nextlocation = infos["url"] if infos["url"] else infos["name"]
|
||||
|
||||
@@ -8,11 +8,13 @@ from django.contrib.auth.decorators import login_required, user_passes_test
|
||||
|
||||
from rest_framework.authtoken.models import Token
|
||||
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.cryptutils import encrypt
|
||||
from modoboa.lib.web_utils import (
|
||||
_render_to_string, render_to_json_response
|
||||
)
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from ..forms import ProfileForm, APIAccessForm
|
||||
|
||||
|
||||
@@ -59,7 +61,9 @@ def profile(request, tplname='core/user_profile.html'):
|
||||
@login_required
|
||||
def preferences(request):
|
||||
if request.method == "POST":
|
||||
for formdef in parameters.get_user_forms(request.user, request.POST):
|
||||
forms = param_tools.registry.get_forms(
|
||||
"user", request.POST, user=request.user)
|
||||
for formdef in forms:
|
||||
form = formdef["form"]
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
@@ -67,12 +71,13 @@ def preferences(request):
|
||||
return render_to_json_response({
|
||||
"prefix": form.app, "form_errors": form.errors
|
||||
}, status=400)
|
||||
|
||||
request.user.save()
|
||||
return render_to_json_response(_("Preferences saved"))
|
||||
|
||||
return render_to_json_response({
|
||||
"content": _render_to_string(request, "core/user_preferences.html", {
|
||||
"forms": parameters.get_user_forms(request.user)
|
||||
"forms": param_tools.registry.get_forms(
|
||||
"user", user=request.user, first_app="general")
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.email_utils import split_mailbox
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
|
||||
class SimpleBackend(ModelBackend):
|
||||
|
||||
"""Simple authentication backend."""
|
||||
|
||||
def authenticate(self, username=None, password=None):
|
||||
@@ -36,8 +35,10 @@ try:
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Load LDAP settings."""
|
||||
parameters.apply_to_django_settings()
|
||||
param_tools.apply_to_django_settings()
|
||||
super(LDAPBackend, self).__init__(*args, **kwargs)
|
||||
self.global_params = dict(
|
||||
param_tools.get_global_parameters("core"))
|
||||
|
||||
def get_or_create_user(self, username, ldap_user):
|
||||
"""
|
||||
@@ -46,12 +47,11 @@ try:
|
||||
the user. ldap_user.dn is the user's DN and
|
||||
ldap_user.attrs contains all of their LDAP attributes.
|
||||
"""
|
||||
group = 'SimpleUsers'
|
||||
admin_groups = parameters \
|
||||
.get_admin('LDAP_ADMIN_GROUPS', app='core').split(';')
|
||||
group = "SimpleUsers"
|
||||
admin_groups = self.global_params["ldap_admin_groups"].split(";")
|
||||
for grp in admin_groups:
|
||||
if grp.strip() in ldap_user.group_names:
|
||||
group = 'DomainAdmins'
|
||||
group = "DomainAdmins"
|
||||
break
|
||||
if group == 'SimpleUsers':
|
||||
lpart, domain = split_mailbox(username)
|
||||
@@ -75,8 +75,7 @@ try:
|
||||
return user
|
||||
|
||||
def authenticate(self, username, password):
|
||||
auth_type = parameters.get_admin("AUTHENTICATION_TYPE", app="core")
|
||||
if auth_type == "ldap":
|
||||
if self.global_params["authentication_type"] == "ldap":
|
||||
return super(LDAPBackend, self).authenticate(
|
||||
username, password)
|
||||
return None
|
||||
|
||||
@@ -7,7 +7,7 @@ import string
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
|
||||
def random_key(l=16):
|
||||
@@ -25,7 +25,7 @@ def random_key(l=16):
|
||||
|
||||
|
||||
def encrypt(clear):
|
||||
key = parameters.get_admin("SECRET_KEY", app="core")
|
||||
key = param_tools.get_global_parameter("secret_key", app="core")
|
||||
obj = AES.new(key, AES.MODE_ECB)
|
||||
if type(clear) is unicode:
|
||||
clear = clear.encode("utf-8")
|
||||
@@ -38,7 +38,8 @@ def encrypt(clear):
|
||||
|
||||
def decrypt(ciph):
|
||||
obj = AES.new(
|
||||
parameters.get_admin("SECRET_KEY", app="core"), AES.MODE_ECB
|
||||
param_tools.get_global_parameter(
|
||||
"secret_key", app="core"), AES.MODE_ECB
|
||||
)
|
||||
ciph = base64.b64decode(ciph)
|
||||
clear = obj.decrypt(ciph)
|
||||
|
||||
+27
-13
@@ -6,7 +6,7 @@ import abc
|
||||
from collections import OrderedDict
|
||||
import re
|
||||
|
||||
from django.forms import ChoiceField
|
||||
from django.forms import TypedChoiceField
|
||||
from django.forms.fields import Field
|
||||
from django.forms.widgets import RadioSelect
|
||||
from django.forms.widgets import RadioChoiceInput
|
||||
@@ -232,9 +232,8 @@ class DynamicForm(object):
|
||||
|
||||
|
||||
class TabForms(object):
|
||||
|
||||
"""
|
||||
Simple forms container
|
||||
Simple forms container.
|
||||
|
||||
This class tries to encapsulate multiple forms that will be
|
||||
displayed using tabs. It is different from a classical formset
|
||||
@@ -371,19 +370,23 @@ class TabForms(object):
|
||||
|
||||
|
||||
class CustomRadioInput(RadioChoiceInput):
|
||||
"""Custom radio input."""
|
||||
|
||||
def __unicode__(self):
|
||||
if 'id' in self.attrs:
|
||||
label_for = ' for="%s"' % self.attrs['id']
|
||||
if "id" in self.attrs:
|
||||
label_for = ' for="%s"' % self.attrs["id"]
|
||||
else:
|
||||
label_for = ''
|
||||
label_for = ""
|
||||
choice_label = conditional_escape(force_unicode(self.choice_label))
|
||||
return mark_safe(
|
||||
u'<label class="radio-inline" %s>%s %s</label>'
|
||||
u"<label class='radio-inline' %s>%s %s</label>"
|
||||
% (label_for, self.tag(), choice_label)
|
||||
)
|
||||
|
||||
|
||||
class InlineRadioRenderer(RadioSelect.renderer):
|
||||
"""Custom inline radio renderer."""
|
||||
|
||||
def __iter__(self):
|
||||
for i, choice in enumerate(self.choices):
|
||||
yield CustomRadioInput(
|
||||
@@ -392,24 +395,35 @@ class InlineRadioRenderer(RadioSelect.renderer):
|
||||
|
||||
def render(self):
|
||||
return mark_safe(
|
||||
u'\n'.join([u'%s\n' % force_unicode(w) for w in self])
|
||||
u"\n".join([u"%s\n" % force_unicode(w) for w in self])
|
||||
)
|
||||
|
||||
|
||||
class InlineRadioSelect(RadioSelect):
|
||||
"""Custom inline radio widget."""
|
||||
|
||||
renderer = InlineRadioRenderer
|
||||
|
||||
|
||||
class SeparatorField(Field):
|
||||
"""Custom field to represent a separator."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs["required"] = False
|
||||
super(SeparatorField, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class YesNoField(ChoiceField):
|
||||
class YesNoField(TypedChoiceField):
|
||||
"""A yes/no form field."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs["choices"] = [
|
||||
("yes", ugettext_lazy("Yes")), ("no", ugettext_lazy("No"))
|
||||
]
|
||||
kwargs["widget"] = InlineRadioSelect
|
||||
"""Constructor."""
|
||||
kwargs.update({
|
||||
"choices": (
|
||||
(True, ugettext_lazy("Yes")),
|
||||
(False, ugettext_lazy("No"))
|
||||
),
|
||||
"widget": InlineRadioSelect,
|
||||
"coerce": lambda x: x == "True"
|
||||
})
|
||||
super(YesNoField, self).__init__(*args, **kwargs)
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# coding: utf-8
|
||||
|
||||
"""
|
||||
:mod:`ldaputils` --- a collection of LDAP based classes/functions
|
||||
-----------------------------------------------------------------
|
||||
A collection of LDAP based classes/functions.
|
||||
|
||||
For a first version, the LDAP support offered by Modoboa only supports
|
||||
one global server definition : the one the django-auth-ldap uses.
|
||||
@@ -39,14 +38,16 @@ from django.conf import settings
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from modoboa.core.password_hashers import get_password_hasher
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.exceptions import InternalError
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
|
||||
class LDAPAuthBackend(object):
|
||||
"""LDAP authentication backend."""
|
||||
|
||||
def __init__(self):
|
||||
parameters.apply_to_django_settings()
|
||||
param_tools.apply_to_django_settings()
|
||||
self.global_params = dict(param_tools.get_global_parameters("core"))
|
||||
self.server_uri = self._setting(
|
||||
"AUTH_LDAP_SERVER_URI", "ldap://localhost"
|
||||
)
|
||||
@@ -74,7 +75,7 @@ class LDAPAuthBackend(object):
|
||||
"""Connect to the server according to configuration."""
|
||||
if self.conn is not None:
|
||||
return
|
||||
mode = parameters.get_admin("LDAP_AUTH_METHOD", app="core")
|
||||
mode = self.global_params["ldap_auth_method"]
|
||||
if mode == "searchbind":
|
||||
bind_dn = self._setting("AUTH_LDAP_BIND_DN", "")
|
||||
bind_pwd = self._setting("AUTH_LDAP_BIND_PASSWORD", "")
|
||||
@@ -86,8 +87,8 @@ class LDAPAuthBackend(object):
|
||||
|
||||
def _find_user_dn(self, user):
|
||||
"""Find the DN of the given user."""
|
||||
sbase = parameters.get_admin("LDAP_SEARCH_BASE", app="core")
|
||||
sfilter = parameters.get_admin("LDAP_SEARCH_FILTER", app="core")
|
||||
sbase = self.global_params["ldap_search_base"]
|
||||
sfilter = self.global_params["ldap_search_filter"]
|
||||
sfilter = sfilter % {"user": user}
|
||||
res = self.conn.search_s(sbase, ldap.SCOPE_SUBTREE, sfilter)
|
||||
try:
|
||||
@@ -102,8 +103,8 @@ class LDAPAuthBackend(object):
|
||||
:param clearpassword: the clear password
|
||||
:return: the encrypted password
|
||||
"""
|
||||
scheme = parameters.get_admin("PASSWORD_SCHEME", app="core")
|
||||
hasher = get_password_hasher(scheme.upper())('ldap')
|
||||
scheme = self.global_params["password_scheme"]
|
||||
hasher = get_password_hasher(scheme.upper())("ldap")
|
||||
return hasher.encrypt(clearpassword)
|
||||
|
||||
def update_user_password(self, user, password, newpassword):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
|
||||
def get_sort_order(qdict, default, allowed_values=None):
|
||||
@@ -34,7 +34,8 @@ def get_listing_page(objects, pagenum):
|
||||
:return: a ``Page`` object
|
||||
"""
|
||||
paginator = Paginator(
|
||||
objects, int(parameters.get_admin("ITEMS_PER_PAGE", app="core")),
|
||||
objects,
|
||||
param_tools.get_global_parameter("items_per_page", app="core"),
|
||||
allow_empty_first_page=False
|
||||
)
|
||||
try:
|
||||
|
||||
@@ -1,371 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
"""
|
||||
This interface provides a simple way to declare and store parameters
|
||||
in Modoboa's database.
|
||||
|
||||
Core components or extensions can register their own parameters, which
|
||||
will be available and modifiable directly from the web interface.
|
||||
|
||||
Only super users will be able to access this part of the web interface.
|
||||
"""
|
||||
|
||||
from django import forms
|
||||
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.exceptions import ModoboaException
|
||||
from modoboa.lib.sysutils import guess_extension_name
|
||||
|
||||
from . import db_utils
|
||||
|
||||
|
||||
_params = {'A': {}, 'U': {}}
|
||||
|
||||
|
||||
class NotDefined(ModoboaException):
|
||||
http_code = 404
|
||||
|
||||
def __init__(self, app, name):
|
||||
self.app = app
|
||||
self.name = name
|
||||
|
||||
def __str__(self):
|
||||
return "Application '%s' and/or parameter '%s' not defined" \
|
||||
% (self.app, self.name)
|
||||
|
||||
|
||||
class GenericParametersForm(forms.Form):
|
||||
"""Base class for parameter forms.
|
||||
|
||||
Each extension has the possibility to define global parameters.
|
||||
"""
|
||||
app = None
|
||||
visibility_rules = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if self.app is None:
|
||||
raise NotImplementedError
|
||||
|
||||
kwargs["prefix"] = self.app
|
||||
super(GenericParametersForm, self).__init__(*args, **kwargs)
|
||||
|
||||
self.visirules = {}
|
||||
if self.visibility_rules is not None:
|
||||
for key, rule in self.visibility_rules.items():
|
||||
field, value = rule.split("=")
|
||||
visibility = {
|
||||
"field": "id_%s-%s" % (self.app, field), "value": value
|
||||
}
|
||||
self.visirules["%s-%s" % (self.app, key)] = visibility
|
||||
|
||||
if not args:
|
||||
self._load_initial_values()
|
||||
|
||||
def _load_initial_values(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def _decode_value(self, value):
|
||||
return value.decode('unicode_escape').replace('\\r\\n', '\n')
|
||||
|
||||
def _load_extra_parameters(self, level):
|
||||
params = events.raiseDictEvent('GetExtraParameters', self.app, level)
|
||||
for pname, pdef in params.items():
|
||||
self.fields[pname] = pdef
|
||||
|
||||
def _save_parameter(self, p, name, value):
|
||||
if p.value == value:
|
||||
return
|
||||
name = name.lower()
|
||||
if hasattr(self, "update_%s" % name):
|
||||
getattr(self, "update_%s" % name)(value)
|
||||
if type(value) is unicode:
|
||||
p.value = value.encode("unicode_escape").strip()
|
||||
else:
|
||||
p.value = str(value)
|
||||
p.save()
|
||||
|
||||
def save(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class AdminParametersForm(GenericParametersForm):
|
||||
|
||||
def _load_initial_values(self):
|
||||
from .models import Parameter
|
||||
|
||||
if not db_utils.db_table_exists("lib_parameter"):
|
||||
return
|
||||
names = [
|
||||
"%s.%s" % (self.app, name.upper()) for name in self.fields.keys()
|
||||
]
|
||||
for p in Parameter.objects.filter(name__in=names):
|
||||
self.fields[p.shortname].initial = self._decode_value(p.value)
|
||||
|
||||
def save(self):
|
||||
from .models import Parameter
|
||||
from modoboa.lib.form_utils import SeparatorField
|
||||
|
||||
for name, value in self.cleaned_data.items():
|
||||
if type(self.fields[name]) is SeparatorField:
|
||||
continue
|
||||
fullname = "%s.%s" % (self.app, name.upper())
|
||||
try:
|
||||
p = Parameter.objects.get(name=fullname)
|
||||
except Parameter.DoesNotExist:
|
||||
p = Parameter()
|
||||
p.name = fullname
|
||||
self._save_parameter(p, name, value)
|
||||
|
||||
def to_django_settings(self):
|
||||
pass
|
||||
|
||||
def get_current_values(self):
|
||||
values = {}
|
||||
for key in self.fields.keys():
|
||||
try:
|
||||
values[key] = get_admin(key.upper(), app=self.app)
|
||||
except NotDefined:
|
||||
pass
|
||||
return values
|
||||
|
||||
|
||||
class UserParametersForm(GenericParametersForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop("user") if "user" in kwargs else None
|
||||
super(UserParametersForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def _load_initial_values(self):
|
||||
if not db_utils.db_table_exists("lib_userparameter"):
|
||||
return
|
||||
if self.user is None:
|
||||
return
|
||||
from .models import UserParameter
|
||||
|
||||
names = [
|
||||
"%s.%s" % (self.app, name.upper()) for name in self.fields.keys()
|
||||
]
|
||||
for p in UserParameter.objects.filter(user=self.user, name__in=names):
|
||||
self.fields[p.shortname].initial = self._decode_value(p.value)
|
||||
|
||||
@staticmethod
|
||||
def has_access(user):
|
||||
return True
|
||||
|
||||
def save(self):
|
||||
from .models import UserParameter
|
||||
from modoboa.lib.form_utils import SeparatorField
|
||||
|
||||
for name, value in self.cleaned_data.items():
|
||||
if type(self.fields[name]) is SeparatorField:
|
||||
continue
|
||||
fullname = "%s.%s" % (self.app, name.upper())
|
||||
try:
|
||||
p = UserParameter.objects.get(user=self.user, name=fullname)
|
||||
except UserParameter.DoesNotExist:
|
||||
p = UserParameter()
|
||||
p.user = self.user
|
||||
p.name = fullname
|
||||
self._save_parameter(p, name, value)
|
||||
|
||||
|
||||
def register(formclass, label):
|
||||
"""Register a form class containing parameters
|
||||
|
||||
formclass must inherit from ``AdminParametersForm`` or
|
||||
``UserParametersForm``.
|
||||
|
||||
:param formclass: a form class
|
||||
:param string label: the label to display in parameters or settings pages
|
||||
"""
|
||||
from modoboa.lib.form_utils import SeparatorField
|
||||
|
||||
if issubclass(formclass, AdminParametersForm):
|
||||
level = 'A'
|
||||
elif issubclass(formclass, UserParametersForm):
|
||||
level = 'U'
|
||||
else:
|
||||
raise RuntimeError("Unknown parameter class")
|
||||
_params[level][formclass.app] = {
|
||||
"label": label, "form": formclass, "defaults": {}
|
||||
}
|
||||
form = formclass()
|
||||
for name, field in form.fields.items():
|
||||
if type(field) is SeparatorField:
|
||||
continue
|
||||
_params[level][formclass.app]["defaults"][name.upper()] = field.initial
|
||||
|
||||
|
||||
def unregister(app=None):
|
||||
"""Unregister an application
|
||||
|
||||
All parameters associated to this application will also be
|
||||
removed.
|
||||
|
||||
:param app: the application's name (string)
|
||||
"""
|
||||
if app is None:
|
||||
app = guess_extension_name()
|
||||
for lvlparams in _params.values():
|
||||
if app in lvlparams:
|
||||
del lvlparams[app]
|
||||
|
||||
|
||||
def __is_defined(app, level, name):
|
||||
not_defined = (
|
||||
level not in ['A', 'U'] or
|
||||
app not in _params[level] or
|
||||
name not in _params[level][app]["defaults"]
|
||||
)
|
||||
if not_defined:
|
||||
raise NotDefined(app, name)
|
||||
|
||||
|
||||
def save_admin(name, value, app=None):
|
||||
"""Set a new value for a given parameter."""
|
||||
from .models import Parameter
|
||||
|
||||
if app is None:
|
||||
app = guess_extension_name()
|
||||
__is_defined(app, 'A', name)
|
||||
fullname = "%s.%s" % (app, name)
|
||||
try:
|
||||
p = Parameter.objects.get(name=fullname)
|
||||
except Parameter.DoesNotExist:
|
||||
p = Parameter()
|
||||
p.name = fullname
|
||||
p.value = None
|
||||
f = get_parameter_form('A', name, app)
|
||||
f()._save_parameter(p, name, value)
|
||||
|
||||
|
||||
def save_user(user, name, value, app=None):
|
||||
from .models import UserParameter
|
||||
|
||||
if app is None:
|
||||
app = guess_extension_name()
|
||||
__is_defined(app, 'U', name)
|
||||
fullname = "%s.%s" % (app, name)
|
||||
try:
|
||||
p = UserParameter.objects.get(user=user, name=fullname)
|
||||
except UserParameter.DoesNotExist:
|
||||
p = UserParameter()
|
||||
p.user = user
|
||||
p.name = fullname
|
||||
f = get_parameter_form('U', name, app)
|
||||
f()._save_parameter(p, name, value)
|
||||
|
||||
|
||||
def get_admin(name, app=None, raise_error=True):
|
||||
"""Return an administrative parameter
|
||||
|
||||
A ``NotDefined`` exception if the parameter doesn't exist.
|
||||
|
||||
:param name: the parameter's name
|
||||
:param app: the application owning the parameter
|
||||
:return: the corresponding value as a string
|
||||
"""
|
||||
from .models import Parameter
|
||||
|
||||
if app is None:
|
||||
app = guess_extension_name()
|
||||
try:
|
||||
__is_defined(app, "A", name)
|
||||
except NotDefined:
|
||||
if raise_error:
|
||||
raise
|
||||
return None
|
||||
try:
|
||||
p = Parameter.objects.get(name="%s.%s" % (app, name))
|
||||
except Parameter.DoesNotExist:
|
||||
return _params["A"][app]["defaults"][name]
|
||||
return p.value.decode("unicode_escape").replace('\\r\\n', '\n')
|
||||
|
||||
|
||||
def get_user(user, name, app=None, raise_error=True):
|
||||
"""Return a parameter for a specific user
|
||||
|
||||
A ``NotDefined`` exception if the parameter doesn't exist.
|
||||
|
||||
:param ``User`` user: the desired user
|
||||
:param name: the parameter's name
|
||||
:param app: the application owning the parameter
|
||||
:return: the corresponding value as a string
|
||||
"""
|
||||
from .models import UserParameter
|
||||
|
||||
if app is None:
|
||||
app = guess_extension_name()
|
||||
try:
|
||||
__is_defined(app, "U", name)
|
||||
except NotDefined:
|
||||
if raise_error:
|
||||
raise
|
||||
return None
|
||||
try:
|
||||
p = UserParameter.objects.get(user=user, name="%s.%s" % (app, name))
|
||||
except UserParameter.DoesNotExist:
|
||||
return _params["U"][app]["defaults"][name]
|
||||
return p.value.decode("unicode_escape")
|
||||
|
||||
|
||||
def get_sorted_apps(level, first="core"):
|
||||
"""Retrieve the sorted list of all registerd applications.
|
||||
|
||||
:param str level: application level
|
||||
:param str first: force the first item of the result
|
||||
:rtype: list
|
||||
"""
|
||||
sorted_apps = []
|
||||
if first in _params[level]:
|
||||
sorted_apps.append(first)
|
||||
sorted_apps += sorted(
|
||||
[app for app in _params[level].keys() if app != first],
|
||||
key=lambda app: _params[level][app]["label"]
|
||||
)
|
||||
return sorted_apps
|
||||
|
||||
|
||||
def get_admin_forms(*args, **kwargs):
|
||||
"""Get all admin level forms.
|
||||
|
||||
Generates an instance of each declared form.
|
||||
"""
|
||||
for app in get_sorted_apps('A'):
|
||||
formdef = _params['A'][app]
|
||||
yield {"label": formdef["label"],
|
||||
"form": formdef["form"](*args, **kwargs)}
|
||||
|
||||
|
||||
def get_user_forms(user, *args, **kwargs):
|
||||
"""Return an instance of each user-level forms."""
|
||||
kwargs["user"] = user
|
||||
sorted_apps = get_sorted_apps('U', first="general")
|
||||
result = []
|
||||
for app in sorted_apps:
|
||||
formdef = _params["U"][app]
|
||||
if not formdef["form"].has_access(user):
|
||||
continue
|
||||
result.append({
|
||||
"label": formdef["label"],
|
||||
"form": formdef["form"](*args, **kwargs)})
|
||||
return result
|
||||
|
||||
|
||||
def get_parameter_form(level, name, app=None):
|
||||
"""Return the form containing a specific parameter
|
||||
|
||||
:param string level: associated level
|
||||
:param string name: paremeter's name
|
||||
:param string app: parameter's application
|
||||
:return: a form class
|
||||
"""
|
||||
if app is None:
|
||||
app = guess_extension_name()
|
||||
__is_defined(app, level, name)
|
||||
return _params[level][app]["form"]
|
||||
|
||||
|
||||
def apply_to_django_settings():
|
||||
for form in get_admin_forms():
|
||||
form["form"].to_django_settings()
|
||||
+30
-78
@@ -3,16 +3,16 @@
|
||||
|
||||
import json
|
||||
|
||||
from django import forms
|
||||
from django.core import management
|
||||
from django.test import TestCase
|
||||
|
||||
from rest_framework.authtoken.models import Token
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.core import models as core_models
|
||||
|
||||
from . import sysutils
|
||||
|
||||
try:
|
||||
import ldap # NOQA
|
||||
NO_LDAP = False
|
||||
@@ -20,16 +20,41 @@ except ImportError:
|
||||
NO_LDAP = True
|
||||
|
||||
|
||||
class ModoTestCase(TestCase):
|
||||
class ParametersMixin(object):
|
||||
"""Add tools to manage parameters."""
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Set LocalConfig instance."""
|
||||
super(ParametersMixin, cls).setUpTestData()
|
||||
cls.localconfig = core_models.LocalConfig.objects.first()
|
||||
|
||||
def set_global_parameter(self, name, value, app=None):
|
||||
"""Set global parameter for the given app."""
|
||||
if app is None:
|
||||
app = sysutils.guess_extension_name()
|
||||
self.localconfig.parameters.set_value(name, value, app=app)
|
||||
self.localconfig.save()
|
||||
|
||||
def set_global_parameters(self, parameters, app=None):
|
||||
"""Set/update global parameters for the given app."""
|
||||
if app is None:
|
||||
app = sysutils.guess_extension_name()
|
||||
self.localconfig.parameters.set_values(parameters, app=app)
|
||||
self.localconfig.save()
|
||||
|
||||
|
||||
class ModoTestCase(ParametersMixin, TestCase):
|
||||
"""All test cases must inherit from this one."""
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Create a default user."""
|
||||
super(ModoTestCase, cls).setUpTestData()
|
||||
management.call_command("load_initial_data")
|
||||
|
||||
def setUp(self, username="admin", password="password"):
|
||||
"""Initiate test context."""
|
||||
self.assertEqual(
|
||||
self.client.login(username=username, password=password), True)
|
||||
|
||||
@@ -54,13 +79,13 @@ class ModoTestCase(TestCase):
|
||||
return self.ajax_request('get', *args, **kwargs)
|
||||
|
||||
|
||||
class ModoAPITestCase(APITestCase):
|
||||
|
||||
class ModoAPITestCase(ParametersMixin, APITestCase):
|
||||
"""All test cases must inherit from this one."""
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Create a default user."""
|
||||
super(ModoAPITestCase, cls).setUpTestData()
|
||||
management.call_command("load_initial_data")
|
||||
cls.token = Token.objects.create(
|
||||
user=core_models.User.objects.get(username="admin"))
|
||||
@@ -69,76 +94,3 @@ class ModoAPITestCase(APITestCase):
|
||||
"""Setup."""
|
||||
super(ModoAPITestCase, self).setUp()
|
||||
self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key)
|
||||
|
||||
|
||||
class TestParams(parameters.AdminParametersForm):
|
||||
app = "test"
|
||||
|
||||
param1 = forms.CharField(label="Test", initial="toto")
|
||||
param2 = forms.IntegerField(label="Int", initial=42)
|
||||
|
||||
def update_param2(self, value):
|
||||
raise RuntimeError("new value %d" % value)
|
||||
|
||||
|
||||
class TestUserParams(parameters.UserParametersForm):
|
||||
app = "test"
|
||||
|
||||
param1 = forms.CharField(label="Test", initial="titi")
|
||||
param2 = forms.IntegerField(label="Int", initial=42)
|
||||
|
||||
def update_param2(self, value):
|
||||
raise RuntimeError("new value %d" % value)
|
||||
|
||||
|
||||
class ParameterTestCase(TestCase):
|
||||
"""Simple test cases for ``modoboa.lib.parameters`` module.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
super(ParameterTestCase, cls).setUpTestData()
|
||||
cls.user = core_models.User.objects.create(username="tester")
|
||||
|
||||
def setUp(self):
|
||||
"""Initialize tests."""
|
||||
super(ParameterTestCase, self).setUp()
|
||||
parameters.register(TestParams, "Test")
|
||||
parameters.register(TestUserParams, "TestUser")
|
||||
|
||||
def test_register_form(self):
|
||||
self.assertIn("test", parameters._params['A'])
|
||||
|
||||
def test_unregister(self):
|
||||
parameters.unregister("test")
|
||||
self.assertNotIn("test", parameters._params['A'])
|
||||
|
||||
def test_get_parameter_form(self):
|
||||
self.assertIs(
|
||||
parameters.get_parameter_form('A', 'PARAM1', 'test'),
|
||||
TestParams
|
||||
)
|
||||
|
||||
def test_save_admin(self):
|
||||
parameters.save_admin("PARAM1", "45", app="test")
|
||||
self.assertEqual(parameters.get_admin("PARAM1", app="test"), "45")
|
||||
|
||||
def test_update_param(self):
|
||||
with self.assertRaisesRegexp(RuntimeError, "new value 35"):
|
||||
parameters.save_admin("PARAM2", 35, app="test")
|
||||
|
||||
def test_get_admin(self):
|
||||
self.assertEqual(parameters.get_admin("PARAM1", "test"), "toto")
|
||||
with self.assertRaises(parameters.NotDefined):
|
||||
parameters.get_admin("TOTO", "test")
|
||||
|
||||
def test_get_user(self):
|
||||
self.assertEqual(
|
||||
parameters.get_user(self.user, "PARAM1", "test"), "titi")
|
||||
with self.assertRaises(parameters.NotDefined):
|
||||
parameters.get_user(self.user, "TOTO", "test")
|
||||
|
||||
def test_save_user(self):
|
||||
parameters.save_user(self.user, "PARAM1", "pouet", "test")
|
||||
self.assertEqual(
|
||||
parameters.get_user(self.user, "PARAM1", "test"), "pouet")
|
||||
|
||||
@@ -4,10 +4,10 @@ from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from modoboa.lib.form_utils import SeparatorField, YesNoField
|
||||
from modoboa.lib.parameters import AdminParametersForm
|
||||
from modoboa.parameters import forms as param_forms
|
||||
|
||||
|
||||
class ParametersForm(AdminParametersForm):
|
||||
class ParametersForm(param_forms.AdminParametersForm):
|
||||
"""Available admin parameters."""
|
||||
|
||||
app = "limits"
|
||||
@@ -16,7 +16,7 @@ class ParametersForm(AdminParametersForm):
|
||||
|
||||
enable_admin_limits = YesNoField(
|
||||
label=_("Enable per-admin limits"),
|
||||
initial="yes",
|
||||
initial=True,
|
||||
help_text=_("Enable or disable per-admin limits")
|
||||
)
|
||||
|
||||
@@ -77,7 +77,7 @@ class ParametersForm(AdminParametersForm):
|
||||
|
||||
enable_domain_limits = YesNoField(
|
||||
label=_("Enable per-domain limits"),
|
||||
initial="no",
|
||||
initial=False,
|
||||
help_text=_("Enable or disable per-domain limits")
|
||||
)
|
||||
|
||||
@@ -125,17 +125,13 @@ class ParametersForm(AdminParametersForm):
|
||||
)
|
||||
|
||||
visibility_rules = {
|
||||
"deflt_user_domains_limit": "enable_admin_limits=yes",
|
||||
"deflt_user_domain_aliases_limit": "enable_admin_limits=yes",
|
||||
"deflt_user_mailboxes_limit": "enable_admin_limits=yes",
|
||||
"deflt_user_mailbox_aliases_limit": "enable_admin_limits=yes",
|
||||
"deflt_user_domain_admins_limit": "enable_admin_limits=yes",
|
||||
"deflt_domain_mailboxes_limit": "enable_domain_limits=yes",
|
||||
"deflt_domain_mailbox_aliases_limit": "enable_domain_limits=yes",
|
||||
"deflt_domain_domain_aliases_limit": "enable_domain_limits=yes",
|
||||
"deflt_domain_domain_admins_limit": "enable_domain_limits=yes",
|
||||
"deflt_user_domains_limit": "enable_admin_limits=True",
|
||||
"deflt_user_domain_aliases_limit": "enable_admin_limits=True",
|
||||
"deflt_user_mailboxes_limit": "enable_admin_limits=True",
|
||||
"deflt_user_mailbox_aliases_limit": "enable_admin_limits=True",
|
||||
"deflt_user_domain_admins_limit": "enable_admin_limits=True",
|
||||
"deflt_domain_mailboxes_limit": "enable_domain_limits=True",
|
||||
"deflt_domain_mailbox_aliases_limit": "enable_domain_limits=True",
|
||||
"deflt_domain_domain_aliases_limit": "enable_domain_limits=True",
|
||||
"deflt_domain_domain_admins_limit": "enable_domain_limits=True",
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AdminParametersForm, self).__init__(*args, **kwargs)
|
||||
self._load_extra_parameters('A')
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
"""App config for limits."""
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
EVENTS = [
|
||||
'GetExtraLimitTemplates'
|
||||
"GetExtraLimitTemplates"
|
||||
]
|
||||
|
||||
|
||||
def load_limits_settings():
|
||||
"""Load settings."""
|
||||
from django.utils.translation import ugettext as _
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.lib import events
|
||||
from modoboa.parameters import tools as param_tools
|
||||
from .app_settings import ParametersForm
|
||||
|
||||
parameters.register(ParametersForm, _("Limits"))
|
||||
param_tools.registry.add("global", ParametersForm, _("Limits"))
|
||||
events.declare(EVENTS)
|
||||
from . import controls
|
||||
from . import general_callbacks
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
:mod:`controls` --- provides event handlers that check if limits are reached
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
"""Event handlers that check if limits are reached."""
|
||||
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.permissions import get_object_owner
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from . import utils
|
||||
|
||||
|
||||
def move_pool_resource(owner, user):
|
||||
"""Move resource from one pool to another
|
||||
"""Move resource from one pool to another.
|
||||
|
||||
When an account doesn't need a pool anymore, we move the
|
||||
associated resource to the pool of its owner.
|
||||
@@ -40,7 +38,7 @@ def user_can_set_role(user, role, account=None):
|
||||
:param str newrole: role to check
|
||||
"""
|
||||
condition = (
|
||||
parameters.get_admin("ENABLE_ADMIN_LIMITS") == "no" or
|
||||
not param_tools.get_global_parameter("enable_admin_limits") or
|
||||
role != "DomainAdmins")
|
||||
if condition:
|
||||
return [True]
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.lib import events
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from . import forms
|
||||
from . import utils
|
||||
@@ -12,7 +13,7 @@ from . import utils
|
||||
@events.observe("ExtraAdminContent")
|
||||
def display_pool_usage(user, target, currentpage):
|
||||
condition = (
|
||||
parameters.get_admin("ENABLE_ADMIN_LIMITS") == "no" or
|
||||
not param_tools.get_global_parameter("enable_admin_limits") or
|
||||
target != "leftcol" or user.is_superuser)
|
||||
if condition:
|
||||
return []
|
||||
@@ -40,7 +41,7 @@ def display_pool_usage(user, target, currentpage):
|
||||
|
||||
@events.observe("ExtraAccountForm")
|
||||
def extra_account_form(user, account=None):
|
||||
if parameters.get_admin("ENABLE_ADMIN_LIMITS") == "no":
|
||||
if not param_tools.get_global_parameter("enable_admin_limits"):
|
||||
return []
|
||||
if user.role not in ["SuperAdmins", "Resellers"]:
|
||||
return []
|
||||
@@ -61,7 +62,7 @@ def extra_account_form(user, account=None):
|
||||
@events.observe("ExtraDomainForm")
|
||||
def extra_domain_form(user, domain):
|
||||
"""Include domain limits form."""
|
||||
if parameters.get_admin("ENABLE_DOMAIN_LIMITS") == "no":
|
||||
if not param_tools.get_global_parameter("enable_domain_limits"):
|
||||
return []
|
||||
if not user.has_perm("admin.change_domain"):
|
||||
return []
|
||||
@@ -74,7 +75,7 @@ def extra_domain_form(user, domain):
|
||||
@events.observe("FillDomainInstances")
|
||||
def fill_domain_instances(user, domain, instances):
|
||||
"""Set domain instance for resources form."""
|
||||
if parameters.get_admin("ENABLE_DOMAIN_LIMITS") == "no":
|
||||
if not param_tools.get_global_parameter("enable_domain_limits"):
|
||||
return
|
||||
if not user.has_perm("admin.change_domain"):
|
||||
return
|
||||
@@ -93,7 +94,7 @@ def check_form_access(account, form):
|
||||
@events.observe("FillAccountInstances")
|
||||
def fill_account_instances(user, account, instances):
|
||||
condition = (
|
||||
parameters.get_admin("ENABLE_ADMIN_LIMITS") == "no" or
|
||||
not param_tools.get_global_parameter("enable_admin_limits") or
|
||||
(not user.is_superuser and user.role != "Resellers")
|
||||
)
|
||||
if condition:
|
||||
@@ -106,7 +107,7 @@ def fill_account_instances(user, account, instances):
|
||||
@events.observe("GetStaticContent")
|
||||
def get_static_content(caller, st_type, user):
|
||||
condition = (
|
||||
parameters.get_admin("ENABLE_ADMIN_LIMITS") == "no" or
|
||||
not param_tools.get_global_parameter("enable_admin_limits") or
|
||||
caller not in ["domains", "identities"] or
|
||||
user.role in ["SuperAdmins", "SimpleUsers"]
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ from modoboa.admin import signals as admin_signals
|
||||
from modoboa.core import models as core_models
|
||||
from modoboa.core import signals as core_signals
|
||||
from modoboa.lib import signals as lib_signals
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from . import lib
|
||||
from . import models
|
||||
@@ -21,13 +21,13 @@ from . import utils
|
||||
def check_object_limit(sender, context, object_type, **kwargs):
|
||||
"""Check if user can create a new object."""
|
||||
if context.__class__.__name__ == "User":
|
||||
if parameters.get_admin("ENABLE_ADMIN_LIMITS") == "no":
|
||||
if not param_tools.get_global_parameter("enable_admin_limits"):
|
||||
return
|
||||
if context.is_superuser:
|
||||
return True
|
||||
limit = context.userobjectlimit_set.get(name=object_type)
|
||||
elif context.__class__.__name__ == "Domain":
|
||||
if parameters.get_admin("ENABLE_DOMAIN_LIMITS") == "no":
|
||||
if not param_tools.get_global_parameter("enable_domain_limits"):
|
||||
return
|
||||
limit = context.domainobjectlimit_set.get(name=object_type)
|
||||
else:
|
||||
@@ -44,15 +44,14 @@ def create_user_limits(sender, instance, **kwargs):
|
||||
return
|
||||
request = lib_signals.get_request()
|
||||
creator = request.user if request else None
|
||||
global_params = dict(param_tools.get_global_parameters("limits"))
|
||||
for name, definition in utils.get_user_limit_templates():
|
||||
ct = ContentType.objects.get_by_natural_key(
|
||||
*definition["content_type"].split("."))
|
||||
max_value = 0
|
||||
# creator can be None if user was created by a factory
|
||||
if not creator or creator.is_superuser:
|
||||
max_value = int(
|
||||
parameters.get_admin(
|
||||
"DEFLT_USER_{}_LIMIT".format(name.upper())))
|
||||
max_value = global_params["deflt_user_{0}_limit".format(name)]
|
||||
models.UserObjectLimit.objects.create(
|
||||
user=instance, name=name, content_type=ct, max_value=max_value)
|
||||
|
||||
@@ -62,9 +61,9 @@ def create_domain_limits(sender, instance, **kwargs):
|
||||
"""Create limits for new domain."""
|
||||
if not kwargs.get("created"):
|
||||
return
|
||||
global_params = dict(param_tools.get_global_parameters("limits"))
|
||||
for name, definition in utils.get_domain_limit_templates():
|
||||
max_value = int(
|
||||
parameters.get_admin("DEFLT_DOMAIN_{}_LIMIT".format(name.upper())))
|
||||
max_value = global_params["deflt_domain_{0}_limit".format(name)]
|
||||
models.DomainObjectLimit.objects.create(
|
||||
domain=instance, name=name, max_value=max_value)
|
||||
|
||||
@@ -72,7 +71,7 @@ def create_domain_limits(sender, instance, **kwargs):
|
||||
@receiver(admin_signals.extra_domain_dashboard_widgets)
|
||||
def display_domain_limits(sender, user, domain, **kwargs):
|
||||
"""Display resources usage for domain."""
|
||||
if parameters.get_admin("ENABLE_DOMAIN_LIMITS") == "no":
|
||||
if not param_tools.get_global_parameter("enable_domain_limits"):
|
||||
return []
|
||||
return [{
|
||||
"column": "right",
|
||||
@@ -87,7 +86,7 @@ def display_domain_limits(sender, user, domain, **kwargs):
|
||||
def display_admin_limits(sender, user, account, **kwargs):
|
||||
"""Display resources usage for admin."""
|
||||
condition = (
|
||||
parameters.get_admin("ENABLE_ADMIN_LIMITS") == "yes" and
|
||||
param_tools.get_global_parameter("enable_admin_limits") and
|
||||
account.role in ["DomainAdmins", "Resellers"]
|
||||
)
|
||||
if not condition:
|
||||
|
||||
@@ -9,7 +9,6 @@ from modoboa.admin.factories import populate_database
|
||||
from modoboa.admin.models import Domain
|
||||
from modoboa.core import factories as core_factories
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib import tests as lib_tests
|
||||
|
||||
from .. import utils
|
||||
@@ -23,8 +22,9 @@ class APIAdminLimitsTestCase(lib_tests.ModoAPITestCase):
|
||||
"""Create test data."""
|
||||
super(APIAdminLimitsTestCase, cls).setUpTestData()
|
||||
for name, tpl in utils.get_user_limit_templates():
|
||||
parameters.save_admin(
|
||||
"DEFLT_USER_{}_LIMIT".format(name.upper()), 2)
|
||||
cls.localconfig.parameters.set_value(
|
||||
"deflt_user_{0}_limit".format(name), 2)
|
||||
cls.localconfig.save()
|
||||
populate_database()
|
||||
cls.user = User.objects.get(username="admin@test.com")
|
||||
cls.da_token = Token.objects.create(user=cls.user)
|
||||
@@ -181,10 +181,12 @@ class APIDomainLimitsTestCase(lib_tests.ModoAPITestCase):
|
||||
def setUpTestData(cls):
|
||||
"""Create test data."""
|
||||
super(APIDomainLimitsTestCase, cls).setUpTestData()
|
||||
parameters.save_admin("ENABLE_DOMAIN_LIMITS", "yes")
|
||||
cls.localconfig.parameters.set_value(
|
||||
"enable_domain_limits", True)
|
||||
for name, tpl in utils.get_domain_limit_templates():
|
||||
parameters.save_admin(
|
||||
"DEFLT_DOMAIN_{}_LIMIT".format(name.upper()), 2)
|
||||
cls.localconfig.parameters.set_value(
|
||||
"deflt_domain_{0}_limit".format(name), 2)
|
||||
cls.localconfig.save()
|
||||
populate_database()
|
||||
|
||||
def test_mailboxes_limit(self):
|
||||
|
||||
@@ -7,7 +7,6 @@ from modoboa.admin import factories as admin_factories
|
||||
from modoboa.admin.models import Domain
|
||||
from modoboa.core import factories as core_factories
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib import tests as lib_tests
|
||||
|
||||
from .. import utils
|
||||
@@ -20,11 +19,14 @@ class DomainLimitsTestCase(lib_tests.ModoTestCase):
|
||||
def setUpTestData(cls):
|
||||
"""Create test data."""
|
||||
super(DomainLimitsTestCase, cls).setUpTestData()
|
||||
parameters.save_admin("ENABLE_ADMIN_LIMITS", "no")
|
||||
parameters.save_admin("ENABLE_DOMAIN_LIMITS", "yes")
|
||||
cls.localconfig.parameters.set_values({
|
||||
"enable_admin_limits": False,
|
||||
"enable_domain_limits": True
|
||||
})
|
||||
for name, tpl in utils.get_domain_limit_templates():
|
||||
parameters.save_admin(
|
||||
"DEFLT_DOMAIN_{}_LIMIT".format(name.upper()), 2)
|
||||
cls.localconfig.parameters.set_value(
|
||||
"deflt_domain_{0}_limit".format(name), 2)
|
||||
cls.localconfig.save()
|
||||
admin_factories.populate_database()
|
||||
|
||||
def test_set_limits(self):
|
||||
|
||||
@@ -6,7 +6,6 @@ from modoboa.admin import factories as admin_factories
|
||||
from modoboa.admin import models as admin_models
|
||||
from modoboa.core import factories as core_factories
|
||||
from modoboa.core import models as core_models
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
|
||||
from .. import utils
|
||||
@@ -135,9 +134,11 @@ class UserLimitImportTestCase(LimitImportTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Create test data."""
|
||||
localconfig = core_models.LocalConfig.objects.first()
|
||||
for name, tpl in utils.get_user_limit_templates():
|
||||
parameters.save_admin(
|
||||
"DEFLT_USER_{}_LIMIT".format(name.upper()), 2)
|
||||
localconfig.parameters.set_value(
|
||||
"deflt_user_{0}_limit".format(name), 2)
|
||||
localconfig.save()
|
||||
super(UserLimitImportTestCase, cls).setUpTestData()
|
||||
|
||||
def test_domains_import(self):
|
||||
@@ -192,11 +193,15 @@ class DomainLimitImportTestCase(LimitImportTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Create test data."""
|
||||
parameters.save_admin("ENABLE_DOMAIN_LIMITS", "yes")
|
||||
parameters.save_admin("ENABLE_ADMIN_LIMITS", "no")
|
||||
localconfig = core_models.LocalConfig.objects.first()
|
||||
localconfig.parameters.set_values({
|
||||
"enable_domain_limits": True,
|
||||
"enable_admin_limits": False
|
||||
})
|
||||
for name, tpl in utils.get_domain_limit_templates():
|
||||
parameters.save_admin(
|
||||
"DEFLT_DOMAIN_{}_LIMIT".format(name.upper()), 2)
|
||||
localconfig.parameters.set_value(
|
||||
"deflt_domain_{0}_limit".format(name), 2)
|
||||
localconfig.save()
|
||||
super(DomainLimitImportTestCase, cls).setUpTestData()
|
||||
mb = admin_factories.MailboxFactory(
|
||||
user__username="user@test4.com",
|
||||
|
||||
@@ -7,7 +7,6 @@ from modoboa.admin.factories import populate_database
|
||||
from modoboa.admin.models import Alias, Domain
|
||||
from modoboa.core.factories import UserFactory
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib import tests as lib_tests
|
||||
|
||||
from .. import utils
|
||||
@@ -48,13 +47,14 @@ class ResourceTestCase(lib_tests.ModoTestCase):
|
||||
def setUpTestData(cls):
|
||||
"""Custom setUpTestData method."""
|
||||
super(ResourceTestCase, cls).setUpTestData()
|
||||
parameters.save_admin("ENABLE_ADMIN_LIMITS", "yes")
|
||||
parameters.save_admin("ENABLE_DOMAIN_LIMITS", "no")
|
||||
cls.localconfig.parameters.set_values({
|
||||
"enable_admin_limits": True,
|
||||
"enable_domain_limits": False
|
||||
})
|
||||
for name, tpl in utils.get_user_limit_templates():
|
||||
parameters.save_admin(
|
||||
"DEFLT_USER_{0}_LIMIT".format(name.upper()), 2,
|
||||
app="limits"
|
||||
)
|
||||
cls.localconfig.parameters.set_value(
|
||||
"deflt_user_{0}_limit".format(name), 2)
|
||||
cls.localconfig.save()
|
||||
populate_database()
|
||||
|
||||
def _create_account(self, username, role='SimpleUsers', status=200):
|
||||
@@ -78,13 +78,15 @@ class ResourceTestCase(lib_tests.ModoTestCase):
|
||||
|
||||
def _create_domain(self, name, status=200, withtpl=False):
|
||||
values = {
|
||||
"name": name, "quota": 100, "create_dom_admin": "no",
|
||||
"create_aliases": "no", "stepid": 'step3', "type": "domain"
|
||||
"name": name, "quota": 100, "create_dom_admin": False,
|
||||
"create_aliases": False, "stepid": "step3", "type": "domain"
|
||||
}
|
||||
if withtpl:
|
||||
values['create_dom_admin'] = 'yes'
|
||||
values['dom_admin_username'] = 'admin'
|
||||
values['create_aliases'] = 'yes'
|
||||
values.update({
|
||||
"create_dom_admin": True,
|
||||
"dom_admin_username": "admin",
|
||||
"create_aliases": True
|
||||
})
|
||||
response = self.ajax_post(
|
||||
reverse("admin:domain_add"), values, status
|
||||
)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ParametersConfig(AppConfig):
|
||||
name = 'parameters'
|
||||
@@ -0,0 +1,107 @@
|
||||
"""Parameters forms."""
|
||||
|
||||
from django import forms
|
||||
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib import db_utils
|
||||
from modoboa.lib import form_utils
|
||||
|
||||
|
||||
class GenericParametersForm(forms.Form):
|
||||
"""Base class for parameter forms.
|
||||
|
||||
Each extension has the possibility to define global parameters.
|
||||
"""
|
||||
app = None
|
||||
visibility_rules = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Constructor."""
|
||||
if self.app is None:
|
||||
raise NotImplementedError
|
||||
|
||||
kwargs["prefix"] = self.app
|
||||
load_values_from_db = kwargs.pop("load_values_from_db", True)
|
||||
super(GenericParametersForm, self).__init__(*args, **kwargs)
|
||||
|
||||
self.visirules = {}
|
||||
if self.visibility_rules is not None:
|
||||
for key, rule in self.visibility_rules.items():
|
||||
field, value = rule.split("=")
|
||||
visibility = {
|
||||
"field": "id_%s-%s" % (self.app, field), "value": value
|
||||
}
|
||||
self.visirules["%s-%s" % (self.app, key)] = visibility
|
||||
|
||||
if not args and load_values_from_db:
|
||||
self._load_initial_values()
|
||||
|
||||
def _load_initial_values(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def has_access(**kwargs):
|
||||
return True
|
||||
|
||||
def save(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class AdminParametersForm(GenericParametersForm):
|
||||
"""Base form to declare admin level parameters."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Store LocalConfig instance."""
|
||||
self.localconfig = kwargs.pop("localconfig", None)
|
||||
super(AdminParametersForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def _load_initial_values(self):
|
||||
"""Load form initial values from database."""
|
||||
condition = (
|
||||
not db_utils.db_table_exists("core_localconfig") or
|
||||
not self.localconfig)
|
||||
if condition:
|
||||
return
|
||||
values = self.localconfig.parameters.get_values(app=self.app)
|
||||
for key, value in values:
|
||||
self.fields[key].initial = value
|
||||
|
||||
def save(self):
|
||||
"""Save parameters to database."""
|
||||
parameters = {}
|
||||
for name, value in self.cleaned_data.items():
|
||||
if type(self.fields[name]) is form_utils.SeparatorField:
|
||||
continue
|
||||
parameters[name] = value
|
||||
self.localconfig.parameters.set_values(parameters, app=self.app)
|
||||
|
||||
def to_django_settings(self):
|
||||
"""Inject parameters into django settings module."""
|
||||
pass
|
||||
|
||||
|
||||
class UserParametersForm(GenericParametersForm):
|
||||
"""Base form to declare user level parameters."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop("user", None)
|
||||
super(UserParametersForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def _load_initial_values(self):
|
||||
"""Load initial values from User instance."""
|
||||
if not db_utils.db_table_exists("core_localconfig"):
|
||||
return
|
||||
if self.user is None:
|
||||
return
|
||||
parameters = self.user.parameters.get_values(self.app)
|
||||
for parameter, value in parameters:
|
||||
self.fields[parameter].initial = value
|
||||
|
||||
def save(self):
|
||||
"""Save new values."""
|
||||
parameters = {}
|
||||
for name, value in self.cleaned_data.items():
|
||||
if type(self.fields[name]) is form_utils.SeparatorField:
|
||||
continue
|
||||
parameters[name] = value
|
||||
self.user.parameters.set_values(parameters, app=self.app)
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{% load form_tags %}
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-tabs">
|
||||
{% for form in forms %}<li{% if forloop.first %} class="active"{% endif %}><a href="#{{ forloop.counter }}" data-toggle="tab">{{ form.label }}</a></li>
|
||||
{% for form in forms %}<li{% if forloop.first %} class="active"{% endif %}><a href="#{{ forloop.counter }}" data-app="{{ form.app }}" data-toggle="tab">{{ form.label }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="tab-content">{% for form in forms %}
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,203 @@
|
||||
"""Parameters management."""
|
||||
|
||||
import copy
|
||||
|
||||
from modoboa.lib import exceptions
|
||||
from modoboa.lib import form_utils
|
||||
from modoboa.lib import signals
|
||||
from modoboa.lib.sysutils import guess_extension_name
|
||||
|
||||
|
||||
class NotDefined(exceptions.ModoboaException):
|
||||
"""Custom exception for undefined parameters."""
|
||||
|
||||
http_code = 404
|
||||
|
||||
def __init__(self, app, name=None):
|
||||
self.app = app
|
||||
self.name = name
|
||||
|
||||
def __str__(self):
|
||||
if self.name is None:
|
||||
return "Application {} not registered".format(self.app)
|
||||
return "Parameter {} not defined for app {}".format(
|
||||
self.name, self.app)
|
||||
|
||||
|
||||
class Registry(object):
|
||||
"""A registry for parameters."""
|
||||
|
||||
def __init__(self):
|
||||
"""Constructor."""
|
||||
self._registry = {
|
||||
"global": {},
|
||||
"user": {}
|
||||
}
|
||||
|
||||
def add(self, level, formclass, label):
|
||||
"""Add a new class containing parameters."""
|
||||
self._registry[level][formclass.app] = {
|
||||
"label": label, "formclass": formclass, "defaults": {}
|
||||
}
|
||||
|
||||
def _load_default_values(self, level):
|
||||
"""Load default values."""
|
||||
for app, data in self._registry[level].items():
|
||||
form = data["formclass"](load_values_from_db=False)
|
||||
for name, field in form.fields.items():
|
||||
if type(field) is form_utils.SeparatorField:
|
||||
continue
|
||||
data["defaults"][name] = field.initial
|
||||
|
||||
def get_forms(self, level, *args, **kwargs):
|
||||
"""Return form instances for all app of the given level."""
|
||||
sorted_apps = []
|
||||
first_app = kwargs.pop("first_app", "core")
|
||||
if first_app in self._registry[level]:
|
||||
sorted_apps.append(first_app)
|
||||
sorted_apps += sorted(
|
||||
[a for a in self._registry[level].keys() if a != first_app],
|
||||
key=lambda a: self._registry[level][a]["label"]
|
||||
)
|
||||
result = []
|
||||
for app in sorted_apps:
|
||||
data = self._registry[level][app]
|
||||
if not data["formclass"].has_access(**kwargs):
|
||||
continue
|
||||
result.append({
|
||||
"app": app,
|
||||
"label": data["label"],
|
||||
"form": data["formclass"](*args, **kwargs)
|
||||
})
|
||||
return result
|
||||
|
||||
def exists(self, level, app, parameter=None):
|
||||
"""Check if parameter exists."""
|
||||
parameters = self._registry[level]
|
||||
result = app in parameters
|
||||
if parameter:
|
||||
result = result and parameter in parameters[app]["defaults"]
|
||||
return result
|
||||
|
||||
def get_default(self, level, app, parameter):
|
||||
"""Retrieve default value for parameter."""
|
||||
if app not in self._registry[level]:
|
||||
raise NotDefined(app)
|
||||
if parameter not in self._registry[level][app]["defaults"]:
|
||||
raise NotDefined(app, parameter)
|
||||
return self._registry[level][app]["defaults"][parameter]
|
||||
|
||||
def get_defaults(self, level, app):
|
||||
"""Retrieve default values for application."""
|
||||
if app not in self._registry[level]:
|
||||
raise NotDefined(app)
|
||||
return self._registry[level][app]["defaults"]
|
||||
|
||||
registry = Registry()
|
||||
|
||||
|
||||
class Manager(object):
|
||||
"""A manager for parameters."""
|
||||
|
||||
def __init__(self, level, parameters):
|
||||
"""Constructor."""
|
||||
self._level = level
|
||||
self._parameters = parameters
|
||||
registry._load_default_values(level)
|
||||
|
||||
def get_value(self, parameter, app=None, raise_exception=True):
|
||||
"""Return the value associated to the specified parameter."""
|
||||
if app is None:
|
||||
app = guess_extension_name()
|
||||
# Compat. with the old way...
|
||||
parameter = parameter.lower()
|
||||
try:
|
||||
default = registry.get_default(self._level, app, parameter)
|
||||
except NotDefined:
|
||||
if raise_exception:
|
||||
raise
|
||||
return None
|
||||
if app in self._parameters:
|
||||
return self._parameters[app][parameter]
|
||||
return default
|
||||
|
||||
def get_values(self, app=None):
|
||||
"""Return all values for the given level/app."""
|
||||
if app is None:
|
||||
app = guess_extension_name()
|
||||
values = registry.get_defaults(self._level, app)
|
||||
for parameter, value in values.iteritems():
|
||||
if app in self._parameters:
|
||||
value = self._parameters[app].get(parameter, value)
|
||||
yield (parameter, value)
|
||||
|
||||
def set_value(self, parameter, value, app=None):
|
||||
"""Set parameter for the given app."""
|
||||
if app is None:
|
||||
app = guess_extension_name()
|
||||
if not registry.exists(self._level, app, parameter):
|
||||
raise NotDefined(app, parameter)
|
||||
if app not in self._parameters:
|
||||
self._parameters[app] = copy.deepcopy(
|
||||
registry.get_defaults(self._level, app))
|
||||
self._parameters[app][parameter] = value
|
||||
|
||||
def set_values(self, values, app=None):
|
||||
"""Set/update values for the given app."""
|
||||
if app is None:
|
||||
app = guess_extension_name()
|
||||
if not registry.exists(self._level, app):
|
||||
raise NotDefined(app)
|
||||
if app not in self._parameters:
|
||||
self._parameters[app] = copy.deepcopy(
|
||||
registry.get_defaults(self._level, app))
|
||||
self._parameters[app].update(values)
|
||||
|
||||
|
||||
def get_localconfig():
|
||||
"""Retrieve current LocalConfig instance."""
|
||||
from modoboa.core import models as core_models
|
||||
request = signals.get_request()
|
||||
if request:
|
||||
return request.localconfig
|
||||
return core_models.LocalConfig.objects.first()
|
||||
|
||||
|
||||
def get_global_parameter(name, app=None, **kwargs):
|
||||
"""Retrieve a global parameter.
|
||||
|
||||
This is a shortcut to use when a LocalConfig instance is not
|
||||
available (ie. when no request object is available).
|
||||
|
||||
A ``NotDefined`` exception if the parameter doesn't exist.
|
||||
|
||||
:param name: the parameter's name
|
||||
:param app: the application owning the parameter
|
||||
:return: the corresponding value
|
||||
|
||||
"""
|
||||
if app is None:
|
||||
app = guess_extension_name()
|
||||
return get_localconfig().parameters.get_value(name, app=app, **kwargs)
|
||||
|
||||
|
||||
def get_global_parameters(app, **kwargs):
|
||||
"""Retrieve global parameters of a given app.
|
||||
|
||||
This is a shortcut to use when a LocalConfig instance is not
|
||||
available (ie. when no request object is available).
|
||||
|
||||
A ``NotDefined`` exception if the parameter doesn't exist.
|
||||
|
||||
:param name: the parameter's name
|
||||
:param app: the application owning the parameter
|
||||
:return: the corresponding value
|
||||
|
||||
"""
|
||||
return get_localconfig().parameters.get_values(app, **kwargs)
|
||||
|
||||
|
||||
def apply_to_django_settings():
|
||||
"""Apply global parameters to Django settings module."""
|
||||
for form in registry.get_forms("global"):
|
||||
form["form"].to_django_settings()
|
||||
@@ -1,16 +1,13 @@
|
||||
"""
|
||||
Relay domains settings definition.
|
||||
"""
|
||||
"""Relay domains settings definition."""
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.form_utils import SeparatorField
|
||||
from modoboa.parameters import forms as param_forms
|
||||
|
||||
|
||||
class AdminParametersForm(parameters.AdminParametersForm):
|
||||
|
||||
class AdminParametersForm(param_forms.AdminParametersForm):
|
||||
"""Admin parameters definition."""
|
||||
|
||||
app = "relaydomains"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""AppConfig for relaydomains."""
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
EVENTS = [
|
||||
"RelayDomainCreated",
|
||||
@@ -16,11 +16,11 @@ EVENTS = [
|
||||
|
||||
def load_relaydomains_settings():
|
||||
"""Load application settings."""
|
||||
from django.utils.translation import ugettext as _
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.parameters import tools as param_tools
|
||||
from modoboa.lib import events
|
||||
from .app_settings import AdminParametersForm
|
||||
|
||||
parameters.register(AdminParametersForm, _("Relay domains"))
|
||||
param_tools.registry.add("global", AdminParametersForm, _("Relay domains"))
|
||||
events.declare(EVENTS)
|
||||
from . import general_callbacks
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from reversion import revisions as reversion
|
||||
|
||||
from modoboa.admin import models as admin_models
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.exceptions import BadRequest
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
|
||||
class ServiceManager(Manager):
|
||||
@@ -20,7 +20,7 @@ class ServiceManager(Manager):
|
||||
entries are saved and outdated ones (ie. present in the
|
||||
database but not in the file) are removed.
|
||||
"""
|
||||
with open(parameters.get_admin('MASTER_CF_PATH')) as fp:
|
||||
with open(param_tools.get_global_parameter("master_cf_path")) as fp:
|
||||
content = fp.read()
|
||||
services = []
|
||||
for line in content.split('\n'):
|
||||
|
||||
@@ -6,7 +6,6 @@ from django.test import TestCase
|
||||
from modoboa.admin import factories as admin_factories
|
||||
from modoboa.admin import models as admin_models
|
||||
from modoboa.core.factories import UserFactory
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
from modoboa.lib.test_utils import MapFilesTestCaseMixin
|
||||
from modoboa.limits import utils as limits_utils
|
||||
@@ -20,7 +19,7 @@ class Operations(object):
|
||||
def _create_relay_domain(self, name, status=200):
|
||||
srv, created = Service.objects.get_or_create(name='dummy')
|
||||
values = {
|
||||
"name": name, "create_dom_admin": "no", "type": "relaydomain",
|
||||
"name": name, "create_dom_admin": False, "type": "relaydomain",
|
||||
"target_host": "external.host.tld", "service": srv.id,
|
||||
"enabled": True, "stepid": "step3"
|
||||
}
|
||||
@@ -213,15 +212,18 @@ class RelayDomainsTestCase(ModoTestCase, Operations):
|
||||
"enabled": True
|
||||
}
|
||||
self.ajax_post(reverse("admin:alias_add"), values)
|
||||
alias = admin_models.Alias.objects.get(address="alias@relaydomain.tld")
|
||||
self.assertTrue(
|
||||
admin_models.Alias.objects.filter(
|
||||
address="alias@relaydomain.tld").exists())
|
||||
values = {
|
||||
"address": "alias2@relaydomain.tld",
|
||||
"recipients": "local@relaydomain.tld",
|
||||
"enabled": True
|
||||
}
|
||||
self.ajax_post(reverse("admin:alias_add"), values)
|
||||
alias = admin_models.Alias.objects.get(
|
||||
address="alias2@relaydomain.tld")
|
||||
self.assertTrue(
|
||||
admin_models.Alias.objects.filter(
|
||||
address="alias2@relaydomain.tld").exists())
|
||||
|
||||
|
||||
class LimitsTestCase(ModoTestCase, Operations):
|
||||
@@ -230,11 +232,10 @@ class LimitsTestCase(ModoTestCase, Operations):
|
||||
def setUpTestData(cls):
|
||||
"""Create test data."""
|
||||
super(LimitsTestCase, cls).setUpTestData()
|
||||
|
||||
for name, tpl in limits_utils.get_user_limit_templates():
|
||||
parameters.save_admin(
|
||||
"DEFLT_USER_{0}_LIMIT".format(name.upper()), 2, app="limits"
|
||||
)
|
||||
cls.localconfig.parameters.set_value(
|
||||
"deflt_user_{0}_limit".format(name), 2, app="limits")
|
||||
cls.localconfig.save()
|
||||
cls.user = UserFactory.create(
|
||||
username='reseller', groups=('Resellers',)
|
||||
)
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
/* global Listing */
|
||||
|
||||
var TwocolsNav = function(options) {
|
||||
Listing.call(this, options);
|
||||
};
|
||||
@@ -33,6 +36,17 @@ TwocolsNav.prototype = {
|
||||
success_cb: $.proxy(this.save_cb, this)
|
||||
});
|
||||
}, this));
|
||||
$(document).on('submit', '#parameters_form', function(evt) {
|
||||
simple_ajax_form_post(evt, {
|
||||
formid: $(this).attr('id'),
|
||||
modal: false,
|
||||
reload_on_success: false,
|
||||
success_cb: $.proxy(this.save_cb, this),
|
||||
error_cb: function(data) {
|
||||
$('a[data-app="' + data.prefix + '"]').tab('show');
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
update_content: function(data) {
|
||||
|
||||
@@ -60,6 +60,7 @@ MODOBOA_APPS = (
|
||||
'modoboa.admin',
|
||||
'modoboa.limits',
|
||||
'modoboa.relaydomains',
|
||||
'modoboa.parameters',
|
||||
)
|
||||
|
||||
INSTALLED_APPS += MODOBOA_APPS
|
||||
@@ -75,6 +76,7 @@ MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'modoboa.core.middleware.LocalConfigMiddleware',
|
||||
'modoboa.lib.middleware.AjaxLoginRedirect',
|
||||
'modoboa.lib.middleware.CommonExceptionCatcher',
|
||||
'modoboa.lib.middleware.RequestCatcherMiddleware',
|
||||
|
||||
Reference in New Issue
Block a user