server/src/metaeducation/auth.py
author durandn
Thu, 21 Apr 2016 11:27:06 +0200
changeset 57 590bf2969451
parent 54 0c8feb3ab516
child 81 709541e05240
permissions -rw-r--r--
quickfix #3

from urllib import parse

import requests, re, json, logging

from django.conf import settings
from django.contrib.auth import get_user_model, login
from django.contrib.auth.models import Permission
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions


logger = logging.getLogger(__name__)

class MtdcOAuth2ClientCredentialsAuthentication(BaseAuthentication):

    def authenticate(self, request):
        # get token, get username
        logger.debug("CLIENT CREDENTIAL AUTH: request meta - %r ", request.META)
        if ("act_as" not in request.GET) and ('HTTP_RENKAN_ACT_AS' not in request.META):
            logger.debug("CLIENT CREDENTIAL AUTH: no act_as, not client credentials, trying other authentication methods.")
            return None

        external_id = request.GET.get('act_as', request.META.get("HTTP_RENKAN_ACT_AS", ""))

        try:
            user = get_user_model().objects.get(external_id=external_id)
        except get_user_model().DoesNotExist:
            logger.debug("CLIENT CREDENTIAL AUTH: User does not exist, abort")
            raise exceptions.AuthenticationFailed('CLIENT CREDENTIAL AUTH: User does not exist, abort')

        if 'HTTP_AUTHORIZATION' not in request.META:
            logger.debug("CLIENT CREDENTIAL AUTH: no Authorization header, abort")
            raise exceptions.AuthenticationFailed('CLIENT CREDENTIAL AUTH: no Authorization header, abort')

        match = re.search("(?<=Bearer\s).*", request.META["HTTP_AUTHORIZATION"])

        if match is None or len(match.group(0)) == 0:
            logger.debug("CLIENT CREDENTIAL AUTH: Authorization header format is invalid: %r", request.META["HTTP_AUTHORIZATION"])
            raise exceptions.AuthenticationFailed("CLIENT CREDENTIAL AUTH: Authorization header format is invalid")

        token = match.group(0)
        logger.debug("CLIENT CREDENTIAL AUTH: token is %r", token)

        # send token to Oauth server
        validation_service_url = settings.MTDC_VALIDATE_TOKEN_BASE_URL+token+"?redirect_uri="+parse.quote_plus(settings.MTDC_GED_REDIRECT_URI)
        logger.debug("CLIENT CREDENTIAL AUTH: Requesting validation service %r", validation_service_url)

        token_validate_response = requests.get(validation_service_url)
        if token_validate_response.status_code != 200:
            logger.warning("CLIENT CREDENTIAL AUTH: Validation service didn't response with 200, there may be a problem")
            raise exceptions.AuthenticationFailed("CLIENT CREDENTIAL AUTH: Validation service didn't response with 200, there may be a problem")

        try:
            validate_response_json = json.loads(token_validate_response.text)
        except json.JSONDecodeError as decode_error:
            logger.warning("CLIENT CREDENTIAL AUTH: Token validate response was not a JSON!")
            logger.warning("CLIENT CREDENTIAL AUTH: Tried to decode: %r", decode_error.doc)
            logger.warning("CLIENT CREDENTIAL AUTH: Error message is %r", decode_error.msg)
            raise exceptions.AuthenticationFailed("CLIENT CREDENTIAL AUTH: Token validate response was not a JSON!")


        # Response json validation
        if "access_token" not in validate_response_json.keys():
            logger.warning("CLIENT_CREDENTIAL_AUTH: Token validate response doesn't have an access_token key!")
        elif validate_response_json["access_token"] != token:
            logger.warning("CLIENT_CREDENTIAL_AUTH: Token in response %r is different from token extracted from header %r", validate_response_json["access_token"], token)

        if "uriredirect" not in validate_response_json.keys():
            logger.warning("CLIENT_CREDENTIAL_AUTH: Token validate response doesn't have a redirect_uri key!")
        elif validate_response_json["uriredirect"] != settings.MTDC_GED_REDIRECT_URI:
            logger.warning("CLIENT_CREDENTIAL_AUTH: redirect_uri in response %r is different from redirect_uri transmitted in request %r", validate_response_json["uriredirect"], settings.MTDC_GED_REDIRECT_URI)

        if "error" not in validate_response_json.keys():
            logger.warning("CLIENT_CREDENTIAL_AUTH: Token validate  response doesn't have an error key!")
            logger.warning("CLIENT_CREDENTIAL_AUTH: Aborting as the error keys is required to check if token was validated")
            logger.warning("CLIENT_CREDENTIAL_AUTH: Response was %r", validate_response_json)
            raise exceptions.AuthenticationFailed("CLIENT_CREDENTIAL_AUTH: Token validate  response doesn't have an error key!")
        elif validate_response_json["error"] != "0" :
            logger.debug("CLIENT CREDENTIAL AUTH: There was an error validating the token: %r", validate_response_json.get("description", "no error description in json response"))
            raise exceptions.AuthenticationFailed("CLIENT CREDENTIAL AUTH: There was an error validating the token")

        if "description" not in validate_response_json.keys():
            logger.warning("CLIENT_CREDENTIAL_AUTH: Token validate response doesn't have a description key!")

        if "scope" not in validate_response_json.keys():
            logger.warning("CLIENT_CREDENTIAL_AUTH: Token validate response doesn't have a scope key!")


        logger.debug("CLIENT CREDENTIAL AUTH: user %r is authenticated by token %r, auth success", external_id, token)
        return (user, None)