refactor(auth): 管理员判定上移至 gateway,经 Token payload 下发 is_admin

gateway 登录签发 Token 时,账号命中 [Admin] users 白名单即在 payload
写入 is_admin=true(随 RSA 签名保护);dnfs 移除本地 ADMIN_USERS 硬编码,
delete_note/update_note 豁免判断改读 payload 的 is_admin 字段。
旧 Token 无此字段按非管理员处理(安全方向)。
This commit is contained in:
qing committed 2026-07-30 09:08:33 +08:00
1 parent e9f9860fe1
commit 46c3a197be
3 files changed
+20 -5

No files matched your search

+5 -4
View File
@@ -37,8 +37,9 @@ from pymsg import MsgClient # noqa: E402
_DBV2_ADDR = '127.0.0.1'
_DBV2_PORT = 8005
# 管理员账号:删除/更新节点时豁免所有权校验(保留旧的 'admin' 以兼容历史数据)
ADMIN_USERS = ('admin@qsont.me', 'qing@qsont.me')
# 管理员判定已上移至 gateway:登录时命中 [Admin] users 白名单即在 Token payload
# 写入 is_admin=true,本服务通过 info.get('is_admin') 读取,不再本地维护名单。
# 旧 Token 无此字段,按非管理员处理(安全方向)。
def _call_dbv2(payload: dict, timeout: int = 10) -> dict:
"""通过 pymsg 短连接调用 dbv2 数据库服务。
@@ -326,7 +327,7 @@ def delete_note():
data = _call_dbv2(select_payload)
notes = data['data']
if info['user'] not in ADMIN_USERS:
if not info.get('is_admin'):
if not len(notes) == 1 or not notes[0]['owner'] == info['user']:
logs.warning('dnfs', f'delete_note 所有权拒绝 id={note_id}', extra={'user': info['user']})
return jsonify({"error": 'It\'s not your note'}), 403
@@ -401,7 +402,7 @@ def update_note():
data = _call_dbv2(select_payload)
notes = data['data']
if info['user'] not in ADMIN_USERS:
if not info.get('is_admin'):
if not len(notes) == 1 or not notes[0]['owner'] == info['user']:
logs.warning('dnfs', f'update_note 所有权拒绝 id={id}', extra={'user': info['user']})
return jsonify({"error": 'It\'s not your note'}), 403
+6
View File
@@ -15,6 +15,12 @@ opened = http://127.0.0.1:9200;http://127.0.0.1:8006;
# Token 有效期(秒),默认 3600 = 1 小时
keep_time = 3600
[Admin]
# 管理员账号白名单(分号分隔)。登录时命中即在 Token payload 写入 is_admin=true,
# 下游服务(dnfs 等)凭该字段豁免所有权校验,无需各自维护名单。
# 修改后需重启 gateway 生效;旧 Token 无此字段,按非管理员处理。
users = qing@qsont.me
[CORS]
# 允许跨域的 Origin 白名单(分号分隔)
# 仅命中的 Origin 才会回填 Access-Control-Allow-Origin;缺省为空(不回填)
+9 -1
View File
@@ -5,6 +5,7 @@
#include <us/SHA256.hpp>
#include <us/Sqlite3.hpp>
#include <iostream>
#include <algorithm>
#include <cctype>
#include <typeinfo>
#include <memory>
@@ -325,6 +326,7 @@ static std::vector<std::string> _OPEN_URL;
static std::string _DB_PATH;
static int _TOKEN_KEEP_TIME = 3600; // Token 有效期(秒),可由 [Token] keep_time 配置
static std::vector<std::string> _CORS_ORIGINS; // CORS 允许的 Origin 白名单,可由 [CORS] allowed_origins 配置
static std::vector<std::string> _ADMIN_USERS; // 管理员账号白名单,可由 [Admin] users 配置(分号分隔),登录时写入 Token payload 的 is_admin
/* instance items */
static std::unique_ptr<HttpServer> _SERVE;
@@ -498,6 +500,7 @@ int main(int argc, char **argv) {
_DB_PATH = cf.get<std::string>("Database.path", "");
_TOKEN_KEEP_TIME = cf.get<int>("Token.keep_time", 3600);
_CORS_ORIGINS = split(cf.get<std::string>("CORS.allowed_origins", ""), ';');
_ADMIN_USERS = split(cf.get<std::string>("Admin.users", ""), ';');
}
@@ -867,10 +870,15 @@ http::response<http::string_body> handleLogin(
/* 生成令牌 */
auto _PRIV = MyRSA::Private_Key::from_pem(_KEYGEN->get_private_key_pem());
/* 管理员判定:账号命中 [Admin] users 白名单即在 payload 中签发 is_admin,
* 下游服务(dnfs 等)直接读取该字段豁免所有权校验,无需各自维护名单 */
bool is_admin = std::find(_ADMIN_USERS.begin(), _ADMIN_USERS.end(), username) != _ADMIN_USERS.end();
json::value jv_t = {
{"user", username},
{"created_at", LocalTime::getCurrent()},
{"keep_time", _TOKEN_KEEP_TIME}
{"keep_time", _TOKEN_KEEP_TIME},
{"is_admin", is_admin}
};
std::string data = json::serialize(jv_t);
std::string auth = Base64::base64_encode(data);