From 4ba922f87d5527261e2a391006f2a192ce345823 Mon Sep 17 00:00:00 2001 From: mwfarb Date: Wed, 7 Aug 2024 22:09:00 -0400 Subject: [PATCH] split persist token subs by version, 1 min exp --- users/mqtt.py | 25 ++++++++++++++++++------- users/views.py | 17 ++++++----------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/users/mqtt.py b/users/mqtt.py index 3635a9b..9bcfabd 100644 --- a/users/mqtt.py +++ b/users/mqtt.py @@ -14,10 +14,12 @@ PUBLIC_NAMESPACE = "public" ANON_REGEX = "anonymous-(?=.*?[a-zA-Z].*?[a-zA-Z])" DEF_JWT_DURATION = datetime.timedelta(minutes=1) -TOPIC_SUPPORTED_API_VERSIONS = ["v1", "v2"] # TODO(mwfarb): remove v1 +API_V1 = "v1" +API_V2 = "v2" +TOPIC_SUPPORTED_API_VERSIONS = [API_V1, API_V2] # TODO (mwfarb): remove v1 -def all_scenes_read_token(): +def all_scenes_read_token(version): config = settings.PUBSUB privkeyfile = settings.MQTT_TOKEN_PRIVKEY if not os.path.exists(privkeyfile): @@ -25,11 +27,20 @@ def all_scenes_read_token(): return None with open(privkeyfile) as privatefile: private_key = privatefile.read() - payload = { - "sub": config["mqtt_username"], - "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=5), - "subs": [f"{config['mqtt_realm']}/s/#"], - } + + realm = config["mqtt_realm"] + username = config["mqtt_username"] + duration = datetime.timedelta(minutes=1) + + payload = {} + payload["sub"] = username + payload["exp"] = datetime.datetime.utcnow() + duration + + if version == API_V2: + payload["subs"] = [f"{realm}/s/+/+/o/#"] # v2 + else: + payload["subs"] = [f"{realm}/s/#"] # v1 + token = jwt.encode(payload, private_key, algorithm="RS256") return token diff --git a/users/views.py b/users/views.py index 9e5a8e9..ab8cf08 100644 --- a/users/views.py +++ b/users/views.py @@ -4,7 +4,6 @@ import re import secrets -import coreapi from allauth.socialaccount import helpers from allauth.socialaccount.models import SocialAccount from allauth.socialaccount.views import SignupView as SocialSignupViewDefault @@ -16,14 +15,11 @@ from django.db import transaction from django.http import HttpResponse, JsonResponse from django.shortcuts import redirect, render -from django.urls import reverse from google.auth.transport import requests as grequests from google.oauth2 import id_token from rest_framework import permissions, status -from rest_framework.compat import coreapi from rest_framework.decorators import api_view, permission_classes from rest_framework.parsers import JSONParser -from rest_framework.schemas import AutoSchema from .filestore import (delete_filestore_user, login_filestore_user, set_filestore_scope) @@ -375,11 +371,11 @@ def my_scenes(request): except (ValueError, SocialAccount.DoesNotExist) as err: return JsonResponse({"error": err}, status=status.HTTP_403_FORBIDDEN) - serializer = SceneNameSerializer(get_my_scenes(user), many=True) + serializer = SceneNameSerializer(get_my_scenes(user, request.version), many=True) return JsonResponse(serializer.data, safe=False) -def get_my_scenes(user): +def get_my_scenes(user, version): """ Internal method to update scene permissions table: 1. Requests list of any scenes with objects saved from /persist/!allscenes to add to scene permissions table. @@ -387,7 +383,7 @@ def get_my_scenes(user): """ # update scene list from object persistance db if user.is_authenticated: - token = all_scenes_read_token() + token = all_scenes_read_token(version) if user.is_staff: # admin/staff p_scenes = get_persist_scenes_all(token) else: # standard user @@ -474,9 +470,8 @@ def user_profile(request): - Shows scenes that the user has permissions to edit and a button to edit them. - Handles account deletes. """ - # TODO (mwfarb): make remote post status 426, local post redirect to valid - # if request.version not in TOPIC_SUPPORTED_API_VERSIONS: - # return reverse("users:user_profile", current_app="users") + if request.version not in TOPIC_SUPPORTED_API_VERSIONS: + return redirect(f"/{TOPIC_SUPPORTED_API_VERSIONS[0]}/user_profile/") if request.method == 'POST': # account delete request @@ -512,7 +507,7 @@ def user_profile(request): except User.DoesNotExist: messages.error(request, "Unable to complete account delete.") - scenes = get_my_scenes(request.user) + scenes = get_my_scenes(request.user, request.version) devices = get_my_devices(request.user) staff = None if request.user.is_staff: # admin/staff