validators.py
131 lines
| 4.1 KiB
| text/x-python
|
PythonLexer
neko259
|
r2065 | import time | ||
import hashlib | ||||
from django.core.cache import cache | ||||
from django.utils.translation import ugettext_lazy as _, ungettext_lazy | ||||
from boards import settings as board_settings | ||||
from boards.settings import SECTION_FORMS | ||||
LAST_POST_TIME = 'last_post_time' | ||||
LAST_LOGIN_TIME = 'last_login_time' | ||||
POW_HASH_LENGTH = 16 | ||||
POW_LIFE_MINUTES = 5 | ||||
ERROR_SPEED = 'Please wait %(delay)d second before sending message' | ||||
ERROR_SPEED_PLURAL = 'Please wait %(delay)d seconds before sending message' | ||||
ERROR_INVALID_POW = 'Invalid PoW.' | ||||
class Validator: | ||||
def __init__(self, session): | ||||
self.errors = [] | ||||
self.session = session | ||||
def validate(self) -> None: | ||||
""" | ||||
Runs validation. If something fails, should add an error to the list. | ||||
""" | ||||
pass | ||||
def get_errors(self): | ||||
return self.errors | ||||
def get_session(self): | ||||
return self.session | ||||
def add_error(self, error): | ||||
self.errors.append(error) | ||||
class TimeValidator(Validator): | ||||
""" | ||||
Validates the posting speed. New post must be sent no less than after some | ||||
specific amount of time defined in settings. | ||||
""" | ||||
def validate(self): | ||||
can_post = True | ||||
posting_delay = board_settings.get_int(SECTION_FORMS, 'PostingDelay') | ||||
if board_settings.get_bool(SECTION_FORMS, 'LimitPostingSpeed'): | ||||
now = time.time() | ||||
current_delay = 0 | ||||
if LAST_POST_TIME not in self.get_session(): | ||||
self.get_session()[LAST_POST_TIME] = now | ||||
need_delay = True | ||||
else: | ||||
last_post_time = self._get_last_post_time() | ||||
current_delay = int(now - last_post_time) | ||||
need_delay = current_delay < posting_delay | ||||
self._set_session_cache(LAST_POST_TIME, now) | ||||
if need_delay: | ||||
delay = posting_delay - current_delay | ||||
error_message = ungettext_lazy(ERROR_SPEED, ERROR_SPEED_PLURAL, | ||||
delay) % {'delay': delay} | ||||
self.add_error(error_message) | ||||
can_post = False | ||||
if can_post: | ||||
self.get_session()[LAST_POST_TIME] = now | ||||
else: | ||||
# Reset the time since posting failed | ||||
self._set_session_cache(LAST_POST_TIME, self.get_session()[LAST_POST_TIME]) | ||||
def _get_cache_key(self, key): | ||||
return '{}_{}'.format(self.get_session().session_key, key) | ||||
def _set_session_cache(self, key, value): | ||||
cache.set(self._get_cache_key(key), value) | ||||
def _get_session_cache(self, key): | ||||
return cache.get(self._get_cache_key(key)) | ||||
def _get_last_post_time(self): | ||||
last = self._get_session_cache(LAST_POST_TIME) | ||||
if last is None: | ||||
last = self.get_session().get(LAST_POST_TIME) | ||||
return last | ||||
class PowValidator(Validator): | ||||
""" | ||||
Validates prof of work. A hash is computed on the client side and validated | ||||
here, all data must match. PoW difficulty is set in the settings. | ||||
""" | ||||
def __init__(self, session, timestamp, iteration, guess, text): | ||||
super().__init__(session) | ||||
self.timestamp = timestamp | ||||
self.iteration = iteration | ||||
self.guess = guess | ||||
self.text = text | ||||
def validate(self): | ||||
if self.timestamp and self.iteration and self.guess: | ||||
self._validate_hash(self.timestamp, self.iteration, | ||||
self.guess, self.text) | ||||
else: | ||||
self.add_error(_(ERROR_INVALID_POW)) | ||||
def _validate_hash(self, timestamp: str, iteration: str, guess: str, message: str): | ||||
payload = timestamp + message.replace('\r\n', '\n') | ||||
difficulty = board_settings.get_int(SECTION_FORMS, 'PowDifficulty') | ||||
target = str(int(2 ** (POW_HASH_LENGTH * 3) / difficulty)) | ||||
if len(target) < POW_HASH_LENGTH: | ||||
target = '0' * (POW_HASH_LENGTH - len(target)) + target | ||||
computed_guess = hashlib.sha256((payload + iteration).encode()) \ | ||||
.hexdigest()[0:POW_HASH_LENGTH] | ||||
if guess != computed_guess or guess > target: | ||||
self.add_error(_('Invalid PoW.')) | ||||