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

Merge pull request #3433 from jumpserver/dev

Dev
parents 96adaca3 f3d2e5d1
...@@ -44,4 +44,4 @@ class GatewayTestConnectionApi(SingleObjectMixin, APIView): ...@@ -44,4 +44,4 @@ class GatewayTestConnectionApi(SingleObjectMixin, APIView):
if ok: if ok:
return Response("ok") return Response("ok")
else: else:
return Response({"failed": e}, status=404) return Response({"error": e}, status=400)
...@@ -14,11 +14,12 @@ ...@@ -14,11 +14,12 @@
# limitations under the License. # limitations under the License.
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.conf import settings
from rest_framework.response import Response from rest_framework.response import Response
from common.serializers import CeleryTaskSerializer from common.serializers import CeleryTaskSerializer
from common.utils import get_logger from common.utils import get_logger
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsAppUser
from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.api import OrgBulkModelViewSet
from orgs.mixins import generics from orgs.mixins import generics
from ..models import SystemUser, Asset from ..models import SystemUser, Asset
...@@ -69,7 +70,7 @@ class SystemUserAssetAuthInfoApi(generics.RetrieveAPIView): ...@@ -69,7 +70,7 @@ class SystemUserAssetAuthInfoApi(generics.RetrieveAPIView):
Get system user with asset auth info Get system user with asset auth info
""" """
model = SystemUser model = SystemUser
permission_classes = (IsOrgAdminOrAppUser,) permission_classes = (IsAppUser,)
serializer_class = serializers.SystemUserAuthSerializer serializer_class = serializers.SystemUserAuthSerializer
def get_object(self): def get_object(self):
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
import uuid import uuid
import random import random
import re
import paramiko import paramiko
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 _
...@@ -63,6 +63,9 @@ class Gateway(AssetUser): ...@@ -63,6 +63,9 @@ class Gateway(AssetUser):
def test_connective(self, local_port=None): def test_connective(self, local_port=None):
if local_port is None: if local_port is None:
local_port = self.port local_port = self.port
if not re.match(r'\w+$', self.password):
return False, _("Password should not contain special characters")
client = paramiko.SSHClient() client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
proxy = paramiko.SSHClient() proxy = paramiko.SSHClient()
......
...@@ -68,7 +68,7 @@ class TreeMixin: ...@@ -68,7 +68,7 @@ class TreeMixin:
@classmethod @classmethod
def refresh_node_assets(cls, t=None): def refresh_node_assets(cls, t=None):
logger.debug("Refresh node tree assets") logger.debug("Refresh node assets")
key = cls.tree_assets_cache_key key = cls.tree_assets_cache_key
ttl = cls.tree_cache_time ttl = cls.tree_cache_time
if not t: if not t:
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
<div class="wrapper wrapper-content"> <div class="wrapper wrapper-content">
<div class="row"> <div class="row">
<div class="col-lg-3" id="split-left" style="padding-left: 3px"> <div class="col-lg-3" id="split-left" style="padding-left: 3px;overflow: auto;max-height: 500px">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px"> <div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px">
<div class="file-manager "> <div class="file-manager ">
......
...@@ -32,8 +32,7 @@ ...@@ -32,8 +32,7 @@
} }
</style> </style>
<div class="ibox treebox float-e-margins" style="overflow:auto;">
<div class="ibox float-e-margins">
<div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px"> <div class="ibox-content mailbox-content" style="padding-top: 0;padding-left: 1px">
<div class="file-manager" id="tree-node-id"> <div class="file-manager" id="tree-node-id">
<div id="{% block treeID %}nodeTree{% endblock %}" class="ztree"> <div id="{% block treeID %}nodeTree{% endblock %}" class="ztree">
...@@ -306,6 +305,7 @@ function defaultCallback(action) { ...@@ -306,6 +305,7 @@ function defaultCallback(action) {
$(document).ready(function () { $(document).ready(function () {
$('.treebox').css('height', window.innerHeight - 180);
}) })
.on('click', '.btn-show-current-asset', function(){ .on('click', '.btn-show-current-asset', function(){
hideRMenu(); hideRMenu();
...@@ -322,4 +322,4 @@ $(document).ready(function () { ...@@ -322,4 +322,4 @@ $(document).ready(function () {
location.reload(); location.reload();
}) })
</script> </script>
\ No newline at end of file
...@@ -426,13 +426,15 @@ $(document).ready(function(){ ...@@ -426,13 +426,15 @@ $(document).ready(function(){
function success(data) { function success(data) {
url = setUrlParam(the_url, 'spm', data.spm); url = setUrlParam(the_url, 'spm', data.spm);
requestApi({ requestApi({
url:url, url: url,
method:'DELETE', method: 'DELETE',
success:refreshPage, success: function () {
flash_message:false, var msg = "{% trans 'Asset Deleted.' %}";
swal("{% trans 'Asset Delete' %}", msg, "success");
refreshPage();
},
flash_message: false,
}); });
var msg = "{% trans 'Asset Deleted.' %}";
swal("{% trans 'Asset Delete' %}", msg, "success");
} }
function fail() { function fail() {
var msg = "{% trans 'Asset Deleting failed.' %}"; var msg = "{% trans 'Asset Deleting failed.' %}";
...@@ -440,10 +442,11 @@ $(document).ready(function(){ ...@@ -440,10 +442,11 @@ $(document).ready(function(){
} }
requestApi({ requestApi({
url: "{% url 'api-common:resources-cache' %}", url: "{% url 'api-common:resources-cache' %}",
method:'POST', method: 'POST',
body:JSON.stringify(data), body: JSON.stringify(data),
success:success, success: success,
error:fail error: fail,
flash_message: false
}) })
}) })
} }
......
...@@ -139,7 +139,7 @@ $(document).ready(function(){ ...@@ -139,7 +139,7 @@ $(document).ready(function(){
method: "POST", method: "POST",
body: JSON.stringify({'port': parseInt(data.port)}), body: JSON.stringify({'port': parseInt(data.port)}),
success_message: "{% trans 'Can be connected' %}", success_message: "{% trans 'Can be connected' %}",
fail_message: "{% trans 'The connection fails' %}" {#fail_message: "{% trans 'The connection fails' %}"#}
}) })
}); });
</script> </script>
......
...@@ -47,7 +47,7 @@ def on_openid_login_success(sender, user=None, request=None, **kwargs): ...@@ -47,7 +47,7 @@ def on_openid_login_success(sender, user=None, request=None, **kwargs):
@receiver(populate_user) @receiver(populate_user)
def on_ldap_create_user(sender, user, ldap_user, **kwargs): def on_ldap_create_user(sender, user, ldap_user, **kwargs):
if user and user.username != 'admin': if user and user.username not in ['admin']:
user.source = user.SOURCE_LDAP user.source = user.SOURCE_LDAP
user.save() user.save()
......
...@@ -395,6 +395,7 @@ defaults = { ...@@ -395,6 +395,7 @@ defaults = {
'FLOWER_URL': "127.0.0.1:5555", 'FLOWER_URL': "127.0.0.1:5555",
'DEFAULT_ORG_SHOW_ALL_USERS': True, 'DEFAULT_ORG_SHOW_ALL_USERS': True,
'PERIOD_TASK_ENABLED': True, 'PERIOD_TASK_ENABLED': True,
'WINDOWS_SKIP_ALL_MANUAL_PASSWORD': False,
} }
......
...@@ -516,7 +516,7 @@ CELERY_TASK_EAGER_PROPAGATES = True ...@@ -516,7 +516,7 @@ CELERY_TASK_EAGER_PROPAGATES = True
CELERY_WORKER_REDIRECT_STDOUTS = True CELERY_WORKER_REDIRECT_STDOUTS = True
CELERY_WORKER_REDIRECT_STDOUTS_LEVEL = "INFO" CELERY_WORKER_REDIRECT_STDOUTS_LEVEL = "INFO"
# CELERY_WORKER_HIJACK_ROOT_LOGGER = True # CELERY_WORKER_HIJACK_ROOT_LOGGER = True
CELERY_WORKER_MAX_TASKS_PER_CHILD = 40 # CELERY_WORKER_MAX_TASKS_PER_CHILD = 40
CELERY_TASK_SOFT_TIME_LIMIT = 3600 CELERY_TASK_SOFT_TIME_LIMIT = 3600
# Cache use redis # Cache use redis
......
This diff is collapsed.
...@@ -11,13 +11,6 @@ from .utils.asset_permission import AssetPermissionUtilV2 ...@@ -11,13 +11,6 @@ from .utils.asset_permission import AssetPermissionUtilV2
logger = get_logger(__file__) logger = get_logger(__file__)
permission_m2m_senders = (
AssetPermission.nodes.through,
AssetPermission.assets.through,
AssetPermission.users.through,
AssetPermission.user_groups.through,
)
@receiver([post_save, post_delete], sender=AssetPermission) @receiver([post_save, post_delete], sender=AssetPermission)
@on_transaction_commit @on_transaction_commit
......
This diff is collapsed.
...@@ -25,6 +25,10 @@ class LDAPTestSerializer(serializers.Serializer): ...@@ -25,6 +25,10 @@ class LDAPTestSerializer(serializers.Serializer):
class LDAPUserSerializer(serializers.Serializer): class LDAPUserSerializer(serializers.Serializer):
id = serializers.CharField() id = serializers.CharField()
username = serializers.CharField() username = serializers.CharField()
name = serializers.CharField()
email = serializers.CharField() email = serializers.CharField()
existing = serializers.BooleanField(read_only=True) existing = serializers.BooleanField(read_only=True)
class PublicSettingSerializer(serializers.Serializer):
data = serializers.DictField(read_only=True)
# coding: utf-8
#
from .ldap import *
# coding: utf-8
#
from celery import shared_task
from common.utils import get_logger
from ..utils import LDAPSyncUtil
__all__ = ['sync_ldap_user_task']
logger = get_logger(__file__)
@shared_task
def sync_ldap_user_task():
LDAPSyncUtil().perform_sync()
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
<div class="row"> <div class="row">
<div class="col-lg-12 animated fadeInRight" id="split-right"> <div class="col-lg-12 animated fadeInRight" id="split-right">
<div class="mail-box-header"> <div class="mail-box-header">
<div class="uc pull-left m-r-5"><a id="id_refresh_cache" class="btn btn-sm btn-primary"> {% trans "Refresh cache" %} </a></div>
<table class="table table-striped table-bordered table-hover " id="ldap_list_users_table" style="width: 100%"> <table class="table table-striped table-bordered table-hover " id="ldap_list_users_table" style="width: 100%">
<thead> <thead>
<tr> <tr>
...@@ -36,6 +37,9 @@ ...@@ -36,6 +37,9 @@
<tbody> <tbody>
</tbody> </tbody>
</table> </table>
<div id="fake_datatable_wrapper_loading" class="dataTables_wrapper" style="display: block;">
<div id="ldap_list_users_table_processing" class="dataTables_processing panel panel-default">{% trans 'Loading' %}...</div>
</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -43,8 +47,11 @@ ...@@ -43,8 +47,11 @@
<script> <script>
var ldap_users_table = 0; var ldap_users_table = 0;
var interval;
function initLdapUsersTable() { function initLdapUsersTable() {
if(ldap_users_table){ if(ldap_users_table){
ldap_users_table.ajax.reload(null, false);
return ldap_users_table return ldap_users_table
} }
var options = { var options = {
...@@ -68,21 +75,93 @@ function initLdapUsersTable() { ...@@ -68,21 +75,93 @@ function initLdapUsersTable() {
], ],
pageLength: 15 pageLength: 15
}; };
ldap_users_table = jumpserver.initServerSideDataTable(options); ldap_users_table = jumpserver.initServerSideDataTable(options);
return ldap_users_table return ldap_users_table
} }
function testRequestLdapUser(){
$("#fake_datatable_wrapper_loading").css('display', 'block');
var the_url = "{% url 'api-settings:ldap-user-list' %}";
var error = function (data, status) {
if (status === 409){
console.log(data);
return
}
if (status === 400){
toastr.error(data);
$("#fake_datatable_wrapper_loading").css('display', 'none');
clearInterval(interval);
interval = undefined;
return
}
console.log(data, status)
};
var success = function() {
$("#fake_datatable_wrapper_loading").css('display', 'none');
initLdapUsersTable();
clearInterval(interval);
interval = undefined
};
requestApi({
url: the_url,
method: 'GET',
flash_message: false,
error: error,
success: success
});
}
function timingTestRequestLdapUser(){
if (interval !== undefined){
return
}
interval = setInterval(testRequestLdapUser, 2000);
}
$(document).ready(function(){ $(document).ready(function(){
}).on('show.bs.modal', function () { }).on('show.bs.modal', function () {
initLdapUsersTable(); timingTestRequestLdapUser()
}) })
.on('click','.close_btn1',function () { .on('click', '#id_refresh_cache', function () {
window.location.reload() var the_url = "{% url "api-settings:ldap-cache-refresh" %}";
function error(data) {
toastr.error(data)
}
function success(){
timingTestRequestLdapUser();
}
requestApi({
url: the_url,
method: 'GET',
error: error,
success: success
})
}) })
.on('click','.close_btn2',function () { .on("click","#btn_ldap_modal_confirm",function () {
window.location.reload() var username_list = ldap_users_table.selected;
if (username_list.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-import" %}";
function error(message) {
toastr.error(message)
}
function success(message) {
toastr.success(message.msg);
ldap_users_table.selected = [];
timingTestRequestLdapUser();
}
requestApi({
url: the_url,
body: JSON.stringify({'username_list':username_list}),
method: "POST",
flash_message: false,
success: success,
error: error
});
}) })
</script> </script>
......
...@@ -63,9 +63,8 @@ ...@@ -63,9 +63,8 @@
<div class="col-sm-4 col-sm-offset-2"> <div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button> <button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button class="btn btn-default btn-test" type="button"> {% trans 'Test connection' %}</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 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> <button class="btn btn-default sync_button " data-toggle="modal" data-target="#ldap_list_users_modal" type="button">{% trans 'Bulk import' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div> </div>
</div> </div>
</form> </form>
...@@ -109,33 +108,6 @@ $(document).ready(function () { ...@@ -109,33 +108,6 @@ $(document).ready(function () {
error: error error: error
}); });
}) })
.on("click","#btn_ldap_modal_confirm",function () {
var username_list = ldap_users_table.selected;
if (username_list.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)
}
function success(message) {
toastr.success(message.msg)
}
requestApi({
url: the_url,
body: JSON.stringify({'username_list':username_list}),
method: "POST",
flash_message: false,
success: success,
error: error
});
})
</script> </script>
{% endblock %} {% endblock %}
...@@ -10,9 +10,11 @@ urlpatterns = [ ...@@ -10,9 +10,11 @@ urlpatterns = [
path('mail/testing/', api.MailTestingAPI.as_view(), name='mail-testing'), path('mail/testing/', api.MailTestingAPI.as_view(), name='mail-testing'),
path('ldap/testing/', api.LDAPTestingAPI.as_view(), name='ldap-testing'), path('ldap/testing/', api.LDAPTestingAPI.as_view(), name='ldap-testing'),
path('ldap/users/', api.LDAPUserListApi.as_view(), name='ldap-user-list'), path('ldap/users/', api.LDAPUserListApi.as_view(), name='ldap-user-list'),
path('ldap/users/sync/', api.LDAPUserSyncAPI.as_view(), name='ldap-user-sync'), path('ldap/users/import/', api.LDAPUserImportAPI.as_view(), name='ldap-user-import'),
path('ldap/cache/refresh/', api.LDAPCacheRefreshAPI.as_view(), name='ldap-cache-refresh'),
path('terminal/replay-storage/create/', api.ReplayStorageCreateAPI.as_view(), name='replay-storage-create'), 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/replay-storage/delete/', api.ReplayStorageDeleteAPI.as_view(), name='replay-storage-delete'),
path('terminal/command-storage/create/', api.CommandStorageCreateAPI.as_view(), name='command-storage-create'), path('terminal/command-storage/create/', api.CommandStorageCreateAPI.as_view(), name='command-storage-create'),
path('terminal/command-storage/delete/', api.CommandStorageDeleteAPI.as_view(), name='command-storage-delete'), path('terminal/command-storage/delete/', api.CommandStorageDeleteAPI.as_view(), name='command-storage-delete'),
path('public/', api.PublicSettingApi.as_view(), name='public-setting'),
] ]
# -*- coding: utf-8 -*-
#
from ldap3 import Server, Connection
from django.utils.translation import ugettext_lazy as _
from users.models import User
from users.utils import construct_user_email
from common.utils import get_logger
from common.const import LDAP_AD_ACCOUNT_DISABLE
from .models import settings
logger = get_logger(__file__)
class LDAPOUGroupException(Exception):
pass
class LDAPUtil:
_conn = None
SEARCH_FIELD_ALL = 'all'
SEARCH_FIELD_USERNAME = 'username'
def __init__(self, use_settings_config=True, server_uri=None, bind_dn=None,
password=None, use_ssl=None, search_ougroup=None,
search_filter=None, attr_map=None, auth_ldap=None):
# config
self.paged_size = settings.AUTH_LDAP_SEARCH_PAGED_SIZE
if use_settings_config:
self._load_config_from_settings()
else:
self.server_uri = server_uri
self.bind_dn = bind_dn
self.password = password
self.use_ssl = use_ssl
self.search_ougroup = search_ougroup
self.search_filter = search_filter
self.attr_map = attr_map
self.auth_ldap = auth_ldap
def _load_config_from_settings(self):
self.server_uri = settings.AUTH_LDAP_SERVER_URI
self.bind_dn = settings.AUTH_LDAP_BIND_DN
self.password = settings.AUTH_LDAP_BIND_PASSWORD
self.use_ssl = settings.AUTH_LDAP_START_TLS
self.search_ougroup = settings.AUTH_LDAP_SEARCH_OU
self.search_filter = settings.AUTH_LDAP_SEARCH_FILTER
self.attr_map = settings.AUTH_LDAP_USER_ATTR_MAP
self.auth_ldap = settings.AUTH_LDAP
@property
def connection(self):
if self._conn is None:
server = Server(self.server_uri, use_ssl=self.use_ssl)
conn = Connection(server, self.bind_dn, self.password)
conn.bind()
self._conn = conn
return self._conn
@staticmethod
def get_user_by_username(username):
try:
user = User.objects.get(username=username)
except Exception as e:
return None
else:
return user
def _ldap_entry_to_user_item(self, entry):
user_item = {}
for attr, mapping in self.attr_map.items():
if not hasattr(entry, mapping):
continue
value = getattr(entry, mapping).value or ''
if mapping.lower() == 'useraccountcontrol' and attr == 'is_active'\
and value:
value = int(value) & LDAP_AD_ACCOUNT_DISABLE \
!= LDAP_AD_ACCOUNT_DISABLE
user_item[attr] = value
return user_item
def _search_user_items_ou(self, search_ou, extra_filter=None, cookie=None):
search_filter = self.search_filter % {"user": "*"}
if extra_filter:
search_filter = '(&{}{})'.format(search_filter, extra_filter)
ok = self.connection.search(
search_ou, search_filter,
attributes=list(self.attr_map.values()),
paged_size=self.paged_size, paged_cookie=cookie
)
if not ok:
error = _("Search no entry matched in ou {}".format(search_ou))
raise LDAPOUGroupException(error)
user_items = []
for entry in self.connection.entries:
user_item = self._ldap_entry_to_user_item(entry)
user = self.get_user_by_username(user_item['username'])
user_item['existing'] = bool(user)
if user_item in user_items:
continue
user_items.append(user_item)
return user_items
def _cookie(self):
if self.paged_size is None:
cookie = None
else:
cookie = self.connection.result['controls']['1.2.840.113556.1.4.319']['value']['cookie']
return cookie
def search_user_items(self, extra_filter=None):
user_items = []
logger.info("Search user items")
for search_ou in str(self.search_ougroup).split("|"):
logger.info("Search user search ou: {}".format(search_ou))
_user_items = self._search_user_items_ou(search_ou, extra_filter=extra_filter)
user_items.extend(_user_items)
while self._cookie():
logger.info("Page Search user search ou: {}".format(search_ou))
_user_items = self._search_user_items_ou(search_ou, extra_filter, self._cookie())
user_items.extend(_user_items)
logger.info("Search user items end")
return user_items
def construct_extra_filter(self, field, q):
if not q:
return None
extra_filter = ''
if field == self.SEARCH_FIELD_ALL:
for attr in self.attr_map.values():
extra_filter += '({}={})'.format(attr, q)
extra_filter = '(|{})'.format(extra_filter)
return extra_filter
if field == self.SEARCH_FIELD_USERNAME and isinstance(q, list):
attr = self.attr_map.get('username')
for username in q:
extra_filter += '({}={})'.format(attr, username)
extra_filter = '(|{})'.format(extra_filter)
return extra_filter
def search_filter_user_items(self, username_list):
extra_filter = self.construct_extra_filter(
self.SEARCH_FIELD_USERNAME, username_list
)
user_items = self.search_user_items(extra_filter)
return user_items
@staticmethod
def save_user(user, user_item):
for field, value in user_item.items():
if not hasattr(user, field):
continue
if isinstance(getattr(user, field), bool):
if isinstance(value, str):
value = value.lower()
value = value in ['true', 1, True]
setattr(user, field, value)
user.save()
def update_user(self, user_item):
user = self.get_user_by_username(user_item['username'])
if user.source != User.SOURCE_LDAP:
msg = _('The user source is not LDAP')
return False, msg
try:
self.save_user(user, user_item)
except Exception as e:
logger.error(e, exc_info=True)
return False, str(e)
else:
return True, None
def create_user(self, user_item):
user = User(source=User.SOURCE_LDAP)
try:
self.save_user(user, user_item)
except Exception as e:
logger.error(e, exc_info=True)
return False, str(e)
else:
return True, None
@staticmethod
def construct_user_email(user_item):
username = user_item['username']
email = user_item.get('email', '')
email = construct_user_email(username, email)
return email
def create_or_update_users(self, user_items):
succeed = failed = 0
for user_item in user_items:
exist = user_item.pop('existing', False)
user_item['email'] = self.construct_user_email(user_item)
if not exist:
ok, error = self.create_user(user_item)
else:
ok, error = self.update_user(user_item)
if not ok:
logger.info("Failed User: {}".format(user_item))
failed += 1
else:
succeed += 1
result = {'total': len(user_items), 'succeed': succeed, 'failed': failed}
return result
def sync_users(self, username_list=None):
user_items = self.search_filter_user_items(username_list)
result = self.create_or_update_users(user_items)
return result
# coding: utf-8
#
from .ldap import *
This diff is collapsed.
...@@ -5,6 +5,7 @@ from django.utils.translation import ugettext as _ ...@@ -5,6 +5,7 @@ from django.utils.translation import ugettext as _
from common.permissions import PermissionsMixin, IsSuperUser from common.permissions import PermissionsMixin, IsSuperUser
from common import utils from common import utils
from .utils import LDAPSyncUtil
from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \ from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \
TerminalSettingForm, SecuritySettingForm, EmailContentSettingForm TerminalSettingForm, SecuritySettingForm, EmailContentSettingForm
...@@ -83,6 +84,7 @@ class LDAPSettingView(PermissionsMixin, TemplateView): ...@@ -83,6 +84,7 @@ class LDAPSettingView(PermissionsMixin, TemplateView):
form.save() form.save()
msg = _("Update setting successfully") msg = _("Update setting successfully")
messages.success(request, msg) messages.success(request, msg)
LDAPSyncUtil().clear_cache()
return redirect('settings:ldap-setting') return redirect('settings:ldap-setting')
else: else:
context = self.get_context_data() context = self.get_context_data()
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.conf import settings
from common.utils import validate_ssh_public_key from common.utils import validate_ssh_public_key
from orgs.mixins.forms import OrgModelForm from orgs.mixins.forms import OrgModelForm
...@@ -21,6 +22,20 @@ class UserCheckOtpCodeForm(forms.Form): ...@@ -21,6 +22,20 @@ class UserCheckOtpCodeForm(forms.Form):
otp_code = forms.CharField(label=_('MFA code'), max_length=6) otp_code = forms.CharField(label=_('MFA code'), max_length=6)
def get_source_choices():
choices_all = dict(User.SOURCE_CHOICES)
choices = [
(User.SOURCE_LOCAL, choices_all[User.SOURCE_LOCAL]),
]
if settings.AUTH_LDAP:
choices.append((User.SOURCE_LDAP, choices_all[User.SOURCE_LDAP]))
if settings.AUTH_OPENID:
choices.append((User.SOURCE_OPENID, choices_all[User.SOURCE_OPENID]))
if settings.AUTH_RADIUS:
choices.append((User.SOURCE_RADIUS, choices_all[User.SOURCE_RADIUS]))
return choices
class UserCreateUpdateFormMixin(OrgModelForm): class UserCreateUpdateFormMixin(OrgModelForm):
role_choices = ((i, n) for i, n in User.ROLE_CHOICES if i != User.ROLE_APP) role_choices = ((i, n) for i, n in User.ROLE_CHOICES if i != User.ROLE_APP)
password = forms.CharField( password = forms.CharField(
...@@ -31,6 +46,10 @@ class UserCreateUpdateFormMixin(OrgModelForm): ...@@ -31,6 +46,10 @@ class UserCreateUpdateFormMixin(OrgModelForm):
choices=role_choices, required=True, choices=role_choices, required=True,
initial=User.ROLE_USER, label=_("Role") initial=User.ROLE_USER, label=_("Role")
) )
source = forms.ChoiceField(
choices=get_source_choices, required=True,
initial=User.SOURCE_LOCAL, label=_("Source")
)
public_key = forms.CharField( public_key = forms.CharField(
label=_('ssh public key'), max_length=5000, required=False, label=_('ssh public key'), max_length=5000, required=False,
widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}), widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}),
...@@ -41,7 +60,8 @@ class UserCreateUpdateFormMixin(OrgModelForm): ...@@ -41,7 +60,8 @@ class UserCreateUpdateFormMixin(OrgModelForm):
model = User model = User
fields = [ fields = [
'username', 'name', 'email', 'groups', 'wechat', 'username', 'name', 'email', 'groups', 'wechat',
'phone', 'role', 'date_expired', 'comment', 'otp_level' 'source', 'phone', 'role', 'date_expired',
'comment', 'otp_level'
] ]
widgets = { widgets = {
'otp_level': forms.RadioSelect(), 'otp_level': forms.RadioSelect(),
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from .v1 import * from .user import *
\ No newline at end of file from .group import *
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.fields import StringManyToManyField
from common.serializers import AdaptedBulkListSerializer
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from ..models import User, UserGroup
from .. import utils
__all__ = [
'UserGroupSerializer', 'UserGroupListSerializer',
'UserGroupUpdateMemberSerializer'
]
class UserGroupSerializer(BulkOrgResourceModelSerializer):
users = serializers.PrimaryKeyRelatedField(
required=False, many=True, queryset=User.objects, label=_('User')
)
class Meta:
model = UserGroup
list_serializer_class = AdaptedBulkListSerializer
fields = [
'id', 'name', 'users', 'comment', 'date_created',
'created_by',
]
extra_kwargs = {
'created_by': {'label': _('Created by'), 'read_only': True}
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_fields_queryset()
def set_fields_queryset(self):
users_field = self.fields['users']
users_field.child_relation.queryset = utils.get_current_org_members()
def validate_users(self, users):
for user in users:
if user.is_super_auditor:
msg = _('Auditors cannot be join in the user group')
raise serializers.ValidationError(msg)
return users
class UserGroupListSerializer(UserGroupSerializer):
users = StringManyToManyField(many=True, read_only=True)
class UserGroupUpdateMemberSerializer(serializers.ModelSerializer):
users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects)
class Meta:
model = UserGroup
fields = ['id', 'users']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_fields_queryset()
def set_fields_queryset(self):
users_field = self.fields['users']
users_field.child_relation.queryset = utils.get_current_org_members()
...@@ -6,19 +6,14 @@ from rest_framework import serializers ...@@ -6,19 +6,14 @@ from rest_framework import serializers
from common.utils import validate_ssh_public_key from common.utils import validate_ssh_public_key
from common.mixins import BulkSerializerMixin from common.mixins import BulkSerializerMixin
from common.fields import StringManyToManyField
from common.serializers import AdaptedBulkListSerializer from common.serializers import AdaptedBulkListSerializer
from common.permissions import CanUpdateDeleteUser from common.permissions import CanUpdateDeleteUser
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from ..models import User, UserGroup from ..models import User, UserGroup
from .. import utils
__all__ = [ __all__ = [
'UserSerializer', 'UserPKUpdateSerializer', 'UserUpdateGroupSerializer', 'UserSerializer', 'UserPKUpdateSerializer', 'UserUpdateGroupSerializer',
'UserGroupSerializer', 'UserGroupListSerializer', 'ChangeUserPasswordSerializer', 'ResetOTPSerializer',
'UserGroupUpdateMemberSerializer', 'ChangeUserPasswordSerializer',
'ResetOTPSerializer',
] ]
...@@ -49,7 +44,6 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer): ...@@ -49,7 +44,6 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
'is_valid': {'label': _('Is valid')}, 'is_valid': {'label': _('Is valid')},
'is_expired': {'label': _('Is expired')}, 'is_expired': {'label': _('Is expired')},
'avatar_url': {'label': _('Avatar url')}, 'avatar_url': {'label': _('Avatar url')},
'source': {'read_only': True},
'created_by': {'read_only': True, 'allow_blank': True}, 'created_by': {'read_only': True, 'allow_blank': True},
'can_update': {'read_only': True}, 'can_update': {'read_only': True},
'can_delete': {'read_only': True}, 'can_delete': {'read_only': True},
...@@ -127,58 +121,6 @@ class UserUpdateGroupSerializer(serializers.ModelSerializer): ...@@ -127,58 +121,6 @@ class UserUpdateGroupSerializer(serializers.ModelSerializer):
fields = ['id', 'groups'] fields = ['id', 'groups']
class UserGroupSerializer(BulkOrgResourceModelSerializer):
users = serializers.PrimaryKeyRelatedField(
required=False, many=True, queryset=User.objects, label=_('User')
)
class Meta:
model = UserGroup
list_serializer_class = AdaptedBulkListSerializer
fields = [
'id', 'name', 'users', 'comment', 'date_created',
'created_by',
]
extra_kwargs = {
'created_by': {'label': _('Created by'), 'read_only': True}
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_fields_queryset()
def set_fields_queryset(self):
users_field = self.fields['users']
users_field.child_relation.queryset = utils.get_current_org_members()
def validate_users(self, users):
for user in users:
if user.is_super_auditor:
msg = _('Auditors cannot be join in the user group')
raise serializers.ValidationError(msg)
return users
class UserGroupListSerializer(UserGroupSerializer):
users = StringManyToManyField(many=True, read_only=True)
class UserGroupUpdateMemberSerializer(serializers.ModelSerializer):
users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects)
class Meta:
model = UserGroup
fields = ['id', 'users']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.set_fields_queryset()
def set_fields_queryset(self):
users_field = self.fields['users']
users_field.child_relation.queryset = utils.get_current_org_members()
class ChangeUserPasswordSerializer(serializers.ModelSerializer): class ChangeUserPasswordSerializer(serializers.ModelSerializer):
class Meta: class Meta:
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
# #
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, m2m_changed
from common.utils import get_logger from common.utils import get_logger
from .signals import post_user_create from .signals import post_user_create
# from .models import User from .models import User
logger = get_logger(__file__) logger = get_logger(__file__)
...@@ -28,3 +28,14 @@ def on_user_create(sender, user=None, **kwargs): ...@@ -28,3 +28,14 @@ def on_user_create(sender, user=None, **kwargs):
logger.info(" - Sending welcome mail ...".format(user.name)) logger.info(" - Sending welcome mail ...".format(user.name))
if user.email: if user.email:
send_user_created_mail(user) send_user_created_mail(user)
@receiver(m2m_changed, sender=User.groups.through)
def on_user_groups_change(sender, instance=None, action='', **kwargs):
"""
资产节点发生变化时,刷新节点
"""
if action.startswith('post'):
logger.debug("User group member change signal recv: {}".format(instance))
from perms.utils import AssetPermissionUtilV2
AssetPermissionUtilV2.expire_all_user_tree_cache()
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import sys
from celery import shared_task from celery import shared_task
from django.conf import settings from django.conf import settings
from ops.celery.utils import create_or_update_celery_periodic_tasks from ops.celery.utils import (
create_or_update_celery_periodic_tasks, disable_celery_periodic_task
)
from ops.celery.decorator import after_app_ready_start from ops.celery.decorator import after_app_ready_start
from common.utils import get_logger from common.utils import get_logger
from .models import User from .models import User
from .utils import ( from .utils import (
send_password_expiration_reminder_mail, send_user_expiration_reminder_mail send_password_expiration_reminder_mail, send_user_expiration_reminder_mail
) )
from settings.utils import LDAPUtil from settings.utils import LDAPServerUtil, LDAPImportUtil
logger = get_logger(__file__) logger = get_logger(__file__)
...@@ -70,19 +73,26 @@ def check_user_expired_periodic(): ...@@ -70,19 +73,26 @@ def check_user_expired_periodic():
@shared_task @shared_task
def sync_ldap_user(): def import_ldap_user():
logger.info("Start sync ldap user periodic task") logger.info("Start import ldap user task")
util = LDAPUtil() util_server = LDAPServerUtil()
result = util.sync_users() util_import = LDAPImportUtil()
logger.info("Result: {}".format(result)) users = util_server.search()
errors = util_import.perform_import(users)
if errors:
logger.error("Imported LDAP users errors: {}".format(errors))
else:
logger.info('Imported {} users successfully'.format(len(users)))
@shared_task @shared_task
@after_app_ready_start @after_app_ready_start
def sync_ldap_user_periodic(): def import_ldap_user_periodic():
if not settings.AUTH_LDAP: if not settings.AUTH_LDAP:
return return
if not settings.AUTH_LDAP_SYNC_IS_PERIODIC: if not settings.AUTH_LDAP_SYNC_IS_PERIODIC:
task_name = sys._getframe().f_code.co_name
disable_celery_periodic_task(task_name)
return return
interval = settings.AUTH_LDAP_SYNC_INTERVAL interval = settings.AUTH_LDAP_SYNC_INTERVAL
...@@ -91,10 +101,9 @@ def sync_ldap_user_periodic(): ...@@ -91,10 +101,9 @@ def sync_ldap_user_periodic():
else: else:
interval = None interval = None
crontab = settings.AUTH_LDAP_SYNC_CRONTAB crontab = settings.AUTH_LDAP_SYNC_CRONTAB
tasks = { tasks = {
'sync_ldap_user_periodic': { 'import_ldap_user_periodic': {
'task': sync_ldap_user.name, 'task': import_ldap_user.name,
'interval': interval, 'interval': interval,
'crontab': crontab, 'crontab': crontab,
'enabled': True, 'enabled': True,
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
<h3>{% trans 'Auth' %}</h3> <h3>{% trans 'Auth' %}</h3>
{% block password %}{% endblock %} {% block password %}{% endblock %}
{% bootstrap_field form.otp_level layout="horizontal" %} {% bootstrap_field form.otp_level layout="horizontal" %}
{% bootstrap_field form.source layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Security and Role' %}</h3> <h3>{% trans 'Security and Role' %}</h3>
......
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