Commit 121f56f4 authored by ibuler's avatar ibuler

[Feature] 添加setting页面

parent b49e3b8f
# -*- coding: utf-8 -*-
#
from django.db.models.signals import post_save, post_init, m2m_changed, pre_save
from django.db.models.signals import post_save, post_init
from django.dispatch import receiver
from django.utils.translation import gettext as _
......
# -*- coding: utf-8 -*-
#
from rest_framework.views import APIView
from rest_framework.views import Response
from django.core.mail import get_connection, send_mail
from django.utils.translation import ugettext_lazy as _
from .permissions import IsSuperUser
from .serializers import MailTestSerializer
class MailTestingAPI(APIView):
permission_classes = (IsSuperUser,)
serializer_class = MailTestSerializer
success_message = _("Test mail sent to {}, please check")
def post(self, request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
email_host_user = serializer.validated_data["EMAIL_HOST_USER"]
kwargs = {
"host": serializer.validated_data["EMAIL_HOST"],
"port": serializer.validated_data["EMAIL_PORT"],
"username": serializer.validated_data["EMAIL_HOST_USER"],
"password": serializer.validated_data["EMAIL_HOST_PASSWORD"],
"use_ssl": serializer.validated_data["EMAIL_USE_SSL"],
"use_tls": serializer.validated_data["EMAIL_USE_TLS"]
}
connection = get_connection(timeout=5, **kwargs)
try:
connection.open()
except Exception as e:
return Response({"error": str(e)}, status=401)
try:
send_mail("Test", "Test smtp setting", email_host_user,
[email_host_user], connection=connection)
except Exception as e:
return Response({"error": str(e)}, status=401)
return Response({"msg": self.success_message.format(email_host_user)})
......@@ -5,3 +5,9 @@ from django.apps import AppConfig
class CommonConfig(AppConfig):
name = 'common'
def ready(self):
from . import signals_handler
from .signals import django_ready
django_ready.send(self.__class__)
return super().ready()
# -*- coding: utf-8 -*-
#
import json
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.db import transaction
from .models import Setting
def to_model_value(value):
try:
return json.dumps(value)
except json.JSONDecodeError:
return None
def to_form_value(value):
try:
return json.loads(value)
except json.JSONDecodeError:
return ''
class BaseForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.is_bound:
settings = Setting.objects.all()
for name, field in self.fields.items():
db_value = getattr(settings, name).value
if db_value:
field.initial = to_form_value(db_value)
def save(self):
if not self.is_bound:
raise ValueError("Form is not bound")
if self.is_valid():
with transaction.atomic():
for name, value in self.cleaned_data.items():
field = self.fields[name]
if isinstance(field.widget, forms.PasswordInput) and not value:
continue
defaults = {
'name': name,
'value': to_model_value(value)
}
Setting.objects.update_or_create(defaults=defaults, name=name)
else:
raise ValueError(self.errors)
class EmailSettingForm(BaseForm):
EMAIL_HOST = forms.CharField(
max_length=1024, label=_("SMTP host"), initial='smtp.jumpserver.org'
)
EMAIL_PORT = forms.CharField(max_length=5, label=_("SMTP port"), initial=25)
EMAIL_HOST_USER = forms.CharField(
max_length=128, label=_("SMTP user"), initial='noreply@jumpserver.org'
)
EMAIL_HOST_PASSWORD = forms.CharField(
max_length=1024, label=_("SMTP password"), widget=forms.PasswordInput,
required=False, help_text=_("Some provider use token except password")
)
EMAIL_USE_SSL = forms.BooleanField(
label=_("Use SSL"), initial=False, required=False,
help_text=_("If SMTP port is 465, may be select")
)
EMAIL_USE_TLS = forms.BooleanField(
label=_("Use TLS"), initial=False, required=False,
help_text=_("If SMTP port is 587, may be select")
)
# coding: utf-8
import inspect
from django.db import models
from django.http import JsonResponse
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.mixins import UserPassesTestMixin
class NoDeleteQuerySet(models.query.QuerySet):
......@@ -113,4 +113,14 @@ class DatetimeSearchMixin:
)
else:
self.date_to = timezone.now()
return super().get(request, *args, **kwargs)
\ No newline at end of file
return super().get(request, *args, **kwargs)
class AdminUserRequiredMixin(UserPassesTestMixin):
def test_func(self):
if not self.request.user.is_authenticated:
return False
elif not self.request.user.is_superuser:
self.raise_exception = True
return False
return True
import json
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
class SettingQuerySet(models.QuerySet):
def __getattr__(self, item):
instances = self.filter(name=item)
if len(instances) == 1:
return instances[0]
else:
return Setting()
class SettingManager(models.Manager):
def get_queryset(self):
return SettingQuerySet(self.model, using=self._db)
class Setting(models.Model):
name = models.CharField(max_length=128, unique=True, verbose_name=_("Name"))
value = models.TextField(verbose_name=_("Value"))
enabled = models.BooleanField(verbose_name=_("Enabled"), default=True)
comment = models.TextField(verbose_name=_("Comment"))
objects = SettingManager()
def __str__(self):
return self.name
@classmethod
def refresh_all_settings(cls):
settings_list = cls.objects.all()
for setting in settings_list:
setting.refresh_setting()
def refresh_setting(self):
try:
value = json.loads(self.value)
except json.JSONDecodeError:
return
setattr(settings, self.name, value)
class Meta:
db_table = "settings"
# -*- coding: utf-8 -*-
#
from rest_framework import permissions
class IsValidUser(permissions.IsAuthenticated, permissions.BasePermission):
"""Allows access to valid user, is active and not expired"""
def has_permission(self, request, view):
return super(IsValidUser, self).has_permission(request, view) \
and request.user.is_valid
class IsAppUser(IsValidUser):
"""Allows access only to app user """
def has_permission(self, request, view):
return super(IsAppUser, self).has_permission(request, view) \
and request.user.is_app
class IsSuperUser(IsValidUser):
"""Allows access only to superuser"""
def has_permission(self, request, view):
return super(IsSuperUser, self).has_permission(request, view) \
and request.user.is_superuser
class IsSuperUserOrAppUser(IsValidUser):
"""Allows access between superuser and app user"""
def has_permission(self, request, view):
return super(IsSuperUserOrAppUser, self).has_permission(request, view) \
and (request.user.is_superuser or request.user.is_app)
class IsSuperUserOrAppUserOrUserReadonly(IsSuperUserOrAppUser):
def has_permission(self, request, view):
if IsValidUser.has_permission(self, request, view) \
and request.method in permissions.SAFE_METHODS:
return True
else:
return IsSuperUserOrAppUser.has_permission(self, request, view)
class IsCurrentUserOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj == request.user
from rest_framework import serializers
class MailTestSerializer(serializers.Serializer):
EMAIL_HOST = serializers.CharField(max_length=1024, required=True)
EMAIL_PORT = serializers.IntegerField(default=25)
EMAIL_HOST_USER = serializers.CharField(max_length=1024)
EMAIL_HOST_PASSWORD = serializers.CharField()
EMAIL_USE_SSL = serializers.BooleanField(default=False)
EMAIL_USE_TLS = serializers.BooleanField(default=False)
# -*- coding: utf-8 -*-
#
from django.dispatch import Signal
django_ready = Signal()
# -*- coding: utf-8 -*-
#
from django.dispatch import receiver
from django.db.models.signals import post_save
from .models import Setting
from .utils import get_logger
from .signals import django_ready
logger = get_logger(__file__)
@receiver(post_save, sender=Setting, dispatch_uid="my_unique_identifier")
def refresh_settings_on_changed(sender, instance=None, **kwargs):
logger.debug("Receive setting item change")
logger.debug(" - refresh setting: {}".format(instance.name))
if instance:
instance.refresh_setting()
@receiver(django_ready, dispatch_uid="my_unique_identifier")
def refresh_all_settings_on_django_ready(sender, **kwargs):
logger.debug("Receive django ready signal")
logger.debug(" - fresh all settings")
Setting.refresh_all_settings()
{% extends 'base.html' %}
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% load common_tags %}
{% block custom_head_css_js %}
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-12">
<div class="ibox float-e-margins">
<div class="panel-options">
<ul class="nav nav-tabs">
<li>
<a href="" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
</li>
<li class="active">
<a class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="col-sm-12" style="padding-left:0">
<div class="ibox-content" style="border-width: 0;padding-top: 40px;">
<form action="" method="post" class="form-horizontal">
{% if form.non_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
{% csrf_token %}
{% for field in form %}
{% if not field.field|is_bool_field %}
{% bootstrap_field field layout="horizontal" %}
{% else %}
<div class="form-group">
<label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-8">
<div class="col-sm-1">
{{ field }}
</div>
<div class="col-sm-9">
<span class="help-block" >{{ field.help_text }}</span>
</div>
</div>
</div>
{% endif %}
{% endfor %}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
</script>
{% endblock %}
......@@ -4,6 +4,7 @@ from django import template
from django.utils import timezone
from django.utils.translation import gettext as _
from django.utils.html import escape
from django import forms
register = template.Library()
......@@ -83,3 +84,11 @@ def time_util_with_seconds(date_from, date_to):
return '{} h'.format(seconds//3600)
else:
return ''
@register.filter
def is_bool_field(field):
if isinstance(field, forms.BooleanField):
return True
else:
return False
from __future__ import absolute_import
from django.conf.urls import url
from .. import api
app_name = 'common'
urlpatterns = [
url(r'^v1/mail/testing/$', api.MailTestingAPI.as_view(), name='mail-testing'),
]
from __future__ import absolute_import
from django.conf.urls import url
from .. import views
app_name = 'common'
urlpatterns = [
url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'),
]
from __future__ import absolute_import, unicode_literals
from django.views.generic import View
from django.shortcuts import render
from django.contrib import messages
from django.utils.translation import ugettext as _
from .forms import EmailSettingForm
from .mixins import AdminUserRequiredMixin
class EmailSettingView(AdminUserRequiredMixin, View):
form_class = EmailSettingForm
template_name = "common/email_setting.html"
def get(self, request):
context = {
'app': 'settings',
'action': 'Email setting',
"form": EmailSettingForm(),
}
return render(request, self.template_name, context)
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
form.save()
messages.success(request, _("Update email setting successfully"))
context = {
'app': 'settings',
'action': 'Email setting',
"form": EmailSettingForm(),
}
return render(request, self.template_name, context)
......@@ -121,15 +121,6 @@ MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
# if CONFIG.DB_ENGINE == 'sqlite':
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': CONFIG.DB_NAME or os.path.join(BASE_DIR, 'data', 'db.sqlite3'),
# 'ATOMIC_REQUESTS': True,
# }
# }
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.{}'.format(CONFIG.DB_ENGINE),
......
......@@ -19,6 +19,8 @@ urlpatterns = [
url(r'^perms/', include('perms.urls.views_urls', namespace='perms')),
url(r'^terminal/', include('terminal.urls.views_urls', namespace='terminal')),
url(r'^ops/', include('ops.urls.view_urls', namespace='ops')),
url(r'^settings/', include('common.urls.view_urls', namespace='settings')),
url(r'^common/', include('common.urls.view_urls', namespace='common')),
# Api url view map
url(r'^api/users/', include('users.urls.api_urls', namespace='api-users')),
......@@ -26,13 +28,12 @@ urlpatterns = [
url(r'^api/perms/', include('perms.urls.api_urls', namespace='api-perms')),
url(r'^api/terminal/', include('terminal.urls.api_urls', namespace='api-terminal')),
url(r'^api/ops/', include('ops.urls.api_urls', namespace='api-ops')),
url(r'^api/common/', include('common.urls.api_urls', namespace='api-common')),
# External apps url
url(r'^captcha/', include('captcha.urls')),
]
if settings.DEBUG:
urlpatterns += [
url(r'^docs/', schema_view, name="docs"),
......
......@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
#
from .group import *
from .user import *
from .group import *
from .authentication import *
from .utils import *
......@@ -20,12 +20,6 @@ class UserGroup(NoDeleteModelMixin):
def __str__(self):
return self.name
def delete(self, using=None, keep_parents=False):
if self.name != 'Default':
self.users.clear()
return super(UserGroup, self).delete()
return True
class Meta:
ordering = ['name']
......
......@@ -13,7 +13,6 @@ from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.shortcuts import reverse
from .group import UserGroup
from common.utils import get_signer, date_expired_default
......@@ -35,7 +34,7 @@ class User(AbstractUser):
username = models.CharField(max_length=128, unique=True, verbose_name=_('Username'))
name = models.CharField(max_length=128, verbose_name=_('Name'))
email = models.EmailField(max_length=128, unique=True, verbose_name=_('Email'))
groups = models.ManyToManyField(UserGroup, related_name='users', blank=True, verbose_name=_('User group'))
groups = models.ManyToManyField('users.UserGroup', related_name='users', blank=True, verbose_name=_('User group'))
role = models.CharField(choices=ROLE_CHOICES, default='User', max_length=10, blank=True, verbose_name=_('Role'))
avatar = models.ImageField(upload_to="avatar", null=True, verbose_name=_('Avatar'))
wechat = models.CharField(max_length=128, blank=True, verbose_name=_('Wechat'))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment