# -*- coding: utf-8 -*- """账号与权限:密码哈希、会话、角色权限表 只依赖标准库。密码用 PBKDF2-HMAC-SHA256,每个账号独立盐值。 """ import hashlib import hmac import secrets ITERATIONS = 120_000 # ---------------------------------------------------------------- 角色定义 # 三级角色,权限逐级递增 ROLES = { "viewer": {"label": "查看", "desc": "只看数据,不能改任何东西", "level": 1}, "editor": {"label": "运营", "desc": "可导入、同步、导出,不能改判定规则", "level": 2}, "admin": {"label": "管理员", "desc": "全部权限,含账号管理", "level": 3}, } ROLE_ORDER = ["admin", "editor", "viewer"] # 权限点 PERMS = { "view": "查看数据", "export": "导出原始文件", "import": "导入数据", "sync": "同步生意经数据", "config": "修改指标阈值 / 文案", "users": "账号管理", } # 角色 → 拥有的权限 GRANTS = { "viewer": {"view"}, "editor": {"view", "export", "import", "sync"}, "admin": {"view", "export", "import", "sync", "config", "users"}, } def can(role, perm): return perm in GRANTS.get(role or "", set()) def role_label(role): return ROLES.get(role or "", {}).get("label", role or "-") # ---------------------------------------------------------------- 密码 def hash_password(password, salt=None): """返回 (hash_hex, salt_hex)""" salt = salt or secrets.token_hex(16) digest = hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt.encode("utf-8"), ITERATIONS) return digest.hex(), salt def verify_password(password, stored_hash, salt): if not stored_hash or not salt: return False digest, _ = hash_password(password, salt) return hmac.compare_digest(digest, stored_hash) def check_strength(password): """返回 (是否合格, 提示)""" if not password or len(password) < 6: return False, "密码至少 6 位" if password.isdigit(): return False, "密码不能全是数字" return True, "" # ---------------------------------------------------------------- 会话 def new_token(): return secrets.token_urlsafe(32) def hash_token(token): """会话令牌在库里只存哈希,泄露数据库也拿不到可用的 cookie""" return hashlib.sha256(token.encode("utf-8")).hexdigest()