| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- const db = require('../plugin/DataBase/db')
- const Redis = require('../plugin/DataBase/Redis')
- const {
- DEFAULT_PERMISSION_POINTS,
- DEFAULT_PERMISSION_RESOURCE_RULES,
- DEFAULT_BASIC_USER_PERMISSION_CODES,
- LEGACY_ROLE_PERMISSION_MAP
- } = require('./PermissionCatalog')
- class AccessControl {
- constructor() {
- this.schemaReady = false
- }
- parseArray(value) {
- if (Array.isArray(value)) return value
- if (!value) return []
- if (typeof value !== 'string') return []
- try {
- const parsed = JSON.parse(value)
- return Array.isArray(parsed) ? parsed : []
- } catch (_) {
- return []
- }
- }
- normalizeCodes(codes) {
- if (!codes) return []
- const list = Array.isArray(codes) ? codes : [codes]
- return [...new Set(list.map(code => String(code || '').trim()).filter(Boolean))]
- }
- async ensurePermissionSchema() {
- if (this.schemaReady) return
- await db.query(`
- CREATE TABLE IF NOT EXISTS permission_points (
- id INT NOT NULL AUTO_INCREMENT,
- code VARCHAR(120) NOT NULL,
- name VARCHAR(120) NOT NULL,
- category VARCHAR(40) NOT NULL DEFAULT 'action',
- scope_type VARCHAR(40) NOT NULL DEFAULT 'action',
- page_route_name VARCHAR(120) DEFAULT NULL,
- enabled TINYINT NOT NULL DEFAULT 1,
- remark VARCHAR(255) DEFAULT '',
- created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (id),
- UNIQUE KEY uniq_permission_points_code (code)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
- `)
- await db.query(`
- CREATE TABLE IF NOT EXISTS user_permission_points (
- id INT NOT NULL AUTO_INCREMENT,
- user_uuid VARCHAR(64) NOT NULL,
- permission_code VARCHAR(120) NOT NULL,
- created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- PRIMARY KEY (id),
- UNIQUE KEY uniq_user_permission (user_uuid, permission_code),
- KEY idx_user_permission_user_uuid (user_uuid),
- KEY idx_user_permission_code (permission_code)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
- `)
- await db.query(`
- CREATE TABLE IF NOT EXISTS user_basic_permission_denials (
- id INT NOT NULL AUTO_INCREMENT,
- user_uuid VARCHAR(64) NOT NULL,
- permission_code VARCHAR(120) NOT NULL,
- created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- PRIMARY KEY (id),
- UNIQUE KEY uniq_user_basic_denial (user_uuid, permission_code),
- KEY idx_user_basic_denial_user_uuid (user_uuid)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
- `)
- await db.query(`
- CREATE TABLE IF NOT EXISTS permission_resource_rules (
- id INT NOT NULL AUTO_INCREMENT,
- resource_type VARCHAR(40) NOT NULL,
- resource_key VARCHAR(180) NOT NULL,
- api_method VARCHAR(16) DEFAULT NULL,
- api_path VARCHAR(180) DEFAULT NULL,
- required_codes TEXT NOT NULL,
- enabled TINYINT NOT NULL DEFAULT 1,
- remark VARCHAR(255) DEFAULT '',
- created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
- PRIMARY KEY (id),
- UNIQUE KEY uniq_permission_resource (resource_type, resource_key),
- KEY idx_permission_resource_api (api_method, api_path)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
- `)
- for (const point of DEFAULT_PERMISSION_POINTS) {
- await db.query(
- `INSERT INTO permission_points
- (code, name, category, scope_type, page_route_name, enabled, remark)
- VALUES (?, ?, ?, ?, ?, 1, ?)
- ON DUPLICATE KEY UPDATE
- name = VALUES(name),
- category = VALUES(category),
- scope_type = VALUES(scope_type),
- page_route_name = VALUES(page_route_name),
- remark = VALUES(remark)`,
- [point.code, point.name, point.category, point.scope_type, point.page_route_name || null, point.remark || '']
- )
- }
- for (const rule of DEFAULT_PERMISSION_RESOURCE_RULES) {
- const requiredCodes = JSON.stringify(this.normalizeCodes(rule.required_codes))
- await db.query(
- `INSERT INTO permission_resource_rules
- (resource_type, resource_key, api_method, api_path, required_codes, enabled, remark)
- VALUES (?, ?, ?, ?, ?, 1, ?)
- ON DUPLICATE KEY UPDATE
- api_method = VALUES(api_method),
- api_path = VALUES(api_path),
- remark = VALUES(remark)`,
- [
- rule.resource_type,
- rule.resource_key,
- rule.api_method || null,
- rule.api_path || null,
- requiredCodes,
- rule.remark || ''
- ]
- )
- }
- this.schemaReady = true
- }
- async checkSession(uuid, session) {
- return (await Redis.get(`userSession:${uuid}`)) === session
- }
- async isBanned(uuid) {
- const sql = 'SELECT COALESCE(is_banned, 0) AS is_banned FROM users WHERE uuid = ?'
- const rows = await db.query(sql, [uuid])
- return Number(rows[0]?.is_banned) === 1
- }
- async invalidateSession(uuid) {
- await Redis.del(`userSession:${uuid}`)
- }
- async getPermission(uuid) {
- const sql = 'SELECT permission FROM users WHERE uuid = ?'
- const rows = await db.query(sql, [uuid])
- return this.parseArray(rows?.[0]?.permission)
- }
- isSuperPermission(permission) {
- return this.parseArray(permission).includes('admin')
- }
- async isSuperAdmin(uuid) {
- const permission = await this.getPermission(uuid)
- return this.isSuperPermission(permission)
- }
- async getPermissionPoints() {
- await this.ensurePermissionSchema()
- const rows = await db.query(`
- SELECT id, code, name, category, scope_type, page_route_name, enabled, remark
- FROM permission_points
- ORDER BY category, id
- `)
- return rows || []
- }
- async getUserDirectPermissionCodes(uuid) {
- await this.ensurePermissionSchema()
- const rows = await db.query(
- `SELECT permission_code FROM user_permission_points WHERE user_uuid = ? ORDER BY permission_code`,
- [uuid]
- )
- return (rows || []).map(row => row.permission_code)
- }
- async getUserDeniedBasicPermissionCodes(uuid) {
- await this.ensurePermissionSchema()
- const rows = await db.query(
- `SELECT permission_code FROM user_basic_permission_denials WHERE user_uuid = ? ORDER BY permission_code`,
- [uuid]
- )
- const denied = (rows || []).map(row => row.permission_code)
- const basicSet = new Set(DEFAULT_BASIC_USER_PERMISSION_CODES)
- return this.normalizeCodes(denied.filter(code => basicSet.has(code)))
- }
- getEnabledBasicPermissionCodes(deniedBasicCodes = []) {
- const deniedSet = new Set(this.normalizeCodes(deniedBasicCodes))
- return DEFAULT_BASIC_USER_PERMISSION_CODES.filter(code => !deniedSet.has(code))
- }
- async getUserPermissionCodes(uuid) {
- await this.ensurePermissionSchema()
- const legacyRoles = await this.getPermission(uuid)
- const directCodes = await this.getUserDirectPermissionCodes(uuid)
- const deniedBasicCodes = await this.getUserDeniedBasicPermissionCodes(uuid)
- const roleCodes = legacyRoles.flatMap(role => LEGACY_ROLE_PERMISSION_MAP[role] || [])
- return this.normalizeCodes([
- ...this.getEnabledBasicPermissionCodes(deniedBasicCodes),
- ...legacyRoles,
- ...roleCodes,
- ...directCodes
- ])
- }
- async setUserPermissionCodes(uuid, codes) {
- await this.ensurePermissionSchema()
- const permissionCodes = this.normalizeCodes(codes)
- const points = await this.getPermissionPoints()
- const validCodes = new Set(points.map(point => point.code))
- const invalidCodes = permissionCodes.filter(code => !validCodes.has(code))
- if (invalidCodes.length > 0)
- throw new Error(`存在无效权限点:${invalidCodes.join(', ')}`)
- const conn = await db.connect()
- await conn.beginTransaction()
- try {
- await conn.execute(`DELETE FROM user_permission_points WHERE user_uuid = ?`, [uuid])
- for (const code of permissionCodes) {
- await conn.execute(
- `INSERT INTO user_permission_points (user_uuid, permission_code) VALUES (?, ?)`,
- [uuid, code]
- )
- }
- await conn.commit()
- } catch (error) {
- try { await conn.rollback() } catch (_) { }
- throw error
- }
- }
- async setUserDeniedBasicPermissionCodes(uuid, codes) {
- await this.ensurePermissionSchema()
- const basicSet = new Set(DEFAULT_BASIC_USER_PERMISSION_CODES)
- const deniedCodes = this.normalizeCodes(codes).filter(code => basicSet.has(code))
- const invalidCodes = this.normalizeCodes(codes).filter(code => !basicSet.has(code))
- if (invalidCodes.length > 0)
- throw new Error(`仅可关闭基础权限:${invalidCodes.join(', ')}`)
- const conn = await db.connect()
- await conn.beginTransaction()
- try {
- await conn.execute(`DELETE FROM user_basic_permission_denials WHERE user_uuid = ?`, [uuid])
- for (const code of deniedCodes) {
- await conn.execute(
- `INSERT INTO user_basic_permission_denials (user_uuid, permission_code) VALUES (?, ?)`,
- [uuid, code]
- )
- }
- await conn.commit()
- } catch (error) {
- try { await conn.rollback() } catch (_) { }
- throw error
- }
- }
- async getResourceRules() {
- await this.ensurePermissionSchema()
- const rows = await db.query(`
- SELECT id, resource_type, resource_key, api_method, api_path, required_codes, enabled, remark
- FROM permission_resource_rules
- ORDER BY FIELD(resource_type, 'page', 'action', 'api'), id
- `)
- return (rows || []).map(row => ({
- ...row,
- required_codes: this.parseArray(row.required_codes)
- }))
- }
- async getResourceRequiredCodes({ resourceType, resourceKey, method, path }) {
- await this.ensurePermissionSchema()
- let rows = []
- if (resourceType && resourceKey) {
- rows = await db.query(
- `SELECT required_codes
- FROM permission_resource_rules
- WHERE resource_type = ? AND resource_key = ? AND enabled = 1
- LIMIT 1`,
- [resourceType, resourceKey]
- )
- } else if (method && path) {
- rows = await db.query(
- `SELECT required_codes
- FROM permission_resource_rules
- WHERE resource_type = 'api' AND api_method = ? AND api_path = ? AND enabled = 1
- LIMIT 1`,
- [String(method).toUpperCase(), path]
- )
- }
- return this.parseArray(rows?.[0]?.required_codes)
- }
- async updateResourceRule({ id, required_codes, enabled }) {
- await this.ensurePermissionSchema()
- const requiredCodes = this.normalizeCodes(required_codes)
- const points = await this.getPermissionPoints()
- const validCodes = new Set(points.map(point => point.code))
- const invalidCodes = requiredCodes.filter(code => !validCodes.has(code))
- if (invalidCodes.length > 0)
- throw new Error(`存在无效权限点:${invalidCodes.join(', ')}`)
- const rows = await db.query(
- `UPDATE permission_resource_rules
- SET required_codes = ?, enabled = ?
- WHERE id = ?`,
- [JSON.stringify(requiredCodes), Number(enabled) === 0 ? 0 : 1, id]
- )
- return rows?.affectedRows === 1
- }
- async canAccess(uuid, requiredCodes) {
- const codes = this.normalizeCodes(requiredCodes)
- if (codes.length === 0) return true
- if (await this.isSuperAdmin(uuid)) return true
- const userCodes = await this.getUserPermissionCodes(uuid)
- return codes.some(code => userCodes.includes(code))
- }
- async checkJwAccount(uuid, username) {
- const sql = 'SELECT password FROM jw_account WHERE create_user = ? AND state = 1 AND username = ?'
- const rows = await db.query(sql, [uuid, username]);
- if (!rows || rows.length !== 1 || !rows[0].password)
- return false
- return rows[0]?.password
- }
- }
- module.exports = new AccessControl();
|