##// END OF EJS Templates
Added domain image for itc.ua
Added domain image for itc.ua

File last commit:

r2065:770356bf default
r2124:18184444 default
Show More
validators.py
131 lines | 4.1 KiB | text/x-python | PythonLexer
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.'))