mirror of https://github.com/jumpserver/jumpserver
[Update] 修改assets users api
parent
f10a7a75ae
commit
e8ebc94191
|
@ -40,9 +40,8 @@ class AssetUser(OrgModelMixin):
|
|||
|
||||
@property
|
||||
def private_key_obj(self):
|
||||
if self._private_key:
|
||||
key_str = signer.unsign(self._private_key)
|
||||
return ssh_key_string_to_obj(key_str, password=self.password)
|
||||
if self.private_key:
|
||||
return ssh_key_string_to_obj(self.private_key, password=self.password)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -52,8 +51,7 @@ class AssetUser(OrgModelMixin):
|
|||
return None
|
||||
project_dir = settings.PROJECT_DIR
|
||||
tmp_dir = os.path.join(project_dir, 'tmp')
|
||||
key_str = signer.unsign(self._private_key)
|
||||
key_name = '.' + md5(key_str.encode('utf-8')).hexdigest()
|
||||
key_name = '.' + md5(self.private_key.encode('utf-8')).hexdigest()
|
||||
key_path = os.path.join(tmp_dir, key_name)
|
||||
if not os.path.exists(key_path):
|
||||
self.private_key_obj.write_private_key_file(key_path)
|
||||
|
|
|
@ -15,20 +15,20 @@ class AdminUserSerializer(BulkOrgResourceModelSerializer):
|
|||
"""
|
||||
管理用户
|
||||
"""
|
||||
password = serializers.CharField(
|
||||
required=False, write_only=True, label=_('Password')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
list_serializer_class = AdaptedBulkListSerializer
|
||||
model = AdminUser
|
||||
fields = [
|
||||
'id', 'name', 'username', 'password', 'comment',
|
||||
'connectivity_amount', 'assets_amount',
|
||||
'id', 'name', 'username', 'password', 'private_key', 'public_key',
|
||||
'comment', 'connectivity_amount', 'assets_amount',
|
||||
'date_created', 'date_updated', 'created_by',
|
||||
]
|
||||
|
||||
extra_kwargs = {
|
||||
'password': {"write_only": True},
|
||||
'private_key': {"write_only": True},
|
||||
'public_key': {"write_only": True},
|
||||
'date_created': {'read_only': True},
|
||||
'date_updated': {'read_only': True},
|
||||
'created_by': {'read_only': True},
|
||||
|
|
|
@ -29,18 +29,6 @@ class AssetUserSerializer(BulkOrgResourceModelSerializer):
|
|||
ip = serializers.CharField(read_only=True, label=_("IP"))
|
||||
connectivity = ConnectivitySerializer(read_only=True, label=_("Connectivity"))
|
||||
|
||||
password = serializers.CharField(
|
||||
max_length=256, allow_blank=True, allow_null=True, write_only=True,
|
||||
required=False, label=_('Password')
|
||||
)
|
||||
public_key = serializers.CharField(
|
||||
max_length=4096, allow_blank=True, allow_null=True, write_only=True,
|
||||
required=False, label=_('Public key')
|
||||
)
|
||||
private_key = serializers.CharField(
|
||||
max_length=4096, allow_blank=True, allow_null=True, write_only=True,
|
||||
required=False, label=_('Private key')
|
||||
)
|
||||
backend = serializers.CharField(read_only=True, label=_("Backend"))
|
||||
|
||||
class Meta:
|
||||
|
@ -57,6 +45,9 @@ class AssetUserSerializer(BulkOrgResourceModelSerializer):
|
|||
]
|
||||
extra_kwargs = {
|
||||
'username': {'required': True},
|
||||
'password': {'write_only': True},
|
||||
'private_key': {'write_only': True},
|
||||
'public_key': {'write_only': True},
|
||||
}
|
||||
|
||||
def validate_private_key(self, key):
|
||||
|
@ -67,17 +58,9 @@ class AssetUserSerializer(BulkOrgResourceModelSerializer):
|
|||
return key
|
||||
|
||||
def create(self, validated_data):
|
||||
kwargs = {
|
||||
'name': validated_data.get('username'),
|
||||
'username': validated_data.get('username'),
|
||||
'asset': validated_data.get('asset'),
|
||||
'comment': validated_data.get('comment', ''),
|
||||
'org_id': validated_data.get('org_id', ''),
|
||||
'password': validated_data.get('password'),
|
||||
'public_key': validated_data.get('public_key'),
|
||||
'private_key': validated_data.get('private_key')
|
||||
}
|
||||
instance = AssetUserManager.create(**kwargs)
|
||||
if not validated_data.get("name") and validated_data.get("username"):
|
||||
validated_data["name"] = validated_data["username"]
|
||||
instance = AssetUserManager.create(**validated_data)
|
||||
return instance
|
||||
|
||||
|
||||
|
|
|
@ -12,20 +12,20 @@ class SystemUserSerializer(BulkOrgResourceModelSerializer):
|
|||
"""
|
||||
系统用户
|
||||
"""
|
||||
password = serializers.CharField(
|
||||
required=False, write_only=True, label=_('Password')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = SystemUser
|
||||
list_serializer_class = AdaptedBulkListSerializer
|
||||
fields = [
|
||||
'id', 'name', 'username', 'login_mode', 'login_mode_display',
|
||||
'priority', 'protocol', 'auto_push', 'password',
|
||||
'cmd_filters', 'sudo', 'shell', 'comment', 'nodes', 'assets',
|
||||
'assets_amount', 'connectivity_amount'
|
||||
'id', 'name', 'username', 'password', 'public_key', 'private_key',
|
||||
'login_mode', 'login_mode_display', 'priority', 'protocol',
|
||||
'auto_push', 'cmd_filters', 'sudo', 'shell', 'comment', 'nodes',
|
||||
'assets', 'assets_amount', 'connectivity_amount'
|
||||
]
|
||||
extra_kwargs = {
|
||||
'password': {"write_only": True},
|
||||
'public_key': {"write_only": True},
|
||||
'private_key': {"write_only": True},
|
||||
'assets_amount': {'label': _('Asset')},
|
||||
'connectivity_amount': {'label': _('Connectivity')},
|
||||
'login_mode_display': {'label': _('Login mode display')},
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from werkzeug.local import Local
|
||||
|
||||
thread_local = Local()
|
||||
|
||||
|
||||
def _find(attr):
|
||||
return getattr(thread_local, attr, None)
|
|
@ -3,12 +3,13 @@
|
|||
import re
|
||||
from collections import defaultdict
|
||||
from django.conf import settings
|
||||
|
||||
from django.dispatch import receiver
|
||||
from django.core.signals import request_finished
|
||||
from django.db import connection
|
||||
|
||||
|
||||
from .utils import get_logger
|
||||
from .local import thread_local
|
||||
|
||||
logger = get_logger(__file__)
|
||||
pattern = re.compile(r'FROM `(\w+)`')
|
||||
|
@ -50,6 +51,11 @@ def on_request_finished_logging_db_query(sender, **kwargs):
|
|||
)
|
||||
|
||||
|
||||
@receiver(request_finished)
|
||||
def on_request_finished_release_local(sender, **kwargs):
|
||||
thread_local.__release_local__()
|
||||
|
||||
|
||||
if settings.DEBUG:
|
||||
request_finished.connect(on_request_finished_logging_db_query)
|
||||
|
||||
|
|
|
@ -176,125 +176,6 @@ def with_cache(func):
|
|||
return wrapper
|
||||
|
||||
|
||||
class LocalProxy(object):
|
||||
|
||||
"""
|
||||
Copy from werkzeug.local.LocalProxy
|
||||
"""
|
||||
__slots__ = ('__local', '__dict__', '__name__', '__wrapped__')
|
||||
|
||||
def __init__(self, local, name=None):
|
||||
object.__setattr__(self, '_LocalProxy__local', local)
|
||||
object.__setattr__(self, '__name__', name)
|
||||
if callable(local) and not hasattr(local, '__release_local__'):
|
||||
# "local" is a callable that is not an instance of Local or
|
||||
# LocalManager: mark it as a wrapped function.
|
||||
object.__setattr__(self, '__wrapped__', local)
|
||||
|
||||
def _get_current_object(self):
|
||||
"""Return the current object. This is useful if you want the real
|
||||
object behind the proxy at a time for performance reasons or because
|
||||
you want to pass the object into a different context.
|
||||
"""
|
||||
if not hasattr(self.__local, '__release_local__'):
|
||||
return self.__local()
|
||||
try:
|
||||
return getattr(self.__local, self.__name__)
|
||||
except AttributeError:
|
||||
raise RuntimeError('no object bound to %s' % self.__name__)
|
||||
|
||||
@property
|
||||
def __dict__(self):
|
||||
try:
|
||||
return self._get_current_object().__dict__
|
||||
except RuntimeError:
|
||||
raise AttributeError('__dict__')
|
||||
|
||||
def __repr__(self):
|
||||
try:
|
||||
obj = self._get_current_object()
|
||||
except RuntimeError:
|
||||
return '<%s unbound>' % self.__class__.__name__
|
||||
return repr(obj)
|
||||
|
||||
def __bool__(self):
|
||||
try:
|
||||
return bool(self._get_current_object())
|
||||
except RuntimeError:
|
||||
return False
|
||||
|
||||
def __dir__(self):
|
||||
try:
|
||||
return dir(self._get_current_object())
|
||||
except RuntimeError:
|
||||
return []
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name == '__members__':
|
||||
return dir(self._get_current_object())
|
||||
return getattr(self._get_current_object(), name)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._get_current_object()[key] = value
|
||||
|
||||
def __delitem__(self, key):
|
||||
del self._get_current_object()[key]
|
||||
|
||||
__setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
|
||||
__delattr__ = lambda x, n: delattr(x._get_current_object(), n)
|
||||
__str__ = lambda x: str(x._get_current_object())
|
||||
__lt__ = lambda x, o: x._get_current_object() < o
|
||||
__le__ = lambda x, o: x._get_current_object() <= o
|
||||
__eq__ = lambda x, o: x._get_current_object() == o
|
||||
__ne__ = lambda x, o: x._get_current_object() != o
|
||||
__gt__ = lambda x, o: x._get_current_object() > o
|
||||
__ge__ = lambda x, o: x._get_current_object() >= o
|
||||
__cmp__ = lambda x, o: cmp(x._get_current_object(), o) # noqa
|
||||
__hash__ = lambda x: hash(x._get_current_object())
|
||||
__call__ = lambda x, *a, **kw: x._get_current_object()(*a, **kw)
|
||||
__len__ = lambda x: len(x._get_current_object())
|
||||
__getitem__ = lambda x, i: x._get_current_object()[i]
|
||||
__iter__ = lambda x: iter(x._get_current_object())
|
||||
__contains__ = lambda x, i: i in x._get_current_object()
|
||||
__add__ = lambda x, o: x._get_current_object() + o
|
||||
__sub__ = lambda x, o: x._get_current_object() - o
|
||||
__mul__ = lambda x, o: x._get_current_object() * o
|
||||
__floordiv__ = lambda x, o: x._get_current_object() // o
|
||||
__mod__ = lambda x, o: x._get_current_object() % o
|
||||
__divmod__ = lambda x, o: x._get_current_object().__divmod__(o)
|
||||
__pow__ = lambda x, o: x._get_current_object() ** o
|
||||
__lshift__ = lambda x, o: x._get_current_object() << o
|
||||
__rshift__ = lambda x, o: x._get_current_object() >> o
|
||||
__and__ = lambda x, o: x._get_current_object() & o
|
||||
__xor__ = lambda x, o: x._get_current_object() ^ o
|
||||
__or__ = lambda x, o: x._get_current_object() | o
|
||||
__div__ = lambda x, o: x._get_current_object().__div__(o)
|
||||
__truediv__ = lambda x, o: x._get_current_object().__truediv__(o)
|
||||
__neg__ = lambda x: -(x._get_current_object())
|
||||
__pos__ = lambda x: +(x._get_current_object())
|
||||
__abs__ = lambda x: abs(x._get_current_object())
|
||||
__invert__ = lambda x: ~(x._get_current_object())
|
||||
__complex__ = lambda x: complex(x._get_current_object())
|
||||
__int__ = lambda x: int(x._get_current_object())
|
||||
__float__ = lambda x: float(x._get_current_object())
|
||||
__oct__ = lambda x: oct(x._get_current_object())
|
||||
__hex__ = lambda x: hex(x._get_current_object())
|
||||
__index__ = lambda x: x._get_current_object().__index__()
|
||||
__coerce__ = lambda x, o: x._get_current_object().__coerce__(x, o)
|
||||
__enter__ = lambda x: x._get_current_object().__enter__()
|
||||
__exit__ = lambda x, *a, **kw: x._get_current_object().__exit__(*a, **kw)
|
||||
__radd__ = lambda x, o: o + x._get_current_object()
|
||||
__rsub__ = lambda x, o: o - x._get_current_object()
|
||||
__rmul__ = lambda x, o: o * x._get_current_object()
|
||||
__rdiv__ = lambda x, o: o / x._get_current_object()
|
||||
__rtruediv__ = __rdiv__
|
||||
__rfloordiv__ = lambda x, o: o // x._get_current_object()
|
||||
__rmod__ = lambda x, o: o % x._get_current_object()
|
||||
__rdivmod__ = lambda x, o: x._get_current_object().__rdivmod__(o)
|
||||
__copy__ = lambda x: copy.copy(x._get_current_object())
|
||||
__deepcopy__ = lambda x, memo: copy.deepcopy(x._get_current_object(), memo)
|
||||
|
||||
|
||||
def random_string(length):
|
||||
import string
|
||||
import random
|
||||
|
|
|
@ -1,24 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from functools import partial
|
||||
|
||||
from common.utils import LocalProxy
|
||||
|
||||
try:
|
||||
from threading import local
|
||||
except ImportError:
|
||||
from django.utils._threading_local import local
|
||||
|
||||
_thread_locals = local()
|
||||
from werkzeug.local import LocalProxy
|
||||
from common.local import thread_local
|
||||
|
||||
|
||||
def set_current_request(request):
|
||||
setattr(_thread_locals, 'current_request', request)
|
||||
setattr(thread_local, 'current_request', request)
|
||||
|
||||
|
||||
def _find(attr):
|
||||
return getattr(_thread_locals, attr, None)
|
||||
return getattr(thread_local, attr, None)
|
||||
|
||||
|
||||
def get_current_request():
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from functools import partial
|
||||
from werkzeug.local import Local
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from common.utils import LocalProxy
|
||||
from common.local import thread_local
|
||||
from .models import Organization
|
||||
|
||||
|
||||
_thread_locals = Local()
|
||||
|
||||
|
||||
def get_org_from_request(request):
|
||||
oid = request.session.get("oid")
|
||||
if not oid:
|
||||
|
@ -19,7 +15,7 @@ def get_org_from_request(request):
|
|||
|
||||
|
||||
def set_current_org(org):
|
||||
setattr(_thread_locals, 'current_org', org)
|
||||
setattr(thread_local, 'current_org', org.id)
|
||||
|
||||
|
||||
def set_to_default_org():
|
||||
|
@ -31,17 +27,18 @@ def set_to_root_org():
|
|||
|
||||
|
||||
def _find(attr):
|
||||
return getattr(_thread_locals, attr, None)
|
||||
return getattr(thread_local, attr, None)
|
||||
|
||||
|
||||
def get_current_org():
|
||||
return _find('current_org')
|
||||
org_id = _find('current_org')
|
||||
org = Organization.get_instance(org_id)
|
||||
return org
|
||||
|
||||
|
||||
def get_current_org_id():
|
||||
org = get_current_org()
|
||||
org_id = str(org.id) if org.is_real() else ''
|
||||
org_id = _find('current_org')
|
||||
return org_id
|
||||
|
||||
|
||||
current_org = LocalProxy(partial(_find, 'current_org'))
|
||||
current_org = LocalProxy(get_current_org)
|
||||
|
|
|
@ -48,9 +48,10 @@ class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
|
|||
|
||||
def perform_create(self, serializer):
|
||||
users = serializer.save()
|
||||
for user in users:
|
||||
if current_org and current_org.is_real():
|
||||
user.orgs.add(current_org.id)
|
||||
if isinstance(users, User):
|
||||
users = [users]
|
||||
if current_org and current_org.is_real():
|
||||
current_org.users.add(*users)
|
||||
self.send_created_signal(users)
|
||||
|
||||
def get_queryset(self):
|
||||
|
@ -174,6 +175,7 @@ class UserResetPKApi(generics.UpdateAPIView):
|
|||
send_reset_ssh_key_mail(user)
|
||||
|
||||
|
||||
# 废弃
|
||||
class UserUpdatePKApi(generics.UpdateAPIView):
|
||||
queryset = User.objects.all()
|
||||
serializer_class = UserPKUpdateSerializer
|
||||
|
@ -181,7 +183,7 @@ class UserUpdatePKApi(generics.UpdateAPIView):
|
|||
|
||||
def perform_update(self, serializer):
|
||||
user = self.get_object()
|
||||
user.public_key = serializer.validated_data['_public_key']
|
||||
user.public_key = serializer.validated_data['public_key']
|
||||
user.save()
|
||||
|
||||
|
||||
|
|
|
@ -19,13 +19,16 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
|||
model = User
|
||||
list_serializer_class = AdaptedBulkListSerializer
|
||||
fields = [
|
||||
'id', 'name', 'username', 'email', 'groups', 'groups_display',
|
||||
'id', 'name', 'username', 'password', 'email', 'public_key',
|
||||
'groups', 'groups_display',
|
||||
'role', 'role_display', 'wechat', 'phone', 'otp_level',
|
||||
'comment', 'source', 'source_display', 'is_valid', 'is_expired',
|
||||
'is_active', 'created_by', 'is_first_login',
|
||||
'date_password_last_updated', 'date_expired', 'avatar_url',
|
||||
]
|
||||
extra_kwargs = {
|
||||
'password': {'write_only': True},
|
||||
'public_key': {'write_only': True},
|
||||
'groups_display': {'label': _('Groups name')},
|
||||
'source_display': {'label': _('Source name')},
|
||||
'is_first_login': {'label': _('Is first login'), 'read_only': True},
|
||||
|
@ -36,14 +39,37 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
|||
'created_by': {'read_only': True}, 'source': {'read_only': True}
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def validate_password(value):
|
||||
from ..utils import check_password_rules
|
||||
if not check_password_rules(value):
|
||||
msg = _('Password does not match security rules')
|
||||
raise serializers.ValidationError(msg)
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def change_password_to_raw(validated_data):
|
||||
password = validated_data.pop('password', None)
|
||||
if password:
|
||||
validated_data['password_raw'] = password
|
||||
return validated_data
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data = self.change_password_to_raw(validated_data)
|
||||
return super().create(validated_data)
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
validated_data = self.change_password_to_raw(validated_data)
|
||||
return super().update(instance, validated_data)
|
||||
|
||||
|
||||
class UserPKUpdateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['id', '_public_key']
|
||||
fields = ['id', 'public_key']
|
||||
|
||||
@staticmethod
|
||||
def validate__public_key(value):
|
||||
def validate_public_key(value):
|
||||
if not validate_ssh_public_key(value):
|
||||
raise serializers.ValidationError(_('Not a valid ssh public key'))
|
||||
return value
|
||||
|
|
|
@ -217,33 +217,9 @@
|
|||
{% endblock %}
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
$(document).on('click', '#btn_update_pk', function() {
|
||||
var $this = $(this);
|
||||
var pk = $('#txt_pk').val();
|
||||
var the_url = '{% url "api-users:user-public-key-update" pk=user.id %}';
|
||||
var body = {'_public_key': pk};
|
||||
var success = function() {
|
||||
$('#txt_pk').val('');
|
||||
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 SSH public key.' %}";
|
||||
swal({
|
||||
title: "{% trans 'User SSH public key update' %}",
|
||||
text: msg,
|
||||
type: "error",
|
||||
showCancelButton: false,
|
||||
confirmButtonColor: "#DD6B55",
|
||||
confirmButtonText: "{% trans 'Confirm' %}",
|
||||
closeOnConfirm: true
|
||||
}, function () {
|
||||
$('#txt_pk').focus();
|
||||
}
|
||||
);
|
||||
};
|
||||
APIUpdateAttr({ url: the_url, body: JSON.stringify(body), success: success, error: fail});
|
||||
}).on('click', '.btn-reset-pubkey', function () {
|
||||
$(document).ready(function () {
|
||||
})
|
||||
.on('click', '.btn-reset-pubkey', function () {
|
||||
var the_url = '{% url "users:user-pubkey-generate" %}';
|
||||
window.open(the_url, "_blank")
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue