Skip to content

Commit

Permalink
Config: Add the read_only setting
Browse files Browse the repository at this point in the history
This setting, which is set to `True` by default, will determine whether
the instance is to be read-only. The `protected_methods_middleware`
function is added as middleware to the application. If the `read_only`
setting is `True` and the request method is `DELETE`, `PATCH`, `POST` or
`PUT`, a `405 Method Not Allowed` response is returned.
  • Loading branch information
sphuber committed May 8, 2023
1 parent 520bd52 commit c7aed7c
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 0 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ uvicorn aiida_restapi:app
uvicorn aiida_restapi:app --reload
```

By default all endpoints of the REST API are available.
The API can be made *read-only* by setting the `read_only` configuration settings to `True`.
This can either be done by setting the environment variable:
```bash
export READ_ONLY=True
```
or by adding the following to the `.env` file:
```ini
read_only=true
```
When the API is read-only, all `DELETE`, `PATCH`, `POST` and `PUT` requests will result in a `405 - Method Not Allowed` response.

## Examples

See the [examples](https://github.com/aiidateam/aiida-restapi/tree/master/examples) directory.
Expand Down
11 changes: 11 additions & 0 deletions aiida_restapi/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@
class Settings(BaseSettings):
"""Configuration settings for the application."""

# pylint: disable=too-few-public-methods

class Config:
"""Config settings."""

env_file = ".env"
env_file_encoding = "utf-8"

secret_key: str = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
"""The secret key used to create access tokens."""

Expand All @@ -17,6 +25,9 @@ class Settings(BaseSettings):
access_token_expire_minutes: int = 30
"""The number of minutes an access token remains valid."""

read_only: bool = False
"""Whether the instance is read-only. If set to ``True`` all DELETE, PATCH, POST and PUT methods will raise 405."""


@lru_cache()
def get_settings():
Expand Down
5 changes: 5 additions & 0 deletions aiida_restapi/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
from aiida_restapi.graphql import main
from aiida_restapi.routers import auth, computers, daemon, groups, nodes, process, users

from .middleware import protected_methods_middleware

app = FastAPI()

app.middleware("http")(protected_methods_middleware)

app.include_router(auth.router)
app.include_router(computers.router)
app.include_router(daemon.router)
Expand Down
24 changes: 24 additions & 0 deletions aiida_restapi/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
"""Module with middleware."""
from fastapi import Request
from fastapi.encoders import jsonable_encoder
from fastapi.responses import JSONResponse

from .config import Settings, get_settings


async def protected_methods_middleware(request: Request, call_next):
"""Middleware that will return a 405 if the instance is read only and the request method is mutating.
Mutating request methods are `DELETE`, `PATCH`, `POST`, `PUT`.
"""
settings: Settings = get_settings()

if settings.read_only and request.method in {"DELETE", "PATCH", "POST", "PUT"}:
return JSONResponse(
status_code=405,
content=jsonable_encoder({"reason": "This instance is read-only."}),
media_type="application/json",
)

return await call_next(request)

0 comments on commit c7aed7c

Please sign in to comment.