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
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
32
eb9e83610c99 added logging and logs config
durandn
parents: 11
diff changeset
     1
from urllib import parse
eb9e83610c99 added logging and logs config
durandn
parents: 11
diff changeset
     2
eb9e83610c99 added logging and logs config
durandn
parents: 11
diff changeset
     3
import requests, re, json, logging
eb9e83610c99 added logging and logs config
durandn
parents: 11
diff changeset
     4
eb9e83610c99 added logging and logs config
durandn
parents: 11
diff changeset
     5
from django.conf import settings
6
39cecdd5260e Added OAuth2 Client Credentials Authentication workflow for Mtdc Application + Corrected mistakes on Authorization Code flow
durandn
parents: 1
diff changeset
     6
from django.contrib.auth import get_user_model, login
39cecdd5260e Added OAuth2 Client Credentials Authentication workflow for Mtdc Application + Corrected mistakes on Authorization Code flow
durandn
parents: 1
diff changeset
     7
from django.contrib.auth.models import Permission
32
eb9e83610c99 added logging and logs config
durandn
parents: 11
diff changeset
     8
from rest_framework.authentication import BaseAuthentication
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
     9
from rest_framework import exceptions
32
eb9e83610c99 added logging and logs config
durandn
parents: 11
diff changeset
    10
eb9e83610c99 added logging and logs config
durandn
parents: 11
diff changeset
    11
eb9e83610c99 added logging and logs config
durandn
parents: 11
diff changeset
    12
logger = logging.getLogger(__name__)
1
5f50937893ac Commit work on metaeducation
durandn
parents:
diff changeset
    13
6
39cecdd5260e Added OAuth2 Client Credentials Authentication workflow for Mtdc Application + Corrected mistakes on Authorization Code flow
durandn
parents: 1
diff changeset
    14
class MtdcOAuth2ClientCredentialsAuthentication(BaseAuthentication):
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    15
1
5f50937893ac Commit work on metaeducation
durandn
parents:
diff changeset
    16
    def authenticate(self, request):
5f50937893ac Commit work on metaeducation
durandn
parents:
diff changeset
    17
        # get token, get username
52
0f2d692a8333 Quickfix on token validation json format
durandn
parents: 47
diff changeset
    18
        logger.debug("CLIENT CREDENTIAL AUTH: request meta - %r ", request.META)
11
cfc868991b82 Added custom user model to store extra data from the GED + corrected signals and api auth class so they interface correctly with the ged
durandn
parents: 6
diff changeset
    19
        if ("act_as" not in request.GET) and ('HTTP_RENKAN_ACT_AS' not in request.META):
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    20
            logger.debug("CLIENT CREDENTIAL AUTH: no act_as, not client credentials, trying other authentication methods.")
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    21
            return None
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    22
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    23
        external_id = request.GET.get('act_as', request.META.get("HTTP_RENKAN_ACT_AS", ""))
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    24
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    25
        try:
11
cfc868991b82 Added custom user model to store extra data from the GED + corrected signals and api auth class so they interface correctly with the ged
durandn
parents: 6
diff changeset
    26
            user = get_user_model().objects.get(external_id=external_id)
6
39cecdd5260e Added OAuth2 Client Credentials Authentication workflow for Mtdc Application + Corrected mistakes on Authorization Code flow
durandn
parents: 1
diff changeset
    27
        except get_user_model().DoesNotExist:
32
eb9e83610c99 added logging and logs config
durandn
parents: 11
diff changeset
    28
            logger.debug("CLIENT CREDENTIAL AUTH: User does not exist, abort")
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    29
            raise exceptions.AuthenticationFailed('CLIENT CREDENTIAL AUTH: User does not exist, abort')
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    30
6
39cecdd5260e Added OAuth2 Client Credentials Authentication workflow for Mtdc Application + Corrected mistakes on Authorization Code flow
durandn
parents: 1
diff changeset
    31
        if 'HTTP_AUTHORIZATION' not in request.META:
40
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    32
            logger.debug("CLIENT CREDENTIAL AUTH: no Authorization header, abort")
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    33
            raise exceptions.AuthenticationFailed('CLIENT CREDENTIAL AUTH: no Authorization header, abort')
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    34
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    35
        match = re.search("(?<=Bearer\s).*", request.META["HTTP_AUTHORIZATION"])
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    36
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    37
        if match is None or len(match.group(0)) == 0:
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    38
            logger.debug("CLIENT CREDENTIAL AUTH: Authorization header format is invalid: %r", request.META["HTTP_AUTHORIZATION"])
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    39
            raise exceptions.AuthenticationFailed("CLIENT CREDENTIAL AUTH: Authorization header format is invalid")
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    40
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    41
        token = match.group(0)
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    42
        logger.debug("CLIENT CREDENTIAL AUTH: token is %r", token)
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    43
1
5f50937893ac Commit work on metaeducation
durandn
parents:
diff changeset
    44
        # send token to Oauth server
44
1b2019523772 use a new MTDC_GED_REDIRECT_URI in auth.py instead of MTDC_GED_BASE_URL
durandn
parents: 43
diff changeset
    45
        validation_service_url = settings.MTDC_VALIDATE_TOKEN_BASE_URL+token+"?redirect_uri="+parse.quote_plus(settings.MTDC_GED_REDIRECT_URI)
40
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    46
        logger.debug("CLIENT CREDENTIAL AUTH: Requesting validation service %r", validation_service_url)
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    47
40
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    48
        token_validate_response = requests.get(validation_service_url)
6
39cecdd5260e Added OAuth2 Client Credentials Authentication workflow for Mtdc Application + Corrected mistakes on Authorization Code flow
durandn
parents: 1
diff changeset
    49
        if token_validate_response.status_code != 200:
40
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    50
            logger.warning("CLIENT CREDENTIAL AUTH: Validation service didn't response with 200, there may be a problem")
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    51
            raise exceptions.AuthenticationFailed("CLIENT CREDENTIAL AUTH: Validation service didn't response with 200, there may be a problem")
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    52
43
425a2f1e08ea added error handling if token validation response is not a valid JSON string
durandn
parents: 40
diff changeset
    53
        try:
425a2f1e08ea added error handling if token validation response is not a valid JSON string
durandn
parents: 40
diff changeset
    54
            validate_response_json = json.loads(token_validate_response.text)
425a2f1e08ea added error handling if token validation response is not a valid JSON string
durandn
parents: 40
diff changeset
    55
        except json.JSONDecodeError as decode_error:
425a2f1e08ea added error handling if token validation response is not a valid JSON string
durandn
parents: 40
diff changeset
    56
            logger.warning("CLIENT CREDENTIAL AUTH: Token validate response was not a JSON!")
425a2f1e08ea added error handling if token validation response is not a valid JSON string
durandn
parents: 40
diff changeset
    57
            logger.warning("CLIENT CREDENTIAL AUTH: Tried to decode: %r", decode_error.doc)
425a2f1e08ea added error handling if token validation response is not a valid JSON string
durandn
parents: 40
diff changeset
    58
            logger.warning("CLIENT CREDENTIAL AUTH: Error message is %r", decode_error.msg)
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    59
            raise exceptions.AuthenticationFailed("CLIENT CREDENTIAL AUTH: Token validate response was not a JSON!")
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    60
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    61
40
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    62
        # Response json validation
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    63
        if "access_token" not in validate_response_json.keys():
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    64
            logger.warning("CLIENT_CREDENTIAL_AUTH: Token validate response doesn't have an access_token key!")
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    65
        elif validate_response_json["access_token"] != token:
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    66
            logger.warning("CLIENT_CREDENTIAL_AUTH: Token in response %r is different from token extracted from header %r", validate_response_json["access_token"], token)
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    67
52
0f2d692a8333 Quickfix on token validation json format
durandn
parents: 47
diff changeset
    68
        if "uriredirect" not in validate_response_json.keys():
40
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    69
            logger.warning("CLIENT_CREDENTIAL_AUTH: Token validate response doesn't have a redirect_uri key!")
57
590bf2969451 quickfix #3
durandn
parents: 54
diff changeset
    70
        elif validate_response_json["uriredirect"] != settings.MTDC_GED_REDIRECT_URI:
590bf2969451 quickfix #3
durandn
parents: 54
diff changeset
    71
            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)
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    72
52
0f2d692a8333 Quickfix on token validation json format
durandn
parents: 47
diff changeset
    73
        if "error" not in validate_response_json.keys():
40
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    74
            logger.warning("CLIENT_CREDENTIAL_AUTH: Token validate  response doesn't have an error key!")
54
0c8feb3ab516 quickfix #2
durandn
parents: 52
diff changeset
    75
            logger.warning("CLIENT_CREDENTIAL_AUTH: Aborting as the error keys is required to check if token was validated")
40
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    76
            logger.warning("CLIENT_CREDENTIAL_AUTH: Response was %r", validate_response_json)
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    77
            raise exceptions.AuthenticationFailed("CLIENT_CREDENTIAL_AUTH: Token validate  response doesn't have an error key!")
54
0c8feb3ab516 quickfix #2
durandn
parents: 52
diff changeset
    78
        elif validate_response_json["error"] != "0" :
40
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    79
            logger.debug("CLIENT CREDENTIAL AUTH: There was an error validating the token: %r", validate_response_json.get("description", "no error description in json response"))
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    80
            raise exceptions.AuthenticationFailed("CLIENT CREDENTIAL AUTH: There was an error validating the token")
40
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    81
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    82
        if "description" not in validate_response_json.keys():
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    83
            logger.warning("CLIENT_CREDENTIAL_AUTH: Token validate response doesn't have a description key!")
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    84
40
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    85
        if "scope" not in validate_response_json.keys():
10a829681179 Changes on auth.py: better token extraction and validation, better logging, fix to validate response interpretation + changes to oauth.py to properly test changes to auth.py
durandn
parents: 35
diff changeset
    86
            logger.warning("CLIENT_CREDENTIAL_AUTH: Token validate response doesn't have a scope key!")
47
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    87
b25a1526aec3 As Rest framework indicates return None if it should try other authentication methods
ymh <ymh.work@gmail.com>
parents: 44
diff changeset
    88
35
e82a0ac6cc2c used '%r' and removed '+str()' in logger.debug() calls
durandn
parents: 32
diff changeset
    89
        logger.debug("CLIENT CREDENTIAL AUTH: user %r is authenticated by token %r, auth success", external_id, token)
1
5f50937893ac Commit work on metaeducation
durandn
parents:
diff changeset
    90
        return (user, None)