Skip to content

Commit 8f53c08

Browse files
authored
Merge branch 'permitio:main' into fix/correct-resource-names
2 parents b33612f + 8f66328 commit 8f53c08

40 files changed

+190
-373
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: help build prepare
1+
.PHONY: help build prepare build-amd64 build-arm64
22

33
.DEFAULT_GOAL := help
44

charts/pdp/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
apiVersion: v2
22
name: pdp
33
description: An official Helm chart for Permit.io PDP (Policy Decision Point)
4-
version: 0.0.4
4+
version: 0.0.4

horizon/facts/client.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Annotated
1+
from typing import Annotated, Any
22
from urllib.parse import urljoin
33

44
from fastapi import Depends, HTTPException
@@ -30,7 +30,13 @@ def client(self) -> AsyncClient:
3030
)
3131
return self._client
3232

33-
async def build_forward_request(self, request: FastApiRequest, path: str) -> HttpxRequest:
33+
async def build_forward_request(
34+
self,
35+
request: FastApiRequest,
36+
path: str,
37+
*,
38+
query_params: dict[str, Any] | None = None,
39+
) -> HttpxRequest:
3440
"""
3541
Build an HTTPX request from a FastAPI request to forward to the facts service.
3642
:param request: FastAPI request
@@ -50,10 +56,11 @@ async def build_forward_request(self, request: FastApiRequest, path: str) -> Htt
5056
)
5157

5258
full_path = urljoin(f"/v2/facts/{project_id}/{environment_id}/", path.removeprefix("/"))
59+
_query_params = {**request.query_params, **(query_params or {})}
5360
return self.client.build_request(
5461
method=request.method,
5562
url=full_path,
56-
params=request.query_params,
63+
params=_query_params,
5764
headers=forward_headers,
5865
content=request.stream(),
5966
)
@@ -62,18 +69,28 @@ async def send(self, request: HttpxRequest, *, stream: bool = False) -> HttpxRes
6269
logger.info(f"Forwarding facts request: {request.method} {request.url}")
6370
return await self.client.send(request, stream=stream)
6471

65-
async def send_forward_request(self, request: FastApiRequest, path: str) -> HttpxResponse:
72+
async def send_forward_request(
73+
self,
74+
request: FastApiRequest,
75+
path: str,
76+
*,
77+
query_params: dict[str, Any] | None = None,
78+
) -> HttpxResponse:
6679
"""
6780
Send a forward request to the facts service.
6881
:param request: FastAPI request
6982
:param path: Backend facts service path to forward to
7083
:return: HTTPX response
7184
"""
72-
forward_request = await self.build_forward_request(request, path)
85+
forward_request = await self.build_forward_request(request, path, query_params=query_params)
7386
return await self.send(forward_request)
7487

7588
@staticmethod
76-
def convert_response(response: HttpxResponse, *, stream: bool = False) -> FastApiResponse:
89+
def convert_response(
90+
response: HttpxResponse,
91+
*,
92+
stream: bool = False,
93+
) -> FastApiResponse:
7794
"""
7895
Convert an HTTPX response to a FastAPI response.
7996
:param response: HTTPX response
@@ -103,6 +120,8 @@ def extract_body(response: HttpxResponse):
103120
return None
104121

105122
try:
123+
if response.status_code == 204:
124+
return None
106125
body = response.json()
107126
except Exception: # noqa: BLE001
108127
logger.exception("Failed to parse response body as JSON, skipping wait for update.")

horizon/facts/router.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,27 @@ async def assign_user_role(
192192
)
193193

194194

195+
@facts_router.delete("/users/{user_id}/roles")
196+
async def unassign_user_role(
197+
request: FastApiRequest,
198+
client: FactsClientDependency,
199+
update_subscriber: DataUpdateSubscriberDependency,
200+
wait_timeout: WaitTimeoutDependency,
201+
timeout_policy: TimeoutPolicyDependency,
202+
user_id: str,
203+
):
204+
return await forward_request_then_wait_for_update(
205+
client,
206+
request,
207+
update_subscriber,
208+
wait_timeout,
209+
path=f"/users/{user_id}/roles",
210+
query_params={"return_deleted": True},
211+
entries_callback=create_role_assignment_data_entries,
212+
timeout_policy=timeout_policy,
213+
)
214+
215+
195216
@facts_router.post("/role_assignments")
196217
async def create_role_assignment(
197218
request: FastApiRequest,
@@ -211,6 +232,26 @@ async def create_role_assignment(
211232
)
212233

213234

235+
@facts_router.delete("/role_assignments")
236+
async def delete_role_assignment(
237+
request: FastApiRequest,
238+
client: FactsClientDependency,
239+
update_subscriber: DataUpdateSubscriberDependency,
240+
wait_timeout: WaitTimeoutDependency,
241+
timeout_policy: TimeoutPolicyDependency,
242+
):
243+
return await forward_request_then_wait_for_update(
244+
client,
245+
request,
246+
update_subscriber,
247+
wait_timeout,
248+
path="/role_assignments",
249+
entries_callback=create_role_assignment_data_entries,
250+
timeout_policy=timeout_policy,
251+
query_params={"return_deleted": True},
252+
)
253+
254+
214255
@facts_router.post("/resource_instances")
215256
async def create_resource_instance(
216257
request: FastApiRequest,
@@ -293,6 +334,12 @@ async def create_relationship_tuple(
293334
)
294335

295336

337+
def cast_delete_200_to_204(response: Response) -> Response:
338+
if response.status_code == 200:
339+
return Response(status_code=204)
340+
return response
341+
342+
296343
async def forward_request_then_wait_for_update(
297344
client: FactsClient,
298345
request: FastApiRequest,
@@ -303,9 +350,10 @@ async def forward_request_then_wait_for_update(
303350
update_id: UUID | None = None,
304351
entries_callback: Callable[[FastApiRequest, dict[str, Any], UUID | None], Iterable[DataSourceEntry]],
305352
timeout_policy: TimeoutPolicy = TimeoutPolicy.IGNORE,
353+
query_params: dict[str, Any] | None = None,
306354
) -> Response:
307355
_update_id = update_id or uuid4()
308-
response = await client.send_forward_request(request, path)
356+
response = await client.send_forward_request(request, path, query_params=query_params)
309357
body = client.extract_body(response)
310358
if body is None:
311359
return client.convert_response(response)

pdp-server/src/api/authn_middleware.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@ pub(super) async fn authentication_middleware(
3333
header_str[7..].to_string()
3434
}
3535
Ok(header_str) => {
36-
warn!(
37-
"Invalid Authorization header format, missing 'Bearer ' prefix: {}",
38-
header_str
39-
);
36+
warn!("Invalid Authorization header format, missing 'Bearer ' prefix: {header_str}");
4037
return Response::builder()
4138
.status(StatusCode::FORBIDDEN)
4239
.body(
@@ -46,7 +43,7 @@ pub(super) async fn authentication_middleware(
4643
.expect("Failed to create response");
4744
}
4845
Err(e) => {
49-
warn!("Failed to parse Authorization header to string: {}", e);
46+
warn!("Failed to parse Authorization header to string: {e}");
5047
return Response::builder()
5148
.status(StatusCode::FORBIDDEN)
5249
.body(

pdp-server/src/api/authz/allowed.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub(super) async fn allowed_handler(
3636
match query_allowed_cached(&state, &query, &cache_control).await {
3737
Ok(result) => (StatusCode::OK, Json(result)).into_response(),
3838
Err(err) => {
39-
log::error!("Failed to send request to OPA: {}", err);
39+
log::error!("Failed to send request to OPA: {err}");
4040
ApiError::from(err).into_response()
4141
}
4242
}

pdp-server/src/api/authz/allowed_bulk.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub(super) async fn allowed_bulk_handler(
3636
match query_allowed_bulk_cached(&state, &queries, &cache_control).await {
3737
Ok(result) => (StatusCode::OK, Json(result)).into_response(),
3838
Err(err) => {
39-
log::error!("Failed to send request to OPA: {}", err);
39+
log::error!("Failed to send request to OPA: {err}");
4040
ApiError::from(err).into_response()
4141
}
4242
}

pdp-server/src/api/authz/authorized_users.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub(super) async fn authorized_users_handler(
3939
match query_authorized_users_cached(&state, &query, &cache_control).await {
4040
Ok(result) => (StatusCode::OK, Json(result)).into_response(),
4141
Err(err) => {
42-
log::error!("Failed to send request to OPA: {}", err);
42+
log::error!("Failed to send request to OPA: {err}");
4343
ApiError::from(err).into_response()
4444
}
4545
}

pdp-server/src/api/authz/user_permissions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pub(super) async fn user_permissions_handler(
5656
let permissions = match query_user_permissions_cached(&state, &query, &cache_control).await {
5757
Ok(permissions) => permissions,
5858
Err(err) => {
59-
log::error!("Failed to send request to OPA: {}", err);
59+
log::error!("Failed to send request to OPA: {err}");
6060
return ApiError::from(err).into_response();
6161
}
6262
};

pdp-server/src/api/authzen/errors.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl From<crate::errors::ApiError> for AuthZenError {
8888
StatusCode::FORBIDDEN => AuthZenError::forbidden("Access denied"),
8989
StatusCode::BAD_REQUEST => AuthZenError::invalid_request(err.detail),
9090
_ => {
91-
log::error!("Internal error converted to AuthZen format: {:?}", err);
91+
log::error!("Internal error converted to AuthZen format: {err:?}");
9292
AuthZenError::internal_error("Internal server error")
9393
}
9494
}
@@ -98,7 +98,7 @@ impl From<crate::errors::ApiError> for AuthZenError {
9898
/// Convert OPA forwarding errors to AuthZen format
9999
impl From<crate::opa_client::ForwardingError> for AuthZenError {
100100
fn from(err: crate::opa_client::ForwardingError) -> Self {
101-
log::error!("OPA forwarding error: {:?}", err);
101+
log::error!("OPA forwarding error: {err:?}");
102102
// Use generic message to avoid leaking internal implementation details
103103
AuthZenError::internal_error("Internal server error")
104104
}

0 commit comments

Comments
 (0)