6 changed files with 186 additions and 25 deletions
@ -0,0 +1,91 @@ |
|||||
|
from functools import wraps |
||||
|
from fastapi.requests import Request |
||||
|
from fastapi.responses import Response |
||||
|
from fastapi.exceptions import HTTPException |
||||
|
|
||||
|
from utils import md5hash |
||||
|
from random import randint |
||||
|
from time import time |
||||
|
|
||||
|
import asyncio |
||||
|
from logger import logger |
||||
|
|
||||
|
class NotValidCode(Exception): |
||||
|
pass |
||||
|
|
||||
|
class AuthManager: |
||||
|
NUM = "NUM" |
||||
|
NUM_SECURED = "NUM_SECURED" |
||||
|
SECRET_KEY = "SECRET_KEY"#todo |
||||
|
MAX_CODE_LIFE = 60 |
||||
|
|
||||
|
def __init__(self, args): |
||||
|
self.salt = args.web_salt |
||||
|
self.code_store = {} |
||||
|
|
||||
|
async def storeCleaner(self): |
||||
|
logger.info("Code store cleaner working...") |
||||
|
run = True |
||||
|
while run: |
||||
|
try: |
||||
|
l = list(self.code_store.keys()) |
||||
|
for code in l: |
||||
|
if code in self.code_store.keys():#check mb is not exists one time |
||||
|
if time() - self.code_store[code]["ts"] > self.MAX_CODE_LIFE: |
||||
|
logger.info(f"Code {code} is ended") |
||||
|
del self.code_store[code] |
||||
|
await asyncio.sleep(1) |
||||
|
except asyncio.exceptions.CancelledError: |
||||
|
run = False |
||||
|
except: |
||||
|
logger.error("Cannot check code store") |
||||
|
pass |
||||
|
|
||||
|
def authRequest(self, method=[]):#todo cookie or secret_key |
||||
|
def decorator(func): |
||||
|
@wraps(func) |
||||
|
async def wrapper(*args, **kwargs): |
||||
|
request: Request = kwargs.get("request", None) |
||||
|
if request is None: |
||||
|
raise HTTPException(status_code=500, detail="Authed endpoint need request arg, but is missing") |
||||
|
|
||||
|
if request.cookies.get(self.NUM, None) and request.cookies.get(self.NUM_SECURED, None): |
||||
|
#check cookie is valid |
||||
|
if md5hash(request.cookies[self.NUM] + self.salt) == request.cookies.get(self.NUM_SECURED): |
||||
|
return await func(*args, **kwargs) |
||||
|
|
||||
|
raise HTTPException(status_code=401) |
||||
|
return wrapper |
||||
|
return decorator |
||||
|
|
||||
|
def setAuth(self, response: Response, num:int, clear = False): |
||||
|
if clear: |
||||
|
response.set_cookie(self.NUM, "") |
||||
|
response.set_cookie(self.NUM_SECURED, "") |
||||
|
else: |
||||
|
response.set_cookie(self.NUM, str(num)) |
||||
|
response.set_cookie(self.NUM_SECURED, md5hash(str(num)+self.salt)) |
||||
|
return response |
||||
|
|
||||
|
def request_auth(self, num: int): |
||||
|
code = randint(1000, 9999) |
||||
|
while code in self.code_store.keys(): |
||||
|
code = randint(1000, 9999) |
||||
|
|
||||
|
self.code_store[code] = { |
||||
|
"code": code, |
||||
|
"num": num, |
||||
|
"ts": time() |
||||
|
} |
||||
|
#logger.info(code) |
||||
|
return code |
||||
|
|
||||
|
def accept_code(self, code:int): |
||||
|
if code in self.code_store.keys(): |
||||
|
num = self.code_store[code]["num"] |
||||
|
del self.code_store[code] |
||||
|
return num |
||||
|
|
||||
|
raise NotValidCode() |
||||
|
|
||||
|
|
||||
@ -0,0 +1,18 @@ |
|||||
|
import random |
||||
|
import string |
||||
|
import hashlib |
||||
|
|
||||
|
def generate_random_string(length): |
||||
|
characters = string.ascii_letters + string.digits |
||||
|
random_string = ''.join(random.choices(characters, k=length)) |
||||
|
return random_string |
||||
|
|
||||
|
def isInt(any): |
||||
|
try: |
||||
|
int(any) |
||||
|
return True |
||||
|
except: |
||||
|
return False |
||||
|
|
||||
|
def md5hash(string: str): |
||||
|
return str(hashlib.md5(string.encode()).hexdigest()) |
||||
@ -0,0 +1,58 @@ |
|||||
|
from fastapi import FastAPI |
||||
|
from fastapi.requests import Request |
||||
|
from fastapi.responses import Response, JSONResponse |
||||
|
from fastapi.exceptions import HTTPException |
||||
|
|
||||
|
class WebExtension: |
||||
|
app: FastAPI |
||||
|
def __init__(self, core): |
||||
|
self.core = core |
||||
|
self.app = core.app |
||||
|
|
||||
|
@self.app.get(f"{self.core.context}/ping") |
||||
|
async def pong(request:Request): |
||||
|
return {"pong": True} |
||||
|
|
||||
|
@self.app.get(f"{self.core.context}/status") |
||||
|
async def status(request:Request): |
||||
|
return { |
||||
|
"status": self.core.meshState, |
||||
|
"last_packet_catch": self.core.last_packet_catch |
||||
|
} |
||||
|
|
||||
|
@self.app.get(f"{self.core.context}/auth/code") |
||||
|
async def sendCodeToNode(request:Request, num: int): |
||||
|
#в сообщение одноразовый код и юзер агент кто слал, так-же чтобы не спапили по фингерпринту один код в минуту |
||||
|
if num: |
||||
|
node = await self.getNodeInfo(num) |
||||
|
if node: |
||||
|
code = self.core.authManager.request_auth(num) |
||||
|
await self.core.device.sendMsgToDM(f"Auth code: {code}", num) |
||||
|
return {"status": f"Code sended to {node['long_name']}"} |
||||
|
|
||||
|
raise HTTPException(status_code=400) |
||||
|
|
||||
|
@self.app.get(f"{self.core.context}/auth/code/check") |
||||
|
async def acceptCodeFromNode(request:Request, code: int): |
||||
|
#если такой код есть то авторизуем пользователя, иначе шлем н***й |
||||
|
if code: |
||||
|
try: |
||||
|
num = self.core.authManager.accept_code(code) |
||||
|
respond = self.core.authManager.setAuth(JSONResponse({"status":True}), num) |
||||
|
return respond |
||||
|
except: |
||||
|
return {"status": False} |
||||
|
|
||||
|
return {"status": False} |
||||
|
|
||||
|
@self.app.get(f"{self.core.context}/auth/logout") |
||||
|
async def clearSession(request:Request): |
||||
|
return self.core.authManager.setAuth(JSONResponse({"status":True}), 0, True) |
||||
|
|
||||
|
async def getNodeInfo(self, num:int): |
||||
|
collection = self.core.dbStore['node_info'] |
||||
|
c = await collection.find_one( |
||||
|
{"num":num}, |
||||
|
sort=[("ts", -1)] |
||||
|
) |
||||
|
return c |
||||
@ -1,11 +0,0 @@ |
|||||
from fastapi import FastAPI |
|
||||
|
|
||||
class WebExtension: |
|
||||
app: FastAPI |
|
||||
def __init__(self, core): |
|
||||
self.core = core |
|
||||
self.app = core.app |
|
||||
|
|
||||
@self.app.get(f"{self.core.context}/ping") |
|
||||
async def pong(): |
|
||||
return {"pong": True} |
|
||||
Loading…
Reference in new issue