def test_clean_deleted_sessions(self): """ tests for clean_deleted_sessions that should delete object for which matching session do not exists anymore """ if django.VERSION >= (1, 8): client1 = Client() client2 = Client() client1.get("/login") client2.get("/login") session = client2.session session['authenticated'] = True session.save() models.FederateSLO.objects.create( username="******", session_key=client1.session.session_key, ticket=utils.gen_st() ) models.FederateSLO.objects.create( username="******", session_key=client2.session.session_key, ticket=utils.gen_st() ) self.assertEqual(len(models.FederateSLO.objects.all()), 2) models.FederateSLO.clean_deleted_sessions() self.assertEqual(len(models.FederateSLO.objects.all()), 1) with self.assertRaises(models.FederateSLO.DoesNotExist): models.FederateSLO.objects.get(username="******")
def test_login_bad_ticket(self): """ Try login with a bad ticket: login should fail and the main login page should be displayed to the user """ provider = "example.com" # get a bare client client = Client() session = client.session session["federate_username"] = models.FederatedIendityProvider.build_username_from_suffix( settings.CAS_TEST_USER, provider ) session["federate_ticket"] = utils.gen_st() if django.VERSION >= (1, 8): session.save() response = client.get("/login") # we should get a page with a from with all widget hidden that auto POST to /login using # javascript. If javascript is disabled, a "connect" button is showed self.assertTrue(response.context['auto_submit']) self.assertEqual(response.context['post_url'], '/login') params = tests_utils.copy_form(response.context["form"]) # POST, as (username, ticket) are not valid, we should get the federate login page response = client.post("/login", params) self.assertEqual(response.status_code, 200) for provider in models.FederatedIendityProvider.objects.all(): self.assertIn( '<option value="%s">%s</option>' % ( provider.suffix, provider.verbose_name ), response.content.decode("utf-8") ) self.assertEqual(response.context['post_url'], '/federate')
def test_renew(self): """ Test authentication renewal with federation mode """ tickets = self.test_login_post_provider() for (provider, _, client) in tickets: # Try to renew authentication(client already authenticated in test_login_post_provider response = client.get("/login?renew=true") # we should be redirected to the user CAS self.assertEqual(response.status_code, 302) self.assertEqual(response["Location"], "%s/federate/%s?renew=true" % ( 'http://testserver' if django.VERSION < (1, 9) else "", provider.suffix )) response = client.get("/federate/%s?renew=true" % provider.suffix) self.assertEqual(response.status_code, 302) service_url = ( "service=http%%3A%%2F%%2Ftestserver%%2Ffederate%%2F%s%%3Frenew%%3Dtrue" ) % provider.suffix self.assertIn(service_url, response["Location"]) self.assertIn("renew=true", response["Location"]) cas_port = int(provider.server_url.split(':')[-1]) # let's generate a ticket ticket = utils.gen_st() # we lauch a dummy CAS server that only validate once for the service # http://testserver/federate/example.com?renew=true with `ticket` tests_utils.DummyCAS.run( ("http://testserver/federate/%s?renew=true" % provider.suffix).encode("ascii"), ticket.encode("ascii"), settings.CAS_TEST_USER.encode("utf8"), [], cas_port ) # we normally provide a good ticket and should be redirected to /login as the ticket # get successfully validated again the dummy CAS response = client.get( '/federate/%s' % provider.suffix, {'ticket': ticket, 'renew': 'true'} ) self.assertEqual(response.status_code, 302) self.assertEqual(response["Location"], "%s/login?renew=true" % ( 'http://testserver' if django.VERSION < (1, 9) else "" )) # follow the redirect and try to get a ticket to see is it has renew set to True response = client.get("/login?renew=true&service=%s" % self.service) # we should get a page with a from with all widget hidden that auto POST to /login using # javascript. If javascript is disabled, a "connect" button is showed self.assertTrue(response.context['auto_submit']) self.assertEqual(response.context['post_url'], '/login') params = tests_utils.copy_form(response.context["form"]) # POST get prefiled from parameters response = client.post("/login", params) self.assertEqual(response.status_code, 302) self.assertTrue(response["Location"].startswith("%s?ticket=" % self.service)) ticket_value = response["Location"].split('ticket=')[-1] ticket = models.ServiceTicket.objects.get(value=ticket_value) self.assertTrue(ticket.renew)
def test_auth_federate_slo(self): """test that SLO receive from backend CAS log out the users""" # get tickets and connected clients tickets = self.test_login_post_provider() for (provider, ticket, client) in tickets: # SLO for an unkown ticket should do nothing response = client.post( "/federate/%s" % provider.suffix, {'logoutRequest': utils.logout_request(utils.gen_st())} ) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b"ok") # Bad SLO format should do nothing response = client.post( "/federate/%s" % provider.suffix, {'logoutRequest': ""} ) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b"ok") # Bad SLO format should do nothing response = client.post( "/federate/%s" % provider.suffix, {'logoutRequest': "<root></root>"} ) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b"ok") response = client.get("/login") self.assert_logged( client, response, username=provider.build_username(settings.CAS_TEST_USER) ) # SLO for a previously logged ticket should log out the user if CAS version is # 3 or 'CAS_2_SAML_1_0' response = client.post( "/federate/%s" % provider.suffix, {'logoutRequest': utils.logout_request(ticket)} ) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b"ok") response = client.get("/login") if provider.cas_protocol_version in {'3', 'CAS_2_SAML_1_0'}: # support SLO self.assert_login_failed(client, response) else: self.assert_logged( client, response, username=provider.build_username(settings.CAS_TEST_USER) )
def test_login_post_provider(self, remember=False): """test a successful login wrokflow""" tickets = [] # choose the example.com provider for (suffix, cas_port) in [ ("example.com", 8080), ("example.org", 8081), ("example.net", 8082), ("example.test", 8083) ]: provider = models.FederatedIendityProvider.objects.get(suffix=suffix) # get a bare client client = Client() # fetch the login page response = client.get("/login") # in federated mode, we shoudl POST do /federate on the login page self.assertEqual(response.context['post_url'], '/federate') # get current form parameter params = tests_utils.copy_form(response.context["form"]) params['provider'] = provider.suffix if remember: params['remember'] = 'on' # just try for one suffix if suffix == "example.com": # if renew=False is posted it should be ignored params["renew"] = False # post the choosed provider response = client.post('/federate', params) # we are redirected to the provider CAS client url self.assertEqual(response.status_code, 302) self.assertEqual(response["Location"], '%s/federate/%s%s' % ( 'http://testserver' if django.VERSION < (1, 9) else "", provider.suffix, "?remember=on" if remember else "" )) # let's follow the redirect response = client.get( '/federate/%s%s' % (provider.suffix, "?remember=on" if remember else "") ) # we are redirected to the provider CAS for authentication self.assertEqual(response.status_code, 302) self.assertEqual( response["Location"], "%s/login?service=http%%3A%%2F%%2Ftestserver%%2Ffederate%%2F%s%s" % ( provider.server_url, provider.suffix, "%3Fremember%3Don" if remember else "" ) ) # let's generate a ticket ticket = utils.gen_st() # we lauch a dummy CAS server that only validate once for the service # http://testserver/federate/example.com with `ticket` tests_utils.DummyCAS.run( ("http://testserver/federate/%s%s" % ( provider.suffix, "?remember=on" if remember else "" )).encode("ascii"), ticket.encode("ascii"), settings.CAS_TEST_USER.encode("utf8"), [], cas_port ) # we normally provide a good ticket and should be redirected to /login as the ticket # get successfully validated again the dummy CAS response = client.get( '/federate/%s' % provider.suffix, {'ticket': ticket, 'remember': 'on' if remember else ''} ) if remember: self.assertIn("remember_provider", client.cookies) self.assertEqual(client.cookies["remember_provider"].value, provider.suffix) self.assertEqual(response.status_code, 302) self.assertEqual(response["Location"], "%s/login" % ( 'http://testserver' if django.VERSION < (1, 9) else "" )) # follow the redirect response = client.get("/login") # we should get a page with a from with all widget hidden that auto POST to /login using # javascript. If javascript is disabled, a "connect" button is showed self.assertTrue(response.context['auto_submit']) self.assertEqual(response.context['post_url'], '/login') params = tests_utils.copy_form(response.context["form"]) # POST ge prefiled from parameters response = client.post("/login", params) # the user should now being authenticated using username test@`provider` self.assert_logged( client, response, username=provider.build_username(settings.CAS_TEST_USER) ) tickets.append((provider, ticket, client)) # try to get a ticket response = client.get("/login", {'service': self.service}) self.assertEqual(response.status_code, 302) self.assertTrue(response["Location"].startswith("%s?ticket=" % self.service)) return tickets
def test_auth_federate_errors(self): """ The federated view should redirect to /login if the provider is unknown or not provided, try to fetch a new ticket if the provided ticket validation fail (network error or bad ticket), redirect to /login with a error message if identity provider CAS return a bad response (invalid XML document) """ good_provider = "example.com" bad_provider = "exemple.fr" client = Client() response = client.get("/federate/%s" % bad_provider) self.assertEqual(response.status_code, 302) self.assertEqual(response["Location"], "%s/login" % ( 'http://testserver' if django.VERSION < (1, 9) else "" )) # test CAS not avaible response = client.get("/federate/%s" % good_provider, {'ticket': utils.gen_st()}) self.assertEqual(response.status_code, 302) self.assertEqual( response["Location"], "%s/login?service=http%%3A%%2F%%2Ftestserver%%2Ffederate%%2F%s" % ( models.FederatedIendityProvider.objects.get(suffix=good_provider).server_url, good_provider ) ) # test CAS avaible but bad ticket tests_utils.DummyCAS.run( ("http://testserver/federate/%s" % good_provider).encode("ascii"), utils.gen_st().encode("ascii"), settings.CAS_TEST_USER.encode("utf-8"), [], 8080 ) response = client.get("/federate/%s" % good_provider, {'ticket': utils.gen_st()}) self.assertEqual(response.status_code, 302) self.assertEqual( response["Location"], "%s/login?service=http%%3A%%2F%%2Ftestserver%%2Ffederate%%2F%s" % ( models.FederatedIendityProvider.objects.get(suffix=good_provider).server_url, good_provider ) ) response = client.post("/federate") self.assertEqual(response.status_code, 302) self.assertEqual(response["Location"], "%s/login" % ( 'http://testserver' if django.VERSION < (1, 9) else "" )) # test CAS avaible but return a bad XML doc, should redirect to /login with a error message # use "example.net" as it is CASv3 tests_utils.HttpParamsHandler.run(8082) response = client.get("/federate/%s" % "example.net", {'ticket': utils.gen_st()}) self.assertEqual(response.status_code, 302) self.assertEqual(response["Location"], "%s/login" % ( 'http://testserver' if django.VERSION < (1, 9) else "" )) response = client.get("/login") self.assertEqual(response.status_code, 200) self.assertIn(b"Invalid response from your identity provider CAS", response.content)