From e0f3ec0e077543f9567d74966c141ac0d7a34d42 Mon Sep 17 00:00:00 2001 From: n9k Date: Tue, 2 Aug 2022 04:18:59 +0000 Subject: [PATCH 1/4] Control socket: add new users --- anonstream/control/spec/methods/help.py | 1 + anonstream/control/spec/methods/user.py | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/anonstream/control/spec/methods/help.py b/anonstream/control/spec/methods/help.py index d38a322..10fa502 100644 --- a/anonstream/control/spec/methods/help.py +++ b/anonstream/control/spec/methods/help.py @@ -18,6 +18,7 @@ async def cmd_help(): ' user set USER ATTR VALUE.......set an attribute of a user\n' ' user eyes USER [show]..........show a list of active video responses\n' ' user eyes USER delete EYES_ID..end a video response\n' + ' user add VERIFIED TOKEN........add new user\n' #' chat show MESSAGES.............show a list of messages\n' ' chat delete SEQS...............delete a set of messages\n' ' allowedness [show].............show the current allowedness\n' diff --git a/anonstream/control/spec/methods/user.py b/anonstream/control/spec/methods/user.py index f293ce0..88af4bb 100644 --- a/anonstream/control/spec/methods/user.py +++ b/anonstream/control/spec/methods/user.py @@ -7,9 +7,11 @@ from quart import current_app from anonstream.control.exceptions import CommandFailed from anonstream.control.spec import BadArgument -from anonstream.control.spec.common import Str, End, ArgsInt, ArgsString, ArgsJson, ArgsJsonString +from anonstream.control.spec.common import Str, End, ArgsInt, ArgsString, ArgsJson, ArgsJsonBoolean, ArgsJsonString from anonstream.control.spec.utils import get_item, json_dumps_contiguous from anonstream.utils.user import USER_WEBSOCKET_ATTRS +from anonstream.routes.wrappers import generate_and_add_user +from anonstream.wrappers import get_timestamp USERS_BY_TOKEN = current_app.users_by_token USERS = current_app.users @@ -49,6 +51,7 @@ async def cmd_user_help(): ' user set USER ATTR VALUE......set an attribute of a user\n' ' user eyes USER [show].........show a user\'s active video responses\n' ' user eyes USER delete EYES_ID.end a video response to a user\n' + ' user add VERIFIED TOKEN.......add new user\n' 'Definitions:\n' ' USER..........................={token TOKEN | hash HASH}\n' ' TOKEN.........................a token, json string\n' @@ -56,6 +59,7 @@ async def cmd_user_help(): ' ATTR..........................a user attribute, re:[a-z0-9_]+\n' ' VALUE.........................json value\n' ' EYES_ID.......................a user\'s eyes_id, base 10 integer\n' + ' VERIFIED......................user\'s verified state: true = normal, false = can\'t chat, null = will be kicked to access captcha\n' ) return normal, response @@ -132,6 +136,21 @@ async def cmd_user_eyes_delete(user, eyes_id): response = '' return normal, response +async def cmd_user_add(verified, token): + if token in USERS_BY_TOKEN: + raise CommandFailed(f'user with token {token!r} already exists') + _user = generate_and_add_user( + timestamp=get_timestamp(), + token=token, + verified=verified, + ) + normal = [ + 'user', 'add', + json_dumps_contiguous(verified), json_dumps_contiguous(token), + ] + response = '' + return normal, response + SPEC = Str({ None: End(cmd_user_show), 'help': End(cmd_user_help), @@ -144,4 +163,5 @@ SPEC = Str({ 'show': End(cmd_user_eyes_show), 'delete': ArgsInt(End(cmd_user_eyes_delete)), })), + 'add': ArgsJsonBoolean(ArgsJsonString(End(cmd_user_add))), }) From 4a22ca8a9226fe0e162012461980cc42a92c81ef Mon Sep 17 00:00:00 2001 From: n9k Date: Tue, 2 Aug 2022 04:46:38 +0000 Subject: [PATCH 2/4] Minor: `add_chat_message` returns seq (or None) It now returns the seq of the just-added message if one was added, and None otherwise. The previous behaviour was to return True and False respectively. --- anonstream/chat.py | 6 +++--- anonstream/routes/nojs.py | 3 ++- anonstream/websocket.py | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/anonstream/chat.py b/anonstream/chat.py index 7075411..667f591 100644 --- a/anonstream/chat.py +++ b/anonstream/chat.py @@ -31,9 +31,9 @@ def get_all_messages_for_websocket(): )) def add_chat_message(user, nonce, comment, ignore_empty=False): - # Special case: if the comment is empty, do nothing and return + # Special case: if the comment is empty, do nothing and return None if ignore_empty and len(comment) == 0: - return False + return None timestamp_ms = time.time_ns() // 1_000_000 timestamp = timestamp_ms // 1000 @@ -137,7 +137,7 @@ def add_chat_message(user, nonce, comment, ignore_empty=False): }, ) - return True + return seq def delete_chat_messages(seqs): seq_set = set(seqs) diff --git a/anonstream/routes/nojs.py b/anonstream/routes/nojs.py index 6f89666..e34c52e 100644 --- a/anonstream/routes/nojs.py +++ b/anonstream/routes/nojs.py @@ -135,12 +135,13 @@ async def nojs_submit_message(timestamp, user): try: # If the comment is empty but the captcha was just solved, # be lenient: don't raise an exception and don't create a notice - message_was_added = add_chat_message( + seq = add_chat_message( user, nonce, comment, ignore_empty=verification_happened, ) + message_was_added = seq is not None except Rejected as e: notice, *_ = e.args state_id = add_state( diff --git a/anonstream/websocket.py b/anonstream/websocket.py index 35be036..48d8467 100644 --- a/anonstream/websocket.py +++ b/anonstream/websocket.py @@ -140,12 +140,13 @@ def handle_inbound_message(timestamp, queue, user, nonce, comment, digest, answe message_was_added = False else: try: - message_was_added = add_chat_message( + seq = add_chat_message( user, nonce, comment, ignore_empty=verification_happened, ) + message_was_added = seq is not None except Rejected as e: notice, *_ = e.args message_was_added = False From 6ddab6c969c107bdb3fd89e57c77a2804211efb4 Mon Sep 17 00:00:00 2001 From: n9k Date: Tue, 2 Aug 2022 04:49:49 +0000 Subject: [PATCH 3/4] Minor: control socket: move around ArgsUser stuff --- anonstream/control/spec/common.py | 30 ++++++++++++++++++++++++- anonstream/control/spec/methods/user.py | 29 ++---------------------- 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/anonstream/control/spec/common.py b/anonstream/control/spec/common.py index bfee339..df4312b 100644 --- a/anonstream/control/spec/common.py +++ b/anonstream/control/spec/common.py @@ -3,9 +3,14 @@ import json -from anonstream.control.spec import Spec, NoParse, Ambiguous, Parsed +from quart import current_app + +from anonstream.control.spec import Spec, NoParse, Ambiguous, BadArgument, Parsed from anonstream.control.spec.utils import get_item, startswith +USERS_BY_TOKEN = current_app.users_by_token +USERS = current_app.users + class Str(Spec): AS_ARG = False @@ -146,3 +151,26 @@ class ArgsJsonStringArray(ArgsJson): f'bad argument at position {index} {obj_json!r}: ' f'could not decode json array of strings' ) + +class ArgsJsonTokenUser(ArgsJsonString): + def transform_obj(self, token): + try: + user = USERS_BY_TOKEN[token] + except KeyError: + raise BadArgument(f'no user with token {token!r}') + return user + +class ArgsJsonHashUser(ArgsString): + def transform_string(self, token_hash): + for user in USERS: + if user['token_hash'] == token_hash: + break + else: + raise BadArgument(f'no user with token_hash {token_hash!r}') + return user + +def ArgsUser(spec): + return Str({ + 'token': ArgsJsonTokenUser(spec), + 'hash': ArgsJsonHashUser(spec), + }) diff --git a/anonstream/control/spec/methods/user.py b/anonstream/control/spec/methods/user.py index 88af4bb..5cd91fd 100644 --- a/anonstream/control/spec/methods/user.py +++ b/anonstream/control/spec/methods/user.py @@ -6,40 +6,15 @@ import json from quart import current_app from anonstream.control.exceptions import CommandFailed -from anonstream.control.spec import BadArgument -from anonstream.control.spec.common import Str, End, ArgsInt, ArgsString, ArgsJson, ArgsJsonBoolean, ArgsJsonString -from anonstream.control.spec.utils import get_item, json_dumps_contiguous +from anonstream.control.spec.common import Str, End, ArgsInt, ArgsString, ArgsJson, ArgsJsonBoolean, ArgsJsonString, ArgsUser +from anonstream.control.spec.utils import json_dumps_contiguous from anonstream.utils.user import USER_WEBSOCKET_ATTRS from anonstream.routes.wrappers import generate_and_add_user from anonstream.wrappers import get_timestamp USERS_BY_TOKEN = current_app.users_by_token -USERS = current_app.users USERS_UPDATE_BUFFER = current_app.users_update_buffer -class ArgsJsonTokenUser(ArgsJsonString): - def transform_obj(self, token): - try: - user = USERS_BY_TOKEN[token] - except KeyError: - raise BadArgument(f'no user with token {token!r}') - return user - -class ArgsJsonHashUser(ArgsString): - def transform_string(self, token_hash): - for user in USERS: - if user['token_hash'] == token_hash: - break - else: - raise BadArgument(f'no user with token_hash {token_hash!r}') - return user - -def ArgsUser(spec): - return Str({ - 'token': ArgsJsonTokenUser(spec), - 'hash': ArgsJsonHashUser(spec), - }) - async def cmd_user_help(): normal = ['user', 'help'] response = ( From f3d613de3bc94718ecf2d3f1ba80e11869e11351 Mon Sep 17 00:00:00 2001 From: n9k Date: Tue, 2 Aug 2022 04:50:45 +0000 Subject: [PATCH 4/4] Control socket: add chat messages --- anonstream/control/spec/methods/chat.py | 36 ++++++++++++++++++++----- anonstream/control/spec/methods/help.py | 1 + 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/anonstream/control/spec/methods/chat.py b/anonstream/control/spec/methods/chat.py index a95cae7..64ee2ea 100644 --- a/anonstream/control/spec/methods/chat.py +++ b/anonstream/control/spec/methods/chat.py @@ -4,9 +4,11 @@ import itertools from anonstream.chat import delete_chat_messages +from anonstream.control.exceptions import CommandFailed from anonstream.control.spec import NoParse -from anonstream.control.spec.common import Str, End, Args +from anonstream.control.spec.common import Str, End, Args, ArgsJsonString, ArgsUser from anonstream.control.spec.utils import get_item, json_dumps_contiguous +from anonstream.chat import add_chat_message, Rejected class ArgsSeqs(Args): def consume(self, words, index): @@ -33,12 +35,18 @@ async def cmd_chat_help(): response = ( 'Usage: chat delete SEQS\n' 'Commands:\n' - #' chat show [MESSAGES]......show chat messages\n' - ' chat delete SEQS..........delete chat messages\n' + #' chat show [MESSAGES]...........show chat messages\n' + ' chat delete SEQS...............delete chat messages\n' + ' chat add USER NONCE COMMENT....add chat message\n' 'Definitions:\n' - #' MESSAGES..................undefined\n' - ' SEQS......................=SEQ [SEQ...]\n' - ' SEQ.......................a chat message\'s seq, base-10 integer\n' + #' MESSAGES...................undefined\n' + ' SEQS.......................=SEQ [SEQ...]\n' + ' SEQ........................a chat message\'s seq, base-10 integer\n' + ' USER.......................={token TOKEN | hash HASH}\n' + ' TOKEN......................a user\'s token, json string\n' + ' HASH.......................a user\'s token hash\n' + ' NONCE......................a chat message\'s nonce, json string\n' + ' COMMENT....................json string\n' ) return normal, response @@ -48,8 +56,24 @@ async def cmd_chat_delete(*seqs): response = '' return normal, response +async def cmd_chat_add(user, nonce, comment): + try: + seq = add_chat_message(user, nonce, comment) + except Rejected as e: + raise CommandFailed(f'rejected: {e}') from e + else: + assert seq is not None + normal = [ + 'chat', 'add', + 'token', json_dumps_contiguous(user['token']), + json_dumps_contiguous(nonce), json_dumps_contiguous(comment), + ] + response = str(seq) + '\n' + return normal, response + SPEC = Str({ None: End(cmd_chat_help), 'help': End(cmd_chat_help), 'delete': ArgsSeqs(End(cmd_chat_delete)), + 'add': ArgsUser(ArgsJsonString(ArgsJsonString(End(cmd_chat_add)))), }) diff --git a/anonstream/control/spec/methods/help.py b/anonstream/control/spec/methods/help.py index 10fa502..bd251e4 100644 --- a/anonstream/control/spec/methods/help.py +++ b/anonstream/control/spec/methods/help.py @@ -21,6 +21,7 @@ async def cmd_help(): ' user add VERIFIED TOKEN........add new user\n' #' chat show MESSAGES.............show a list of messages\n' ' chat delete SEQS...............delete a set of messages\n' + ' chat add USER NONCE COMMENT....add chat message\n' ' allowedness [show].............show the current allowedness\n' ' allowedness setdefault BOOLEAN.set the default allowedness\n' ' allowedness add SET STRING.....add to the blacklist/whitelist\n'