text: '''
  serializers for APP
  '''
  import os
  import copy
  import uuid
  from six import text_type
  from saml2 import BINDING_HTTP_POST, BINDING_HTTP_REDIRECT
  from saml2.config import SPConfig
  from saml2.metadata import entity_descriptor
  from saml2.entity_category.edugain import COC
  from saml2.saml import NAME_FORMAT_URI
  from saml2.sigver import CertificateError
  try:
      from saml2.sigver import get_xmlsec_binary
  except ImportError:
      get_xmlsec_binary = None
  from rest_framework import serializers
  from rest_framework.exceptions import ValidationError, MethodNotAllowed
  from common.django.drf.serializer import DynamicFieldsModelSerializer
  from oneid_meta.models import (
      APP,
      OAuthAPP,
      OIDCAPP,
      SAMLAPP,
      LDAPAPP,
      HTTPAPP,
      Dept,
      User,
  )
  from siteapi.v1.views.utils import gen_uid
  from siteapi.v1.serializers.perm import PermWithOwnerSerializer
  
  if get_xmlsec_binary:
      xmlsec_path = get_xmlsec_binary(["/opt/local/bin", "/usr/local/bin"])    # pylint: disable=invalid-name
  else:
      xmlsec_path = '/usr/local/bin/xmlsec1'    # pylint: disable=invalid-name
  BASEDIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
  
  
  class OAuthAPPSerializer(DynamicFieldsModelSerializer):
      '''
      Serializer for OAuthAPP
      '''
      class Meta:    # pylint: disable=missing-docstring
          model = OAuthAPP
  
          fields = (
              'client_id',
              'client_secret',
              'redirect_uris',
              'client_type',
              'authorization_grant_type',
              'more_detail',
          )
  
          read_only_fields = (
              'client_id',
              'client_secret',
              'more_detail',
          )
  
  
  class OIDCAPPSerializer(DynamicFieldsModelSerializer):
      '''Serializer for OIDCAPP
      '''
      class Meta:    # pylint: disable=missing-docstring
          model = OIDCAPP
  
          fields = (
              'client_id',
              'client_secret',
              'redirect_uris',
              'client_type',
              'response_type',
              'more_detail',
          )
  
          read_only_fields = (
              'client_id',
              'client_secret',
              'more_detail',
          )
  
  
  class SAMLAPPSerializer(DynamicFieldsModelSerializer):
      '''Serializer for SAMLAPP
      '''
      class Meta:    # pylint: disable=missing-docstring
          model = SAMLAPP
  
          fields = (
              'entity_id',
              'acs',
              'sls',
              'cert',
              'xmldata',
              'more_detail',
          )
  
      def gen_xml(self, filename, entity_id, acs, sls):    # pylint: disable=no-self-use
          '''将SAMLAPP配置写入指定路径xml文件
          '''
          conf = SPConfig()
          endpointconfig = {
              "entityid": entity_id,
              'entity_category': [COC],
              "description": "extra SP setup",
              "service": {
                  "sp": {
                      "want_response_signed": False,
                      "authn_requests_signed": True,
                      "logout_requests_signed": True,
                      "endpoints": {
                          "assertion_consumer_service": [(acs, BINDING_HTTP_POST)],
                          "single_logout_service": [
                              (sls, BINDING_HTTP_REDIRECT),
                              (sls.replace('redirect', 'post'), BINDING_HTTP_POST),
                          ],
                      }
                  },
              },
              "key_file": BASEDIR + "/djangosaml2idp/certificates/mykey.pem",    # 随便放一个私钥，并不知道SP私钥
              "cert_file": BASEDIR + '/djangosaml2idp/saml2_config/sp_cert/%s.pem' % filename,
              "xmlsec_binary": xmlsec_path,
              "metadata": {
                  "local": [BASEDIR + '/djangosaml2idp/saml2_config/idp_metadata.xml']
              },
              "name_form": NAME_FORMAT_URI,
          }
          conf.load(copy.deepcopy(endpointconfig))
          meta_data = entity_descriptor(conf)
          content = text_type(meta_data).encode('utf-8')
          with open(BASEDIR + '/djangosaml2idp/saml2_config/%s.xml' % filename, 'wb+') as f:
              f.write(content)
  
      def create(self, validated_data):
          filename = uuid.uuid4()
          app = validated_data['app']
          xmldata = validated_data.get('xmldata', '')
          entity_id = validated_data.get('entity_id', '')
          cert = validated_data.get('cert', '')
          acs = validated_data.get('acs', '')
          sls = validated_data.get('sls', '')
  
          if xmldata not in ['', None]:
              with open(BASEDIR + '/djangosaml2idp/saml2_config/%s.xml' % filename, 'w+') as f:
                  f.write(xmldata)
          else:
              self.dump_cert(filename, cert)
              try:
                  self.gen_xml(filename=filename, entity_id=entity_id, acs=acs, sls=sls)
              except CertificateError:
                  raise ValidationError({'msg': 'perm incorrect'})
  
          if os.path.exists(BASEDIR + '/djangosaml2idp/saml2_config/sp_cert/%s.pem' % filename):
              os.remove(BASEDIR + '/djangosaml2idp/saml2_config/sp_cert/%s.pem' % filename)
  
          saml_app = SAMLAPP.objects.create(app=app, xmldata=xmldata, entity_id=entity_id,\
              acs=acs, sls=sls, cert=cert)
          saml_app.save()
          saml_app.refresh_from_db()
          return saml_app
  
      def update(self, instance, validated_data):
          saml_app = instance
          filename = uuid.uuid4()
          xmldata = validated_data.get('xmldata', '')
          cert = validated_data.get('cert', '')
          entity_id = validated_data.get('entity_id', '')
          acs = validated_data.get('ace', '')
          sls = validated_data.get('sls', '')
          kwargs = {}
  
          if entity_id != '':
              kwargs['entity_id'] = entity_id
          if acs != '':
              kwargs['acs'] = acs
          if sls != '':
              kwargs['sls'] = sls
          if cert != '':
              kwargs['cert'] = cert
          if xmldata != '':
              with open(BASEDIR + '/djangosaml2idp/saml2_config/%s.xml' % filename, 'w+') as f:
                  f.write(xmldata)
          else:
              self.dump_cert(filename, cert)
              self.gen_xml(filename=filename, entity_id=entity_id, acs=acs, sls=sls)
              with open(BASEDIR + '/djangosaml2idp/saml2_config/%s.xml' % filename, 'rb') as f:
                  xmldata = f.read()
          kwargs['xmldata'] = xmldata
          saml_app.__dict__.update(**kwargs)
          saml_app.save()
          saml_app.refresh_from_db()
          return saml_app
  
      def dump_cert(self, filename, cert):    # pylint: disable=no-self-use
          '''存储SP方公钥,以备生成元数据文件时用
          '''
          with open(BASEDIR + '/djangosaml2idp/saml2_config/sp_cert/%s.pem' % filename, 'w+') as f:
              f.write(cert)
  
  
  class LDAPAPPSerializer(DynamicFieldsModelSerializer):
      '''
      Serializer for LDAP APP
      '''
      class Meta:    # pylint: disable=missing-docstring
          model = LDAPAPP
  
          fields = ('more_detail', )
          read_only_fields = ('more_detail', )
  
  
  class HTTPAPPSerializer(DynamicFieldsModelSerializer):
      '''
      Serializer for HTTP APP
      '''
      class Meta:    # pylint: disable=missing-docstring
          model = HTTPAPP
  
          fields = ('more_detail', )
          read_only_fields = ('more_detail', )
  
  
  class APPPublicSerializer(DynamicFieldsModelSerializer):
      '''
      public serializer for APP
      '''
      app_id = serializers.IntegerField(source='id', read_only=True)
      uid = serializers.CharField(required=False, help_text='默认情况下根据`name`生成')
  
      class Meta:    # pylint: disable=missing-docstring
          model = APP
  
          fields = (
              "app_id",
              "uid",
              "name",
              "index",
              "logo",
              "remark",
              'auth_protocols',
          )
  
  
  class APPSerializer(DynamicFieldsModelSerializer):
      '''
      Serializer for APP
      '''
      app_id = serializers.IntegerField(source='id', read_only=True)
      oauth_app = OAuthAPPSerializer(many=False, required=False, allow_null=True)
      http_app = HTTPAPPSerializer(many=False, required=False, allow_null=True)
      ldap_app = LDAPAPPSerializer(many=False, required=False, allow_null=True)
      oidc_app = OIDCAPPSerializer(many=False, required=False)
      saml_app = SAMLAPPSerializer(many=False, required=False, allow_null=True)
  
      uid = serializers.CharField(required=False, help_text='默认情况下根据`name`生成')
  
      class Meta:    # pylint: disable=missing-docstring
          model = APP
  
          fields = (
              "app_id",
              "uid",
              "name",
              "index",
              "logo",
              "remark",
              "oauth_app",
              "ldap_app",
              "http_app",
              "oidc_app",
              "saml_app",
              "allow_any_user",
              'auth_protocols',
          )
  
      def validate_uid(self, value):
          '''
          校验uid唯一
          '''
          exclude = {'pk': self.instance.pk} if self.instance else {}
          if self.Meta.model.valid_objects.filter(uid=value).exclude(**exclude).exists():
              raise ValidationError(['this value has be used'])
          return value
  
      def create(self, validated_data):
          '''
          create app
          create oauth_app if provided
          create oidc_app if provided
          create http_app if provided
          create saml_app if provided
          '''
          if 'uid' not in validated_data:
              validated_data['uid'] = gen_uid(validated_data['name'], cls=self.Meta.model)
  
          oauth_app_data = validated_data.pop('oauth_app', None)
          oidc_app_data = validated_data.pop('oidc_app', None)
          saml_app_data = validated_data.pop('saml_app', None)
          ldap_app_data = validated_data.pop('ldap_app', None)
          http_app_data = validated_data.pop('http_app', None)
  
          app = APP.objects.create(**validated_data)
  
          if oauth_app_data is not None:
              oauth_app_serializer = OAuthAPPSerializer(data=oauth_app_data)
              oauth_app_serializer.is_valid(raise_exception=True)
              oauth_app_serializer.save(app=app, name=app.name)
  
          if ldap_app_data is not None:
              serializer = LDAPAPPSerializer(data=ldap_app_data)
              serializer.is_valid(raise_exception=True)
              serializer.save(app=app)
  
          if http_app_data is not None:
              serializer = HTTPAPPSerializer(data=http_app_data)
              serializer.is_valid(raise_exception=True)
              serializer.save(app=app)
  
          if oidc_app_data is not None:
              oidc_app_serializer = OIDCAPPSerializer(data=oidc_app_data)
              oidc_app_serializer.is_valid(raise_exception=True)
              oidc_app_serializer.save(app=app, name=app.name)
  
          if saml_app_data is not None:
              saml_app_data['app'] = app
              serializer = SAMLAPPSerializer(data=saml_app_data)
              serializer.is_valid(raise_exception=True)
              serializer.save(app=app)
          return app
  
      # TODO: support update name of app
      def update(self, instance, validated_data):    # pylint: disable=too-many-branches, too-many-statements
          '''
          update app
          update/create oauth_app if provided
          update/create oidc_app if provided
          update/create saml_app if provided
          '''
          app = instance
          if not app.editable:
              raise MethodNotAllowed('MODIFY protected APP')
          oidc_app_data = validated_data.pop('oidc_app', None)
          uid = validated_data.pop('uid', '')
          if uid and uid != app.uid:
              raise ValidationError({'uid': ['this field is immutable']})
  
          if 'oauth_app' in validated_data:
              oauth_app_data = validated_data.pop('oauth_app')
              if oauth_app_data is None:
                  if hasattr(app, 'oauth_app'):
                      instance = app.oauth_app
                      instance.delete()
              else:
                  if hasattr(app, 'oauth_app'):
                      serializer = OAuthAPPSerializer(app.oauth_app, data=oauth_app_data, partial=True)
                      serializer.is_valid(raise_exception=True)
                      serializer.save()
                  else:
                      serializer = OAuthAPPSerializer(data=oauth_app_data)
                      serializer.is_valid(raise_exception=True)
                      serializer.save(app=app)
  
          if 'ldap_app' in validated_data:
              data = validated_data.pop('ldap_app')
              if data is None:
                  if hasattr(app, 'ldap_app'):
                      instance = app.ldap_app
                      instance.delete()
              else:
                  if hasattr(app, 'ldap_app'):
                      serializer = LDAPAPPSerializer(app.ldap_app, data=data, partial=True)
                      serializer.is_valid(raise_exception=True)
                      serializer.save()
                  else:
                      serializer = LDAPAPPSerializer(data=data)
                      serializer.is_valid(raise_exception=True)
                      serializer.save(app=app)
  
          if 'http_app' in validated_data:
              data = validated_data.pop('http_app')
              if data is None:
                  if hasattr(app, 'http_app'):
                      instance = app.http_app
                      instance.delete()
              else:
                  if hasattr(app, 'http_app'):
                      serializer = HTTPAPPSerializer(app.http_app, data=data, partial=True)
                      serializer.is_valid(raise_exception=True)
                      serializer.save()
                  else:
                      serializer = HTTPAPPSerializer(data=data)
                      serializer.is_valid(raise_exception=True)
                      serializer.save(app=app)
  
          if oidc_app_data:
              pass
  
          if 'saml_app' in validated_data:
              data = validated_data.pop('saml_app')
              if data is None:
                  if hasattr(app, 'saml_app'):
                      instance = app.saml_app
                      instance.delete()
              else:
                  if hasattr(app, 'saml_app'):
                      serializer = SAMLAPPSerializer(app.saml_app, data=data, partial=True)
                      serializer.is_valid(raise_exception=True)
                      serializer.save()
                  else:
                      serializer = SAMLAPPSerializer(data=data)
                      serializer.is_valid(raise_exception=True)
                      serializer.save(app=app)
          app.__dict__.update(validated_data)
          app.save()
          app.refresh_from_db()
          return app
  
  
  class APPWithAccessSerializer(APPSerializer):
      '''
      Serializer for APP with access perm
      '''
      access_perm = PermWithOwnerSerializer(required=False, read_only=True)
  
      class Meta:    # pylint: disable=missing-docstring
          model = APP
  
          fields = (
              "app_id",
              "uid",
              "name",
              "logo",
              "remark",
              "oauth_app",
              "oidc_app",
              "saml_app",
              "ldap_app",
              "http_app",
              "index",
              "allow_any_user",
              'access_perm',
              'auth_protocols',
          )
  
  
  class APPWithAccessOwnerSerializer(APPWithAccessSerializer):
      '''
      Serializer for APP with access perm
      '''
  
      access_result = serializers.SerializerMethodField()
  
      class Meta:    # pylint: disable=missing-docstring
          model = APP
  
          fields = (
              "app_id",
              "uid",
              "name",
              "logo",
              "remark",
              "oauth_app",
              "oidc_app",
              "saml_app",
              "ldap_app",
              "http_app",
              "index",
              "allow_any_user",
              'access_perm',
              'auth_protocols',
              'access_result',
          )
  
      def get_access_result(self, instance):
          '''
          某个节点或人，有无访问该应用的权限
          '''
          request = self.context['request']
          owner = None
          node_uid = request.query_params.get('node_uid', '')
          if node_uid:
              node, _ = Dept.retrieve_node(node_uid)
              if not node:
                  raise ValidationError({'node_uid': ['not found']})
              owner = node
          user_uid = request.query_params.get('user_uid', '')
          if user_uid:
              user = User.valid_objects.filter(username=user_uid).first()
              if not user:
                  raise ValidationError({'user_uid': ['not found']})
              owner = user
          return {
              'node_uid': node_uid,
              'user_uid': user_uid,
              'value': owner.owner_perm_cls.get(perm=instance.access_perm, owner=owner).value if owner else False,
          }
  