mirror of https://github.com/jumpserver/jumpserver
Update api
parent
92d854b971
commit
70da177ed7
|
@ -1,6 +1,7 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
from rest_framework import viewsets, generics, mixins
|
||||
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework_bulk import BulkListSerializer, BulkSerializerMixin, ListBulkCreateUpdateDestroyAPIView
|
||||
|
@ -90,34 +91,19 @@ class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
|
|||
permission_classes = (IsSuperUser,)
|
||||
|
||||
|
||||
class SystemUserAuthApi(APIView):
|
||||
class SystemUserAuthInfoApi(generics.RetrieveAPIView):
|
||||
queryset = SystemUser.objects.all()
|
||||
permission_classes = (IsSuperUserOrAppUser,)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
system_user_id = request.query_params.get('system_user_id', -1)
|
||||
system_user_username = request.query_params.get('system_user_username', '')
|
||||
|
||||
system_user = get_object_or_none(SystemUser, id=system_user_id, username=system_user_username)
|
||||
|
||||
if system_user:
|
||||
if system_user.password:
|
||||
password = signer.sign(system_user.password)
|
||||
else:
|
||||
password = signer.sign('')
|
||||
|
||||
if system_user.private_key:
|
||||
private_key = signer.sign(system_user.private_key)
|
||||
else:
|
||||
private_key = signer.sign(None)
|
||||
|
||||
response = {
|
||||
'id': system_user.id,
|
||||
'password': password,
|
||||
'private_key': private_key,
|
||||
}
|
||||
|
||||
return Response(response)
|
||||
else:
|
||||
return Response({'msg': 'error system user id or username'}, status=401)
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
system_user = self.get_object()
|
||||
data = {
|
||||
'id': system_user.id,
|
||||
'name': system_user.name,
|
||||
'username': system_user.username,
|
||||
'password': system_user.password,
|
||||
'private_key': system_user.private_key,
|
||||
'auth_method': system_user.auth_method,
|
||||
}
|
||||
return Response(data)
|
||||
|
||||
|
|
|
@ -263,7 +263,7 @@ class SystemUserForm(forms.ModelForm):
|
|||
class Meta:
|
||||
model = SystemUser
|
||||
fields = [
|
||||
'name', 'username', 'protocol', 'auto_generate_key', 'password', 'private_key_file', 'as_default',
|
||||
'name', 'username', 'protocol', 'auto_generate_key', 'password', 'private_key_file', 'auth_method',
|
||||
'auto_push', 'auto_update', 'sudo', 'comment', 'shell', 'home', 'uid',
|
||||
]
|
||||
widgets = {
|
||||
|
@ -273,8 +273,8 @@ class SystemUserForm(forms.ModelForm):
|
|||
help_texts = {
|
||||
'name': '* required',
|
||||
'username': '* required',
|
||||
'auth_push': 'Auto push system user to asset',
|
||||
'auth_update': 'Auto update system user ssh key',
|
||||
'auto_push': 'Auto push system user to asset',
|
||||
'auto_update': 'Auto update system user ssh key',
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -95,13 +95,18 @@ class SystemUser(models.Model):
|
|||
PROTOCOL_CHOICES = (
|
||||
('ssh', 'ssh'),
|
||||
)
|
||||
AUTH_METHOD_CHOICES = (
|
||||
('P', 'Password'),
|
||||
('K', 'Public key'),
|
||||
)
|
||||
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
|
||||
username = models.CharField(max_length=16, verbose_name=_('Username'))
|
||||
_password = models.CharField(max_length=256, blank=True, verbose_name=_('Password'))
|
||||
protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
|
||||
_private_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH private key'))
|
||||
_public_key = models.CharField(max_length=4096, blank=True, verbose_name=_('SSH public key'))
|
||||
as_default = models.BooleanField(default=False, verbose_name=_('As default'))
|
||||
auth_method = models.CharField(choices=AUTH_METHOD_CHOICES, default='K',
|
||||
max_length=1, verbose_name=_('Auth method'))
|
||||
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
|
||||
auto_update = models.BooleanField(default=True, verbose_name=_('Auto update pass/key'))
|
||||
sudo = models.TextField(max_length=4096, default='/user/bin/whoami', verbose_name=_('Sudo'))
|
||||
|
|
|
@ -17,6 +17,7 @@ class AssetGroupSerializer(serializers.ModelSerializer):
|
|||
def get_assets_amount(obj):
|
||||
return obj.assets.count()
|
||||
|
||||
|
||||
class AssetUpdateGroupSerializer(serializers.ModelSerializer):
|
||||
groups = serializers.PrimaryKeyRelatedField(many=True, queryset=AssetGroup.objects.all())
|
||||
|
||||
|
@ -24,6 +25,7 @@ class AssetUpdateGroupSerializer(serializers.ModelSerializer):
|
|||
model = Asset
|
||||
fields = ['id', 'groups']
|
||||
|
||||
|
||||
class AssetUpdateSystemUserSerializer(serializers.ModelSerializer):
|
||||
system_users = serializers.PrimaryKeyRelatedField(many=True, queryset=SystemUser.objects.all())
|
||||
|
||||
|
@ -31,6 +33,7 @@ class AssetUpdateSystemUserSerializer(serializers.ModelSerializer):
|
|||
model = Asset
|
||||
fields = ['id', 'system_users']
|
||||
|
||||
|
||||
class AdminUserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = AdminUser
|
||||
|
@ -52,6 +55,12 @@ class SystemUserSerializer(serializers.ModelSerializer):
|
|||
return fields
|
||||
|
||||
|
||||
class SystemUserSimpleSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = SystemUser
|
||||
fields = ('id', 'name', 'username')
|
||||
|
||||
|
||||
class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
||||
# system_users = SystemUserSerializer(many=True, read_only=True)
|
||||
# admin_user = AdminUserSerializer(many=False, read_only=True)
|
||||
|
@ -75,7 +84,7 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
|||
|
||||
|
||||
class AssetGrantedSerializer(serializers.ModelSerializer):
|
||||
system_users = SystemUserSerializer(many=True, read_only=True)
|
||||
system_users = SystemUserSimpleSerializer(many=True, read_only=True)
|
||||
is_inherited = serializers.SerializerMethodField()
|
||||
system_users_join = serializers.SerializerMethodField()
|
||||
|
||||
|
|
|
@ -16,11 +16,12 @@ router.register(r'v1/system-user', api.SystemUserViewSet, 'system-user')
|
|||
urlpatterns = [
|
||||
url(r'^v1/assets_bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'),
|
||||
# url(r'^v1/idc/(?P<pk>[0-9]+)/assets/$', api.IDCAssetsApi.as_view(), name='api-idc-assets'),
|
||||
url(r'^v1/system-user/auth/', api.SystemUserAuthApi.as_view(), name='system-user-auth'),
|
||||
url(r'^v1/system-user/(?P<pk>[0-9]+)/auth-info/', api.SystemUserAuthInfoApi.as_view(),
|
||||
name='system-user-auth-info'),
|
||||
url(r'^v1/assets/(?P<pk>\d+)/groups/$',
|
||||
api.AssetUpdateGroupApi.as_view(), name='asset-update-group'),
|
||||
url(r'^v1/assets/(?P<pk>\d+)/system-users/$',
|
||||
api.SystemUserUpdateApi.as_view(), name='asset-update-systemusers'),
|
||||
api.SystemUserUpdateApi.as_view(), name='asset-update-system-users'),
|
||||
]
|
||||
|
||||
urlpatterns += router.urls
|
||||
|
|
|
@ -18,7 +18,6 @@ class LoginLog(models.Model):
|
|||
username = models.CharField(max_length=20, verbose_name=_('Username'))
|
||||
name = models.CharField(max_length=20, blank=True, verbose_name=_('Name'))
|
||||
login_type = models.CharField(choices=LOGIN_TYPE_CHOICE, max_length=2, verbose_name=_('Login type'))
|
||||
terminal = models.CharField(max_length=32, verbose_name=_('Terminal'))
|
||||
login_ip = models.GenericIPAddressField(verbose_name=_('Login ip'))
|
||||
login_city = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('Login city'))
|
||||
user_agent = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('User agent'))
|
||||
|
|
|
@ -17,25 +17,28 @@ def validate_ip(ip):
|
|||
return False
|
||||
|
||||
|
||||
def write_login_log(username, name='', login_type='W',
|
||||
terminal='', login_ip='', user_agent=''):
|
||||
def write_login_log(username, name='', login_type='',
|
||||
login_ip='', user_agent=''):
|
||||
if not (login_ip and validate_ip(login_ip)):
|
||||
login_ip = '0.0.0.0'
|
||||
if not name:
|
||||
name = username
|
||||
login_city = get_ip_city(login_ip)
|
||||
LoginLog.objects.create(username=username, name=name, login_type=login_type, login_ip=login_ip,
|
||||
terminal=terminal, login_city=login_city, user_agent=user_agent)
|
||||
LoginLog.objects.create(username=username, name=name, login_type=login_type,
|
||||
login_ip=login_ip, login_city=login_city, user_agent=user_agent)
|
||||
|
||||
|
||||
def get_ip_city(ip, timeout=10):
|
||||
# Taobao ip api: http://ip.taobao.com//service/getIpInfo.php?ip=8.8.8.8
|
||||
# Sina ip api: http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=8.8.8.8&format=js
|
||||
# Sina ip api: http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=8.8.8.8&format=json
|
||||
|
||||
url = 'http://ip.taobao.com/service/getIpInfo.php?ip=' + ip
|
||||
r = requests.get(url, timeout=timeout)
|
||||
url = 'http://int.dpool.sina.com.cn/iplookup/iplookup.php?ip=%s&format=json' % ip
|
||||
try:
|
||||
r = requests.get(url, timeout=timeout)
|
||||
except requests.Timeout:
|
||||
r = None
|
||||
city = 'Unknown'
|
||||
if r.status_code == 200:
|
||||
if r and r.status_code == 200:
|
||||
try:
|
||||
data = r.json()
|
||||
if data['code'] == 0:
|
||||
|
|
|
@ -100,7 +100,7 @@ class UserToken(APIView):
|
|||
user, msg = check_user_valid(username=username, email=email,
|
||||
password=password, public_key=public_key)
|
||||
if user:
|
||||
token = generate_token(request)
|
||||
token = generate_token(request, user)
|
||||
return Response({'Token': token, 'key': 'Bearer'}, status=200)
|
||||
else:
|
||||
return Response({'error': msg}, status=406)
|
||||
|
@ -114,28 +114,22 @@ class UserProfile(APIView):
|
|||
|
||||
|
||||
class UserAuthApi(APIView):
|
||||
permission_classes = ()
|
||||
expiration = settings.CONFIG.TOKEN_EXPIRATION or 3600
|
||||
permission_classes = (AllowAny,)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
username = request.data.get('username', '')
|
||||
password = request.data.get('password', '')
|
||||
public_key = request.data.get('public_key', '')
|
||||
remote_addr = request.data.get('remote_addr', '')
|
||||
terminal = request.data.get('applications', '')
|
||||
login_type = request.data.get('login_type', 'T')
|
||||
user = check_user_valid(username=username, password=password, public_key=public_key)
|
||||
login_type = request.data.get('login_type', '')
|
||||
login_ip = request.META.get('REMOTE_ADDR', '')
|
||||
user_agent = request.data.get('HTTP_USER_AGENT', '')
|
||||
|
||||
user, msg = check_user_valid(username=username, password=password, public_key=public_key)
|
||||
|
||||
if user:
|
||||
token = cache.get('%s_%s' % (user.id, remote_addr))
|
||||
if not token:
|
||||
token = generate_token(request)
|
||||
|
||||
cache.set(token, user.id, self.expiration)
|
||||
cache.set('%s_%s' % (user.id, remote_addr), token, self.expiration)
|
||||
write_login_log_async.delay(user.username, name=user.name, terminal=terminal,
|
||||
login_ip=remote_addr, login_type=login_type)
|
||||
return Response({'token': token, 'id': user.id, 'username': user.username,
|
||||
'name': user.name, 'is_active': user.is_active})
|
||||
token = generate_token(request, user)
|
||||
write_login_log_async.delay(user.username, name=user.name, user_agent=user_agent,
|
||||
login_ip=login_ip, login_type=login_type)
|
||||
return Response({'token': token, 'user': user.to_json()})
|
||||
else:
|
||||
return Response({'msg': 'Invalid password or public key or user is not active or expired'}, status=401)
|
||||
return Response({'msg': msg}, status=401)
|
||||
|
|
|
@ -43,7 +43,6 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
|
|||
msg = _('Invalid signature header. Signature string should not contain spaces.')
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
|
||||
|
||||
try:
|
||||
sign = auth[1].decode().split(':')
|
||||
if len(sign) != 2:
|
||||
|
@ -58,7 +57,8 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
|
|||
|
||||
return self.authenticate_credentials(request, access_key_id, request_signature)
|
||||
|
||||
def authenticate_credentials(self, request, access_key_id, request_signature):
|
||||
@staticmethod
|
||||
def authenticate_credentials(request, access_key_id, request_signature):
|
||||
access_key = get_object_or_none(AccessKey, id=access_key_id)
|
||||
request_date = get_request_date_header(request)
|
||||
if access_key is None or not access_key.user:
|
||||
|
@ -109,7 +109,8 @@ class AccessTokenAuthentication(authentication.BaseAuthentication):
|
|||
raise exceptions.AuthenticationFailed(msg)
|
||||
return self.authenticate_credentials(token)
|
||||
|
||||
def authenticate_credentials(self, token):
|
||||
@staticmethod
|
||||
def authenticate_credentials(token):
|
||||
user_id = cache.get(token)
|
||||
user = get_object_or_none(User, id=user_id)
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ router.register(r'v1/user-groups', api.UserGroupViewSet, 'user-group')
|
|||
urlpatterns = [
|
||||
url(r'^v1/token/$', api.UserToken.as_view(), name='user-token'),
|
||||
url(r'^v1/profile/$', api.UserProfile.as_view(), name='user-profile'),
|
||||
url(r'^v1/auth/$', api.UserAuthApi.as_view(), name='user-auth'),
|
||||
url(r'^v1/users/(?P<pk>\d+)/password/reset/$', api.UserResetPasswordApi.as_view(), name='user-reset-password'),
|
||||
url(r'^v1/users/(?P<pk>\d+)/public-key/reset/$', api.UserResetPKApi.as_view(), name='user-public-key-reset'),
|
||||
url(r'^v1/users/(?P<pk>\d+)/public-key/update/$', api.UserUpdatePKApi.as_view(), name='user-public-key-update'),
|
||||
|
|
|
@ -180,8 +180,8 @@ def send_reset_ssh_key_mail(user):
|
|||
def check_user_valid(**kwargs):
|
||||
password = kwargs.pop('password', None)
|
||||
public_key = kwargs.pop('public_key', None)
|
||||
email = kwargs.pop('email')
|
||||
username = kwargs.pop('username')
|
||||
email = kwargs.pop('email', None)
|
||||
username = kwargs.pop('username', None)
|
||||
|
||||
if username:
|
||||
user = get_object_or_none(User, username=username)
|
||||
|
@ -206,24 +206,23 @@ def check_user_valid(**kwargs):
|
|||
elif len(public_key_saved) > 1:
|
||||
if public_key == public_key_saved[1]:
|
||||
return user, ''
|
||||
return None, _('Passowrd or SSH public key invalid')
|
||||
return None, _('Password or SSH public key invalid')
|
||||
|
||||
|
||||
def refresh_token(token, user):
|
||||
expiration = settings.CONFIG.TOKEN_EXPIRATION or 3600
|
||||
def refresh_token(token, user, expiration=3600):
|
||||
cache.set(token, user.id, expiration)
|
||||
|
||||
|
||||
def generate_token(request):
|
||||
def generate_token(request, user):
|
||||
expiration = settings.CONFIG.TOKEN_EXPIRATION or 3600
|
||||
remote_addr = request.META.get('REMOTE_ADDR', '')
|
||||
remote_addr = base64.b16encode(remote_addr).replace('=', '')
|
||||
token = cache.get('%s_%s' % (request.user.id, remote_addr))
|
||||
token = cache.get('%s_%s' % (user.id, remote_addr))
|
||||
if not token:
|
||||
token = uuid.uuid4().get_hex()
|
||||
print('Set cache: %s' % token)
|
||||
cache.set(token, request.user.id, expiration)
|
||||
cache.set('%s_%s' % (request.user.id, remote_addr), token, expiration)
|
||||
cache.set(token, user.id, expiration)
|
||||
cache.set('%s_%s' % (user.id, remote_addr), token, expiration)
|
||||
return token
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue