import re from django.http.multipartparser import parse_header_parameters from rest_framework.utils import encoders, json from rest_framework.settings import api_settings from rest_framework.utils import encoders from rest_framework.renderers import BaseRenderer, zero_as_none from rest_framework.compat import ( INDENT_SEPARATORS, LONG_SEPARATORS, SHORT_SEPARATORS ) PHONE_PATTERN = re.compile(r'\"1\d{10}\"') EMAIL_PATTERN = re.compile(r'\"[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+\"') class AdminJSONRenderer(BaseRenderer): """ Renderer which serializes to JSON. """ media_type = 'application/json' format = 'json' encoder_class = encoders.JSONEncoder ensure_ascii = not api_settings.UNICODE_JSON compact = api_settings.COMPACT_JSON strict = api_settings.STRICT_JSON # We don't set a charset because JSON is a binary encoding, # that can be encoded as utf-8, utf-16 or utf-32. # See: https://www.ietf.org/rfc/rfc4627.txt # Also: http://lucumr.pocoo.org/2013/7/19/application-mimetypes-and-encodings/ charset = None def get_indent(self, accepted_media_type, renderer_context): if accepted_media_type: # If the media type looks like 'application/json; indent=4', # then pretty print the result. # Note that we coerce `indent=0` into `indent=None`. base_media_type, params = parse_header_parameters(accepted_media_type) try: return zero_as_none(max(min(int(params['indent']), 8), 0)) except (KeyError, ValueError, TypeError): pass # If 'indent' is provided in the context, then pretty print the result. # E.g. If we're being called by the BrowsableAPIRenderer. return renderer_context.get('indent', None) def render(self, data, accepted_media_type=None, renderer_context=None): """ Render `data` into JSON, returning a bytestring. """ if data is None: return b'' renderer_context = renderer_context or {} indent = self.get_indent(accepted_media_type, renderer_context) if indent is None: separators = SHORT_SEPARATORS if self.compact else LONG_SEPARATORS else: separators = INDENT_SEPARATORS ret = json.dumps( data, cls=self.encoder_class, indent=indent, ensure_ascii=self.ensure_ascii, allow_nan=not self.strict, separators=separators ) # We always fully escape \u2028 and \u2029 to ensure we output JSON # that is a strict javascript subset. # See: http://timelessrepo.com/json-isnt-a-javascript-subset ret = ret.replace('\u2028', '\\u2028').replace('\u2029', '\\u2029') request = renderer_context.get('request') if not ( request and request.user.is_authenticated and request.user.is_staff and request.user.has_perm('pingcap_sso.view_user_contact_info') ): ret = re.sub(PHONE_PATTERN, '"13*******"', ret) ret = re.sub(EMAIL_PATTERN, '"***@**"', ret) return ret.encode()