diff --git a/.dockerignore b/.dockerignore index 189e5f8bc38944904dd9cb7099b0380496edf393..034aba89cad57c5f268fcd26572a054dea0c5ceb 100644 --- a/.dockerignore +++ b/.dockerignore @@ -12,3 +12,4 @@ README.md htmlcov app/tests figures/ +.git diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 54400ad1056c08e3f0cee007bc0d54410fd5892a..c4e88ae624498ba60f96aaad4585c7d4ac93b913 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,7 +9,7 @@ variables: CEPH_USERNAME: "" FF_NETWORK_PER_BUILD: 1 PUBLIC_KEY_VALUE: "empty" - AUTHZ_ENDPOINT: "http://127.0.0.1:8001" + OPA_URI: "http://127.0.0.1:8181" SLURM_TOKEN: "empty" SLURM_ENDPOINT: "http://127.0.0.1:8002" ACTIVE_WORKFLOW_EXECUTION_LIMIT: 3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d74f92ba7ce1395d44b5fe431b7f6ecb85d164cb..b0c5251a83107b080366ebfd6da4ce551e98283f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,11 +21,11 @@ repos: files: app args: [--check] - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: 'v0.0.261' + rev: 'v0.0.262' hooks: - id: ruff - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.1.1 + rev: v1.2.0 hooks: - id: mypy files: app diff --git a/README.md b/README.md index 9de9129b8041b2842cee1d3ad1f385605309f39b..534f2693216da226fc1ce74c980a0215006b758e 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ This is the Workflow service of the CloWM service. | `OBJECT_GATEWAY_URI` | unset | HTTP URL | HTTP URL of the Ceph Object Gateway | | `CEPH_ACCESS_KEY` | unset | \<access key> | Ceph access key with admin privileges | | `CEPH_SECRET_KEY` | unset | \<secret key> | Ceph secret key with admin privileges | -| `AUTHZ_ENDPOINT` | unset | HTTP URL | HTTP URL to ask the Auth Service for Authorization | +| `OPA_URI` | unset | HTTP URL | HTTP URL of the OPA service | | `SLURM_ENDPOINT` | unset | HTTP URL | HTTP URL to communicate with the Slurm cluster | | `SLURM_TOKEN` | unset | \<JWT> | JWT for communication with the Slurm REST API. Should belong to the user of the `SLURM_USER` | @@ -40,6 +40,8 @@ This is the Workflow service of the CloWM service. | `SLURM_WORKING_DIRECTORY` | `/tmp` | Path on slurm cluster | Working directory for the slurm job with the nextflow command | | `ACTIVE_WORKFLOW_EXECUTION_LIMIT` | `3` | Integer | Limit of active workflow execution a user is allowed to have. `-1` means infinite. | | `DEV_SYSTEM` | `False` | `<"True"|"False">` | Activates an endpoint that allows execution of an workflow from an arbitrary Git Repository.<br>HAS TO BE `False` in PRODUCTION! | +| `OPA_POLICY_PATH` | `/clowm/authz/allow` | URL path | Path to the OPA Policy for Authorization | + ### Nextflow Variables diff --git a/app/api/dependencies.py b/app/api/dependencies.py index 4c73879231336b7f6951551a48b01b4a9e12e4fb..eeba6f3cdb672e54ff8b78f263d33ebf291ad431 100644 --- a/app/api/dependencies.py +++ b/app/api/dependencies.py @@ -128,7 +128,7 @@ class AuthorizationDependency: def __call__( self, - token: HTTPAuthorizationCredentials = Depends(bearer_token), + token: JWT = Depends(decode_bearer_token), client: AsyncClient = Depends(get_httpx_client), ) -> Callable[[str], Awaitable[AuthzResponse]]: """ @@ -136,8 +136,8 @@ class AuthorizationDependency: Parameters ---------- - token : fastapi.security.http.HTTPAuthorizationCredentials - Bearer token sent with the HTTP request. Dependency Injection. + token : app.schemas.security.JWT + The verified and decoded JWT. Dependency Injection. client : httpx.AsyncClient HTTP Client with an open connection. Dependency Injection. @@ -148,7 +148,7 @@ class AuthorizationDependency: """ async def authorization_wrapper(operation: str) -> AuthzResponse: - params = AuthzRequest(operation=operation, resource=self.resource, token=token.credentials) + params = AuthzRequest(operation=operation, resource=self.resource, uid=token.sub) return await request_authorization(request_params=params, client=client) return authorization_wrapper diff --git a/app/core/config.py b/app/core/config.py index d5e1e7edd5085363cd44b7a56b0c59877381d9d0..ddd9d4fb1bed6fe9e49dd72d427fd4499327403a 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -74,7 +74,8 @@ class Settings(BaseSettings): OBJECT_GATEWAY_URI: AnyHttpUrl = Field(..., description="URI of the Ceph Object Gateway.") CEPH_ACCESS_KEY: str = Field(..., description="Access key for the Ceph Object Gateway with admin privileges.") CEPH_SECRET_KEY: str = Field(..., description="Secret key for the Ceph Object Gateway with admin privileges.") - AUTHZ_ENDPOINT: AnyHttpUrl = Field(..., description="Endpoint of the CloWM Auth service to authorize requests") + OPA_URI: AnyHttpUrl = Field(..., description="URI of the OPA Service") + OPA_POLICY_PATH: str = Field("/clowm/authz/allow", description="Path to the OPA Policy for Authorization") PARAMS_BUCKET: str = Field("nxf-params", description="Bucket where the nextflow configurations should be saved") WORKFLOW_BUCKET: str = Field("clowm-workflows", description="Bucket where to save workflow files") ICON_BUCKET: str = Field("clowm-icons", description="Bucket where to save workflow icons") diff --git a/app/core/security.py b/app/core/security.py index 1dbbcbfbd04132d870031a7c44c97cdd2fbefe3e..f34d133aefcc09f58909bdd4204b001bdb7cc61a 100644 --- a/app/core/security.py +++ b/app/core/security.py @@ -53,7 +53,10 @@ async def request_authorization(request_params: AuthzRequest, client: AsyncClien response : app.schemas.security.AuthzResponse Response by the Auth service about the authorization request """ - response = await client.post(settings.AUTHZ_ENDPOINT, json=request_params.dict()) + response = await client.post( + f"{settings.OPA_URI}/v1/data{settings.OPA_POLICY_PATH}", json={"input": request_params.dict()} + ) + parsed_response = AuthzResponse(**response.json()) if not parsed_response.result: # pragma: no cover raise HTTPException( diff --git a/app/schemas/security.py b/app/schemas/security.py index 65e591c787638509e1e4edd7055c0b802962c790..5bd3799182272053553920c247ddf47010b7e177 100644 --- a/app/schemas/security.py +++ b/app/schemas/security.py @@ -17,7 +17,7 @@ class AuthzResponse(BaseModel): class AuthzRequest(BaseModel): """Schema for a Request to OPA""" - token: str = Field(..., description="JWT Token send by user in request") + uid: str = Field(..., description="UID of user", example="28c5353b8bb34984a8bd4169ba94c606") operation: str = Field(..., description="Operation the user wants to perform", example="read") resource: str = Field(..., description="Resource the operation should be performed on", example="bucket") diff --git a/app/tests/utils/utils.py b/app/tests/utils/utils.py index db275455364ace1224ed608ddbee41804f116851..584503d63799d69eaf5e9f4403e665a3a890d286 100644 --- a/app/tests/utils/utils.py +++ b/app/tests/utils/utils.py @@ -94,8 +94,8 @@ def handle_http_request(request: httpx.Request, raise_error: bool = False) -> ht Generated mock request. """ url = str(request.url) - if url.startswith(settings.AUTHZ_ENDPOINT): - request_body: dict[str, str] = eval(request.content.decode("utf-8")) + if url.startswith(settings.OPA_URI): + request_body: dict[str, str] = eval(request.content.decode("utf-8"))["input"] return auth_handle_request(body=request_body) elif url.startswith(settings.SLURM_ENDPOINT): return slurm_handle_request(request.method) diff --git a/requirements-dev.txt b/requirements-dev.txt index 7a2394487339991fad7f821895c0fa438a92a044..822a0542b20fe265f8dbf49556080ef11d56e4e9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,5 +1,5 @@ # test packages -pytest>=7.2.0,<7.3.0 +pytest>=7.3.0,<7.4.0 pytest-asyncio>=0.21.0,<0.22.0 pytest-cov>=4.0.0,<4.1.0 coverage[toml]>=7.2.0,<7.3.0 @@ -7,7 +7,7 @@ coverage[toml]>=7.2.0,<7.3.0 ruff black>=23.03.0,<23.04.0 isort>=5.12.0,<5.13.0 -mypy>=1.1.0,<1.2.0 +mypy>=1.2.0,<1.3.0 # stubs for mypy boto3-stubs-lite[s3]>=1.26.0,<1.27.0 sqlalchemy2-stubs diff --git a/requirements.txt b/requirements.txt index 81318682c4711bb3b0627a59e18c8caec31801bf..47d8fb8e74646b8af7b7a737451617daa5b30dd4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,7 +17,7 @@ authlib>=1.2.0,<1.3.0 boto3>=1.26.0,<1.27.0 # Miscellaneous tenacity>=8.1.0,<8.2.0 -httpx>=0.23.0,<0.24.0 +httpx>=0.24.0,<0.25.0 itsdangerous jsonschema>=4.0.0,<5.0.0 mako