You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

227 lines
8.4 KiB
Python

# -*- coding: utf-8 -*-
import base64
import json
import logging
import uuid
from io import BytesIO
from typing import Optional, Awaitable
from sqlalchemy import text
from website import db_mysql
from website import errors
from website import consts
from website import settings
from website.handler import APIHandler, authenticated
from website.util import aes
from website.util import shortuuid
from website.util.captcha import create_validate_code
class CaptchaHandler(APIHandler):
def get(self):
self.set_header("Content-Type", "image/png")
image, image_str = create_validate_code()
c = uuid.uuid4().hex
token = self.create_signed_value("logc", c)
self.r_app.set("logincaptcha:%s" % c, image_str, ex=120)
buffered = BytesIO()
# 保存验证码图片
image.save(buffered, 'png')
img_b64 = base64.b64encode(buffered.getvalue())
# for line in buffered.getvalue():
# self.write(line)
# output.close()
self.finish({"token": self.tostr(token), "captcha": self.tostr(img_b64)})
class LogoutHandler(APIHandler):
def get(self):
if self.current_user:
# self.db_app.insert(
# "insert into system_log(user, ip, first_module, second_module, op_type, op_content, description) "
# "values(%s, %s, %s, %s, %s, %s, %s)",
# self.current_user.name, self.request.remote_ip, "平台管理中心", "账号管理", "登出", "系统登出", "系统登出"
# )
with self.app_mysql.connect() as conn:
conn.execute(text(
"insert into sys_log(user, ip, primary_menu, sub_menu, op_type, content, comment) "
"values(:user, :ip, :primary_menu, :sub_menu, :op_type, :content, :comment)"
),
{"user": self.current_user.name,
"ip": self.request.headers[
"X-Forwarded-For"] if "X-Forwarded-For" in self.request.headers else self.request.remote_ip,
"primary_menu": "系统操作",
"sub_menu": "登录/登出",
"op_type": consts.op_type_login_logout_str,
"content": "退出登录",
"comment": ""
}
)
conn.commit()
self.r_app.delete(settings.session_key_prefix % self.current_user.uuid)
self.finish()
class LoginHandler(APIHandler):
def post(self):
# suid = shortuuid.ShortUUID().random(10)
# logging.info(suid)
username = self.get_escaped_argument("username")
password = self.get_escaped_argument("pwd")
# captcha = self.get_escaped_argument("captcha", "")
# captcha_token = self.get_escaped_argument("captcha_token", "")
# wrong_time_lock = self.r_app.get("pwd:wrong:time:%s:lock" % self.tostr(username))
# if wrong_time_lock:
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "账号处于冷却期,请稍后再试")
# return
if not username or not password:
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请输入用户名和密码")
# if not captcha:
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "请输入验证码")
# if not captcha_token:
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "缺少参数")
# c = tornado.web.decode_signed_value(
# settings.cookie_secret,
# "logc",
# self.tostr(captcha_token)
# )
# code = self.r_app.get("logincaptcha:%s" % self.tostr(c))
# 清除校验码缓存
# self.r_app.delete("logincaptcha:%s" % c)
# if not code:
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "验证码已过期")
# 判断验证码与缓存是否一致
# if self.tostr(captcha).lower() != self.tostr(code).lower():
# raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "验证码错误")
username = self.tostr(username)
password = self.tostr(password)
pwd_enc = aes.encrypt(settings.pwd_aes_key, password)
row = {}
with self.app_mysql.connect() as conn:
cur = conn.execute(
text("select id, uid, available from sys_user where name=:name and pwd=:pwd"),
{"name": username, "pwd": pwd_enc}
)
# keys = list(cur.keys())
#
# one = cur.fetchone()
# row = dict(zip(keys, one))
# logging.info(db.Row(itertools.zip_longest(keys, one)))
row = db_mysql.to_json(cur)
cur.close()
# data = [dict(zip(keys, res)) for res in cur.fetchall()]
if not row:
# wrong_time = self.r_app.get("pwd:wrong:time:%s" % username)
# logging.info(wrong_time)
# logging.info(settings.pwd_error_limit - 1)
# if wrong_time and int(wrong_time) > settings.pwd_error_limit - 1:
# self.r_app.set("pwd:wrong:time:%s:lock" % username, 1, ex=3600)
# self.r_app.delete("pwd:wrong:time:%s" % username)
# else:
# self.r_app.incr("pwd:wrong:time:%s" % username)
raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "用户名或者密码错误")
return
if row["available"] == 0:
raise errors.HTTPAPIError(errors.ERROR_FORBIDDEN, "当前用户被禁用")
return
# row_role = self.db_app.get("select role from user_role where userid=%s", row["id"])
# user_role = row_role["role"]
userId = row["id"]
jsessionid = row["uid"]
# create sign value admin_login_sign
secure_cookie = self.create_signed_value(settings.secure_cookie_name, str(jsessionid))
self.r_app.set(
settings.session_key_prefix % jsessionid,
json.dumps({
"id": userId,
"name": username,
"uuid": row["uid"],
# "role": user_role
}),
ex=settings.session_ttl
)
# self.db_app.insert(
# "insert into system_log(user, ip, first_module, second_module, op_type, op_content, description) "
# "values(%s, %s, %s, %s, %s, %s, %s)",
# username, self.request.remote_ip, "平台管理中心", "账号管理", "登录", "系统登录", "系统登录"
# )
# license_row = self.db_app.get(
# "select expireat from license limit 1"
# )
# system_status = get_license_status(license_row)
render_data = {
"token": str(secure_cookie, encoding="utf-8"),
# "role": user_role,
"username": username,
# "system_status": system_status, # 9000/未激活, 9001/已激活, 9002/过期可查看, 9003/完全过期
}
with self.app_mysql.connect() as conn:
conn.execute(text(
"insert into sys_log(user, ip, primary_menu, sub_menu, op_type, content, comment) "
"values(:user, :ip, :primary_menu, :sub_menu, :op_type, :content, :comment)"
),
{"user": username,
"ip": self.request.headers[
"X-Forwarded-For"] if "X-Forwarded-For" in self.request.headers else self.request.remote_ip,
"primary_menu": "系统操作",
"sub_menu": "登录/登出",
"op_type": consts.op_type_login_logout_str,
"content": "登录成功",
"comment": ""
}
)
conn.commit()
self.finish(render_data)
class UserInfoHandler(APIHandler):
def post(self):
# token = self.get_argument("token")
# user = self.get_current_user(token_body=self.tostr(token))
user = self.get_current_user()
if not user:
raise errors.HTTPAPIError(errors.ERROR_UNAUTHORIZED)
self.finish({"name": user.name, "avtar": ""})
class UserListHandler(APIHandler):
@authenticated
def post(self):
with self.app_mysql.connect() as conn:
cur = conn.execute(
text(
"select name from sys_user"
)
)
res = db_mysql.to_json_list(cur)
names = [row["name"] for row in res]
self.finish({"data": names})