[Update] Merge local branch dev to dev_ldap2

pull/3426/head
BaiJiangJie 2019-11-11 17:54:28 +08:00
commit c31b56ddd9
15 changed files with 155 additions and 90 deletions

View File

@ -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:

View File

@ -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 ">

View File

@ -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) {
$(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();

View File

@ -428,11 +428,13 @@ $(document).ready(function(){
requestApi({ requestApi({
url: url, url: url,
method: 'DELETE', method: 'DELETE',
success:refreshPage, success: function () {
flash_message:false,
});
var msg = "{% trans 'Asset Deleted.' %}"; var msg = "{% trans 'Asset Deleted.' %}";
swal("{% trans 'Asset Delete' %}", msg, "success"); swal("{% trans 'Asset Delete' %}", msg, "success");
refreshPage();
},
flash_message: false,
});
} }
function fail() { function fail() {
var msg = "{% trans 'Asset Deleting failed.' %}"; var msg = "{% trans 'Asset Deleting failed.' %}";
@ -443,7 +445,8 @@ $(document).ready(function(){
method: 'POST', method: 'POST',
body: JSON.stringify(data), body: JSON.stringify(data),
success: success, success: success,
error:fail error: fail,
flash_message: false
}) })
}) })
} }

View File

@ -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,
} }

View File

@ -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

View File

@ -21,7 +21,10 @@ from .utils import (
from .tasks import sync_ldap_user_task from .tasks import sync_ldap_user_task
from common.permissions import IsOrgAdmin, IsSuperUser from common.permissions import IsOrgAdmin, IsSuperUser
from common.utils import get_logger from common.utils import get_logger
from .serializers import MailTestSerializer, LDAPTestSerializer, LDAPUserSerializer from .serializers import (
MailTestSerializer, LDAPTestSerializer, LDAPUserSerializer,
PublicSettingSerializer,
)
from users.models import User from users.models import User
@ -29,7 +32,7 @@ logger = get_logger(__file__)
class MailTestingAPI(APIView): class MailTestingAPI(APIView):
permission_classes = (IsOrgAdmin,) permission_classes = (IsSuperUser,)
serializer_class = MailTestSerializer serializer_class = MailTestSerializer
success_message = _("Test mail sent to {}, please check") success_message = _("Test mail sent to {}, please check")
@ -68,7 +71,7 @@ class MailTestingAPI(APIView):
class LDAPTestingAPI(APIView): class LDAPTestingAPI(APIView):
permission_classes = (IsOrgAdmin,) permission_classes = (IsSuperUser,)
serializer_class = LDAPTestSerializer serializer_class = LDAPTestSerializer
success_message = _("Test ldap success") success_message = _("Test ldap success")
@ -114,7 +117,7 @@ class LDAPTestingAPI(APIView):
class LDAPUserListApi(generics.ListAPIView): class LDAPUserListApi(generics.ListAPIView):
permission_classes = (IsOrgAdmin,) permission_classes = (IsSuperUser,)
serializer_class = LDAPUserSerializer serializer_class = LDAPUserSerializer
def get_queryset_from_cache(self): def get_queryset_from_cache(self):
@ -200,7 +203,7 @@ class LDAPUserListApi(generics.ListAPIView):
class LDAPUserImportAPI(APIView): class LDAPUserImportAPI(APIView):
permission_classes = (IsOrgAdmin,) permission_classes = (IsSuperUser,)
def get_ldap_users(self): def get_ldap_users(self):
username_list = self.request.data.get('username_list', []) username_list = self.request.data.get('username_list', [])
@ -229,6 +232,7 @@ class LDAPUserImportAPI(APIView):
class LDAPCacheRefreshAPI(generics.RetrieveAPIView): class LDAPCacheRefreshAPI(generics.RetrieveAPIView):
permission_classes = (IsSuperUser,)
def retrieve(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs):
try: try:
@ -318,3 +322,19 @@ class CommandStorageDeleteAPI(APIView):
storage_name = str(request.data.get('name')) storage_name = str(request.data.get('name'))
Setting.delete_storage('TERMINAL_COMMAND_STORAGE', storage_name) Setting.delete_storage('TERMINAL_COMMAND_STORAGE', storage_name)
return Response({"msg": _('Delete succeed')}, status=200) return Response({"msg": _('Delete succeed')}, status=200)
class PublicSettingApi(generics.RetrieveAPIView):
permission_classes = ()
serializer_class = PublicSettingSerializer
def get_object(self):
c = settings.CONFIG
instance = {
"data": {
"WINDOWS_SKIP_ALL_MANUAL_PASSWORD": c.WINDOWS_SKIP_ALL_MANUAL_PASSWORD
}
}
return instance

View File

@ -29,3 +29,6 @@ class LDAPUserSerializer(serializers.Serializer):
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)

View File

@ -16,4 +16,5 @@ urlpatterns = [
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'),
] ]

View File

@ -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):
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):
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):
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(),

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from .v1 import * from .user import *
from .group import *

View File

@ -0,0 +1,69 @@
# -*- 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()

View File

@ -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):
'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):
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:

View File

@ -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):
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()

View File

@ -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>