Commit abb1a40a authored by ibuler's avatar ibuler

[Feature] 添加basic settings

parent 121f56f4
...@@ -85,7 +85,7 @@ class AdminUserDetailView(AdminUserRequiredMixin, DetailView): ...@@ -85,7 +85,7 @@ class AdminUserDetailView(AdminUserRequiredMixin, DetailView):
class AdminUserAssetsView(AdminUserRequiredMixin, SingleObjectMixin, ListView): class AdminUserAssetsView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.DISPLAY_PER_PAGE
template_name = 'assets/admin_user_assets.html' template_name = 'assets/admin_user_assets.html'
context_object_name = 'admin_user' context_object_name = 'admin_user'
object = None object = None
......
...@@ -92,7 +92,7 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): ...@@ -92,7 +92,7 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
class AssetModalListView(AdminUserRequiredMixin, ListView): class AssetModalListView(AdminUserRequiredMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.DISPLAY_PER_PAGE
model = Asset model = Asset
context_object_name = 'asset_modal_list' context_object_name = 'asset_modal_list'
template_name = 'assets/asset_modal_list.html' template_name = 'assets/asset_modal_list.html'
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import json
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.views import Response from rest_framework.views import Response
from ldap3 import Server, Connection
from django.core.mail import get_connection, send_mail from django.core.mail import get_connection, send_mail
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from .permissions import IsSuperUser from .permissions import IsSuperUser
from .serializers import MailTestSerializer from .serializers import MailTestSerializer, LDAPTestSerializer
class MailTestingAPI(APIView): class MailTestingAPI(APIView):
...@@ -27,7 +31,6 @@ class MailTestingAPI(APIView): ...@@ -27,7 +31,6 @@ class MailTestingAPI(APIView):
"use_tls": serializer.validated_data["EMAIL_USE_TLS"] "use_tls": serializer.validated_data["EMAIL_USE_TLS"]
} }
connection = get_connection(timeout=5, **kwargs) connection = get_connection(timeout=5, **kwargs)
try: try:
connection.open() connection.open()
except Exception as e: except Exception as e:
...@@ -40,3 +43,68 @@ class MailTestingAPI(APIView): ...@@ -40,3 +43,68 @@ class MailTestingAPI(APIView):
return Response({"error": str(e)}, status=401) return Response({"error": str(e)}, status=401)
return Response({"msg": self.success_message.format(email_host_user)}) return Response({"msg": self.success_message.format(email_host_user)})
else:
return Response({"error": str(serializer.errors)}, status=401)
class LDAPTestingAPI(APIView):
permission_classes = (IsSuperUser,)
serializer_class = LDAPTestSerializer
success_message = _("Test ldap success")
def post(self, request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
host = serializer.validated_data["AUTH_LDAP_SERVER_URI"]
bind_dn = serializer.validated_data["AUTH_LDAP_BIND_DN"]
password = serializer.validated_data["AUTH_LDAP_BIND_PASSWORD"]
use_ssl = serializer.validated_data.get("AUTH_LDAP_START_TLS", False)
search_ou = serializer.validated_data["AUTH_LDAP_SEARCH_OU"]
search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"]
attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
print(serializer.validated_data)
try:
attr_map = json.loads(attr_map)
except json.JSONDecodeError:
return Response({"error": "AUTH_LDAP_USER_ATTR_MAP not valid"}, status=401)
server = Server(host, use_ssl=use_ssl)
conn = Connection(server, bind_dn, password)
try:
conn.bind()
except Exception as e:
return Response({"error": str(e)}, status=401)
print(search_ou)
print(search_filter % ({"user": "*"}))
print(attr_map.values())
ok = conn.search(search_ou, search_filter % ({"user": "*"}),
attributes=list(attr_map.values()))
if not ok:
return Response({"error": "Search no entry matched"}, status=401)
users = []
for entry in conn.entries:
user = {}
for attr, mapping in attr_map.items():
if hasattr(entry, mapping):
user[attr] = getattr(entry, mapping)
users.append(user)
if len(users) > 0:
return Response({"msg": "Match {} s users".format(len(users))})
else:
return Response({"error": "Have user but attr mapping error"}, status=401)
else:
return Response({"error": str(serializer.errors)}, status=401)
class DjangoSettingsAPI(APIView):
def get(self, request):
configs = {}
for i in dir(settings):
if i.isupper():
configs[i] = str(getattr(settings, i))
return Response(configs)
# -*- coding: utf-8 -*-
#
import json
from django import forms
from django.utils import six
from django.core.exceptions import ValidationError
class DictField(forms.Field):
widget = forms.Textarea
def to_python(self, value):
"""Returns a Python boolean object."""
# Explicitly check for the string 'False', which is what a hidden field
# will submit for False. Also check for '0', since this is what
# RadioSelect will provide. Because bool("True") == bool('1') == True,
# we don't need to handle that explicitly.
if isinstance(value, six.string_types):
try:
print(value)
value = json.loads(value)
return value
except json.JSONDecodeError:
pass
value = {}
return value
def validate(self, value):
print(value)
if not value and self.required:
raise ValidationError(self.error_messages['required'], code='required')
def has_changed(self, initial, data):
# Sometimes data or initial may be a string equivalent of a boolean
# so we should run it through to_python first to get a boolean value
return self.to_python(initial) != self.to_python(data)
...@@ -7,6 +7,7 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -7,6 +7,7 @@ from django.utils.translation import ugettext_lazy as _
from django.db import transaction from django.db import transaction
from .models import Setting from .models import Setting
from .fields import DictField
def to_model_value(value): def to_model_value(value):
...@@ -18,7 +19,10 @@ def to_model_value(value): ...@@ -18,7 +19,10 @@ def to_model_value(value):
def to_form_value(value): def to_form_value(value):
try: try:
return json.loads(value) data = json.loads(value)
if isinstance(data, dict):
data = value
return data
except json.JSONDecodeError: except json.JSONDecodeError:
return '' return ''
...@@ -26,7 +30,6 @@ def to_form_value(value): ...@@ -26,7 +30,6 @@ def to_form_value(value):
class BaseForm(forms.Form): class BaseForm(forms.Form):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if not self.is_bound:
settings = Setting.objects.all() settings = Setting.objects.all()
for name, field in self.fields.items(): for name, field in self.fields.items():
db_value = getattr(settings, name).value db_value = getattr(settings, name).value
...@@ -37,12 +40,16 @@ class BaseForm(forms.Form): ...@@ -37,12 +40,16 @@ class BaseForm(forms.Form):
if not self.is_bound: if not self.is_bound:
raise ValueError("Form is not bound") raise ValueError("Form is not bound")
settings = Setting.objects.all()
if self.is_valid(): if self.is_valid():
with transaction.atomic(): with transaction.atomic():
for name, value in self.cleaned_data.items(): for name, value in self.cleaned_data.items():
field = self.fields[name] field = self.fields[name]
if isinstance(field.widget, forms.PasswordInput) and not value: if isinstance(field.widget, forms.PasswordInput) and not value:
continue continue
if value == to_form_value(getattr(settings, name).value):
continue
defaults = { defaults = {
'name': name, 'name': name,
'value': to_model_value(value) 'value': to_model_value(value)
...@@ -52,6 +59,24 @@ class BaseForm(forms.Form): ...@@ -52,6 +59,24 @@ class BaseForm(forms.Form):
raise ValueError(self.errors) raise ValueError(self.errors)
class BasicSettingForm(BaseForm):
SITE_URL = forms.URLField(
label=_("Current SITE URL"),
help_text="http://jumpserver.abc.com:8080"
)
USER_GUIDE_URL = forms.URLField(
label=_("User Guide URL"),
help_text=_("User first login update profile done redirect to it")
)
EMAIL_SUBJECT_PREFIX = forms.CharField(
max_length=1024, label=_("Email Subject Prefix"),
initial="[Jumpserver] "
)
AUTH_LDAP = forms.BooleanField(
label=_("Enable LDAP Auth"), initial=False, required=False
)
class EmailSettingForm(BaseForm): class EmailSettingForm(BaseForm):
EMAIL_HOST = forms.CharField( EMAIL_HOST = forms.CharField(
max_length=1024, label=_("SMTP host"), initial='smtp.jumpserver.org' max_length=1024, label=_("SMTP host"), initial='smtp.jumpserver.org'
...@@ -72,3 +97,35 @@ class EmailSettingForm(BaseForm): ...@@ -72,3 +97,35 @@ class EmailSettingForm(BaseForm):
label=_("Use TLS"), initial=False, required=False, label=_("Use TLS"), initial=False, required=False,
help_text=_("If SMTP port is 587, may be select") help_text=_("If SMTP port is 587, may be select")
) )
class LDAPSettingForm(BaseForm):
AUTH_LDAP_SERVER_URI = forms.CharField(
label=_("LDAP server"), initial='ldap://localhost:389'
)
AUTH_LDAP_BIND_DN = forms.CharField(
label=_("Bind DN"), initial='cn=admin,dc=jumpserver,dc=org'
)
AUTH_LDAP_BIND_PASSWORD = forms.CharField(
label=_("Password"), initial='',
widget=forms.PasswordInput, required=False
)
AUTH_LDAP_SEARCH_OU = forms.CharField(
label=_("User OU"), initial='ou=tech,dc=jumpserver,dc=org'
)
AUTH_LDAP_SEARCH_FILTER = forms.CharField(
label=_("User search filter"), initial='(cn=%(user)s)'
)
AUTH_LDAP_USER_ATTR_MAP = DictField(
label=_("User attr map"),
initial=json.dumps({
"username": "cn",
"name": "sn",
"email": "mail"
})
)
# AUTH_LDAP_GROUP_SEARCH_OU = CONFIG.AUTH_LDAP_GROUP_SEARCH_OU
# AUTH_LDAP_GROUP_SEARCH_FILTER = CONFIG.AUTH_LDAP_GROUP_SEARCH_FILTER
AUTH_LDAP_START_TLS = forms.BooleanField(
label=_("Use SSL"), initial=False, required=False
)
import json import json
import ldap
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings from django.conf import settings
from django_auth_ldap.config import LDAPSearch
class SettingQuerySet(models.QuerySet): class SettingQuerySet(models.QuerySet):
...@@ -30,6 +32,13 @@ class Setting(models.Model): ...@@ -30,6 +32,13 @@ class Setting(models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
@property
def value_(self):
try:
return json.loads(self.value)
except json.JSONDecodeError:
return None
@classmethod @classmethod
def refresh_all_settings(cls): def refresh_all_settings(cls):
settings_list = cls.objects.all() settings_list = cls.objects.all()
...@@ -43,5 +52,17 @@ class Setting(models.Model): ...@@ -43,5 +52,17 @@ class Setting(models.Model):
return return
setattr(settings, self.name, value) setattr(settings, self.name, value)
if self.name == "AUTH_LDAP":
if self.value_ and settings.AUTH_LDAP_BACKEND not in settings.AUTHENTICATION_BACKENDS:
settings.AUTHENTICATION_BACKENDS.insert(0, settings.AUTH_LDAP_BACKEND)
elif not self.value_ and settings.AUTH_LDAP_BACKEND in settings.AUTHENTICATION_BACKENDS:
settings.AUTHENTICATION_BACKENDS.remove(settings.AUTH_LDAP_BACKEND)
if self.name == "AUTH_LDAP_SEARCH_FILTER":
settings.AUTH_LDAP_USER_SEARCH = LDAPSearch(
settings.AUTH_LDAP_SEARCH_OU, ldap.SCOPE_SUBTREE,
settings.AUTH_LDAP_SEARCH_FILTER,
)
class Meta: class Meta:
db_table = "settings" db_table = "settings"
...@@ -8,3 +8,14 @@ class MailTestSerializer(serializers.Serializer): ...@@ -8,3 +8,14 @@ class MailTestSerializer(serializers.Serializer):
EMAIL_HOST_PASSWORD = serializers.CharField() EMAIL_HOST_PASSWORD = serializers.CharField()
EMAIL_USE_SSL = serializers.BooleanField(default=False) EMAIL_USE_SSL = serializers.BooleanField(default=False)
EMAIL_USE_TLS = serializers.BooleanField(default=False) EMAIL_USE_TLS = serializers.BooleanField(default=False)
class LDAPTestSerializer(serializers.Serializer):
AUTH_LDAP_SERVER_URI = serializers.CharField(max_length=1024)
AUTH_LDAP_BIND_DN = serializers.CharField(max_length=1024)
AUTH_LDAP_BIND_PASSWORD = serializers.CharField()
AUTH_LDAP_SEARCH_OU = serializers.CharField()
AUTH_LDAP_SEARCH_FILTER = serializers.CharField()
AUTH_LDAP_USER_ATTR_MAP = serializers.CharField()
AUTH_LDAP_START_TLS = serializers.BooleanField(required=False)
...@@ -4,3 +4,4 @@ ...@@ -4,3 +4,4 @@
from django.dispatch import Signal from django.dispatch import Signal
django_ready = Signal() django_ready = Signal()
ldap_auth_enable = Signal(providing_args=["enabled"])
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import ldap
from django.dispatch import receiver from django.dispatch import receiver
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.conf import settings
from django_auth_ldap.config import LDAPSearch
from .models import Setting from .models import Setting
from .utils import get_logger from .utils import get_logger
from .signals import django_ready from .signals import django_ready, ldap_auth_enable
logger = get_logger(__file__) logger = get_logger(__file__)
...@@ -25,3 +26,17 @@ def refresh_all_settings_on_django_ready(sender, **kwargs): ...@@ -25,3 +26,17 @@ def refresh_all_settings_on_django_ready(sender, **kwargs):
logger.debug("Receive django ready signal") logger.debug("Receive django ready signal")
logger.debug(" - fresh all settings") logger.debug(" - fresh all settings")
Setting.refresh_all_settings() Setting.refresh_all_settings()
@receiver(ldap_auth_enable, dispatch_uid="my_unique_identifier")
def ldap_auth_on_changed(sender, enabled=True, **kwargs):
if enabled:
logger.debug("Enable LDAP auth")
if settings.AUTH_LDAP_BACKEND not in settings.AUTH_LDAP_BACKEND:
settings.AUTHENTICATION_BACKENDS.insert(0, settings.AUTH_LDAP_BACKEND)
else:
logger.debug("Disable LDAP auth")
if settings.AUTH_LDAP_BACKEND in settings.AUTHENTICATION_BACKENDS:
settings.AUTHENTICATION_BACKENDS.remove(settings.AUTH_LDAP_BACKEND)
{% extends 'base.html' %}
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% load common_tags %}
{% 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 class="active">
<a href="" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
</li>
<li>
<a href="{% url 'settings:email-setting' %}" class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
</li>
<li>
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP 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 btn-test" type="button"> {% trans 'Test connection' %}</button>
<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>
$(document).ready(function () {
})
.on("click", ".btn-test", function () {
var data = {};
var form = $("form").serializeArray();
$.each(form, function (i, field) {
data[field.name] = field.value;
});
var the_url = "{% url 'api-common:mail-testing' %}";
function error(message) {
toastr.error(message)
}
function success(message) {
toastr.success(message.msg)
}
APIUpdateAttr({
url: the_url,
body: JSON.stringify(data),
method: "POST",
flash_message: false,
success: success,
error: error
});
})
</script>
{% endblock %}
...@@ -4,10 +4,6 @@ ...@@ -4,10 +4,6 @@
{% load i18n %} {% load i18n %}
{% load common_tags %} {% 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 %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
<div class="row"> <div class="row">
...@@ -16,10 +12,13 @@ ...@@ -16,10 +12,13 @@
<div class="panel-options"> <div class="panel-options">
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li> <li>
<a href="" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a> <a href="{% url 'settings:basic-setting' %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
</li> </li>
<li class="active"> <li class="active">
<a class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a> <a href="{% url 'settings:email-setting' %}" class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
</li>
<li>
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP setting' %} </a>
</li> </li>
</ul> </ul>
</div> </div>
...@@ -53,6 +52,7 @@ ...@@ -53,6 +52,7 @@
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-4 col-sm-offset-2"> <div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default btn-test" type="button"> {% trans 'Test connection' %}</button>
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button> <button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button> <button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div> </div>
...@@ -69,5 +69,33 @@ ...@@ -69,5 +69,33 @@
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
$(document).ready(function () {
})
.on("click", ".btn-test", function () {
var data = {};
var form = $("form").serializeArray();
$.each(form, function (i, field) {
data[field.name] = field.value;
});
var the_url = "{% url 'api-common:mail-testing' %}";
function error(message) {
toastr.error(message)
}
function success(message) {
toastr.success(message.msg)
}
APIUpdateAttr({
url: the_url,
body: JSON.stringify(data),
method: "POST",
flash_message: false,
success: success,
error: error
});
})
</script> </script>
{% endblock %} {% endblock %}
{% extends 'base.html' %}
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% load common_tags %}
{% 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="{% url 'settings:basic-setting' %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Basic setting' %}</a>
</li>
<li>
<a href="{% url 'settings:email-setting' %}" class="text-center"><i class="fa fa-envelope"></i> {% trans 'Email setting' %} </a>
</li>
<li class="active">
<a href="{% url 'settings:ldap-setting' %}" class="text-center"><i class="fa fa-archive"></i> {% trans 'LDAP 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 btn-test" type="button"> {% trans 'Test connection' %}</button>
<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>
$(document).ready(function () {
})
.on("click", ".btn-test", function () {
var data = {};
var form = $("form").serializeArray();
$.each(form, function (i, field) {
data[field.name] = field.value;
});
var the_url = "{% url 'api-common:ldap-testing' %}";
function error(message) {
toastr.error(message)
}
function success(message) {
toastr.success(message.msg)
}
APIUpdateAttr({
url: the_url,
body: JSON.stringify(data),
method: "POST",
flash_message: false,
success: success,
error: error
});
})
</script>
{% endblock %}
...@@ -8,4 +8,6 @@ app_name = 'common' ...@@ -8,4 +8,6 @@ app_name = 'common'
urlpatterns = [ urlpatterns = [
url(r'^v1/mail/testing/$', api.MailTestingAPI.as_view(), name='mail-testing'), url(r'^v1/mail/testing/$', api.MailTestingAPI.as_view(), name='mail-testing'),
url(r'^v1/ldap/testing/$', api.LDAPTestingAPI.as_view(), name='ldap-testing'),
url(r'^v1/django-settings/$', api.DjangoSettingsAPI.as_view(), name='django-settings'),
] ]
...@@ -7,5 +7,7 @@ from .. import views ...@@ -7,5 +7,7 @@ from .. import views
app_name = 'common' app_name = 'common'
urlpatterns = [ urlpatterns = [
url(r'^$', views.BasicSettingView.as_view(), name='basic-setting'),
url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'), url(r'^email/$', views.EmailSettingView.as_view(), name='email-setting'),
url(r'^ldap/$', views.LDAPSettingView.as_view(), name='ldap-setting'),
] ]
...@@ -91,7 +91,7 @@ class Signer(metaclass=Singleton): ...@@ -91,7 +91,7 @@ class Signer(metaclass=Singleton):
def date_expired_default(): def date_expired_default():
try: try:
years = int(settings.CONFIG.DEFAULT_EXPIRED_YEARS) years = int(settings.DEFAULT_EXPIRED_YEARS)
except TypeError: except TypeError:
years = 70 years = 70
return timezone.now() + timezone.timedelta(days=365*years) return timezone.now() + timezone.timedelta(days=365*years)
......
from django.views.generic import View from django.views.generic import View, TemplateView
from django.shortcuts import render from django.shortcuts import render, redirect
from django.contrib import messages from django.contrib import messages
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from .forms import EmailSettingForm from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm
from .mixins import AdminUserRequiredMixin from .mixins import AdminUserRequiredMixin
from .signals import ldap_auth_enable
class EmailSettingView(AdminUserRequiredMixin, View): class BasicSettingView(AdminUserRequiredMixin, TemplateView):
form_class = BasicSettingForm
template_name = "common/basic_setting.html"
def get_context_data(self, **kwargs):
context = {
'app': _('Settings'),
'action': _('Basic setting'),
'form': self.form_class(),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
form.save()
if "AUTH_LDAP" in form.cleaned_data:
ldap_auth_enable.send(form.cleaned_data["AUTH_LDAP"])
messages.success(request, _("Update basic setting successfully"))
return redirect('settings:basic-setting')
else:
context = self.get_context_data()
context.update({"form": form})
return render(request, self.template_name, context)
class EmailSettingView(AdminUserRequiredMixin, TemplateView):
form_class = EmailSettingForm form_class = EmailSettingForm
template_name = "common/email_setting.html" template_name = "common/email_setting.html"
def get(self, request): def get_context_data(self, **kwargs):
context = { context = {
'app': 'settings', 'app': _('Settings'),
'action': 'Email setting', 'action': _('Email setting'),
"form": EmailSettingForm(), 'form': self.form_class(),
} }
return render(request, self.template_name, context) kwargs.update(context)
return super().get_context_data(**kwargs)
def post(self, request): def post(self, request):
form = self.form_class(request.POST) form = self.form_class(request.POST)
if form.is_valid(): if form.is_valid():
form.save() form.save()
messages.success(request, _("Update email setting successfully")) messages.success(request, _("Update email setting successfully"))
return redirect('settings:email-setting')
else:
context = self.get_context_data()
context.update({"form": form})
return render(request, self.template_name, context)
class LDAPSettingView(AdminUserRequiredMixin, TemplateView):
form_class = LDAPSettingForm
template_name = "common/ldap_setting.html"
def get_context_data(self, **kwargs):
context = { context = {
'app': 'settings', 'app': _('Settings'),
'action': 'Email setting', 'action': _('LDAP setting'),
"form": EmailSettingForm(), 'form': self.form_class(),
} }
kwargs.update(context)
return super().get_context_data(**kwargs)
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
form.save()
messages.success(request, _("Update ldap setting successfully"))
return redirect('settings:ldap-setting')
else:
context = self.get_context_data()
context.update({"form": form})
return render(request, self.template_name, context) return render(request, self.template_name, context)
...@@ -15,7 +15,6 @@ import sys ...@@ -15,7 +15,6 @@ import sys
import ldap import ldap
from django_auth_ldap.config import LDAPSearch from django_auth_ldap.config import LDAPSearch
from django.urls import reverse_lazy from django.urls import reverse_lazy
...@@ -303,18 +302,28 @@ AUTH_USER_MODEL = 'users.User' ...@@ -303,18 +302,28 @@ AUTH_USER_MODEL = 'users.User'
# Auth LDAP settings # Auth LDAP settings
if CONFIG.AUTH_LDAP: AUTH_LDAP = CONFIG.AUTH_LDAP
AUTHENTICATION_BACKENDS.insert(0, 'django_auth_ldap.backend.LDAPBackend') AUTH_LDAP_SERVER_URI = CONFIG.AUTH_LDAP_SERVER_URI
AUTH_LDAP_SERVER_URI = CONFIG.AUTH_LDAP_SERVER_URI AUTH_LDAP_BIND_DN = CONFIG.AUTH_LDAP_BIND_DN
AUTH_LDAP_BIND_DN = CONFIG.AUTH_LDAP_BIND_DN AUTH_LDAP_BIND_PASSWORD = CONFIG.AUTH_LDAP_BIND_PASSWORD
AUTH_LDAP_BIND_PASSWORD = CONFIG.AUTH_LDAP_BIND_PASSWORD AUTH_LDAP_SEARCH_OU = CONFIG.AUTH_LDAP_SEARCH_OU
AUTH_LDAP_USER_SEARCH = LDAPSearch( AUTH_LDAP_SEARCH_FILTER = CONFIG.AUTH_LDAP_SEARCH_FILTER
CONFIG.AUTH_LDAP_SEARCH_OU, AUTH_LDAP_START_TLS = CONFIG.AUTH_LDAP_START_TLS
ldap.SCOPE_SUBTREE, AUTH_LDAP_USER_ATTR_MAP = CONFIG.AUTH_LDAP_USER_ATTR_MAP
CONFIG.AUTH_LDAP_SEARCH_FILTER AUTH_LDAP_USER_SEARCH = LDAPSearch(
) AUTH_LDAP_SEARCH_OU, ldap.SCOPE_SUBTREE, AUTH_LDAP_SEARCH_FILTER,
AUTH_LDAP_START_TLS = CONFIG.AUTH_LDAP_START_TLS )
AUTH_LDAP_USER_ATTR_MAP = CONFIG.AUTH_LDAP_USER_ATTR_MAP AUTH_LDAP_GROUP_SEARCH_OU = CONFIG.AUTH_LDAP_GROUP_SEARCH_OU
AUTH_LDAP_GROUP_SEARCH_FILTER = CONFIG.AUTH_LDAP_GROUP_SEARCH_FILTER
AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
AUTH_LDAP_GROUP_SEARCH_OU, ldap.SCOPE_SUBTREE, AUTH_LDAP_GROUP_SEARCH_FILTER
)
AUTH_LDAP_ALWAYS_UPDATE_USER = True
AUTH_LDAP_BACKEND = 'django_auth_ldap.backend.LDAPBackend'
if AUTH_LDAP:
AUTHENTICATION_BACKENDS.insert(0, AUTH_LDAP_BACKEND)
# Celery using redis as broker # Celery using redis as broker
CELERY_BROKER_URL = 'redis://:%(password)s@%(host)s:%(port)s/3' % { CELERY_BROKER_URL = 'redis://:%(password)s@%(host)s:%(port)s/3' % {
...@@ -367,3 +376,7 @@ BOOTSTRAP3 = { ...@@ -367,3 +376,7 @@ BOOTSTRAP3 = {
'set_placeholder': True, 'set_placeholder': True,
'success_css_class': '', 'success_css_class': '',
} }
TOKEN_EXPIRATION = CONFIG.TOKEN_EXPIRATION or 3600
DISPLAY_PER_PAGE = CONFIG.DISPLAY_PER_PAGE
DEFAULT_EXPIRED_YEARS = 70
...@@ -10,7 +10,7 @@ from .hands import AdminUserRequiredMixin ...@@ -10,7 +10,7 @@ from .hands import AdminUserRequiredMixin
class TaskListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView): class TaskListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.DISPLAY_PER_PAGE
model = Task model = Task
ordering = ('-date_created',) ordering = ('-date_created',)
context_object_name = 'task_list' context_object_name = 'task_list'
......
...@@ -93,7 +93,7 @@ class AssetPermissionUserView(AdminUserRequiredMixin, ...@@ -93,7 +93,7 @@ class AssetPermissionUserView(AdminUserRequiredMixin,
ListView): ListView):
template_name = 'perms/asset_permission_user.html' template_name = 'perms/asset_permission_user.html'
context_object_name = 'asset_permission' context_object_name = 'asset_permission'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.DISPLAY_PER_PAGE
object = None object = None
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
...@@ -123,7 +123,7 @@ class AssetPermissionAssetView(AdminUserRequiredMixin, ...@@ -123,7 +123,7 @@ class AssetPermissionAssetView(AdminUserRequiredMixin,
ListView): ListView):
template_name = 'perms/asset_permission_asset.html' template_name = 'perms/asset_permission_asset.html'
context_object_name = 'asset_permission' context_object_name = 'asset_permission'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.DISPLAY_PER_PAGE
object = None object = None
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
......
...@@ -157,6 +157,11 @@ function APIUpdateAttr(props) { ...@@ -157,6 +157,11 @@ function APIUpdateAttr(props) {
props = props || {}; props = props || {};
var success_message = props.success_message || '更新成功!'; var success_message = props.success_message || '更新成功!';
var fail_message = props.fail_message || '更新时发生未知错误.'; var fail_message = props.fail_message || '更新时发生未知错误.';
var flash_message = true;
if (props.flash_message === false){
flash_message = false;
}
$.ajax({ $.ajax({
url: props.url, url: props.url,
type: props.method || "PATCH", type: props.method || "PATCH",
...@@ -164,12 +169,16 @@ function APIUpdateAttr(props) { ...@@ -164,12 +169,16 @@ function APIUpdateAttr(props) {
contentType: props.content_type || "application/json; charset=utf-8", contentType: props.content_type || "application/json; charset=utf-8",
dataType: props.data_type || "json" dataType: props.data_type || "json"
}).done(function(data, textStatue, jqXHR) { }).done(function(data, textStatue, jqXHR) {
if (flash_message) {
toastr.success(success_message); toastr.success(success_message);
}
if (typeof props.success === 'function') { if (typeof props.success === 'function') {
return props.success(data); return props.success(data);
} }
}).fail(function(jqXHR, textStatus, errorThrown) { }).fail(function(jqXHR, textStatus, errorThrown) {
if (flash_message) {
toastr.error(fail_message); toastr.error(fail_message);
}
if (typeof props.error === 'function') { if (typeof props.error === 'function') {
return props.error(jqXHR.responseText); return props.error(jqXHR.responseText);
} }
......
...@@ -69,8 +69,8 @@ ...@@ -69,8 +69,8 @@
{# <li id="download"><a href="">{% trans 'File download' %}</a></li>#} {# <li id="download"><a href="">{% trans 'File download' %}</a></li>#}
{# </ul>#} {# </ul>#}
{#</li>#} {#</li>#}
{#<li id="">#} <li id="settings">
{# <a href="">#} <a href="{% url 'settings:email-setting' %}">
{# <i class="fa fa-gears"></i> <span class="nav-label">{% trans 'Settings' %}</span><span class="label label-info pull-right"></span>#} <i class="fa fa-gears"></i> <span class="nav-label">{% trans 'Settings' %}</span><span class="label label-info pull-right"></span>
{# </a>#} </a>
{#</li>#} </li>
\ No newline at end of file \ No newline at end of file
...@@ -19,7 +19,7 @@ class CommandListView(DatetimeSearchMixin, ListView): ...@@ -19,7 +19,7 @@ class CommandListView(DatetimeSearchMixin, ListView):
model = Command model = Command
template_name = "terminal/command_list.html" template_name = "terminal/command_list.html"
context_object_name = 'command_list' context_object_name = 'command_list'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.DISPLAY_PER_PAGE
command = user = asset = system_user = "" command = user = asset = system_user = ""
date_from = date_to = None date_from = date_to = None
......
...@@ -26,7 +26,7 @@ class SessionListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView): ...@@ -26,7 +26,7 @@ class SessionListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
model = Session model = Session
template_name = 'terminal/session_list.html' template_name = 'terminal/session_list.html'
context_object_name = 'session_list' context_object_name = 'session_list'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.DISPLAY_PER_PAGE
user = asset = system_user = '' user = asset = system_user = ''
date_from = date_to = None date_from = date_to = None
......
...@@ -113,7 +113,7 @@ class AccessKeyAuthentication(authentication.BaseAuthentication): ...@@ -113,7 +113,7 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
class AccessTokenAuthentication(authentication.BaseAuthentication): class AccessTokenAuthentication(authentication.BaseAuthentication):
keyword = 'Bearer' keyword = 'Bearer'
model = User model = User
expiration = settings.CONFIG.TOKEN_EXPIRATION or 3600 expiration = settings.TOKEN_EXPIRATION or 3600
def authenticate(self, request): def authenticate(self, request):
auth = authentication.get_authorization_header(request).split() auth = authentication.get_authorization_header(request).split()
......
...@@ -148,12 +148,7 @@ class User(AbstractUser): ...@@ -148,12 +148,7 @@ class User(AbstractUser):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.name: if not self.name:
self.name = self.username self.name = self.username
super().save(*args, **kwargs) super().save(*args, **kwargs)
# Add the current user to the default group.
if not self.groups.count():
group = UserGroup.initial()
self.groups.add(group)
@property @property
def private_token(self): def private_token(self):
...@@ -253,6 +248,7 @@ class User(AbstractUser): ...@@ -253,6 +248,7 @@ class User(AbstractUser):
#: Use this method initial user #: Use this method initial user
@classmethod @classmethod
def initial(cls): def initial(cls):
from .group import UserGroup
user = cls(username='admin', user = cls(username='admin',
email='admin@jumpserver.org', email='admin@jumpserver.org',
name=_('Administrator'), name=_('Administrator'),
...@@ -268,6 +264,7 @@ class User(AbstractUser): ...@@ -268,6 +264,7 @@ class User(AbstractUser):
from random import seed, choice from random import seed, choice
import forgery_py import forgery_py
from django.db import IntegrityError from django.db import IntegrityError
from .group import UserGroup
seed() seed()
for i in range(count): for i in range(count):
......
...@@ -2,16 +2,19 @@ ...@@ -2,16 +2,19 @@
# #
from django.dispatch import Signal, receiver from django.dispatch import Signal, receiver
from django.db.models.signals import post_save
from common.utils import get_logger from common.utils import get_logger
from .models import User
logger = get_logger(__file__) logger = get_logger(__file__)
on_user_created = Signal(providing_args=['user', 'request'])
@receiver(on_user_created) @receiver(post_save, sender=User)
def send_user_add_mail_to_user(sender, user=None, **kwargs): def on_user_created(sender, instance=None, created=False, **kwargs):
if created:
logger.debug("Receive user `{}` create signal".format(instance.name))
from .utils import send_user_created_mail from .utils import send_user_created_mail
logger.debug("Receive asset create signal, update asset hardware info") logger.info(" - Sending welcome mail ...".format(instance.name))
send_user_created_mail(user) send_user_created_mail(instance)
...@@ -151,12 +151,12 @@ def check_user_valid(**kwargs): ...@@ -151,12 +151,12 @@ def check_user_valid(**kwargs):
return None, _('Password or SSH public key invalid') return None, _('Password or SSH public key invalid')
def refresh_token(token, user, expiration=settings.CONFIG.TOKEN_EXPIRATION or 3600): def refresh_token(token, user, expiration=settings.TOKEN_EXPIRATION or 3600):
cache.set(token, user.id, expiration) cache.set(token, user.id, expiration)
def generate_token(request, user): def generate_token(request, user):
expiration = settings.CONFIG.TOKEN_EXPIRATION or 3600 expiration = settings.TOKEN_EXPIRATION or 3600
remote_addr = request.META.get('REMOTE_ADDR', '') remote_addr = request.META.get('REMOTE_ADDR', '')
if not isinstance(remote_addr, bytes): if not isinstance(remote_addr, bytes):
remote_addr = remote_addr.encode("utf-8") remote_addr = remote_addr.encode("utf-8")
......
...@@ -185,7 +185,7 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView): ...@@ -185,7 +185,7 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
user.is_public_key_valid = True user.is_public_key_valid = True
user.save() user.save()
context = { context = {
'user_guide_url': settings.CONFIG.USER_GUIDE_URL 'user_guide_url': settings.USER_GUIDE_URL
} }
return render(self.request, 'users/first_login_done.html', context) return render(self.request, 'users/first_login_done.html', context)
...@@ -216,7 +216,7 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView): ...@@ -216,7 +216,7 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
class LoginLogListView(DatetimeSearchMixin, ListView): class LoginLogListView(DatetimeSearchMixin, ListView):
template_name = 'users/login_log_list.html' template_name = 'users/login_log_list.html'
model = LoginLog model = LoginLog
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.DISPLAY_PER_PAGE
user = keyword = "" user = keyword = ""
date_to = date_from = None date_to = date_from = None
......
...@@ -76,7 +76,6 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): ...@@ -76,7 +76,6 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
user = form.save(commit=False) user = form.save(commit=False)
user.created_by = self.request.user.username or 'System' user.created_by = self.request.user.username or 'System'
user.save() user.save()
on_user_created.send(self.__class__, user=user)
return super().form_valid(form) return super().form_valid(form)
......
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