diff --git a/api/kiwi_vpn_api/easyrsa.py b/api/kiwi_vpn_api/easyrsa.py index a83bbda..e15697b 100644 --- a/api/kiwi_vpn_api/easyrsa.py +++ b/api/kiwi_vpn_api/easyrsa.py @@ -299,7 +299,7 @@ class EasyRSA: dn: DistinguishedName | None = None, ) -> x509.Certificate | None: """ - Issue a client or server certificate + Renew a client or server certificate """ if dn is None: diff --git a/api/kiwi_vpn_api/routers/_common.py b/api/kiwi_vpn_api/routers/_common.py index 45a7bfd..0c2433e 100644 --- a/api/kiwi_vpn_api/routers/_common.py +++ b/api/kiwi_vpn_api/routers/_common.py @@ -23,10 +23,6 @@ class Responses: OK = { "content": None, } - INSTALLED = { - "description": "kiwi-vpn already installed", - "content": None, - } NOT_INSTALLED = { "description": "kiwi-vpn not installed", "content": None, @@ -80,6 +76,8 @@ async def get_current_user( Status: + - (400: `kiwi-vpn` not installed) + - 401: No auth token provided/not logged in - 403: invalid auth token, or user not found """ @@ -103,12 +101,14 @@ async def get_user_by_name( Status: - - 404: user not found + - 403: user not found """ - # fail if device doesn't exist + # don't use error 404 here - possible user enumeration + + # fail if user doesn't exist if (user := User.get(user_name)) is None: - raise HTTPException(status_code=status.HTTP_404_NOT_FOUND) + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN) return user diff --git a/api/kiwi_vpn_api/routers/admin.py b/api/kiwi_vpn_api/routers/admin.py index 5463d23..f7fa78e 100644 --- a/api/kiwi_vpn_api/routers/admin.py +++ b/api/kiwi_vpn_api/routers/admin.py @@ -16,7 +16,7 @@ router = APIRouter(prefix="/admin", tags=["admin"]) "/install/config", responses={ status.HTTP_200_OK: Responses.OK, - status.HTTP_400_BAD_REQUEST: Responses.INSTALLED, + status.HTTP_409_CONFLICT: Responses.ENTRY_EXISTS, }, ) async def initial_configure( @@ -25,11 +25,15 @@ async def initial_configure( ): """ PUT ./install/config: Configure `kiwi-vpn`. + + Status: + + - 409: `kiwi-vpn` already installed """ # fail if already configured if current_config is not None: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST) + raise HTTPException(status_code=status.HTTP_409_CONFLICT) # create config file, connect to database config.save() @@ -39,10 +43,11 @@ async def initial_configure( @router.put( "/install/admin", responses={ - status.HTTP_200_OK: Responses.OK, + status.HTTP_201_CREATED: Responses.OK, status.HTTP_400_BAD_REQUEST: Responses.NOT_INSTALLED, status.HTTP_409_CONFLICT: Responses.ENTRY_EXISTS, }, + status_code=status.HTTP_201_CREATED, ) async def create_initial_admin( admin_user: UserCreate, @@ -50,6 +55,10 @@ async def create_initial_admin( ): """ PUT ./install/admin: Create the first administrative user. + + Status: + + - 409: not the first user """ # fail if any user exists diff --git a/api/kiwi_vpn_api/routers/device.py b/api/kiwi_vpn_api/routers/device.py index 61f4a10..3f78607 100644 --- a/api/kiwi_vpn_api/routers/device.py +++ b/api/kiwi_vpn_api/routers/device.py @@ -19,7 +19,6 @@ router = APIRouter(prefix="/device", tags=["device"]) status.HTTP_400_BAD_REQUEST: Responses.NOT_INSTALLED, status.HTTP_401_UNAUTHORIZED: Responses.NEEDS_USER, status.HTTP_403_FORBIDDEN: Responses.NEEDS_PERMISSION, - status.HTTP_404_NOT_FOUND: Responses.ENTRY_DOESNT_EXIST, status.HTTP_409_CONFLICT: Responses.ENTRY_EXISTS, }, response_model=DeviceRead, @@ -32,6 +31,11 @@ async def add_device( ) -> Device: """ POST ./: Create a new device in the database. + + Status: + + - 403: no user permission to create device + - 409: device creation unsuccessful """ # check permission @@ -59,6 +63,7 @@ async def add_device( status.HTTP_400_BAD_REQUEST: Responses.NOT_INSTALLED, status.HTTP_401_UNAUTHORIZED: Responses.NEEDS_USER, status.HTTP_403_FORBIDDEN: Responses.NEEDS_PERMISSION, + status.HTTP_404_NOT_FOUND: Responses.ENTRY_DOESNT_EXIST, }, response_model=User, ) @@ -68,6 +73,10 @@ async def remove_device( ): """ DELETE ./{device_id}: Remove a device from the database. + + Status: + + - 403: no user permission to edit device """ # check permission @@ -96,6 +105,11 @@ async def request_certificate_issuance( ) -> Device: """ POST ./{device_id}/issue: Request certificate issuance for a device. + + Status: + + - 403: no user permission to edit device + - 409: device certificate cannot be "issued" """ # check permission @@ -139,6 +153,11 @@ async def request_certificate_renewal( ) -> Device: """ POST ./{device_id}/renew: Request certificate renewal for a device. + + Status: + + - 403: no user permission to edit device + - 409: device certificate cannot be "renewed" """ # check permission @@ -182,6 +201,11 @@ async def revoke_certificate( ) -> Device: """ POST ./{device_id}/revoke: Revoke a device certificate. + + Status: + + - 403: no user permission to edit device + - 409: device certificate cannot be "revoked" """ # check permission diff --git a/api/kiwi_vpn_api/routers/user.py b/api/kiwi_vpn_api/routers/user.py index df22934..b75e441 100644 --- a/api/kiwi_vpn_api/routers/user.py +++ b/api/kiwi_vpn_api/routers/user.py @@ -23,13 +23,25 @@ class Token(BaseModel): token_type: str -@router.post("/authenticate", response_model=Token) +@router.post( + "/authenticate", + responses={ + status.HTTP_200_OK: Responses.OK, + status.HTTP_400_BAD_REQUEST: Responses.NOT_INSTALLED, + status.HTTP_401_UNAUTHORIZED: Responses.NEEDS_USER, + }, + response_model=Token, +) async def login( form_data: OAuth2PasswordRequestForm = Depends(), current_config: Config = Depends(get_current_config), ): """ POST ./authenticate: Authenticate a user. Issues a bearer token. + + Status: + + - 401: username/password is incorrect """ # try logging in @@ -49,7 +61,16 @@ async def login( return {"access_token": access_token, "token_type": "bearer"} -@router.get("/current", response_model=UserRead) +@router.get( + "/current", + responses={ + status.HTTP_200_OK: Responses.OK, + status.HTTP_400_BAD_REQUEST: Responses.NOT_INSTALLED, + status.HTTP_401_UNAUTHORIZED: Responses.NEEDS_USER, + status.HTTP_403_FORBIDDEN: Responses.NEEDS_USER, + }, + response_model=UserRead, +) async def get_current_user_route( current_user: User = Depends(get_current_user), ): @@ -78,6 +99,11 @@ async def add_user( ) -> User: """ POST ./: Create a new user in the database. + + Status: + + - 403: no user permission to create user + - 409: user could not be created """ # check permissions @@ -105,7 +131,6 @@ async def add_user( status.HTTP_400_BAD_REQUEST: Responses.NOT_INSTALLED, status.HTTP_401_UNAUTHORIZED: Responses.NEEDS_USER, status.HTTP_403_FORBIDDEN: Responses.NEEDS_PERMISSION, - status.HTTP_404_NOT_FOUND: Responses.ENTRY_DOESNT_EXIST, }, response_model=User, ) @@ -115,6 +140,10 @@ async def remove_user( ): """ DELETE ./{user_name}: Remove a user from the database. + + Status: + + - 403: no user permission to admin user """ # check permissions @@ -142,6 +171,10 @@ async def extend_tags( ): """ POST ./{user_name}/tags: Add tags to a user. + + Status: + + - 403: no user permission to admin user """ # check permissions @@ -169,6 +202,10 @@ async def remove_tags( ): """ DELETE ./{user_name}/tags: Remove tags from a user. + + Status: + + - 403: no user permission to admin user """ # check permissions