powermem 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- powermem/__init__.py +103 -0
- powermem/agent/__init__.py +35 -0
- powermem/agent/abstract/__init__.py +22 -0
- powermem/agent/abstract/collaboration.py +259 -0
- powermem/agent/abstract/context.py +187 -0
- powermem/agent/abstract/manager.py +232 -0
- powermem/agent/abstract/permission.py +217 -0
- powermem/agent/abstract/privacy.py +267 -0
- powermem/agent/abstract/scope.py +199 -0
- powermem/agent/agent.py +791 -0
- powermem/agent/components/__init__.py +18 -0
- powermem/agent/components/collaboration_coordinator.py +645 -0
- powermem/agent/components/permission_controller.py +586 -0
- powermem/agent/components/privacy_protector.py +767 -0
- powermem/agent/components/scope_controller.py +685 -0
- powermem/agent/factories/__init__.py +16 -0
- powermem/agent/factories/agent_factory.py +266 -0
- powermem/agent/factories/config_factory.py +308 -0
- powermem/agent/factories/memory_factory.py +229 -0
- powermem/agent/implementations/__init__.py +16 -0
- powermem/agent/implementations/hybrid.py +728 -0
- powermem/agent/implementations/multi_agent.py +1040 -0
- powermem/agent/implementations/multi_user.py +1020 -0
- powermem/agent/types.py +53 -0
- powermem/agent/wrappers/__init__.py +14 -0
- powermem/agent/wrappers/agent_memory_wrapper.py +427 -0
- powermem/agent/wrappers/compatibility_wrapper.py +520 -0
- powermem/config_loader.py +318 -0
- powermem/configs.py +249 -0
- powermem/core/__init__.py +19 -0
- powermem/core/async_memory.py +1493 -0
- powermem/core/audit.py +258 -0
- powermem/core/base.py +165 -0
- powermem/core/memory.py +1567 -0
- powermem/core/setup.py +162 -0
- powermem/core/telemetry.py +215 -0
- powermem/integrations/__init__.py +17 -0
- powermem/integrations/embeddings/__init__.py +13 -0
- powermem/integrations/embeddings/aws_bedrock.py +100 -0
- powermem/integrations/embeddings/azure_openai.py +55 -0
- powermem/integrations/embeddings/base.py +31 -0
- powermem/integrations/embeddings/config/base.py +132 -0
- powermem/integrations/embeddings/configs.py +31 -0
- powermem/integrations/embeddings/factory.py +48 -0
- powermem/integrations/embeddings/gemini.py +39 -0
- powermem/integrations/embeddings/huggingface.py +41 -0
- powermem/integrations/embeddings/langchain.py +35 -0
- powermem/integrations/embeddings/lmstudio.py +29 -0
- powermem/integrations/embeddings/mock.py +11 -0
- powermem/integrations/embeddings/ollama.py +53 -0
- powermem/integrations/embeddings/openai.py +49 -0
- powermem/integrations/embeddings/qwen.py +102 -0
- powermem/integrations/embeddings/together.py +31 -0
- powermem/integrations/embeddings/vertexai.py +54 -0
- powermem/integrations/llm/__init__.py +18 -0
- powermem/integrations/llm/anthropic.py +87 -0
- powermem/integrations/llm/base.py +132 -0
- powermem/integrations/llm/config/anthropic.py +56 -0
- powermem/integrations/llm/config/azure.py +56 -0
- powermem/integrations/llm/config/base.py +62 -0
- powermem/integrations/llm/config/deepseek.py +56 -0
- powermem/integrations/llm/config/ollama.py +56 -0
- powermem/integrations/llm/config/openai.py +79 -0
- powermem/integrations/llm/config/qwen.py +68 -0
- powermem/integrations/llm/config/qwen_asr.py +46 -0
- powermem/integrations/llm/config/vllm.py +56 -0
- powermem/integrations/llm/configs.py +26 -0
- powermem/integrations/llm/deepseek.py +106 -0
- powermem/integrations/llm/factory.py +118 -0
- powermem/integrations/llm/gemini.py +201 -0
- powermem/integrations/llm/langchain.py +65 -0
- powermem/integrations/llm/ollama.py +106 -0
- powermem/integrations/llm/openai.py +166 -0
- powermem/integrations/llm/openai_structured.py +80 -0
- powermem/integrations/llm/qwen.py +207 -0
- powermem/integrations/llm/qwen_asr.py +171 -0
- powermem/integrations/llm/vllm.py +106 -0
- powermem/integrations/rerank/__init__.py +20 -0
- powermem/integrations/rerank/base.py +43 -0
- powermem/integrations/rerank/config/__init__.py +7 -0
- powermem/integrations/rerank/config/base.py +27 -0
- powermem/integrations/rerank/configs.py +23 -0
- powermem/integrations/rerank/factory.py +68 -0
- powermem/integrations/rerank/qwen.py +159 -0
- powermem/intelligence/__init__.py +17 -0
- powermem/intelligence/ebbinghaus_algorithm.py +354 -0
- powermem/intelligence/importance_evaluator.py +361 -0
- powermem/intelligence/intelligent_memory_manager.py +284 -0
- powermem/intelligence/manager.py +148 -0
- powermem/intelligence/plugin.py +229 -0
- powermem/prompts/__init__.py +29 -0
- powermem/prompts/graph/graph_prompts.py +217 -0
- powermem/prompts/graph/graph_tools_prompts.py +469 -0
- powermem/prompts/importance_evaluation.py +246 -0
- powermem/prompts/intelligent_memory_prompts.py +163 -0
- powermem/prompts/templates.py +193 -0
- powermem/storage/__init__.py +14 -0
- powermem/storage/adapter.py +896 -0
- powermem/storage/base.py +109 -0
- powermem/storage/config/base.py +13 -0
- powermem/storage/config/oceanbase.py +58 -0
- powermem/storage/config/pgvector.py +52 -0
- powermem/storage/config/sqlite.py +27 -0
- powermem/storage/configs.py +159 -0
- powermem/storage/factory.py +59 -0
- powermem/storage/migration_manager.py +438 -0
- powermem/storage/oceanbase/__init__.py +8 -0
- powermem/storage/oceanbase/constants.py +162 -0
- powermem/storage/oceanbase/oceanbase.py +1384 -0
- powermem/storage/oceanbase/oceanbase_graph.py +1441 -0
- powermem/storage/pgvector/__init__.py +7 -0
- powermem/storage/pgvector/pgvector.py +420 -0
- powermem/storage/sqlite/__init__.py +0 -0
- powermem/storage/sqlite/sqlite.py +218 -0
- powermem/storage/sqlite/sqlite_vector_store.py +311 -0
- powermem/utils/__init__.py +35 -0
- powermem/utils/utils.py +605 -0
- powermem/version.py +23 -0
- powermem-0.1.0.dist-info/METADATA +187 -0
- powermem-0.1.0.dist-info/RECORD +123 -0
- powermem-0.1.0.dist-info/WHEEL +5 -0
- powermem-0.1.0.dist-info/licenses/LICENSE +206 -0
- powermem-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Multi-Agent Permission Controller
|
|
3
|
+
|
|
4
|
+
Manages access permissions for memories across different agents and scopes.
|
|
5
|
+
Implements role-based and attribute-based access control. Migrated and
|
|
6
|
+
refactored from the original multi_agent implementation.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from typing import Any, Dict, List, Optional, Set
|
|
12
|
+
|
|
13
|
+
from typing import Any, Dict
|
|
14
|
+
from powermem.agent.types import AccessPermission
|
|
15
|
+
from powermem.agent.abstract.permission import AgentPermissionManagerBase
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class PermissionController(AgentPermissionManagerBase):
|
|
21
|
+
"""
|
|
22
|
+
Multi-agent permission controller implementation.
|
|
23
|
+
|
|
24
|
+
Manages access permissions for memories, implements RBAC and ABAC,
|
|
25
|
+
handles permission grants, revocations, and audits.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, config: Dict[str, Any]):
|
|
29
|
+
"""
|
|
30
|
+
Initialize the permission controller.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
config: Memory configuration object
|
|
34
|
+
"""
|
|
35
|
+
super().__init__(config.agent_memory.multi_agent_config.__dict__)
|
|
36
|
+
self.config = config
|
|
37
|
+
self.multi_agent_config = config.agent_memory.multi_agent_config
|
|
38
|
+
|
|
39
|
+
# Permission storage: memory_id -> agent_id -> permissions
|
|
40
|
+
self.memory_permissions: Dict[str, Dict[str, Set[AccessPermission]]] = {}
|
|
41
|
+
|
|
42
|
+
# Role-based permissions
|
|
43
|
+
self.role_permissions = self.multi_agent_config.default_permissions
|
|
44
|
+
|
|
45
|
+
# Access audit log
|
|
46
|
+
self.access_log: List[Dict[str, Any]] = []
|
|
47
|
+
|
|
48
|
+
# Agent roles mapping
|
|
49
|
+
self.agent_roles: Dict[str, List[str]] = {}
|
|
50
|
+
|
|
51
|
+
# Initialize default permissions
|
|
52
|
+
self._initialize_default_permissions()
|
|
53
|
+
|
|
54
|
+
def initialize(self) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Initialize the permission controller.
|
|
57
|
+
"""
|
|
58
|
+
try:
|
|
59
|
+
# Initialize default permissions
|
|
60
|
+
self._initialize_default_permissions()
|
|
61
|
+
|
|
62
|
+
# Initialize agent roles
|
|
63
|
+
self._initialize_agent_roles()
|
|
64
|
+
|
|
65
|
+
self.initialized = True
|
|
66
|
+
logger.info("Permission controller initialized successfully")
|
|
67
|
+
|
|
68
|
+
except Exception as e:
|
|
69
|
+
logger.error(f"Failed to initialize permission controller: {e}")
|
|
70
|
+
raise
|
|
71
|
+
|
|
72
|
+
def _initialize_default_permissions(self) -> None:
|
|
73
|
+
"""Initialize default permissions from configuration."""
|
|
74
|
+
# Default permissions are already loaded from config
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
def _initialize_agent_roles(self) -> None:
|
|
78
|
+
"""Initialize agent roles from configuration."""
|
|
79
|
+
# Initialize agent roles from config if available
|
|
80
|
+
if hasattr(self.multi_agent_config, 'agent_groups'):
|
|
81
|
+
for group_name, group_config in self.multi_agent_config.agent_groups.items():
|
|
82
|
+
for agent_id in group_config.get('members', []):
|
|
83
|
+
if agent_id not in self.agent_roles:
|
|
84
|
+
self.agent_roles[agent_id] = []
|
|
85
|
+
self.agent_roles[agent_id].append(group_name)
|
|
86
|
+
|
|
87
|
+
def check_permission(
|
|
88
|
+
self,
|
|
89
|
+
agent_id: str,
|
|
90
|
+
memory_id: str,
|
|
91
|
+
permission: AccessPermission
|
|
92
|
+
) -> bool:
|
|
93
|
+
"""
|
|
94
|
+
Check if an agent has a specific permission for a memory.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
agent_id: ID of the agent
|
|
98
|
+
memory_id: ID of the memory
|
|
99
|
+
permission: Permission to check
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
True if the agent has the permission, False otherwise
|
|
103
|
+
"""
|
|
104
|
+
try:
|
|
105
|
+
# Check direct memory permissions
|
|
106
|
+
if memory_id in self.memory_permissions:
|
|
107
|
+
if agent_id in self.memory_permissions[memory_id]:
|
|
108
|
+
if permission in self.memory_permissions[memory_id][agent_id]:
|
|
109
|
+
self._log_access(agent_id, memory_id, permission, "granted")
|
|
110
|
+
return True
|
|
111
|
+
|
|
112
|
+
# Check role-based permissions
|
|
113
|
+
if agent_id in self.agent_roles:
|
|
114
|
+
for role in self.agent_roles[agent_id]:
|
|
115
|
+
if role in self.role_permissions:
|
|
116
|
+
if permission in self.role_permissions[role]:
|
|
117
|
+
self._log_access(agent_id, memory_id, permission, "granted_by_role")
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
# Check default permissions
|
|
121
|
+
if "public" in self.role_permissions:
|
|
122
|
+
if permission in self.role_permissions["public"]:
|
|
123
|
+
self._log_access(agent_id, memory_id, permission, "granted_by_default")
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
# Log detailed debug information
|
|
127
|
+
logger.debug(f"Permission check failed for agent {agent_id} on memory {memory_id}: "
|
|
128
|
+
f"memory_permissions={memory_id in self.memory_permissions}, "
|
|
129
|
+
f"agent_roles={agent_id in self.agent_roles}, "
|
|
130
|
+
f"permission={permission.value}")
|
|
131
|
+
|
|
132
|
+
self._log_access(agent_id, memory_id, permission, "denied")
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
except Exception as e:
|
|
136
|
+
logger.error(f"Error checking permission: {e}")
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
def grant_permission(
|
|
140
|
+
self,
|
|
141
|
+
memory_id: str,
|
|
142
|
+
agent_id: str,
|
|
143
|
+
permission: AccessPermission,
|
|
144
|
+
granted_by: str
|
|
145
|
+
) -> Dict[str, Any]:
|
|
146
|
+
"""
|
|
147
|
+
Grant a permission to an agent for a memory.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
memory_id: ID of the memory
|
|
151
|
+
agent_id: ID of the agent to grant permission to
|
|
152
|
+
permission: Permission to grant
|
|
153
|
+
granted_by: ID of the agent granting the permission
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
Dictionary containing the grant result
|
|
157
|
+
"""
|
|
158
|
+
try:
|
|
159
|
+
# Initialize memory permissions if not exists
|
|
160
|
+
if memory_id not in self.memory_permissions:
|
|
161
|
+
self.memory_permissions[memory_id] = {}
|
|
162
|
+
|
|
163
|
+
if agent_id not in self.memory_permissions[memory_id]:
|
|
164
|
+
self.memory_permissions[memory_id][agent_id] = set()
|
|
165
|
+
|
|
166
|
+
# Grant permission
|
|
167
|
+
self.memory_permissions[memory_id][agent_id].add(permission)
|
|
168
|
+
|
|
169
|
+
# Log the grant
|
|
170
|
+
self._log_permission_change(
|
|
171
|
+
memory_id, agent_id, permission, "granted", granted_by
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
logger.info(f"Granted {permission.value} permission to {agent_id} for memory {memory_id}")
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
'success': True,
|
|
178
|
+
'memory_id': memory_id,
|
|
179
|
+
'agent_id': agent_id,
|
|
180
|
+
'permission': permission.value,
|
|
181
|
+
'granted_by': granted_by,
|
|
182
|
+
'granted_at': datetime.now().isoformat(),
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
except Exception as e:
|
|
186
|
+
logger.error(f"Error granting permission: {e}")
|
|
187
|
+
return {
|
|
188
|
+
'success': False,
|
|
189
|
+
'error': str(e),
|
|
190
|
+
'memory_id': memory_id,
|
|
191
|
+
'agent_id': agent_id,
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
def revoke_permission(
|
|
195
|
+
self,
|
|
196
|
+
memory_id: str,
|
|
197
|
+
agent_id: str,
|
|
198
|
+
permission: AccessPermission,
|
|
199
|
+
revoked_by: str
|
|
200
|
+
) -> Dict[str, Any]:
|
|
201
|
+
"""
|
|
202
|
+
Revoke a permission from an agent for a memory.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
memory_id: ID of the memory
|
|
206
|
+
agent_id: ID of the agent to revoke permission from
|
|
207
|
+
permission: Permission to revoke
|
|
208
|
+
revoked_by: ID of the agent revoking the permission
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Dictionary containing the revoke result
|
|
212
|
+
"""
|
|
213
|
+
try:
|
|
214
|
+
# Check if memory and agent exist
|
|
215
|
+
if memory_id not in self.memory_permissions:
|
|
216
|
+
return {
|
|
217
|
+
'success': False,
|
|
218
|
+
'error': 'Memory not found',
|
|
219
|
+
'memory_id': memory_id,
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if agent_id not in self.memory_permissions[memory_id]:
|
|
223
|
+
return {
|
|
224
|
+
'success': False,
|
|
225
|
+
'error': 'Agent not found for this memory',
|
|
226
|
+
'memory_id': memory_id,
|
|
227
|
+
'agent_id': agent_id,
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
# Revoke permission
|
|
231
|
+
if permission in self.memory_permissions[memory_id][agent_id]:
|
|
232
|
+
self.memory_permissions[memory_id][agent_id].remove(permission)
|
|
233
|
+
|
|
234
|
+
# Log the revocation
|
|
235
|
+
self._log_permission_change(
|
|
236
|
+
memory_id, agent_id, permission, "revoked", revoked_by
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
logger.info(f"Revoked {permission.value} permission from {agent_id} for memory {memory_id}")
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
'success': True,
|
|
243
|
+
'memory_id': memory_id,
|
|
244
|
+
'agent_id': agent_id,
|
|
245
|
+
'permission': permission.value,
|
|
246
|
+
'revoked_by': revoked_by,
|
|
247
|
+
'revoked_at': datetime.now().isoformat(),
|
|
248
|
+
}
|
|
249
|
+
else:
|
|
250
|
+
return {
|
|
251
|
+
'success': False,
|
|
252
|
+
'error': 'Permission not found',
|
|
253
|
+
'memory_id': memory_id,
|
|
254
|
+
'agent_id': agent_id,
|
|
255
|
+
'permission': permission.value,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
except Exception as e:
|
|
259
|
+
logger.error(f"Error revoking permission: {e}")
|
|
260
|
+
return {
|
|
261
|
+
'success': False,
|
|
262
|
+
'error': str(e),
|
|
263
|
+
'memory_id': memory_id,
|
|
264
|
+
'agent_id': agent_id,
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
def get_permissions(
|
|
268
|
+
self,
|
|
269
|
+
memory_id: str,
|
|
270
|
+
agent_id: Optional[str] = None
|
|
271
|
+
) -> Dict[str, Any]:
|
|
272
|
+
"""
|
|
273
|
+
Get permissions for a memory or agent.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
memory_id: ID of the memory
|
|
277
|
+
agent_id: Optional ID of the agent
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Dictionary containing permission information
|
|
281
|
+
"""
|
|
282
|
+
try:
|
|
283
|
+
if agent_id:
|
|
284
|
+
# Get permissions for specific agent
|
|
285
|
+
if memory_id in self.memory_permissions:
|
|
286
|
+
if agent_id in self.memory_permissions[memory_id]:
|
|
287
|
+
permissions = [
|
|
288
|
+
perm.value for perm in self.memory_permissions[memory_id][agent_id]
|
|
289
|
+
]
|
|
290
|
+
return {
|
|
291
|
+
'memory_id': memory_id,
|
|
292
|
+
'agent_id': agent_id,
|
|
293
|
+
'permissions': permissions,
|
|
294
|
+
'permission_count': len(permissions),
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
'memory_id': memory_id,
|
|
299
|
+
'agent_id': agent_id,
|
|
300
|
+
'permissions': [],
|
|
301
|
+
'permission_count': 0,
|
|
302
|
+
}
|
|
303
|
+
else:
|
|
304
|
+
# Get all permissions for memory
|
|
305
|
+
if memory_id in self.memory_permissions:
|
|
306
|
+
all_permissions = {}
|
|
307
|
+
for agent_id, permissions in self.memory_permissions[memory_id].items():
|
|
308
|
+
all_permissions[agent_id] = [perm.value for perm in permissions]
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
'memory_id': memory_id,
|
|
312
|
+
'all_permissions': all_permissions,
|
|
313
|
+
'agent_count': len(all_permissions),
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return {
|
|
317
|
+
'memory_id': memory_id,
|
|
318
|
+
'all_permissions': {},
|
|
319
|
+
'agent_count': 0,
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
except Exception as e:
|
|
323
|
+
logger.error(f"Error getting permissions: {e}")
|
|
324
|
+
return {
|
|
325
|
+
'memory_id': memory_id,
|
|
326
|
+
'error': str(e),
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
def set_default_permissions(
|
|
330
|
+
self,
|
|
331
|
+
memory_id: str,
|
|
332
|
+
permissions: Dict[str, List[AccessPermission]],
|
|
333
|
+
set_by: str
|
|
334
|
+
) -> Dict[str, Any]:
|
|
335
|
+
"""
|
|
336
|
+
Set default permissions for a memory.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
memory_id: ID of the memory
|
|
340
|
+
permissions: Dictionary of default permissions
|
|
341
|
+
set_by: ID of the agent setting the permissions
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
Dictionary containing the set result
|
|
345
|
+
"""
|
|
346
|
+
try:
|
|
347
|
+
# Initialize memory permissions if not exists
|
|
348
|
+
if memory_id not in self.memory_permissions:
|
|
349
|
+
self.memory_permissions[memory_id] = {}
|
|
350
|
+
|
|
351
|
+
# Set default permissions
|
|
352
|
+
for agent_id, agent_permissions in permissions.items():
|
|
353
|
+
if agent_id not in self.memory_permissions[memory_id]:
|
|
354
|
+
self.memory_permissions[memory_id][agent_id] = set()
|
|
355
|
+
|
|
356
|
+
self.memory_permissions[memory_id][agent_id].update(agent_permissions)
|
|
357
|
+
|
|
358
|
+
# Log the setting
|
|
359
|
+
self._log_permission_change(
|
|
360
|
+
memory_id, "default", AccessPermission.ADMIN, "set_default", set_by
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
logger.info(f"Set default permissions for memory {memory_id}")
|
|
364
|
+
|
|
365
|
+
return {
|
|
366
|
+
'success': True,
|
|
367
|
+
'memory_id': memory_id,
|
|
368
|
+
'permissions': {k: [p.value for p in v] for k, v in permissions.items()},
|
|
369
|
+
'set_by': set_by,
|
|
370
|
+
'set_at': datetime.now().isoformat(),
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
except Exception as e:
|
|
374
|
+
logger.error(f"Error setting default permissions: {e}")
|
|
375
|
+
return {
|
|
376
|
+
'success': False,
|
|
377
|
+
'error': str(e),
|
|
378
|
+
'memory_id': memory_id,
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
def inherit_permissions(
|
|
382
|
+
self,
|
|
383
|
+
target_memory_id: str,
|
|
384
|
+
source_memory_id: str,
|
|
385
|
+
inherited_by: str
|
|
386
|
+
) -> Dict[str, Any]:
|
|
387
|
+
"""
|
|
388
|
+
Inherit permissions from another memory.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
target_memory_id: ID of the target memory
|
|
392
|
+
source_memory_id: ID of the source memory
|
|
393
|
+
inherited_by: ID of the agent inheriting the permissions
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
Dictionary containing the inherit result
|
|
397
|
+
"""
|
|
398
|
+
try:
|
|
399
|
+
# Check if source memory exists
|
|
400
|
+
if source_memory_id not in self.memory_permissions:
|
|
401
|
+
return {
|
|
402
|
+
'success': False,
|
|
403
|
+
'error': 'Source memory not found',
|
|
404
|
+
'source_memory_id': source_memory_id,
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
# Initialize target memory permissions if not exists
|
|
408
|
+
if target_memory_id not in self.memory_permissions:
|
|
409
|
+
self.memory_permissions[target_memory_id] = {}
|
|
410
|
+
|
|
411
|
+
# Copy permissions from source to target
|
|
412
|
+
source_permissions = self.memory_permissions[source_memory_id]
|
|
413
|
+
inherited_count = 0
|
|
414
|
+
|
|
415
|
+
for agent_id, permissions in source_permissions.items():
|
|
416
|
+
if agent_id not in self.memory_permissions[target_memory_id]:
|
|
417
|
+
self.memory_permissions[target_memory_id][agent_id] = set()
|
|
418
|
+
|
|
419
|
+
self.memory_permissions[target_memory_id][agent_id].update(permissions)
|
|
420
|
+
inherited_count += len(permissions)
|
|
421
|
+
|
|
422
|
+
# Log the inheritance
|
|
423
|
+
self._log_permission_change(
|
|
424
|
+
target_memory_id, "inherited", AccessPermission.ADMIN, "inherited", inherited_by
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
logger.info(f"Inherited permissions from {source_memory_id} to {target_memory_id}")
|
|
428
|
+
|
|
429
|
+
return {
|
|
430
|
+
'success': True,
|
|
431
|
+
'target_memory_id': target_memory_id,
|
|
432
|
+
'source_memory_id': source_memory_id,
|
|
433
|
+
'inherited_count': inherited_count,
|
|
434
|
+
'inherited_by': inherited_by,
|
|
435
|
+
'inherited_at': datetime.now().isoformat(),
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
except Exception as e:
|
|
439
|
+
logger.error(f"Error inheriting permissions: {e}")
|
|
440
|
+
return {
|
|
441
|
+
'success': False,
|
|
442
|
+
'error': str(e),
|
|
443
|
+
'target_memory_id': target_memory_id,
|
|
444
|
+
'source_memory_id': source_memory_id,
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
def get_permission_history(
|
|
448
|
+
self,
|
|
449
|
+
memory_id: str,
|
|
450
|
+
agent_id: Optional[str] = None,
|
|
451
|
+
limit: Optional[int] = None
|
|
452
|
+
) -> List[Dict[str, Any]]:
|
|
453
|
+
"""
|
|
454
|
+
Get permission history for a memory or agent.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
memory_id: ID of the memory
|
|
458
|
+
agent_id: Optional ID of the agent
|
|
459
|
+
limit: Optional limit on number of entries
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
List of permission history entries
|
|
463
|
+
"""
|
|
464
|
+
try:
|
|
465
|
+
history = []
|
|
466
|
+
|
|
467
|
+
# Filter access log for this memory and agent
|
|
468
|
+
for entry in self.access_log:
|
|
469
|
+
if entry.get('memory_id') == memory_id:
|
|
470
|
+
if agent_id is None or entry.get('agent_id') == agent_id:
|
|
471
|
+
history.append(entry)
|
|
472
|
+
|
|
473
|
+
# Sort by timestamp (newest first)
|
|
474
|
+
history.sort(key=lambda x: x.get('timestamp', ''), reverse=True)
|
|
475
|
+
|
|
476
|
+
# Apply limit if specified
|
|
477
|
+
if limit:
|
|
478
|
+
history = history[:limit]
|
|
479
|
+
|
|
480
|
+
return history
|
|
481
|
+
|
|
482
|
+
except Exception as e:
|
|
483
|
+
logger.error(f"Error getting permission history: {e}")
|
|
484
|
+
return []
|
|
485
|
+
|
|
486
|
+
def validate_permission_chain(
|
|
487
|
+
self,
|
|
488
|
+
agent_id: str,
|
|
489
|
+
memory_id: str,
|
|
490
|
+
permission: AccessPermission
|
|
491
|
+
) -> Dict[str, Any]:
|
|
492
|
+
"""
|
|
493
|
+
Validate the permission chain for an agent and memory.
|
|
494
|
+
|
|
495
|
+
Args:
|
|
496
|
+
agent_id: ID of the agent
|
|
497
|
+
memory_id: ID of the memory
|
|
498
|
+
permission: Permission to validate
|
|
499
|
+
|
|
500
|
+
Returns:
|
|
501
|
+
Dictionary containing validation results
|
|
502
|
+
"""
|
|
503
|
+
try:
|
|
504
|
+
validation_result = {
|
|
505
|
+
'valid': False,
|
|
506
|
+
'agent_id': agent_id,
|
|
507
|
+
'memory_id': memory_id,
|
|
508
|
+
'permission': permission.value,
|
|
509
|
+
'validation_path': [],
|
|
510
|
+
'errors': [],
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
# Check direct memory permissions
|
|
514
|
+
if memory_id in self.memory_permissions:
|
|
515
|
+
if agent_id in self.memory_permissions[memory_id]:
|
|
516
|
+
if permission in self.memory_permissions[memory_id][agent_id]:
|
|
517
|
+
validation_result['valid'] = True
|
|
518
|
+
validation_result['validation_path'].append('direct_memory_permission')
|
|
519
|
+
return validation_result
|
|
520
|
+
|
|
521
|
+
# Check role-based permissions
|
|
522
|
+
if agent_id in self.agent_roles:
|
|
523
|
+
for role in self.agent_roles[agent_id]:
|
|
524
|
+
if role in self.role_permissions:
|
|
525
|
+
if permission in self.role_permissions[role]:
|
|
526
|
+
validation_result['valid'] = True
|
|
527
|
+
validation_result['validation_path'].append(f'role_permission:{role}')
|
|
528
|
+
return validation_result
|
|
529
|
+
|
|
530
|
+
# Check default permissions
|
|
531
|
+
if "public" in self.role_permissions:
|
|
532
|
+
if permission in self.role_permissions["public"]:
|
|
533
|
+
validation_result['valid'] = True
|
|
534
|
+
validation_result['validation_path'].append('default_permission')
|
|
535
|
+
return validation_result
|
|
536
|
+
|
|
537
|
+
validation_result['errors'].append('No valid permission path found')
|
|
538
|
+
return validation_result
|
|
539
|
+
|
|
540
|
+
except Exception as e:
|
|
541
|
+
logger.error(f"Error validating permission chain: {e}")
|
|
542
|
+
return {
|
|
543
|
+
'valid': False,
|
|
544
|
+
'error': str(e),
|
|
545
|
+
'agent_id': agent_id,
|
|
546
|
+
'memory_id': memory_id,
|
|
547
|
+
'permission': permission.value,
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
def _log_access(
|
|
551
|
+
self,
|
|
552
|
+
agent_id: str,
|
|
553
|
+
memory_id: str,
|
|
554
|
+
permission: AccessPermission,
|
|
555
|
+
result: str
|
|
556
|
+
) -> None:
|
|
557
|
+
"""Log access attempt."""
|
|
558
|
+
log_entry = {
|
|
559
|
+
'timestamp': datetime.now().isoformat(),
|
|
560
|
+
'agent_id': agent_id,
|
|
561
|
+
'memory_id': memory_id,
|
|
562
|
+
'permission': permission.value,
|
|
563
|
+
'result': result,
|
|
564
|
+
'type': 'access_check',
|
|
565
|
+
}
|
|
566
|
+
self.access_log.append(log_entry)
|
|
567
|
+
|
|
568
|
+
def _log_permission_change(
|
|
569
|
+
self,
|
|
570
|
+
memory_id: str,
|
|
571
|
+
agent_id: str,
|
|
572
|
+
permission: AccessPermission,
|
|
573
|
+
action: str,
|
|
574
|
+
performed_by: str
|
|
575
|
+
) -> None:
|
|
576
|
+
"""Log permission change."""
|
|
577
|
+
log_entry = {
|
|
578
|
+
'timestamp': datetime.now().isoformat(),
|
|
579
|
+
'memory_id': memory_id,
|
|
580
|
+
'agent_id': agent_id,
|
|
581
|
+
'permission': permission.value,
|
|
582
|
+
'action': action,
|
|
583
|
+
'performed_by': performed_by,
|
|
584
|
+
'type': 'permission_change',
|
|
585
|
+
}
|
|
586
|
+
self.access_log.append(log_entry)
|