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

11
12
13
import logging
logger = logging.getLogger(__name__)

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
27
28
29
30
31
32
33

    @property
    def registration_response(self):
        info = {"client_id": self.client_id,
                "client_secret": self.client_secret}
        return RegistrationResponse(**info)

    @property
    def oidc_client(self):
        global oidc_client
34
35
36
37
38
39
        if self.id not in oidc_client:
            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)
            oidc_client[self.id] = new_oidc_client
        return oidc_client[self.id]
Lukas Burgey's avatar
Lukas Burgey committed
40
41
42
43
44

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

45
46
47
48
    @property
    def name(self):
        return self.issuer_uri

Lukas Burgey's avatar
Lukas Burgey committed
49
50
51
52
53
54
55
56
57
58
59
60
61
62
    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(
                request_args=args)
        return auth_req.request(client.authorization_endpoint)

    def __str__(self):
63
        return self.name
Lukas Burgey's avatar
Lukas Burgey committed
64
65


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):
71
72
73
74
75
76
77
78
79
    def get_user_info(self, request, access_token, token_type='Bearer'):
        try:
            idp_id = request.session['idp_id']
        except OperationalError:
            logger.error('OperationalError: Unable to get from session')
            raise

        oidc_config = OIDCConfig.objects.get(id=idp_id)
        q = Request(oidc_config.oidc_client.provider_info['userinfo_endpoint'])
Lukas Burgey's avatar
Lukas Burgey committed
80
81
82
83
84
85
86
87
88
89
        auth = (token_type + ' ' + access_token)
        q.add_header('Authorization', auth)

        userinfo_bytes = urlopen(q).read()
        return json.loads(userinfo_bytes.decode('UTF-8'))

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

90
        user_info = self.get_user_info(request, token)
Lukas Burgey's avatar
Lukas Burgey committed
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
        try:
            user = models.User.objects.get(
                    sub=user_info['sub'])

            return user
        except ObjectDoesNotExist:
            # if we do not know the user yet, we create him
            user = models.construct_user(user_info)
            user.save()
            return user

        return None

    def get_user(self, user_id):
        try:
            return models.User.objects.get(pk=user_id)
        except models.User.DoesNotExist:
            return None