Unverified Commit db290609 authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #2551 from jumpserver/dev

Dev
parents 30ba1e58 4bc5eced
......@@ -8,11 +8,11 @@
----
Jumpserver是全球首款完全开源的堡垒机,使用GNU GPL v2.0开源协议,是符合 4A 的专业运维审计系统。
Jumpserver 是全球首款完全开源的堡垒机,使用 GNU GPL v2.0 开源协议,是符合 4A 的专业运维审计系统。
Jumpserver使用Python / Django 进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web Terminal 解决方案,交互界面美观、用户体验好。
Jumpserver 使用 Python / Django 进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web Terminal 解决方案,交互界面美观、用户体验好。
Jumpserver采纳分布式架构,支持多机房跨区域部署,中心节点提供 API,各机房部署登录节点,可横向扩展、无并发限制。
Jumpserver 采纳分布式架构,支持多机房跨区域部署,中心节点提供 API,各机房部署登录节点,可横向扩展、无并发限制。
改变世界,从一点点开始。
......@@ -20,29 +20,30 @@ Jumpserver采纳分布式架构,支持多机房跨区域部署,中心节点
### 功能
![Jumpserver功能](https://jumpserver-release.oss-cn-hangzhou.aliyuncs.com/Jumpserver-14.png "Jumpserver功能")
![Jumpserver 功能](https://jumpserver-release.oss-cn-hangzhou.aliyuncs.com/Jumpserver-14.png "Jumpserver 功能")
### 开始使用
快速开始文档 [Docker安装](http://docs.jumpserver.org/zh/docs/dockerinstall.html)
快速开始文档 [Docker 安装](http://docs.jumpserver.org/zh/docs/dockerinstall.html)
一步一步安装文档 [详细部署](http://docs.jumpserver.org/zh/docs/step_by_step.html)
Step by Step 安装文档 [详细部署](http://docs.jumpserver.org/zh/docs/step_by_step.html)
也可以查看我们完整文档包括了使用和开发 [文档](http://docs.jumpserver.org)
也可以查看我们完整文档 [文档](http://docs.jumpserver.org)
### Demo 和 截图
### Demo、视频 和 截图
我们提供了DEMO和截图可以让你快速了解Jumpserver
我们提供了 Demo 、演示视频和截图可以让你快速了解 Jumpserver
[DEMO](https://demo.jumpserver.org)
[Demo](https://demo.jumpserver.org/auth/login/?next=/)
[视频](https://fit2cloud2-offline-installer.oss-cn-beijing.aliyuncs.com/tools/Jumpserver%20%E4%BB%8B%E7%BB%8Dv1.4.mp4)
[截图](http://docs.jumpserver.org/zh/docs/snapshot.html)
### SDK
我们还编写了一些SDK,供你其它系统快速和Jumpserver APi交互,
我们还编写了一些SDK,供你其它系统快速和 Jumpserver API 交互
- [python](https://github.com/jumpserver/jumpserver-python-sdk) Jumpserver其它组件使用这个SDK完成交互
- [java](https://github.com/KaiJunYan/jumpserver-java-sdk.git) 恺珺同学提供的Java版本的SDK
- [Python](https://github.com/jumpserver/jumpserver-python-sdk) Jumpserver其它组件使用这个SDK完成交互
- [Java](https://github.com/KaiJunYan/jumpserver-java-sdk.git) 恺珺同学提供的Java版本的SDK
### License & Copyright
......
This diff is collapsed.
......@@ -5,18 +5,21 @@ import os
import json
import jms_storage
from ldap3 import Server, Connection
from rest_framework.views import Response, APIView
from django.conf import settings
from django.core.mail import send_mail
from django.utils.translation import ugettext_lazy as _
from .models import Setting
from .utils import get_ldap_users_list, save_user
from .utils import LDAPUtil
from common.permissions import IsOrgAdmin, IsSuperUser
from common.utils import get_logger
from .serializers import MailTestSerializer, LDAPTestSerializer
logger = get_logger(__file__)
class MailTestingAPI(APIView):
permission_classes = (IsOrgAdmin,)
serializer_class = MailTestSerializer
......@@ -46,78 +49,78 @@ class LDAPTestingAPI(APIView):
serializer_class = LDAPTestSerializer
success_message = _("Test ldap success")
@staticmethod
def get_ldap_util(serializer):
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_ougroup = 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"]
try:
attr_map = json.loads(attr_map)
except json.JSONDecodeError:
return Response({"error": "AUTH_LDAP_USER_ATTR_MAP not valid"}, status=401)
util = LDAPUtil(
use_settings_config=False, server_uri=host, bind_dn=bind_dn,
password=password, use_ssl=use_ssl,
search_ougroup=search_ougroup, search_filter=search_filter,
attr_map=attr_map
)
return util
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_ougroup = 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"]
if not serializer.is_valid():
return Response({"error": str(serializer.errors)}, status=401)
try:
attr_map = json.loads(attr_map)
except json.JSONDecodeError:
return Response({"error": "AUTH_LDAP_USER_ATTR_MAP not valid"}, status=401)
util = self.get_ldap_util(serializer)
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)
try:
users = util.get_search_user_items()
except Exception as e:
return Response({"error": str(e)}, status=401)
users = []
for search_ou in str(search_ougroup).split("|"):
ok = conn.search(search_ou, search_filter % ({"user": "*"}),
attributes=list(attr_map.values()))
if not ok:
return Response({"error": _("Search no entry matched in ou {}").format(search_ou)}, status=401)
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)
if len(users) > 0:
return Response({"msg": _("Match {} s users").format(len(users))})
else:
return Response({"error": str(serializer.errors)}, status=401)
return Response({"error": "Have user but attr mapping error"}, status=401)
class LDAPSyncAPI(APIView):
class LDAPUserListApi(APIView):
permission_classes = (IsOrgAdmin,)
def get(self, request):
ldap_users_list = get_ldap_users_list()
if not isinstance(ldap_users_list, list):
return Response(ldap_users_list, status=401)
return Response(ldap_users_list)
util = LDAPUtil()
try:
users = util.get_search_user_items()
except Exception as e:
users = []
logger.error(e, exc_info=True)
else:
users = sorted(users, key=lambda u: (u['existing'], u['username']))
return Response(users)
class LDAPConfirmSyncAPI(APIView):
class LDAPUserSyncAPI(APIView):
permission_classes = (IsOrgAdmin,)
def post(self, request):
user_names = request.data.get('user_names', '')
if not user_names:
error = _('User is not currently selected, please check the user '
'you want to import')
return Response({'error': error}, status=401)
ldap_users_list = get_ldap_users_list(user_names=user_names)
if not isinstance(ldap_users_list, list):
return Response(ldap_users_list, status=401)
save_result = save_user(ldap_users_list)
if 'error' in save_result.keys():
return Response(save_result, status=401)
return Response(save_result)
util = LDAPUtil()
try:
result = util.sync_users(username_set=user_names)
except Exception as e:
logger.error(e, exc_info=True)
return Response({'error': str(e)}, status=401)
else:
msg = _("succeed: {} failed: {} total: {}").format(
result['succeed'], result['failed'], result['total']
)
return Response({'msg': msg})
class ReplayStorageCreateAPI(APIView):
......
......@@ -79,6 +79,8 @@ class Setting(models.Model):
obj.cleaned_value = data
else:
value = obj.cleaned_value
if value is None:
value = {}
value.update(data)
obj.cleaned_value = value
obj.save()
......
......@@ -4,7 +4,10 @@
{% block modal_class %}modal-lg{% endblock %}
{% block modal_id %}ldap_list_users_modal{% endblock %}
{% block modal_title%}{% trans "Ldap users" %}{% endblock %}
{% block modal_title%}{% trans "LDAP user list" %}{% endblock %}
{% block modal_help_message%} <div class="alert alert-info help-message" style="width: 838px; margin-left: 30px">{% trans 'Please submit the LDAP configuration before import' %}</div>{% endblock %}
{% block modal_body %}
<link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
<script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
......@@ -34,7 +37,7 @@
<th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Email' %}</th>
<th class="text-center">{% trans 'Is imported' %}</th>
<th class="text-center">{% trans 'Existing' %}</th>
</tr>
</thead>
<tbody>
......@@ -47,16 +50,25 @@
<script>
var ldap_users_table = 0;
function initLdapTable() {
function initLdapUsersTable() {
if(ldap_users_table){
return
}
var options = {
ele: $('#ldap_list_users_table'),
ajax_url: '{% url "api-settings:ldap-sync" %}',
ajax_url: '{% url "api-settings:ldap-user-list" %}',
columnDefs: [
{targets: 4, createdCell: function (td, cellData, rowData) {
if(cellData){
$(td).html('<i class="fa fa-check text-navy"></i>')
}else{
$(td).html('<i class="fa fa-times text-danger"></i>')
}
}}
],
columns: [
{data: "username" },{data: "username" }, {data: "name" },
{data:"email"}, {data:'is_imported'}
{data:"email"}, {data:'existing'}
],
pageLength: 10
};
......@@ -68,8 +80,7 @@ function initLdapTable() {
$(document).ready(function(){
}).on('show.bs.modal', function () {
initLdapTable();
initLdapUsersTable();
})
.on('click','.close_btn1',function () {
window.location.reload()
......@@ -82,9 +93,9 @@ $(document).ready(function(){
{% endblock %}
{% block modal_button %}
{{ block.super }}
<button data-dismiss="modal" class="btn btn-white close_btn2" type="button">{% trans "Close" %}</button>
<button class="btn btn-primary" type="button" id="{% block modal_confirm_id %}btn_ldap_modal_confirm{% endblock %}">{% trans 'Import' %}</button>
{% endblock %}
{% block modal_confirm_id %}btn_ldap_modal_confirm{% endblock %}
......@@ -58,11 +58,11 @@
<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>
<button class="btn btn-default btn-test" type="button"> {% trans 'Test connection' %}</button>
{# <button class="btn btn-primary sync_button " data-toggle="modal" data-target="#sync_users_modal" type="button">{% trans 'Synchronization' %}</button>#}
<button class="btn btn-primary sync_button " data-toggle="modal" data-target="#ldap_list_users_modal" type="button">{% trans 'Sync User' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
<button class="btn btn-default sync_button " data-toggle="modal" data-target="#ldap_list_users_modal" type="button">{% trans 'Bulk import' %}</button>
</div>
</div>
</form>
......@@ -108,11 +108,17 @@ $(document).ready(function () {
})
.on("click","#btn_ldap_modal_confirm",function () {
var user_names=[];
var cheked = $("tbody input[type='checkbox']:checked").each(function () {
$("tbody input[type='checkbox']:checked").each(function () {
user_names.push($(this).attr('id'));
});
var the_url = "{% url "api-settings:ldap-comfirm-sync" %}";
if (user_names.length === 0){
var msg = "{% trans 'User is not currently selected, please check the user you want to import'%}"
toastr.error(msg);
return
}
var the_url = "{% url "api-settings:ldap-user-sync" %}";
function error(message) {
toastr.error(message)
......
......@@ -108,6 +108,7 @@
<label class="col-md-2 control-label" for="id_endpoint">{% trans "Endpoint" %}</label>
<div class="col-md-9">
<input id="id_endpoint" class="form-control" type="text" name="ENDPOINT" value="" placeholder="Endpoint">
<div id="endpoint_error" style="color: red;"></div>
<div class="help-block">
<span class="oss">
{% trans 'OSS: http://{REGION_NAME}.aliyuncs.com' %}
......@@ -251,6 +252,13 @@ $(document).ready(function() {
var name = $(id_field).attr('name');
data[name] = $(id_field).val();
});
if (data['ENDPOINT'] !== '' && data['ENDPOINT'].indexOf('http') === -1) {
var msg = "{% trans 'Endpoint need contain protocol, ex: http' %}";
$("#endpoint_error").html(msg);
submitBtn.removeClass('disabled');
submitBtn.html(origin_text);
return
}
var url = "{% url 'api-settings:replay-storage-create' %}";
var success = function(data, textStatus) {
location = "{% url 'settings:terminal-setting' %}";
......
......@@ -9,8 +9,8 @@ app_name = 'common'
urlpatterns = [
path('mail/testing/', api.MailTestingAPI.as_view(), name='mail-testing'),
path('ldap/testing/', api.LDAPTestingAPI.as_view(), name='ldap-testing'),
path('ldap/sync/', api.LDAPSyncAPI.as_view(), name='ldap-sync'),
path('ldap/comfirm/sync/', api.LDAPConfirmSyncAPI.as_view(), name='ldap-comfirm-sync'),
path('ldap/users/', api.LDAPUserListApi.as_view(), name='ldap-user-list'),
path('ldap/users/sync/', api.LDAPUserSyncAPI.as_view(), name='ldap-user-sync'),
path('terminal/replay-storage/create/', api.ReplayStorageCreateAPI.as_view(), name='replay-storage-create'),
path('terminal/replay-storage/delete/', api.ReplayStorageDeleteAPI.as_view(), name='replay-storage-delete'),
path('terminal/command-storage/create/', api.CommandStorageCreateAPI.as_view(), name='command-storage-create'),
......
This diff is collapsed.
apps/static/img/logo_text.png

17.7 KB | W: | H:

apps/static/img/logo_text.png

20.7 KB | W: | H:

apps/static/img/logo_text.png
apps/static/img/logo_text.png
apps/static/img/logo_text.png
apps/static/img/logo_text.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -12,6 +12,7 @@
<h4 class="modal-title">{% block modal_title %}{% endblock %}</h4>
<small>{% block modal_comment %}{% endblock %}</small>
</div>
{% block modal_help_message %}{% endblock %}
<div class="modal-body">
{% block modal_body %}
{% endblock %}
......
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