def test_request_full_signed(self): """Test full SAML Request/Response flow, fully signed""" http_request = self.factory.get("/") http_request.user = get_anonymous_user() middleware = SessionMiddleware(dummy_get_response) middleware.process_request(http_request) http_request.session.save() # First create an AuthNRequest request_proc = RequestProcessor(self.source, http_request, "test_state") request = request_proc.build_auth_n() # To get an assertion we need a parsed request (parsed by provider) parsed_request = AuthNRequestParser(self.provider).parse( b64encode(request.encode()).decode(), "test_state") # Now create a response and convert it to string (provider) response_proc = AssertionProcessor(self.provider, http_request, parsed_request) response = response_proc.build_response() # Now parse the response (source) http_request.POST = QueryDict(mutable=True) http_request.POST["SAMLResponse"] = b64encode( response.encode()).decode() response_parser = ResponseProcessor(self.source) response_parser.parse(http_request)
def test_request_id_invalid(self): """Test generated AuthNRequest with invalid request ID""" http_request = self.factory.get("/") http_request.user = get_anonymous_user() middleware = SessionMiddleware(dummy_get_response) middleware.process_request(http_request) http_request.session.save() # First create an AuthNRequest request_proc = RequestProcessor(self.source, http_request, "test_state") request = request_proc.build_auth_n() # change the request ID http_request.session[SESSION_REQUEST_ID] = "test" http_request.session.save() # To get an assertion we need a parsed request (parsed by provider) parsed_request = AuthNRequestParser(self.provider).parse( b64encode(request.encode()).decode(), "test_state") # Now create a response and convert it to string (provider) response_proc = AssertionProcessor(self.provider, http_request, parsed_request) response = response_proc.build_response() # Now parse the response (source) http_request.POST = QueryDict(mutable=True) http_request.POST["SAMLResponse"] = b64encode( response.encode()).decode() response_parser = ResponseProcessor(self.source) with self.assertRaises(MismatchedRequestID): response_parser.parse(http_request)
def check_saml_request(self) -> Optional[HttpRequest]: """Create SAML Response from scratch""" LOGGER.debug( "handle_saml_no_request: No SAML Request, using IdP-initiated flow." ) auth_n_request = AuthNRequestParser(self.provider).idp_initiated() self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request
def test_response_schema(self): """Test generated AuthNRequest against Schema""" http_request = self.factory.get("/") http_request.user = get_anonymous_user() middleware = SessionMiddleware(dummy_get_response) middleware.process_request(http_request) http_request.session.save() # First create an AuthNRequest request_proc = RequestProcessor(self.source, http_request, "test_state") request = request_proc.build_auth_n() # To get an assertion we need a parsed request (parsed by provider) parsed_request = AuthNRequestParser(self.provider).parse( b64encode(request.encode()).decode(), "test_state") # Now create a response and convert it to string (provider) response_proc = AssertionProcessor(self.provider, http_request, parsed_request) response = response_proc.build_response() metadata = etree.fromstring(response) # nosec schema = etree.XMLSchema( etree.parse("xml/saml-schema-protocol-2.0.xsd")) self.assertTrue(schema.validate(metadata))
def test_request_attributes_invalid(self): """Test full SAML Request/Response flow, fully signed""" user = create_test_admin_user() http_request = get_request("/", user=user) # First create an AuthNRequest request_proc = RequestProcessor(self.source, http_request, "test_state") request = request_proc.build_auth_n() # Create invalid PropertyMapping scope = SAMLPropertyMapping.objects.create(name="test", saml_name="test", expression="q") self.provider.property_mappings.add(scope) # To get an assertion we need a parsed request (parsed by provider) parsed_request = AuthNRequestParser(self.provider).parse( b64encode(request.encode()).decode(), "test_state") # Now create a response and convert it to string (provider) response_proc = AssertionProcessor(self.provider, http_request, parsed_request) self.assertIn(user.username, response_proc.build_response()) events = Event.objects.filter(action=EventAction.CONFIGURATION_ERROR, ) self.assertTrue(events.exists()) self.assertEqual( events.first().context["message"], "Failed to evaluate property-mapping: name 'q' is not defined", )
def test_signed_valid(self): """Test generated AuthNRequest with valid signature""" http_request = get_request("/") # First create an AuthNRequest request_proc = RequestProcessor(self.source, http_request, "test_state") request = request_proc.build_auth_n() # Now we check the ID and signature parsed_request = AuthNRequestParser(self.provider).parse( b64encode(request.encode()).decode(), "test_state") self.assertEqual(parsed_request.id, request_proc.request_id) self.assertEqual(parsed_request.relay_state, "test_state")
def test_signed_static(self): """Test post request with static request""" provider = SAMLProvider( name="aws", authorization_flow=create_test_flow(), acs_url=("https://eu-central-1.signin.aws.amazon.com/platform/" "saml/acs/2d737f96-55fb-4035-953e-5e24134eb778"), audience="https://10.120.20.200/saml-sp/SAML2/POST", issuer="https://10.120.20.200/saml-sp/SAML2/POST", signing_kp=create_test_cert(), ) parsed_request = AuthNRequestParser(provider).parse(POST_REQUEST) self.assertEqual(parsed_request.id, "aws_LDxLGeubpc5lx12gxCgS6uPbix1yd5re") self.assertEqual(parsed_request.name_id_policy, SAML_NAME_ID_FORMAT_EMAIL)
def test_signed_valid(self): """Test generated AuthNRequest with valid signature""" http_request = self.factory.get("/") middleware = SessionMiddleware(dummy_get_response) middleware.process_request(http_request) http_request.session.save() # First create an AuthNRequest request_proc = RequestProcessor(self.source, http_request, "test_state") request = request_proc.build_auth_n() # Now we check the ID and signature parsed_request = AuthNRequestParser(self.provider).parse( b64encode(request.encode()).decode(), "test_state") self.assertEqual(parsed_request.id, request_proc.request_id) self.assertEqual(parsed_request.relay_state, "test_state")
def check_saml_request(self) -> Optional[HttpRequest]: """Handle POST bindings""" if REQUEST_KEY_SAML_REQUEST not in self.request.POST: LOGGER.info("check_saml_request: SAML payload missing") return bad_request_message(self.request, "The SAML request payload is missing.") try: auth_n_request = AuthNRequestParser(self.provider).parse( self.request.POST[REQUEST_KEY_SAML_REQUEST], self.request.POST.get(REQUEST_KEY_RELAY_STATE), ) self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request except CannotHandleAssertion as exc: LOGGER.info(str(exc)) return bad_request_message(self.request, str(exc)) return None
def test_request_attributes(self): """Test full SAML Request/Response flow, fully signed""" user = create_test_admin_user() http_request = get_request("/", user=user) # First create an AuthNRequest request_proc = RequestProcessor(self.source, http_request, "test_state") request = request_proc.build_auth_n() # To get an assertion we need a parsed request (parsed by provider) parsed_request = AuthNRequestParser(self.provider).parse( b64encode(request.encode()).decode(), "test_state") # Now create a response and convert it to string (provider) response_proc = AssertionProcessor(self.provider, http_request, parsed_request) self.assertIn(user.username, response_proc.build_response())
def test_signed_valid_detached(self): """Test generated AuthNRequest with valid signature (detached)""" http_request = get_request("/") # First create an AuthNRequest request_proc = RequestProcessor(self.source, http_request, "test_state") params = request_proc.build_auth_n_detached() # Now we check the ID and signature parsed_request = AuthNRequestParser(self.provider).parse_detached( params["SAMLRequest"], params["RelayState"], params["Signature"], params["SigAlg"], ) self.assertEqual(parsed_request.id, request_proc.request_id) self.assertEqual(parsed_request.relay_state, "test_state")
def test_signed_detached_static(self): """Test request with detached signature, taken from https://www.samltool.com/generic_sso_req.php""" static_keypair = CertificateKeyPair.objects.create( name="samltool", certificate_data=REDIRECT_CERT) provider = SAMLProvider( name="samltool", authorization_flow=create_test_flow(), acs_url="https://10.120.20.200/saml-sp/SAML2/POST", audience="https://10.120.20.200/saml-sp/SAML2/POST", issuer="https://10.120.20.200/saml-sp/SAML2/POST", signing_kp=static_keypair, verification_kp=static_keypair, ) parsed_request = AuthNRequestParser(provider).parse_detached( REDIRECT_REQUEST, REDIRECT_RELAY_STATE, REDIRECT_SIGNATURE, REDIRECT_SIG_ALG) self.assertEqual(parsed_request.id, "_dcf55fcd27a887e60a7ef9ee6fd3adab") self.assertEqual(parsed_request.name_id_policy, SAML_NAME_ID_FORMAT_UNSPECIFIED) self.assertEqual(parsed_request.relay_state, REDIRECT_RELAY_STATE)
def check_saml_request(self) -> Optional[HttpRequest]: """Handle POST bindings""" payload = self.request.POST # Restore the post body from the session # This happens when using POST bindings but the user isn't logged in # (user gets redirected and POST body is 'lost') if SESSION_KEY_POST in self.request.session: payload = self.request.session.pop(SESSION_KEY_POST) if REQUEST_KEY_SAML_REQUEST not in payload: LOGGER.info("check_saml_request: SAML payload missing") return bad_request_message(self.request, "The SAML request payload is missing.") try: auth_n_request = AuthNRequestParser(self.provider).parse( payload[REQUEST_KEY_SAML_REQUEST], payload.get(REQUEST_KEY_RELAY_STATE), ) self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request except CannotHandleAssertion as exc: LOGGER.info(str(exc)) return bad_request_message(self.request, str(exc)) return None
def check_saml_request(self) -> Optional[HttpRequest]: """Handle REDIRECT bindings""" if REQUEST_KEY_SAML_REQUEST not in self.request.GET: LOGGER.info("handle_saml_request: SAML payload missing") return bad_request_message(self.request, "The SAML request payload is missing.") try: auth_n_request = AuthNRequestParser(self.provider).parse_detached( self.request.GET[REQUEST_KEY_SAML_REQUEST], self.request.GET.get(REQUEST_KEY_RELAY_STATE), self.request.GET.get(REQUEST_KEY_SAML_SIGNATURE), self.request.GET.get(REQUEST_KEY_SAML_SIG_ALG), ) self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request except CannotHandleAssertion as exc: Event.new( EventAction.CONFIGURATION_ERROR, provider=self.provider, message=str(exc), ).save() LOGGER.info(str(exc)) return bad_request_message(self.request, str(exc)) return None