예제 #1
0
class SecurityHandler(web.RequestHandler):
    key = "user_blacklist"

    def __init__(self, application, request, **kwargs):
        super().__init__(application, request, **kwargs)
        self.r = Redis().r

    def prepare(self):
        if self.check_request():
            self.set_status(HTTPStatus.FORBIDDEN)
            self.finish()

    def data_received(self, chunk):
        pass

    def check_request(self):
        ban = self.__ip_check()
        user = self.__user_check()
        result = ban or user
        if result:
            self.ban()
        return result

    def get_real_ip(self):
        x_real = self.request.headers.get("X-Real-IP")
        remote_ip = self.request.remote_ip
        logging.debug("X-Real-IP:%s, Remote-IP:%s", x_real, remote_ip)
        return x_real or remote_ip

    def ban(self):
        ip = self.get_real_ip()
        self.r.incr(ip)
        count = int(self.r.get(ip))
        # ban rule: (count-10)*600
        if count <= 10:
            ex = 120
        else:
            ex = (count - 10) * 600
        if count >= 30:
            add_cf_blacklist(ip)
        self.r.set(ip, count, ex)
        user = self.get_current_user()
        if user:
            self.r.hincrby(self.key, user)

    def get_current_user(self) -> str:
        username = self.get_secure_cookie("username") or b""
        return username.decode("u8")

    def __user_check(self):
        count = self.r.hget(self.key, self.get_current_user()) or 0
        count = int(count)
        if count >= 20:
            return True

    def __ip_check(self):
        d = self.r.get(self.get_real_ip()) or 0
        if int(d) >= 10:
            return True
예제 #2
0
    def login_user(self, username: str, password: str, captcha: str,
                   captcha_id: str, ip: str, browser: str) -> dict:
        # verify captcha in the first place.
        redis = Redis().r
        correct_captcha = redis.get(captcha_id)
        if correct_captcha is None:
            return {
                "status_code": HTTPStatus.BAD_REQUEST,
                "message": "验证码已过期",
                "status": False
            }
        elif correct_captcha.lower() == captcha.lower():
            redis.expire(captcha_id, 0)
        else:
            return {
                "status_code": HTTPStatus.FORBIDDEN,
                "message": "验证码错误",
                "status": False
            }
        # check user account is locked.

        data = self.db["users"].find_one({"username": username}) or {}
        if data.get("status", {}).get("disable"):
            return {
                "status_code": HTTPStatus.FORBIDDEN,
                "status": False,
                "message": data.get("status", {}).get("reason")
            }

        returned_value = {"status_code": 0, "message": ""}

        if data:
            # try to login
            stored_password = data["password"]
            if pbkdf2_sha256.verify(password, stored_password):
                returned_value["status_code"] = HTTPStatus.OK
            else:
                returned_value["status_code"] = HTTPStatus.FORBIDDEN
                returned_value["message"] = "用户名或密码错误"

        else:
            # register
            hash_value = pbkdf2_sha256.hash(password)
            try:
                self.db["users"].insert_one(
                    dict(username=username,
                         password=hash_value,
                         date=ts_date(),
                         ip=ip,
                         browser=browser))
                returned_value["status_code"] = HTTPStatus.CREATED

            except Exception as e:
                returned_value[
                    "status_code"] = HTTPStatus.INTERNAL_SERVER_ERROR
                returned_value["message"] = str(e)

        returned_value["username"] = data.get("username")
        returned_value["group"] = data.get("group", ["user"])
        return returned_value
예제 #3
0
 def get_latest_resource() -> dict:
     redis = Redis().r
     key = "latest-resource"
     latest = redis.get(key)
     if latest:
         logging.info("Cache hit for latest resource")
         latest = json.loads(latest)
         latest["data"] = latest["data"][:100]
     else:
         logging.warning("Cache miss for latest resource")
         latest = ResourceLatestMongoResource().query_db()
         redis.set(key, json.dumps(latest, ensure_ascii=False))
     return latest
예제 #4
0
    def update_user_info(self, username: str, data: dict) -> dict:
        redis = Redis().r
        valid_fields = ["email"]
        valid_data = {}
        for field in valid_fields:
            if data.get(field):
                valid_data[field] = data[field]

        if valid_data.get("email") and not re.findall(r"\S@\S",
                                                      valid_data.get("email")):
            return {
                "status_code": HTTPStatus.BAD_REQUEST,
                "status": False,
                "message": "email format error  "
            }
        elif valid_data.get("email"):
            # rate limit
            user_email = valid_data.get("email")
            timeout_key = f"timeout-{user_email}"
            if redis.get(timeout_key):
                return {
                    "status_code": HTTPStatus.TOO_MANY_REQUESTS,
                    "status": False,
                    "message": f"try again in {redis.ttl(timeout_key)}s"
                }

            verify_code = random.randint(10000, 99999)
            valid_data["email"] = {"verified": False, "address": user_email}
            # send email confirm
            subject = "[人人影视下载分享站] 请验证你的邮箱"
            body = f"{username} 您好,<br>请输入如下验证码完成你的邮箱认证。验证码有效期为24小时。<br>" \
                   f"如果您未有此请求,请忽略此邮件。<br><br>验证码: {verify_code}"

            redis.set(timeout_key, username, ex=1800)
            redis.hset(user_email, mapping={"code": verify_code, "wrong": 0})
            redis.expire(user_email, 24 * 3600)
            send_mail(user_email, subject, body)

        self.db["users"].update_one({"username": username},
                                    {"$set": valid_data})
        return {
            "status_code": HTTPStatus.CREATED,
            "status": True,
            "message": "success"
        }