Skip to content

Commit

Permalink
Merge pull request #29 from bento-platform/fix/list-permissions-err-s…
Browse files Browse the repository at this point in the history
…tate

fix!: bad list permissions error state responses
  • Loading branch information
davidlougheed authored Mar 15, 2024
2 parents 4e29d60 + ba91545 commit 8d1fc60
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 27 deletions.
2 changes: 1 addition & 1 deletion bento_authorization_service/routers/policy/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ async def use_token_data_or_return_error_state(
authorization: OptionalBearerToken,
idp_manager: IdPManager,
err_state: ResponseType,
create_response: Callable[[dict | None], Awaitable[ResponseType]],
create_response: Callable[[TokenData | None], Awaitable[ResponseType]],
) -> ResponseType:
try:
token_data = (await idp_manager.decode(authorization.credentials)) if authorization is not None else None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ async def req_list_permissions(
# Given a token and a resource, figure out what permissions the token bearer has on the resource.
# In general, the evaluation endpoints SHOULD be used unless necessary or for cosmetic purposes (UI rendering).

async def _create_response(token_data: TokenData | None):
async def _create_response(token_data: TokenData | None) -> ListPermissionsResponse:
grants: tuple[StoredGrantModel, ...]
groups: dict[int, StoredGroupModel]
grants, groups = await asyncio.gather(db.get_grants(), db.get_groups_dict())
Expand All @@ -82,7 +82,7 @@ async def _create_response(token_data: TokenData | None):
return await use_token_data_or_return_error_state(
authorization,
idp_manager,
err_state={"result": [list() for _ in r_resources]},
err_state=ListPermissionsResponse(result=[list() for _ in r_resources]),
create_response=_create_response,
)

Expand Down Expand Up @@ -143,6 +143,8 @@ async def _create_response(token_data: TokenData | None):
return await use_token_data_or_return_error_state(
authorization,
idp_manager,
err_state={"result": [dict() for _ in r_resources]},
err_state=PermissionsMapResponse(
result=[{p: False for p in valid_permissions_for_resource(r.model_dump())} for r in r_resources]
),
create_response=_create_response,
)
58 changes: 35 additions & 23 deletions tests/test_evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,39 +307,45 @@ async def test_permissions_endpoint(db: Database, test_client: TestClient, db_cl
assert P_QUERY_DATA in res.json()["result"][0]


PERMISSIONS_RESOURCES_LIST = {
"resources": [
sd.RESOURCE_PROJECT_1.model_dump(mode="json"),
sd.RESOURCE_PROJECT_2.model_dump(mode="json"),
],
}


# noinspection PyUnusedLocal
@pytest.mark.asyncio
async def test_permissions_endpoint_list(db: Database, test_client: TestClient, db_cleanup):
tkn = await _eval_test_data(db)
res = test_client.post(
"/policy/permissions",
headers={"Authorization": f"Bearer {tkn}"},
json={
"resources": [
sd.RESOURCE_PROJECT_1.model_dump(mode="json"),
sd.RESOURCE_PROJECT_2.model_dump(mode="json"),
],
},
"/policy/permissions", headers={"Authorization": f"Bearer {tkn}"}, json=PERMISSIONS_RESOURCES_LIST
)
assert res.status_code == status.HTTP_200_OK
res_json = res.json()
assert P_QUERY_DATA in res_json["result"][0]
assert P_QUERY_DATA not in res_json["result"][1]


# noinspection PyUnusedLocal
@pytest.mark.asyncio
async def test_permissions_endpoint_list_expired_token(db: Database, test_client: TestClient, db_cleanup):
await _eval_test_data(db)
tkn = sd.make_fresh_david_token_encoded(exp_offset=-10)
res = test_client.post(
"/policy/permissions", headers={"Authorization": f"Bearer {tkn}"}, json=PERMISSIONS_RESOURCES_LIST
)
assert res.status_code == status.HTTP_200_OK # 'fine', but no permissions - expired token
assert res.json()["result"] == [[], []]


# noinspection PyUnusedLocal
@pytest.mark.asyncio
async def test_permissions_endpoint_map(db: Database, test_client: TestClient, db_cleanup):
tkn = await _eval_test_data(db)
res = test_client.post(
"/policy/permissions_map",
headers={"Authorization": f"Bearer {tkn}"},
json={
"resources": [
sd.RESOURCE_PROJECT_1.model_dump(mode="json"),
sd.RESOURCE_PROJECT_2.model_dump(mode="json"),
],
},
"/policy/permissions_map", headers={"Authorization": f"Bearer {tkn}"}, json=PERMISSIONS_RESOURCES_LIST
)
assert res.status_code == status.HTTP_200_OK
res_json = res.json()
Expand All @@ -349,6 +355,18 @@ async def test_permissions_endpoint_map(db: Database, test_client: TestClient, d
assert not res_json["result"][1][P_QUERY_DATA]


# noinspection PyUnusedLocal
@pytest.mark.asyncio
async def test_permissions_endpoint_map_expired_token(db: Database, test_client: TestClient, db_cleanup):
await _eval_test_data(db)
tkn = sd.make_fresh_david_token_encoded(exp_offset=-10)
res = test_client.post(
"/policy/permissions_map", headers={"Authorization": f"Bearer {tkn}"}, json=PERMISSIONS_RESOURCES_LIST
)
assert res.status_code == status.HTTP_200_OK # 'fine', but no permissions - expired token
assert not res.json()["result"][0][P_QUERY_DATA]


# noinspection PyUnusedLocal
@pytest.mark.asyncio
async def test_evaluate_endpoint(db: Database, test_client: TestClient, db_cleanup):
Expand Down Expand Up @@ -380,13 +398,7 @@ async def test_evaluate_one_endpoint(db: Database, test_client: TestClient, db_c
assert r


TWO_PROJECT_DATA_QUERY = {
"resources": [
sd.RESOURCE_PROJECT_1.model_dump(mode="json"),
sd.RESOURCE_PROJECT_2.model_dump(mode="json"),
],
"permissions": [P_QUERY_DATA],
}
TWO_PROJECT_DATA_QUERY = {**PERMISSIONS_RESOURCES_LIST, "permissions": [P_QUERY_DATA]}


# noinspection PyUnusedLocal
Expand Down

0 comments on commit 8d1fc60

Please sign in to comment.