Exemple #1
0
class PinkMgr(BaseMgr):
    module = Pink

    t_life = FromConf.load('TL_PWD_RESET')
    ve_life = FromConf.load('TL_EMAIL_VERIFICATION')

    @classmethod
    def verify_email(cls, email):
        token = pat.encode(
            exp=(g.now + cls.ve_life).timestamp(),
            payload={
                'old': Pink.query.get(g.pink_id) if g.pink_id else None,
                'new': email
            })

        sendgrid.send(to=email,
                      template_name='email verification',
                      email=email,
                      token=token)

    @classmethod
    def forget_pwd(cls, name, email):
        pink = cls.model.query.filter_by(name=name).one()
        if pink and pink.email != email:
            return

        token = random_b85(k=20)
        redis.set(f'rst-{token}', pink.id, ex=cls.t_life.seconds)

        sendgrid.send(to=email,
                      template_name='pwd reset',
                      name=name,
                      token=token)

    @staticmethod
    def reset_pwd(token, pwd, device_id):
        if not (pink_id := redis.get(f'rst-{token}')):
            raise InvalidCredential(type_=InvalidCredential.T.rst)

        lemon = PinkMgr(pink_id).set_pwd(pwd, device_id)
        redis.delete(token)

        return lemon
Exemple #2
0
class MangoMgr(BaseMgr):
    model = Mango

    t_life = FromConf.load('TL_PIT_SUMBIT')

    @classmethod
    def _create(cls, pit, i):
        if mango := cls.model.query.filter_by(sha1=i['sha1']):
            raise FileExist(pit=mango.pit)

        if last := pit.mango:
            onedrive.delete(item_id=last.id)
Exemple #3
0
class ProjMgr(BaseMgr):
    model = Proj

    shift_buffer = FromConf.load('PIT_SHIFT_BUFFER')

    def _upload(self):
        self.o.status = Proj.S.upload
        self.o.add_track(info=Proj.T.upload, now=g.now)

    def post_works(self, pit):
        # check if all pits in this department are done
        exists = Pit.query.join(Role). \
            filter(Role.dep == pit.role.dep). \
            filter(Role.proj_id == self.o.id). \
            filter(Pit.status.notin_({Pit.S.fin, Pit.S.fin_p, Pit.S.dropped})). \
            exists()
        if not db.session.query(exists).scalar():
            return

        # alter start and due for the subsequent departments
        extended = pit.finish_at \
            - _dep_graph.get_finish_time(cat=self.o.cat, base=self.o.start_at, dep=pit.role.dep)
        pits = Pit.query.join(Role). \
            filter(Role.dep.in_(_dep_graph.get_dependents(cat=self.o.cat, dep=pit.role.dep))). \
            filter(Role.proj_id == self.o.id). \
            all()
        for pit_ in pits:
            pit_.status = Pit.S.working

            # sequence of the following block MUST NOT BE CHANGED
            if extended < -self.shift_buffer:
                # finished before 1 day prior to due
                pit_.add_track(info=Pit.T.shift, by=pit.id)
                pit_.start_at += extended
                pit_.due += extended

            elif extended.seconds > 0:
                # pit is extended
                pit_.add_track(info=Pit.T.cascade, by=pit.id)
                pit_.due += extended

        # check if proj can upload
        if not pits:
            # all pits in this dep are done + no further pits to fill = project can upload
            self._upload()
Exemple #4
0
class PinkMgr(BaseMgr):
    model = Pink

    t_life = FromConf.load('TL_NEW_PINK')

    @classmethod
    def assign_token(cls, deps, amount):
        check_scopes(deps)

        deps_s = ','.join(deps)
        tokens = [random_b85(k=20) for __ in range(amount)]
        redis.mset({f'deps-{token}': deps_s
                    for token in tokens},
                   ex=cls.t_life.seconds)

        return tokens

    @classmethod
    def sign_up(cls, name: str, pwd, qq: int, other: str, email_token: str,
                deps_token: str):
        if not (deps_s := redis.get(f'deps-{deps_token}')):
            raise InvalidCredential(type_=InvalidCredential.T.new)

        pink = cls.model(id=cls.gen_id(),
                         name=name,
                         email=None,
                         qq=str(qq),
                         other=other)
        pink.pwd = pwd
        pink.deps = deps_s.split(',')
        PinkMgr(pink).set_email(email_token)
        db.session.add(pink)

        sendgrid.send(to=pink.email, template_name='new pink', name=pink.name)

        return pink
Exemple #5
0
import re

import requests
from bs4 import BeautifulSoup

from models import Proj
from core.errors import InvalidSource
from core.singleton import redis
from core.utils import FromConf

_CN_SITE_URL = 'http://scp-wiki-cn.wikidot.com'
_web_exp = FromConf.load('WEB_EXP')


def fetch_web(url):
    if not (web_page_t := redis.get(f'web-{url}')):
        web_page = requests.get(f'{_CN_SITE_URL}/{url}')
        if web_page.status_code == 404:
            raise InvalidSource(rsn=InvalidSource.Rsn.web, url=url)

        web_page_t = web_page.text
        redis.set(f'web-{url}', web_page_t, ex=_web_exp)

    soup = BeautifulSoup(web_page_t, 'lxml')

    return soup.find('div', {'id': 'main-content'})


def fetch_title_by_url(url):
    if not re.match(r'^/[a-z]+(?:(?:-[a-z]+)+)?$', url):
        raise InvalidSource(rsn=InvalidSource.Rsn.inp)
Exemple #6
0
class LemonMgr(BaseMgr):
    model = Lemon

    a_life = FromConf.load('TL_ACCESS_TOKEN')
    r_life = FromConf.load('TL_REFRESH_TOKEN')

    def __init__(self, obj_or_id):
        try:
            super().__init__(obj_or_id)
            if self.o.pink_id != g.pink_id:
                raise RecordNotFound(cls_=Lemon, id_='< - secret - >')
        except RecordNotFound:
            raise InvalidRefreshToken(rsn=InvalidRefreshToken.Rsn.non)

    @classmethod
    def _grant(cls, pink, device_id):
        pink.lemons.filter_by(device_id=device_id).delete()

        lemon = cls.model(id=cls.gen_id(),
                          key=random_b85(k=40),
                          pink=pink,
                          device_id=device_id,
                          ip=request.remote_addr,
                          exp=g.now + cls.r_life,
                          timestamp=g.now)
        db.session.add(lemon)

        return lemon

    @staticmethod
    def login(name, pwd, device_id):
        pink: Pink = Pink.query.filter_by(name=name).one()
        if not pink.active:
            raise AccountDeactivated()
        if not pink or not pink.check_pwd(pwd):
            raise InvalidCredential(type_=InvalidCredential.T.pwd)

        sqlogger.log('login', '')

        return LemonMgr._grant(pink, device_id)

    def grant_access_token(self, key, device_id):
        if self.o.key != key or self.o.device_id != device_id:
            raise InvalidRefreshToken(rsn=InvalidRefreshToken.Rsn.key)
        if self.o.ip != request.remote_addr \
                and ip2loc.get_city(self.o.ip) != ip2loc.get_city(request.remote_addr):
            raise InvalidRefreshToken(rsn=InvalidRefreshToken.Rsn.ip)
        if self.o.expiration < g.now:
            self.revoke()
            raise InvalidRefreshToken(rsn=InvalidRefreshToken.Rsn.exp,
                                      at=self.o.expiration)

        # buffer
        last = redis.hget('last_access', g.pink_id)
        if last and g.now.timestamp() - last > 86400:
            self.o.expiration = g.now + self.r_life

        # assign token
        token = random_b85(k=20)
        with redis.pipeline(transaction=False) as p:
            p.hset('last_access', g.pink_id, g.now.timestamp())
            p.set(token, g.pink_id, ex=self.a_life.seconds)
            p.execute()

        return token, g.now + self.a_life

    def revoke(self):
        db.session.delete(self.o)

    @staticmethod
    def revoke_all():
        revoke_all_lemons(pink_or_id=g.pink_id)
Exemple #7
0
import pickle
from math import log2
from typing import Set

from core.errors import WeekPwd
from core.utils import FromConf

common_pwd: Set[str] = set()
with FromConf.load('PWDDB_PATH').open('rb') as f:
    common_pwd = pickle.load(f)


def is_common_pwd(pwd: str) -> bool:
    return pwd in common_pwd


# modified from
# https://github.com/kolypto/py-password-strength/blob/master/password_strength/stats.py

WEAK_BITS: int = 30
WEAK_MAX: float = 1 / 3
HARD_BITS: int = WEAK_BITS * 3
HARD_VAL: float = 0.950
K: float = -log2((1 - HARD_VAL) / (1 - WEAK_MAX)) / HARD_BITS


def measure_strength(pwd: str) -> float:
    ''' Get password strength as a number normalized to range {0 .. 1}.
    Normalization is done in the following fashion:
    1. If entropy_bits <= weak_bits   -- linear in range{0.0 .. 0.33} (weak)
    2. If entropy_bits <= weak_bits*2 -- almost linear in range{0.33 .. 0.66} (medium)