def test_ratelimits(monkeypatch, tmpdir): """ This tests if the server does rate-limiting correctly. """ # the basedir will be the pytest provided temporary directory basedir = str(tmpdir) # we'll make the auth DB and secrets file first ( authdb_path, creds, secrets_file, salt_file, env_file, ) = autogen_secrets_authdb(basedir, interactive=False) # read in the secrets file for the secret with open(secrets_file, "r") as infd: secret = infd.read().strip("\n") # read in the salts file for the salt with open(salt_file, "r") as infd: salt = infd.read().strip("\n") # read the creds file so we can try logging in with open(creds, "r") as infd: useremail, password = infd.read().strip("\n").split() # get a temp directory tmpdir = os.path.join("/tmp", "authnzrv-%s" % secrets.token_urlsafe(8)) server_listen = "127.0.0.1" server_port = "18158" # set up the environment monkeypatch.setenv("AUTHNZERVER_AUTHDB", authdb_path) monkeypatch.setenv("AUTHNZERVER_BASEDIR", basedir) monkeypatch.setenv("AUTHNZERVER_CACHEDIR", tmpdir) monkeypatch.setenv("AUTHNZERVER_DEBUGMODE", "0") monkeypatch.setenv("AUTHNZERVER_LISTEN", server_listen) monkeypatch.setenv("AUTHNZERVER_PORT", server_port) monkeypatch.setenv("AUTHNZERVER_SECRET", secret) monkeypatch.setenv("AUTHNZERVER_PIISALT", "none") monkeypatch.setenv("AUTHNZERVER_SESSIONEXPIRY", "60") monkeypatch.setenv("AUTHNZERVER_WORKERS", "1") monkeypatch.setenv("AUTHNZERVER_EMAILSERVER", "smtp.test.org") monkeypatch.setenv("AUTHNZERVER_EMAILPORT", "25") monkeypatch.setenv("AUTHNZERVER_EMAILUSER", "testuser") monkeypatch.setenv("AUTHNZERVER_EMAILPASS", "testpass") # set the session request rate-limit to 120 per 60 seconds # set the limit for the 'user-list' API call to 10 per 60 seconds monkeypatch.setenv( "AUTHNZERVER_RATELIMITS", "ipaddr:300;user:360;session:120;apikey:720;burst:150;user-list:10", ) # launch the server subprocess p = subprocess.Popen("authnzrv", shell=True) # wait 2.5 seconds for the server to start time.sleep(2.5) try: # # 1. hit the server with 300 session-new requests # nreqs = 300 resplist = [] for req_ind in range(1, nreqs + 1): # create a new anonymous session token session_payload = { "user_id": 2, "user_agent": "Mozzarella Killerwhale", "expires": datetime.utcnow() + timedelta(hours=1), "ip_address": "1.1.1.1", "extra_info_json": { "pref_datasets_always_private": True }, } request_dict = { "request": "session-new", "body": session_payload, "reqid": req_ind, "client_ipaddr": "1.1.1.1", } encrypted_request = encrypt_message(request_dict, secret) # send the request to the authnzerver resp = requests.post( "http://%s:%s" % (server_listen, server_port), data=encrypted_request, timeout=5.0, ) resplist.append(resp.status_code) # now check if we have about the right number of successful requests # should be around 150 (max burst allowed) after which we get all 429s respcounter = Counter(resplist) print(respcounter) assert respcounter[200] / nreqs == approx(150 / nreqs, rel=1.0e-3) assert respcounter[429] / nreqs == approx(150 / nreqs, rel=1.0e-3) # # 2. check if the specific rate-limiting works as expected # resplist = [] for req_ind in range(1, nreqs + 1): # create a new anonymous session token list_payload = { "user_id": 3, } request_dict = { "request": "user-list", "body": list_payload, "reqid": req_ind, "client_ipaddr": "1.1.1.1", } encrypted_request = encrypt_message(request_dict, secret) # send the request to the authnzerver resp = requests.post( "http://%s:%s" % (server_listen, server_port), data=encrypted_request, timeout=5.0, ) resplist.append(resp.status_code) # now check if we have about the right number of successful requests # should be around 10 (max allowed / 60 sec) after which we get all # 429s respcounter = Counter(resplist) print(respcounter) assert respcounter[200] / nreqs == approx(10 / nreqs, rel=1.0e-3) assert respcounter[429] / nreqs == approx(290 / nreqs, rel=1.0e-3) # # 2. check if the default aggressive rate-limiting on sensitive # operations works as expected # resplist = [] for req_ind in range(1, nreqs + 1): # create a new anonymous session token user_payload = { "email": f"{secrets.token_hex(6)}@example.com", "password": secrets.token_hex(16), "full_name": secrets.token_hex(8), } request_dict = { "request": "user-new", "body": user_payload, "reqid": req_ind, "client_ipaddr": "1.1.1.1", } encrypted_request = encrypt_message(request_dict, secret) # send the request to the authnzerver resp = requests.post( "http://%s:%s" % (server_listen, server_port), data=encrypted_request, timeout=5.0, ) resplist.append(resp.status_code) # now check if we have about the right number of successful requests # should be around 5 (max allowed / 60 sec) after which we get all # 429s respcounter = Counter(resplist) print(respcounter) assert respcounter[200] / nreqs == approx(5 / nreqs, rel=1.0e-3) assert respcounter[429] / nreqs == approx(295 / nreqs, rel=1.0e-3) # # kill the server at the end # finally: p.terminate() try: p.communicate(timeout=5.0) p.kill() except Exception: pass
def test_client(monkeypatch, tmpdir): """ This tests if the server does rate-limiting correctly. """ # the basedir will be the pytest provided temporary directory basedir = str(tmpdir) # we'll make the auth DB and secrets file first ( authdb_path, creds, secrets_file, salt_file, env_file, ) = autogen_secrets_authdb(basedir, interactive=False) # read in the secrets file for the secret with open(secrets_file, "r") as infd: secret = infd.read().strip("\n") # read in the salts file for the salt with open(salt_file, "r") as infd: salt = infd.read().strip("\n") # read the creds file so we can try logging in with open(creds, "r") as infd: useremail, password = infd.read().strip("\n").split() # get a temp directory tmpdir = os.path.join("/tmp", "authnzrv-%s" % secrets.token_urlsafe(8)) server_listen = "127.0.0.1" server_port = "18158" # set up the environment monkeypatch.setenv("AUTHNZERVER_AUTHDB", authdb_path) monkeypatch.setenv("AUTHNZERVER_BASEDIR", basedir) monkeypatch.setenv("AUTHNZERVER_CACHEDIR", tmpdir) monkeypatch.setenv("AUTHNZERVER_DEBUGMODE", "0") monkeypatch.setenv("AUTHNZERVER_LISTEN", server_listen) monkeypatch.setenv("AUTHNZERVER_PORT", server_port) monkeypatch.setenv("AUTHNZERVER_SECRET", secret) monkeypatch.setenv("AUTHNZERVER_PIISALT", salt) monkeypatch.setenv("AUTHNZERVER_SESSIONEXPIRY", "60") monkeypatch.setenv("AUTHNZERVER_WORKERS", "1") monkeypatch.setenv("AUTHNZERVER_EMAILSERVER", "smtp.test.org") monkeypatch.setenv("AUTHNZERVER_EMAILPORT", "25") monkeypatch.setenv("AUTHNZERVER_EMAILUSER", "testuser") monkeypatch.setenv("AUTHNZERVER_EMAILPASS", "testpass") # set the session request rate-limit to 120 per 60 seconds monkeypatch.setenv( "AUTHNZERVER_RATELIMITS", "ipaddr:720;user:360;session:120;apikey:720;burst:150", ) # launch the server subprocess p = subprocess.Popen("authnzrv", shell=True) # wait 2.5 seconds for the server to start time.sleep(2.5) # # start tests # try: client = Authnzerver( authnzerver_url=f"http://{server_listen}:{server_port}", authnzerver_secret=secret, ) # create a new user resp = client.request( "user-new", { "email": "*****@*****.**", "password": "******", "full_name": "New User", "client_ipaddr": "1.2.3.4", }, ) assert resp.success is True assert resp.response["user_id"] == 4 assert resp.response["send_verification"] is True # edit their info resp = client.request( "internal-user-edit", { "target_userid": 4, "client_ipaddr": "1.2.3.4", "update_dict": { "email_verified": True, "is_active": True, "extra_info": { "provenance": "pytest-user", "type": "test", "hello": "world", }, }, }, ) assert resp.success is True assert resp.response.get("user_info", None) is not None assert (resp.response["user_info"]["extra_info"]["provenance"] == "pytest-user") assert resp.response["user_info"]["extra_info"]["type"] == "test" assert resp.response["user_info"]["extra_info"]["hello"] == "world" assert resp.response["user_info"]["email_verified"] is True assert resp.response["user_info"]["is_active"] is True # # kill the server at the end # finally: p.terminate() try: p.communicate(timeout=3.0) p.kill() except Exception: pass
def new_authnzerver(monkeypatch, tmpdir): """ This sets up an authnzerver and then returns connection params. Tears it down later. """ # the basedir will be the pytest provided temporary directory basedir = str(tmpdir) # we'll make the auth DB and secrets file first ( authdb_path, creds, secrets_file, salt_file, env_file, ) = autogen_secrets_authdb(basedir, interactive=False) # read in the secrets file for the secret with open(secrets_file, "r") as infd: secret = infd.read().strip("\n") # read in the salts file for the salt with open(salt_file, "r") as infd: salt = infd.read().strip("\n") # read the creds file so we can try logging in with open(creds, "r") as infd: useremail, password = infd.read().strip("\n").split() # get a temp directory tmpdir = os.path.join("/tmp", "authnzrv-%s" % secrets.token_urlsafe(8)) server_listen = "127.0.0.1" server_port = "18158" # set up the environment monkeypatch.setenv("AUTHNZERVER_AUTHDB", authdb_path) monkeypatch.setenv("AUTHNZERVER_BASEDIR", basedir) monkeypatch.setenv("AUTHNZERVER_CACHEDIR", tmpdir) monkeypatch.setenv("AUTHNZERVER_DEBUGMODE", "0") monkeypatch.setenv("AUTHNZERVER_LISTEN", server_listen) monkeypatch.setenv("AUTHNZERVER_PORT", server_port) monkeypatch.setenv("AUTHNZERVER_SECRET", secret) monkeypatch.setenv("AUTHNZERVER_PIISALT", salt) monkeypatch.setenv("AUTHNZERVER_SESSIONEXPIRY", "60") monkeypatch.setenv("AUTHNZERVER_WORKERS", "1") monkeypatch.setenv("AUTHNZERVER_EMAILSERVER", "smtp.test.org") monkeypatch.setenv("AUTHNZERVER_EMAILPORT", "25") monkeypatch.setenv("AUTHNZERVER_EMAILUSER", "testuser") monkeypatch.setenv("AUTHNZERVER_EMAILPASS", "testpass") # set the session request rate-limit to 120 per 60 seconds monkeypatch.setenv( "AUTHNZERVER_RATELIMITS", "ipaddr:720;user:360;session:120;apikey:720;burst:150", ) # launch the server subprocess p = subprocess.Popen("authnzrv", shell=True) # wait 2.5 seconds for the server to start time.sleep(2.5) # return the needed bits for the test yield f"http://{server_listen}:{server_port}", secret # # teardown # p.kill() try: p.communicate(timeout=1.0) p.kill() except Exception: pass # make sure to kill authnzrv on some Linux machines. use lsof and the # port number to find the remaining authnzrv processes and kill them subprocess.call( "lsof | grep 18158 | awk '{ print $2 }' | sort | uniq | xargs kill -2", shell=True, )
def test_server_invalid_passchecks_with_lock(monkeypatch, tmpdir): """This tests if the server responds appropriately to invalid password checks. The timing difference between successive failed password checks should increase roughly exponentially. In addition, the user should be locked out of their account for the configured amount of time and unlocked thereafter. """ # the basedir will be the pytest provided temporary directory basedir = str(tmpdir) # we'll make the auth DB and secrets file first ( authdb_path, creds, secrets_file, salt_file, env_file, ) = autogen_secrets_authdb(basedir, interactive=False) # read in the secrets file for the secret with open(secrets_file, "r") as infd: secret = infd.read().strip("\n") # read in the salts file for the salt with open(salt_file, "r") as infd: salt = infd.read().strip("\n") # read the creds file so we can try logging in with open(creds, "r") as infd: useremail, password = infd.read().strip("\n").split() # get a temp directory tmpdir = os.path.join("/tmp", "authnzrv-%s" % secrets.token_urlsafe(8)) server_listen = "127.0.0.1" server_port = "18158" # set up the environment monkeypatch.setenv("AUTHNZERVER_AUTHDB", authdb_path) monkeypatch.setenv("AUTHNZERVER_BASEDIR", basedir) monkeypatch.setenv("AUTHNZERVER_CACHEDIR", tmpdir) monkeypatch.setenv("AUTHNZERVER_DEBUGMODE", "0") monkeypatch.setenv("AUTHNZERVER_LISTEN", server_listen) monkeypatch.setenv("AUTHNZERVER_PORT", server_port) monkeypatch.setenv("AUTHNZERVER_SECRET", secret) monkeypatch.setenv("AUTHNZERVER_PIISALT", salt) monkeypatch.setenv("AUTHNZERVER_SESSIONEXPIRY", "60") monkeypatch.setenv("AUTHNZERVER_WORKERS", "1") monkeypatch.setenv("AUTHNZERVER_EMAILSERVER", "smtp.test.org") monkeypatch.setenv("AUTHNZERVER_EMAILPORT", "25") monkeypatch.setenv("AUTHNZERVER_EMAILUSER", "testuser") monkeypatch.setenv("AUTHNZERVER_EMAILPASS", "testpass") monkeypatch.setenv("AUTHNZERVER_USERLOCKTRIES", "2") monkeypatch.setenv("AUTHNZERVER_USERLOCKTIME", "20") monkeypatch.setenv( "AUTHNZERVER_RATELIMITS", "ipaddr:300;user:360;session:120;apikey:720;burst:150;" "user-new:50;user-login:50", ) # launch the server subprocess p = subprocess.Popen("authnzrv", shell=True) # wait 2.5 seconds for the server to start time.sleep(2.5) timing = [] try: # # attempt to login as the superuser several times with the wrong # password # for i in range(4): # create a new anonymous session token session_payload = { "user_id": 2, "user_agent": "Mozzarella Killerwhale", "expires": datetime.utcnow() + timedelta(hours=1), "ip_address": "1.1.1.1", "extra_info_json": { "pref_datasets_always_private": True }, } request_dict = { "request": "session-new", "body": session_payload, "reqid": i, "client_ipaddr": "1.2.3.4", } encrypted_request = encrypt_message(request_dict, secret) # send the request to the authnzerver resp = requests.post( "http://%s:%s" % (server_listen, server_port), data=encrypted_request, timeout=1.0, ) resp.raise_for_status() # decrypt the response session_dict = decrypt_message(resp.text, secret) assert session_dict["reqid"] == request_dict["reqid"] assert session_dict["success"] is True assert isinstance(session_dict["response"], dict) assert session_dict["response"]["session_token"] is not None request_dict = { "request": "user-passcheck-nosession", "body": { "email": useremail, "password": "******" % (password, i), }, "reqid": 10 * i + 10, "client_ipaddr": "1.2.3.4", } encrypted_request = encrypt_message(request_dict, secret) start_login_time = time.monotonic() # send the request to the authnzerver resp = requests.post( "http://%s:%s" % (server_listen, server_port), data=encrypted_request, timeout=60.0, ) resp.raise_for_status() timing.append(time.monotonic() - start_login_time) # decrypt the response response_dict = decrypt_message(resp.text, secret) assert response_dict["reqid"] == request_dict["reqid"] assert response_dict["success"] is False assert isinstance(response_dict["response"], dict) assert response_dict["response"]["user_id"] is None # for the last attempt, we should get back a "locked" account # message if i >= 2: assert ( "Your user account has been locked " "after repeated login failures. " "Try again in an hour or " "contact the server admins.") in response_dict["messages"] # wait 30 seconds for the lock time to expire time.sleep(30) # now login wih the correct password and see if we can login now session_payload = { "user_id": 2, "user_agent": "Mozzarella Killerwhale", "expires": datetime.utcnow() + timedelta(hours=1), "ip_address": "1.1.1.1", "extra_info_json": { "pref_datasets_always_private": True }, } request_dict = { "request": "session-new", "body": session_payload, "reqid": 1004, "client_ipaddr": "1.2.3.4", } encrypted_request = encrypt_message(request_dict, secret) # send the request to the authnzerver resp = requests.post( "http://%s:%s" % (server_listen, server_port), data=encrypted_request, timeout=1.0, ) resp.raise_for_status() # decrypt the response session_dict = decrypt_message(resp.text, secret) assert session_dict["reqid"] == request_dict["reqid"] assert session_dict["success"] is True assert isinstance(session_dict["response"], dict) assert session_dict["response"]["session_token"] is not None request_dict = { "request": "user-passcheck-nosession", "body": { "email": useremail, "password": password }, "reqid": 1005, "client_ipaddr": "1.2.3.4", } encrypted_request = encrypt_message(request_dict, secret) start_login_time = time.monotonic() # send the request to the authnzerver resp = requests.post( "http://%s:%s" % (server_listen, server_port), data=encrypted_request, timeout=60.0, ) resp.raise_for_status() timing.append(time.monotonic() - start_login_time) # decrypt the response response_dict = decrypt_message(resp.text, secret) assert response_dict["reqid"] == request_dict["reqid"] assert response_dict["success"] is True assert isinstance(response_dict["response"], dict) assert response_dict["response"]["user_id"] == 1 assert response_dict["response"]["user_role"] == "superuser" finally: # # kill the server at the end # p.terminate() try: p.communicate(timeout=3.0) p.kill() except Exception: pass
def test_server_with_env(monkeypatch, tmpdir): """ This tests if the server starts fine with all config in the environment. """ # the basedir will be the pytest provided temporary directory basedir = str(tmpdir) # we'll make the auth DB and secrets file first ( authdb_path, creds, secrets_file, salt_file, env_file, ) = autogen_secrets_authdb(basedir, interactive=False) # read in the secrets file for the secret with open(secrets_file, "r") as infd: secret = infd.read().strip("\n") # read in the salts file for the salt with open(salt_file, "r") as infd: salt = infd.read().strip("\n") # read the creds file so we can try logging in with open(creds, "r") as infd: useremail, password = infd.read().strip("\n").split() # get a temp directory tmpdir = os.path.join("/tmp", "authnzrv-%s" % secrets.token_urlsafe(8)) server_listen = "127.0.0.1" server_port = "18158" # set up the environment monkeypatch.setenv("AUTHNZERVER_AUTHDB", authdb_path) monkeypatch.setenv("AUTHNZERVER_BASEDIR", basedir) monkeypatch.setenv("AUTHNZERVER_CACHEDIR", tmpdir) monkeypatch.setenv("AUTHNZERVER_DEBUGMODE", "0") monkeypatch.setenv("AUTHNZERVER_LISTEN", server_listen) monkeypatch.setenv("AUTHNZERVER_PORT", server_port) monkeypatch.setenv("AUTHNZERVER_SECRET", secret) monkeypatch.setenv("AUTHNZERVER_PIISALT", salt) monkeypatch.setenv("AUTHNZERVER_SESSIONEXPIRY", "60") monkeypatch.setenv("AUTHNZERVER_WORKERS", "1") monkeypatch.setenv("AUTHNZERVER_EMAILSERVER", "smtp.test.org") monkeypatch.setenv("AUTHNZERVER_EMAILPORT", "25") monkeypatch.setenv("AUTHNZERVER_EMAILUSER", "testuser") monkeypatch.setenv("AUTHNZERVER_EMAILPASS", "testpass") monkeypatch.setenv( "AUTHNZERVER_RATELIMITS", "ipaddr:300;user:360;session:120;apikey:720;burst:150;" "user-new:50;user-login:50", ) # launch the server subprocess p = subprocess.Popen("authnzrv", shell=True) # wait 2.5 seconds for the server to start time.sleep(2.5) try: # # 1. hit the server with a request for a new session # # create a new anonymous session token session_payload = { "user_id": 2, "user_agent": "Mozzarella Killerwhale", "expires": datetime.utcnow() + timedelta(hours=1), "ip_address": "1.1.1.1", "extra_info_json": { "pref_datasets_always_private": True }, } request_dict = { "request": "session-new", "body": session_payload, "reqid": 101, "client_ipaddr": "1.2.3.4", } encrypted_request = encrypt_message(request_dict, secret) # send the request to the authnzerver resp = requests.post( "http://%s:%s" % (server_listen, server_port), data=encrypted_request, timeout=1.0, ) resp.raise_for_status() # decrypt the response response_dict = decrypt_message(resp.text, secret) assert response_dict["reqid"] == request_dict["reqid"] assert response_dict["success"] is True assert isinstance(response_dict["response"], dict) assert response_dict["response"]["session_token"] is not None # # 2. login as the superuser # request_dict = { "request": "user-login", "body": { "session_token": response_dict["response"]["session_token"], "email": useremail, "password": password, }, "reqid": 102, "client_ipaddr": "1.2.3.4", } encrypted_request = encrypt_message(request_dict, secret) # send the request to the authnzerver resp = requests.post( "http://%s:%s" % (server_listen, server_port), data=encrypted_request, timeout=1.0, ) resp.raise_for_status() # decrypt the response response_dict = decrypt_message(resp.text, secret) assert response_dict["reqid"] == request_dict["reqid"] assert response_dict["success"] is True assert isinstance(response_dict["response"], dict) assert response_dict["response"]["user_id"] == 1 # # kill the server at the end # finally: p.terminate() try: p.communicate(timeout=3.0) p.kill() except Exception: pass
def test_server_invalid_logins(monkeypatch, tmpdir): """This tests if the server responds appropriately to invalid logins. The timing difference between successive failed logins should increase roughly exponentially. """ # the basedir will be the pytest provided temporary directory basedir = str(tmpdir) # we'll make the auth DB and secrets file first ( authdb_path, creds, secrets_file, salt_file, env_file, ) = autogen_secrets_authdb(basedir, interactive=False) # read in the secrets file for the secret with open(secrets_file, "r") as infd: secret = infd.read().strip("\n") # read in the salts file for the salt with open(salt_file, "r") as infd: salt = infd.read().strip("\n") # read the creds file so we can try logging in with open(creds, "r") as infd: useremail, password = infd.read().strip("\n").split() # get a temp directory tmpdir = os.path.join("/tmp", "authnzrv-%s" % secrets.token_urlsafe(8)) server_listen = "127.0.0.1" server_port = "18158" # set up the environment monkeypatch.setenv("AUTHNZERVER_AUTHDB", authdb_path) monkeypatch.setenv("AUTHNZERVER_BASEDIR", basedir) monkeypatch.setenv("AUTHNZERVER_CACHEDIR", tmpdir) monkeypatch.setenv("AUTHNZERVER_DEBUGMODE", "0") monkeypatch.setenv("AUTHNZERVER_LISTEN", server_listen) monkeypatch.setenv("AUTHNZERVER_PORT", server_port) monkeypatch.setenv("AUTHNZERVER_SECRET", secret) monkeypatch.setenv("AUTHNZERVER_PIISALT", salt) monkeypatch.setenv("AUTHNZERVER_SESSIONEXPIRY", "60") monkeypatch.setenv("AUTHNZERVER_WORKERS", "1") monkeypatch.setenv("AUTHNZERVER_EMAILSERVER", "smtp.test.org") monkeypatch.setenv("AUTHNZERVER_EMAILPORT", "25") monkeypatch.setenv("AUTHNZERVER_EMAILUSER", "testuser") monkeypatch.setenv("AUTHNZERVER_EMAILPASS", "testpass") monkeypatch.setenv( "AUTHNZERVER_RATELIMITS", "ipaddr:300;user:360;session:120;apikey:720;burst:150;" "user-new:50;user-login:50", ) # launch the server subprocess p = subprocess.Popen("authnzrv", shell=True) # wait 2.5 seconds for the server to start time.sleep(2.5) timing = [] try: # # attempt to login as the superuser several times with the wrong # password # for i in range(5): # create a new anonymous session token session_payload = { "user_id": 2, "user_agent": "Mozzarella Killerwhale", "expires": datetime.utcnow() + timedelta(hours=1), "ip_address": "1.1.1.1", "extra_info_json": { "pref_datasets_always_private": True }, } request_dict = { "request": "session-new", "body": session_payload, "reqid": i, "client_ipaddr": "1.2.3.4", } encrypted_request = encrypt_message(request_dict, secret) # send the request to the authnzerver resp = requests.post( "http://%s:%s" % (server_listen, server_port), data=encrypted_request, timeout=1.0, ) resp.raise_for_status() # decrypt the response session_dict = decrypt_message(resp.text, secret) assert session_dict["reqid"] == request_dict["reqid"] assert session_dict["success"] is True assert isinstance(session_dict["response"], dict) assert session_dict["response"]["session_token"] is not None request_dict = { "request": "user-login", "body": { "session_token": session_dict["response"]["session_token"], "email": useremail, "password": "******" % (password, i), }, "reqid": 10 * i + 10, "client_ipaddr": "1.2.3.4", } encrypted_request = encrypt_message(request_dict, secret) start_login_time = time.monotonic() # send the request to the authnzerver resp = requests.post( "http://%s:%s" % (server_listen, server_port), data=encrypted_request, timeout=60.0, ) resp.raise_for_status() timing.append(time.monotonic() - start_login_time) # decrypt the response response_dict = decrypt_message(resp.text, secret) assert response_dict["reqid"] == request_dict["reqid"] assert response_dict["success"] is False assert isinstance(response_dict["response"], dict) assert response_dict["response"]["user_id"] is None # # check if the timings follow the expected trend # diffs = [timing[x + 1] - timing[x] for x in range(4)] diffs_increasing = all(diffs[x + 1] > diffs[x] for x in range(3)) assert diffs_increasing is True # now login wih the correct password and see if the login time goes back # to normal session_payload = { "user_id": 2, "user_agent": "Mozzarella Killerwhale", "expires": datetime.utcnow() + timedelta(hours=1), "ip_address": "1.1.1.1", "extra_info_json": { "pref_datasets_always_private": True }, } request_dict = { "request": "session-new", "body": session_payload, "reqid": 1004, "client_ipaddr": "1.2.3.4", } encrypted_request = encrypt_message(request_dict, secret) # send the request to the authnzerver resp = requests.post( "http://%s:%s" % (server_listen, server_port), data=encrypted_request, timeout=1.0, ) resp.raise_for_status() # decrypt the response session_dict = decrypt_message(resp.text, secret) assert session_dict["reqid"] == request_dict["reqid"] assert session_dict["success"] is True assert isinstance(session_dict["response"], dict) assert session_dict["response"]["session_token"] is not None request_dict = { "request": "user-login", "body": { "session_token": session_dict["response"]["session_token"], "email": useremail, "password": password, }, "reqid": 1005, "client_ipaddr": "1.2.3.4", } encrypted_request = encrypt_message(request_dict, secret) start_login_time = time.monotonic() # send the request to the authnzerver resp = requests.post( "http://%s:%s" % (server_listen, server_port), data=encrypted_request, timeout=60.0, ) resp.raise_for_status() timing.append(time.monotonic() - start_login_time) # decrypt the response response_dict = decrypt_message(resp.text, secret) assert response_dict["reqid"] == request_dict["reqid"] assert response_dict["success"] is True assert isinstance(response_dict["response"], dict) assert response_dict["response"]["user_id"] == 1 # the latest time should be less than the 1st time (when throttling was # activated) and also less than the immediately previous time assert (timing[-1] < timing[0]) and (timing[-1] < timing[-2]) finally: # # kill the server at the end # p.terminate() try: p.communicate(timeout=3.0) p.kill() except Exception: pass
def test_server_with_env(monkeypatch, tmpdir): ''' This tests if the server starts fine with all config in the environment. ''' # the basedir will be the pytest provided temporary directory basedir = str(tmpdir) # we'll make the auth DB and secrets file first authdb_path, creds, secrets_file = autogen_secrets_authdb( basedir, interactive=False) # read in the secrets file for the secret with open(secrets_file, 'r') as infd: secret = infd.read().strip('\n') # read the creds file so we can try logging in with open(creds, 'r') as infd: useremail, password = infd.read().strip('\n').split() # get a temp directory tmpdir = os.path.join('/tmp', 'authnzrv-%s' % secrets.token_urlsafe(8)) server_listen = '127.0.0.1' server_port = '18158' # set up the environment monkeypatch.setenv("AUTHNZERVER_AUTHDB", authdb_path) monkeypatch.setenv("AUTHNZERVER_BASEDIR", basedir) monkeypatch.setenv("AUTHNZERVER_CACHEDIR", tmpdir) monkeypatch.setenv("AUTHNZERVER_DEBUGMODE", "0") monkeypatch.setenv("AUTHNZERVER_LISTEN", server_listen) monkeypatch.setenv("AUTHNZERVER_PORT", server_port) monkeypatch.setenv("AUTHNZERVER_SECRET", secret) monkeypatch.setenv("AUTHNZERVER_SESSIONEXPIRY", "60") monkeypatch.setenv("AUTHNZERVER_WORKERS", "1") monkeypatch.setenv("AUTHNZERVER_EMAILSERVER", "smtp.test.org") monkeypatch.setenv("AUTHNZERVER_EMAILPORT", "25") monkeypatch.setenv("AUTHNZERVER_EMAILUSER", "testuser") monkeypatch.setenv("AUTHNZERVER_EMAILPASS", "testpass") # launch the server subprocess p = subprocess.Popen("authnzrv", shell=True) # wait 2.5 seconds for the server to start time.sleep(2.5) try: # # 1. hit the server with a request for a new session # # create a new anonymous session token session_payload = { 'user_id': 2, 'user_agent': 'Mozzarella Killerwhale', 'expires': datetime.utcnow() + timedelta(hours=1), 'ip_address': '1.1.1.1', 'extra_info_json': { 'pref_datasets_always_private': True } } request_dict = { 'request': 'session-new', 'body': session_payload, 'reqid': 101 } encrypted_request = encrypt_response(request_dict, secret) # send the request to the authnzerver resp = requests.post('http://%s:%s' % (server_listen, server_port), data=encrypted_request, timeout=1.0) resp.raise_for_status() # decrypt the response response_dict = decrypt_request(resp.text, secret) assert response_dict['reqid'] == request_dict['reqid'] assert response_dict['success'] is True assert isinstance(response_dict['response'], dict) assert response_dict['response']['session_token'] is not None # # 2. login as the superuser # request_dict = { 'request': 'user-login', 'body': { 'session_token': response_dict['response']['session_token'], 'email': useremail, 'password': password }, 'reqid': 102 } encrypted_request = encrypt_response(request_dict, secret) # send the request to the authnzerver resp = requests.post('http://%s:%s' % (server_listen, server_port), data=encrypted_request, timeout=1.0) resp.raise_for_status() # decrypt the response response_dict = decrypt_request(resp.text, secret) assert response_dict['reqid'] == request_dict['reqid'] assert response_dict['success'] is True assert isinstance(response_dict['response'], dict) assert response_dict['response']['user_id'] == 1 # # kill the server at the end # finally: p.kill() try: p.communicate(timeout=1.0) p.kill() except Exception: pass # make sure to kill authnzrv on some Linux machines. use lsof and the # port number to find the remaining authnzrv processes and kill them subprocess.call( "lsof | grep 18158 | awk '{ print $2 }' | sort | uniq | xargs kill", shell=True)
def test_server_invalid_logins(monkeypatch, tmpdir): '''This tests if the server responds appropriately to invalid logins. The timing difference between successive failed logins should increase roughly exponentially. ''' # the basedir will be the pytest provided temporary directory basedir = str(tmpdir) # we'll make the auth DB and secrets file first authdb_path, creds, secrets_file = autogen_secrets_authdb( basedir, interactive=False) # read in the secrets file for the secret with open(secrets_file, 'r') as infd: secret = infd.read().strip('\n') # read the creds file so we can try logging in with open(creds, 'r') as infd: useremail, password = infd.read().strip('\n').split() # get a temp directory tmpdir = os.path.join('/tmp', 'authnzrv-%s' % secrets.token_urlsafe(8)) server_listen = '127.0.0.1' server_port = '18158' # set up the environment monkeypatch.setenv("AUTHNZERVER_AUTHDB", authdb_path) monkeypatch.setenv("AUTHNZERVER_BASEDIR", basedir) monkeypatch.setenv("AUTHNZERVER_CACHEDIR", tmpdir) monkeypatch.setenv("AUTHNZERVER_DEBUGMODE", "0") monkeypatch.setenv("AUTHNZERVER_LISTEN", server_listen) monkeypatch.setenv("AUTHNZERVER_PORT", server_port) monkeypatch.setenv("AUTHNZERVER_SECRET", secret) monkeypatch.setenv("AUTHNZERVER_SESSIONEXPIRY", "60") monkeypatch.setenv("AUTHNZERVER_WORKERS", "1") monkeypatch.setenv("AUTHNZERVER_EMAILSERVER", "smtp.test.org") monkeypatch.setenv("AUTHNZERVER_EMAILPORT", "25") monkeypatch.setenv("AUTHNZERVER_EMAILUSER", "testuser") monkeypatch.setenv("AUTHNZERVER_EMAILPASS", "testpass") # launch the server subprocess p = subprocess.Popen("authnzrv", shell=True) # wait 2.5 seconds for the server to start time.sleep(2.5) timing = [] try: # # attempt to login as the superuser several times with the wrong # password # for i in range(5): # create a new anonymous session token session_payload = { 'user_id': 2, 'user_agent': 'Mozzarella Killerwhale', 'expires': datetime.utcnow() + timedelta(hours=1), 'ip_address': '1.1.1.1', 'extra_info_json': { 'pref_datasets_always_private': True } } request_dict = { 'request': 'session-new', 'body': session_payload, 'reqid': i } encrypted_request = encrypt_response(request_dict, secret) # send the request to the authnzerver resp = requests.post('http://%s:%s' % (server_listen, server_port), data=encrypted_request, timeout=1.0) resp.raise_for_status() # decrypt the response session_dict = decrypt_request(resp.text, secret) assert session_dict['reqid'] == request_dict['reqid'] assert session_dict['success'] is True assert isinstance(session_dict['response'], dict) assert session_dict['response']['session_token'] is not None request_dict = { 'request': 'user-login', 'body': { 'session_token': session_dict['response']['session_token'], 'email': useremail, 'password': '******' % (password, i) }, 'reqid': 10 * i + 10 } encrypted_request = encrypt_response(request_dict, secret) start_login_time = time.monotonic() # send the request to the authnzerver resp = requests.post('http://%s:%s' % (server_listen, server_port), data=encrypted_request, timeout=60.0) resp.raise_for_status() timing.append(time.monotonic() - start_login_time) # decrypt the response response_dict = decrypt_request(resp.text, secret) assert response_dict['reqid'] == request_dict['reqid'] assert response_dict['success'] is False assert isinstance(response_dict['response'], dict) assert response_dict['response']['user_id'] is None # # check if the timings follow the expected trend # diffs = [timing[x + 1] - timing[x] for x in range(4)] diffs_increasing = all(diffs[x + 1] > diffs[x] for x in range(3)) assert diffs_increasing is True # now login wih the correct password and see if the login time goes back # to normal session_payload = { 'user_id': 2, 'user_agent': 'Mozzarella Killerwhale', 'expires': datetime.utcnow() + timedelta(hours=1), 'ip_address': '1.1.1.1', 'extra_info_json': { 'pref_datasets_always_private': True } } request_dict = { 'request': 'session-new', 'body': session_payload, 'reqid': 1004 } encrypted_request = encrypt_response(request_dict, secret) # send the request to the authnzerver resp = requests.post('http://%s:%s' % (server_listen, server_port), data=encrypted_request, timeout=1.0) resp.raise_for_status() # decrypt the response session_dict = decrypt_request(resp.text, secret) assert session_dict['reqid'] == request_dict['reqid'] assert session_dict['success'] is True assert isinstance(session_dict['response'], dict) assert session_dict['response']['session_token'] is not None request_dict = { 'request': 'user-login', 'body': { 'session_token': session_dict['response']['session_token'], 'email': useremail, 'password': password }, 'reqid': 1005 } encrypted_request = encrypt_response(request_dict, secret) start_login_time = time.monotonic() # send the request to the authnzerver resp = requests.post('http://%s:%s' % (server_listen, server_port), data=encrypted_request, timeout=60.0) resp.raise_for_status() timing.append(time.monotonic() - start_login_time) # decrypt the response response_dict = decrypt_request(resp.text, secret) assert response_dict['reqid'] == request_dict['reqid'] assert response_dict['success'] is True assert isinstance(response_dict['response'], dict) assert response_dict['response']['user_id'] == 1 # the latest time should be less than the 1st time (when throttling was # activated) and also less than the immediately previous time assert ((timing[-1] < timing[0]) and (timing[-1] < timing[-2])) finally: # # kill the server at the end # p.kill() try: p.communicate(timeout=1.0) p.kill() except Exception: pass # make sure to kill authnzrv on some Linux machines. use lsof and the # port number to find the remaining authnzrv processes and kill them subprocess.call( "lsof | grep 18158 | awk '{ print $2 }' | sort | uniq | xargs kill", shell=True)