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.

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))))