Browse Source

auth

master
gsd 8 months ago
parent
commit
fcf459f9d7
  1. 2
      backend/config_parser.py
  2. 7
      backend/global_funcs.py
  3. 36
      backend/server.py
  4. 36
      frontend/ang_dvrip/src/app/app.component.html
  5. 24
      frontend/ang_dvrip/src/app/app.component.ts
  6. BIN
      frontend/ang_dvrip/src/assets/nonauth.webp

2
backend/config_parser.py

@ -368,6 +368,8 @@ class Config:
raw = load_config(config_name) if args == None or not args.err_check else {} 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_address = raw.get("backend", {}).get("address", "0.0.0.0")
self.listen_port = int(raw.get("backend", {}).get("port", "8080")) self.listen_port = int(raw.get("backend", {}).get("port", "8080"))
self.auth = raw.get("backend", {}).get("auth", {}).items()
self.recorders = [] self.recorders = []
i = 0 i = 0
for raw_server in raw.get("recorders", []): for raw_server in raw.get("recorders", []):

7
backend/global_funcs.py

@ -6,9 +6,12 @@ import uuid
import logging import logging
def hexed(string:str):
return hashlib.md5(string.encode("utf8")).hexdigest()
def uuid_from_string(string:str): def uuid_from_string(string:str):
hex_string = hashlib.md5(string.encode("utf8")).hexdigest() return uuid.UUID(hex=hexed(string))
return uuid.UUID(hex=hex_string)
def app_dir(): def app_dir():

36
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.responses import StreamingResponse, FileResponse
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.exceptions import HTTPException
import uvicorn import uvicorn
import traceback import traceback
import aiofiles import aiofiles
from config_parser import Config as ConfigParser from config_parser import Config as ConfigParser
from config_parser import TranscodeStatus, TranscodeTools, Go2Rtc, NeedNVR 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_core import NVR
from nvr_types import File from nvr_types import File
import os import os
import time
class Server: class Server:
app: FastAPI = FastAPI() app: FastAPI = FastAPI()
config: ConfigParser = ConfigParser() config: ConfigParser = ConfigParser()
go2rtc: Go2Rtc = Go2Rtc() go2rtc: Go2Rtc = Go2Rtc()
security = HTTPBasic()
API_BASE_REF = "/api/dvrip" API_BASE_REF = "/api/dvrip"
@ -22,6 +26,7 @@ class Server:
self.logger = create_logger(Server.__name__) self.logger = create_logger(Server.__name__)
self.setup_events() self.setup_events()
self.setup_routers() self.setup_routers()
self.setup_middleware()
def setup_events(self): def setup_events(self):
@self.app.on_event('startup') @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}") self.logger.info(f"{self.config.recorders[i]} channels count: {self.config.recorders[i].channels}")
await self.go2rtc.start_go2rtc(self.config.recorders) 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): 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) @self.app.get(self.API_BASE_REF, status_code=200)
async def getRecorders(response: Response): async def getRecorders(response: Response):
try: try:

36
frontend/ang_dvrip/src/app/app.component.html

@ -1,5 +1,17 @@
<ng-container> <ng-container *ngIf="authed == 0">
<mat-spinner *ngIf="loading" style="margin:0 auto;" diameter="500"></mat-spinner>
<p style="text-align: center">Загрузка...</p>
</ng-container>
<ng-container *ngIf="authed == -1">
<div style="text-align: center">
<img src="assets/nonauth.webp" style="max-width: 50%">
<p>Не авторизован</p>
<button mat-button (click)="reload()">Обновить страницу</button>
</div>
</ng-container>
<ng-container *ngIf="authed == 1">
<mat-sidenav-container class="container"> <mat-sidenav-container class="container">
<mat-sidenav #sidenav mode="over"> <mat-sidenav #sidenav mode="over">
<div> <div>
@ -10,6 +22,8 @@
<span>Закрыть</span> <span>Закрыть</span>
</mat-toolbar> </mat-toolbar>
<br> <br>
<span>DVRIP Клиент <span style="color: darkgrey">{{version}}</span></span>
<br>
<mat-form-field *ngIf="availble_recorders.length>0"> <mat-form-field *ngIf="availble_recorders.length>0">
<mat-label>Выбранный рекордер</mat-label> <mat-label>Выбранный рекордер</mat-label>
<mat-select [(value)]="selected_recorder" (valueChange)="getChannels(selected_recorder)"> <mat-select [(value)]="selected_recorder" (valueChange)="getChannels(selected_recorder)">
@ -26,7 +40,7 @@
</mat-form-field> </mat-form-field>
<p *ngIf="availble_channels.length==0">Нет доступных каналов</p> <p *ngIf="availble_channels.length==0">Нет доступных каналов</p>
<br> <br>
<span>DVRIP Клиент <span style="color: darkgrey">{{version}}</span></span> <button mat-button (click)="logout()">Выйти</button>
</div> </div>
</mat-sidenav> </mat-sidenav>
@ -51,21 +65,3 @@
</mat-sidenav-content> </mat-sidenav-content>
</mat-sidenav-container> </mat-sidenav-container>
</ng-container> </ng-container>
<!--<ng-container>
<mat-toolbar style="border-bottom: black 1px solid">
<span>DVRIP Клиент <span style="color: darkgrey">{{version}}</span></span>
<mat-spinner *ngIf="loading" [diameter]="50"></mat-spinner>
<span class="spacer"></span>
<ng-container *ngIf="!loading">
<span class="spacer"></span>
</ng-container>
</mat-toolbar>
<mat-toolbar *ngIf="!loading && availble_channels.length>0 && availble_recorders.length>0" style="border-bottom: black 1px solid">
</mat-toolbar>
</ng-container>
-->

24
frontend/ang_dvrip/src/app/app.component.ts

@ -16,9 +16,18 @@ export class AppComponent implements OnInit {
selected_channel:number = 0; selected_channel:number = 0;
loading:boolean = true loading:boolean = true
version = version; version = version;
authed:number = 0;
ngOnInit(): void { ngOnInit(): void {
this.getRecorders(); this.getPing().subscribe(
(a:any) => {
this.authed = 1;
this.getRecorders();
},
(e:any) => {
this.authed = -1;
}
)
} }
constructor(private api:ApiService, constructor(private api:ApiService,
@ -26,6 +35,10 @@ export class AppComponent implements OnInit {
private router:Router) { private router:Router) {
} }
getPing() {
return this.http.get("/api/dvrip/ping")
}
getRecorders() { getRecorders() {
this.loading = true; this.loading = true;
this.http.get("/api/dvrip", {}).subscribe((a:any) => { this.http.get("/api/dvrip", {}).subscribe((a:any) => {
@ -49,4 +62,13 @@ export class AppComponent implements OnInit {
goToRoot() { goToRoot() {
this.router.navigate(["/"]) this.router.navigate(["/"])
} }
logout() {
this.http.get("api/dvrip/logout").subscribe();
this.reload();
}
reload() {
location.reload();
}
} }

BIN
frontend/ang_dvrip/src/assets/nonauth.webp

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Loading…
Cancel
Save