1 |
c8204b73
|
Serghei Mihai
|
from oic.exception import MissingAttribute
|
2 |
|
|
from oic import oic
|
3 |
|
|
from oic.oauth2 import rndstr, ErrorResponse
|
4 |
|
|
from oic.oic import ProviderConfigurationResponse, AuthorizationResponse
|
5 |
|
|
from oic.oic import RegistrationResponse
|
6 |
|
|
from oic.oic import AuthorizationRequest
|
7 |
|
|
from oic.utils.authn.client import CLIENT_AUTHN_METHOD
|
8 |
|
|
|
9 |
|
|
import logging
|
10 |
|
|
|
11 |
|
|
logger = logging.getLogger(__name__)
|
12 |
|
|
|
13 |
b71e8531
|
Serghei MIHAI
|
import conf
|
14 |
c8204b73
|
Serghei Mihai
|
|
15 |
|
|
class OIDCError(Exception):
|
16 |
|
|
pass
|
17 |
|
|
|
18 |
|
|
|
19 |
|
|
class Client(oic.Client):
|
20 |
cdadd39b
|
Serghei MIHAI
|
def __init__(self, client_id=None, client_secret=None, ca_certs=None,
|
21 |
c8204b73
|
Serghei Mihai
|
client_prefs=None, client_authn_method=None, keyjar=None,
|
22 |
|
|
verify_ssl=True, behaviour=None):
|
23 |
cdadd39b
|
Serghei MIHAI
|
oic.Client.__init__(self, client_id, client_secret, ca_certs,
|
24 |
|
|
client_prefs, client_authn_method,
|
25 |
|
|
keyjar, verify_ssl)
|
26 |
c8204b73
|
Serghei Mihai
|
if behaviour:
|
27 |
|
|
self.behaviour = behaviour
|
28 |
|
|
|
29 |
6388360c
|
Serghei MIHAI
|
def create_authn_request(self, acr_value=None):
|
30 |
|
|
self.state = rndstr()
|
31 |
|
|
nonce = rndstr()
|
32 |
c8204b73
|
Serghei Mihai
|
request_args = {
|
33 |
|
|
"response_type": self.behaviour["response_type"],
|
34 |
|
|
"scope": self.behaviour["scope"],
|
35 |
6388360c
|
Serghei MIHAI
|
"state": self.state,
|
36 |
|
|
"nonce": nonce,
|
37 |
c8204b73
|
Serghei Mihai
|
"redirect_uri": self.registration_response["redirect_uris"][0]
|
38 |
|
|
}
|
39 |
|
|
|
40 |
|
|
if acr_value is not None:
|
41 |
|
|
request_args["acr_values"] = acr_value
|
42 |
|
|
|
43 |
|
|
cis = self.construct_AuthorizationRequest(request_args=request_args)
|
44 |
|
|
logger.debug("request: %s" % cis)
|
45 |
|
|
|
46 |
|
|
url, body, ht_args, cis = self.uri_and_body(AuthorizationRequest, cis,
|
47 |
|
|
method="GET",
|
48 |
|
|
request_args=request_args)
|
49 |
|
|
|
50 |
|
|
logger.debug("body: %s" % body)
|
51 |
|
|
logger.info("URL: %s" % url)
|
52 |
|
|
logger.debug("ht_args: %s" % ht_args)
|
53 |
|
|
|
54 |
|
|
return str(url), ht_args
|
55 |
|
|
|
56 |
|
|
def callback(self, response):
|
57 |
|
|
"""
|
58 |
|
|
This is the method that should be called when an AuthN response has been
|
59 |
|
|
received from the OP.
|
60 |
|
|
|
61 |
|
|
:param response: The URL returned by the OP
|
62 |
|
|
:return:
|
63 |
|
|
"""
|
64 |
|
|
authresp = self.parse_response(AuthorizationResponse, response,
|
65 |
|
|
sformat="dict", keyjar=self.keyjar)
|
66 |
|
|
|
67 |
6388360c
|
Serghei MIHAI
|
if self.state != authresp['state']:
|
68 |
|
|
raise OIDCError("Invalid state %s." % authresp["state"])
|
69 |
|
|
|
70 |
c8204b73
|
Serghei Mihai
|
if isinstance(authresp, ErrorResponse):
|
71 |
|
|
return OIDCError("Access denied")
|
72 |
|
|
|
73 |
|
|
try:
|
74 |
|
|
self.id_token[authresp["state"]] = authresp["id_token"]
|
75 |
|
|
except KeyError:
|
76 |
|
|
pass
|
77 |
|
|
|
78 |
|
|
if self.behaviour["response_type"] == "code":
|
79 |
|
|
# get the access token
|
80 |
|
|
try:
|
81 |
|
|
args = {
|
82 |
|
|
"grant_type": "authorization_code",
|
83 |
|
|
"code": authresp["code"],
|
84 |
|
|
"redirect_uri": self.registration_response[
|
85 |
|
|
"redirect_uris"][0],
|
86 |
|
|
"client_id": self.client_id,
|
87 |
|
|
"client_secret": self.client_secret
|
88 |
|
|
}
|
89 |
|
|
|
90 |
|
|
atresp = self.do_access_token_request(
|
91 |
|
|
scope="openid", state=authresp["state"], request_args=args,
|
92 |
|
|
authn_method=self.registration_response["token_endpoint_auth_method"])
|
93 |
|
|
except Exception as err:
|
94 |
|
|
logger.error("%s" % err)
|
95 |
|
|
raise
|
96 |
|
|
|
97 |
|
|
if isinstance(atresp, ErrorResponse):
|
98 |
|
|
raise OIDCError("Invalid response %s." % atresp["error"])
|
99 |
|
|
|
100 |
1f01e54b
|
Serghei MIHAI
|
inforesp = self.do_user_info_request(state=authresp["state"],
|
101 |
|
|
behavior='use_authorization_header')
|
102 |
c8204b73
|
Serghei Mihai
|
|
103 |
|
|
if isinstance(inforesp, ErrorResponse):
|
104 |
|
|
raise OIDCError("Invalid response %s." % inforesp["error"])
|
105 |
|
|
|
106 |
|
|
userinfo = inforesp.to_dict()
|
107 |
|
|
|
108 |
|
|
logger.debug("UserInfo: %s" % inforesp)
|
109 |
|
|
|
110 |
|
|
return userinfo
|
111 |
|
|
|
112 |
b71e8531
|
Serghei MIHAI
|
def create_client(**kwargs):
|
113 |
|
|
"""
|
114 |
|
|
kwargs = config.CLIENT.iteritems
|
115 |
|
|
"""
|
116 |
|
|
_key_set = set(kwargs.keys())
|
117 |
|
|
args = {}
|
118 |
cdadd39b
|
Serghei MIHAI
|
for param in ["verify_ssl", "client_id", "client_secret"]:
|
119 |
b71e8531
|
Serghei MIHAI
|
try:
|
120 |
|
|
args[param] = kwargs[param]
|
121 |
|
|
except KeyError:
|
122 |
aec1dadb
|
Serghei MIHAI
|
try:
|
123 |
|
|
args[param] = kwargs['client_registration'][param]
|
124 |
|
|
except KeyError:
|
125 |
|
|
pass
|
126 |
c8204b73
|
Serghei Mihai
|
else:
|
127 |
b71e8531
|
Serghei MIHAI
|
_key_set.discard(param)
|
128 |
c8204b73
|
Serghei Mihai
|
|
129 |
b71e8531
|
Serghei MIHAI
|
client = Client(client_authn_method=CLIENT_AUTHN_METHOD,
|
130 |
|
|
behaviour=kwargs["behaviour"],
|
131 |
|
|
verify_ssl=conf.VERIFY_SSL, **args)
|
132 |
c8204b73
|
Serghei Mihai
|
|
133 |
b71e8531
|
Serghei MIHAI
|
# The behaviour parameter is not significant for the election process
|
134 |
|
|
_key_set.discard("behaviour")
|
135 |
|
|
for param in ["allow"]:
|
136 |
c8204b73
|
Serghei Mihai
|
try:
|
137 |
b71e8531
|
Serghei MIHAI
|
setattr(client, param, kwargs[param])
|
138 |
c8204b73
|
Serghei Mihai
|
except KeyError:
|
139 |
b71e8531
|
Serghei MIHAI
|
pass
|
140 |
|
|
else:
|
141 |
|
|
_key_set.discard(param)
|
142 |
|
|
|
143 |
|
|
if _key_set == set(["client_info"]): # Everything dynamic
|
144 |
|
|
# There has to be a userid
|
145 |
|
|
if not userid:
|
146 |
|
|
raise MissingAttribute("Missing userid specification")
|
147 |
c8204b73
|
Serghei Mihai
|
|
148 |
b71e8531
|
Serghei MIHAI
|
# Find the service that provides information about the OP
|
149 |
|
|
issuer = client.wf.discovery_query(userid)
|
150 |
|
|
# Gather OP information
|
151 |
|
|
_ = client.provider_config(issuer)
|
152 |
|
|
# register the client
|
153 |
|
|
_ = client.register(client.provider_info["registration_endpoint"],
|
154 |
|
|
**kwargs["client_info"])
|
155 |
|
|
elif _key_set == set(["client_info", "srv_discovery_url"]):
|
156 |
|
|
# Ship the webfinger part
|
157 |
|
|
# Gather OP information
|
158 |
|
|
_ = client.provider_config(kwargs["srv_discovery_url"])
|
159 |
|
|
# register the client
|
160 |
|
|
_ = client.register(client.provider_info["registration_endpoint"],
|
161 |
|
|
**kwargs["client_info"])
|
162 |
|
|
elif _key_set == set(["provider_info", "client_info"]):
|
163 |
|
|
client.handle_provider_config(
|
164 |
|
|
ProviderConfigurationResponse(**kwargs["provider_info"]),
|
165 |
|
|
kwargs["provider_info"]["issuer"])
|
166 |
|
|
_ = client.register(client.provider_info["registration_endpoint"],
|
167 |
|
|
**kwargs["client_info"])
|
168 |
|
|
elif _key_set == set(["provider_info", "client_registration"]):
|
169 |
|
|
client.handle_provider_config(
|
170 |
|
|
ProviderConfigurationResponse(**kwargs["provider_info"]),
|
171 |
|
|
kwargs["provider_info"]["issuer"])
|
172 |
|
|
client.store_registration_info(RegistrationResponse(
|
173 |
|
|
**kwargs["client_registration"]))
|
174 |
|
|
elif _key_set == set(["srv_discovery_url", "client_registration"]):
|
175 |
|
|
_ = client.provider_config(kwargs["srv_discovery_url"])
|
176 |
|
|
client.store_registration_info(RegistrationResponse(
|
177 |
|
|
**kwargs["client_registration"]))
|
178 |
|
|
else:
|
179 |
|
|
raise Exception("Configuration error ?")
|
180 |
|
|
|
181 |
|
|
return client
|