perf: 用户确认和access key

Merge branch 'dev' of github.com:jumpserver/jumpserver into dev
pull/11841/head
老广 2023-10-13 16:37:45 +08:00 committed by GitHub
commit f80ff279d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 47 additions and 14 deletions

View File

@ -5,15 +5,18 @@ from django.utils.translation import gettext as _
from rest_framework import serializers
from rest_framework.response import Response
from authentication.permissions import UserConfirmation
from common.api import JMSModelViewSet
from rbac.permissions import RBACPermission
from ..const import ConfirmType
from ..serializers import AccessKeySerializer
from ..permissions import UserConfirmation
from ..serializers import AccessKeySerializer, AccessKeyCreateSerializer
class AccessKeyViewSet(JMSModelViewSet):
serializer_class = AccessKeySerializer
serializer_classes = {
'default': AccessKeySerializer,
'create': AccessKeyCreateSerializer
}
search_fields = ['^id']
permission_classes = [RBACPermission]
@ -41,4 +44,5 @@ class AccessKeyViewSet(JMSModelViewSet):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
key = self.perform_create(serializer)
return Response({'secret': key.secret, 'id': key.id}, status=201)
serializer = self.get_serializer(instance=key)
return Response(serializer.data, status=201)

View File

@ -4,10 +4,12 @@ import time
from django.utils.translation import gettext_lazy as _
from rest_framework import status
from rest_framework.generics import RetrieveAPIView, CreateAPIView
from rest_framework.decorators import action
from rest_framework.generics import RetrieveAPIView
from rest_framework.response import Response
from authentication.permissions import UserConfirmation
from common.api import JMSGenericViewSet
from common.permissions import IsValidUser
from ..const import ConfirmType
from ..serializers import ConfirmSerializer
@ -20,10 +22,17 @@ class ConfirmBindORUNBindOAuth(RetrieveAPIView):
return Response('ok')
class ConfirmApi(RetrieveAPIView, CreateAPIView):
class UserConfirmationViewSet(JMSGenericViewSet):
permission_classes = (IsValidUser,)
serializer_class = ConfirmSerializer
@action(methods=['get'], detail=False)
def check(self, request):
confirm_type = request.query_params.get('confirm_type', 'password')
permission = UserConfirmation.require(confirm_type)()
permission.has_permission(request, self)
return Response('ok')
def get_confirm_backend(self, confirm_type):
backend_classes = ConfirmType.get_prop_backends(confirm_type)
if not backend_classes:
@ -34,12 +43,12 @@ class ConfirmApi(RetrieveAPIView, CreateAPIView):
continue
return backend
def retrieve(self, request, *args, **kwargs):
def list(self, request, *args, **kwargs):
confirm_type = request.query_params.get('confirm_type', 'password')
backend = self.get_confirm_backend(confirm_type)
if backend is None:
msg = _('This action require verify your MFA')
return Response(data={'error': msg}, status=status.HTTP_404_NOT_FOUND)
return Response(data={'error': msg}, status=status.HTTP_400_BAD_REQUEST)
data = {
'confirm_type': backend.name,

View File

@ -4,19 +4,30 @@ from django.shortcuts import render
from django.utils.translation import gettext as _
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.viewsets import ModelViewSet
from authentication.mixins import AuthMixin
from common.api import JMSModelViewSet
from .fido import register_begin, register_complete, auth_begin, auth_complete
from .models import Passkey
from .serializer import PasskeySerializer
from ...const import ConfirmType
from ...permissions import UserConfirmation
from ...views import FlashMessageMixin
class PasskeyViewSet(AuthMixin, FlashMessageMixin, ModelViewSet):
class PasskeyViewSet(AuthMixin, FlashMessageMixin, JMSModelViewSet):
serializer_class = PasskeySerializer
permission_classes = (IsAuthenticated,)
def get_permissions(self):
if self.is_swagger_request():
return super().get_permissions()
if self.action == 'register':
self.permission_classes = [
IsAuthenticated, UserConfirmation.require(ConfirmType.PASSWORD)
]
return super().get_permissions()
def get_queryset(self):
return Passkey.objects.filter(user=self.request.user)

View File

@ -7,4 +7,4 @@ from ..const import ConfirmType, MFAType
class ConfirmSerializer(serializers.Serializer):
confirm_type = serializers.ChoiceField(required=True, allow_blank=True, choices=ConfirmType.choices)
mfa_type = serializers.ChoiceField(required=False, allow_blank=True, choices=MFAType.choices)
secret_key = EncryptedField(allow_blank=True)
secret_key = EncryptedField(allow_blank=True, required=False)

View File

@ -12,6 +12,7 @@ from ..models import AccessKey, TempToken
__all__ = [
'AccessKeySerializer', 'BearerTokenSerializer',
'SSOTokenSerializer', 'TempTokenSerializer',
'AccessKeyCreateSerializer'
]
@ -22,6 +23,11 @@ class AccessKeySerializer(serializers.ModelSerializer):
read_only_fields = ['id', 'date_created', 'date_last_used']
class AccessKeyCreateSerializer(AccessKeySerializer):
class Meta(AccessKeySerializer.Meta):
fields = AccessKeySerializer.Meta.fields + ['secret']
class BearerTokenSerializer(serializers.Serializer):
username = serializers.CharField(allow_null=True, required=False, write_only=True)
password = serializers.CharField(write_only=True, allow_null=True,

View File

@ -13,6 +13,7 @@ router.register('sso', api.SSOViewSet, 'sso')
router.register('temp-tokens', api.TempTokenViewSet, 'temp-token')
router.register('connection-token', api.ConnectionTokenViewSet, 'connection-token')
router.register('super-connection-token', api.SuperConnectionTokenViewSet, 'super-connection-token')
router.register('confirm', api.UserConfirmationViewSet, 'confirm')
urlpatterns = [
path('wecom/qr/unbind/', api.WeComQRUnBindForUserApi.as_view(), name='wecom-qr-unbind'),
@ -29,7 +30,6 @@ urlpatterns = [
name='feishu-event-subscription-callback'),
path('auth/', api.TokenCreateApi.as_view(), name='user-auth'),
path('confirm/', api.ConfirmApi.as_view(), name='user-confirm'),
path('confirm-oauth/', api.ConfirmBindORUNBindOAuth.as_view(), name='confirm-oauth'),
path('tokens/', api.TokenCreateApi.as_view(), name='auth-token'),
path('mfa/verify/', api.MFAChallengeVerifyApi.as_view(), name='mfa-verify'),

View File

@ -42,8 +42,11 @@ class ReferencedByOthers(JMSException):
class UserConfirmRequired(JMSException):
status_code = status.HTTP_412_PRECONDITION_FAILED
def __init__(self, code=None):
detail = {
'type': 'user_confirm_required',
'code': code,
'detail': _('This action require confirm current user')
}

View File

@ -293,8 +293,8 @@ class ServiceAccountSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
from authentication.serializers import AccessKeySerializer
self.fields["access_key"] = AccessKeySerializer(read_only=True)
from authentication.serializers import AccessKeyCreateSerializer
self.fields["access_key"] = AccessKeyCreateSerializer(read_only=True)
def get_username(self):
return self.initial_data.get("name")