Projet

Général

Profil

Télécharger (8,33 ko) Statistiques
| Branche: | Tag: | Révision:

oidc / ckanext / ozwillo_pyoidc / oidc.py @ 1f01e54b

1
from oic.utils.http_util import Redirect
2
from oic.exception import MissingAttribute
3
from oic import oic
4
from oic.oauth2 import rndstr, ErrorResponse
5
from oic.oic import ProviderConfigurationResponse, AuthorizationResponse
6
from oic.oic import RegistrationResponse
7
from oic.oic import AuthorizationRequest
8
from oic.utils.authn.client import CLIENT_AUTHN_METHOD
9

    
10
__author__ = 'roland'
11

    
12
import logging
13

    
14
logger = logging.getLogger(__name__)
15

    
16

    
17
class OIDCError(Exception):
18
    pass
19

    
20

    
21
class Client(oic.Client):
22
    def __init__(self, client_id=None, ca_certs=None,
23
                 client_prefs=None, client_authn_method=None, keyjar=None,
24
                 verify_ssl=True, behaviour=None):
25
        oic.Client.__init__(self, client_id, ca_certs, client_prefs,
26
                            client_authn_method, keyjar, verify_ssl)
27
        if behaviour:
28
            self.behaviour = behaviour
29

    
30
    def create_authn_request(self, session, acr_value=None):
31
        session["state"] = rndstr()
32
        session["nonce"] = rndstr()
33
        request_args = {
34
            "response_type": self.behaviour["response_type"],
35
            "scope": self.behaviour["scope"],
36
            "state": session["state"],
37
            # "nonce": session["nonce"],
38
            "redirect_uri": self.registration_response["redirect_uris"][0]
39
        }
40

    
41
        if acr_value is not None:
42
            request_args["acr_values"] = acr_value
43

    
44
        cis = self.construct_AuthorizationRequest(request_args=request_args)
45
        logger.debug("request: %s" % cis)
46

    
47
        url, body, ht_args, cis = self.uri_and_body(AuthorizationRequest, cis,
48
                                                    method="GET",
49
                                                    request_args=request_args)
50

    
51
        logger.debug("body: %s" % body)
52
        logger.info("URL: %s" % url)
53
        logger.debug("ht_args: %s" % ht_args)
54

    
55
        return str(url), ht_args
56

    
57
    def callback(self, response):
58
        """
59
        This is the method that should be called when an AuthN response has been
60
        received from the OP.
61

    
62
        :param response: The URL returned by the OP
63
        :return:
64
        """
65
        authresp = self.parse_response(AuthorizationResponse, response,
66
                                       sformat="dict", keyjar=self.keyjar)
67

    
68
        if isinstance(authresp, ErrorResponse):
69
            return OIDCError("Access denied")
70

    
71
        try:
72
            self.id_token[authresp["state"]] = authresp["id_token"]
73
        except KeyError:
74
            pass
75

    
76
        if self.behaviour["response_type"] == "code":
77
            # get the access token
78
            try:
79
                args = {
80
                    "grant_type": "authorization_code",
81
                    "code": authresp["code"],
82
                    "redirect_uri": self.registration_response[
83
                        "redirect_uris"][0],
84
                    "client_id": self.client_id,
85
                    "client_secret": self.client_secret
86
                }
87

    
88
                atresp = self.do_access_token_request(
89
                    scope="openid", state=authresp["state"], request_args=args,
90
                    authn_method=self.registration_response["token_endpoint_auth_method"])
91
            except Exception as err:
92
                logger.error("%s" % err)
93
                raise
94

    
95
            if isinstance(atresp, ErrorResponse):
96
                raise OIDCError("Invalid response %s." % atresp["error"])
97

    
98
        inforesp = self.do_user_info_request(state=authresp["state"],
99
                                             behavior='use_authorization_header')
100

    
101
        if isinstance(inforesp, ErrorResponse):
102
            raise OIDCError("Invalid response %s." % inforesp["error"])
103

    
104
        userinfo = inforesp.to_dict()
105

    
106
        logger.debug("UserInfo: %s" % inforesp)
107

    
108
        return userinfo
109

    
110

    
111
class OIDCClients(object):
112
    def __init__(self, config):
113
        """
114

    
115
        :param config: Imported configuration module
116
        :return:
117
        """
118
        self.client = {}
119
        self.client_cls = Client
120
        self.config = config
121

    
122
        for key, val in config.CLIENTS.items():
123
            if key == "":
124
                continue
125
            else:
126
                self.client[key] = self.create_client(**val)
127

    
128
    def create_client(self, userid="", **kwargs):
129
        """
130
        Do an instantiation of a client instance
131

    
132
        :param userid: An identifier of the user
133
        :param: Keyword arguments
134
            Keys are ["srv_discovery_url", "client_info", "client_registration",
135
            "provider_info"]
136
        :return: client instance
137
        """
138

    
139
        _key_set = set(kwargs.keys())
140
        args = {}
141
        for param in ["verify_ssl"]:
142
            try:
143
                args[param] = kwargs[param]
144
            except KeyError:
145
                pass
146
            else:
147
                _key_set.discard(param)
148

    
149
        client = self.client_cls(client_authn_method=CLIENT_AUTHN_METHOD,
150
                                 behaviour=kwargs["behaviour"], verify_ssl=self.config.VERIFY_SSL, **args)
151

    
152
        # The behaviour parameter is not significant for the election process
153
        _key_set.discard("behaviour")
154
        for param in ["allow"]:
155
            try:
156
                setattr(client, param, kwargs[param])
157
            except KeyError:
158
                pass
159
            else:
160
                _key_set.discard(param)
161

    
162
        if _key_set == set(["client_info"]):  # Everything dynamic
163
            # There has to be a userid
164
            if not userid:
165
                raise MissingAttribute("Missing userid specification")
166

    
167
            # Find the service that provides information about the OP
168
            issuer = client.wf.discovery_query(userid)
169
            # Gather OP information
170
            _ = client.provider_config(issuer)
171
            # register the client
172
            _ = client.register(client.provider_info["registration_endpoint"],
173
                                **kwargs["client_info"])
174
        elif _key_set == set(["client_info", "srv_discovery_url"]):
175
            # Ship the webfinger part
176
            # Gather OP information
177
            _ = client.provider_config(kwargs["srv_discovery_url"])
178
            # register the client
179
            _ = client.register(client.provider_info["registration_endpoint"],
180
                                **kwargs["client_info"])
181
        elif _key_set == set(["provider_info", "client_info"]):
182
            client.handle_provider_config(
183
                ProviderConfigurationResponse(**kwargs["provider_info"]),
184
                kwargs["provider_info"]["issuer"])
185
            _ = client.register(client.provider_info["registration_endpoint"],
186
                                **kwargs["client_info"])
187
        elif _key_set == set(["provider_info", "client_registration"]):
188
            client.handle_provider_config(
189
                ProviderConfigurationResponse(**kwargs["provider_info"]),
190
                kwargs["provider_info"]["issuer"])
191
            client.store_registration_info(RegistrationResponse(
192
                **kwargs["client_registration"]))
193
        elif _key_set == set(["srv_discovery_url", "client_registration"]):
194
            _ = client.provider_config(kwargs["srv_discovery_url"])
195
            client.store_registration_info(RegistrationResponse(
196
                **kwargs["client_registration"]))
197
        else:
198
            raise Exception("Configuration error ?")
199

    
200
        return client
201

    
202
    def dynamic_client(self, userid):
203
        client = self.client_cls(client_authn_method=CLIENT_AUTHN_METHOD,
204
                                 verify_ssl=self.config.VERIFY_SSL)
205

    
206
        issuer = client.wf.discovery_query(userid)
207
        if issuer in self.client:
208
            return self.client[issuer]
209
        else:
210
            # Gather OP information
211
            _pcr = client.provider_config(issuer)
212
            # register the client
213
            _ = client.register(_pcr["registration_endpoint"],
214
                                **self.config.CLIENTS[""]["client_info"])
215
            try:
216
                client.behaviour.update(**self.config.CLIENTS[""]["behaviour"])
217
            except KeyError:
218
                pass
219

    
220
            self.client[issuer] = client
221
            return client
222

    
223
    def __getitem__(self, item):
224
        """
225
        Given a service or user identifier return a suitable client
226
        :param item:
227
        :return:
228
        """
229
        try:
230
            return self.client[item]
231
        except KeyError:
232
            return self.dynamic_client(item)
233

    
234
    def keys(self):
235
        return self.client.keys()
(3-3/4)