diff --git a/backend/config_parser.py b/backend/config_parser.py index 8f6ea69..fe2c42b 100644 --- a/backend/config_parser.py +++ b/backend/config_parser.py @@ -368,6 +368,8 @@ class Config: raw = load_config(config_name) if args == None or not args.err_check else {} self.listen_address = raw.get("backend", {}).get("address", "0.0.0.0") self.listen_port = int(raw.get("backend", {}).get("port", "8080")) + self.auth = raw.get("backend", {}).get("auth", {}).items() + self.recorders = [] i = 0 for raw_server in raw.get("recorders", []): diff --git a/backend/global_funcs.py b/backend/global_funcs.py index b44c81b..5c7ebfa 100644 --- a/backend/global_funcs.py +++ b/backend/global_funcs.py @@ -6,9 +6,12 @@ import uuid import logging +def hexed(string:str): + return hashlib.md5(string.encode("utf8")).hexdigest() + + def uuid_from_string(string:str): - hex_string = hashlib.md5(string.encode("utf8")).hexdigest() - return uuid.UUID(hex=hex_string) + return uuid.UUID(hex=hexed(string)) def app_dir(): diff --git a/backend/server.py b/backend/server.py index 0fc92c1..caf8e97 100644 --- a/backend/server.py +++ b/backend/server.py @@ -1,20 +1,24 @@ -from fastapi import FastAPI, Response, BackgroundTasks, Header +from fastapi import FastAPI, Response, BackgroundTasks, Header, Request from fastapi.responses import StreamingResponse, FileResponse +from fastapi.security import HTTPBasic, HTTPBasicCredentials +from fastapi.exceptions import HTTPException import uvicorn import traceback import aiofiles from config_parser import Config as ConfigParser from config_parser import TranscodeStatus, TranscodeTools, Go2Rtc, NeedNVR -from global_funcs import create_logger +from global_funcs import create_logger, hexed from nvr_core import NVR from nvr_types import File import os +import time class Server: app: FastAPI = FastAPI() config: ConfigParser = ConfigParser() go2rtc: Go2Rtc = Go2Rtc() + security = HTTPBasic() API_BASE_REF = "/api/dvrip" @@ -22,6 +26,7 @@ class Server: self.logger = create_logger(Server.__name__) self.setup_events() self.setup_routers() + self.setup_middleware() def setup_events(self): @self.app.on_event('startup') @@ -35,7 +40,34 @@ class Server: self.logger.info(f"{self.config.recorders[i]} channels count: {self.config.recorders[i].channels}") await self.go2rtc.start_go2rtc(self.config.recorders) + def setup_middleware(self): + if len(self.config.auth) != 0: + self.logger.info("auth is enabled") + + @self.app.middleware("http") + async def auth_handler(request: Request, call_next): + try: + s = await self.security(request) + if s.username not in self.config.auth.mapping or hexed(s.password) != self.config.auth.mapping[s.username]: + raise HTTPException(status_code=401, headers={"WWW-Authenticate": "Basic"}) + except HTTPException as e: + return Response(status_code=e.status_code, headers=e.headers) + + return await call_next(request) + + @self.app.get(self.API_BASE_REF + "/logout") + async def logout(request: Request): + request.scope.update(headers={"Authorization":"b"}) + return Response(status_code=401) + + else: + self.logger.warn("auth is disabled") + def setup_routers(self): + @self.app.get(self.API_BASE_REF + "/ping", status_code=200) + async def getPing(): + return {"pong":time.time()} + @self.app.get(self.API_BASE_REF, status_code=200) async def getRecorders(response: Response): try: diff --git a/frontend/ang_dvrip/src/app/app.component.html b/frontend/ang_dvrip/src/app/app.component.html index dd23fe6..3be95c8 100644 --- a/frontend/ang_dvrip/src/app/app.component.html +++ b/frontend/ang_dvrip/src/app/app.component.html @@ -1,5 +1,17 @@ - + + +

Загрузка...

+
+ + +
+ +

Не авторизован

+ +
+
+
@@ -10,6 +22,8 @@ Закрыть
+ DVRIP Клиент {{version}} +
Выбранный рекордер @@ -26,7 +40,7 @@

Нет доступных каналов


- DVRIP Клиент {{version}} +
@@ -51,21 +65,3 @@
- diff --git a/frontend/ang_dvrip/src/app/app.component.ts b/frontend/ang_dvrip/src/app/app.component.ts index d47d98d..feb34c2 100644 --- a/frontend/ang_dvrip/src/app/app.component.ts +++ b/frontend/ang_dvrip/src/app/app.component.ts @@ -16,9 +16,18 @@ export class AppComponent implements OnInit { selected_channel:number = 0; loading:boolean = true version = version; + authed:number = 0; ngOnInit(): void { - this.getRecorders(); + this.getPing().subscribe( + (a:any) => { + this.authed = 1; + this.getRecorders(); + }, + (e:any) => { + this.authed = -1; + } + ) } constructor(private api:ApiService, @@ -26,6 +35,10 @@ export class AppComponent implements OnInit { private router:Router) { } + getPing() { + return this.http.get("/api/dvrip/ping") + } + getRecorders() { this.loading = true; this.http.get("/api/dvrip", {}).subscribe((a:any) => { @@ -49,4 +62,13 @@ export class AppComponent implements OnInit { goToRoot() { this.router.navigate(["/"]) } + + logout() { + this.http.get("api/dvrip/logout").subscribe(); + this.reload(); + } + + reload() { + location.reload(); + } } diff --git a/frontend/ang_dvrip/src/assets/nonauth.webp b/frontend/ang_dvrip/src/assets/nonauth.webp new file mode 100644 index 0000000..d48158d Binary files /dev/null and b/frontend/ang_dvrip/src/assets/nonauth.webp differ