diff --git a/diracx-client/src/diracx/client/aio/operations/_operations.py b/diracx-client/src/diracx/client/aio/operations/_operations.py index 439c615cd..ba44dffc9 100644 --- a/diracx-client/src/diracx/client/aio/operations/_operations.py +++ b/diracx-client/src/diracx/client/aio/operations/_operations.py @@ -29,6 +29,7 @@ build_auth_do_device_flow_request, build_auth_finish_device_flow_request, build_auth_finished_request, + build_auth_get_proxy_request, build_auth_get_refresh_tokens_request, build_auth_initiate_device_flow_request, build_auth_revoke_refresh_token_request, @@ -763,6 +764,57 @@ async def userinfo(self, **kwargs: Any) -> _models.UserInfoResponse: return deserialized + @distributed_trace_async + async def get_proxy(self, **kwargs: Any) -> Any: + """Get Proxy. + + Get Proxy. + + :return: any + :rtype: any + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Any] = kwargs.pop("cls", None) + + request = build_auth_get_proxy_request( + headers=_headers, + params=_params, + ) + request.url = self._client.format_url(request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + await self._client._pipeline.run( # pylint: disable=protected-access + request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error( + status_code=response.status_code, response=response, error_map=error_map + ) + raise HttpResponseError(response=response) + + deserialized = self._deserialize("object", pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + class ConfigOperations: """ diff --git a/diracx-client/src/diracx/client/operations/_operations.py b/diracx-client/src/diracx/client/operations/_operations.py index cbb3e8f98..8f2dca6b3 100644 --- a/diracx-client/src/diracx/client/operations/_operations.py +++ b/diracx-client/src/diracx/client/operations/_operations.py @@ -263,6 +263,20 @@ def build_auth_userinfo_request(**kwargs: Any) -> HttpRequest: return HttpRequest(method="GET", url=_url, headers=_headers, **kwargs) +def build_auth_get_proxy_request(**kwargs: Any) -> HttpRequest: + _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) + + accept = _headers.pop("Accept", "application/json") + + # Construct URL + _url = "/api/auth/proxy" + + # Construct headers + _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") + + return HttpRequest(method="GET", url=_url, headers=_headers, **kwargs) + + def build_config_serve_config_request( vo: str, *, @@ -1480,6 +1494,57 @@ def userinfo(self, **kwargs: Any) -> _models.UserInfoResponse: return deserialized + @distributed_trace + def get_proxy(self, **kwargs: Any) -> Any: + """Get Proxy. + + Get Proxy. + + :return: any + :rtype: any + :raises ~azure.core.exceptions.HttpResponseError: + """ + error_map = { + 401: ClientAuthenticationError, + 404: ResourceNotFoundError, + 409: ResourceExistsError, + 304: ResourceNotModifiedError, + } + error_map.update(kwargs.pop("error_map", {}) or {}) + + _headers = kwargs.pop("headers", {}) or {} + _params = kwargs.pop("params", {}) or {} + + cls: ClsType[Any] = kwargs.pop("cls", None) + + request = build_auth_get_proxy_request( + headers=_headers, + params=_params, + ) + request.url = self._client.format_url(request.url) + + _stream = False + pipeline_response: PipelineResponse = ( + self._client._pipeline.run( # pylint: disable=protected-access + request, stream=_stream, **kwargs + ) + ) + + response = pipeline_response.http_response + + if response.status_code not in [200]: + map_error( + status_code=response.status_code, response=response, error_map=error_map + ) + raise HttpResponseError(response=response) + + deserialized = self._deserialize("object", pipeline_response) + + if cls: + return cls(pipeline_response, deserialized, {}) + + return deserialized + class ConfigOperations: """ diff --git a/diracx-routers/src/diracx/routers/auth.py b/diracx-routers/src/diracx/routers/auth.py index 26f171ec0..08cfa2c99 100644 --- a/diracx-routers/src/diracx/routers/auth.py +++ b/diracx-routers/src/diracx/routers/auth.py @@ -34,6 +34,7 @@ DiracHttpResponse, ExpiredFlowError, PendingAuthorizationError, + ProxyNotFoundError, ) from diracx.core.models import TokenResponse, UserInfo from diracx.core.properties import ( @@ -48,6 +49,7 @@ AuthDB, AvailableSecurityProperties, Config, + ProxyDB, add_settings_annotation, ) from .fastapi_classes import DiracxRouter @@ -1119,3 +1121,31 @@ async def legacy_exchange( refresh_token_expire_minutes=expires_minutes, legacy_exchange=True, ) + + +@router.get("/proxy") +async def get_proxy( + proxy_db: ProxyDB, + user_info: Annotated[AuthorizedUserInfo, Depends(verify_dirac_access_token)], + config: Config, +): + voms_role = config.Registry[user_info.vo].Groups[user_info.dirac_group].VOMSRole + user_dns = config.Registry[user_info.vo].Users[user_info.sub].DNs + + lifetime_seconds = 3600 + for user_dn in user_dns: + try: + return await proxy_db.get_proxy( + user_dn, + user_info.vo, + user_info.dirac_group, + voms_role, + lifetime_seconds, + ) + except ProxyNotFoundError: + pass + else: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"No available proxy for {user_info.sub}", + )