Esempio n. 1
0
def absolute_uri(request, base_url):
    if not absolute_http_url_re.match(base_url):
        if base_url.startswith("/"):
            base_url = base_url[1:]

        return '%s://%s/%s' %  (
                request.is_secure() and 'https' or 'http', 
                request.get_host(), base_url)
    return base_url
Esempio n. 2
0
def build_absolute_uri(location, is_secure=False):
    try:
        host = settings.CANONICAL_HOSTNAME
    except AttributeError:
        raise ImproperlyConfigured('You need to specify CANONICAL_HOSTNAME in '
                                   'your Django settings file')
    if not absolute_http_url_re.match(location):
        current_uri = '%s://%s' % ('https' if is_secure else 'http', host)
        location = urljoin(current_uri, location)
    return iri_to_uri(location)
Esempio n. 3
0
def build_absolute_uri(location, is_secure=False):
    try:
        host = settings.CANONICAL_HOSTNAME
    except AttributeError:
        raise ImproperlyConfigured('You need to specify CANONICAL_HOSTNAME in '
                                   'your Django settings file')
    if not absolute_http_url_re.match(location):
        current_uri = '%s://%s' % ('https' if is_secure else 'http', host)
        location = urljoin(current_uri, location)
    return iri_to_uri(location)
Esempio n. 4
0
def rewrite_location(request, prefix_path, location):
    prefix_path = prefix_path or ''
    url = urlparse(location)
    scheme = request.is_secure() and 'https' or 'http'

    if not absolute_http_url_re.match(location):
        # remote server doesn't follow rfc2616
        proxy_uri = '%s://%s%s' % (scheme,
                request.get_host(), prefix_path)
        return  urljoin(proxy_uri, location)
    elif url.scheme != scheme or url.netloc != request.get_host():
        return urlunparse((scheme, request.get_host(), 
            prefix_path + url.path, url.params, url.query, url.fragment))
    return location
Esempio n. 5
0
 def _validate(self):
     """Validate the request."""
     if self.client_id is None:
         raise InvalidRequest('No client_id')
     try:
         self.client = Client.objects.get(key=self.client_id)
     except Client.DoesNotExist:
         raise InvalidClient("client_id %s doesn't exist" % self.client_id)
     # Redirect URI
     if self.redirect_uri is None:
         if self.client.redirect_uri is None:
             raise MissingRedirectURI("No redirect_uri"
                                      "provided or registered.")
     elif self.client.redirect_uri is not None:
         if normalize(self.redirect_uri) != normalize(
                 self.client.redirect_uri):
             self.redirect_uri = self.client.redirect_uri
             raise InvalidRequest("Registered redirect_uri doesn't "
                                  "match provided redirect_uri.")
     self.redirect_uri = self.redirect_uri or self.client.redirect_uri
     # Check response type
     if self.response_type is None:
         raise InvalidRequest('response_type is a required parameter.')
     if self.response_type not in ["code", "token"]:
         raise InvalidRequest("No such response type %s" %
                              self.response_type)
     # Response type
     if self.authorized_response_type & RESPONSE_TYPES[
             self.response_type] == 0:
         raise UnauthorizedClient("Response type %s not allowed." %
                                  self.response_type)
     if not absolute_http_url_re.match(self.redirect_uri):
         raise InvalidRequest('Absolute URI required for redirect_uri')
     # Scope
     if self.authorized_scope is not None and self.scope is None:
         self.scope = self.authorized_scope
     if self.scope is not None:
         self.access_ranges = AccessRange.objects.filter(key__in=self.scope)
         access_ranges = set(
             self.access_ranges.values_list('key', flat=True))
         difference = access_ranges.symmetric_difference(self.scope)
         if len(difference) != 0:
             raise InvalidScope("Following access ranges do not "
                                "exist: %s" % ', '.join(difference))
         if self.authorized_scope is not None:
             new_scope = self.scope - self.authorized_scope
             if len(new_scope) > 0:
                 raise InvalidScope("Invalid scope: %s" %
                                    ','.join(new_scope))
Esempio n. 6
0
    def _validate(self):
        """Validate the request."""
        ClientModel = get_model(*CLIENT_MODEL.split('.', 1))

        if self.client_id is None:
            raise InvalidRequest('No client_id')
        try:
            self.client = ClientModel.objects.get(key=self.client_id)
        except ClientModel.DoesNotExist:
            raise InvalidClient("client_id %s doesn't exist" % self.client_id)
        # Redirect URI
        if self.redirect_uri is None:
            if self.client.redirect_uri is None:
                raise MissingRedirectURI("No redirect_uri"
                    "provided or registered.")
        elif self.client.redirect_uri is not None:
            if normalize(self.redirect_uri) != normalize(self.client.redirect_uri):
                self.redirect_uri = self.client.redirect_uri
                raise InvalidRequest("Registered redirect_uri doesn't "
                    "match provided redirect_uri.")
        self.redirect_uri = self.redirect_uri or self.client.redirect_uri
        # Check response type
        if self.response_type is None:
            raise InvalidRequest('response_type is a required parameter.')
        if self.response_type not in ["code", "token"]:
            raise InvalidRequest("No such response type %s" % self.response_type)
        # Response type
        if self.authorized_response_type & RESPONSE_TYPES[self.response_type] == 0:
            raise UnauthorizedClient("Response type %s not allowed." %
                self.response_type)
        if not absolute_http_url_re.match(self.redirect_uri):
            raise InvalidRequest('Absolute URI required for redirect_uri')
        # Scope
        if self.authorized_scope is not None and self.scope is None:
            self.scope = self.authorized_scope
        if self.scope is not None:
            self.access_ranges = AccessRange.objects.filter(key__in=self.scope)
            access_ranges = set(self.access_ranges.values_list('key', flat=True))
            difference = access_ranges.symmetric_difference(self.scope)
            if len(difference) != 0:
                raise InvalidScope("Following access ranges do not "
                    "exist: %s" % ', '.join(difference))
            if self.authorized_scope is not None:
                new_scope = self.scope - self.authorized_scope
                if len(new_scope) > 0:
                    raise InvalidScope("Invalid scope: %s" % ','.join(new_scope))
Esempio n. 7
0
 def _check_redirect_uri(self):
     """Raise MissingRedirectURI if no redirect_uri is available."""
     if self.redirect_uri is None:
         raise MissingRedirectURI('No redirect_uri to send response.')
     if not absolute_http_url_re.match(self.redirect_uri):
         raise MissingRedirectURI('Absolute redirect_uri required.')
Esempio n. 8
0
 def _check_redirect_uri(self):
     """Raise MissingRedirectURI if no redirect_uri is available."""
     if self.redirect_uri is None:
         raise MissingRedirectURI('No redirect_uri to send response.')
     if not absolute_http_url_re.match(self.redirect_uri):
         raise MissingRedirectURI('Absolute redirect_uri required.')
Esempio n. 9
0
  def validate(self, request):
    """ Check that a Client's authorization request is valid.

    If the request is invalid or malformed in any way, raises the appropriate
    exception.  Read `the relevant section of the specification
    <http://tools.ietf.org/html/rfc6749#section-4.1 .>`_ for descriptions of
    each type of error.

    :raises: a :py:class:`AuthorizationException` if the request is invalid.
    """

    # From http://tools.ietf.org/html/rfc6749#section-3.1 :
    #
    #     The authorization server MUST support the use of the HTTP "GET"
    #     method [RFC2616] for the authorization endpoint and MAY support the
    #     use of the "POST" method as well.
    #
    if not request.method in ['GET', 'POST']:
      raise InvalidRequest('must be GET or POST request')

    if settings.DJOAUTH2_SSL_ONLY and not request.is_secure():
      raise InvalidRequest('all requests must use TLS')

    self.request = request
    self.user = request.user
    if not self.user.is_authenticated():
      raise UnauthenticatedUser('user must be authenticated')

    client_id = request.REQUEST.get('client_id')
    if not client_id:
      raise InvalidRequest('no "client_id" provided')

    try:
      self.client = Client.objects.get(key=client_id)
    except Client.DoesNotExist:
      raise InvalidRequest('"client_id" does not exist')

    # From http://tools.ietf.org/html/rfc6749#section-3.1.2.3 :
    #
    #     If multiple redirection URIs have been registered, if only part of
    #     the redirection URI has been registered, or if no redirection URI has
    #     been registered, the client MUST include a redirection URI with the
    #     authorization request using the "redirect_uri" request parameter.
    #
    self.request_redirect_uri = request.REQUEST.get('redirect_uri')
    if not (self.client.redirect_uri or self.request_redirect_uri):
      raise InvalidRequest('no "redirect_uri" provided or registered')

    # From http://tools.ietf.org/html/rfc6749#section-3.1.2.3 :
    #
    #     When a redirection URI is included in an authorization request, the
    #     authorization server MUST compare and match the value received
    #     against at least one of the registered redirection URIs (or URI
    #     components) as defined in [RFC3986] Section 6, if any redirection
    #     URIs were registered.  If the client registration included the full
    #     redirection URI, the authorization server MUST compare the two URIs
    #     using simple string comparison as defined in [RFC3986] Section 6.2.1.
    #
    if (self.client.redirect_uri and
          self.request_redirect_uri and
          self.client.redirect_uri != self.request_redirect_uri):
      raise InvalidRequest('"redirect_uri" does not matched the registered URI')

    # From http://tools.ietf.org/html/rfc6749#section-3.1.2 :
    #
    #     The redirection endpoint URI MUST be an absolute URI as defined by
    #     [RFC3986] Section 4.3.
    #
    redirect_uri = self.client.redirect_uri or self.request_redirect_uri
    if not absolute_http_url_re.match(redirect_uri):
      raise InvalidRequest('"redirect_uri" must be absolute')

    # From http://tools.ietf.org/html/rfc6749#section-3.1.2 :
    #
    #     The endpoint URI MUST NOT include a fragment component.
    #
    if urlparse(redirect_uri).fragment:
      raise InvalidRequest('"redirect_uri" must not contain a fragment')

    # From http://tools.ietf.org/html/rfc6749#section-3.1.2.1 :
    #
    #     The redirection endpoint SHOULD require the use of TLS as described
    #     in Section 1.6 when the requested response type is "code" or "token",
    #     or when the redirection request will result in the transmission of
    #     sensitive credentials over an open network.  This specification does
    #     not mandate the use of TLS because at the time of this writing,
    #     requiring clients to deploy TLS is a significant hurdle for many
    #     client developers.  If TLS is not available, the authorization server
    #     SHOULD warn the resource owner about the insecure endpoint prior to
    #     redirection (e.g., display a message during the authorization
    #     request).
    #
    if (settings.DJOAUTH2_SSL_ONLY and
        urlparse(redirect_uri).scheme != 'https'):
      raise InvalidRequest('"redirect_uri" must use TLS')

    # Only store the redirect_uri value if it validates successfully. The
    # 'make_error_redirect' method will use the 'missing_redirect_uri' passed
    # to the '__init__' method if 'self.redirect_uri' is None.
    self.redirect_uri = redirect_uri

    # From http://tools.ietf.org/html/rfc6749#section-3.1.1 :
    #
    #     The client informs the authorization server of the desired grant type
    #     using the following parameter:
    #
    #     response_type
    #           REQUIRED.  The value MUST be one of "code" for requesting an
    #           authorization code as described by Section 4.1.1, "token" for
    #           requesting an access token (implicit grant) as described by
    #           Section 4.2.1, or a registered extension value as described by
    #           Section 8.4.
    #
    # This implementation only supports the "code" "response_type".
    response_type = request.REQUEST.get('response_type')
    if response_type != 'code':
      raise UnsupportedResponseType('"response_type" must be "code"')

    # As recommended by http://tools.ietf.org/html/rfc6749#section-4.1.1 :
    #
    #     state
    #           RECOMMENDED.  An opaque value used by the client to maintain
    #           state between the request and callback.  The authorization
    #           server includes this value when redirecting the user-agent back
    #           to the client.  The parameter SHOULD be used for preventing
    #           cross-site request forgery as described in Section 10.12.
    #
    # and necessary for the CSRF recommendation mandated by
    # http://tools.ietf.org/html/rfc6749#section-10.12 :
    #
    #     The client MUST implement CSRF protection for its redirection URI.
    #     This is typically accomplished by requiring any request sent to the
    #     redirection URI endpoint to include a value that binds the request to
    #     the user-agent's authenticated state (e.g., a hash of the session
    #     cookie used to authenticate the user-agent).  The client SHOULD
    #     utilize the "state" request parameter to deliver this value to the
    #     authorization server when making an authorization request.
    #
    self.state = request.REQUEST.get('state')
    if settings.DJOAUTH2_REQUIRE_STATE and not self.state:
      raise InvalidRequest('"state" must be included')

    requested_scope_string = request.REQUEST.get('scope', '')
    if not requested_scope_string:
      raise InvalidScope('"scope" must be included')

    requested_scope_names = set(requested_scope_string.split(' '))
    self.valid_scope_objects = Scope.objects.filter(
        name__in=requested_scope_names)
    valid_scope_names = {scope.name for scope in self.valid_scope_objects}
    if valid_scope_names < requested_scope_names:
      raise InvalidScope('The following scopes are invalid: {}'.format(
          ', '.join('"{}"'.format(name)
                    for name in (requested_scope_names - valid_scope_names))))
Esempio n. 10
0
  def validate(self, request):
    """ Check that a Client's authorization request is valid.

    If the request is invalid or malformed in any way, raises the appropriate
    exception.  Read `the relevant section of the specification
    <http://tools.ietf.org/html/rfc6749#section-4.1 .>`_ for descriptions of
    each type of error.

    :raises: a :py:class:`AuthorizationError` if the request is invalid.
    """

    # From http://tools.ietf.org/html/rfc6749#section-3.1 :
    #
    #     The authorization server MUST support the use of the HTTP "GET"
    #     method [RFC2616] for the authorization endpoint and MAY support the
    #     use of the "POST" method as well.
    #
    if not request.method in ['GET', 'POST']:
      raise InvalidRequest('must be GET or POST request')

    if settings.DJOAUTH2_SSL_ONLY and not request.is_secure():
      raise InvalidRequest('all requests must use TLS')

    self.request = request
    self.user = request.user
    if not self.user.is_authenticated():
      raise UnauthenticatedUser('user must be authenticated')

    client_id = request.REQUEST.get('client_id')
    if not client_id:
      raise InvalidRequest('no "client_id" provided')

    try:
      self.client = Client.objects.get(key=client_id)
    except Client.DoesNotExist:
      raise InvalidRequest('"client_id" does not exist')

    # From http://tools.ietf.org/html/rfc6749#section-3.1.2.3 :
    #
    #     If multiple redirection URIs have been registered, if only part of
    #     the redirection URI has been registered, or if no redirection URI has
    #     been registered, the client MUST include a redirection URI with the
    #     authorization request using the "redirect_uri" request parameter.
    #
    self.request_redirect_uri = request.REQUEST.get('redirect_uri')
    if not (self.client.redirect_uri or self.request_redirect_uri):
      raise InvalidRequest('no "redirect_uri" provided or registered')

    # From http://tools.ietf.org/html/rfc6749#section-3.1.2.3 :
    #
    #     When a redirection URI is included in an authorization request, the
    #     authorization server MUST compare and match the value received
    #     against at least one of the registered redirection URIs (or URI
    #     components) as defined in [RFC3986] Section 6, if any redirection
    #     URIs were registered.  If the client registration included the full
    #     redirection URI, the authorization server MUST compare the two URIs
    #     using simple string comparison as defined in [RFC3986] Section 6.2.1.
    #
    if (self.client.redirect_uri and
          self.request_redirect_uri and
          self.client.redirect_uri != self.request_redirect_uri):
      raise InvalidRequest('"redirect_uri" does not matched the registered URI')

    # From http://tools.ietf.org/html/rfc6749#section-3.1.2 :
    #
    #     The redirection endpoint URI MUST be an absolute URI as defined by
    #     [RFC3986] Section 4.3.
    #
    redirect_uri = self.client.redirect_uri or self.request_redirect_uri
    if not absolute_http_url_re.match(redirect_uri):
      raise InvalidRequest('"redirect_uri" must be absolute')

    # From http://tools.ietf.org/html/rfc6749#section-3.1.2 :
    #
    #     The endpoint URI MUST NOT include a fragment component.
    #
    if urlparse(redirect_uri).fragment:
      raise InvalidRequest('"redirect_uri" must not contain a fragment')

    # From http://tools.ietf.org/html/rfc6749#section-3.1.2.1 :
    #
    #     The redirection endpoint SHOULD require the use of TLS as described
    #     in Section 1.6 when the requested response type is "code" or "token",
    #     or when the redirection request will result in the transmission of
    #     sensitive credentials over an open network.  This specification does
    #     not mandate the use of TLS because at the time of this writing,
    #     requiring clients to deploy TLS is a significant hurdle for many
    #     client developers.  If TLS is not available, the authorization server
    #     SHOULD warn the resource owner about the insecure endpoint prior to
    #     redirection (e.g., display a message during the authorization
    #     request).
    #
    if (settings.DJOAUTH2_SSL_ONLY and
        urlparse(redirect_uri).scheme != 'https'):
      raise InvalidRequest('"redirect_uri" must use TLS')

    # Only store the redirect_uri value if it validates successfully. The
    # 'make_error_redirect' method will use the 'missing_redirect_uri' passed
    # to the '__init__' method if 'self.redirect_uri' is None.
    self.redirect_uri = redirect_uri

    # From http://tools.ietf.org/html/rfc6749#section-3.1.1 :
    #
    #     The client informs the authorization server of the desired grant type
    #     using the following parameter:
    #
    #     response_type
    #           REQUIRED.  The value MUST be one of "code" for requesting an
    #           authorization code as described by Section 4.1.1, "token" for
    #           requesting an access token (implicit grant) as described by
    #           Section 4.2.1, or a registered extension value as described by
    #           Section 8.4.
    #
    # This implementation only supports the "code" "response_type".
    response_type = request.REQUEST.get('response_type')
    if response_type != 'code':
      raise UnsupportedResponseType('"response_type" must be "code"')

    # As recommended by http://tools.ietf.org/html/rfc6749#section-4.1.1 :
    #
    #     state
    #           RECOMMENDED.  An opaque value used by the client to maintain
    #           state between the request and callback.  The authorization
    #           server includes this value when redirecting the user-agent back
    #           to the client.  The parameter SHOULD be used for preventing
    #           cross-site request forgery as described in Section 10.12.
    #
    # and necessary for the CSRF recommendation mandated by
    # http://tools.ietf.org/html/rfc6749#section-10.12 :
    #
    #     The client MUST implement CSRF protection for its redirection URI.
    #     This is typically accomplished by requiring any request sent to the
    #     redirection URI endpoint to include a value that binds the request to
    #     the user-agent's authenticated state (e.g., a hash of the session
    #     cookie used to authenticate the user-agent).  The client SHOULD
    #     utilize the "state" request parameter to deliver this value to the
    #     authorization server when making an authorization request.
    #
    self.state = request.REQUEST.get('state')
    if settings.DJOAUTH2_REQUIRE_STATE and not self.state:
      raise InvalidRequest('"state" must be included')

    requested_scope_string = request.REQUEST.get('scope', '')
    if not requested_scope_string:
      raise InvalidScope('"scope" must be included')

    requested_scope_names = set(requested_scope_string.split(' '))
    self.valid_scope_objects = Scope.objects.filter(
        name__in=requested_scope_names)
    valid_scope_names = {scope.name for scope in self.valid_scope_objects}
    if valid_scope_names < requested_scope_names:
      raise InvalidScope('The following scopes are invalid: {}'.format(
          ', '.join('"{}"'.format(name)
                    for name in (requested_scope_names - valid_scope_names))))