Skip to content

Commit

Permalink
feat(阿里云角色sso对接): 支持用户角色SSO信息管理
Browse files Browse the repository at this point in the history
  • Loading branch information
Oo-RR-oO committed Jul 24, 2020
1 parent bdc71de commit 52ba15c
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 22 deletions.
77 changes: 74 additions & 3 deletions djangosaml2idp/idpview.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from OpenSSL import crypto

from django.conf import settings
from django.db import transaction
from django.shortcuts import reverse
from django.contrib.auth import logout, REDIRECT_FIELD_NAME
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
Expand All @@ -19,6 +20,10 @@
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from rest_framework import generics, status
from rest_framework.exceptions import NotFound
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from six import text_type
from saml2.saml import NAMEID_FORMAT_EMAILADDRESS, NAMEID_FORMAT_UNSPECIFIED
Expand All @@ -31,10 +36,13 @@
from saml2.s_utils import UnknownPrincipal, UnsupportedBinding
from saml2.server import Server
from saml2.md import entity_descriptor_from_string
from drf_expiring_authtoken.models import ExpiringToken

from djangosaml2idp.serializers.aliyun import AliyunSSORoleSerializer
from djangosaml2idp.processors import BaseProcessor
from djangosaml2idp import idpsettings
from oneid_meta.models import SAMLAPP
from oneid.permissions import IsAdminUser, IsUserManager
from oneid_meta.models import SAMLAPP, AliyunSSORole
from drf_expiring_authtoken.models import ExpiringToken

logger = logging.getLogger(__name__) # pylint: disable=invalid-name

Expand Down Expand Up @@ -509,7 +517,7 @@ def handle_no_permission(self, request_data=None):


@method_decorator(never_cache, name='dispatch')
class AliyunRoleSSOView(AliyunRoleSSOAccessMixin, IdPHandlerViewMixin, View):
class AliyunSSORoleView(AliyunRoleSSOAccessMixin, IdPHandlerViewMixin, View):
"""
阿里云角色SSO登录
"""
Expand Down Expand Up @@ -590,3 +598,66 @@ def get(self, request, *args, **kwargs): # pylint: disable=missing-function-d
destination=resp_args['destination'],
response=True)
return HttpResponse(http_args['data'])


class AliyunSSORoleListCreateAPIView(generics.ListCreateAPIView):
"""用户关联阿里云SSO关联信息"""
serializer_class = AliyunSSORoleSerializer
permission_classes = [IsAuthenticated & (IsAdminUser | IsUserManager)]

def get_queryset(self):
"""return queryset for list [GET]"""
queryset = AliyunSSORole.valid_objects.all()
return queryset

@transaction.atomic()
def create(self, request, *args, **kwargs): # pylint: disable=unused-argument
"""
create aliyun sso role [POST]
"""
data = request.data
user_ids = data.get('user_ids', [])
role_info = {
'role': data.get('role', []),
'session_duration': data.get('session_duration', 900),
'user_id': None
}
for user_id in user_ids:
role_info.update(user_id=user_id)
serializer = AliyunSSORoleSerializer(data=role_info)
serializer.is_valid(raise_exception=True)
serializer.save()
queryset = AliyunSSORole.valid_objects.filter(user_id__in=user_ids)
serializer = AliyunSSORoleSerializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_201_CREATED)


class AliyunSSORoleDetailCreateAPIView(generics.RetrieveUpdateAPIView):
"""特定阿里云角色SSO信息 [GET],[PATCH]"""
serializer_class = AliyunSSORoleSerializer
permission_classes = [IsAuthenticated & (IsAdminUser | IsUserManager)]

def get_object(self):
"""
find aliyun role sso
:rtype: oneid_meta.models.AliyunSSORole
"""
role = AliyunSSORole.valid_objects.filter(user__username=self.kwargs['username']).first()
if not role:
raise NotFound
# try:
# self.check_object_permissions(self.request, user)
# except PermissionDenied:
# raise NotFound
return role

def update(self, request, *args, **kwargs): # pylint: disable=unused-argument
"""
update aliyun role sso detail [PATCH]
"""
role = self.get_object()
data = request.data
serializer = AliyunSSORoleSerializer(role, data=data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
52 changes: 52 additions & 0 deletions djangosaml2idp/serializers/aliyun.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""
serializer for aliyun Role SSO
"""
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from common.django.drf.serializer import DynamicFieldsModelSerializer
from oneid_meta.models import AliyunSSORole, User


class AliyunSSORoleSerializer(DynamicFieldsModelSerializer):
"""Serializer for Aliyun SSO Role"""

user_id = serializers.IntegerField(source='user.id', required=True)
role = serializers.ListField(required=False, child=serializers.CharField(allow_blank=False))
session_duration = serializers.IntegerField(required=False)
is_active = serializers.BooleanField(required=False)

class Meta: # pylint: disable=missing-docstring
model = AliyunSSORole

fields = ('user_id', 'role', 'session_duration', 'is_active')

def create(self, validated_data):
"""create sso role"""
user_id = validated_data.pop('user')['id']
user = User.valid_objects.filter(id=user_id).first()
role = AliyunSSORole.objects.create(user=user, **validated_data)
return role

def update(self, instance, validated_data): # pylint: disable=too-many-statements,too-many-branches
"""update sso role"""
role = instance

user_id = validated_data.pop('user')['id']
if user_id and user_id != role.user.id:
raise ValidationError({'user_id': ['this field is immutable']})

role.__dict__.update(validated_data)
role.save()
return role

def validate_user_id(self, value):
"""
校验user是否已经配置阿里云角色SSO信息
"""
if not User.valid_objects.filter(id=value).exists():
raise ValidationError(['user not existed'])
exclude = {'pk': self.instance.pk} if self.instance else {}
if self.Meta.model.valid_objects.filter(user__id=value).exclude(**exclude).exists():
raise ValidationError(['aliyun sso role existed'])
return value
8 changes: 6 additions & 2 deletions djangosaml2idp/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@
# path('login/process_multi_factor/', idpview.ProcessMultiFactorView.as_view(), name='saml_multi_factor'),
path('metadata/', idpview.metadata, name='saml2_idp_metadata'),
path('download/metadata/', idpview.download_metadata, name='saml2_idp_download_metadata'),
path('aliyun/sso-role/login/', idpview.AliyunRoleSSOView.as_view(), name='aliyun_role_sso_login'),
path('aliyun/sso-role/login/', idpview.AliyunSSORoleView.as_view(), name='aliyun_sso_role_login'),
path('aliyun/sso-role/', idpview.AliyunSSORoleListCreateAPIView.as_view(), name='aliyun_sso_role_list'),
path('aliyun/sso-role/<str:username>/',
idpview.AliyunSSORoleDetailCreateAPIView.as_view(),
name='aliyun_sso_role_detail'),
]

fe_urlpatterns = [
path('fe/login/', dev_views.LoginView.as_view(), name='fe_login'),
path('aliyun/sso-role/fe/login/', dev_views.AliyunRoleSSOLoginView.as_view(), name='aliyun_role_sso_fe_login'),
path('aliyun/sso-role/fe/login/', dev_views.AliyunRoleSSOLoginView.as_view(), name='aliyun_sso_role_fe_login'),
]

urlpatterns = base_urlpatterns + fe_urlpatterns
Loading

0 comments on commit 52ba15c

Please sign in to comment.