diff options
Diffstat (limited to 'rest_framework')
| -rw-r--r-- | rest_framework/authentication.py | 183 | ||||
| -rw-r--r-- | rest_framework/compat.py | 66 | ||||
| -rw-r--r-- | rest_framework/parsers.py | 98 | ||||
| -rw-r--r-- | rest_framework/permissions.py | 28 | ||||
| -rw-r--r-- | rest_framework/renderers.py | 111 | ||||
| -rw-r--r-- | rest_framework/settings.py | 4 | ||||
| -rw-r--r-- | rest_framework/utils/encoders.py | 64 | 
7 files changed, 4 insertions, 550 deletions
| diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index 4832ad33..124ef68a 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -3,14 +3,9 @@ Provides various authentication policies.  """  from __future__ import unicode_literals  import base64 -  from django.contrib.auth import authenticate -from django.core.exceptions import ImproperlyConfigured  from django.middleware.csrf import CsrfViewMiddleware -from django.conf import settings  from rest_framework import exceptions, HTTP_HEADER_ENCODING -from rest_framework.compat import oauth, oauth_provider, oauth_provider_store -from rest_framework.compat import oauth2_provider, provider_now, check_nonce  from rest_framework.authtoken.models import Token @@ -178,181 +173,3 @@ class TokenAuthentication(BaseAuthentication):      def authenticate_header(self, request):          return 'Token' - - -class OAuthAuthentication(BaseAuthentication): -    """ -    OAuth 1.0a authentication backend using `django-oauth-plus` and `oauth2`. - -    Note: The `oauth2` package actually provides oauth1.0a support.  Urg. -          We import it from the `compat` module as `oauth`. -    """ -    www_authenticate_realm = 'api' - -    def __init__(self, *args, **kwargs): -        super(OAuthAuthentication, self).__init__(*args, **kwargs) - -        if oauth is None: -            raise ImproperlyConfigured( -                "The 'oauth2' package could not be imported." -                "It is required for use with the 'OAuthAuthentication' class.") - -        if oauth_provider is None: -            raise ImproperlyConfigured( -                "The 'django-oauth-plus' package could not be imported." -                "It is required for use with the 'OAuthAuthentication' class.") - -    def authenticate(self, request): -        """ -        Returns two-tuple of (user, token) if authentication succeeds, -        or None otherwise. -        """ -        try: -            oauth_request = oauth_provider.utils.get_oauth_request(request) -        except oauth.Error as err: -            raise exceptions.AuthenticationFailed(err.message) - -        if not oauth_request: -            return None - -        oauth_params = oauth_provider.consts.OAUTH_PARAMETERS_NAMES - -        found = any(param for param in oauth_params if param in oauth_request) -        missing = list(param for param in oauth_params if param not in oauth_request) - -        if not found: -            # OAuth authentication was not attempted. -            return None - -        if missing: -            # OAuth was attempted but missing parameters. -            msg = 'Missing parameters: %s' % (', '.join(missing)) -            raise exceptions.AuthenticationFailed(msg) - -        if not self.check_nonce(request, oauth_request): -            msg = 'Nonce check failed' -            raise exceptions.AuthenticationFailed(msg) - -        try: -            consumer_key = oauth_request.get_parameter('oauth_consumer_key') -            consumer = oauth_provider_store.get_consumer(request, oauth_request, consumer_key) -        except oauth_provider.store.InvalidConsumerError: -            msg = 'Invalid consumer token: %s' % oauth_request.get_parameter('oauth_consumer_key') -            raise exceptions.AuthenticationFailed(msg) - -        if consumer.status != oauth_provider.consts.ACCEPTED: -            msg = 'Invalid consumer key status: %s' % consumer.get_status_display() -            raise exceptions.AuthenticationFailed(msg) - -        try: -            token_param = oauth_request.get_parameter('oauth_token') -            token = oauth_provider_store.get_access_token(request, oauth_request, consumer, token_param) -        except oauth_provider.store.InvalidTokenError: -            msg = 'Invalid access token: %s' % oauth_request.get_parameter('oauth_token') -            raise exceptions.AuthenticationFailed(msg) - -        try: -            self.validate_token(request, consumer, token) -        except oauth.Error as err: -            raise exceptions.AuthenticationFailed(err.message) - -        user = token.user - -        if not user.is_active: -            msg = 'User inactive or deleted: %s' % user.username -            raise exceptions.AuthenticationFailed(msg) - -        return (token.user, token) - -    def authenticate_header(self, request): -        """ -        If permission is denied, return a '401 Unauthorized' response, -        with an appropriate 'WWW-Authenticate' header. -        """ -        return 'OAuth realm="%s"' % self.www_authenticate_realm - -    def validate_token(self, request, consumer, token): -        """ -        Check the token and raise an `oauth.Error` exception if invalid. -        """ -        oauth_server, oauth_request = oauth_provider.utils.initialize_server_request(request) -        oauth_server.verify_request(oauth_request, consumer, token) - -    def check_nonce(self, request, oauth_request): -        """ -        Checks nonce of request, and return True if valid. -        """ -        oauth_nonce = oauth_request['oauth_nonce'] -        oauth_timestamp = oauth_request['oauth_timestamp'] -        return check_nonce(request, oauth_request, oauth_nonce, oauth_timestamp) - - -class OAuth2Authentication(BaseAuthentication): -    """ -    OAuth 2 authentication backend using `django-oauth2-provider` -    """ -    www_authenticate_realm = 'api' -    allow_query_params_token = settings.DEBUG - -    def __init__(self, *args, **kwargs): -        super(OAuth2Authentication, self).__init__(*args, **kwargs) - -        if oauth2_provider is None: -            raise ImproperlyConfigured( -                "The 'django-oauth2-provider' package could not be imported. " -                "It is required for use with the 'OAuth2Authentication' class.") - -    def authenticate(self, request): -        """ -        Returns two-tuple of (user, token) if authentication succeeds, -        or None otherwise. -        """ - -        auth = get_authorization_header(request).split() - -        if len(auth) == 1: -            msg = 'Invalid bearer header. No credentials provided.' -            raise exceptions.AuthenticationFailed(msg) -        elif len(auth) > 2: -            msg = 'Invalid bearer header. Token string should not contain spaces.' -            raise exceptions.AuthenticationFailed(msg) - -        if auth and auth[0].lower() == b'bearer': -            access_token = auth[1] -        elif 'access_token' in request.POST: -            access_token = request.POST['access_token'] -        elif 'access_token' in request.GET and self.allow_query_params_token: -            access_token = request.GET['access_token'] -        else: -            return None - -        return self.authenticate_credentials(request, access_token) - -    def authenticate_credentials(self, request, access_token): -        """ -        Authenticate the request, given the access token. -        """ - -        try: -            token = oauth2_provider.oauth2.models.AccessToken.objects.select_related('user') -            # provider_now switches to timezone aware datetime when -            # the oauth2_provider version supports to it. -            token = token.get(token=access_token, expires__gt=provider_now()) -        except oauth2_provider.oauth2.models.AccessToken.DoesNotExist: -            raise exceptions.AuthenticationFailed('Invalid token') - -        user = token.user - -        if not user.is_active: -            msg = 'User inactive or deleted: %s' % user.get_username() -            raise exceptions.AuthenticationFailed(msg) - -        return (user, token) - -    def authenticate_header(self, request): -        """ -        Bearer is the only finalized type currently - -        Check details on the `OAuth2Authentication.authenticate` method -        """ -        return 'Bearer realm="%s"' % self.www_authenticate_realm diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 71520b92..43ad6eaa 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -190,72 +190,6 @@ except ImportError:      apply_markdown = None -# Yaml is optional -try: -    import yaml -except ImportError: -    yaml = None - - -# XML is optional -try: -    import defusedxml.ElementTree as etree -except ImportError: -    etree = None - - -# OAuth2 is optional -try: -    # Note: The `oauth2` package actually provides oauth1.0a support.  Urg. -    import oauth2 as oauth -except ImportError: -    oauth = None - - -# OAuthProvider is optional -try: -    import oauth_provider -    from oauth_provider.store import store as oauth_provider_store - -    # check_nonce's calling signature in django-oauth-plus changes sometime -    # between versions 2.0 and 2.2.1 -    def check_nonce(request, oauth_request, oauth_nonce, oauth_timestamp): -        check_nonce_args = inspect.getargspec(oauth_provider_store.check_nonce).args -        if 'timestamp' in check_nonce_args: -            return oauth_provider_store.check_nonce( -                request, oauth_request, oauth_nonce, oauth_timestamp -            ) -        return oauth_provider_store.check_nonce( -            request, oauth_request, oauth_nonce -        ) - -except (ImportError, ImproperlyConfigured): -    oauth_provider = None -    oauth_provider_store = None -    check_nonce = None - - -# OAuth 2 support is optional -try: -    import provider as oauth2_provider -    from provider import scope as oauth2_provider_scope -    from provider import constants as oauth2_constants - -    if oauth2_provider.__version__ in ('0.2.3', '0.2.4'): -        # 0.2.3 and 0.2.4 are supported version that do not support -        # timezone aware datetimes -        import datetime - -        provider_now = datetime.datetime.now -    else: -        # Any other supported version does use timezone aware datetimes -        from django.utils.timezone import now as provider_now -except ImportError: -    oauth2_provider = None -    oauth2_provider_scope = None -    oauth2_constants = None -    provider_now = None -  # `separators` argument to `json.dumps()` differs between 2.x and 3.x  # See: http://bugs.python.org/issue22767  if six.PY3: diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py index 3e3395c0..cb23423d 100644 --- a/rest_framework/parsers.py +++ b/rest_framework/parsers.py @@ -14,12 +14,9 @@ from django.http.multipartparser import MultiPartParserError, parse_header, Chun  from django.utils import six  from django.utils.six.moves.urllib import parse as urlparse  from django.utils.encoding import force_text -from rest_framework.compat import etree, yaml  from rest_framework.exceptions import ParseError  from rest_framework import renderers  import json -import datetime -import decimal  class DataAndFiles(object): @@ -67,29 +64,6 @@ class JSONParser(BaseParser):              raise ParseError('JSON parse error - %s' % six.text_type(exc)) -class YAMLParser(BaseParser): -    """ -    Parses YAML-serialized data. -    """ - -    media_type = 'application/yaml' - -    def parse(self, stream, media_type=None, parser_context=None): -        """ -        Parses the incoming bytestream as YAML and returns the resulting data. -        """ -        assert yaml, 'YAMLParser requires pyyaml to be installed' - -        parser_context = parser_context or {} -        encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) - -        try: -            data = stream.read().decode(encoding) -            return yaml.safe_load(data) -        except (ValueError, yaml.parser.ParserError) as exc: -            raise ParseError('YAML parse error - %s' % six.text_type(exc)) - -  class FormParser(BaseParser):      """      Parser for form data. @@ -138,78 +112,6 @@ class MultiPartParser(BaseParser):              raise ParseError('Multipart form parse error - %s' % six.text_type(exc)) -class XMLParser(BaseParser): -    """ -    XML parser. -    """ - -    media_type = 'application/xml' - -    def parse(self, stream, media_type=None, parser_context=None): -        """ -        Parses the incoming bytestream as XML and returns the resulting data. -        """ -        assert etree, 'XMLParser requires defusedxml to be installed' - -        parser_context = parser_context or {} -        encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) -        parser = etree.DefusedXMLParser(encoding=encoding) -        try: -            tree = etree.parse(stream, parser=parser, forbid_dtd=True) -        except (etree.ParseError, ValueError) as exc: -            raise ParseError('XML parse error - %s' % six.text_type(exc)) -        data = self._xml_convert(tree.getroot()) - -        return data - -    def _xml_convert(self, element): -        """ -        convert the xml `element` into the corresponding python object -        """ - -        children = list(element) - -        if len(children) == 0: -            return self._type_convert(element.text) -        else: -            # if the fist child tag is list-item means all children are list-item -            if children[0].tag == "list-item": -                data = [] -                for child in children: -                    data.append(self._xml_convert(child)) -            else: -                data = {} -                for child in children: -                    data[child.tag] = self._xml_convert(child) - -            return data - -    def _type_convert(self, value): -        """ -        Converts the value returned by the XMl parse into the equivalent -        Python type -        """ -        if value is None: -            return value - -        try: -            return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S') -        except ValueError: -            pass - -        try: -            return int(value) -        except ValueError: -            pass - -        try: -            return decimal.Decimal(value) -        except decimal.InvalidOperation: -            pass - -        return value - -  class FileUploadParser(BaseParser):      """      Parser for file upload data. diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 3f6f5961..9069d315 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -3,8 +3,7 @@ Provides a set of pluggable permission policies.  """  from __future__ import unicode_literals  from django.http import Http404 -from rest_framework.compat import (get_model_name, oauth2_provider_scope, -                                   oauth2_constants) +from rest_framework.compat import get_model_name  SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS'] @@ -199,28 +198,3 @@ class DjangoObjectPermissions(DjangoModelPermissions):              return False          return True - - -class TokenHasReadWriteScope(BasePermission): -    """ -    The request is authenticated as a user and the token used has the right scope -    """ - -    def has_permission(self, request, view): -        token = request.auth -        read_only = request.method in SAFE_METHODS - -        if not token: -            return False - -        if hasattr(token, 'resource'):  # OAuth 1 -            return read_only or not request.auth.resource.is_readonly -        elif hasattr(token, 'scope'):  # OAuth 2 -            required = oauth2_constants.READ if read_only else oauth2_constants.WRITE -            return oauth2_provider_scope.check(required, request.auth.scope) - -        assert False, ( -            'TokenHasReadWriteScope requires either the' -            '`OAuthAuthentication` or `OAuth2Authentication` authentication ' -            'class to be used.' -        ) diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index cfcf1f5d..f8d18fc6 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -17,11 +17,8 @@ from django.http.multipartparser import parse_header  from django.template import Context, RequestContext, loader, Template  from django.test.client import encode_multipart  from django.utils import six -from django.utils.encoding import smart_text -from django.utils.xmlutils import SimplerXMLGenerator -from django.utils.six.moves import StringIO  from rest_framework import exceptions, serializers, status, VERSION -from rest_framework.compat import SHORT_SEPARATORS, LONG_SEPARATORS, yaml +from rest_framework.compat import SHORT_SEPARATORS, LONG_SEPARATORS  from rest_framework.exceptions import ParseError  from rest_framework.settings import api_settings  from rest_framework.request import is_form_media_type, override_method @@ -112,112 +109,6 @@ class JSONRenderer(BaseRenderer):          return ret -class JSONPRenderer(JSONRenderer): -    """ -    Renderer which serializes to json, -    wrapping the json output in a callback function. -    """ - -    media_type = 'application/javascript' -    format = 'jsonp' -    callback_parameter = 'callback' -    default_callback = 'callback' -    charset = 'utf-8' - -    def get_callback(self, renderer_context): -        """ -        Determine the name of the callback to wrap around the json output. -        """ -        request = renderer_context.get('request', None) -        params = request and request.query_params or {} -        return params.get(self.callback_parameter, self.default_callback) - -    def render(self, data, accepted_media_type=None, renderer_context=None): -        """ -        Renders into jsonp, wrapping the json output in a callback function. - -        Clients may set the callback function name using a query parameter -        on the URL, for example: ?callback=exampleCallbackName -        """ -        renderer_context = renderer_context or {} -        callback = self.get_callback(renderer_context) -        json = super(JSONPRenderer, self).render(data, accepted_media_type, -                                                 renderer_context) -        return callback.encode(self.charset) + b'(' + json + b');' - - -class XMLRenderer(BaseRenderer): -    """ -    Renderer which serializes to XML. -    """ - -    media_type = 'application/xml' -    format = 'xml' -    charset = 'utf-8' - -    def render(self, data, accepted_media_type=None, renderer_context=None): -        """ -        Renders `data` into serialized XML. -        """ -        if data is None: -            return '' - -        stream = StringIO() - -        xml = SimplerXMLGenerator(stream, self.charset) -        xml.startDocument() -        xml.startElement("root", {}) - -        self._to_xml(xml, data) - -        xml.endElement("root") -        xml.endDocument() -        return stream.getvalue() - -    def _to_xml(self, xml, data): -        if isinstance(data, (list, tuple)): -            for item in data: -                xml.startElement("list-item", {}) -                self._to_xml(xml, item) -                xml.endElement("list-item") - -        elif isinstance(data, dict): -            for key, value in six.iteritems(data): -                xml.startElement(key, {}) -                self._to_xml(xml, value) -                xml.endElement(key) - -        elif data is None: -            # Don't output any value -            pass - -        else: -            xml.characters(smart_text(data)) - - -class YAMLRenderer(BaseRenderer): -    """ -    Renderer which serializes to YAML. -    """ - -    media_type = 'application/yaml' -    format = 'yaml' -    encoder = encoders.SafeDumper -    charset = 'utf-8' -    ensure_ascii = False - -    def render(self, data, accepted_media_type=None, renderer_context=None): -        """ -        Renders `data` into serialized YAML. -        """ -        assert yaml, 'YAMLRenderer requires pyyaml to be installed' - -        if data is None: -            return '' - -        return yaml.dump(data, stream=None, encoding=self.charset, Dumper=self.encoder, allow_unicode=not self.ensure_ascii) - -  class TemplateHTMLRenderer(BaseRenderer):      """      An HTML renderer for use with templates. diff --git a/rest_framework/settings.py b/rest_framework/settings.py index 79da23ca..0aac6d43 100644 --- a/rest_framework/settings.py +++ b/rest_framework/settings.py @@ -5,11 +5,11 @@ For example your project's `settings.py` file might look like this:  REST_FRAMEWORK = {      'DEFAULT_RENDERER_CLASSES': (          'rest_framework.renderers.JSONRenderer', -        'rest_framework.renderers.YAMLRenderer', +        'rest_framework.renderers.TemplateHTMLRenderer',      )      'DEFAULT_PARSER_CLASSES': (          'rest_framework.parsers.JSONParser', -        'rest_framework.parsers.YAMLParser', +        'rest_framework.parsers.TemplateHTMLRenderer',      )  } diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index adc83e57..0bd24939 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -6,10 +6,8 @@ from django.db.models.query import QuerySet  from django.utils import six, timezone  from django.utils.encoding import force_text  from django.utils.functional import Promise -from rest_framework.compat import OrderedDict  import datetime  import decimal -import types  import json @@ -57,65 +55,3 @@ class JSONEncoder(json.JSONEncoder):          elif hasattr(obj, '__iter__'):              return tuple(item for item in obj)          return super(JSONEncoder, self).default(obj) - - -try: -    import yaml -except ImportError: -    SafeDumper = None -else: -    # Adapted from http://pyyaml.org/attachment/ticket/161/use_ordered_dict.py -    class SafeDumper(yaml.SafeDumper): -        """ -        Handles decimals as strings. -        Handles OrderedDicts as usual dicts, but preserves field order, rather -        than the usual behaviour of sorting the keys. -        """ -        def represent_decimal(self, data): -            return self.represent_scalar('tag:yaml.org,2002:str', six.text_type(data)) - -        def represent_mapping(self, tag, mapping, flow_style=None): -            value = [] -            node = yaml.MappingNode(tag, value, flow_style=flow_style) -            if self.alias_key is not None: -                self.represented_objects[self.alias_key] = node -            best_style = True -            if hasattr(mapping, 'items'): -                mapping = list(mapping.items()) -                if not isinstance(mapping, OrderedDict): -                    mapping.sort() -            for item_key, item_value in mapping: -                node_key = self.represent_data(item_key) -                node_value = self.represent_data(item_value) -                if not (isinstance(node_key, yaml.ScalarNode) and not node_key.style): -                    best_style = False -                if not (isinstance(node_value, yaml.ScalarNode) and not node_value.style): -                    best_style = False -                value.append((node_key, node_value)) -            if flow_style is None: -                if self.default_flow_style is not None: -                    node.flow_style = self.default_flow_style -                else: -                    node.flow_style = best_style -            return node - -    SafeDumper.add_representer( -        decimal.Decimal, -        SafeDumper.represent_decimal -    ) -    SafeDumper.add_representer( -        OrderedDict, -        yaml.representer.SafeRepresenter.represent_dict -    ) -    # SafeDumper.add_representer( -    #     DictWithMetadata, -    #     yaml.representer.SafeRepresenter.represent_dict -    # ) -    # SafeDumper.add_representer( -    #     OrderedDictWithMetadata, -    #     yaml.representer.SafeRepresenter.represent_dict -    # ) -    SafeDumper.add_representer( -        types.GeneratorType, -        yaml.representer.SafeRepresenter.represent_list -    ) | 
