Adapt admin api to use new key format
Filter out deleted an previous version in count Remove todo comment Allow explicit ID specification Update tests
This commit is contained in:
@ -12,13 +12,12 @@ from sshecret.backend import (
|
||||
AuditListResult,
|
||||
Client,
|
||||
ClientFilter,
|
||||
Secret,
|
||||
SshecretBackend,
|
||||
Operation,
|
||||
SubSystem,
|
||||
)
|
||||
from sshecret.backend.models import DetailedSecrets
|
||||
from sshecret.backend.api import AuditAPI
|
||||
from sshecret.backend.api import AuditAPI, KeySpec
|
||||
from sshecret.crypto import encrypt_string, load_public_key
|
||||
|
||||
from .keepass import PasswordContext, load_password_manager
|
||||
@ -113,13 +112,13 @@ class AdminBackend:
|
||||
except Exception as e:
|
||||
raise BackendUnavailableError() from e
|
||||
|
||||
async def _get_client(self, name: str) -> Client | None:
|
||||
async def _get_client(self, idname: KeySpec) -> Client | None:
|
||||
"""Get a client from the backend."""
|
||||
return await self.backend.get_client(name)
|
||||
return await self.backend.get_client(idname)
|
||||
|
||||
async def _verify_client_exists(self, name: str) -> None:
|
||||
async def _verify_client_exists(self, idname: KeySpec) -> None:
|
||||
"""Check that a client exists."""
|
||||
client = await self.backend.get_client(name)
|
||||
client = await self.backend.get_client(idname)
|
||||
if not client:
|
||||
raise ClientNotFoundError()
|
||||
return None
|
||||
@ -133,10 +132,13 @@ class AdminBackend:
|
||||
except Exception as e:
|
||||
raise BackendUnavailableError() from e
|
||||
|
||||
async def get_client(self, name: str) -> Client | None:
|
||||
async def get_client(self, name: str, is_id: bool = False) -> Client | None:
|
||||
"""Get a client from the backend."""
|
||||
key = name
|
||||
if is_id:
|
||||
key = ("id", name)
|
||||
try:
|
||||
return await self._get_client(name)
|
||||
return await self._get_client(key)
|
||||
except ClientManagementError:
|
||||
raise
|
||||
except Exception as e:
|
||||
@ -176,16 +178,20 @@ class AdminBackend:
|
||||
raise BackendUnavailableError() from e
|
||||
|
||||
async def _update_client_public_key(
|
||||
self, name: str, new_key: str, password_manager: PasswordContext
|
||||
self, name: str, new_key: str, password_manager: PasswordContext, is_id: bool = False,
|
||||
) -> list[str]:
|
||||
"""Update client public key."""
|
||||
LOG.info(
|
||||
"Updating client %s public key. This will invalidate all existing secrets."
|
||||
)
|
||||
client = await self.get_client(name)
|
||||
client = await self.get_client(name, is_id=is_id)
|
||||
if not client:
|
||||
raise ClientNotFoundError()
|
||||
await self.backend.update_client_key(name, new_key)
|
||||
idname: KeySpec = name
|
||||
if is_id:
|
||||
idname = ("id", name)
|
||||
|
||||
await self.backend.update_client_key(idname, new_key)
|
||||
updated_secrets: list[str] = []
|
||||
for secret in client.secrets:
|
||||
LOG.debug("Re-encrypting secret %s for client %s", secret, name)
|
||||
@ -198,17 +204,17 @@ class AdminBackend:
|
||||
rsa_public_key = load_public_key(client.public_key.encode())
|
||||
encrypted = encrypt_string(secret_value, rsa_public_key)
|
||||
LOG.debug("Sending new encrypted value to backend.")
|
||||
await self.backend.create_client_secret(name, secret, encrypted)
|
||||
await self.backend.create_client_secret(idname, secret, encrypted)
|
||||
updated_secrets.append(secret)
|
||||
|
||||
return updated_secrets
|
||||
|
||||
async def update_client_public_key(self, name: str, new_key: str) -> list[str]:
|
||||
async def update_client_public_key(self, name: str, new_key: str, is_id: bool = False) -> list[str]:
|
||||
"""Update client public key."""
|
||||
try:
|
||||
with self.password_manager() as password_manager:
|
||||
return await self._update_client_public_key(
|
||||
name, new_key, password_manager
|
||||
name, new_key, password_manager, is_id=is_id
|
||||
)
|
||||
except ClientManagementError:
|
||||
raise
|
||||
@ -238,10 +244,13 @@ class AdminBackend:
|
||||
except Exception as e:
|
||||
raise BackendUnavailableError() from e
|
||||
|
||||
async def update_client_sources(self, name: str, sources: list[str]) -> None:
|
||||
async def update_client_sources(self, name: str, sources: list[str], is_id: bool = False) -> None:
|
||||
"""Update client sources."""
|
||||
key: KeySpec = name
|
||||
if is_id:
|
||||
key = ("id", name)
|
||||
try:
|
||||
await self.backend.update_client_sources(name, sources)
|
||||
await self.backend.update_client_sources(key, sources)
|
||||
except Exception as e:
|
||||
raise BackendUnavailableError() from e
|
||||
|
||||
@ -265,26 +274,10 @@ class AdminBackend:
|
||||
except Exception as e:
|
||||
raise BackendUnavailableError() from e
|
||||
|
||||
async def _get_secrets(self) -> list[Secret]:
|
||||
"""Get secrets.
|
||||
|
||||
This fetches the secret to client mapping from backend, and adds secrets from the password manager.
|
||||
"""
|
||||
with self.password_manager() as password_manager:
|
||||
all_secrets = password_manager.get_available_secrets()
|
||||
|
||||
secrets = await self.backend.get_secrets()
|
||||
backend_secret_names = [secret.name for secret in secrets]
|
||||
for secret in all_secrets:
|
||||
if secret not in backend_secret_names:
|
||||
secrets.append(Secret(name=secret, clients=[]))
|
||||
|
||||
return secrets
|
||||
|
||||
async def get_secrets(self) -> list[Secret]:
|
||||
async def get_secrets(self) -> list[DetailedSecrets]:
|
||||
"""Get secrets from backend."""
|
||||
try:
|
||||
return await self._get_secrets()
|
||||
return await self._get_detailed_secrets()
|
||||
except ClientManagementError:
|
||||
raise
|
||||
except Exception as e:
|
||||
@ -298,7 +291,7 @@ class AdminBackend:
|
||||
with self.password_manager() as password_manager:
|
||||
all_secrets = password_manager.get_available_secrets()
|
||||
|
||||
secrets = await self.backend.get_detailed_secrets()
|
||||
secrets = await self.backend.get_secrets()
|
||||
backend_secret_names = [secret.name for secret in secrets]
|
||||
for secret in all_secrets:
|
||||
if secret not in backend_secret_names:
|
||||
@ -368,7 +361,7 @@ class AdminBackend:
|
||||
|
||||
Groups are returned in a tree, unless flat is True.
|
||||
"""
|
||||
all_secrets = await self.backend.get_detailed_secrets()
|
||||
all_secrets = await self.backend.get_secrets()
|
||||
secrets_mapping = {secret.name: secret for secret in all_secrets}
|
||||
with self.password_manager() as password_manager:
|
||||
if flat:
|
||||
@ -427,7 +420,7 @@ class AdminBackend:
|
||||
secret_view = SecretView(name=name, secret=secret, group=secret_group)
|
||||
secret_mapping = await self.backend.get_secret(name)
|
||||
if secret_mapping:
|
||||
secret_view.clients = secret_mapping.clients
|
||||
secret_view.clients = [ref.name for ref in secret_mapping.clients]
|
||||
|
||||
return secret_view
|
||||
|
||||
@ -450,7 +443,7 @@ class AdminBackend:
|
||||
return
|
||||
for client in secret_mapping.clients:
|
||||
LOG.info("Deleting secret %s from client %s", name, client)
|
||||
await self.backend.delete_client_secret(client, name)
|
||||
await self.backend.delete_client_secret(("id", client.id), name)
|
||||
|
||||
async def _add_secret(
|
||||
self,
|
||||
@ -467,7 +460,7 @@ class AdminBackend:
|
||||
if update:
|
||||
secret_map = await self.backend.get_secret(name)
|
||||
if secret_map:
|
||||
clients = secret_map.clients
|
||||
clients = [ref.name for ref in secret_map.clients]
|
||||
|
||||
if not clients:
|
||||
return
|
||||
|
||||
@ -128,7 +128,7 @@ class ClientOperations:
|
||||
"""Delete client."""
|
||||
db_client = await self._get_client(client)
|
||||
if not db_client:
|
||||
return
|
||||
raise HTTPException(status_code=404, detail="Client not found.")
|
||||
if db_client.is_deleted:
|
||||
return
|
||||
db_client.is_deleted = True
|
||||
@ -271,7 +271,7 @@ async def get_clients(
|
||||
filter_query: ClientListParams,
|
||||
) -> ClientQueryResult:
|
||||
"""Get Clients."""
|
||||
count_statement = select(func.count("*")).select_from(Client)
|
||||
count_statement = select(func.count("*")).select_from(Client).where(Client.is_deleted.is_not(True)).where(Client.is_active.is_not(False))
|
||||
count_statement = cast(
|
||||
Select[tuple[int]],
|
||||
filter_client_statement(count_statement, filter_query, True),
|
||||
|
||||
@ -72,7 +72,6 @@ def create_client_secrets_router(get_db_session: AsyncDBSessionDep) -> APIRouter
|
||||
client_op = ClientSecretOperations(session, request, client)
|
||||
return await client_op.get_client_secret(secret)
|
||||
|
||||
# TODO: delete_client_secret
|
||||
@router.delete("/clients/{client_identifier}/secrets/{secret_identifier}")
|
||||
async def delete_client_secret(
|
||||
request: Request,
|
||||
|
||||
Reference in New Issue
Block a user