mirror of https://github.com/jumpserver/jumpserver
[Feature] 添加basic settings
parent
121f56f44b
commit
abb1a40a4f
|
@ -85,7 +85,7 @@ class AdminUserDetailView(AdminUserRequiredMixin, DetailView):
|
|||
|
||||
|
||||
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'
|
||||
context_object_name = 'admin_user'
|
||||
object = None
|
||||
|
|
|
@ -92,7 +92,7 @@ class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
|
|||
|
||||
|
||||
class AssetModalListView(AdminUserRequiredMixin, ListView):
|
||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||
paginate_by = settings.DISPLAY_PER_PAGE
|
||||
model = Asset
|
||||
context_object_name = 'asset_modal_list'
|
||||
template_name = 'assets/asset_modal_list.html'
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import json
|
||||
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.views import Response
|
||||
from ldap3 import Server, Connection
|
||||
from django.core.mail import get_connection, send_mail
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
|
||||
from .permissions import IsSuperUser
|
||||
from .serializers import MailTestSerializer
|
||||
from .serializers import MailTestSerializer, LDAPTestSerializer
|
||||
|
||||
|
||||
class MailTestingAPI(APIView):
|
||||
|
@ -27,7 +31,6 @@ class MailTestingAPI(APIView):
|
|||
"use_tls": serializer.validated_data["EMAIL_USE_TLS"]
|
||||
}
|
||||
connection = get_connection(timeout=5, **kwargs)
|
||||
|
||||
try:
|
||||
connection.open()
|
||||
except Exception as e:
|
||||
|
@ -40,3 +43,68 @@ class MailTestingAPI(APIView):
|
|||
return Response({"error": str(e)}, status=401)
|
||||
|
||||
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)
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# -*- 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 _
|
|||
from django.db import transaction
|
||||
|
||||
from .models import Setting
|
||||
from .fields import DictField
|
||||
|
||||
|
||||
def to_model_value(value):
|
||||
|
@ -18,7 +19,10 @@ def to_model_value(value):
|
|||
|
||||
def to_form_value(value):
|
||||
try:
|
||||
return json.loads(value)
|
||||
data = json.loads(value)
|
||||
if isinstance(data, dict):
|
||||
data = value
|
||||
return data
|
||||
except json.JSONDecodeError:
|
||||
return ''
|
||||
|
||||
|
@ -26,23 +30,26 @@ def to_form_value(value):
|
|||
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)
|
||||
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")
|
||||
|
||||
settings = Setting.objects.all()
|
||||
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
|
||||
if value == to_form_value(getattr(settings, name).value):
|
||||
continue
|
||||
|
||||
defaults = {
|
||||
'name': name,
|
||||
'value': to_model_value(value)
|
||||
|
@ -52,6 +59,24 @@ class BaseForm(forms.Form):
|
|||
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):
|
||||
EMAIL_HOST = forms.CharField(
|
||||
max_length=1024, label=_("SMTP host"), initial='smtp.jumpserver.org'
|
||||
|
@ -72,3 +97,35 @@ class EmailSettingForm(BaseForm):
|
|||
label=_("Use TLS"), initial=False, required=False,
|
||||
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
|
||||
)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import json
|
||||
|
||||
import ldap
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.conf import settings
|
||||
from django_auth_ldap.config import LDAPSearch
|
||||
|
||||
|
||||
class SettingQuerySet(models.QuerySet):
|
||||
|
@ -30,6 +32,13 @@ class Setting(models.Model):
|
|||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def value_(self):
|
||||
try:
|
||||
return json.loads(self.value)
|
||||
except json.JSONDecodeError:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def refresh_all_settings(cls):
|
||||
settings_list = cls.objects.all()
|
||||
|
@ -43,5 +52,17 @@ class Setting(models.Model):
|
|||
return
|
||||
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:
|
||||
db_table = "settings"
|
||||
|
|
|
@ -8,3 +8,14 @@ class MailTestSerializer(serializers.Serializer):
|
|||
EMAIL_HOST_PASSWORD = serializers.CharField()
|
||||
EMAIL_USE_SSL = 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 @@
|
|||
from django.dispatch import Signal
|
||||
|
||||
django_ready = Signal()
|
||||
ldap_auth_enable = Signal(providing_args=["enabled"])
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
import ldap
|
||||
from django.dispatch import receiver
|
||||
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 .utils import get_logger
|
||||
from .signals import django_ready
|
||||
|
||||
from .signals import django_ready, ldap_auth_enable
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
@ -25,3 +26,17 @@ def refresh_all_settings_on_django_ready(sender, **kwargs):
|
|||
logger.debug("Receive django ready signal")
|
||||
logger.debug(" - fresh 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)
|
||||
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
{% 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 @@
|
|||
{% 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">
|
||||
|
@ -16,10 +12,13 @@
|
|||
<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>
|
||||
<a href="{% url 'settings:basic-setting' %}" 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>
|
||||
<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>
|
||||
|
@ -53,6 +52,7 @@
|
|||
<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>
|
||||
|
@ -69,5 +69,33 @@
|
|||
{% 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 %}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
{% 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'
|
|||
|
||||
urlpatterns = [
|
||||
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
|
|||
app_name = 'common'
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.BasicSettingView.as_view(), name='basic-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):
|
|||
|
||||
def date_expired_default():
|
||||
try:
|
||||
years = int(settings.CONFIG.DEFAULT_EXPIRED_YEARS)
|
||||
years = int(settings.DEFAULT_EXPIRED_YEARS)
|
||||
except TypeError:
|
||||
years = 70
|
||||
return timezone.now() + timezone.timedelta(days=365*years)
|
||||
|
|
|
@ -1,33 +1,85 @@
|
|||
from django.views.generic import View
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import View, TemplateView
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib import messages
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from .forms import EmailSettingForm
|
||||
from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm
|
||||
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
|
||||
template_name = "common/email_setting.html"
|
||||
|
||||
def get(self, request):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'settings',
|
||||
'action': 'Email setting',
|
||||
"form": EmailSettingForm(),
|
||||
'app': _('Settings'),
|
||||
'action': _('Email setting'),
|
||||
'form': self.form_class(),
|
||||
}
|
||||
return render(request, self.template_name, context)
|
||||
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 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 = {
|
||||
'app': 'settings',
|
||||
'action': 'Email setting',
|
||||
"form": EmailSettingForm(),
|
||||
'app': _('Settings'),
|
||||
'action': _('LDAP setting'),
|
||||
'form': self.form_class(),
|
||||
}
|
||||
return render(request, self.template_name, context)
|
||||
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)
|
||||
|
|
|
@ -15,7 +15,6 @@ import sys
|
|||
|
||||
import ldap
|
||||
from django_auth_ldap.config import LDAPSearch
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
|
||||
|
@ -303,18 +302,28 @@ AUTH_USER_MODEL = 'users.User'
|
|||
|
||||
|
||||
# Auth LDAP settings
|
||||
if CONFIG.AUTH_LDAP:
|
||||
AUTHENTICATION_BACKENDS.insert(0, 'django_auth_ldap.backend.LDAPBackend')
|
||||
AUTH_LDAP_SERVER_URI = CONFIG.AUTH_LDAP_SERVER_URI
|
||||
AUTH_LDAP_BIND_DN = CONFIG.AUTH_LDAP_BIND_DN
|
||||
AUTH_LDAP_BIND_PASSWORD = CONFIG.AUTH_LDAP_BIND_PASSWORD
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearch(
|
||||
CONFIG.AUTH_LDAP_SEARCH_OU,
|
||||
ldap.SCOPE_SUBTREE,
|
||||
CONFIG.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 = CONFIG.AUTH_LDAP
|
||||
AUTH_LDAP_SERVER_URI = CONFIG.AUTH_LDAP_SERVER_URI
|
||||
AUTH_LDAP_BIND_DN = CONFIG.AUTH_LDAP_BIND_DN
|
||||
AUTH_LDAP_BIND_PASSWORD = CONFIG.AUTH_LDAP_BIND_PASSWORD
|
||||
AUTH_LDAP_SEARCH_OU = CONFIG.AUTH_LDAP_SEARCH_OU
|
||||
AUTH_LDAP_SEARCH_FILTER = CONFIG.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_USER_SEARCH = LDAPSearch(
|
||||
AUTH_LDAP_SEARCH_OU, ldap.SCOPE_SUBTREE, AUTH_LDAP_SEARCH_FILTER,
|
||||
)
|
||||
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_BROKER_URL = 'redis://:%(password)s@%(host)s:%(port)s/3' % {
|
||||
|
@ -367,3 +376,7 @@ BOOTSTRAP3 = {
|
|||
'set_placeholder': True,
|
||||
'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
|
|||
|
||||
|
||||
class TaskListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
|
||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||
paginate_by = settings.DISPLAY_PER_PAGE
|
||||
model = Task
|
||||
ordering = ('-date_created',)
|
||||
context_object_name = 'task_list'
|
||||
|
|
|
@ -93,7 +93,7 @@ class AssetPermissionUserView(AdminUserRequiredMixin,
|
|||
ListView):
|
||||
template_name = 'perms/asset_permission_user.html'
|
||||
context_object_name = 'asset_permission'
|
||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||
paginate_by = settings.DISPLAY_PER_PAGE
|
||||
object = None
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
@ -123,7 +123,7 @@ class AssetPermissionAssetView(AdminUserRequiredMixin,
|
|||
ListView):
|
||||
template_name = 'perms/asset_permission_asset.html'
|
||||
context_object_name = 'asset_permission'
|
||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||
paginate_by = settings.DISPLAY_PER_PAGE
|
||||
object = None
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
|
|
@ -157,6 +157,11 @@ function APIUpdateAttr(props) {
|
|||
props = props || {};
|
||||
var success_message = props.success_message || '更新成功!';
|
||||
var fail_message = props.fail_message || '更新时发生未知错误.';
|
||||
var flash_message = true;
|
||||
if (props.flash_message === false){
|
||||
flash_message = false;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: props.url,
|
||||
type: props.method || "PATCH",
|
||||
|
@ -164,12 +169,16 @@ function APIUpdateAttr(props) {
|
|||
contentType: props.content_type || "application/json; charset=utf-8",
|
||||
dataType: props.data_type || "json"
|
||||
}).done(function(data, textStatue, jqXHR) {
|
||||
toastr.success(success_message);
|
||||
if (flash_message) {
|
||||
toastr.success(success_message);
|
||||
}
|
||||
if (typeof props.success === 'function') {
|
||||
return props.success(data);
|
||||
}
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
toastr.error(fail_message);
|
||||
if (flash_message) {
|
||||
toastr.error(fail_message);
|
||||
}
|
||||
if (typeof props.error === 'function') {
|
||||
return props.error(jqXHR.responseText);
|
||||
}
|
||||
|
|
|
@ -69,8 +69,8 @@
|
|||
{# <li id="download"><a href="">{% trans 'File download' %}</a></li>#}
|
||||
{# </ul>#}
|
||||
{#</li>#}
|
||||
{#<li id="">#}
|
||||
{# <a href="">#}
|
||||
{# <i class="fa fa-gears"></i> <span class="nav-label">{% trans 'Settings' %}</span><span class="label label-info pull-right"></span>#}
|
||||
{# </a>#}
|
||||
{#</li>#}
|
||||
<li id="settings">
|
||||
<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>
|
||||
</a>
|
||||
</li>
|
|
@ -19,7 +19,7 @@ class CommandListView(DatetimeSearchMixin, ListView):
|
|||
model = Command
|
||||
template_name = "terminal/command_list.html"
|
||||
context_object_name = 'command_list'
|
||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||
paginate_by = settings.DISPLAY_PER_PAGE
|
||||
command = user = asset = system_user = ""
|
||||
date_from = date_to = None
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ class SessionListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
|
|||
model = Session
|
||||
template_name = 'terminal/session_list.html'
|
||||
context_object_name = 'session_list'
|
||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||
paginate_by = settings.DISPLAY_PER_PAGE
|
||||
user = asset = system_user = ''
|
||||
date_from = date_to = None
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
|
|||
class AccessTokenAuthentication(authentication.BaseAuthentication):
|
||||
keyword = 'Bearer'
|
||||
model = User
|
||||
expiration = settings.CONFIG.TOKEN_EXPIRATION or 3600
|
||||
expiration = settings.TOKEN_EXPIRATION or 3600
|
||||
|
||||
def authenticate(self, request):
|
||||
auth = authentication.get_authorization_header(request).split()
|
||||
|
|
|
@ -148,12 +148,7 @@ class User(AbstractUser):
|
|||
def save(self, *args, **kwargs):
|
||||
if not self.name:
|
||||
self.name = self.username
|
||||
|
||||
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
|
||||
def private_token(self):
|
||||
|
@ -253,6 +248,7 @@ class User(AbstractUser):
|
|||
#: Use this method initial user
|
||||
@classmethod
|
||||
def initial(cls):
|
||||
from .group import UserGroup
|
||||
user = cls(username='admin',
|
||||
email='admin@jumpserver.org',
|
||||
name=_('Administrator'),
|
||||
|
@ -268,6 +264,7 @@ class User(AbstractUser):
|
|||
from random import seed, choice
|
||||
import forgery_py
|
||||
from django.db import IntegrityError
|
||||
from .group import UserGroup
|
||||
|
||||
seed()
|
||||
for i in range(count):
|
||||
|
|
|
@ -2,16 +2,19 @@
|
|||
#
|
||||
|
||||
from django.dispatch import Signal, receiver
|
||||
from django.db.models.signals import post_save
|
||||
|
||||
from common.utils import get_logger
|
||||
from .models import User
|
||||
|
||||
logger = get_logger(__file__)
|
||||
on_user_created = Signal(providing_args=['user', 'request'])
|
||||
|
||||
|
||||
@receiver(on_user_created)
|
||||
def send_user_add_mail_to_user(sender, user=None, **kwargs):
|
||||
from .utils import send_user_created_mail
|
||||
logger.debug("Receive asset create signal, update asset hardware info")
|
||||
send_user_created_mail(user)
|
||||
@receiver(post_save, sender=User)
|
||||
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
|
||||
logger.info(" - Sending welcome mail ...".format(instance.name))
|
||||
send_user_created_mail(instance)
|
||||
|
||||
|
|
|
@ -151,12 +151,12 @@ def check_user_valid(**kwargs):
|
|||
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)
|
||||
|
||||
|
||||
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', '')
|
||||
if not isinstance(remote_addr, bytes):
|
||||
remote_addr = remote_addr.encode("utf-8")
|
||||
|
|
|
@ -185,7 +185,7 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
|
|||
user.is_public_key_valid = True
|
||||
user.save()
|
||||
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)
|
||||
|
||||
|
@ -216,7 +216,7 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
|
|||
class LoginLogListView(DatetimeSearchMixin, ListView):
|
||||
template_name = 'users/login_log_list.html'
|
||||
model = LoginLog
|
||||
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
|
||||
paginate_by = settings.DISPLAY_PER_PAGE
|
||||
user = keyword = ""
|
||||
date_to = date_from = None
|
||||
|
||||
|
|
|
@ -76,7 +76,6 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
|
|||
user = form.save(commit=False)
|
||||
user.created_by = self.request.user.username or 'System'
|
||||
user.save()
|
||||
on_user_created.send(self.__class__, user=user)
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue