diff --git a/apps/common/utils.py b/apps/common/utils.py index 9a998adf9..2aa70507a 100644 --- a/apps/common/utils.py +++ b/apps/common/utils.py @@ -11,6 +11,7 @@ import datetime import paramiko import paramiko +import sshpubkeys from itsdangerous import TimedJSONWebSignatureSerializer, JSONWebSignatureSerializer, \ BadSignature, SignatureExpired from django.shortcuts import reverse as dj_reverse @@ -236,4 +237,15 @@ def validate_ssh_private_key(text): return True +def validate_ssh_public_key(text): + ssh = sshpubkeys.SSHKey(text) + try: + ssh.parse() + except sshpubkeys.InvalidKeyException: + return False + except NotImplementedError as e: + return False + return True + + signer = Signer() \ No newline at end of file diff --git a/apps/static/js/jumpserver.js b/apps/static/js/jumpserver.js index 6f1fff39c..600cfa606 100644 --- a/apps/static/js/jumpserver.js +++ b/apps/static/js/jumpserver.js @@ -191,7 +191,6 @@ function APIUpdateAttr(props) { props = props || {}; var success_message = props.success_message || 'Update Successfully!'; var fail_message = props.fail_message || 'Error occurred while updating.'; - console.log(props.body); $.ajax({ url: props.url, type: props.method || "PATCH", @@ -208,7 +207,7 @@ function APIUpdateAttr(props) { if (typeof props.error === 'function') { return props.error(errorThrown); } else { - toastr.error(fail_message); + toastr.error(textStatue); } }); return true; diff --git a/apps/users/api.py b/apps/users/api.py index 8c945ca53..b074ab2e1 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -29,10 +29,10 @@ class UserViewSet(BulkModelViewSet): permission_classes = (IsSuperUser,) -# class UserAndGroupEditApi(generics.RetrieveUpdateAPIView): -# queryset = User.objects.all() -# serializer_class = serializers.UserAndGroupSerializer -# permission_classes = (IsSuperUser,) +class UserAndGroupEditApi(generics.RetrieveUpdateAPIView): + queryset = User.objects.all() + serializer_class = serializers.UserAndGroupSerializer + permission_classes = (IsSuperUser,) class UserResetPasswordApi(generics.UpdateAPIView): @@ -50,7 +50,7 @@ class UserResetPasswordApi(generics.UpdateAPIView): send_reset_password_mail(user) -class UserResetPubKeyApi(generics.UpdateAPIView): +class UserResetPKApi(generics.UpdateAPIView): queryset = User.objects.all() serializer_class = serializers.UserSerializer @@ -61,16 +61,16 @@ class UserResetPubKeyApi(generics.UpdateAPIView): user.save() send_reset_ssh_key_mail(user) -# -# class UserUpdatePKApi(generics.UpdateAPIView): -# queryset = User.objects.all() -# serializer_class = serializers.UserPKUpdateSerializer -# -# def perform_update(self, serializer): -# user = self.get_object() -# user.private_key = serializer.validated_data['_public_key'] -# user.save() -# + +class UserUpdatePKApi(generics.UpdateAPIView): + queryset = User.objects.all() + serializer_class = serializers.UserPKUpdateSerializer + + def perform_update(self, serializer): + user = self.get_object() + user.public_key = serializer.validated_data['_public_key'] + user.save() + # # class GroupDetailApi(generics.RetrieveUpdateDestroyAPIView): # queryset = UserGroup.objects.all() @@ -87,8 +87,8 @@ class UserResetPubKeyApi(generics.UpdateAPIView): # group.save() # return # serializer.save() -# -# + + # class UserListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView): # queryset = User.objects.all() # serializer_class = serializers.UserBulkUpdateSerializer @@ -96,12 +96,12 @@ class UserResetPubKeyApi(generics.UpdateAPIView): # # def get(self, request, *args, **kwargs): # return super(UserListUpdateApi, self).get(request, *args, **kwargs) -# + # # class GroupListUpdateApi(BulkDeleteApiMixin, ListBulkCreateUpdateDestroyAPIView): # queryset = UserGroup.objects.all() # serializer_class = serializers.GroupBulkUpdateSerializer - +# # class DeleteUserFromGroupApi(generics.DestroyAPIView): # queryset = UserGroup.objects.all() diff --git a/apps/users/forms.py b/apps/users/forms.py index 08c14f860..51947372d 100644 --- a/apps/users/forms.py +++ b/apps/users/forms.py @@ -5,6 +5,7 @@ from django.contrib.auth.forms import AuthenticationForm from django.utils.translation import gettext_lazy as _ from captcha.fields import CaptchaField +from common.utils import validate_ssh_public_key from .models import User, UserGroup from .hands import AssetPermission @@ -17,7 +18,7 @@ class UserLoginForm(AuthenticationForm): captcha = CaptchaField() -class UserCreateForm(forms.ModelForm): +class UserCreateUpdateForm(forms.ModelForm): class Meta: model = User @@ -42,22 +43,22 @@ class UserBulkImportForm(forms.ModelForm): fields = ['username', 'email', 'enable_otp', 'role'] -class UserUpdateForm(forms.ModelForm): - - class Meta: - model = User - fields = [ - 'name', 'email', 'groups', 'wechat', - 'phone', 'enable_otp', 'role', 'date_expired', 'comment', - ] - help_texts = { - 'username': '* required', - 'email': '* required', - 'groups': '* required' - } - widgets = { - 'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join user groups')}), - } +# class UserUpdateForm(forms.ModelForm): +# +# class Meta: +# model = User +# fields = [ +# 'name', 'email', 'groups', 'wechat', +# 'phone', 'enable_otp', 'role', 'date_expired', 'comment', +# ] +# help_texts = { +# 'username': '* required', +# 'email': '* required', +# 'groups': '* required' +# } +# widgets = { +# 'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Join user groups')}), +# } class UserGroupForm(forms.ModelForm): @@ -84,22 +85,14 @@ class UserKeyForm(forms.Form): public_key = forms.CharField( label=_('ssh public key'), max_length=5000, widget=forms.Textarea(attrs={'placeholder': _('ssh-rsa AAAA...')}), - help_text=_('Paste your id_ras.pub here.')) + help_text=_('Paste your id_rsa.pub here.')) def clean_public_key(self): public_key = self.cleaned_data['public_key'] - if self.user._public_key and public_key == self.user.public_key: + if self.user.public_key and public_key == self.user.public_key: raise forms.ValidationError(_('Public key should not be the same as your old one.')) - from sshpubkeys import SSHKey - from sshpubkeys.exceptions import InvalidKeyException - ssh = SSHKey(public_key) - try: - ssh.parse() - except InvalidKeyException as e: - print e - raise forms.ValidationError(_('Not a valid ssh public key')) - except NotImplementedError as e: - print e + + if not validate_ssh_public_key(public_key): raise forms.ValidationError(_('Not a valid ssh public key')) return public_key @@ -126,3 +119,7 @@ class UserPrivateAssetPermissionForm(forms.ModelForm): 'system_users': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select system users')}), } + + +class FileForm(forms.Form): + excel = forms.FileField() diff --git a/apps/users/serializers.py b/apps/users/serializers.py index 0d02f85d5..9d148aa20 100644 --- a/apps/users/serializers.py +++ b/apps/users/serializers.py @@ -5,7 +5,7 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin -from common.utils import signer +from common.utils import signer, validate_ssh_public_key from .models import User, UserGroup @@ -47,16 +47,9 @@ class UserPKUpdateSerializer(serializers.ModelSerializer): @staticmethod def validate__public_key(value): - from sshpubkeys import SSHKey - from sshpubkeys.exceptions import InvalidKeyException - ssh = SSHKey(value) - try: - ssh.parse() - except InvalidKeyException as e: - print e - raise serializers.ValidationError(_('Not a valid ssh public key')) - except NotImplementedError as e: - print e + if not validate_ssh_public_key(value): + print('Not a valid key') + print(value) raise serializers.ValidationError(_('Not a valid ssh public key')) return value diff --git a/apps/users/templates/users/_user.html b/apps/users/templates/users/_user.html index dd9f8232b..a9df818d4 100644 --- a/apps/users/templates/users/_user.html +++ b/apps/users/templates/users/_user.html @@ -6,7 +6,7 @@
{% csrf_token %}

{% trans 'Account' %}

- {% block username %} {% endblock %} + {{ form.username|bootstrap_horizontal }} {{ form.name|bootstrap_horizontal }} {{ form.email|bootstrap_horizontal }} {{ form.groups|bootstrap_horizontal }} @@ -27,7 +27,7 @@ {{ form.date_expired.errors }} - {{ form.date_expired|bootstrap_horizontal }}#} + {{ form.date_expired|bootstrap_horizontal }}
diff --git a/apps/users/templates/users/user_create.html b/apps/users/templates/users/user_create.html index 07235d018..9f3cba36e 100644 --- a/apps/users/templates/users/user_create.html +++ b/apps/users/templates/users/user_create.html @@ -2,9 +2,9 @@ {% load i18n %} {% load bootstrap %} {% block user_template_title %}{% trans "Create user" %}{% endblock %} -{% block username %} - {{ form.username|bootstrap_horizontal }} -{% endblock %} +{#{% block username %}#} +{# {{ form.username|bootstrap_horizontal }}#} +{#{% endblock %}#} {% block password %}

{% trans 'Password' %}

diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html index 768746b08..c1cdbc5f5 100644 --- a/apps/users/templates/users/user_detail.html +++ b/apps/users/templates/users/user_detail.html @@ -225,7 +225,7 @@ jumpserver.selected_groups = {}; function updateUserGroups(user_groups) { -{# var the_url = "{% url 'users:group-user-edit-api' pk=user.id %}";#} + var the_url = "{% url 'users:group-user-edit-api' pk=user.id %}"; var body = { id: {{ user.id }}, groups: Object.assign([], user_groups) @@ -313,10 +313,11 @@ $(document).ready(function() { var user_groups = $('.bdg_user_group').map(function() { return $(this).data('gid'); }).get(); + console.log(user_groups); updateUserGroups(user_groups) }).on('click', '#btn_reset_password', function() { function doReset() { - var the_url = '{% url "users:user-reset-password-api" pk=user.id %}'; + var the_url = '{% url "users:api-user-reset-password" pk=user.id %}'; var body = {}; var success = function() { var msg = "{% trans "An e-mail has been sent to the user\'s mailbox." %}"; @@ -341,7 +342,7 @@ $(document).ready(function() { }); }).on('click', '#btn_reset_pk', function() { function doReset() { -{# var the_url = '{% url "users:user-reset-pk-api" pk=user.id %}';#} + var the_url = '{% url "users:api-user-reset-pk" pk=user.id %}'; var body = {}; var success = function() { var msg = "{% trans 'The reset-ssh-public-key E-mail has been sent successfully. Please inform the user to update his new ssh public key.' %}"; @@ -349,7 +350,7 @@ $(document).ready(function() { }; APIUpdateAttr({ url: the_url, - body: JSON.stringify(body), + body: body, success: success }); } @@ -367,7 +368,7 @@ $(document).ready(function() { }).on('click', '#btn_user_update_pk', function(){ var $this = $(this); var pk = $('#txt_pk').val(); -{# var the_url = '{% url "users:user-update-pk-api" pk=user.id %}';#} + var the_url = '{% url "users:api-user-update-pk" pk=user.id %}'; var body = {'_public_key': pk}; var success = function() { $('#txt_pk').val(''); @@ -375,8 +376,7 @@ $(document).ready(function() { var msg = "{% trans 'Successfully updated the SSH public key.' %}"; swal("{% trans 'User SSH Public Key Update' %}", msg, "success"); }; - var fail = function() { - var msg = "{% trans 'Failed to update the user\'s SSH public key.' %}"; + var fail = function(msg) { swal({ title: "{% trans 'User SSH Public Key Update' %}", text: msg, @@ -389,7 +389,7 @@ $(document).ready(function() { $('#txt_pk').focus(); } ); - } + }; APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail}); }); diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index 24b4f8e0c..087d27db0 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -2,8 +2,8 @@ {% load i18n static %} {% block table_search %}{% endblock %} {% block table_container %} - + diff --git a/apps/users/templates/users/user_update.html b/apps/users/templates/users/user_update.html index 36294a01f..b431fb013 100644 --- a/apps/users/templates/users/user_update.html +++ b/apps/users/templates/users/user_update.html @@ -1,14 +1,14 @@ {% extends 'users/_user.html' %} {% load i18n %} {% block user_template_title %}{% trans "Update user" %}{% endblock %} -{% block username %} -
- -
- -
-
-{% endblock %} +{#{% block username %}#} +{#
#} +{# #} +{#
#} +{# #} +{#
#} +{#
#} +{#{% endblock %}#} {% block password %}

{% trans 'Password' %}

diff --git a/apps/users/urls.py b/apps/users/urls.py index 2efd430f4..6fd732d8c 100644 --- a/apps/users/urls.py +++ b/apps/users/urls.py @@ -44,16 +44,16 @@ router.register(r'v1/users', api.UserViewSet, 'api-user') urlpatterns += [ # url(r'^v1/users/$', api.UserListUpdateApi.as_view(), name='user-bulk-update-api'), - url(r'^v1/users/token/$', api.UserAuthApi.as_view(), name='user-token-api'), - url(r'^v1/users/(?P\d+)/reset-password/$', api.UserResetPasswordApi.as_view(), name='user-reset-password-api'), - # url(r'^v1/users/(?P\d+)/reset-pk/$', api.UserResetPKApi.as_view(), name='user-reset-pk-api'), - # url(r'^v1/users/(?P\d+)/update-pk/$', api.UserUpdatePKApi.as_view(), name='user-update-pk-api'), + url(r'^v1/users/token/$', api.UserAuthApi.as_view(), name='api-user-token'), + url(r'^v1/users/(?P\d+)/reset-password/$', api.UserResetPasswordApi.as_view(), name='api-user-reset-password'), + url(r'^v1/users/(?P\d+)/reset-pk/$', api.UserResetPKApi.as_view(), name='api-user-reset-pk'), + url(r'^v1/users/(?P\d+)/update-pk/$', api.UserUpdatePKApi.as_view(), name='api-user-update-pk'), # url(r'^v1/user-groups/$', api.GroupListUpdateApi.as_view(), name='user-group-bulk-update-api'), # url(r'^v1/user-groups/(?P\d+)/$', api.GroupDetailApi.as_view(), name='user-group-detail-api'), # url(r'^v1/user-groups/(?P\d+)/user/(?P\d+)/$', # api.DeleteUserFromGroupApi.as_view(), name='delete-user-from-group-api'), - # url(r'^v1/user-groups/(?P\d+)/users/$', - # api.UserAndGroupEditApi.as_view(), name='group-user-edit-api'), + url(r'^v1/users/(?P\d+)/groups/$', + api.UserAndGroupEditApi.as_view(), name='group-user-edit-api'), ] urlpatterns += router.urls diff --git a/apps/users/utils.py b/apps/users/utils.py index 1e9e2d8e4..7ce37a463 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -34,9 +34,6 @@ class AdminUserRequiredMixin(UserPassesTestMixin): return self.request.user.is_staff - - - def user_add_success_next(user): subject = _('Create account successfully') recipient_list = [user.email] diff --git a/apps/users/views.py b/apps/users/views.py index 7b472584e..006df3328 100644 --- a/apps/users/views.py +++ b/apps/users/views.py @@ -27,10 +27,9 @@ from formtools.wizard.views import SessionWizardView from common.mixins import JSONResponseMixin from common.utils import get_object_or_none, get_logger from .models import User, UserGroup -from .forms import UserCreateForm, UserUpdateForm, UserGroupForm, UserLoginForm, UserInfoForm, UserKeyForm, \ - UserPrivateAssetPermissionForm, UserBulkImportForm from .utils import AdminUserRequiredMixin, user_add_success_next, send_reset_password_mail from .hands import AssetPermission, get_user_granted_asset_groups, get_user_granted_assets +from . import forms logger = get_logger(__name__) @@ -41,7 +40,7 @@ logger = get_logger(__name__) @method_decorator(never_cache, name='dispatch') class UserLoginView(FormView): template_name = 'users/login.html' - form_class = UserLoginForm + form_class = forms.UserLoginForm redirect_field_name = 'next' def get(self, request, *args, **kwargs): @@ -92,7 +91,7 @@ class UserListView(AdminUserRequiredMixin, TemplateView): class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): model = User - form_class = UserCreateForm + form_class = forms.UserCreateUpdateForm template_name = 'users/user_create.html' success_url = reverse_lazy('users:user-list') success_message = _('Create user %s successfully.') @@ -118,7 +117,7 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): class UserUpdateView(AdminUserRequiredMixin, UpdateView): model = User - form_class = UserUpdateForm + form_class = forms.UserCreateUpdateForm template_name = 'users/user_update.html' context_object_name = 'user_object' success_url = reverse_lazy('users:user-list') @@ -162,7 +161,7 @@ class UserGroupListView(AdminUserRequiredMixin, TemplateView): class UserGroupCreateView(AdminUserRequiredMixin, CreateView): model = UserGroup - form_class = UserGroupForm + form_class = forms.UserGroupForm template_name = 'users/user_group_create.html' success_url = reverse_lazy('users:user-group-list') @@ -184,7 +183,7 @@ class UserGroupCreateView(AdminUserRequiredMixin, CreateView): class UserGroupUpdateView(AdminUserRequiredMixin, UpdateView): model = UserGroup - form_class = UserGroupForm + form_class = forms.UserGroupForm template_name = 'users/user_group_create.html' success_url = reverse_lazy('users:user-group-list') @@ -294,7 +293,7 @@ class UserResetPasswordView(TemplateView): class UserFirstLoginView(LoginRequiredMixin, SessionWizardView): template_name = 'users/first_login.html' - form_list = [UserInfoForm, UserKeyForm] + form_list = [forms.UserInfoForm, forms.UserKeyForm] file_storage = default_storage def dispatch(self, request, *args, **kwargs): @@ -346,7 +345,7 @@ class UserAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMix paginate_by = settings.CONFIG.DISPLAY_PER_PAGE template_name = 'users/user_asset_permission.html' context_object_name = 'user_object' - form_class = UserPrivateAssetPermissionForm + form_class = forms.UserPrivateAssetPermissionForm def get(self, request, *args, **kwargs): self.object = self.get_object(queryset=User.objects.all()) @@ -379,7 +378,7 @@ class UserAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMix class UserAssetPermissionCreateView(AdminUserRequiredMixin, CreateView): - form_class = UserPrivateAssetPermissionForm + form_class = forms.UserPrivateAssetPermissionForm model = AssetPermission def get(self, request, *args, **kwargs): @@ -432,12 +431,8 @@ class UserGrantedAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListView): return super(UserGrantedAssetView, self).get_context_data(**kwargs) -class FileForm(forms.Form): - excel = forms.FileField() - - class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView): - form_class = FileForm + form_class = forms.FileForm def form_invalid(self, form): try: @@ -478,7 +473,7 @@ class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView): 'enable_otp': True if enable_otp in ['T', '1', 1, True] else False, 'role': role } - form = UserBulkImportForm(data, auto_id=False) + form = forms.UserBulkImportForm(data, auto_id=False) if form.is_valid(): form.save() else: diff --git a/run_server.py b/run_server.py index e1d3240c3..689740f0f 100644 --- a/run_server.py +++ b/run_server.py @@ -28,6 +28,7 @@ def start_django(): def start_celery(): os.chdir(apps_dir) + os.environ.setdefault('C_FORCE_ROOT', '1') print('start celery') subprocess.call('celery -A common worker -l info', shell=True)