models.py 3.3 KB
Newer Older
Lukas Burgey's avatar
Lukas Burgey committed
1
2
3
import logging
import json
from urllib.request import Request, urlopen
4
from django.core.exceptions import ObjectDoesNotExist
Lukas Burgey's avatar
Lukas Burgey committed
5
6
7
from django.db import models as db_models
from oic.oic import Client
from oic.oic.message import RegistrationResponse
8
from oic.utils.authn.client import CLIENT_AUTHN_METHOD
Lukas Burgey's avatar
Lukas Burgey committed
9
from ... import models
Lukas Burgey's avatar
Lukas Burgey committed
10
from . import utils
Lukas Burgey's avatar
Lukas Burgey committed
11

Lukas Burgey's avatar
Lukas Burgey committed
12
LOGGER = logging.getLogger(__name__)
13

Lukas Burgey's avatar
Lukas Burgey committed
14
OIDC_CLIENT = {}
Lukas Burgey's avatar
Lukas Burgey committed
15
16
17
18
19
20
21


class OIDCConfig(db_models.Model):
    client_id = db_models.CharField(max_length=200)
    client_secret = db_models.CharField(max_length=200)
    redirect_uri = db_models.CharField(max_length=200)
    issuer_uri = db_models.CharField(max_length=200)
22
23
    enabled = db_models.BooleanField(default=False)
    name = db_models.CharField(max_length=200)
Lukas Burgey's avatar
Lukas Burgey committed
24
25
26

    @property
    def registration_response(self):
Lukas Burgey's avatar
Lukas Burgey committed
27
28
29
30
        info = {
            'client_id': self.client_id,
            'client_secret': self.client_secret
        }
Lukas Burgey's avatar
Lukas Burgey committed
31
32
33
34
        return RegistrationResponse(**info)

    @property
    def oidc_client(self):
Lukas Burgey's avatar
Lukas Burgey committed
35
        # create the client object if does no yet exist
Lukas Burgey's avatar
Lukas Burgey committed
36
        if self.id not in OIDC_CLIENT:
37
38
39
            new_oidc_client = Client(client_authn_method=CLIENT_AUTHN_METHOD)
            new_oidc_client.provider_config(self.issuer_uri)
            new_oidc_client.store_registration_info(self.registration_response)
Lukas Burgey's avatar
Lukas Burgey committed
40
            OIDC_CLIENT[self.id] = new_oidc_client
Lukas Burgey's avatar
Lukas Burgey committed
41

Lukas Burgey's avatar
Lukas Burgey committed
42
        return OIDC_CLIENT[self.id]
Lukas Burgey's avatar
Lukas Burgey committed
43
44
45
46
47

    @property
    def provider_info(self):
        return self.oidc_client.provider_info

Lukas Burgey's avatar
Lukas Burgey committed
48
49
    def __str__(self):
        return self.name
50

Lukas Burgey's avatar
Lukas Burgey committed
51
52
53
54
55
56
57
58
59
60
    def get_auth_request(self, client, state):
        args = {
            'client_id': self.client_id,
            'response_type': 'code',
            'scope': ['openid', 'profile', 'email'],
            'redirect_uri': self.redirect_uri,
            'state': state,
        }

        auth_req = client.construct_AuthorizationRequest(
Lukas Burgey's avatar
Lukas Burgey committed
61
62
            request_args=args
        )
Lukas Burgey's avatar
Lukas Burgey committed
63
64
65
        return auth_req.request(client.authorization_endpoint)


66
67
def default_idp():
    return OIDCConfig.objects.filter(enabled=True).first()
Lukas Burgey's avatar
Lukas Burgey committed
68
69
70


class OIDCTokenAuthBackend(object):
Lukas Burgey's avatar
Lukas Burgey committed
71
    def get_userinfo(self, request, access_token, token_type='Bearer'):
Lukas Burgey's avatar
Lukas Burgey committed
72
        idp_id = utils.get_session(request, 'idp_id', None)
Lukas Burgey's avatar
Lukas Burgey committed
73
        idp = OIDCConfig.objects.get(id=idp_id)
Lukas Burgey's avatar
Lukas Burgey committed
74
        req = Request(
Lukas Burgey's avatar
Lukas Burgey committed
75
            idp.oidc_client.provider_info['userinfo_endpoint']
Lukas Burgey's avatar
Lukas Burgey committed
76
        )
Lukas Burgey's avatar
Lukas Burgey committed
77
        auth = (token_type + ' ' + access_token)
Lukas Burgey's avatar
Lukas Burgey committed
78
        req.add_header('Authorization', auth)
Lukas Burgey's avatar
Lukas Burgey committed
79

Lukas Burgey's avatar
Lukas Burgey committed
80
        userinfo_bytes = urlopen(req).read()
Lukas Burgey's avatar
Lukas Burgey committed
81
82
83
84
85
86
        return json.loads(userinfo_bytes.decode('UTF-8'))

    def authenticate(self, request, token=None):
        if token is None:
            return None

Lukas Burgey's avatar
Lukas Burgey committed
87
        # get the user info from the idp
Lukas Burgey's avatar
Lukas Burgey committed
88
        userinfo = self.get_userinfo(request, token)
Lukas Burgey's avatar
Lukas Burgey committed
89
        idp_id = utils.get_session(request, 'idp_id', None)
Lukas Burgey's avatar
Lukas Burgey committed
90
        idp = OIDCConfig.objects.get(id=idp_id)
Lukas Burgey's avatar
Lukas Burgey committed
91

Lukas Burgey's avatar
Lukas Burgey committed
92
        try:
Lukas Burgey's avatar
Lukas Burgey committed
93
94
95
            return models.User.get_user(
                userinfo,
                idp,
Lukas Burgey's avatar
Lukas Burgey committed
96
            )
Lukas Burgey's avatar
Lukas Burgey committed
97
98
99
100

        except Exception as exception:
            LOGGER.error('OIDCTokenAuthBackend: error constructing user: %s', exception)
            return None
Lukas Burgey's avatar
Lukas Burgey committed
101
102
103

    def get_user(self, user_id):
        try:
Lukas Burgey's avatar
Lukas Burgey committed
104
            return models.User.objects.get(
Lukas Burgey's avatar
Lukas Burgey committed
105
                user_type='oidcuser',
Lukas Burgey's avatar
Lukas Burgey committed
106
107
108
                pk=user_id
            )
        except ObjectDoesNotExist:
Lukas Burgey's avatar
Lukas Burgey committed
109
            return None