A very rudimentary implementation of a private mode with key base autorization

This commit is contained in:
Christoph Stahl 2023-03-24 13:30:33 +01:00
parent 1ac2f6bf32
commit f4389516f0
2 changed files with 49 additions and 11 deletions

View file

@ -14,6 +14,7 @@ Excerp from the help::
--room ROOM, -r ROOM --room ROOM, -r ROOM
--secret SECRET, -s SECRET --secret SECRET, -s SECRET
--config-file CONFIG_FILE, -C CONFIG_FILE --config-file CONFIG_FILE, -C CONFIG_FILE
--key KEY, -k KEY
The config file should be a json file in the following style:: The config file should be a json file in the following style::
@ -82,6 +83,8 @@ class State:
a room, this must be identical. Also, if a webclient wants to have a room, this must be identical. Also, if a webclient wants to have
admin privileges, this must be included. admin privileges, this must be included.
:type secret: str :type secret: str
:param key: An optional key, if registration on the server is limited.
:type key: Optional[str]
:param preview_duration: Amount of seconds the preview before a song be :param preview_duration: Amount of seconds the preview before a song be
displayed. displayed.
:type preview_duration: int :type preview_duration: int
@ -98,6 +101,7 @@ class State:
room: str = "" room: str = ""
server: str = "" server: str = ""
secret: str = "" secret: str = ""
key: Optional[str] = None
preview_duration: int = 3 preview_duration: int = 3
last_song: Optional[datetime.datetime] = None last_song: Optional[datetime.datetime] = None
@ -187,16 +191,16 @@ async def handle_connect() -> None:
:rtype: None :rtype: None
""" """
logging.info("Connected to server") logging.info("Connected to server")
await sio.emit( data = {
"register-client", "queue": state.queue,
{ "recent": state.recent,
"queue": state.queue, "room": state.room,
"recent": state.recent, "secret": state.secret,
"room": state.room, "config": state.get_config(),
"secret": state.secret, }
"config": state.get_config(), if state.key:
}, data["registration-key"] = state.key
) await sio.emit("register-client", data)
@sio.on("get-meta-info") @sio.on("get-meta-info")
@ -390,6 +394,7 @@ async def aiomain() -> None:
parser.add_argument("--room", "-r") parser.add_argument("--room", "-r")
parser.add_argument("--secret", "-s") parser.add_argument("--secret", "-s")
parser.add_argument("--config-file", "-C", default="syng-client.json") parser.add_argument("--config-file", "-C", default="syng-client.json")
parser.add_argument("--key", "-k", default=None)
parser.add_argument("server") parser.add_argument("server")
args = parser.parse_args() args = parser.parse_args()
@ -406,6 +411,8 @@ async def aiomain() -> None:
if "preview_duration" in config["config"]: if "preview_duration" in config["config"]:
state.preview_duration = config["config"]["preview_duration"] state.preview_duration = config["config"]["preview_duration"]
state.key = args.key if args.key else None
if args.room: if args.room:
state.room = args.room state.room = args.room

View file

@ -16,6 +16,7 @@ from __future__ import annotations
import asyncio import asyncio
import datetime import datetime
import hashlib
import logging import logging
import os import os
import random import random
@ -350,10 +351,12 @@ async def handle_register_client(sid: str, data: dict[str, Any]) -> None:
Handle the "register-client" message. Handle the "register-client" message.
The data dictionary should have the following keys: The data dictionary should have the following keys:
- `registration_key` (Optional), a key corresponding to those stored
in `app["registration-keyfile"]`
- `room` (Optional), the requested room - `room` (Optional), the requested room
- `config`, an dictionary of initial configurations - `config`, an dictionary of initial configurations
- `queue`, a list of initial entries for the queue. The entries are - `queue`, a list of initial entries for the queue. The entries are
encoded as a dictionary. encoded as a dictionary.
- `recent`, a list of initial entries for the recent list. The entries - `recent`, a list of initial entries for the recent list. The entries
are encoded as a dictionary. are encoded as a dictionary.
- `secret`, the secret of the room - `secret`, the secret of the room
@ -363,6 +366,9 @@ async def handle_register_client(sid: str, data: dict[str, Any]) -> None:
playback client will be replaced if and only if, the new playback playback client will be replaced if and only if, the new playback
client has the same secret. client has the same secret.
If registration is restricted, abort, if the given key is not in the
registration keyfile.
If no room is provided, a fresh room id is generated. If no room is provided, a fresh room id is generated.
If the client provides a new room, or a new room id was generated, the If the client provides a new room, or a new room id was generated, the
@ -394,6 +400,25 @@ async def handle_register_client(sid: str, data: dict[str, Any]) -> None:
client_id = gen_id(length + 1) client_id = gen_id(length + 1)
return client_id return client_id
if not app["public"]:
with open(app["registration-keyfile"]) as f:
raw_keys = f.readlines()
keys = [key[:64] for key in raw_keys]
if (
"registration-key" not in data
or hashlib.sha256(
data["registration-key"].encode()
).hexdigest()
not in keys
):
await sio.emit(
"client-registered",
{"success": False, "room": None},
room=sid,
)
return
room: str = data["room"] if "room" in data and data["room"] else gen_id() room: str = data["room"] if "room" in data and data["room"] else gen_id()
async with sio.session(sid) as session: async with sio.session(sid) as session:
session["room"] = room session["room"] = room
@ -791,8 +816,14 @@ def main() -> None:
parser.add_argument("--host", "-H", default="localhost") parser.add_argument("--host", "-H", default="localhost")
parser.add_argument("--port", "-p", default="8080") parser.add_argument("--port", "-p", default="8080")
parser.add_argument("--root-folder", "-r", default="syng/static/") parser.add_argument("--root-folder", "-r", default="syng/static/")
parser.add_argument("--registration-keyfile", "-k", default=None)
args = parser.parse_args() args = parser.parse_args()
app["public"] = True
if args.registration_keyfile:
app["public"] = False
app["registration-keyfile"] = args.registration_keyfile
app["root_folder"] = args.root_folder app["root_folder"] = args.root_folder
app.add_routes( app.add_routes(