feat: Go后端LDAP真实对接 — AES-256-GCM密码加解密 + 登录LDAP fallback
- 新增 crypto.go: AES-256-GCM 加解密,与 Python backend 兼容 - auth.go: Login 添加 LDAP fallback(本地优先→LDAP绑定→自动创建本地用户) - ldap.go: 实现真实 LDAP 连接/用户搜索/密码验证/OU列表/用户列表 - 所有 LDAP 操作解密 admin_password 后使用 - 新增/更新密码存储时加密 - router.go: 补充 delete-user 路由 - 添加 go-ldap/v3 依赖
This commit is contained in:
1 parent
622923821b
commit
88e5f4e7ee
71 files changed
+20390
-64
No files matched your search
@@ -0,0 +1,157 @@
|
||||
"""Tests for SQL AST Analyzer (Phase 4.1)."""
|
||||
import pytest
|
||||
from app.services.sql_analyzer import SQLAnalyzer, SQLType, RiskLevel
|
||||
|
||||
|
||||
analyzer = SQLAnalyzer()
|
||||
|
||||
|
||||
class TestSQLTypeDetection:
|
||||
def test_select(self):
|
||||
result = analyzer.analyze("SELECT id, name FROM users WHERE id = 1;")
|
||||
assert result.sql_type == SQLType.SELECT
|
||||
|
||||
def test_insert(self):
|
||||
result = analyzer.analyze("INSERT INTO users (name) VALUES ('test');")
|
||||
assert result.sql_type == SQLType.INSERT
|
||||
|
||||
def test_update(self):
|
||||
result = analyzer.analyze("UPDATE users SET name = 'test' WHERE id = 1;")
|
||||
assert result.sql_type == SQLType.UPDATE
|
||||
|
||||
def test_delete(self):
|
||||
result = analyzer.analyze("DELETE FROM users WHERE id = 1;")
|
||||
assert result.sql_type == SQLType.DELETE
|
||||
|
||||
def test_drop_table(self):
|
||||
result = analyzer.analyze("DROP TABLE users;")
|
||||
assert result.sql_type == SQLType.DROP
|
||||
|
||||
def test_truncate(self):
|
||||
result = analyzer.analyze("TRUNCATE TABLE logs;")
|
||||
assert result.sql_type == SQLType.TRUNCATE
|
||||
|
||||
def test_alter_table(self):
|
||||
result = analyzer.analyze("ALTER TABLE users ADD COLUMN age INT;")
|
||||
assert result.sql_type == SQLType.ALTER
|
||||
|
||||
def test_create_table(self):
|
||||
result = analyzer.analyze("CREATE TABLE test (id INT PRIMARY KEY);")
|
||||
assert result.sql_type == SQLType.CREATE
|
||||
|
||||
def test_cte_select(self):
|
||||
result = analyzer.analyze("WITH cte AS (SELECT 1) SELECT * FROM cte;")
|
||||
assert result.sql_type == SQLType.SELECT
|
||||
|
||||
def test_explain(self):
|
||||
result = analyzer.analyze("EXPLAIN SELECT * FROM users;")
|
||||
assert result.sql_type == SQLType.SELECT
|
||||
|
||||
|
||||
class TestReadonlyValidation:
|
||||
def test_select_is_safe(self):
|
||||
safe, msg = analyzer.validate_readonly("SELECT id FROM users LIMIT 10;")
|
||||
assert safe is True
|
||||
|
||||
def test_insert_not_safe(self):
|
||||
safe, msg = analyzer.validate_readonly("INSERT INTO users (name) VALUES ('test');")
|
||||
assert safe is False
|
||||
assert "INSERT" in msg
|
||||
|
||||
def test_update_not_safe(self):
|
||||
safe, msg = analyzer.validate_readonly("UPDATE users SET name = 'test' WHERE id = 1;")
|
||||
assert safe is False
|
||||
|
||||
def test_delete_not_safe(self):
|
||||
safe, msg = analyzer.validate_readonly("DELETE FROM users WHERE id = 1;")
|
||||
assert safe is False
|
||||
|
||||
def test_drop_not_safe(self):
|
||||
safe, msg = analyzer.validate_readonly("DROP TABLE users;")
|
||||
assert safe is False
|
||||
|
||||
def test_truncate_not_safe(self):
|
||||
safe, msg = analyzer.validate_readonly("TRUNCATE TABLE logs;")
|
||||
assert safe is False
|
||||
|
||||
def test_alter_not_safe(self):
|
||||
safe, msg = analyzer.validate_readonly("ALTER TABLE users ADD COLUMN age INT;")
|
||||
assert safe is False
|
||||
|
||||
def test_create_not_safe(self):
|
||||
safe, msg = analyzer.validate_readonly("CREATE TABLE test (id INT);")
|
||||
assert safe is False
|
||||
|
||||
|
||||
class TestRiskLevels:
|
||||
def test_select_is_safe_risk(self):
|
||||
result = analyzer.analyze("SELECT * FROM users;")
|
||||
assert result.risk_level == RiskLevel.SAFE
|
||||
|
||||
def test_dml_with_where_is_medium(self):
|
||||
result = analyzer.analyze("UPDATE users SET name = 'a' WHERE id = 1;")
|
||||
assert result.risk_level == RiskLevel.MEDIUM
|
||||
|
||||
def test_dml_without_where_is_high(self):
|
||||
result = analyzer.analyze("UPDATE users SET name = 'a';")
|
||||
assert result.risk_level == RiskLevel.HIGH
|
||||
|
||||
def test_ddl_is_dangerous(self):
|
||||
result = analyzer.analyze("DROP TABLE users;")
|
||||
assert result.risk_level == RiskLevel.DANGEROUS
|
||||
|
||||
|
||||
class TestSecurityChecks:
|
||||
def test_multi_statement_rejected(self):
|
||||
safe, msg = analyzer.validate_readonly("SELECT 1; DROP TABLE users;")
|
||||
assert safe is False
|
||||
assert "多条" in msg or "分号" in msg
|
||||
|
||||
def test_into_outfile_rejected(self):
|
||||
safe, msg = analyzer.validate_readonly("SELECT * INTO OUTFILE '/tmp/data.txt' FROM users;")
|
||||
assert safe is False
|
||||
assert "OUTFILE" in msg
|
||||
|
||||
def test_subquery_dml_rejected(self):
|
||||
safe, msg = analyzer.validate_readonly("SELECT * FROM (DELETE FROM users) AS t;")
|
||||
assert safe is False
|
||||
|
||||
def test_normal_subquery_ok(self):
|
||||
safe, msg = analyzer.validate_readonly("SELECT * FROM (SELECT id FROM users) AS t;")
|
||||
assert safe is True
|
||||
|
||||
def test_semicolon_in_string_ok(self):
|
||||
safe, msg = analyzer.validate_readonly("SELECT 'hello;world' FROM users;")
|
||||
assert safe is True
|
||||
|
||||
|
||||
class TestAnalysis:
|
||||
def test_has_where(self):
|
||||
result = analyzer.analyze("SELECT * FROM users WHERE id = 1;")
|
||||
assert result.has_where is True
|
||||
|
||||
def test_no_where(self):
|
||||
result = analyzer.analyze("SELECT * FROM users;")
|
||||
assert result.has_where is False
|
||||
|
||||
def test_to_dict(self):
|
||||
result = analyzer.analyze("SELECT id FROM users;")
|
||||
d = result.to_dict()
|
||||
assert "sql_type" in d
|
||||
assert "risk_level" in d
|
||||
assert "is_readonly" in d
|
||||
assert d["sql_type"] == "SELECT"
|
||||
assert d["is_readonly"] is True
|
||||
|
||||
def test_complex_select(self):
|
||||
result = analyzer.analyze("""
|
||||
SELECT u.id, u.name, o.total
|
||||
FROM users u
|
||||
JOIN orders o ON u.id = o.user_id
|
||||
WHERE u.status = 'active'
|
||||
ORDER BY o.total DESC
|
||||
LIMIT 50;
|
||||
""")
|
||||
assert result.sql_type == SQLType.SELECT
|
||||
assert result.is_readonly is True
|
||||
assert result.has_where is True
|
||||
Reference in new issue
Block a user