mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-19 05:58:34 +02:00
WIP: new system to manage settings.
This commit is contained in:
@@ -16,3 +16,16 @@ export const createDomain = data => {
|
||||
export const updateDomain = data => {
|
||||
return domainResource.update({ pk: data.pk }, data)
|
||||
}
|
||||
|
||||
var customParametersActions = {
|
||||
structure: {method: 'GET', url: '/api/v1/parameters/structure/'}
|
||||
}
|
||||
var parametersResource = Vue.resource('/api/v1/parameters/', {}, customParametersActions)
|
||||
|
||||
export const getParameters = () => {
|
||||
return parametersResource.get()
|
||||
}
|
||||
|
||||
export const getParametersStructure = () => {
|
||||
return parametersResource.structure()
|
||||
}
|
||||
|
||||
@@ -59,9 +59,9 @@ export default {
|
||||
label: 'Logs'
|
||||
},
|
||||
{
|
||||
route: 'DomainList',
|
||||
route: 'ParametersEdit',
|
||||
icon: 'tune',
|
||||
label: 'Settings'
|
||||
label: 'Parameters'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1 class="title">Parameters</h1>
|
||||
<form>
|
||||
<h2 class="subtitle" v-for="(element, index) in structure"
|
||||
v-if="element.type === 'section'" :key="index">
|
||||
{{ element.label }}
|
||||
</h2>
|
||||
<b-field v-else :label="element.label" horizontal>
|
||||
<b-input v-if="element.widget === 'CharField' || element.widget === 'IntegerField'"
|
||||
v-model="parameters[element.name]">
|
||||
</b-input>
|
||||
<b-checkbox v-else-if="element.widget === 'BooleanField'"
|
||||
v-model="parameters[element.name]"></b-checkbox>
|
||||
<b-select v-else-if="element.widget === 'ChoiceField'"
|
||||
v-model="parameters[element.name]">
|
||||
<option v-for="choice in Object.entries(element.choices)"
|
||||
:value="choice[0]" :key="choice[0]">
|
||||
{{ choice[1] }}
|
||||
</option>
|
||||
</b-select>
|
||||
</b-field>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as api from '@/api'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
structure: [],
|
||||
parameters: {}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
api.getParametersStructure().then(response => {
|
||||
this.structure = response.data
|
||||
})
|
||||
api.getParameters().then(response => {
|
||||
this.parameters = response.data
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -2,25 +2,31 @@ import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import DomainList from '@/components/domains/DomainList'
|
||||
import DomainForm from '@/components/domains/DomainForm'
|
||||
import ParametersForm from '@/components/parameters/ParametersForm'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
export default new Router({
|
||||
routes: [
|
||||
{
|
||||
path: '/domains/',
|
||||
name: 'DomainList',
|
||||
component: DomainList
|
||||
},
|
||||
{
|
||||
path: '/domains/add/',
|
||||
name: 'DomainAdd',
|
||||
component: DomainForm
|
||||
},
|
||||
{
|
||||
path: '/domains/:domainPk([0-9]+)/',
|
||||
name: 'DomainEdit',
|
||||
component: DomainForm
|
||||
}
|
||||
]
|
||||
routes: [
|
||||
{
|
||||
path: '/domains/',
|
||||
name: 'DomainList',
|
||||
component: DomainList
|
||||
},
|
||||
{
|
||||
path: '/domains/add/',
|
||||
name: 'DomainAdd',
|
||||
component: DomainForm
|
||||
},
|
||||
{
|
||||
path: '/domains/:domainPk([0-9]+)/',
|
||||
name: 'DomainEdit',
|
||||
component: DomainForm
|
||||
},
|
||||
{
|
||||
path: '/parameters/',
|
||||
name: 'ParametersEdit',
|
||||
component: ParametersForm
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
@@ -7,6 +7,8 @@ from django.conf import settings
|
||||
from django.contrib.auth import password_validation
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from modoboa.lib import fields as lib_fields
|
||||
from modoboa.lib.form_utils import (
|
||||
HorizontalRadioSelect, SeparatorField, YesNoField
|
||||
@@ -441,3 +443,180 @@ class GeneralParametersForm(param_forms.AdminParametersForm):
|
||||
})
|
||||
else:
|
||||
settings.AUTH_LDAP_GLOBAL_OPTIONS[ldap.OPT_REFERRALS] = False
|
||||
|
||||
|
||||
GLOBAL_PARAMETERS = {
|
||||
"authentication": {
|
||||
"label": ugettext_lazy("Authentication"),
|
||||
"params": {
|
||||
"authentication_type": {
|
||||
"field": serializers.ChoiceField(
|
||||
choices=[("local", ugettext_lazy("Local")),
|
||||
("ldap", "LDAP")],
|
||||
default="local"
|
||||
),
|
||||
"label": ugettext_lazy("Authentication type"),
|
||||
"help_text": ugettext_lazy(
|
||||
"The backend used for authentication"),
|
||||
},
|
||||
"password_scheme": {
|
||||
"label": ugettext_lazy("Default password scheme"),
|
||||
"field": serializers.ChoiceField(
|
||||
choices=[("sha512crypt", "sha512crypt"),
|
||||
("sha256crypt", "sha256crypt"),
|
||||
("blfcrypt", "bcrypt"),
|
||||
("md5crypt", ugettext_lazy("md5crypt (weak)")),
|
||||
("sha256", ugettext_lazy("sha256 (weak)")),
|
||||
("md5", ugettext_lazy("md5 (weak)")),
|
||||
("crypt", ugettext_lazy("crypt (weak)")),
|
||||
("plain", ugettext_lazy("plain (weak)"))],
|
||||
default="sha512crypt"
|
||||
),
|
||||
"help_text": ugettext_lazy(
|
||||
"Scheme used to crypt mailbox passwords"),
|
||||
},
|
||||
"rounds_number": {
|
||||
"label": ugettext_lazy("Rounds"),
|
||||
"field": serializers.IntegerField(default=70000),
|
||||
"help_text": ugettext_lazy(
|
||||
"Number of rounds to use (only used by sha256crypt and "
|
||||
"sha512crypt). Must be between 1000 and 999999999, "
|
||||
"inclusive."
|
||||
)
|
||||
},
|
||||
"default_password": {
|
||||
"label": ugettext_lazy("Default password"),
|
||||
"field": serializers.CharField(default="password"),
|
||||
"help_text": ugettext_lazy(
|
||||
"Default password for automatically created accounts.")
|
||||
},
|
||||
"random_password_length": {
|
||||
"label": ugettext_lazy("Random password length"),
|
||||
"field": serializers.IntegerField(min_value=8, default=8),
|
||||
"help_text": ugettext_lazy(
|
||||
"Length of randomly generated passwords.")
|
||||
}
|
||||
}
|
||||
},
|
||||
"ldap": {
|
||||
"label": _("LDAP"),
|
||||
"display": "authentication_type=ldap",
|
||||
"params": {
|
||||
"ldap_server_address": {
|
||||
"label": ugettext_lazy("Server address"),
|
||||
"field": serializers.CharField(default="localhost"),
|
||||
"help_text": ugettext_lazy(
|
||||
"The IP address or the DNS name of the LDAP server"),
|
||||
},
|
||||
"ldap_server_port": {
|
||||
"label": ugettext_lazy("Server port"),
|
||||
"field": serializers.IntegerField(default=389),
|
||||
"help_text": ugettext_lazy(
|
||||
"The TCP port number used by the LDAP server")
|
||||
},
|
||||
"ldap_secured": {
|
||||
"label": ugettext_lazy("Use a secured connection"),
|
||||
"field": serializers.ChoiceField(
|
||||
choices=constants.LDAP_SECURE_MODES,
|
||||
default="none"
|
||||
),
|
||||
"help_text": ugettext_lazy(
|
||||
"Use an SSL/STARTTLS connection to access the LDAP server")
|
||||
},
|
||||
"ldap_auth_method": {
|
||||
"label": ugettext_lazy("Authentication method"),
|
||||
"field": serializers.ChoiceField(
|
||||
choices=constants.LDAP_AUTH_METHODS,
|
||||
default="searchbind",
|
||||
),
|
||||
"help_text": ugettext_lazy(
|
||||
"Choose the authentication method to use"),
|
||||
},
|
||||
"ldap_bind_dn": {
|
||||
"label": ugettext_lazy("Bind DN"),
|
||||
"field": serializers.CharField(default="", required=False),
|
||||
"help_text": ugettext_lazy(
|
||||
"The distinguished name to use when binding to the LDAP "
|
||||
"server. Leave empty for an anonymous bind"
|
||||
),
|
||||
"display": "ldap_auth_method=searchbind"
|
||||
},
|
||||
"ldap_bind_password": {
|
||||
"label": ugettext_lazy("Bind password"),
|
||||
"field": serializers.CharField(default="", required=False),
|
||||
"help_text": ugettext_lazy(
|
||||
"The password to use when binding to the LDAP server "
|
||||
"(with 'Bind DN')"
|
||||
),
|
||||
"display": "ldap_auth_method=searchbind"
|
||||
},
|
||||
"ldap_search_base": {
|
||||
"label": ugettext_lazy("Users search base"),
|
||||
"field": serializers.CharField(default="", required=False),
|
||||
"help_text": ugettext_lazy(
|
||||
"The distinguished name of the search base used to find "
|
||||
"users"
|
||||
),
|
||||
"display": "ldap_auth_method=searchbind"
|
||||
},
|
||||
"ldap_search_filter": {
|
||||
"label": ugettext_lazy("Search filter"),
|
||||
"field": serializers.CharField(
|
||||
default="(mail=%(user)s)", required=False),
|
||||
"help_text": ugettext_lazy(
|
||||
"An optional filter string (e.g. '(objectClass=person)'). "
|
||||
"In order to be valid, it must be enclosed in parentheses."
|
||||
),
|
||||
"display": "ldap_auth_method=searchbind"
|
||||
},
|
||||
"ldap_user_dn_template": {
|
||||
"label": ugettext_lazy("User DN template"),
|
||||
"field": serializers.CharField(default="", required=False),
|
||||
"help_text": ugettext_lazy(
|
||||
"The template used to construct a user's DN. It should "
|
||||
"contain one placeholder (ie. %(user)s)"
|
||||
),
|
||||
"display": "ldap_auth_method=directbind"
|
||||
},
|
||||
"ldap_password_attribute": {
|
||||
"label": ugettext_lazy("Password attribute"),
|
||||
"field": serializers.CharField(default="userPassword"),
|
||||
"help_text": ugettext_lazy(
|
||||
"The attribute used to store user passwords"),
|
||||
},
|
||||
"ldap_is_active_directory": {
|
||||
"label": ugettext_lazy("Active Directory"),
|
||||
"field": serializers.BooleanField(default=False),
|
||||
"help_text": ugettext_lazy(
|
||||
"Tell if the LDAP server is an Active Directory one")
|
||||
},
|
||||
"ldap_admin_groups": {
|
||||
"label": ugettext_lazy("Administrator groups"),
|
||||
"field": serializers.CharField(default="", required=False),
|
||||
"help_text": ugettext_lazy(
|
||||
"Members of those LDAP Posix groups will be created as "
|
||||
"domain administrators. Use ';' characters to separate "
|
||||
"groups."
|
||||
)
|
||||
},
|
||||
"ldap_group_type": {
|
||||
"label": ugettext_lazy("Group type"),
|
||||
"field": serializers.ChoiceField(
|
||||
default="posixgroup",
|
||||
choices=constants.LDAP_GROUP_TYPES
|
||||
),
|
||||
"help_text": ugettext_lazy(
|
||||
"The LDAP group type to use with your directory."
|
||||
)
|
||||
},
|
||||
"ldap_groups_search_base": {
|
||||
"label": ugettext_lazy("Groups search base"),
|
||||
"field": serializers.CharField(default="", required=False),
|
||||
"help_text": ugettext_lazy(
|
||||
"The distinguished name of the search base used to find "
|
||||
"groups"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,13 @@ def load_core_settings():
|
||||
order to load base settings.
|
||||
"""
|
||||
from modoboa.parameters import tools as param_tools
|
||||
from .app_settings import GeneralParametersForm
|
||||
from . import app_settings
|
||||
|
||||
param_tools.registry.add(
|
||||
"global", GeneralParametersForm, ugettext_lazy("General"))
|
||||
"global", app_settings.GeneralParametersForm, ugettext_lazy("General"))
|
||||
param_tools.registry.add2(
|
||||
"global", "core", ugettext_lazy("General"),
|
||||
app_settings.GLOBAL_PARAMETERS)
|
||||
|
||||
|
||||
class CoreConfig(AppConfig):
|
||||
|
||||
@@ -55,6 +55,11 @@ LDAP_SECURE_MODES = [
|
||||
("ssl", "SSL/TLS")
|
||||
]
|
||||
|
||||
LDAP_AUTH_METHODS = [
|
||||
("searchbind", ugettext_lazy("Search and bind")),
|
||||
("directbind", ugettext_lazy("Direct bind"))
|
||||
]
|
||||
|
||||
PERMISSIONS = {
|
||||
"SimpleUsers": [],
|
||||
"DomainAdmins": [
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
"""Parameters serializers."""
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from . import tools
|
||||
|
||||
|
||||
class ParametersSerializer(serializers.Serializer):
|
||||
"""Parameters serializer."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Generate serializer."""
|
||||
super(ParametersSerializer, self).__init__(*args, **kwargs)
|
||||
for name, field in tools.registry.get_fields("global"):
|
||||
field.source = None
|
||||
self.fields[name] = field
|
||||
@@ -35,6 +35,10 @@ class Registry(object):
|
||||
"global": {},
|
||||
"user": {}
|
||||
}
|
||||
self._registry2 = {
|
||||
"global": {},
|
||||
"user": {}
|
||||
}
|
||||
|
||||
def add(self, level, formclass, label):
|
||||
"""Add a new class containing parameters."""
|
||||
@@ -42,6 +46,12 @@ class Registry(object):
|
||||
"label": label, "formclass": formclass, "defaults": {}
|
||||
}
|
||||
|
||||
def add2(self, level, app, label, params):
|
||||
"""Add new parameters for given app and level."""
|
||||
self._registry2[level][app] = {
|
||||
"label": label, "params": params
|
||||
}
|
||||
|
||||
def _load_default_values(self, level):
|
||||
"""Load default values."""
|
||||
for _app, data in list(self._registry[level].items()):
|
||||
@@ -76,6 +86,39 @@ class Registry(object):
|
||||
})
|
||||
return result
|
||||
|
||||
def get_fields(self, level, *args, **kwargs):
|
||||
"""Return fields to include in serializer."""
|
||||
result = []
|
||||
for app, fields in list(self._registry2[level].items()):
|
||||
for section, sconfig in list(fields["params"].items()):
|
||||
for name, config in list(sconfig["params"].items()):
|
||||
result.append(("{}-{}".format(app, name), config["field"]))
|
||||
return result
|
||||
|
||||
def get_structure(self, level):
|
||||
"""Return fields structure."""
|
||||
result = []
|
||||
for app, fields in list(self._registry2[level].items()):
|
||||
for section, sconfig in list(fields["params"].items()):
|
||||
result.append({
|
||||
"type": "section",
|
||||
"label": sconfig["label"],
|
||||
"display": sconfig.get("display", "")
|
||||
})
|
||||
for name, config in list(sconfig["params"].items()):
|
||||
data = {
|
||||
"type": "field",
|
||||
"name": "{}-{}".format(app, name),
|
||||
"label": config["label"],
|
||||
"help_text": config.get("help_text", ""),
|
||||
"display": config.get("display", ""),
|
||||
"widget": config["field"].__class__.__name__,
|
||||
}
|
||||
if data["widget"] == "ChoiceField":
|
||||
data["choices"] = config["field"].choices
|
||||
result.append(data)
|
||||
return result
|
||||
|
||||
def exists(self, level, app, parameter=None):
|
||||
"""Check if parameter exists."""
|
||||
parameters = self._registry[level]
|
||||
@@ -137,6 +180,15 @@ class Manager(object):
|
||||
value = self._parameters[app].get(parameter, value)
|
||||
yield (parameter, value)
|
||||
|
||||
def get_values_dict(self, app=None):
|
||||
"""Return all values for the given app as a dictionary."""
|
||||
if app is None:
|
||||
app = guess_extension_name()
|
||||
values = registry.get_defaults(self._level, app)
|
||||
return {
|
||||
parameter: value for parameter, value in list(values.items())
|
||||
}
|
||||
|
||||
def set_value(self, parameter, value, app=None):
|
||||
"""Set parameter for the given app."""
|
||||
if app is None:
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
"""Parameters API urls."""
|
||||
|
||||
from rest_framework import routers
|
||||
|
||||
from . import viewsets
|
||||
|
||||
|
||||
router = routers.SimpleRouter()
|
||||
router.register(
|
||||
r"parameters", viewsets.ParametersViewSet, base_name="parameter")
|
||||
urlpatterns = router.urls
|
||||
@@ -0,0 +1,23 @@
|
||||
"""Parameters viewsets."""
|
||||
|
||||
from rest_framework import decorators, response, viewsets
|
||||
|
||||
from . import serializers
|
||||
from . import tools
|
||||
|
||||
|
||||
class ParametersViewSet(viewsets.ViewSet):
|
||||
"""Parameter viewset."""
|
||||
|
||||
@decorators.list_route(methods=["get"])
|
||||
def structure(self, request):
|
||||
"""Return parameter schema."""
|
||||
data = tools.registry.get_structure("global")
|
||||
return response.Response(data)
|
||||
|
||||
def list(self, request):
|
||||
"""Return all parameters."""
|
||||
parameters = request.localconfig.parameters.get_values_dict("core")
|
||||
serializer = serializers.ParametersSerializer(data=parameters)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
return response.Response(serializer.validated_data)
|
||||
@@ -10,6 +10,7 @@ from modoboa.core.extensions import exts_pool
|
||||
|
||||
urlpatterns = [
|
||||
url("", include("modoboa.admin.urls_api")),
|
||||
url("", include("modoboa.parameters.urls_api")),
|
||||
url("", include("modoboa.limits.urls_api")),
|
||||
url("", include("modoboa.relaydomains.urls_api")),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user