Ejemplo n.º 1
0
async def test_42cbae986650_upgrade(resource_path):
    # before "Add policy_id and revoke columns to request table" migration
    alembic_main(["--raiseerr", "downgrade", "c0a92da5ac69"])

    # insert a request
    uuid = "571c6a1a-f21f-11ea-adc1-0242ac120002"
    date = str(datetime.now())
    sql_resource_path = resource_path.replace("'", "''")  # escape single quotes
    insert_stmt = f"INSERT INTO requests(\"request_id\", \"username\", \"resource_path\", \"resource_id\", \"resource_display_name\", \"status\", \"created_time\", \"updated_time\") VALUES ('{uuid}', 'username', '{sql_resource_path}', 'my_resource', 'My Resource', 'DRAFT', '{date}', '{date}')"
    await db.scalar(db.text(insert_stmt))

    # check that the request data was inserted correctly
    data = await db.all(db.text("SELECT * FROM requests"))
    request = {k: str(v) for row in data for k, v in row.items()}
    assert request == {
        "request_id": uuid,
        "username": "******",
        "resource_id": "my_resource",
        "resource_display_name": "My Resource",
        "status": "DRAFT",
        "resource_path": resource_path,
        "created_time": date,
        "updated_time": date,
    }

    # run "Add policy_id and revoke columns to request table" migration
    alembic_main(["--raiseerr", "upgrade", "42cbae986650"])

    # check that the migration updated the request data correctly
    data = await db.all(db.text("SELECT * FROM requests"))
    assert len(data) == 1
    request = {k: v for k, v in data[0].items()}
    assert resource_path not in request
    assert request["policy_id"] == get_auto_policy_id_for_resource_path(resource_path)
    assert request["revoke"] == False
Ejemplo n.º 2
0
def upgrade():
    # get the list of existing policies from Arborist
    if not config["LOCAL_MIGRATION"]:
        existing_policies = list_policies(arborist_client)

    # add the new columns
    op.add_column("requests", sa.Column("policy_id", sa.String()))
    op.add_column("requests", sa.Column("revoke", Boolean))

    # get the list of existing resource_paths in the database
    connection = op.get_bind()
    results = connection.execute("SELECT resource_path FROM requests").fetchall()
    existing_resource_paths = set(r[0] for r in results)

    # add the `policy_id` corresponding to each row's `resource_path`
    # and default `revoke` to False
    for resource_path in existing_resource_paths:
        policy_id = get_auto_policy_id_for_resource_path(resource_path)
        if not config["LOCAL_MIGRATION"] and policy_id not in existing_policies:
            create_arborist_policy(arborist_client, resource_path)
        connection.execute(
            f"UPDATE requests SET policy_id='{escape(policy_id)}', revoke=False WHERE resource_path='{escape(resource_path)}'"
        )

    # now that there are no null values, make the columns non-nullable
    op.alter_column("requests", "policy_id", nullable=False)
    op.alter_column("requests", "revoke", nullable=False)

    # drop the `resource_path` column, replaced by `policy_id`
    op.drop_column("requests", "resource_path")
Ejemplo n.º 3
0
def test_create_request_with_redirect_resource_paths(client):
    """
    When a redirect is configured for the requested resource, a
    redirect URL should be returned to the client.
    (creating with resource_paths)
    """
    # create a request
    data = {
        "username": "******",
        "resource_path": "/resource-with-redirect/resource",
        "resource_id": "uniqid",
        "resource_display_name": "My Resource",
    }
    res = client.post("/request", json=data)

    assert res.status_code == 201, res.text
    request_data = res.json()
    request_id = request_data.get("request_id")
    assert request_id, "POST /request did not return a request_id"
    assert request_data == {
        "request_id": request_id,
        "username": data["username"],
        "policy_id":
        get_auto_policy_id_for_resource_path(data["resource_path"]),
        "resource_id": data["resource_id"],
        "resource_display_name": data["resource_display_name"],
        "status": config["DEFAULT_INITIAL_STATUS"],
        "redirect_url":
        f"http://localhost?something=&request_id={request_id}&resource_id={data['resource_id']}&resource_display_name=My+Resource",
        # just ensure revoke, created_time and updated_time are there:
        "revoke": False,
        "created_time": request_data["created_time"],
        "updated_time": request_data["updated_time"],
    }
Ejemplo n.º 4
0
def test_create_request_without_username(client):
    """
    When a username is not provided in the body, the request is created
    using the username from the provided access token.
    """
    fake_jwt = "1.2.3"

    # create a request
    data = {
        "resource_path": "/my/resource",
        "resource_id": "uniqid",
        "resource_display_name": "My Resource",
    }
    res = client.post("/request",
                      json=data,
                      headers={"Authorization": f"bearer {fake_jwt}"})

    assert res.status_code == 201, res.text
    request_data = res.json()
    request_id = request_data.get("request_id")
    assert request_id, "POST /request did not return a request_id"
    assert request_data == {
        "request_id": request_id,
        "username": "******",  # username from access_token_patcher
        "policy_id":
        get_auto_policy_id_for_resource_path(data["resource_path"]),
        "resource_id": data["resource_id"],
        "resource_display_name": data["resource_display_name"],
        "status": config["DEFAULT_INITIAL_STATUS"],
        # just ensure revoke, created_time and updated_time are there:
        "revoke": False,
        "created_time": request_data["created_time"],
        "updated_time": request_data["updated_time"],
    }
Ejemplo n.º 5
0
def list_policies_patcher(test_data):
    """
    This fixture patches the list_policies method with a mock implementation based on
    the test_data provided which is a dictionary consisting of resource_path(s) and
    policy_id wherever appropriate
    """
    resource_paths = (test_data["resource_paths"] if "resource_paths"
                      in test_data else [test_data["resource_path"]])
    expanded_permissions = (test_data["permissions"]
                            if "permissions" in test_data else [{
                                "id": permission,
                                "description": "",
                                "action": {
                                    "service": "*",
                                    "method": permission,
                                },
                            } for permission in ["reader", "storage_reader"]])
    policy_id = (test_data["policy_id"] if "policy_id" in test_data else
                 get_auto_policy_id_for_resource_path(resource_paths[0]))

    future = asyncio.Future()
    future.set_result({
        "policies": [
            {
                "id":
                policy_id,
                "resource_paths":
                resource_paths,
                "roles": [{
                    "id": "reader",
                    "description": "",
                    "permissions": expanded_permissions,
                }],
            },
        ]
    })

    list_policies_mock = MagicMock()
    list_policies_mock.return_value = future
    policy_expand_patch = patch(
        "requestor.routes.query.arborist.list_policies", list_policies_mock)
    policy_expand_patch.start()

    yield

    policy_expand_patch.stop()
Ejemplo n.º 6
0
def test_create_get_and_list_request(client):
    fake_jwt = "1.2.3"

    # list requests: empty
    res = client.get("/request", headers={"Authorization": f"bearer {fake_jwt}"})
    assert res.status_code == 200
    assert res.json() == []

    # create a request
    data = {
        "username": "******",
        "resource_path": "/my/resource",
        "resource_id": "uniqid",
        "resource_display_name": "My Resource",
    }
    res = client.post(
        "/request", json=data, headers={"Authorization": f"bearer {fake_jwt}"}
    )
    assert res.status_code == 201, res.text
    request_data = res.json()
    request_id = request_data.get("request_id")
    assert request_id, "POST /request did not return a request_id"
    assert request_data == {
        "request_id": request_id,
        "username": data["username"],
        "policy_id": get_auto_policy_id_for_resource_path(data["resource_path"]),
        "resource_id": data["resource_id"],
        "resource_display_name": data["resource_display_name"],
        "status": config["DEFAULT_INITIAL_STATUS"],
        # just ensure revoke, created_time and updated_time are there:
        "revoke": False,
        "created_time": request_data["created_time"],
        "updated_time": request_data["updated_time"],
    }

    # get the request
    res = client.get(f"/request/{request_id}")
    assert res.status_code == 200, res.text
    assert res.json() == request_data

    # list requests
    res = client.get("/request", headers={"Authorization": f"bearer {fake_jwt}"})
    assert res.status_code == 200, res.text
    assert res.json() == [request_data]
Ejemplo n.º 7
0
def test_create_duplicate_request(client, data):
    """
    Users can only request access to a resource once.
    (username, resource_path) should be unique, except if other
    requests statuses are in DRAFT_STATUSES or FINAL_STATUSES.
    """
    fake_jwt = "1.2.3"

    # create a request
    data = {
        "username": "******",
        "resource_path": "/my/resource",
        "resource_id": "uniqid",
        "resource_display_name": "My Resource",
    }
    res = client.post("/request",
                      json=data,
                      headers={"Authorization": f"bearer {fake_jwt}"})
    assert res.status_code == 201, res.text
    request_data = res.json()
    request_id = request_data.get("request_id")
    assert request_id, "POST /request did not return a request_id"
    assert request_data == {
        "request_id": request_id,
        "username": data["username"],
        "policy_id":
        get_auto_policy_id_for_resource_path(data["resource_path"]),
        "resource_id": data["resource_id"],
        "resource_display_name": data["resource_display_name"],
        "status": config["DEFAULT_INITIAL_STATUS"],
        # just ensure revoke, created_time and updated_time are there:
        "revoke": False,
        "created_time": request_data["created_time"],
        "updated_time": request_data["updated_time"],
    }

    # create a request with the same username and resource_path.
    # since the previous request is still a draft, it should work.
    res = client.post("/request",
                      json=data,
                      headers={"Authorization": f"bearer {fake_jwt}"})
    assert res.status_code == 201, res.text
    new_request_id = request_data.get("request_id")
    assert new_request_id == request_id

    # update the orignal request's status to a non-final, non-draft status
    res = client.put(f"/request/{request_id}",
                     json={"status": "INTERMEDIATE_STATUS"})
    assert res.status_code == 200, res.text

    # attempt to create a request with the same username and resource_path.
    # it should not work: the previous request is in progress.
    res = client.post("/request",
                      json=data,
                      headers={"Authorization": f"bearer {fake_jwt}"})
    assert res.status_code == 409, res.text

    # update the orignal request's status to a final status
    status = config["FINAL_STATUSES"][-1]
    res = client.put(f"/request/{request_id}", json={"status": status})
    assert res.status_code == 200, res.text

    # create a request with the same username and resource_path.
    # now it should work: the previous request is not in progress anymore.
    res = client.post("/request",
                      json=data,
                      headers={"Authorization": f"bearer {fake_jwt}"})
    assert res.status_code == 201, res.text
Ejemplo n.º 8
0
def test_get_auto_policy_id_for_resource_path(client):
    # single resource_path without policy_id or role_ids
    resource_path = "/study/123456"
    assert (get_auto_policy_id_for_resource_path(resource_path) ==
            "study.123456_accessor")