Skip to content

Commit 85a1090

Browse files
Merge pull request #112 from c00kiemon5ter/feature-disallow-unsolicited-support
Store outstanding queries to disallow unsolicited responses
2 parents 51a59e1 + e8669fe commit 85a1090

File tree

1 file changed

+22
-4
lines changed

1 file changed

+22
-4
lines changed

src/satosa/backends/saml2.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def __init__(self, outgoing, internal_attributes, config, base_url, name):
6060
self.bindings = [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST]
6161
self.discosrv = config.get("disco_srv")
6262
self.encryption_keys = []
63+
self.outstanding_queries = {}
6364

6465
key_file_paths = None
6566
if 'encryption_keypairs' in self.config['sp_config']: # prioritize explicit encryption keypairs
@@ -120,7 +121,7 @@ def authn_request(self, context, entity_id):
120121
:type entity_id: str
121122
:rtype: satosa.response.Response
122123
123-
:param context: The curretn context
124+
:param context: The current context
124125
:param entity_id: Target IDP entity id
125126
:return: response to the user agent
126127
"""
@@ -139,6 +140,13 @@ def authn_request(self, context, entity_id):
139140
exc_info=True)
140141
raise SATOSAAuthenticationError(context.state, "Failed to construct the AuthnRequest") from exc
141142

143+
if self.sp.config.getattr('allow_unsolicited', 'sp') is False:
144+
if req_id in self.outstanding_queries:
145+
errmsg = "Request with duplicate id {}".format(req_id)
146+
satosa_logging(logger, logging.DEBUG, errmsg, context.state)
147+
raise SATOSAAuthenticationError(context.state, errmsg)
148+
self.outstanding_queries[req_id] = req
149+
142150
context.state[self.name] = {"relay_state": relay_state}
143151
return make_saml_response(binding, ht_args)
144152

@@ -158,12 +166,22 @@ def authn_response(self, context, binding):
158166
raise SATOSAAuthenticationError(context.state, "Missing Response")
159167

160168
try:
161-
authn_response = self.sp.parse_authn_request_response(context.request["SAMLResponse"], binding)
169+
authn_response = self.sp.parse_authn_request_response(
170+
context.request["SAMLResponse"],
171+
binding, outstanding=self.outstanding_queries)
162172
except Exception as err:
163173
satosa_logging(logger, logging.DEBUG, "Failed to parse authn request for state", context.state,
164174
exc_info=True)
165175
raise SATOSAAuthenticationError(context.state, "Failed to parse authn request") from err
166176

177+
if self.sp.config.getattr('allow_unsolicited', 'sp') is False:
178+
req_id = authn_response.in_response_to
179+
if req_id not in self.outstanding_queries:
180+
errmsg = "No request with id: {}".format(req_id),
181+
satosa_logging(logger, logging.DEBUG, errmsg, context.state)
182+
raise SATOSAAuthenticationError(context.state, errmsg)
183+
del self.outstanding_queries[req_id]
184+
167185
# check if the relay_state matches the cookie state
168186
if context.state[self.name]["relay_state"] != context.request["RelayState"]:
169187
satosa_logging(logger, logging.DEBUG,
@@ -218,7 +236,7 @@ def _translate_response(self, response, state):
218236

219237
internal_resp.user_id = response.get_subject().text
220238
internal_resp.attributes = self.converter.to_internal(self.attribute_profile, response.ava)
221-
239+
222240
# The SAML response may not include a NameID
223241
try:
224242
internal_resp.name_id = response.assertion.subject.name_id
@@ -324,7 +342,7 @@ def get_metadata_desc(self):
324342

325343
class SAMLInternalResponse(InternalResponse):
326344
"""
327-
Like the parent InternalResponse, holds internal representation of
345+
Like the parent InternalResponse, holds internal representation of
328346
service related data, but includes additional details relevant to
329347
SAML interoperability.
330348

0 commit comments

Comments
 (0)