feat: Phase 3 - AES encryption, SQL audit engine, audit logs (85 tests)
Phase 3.1: Instance password AES-256-GCM encryption Phase 3.2: SQL audit rule engine (10 rules) Phase 3.3: Auto-audit on ticket creation Phase 3.4: Operation audit logs + GET /audit/logs Tests: 44 -> 85 (41 new), Frontend: AuditLogsPage
This commit is contained in:
1 parent
27989274f5
commit
66b09e4d85
16 files changed
+1146
-6
No files matched your search
@@ -0,0 +1,45 @@
|
||||
"""AES-256-GCM encryption for instance passwords."""
|
||||
import base64
|
||||
import os
|
||||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||||
|
||||
from app.core.config import get_settings
|
||||
|
||||
_NONCE_SIZE = 12 # 96-bit nonce for AES-GCM
|
||||
|
||||
|
||||
def _get_key() -> bytes:
|
||||
"""Derive a 32-byte AES key from settings.SECRET_KEY."""
|
||||
settings = get_settings()
|
||||
# Use first 32 bytes of utf-8 encoded secret key (padded if needed)
|
||||
raw = settings.SECRET_KEY.encode("utf-8")
|
||||
if len(raw) >= 32:
|
||||
return raw[:32]
|
||||
return raw.ljust(32, b"\x00")
|
||||
|
||||
|
||||
def encrypt_password(plain: str) -> str:
|
||||
"""Encrypt a password string, return base64-encoded ciphertext (nonce + ciphertext)."""
|
||||
key = _get_key()
|
||||
nonce = os.urandom(_NONCE_SIZE)
|
||||
aesgcm = AESGCM(key)
|
||||
ct = aesgcm.encrypt(nonce, plain.encode("utf-8"), None)
|
||||
return base64.b64encode(nonce + ct).decode("ascii")
|
||||
|
||||
|
||||
def decrypt_password(encrypted: str) -> str:
|
||||
"""Decrypt a base64-encoded encrypted password back to plaintext.
|
||||
If the value is not valid base64 (e.g., legacy plaintext), return as-is.
|
||||
"""
|
||||
try:
|
||||
key = _get_key()
|
||||
raw = base64.b64decode(encrypted, validate=True)
|
||||
if len(raw) <= _NONCE_SIZE:
|
||||
return encrypted # too short to be encrypted
|
||||
nonce = raw[:_NONCE_SIZE]
|
||||
ct = raw[_NONCE_SIZE:]
|
||||
aesgcm = AESGCM(key)
|
||||
return aesgcm.decrypt(nonce, ct, None).decode("utf-8")
|
||||
except Exception:
|
||||
# Not encrypted (legacy plaintext password) — return as-is
|
||||
return encrypted
|
||||
Reference in new issue
Block a user