Compare commits
3 commits
0da1a50058
...
dfbaf2fd65
Author | SHA1 | Date | |
---|---|---|---|
|
dfbaf2fd65 | ||
|
005dc42433 | ||
|
899fe7f45c |
9 changed files with 138 additions and 33 deletions
|
@ -156,6 +156,60 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/control-box": {
|
||||||
|
"get": {
|
||||||
|
"summary": "Get control box information",
|
||||||
|
"operationId": "viewBox",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Control box info",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Box"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"summary": "Change box state",
|
||||||
|
"description": "Changes box state, you need to be cof to act on this endpoint. The json do not need to contain all fields, it can be partial and only the provided values will be updated.",
|
||||||
|
"operationId": "changeBox",
|
||||||
|
"parameters": [],
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"bearerAuth": [
|
||||||
|
"exp",
|
||||||
|
"scope",
|
||||||
|
"user",
|
||||||
|
"is_cof",
|
||||||
|
"sub"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"description": "Box state",
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Box"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Box state updated successfully",
|
||||||
|
"content": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
|
@ -181,6 +235,24 @@
|
||||||
"format": "int8"
|
"format": "int8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Box": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [ ],
|
||||||
|
"properties": {
|
||||||
|
"pan": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int8"
|
||||||
|
},
|
||||||
|
"tilt": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int8"
|
||||||
|
},
|
||||||
|
"focus": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int8"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"securitySchemes": {
|
"securitySchemes": {
|
||||||
|
|
|
@ -59,3 +59,19 @@ pub async fn jwt_middleware(
|
||||||
Err(StatusCode::FORBIDDEN)
|
Err(StatusCode::FORBIDDEN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn jwt_middleware_cof(
|
||||||
|
State(state): State<DB>,
|
||||||
|
TypedHeader(auth): TypedHeader<headers::Authorization<headers::authorization::Bearer>>,
|
||||||
|
mut request: Request,
|
||||||
|
next: Next,
|
||||||
|
) -> Result<Response, StatusCode> {
|
||||||
|
let token = auth.token();
|
||||||
|
if let Some(user) = check_token(token, &state.static_state.jwt_key) {
|
||||||
|
if user.is_cof {
|
||||||
|
request.extensions_mut().insert(user);
|
||||||
|
return Ok(next.run(request).await)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Err(StatusCode::FORBIDDEN)
|
||||||
|
}
|
||||||
|
|
|
@ -92,12 +92,8 @@ pub async fn get_motor_value_handler(
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
pub async fn edit_motor_value_handler(
|
pub async fn edit_motor_value_handler(
|
||||||
State(db): State<DB>,
|
State(db): State<DB>,
|
||||||
Extension(user): Extension<User>,
|
|
||||||
Json(body): Json<DMXBeamChange>,
|
Json(body): Json<DMXBeamChange>,
|
||||||
) -> Result<(), StatusCode> {
|
) -> Result<(), StatusCode> {
|
||||||
if !user.is_cof {
|
|
||||||
return Err(StatusCode::FORBIDDEN);
|
|
||||||
}
|
|
||||||
let mut lock = db.mut_state.write().await;
|
let mut lock = db.mut_state.write().await;
|
||||||
lock.dmx.motor = DMXBeam {
|
lock.dmx.motor = DMXBeam {
|
||||||
pan: body.pan.unwrap_or(lock.dmx.motor.pan),
|
pan: body.pan.unwrap_or(lock.dmx.motor.pan),
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::authorization::jwt_middleware;
|
use crate::authorization::{ jwt_middleware, jwt_middleware_cof };
|
||||||
use crate::handler;
|
use crate::handler;
|
||||||
use crate::model;
|
use crate::model;
|
||||||
use axum::{handler::Handler, middleware};
|
use axum::{handler::Handler, middleware};
|
||||||
|
@ -61,10 +61,10 @@ pub fn create_router() -> Router {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/api/motor",
|
"/api/control-box",
|
||||||
get(handler::get_motor_value_handler).post(
|
get(handler::get_motor_value_handler).post(
|
||||||
handler::edit_motor_value_handler
|
handler::edit_motor_value_handler
|
||||||
.layer(middleware::from_fn_with_state(db.clone(), jwt_middleware)),
|
.layer(middleware::from_fn_with_state(db.clone(), jwt_middleware_cof)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.layer(cors)
|
.layer(cors)
|
||||||
|
|
0
frontend/frontend/management/__init__.py
Normal file
0
frontend/frontend/management/__init__.py
Normal file
0
frontend/frontend/management/commands/__init__.py
Normal file
0
frontend/frontend/management/commands/__init__.py
Normal file
22
frontend/frontend/management/commands/craft_token.py
Normal file
22
frontend/frontend/management/commands/craft_token.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from frontend.utils import craft_token
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Craft a token for the backend"
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--is_cof",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
|
parser.add_argument("user", type=str)
|
||||||
|
parser.add_argument("exp_time", type=int)
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
token = craft_token(options["user"], options["is_cof"], options["exp_time"])
|
||||||
|
|
||||||
|
self.stdout.write(f"Token:\n{pprint.pformat(token)}")
|
21
frontend/frontend/utils.py
Normal file
21
frontend/frontend/utils.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|
||||||
|
import jwt
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
def craft_token(username, is_cof, hours=9):
|
||||||
|
claims = {
|
||||||
|
"exp": datetime.now(tz=timezone.utc) + timedelta(hours=hours),
|
||||||
|
"sub": "ragb",
|
||||||
|
"user": username,
|
||||||
|
"is_cof": is_cof,
|
||||||
|
"scope": "modify",
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"token": jwt.encode(
|
||||||
|
claims,
|
||||||
|
settings.JWT_SECRET,
|
||||||
|
),
|
||||||
|
"claims": claims,
|
||||||
|
}
|
|
@ -1,6 +1,3 @@
|
||||||
from datetime import datetime, timedelta, timezone
|
|
||||||
|
|
||||||
import jwt
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.core.exceptions import ViewDoesNotExist
|
from django.core.exceptions import ViewDoesNotExist
|
||||||
|
@ -8,6 +5,8 @@ from django.http import Http404, JsonResponse
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
|
|
||||||
|
from .utils import craft_token
|
||||||
|
|
||||||
|
|
||||||
def get_context_from_proj(kind, chans):
|
def get_context_from_proj(kind, chans):
|
||||||
print(kind, chans)
|
print(kind, chans)
|
||||||
|
@ -40,20 +39,7 @@ def get_context_from_proj(kind, chans):
|
||||||
|
|
||||||
class TokenView(LoginRequiredMixin, View):
|
class TokenView(LoginRequiredMixin, View):
|
||||||
def get(self, request, *arg, **kwargs):
|
def get(self, request, *arg, **kwargs):
|
||||||
return JsonResponse(
|
return JsonResponse(craft_token(self.request.user.username, self.request.user.groups.filter(name="cof").exists()))
|
||||||
{
|
|
||||||
"token": jwt.encode(
|
|
||||||
{
|
|
||||||
"exp": datetime.now(tz=timezone.utc) + timedelta(hours=9),
|
|
||||||
"sub": "ragb",
|
|
||||||
"user": self.request.user.username,
|
|
||||||
"is_cof": self.requests.user.groups.filter(name="cof").exists(),
|
|
||||||
"scope": "modify",
|
|
||||||
},
|
|
||||||
settings.JWT_SECRET,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class LightView(TemplateView):
|
class LightView(TemplateView):
|
||||||
|
@ -64,15 +50,7 @@ class LightView(TemplateView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
if self.request.user.is_authenticated:
|
if self.request.user.is_authenticated:
|
||||||
context["jwt"] = jwt.encode(
|
context["jwt"] = craft_token(self.request.user.username, self.request.user.groups.filter(name="cof").exists())["token"]
|
||||||
{
|
|
||||||
"exp": datetime.now(tz=timezone.utc) + timedelta(hours=9),
|
|
||||||
"sub": "ragb",
|
|
||||||
"user": self.request.user.username,
|
|
||||||
"scope": "modify",
|
|
||||||
},
|
|
||||||
settings.JWT_SECRET,
|
|
||||||
)
|
|
||||||
context["websocket_endpoint"] = settings.WEBSOCKET_ENDPOINT
|
context["websocket_endpoint"] = settings.WEBSOCKET_ENDPOINT
|
||||||
light = self.kwargs["light"]
|
light = self.kwargs["light"]
|
||||||
if light not in settings.LIGHTS["lights"]:
|
if light not in settings.LIGHTS["lights"]:
|
||||||
|
|
Loading…
Reference in a new issue