rebuild(stage-3): establish accounts permissions and secure settings
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from backend.database.connection import Database
|
||||
from backend.errors import BusinessError
|
||||
from backend.features.accounts.models import Principal
|
||||
from backend.features.accounts.repository import SystemCredentialRepository
|
||||
from backend.features.accounts.service import now_utc
|
||||
from backend.security import SecretCipher
|
||||
|
||||
CredentialName = Literal["tushare_token", "ifind_refresh_token", "ifind_access_token"]
|
||||
ALLOWED_CREDENTIALS: tuple[CredentialName, ...] = (
|
||||
"tushare_token",
|
||||
"ifind_refresh_token",
|
||||
"ifind_access_token",
|
||||
)
|
||||
|
||||
|
||||
class SystemCredentialService:
|
||||
def __init__(
|
||||
self,
|
||||
database: Database,
|
||||
repository: SystemCredentialRepository,
|
||||
cipher: SecretCipher,
|
||||
) -> None:
|
||||
self._database = database
|
||||
self._repository = repository
|
||||
self._cipher = cipher
|
||||
|
||||
def list_status(self) -> tuple[dict[str, str | bool | None], ...]:
|
||||
with self._database.read() as connection:
|
||||
configured = self._repository.list_status(connection)
|
||||
return tuple(
|
||||
{
|
||||
"name": name,
|
||||
"configured": name in configured,
|
||||
"updated_at": configured.get(name),
|
||||
}
|
||||
for name in ALLOWED_CREDENTIALS
|
||||
)
|
||||
|
||||
def save(self, actor: Principal, name: str, value: str) -> None:
|
||||
if name not in ALLOWED_CREDENTIALS:
|
||||
raise BusinessError("invalid_credential", "系统凭据类型无效。")
|
||||
cleaned = value.strip()
|
||||
if not cleaned or len(cleaned) > 4096:
|
||||
raise BusinessError("invalid_credential", "系统凭据内容无效。")
|
||||
with self._database.transaction() as connection:
|
||||
self._repository.save(
|
||||
connection, name, self._cipher.encrypt(cleaned), actor.user.id, now_utc()
|
||||
)
|
||||
|
||||
def get(self, name: CredentialName) -> str | None:
|
||||
with self._database.read() as connection:
|
||||
encrypted = self._repository.get_encrypted(connection, name)
|
||||
return self._cipher.decrypt(encrypted) if encrypted else None
|
||||
Reference in New Issue
Block a user