text: from __future__ import unicode_literals
  
  import base64
  import logging
  from datetime import timedelta
  
  from django.utils import timezone
  from django.contrib.auth import authenticate, get_backends
  from oauthlib.oauth2 import RequestValidator
  
  from .compat import unquote_plus
  from .models import Grant, AccessToken, RefreshToken, get_application_model
  from .settings import oauth2_settings
  
  
  
  def find_user_by_id(user_id):
      user = None
      backends = get_backends()
  
      for auth_backend in backends:
          try:
              user = auth_backend.get_user(user_id)
          except ValueError: 
              continue
  
          if user:
              break
      return user 
  
  
  
  Application = get_application_model()
  
  log = logging.getLogger('oauth2_provider')
  
  GRANT_TYPE_MAPPING = {
      'authorization_code': (Application.GRANT_AUTHORIZATION_CODE,),
      'password': (Application.GRANT_PASSWORD,),
      'client_credentials': (Application.GRANT_CLIENT_CREDENTIALS,),
      'refresh_token': (Application.GRANT_AUTHORIZATION_CODE, Application.GRANT_PASSWORD,
                        Application.GRANT_CLIENT_CREDENTIALS)
  }
  
  
  class OAuth2Validator(RequestValidator):
      def _extract_basic_auth(self, request):
          """
          Return authentication string if request contains basic auth credentials, else return None
          """
          auth = request.headers.get('HTTP_AUTHORIZATION', None)
          if not auth:
              return None
  
          auth_type, auth_string = auth.split(' ')
          if auth_type != "Basic":
              return None
  
          return auth_string
  
      def _authenticate_basic_auth(self, request):
          """
          Authenticates with HTTP Basic Auth.
  
          Note: as stated in rfc:`2.3.1`, client_id and client_secret must be encoded with
          "application/x-www-form-urlencoded" encoding algorithm.
          """
          auth_string = self._extract_basic_auth(request)
          if not auth_string:
              return False
  
          encoding = request.encoding or 'utf-8'
  
          auth_string_decoded = base64.b64decode(auth_string).decode(encoding)
          client_id, client_secret = map(unquote_plus, auth_string_decoded.split(':', 1))
  
          if self._load_application(client_id, request) is None:
              log.debug("Failed basic auth: Application %s does not exist" % client_id)
              return False
          elif request.client.client_secret != client_secret:
              log.debug("Failed basic auth: wrong client secret %s" % client_secret)
              return False
          else:
              return True
  
      def _authenticate_request_body(self, request):
          """
          Try to authenticate the client using client_id and client_secret parameters
          included in body.
  
          Remember that this method is NOT RECOMMENDED and SHOULD be limited to clients unable to
          directly utilize the HTTP Basic authentication scheme. See rfc:`2.3.1` for more details.
          """
          #TODO: check if oauthlib has already unquoted client_id and client_secret
          client_id = request.client_id
          client_secret = request.client_secret
  
          if not client_id or not client_secret:
              return False
  
          if self._load_application(client_id, request) is None:
              log.debug("Failed body auth: Application %s does not exists" % client_id)
              return False
          elif request.client.client_secret != client_secret:
              log.debug("Failed body auth: wrong client secret %s" % client_secret)
              return False
          else:
              return True
  
      def _load_application(self, client_id, request):
          """
          If request.client was not set, load application instance for given client_id and store it
          in request.client
          """
          try:
              request.client = request.client or Application.objects.get(client_id=client_id)
              return request.client
          except Application.DoesNotExist:
              log.debug("Failed body authentication: Application %s does not exist" % client_id)
              return None
  
      def client_authentication_required(self, request, *args, **kwargs):
          """
          Determine if the client has to be authenticated
  
          This method is called only for grant types that supports client authentication:
              * Authorization code grant
              * Resource owner password grant
              * Refresh token grant
  
          If the request contains authorization headers, always authenticate the client no matter
          the grant type.
  
          If the request does not contain authorization headers, proceed with authentication only if
          the client is of type `Confidential`.
  
          If something goes wrong, call oauthlib implementation of the method.
          """
          if self._extract_basic_auth(request):
              return True
  
          if request.client_id and request.client_secret:
              return True
  
          self._load_application(request.client_id, request)
          if request.client:
              return request.client.client_type == Application.CLIENT_CONFIDENTIAL
  
          return super(OAuth2Validator, self).client_authentication_required(request,
                                                                             *args, **kwargs)
  
      def authenticate_client(self, request, *args, **kwargs):
          """
          Check if client exists and it's authenticating itself as in rfc:`3.2.1`
  
          First we try to authenticate with HTTP Basic Auth, and that is the PREFERRED
          authentication method.
          Whether this fails we support including the client credentials in the request-body, but
          this method is NOT RECOMMENDED and SHOULD be limited to clients unable to directly utilize
          the HTTP Basic authentication scheme. See rfc:`2.3.1` for more details
          """
          authenticated = self._authenticate_basic_auth(request)
  
          if not authenticated:
              authenticated = self._authenticate_request_body(request)
  
          return authenticated
  
      def authenticate_client_id(self, client_id, request, *args, **kwargs):
          """
          If we are here, the client did not authenticate itself as in rfc:`3.2.1` and we can
          proceed only if the client exists and it's not of type 'Confidential'.
          Also assign Application instance to request.client.
          """
          if self._load_application(client_id, request) is not None:
              log.debug("Application %s has type %s" % (client_id, request.client.client_type))
              return request.client.client_type != Application.CLIENT_CONFIDENTIAL
          return False
  
      def confirm_redirect_uri(self, client_id, code, redirect_uri, client, *args, **kwargs):
          """
          Ensure the redirect_uri is listed in the Application instance redirect_uris field
          """
          grant = Grant.objects.get(code=code, application=client)
          return grant.redirect_uri_allowed(redirect_uri)
  
      def invalidate_authorization_code(self, client_id, code, request, *args, **kwargs):
          """
          Remove the temporary grant used to swap the authorization token
          """
          grant = Grant.objects.get(code=code, application=request.client)
          grant.delete()
  
      def validate_client_id(self, client_id, request, *args, **kwargs):
          """
          Ensure an Application exists with given client_id. If it exists, it's assigned to
          request.client.
          """
          return self._load_application(client_id, request) is not None
  
      def get_default_redirect_uri(self, client_id, request, *args, **kwargs):
          return request.client.default_redirect_uri
  
      def validate_bearer_token(self, token, scopes, request):
          """
          When users try to access resources, check that provided token is valid
          """
          if not token:
              return False
  
          try:
              access_token = AccessToken.objects.select_related("application").get(
                  token=token)
              if access_token.is_valid(scopes):
                  request.client = access_token.application
  
                  #request.user = access_token.user
                  request.user = find_user_by_id(access_token.user)
                  request.scopes = scopes
  
                  # this is needed by django rest framework
                  request.access_token = access_token
                  return True
              return False
          except AccessToken.DoesNotExist:
              return False
  
      def validate_code(self, client_id, code, client, request, *args, **kwargs):
          try:
              grant = Grant.objects.get(code=code, application=client)
              if not grant.is_expired():
                  request.scopes = grant.scope.split(' ')
                  request.user = find_user_by_id(grant.user)
                  return True
              return False
  
          except Grant.DoesNotExist:
              return False
  
      def validate_grant_type(self, client_id, grant_type, client, request, *args, **kwargs):
          """
          Validate both grant_type is a valid string and grant_type is allowed for current workflow
          """
          assert(grant_type in GRANT_TYPE_MAPPING)  # mapping misconfiguration
          return request.client.authorization_grant_type in GRANT_TYPE_MAPPING[grant_type]
  
      def validate_response_type(self, client_id, response_type, client, request, *args, **kwargs):
          """
          We currently do not support the Authorization Endpoint Response Types registry as in
          rfc:`8.4`, so validate the response_type only if it matches 'code' or 'token'
          """
          if response_type == 'code':
              return client.authorization_grant_type == Application.GRANT_AUTHORIZATION_CODE
          elif response_type == 'token':
              return client.authorization_grant_type == Application.GRANT_IMPLICIT
          else:
              return False
  
      def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs):
          """
          Ensure required scopes are permitted (as specified in the settings file)
          """
          return set(scopes).issubset(set(oauth2_settings._SCOPES))
  
      def get_default_scopes(self, client_id, request, *args, **kwargs):
          return oauth2_settings._SCOPES
  
      def validate_redirect_uri(self, client_id, redirect_uri, request, *args, **kwargs):
          return request.client.redirect_uri_allowed(redirect_uri)
  
      def save_authorization_code(self, client_id, code, request, *args, **kwargs):
          expires = timezone.now() + timedelta(
              seconds=oauth2_settings.AUTHORIZATION_CODE_EXPIRE_SECONDS)
          g = Grant(application=request.client, user=str(request.user.id), code=code['code'],
                    expires=expires, redirect_uri=request.redirect_uri,
                    scope=' '.join(request.scopes))
          g.save()
  
      def save_bearer_token(self, token, request, *args, **kwargs):
          """
          Save access and refresh token, If refresh token is issued, remove old refresh tokens as
          in rfc:`6`
          """
          if request.refresh_token:
              # remove used refresh token
              try:
                  RefreshToken.objects.get(token=request.refresh_token).delete()
              except RefreshToken.DoesNotExist:
                  assert()  # TODO though being here would be very strange, at least log the error
  
          expires = timezone.now() + timedelta(seconds=oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS)
          if request.grant_type == 'client_credentials':
              request.user = request.client.user
  
          access_token = AccessToken(
              user=str(request.user.id),
              scope=token['scope'],
              expires=expires,
              token=token['access_token'],
              application=request.client)
          access_token.save()
  
          if 'refresh_token' in token:
              refresh_token = RefreshToken(
                  user=str(request.user.id),
                  token=token['refresh_token'],
                  application=request.client,
                  access_token=access_token
              )
              refresh_token.save()
  
          # TODO check out a more reliable way to communicate expire time to oauthlib
          token['expires_in'] = oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS
  
      def validate_user(self, username, password, client, request, *args, **kwargs):
          """
          Check username and password correspond to a valid and active User
          """
          u = authenticate(username=username, password=password)
          if u is not None and u.is_active:
              request.user = u
              return True
          return False
  
      def get_original_scopes(self, refresh_token, request, *args, **kwargs):
          # TODO: since this method is invoked *after* validate_refresh_token, could we avoid this
          # second query for RefreshToken?
          rt = RefreshToken.objects.get(token=refresh_token)
          return rt.access_token.scope
  
      def validate_refresh_token(self, refresh_token, client, request, *args, **kwargs):
          """
          Check refresh_token exists and refers to the right client.
          Also attach User instance to the request object
          """
          try:
              rt = RefreshToken.objects.get(token=refresh_token)
              request.user = rt.user
              request.refresh_token = rt
              return rt.application == client
  
          except RefreshToken.DoesNotExist:
              return False
  