diff --git a/api/kiwi_vpn_api/config.py b/api/kiwi_vpn_api/config.py index 960cdd8..41ab5ae 100644 --- a/api/kiwi_vpn_api/config.py +++ b/api/kiwi_vpn_api/config.py @@ -1,5 +1,13 @@ -from peewee import SqliteDatabase +from __future__ import annotations + +from enum import Enum +from pathlib import Path +from typing import Optional + +from jose.constants import ALGORITHMS from passlib.context import CryptContext +from peewee import Database, SqliteDatabase +from pydantic import BaseModel, Field DB = SqliteDatabase("tmp/vpn.db") PRODUCTION_MODE = False @@ -11,3 +19,42 @@ ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 CRYPT_CONTEXT = CryptContext(schemes=["bcrypt"], deprecated="auto") + +CONFIG_FILE = "tmp/config.json" + + +class DBType(Enum): + sqlite = "sqlite" + mysql = "mysql" + + +class DBConfig(BaseModel): + db_type: DBType = "sqlite" + + @property + def database(self) -> Database: + return SqliteDatabase("tmp/vpn.db") + + +class JWTConfig(BaseModel): + secret: Optional[str] = None + hash_algorithm: str = ALGORITHMS.HS256 + expiry_minutes: int = 30 + + +class CryptoConfig(BaseModel): + schemes: list[str] = ["bcrypt"] + + @property + def cryptContext(self) -> CryptContext: + return CryptContext(schemes=self.schemes, deprecated="auto") + + +class BaseConfig(BaseModel): + db: DBConfig = Field(default_factory=DBConfig) + jwt: JWTConfig = Field(default_factory=JWTConfig) + crypto: CryptoConfig = Field(default_factory=CryptoConfig) + + def save(self, filename: Path) -> None: + with open(filename, "w") as kv: + kv.write(self.json(indent=2)) diff --git a/api/kiwi_vpn_api/routers/install.py b/api/kiwi_vpn_api/routers/install.py index 52a36ba..22486c9 100644 --- a/api/kiwi_vpn_api/routers/install.py +++ b/api/kiwi_vpn_api/routers/install.py @@ -1,12 +1,75 @@ +import json +from pathlib import Path + from fastapi import APIRouter, Depends, status from fastapi.responses import JSONResponse -from ..config import CRYPT_CONTEXT, DB +from ..config import CONFIG_FILE, CRYPT_CONTEXT, DB, BaseConfig from ..db import Certificate, DistinguishedName, User, UserCapability router = APIRouter(prefix="/install") +async def get_default_config() -> BaseConfig: + return BaseConfig() + + +async def get_config() -> BaseConfig: + with open(CONFIG_FILE, "r") as kv: + return BaseConfig.parse_obj(json.load(kv)) + + +async def is_configured() -> bool: + return Path(CONFIG_FILE).is_file() + + +@router.get("/config/get_default", response_model=BaseConfig) +async def config_get_default(config: BaseConfig = Depends(get_default_config)): + return config + + +@router.get( + "/config/get", + response_model=BaseConfig, + responses={ + status.HTTP_404_NOT_FOUND: { + "description": "Not configured", + "content": None, + }, + }, +) +async def config_get( + config: BaseConfig = Depends(get_config), + is_configured: bool = Depends(is_configured), +): + if not is_configured: + return JSONResponse(status_code=status.HTTP_404_NOT_FOUND) + + return config + + +@router.post( + "/config/set", + responses={ + status.HTTP_200_OK: { + "content": None, + }, + status.HTTP_403_FORBIDDEN: { + "description": "Must be admin", + "content": None, + }, + }, +) +async def config_set( + config: BaseConfig, + is_configured: bool = Depends(is_configured), +): + if is_configured: + return JSONResponse(status_code=status.HTTP_403_FORBIDDEN) + + config.save(CONFIG_FILE) + + async def is_installed(): return DB.table_exists(User) @@ -20,16 +83,18 @@ async def check_installed(is_installed: bool = Depends(is_installed)): return is_installed -@router.get("/create_db", responses={ - status.HTTP_200_OK: { - "description": "Database created", - "content": None, +@router.get( + "/create_db", + responses={ + status.HTTP_200_OK: { + "content": None, + }, + status.HTTP_400_BAD_REQUEST: { + "description": "Could not create Database", + "content": None, + }, }, - status.HTTP_400_BAD_REQUEST: { - "description": "Could not create Database", - "content": None, - } -}) +) async def create_db(is_installed: bool = Depends(is_installed)): if is_installed: return JSONResponse(