models.py 3.99 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

Lukas Burgey's avatar
Lukas Burgey committed
25
26
27
    # does this idp provide us with group informations?
    group_provider = db_models.BooleanField(default=False)

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

    @property
    def oidc_client(self):
Lukas Burgey's avatar
Lukas Burgey committed
38
        # create the client object if does no yet exist
Lukas Burgey's avatar
Lukas Burgey committed
39
        if self.id not in OIDC_CLIENT:
40
41
42
            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
43
            OIDC_CLIENT[self.id] = new_oidc_client
Lukas Burgey's avatar
Lukas Burgey committed
44

Lukas Burgey's avatar
Lukas Burgey committed
45
        return OIDC_CLIENT[self.id]
Lukas Burgey's avatar
Lukas Burgey committed
46
47
48
49
50

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

Lukas Burgey's avatar
Lukas Burgey committed
51
52
    def __str__(self):
        return self.name
53

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

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

Lukas Burgey's avatar
Lukas Burgey committed
68
69
70
71
72
73
74
75
    def get_user_groupinformation(self, userinfo):
        if not self.group_provider:
            return models.AuthGroup.objects.none()

        LOGGER.debug('Retrieving group information for %s', userinfo)
        # TODO actually retrieve the group information
        return models.AuthGroup.objects.none()

Lukas Burgey's avatar
Lukas Burgey committed
76

77
78
def default_idp():
    return OIDCConfig.objects.filter(enabled=True).first()
Lukas Burgey's avatar
Lukas Burgey committed
79
80
81


class OIDCTokenAuthBackend(object):
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
    def get_userinfo(self, request, oidc_client, access_token='', state=None):
        user_info = None

        if access_token is not None:
            req = Request(
                oidc_client.provider_info['userinfo_endpoint']+'?scope=openid&scope=profile',
            )
            auth = ('Bearer ' + access_token)
            req.add_header('Authorization', auth)

            userinfo_bytes = urlopen(req).read()
            user_info = json.loads(userinfo_bytes.decode('UTF-8'))

        else:
            LOGGER.error("Invalid parameters for get_userinfo")
Lukas Burgey's avatar
Lukas Burgey committed
97

98
99
100

        LOGGER.debug("Got user info:\n%s\n", user_info)
        return user_info
Lukas Burgey's avatar
Lukas Burgey committed
101
102
103
104
105

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

Lukas Burgey's avatar
Lukas Burgey committed
106
        idp_id = utils.get_session(request, 'idp_id', None)
107
108
109
110
111
112
113
114
        oidc_client = OIDCConfig.objects.get(id=idp_id)

        # get the user info from the idp
        userinfo = self.get_userinfo(
            request,
            oidc_client,
            access_token=token,
        )
Lukas Burgey's avatar
Lukas Burgey committed
115

Lukas Burgey's avatar
Lukas Burgey committed
116
        try:
Lukas Burgey's avatar
Lukas Burgey committed
117
118
            return models.User.get_user(
                userinfo,
119
                oidc_client,
Lukas Burgey's avatar
Lukas Burgey committed
120
            )
Lukas Burgey's avatar
Lukas Burgey committed
121
122
123
        except Exception as exception:
            LOGGER.error('OIDCTokenAuthBackend: error constructing user: %s', exception)
            return None
Lukas Burgey's avatar
Lukas Burgey committed
124
125
126

    def get_user(self, user_id):
        try:
Lukas Burgey's avatar
Lukas Burgey committed
127
            return models.User.objects.get(
Lukas Burgey's avatar
Lukas Burgey committed
128
                user_type='oidcuser',
Lukas Burgey's avatar
Lukas Burgey committed
129
130
131
                pk=user_id
            )
        except ObjectDoesNotExist:
Lukas Burgey's avatar
Lukas Burgey committed
132
            return None