mirror of https://github.com/jumpserver/jumpserver
perf: 去掉 v2 的api
parent
d363118911
commit
dd5b2b9101
|
@ -4,8 +4,6 @@ from __future__ import unicode_literals
|
||||||
from django.urls import path, include, re_path
|
from django.urls import path, include, re_path
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
from django.conf.urls.i18n import i18n_patterns
|
|
||||||
from django.http import HttpResponse
|
|
||||||
from django.views.i18n import JavaScriptCatalog
|
from django.views.i18n import JavaScriptCatalog
|
||||||
|
|
||||||
from . import views, api
|
from . import views, api
|
||||||
|
@ -27,11 +25,6 @@ api_v1 = [
|
||||||
path('prometheus/metrics/', api.PrometheusMetricsApi.as_view())
|
path('prometheus/metrics/', api.PrometheusMetricsApi.as_view())
|
||||||
]
|
]
|
||||||
|
|
||||||
api_v2 = [
|
|
||||||
path('terminal/', include('terminal.urls.api_urls_v2', namespace='api-terminal-v2')),
|
|
||||||
path('users/', include('users.urls.api_urls_v2', namespace='api-users-v2')),
|
|
||||||
]
|
|
||||||
|
|
||||||
app_view_patterns = [
|
app_view_patterns = [
|
||||||
path('auth/', include('authentication.urls.view_urls'), name='auth'),
|
path('auth/', include('authentication.urls.view_urls'), name='auth'),
|
||||||
path('ops/', include('ops.urls.view_urls'), name='ops'),
|
path('ops/', include('ops.urls.view_urls'), name='ops'),
|
||||||
|
@ -53,7 +46,6 @@ apps = [
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.IndexView.as_view(), name='index'),
|
path('', views.IndexView.as_view(), name='index'),
|
||||||
path('api/v1/', include(api_v1)),
|
path('api/v1/', include(api_v1)),
|
||||||
path('api/v2/', include(api_v2)),
|
|
||||||
re_path('api/(?P<app>\w+)/(?P<version>v\d)/.*', views.redirect_format_api),
|
re_path('api/(?P<app>\w+)/(?P<version>v\d)/.*', views.redirect_format_api),
|
||||||
path('api/health/', views.HealthCheckView.as_view(), name="health"),
|
path('api/health/', views.HealthCheckView.as_view(), name="health"),
|
||||||
# External apps url
|
# External apps url
|
||||||
|
@ -77,11 +69,6 @@ urlpatterns += [
|
||||||
views.get_swagger_view().without_ui(cache_timeout=1), name='schema-json'),
|
views.get_swagger_view().without_ui(cache_timeout=1), name='schema-json'),
|
||||||
re_path('api/docs/?', views.get_swagger_view().with_ui('swagger', cache_timeout=1), name="docs"),
|
re_path('api/docs/?', views.get_swagger_view().with_ui('swagger', cache_timeout=1), name="docs"),
|
||||||
re_path('api/redoc/?', views.get_swagger_view().with_ui('redoc', cache_timeout=1), name='redoc'),
|
re_path('api/redoc/?', views.get_swagger_view().with_ui('redoc', cache_timeout=1), name='redoc'),
|
||||||
|
|
||||||
re_path('^api/v2/swagger(?P<format>\.json|\.yaml)$',
|
|
||||||
views.get_swagger_view().without_ui(cache_timeout=1), name='schema-json'),
|
|
||||||
path('api/docs/v2/', views.get_swagger_view("v2").with_ui('swagger', cache_timeout=1), name="docs"),
|
|
||||||
path('api/redoc/v2/', views.get_swagger_view("v2").with_ui('redoc', cache_timeout=1), name='redoc'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -60,20 +60,12 @@ api_info = openapi.Info(
|
||||||
|
|
||||||
|
|
||||||
def get_swagger_view(version='v1'):
|
def get_swagger_view(version='v1'):
|
||||||
from ..urls import api_v1, api_v2
|
from ..urls import api_v1
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
api_v1_patterns = [
|
api_v1_patterns = [
|
||||||
path('api/v1/', include(api_v1))
|
path('api/v1/', include(api_v1))
|
||||||
]
|
]
|
||||||
|
patterns = api_v1_patterns
|
||||||
api_v2_patterns = [
|
|
||||||
path('api/v2/', include(api_v2))
|
|
||||||
]
|
|
||||||
|
|
||||||
if version == "v2":
|
|
||||||
patterns = api_v2_patterns
|
|
||||||
else:
|
|
||||||
patterns = api_v1_patterns
|
|
||||||
schema_view = get_schema_view(
|
schema_view = get_schema_view(
|
||||||
api_info,
|
api_info,
|
||||||
patterns=patterns,
|
patterns=patterns,
|
||||||
|
|
|
@ -5,18 +5,22 @@ import uuid
|
||||||
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets, generics
|
||||||
from rest_framework.views import APIView, Response
|
from rest_framework.views import APIView, Response
|
||||||
|
from rest_framework import status
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
from common.drf.api import JMSBulkModelViewSet
|
from common.drf.api import JMSBulkModelViewSet
|
||||||
from common.utils import get_object_or_none
|
from common.utils import get_object_or_none
|
||||||
from common.permissions import IsAppUser, IsOrgAdminOrAppUser, IsSuperUser
|
from common.permissions import IsAppUser, IsOrgAdminOrAppUser, IsSuperUser, WithBootstrapToken
|
||||||
from ..models import Terminal, Status, Session
|
from ..models import Terminal, Status, Session
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
from .. import exceptions
|
from .. import exceptions
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'TerminalViewSet', 'StatusViewSet', 'TerminalConfig',
|
'TerminalViewSet', 'StatusViewSet', 'TerminalConfig',
|
||||||
|
'TerminalRegistrationApi',
|
||||||
]
|
]
|
||||||
logger = logging.getLogger(__file__)
|
logger = logging.getLogger(__file__)
|
||||||
|
|
||||||
|
@ -112,4 +116,16 @@ class TerminalConfig(APIView):
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
config = request.user.terminal.config
|
config = request.user.terminal.config
|
||||||
return Response(config, status=200)
|
return Response(config, status=200)
|
||||||
|
|
||||||
|
|
||||||
|
class TerminalRegistrationApi(generics.CreateAPIView):
|
||||||
|
serializer_class = serializers.TerminalRegistrationSerializer
|
||||||
|
permission_classes = [WithBootstrapToken]
|
||||||
|
http_method_names = ['post']
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
if not settings.SECURITY_SERVICE_ACCOUNT_REGISTRATION:
|
||||||
|
data = {"error": "service account registration disabled"}
|
||||||
|
return Response(data=data, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
return super().create(request, *args, **kwargs)
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
from .terminal import *
|
|
|
@ -1,44 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
from rest_framework import viewsets, generics
|
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework.response import Response
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
from common.permissions import IsSuperUser, WithBootstrapToken
|
|
||||||
|
|
||||||
|
|
||||||
from ..models import Terminal
|
|
||||||
from .. import serializers_v2 as serializers
|
|
||||||
|
|
||||||
__all__ = ['TerminalViewSet', 'TerminalRegistrationApi']
|
|
||||||
|
|
||||||
|
|
||||||
class TerminalViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Terminal.objects.filter(is_deleted=False)
|
|
||||||
serializer_class = serializers.TerminalSerializer
|
|
||||||
permission_classes = [IsSuperUser]
|
|
||||||
http_method_names = [
|
|
||||||
'get', 'put', 'patch', 'delete', 'head', 'options', 'trace'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class TerminalRegistrationApi(generics.CreateAPIView):
|
|
||||||
serializer_class = serializers.TerminalRegistrationSerializer
|
|
||||||
permission_classes = [WithBootstrapToken]
|
|
||||||
http_method_names = ['post']
|
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
|
||||||
data = {k: v for k, v in request.data.items()}
|
|
||||||
serializer = serializers.TerminalSerializer(
|
|
||||||
data=data, context={'request': request}
|
|
||||||
)
|
|
||||||
if not settings.SECURITY_SERVICE_ACCOUNT_REGISTRATION:
|
|
||||||
data = {"error": "service account registration disabled"}
|
|
||||||
return Response(data=data, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
terminal = serializer.save()
|
|
||||||
sa_serializer = serializer.sa_serializer_class(instance=terminal.user)
|
|
||||||
data['service_account'] = sa_serializer.data
|
|
||||||
return Response(data, status=status.HTTP_201_CREATED)
|
|
||||||
|
|
|
@ -3,6 +3,9 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.drf.serializers import BulkModelSerializer, AdaptedBulkListSerializer
|
from common.drf.serializers import BulkModelSerializer, AdaptedBulkListSerializer
|
||||||
from common.utils import is_uuid
|
from common.utils import is_uuid
|
||||||
|
from users.serializers import ServiceAccountSerializer
|
||||||
|
from common.utils import get_request_ip
|
||||||
|
|
||||||
from ..models import (
|
from ..models import (
|
||||||
Terminal, Status, Session, Task, CommandStorage, ReplayStorage
|
Terminal, Status, Session, Task, CommandStorage, ReplayStorage
|
||||||
)
|
)
|
||||||
|
@ -63,9 +66,42 @@ class StatusSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class TaskSerializer(BulkModelSerializer):
|
class TaskSerializer(BulkModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
model = Task
|
model = Task
|
||||||
list_serializer_class = AdaptedBulkListSerializer
|
list_serializer_class = AdaptedBulkListSerializer
|
||||||
ref_name = 'TerminalTaskSerializer'
|
ref_name = 'TerminalTaskSerializer'
|
||||||
|
|
||||||
|
|
||||||
|
class TerminalRegistrationSerializer(serializers.ModelSerializer):
|
||||||
|
service_account = ServiceAccountSerializer(read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Terminal
|
||||||
|
fields = ['name', 'type', 'comment', 'service_account', 'remote_addr']
|
||||||
|
extra_fields = {
|
||||||
|
'remote_addr': {'readonly': True}
|
||||||
|
}
|
||||||
|
|
||||||
|
def is_valid(self, raise_exception=False):
|
||||||
|
valid = super().is_valid(raise_exception=raise_exception)
|
||||||
|
if not valid:
|
||||||
|
return valid
|
||||||
|
data = {'name': self.validated_data.get('name')}
|
||||||
|
kwargs = {'data': data}
|
||||||
|
if self.instance and self.instance.user:
|
||||||
|
kwargs['instance'] = self.instance.user
|
||||||
|
self.service_account = ServiceAccountSerializer(**kwargs)
|
||||||
|
valid = self.service_account.is_valid(raise_exception=True)
|
||||||
|
return valid
|
||||||
|
|
||||||
|
def save(self, **kwargs):
|
||||||
|
instance = super().save(**kwargs)
|
||||||
|
request = self.context.get('request')
|
||||||
|
instance.is_accepted = True
|
||||||
|
if request:
|
||||||
|
instance.remote_addr = get_request_ip(request)
|
||||||
|
sa = self.service_account.save()
|
||||||
|
instance.user = sa
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
|
|
||||||
from .terminal import *
|
|
|
@ -1,60 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
from django.conf import settings
|
|
||||||
from rest_framework import serializers
|
|
||||||
|
|
||||||
from common.utils import get_request_ip
|
|
||||||
from users.serializers_v2 import ServiceAccountSerializer
|
|
||||||
from ..models import Terminal
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['TerminalSerializer', 'TerminalRegistrationSerializer']
|
|
||||||
|
|
||||||
|
|
||||||
class TerminalSerializer(serializers.ModelSerializer):
|
|
||||||
sa_serializer_class = ServiceAccountSerializer
|
|
||||||
sa_serializer = None
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Terminal
|
|
||||||
fields = [
|
|
||||||
'id', 'name', 'type', 'remote_addr', 'command_storage',
|
|
||||||
'replay_storage', 'user', 'is_accepted', 'is_deleted',
|
|
||||||
'date_created', 'comment'
|
|
||||||
]
|
|
||||||
read_only_fields = ['remote_addr', 'user', 'date_created']
|
|
||||||
|
|
||||||
def is_valid(self, raise_exception=False):
|
|
||||||
valid = super().is_valid(raise_exception=raise_exception)
|
|
||||||
if not valid:
|
|
||||||
return valid
|
|
||||||
data = {'name': self.validated_data.get('name')}
|
|
||||||
kwargs = {'data': data}
|
|
||||||
if self.instance and self.instance.user:
|
|
||||||
kwargs['instance'] = self.instance.user
|
|
||||||
self.sa_serializer = ServiceAccountSerializer(**kwargs)
|
|
||||||
valid = self.sa_serializer.is_valid(raise_exception=True)
|
|
||||||
return valid
|
|
||||||
|
|
||||||
def save(self, **kwargs):
|
|
||||||
instance = super().save(**kwargs)
|
|
||||||
sa = self.sa_serializer.save()
|
|
||||||
instance.user = sa
|
|
||||||
instance.save()
|
|
||||||
return instance
|
|
||||||
|
|
||||||
def create(self, validated_data):
|
|
||||||
request = self.context.get('request')
|
|
||||||
instance = super().create(validated_data)
|
|
||||||
instance.is_accepted = True
|
|
||||||
if request:
|
|
||||||
instance.remote_addr = get_request_ip(request)
|
|
||||||
instance.save()
|
|
||||||
return instance
|
|
||||||
|
|
||||||
|
|
||||||
class TerminalRegistrationSerializer(serializers.Serializer):
|
|
||||||
name = serializers.CharField(max_length=128)
|
|
||||||
comment = serializers.CharField(max_length=128)
|
|
||||||
type = serializers.CharField(max_length=64)
|
|
||||||
service_account = ServiceAccountSerializer(read_only=True)
|
|
|
@ -22,6 +22,7 @@ router.register(r'replay-storages', api.ReplayStorageViewSet, 'replay-storage')
|
||||||
router.register(r'command-storages', api.CommandStorageViewSet, 'command-storage')
|
router.register(r'command-storages', api.CommandStorageViewSet, 'command-storage')
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
path('terminal-registrations/', api.TerminalRegistrationApi.as_view(), name='terminal-registration'),
|
||||||
path('sessions/join/validate/', api.SessionJoinValidateAPI.as_view(), name='join-session-validate'),
|
path('sessions/join/validate/', api.SessionJoinValidateAPI.as_view(), name='join-session-validate'),
|
||||||
path('sessions/<uuid:pk>/replay/',
|
path('sessions/<uuid:pk>/replay/',
|
||||||
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
|
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
|
|
||||||
from django.urls import path, re_path
|
|
||||||
from rest_framework_bulk.routes import BulkRouter
|
|
||||||
|
|
||||||
from common import api as capi
|
|
||||||
from .. import api_v2 as api
|
|
||||||
|
|
||||||
app_name = 'terminal'
|
|
||||||
|
|
||||||
router = BulkRouter()
|
|
||||||
router.register(r'terminals', api.TerminalViewSet, 'terminal')
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path('terminal-registrations/', api.TerminalRegistrationApi.as_view(),
|
|
||||||
name='terminal-registration')
|
|
||||||
]
|
|
||||||
|
|
||||||
old_version_urlpatterns = [
|
|
||||||
re_path('(?P<resource>terminal)/.*', capi.redirect_plural_name_api)
|
|
||||||
]
|
|
||||||
|
|
||||||
urlpatterns += router.urls
|
|
|
@ -4,4 +4,5 @@
|
||||||
from .user import *
|
from .user import *
|
||||||
from .group import *
|
from .group import *
|
||||||
from .profile import *
|
from .profile import *
|
||||||
|
from .service_account import *
|
||||||
from .relation import *
|
from .relation import *
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
|
||||||
from common.permissions import WithBootstrapToken
|
from common.permissions import WithBootstrapToken
|
||||||
from .. import serializers_v2 as serializers
|
from .. import serializers
|
||||||
|
|
||||||
|
|
||||||
class ServiceAccountRegistrationViewSet(viewsets.ModelViewSet):
|
class ServiceAccountRegistrationViewSet(viewsets.ModelViewSet):
|
|
@ -1,4 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
|
|
||||||
from .user import *
|
|
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
from .user import *
|
from .user import *
|
||||||
|
from .profile import *
|
||||||
from .group import *
|
from .group import *
|
||||||
from .realtion import *
|
from .realtion import *
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from common.utils import validate_ssh_public_key
|
||||||
|
from ..models import User
|
||||||
|
|
||||||
|
from .user import UserSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class UserOrgSerializer(serializers.Serializer):
|
||||||
|
id = serializers.CharField()
|
||||||
|
name = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
|
class UserOrgLabelSerializer(serializers.Serializer):
|
||||||
|
value = serializers.CharField(source='id')
|
||||||
|
label = serializers.CharField(source='name')
|
||||||
|
|
||||||
|
|
||||||
|
class UserUpdatePasswordSerializer(serializers.ModelSerializer):
|
||||||
|
old_password = serializers.CharField(required=True, max_length=128, write_only=True)
|
||||||
|
new_password = serializers.CharField(required=True, max_length=128, write_only=True)
|
||||||
|
new_password_again = serializers.CharField(required=True, max_length=128, write_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['old_password', 'new_password', 'new_password_again']
|
||||||
|
|
||||||
|
def validate_old_password(self, value):
|
||||||
|
if not self.instance.check_password(value):
|
||||||
|
msg = _('The old password is incorrect')
|
||||||
|
raise serializers.ValidationError(msg)
|
||||||
|
return value
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validate_new_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
|
||||||
|
|
||||||
|
def validate_new_password_again(self, value):
|
||||||
|
if value != self.initial_data.get('new_password', ''):
|
||||||
|
msg = _('The newly set password is inconsistent')
|
||||||
|
raise serializers.ValidationError(msg)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
new_password = self.validated_data.get('new_password')
|
||||||
|
instance.reset_password(new_password)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class UserUpdatePublicKeySerializer(serializers.ModelSerializer):
|
||||||
|
public_key_comment = serializers.CharField(
|
||||||
|
source='get_public_key_comment', required=False, read_only=True, max_length=128
|
||||||
|
)
|
||||||
|
public_key_hash_md5 = serializers.CharField(
|
||||||
|
source='get_public_key_hash_md5', required=False, read_only=True, max_length=128
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['public_key_comment', 'public_key_hash_md5', 'public_key']
|
||||||
|
extra_kwargs = {
|
||||||
|
'public_key': {'required': True, 'write_only': True, 'max_length': 2048}
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validate_public_key(value):
|
||||||
|
if not validate_ssh_public_key(value):
|
||||||
|
raise serializers.ValidationError(_('Not a valid ssh public key'))
|
||||||
|
return value
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
new_public_key = self.validated_data.get('public_key')
|
||||||
|
instance.set_public_key(new_public_key)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class UserRoleSerializer(serializers.Serializer):
|
||||||
|
name = serializers.CharField(max_length=24)
|
||||||
|
display = serializers.CharField(max_length=64)
|
||||||
|
|
||||||
|
|
||||||
|
class UserProfileSerializer(UserSerializer):
|
||||||
|
admin_or_audit_orgs = UserOrgSerializer(many=True, read_only=True)
|
||||||
|
user_all_orgs = UserOrgLabelSerializer(many=True, read_only=True)
|
||||||
|
current_org_roles = serializers.ListField(read_only=True)
|
||||||
|
public_key_comment = serializers.CharField(
|
||||||
|
source='get_public_key_comment', required=False, read_only=True, max_length=128
|
||||||
|
)
|
||||||
|
public_key_hash_md5 = serializers.CharField(
|
||||||
|
source='get_public_key_hash_md5', required=False, read_only=True, max_length=128
|
||||||
|
)
|
||||||
|
MFA_LEVEL_CHOICES = (
|
||||||
|
(0, _('Disable')),
|
||||||
|
(1, _('Enable')),
|
||||||
|
)
|
||||||
|
mfa_level = serializers.ChoiceField(choices=MFA_LEVEL_CHOICES, label=_('MFA'), required=False)
|
||||||
|
guide_url = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta(UserSerializer.Meta):
|
||||||
|
fields = UserSerializer.Meta.fields + [
|
||||||
|
'public_key_comment', 'public_key_hash_md5', 'admin_or_audit_orgs', 'current_org_roles',
|
||||||
|
'guide_url', 'user_all_orgs'
|
||||||
|
]
|
||||||
|
read_only_fields = [
|
||||||
|
'date_joined', 'last_login', 'created_by', 'source'
|
||||||
|
]
|
||||||
|
extra_kwargs = dict(UserSerializer.Meta.extra_kwargs)
|
||||||
|
extra_kwargs.update({
|
||||||
|
'name': {'read_only': True, 'max_length': 128},
|
||||||
|
'username': {'read_only': True, 'max_length': 128},
|
||||||
|
'email': {'read_only': True},
|
||||||
|
'is_first_login': {'label': _('Is first login'), 'read_only': False},
|
||||||
|
'source': {'read_only': True},
|
||||||
|
'is_valid': {'read_only': True},
|
||||||
|
'is_active': {'read_only': True},
|
||||||
|
'groups': {'read_only': True},
|
||||||
|
'roles': {'read_only': True},
|
||||||
|
'password_strategy': {'read_only': True},
|
||||||
|
'date_expired': {'read_only': True},
|
||||||
|
'date_joined': {'read_only': True},
|
||||||
|
'last_login': {'read_only': True},
|
||||||
|
'role': {'read_only': True},
|
||||||
|
})
|
||||||
|
|
||||||
|
if 'password' in fields:
|
||||||
|
fields.remove('password')
|
||||||
|
extra_kwargs.pop('password', None)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_guide_url(obj):
|
||||||
|
return settings.USER_GUIDE_URL
|
||||||
|
|
||||||
|
def validate_mfa_level(self, mfa_level):
|
||||||
|
if self.instance and self.instance.mfa_force_enabled:
|
||||||
|
return 2
|
||||||
|
return mfa_level
|
||||||
|
|
||||||
|
def validate_public_key(self, public_key):
|
||||||
|
if self.instance and self.instance.can_update_ssh_key():
|
||||||
|
if not validate_ssh_public_key(public_key):
|
||||||
|
raise serializers.ValidationError(_('Not a valid ssh public key'))
|
||||||
|
return public_key
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class UserPKUpdateSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['id', 'public_key']
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validate_public_key(value):
|
||||||
|
if not validate_ssh_public_key(value):
|
||||||
|
raise serializers.ValidationError(_('Not a valid ssh public key'))
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeUserPasswordSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['password']
|
||||||
|
|
||||||
|
class ResetOTPSerializer(serializers.Serializer):
|
||||||
|
msg = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
pass
|
|
@ -1,11 +1,9 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.conf import settings
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from common.utils import validate_ssh_public_key
|
|
||||||
from common.mixins import CommonBulkSerializerMixin
|
from common.mixins import CommonBulkSerializerMixin
|
||||||
from common.permissions import CanUpdateDeleteUser
|
from common.permissions import CanUpdateDeleteUser
|
||||||
from orgs.models import ROLE as ORG_ROLE
|
from orgs.models import ROLE as ORG_ROLE
|
||||||
|
@ -13,24 +11,11 @@ from ..models import User
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'UserSerializer', 'UserPKUpdateSerializer',
|
'UserSerializer', 'UserRetrieveSerializer', 'MiniUserSerializer',
|
||||||
'ChangeUserPasswordSerializer', 'ResetOTPSerializer',
|
'InviteSerializer', 'ServiceAccountSerializer',
|
||||||
'UserProfileSerializer', 'UserOrgSerializer',
|
|
||||||
'UserUpdatePasswordSerializer', 'UserUpdatePublicKeySerializer',
|
|
||||||
'UserRetrieveSerializer', 'MiniUserSerializer', 'InviteSerializer'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class UserOrgSerializer(serializers.Serializer):
|
|
||||||
id = serializers.CharField()
|
|
||||||
name = serializers.CharField()
|
|
||||||
|
|
||||||
|
|
||||||
class UserOrgLabelSerializer(serializers.Serializer):
|
|
||||||
value = serializers.CharField(source='id')
|
|
||||||
label = serializers.CharField(source='name')
|
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer):
|
class UserSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer):
|
||||||
EMAIL_SET_PASSWORD = _('Reset link will be generated and sent to the user')
|
EMAIL_SET_PASSWORD = _('Reset link will be generated and sent to the user')
|
||||||
CUSTOM_PASSWORD = _('Set password')
|
CUSTOM_PASSWORD = _('Set password')
|
||||||
|
@ -181,166 +166,6 @@ class UserRetrieveSerializer(UserSerializer):
|
||||||
fields = UserSerializer.Meta.fields + ['login_confirm_settings']
|
fields = UserSerializer.Meta.fields + ['login_confirm_settings']
|
||||||
|
|
||||||
|
|
||||||
class UserPKUpdateSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
||||||
fields = ['id', 'public_key']
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def validate_public_key(value):
|
|
||||||
if not validate_ssh_public_key(value):
|
|
||||||
raise serializers.ValidationError(_('Not a valid ssh public key'))
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
class ChangeUserPasswordSerializer(serializers.ModelSerializer):
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
||||||
fields = ['password']
|
|
||||||
|
|
||||||
|
|
||||||
class ResetOTPSerializer(serializers.Serializer):
|
|
||||||
msg = serializers.CharField(read_only=True)
|
|
||||||
|
|
||||||
def create(self, validated_data):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class UserRoleSerializer(serializers.Serializer):
|
|
||||||
name = serializers.CharField(max_length=24)
|
|
||||||
display = serializers.CharField(max_length=64)
|
|
||||||
|
|
||||||
|
|
||||||
class UserProfileSerializer(UserSerializer):
|
|
||||||
admin_or_audit_orgs = UserOrgSerializer(many=True, read_only=True)
|
|
||||||
user_all_orgs = UserOrgLabelSerializer(many=True, read_only=True)
|
|
||||||
current_org_roles = serializers.ListField(read_only=True)
|
|
||||||
public_key_comment = serializers.CharField(
|
|
||||||
source='get_public_key_comment', required=False, read_only=True, max_length=128
|
|
||||||
)
|
|
||||||
public_key_hash_md5 = serializers.CharField(
|
|
||||||
source='get_public_key_hash_md5', required=False, read_only=True, max_length=128
|
|
||||||
)
|
|
||||||
MFA_LEVEL_CHOICES = (
|
|
||||||
(0, _('Disable')),
|
|
||||||
(1, _('Enable')),
|
|
||||||
)
|
|
||||||
mfa_level = serializers.ChoiceField(choices=MFA_LEVEL_CHOICES, label=_('MFA'), required=False)
|
|
||||||
guide_url = serializers.SerializerMethodField()
|
|
||||||
|
|
||||||
class Meta(UserSerializer.Meta):
|
|
||||||
fields = UserSerializer.Meta.fields + [
|
|
||||||
'public_key_comment', 'public_key_hash_md5', 'admin_or_audit_orgs', 'current_org_roles',
|
|
||||||
'guide_url', 'user_all_orgs'
|
|
||||||
]
|
|
||||||
read_only_fields = [
|
|
||||||
'date_joined', 'last_login', 'created_by', 'source'
|
|
||||||
]
|
|
||||||
extra_kwargs = dict(UserSerializer.Meta.extra_kwargs)
|
|
||||||
extra_kwargs.update({
|
|
||||||
'name': {'read_only': True, 'max_length': 128},
|
|
||||||
'username': {'read_only': True, 'max_length': 128},
|
|
||||||
'email': {'read_only': True},
|
|
||||||
'is_first_login': {'label': _('Is first login'), 'read_only': False},
|
|
||||||
'source': {'read_only': True},
|
|
||||||
'is_valid': {'read_only': True},
|
|
||||||
'is_active': {'read_only': True},
|
|
||||||
'groups': {'read_only': True},
|
|
||||||
'roles': {'read_only': True},
|
|
||||||
'password_strategy': {'read_only': True},
|
|
||||||
'date_expired': {'read_only': True},
|
|
||||||
'date_joined': {'read_only': True},
|
|
||||||
'last_login': {'read_only': True},
|
|
||||||
'role': {'read_only': True},
|
|
||||||
})
|
|
||||||
|
|
||||||
if 'password' in fields:
|
|
||||||
fields.remove('password')
|
|
||||||
extra_kwargs.pop('password', None)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_guide_url(obj):
|
|
||||||
return settings.USER_GUIDE_URL
|
|
||||||
|
|
||||||
def validate_mfa_level(self, mfa_level):
|
|
||||||
if self.instance and self.instance.mfa_force_enabled:
|
|
||||||
return 2
|
|
||||||
return mfa_level
|
|
||||||
|
|
||||||
def validate_public_key(self, public_key):
|
|
||||||
if self.instance and self.instance.can_update_ssh_key():
|
|
||||||
if not validate_ssh_public_key(public_key):
|
|
||||||
raise serializers.ValidationError(_('Not a valid ssh public key'))
|
|
||||||
return public_key
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class UserUpdatePasswordSerializer(serializers.ModelSerializer):
|
|
||||||
old_password = serializers.CharField(required=True, max_length=128, write_only=True)
|
|
||||||
new_password = serializers.CharField(required=True, max_length=128, write_only=True)
|
|
||||||
new_password_again = serializers.CharField(required=True, max_length=128, write_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
||||||
fields = ['old_password', 'new_password', 'new_password_again']
|
|
||||||
|
|
||||||
def validate_old_password(self, value):
|
|
||||||
if not self.instance.check_password(value):
|
|
||||||
msg = _('The old password is incorrect')
|
|
||||||
raise serializers.ValidationError(msg)
|
|
||||||
return value
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def validate_new_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
|
|
||||||
|
|
||||||
def validate_new_password_again(self, value):
|
|
||||||
if value != self.initial_data.get('new_password', ''):
|
|
||||||
msg = _('The newly set password is inconsistent')
|
|
||||||
raise serializers.ValidationError(msg)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
|
||||||
new_password = self.validated_data.get('new_password')
|
|
||||||
instance.reset_password(new_password)
|
|
||||||
return instance
|
|
||||||
|
|
||||||
|
|
||||||
class UserUpdatePublicKeySerializer(serializers.ModelSerializer):
|
|
||||||
public_key_comment = serializers.CharField(
|
|
||||||
source='get_public_key_comment', required=False, read_only=True, max_length=128
|
|
||||||
)
|
|
||||||
public_key_hash_md5 = serializers.CharField(
|
|
||||||
source='get_public_key_hash_md5', required=False, read_only=True, max_length=128
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
||||||
fields = ['public_key_comment', 'public_key_hash_md5', 'public_key']
|
|
||||||
extra_kwargs = {
|
|
||||||
'public_key': {'required': True, 'write_only': True, 'max_length': 2048}
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def validate_public_key(value):
|
|
||||||
if not validate_ssh_public_key(value):
|
|
||||||
raise serializers.ValidationError(_('Not a valid ssh public key'))
|
|
||||||
return value
|
|
||||||
|
|
||||||
def update(self, instance, validated_data):
|
|
||||||
new_public_key = self.validated_data.get('public_key')
|
|
||||||
instance.set_public_key(new_public_key)
|
|
||||||
return instance
|
|
||||||
|
|
||||||
|
|
||||||
class MiniUserSerializer(serializers.ModelSerializer):
|
class MiniUserSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
@ -352,3 +177,46 @@ class InviteSerializer(serializers.Serializer):
|
||||||
queryset=User.objects.exclude(role=User.ROLE.APP)
|
queryset=User.objects.exclude(role=User.ROLE.APP)
|
||||||
)
|
)
|
||||||
role = serializers.ChoiceField(choices=ORG_ROLE.choices)
|
role = serializers.ChoiceField(choices=ORG_ROLE.choices)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceAccountSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['id', 'name', 'access_key']
|
||||||
|
read_only_fields = ['access_key']
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
from authentication.serializers import AccessKeySerializer
|
||||||
|
self.fields['access_key'] = AccessKeySerializer(read_only=True)
|
||||||
|
|
||||||
|
def get_username(self):
|
||||||
|
return self.initial_data.get('name')
|
||||||
|
|
||||||
|
def get_email(self):
|
||||||
|
name = self.initial_data.get('name')
|
||||||
|
return '{}@serviceaccount.local'.format(name)
|
||||||
|
|
||||||
|
def validate_name(self, name):
|
||||||
|
email = self.get_email()
|
||||||
|
username = self.get_username()
|
||||||
|
if self.instance:
|
||||||
|
users = User.objects.exclude(id=self.instance.id)
|
||||||
|
else:
|
||||||
|
users = User.objects.all()
|
||||||
|
if users.filter(email=email) or \
|
||||||
|
users.filter(username=username):
|
||||||
|
raise serializers.ValidationError(_('name not unique'), code='unique')
|
||||||
|
return name
|
||||||
|
|
||||||
|
def save(self, **kwargs):
|
||||||
|
self.validated_data['email'] = self.get_email()
|
||||||
|
self.validated_data['username'] = self.get_username()
|
||||||
|
self.validated_data['role'] = User.ROLE.APP
|
||||||
|
return super().save(**kwargs)
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
instance = super().create(validated_data)
|
||||||
|
instance.create_access_key()
|
||||||
|
return instance
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
from .user import *
|
|
|
@ -1,48 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
from rest_framework import serializers
|
|
||||||
from ..models import User
|
|
||||||
|
|
||||||
from authentication.serializers import AccessKeySerializer
|
|
||||||
|
|
||||||
__all__ = ['ServiceAccountSerializer']
|
|
||||||
|
|
||||||
|
|
||||||
class ServiceAccountSerializer(serializers.ModelSerializer):
|
|
||||||
access_key = AccessKeySerializer(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = User
|
|
||||||
fields = ['id', 'name', 'access_key']
|
|
||||||
read_only_fields = ['access_key']
|
|
||||||
|
|
||||||
def get_username(self):
|
|
||||||
return self.initial_data.get('name')
|
|
||||||
|
|
||||||
def get_email(self):
|
|
||||||
name = self.initial_data.get('name')
|
|
||||||
return '{}@serviceaccount.local'.format(name)
|
|
||||||
|
|
||||||
def validate_name(self, name):
|
|
||||||
email = self.get_email()
|
|
||||||
username = self.get_username()
|
|
||||||
if self.instance:
|
|
||||||
users = User.objects.exclude(id=self.instance.id)
|
|
||||||
else:
|
|
||||||
users = User.objects.all()
|
|
||||||
if users.filter(email=email) or \
|
|
||||||
users.filter(username=username):
|
|
||||||
raise serializers.ValidationError(_('name not unique'), code='unique')
|
|
||||||
return name
|
|
||||||
|
|
||||||
def save(self, **kwargs):
|
|
||||||
self.validated_data['email'] = self.get_email()
|
|
||||||
self.validated_data['username'] = self.get_username()
|
|
||||||
self.validated_data['role'] = User.ROLE.APP
|
|
||||||
return super().save(**kwargs)
|
|
||||||
|
|
||||||
def create(self, validated_data):
|
|
||||||
instance = super().create(validated_data)
|
|
||||||
instance.create_access_key()
|
|
||||||
return instance
|
|
|
@ -15,6 +15,7 @@ router = BulkRouter()
|
||||||
router.register(r'users', api.UserViewSet, 'user')
|
router.register(r'users', api.UserViewSet, 'user')
|
||||||
router.register(r'groups', api.UserGroupViewSet, 'user-group')
|
router.register(r'groups', api.UserGroupViewSet, 'user-group')
|
||||||
router.register(r'users-groups-relations', api.UserUserGroupRelationViewSet, 'users-groups-relation')
|
router.register(r'users-groups-relations', api.UserUserGroupRelationViewSet, 'users-groups-relation')
|
||||||
|
router.register(r'service-account-registrations', api.ServiceAccountRegistrationViewSet, 'service-account-registration')
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# ~*~ coding: utf-8 ~*~
|
|
||||||
#
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from django.urls import path, include
|
|
||||||
from rest_framework_bulk.routes import BulkRouter
|
|
||||||
from .. import api_v2 as api
|
|
||||||
|
|
||||||
app_name = 'users'
|
|
||||||
|
|
||||||
router = BulkRouter()
|
|
||||||
router.register(r'service-account-registrations',
|
|
||||||
api.ServiceAccountRegistrationViewSet,
|
|
||||||
'service-account-registration')
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
# path('token/', api.UserToken.as_view(), name='user-token'),
|
|
||||||
]
|
|
||||||
urlpatterns += router.urls
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue