|
6 | 6 | import json
|
7 | 7 | import logging
|
8 | 8 | from base64 import urlsafe_b64decode
|
| 9 | +from base64 import urlsafe_b64encode |
| 10 | +from urllib.parse import quote |
| 11 | +from urllib.parse import unquote |
9 | 12 | from urllib.parse import urlparse
|
| 13 | +from http.cookies import SimpleCookie |
10 | 14 |
|
11 | 15 | from saml2 import SAMLError, xmldsig
|
12 | 16 | from saml2.config import IdPConfig
|
@@ -359,6 +363,12 @@ def _handle_authn_response(self, context, internal_response, idp):
|
359 | 363 | http_args = idp.apply_binding(
|
360 | 364 | resp_args["binding"], str(resp), resp_args["destination"],
|
361 | 365 | request_state["relay_state"], response=True)
|
| 366 | + |
| 367 | + # Set the common domain cookie _saml_idp if so configured. |
| 368 | + if 'common_domain_cookie' in self.config: |
| 369 | + if self.config['common_domain_cookie']: |
| 370 | + self._set_common_domain_cookie(internal_response, http_args, context) |
| 371 | + |
362 | 372 | del context.state[self.name]
|
363 | 373 | return make_saml_response(resp_args["binding"], http_args)
|
364 | 374 |
|
@@ -427,6 +437,62 @@ def _register_endpoints(self, providers):
|
427 | 437 |
|
428 | 438 | return url_map
|
429 | 439 |
|
| 440 | + def _set_common_domain_cookie(self, internal_response, http_args, context): |
| 441 | + """ |
| 442 | + """ |
| 443 | + # Find any existing common domain cookie and deconsruct it to |
| 444 | + # obtain the list of IdPs. |
| 445 | + cookie = SimpleCookie(context.cookie) |
| 446 | + if '_saml_idp' in cookie: |
| 447 | + common_domain_cookie = cookie['_saml_idp'] |
| 448 | + satosa_logging(logger, logging.DEBUG, "Found existing common domain cookie {}".format(common_domain_cookie), context.state) |
| 449 | + space_separated_b64_idp_string = unquote(common_domain_cookie.value) |
| 450 | + b64_idp_list = space_separated_b64_idp_string.split() |
| 451 | + idp_list = [urlsafe_b64decode(b64_idp).decode('utf-8') for b64_idp in b64_idp_list] |
| 452 | + else: |
| 453 | + satosa_logging(logger, logging.DEBUG, "No existing common domain cookie found", context.state) |
| 454 | + idp_list = [] |
| 455 | + |
| 456 | + satosa_logging(logger, logging.DEBUG, "Common domain cookie list of IdPs is {}".format(idp_list), context.state) |
| 457 | + |
| 458 | + # Identity the current IdP just used for authentication in this flow. |
| 459 | + this_flow_idp = internal_response.to_dict()['auth_info']['issuer'] |
| 460 | + |
| 461 | + # Remove all occurrences of the current IdP from the list of IdPs. |
| 462 | + idp_list = [idp for idp in idp_list if idp != this_flow_idp] |
| 463 | + |
| 464 | + # Append the current IdP. |
| 465 | + idp_list.append(this_flow_idp) |
| 466 | + satosa_logging(logger, logging.DEBUG, "Added IdP {} to common domain cookie list of IdPs".format(this_flow_idp), context.state) |
| 467 | + satosa_logging(logger, logging.DEBUG, "Common domain cookie list of IdPs is now {}".format(idp_list), context.state) |
| 468 | + |
| 469 | + # Construct the cookie. |
| 470 | + b64_idp_list = [urlsafe_b64encode(idp.encode()).decode("utf-8") for idp in idp_list] |
| 471 | + space_separated_b64_idp_string = " ".join(b64_idp_list) |
| 472 | + url_encoded_space_separated_b64_idp_string = quote(space_separated_b64_idp_string) |
| 473 | + |
| 474 | + cookie = SimpleCookie() |
| 475 | + cookie['_saml_idp'] = url_encoded_space_separated_b64_idp_string |
| 476 | + cookie['_saml_idp']['path'] = '/' |
| 477 | + |
| 478 | + # Use the domain from configuration if present else use the domain |
| 479 | + # from the base URL for the front end. |
| 480 | + domain = urlparse(self.base_url).netloc |
| 481 | + if isinstance(self.config['common_domain_cookie'], dict): |
| 482 | + if 'domain' in self.config['common_domain_cookie']: |
| 483 | + domain = self.config['common_domain_cookie']['domain'] |
| 484 | + |
| 485 | + # Ensure that the domain begins with a '.' |
| 486 | + if domain[0] != '.': |
| 487 | + domain = '.' + domain |
| 488 | + |
| 489 | + cookie['_saml_idp']['domain'] = domain |
| 490 | + cookie['_saml_idp']['secure'] = True |
| 491 | + |
| 492 | + # Set the cookie. |
| 493 | + satosa_logging(logger, logging.DEBUG, "Setting common domain cookie with {}".format(cookie.output()), context.state) |
| 494 | + http_args['headers'].append(tuple(cookie.output().split(": ", 1))) |
| 495 | + |
430 | 496 | def _build_idp_config_endpoints(self, config, providers):
|
431 | 497 | """
|
432 | 498 | Builds the final frontend module config
|
|
0 commit comments