Create views for organizing secrets in groups
This commit is contained in:
@ -23,7 +23,13 @@ from sshecret.crypto import encrypt_string, load_public_key
|
||||
|
||||
from .keepass import PasswordContext, load_password_manager
|
||||
from sshecret_admin.core.settings import AdminServerSettings
|
||||
from .models import ClientSecretGroup, SecretClientMapping, SecretGroup, SecretView
|
||||
from .models import (
|
||||
ClientSecretGroup,
|
||||
ClientSecretGroupList,
|
||||
SecretClientMapping,
|
||||
SecretGroup,
|
||||
SecretView,
|
||||
)
|
||||
|
||||
|
||||
class ClientManagementError(Exception):
|
||||
@ -353,7 +359,7 @@ class AdminBackend:
|
||||
self,
|
||||
group_filter: str | None = None,
|
||||
regex: bool = True,
|
||||
) -> list[ClientSecretGroup]:
|
||||
) -> ClientSecretGroupList:
|
||||
"""Get secret groups.
|
||||
|
||||
The starting group can be filtered with the group_name argument, which
|
||||
@ -363,19 +369,33 @@ class AdminBackend:
|
||||
secrets_mapping = {secret.name: secret for secret in all_secrets}
|
||||
with self.password_manager() as password_manager:
|
||||
all_groups = password_manager.get_secret_groups(group_filter, regex=regex)
|
||||
ungrouped = password_manager.get_ungrouped_secrets()
|
||||
|
||||
result: list[ClientSecretGroup] = []
|
||||
group_result: list[ClientSecretGroup] = []
|
||||
for group in all_groups:
|
||||
# We have to do this recursively.
|
||||
result.append(add_clients_to_secret_group(group, secrets_mapping))
|
||||
group_result.append(add_clients_to_secret_group(group, secrets_mapping))
|
||||
|
||||
result = ClientSecretGroupList(groups=group_result)
|
||||
if group_filter:
|
||||
return result
|
||||
|
||||
ungrouped_clients: list[SecretClientMapping] = []
|
||||
for name in ungrouped:
|
||||
mapping = SecretClientMapping(name=name)
|
||||
if client_mapping := secrets_mapping.get(name):
|
||||
mapping.clients = client_mapping.clients
|
||||
ungrouped_clients.append(mapping)
|
||||
|
||||
result.ungrouped = ungrouped_clients
|
||||
return result
|
||||
|
||||
async def get_secret_group(self, name: str) -> ClientSecretGroup | None:
|
||||
"""Get a single secret group by name."""
|
||||
matches = await self.get_secret_groups(group_filter=name, regex=False)
|
||||
if not matches:
|
||||
return None
|
||||
return matches[0]
|
||||
if matches.groups:
|
||||
return matches.groups[0]
|
||||
return None
|
||||
|
||||
async def get_secret(self, name: str) -> SecretView | None:
|
||||
"""Get secrets from backend."""
|
||||
@ -390,10 +410,11 @@ class AdminBackend:
|
||||
"""Get a secret, including the actual unencrypted value and clients."""
|
||||
with self.password_manager() as password_manager:
|
||||
secret = password_manager.get_secret(name)
|
||||
secret_group = password_manager.get_entry_group(name)
|
||||
|
||||
if not secret:
|
||||
return None
|
||||
secret_view = SecretView(name=name, secret=secret)
|
||||
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
|
||||
|
||||
@ -134,6 +134,16 @@ class PasswordContext:
|
||||
|
||||
raise RuntimeError(f"Cannot get password for entry {entry_name}")
|
||||
|
||||
def get_entry_group(self, entry_name: str) -> str | None:
|
||||
"""Get the group for an entry."""
|
||||
entry = self._get_entry(entry_name)
|
||||
if not entry:
|
||||
return None
|
||||
if entry.group.is_root_group:
|
||||
return None
|
||||
return str(entry.group.name)
|
||||
|
||||
|
||||
def get_secret_groups(self, pattern: str | None = None, regex: bool = True) -> list[SecretGroup]:
|
||||
"""Get secret groups.
|
||||
|
||||
@ -145,13 +155,19 @@ class PasswordContext:
|
||||
self.keepass.find_groups(name=pattern, regex=regex),
|
||||
)
|
||||
else:
|
||||
all_groups = cast(list[pykeepass.group.Group], self.keepass.groups)
|
||||
# We skip the root group
|
||||
groups = [group for group in all_groups if not group.is_root_group]
|
||||
groups = self._root_group.subgroups
|
||||
|
||||
secret_groups = [_kp_group_to_secret_group(group) for group in groups]
|
||||
return secret_groups
|
||||
|
||||
def get_ungrouped_secrets(self) -> list[str]:
|
||||
"""Get secrets without groups."""
|
||||
entries: list[str] = []
|
||||
for entry in self._root_group.entries:
|
||||
entries.append(str(entry.title))
|
||||
|
||||
return entries
|
||||
|
||||
def add_group(
|
||||
self, name: str, description: str | None = None, parent_group: str | None = None
|
||||
) -> None:
|
||||
|
||||
@ -33,6 +33,7 @@ class SecretView(BaseModel):
|
||||
|
||||
name: str
|
||||
secret: str
|
||||
group: str | None = None
|
||||
clients: list[str] = Field(default_factory=list) # Clients that have access to it.
|
||||
|
||||
|
||||
@ -105,6 +106,7 @@ class SecretCreate(SecretUpdate):
|
||||
{
|
||||
"name": "MySecret",
|
||||
"clients": ["client-1", "client-2"],
|
||||
"group": None,
|
||||
"value": {"auto_generate": True, "length": 32},
|
||||
},
|
||||
{
|
||||
@ -152,3 +154,10 @@ class SecretGroupCreate(BaseModel):
|
||||
name: str
|
||||
description: str | None = None
|
||||
parent_group: str | None = None
|
||||
|
||||
|
||||
class ClientSecretGroupList(BaseModel):
|
||||
"""Secret group list."""
|
||||
|
||||
ungrouped: list[SecretClientMapping] = Field(default_factory=list)
|
||||
groups: list[ClientSecretGroup] = Field(default_factory=list)
|
||||
|
||||
Reference in New Issue
Block a user