70 lines
2.5 KiB
Python
70 lines
2.5 KiB
Python
import hashlib
|
|
import hmac
|
|
import json
|
|
|
|
from fastapi import Request, Security
|
|
from fastapi.security import HTTPBearer
|
|
|
|
from pigway_frame_flow.core.accounts import session_user
|
|
from pigway_frame_flow.errors import ErrorCode as E
|
|
from pigway_frame_flow.errors import FlowError
|
|
from pigway_frame_flow.storage.database import encode, now_iso
|
|
|
|
bearer = HTTPBearer(auto_error=False)
|
|
ALL_SCOPES = [
|
|
"devices:read",
|
|
"images:read",
|
|
"images:delete",
|
|
"display:write",
|
|
"templates:read",
|
|
"render:write",
|
|
"jobs:read",
|
|
"jobs:write",
|
|
"history:read",
|
|
"settings:write",
|
|
"admin",
|
|
]
|
|
|
|
|
|
def seed_client(db, token):
|
|
if token:
|
|
digest = hashlib.sha256(token.encode()).hexdigest()
|
|
db.execute(
|
|
"INSERT INTO clients VALUES(?,?,?,?,?,?,?) ON CONFLICT(client_id) DO UPDATE SET token_hash=excluded.token_hash, enabled=1",
|
|
("admin", digest, "Administrator", encode(ALL_SCOPES), now_iso(), None, 1),
|
|
)
|
|
else:
|
|
db.execute("UPDATE clients SET enabled=0 WHERE client_id='admin'")
|
|
|
|
|
|
def require(scope):
|
|
def authorize(request: Request, credentials=Security(bearer)):
|
|
db = request.app.state.service.db
|
|
if not credentials:
|
|
account = session_user(request)
|
|
if account:
|
|
request.state.client_id = "user:" + account["username"]
|
|
return request.state.client_id
|
|
raise FlowError(E.UNAUTHORIZED, "Frame Flow API token required", 401)
|
|
digest = hashlib.sha256(credentials.credentials.encode()).hexdigest()
|
|
for row in db.all("SELECT * FROM clients WHERE enabled=1"):
|
|
if hmac.compare_digest(digest, row["token_hash"]):
|
|
permissions = json.loads(row["permissions"])
|
|
if scope not in permissions and "admin" not in permissions:
|
|
raise FlowError(
|
|
E.FORBIDDEN, "Token scope does not allow this action", 403
|
|
)
|
|
request.state.client_id = row["client_id"]
|
|
# Avoid writing last_used on every image or poll.
|
|
if not row["last_used_at"] or (
|
|
now_iso()[:16] != row["last_used_at"][:16]
|
|
):
|
|
db.execute(
|
|
"UPDATE clients SET last_used_at=? WHERE client_id=?",
|
|
(now_iso(), row["client_id"]),
|
|
)
|
|
return row["client_id"]
|
|
raise FlowError(E.UNAUTHORIZED, "Invalid Frame Flow API token", 401)
|
|
|
|
return authorize
|