diff --git a/anonstream/__init__.py b/anonstream/__init__.py index 4364d30..27b6643 100644 --- a/anonstream/__init__.py +++ b/anonstream/__init__.py @@ -6,8 +6,9 @@ from collections import OrderedDict from quart_compress import Compress from anonstream.config import update_flask_from_toml -from anonstream.utils.captcha import create_captcha_factory, create_captcha_signer from anonstream.quart import Quart +from anonstream.utils.captcha import create_captcha_factory, create_captcha_signer +from anonstream.utils.user import generate_blank_allowedness __version__ = '1.3.6' @@ -30,7 +31,7 @@ def create_app(toml_config): 'COMPRESS_LEVEL': 9, }) - # Global state: messages, users, captchas + # Global state: messages, users, captchas, etc. app.messages_by_id = OrderedDict() app.messages = app.messages_by_id.values() @@ -41,7 +42,8 @@ def create_app(toml_config): app.captcha_factory = create_captcha_factory(app.config['CAPTCHA_FONTS']) app.captcha_signer = create_captcha_signer(app.config['SECRET_KEY']) - app.failures = OrderedDict() + app.failures = OrderedDict() # access captcha failures + app.allowedness = generate_blank_allowedness() # State for tasks app.users_update_buffer = set() diff --git a/anonstream/helpers/user.py b/anonstream/helpers/user.py index 4668d29..9715dac 100644 --- a/anonstream/helpers/user.py +++ b/anonstream/helpers/user.py @@ -48,6 +48,7 @@ def generate_user( 'watching': -inf, 'eyes': -inf, 'reading': -inf, + 'allowed': -inf, }, 'presence': presence, 'linespan': deque(), diff --git a/anonstream/user.py b/anonstream/user.py index c27c56e..69fbbfa 100644 --- a/anonstream/user.py +++ b/anonstream/user.py @@ -3,6 +3,7 @@ import operator import time +from functools import reduce from math import inf from quart import current_app @@ -17,6 +18,7 @@ from anonstream.utils.user import get_user_for_websocket, trilean CONFIG = current_app.config MESSAGES = current_app.messages USERS = current_app.users +ALLOWEDNESS = current_app.allowedness CAPTCHA_SIGNER = current_app.captcha_signer USERS_UPDATE_BUFFER = current_app.users_update_buffer @@ -41,6 +43,15 @@ class DeletedEyes(EyesException): class ExpiredEyes(EyesException): pass +class AllowednessException(Exception): + pass + +class Blacklisted(AllowednessException): + pass + +class SecretClub(AllowednessException): + pass + def add_state(user, **state): state_id = time.time_ns() // 1_000_000 user['state'][state_id] = state @@ -302,3 +313,31 @@ def renew_eyes(timestamp, user, eyes_id, just_read_new_segment=False): if just_read_new_segment: eyes['n_segments'] += 1 eyes['renewed'] = timestamp + +def ensure_allowedness(user, timestamp=None): + if timestamp is None: + timestamp = get_timestamp() + + # Check against blacklist + for keytuple, values in ALLOWEDNESS['blacklist'].items(): + try: + value = reduce(lambda mapping, key: mapping[key], keytuple, user) + except (KeyError, TypeError): + value = None + if value in values: + raise Blacklisted + + # Check against whitelist + for keytuple, values in ALLOWEDNESS['whitelist'].items(): + try: + value = reduce(lambda mapping, key: mapping[key], keytuple, user) + except (KeyError, TypeError): + value = None + if value in values: + break + else: + # Apply default + if not ALLOWEDNESS['default']: + raise SecretClub + + user['last']['allowed'] = timestamp diff --git a/anonstream/utils/user.py b/anonstream/utils/user.py index 463db6a..98120b2 100644 --- a/anonstream/utils/user.py +++ b/anonstream/utils/user.py @@ -60,3 +60,17 @@ def identifying_string(user, ansi=True): token_hash = f'\033[32m{token_hash}\033[0m' token = f'\033[35m{token}\033[0m' return '/'.join((tag, token_hash, token)) + +def generate_blank_allowedness(): + return { + 'blacklist': { + ('token',): set(), + ('token_hash',): set(), + }, + 'whitelist': { + ('token',): set(), + ('token_hash',): set(), + ('tripcode', 'digest'): set(), + }, + 'default': True, + }