[Update] 修复系统用户管理用户提及重置密码的bug (#2899)

* [Update] 修复系统用户管理用户提及重置密码的bug

* [Update]  去掉forms

* [Update] 修改翻译

* [Update] 去掉debug信息
pull/2901/head
老广 2019-07-08 15:35:20 +08:00 committed by GitHub
parent 4c34246750
commit bf5acf7ef1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 411 additions and 276 deletions

View File

@ -65,17 +65,7 @@ class PasswordAndKeyAuthForm(forms.ModelForm):
class AdminUserForm(PasswordAndKeyAuthForm): class AdminUserForm(PasswordAndKeyAuthForm):
def save(self, commit=True): def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save` raise forms.ValidationError("Use api to save")
admin_user = super().save(commit=commit)
password = self.cleaned_data.get('password', '') or None
private_key, public_key = super().gen_keys()
admin_user.set_auth(password=password, public_key=public_key, private_key=private_key)
return admin_user
def clean(self):
super().clean()
if not self.instance:
super().validate_password_key()
class Meta: class Meta:
model = AdminUser model = AdminUser
@ -91,51 +81,7 @@ class SystemUserForm(OrgModelForm, PasswordAndKeyAuthForm):
auto_generate_key = forms.BooleanField(initial=True, required=False) auto_generate_key = forms.BooleanField(initial=True, required=False)
def save(self, commit=True): def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save` raise forms.ValidationError("Use api to save")
system_user = super().save()
password = self.cleaned_data.get('password', '') or None
login_mode = self.cleaned_data.get('login_mode', '') or None
protocol = self.cleaned_data.get('protocol') or None
auto_generate_key = self.cleaned_data.get('auto_generate_key', False)
private_key, public_key = super().gen_keys()
if login_mode == SystemUser.LOGIN_MANUAL or \
protocol in [SystemUser.PROTOCOL_TELNET,
SystemUser.PROTOCOL_VNC]:
system_user.auto_push = 0
system_user.save()
auto_generate_key = False
if auto_generate_key:
logger.info('Auto generate key and set system user auth')
if protocol == SystemUser.PROTOCOL_SSH:
system_user.auto_gen_auth()
elif protocol == SystemUser.PROTOCOL_RDP:
system_user.auto_gen_auth_password()
else:
system_user.set_auth(password=password, private_key=private_key,
public_key=public_key)
return system_user
def clean(self):
super().clean()
auto_generate = self.cleaned_data.get('auto_generate_key')
if not self.instance and not auto_generate:
super().validate_password_key()
def clean_username(self):
username = self.data.get('username')
login_mode = self.data.get('login_mode')
protocol = self.data.get('protocol')
if username:
return username
if login_mode == SystemUser.LOGIN_AUTO and \
protocol != SystemUser.PROTOCOL_VNC:
msg = _('* Automatic login mode must fill in the username.')
raise forms.ValidationError(msg)
return username
class Meta: class Meta:
model = SystemUser model = SystemUser

View File

@ -197,6 +197,17 @@ class AssetUser(OrgModelMixin):
self.public_key = '' self.public_key = ''
self.save() self.save()
@staticmethod
def gen_password():
return str(uuid.uuid4())
@staticmethod
def gen_key(username):
private_key, public_key = ssh_key_gen(
username=username
)
return private_key, public_key
def auto_gen_auth(self): def auto_gen_auth(self):
password = str(uuid.uuid4()) password = str(uuid.uuid4())
private_key, public_key = ssh_key_gen( private_key, public_key = ssh_key_gen(

View File

@ -8,10 +8,10 @@ from common.serializers import AdaptedBulkListSerializer
from ..models import Node, AdminUser from ..models import Node, AdminUser
from orgs.mixins import BulkOrgResourceModelSerializer from orgs.mixins import BulkOrgResourceModelSerializer
from .base import AuthSerializer from .base import AuthSerializer, AuthSerializerMixin
class AdminUserSerializer(BulkOrgResourceModelSerializer): class AdminUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
""" """
管理用户 管理用户
""" """

View File

@ -4,12 +4,11 @@
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from rest_framework import serializers from rest_framework import serializers
from common.utils import validate_ssh_private_key
from common.serializers import AdaptedBulkListSerializer from common.serializers import AdaptedBulkListSerializer
from orgs.mixins import BulkOrgResourceModelSerializer from orgs.mixins import BulkOrgResourceModelSerializer
from ..models import AuthBook, Asset from ..models import AuthBook, Asset
from ..backends import AssetUserManager from ..backends import AssetUserManager
from .base import ConnectivitySerializer from .base import ConnectivitySerializer, AuthSerializerMixin
__all__ = [ __all__ = [
@ -24,7 +23,7 @@ class BasicAssetSerializer(serializers.ModelSerializer):
fields = ['hostname', 'ip'] fields = ['hostname', 'ip']
class AssetUserSerializer(BulkOrgResourceModelSerializer): class AssetUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
hostname = serializers.CharField(read_only=True, label=_("Hostname")) hostname = serializers.CharField(read_only=True, label=_("Hostname"))
ip = serializers.CharField(read_only=True, label=_("IP")) ip = serializers.CharField(read_only=True, label=_("IP"))
connectivity = ConnectivitySerializer(read_only=True, label=_("Connectivity")) connectivity = ConnectivitySerializer(read_only=True, label=_("Connectivity"))
@ -50,13 +49,6 @@ class AssetUserSerializer(BulkOrgResourceModelSerializer):
'public_key': {'write_only': True}, 'public_key': {'write_only': True},
} }
def validate_private_key(self, key):
password = self.initial_data.get("password")
valid = validate_ssh_private_key(key, password)
if not valid:
raise serializers.ValidationError(_("private key invalid"))
return key
def create(self, validated_data): def create(self, validated_data):
if not validated_data.get("name") and validated_data.get("username"): if not validated_data.get("name") and validated_data.get("username"):
validated_data["name"] = validated_data["username"] validated_data["name"] = validated_data["username"]

View File

@ -2,7 +2,7 @@
# #
from rest_framework import serializers from rest_framework import serializers
from common.utils import ssh_pubkey_gen from common.utils import ssh_pubkey_gen, validate_ssh_private_key
class AuthSerializer(serializers.ModelSerializer): class AuthSerializer(serializers.ModelSerializer):
@ -28,4 +28,38 @@ class AuthSerializer(serializers.ModelSerializer):
class ConnectivitySerializer(serializers.Serializer): class ConnectivitySerializer(serializers.Serializer):
status = serializers.IntegerField() status = serializers.IntegerField()
datetime = serializers.DateTimeField() datetime = serializers.DateTimeField()
class AuthSerializerMixin:
def validate_password(self, password):
return password
def validate_private_key(self, private_key):
if not private_key:
return
password = self.initial_data.get("password")
valid = validate_ssh_private_key(private_key, password)
if not valid:
raise serializers.ValidationError(_("private key invalid"))
return private_key
def validate_public_key(self, public_key):
return public_key
@staticmethod
def clean_auth_fields(validated_data):
for field in ('password', 'private_key', 'public_key'):
value = validated_data.get(field)
if not value:
validated_data.pop(field, None)
# print(validated_data)
# raise serializers.ValidationError(">>>>>>")
def create(self, validated_data):
self.clean_auth_fields(validated_data)
return super().create(validated_data)
def update(self, instance, validated_data):
self.clean_auth_fields(validated_data)
return super().update(instance, validated_data)

View File

@ -5,13 +5,14 @@ from django.utils.translation import ugettext_lazy as _
from common.serializers import AdaptedBulkListSerializer from common.serializers import AdaptedBulkListSerializer
from orgs.mixins import BulkOrgResourceModelSerializer from orgs.mixins import BulkOrgResourceModelSerializer
from ..models import SystemUser from ..models import SystemUser
from .base import AuthSerializer from .base import AuthSerializer, AuthSerializerMixin
class SystemUserSerializer(BulkOrgResourceModelSerializer): class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
""" """
系统用户 系统用户
""" """
auto_generate_key = serializers.BooleanField(initial=True, required=False, write_only=True)
class Meta: class Meta:
model = SystemUser model = SystemUser
@ -20,7 +21,7 @@ class SystemUserSerializer(BulkOrgResourceModelSerializer):
'id', 'name', 'username', 'password', 'public_key', 'private_key', 'id', 'name', 'username', 'password', 'public_key', 'private_key',
'login_mode', 'login_mode_display', 'priority', 'protocol', 'login_mode', 'login_mode_display', 'priority', 'protocol',
'auto_push', 'cmd_filters', 'sudo', 'shell', 'comment', 'nodes', 'auto_push', 'cmd_filters', 'sudo', 'shell', 'comment', 'nodes',
'assets_amount', 'connectivity_amount' 'assets_amount', 'connectivity_amount', 'auto_generate_key'
] ]
extra_kwargs = { extra_kwargs = {
'password': {"write_only": True}, 'password': {"write_only": True},
@ -32,6 +33,63 @@ class SystemUserSerializer(BulkOrgResourceModelSerializer):
'created_by': {'read_only': True}, 'created_by': {'read_only': True},
} }
def validate_auto_push(self, value):
login_mode = self.initial_data.get("login_mode")
protocol = self.initial_data.get("protocol")
if login_mode == SystemUser.LOGIN_MANUAL or \
protocol in [SystemUser.PROTOCOL_TELNET,
SystemUser.PROTOCOL_VNC]:
value = False
return value
def validate_auto_generate_key(self, value):
login_mode = self.initial_data.get("login_mode")
protocol = self.initial_data.get("protocol")
if self.context["request"].method.lower() != "post":
value = False
elif self.instance:
value = False
elif login_mode == SystemUser.LOGIN_MANUAL:
value = False
elif protocol in [SystemUser.PROTOCOL_TELNET, SystemUser.PROTOCOL_VNC]:
value = False
return value
def validate_username(self, username):
if username:
return username
login_mode = self.validated_data.get("login_mode")
protocol = self.validated_data.get("protocol")
if login_mode == SystemUser.LOGIN_AUTO and \
protocol != SystemUser.PROTOCOL_VNC:
msg = _('* Automatic login mode must fill in the username.')
raise serializers.ValidationError(msg)
return username
def validate_password(self, password):
super().validate_password(password)
auto_gen_key = self.initial_data.get("auto_generate_key", False)
private_key = self.initial_data.get("private_key")
if not self.instance and not auto_gen_key and not password and not private_key:
raise serializers.ValidationError(_("Password or private key required"))
return password
def validate(self, attrs):
username = attrs.get("username", "manual")
protocol = attrs.get("protocol")
auto_gen_key = attrs.get("auto_generate_key", False)
if auto_gen_key:
password = SystemUser.gen_password()
attrs["password"] = password
if protocol == SystemUser.PROTOCOL_SSH:
private_key, public_key = SystemUser.gen_key(username)
attrs["private_key"] = private_key
attrs["public_key"] = public_key
attrs.pop("auto_generate_key", None)
return attrs
@classmethod @classmethod
def setup_eager_loading(cls, queryset): def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """ """ Perform necessary eager loading of data. """
@ -52,7 +110,6 @@ class SystemUserAuthSerializer(AuthSerializer):
] ]
class SystemUserSimpleSerializer(serializers.ModelSerializer): class SystemUserSimpleSerializer(serializers.ModelSerializer):
""" """
系统用户最基本信息的数据结构 系统用户最基本信息的数据结构

View File

@ -218,6 +218,31 @@ $(document).ready(function () {
}) })
.on('change', protocol_id, function(){ .on('change', protocol_id, function(){
fieldDisplay(); fieldDisplay();
}).on("submit", "form", function (evt) {
evt.preventDefault();
{% block formUrl %}
var the_url = '{% url 'api-assets:system-user-list' %}';
var redirect_to = '{% url "assets:system-user-list" %}';
var method = "POST";
{% endblock %}
var form = $("form");
var data = form.serializeObject();
objectAttrsIsBool(data, ["auto_generate_key", "auto_push"]);
data["private_key"] = $("#id_private_key_file").data('file');
var props = {
url: the_url,
data: data,
method: method,
form: form,
redirect_to: redirect_to
};
formSubmit(props);
}).on('change', '#id_private_key_file', function () {
readFile($(this)).on("onload", function (evt, data) {
$(this).attr("data-file", data)
})
}) })
</script> </script>

View File

@ -54,9 +54,38 @@
</div> </div>
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2(); $('.select2').select2();
}) })
</script> .on("submit", "form", function (evt) {
evt.preventDefault();
var the_url = '{% url 'api-assets:admin-user-list' %}';
var redirect_to = '{% url "assets:admin-user-list" %}';
var method = "POST";
{% if type == "update" %}
the_url = '{% url 'api-assets:admin-user-detail' pk=object.id %}';
redirect_to = '{% url "assets:admin-user-list" %}';
method = "PUT";
{% endif %}
var form = $("form");
var data = form.serializeObject();
data["private_key"] = $("#id_private_key_file").data('file');
var props = {
url: the_url,
data: data,
method: method,
form: form,
redirect_to: redirect_to
};
formSubmit(props);
})
.on('change', '#id_private_key_file', function () {
readFile($(this)).on("onload", function (evt, data) {
$(this).attr("data-file", data)
})
})
</script>
{% endblock %} {% endblock %}

View File

@ -14,3 +14,9 @@
</div> </div>
{% endblock %} {% endblock %}
{% block formUrl %}
var the_url = '{% url 'api-assets:system-user-detail' pk=object.pk %}';
var redirect_to = '{% url "assets:system-user-list" %}';
var method = "PUT";
{% endblock %}

View File

@ -47,7 +47,8 @@ class AdminUserCreateView(PermissionsMixin,
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Create admin user') 'action': _('Create admin user'),
"type": "create"
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
@ -65,6 +66,7 @@ class AdminUserUpdateView(PermissionsMixin, SuccessMessageMixin, UpdateView):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Update admin user'), 'action': _('Update admin user'),
"type": "update"
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1156,4 +1156,22 @@ function timeOffset(a, b) {
return seconds.toFixed(1) + " " + getTimeUnits("s") return seconds.toFixed(1) + " " + getTimeUnits("s")
} }
return "" return ""
} }
function readFile(ref) {
var files = ref.prop('files');
var hasFile = files && files.length > 0;
if (hasFile) {
var reader = new FileReader();//新建一个FileReader
console.log(typeof files[0]);
reader.readAsText(files[0], "UTF-8");//读取文件
reader.onload = function(evt){ //读取完文件之后会回来这里
ref.trigger("onload", evt.target.result);
};
} else {
ref.trigger("onload", null);
}
return ref
}