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
Exemple #3
0
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,
    )
Exemple #4
0
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
Exemple #5
0
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
Exemple #6
0
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
Exemple #7
0
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)
Exemple #8
0
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)