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"))
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
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
def userscript(): if g.signin and is_true(g.userinfo.ucfg_userscript): resp = make_response(render_template("public/userscript.js")) resp.headers['Content-type'] = 'application/javascript; charset=utf-8' return resp else: return abort(404)
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")
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
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
def before_request(): g.signin, g.userinfo = default_login_auth() #: No Required field g.site = get_site_config() g.cfg = Attribute(g.site) #: Required field: username, is_admin g.userinfo = Attribute(g.userinfo) g.is_admin = is_true(g.userinfo.is_admin)
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"]
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
def sendmail(subject, message, to_addr): """Web环境下发送邮件""" #: from_addr建议设置发件人邮箱,否则基本会被拦截或进入垃圾邮箱 from_addr = "sapic@{}".format(request.host) from_name = g.cfg.email_from_name or g.site_name #: 关闭通过本地服务器发送邮件 no_local = g.cfg.email_nolocal if is_true(no_local): res = dict(code=1) else: #: 通过本地邮件服务发送 mb = Mailbox(from_addr, "", "localhost") res = mb.send(subject, message, to_addr, from_name) logger.debug("sendmail with localhost: {}".format(res)) if res["code"] != 0: #: 根据钩子配置依次发送邮件,除非某次发送成功 API_USER = g.cfg.email_sendcloud_apiuser API_KEY = g.cfg.email_sendcloud_apikey TOKEN = g.cfg.email_open_token #: SaintIC Open(open.saintic.com) Email Service(private now) if TOKEN: res = _saintic_open(TOKEN, subject, message, to_addr, from_name) logger.debug("sendmail with saintic open: {}".format(res)) res.update(method="open") #: Sohu(sendcloud.sohu.com) public service if res["code"] != 0 and API_USER and API_KEY: #: See docs: https://www.sendcloud.net/doc/email_v2/ _sr = _sendcloud( API_USER, API_KEY, subject, message, to_addr, g.cfg.email_sendcloud_from or from_addr, from_name, ) if is_true(_sr.get("result")): res = dict(code=0, data=_sr.get("info")) else: res = dict(code=_sr.get("statusCode"), msg=_sr.get("message")) logger.debug("sendmail with sendcloud: {}".format(res)) res.update(method="sendcloud") else: res.update(method="local") return res
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
def before_request(): g.rc = rc g.signin, g.userinfo = default_login_auth() #: Trigger hook, you can modify flask.g hm.call("before_request") #: No Required field g.site = get_site_config() g.cfg = Attribute(g.site) #: Required field: username, is_admin g.userinfo = Attribute(g.userinfo) g.is_admin = is_true(g.userinfo.is_admin)
def before_request(): g.rc = rc g.site = get_site_config() g.cfg = Attribute(g.site) g.signin, g.userinfo = default_login_auth() #: Trigger hook, you can modify flask.g hm.call("before_request") #: (Logged-on state)required field: username, is_admin g.userinfo = Attribute(change_userinfo(g.userinfo)) g.is_admin = is_true(g.userinfo.is_admin) g.next = get_redirect_url()
def fmt(d): d = dict(zip(fds, d)) if d.get("status") is None: d["status"] = 1 if d.get("email_verified") is None: d["email_verified"] = 0 d.update( is_admin=is_true(d["is_admin"]), ctime=int(d["ctime"]), status=int(d.get("status", 1)), email_verified=int(d.get("email_verified", 1)), ) if d.get("mtime"): d["mtime"] = int(d["mtime"]) return d
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
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
def pip_install(): """Use the pip command to install third-party modules. .. versionadded:: 1.6.0 """ res = dict(code=1, msg=None) pkg = request.form.get("package") index = request.form.get("index") upgrade = is_true(request.form.get("upgrade")) if pkg and not pkg.startswith(".") and not pkg.startswith("-"): thread.start_new_thread(_pip_install, (pkg, index, upgrade)) res.update(code=0, msg="accepted") return jsonify(res), 201 else: res.update(msg="Parameter error") return res
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
def register(): if is_true(g.cfg.register): return render_template("public/register.html") else: return abort(404)
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
def user(): """Manage users. .. versionadded:: 1.6.0 """ res = dict(code=1, msg=None) ak = rsp("accounts") if request.method == "GET": #: 查询用户 sort = request.args.get("sort") or "desc" page = request.args.get("page") or 1 limit = request.args.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") else: fds = ("username", "nickname", "avatar", "ctime", "mtime", "is_admin", "status", "message", "email", "email_verified") pipe = g.rc.pipeline() for u in g.rc.smembers(ak): pipe.hmget(rsp("account", u), *fds) try: data = pipe.execute() except RedisError: res.update(msg="Program data storage service error") else: def fmt(d): d = dict(zip(fds, d)) if d.get("status") is None: d["status"] = 1 if d.get("email_verified") is None: d["email_verified"] = 0 d.update( is_admin=is_true(d["is_admin"]), ctime=int(d["ctime"]), status=int(d.get("status", 1)), email_verified=int(d.get("email_verified", 1)), ) if d.get("mtime"): d["mtime"] = int(d["mtime"]) return d data = [fmt(d) for d in data] data = sorted(data, key=lambda k: k.get('ctime', 0), 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") elif request.method == "DELETE": #: 删除用户 username = request.form.get("username") if username: if g.rc.sismember(ak, username): pipe = g.rc.pipeline() pipe.srem(ak, username) pipe.delete(rsp("account", username)) try: pipe.execute() except RedisError: res.update(msg="Program data storage service error") else: res.update(code=0) else: res.update(code=3, msg="No valid username found") else: res.update(code=2, msg="No valid username found") elif request.method == "PUT": Action = request.args.get("Action") username = request.form.get("username") if Action in ("review", "disable", "enable"): if username: if g.rc.sismember(ak, username): if Action == "review": s = 1 elif Action == "disable": s = 0 else: s = 1 try: g.rc.hset(rsp("account", username), "status", s) except RedisError: res.update(msg="Program data storage service error") else: res.update(code=0) else: res.update(code=3, msg="No valid username found") else: res.update(code=2, msg="No valid username found") elif Action == "admin": adm = 1 if is_true(request.form.get("is_admin")) else 0 if username: if username != g.userinfo.username and \ g.rc.sismember(ak, username): try: g.rc.hset(rsp("account", username), "is_admin", adm) except RedisError: res.update(msg="Program data storage service error") else: res.update(code=0) else: res.update(code=3, msg="No valid username found") else: res.update(code=2, msg="No valid username found") return res
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
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())
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 = {}
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
def upimg_save(**kwargs): res = dict(code=1) try: filename = kwargs["filename"] stream = kwargs["stream"] upload_path = kwargs.get("upload_path") or "" if not filename or not stream: return ValueError except (KeyError, ValueError): res.update(msg="Parameter error") else: token = g.cfg.github_token repo = g.cfg.github_repo branch = g.cfg.github_branch dn = g.cfg.github_dn github_basedir = g.cfg.github_basedir or '/' if not token or not repo or "/" not in repo: res.update(msg="The github parameter error") return res if isinstance(upload_path, string_types): if upload_path.startswith("/"): upload_path = upload_path.lstrip('/') if github_basedir.startswith("/"): github_basedir = github_basedir.lstrip('/') saveto = join(github_basedir, upload_path) filepath = join(saveto, filename) #: 通过API上传图片 data = dict( message="Create %s by picbed" % filepath, content=b64encode(stream).decode("utf-8"), ) if branch: data["branch"] = branch headers = { "User-Agent": "picbed-up2github/%s" % __version__, "Authorization": "token %s" % token } try: r = try_request( "https://api.github.com/repos/%s/contents/%s" % ( repo, filepath ), data=json.dumps(data), headers=headers, timeout=30, method="put" ) except requests.exceptions.RequestException as e: res.update(msg=e) else: result = r.json() if r.status_code == 201: content = result["content"] src = content["download_url"] if dn: src = slash_join(dn, filepath) elif is_true(g.cfg.github_jsdelivr): src = slash_join( "https://cdn.jsdelivr.net/gh/", repo, filepath ) res.update( code=0, src=src, basedir=github_basedir, branch=branch, size=content["size"], content_sha=content["sha"], download_url=content["download_url"], repo=repo, ) else: res.update( code=r.status_code, msg=result.get("message", "").replace( '"', '\'' ).replace('\n\n', ' ').replace('\n', '').replace( '\\', '' ), ) else: res.update(msg="The upload_path type error") return res
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
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>"), "<script>var abc</script>", ) # re self.assertEqual(parse_author_mail("staugur"), ("staugur", None)) self.assertEqual(parse_author_mail("staugur <mail>"), ("staugur", "mail"))
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", "email"] data = { k: v for k, v in iteritems(request.form.to_dict()) if k in allowed_fields } data.update(mtime=get_current_timestamp()) if is_true(g.userinfo.email_verified) and \ data.get("email") != g.userinfo.email: data["email_verified"] = 0 try: 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", _kwargs=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) elif Action == "leaveMessage": message = request.form.get("message") if message: try: g.rc.hset(ak, "message", message) except RedisError: res.update(msg="Program data storage service error") else: res.update(code=0) elif Action == "verifyEmail": html = make_email_tpl("activate_email.html", activate_url=url_for( "front.activate", token=generate_activate_token( dict( Action=Action, username=g.userinfo.username, email=g.userinfo.email, )), _external=True, )) res = sendmail( subject="{}邮箱验证".format(g.cfg.title_name or "picbed图床"), message=html, to=g.userinfo.email, ) return res