def test_generate_jwt(app, user):
    # A JWT token is generated for a user and is verifiable
    SECRET_KEY = app.config["SECRET_KEY"]
    a_jwt = utils.make_jwt(user)

    # A JWT token will differ over time:
    with freeze_time("2020-04-02"):
        decoded_jwt = jwt.decode(a_jwt,
                                 SECRET_KEY,
                                 algorithms=utils.JWT_ALGORITHM)
        a_jwt2 = utils.make_jwt(user)
        assert a_jwt != a_jwt2

    # A JWT has basic JWT and the user id.
    assert set(decoded_jwt.keys()) == set(
        ["id", "exp", "iat", "iss", "max-exp"])
    assert decoded_jwt["id"] == user.id
    assert utils.verify_jwt(user, a_jwt)

    # An expired JWT token will not be valid:
    with freeze_time("2020-06-01"):
        assert not utils.verify_jwt(user, a_jwt)

    # A JWT token can't be verified with the wrong secret
    assert not utils.verify_jwt(user, a_jwt, "bad secret")

    # A jwt with an expired max-exp is not valid.
    a_jwt = utils.make_jwt(user, datetime.now())
    with freeze_time("2020-04-02"):
        assert not utils.verify_jwt(user, a_jwt)
def test_decode_jwt(app, user):
    # A generated token can be decoded
    a_jwt = utils.make_jwt(user)
    decoded_jwt = utils.decode_jwt(a_jwt.decode("utf-8"))
    assert decoded_jwt["id"] == user.id
    assert set(decoded_jwt.keys()) == set(
        ["id", "exp", "iat", "iss", "max-exp"])

    # A blank string cannot be decoded
    assert not utils.decode_jwt("")

    # A random string cannot be decoded
    assert not utils.decode_jwt("aoeucenet")

    # A jwt without an id key is not valid:
    a_jwt = jwt.encode(
        {},
        app.config["SECRET_KEY"],
        algorithm=utils.JWT_ALGORITHM,
    )
    assert not utils.decode_jwt(a_jwt.decode("utf-8"))

    # A jwt with an expired max-exp can be decoded
    a_jwt = utils.make_jwt(user)
    with freeze_time("2020-04-02"):
        assert utils.decode_jwt(a_jwt.decode("utf-8")) is not False
def test_refresh(client, user):
    db.session.add(user)
    db.session.commit()
    calling_token = make_jwt(user, TOMORROW).decode("utf-8")
    calling_jwt = decode_jwt(calling_token)

    # A non json contenttype returns a 400
    result = client.post(
        "/users/auth/refresh",
        headers={"Authorization": f"Bearer {calling_token}"},
    )
    assert result.status_code == 400

    # A valid JWT token returns a new token
    # tokens aren't gauranteed to be unique and will be sub-second...so let a
    # little time go by to gaurantee a unique JWT is returned by the endpoint.
    time.sleep(1)
    result = client.post(
        "/users/auth/refresh",
        content_type="application/json",
        headers={"Authorization": f"Bearer {calling_token}"},
    )
    result_jwt = decode_jwt(result.json["token"])
    assert result.status_code == 200
    assert verify_jwt(user, result.json["token"])
    assert result.json["token"] != calling_token
    # its max-exp always equals the calling_token's value
    assert result_jwt["max-exp"] == calling_jwt["max-exp"]
def test_jwt_required(client, user):
    # No authorization header returns a 401
    result = client.post("/users/auth/refresh",
                         content_type="application/json")
    assert result.status_code == 401

    # A non bearer header returns a 401
    result = client.post(
        "/users/auth/refresh",
        content_type="application/json",
        headers={"Authorization": "Basic 123456789"},
    )
    assert result.status_code == 401

    # A badly formatted bearer token returns a 401
    result = client.post(
        "/users/auth/refresh",
        content_type="application/json",
        headers={
            "Authorization":
            "Bearer eyJ0e  XAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNTkzNjI2MjQ5LCJpc3MiOiJhcHAiLCJpYXQiOjE1OTEwMzQyNDl9.-j5K1xiYx6GzyKoI5UsKnpiCA1vF1D5gBreCUlnm01o"
        },
    )
    assert result.status_code == 401

    # An badly formatted JWT token returns a 401
    result = client.post(
        "/users/auth/refresh",
        content_type="application/json",
        headers={
            "Authorization":
            "Bearer XXXXXXXXXXXKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNTkzNjI2MjQ5LCJpc3MiOiJhcHAiLCJpYXQiOjE1OTEwMzQyNDl9.-j5K1xiYx6GzyKoI5UsKnpiCA1vF1D5gBreCUlnm01o"
        },
    )
    assert result.status_code == 401

    # A valid JWT that is expired returns a 401
    db.session.add(user)
    db.session.commit()
    calling_token = make_jwt(user, TOMORROW).decode("utf-8")
    with freeze_time((TOMORROW + timedelta(days=1)).strftime("%Y-%m-%d")):
        result = client.post(
            "/users/auth/refresh",
            content_type="application/json",
            headers={"Authorization": f"Bearer {calling_token}"},
        )
        assert result.status_code == 401

    # A valid JWT token for a user that doesn't exist returns a 401
    db.session.delete(user)
    db.session.commit()
    time.sleep(1)
    result = client.post(
        "/users/auth/refresh",
        content_type="application/json",
        headers={"Authorization": f"Bearer {calling_token}"},
    )
    assert result.status_code == 401
def test_refresh_max_exp(client, user):
    db.session.add(user)
    db.session.commit()
    calling_token = make_jwt(user, datetime.now()).decode("utf-8")

    # Renewing a token that is older than its max-exp returns a 401
    with freeze_time("2020-05-06"):
        result = client.post(
            "/users/auth/refresh",
            content_type="application/json",
            headers={"Authorization": f"Bearer {calling_token}"},
        )
        assert result.status_code == 401
def test_fetch_valid_token(client, user, catalog_entry, monkeypatch):
    # Fetching with valid JWT token succeeds
    db.session.add(user)
    db.session.add(catalog_entry)
    db.session.commit()
    calling_token = make_jwt(user).decode("utf-8")

    result = client.post("/api/v1/fetch",
                         content_type="application/json",
                         headers={"Authorization": f"Bearer {calling_token}"},
                         json={
                             'catalog': 'cfo',
                             'asset_id': 'an-asset-id',
                             'type': 'uri',
                         })
    assert result.status_code == 200