示例#1
0
文件: cli.py 项目: djfbob/picbed
def exec_createuser(username, password, **kwargs):
    """创建账号"""
    ak = rsp("accounts")
    if check_username(username):
        if not password or len(password) < 6:
            echo("密码最少6位", "yellow")
        else:
            rc = create_redis_engine()
            if rc.sismember(ak, username):
                echo("用户名已存在", "red")
            else:
                is_admin = kwargs.pop("is_admin", 0)
                uk = rsp("account", username)
                pipe = rc.pipeline()
                pipe.sadd(ak, username)
                if kwargs:
                    pipe.hmset(uk, kwargs)
                pipe.hmset(
                    uk,
                    dict(username=username,
                         password=generate_password_hash(password),
                         is_admin=1 if is_true(is_admin) else 0,
                         ctime=get_current_timestamp()))
                try:
                    pipe.execute()
                except RedisError as e:
                    echo(e.message, "red")
                else:
                    echo("注册成功!", "green")
                finally:
                    rc.connection_pool.disconnect()
    else:
        echo("用户名不合法或不允许注册", "yellow")
示例#2
0
文件: api.py 项目: qdjx/picbed
def album():
    res = dict(code=1, msg=None)
    #: 管理员账号查询所有相册
    is_mgr = is_true(request.args.get("is_mgr"))
    if g.userinfo.username:
        if is_mgr and g.is_admin:
            uk = rsp("index", "global")
        else:
            uk = rsp("index", "user", g.userinfo.username)
        pipe = g.rc.pipeline()
        for sha in g.rc.smembers(uk):
            pipe.hget(rsp("image", sha), "album")
        try:
            result = pipe.execute()
        except RedisError:
            res.update(msg="Program data storage service error")
        else:
            albums = [i for i in result if i]
            res.update(
                code=0,
                data=list(set(albums)),
                counter=Counter(albums)
            )
    else:
        res.update(msg="No valid username found")
    return res
示例#3
0
def before_request():
    if g.signin:
        return
    token = request.form.get("token") or parseAuthorization()
    if token:
        try:
            oldToken = token
            if PY2 and isinstance(token, text_type):
                token = token.encode("utf-8")
            token = b64decode(token)
            if not PY2 and not isinstance(token, text_type):
                token = token.decode("utf-8")
            rdm, usr, ctime, sig = token.split(".")
            ctime = int(ctime)
            assert len(rdm) == 6
        except (TypeError, ValueError, AttributeError, Exception):
            pass
        else:
            tk = rsp("tokens")
            token2usr = g.rc.hget(tk, oldToken)
            if token2usr and token2usr == usr:
                ak = rsp("account", usr)
                userinfo = g.rc.hgetall(ak)
                if userinfo and isinstance(userinfo, dict):
                    pwd = userinfo.pop("password", None)
                    if hmac_sha256(pwd, usr) == sig:
                        g.signin = True
                        g.userinfo = userinfo
示例#4
0
文件: api.py 项目: 993544899/picbed
def forgot():
    res = dict(code=1)
    Action = request.args.get("Action")
    username = request.form.get("username")
    if not username:
        res.update(msg="Parameter error")
        return res
    username = username.lower()
    ak = rsp("accounts")
    uk = rsp("account", username)

    #: 发送邮件
    if Action == "sending":
        if g.rc.sismember(ak, username):
            if is_true(int(g.rc.hget(uk, "email_verified") or 0)):
                html = make_email_tpl(
                    "activate_forgot.html",
                    activate_url=url_for(
                        "front.activate",
                        token=generate_activate_token(
                            dict(
                                Action="resetPassword",
                                username=username,
                            )),
                        _external=True,
                    ),
                    username=username,
                )
                res = sendmail(
                    subject="{}忘记密码".format(g.site_name),
                    message=html,
                    to=g.rc.hget(uk, "email"),
                )
            else:
                res.update(msg="The user has no authenticated mailbox")
        else:
            res.update(msg="No valid username found")

    #: 邮件验证通过,重置密码
    elif Action == "reset":
        token = request.form.get("token")
        password = request.form.get("password")
        if token and password:
            if len(password) < 6:
                res.update(msg="Password must be at least 6 characters")
            else:
                res = check_activate_token(token)
                if res["code"] == 0:
                    try:
                        g.rc.hset(uk, "password",
                                  generate_password_hash(password))
                    except RedisError:
                        res.update(code=1,
                                   msg="Program data storage service error")
                    else:
                        res.update(code=0)
        else:
            res.update(msg="Parameter error")

    return res
示例#5
0
文件: api.py 项目: howardyan93/picbed
def token():
    res = dict(code=1, msg=None)
    usr = g.userinfo.username
    tk = rsp("tokens")
    ak = rsp("account", usr)

    def gen_token(key):
        """生成可以用key校验的token"""
        return b64encode(
            ("%s.%s.%s.%s" %
             (generate_random(), usr, get_current_timestamp(),
              hmac_sha256(key, usr))).encode("utf-8")).decode("utf-8")

    Action = request.args.get("Action")
    if Action == "create":
        if g.rc.hget(ak, "token"):
            res.update(msg="Existing token")
        else:
            tkey = generate_random(randint(6, 12))
            token = gen_token(tkey)
            try:
                pipe = g.rc.pipeline()
                pipe.hset(tk, token, usr)
                pipe.hmset(ak, dict(token=token, token_key=tkey))
                pipe.execute()
            except RedisError:
                res.update(msg="Program data storage service error")
            else:
                res.update(code=0, token=token)
    elif Action == "revoke":
        token = g.rc.hget(ak, "token")
        if token:
            try:
                pipe = g.rc.pipeline()
                pipe.hdel(tk, token)
                pipe.hdel(ak, "token")
                pipe.execute()
            except RedisError:
                res.update(msg="Program data storage service error")
            else:
                res.update(code=0)
        else:
            res.update(msg="No tokens yet")
    elif Action == "reset":
        oldToken = g.rc.hget(ak, "token")
        tkey = generate_random(randint(6, 12))
        token = gen_token(tkey)
        try:
            pipe = g.rc.pipeline()
            if oldToken:
                pipe.hdel(tk, oldToken)
            pipe.hset(tk, token, usr)
            pipe.hmset(ak, dict(token=token, token_key=tkey))
            pipe.execute()
        except RedisError:
            res.update(msg="Program data storage service error")
        else:
            res.update(code=0, token=token)
    return res
示例#6
0
文件: api.py 项目: howardyan93/picbed
def login():
    res = dict(code=1, msg=None)
    usr = request.form.get("username")
    pwd = request.form.get("password")
    #: 定义是否设置cookie状态
    set_state = is_true(request.form.get("set_state"))
    is_secure = False if request.url_root.split("://")[0] == "http" else True
    max_age = 7200
    if is_true(request.form.get("remember")):
        #: Remember me 7d
        max_age = 604800
    #: 登录接口钩子
    try:
        if g.cfg.site_auth:
            so = current_app.extensions["hookmanager"].proxy(g.cfg.site_auth)
            if so and hasattr(so, "login_api"):
                result = so.login_api(usr, pwd, set_state, max_age, is_secure)
                if result and isinstance(result, Response):
                    return result
    except (ValueError, TypeError, Exception) as e:
        logger.warning(e, exc_info=True)
    if usr and username_pat.match(usr) and pwd and len(pwd) >= 6:
        ak = rsp("accounts")
        usr = usr.lower()
        if g.rc.sismember(ak, usr):
            userinfo = g.rc.hgetall(rsp("account", usr))
            if is_true(g.cfg.disable_login) and \
                    not is_true(userinfo.get("is_admin")):
                res.update(msg="Normal user login has been disabled")
                return res
            password = userinfo.get("password")
            if password and check_password_hash(password, pwd):
                expire = get_current_timestamp() + max_age
                sid = "%s.%s.%s" % (usr, expire,
                                    sha256("%s:%s:%s:%s" %
                                           (usr, password, expire,
                                            current_app.config["SECRET_KEY"])))
                sid = b64encode(sid.encode("utf-8")).decode("utf-8")
                res.update(
                    code=0,
                    sid=sid,
                    expire=expire,
                    # is_admin=is_true(userinfo.get("is_admin"))
                )
                if set_state:
                    res = make_response(jsonify(res))
                    res.set_cookie(key="dSid",
                                   value=sid,
                                   max_age=max_age,
                                   httponly=True,
                                   secure=is_secure)
            else:
                res.update(msg="Password verification failed")
        else:
            res.update(msg="No valid username found")
    else:
        res.update(msg="The username or password parameter error")
    return res
示例#7
0
def waterfall():
    if request.method == "GET":
        return abort(404)
    res = dict(code=1, msg=None)
    #: 依次根据ctime、filename排序
    sort = request.args.get("sort") or "desc"
    #: 符合人类习惯的page,第一页是1(程序计算需要减1)
    page = request.args.get("page") or 1
    #: 返回数据条数
    limit = request.args.get("limit") or 10
    #: 管理员账号读取所有图片数据
    is_mgr = is_true(request.args.get("is_mgr"))
    try:
        page = int(page) - 1
        limit = int(limit)
        if page < 0:
            raise ValueError
    except (ValueError, TypeError):
        res.update(code=2, msg="Parameter error")
    else:
        if g.userinfo.username:
            if is_mgr and g.is_admin:
                uk = rsp("index", "global")
            else:
                uk = rsp("index", "user", g.userinfo.username)
            pipe = g.rc.pipeline()
            for sha in g.rc.smembers(uk):
                pipe.hgetall(rsp("image", sha))
            try:
                result = pipe.execute()
            except RedisError:
                res.update(msg="Program data storage service error")
            else:
                data = []
                if result and isinstance(result, (tuple, list)):
                    for i in result:
                        i.update(
                            senders=json.loads(i["senders"]),
                            ctime=int(i["ctime"]),
                        )
                        data.append(i)
                data = sorted(data,
                              key=lambda k:
                              (k.get('ctime', 0), k.get('filename', '')),
                              reverse=False if sort == "asc" else True)
                count = len(data)
                data = ListEqualSplit(data, limit)
                pageCount = len(data)
                if page < pageCount:
                    res.update(code=0,
                               count=count,
                               data=data[page],
                               pageCount=pageCount)
                else:
                    res.update(code=3, msg="No data")
        else:
            res.update(msg="No valid username found")
    return res
示例#8
0
def token():
    if request.method == "GET":
        return abort(404)
    res = dict(code=1)
    usr = g.userinfo.username
    tk = rsp("tokens")
    ak = rsp("account", usr)
    #: 生成token
    gen_token = lambda: b64encode(
        ("%s.%s.%s.%s" % (generate_random(), usr, get_current_timestamp(),
                          hmac_sha256(g.rc.hget(ak, "password"), usr))).encode(
                              "utf-8")).decode("utf-8")
    Action = request.args.get("Action")
    if Action == "create":
        if g.rc.hget(ak, "token"):
            res.update(msg="Existing token")
        else:
            token = gen_token()
            try:
                pipe = g.rc.pipeline()
                pipe.hset(tk, token, usr)
                pipe.hset(ak, "token", token)
                pipe.execute()
            except RedisError:
                res.update(msg="Program data storage service error")
            else:
                res.update(code=0, token=token)
    elif Action == "revoke":
        token = g.rc.hget(ak, "token")
        if token:
            try:
                pipe = g.rc.pipeline()
                pipe.hdel(tk, token)
                pipe.hdel(ak, "token")
                pipe.execute()
            except RedisError:
                res.update(msg="Program data storage service error")
            else:
                res.update(code=0)
        else:
            res.update(msg="No tokens yet")
    elif Action == "reset":
        oldToken = g.rc.hget(ak, "token")
        token = gen_token()
        try:
            pipe = g.rc.pipeline()
            if oldToken:
                pipe.hdel(tk, oldToken)
            pipe.hset(tk, token, usr)
            pipe.hset(ak, "token", token)
            pipe.execute()
        except RedisError:
            res.update(msg="Program data storage service error")
        else:
            res.update(code=0, token=token)
    return res
示例#9
0
def waterfall():
    res = dict(code=1, msg=None)
    if not g.signin:
        res.update(code=403, msg="Anonymous user is not sign in")
        return res
    #: 依次根据ctime、filename排序
    sort = request.args.get("sort") or request.form.get("sort") or "desc"
    #: 符合人类习惯的page,第一页是1(程序计算需要减1)
    page = request.args.get("page") or request.form.get("page") or 1
    #: 返回数据条数
    limit = request.args.get("limit") or request.form.get("limit") or 10
    try:
        page = int(page) - 1
        limit = int(limit)
        if page < 0:
            raise ValueError
    except (ValueError, TypeError):
        res.update(code=2, msg="Parameter error")
        return res

    vs = g.rc.smembers(rsp("index", "video", g.userinfo.username))
    pipe = g.rc.pipeline()
    for sha in vs:
        pipe.hgetall(rsp("video", sha))
    try:
        result = pipe.execute()
    except RedisError:
        res.update(code=3, msg="Program data storage service error")
    else:
        data = []
        if result and isinstance(result, (tuple, list)):
            for i in result:
                i.update(
                    senders=json.loads(i["senders"]),
                    ctime=int(i["ctime"]),
                )
                data.append(i)
        data = sorted(data,
                      key=lambda k: (k.get('ctime', 0), k.get('filename', '')),
                      reverse=False if sort == "asc" else True)
        count = len(data)
        data = list_equal_split(data, limit)
        pageCount = len(data)
        if page < pageCount:
            res.update(
                code=0,
                count=count,
                data=data[page],
                pageCount=pageCount,
            )
        else:
            res.update(code=3, msg="No data")
    return res
示例#10
0
文件: front.py 项目: xrmm/sapic
def feed():
    pipe = g.rc.pipeline()
    uk = rsp("index", "user", g.userinfo.username)
    fields = ["title", "filename", "ctime", "user", "src", "is_video"]
    for sha in g.rc.smembers(uk):
        pipe.hmget(rsp("image", sha), *fields)
    result = pipe.execute()
    data = [dict(zip(fields, i)) for i in result if i]
    xml = render_template('public/feed.xml',
                          items=sorted(data,
                                       key=lambda k: int(k.get('ctime', 0)),
                                       reverse=True)[:10])
    response = make_response(xml)
    response.headers['Content-Type'] = 'application/xml'
    return response
示例#11
0
def register():
    if is_true(g.cfg.register) is False:
        return abort(404)
    res = dict(code=1, msg=None)
    #: Required fields
    username = request.form.get("username")
    password = request.form.get("password")
    if username and password:
        username = username.lower()
        if check_username(username):
            if len(password) < 6:
                res.update(msg="Password must be at least 6 characters")
            else:
                ak = rsp("accounts")
                if g.rc.sismember(ak, username):
                    res.update(msg="The username already exists")
                else:
                    #: 用户状态 -1待审核 0禁用 1启用
                    #: 后台开启审核时默认是-1,否则是1
                    #: 禁用时无认证权限(无法登陆,无API权限)
                    # ;待审核仅无法上传,允许登录和API调用
                    status = -1 if is_true(g.cfg.review) else 1
                    #: 参数校验通过,执行注册
                    options = dict(
                        username=username,
                        password=generate_password_hash(password),
                        is_admin=0,
                        avatar=request.form.get("avatar") or "",
                        nickname=request.form.get("nickname") or "",
                        ctime=get_current_timestamp(),
                        status=status,
                    )
                    uk = rsp("account", username)
                    pipe = g.rc.pipeline()
                    pipe.sadd(ak, username)
                    pipe.hmset(uk, options)
                    try:
                        pipe.execute()
                    except RedisError:
                        res.update(msg="Program data storage service error")
                    else:
                        res.update(code=0)
        else:
            res.update(
                msg="The username is invalid or registration is not allowed")
    else:
        res.update(msg="Parameter error")
    return res
示例#12
0
文件: front.py 项目: 993544899/picbed
def activate(token):
    res = dfr(check_activate_token(token))
    if res["code"] == 0:
        data = res["data"]
        Action = data["Action"]

        if Action == "verifyEmail":
            username = data["username"]
            checkmail = data["email"]
            uk = rsp("account", username)
            usermail = g.rc.hget(uk, "email")
            success = False
            url = url_for("front.my") if g.signin else url_for("front.login")
            if checkmail == usermail:
                g.rc.hset(uk, "email_verified", 1)
                success = True
            return render_template("public/go.html", url=url, success=success)

        elif Action == "resetPassword":
            username = data["username"]
            return render_template("public/forgot.html",
                                   is_reset=True,
                                   token=token,
                                   user=username)

    else:
        name = res["msg"]
        if PY2 and not isinstance(name, text_type):
            name = name.decode("utf-8")
        return render_template("public/error.html",
                               code=res["code"],
                               name=name)
示例#13
0
 def test_utils(self):
     self.assertEqual("900150983cd24fb0d6963f7d28e17f72", md5("abc"))
     self.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d",
                      sha1("abc"))
     self.assertEqual("picbed:a:b", rsp("a", "b"))
     self.assertEqual(parse_valid_comma("a,b, c,"), ["a", "b", "c"])
     self.assertEqual(parse_valid_verticaline("a|b| c"), ["a", "b", "c"])
     self.assertTrue(is_true(1))
     self.assertTrue(is_true("on"))
     self.assertTrue(is_true("true"))
     self.assertFalse(is_true(0))
     self.assertIsInstance(get_current_timestamp(), int)
     self.assertTrue(allowed_file("test.PNG"))
     self.assertTrue(allowed_file(".jpeg"))
     self.assertFalse(allowed_file("my.psd"))
     self.assertFalse(allowed_file("ha.gif", ["jpg"]))
     self.assertFalse(allowed_file("ha.jpeg", ["jpg"]))
     self.assertFalse(allowed_file("ha.png", ["jpg"]))
     self.assertTrue(allowed_file("ha.jpg", ["jpg"]))
     v = "6afa9046a9579cad143a384c1b564b9a250d27d6f6a63f9f20bf3a7594c9e2c6"
     self.assertEqual(v, hmac_sha256('key', 'text'))
     self.assertEqual(v, hmac_sha256(b'key', b'text'))
     self.assertEqual(v, hmac_sha256(u'key', u'text'))
     self.assertEqual(
         "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
         sha256("abc"))
示例#14
0
文件: api.py 项目: amikoj/picbed
def login():
    if request.method == "GET":
        return abort(404)
    res = dict(code=1)
    usr = request.form.get("username")
    pwd = request.form.get("password")
    #: 定义是否设置cookie状态
    set_state = is_true(request.form.get("set_state"))
    is_secure = False if request.url_root.split("://")[0] == "http" else True
    max_age = 7200
    if is_true(request.form.get("remember")):
        #: Remember me 7d
        max_age = 604800
    if usr and pwd and check_username(usr) and len(pwd) >= 6:
        ak = rsp("accounts")
        if rc.sismember(ak, usr):
            userinfo = rc.hgetall(rsp("account", usr))
            password = userinfo.get("password")
            if password and check_password_hash(password, pwd):
                expire = get_current_timestamp() + max_age
                sid = "%s.%s.%s" % (usr, expire,
                                    sha256("%s:%s:%s:%s" %
                                           (usr, password, expire,
                                            current_app.config["SECRET_KEY"])))
                sid = b64encode(sid.encode("utf-8")).decode("utf-8")
                res.update(
                    code=0,
                    sid=sid,
                    expire=expire,
                    # is_admin=is_true(userinfo.get("is_admin"))
                )
                if set_state:
                    res = make_response(jsonify(res))
                    res.set_cookie(key="dSid",
                                   value=sid,
                                   max_age=max_age,
                                   httponly=True,
                                   secure=is_secure)
            else:
                res.update(msg="Password verification failed")
        else:
            res.update(msg="No valid username found")
    else:
        res.update(msg="Parameter error")
    return res
示例#15
0
文件: api.py 项目: qdjx/picbed
def my():
    res = dict(code=1, msg=None)
    ak = rsp("account", g.userinfo.username)
    Action = request.args.get("Action")
    if Action == "updateProfile":
        allowed_fields = ["nickname", "avatar"]
        data = {
            k: v
            for k, v in iteritems(request.form.to_dict())
            if k in allowed_fields
        }
        try:
            data.update(mtime=get_current_timestamp())
            g.rc.hmset(ak, data)
        except RedisError:
            res.update(msg="Program data storage service error")
        else:
            res.update(code=0)
            #: 更新资料触发一次钩子
            current_app.extensions["hookmanager"].call(
                "profile_update", **data
            )
    elif Action == "updatePassword":
        passwd = request.form.get("passwd")
        repasswd = request.form.get("repasswd")
        if passwd and repasswd:
            if len(passwd) < 6:
                res.update(msg="Password must be at least 6 characters")
            else:
                if passwd != repasswd:
                    res.update(msg="Confirm passwords do not match")
                else:
                    try:
                        g.rc.hmset(ak, dict(
                            password=generate_password_hash(passwd),
                        ))
                    except RedisError:
                        res.update(msg="Program data storage service error")
                    else:
                        res.update(code=0)
        else:
            res.update(msg="Parameter error")
    elif Action == "updateUserCfg":
        #: 更新用户设置,为避免覆盖ak字段,所有更新的key必须`ucfg_`开头
        cfgs = request.form.to_dict()
        if cfgs:
            for k in cfgs:
                if not k.startswith("ucfg_"):
                    res.update(msg="The user setting must start with `ucfg_`")
                    return res
            try:
                g.rc.hmset(ak, cfgs)
            except RedisError:
                res.update(msg="Program data storage service error")
            else:
                res.update(code=0)
    return res
示例#16
0
def register():
    if request.method == "GET" or is_true(g.cfg.register) is False:
        return abort(404)
    res = dict(code=1)
    #: Required fields
    username = request.form.get("username")
    password = request.form.get("password")
    if username and password:
        if check_username(username):
            if len(password) < 6:
                res.update(msg="Password must be at least 6 characters")
            else:
                ak = rsp("accounts")
                if g.rc.sismember(ak, username):
                    res.update(msg="The username already exists")
                else:
                    #: 参数校验通过,执行注册
                    options = dict(
                        username=username,
                        password=generate_password_hash(password),
                        is_admin=0,
                        avatar=request.form.get("avatar") or "",
                        nickname=request.form.get("nickname") or "",
                        ctime=get_current_timestamp(),
                    )
                    uk = rsp("account", username)
                    pipe = g.rc.pipeline()
                    pipe.sadd(ak, username)
                    pipe.hmset(uk, options)
                    try:
                        pipe.execute()
                    except RedisError:
                        res.update(msg="Program data storage service error")
                    else:
                        res.update(code=0)
        else:
            res.update(
                msg="The username is invalid or registration is not allowed"
            )
    return res
示例#17
0
def remove():
    res = dict(code=1, msg=None)
    if not g.signin:
        res.update(code=403, msg="Anonymous user is not sign in")
        return res
    sha = request.form.get("sha")
    ivs = rsp("index", "video", g.userinfo.username)
    if not sha or not g.rc.sismember(ivs, sha):
        res.update(code=404, msg="Not Found")
        return res
    info = g.rc.hgetall(rsp("video", sha))
    if g.userinfo.username == info.get("user"):
        pipe = g.rc.pipeline()
        pipe.srem(ivs, sha)
        pipe.delete(rsp("video", sha))
        try:
            pipe.execute()
        except RedisError:
            res.update(msg="Program data storage service error")
        else:
            res.update(code=0)
            try:
                senders = json.loads(info.get("senders"))
                for i in senders:
                    current_app.extensions["hookmanager"].proxy(
                        i["sender"]).upimg_delete(
                            sha=sha,
                            upload_path=info["upload_path"],
                            filename=info["filename"],
                            basedir=(join(current_app.root_path,
                                          current_app.static_folder,
                                          UPLOAD_FOLDER) if i["sender"]
                                     == "up2local" else i.get("basedir")),
                            save_result=i)
            except (ValueError, AttributeError, Exception):
                pass
    else:
        res.update(code=403, msg="Forbidden")
    return res
示例#18
0
class RedisStorage(BaseStorage):
    """Use redis stand-alone storage"""

    DEFAULT_INDEX = rsp("dat")

    def __init__(self, index=None, redis_url=None, redis_connection=None):
        self.COVERED_INDEX = index
        self._db = self._open(redis_url) if redis_url else redis_connection

    def _open(self, redis_url):
        try:
            return create_redis_engine(redis_url)
        except ImportError:
            raise ImportError(
                "Please install the redis module, eg: pip install redis"
            )

    @property
    def list(self):
        """list redis hash data"""
        return {
            k: json.loads(v)
            for k, v in iteritems(self._db.hgetall(self.index))
        }

    def set(self, key, value):
        """set key data"""
        return self._db.hset(self.index, key, json.dumps(value))

    def setmany(self, **mapping):
        if mapping and isinstance(mapping, dict):
            mapping = {k: json.dumps(v) for k, v in iteritems(mapping)}
            return self._db.hmset(self.index, mapping)

    def get(self, key, default=None):
        """get key data from redis"""
        v = self._db.hget(self.index, key)
        if v:
            return json.loads(v)
        return default

    def remove(self, key):
        """delete key from redis"""
        return self._db.hdel(self.index, key)

    def __len__(self):
        return self._db.hlen(self.index)

    def __del__(self):
        if self._db:
            self._db.connection_pool.disconnect()
示例#19
0
def report(classify):
    res = dict(code=1, msg=None)
    start = request.args.get("start")
    end = request.args.get("end")
    page = request.args.get("page")
    limit = request.args.get("limit")
    sort = (request.args.get("sort") or "asc").upper()
    is_mgr = is_true(request.args.get("is_mgr"))
    if classify in ("linktokens", ):
        try:
            #: start、end可正可负
            start = int(start)
            end = int(end)
        except (ValueError, TypeError):
            try:
                page = int(page)
                limit = int(limit or 10)
                if page - 1 < 0:
                    raise ValueError
            except (ValueError, TypeError):
                res.update(msg="Parameter error")
                return res
            else:
                start = (page - 1) * limit
                end = start + limit - 1
        if isinstance(start, int) and isinstance(end, int):
            key = rsp("report", classify, g.userinfo.username)
            try:
                pipe = g.rc.pipeline()
                pipe.llen(key)
                pipe.lrange(key, start, end)
                result = pipe.execute()
            except RedisError:
                res.update(msg="Program data storage service error")
            else:
                count, data = result
                if sort == "DESC":
                    data.reverse()
                res.update(
                    code=0,
                    data=[json.loads(r) for r in data if r],
                    count=count,
                )
        else:
            res.update(msg="Wrong query range parameter")
    else:
        return abort(404)
    return res
示例#20
0
文件: api.py 项目: howardyan93/picbed
def report(classify):
    res = dict(code=1, msg=None)
    start = request.args.get("start")
    end = request.args.get("end")
    page = request.args.get("page")
    limit = request.args.get("limit")
    sort = (request.args.get("sort") or "asc").upper()
    is_mgr = is_true(request.args.get("is_mgr"))
    if classify in ("linktokens", ):
        try:
            #: start、end可正可负
            start = int(start)
            end = int(end)
        except (ValueError, TypeError):
            try:
                page = int(page)
                limit = int(limit or 10)
                if page - 1 < 0:
                    raise ValueError
            except (ValueError, TypeError):
                res.update(msg="Parameter error")
                return res
            else:
                start = (page - 1) * limit
                end = start + limit - 1
        if isinstance(start, int) and isinstance(end, int):
            key = rsp("report", classify)
            try:
                data = g.rc.lrange(key, start, end)
            except RedisError:
                res.update(msg="Program data storage service error")
            else:
                if sort == "DESC":
                    data.reverse()
                new = []
                for r in data:
                    r = json.loads(r)
                    if is_mgr and g.is_admin:
                        new.append(r)
                    else:
                        if r.get("user") == g.userinfo.username:
                            new.append(r)
                res.update(code=0, data=new, count=len(new))
        else:
            res.update(msg="Wrong query range parameter")
    else:
        return abort(404)
    return res
示例#21
0
def my():
    if request.method == "GET":
        return abort(404)
    res = dict(code=1)
    ak = rsp("account", g.userinfo.username)
    Action = request.args.get("Action")
    if Action == "updateProfile":
        allowed_fields = ["nickname", "avatar"]
        data = {
            k: v
            for k, v in iteritems(request.form.to_dict())
            if k in allowed_fields
        }
        try:
            data.update(mtime=get_current_timestamp())
            g.rc.hmset(ak, data)
        except RedisError:
            res.update(msg="Program data storage service error")
        else:
            res.update(code=0)
            #: 更新资料触发一次钩子
            current_app.extensions["hookmanager"].call(
                "profile_update", **data
            )
    elif Action == "updatePassword":
        passwd = request.form.get("passwd")
        repasswd = request.form.get("repasswd")
        if passwd and repasswd:
            if len(passwd) < 6:
                res.update(msg="Password must be at least 6 characters")
            else:
                if passwd != repasswd:
                    res.update(msg="Confirm passwords do not match")
                else:
                    try:
                        g.rc.hmset(ak, dict(
                            password=generate_password_hash(passwd),
                        ))
                    except RedisError:
                        res.update(msg="Program data storage service error")
                    else:
                        res.update(code=0)
        else:
            res.update(msg="Parameter error")
    return res
示例#22
0
文件: api.py 项目: amikoj/picbed
def shamgr(sha):
    """图片查询、删除接口"""
    res = dict(code=1)
    if request.method == "GET":
        gk = rsp("index", "global")
        ik = rsp("image", sha)
        if rc.sismember(gk, sha):
            data = rc.hgetall(ik)
            data.update(
                senders=json.loads(data["senders"]) if g.is_admin else None,
                ctime=int(data["ctime"]),
            )
            res.update(code=0, data=data)
        else:
            return abort(404)
    elif request.method == "DELETE":
        if not g.signin:
            return abort(403)
        gk = rsp("index", "global")
        dk = rsp("index", "deleted")
        ik = rsp("image", sha)
        if rc.sismember(gk, sha):
            #: 图片所属用户
            #: - 如果不是匿名,那么判断请求用户是否属所属用户或管理员
            #: - 如果是匿名上传,那么只有管理员有权删除
            husr = rc.hget(ik, "user")
            if g.is_admin or (g.userinfo.username == husr):
                pipe = rc.pipeline()
                pipe.srem(gk, sha)
                pipe.sadd(dk, sha)
                pipe.hset(ik, "status", "deleted")
                pipe.srem(rsp("index", "user", husr), sha)
                try:
                    pipe.execute()
                except RedisError:
                    res.update(msg="Program data storage service error")
                else:
                    res.update(code=0)
            else:
                return abort(403)
        else:
            return abort(404)
    else:
        return abort(405)
    return res
示例#23
0
文件: api.py 项目: howardyan93/picbed
def shamgr(sha):
    """图片查询、删除接口"""
    res = dict(code=1, msg=None)
    gk = rsp("index", "global")
    ik = rsp("image", sha)
    dk = rsp("index", "deleted")
    if request.method == "GET":

        def get_url_with_suffix(d, _type):
            """根据type返回src"""
            if g.userinfo and "parsed_ucfg_url_rule_switch" in g.userinfo and \
                    is_true(g.userinfo.parsed_ucfg_url_rule_switch.get(_type)):
                return "%s%s" % (d["src"],
                                 g.userinfo.parsed_ucfg_url_rule.get(
                                     d["sender"], ""))
            return d["src"]

        if g.rc.sismember(gk, sha):
            data = g.rc.hgetall(ik)
            n = data["filename"]
            data.update(
                senders=json.loads(data["senders"]) if g.is_admin else None,
                ctime=int(data["ctime"]),
                tpl=dict(URL="%s" % get_url_with_suffix(data, "url"),
                         HTML="<img src='%s' alt='%s'>" %
                         (get_url_with_suffix(data, "html"), n),
                         rST=".. image:: %s" %
                         get_url_with_suffix(data, "rst"),
                         Markdown="![%s](%s)" %
                         (n, get_url_with_suffix(data, "markdown"))))
            res.update(code=0, data=data)
        else:
            return abort(404)
    elif request.method == "DELETE":
        if not g.signin:
            return abort(403)
        if g.rc.sismember(gk, sha):
            #: 图片所属用户
            #: - 如果不是匿名,那么判断请求用户是否属所属用户或管理员
            #: - 如果是匿名上传,那么只有管理员有权删除
            info = g.rc.hgetall(ik)
            husr = info.get("user")
            if g.is_admin or (g.userinfo.username == husr):
                pipe = g.rc.pipeline()
                pipe.srem(gk, sha)
                pipe.sadd(dk, sha)
                pipe.hset(ik, "status", "deleted")
                pipe.srem(rsp("index", "user", husr), sha)
                try:
                    pipe.execute()
                except RedisError:
                    res.update(msg="Program data storage service error")
                else:
                    res.update(code=0)
                    try:
                        #: 删除图片尝试执行senders的upimg_delete方法
                        #: 后端钩子未禁用时才会执行删除
                        #: TODO 无论禁用与否都删除?
                        senders = json.loads(info.get("senders"))
                        for i in senders:
                            current_app.extensions["hookmanager"].proxy(
                                i["sender"]).upimg_delete(
                                    sha=sha,
                                    upload_path=info["upload_path"],
                                    filename=info["filename"],
                                    basedir=(join(current_app.root_path,
                                                  current_app.static_folder,
                                                  UPLOAD_FOLDER)
                                             if i["sender"] == "up2local" else
                                             i.get("basedir")),
                                    save_result=i)
                    except (ValueError, AttributeError, Exception) as e:
                        logger.warning(e, exc_info=True)
            else:
                return abort(403)
        else:
            return abort(404)
    elif request.method == "PUT":
        Action = request.args.get("Action") or request.form.get("Action")
        if Action == "updateAlbum":
            if not g.signin:
                return abort(403)
            if not g.rc.sismember(gk, sha):
                return abort(404)
            #: 更改相册名,允许图片所属用户或管理员修改,允许置空
            album = request.form.get("album")
            shaOwner = g.rc.hget(ik, "user")
            if g.userinfo.username == shaOwner or g.is_admin:
                try:
                    g.rc.hset(ik, "album", album)
                except RedisError:
                    res.update(msg="Program data storage service error")
                else:
                    res.update(code=0)
            else:
                return abort(403)
    else:
        return abort(405)
    return res
示例#24
0
def upload():
    """上传逻辑:
    0. 判断是否登录,如果未登录则判断是否允许匿名上传
    1. 获取上传的文件,判断允许格式
    2. 生成文件名、唯一sha值、上传目录等,选择图片存储的后端钩子(单一)
        - 存储图片的钩子目前版本仅一个,默认是up2local(如果禁用则保存失败)
    3. 解析钩子数据,钩子回调数据格式:{code=?, sender=hook_name, src=?}
        - 后端保存失败或无后端的情况都要立刻返回响应
    4. 此时保存图片成功,持久化存储到全局索引、用户索引
    5. 返回响应:{code:0, data={src=?, sender=success_saved_hook_name}}
    """
    res = dict(code=1, msg=None)
    #: 匿名上传开关检测
    if not is_true(g.cfg.anonymous) and not g.signin:
        res.update(code=403, msg="Anonymous user is not sign in")
        return res
    f = request.files.get('picbed')
    #: 实时获取后台配置中允许上传的后缀,如: jpg|jpeg|png
    allowed_suffix = partial(allowed_file,
                             suffix=parse_valid_verticaline(g.cfg.upload_exts))
    if f and allowed_suffix(f.filename):
        try:
            g.rc.ping()
        except RedisError as e:
            logger.error(e, exc_info=True)
            res.update(code=2, msg="Program data storage service error")
            return res
        stream = f.stream.read()
        suffix = splitext(f.filename)[-1]
        filename = secure_filename(f.filename)
        if "." not in filename:
            filename = "%s%s" % (generate_random(8), suffix)
        #: 根据文件名规则重定义图片名
        upload_file_rule = g.cfg.upload_file_rule
        if upload_file_rule in ("time1", "time2", "time3"):
            filename = "%s%s" % (gen_rnd_filename(upload_file_rule), suffix)
        #: 上传文件位置前缀规则
        upload_path_rule = g.cfg.upload_path_rule
        if upload_path_rule == 'date1':
            upload_path = get_today("%Y/%m/%d")
        elif upload_path_rule == 'date2':
            upload_path = get_today("%Y%m%d")
        else:
            upload_path = ''
        upload_path = join(g.userinfo.username or 'anonymous', upload_path)
        #: 定义文件名唯一索引
        sha = "sha1.%s.%s" % (get_current_timestamp(True), sha1(filename))
        #: 定义保存图片时仅使用某些钩子,如: up2local
        #: TODO 目前版本仅允许设置了一个,后续考虑聚合
        includes = parse_valid_comma(g.cfg.upload_includes or 'up2local')
        if len(includes) > 1:
            includes = [choice(includes)]
        #: TODO 定义保存图片时排除某些钩子,如: up2local, up2other
        # excludes = parse_valid_comma(g.cfg.upload_excludes or '')
        #: 钩子返回结果(目前版本最终结果中应该最多只有1条数据)
        data = []

        #: 保存图片的钩子回调
        def callback(result):
            logger.info(result)
            if result["sender"] == "up2local":
                result["src"] = url_for("static",
                                        filename=join(UPLOAD_FOLDER,
                                                      upload_path, filename),
                                        _external=True)
            data.append(dfr(result))

        #: 调用钩子中upimg_save方法
        current_app.extensions["hookmanager"].call(
            _funcname="upimg_save",
            _callback=callback,
            _include=includes,
            filename=filename,
            stream=stream,
            upload_path=upload_path,
            local_basedir=join(current_app.root_path,
                               current_app.static_folder, UPLOAD_FOLDER))
        #: 判定后端存储全部失败时,上传失败
        if not data:
            res.update(code=1, msg="No valid backend storage service")
            return res
        if len(data) == len([i for i in data if i.get("code") != 0]):
            res.update(
                code=1,
                msg="All backend storage services failed to save pictures")
            return res
        #: 存储数据
        defaultSrc = data[0]["src"]
        pipe = g.rc.pipeline()
        pipe.sadd(rsp("index", "global"), sha)
        if g.signin and g.userinfo.username:
            pipe.sadd(rsp("index", "user", g.userinfo.username), sha)
        pipe.hmset(
            rsp("image", sha),
            dict(
                sha=sha,
                filename=filename,
                upload_path=upload_path,
                user=g.userinfo.username if g.signin else 'anonymous',
                ctime=get_current_timestamp(),
                status='enabled',  # disabled, deleted
                src=defaultSrc,
                sender=data[0]["sender"],
                senders=json.dumps(data)))
        try:
            pipe.execute()
        except RedisError as e:
            logger.error(e, exc_info=True)
            res.update(code=3, msg="Program data storage service error")
        else:
            res.update(
                code=0,
                filename=filename,
                sender=data[0]["sender"],
                api=url_for("api.shamgr", sha=sha, _external=True),
            )
            #: format指定图片地址的显示字段,默认src,可以用点号指定
            #: 比如data.src,那么返回格式{code, filename..., data:{src}, ...}
            fmt = request.form.get("format", request.args.get("format"))
            res.update(format_upload_src(fmt, defaultSrc))
    else:
        res.update(msg="No file or image format allowed")
    return res
示例#25
0
def shamgr(sha):
    """图片查询、删除接口"""
    res = dict(code=1)
    gk = rsp("index", "global")
    ik = rsp("image", sha)
    dk = rsp("index", "deleted")
    if request.method == "GET":
        if g.rc.sismember(gk, sha):
            data = g.rc.hgetall(ik)
            u, n = data["src"], data["filename"]
            data.update(
                senders=json.loads(data["senders"]) if g.is_admin else None,
                ctime=int(data["ctime"]),
                tpl=dict(HTML="<img src='%s' alt='%s'>" % (u, n),
                         rST=".. image:: %s" % u,
                         Markdown="![%s](%s)" % (n, u)))
            res.update(code=0, data=data)
        else:
            return abort(404)
    elif request.method == "DELETE":
        if not g.signin:
            return abort(403)
        if g.rc.sismember(gk, sha):
            #: 图片所属用户
            #: - 如果不是匿名,那么判断请求用户是否属所属用户或管理员
            #: - 如果是匿名上传,那么只有管理员有权删除
            info = g.rc.hgetall(ik)
            husr = info.get("user")
            if g.is_admin or (g.userinfo.username == husr):
                pipe = g.rc.pipeline()
                pipe.srem(gk, sha)
                pipe.sadd(dk, sha)
                pipe.hset(ik, "status", "deleted")
                pipe.srem(rsp("index", "user", husr), sha)
                try:
                    pipe.execute()
                except RedisError:
                    res.update(msg="Program data storage service error")
                else:
                    res.update(code=0)
                    try:
                        #: 删除图片尝试执行senders的upimg_delete方法
                        #: 后端钩子未禁用时才会执行删除
                        #: TODO 无论禁用与否都删除?
                        senders = json.loads(info.get("senders"))
                        for i in senders:
                            current_app.extensions["hookmanager"].proxy(
                                i["sender"]).upimg_delete(
                                    sha=sha,
                                    upload_path=info["upload_path"],
                                    filename=info["filename"],
                                    basedir=(join(current_app.root_path,
                                                  current_app.static_folder,
                                                  UPLOAD_FOLDER)
                                             if i["sender"] == "up2local" else
                                             i.get("basedir")),
                                    save_result=i)
                    except (ValueError, AttributeError, Exception) as e:
                        logger.warning(e, exc_info=True)
            else:
                return abort(403)
        else:
            return abort(404)
    else:
        return abort(405)
    return res
示例#26
0
 def test_utils(self):
     self.assertEqual("900150983cd24fb0d6963f7d28e17f72", md5("abc"))
     self.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d",
                      sha1("abc"))
     self.assertEqual("picbed:a:b", rsp("a", "b"))
     self.assertEqual(parse_valid_comma("a,b, c,"), ["a", "b", "c"])
     self.assertEqual(parse_valid_verticaline("a|b| c"), ["a", "b", "c"])
     self.assertTrue(is_true(1))
     self.assertTrue(is_true("on"))
     self.assertTrue(is_true("true"))
     self.assertFalse(is_true(0))
     self.assertIsInstance(get_current_timestamp(), int)
     self.assertTrue(allowed_file("test.PNG"))
     self.assertTrue(allowed_file(".jpeg"))
     self.assertFalse(allowed_file("my.psd"))
     self.assertFalse(allowed_file("ha.gif", ["jpg"]))
     self.assertFalse(allowed_file("ha.jpeg", ["jpg"]))
     self.assertFalse(allowed_file("ha.png", ["jpg"]))
     self.assertTrue(allowed_file("ha.jpg", ["jpg"]))
     v = "6afa9046a9579cad143a384c1b564b9a250d27d6f6a63f9f20bf3a7594c9e2c6"
     self.assertEqual(v, hmac_sha256("key", "text"))
     self.assertEqual(v, hmac_sha256(b"key", b"text"))
     self.assertEqual(v, hmac_sha256(u"key", u"text"))
     self.assertEqual(
         "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
         sha256("abc"),
     )
     #: test format_upload_src
     baseimg = "img-url"
     basefmt = {"src": baseimg}
     self.assertEqual(format_upload_src(123, baseimg), basefmt)
     self.assertEqual(format_upload_src(None, baseimg), basefmt)
     self.assertEqual(format_upload_src([0], baseimg), basefmt)
     self.assertEqual(format_upload_src("", baseimg), basefmt)
     self.assertEqual(format_upload_src(".", baseimg), basefmt)
     self.assertEqual(format_upload_src(".1", baseimg), basefmt)
     self.assertEqual(format_upload_src("1.", baseimg), basefmt)
     self.assertEqual(format_upload_src(1.1, baseimg), basefmt)
     self.assertEqual(format_upload_src("1.1", baseimg),
                      {"1": {
                          "1": baseimg
                      }})
     self.assertEqual(format_upload_src("u", baseimg), basefmt)
     self.assertEqual(format_upload_src("im", baseimg), {"im": baseimg})
     self.assertEqual(format_upload_src("url", baseimg), {"url": baseimg})
     self.assertEqual(format_upload_src("i.am.src", baseimg), basefmt)
     self.assertEqual(format_upload_src("src.url", baseimg),
                      {"src": {
                          "url": baseimg
                      }})
     #: test format_apires
     self.assertEqual(format_apires({"code": 0}, "success", "bool"),
                      {"success": True})
     self.assertEqual(format_apires({"code": 0}, oc="200"), {"code": 200})
     self.assertEqual(format_apires({"code": -1}, "status", "bool"),
                      {"status": False})
     self.assertEqual(
         format_apires(dict(code=-1, msg="xxx"), "errno", "200"),
         {
             "errno": -1,
             "msg": "xxx"
         },
     )
     self.assertEqual(
         format_apires(dict(code=-1, msg="xxx"), "errno", "200", "errmsg"),
         {
             "errno": -1,
             "errmsg": "xxx"
         },
     )
     self.assertEqual(
         format_apires(dict(code=0, msg="xxx"), "", "200", "errmsg"),
         {
             "code": 200,
             "errmsg": "xxx"
         },
     )
     self.assertEqual(len(generate_random()), 6)
     self.assertIn("Mozilla/5.0", gen_ua())
     # bleach
     self.assertEqual(bleach_html("<i>abc</i>"), "<i>abc</i>")
     self.assertEqual(
         bleach_html("<script>var abc</script>"),
         "&lt;script&gt;var abc&lt;/script&gt;",
     )
     # re
     self.assertEqual(parse_author_mail("staugur"), ("staugur", None))
     self.assertEqual(parse_author_mail("staugur <mail>"),
                      ("staugur", "mail"))
示例#27
0
文件: api.py 项目: howardyan93/picbed
def upload():
    """上传逻辑:
    0. 判断是否登录,如果未登录则判断是否允许匿名上传
    1. 获取上传的文件,判断允许格式
        - 拦截下判断文件,如果为空,尝试获取body中提交的picbed
        - 如果picbed是合法base64,那么会返回Base64FileStorage类;
          如果picbed是合法url[图片],那么服务端会自动下载,返回ImgUrlFileStorage类;
          否则为空。
          PS: 允许DATA URI形式, eg: data:image/png;base64,the base64 of image
    2. 生成文件名、唯一sha值、上传目录等,选择图片存储的后端钩子(单一)
        - 存储图片的钩子目前版本仅一个,默认是up2local(如果禁用则保存失败)
        - 如果提交album参数会自动创建相册,否则归档到默认相册
    3. 解析钩子数据,钩子回调数据格式:{code=?, sender=hook_name, src=?}
        - 后端保存失败或无后端的情况都要立刻返回响应
    4. 此时保存图片成功,持久化存储到全局索引、用户索引
    5. 返回响应:{code:0, data={src=?, sender=success_saved_hook_name}}
        - 允许使用一些参数调整响应数据、格式
    """
    res = dict(code=1, msg=None)
    #: 文件域或base64上传字段
    FIELD_NAME = g.cfg.upload_field or "picbed"
    #: 匿名上传开关检测
    if not is_true(g.cfg.anonymous) and not g.signin:
        res.update(code=403, msg="Anonymous user is not sign in")
        return res
    #: 相册名称,可以是任意字符串
    album = (request.form.get("album")
             or getattr(g, "up_album", "")) if g.signin else 'anonymous'
    #: 实时获取后台配置中允许上传的后缀,如: jpg|jpeg|png
    allowed_suffix = partial(allowed_file,
                             suffix=parse_valid_verticaline(g.cfg.upload_exts))
    #: 尝试读取上传数据
    fp = request.files.get(FIELD_NAME)
    #: 当fp无效时尝试读取base64或url
    if not fp:
        picstrurl = request.form.get(FIELD_NAME)
        filename = request.form.get("filename")
        if picstrurl:
            if picstrurl.startswith("http://") or \
                    picstrurl.startswith("https://"):
                fp = ImgUrlFileStorage(picstrurl, filename).getObj
            else:
                try:
                    #: base64在部分场景发起http请求时,+可能会换成空格导致异常
                    fp = Base64FileStorage(picstrurl, filename)
                except ValueError as e:
                    logger.debug(e)
    if fp and allowed_suffix(fp.filename):
        try:
            g.rc.ping()
        except RedisError as e:
            logger.error(e, exc_info=True)
            res.update(code=2, msg="Program data storage service error")
            return res
        stream = fp.stream.read()
        suffix = splitext(fp.filename)[-1]
        filename = secure_filename(fp.filename)
        if "." not in filename:
            filename = "%s%s" % (generate_random(8), suffix)
        #: 根据文件名规则重定义图片名
        upload_file_rule = (
            g.userinfo.ucfg_upload_file_rule
            or g.cfg.upload_file_rule) if is_true(
                g.cfg.upload_rule_overridden) else g.cfg.upload_file_rule
        if upload_file_rule in ("time1", "time2", "time3"):
            filename = "%s%s" % (gen_rnd_filename(upload_file_rule), suffix)
        #: 上传文件位置前缀规则
        upload_path_rule = (
            g.userinfo.ucfg_upload_path_rule
            or g.cfg.upload_path_rule) if is_true(
                g.cfg.upload_rule_overridden) else g.cfg.upload_path_rule
        if upload_path_rule == 'date1':
            upload_path = get_today("%Y/%m/%d")
        elif upload_path_rule == 'date2':
            upload_path = get_today("%Y%m%d")
        else:
            upload_path = ''
        upload_path = join(g.userinfo.username or 'anonymous', upload_path)
        #: 定义文件名唯一索引
        sha = "sha1.%s.%s" % (get_current_timestamp(True), sha1(filename))
        #: 定义保存图片时仅使用某些钩子,如: up2local
        #: TODO 目前版本仅允许设置了一个,后续聚合
        includes = parse_valid_comma(g.cfg.upload_includes or 'up2local')
        if len(includes) > 1:
            includes = [choice(includes)]
        #: TODO 定义保存图片时排除某些钩子,如: up2local, up2other
        # excludes = parse_valid_comma(g.cfg.upload_excludes or '')
        #: 钩子返回结果(目前版本最终结果中应该最多只有1条数据)
        data = []

        #: 保存图片的钩子回调

        def callback(result):
            logger.info(result)
            if result["sender"] == "up2local":
                result["src"] = url_for("static",
                                        filename=join(UPLOAD_FOLDER,
                                                      upload_path, filename),
                                        _external=True)
            data.append(dfr(result))

        #: 调用钩子中upimg_save方法
        current_app.extensions["hookmanager"].call(
            _funcname="upimg_save",
            _callback=callback,
            _include=includes,
            filename=filename,
            stream=stream,
            upload_path=upload_path,
            local_basedir=join(current_app.root_path,
                               current_app.static_folder, UPLOAD_FOLDER))
        #: 判定后端存储全部失败时,上传失败
        if not data:
            res.update(code=1, msg="No valid backend storage service")
            return res
        if len(data) == len([i for i in data if i.get("code") != 0]):
            res.update(
                code=1,
                msg="All backend storage services failed to save pictures",
                errors={
                    i["sender"]: i["msg"]
                    for i in data if i.get("code") != 0
                },
            )
            return res
        #: 存储数据
        defaultSrc = data[0]["src"]
        pipe = g.rc.pipeline()
        pipe.sadd(rsp("index", "global"), sha)
        if g.signin and g.userinfo.username:
            pipe.sadd(rsp("index", "user", g.userinfo.username), sha)
        pipe.hmset(
            rsp("image", sha),
            dict(
                sha=sha,
                album=album,
                filename=filename,
                upload_path=upload_path,
                user=g.userinfo.username if g.signin else 'anonymous',
                ctime=get_current_timestamp(),
                status='enabled',  # disabled, deleted
                src=defaultSrc,
                sender=data[0]["sender"],
                senders=json.dumps(data),
                agent=request.form.get("origin",
                                       request.headers.get('User-Agent', '')),
                method=getUploadMethod(fp.__class__.__name__),
            ))
        try:
            pipe.execute()
        except RedisError as e:
            logger.error(e, exc_info=True)
            res.update(code=3, msg="Program data storage service error")
        else:
            res.update(
                code=0,
                filename=filename,
                sender=data[0]["sender"],
                api=url_for("api.shamgr", sha=sha, _external=True),
            )
            #: format指定图片地址的显示字段,默认src,可以用点号指定
            #: 比如data.src,那么返回格式{code, filename..., data:{src}, ...}
            #: 比如imgUrl,那么返回格式{code, filename..., imgUrl(=src), ...}
            fmt = request.form.get("format", request.args.get("format"))
            res.update(format_upload_src(fmt, defaultSrc))
    else:
        res.update(msg="No file or image format error")
    return res
示例#28
0
文件: api.py 项目: howardyan93/picbed
def link():
    res = dict(code=1, msg=None)
    ltk = rsp("linktokens")
    username = g.userinfo.username

    def check_body():
        """校验post、put参数,返回值有效说明校验不通过"""
        allow_origin = request.form.get("allow_origin")
        allow_ip = request.form.get("allow_ip")
        allow_ep = request.form.get("allow_ep")
        allow_method = request.form.get("allow_method")
        er = request.form.get("exterior_relation")
        ir = request.form.get("interior_relation")
        if allow_origin:
            origins = parse_valid_comma(allow_origin)
            if not origins or not isinstance(origins, (tuple, list)):
                return "Invalid url address"
            for url in origins:
                if url and not check_origin(url):
                    return "Invalid url address"
        if allow_ip:
            ips = parse_valid_comma(allow_ip)
            if not ips or not isinstance(ips, (tuple, list)):
                return "Invalid IP address"
            for ip in ips:
                if ip and not check_ip(ip):
                    return "Invalid IP address"
        if allow_ep:
            eps = parse_valid_comma(allow_ep)
            if not eps or not isinstance(eps, (tuple, list)):
                return "Not found the endpoint"
            for ep in eps:
                if ep and ep not in current_app.view_functions.keys():
                    return "Not found the endpoint"
        if allow_method:
            methods = parse_valid_comma(allow_method)
            if not methods or not isinstance(methods, (tuple, list)):
                return "Invalid HTTP method"
            for md in methods:
                if md and md.upper() not in ["GET", "POST", "PUT", "DELETE"]:
                    return "Invalid HTTP method"
        if er:
            if not er_pat.match(er.strip()):
                return "Invalid exterior_relation"
        if ir:
            if not ir_pat.match(ir.strip()):
                return "Invalid interior_relation"
            else:
                try:
                    check_ir(ir)
                except (ValueError, TypeError):
                    return "Invalid interior_relation"

    if request.method == "GET":
        is_mgr = is_true(request.args.get("is_mgr"))
        linktokens = g.rc.hgetall(ltk)
        pipe = g.rc.pipeline()
        for ltid, usr in iteritems(linktokens):
            if is_mgr and g.is_admin:
                pipe.hgetall(rsp("linktoken", ltid))
            else:
                if username == usr:
                    pipe.hgetall(rsp("linktoken", ltid))
        try:
            result = pipe.execute()
        except RedisError:
            res.update(msg="Program data storage service error")
        else:
            res.update(code=0, data=result, count=len(result))

    elif request.method == "POST":
        comment = request.form.get("comment") or ""
        #: 定义此引用上传图片时默认设置的相册名
        album = request.form.get("album") or ""
        #: 定义以下几个权限之间的允许访问条件,opt and/or/not opt
        er = request.form.get("exterior_relation", "").strip()
        #: 定义权限内部允许访问条件 in/not in:opt,
        ir = request.form.get("interior_relation", "").strip()
        #: 定义权限项及默认值,检测参数时不包含默认值
        allow_origin = request.form.get("allow_origin") or ""
        allow_ip = request.form.get("allow_ip") or ""
        allow_ep = request.form.get("allow_ep") or "api.upload"
        allow_method = request.form.get("allow_method") or "post"
        #: 判断用户是否有token
        ak = rsp("account", username)
        if not g.rc.hget(ak, "token"):
            res.update(msg="No tokens yet")
            return res
        cv = check_body()
        if cv:
            res.update(msg=cv)
            return res
        if allow_origin:
            allow_origin = ",".join([
                get_origin(url) for url in parse_valid_comma(allow_origin)
                if url
            ])
        #: 生成一个引用
        LinkId = gen_uuid()
        LinkSecret = generate_password_hash(LinkId)
        lid = "%s:%s:%s" % (get_current_timestamp(), LinkId,
                            hmac_sha256(LinkId, LinkSecret))
        LinkToken = b64encode(lid.encode("utf-8")).decode("utf-8")
        pipe = g.rc.pipeline()
        pipe.hset(ltk, LinkId, username)
        pipe.hmset(
            rsp("linktoken", LinkId),
            dict(
                LinkId=LinkId,
                LinkSecret=LinkSecret,
                LinkToken=LinkToken,
                ctime=get_current_timestamp(),
                user=username,
                comment=comment,
                album=album,
                status=1,  # 状态,1是启用,0是禁用
                allow_origin=allow_origin,
                allow_ip=allow_ip,
                allow_ep=allow_ep,
                allow_method=allow_method,
                exterior_relation=er,
                interior_relation=ir,
            ))
        try:
            pipe.execute()
        except RedisError:
            res.update(msg="Program data storage service error")
        else:
            res.update(code=0, LinkToken=LinkToken)

    elif request.method == "PUT":
        LinkId = request.form.get("LinkId")
        Action = request.args.get("Action")
        key = rsp("linktoken", LinkId)
        if Action == "disable":
            try:
                g.rc.hset(key, "status", 0)
            except RedisError:
                res.update(msg="Program data storage service error")
            else:
                res.update(code=0)
            return res
        elif Action == "enable":
            try:
                g.rc.hset(key, "status", 1)
            except RedisError:
                res.update(msg="Program data storage service error")
            else:
                res.update(code=0)
            return res
        if LinkId and g.rc.exists(key):
            comment = request.form.get("comment") or ""
            album = request.form.get("album") or ""
            er = request.form.get("exterior_relation", "").strip()
            ir = request.form.get("interior_relation", "").strip()
            allow_origin = request.form.get("allow_origin") or ""
            allow_ip = request.form.get("allow_ip") or ""
            allow_ep = request.form.get("allow_ep") or "api.upload"
            allow_method = request.form.get("allow_method") or "post"
            cv = check_body()
            if cv:
                res.update(msg=cv)
                return res
            if allow_origin:
                allow_origin = ",".join([
                    get_origin(url) for url in parse_valid_comma(allow_origin)
                    if url
                ])
            pipe = g.rc.pipeline()
            pipe.hset(ltk, LinkId, username)
            pipe.hmset(
                key,
                dict(
                    mtime=get_current_timestamp(),
                    comment=comment,
                    album=album,
                    allow_origin=allow_origin,
                    allow_ip=allow_ip,
                    allow_ep=allow_ep,
                    allow_method=allow_method,
                    exterior_relation=er,
                    interior_relation=ir,
                ))
            try:
                pipe.execute()
            except RedisError:
                res.update(msg="Program data storage service error")
            else:
                res.update(code=0)
        else:
            res.update(msg="Not found the LinkId")

    elif request.method == "DELETE":
        LinkId = request.form.get("LinkId")
        if LinkId:
            pipe = g.rc.pipeline()
            pipe.hdel(ltk, LinkId)
            pipe.delete(rsp("linktoken", LinkId))
            try:
                pipe.execute()
            except RedisError:
                res.update(msg="Program data storage service error")
            else:
                res.update(code=0)
        else:
            res.update(msg="Parameter error")

    return res
示例#29
0
文件: token.py 项目: 993544899/picbed
def before_request():
    if g.signin:
        return
    token = request.form.get("token") or parse_authorization()
    #: 尝试使用LinkToken映射出token
    LinkToken = request.form.get(
        "LinkToken", request.args.get("LinkToken")
    ) or parse_authorization("LinkToken")
    if not token and LinkToken:
        try:
            if PY2 and isinstance(LinkToken, text_type):
                LinkToken = LinkToken.encode("utf-8")
            LinkToken = b64decode(LinkToken)
            if not PY2 and not isinstance(LinkToken, text_type):
                LinkToken = LinkToken.decode("utf-8")
            ctime, LinkId, LinkSig = LinkToken.split(":")
            ctime = int(ctime)
            if not LinkId or not LinkSig:
                raise ValueError
        except (TypeError, ValueError, AttributeError) as e:
            logger.debug(e, exc_info=True)
        else:
            pipe = g.rc.pipeline()
            pipe.hexists(rsp("linktokens"), LinkId)
            pipe.hgetall(rsp("linktoken", LinkId))
            result = pipe.execute()
            if result[0] is True and result[1] and isinstance(result[1], dict):
                #: LinkId有效,但等待校验签名与权限,可以统计请求数据
                Ld = result[1]
                usr = Ld.get("user")
                secret = Ld.get("LinkSecret")
                status = int(Ld.get("status", 1))
                userinfo = g.rc.hmget(
                    rsp("account", usr), "token", "ucfg_report_linktoken"
                )
                if status == 1 and hmac_sha256(LinkId, secret) == LinkSig:
                    authentication = "ok"
                    #: 权限校验规则
                    if verify_rule(Ld):
                        authorization = "ok"
                        logger.info("LinkToken ok and permission pass")
                        g.up_album = Ld.get("album")
                        token = userinfo[0]
                    else:
                        authorization = "fail"
                        logger.info("LinkToken ok and permission deny")
                else:
                    authentication = "fail"
                    authorization = "fail"
                #: 统计入库
                if is_true(userinfo[1]):
                    g.rc.lpush(
                        rsp("report", "linktokens", usr),
                        json.dumps(dict(
                            LinkId=LinkId,
                            user=usr,
                            ctime=get_current_timestamp(),
                            ip=get_ip(),
                            agent=get_ua(),
                            uap=parse_ua(get_ua()),
                            referer=request.headers.get('Referer', ''),
                            origin=get_origin(),
                            ep=request.endpoint,
                            authentication=authentication,
                            authorization=authorization,
                        ))
                    )
    if token:
        try:
            oldToken = token
            if PY2 and isinstance(token, text_type):
                token = token.encode("utf-8")
            token = b64decode(token)
            if not PY2 and not isinstance(token, text_type):
                token = token.decode("utf-8")
            rdm, usr, ctime, sig = token.split(".")
            ctime = int(ctime)
            assert len(rdm) == 6
        except (TypeError, ValueError, AttributeError, Exception) as e:
            logger.debug(e, exc_info=True)
        else:
            tk = rsp("tokens")
            token2usr = g.rc.hget(tk, oldToken)
            if token2usr and token2usr == usr:
                ak = rsp("account", usr)
                userinfo = g.rc.hgetall(ak)
                userstatus = int(userinfo.get("status", 1))
                if userinfo and userstatus != 0:
                    pwd = userinfo.pop("password", None)
                    tkey = userinfo.pop("token_key", None)
                    if hmac_sha256(pwd, usr) == sig or \
                            (tkey and hmac_sha256(tkey, usr) == sig):
                        g.signin = True
                        g.userinfo = userinfo
                        #: token验证通过,判断是否禁止普通用户登录
                        if is_true(g.cfg.disable_login) and \
                                not is_true(userinfo.get("is_admin")):
                            g.signin = False
                            g.userinfo = {}
示例#30
0
文件: api.py 项目: howardyan93/picbed
def waterfall():
    res = dict(code=1, msg=None)
    #: 依次根据ctime、filename排序
    sort = request.args.get("sort") or "desc"
    #: 符合人类习惯的page,第一页是1(程序计算需要减1)
    page = request.args.get("page") or 1
    #: 返回数据条数
    limit = request.args.get("limit") or 10
    #: 管理员账号读取所有图片数据
    is_mgr = is_true(request.args.get("is_mgr"))
    #: 相册,当album不为空时,近返回此相册数据,允许逗号分隔多个
    album = request.args.get("album", request.form.get("album"))
    try:
        page = int(page) - 1
        limit = int(limit)
        if page < 0:
            raise ValueError
    except (ValueError, TypeError):
        res.update(code=2, msg="Parameter error")
    else:
        if g.userinfo.username:
            if is_mgr and g.is_admin:
                uk = rsp("index", "global")
            else:
                uk = rsp("index", "user", g.userinfo.username)
            pipe = g.rc.pipeline()
            for sha in g.rc.smembers(uk):
                pipe.hgetall(rsp("image", sha))
            try:
                result = pipe.execute()
            except RedisError:
                res.update(msg="Program data storage service error")
            else:
                #: 最终返回的经过过滤、排序等处理后的数据
                data = []
                #: 该用户或管理员级别所能查看的所有相册
                albums = []
                ask_albums = parse_valid_comma(album)
                if result and isinstance(result, (tuple, list)):
                    for i in result:
                        albums.append(i.get("album"))
                        i.update(
                            senders=json.loads(i["senders"]),
                            ctime=int(i["ctime"]),
                        )
                        if ask_albums:
                            if i.get("album") in ask_albums:
                                data.append(i)
                        else:
                            data.append(i)
                data = sorted(data,
                              key=lambda k:
                              (k.get('ctime', 0), k.get('filename', '')),
                              reverse=False if sort == "asc" else True)
                count = len(data)
                data = ListEqualSplit(data, limit)
                pageCount = len(data)
                if page < pageCount:
                    res.update(
                        code=0,
                        count=count,
                        data=data[page],
                        pageCount=pageCount,
                        albums=list(set([i for i in albums if i])),
                    )
                else:
                    res.update(code=3, msg="No data")
        else:
            res.update(msg="No valid username found")
    return res