Initial SFERA platform baseline

This commit is contained in:
2026-05-16 19:03:49 +03:00
commit 3b845c8fce
282 changed files with 55045 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
# sfera-security-core
Security primitives for SFERA.
Provides:
- RBAC roles, grants, permission checks, and effective permissions;
- default admin/developer/viewer policy;
- privacy modes and classifications;
- project/target scoped privacy markers;
- AI usage policy configuration.
+10
View File
@@ -0,0 +1,10 @@
[project]
name = "sfera-security-core"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"pydantic>=2.0",
]
[tool.uv]
package = true
@@ -0,0 +1,147 @@
from __future__ import annotations
from enum import Enum
from pydantic import BaseModel, Field
class Permission(str, Enum):
INDEX_PROJECT = "INDEX_PROJECT"
READ_GRAPH = "READ_GRAPH"
WRITE_KNOWLEDGE = "WRITE_KNOWLEDGE"
MANAGE_TASKS = "MANAGE_TASKS"
MANAGE_USERS = "MANAGE_USERS"
ADMIN = "ADMIN"
class PrivacyMode(str, Enum):
LOCAL_ONLY = "LOCAL_ONLY"
WORKSPACE_SHARED = "WORKSPACE_SHARED"
EXTERNAL_ALLOWED = "EXTERNAL_ALLOWED"
class PrivacyClassification(str, Enum):
PUBLIC = "PUBLIC"
INTERNAL = "INTERNAL"
CONFIDENTIAL = "CONFIDENTIAL"
PERSONAL_DATA = "PERSONAL_DATA"
SECRET = "SECRET"
class PrivacyMarker(BaseModel):
project_id: str
target_id: str
classification: PrivacyClassification
reason: str | None = None
attributes: dict = Field(default_factory=dict)
class Role(BaseModel):
role_id: str
name: str
permissions: set[Permission] = Field(default_factory=set)
class UserAccess(BaseModel):
user_id: str
roles: set[str] = Field(default_factory=set)
class RbacPolicy(BaseModel):
roles: dict[str, Role] = Field(default_factory=dict)
users: dict[str, UserAccess] = Field(default_factory=dict)
def grant_role(self, user_id: str, role_id: str) -> None:
if role_id not in self.roles:
raise KeyError(f"Unknown role: {role_id}")
access = self.users.setdefault(user_id, UserAccess(user_id=user_id))
access.roles.add(role_id)
def is_allowed(self, user_id: str, permission: Permission) -> bool:
access = self.users.get(user_id)
if access is None:
return False
for role_id in access.roles:
role = self.roles.get(role_id)
if role and (Permission.ADMIN in role.permissions or permission in role.permissions):
return True
return False
def effective_permissions(self, user_id: str) -> set[Permission]:
access = self.users.get(user_id)
if access is None:
return set()
permissions: set[Permission] = set()
for role_id in access.roles:
role = self.roles.get(role_id)
if role is None:
continue
if Permission.ADMIN in role.permissions:
return set(Permission)
permissions.update(role.permissions)
return permissions
class AiUsagePolicy(BaseModel):
privacy_mode: PrivacyMode = PrivacyMode.LOCAL_ONLY
allow_code_context: bool = False
allow_external_calls: bool = False
token_limit_per_day: int | None = None
class InMemoryPrivacyStore:
def __init__(self) -> None:
self.markers: dict[str, PrivacyMarker] = {}
def upsert_marker(self, marker: PrivacyMarker) -> PrivacyMarker:
self.markers[self._key(marker.project_id, marker.target_id)] = marker
return marker
def markers_for_project(self, project_id: str) -> list[PrivacyMarker]:
return sorted(
[
marker
for marker in self.markers.values()
if marker.project_id == project_id
],
key=lambda item: item.target_id,
)
def marker_for_target(self, project_id: str, target_id: str) -> PrivacyMarker | None:
return self.markers.get(self._key(project_id, target_id))
def _key(self, project_id: str, target_id: str) -> str:
return f"{project_id}:{target_id}"
def default_rbac_policy() -> RbacPolicy:
return RbacPolicy(
roles={
"admin": Role(role_id="admin", name="Administrator", permissions={Permission.ADMIN}),
"developer": Role(
role_id="developer",
name="Developer",
permissions={
Permission.INDEX_PROJECT,
Permission.READ_GRAPH,
Permission.WRITE_KNOWLEDGE,
Permission.MANAGE_TASKS,
},
),
"viewer": Role(role_id="viewer", name="Viewer", permissions={Permission.READ_GRAPH}),
}
)
__all__ = [
"AiUsagePolicy",
"InMemoryPrivacyStore",
"Permission",
"PrivacyClassification",
"PrivacyMarker",
"PrivacyMode",
"RbacPolicy",
"Role",
"UserAccess",
"default_rbac_policy",
]
@@ -0,0 +1,43 @@
from security_core import (
InMemoryPrivacyStore,
Permission,
PrivacyClassification,
PrivacyMarker,
default_rbac_policy,
)
def test_rbac_allows_permissions_from_granted_role():
policy = default_rbac_policy()
policy.grant_role("user.1", "developer")
assert policy.is_allowed("user.1", Permission.INDEX_PROJECT)
assert not policy.is_allowed("user.1", Permission.MANAGE_USERS)
assert policy.effective_permissions("user.1") == {
Permission.INDEX_PROJECT,
Permission.READ_GRAPH,
Permission.WRITE_KNOWLEDGE,
Permission.MANAGE_TASKS,
}
def test_admin_effective_permissions_expand_to_all_permissions():
policy = default_rbac_policy()
policy.grant_role("user.1", "admin")
assert policy.effective_permissions("user.1") == set(Permission)
def test_privacy_store_is_project_and_target_scoped():
store = InMemoryPrivacyStore()
marker = store.upsert_marker(
PrivacyMarker(
project_id="demo",
target_id="lineage.attribute.phone",
classification=PrivacyClassification.PERSONAL_DATA,
reason="Phone number",
)
)
assert store.markers_for_project("demo") == [marker]
assert store.marker_for_target("demo", "lineage.attribute.phone") == marker