Load 1C access profiles groups and users
This commit is contained in:
@@ -68,6 +68,30 @@ def import_quality_response(
|
||||
f"Найдено прав: {summary.rights_count}",
|
||||
summary.rights_count,
|
||||
),
|
||||
_quality_check(
|
||||
"access_profiles",
|
||||
"Access profiles",
|
||||
True,
|
||||
f"Найдено профилей групп доступа: {summary.access_profile_count}",
|
||||
summary.access_profile_count,
|
||||
severity="INFO",
|
||||
),
|
||||
_quality_check(
|
||||
"access_groups",
|
||||
"Access groups",
|
||||
True,
|
||||
f"Найдено групп доступа: {summary.access_group_count}",
|
||||
summary.access_group_count,
|
||||
severity="INFO",
|
||||
),
|
||||
_quality_check(
|
||||
"access_users",
|
||||
"Access users",
|
||||
True,
|
||||
f"Найдено пользователей ИБ: {summary.access_user_count}",
|
||||
summary.access_user_count,
|
||||
severity="INFO",
|
||||
),
|
||||
_quality_check(
|
||||
"extensions",
|
||||
"Extensions",
|
||||
|
||||
@@ -2889,6 +2889,34 @@ async def get_normalized_project_summary(project_id: str) -> NormalizedProjectSu
|
||||
return _normalized_project_summary(normalized)
|
||||
|
||||
|
||||
@app.get("/projects/{project_id}/access")
|
||||
async def get_project_access_model(project_id: str) -> dict:
|
||||
normalized = _load_normalized_project(project_id)
|
||||
if normalized is None:
|
||||
raise HTTPException(status_code=404, detail="NormalizedProject not found")
|
||||
return normalized.access.model_dump(mode="json")
|
||||
|
||||
|
||||
@app.get("/projects/{project_id}/access/users/{user_name}")
|
||||
async def get_project_access_user(project_id: str, user_name: str) -> dict:
|
||||
normalized = _load_normalized_project(project_id)
|
||||
if normalized is None:
|
||||
raise HTTPException(status_code=404, detail="NormalizedProject not found")
|
||||
wanted = user_name.casefold()
|
||||
user = next(
|
||||
(
|
||||
item
|
||||
for item in normalized.access.users
|
||||
if item.name.casefold() == wanted or str(item.qualified_name or "").casefold() == wanted
|
||||
),
|
||||
None,
|
||||
)
|
||||
if user is None:
|
||||
raise HTTPException(status_code=404, detail="Access user not found")
|
||||
effective_roles = _effective_access_roles(normalized, user)
|
||||
return {"user": user.model_dump(mode="json"), "effective_roles": effective_roles}
|
||||
|
||||
|
||||
@app.get("/projects/{project_id}/imports/quality", response_model=ImportQualityResponse)
|
||||
async def get_import_quality(project_id: str) -> ImportQualityResponse:
|
||||
return _import_quality_response(project_id)
|
||||
@@ -3985,6 +4013,9 @@ _FLOWCHART_KIND_LABELS = {
|
||||
NodeKind.STYLE_ITEM: "Элементы стиля",
|
||||
NodeKind.STYLE: "Стили",
|
||||
NodeKind.LANGUAGE: "Языки",
|
||||
NodeKind.ACCESS_PROFILE: "Профили групп доступа",
|
||||
NodeKind.ACCESS_GROUP: "Группы доступа",
|
||||
NodeKind.ACCESS_USER: "Пользователи ИБ",
|
||||
NodeKind.HTTP_SERVICE: "HTTP-сервисы",
|
||||
NodeKind.INTEGRATION_ENDPOINT: "Интеграции",
|
||||
NodeKind.SCHEDULED_JOB: "Регламентные задания",
|
||||
@@ -4008,6 +4039,8 @@ _FLOWCHART_EDGE_LABELS = {
|
||||
EdgeKind.RUNS: "запускает",
|
||||
EdgeKind.USES_INTEGRATION: "интеграция",
|
||||
EdgeKind.HANDLES: "обработчик",
|
||||
EdgeKind.ASSIGNS_ROLE: "назначает роль",
|
||||
EdgeKind.MEMBER_OF: "участник",
|
||||
}
|
||||
|
||||
_FLOWCHART_IMPORTANT_EDGES = {
|
||||
@@ -4017,6 +4050,8 @@ _FLOWCHART_IMPORTANT_EDGES = {
|
||||
EdgeKind.WRITES,
|
||||
EdgeKind.HAS_ROLE,
|
||||
EdgeKind.GRANTS_ACCESS,
|
||||
EdgeKind.ASSIGNS_ROLE,
|
||||
EdgeKind.MEMBER_OF,
|
||||
EdgeKind.RUNS,
|
||||
EdgeKind.USES_INTEGRATION,
|
||||
EdgeKind.HANDLES,
|
||||
@@ -4045,6 +4080,9 @@ _FLOWCHART_LOGIC_NODE_KINDS = {
|
||||
NodeKind.XDTO_PACKAGE,
|
||||
NodeKind.EXTENSION,
|
||||
NodeKind.INTEGRATION_ENDPOINT,
|
||||
NodeKind.ACCESS_PROFILE,
|
||||
NodeKind.ACCESS_GROUP,
|
||||
NodeKind.ACCESS_USER,
|
||||
NodeKind.ROLE,
|
||||
}
|
||||
|
||||
@@ -7210,6 +7248,39 @@ def _load_normalized_project(project_id: str) -> NormalizedProject | None:
|
||||
return normalized
|
||||
|
||||
|
||||
def _effective_access_roles(normalized: NormalizedProject, user) -> list[dict]:
|
||||
roles = [item.model_dump(mode="json") for item in user.roles]
|
||||
user_groups = {group.casefold() for group in user.groups}
|
||||
groups = [
|
||||
group
|
||||
for group in normalized.access.groups
|
||||
if group.name.casefold() in user_groups
|
||||
or str(group.qualified_name or "").casefold() in user_groups
|
||||
or user.name in group.users
|
||||
]
|
||||
profile_names = {
|
||||
value.casefold()
|
||||
for group in groups
|
||||
for value in (group.profile, group.profile_qualified_name)
|
||||
if value
|
||||
}
|
||||
profiles = [
|
||||
profile
|
||||
for profile in normalized.access.profiles
|
||||
if profile.name.casefold() in profile_names or str(profile.qualified_name or "").casefold() in profile_names
|
||||
]
|
||||
for group in groups:
|
||||
roles.extend(item.model_dump(mode="json") for item in group.roles)
|
||||
for profile in profiles:
|
||||
roles.extend(item.model_dump(mode="json") for item in profile.roles)
|
||||
unique: dict[str, dict] = {}
|
||||
for role in roles:
|
||||
key = str(role.get("role_qualified_name") or role.get("role") or "").casefold()
|
||||
if key:
|
||||
unique.setdefault(key, role)
|
||||
return sorted(unique.values(), key=lambda item: str(item.get("role") or "").casefold())
|
||||
|
||||
|
||||
def _import_quality_response(project_id: str) -> ImportQualityResponse:
|
||||
normalized = _load_normalized_project(project_id)
|
||||
summary = _normalized_project_summary(normalized) if normalized is not None else None
|
||||
|
||||
@@ -20,6 +20,10 @@ class NormalizedProjectSummary(BaseModel):
|
||||
command_count: int = 0
|
||||
role_count: int = 0
|
||||
rights_count: int = 0
|
||||
access_profile_count: int = 0
|
||||
access_group_count: int = 0
|
||||
access_user_count: int = 0
|
||||
access_assignment_count: int = 0
|
||||
module_count: int = 0
|
||||
layout_count: int = 0
|
||||
movement_count: int = 0
|
||||
|
||||
@@ -30,6 +30,13 @@ def normalized_project_summary(normalized: NormalizedProject) -> NormalizedProje
|
||||
command_count=sum(len(item.commands) for item in objects),
|
||||
role_count=sum(1 for item in objects if item.object_kind == "ROLE"),
|
||||
rights_count=sum(len(item.rights) for item in objects),
|
||||
access_profile_count=len(normalized.access.profiles),
|
||||
access_group_count=len(normalized.access.groups),
|
||||
access_user_count=len(normalized.access.users),
|
||||
access_assignment_count=sum(len(item.roles) for item in normalized.access.profiles)
|
||||
+ sum(len(item.roles) for item in normalized.access.groups)
|
||||
+ sum(len(item.roles) for item in normalized.access.users)
|
||||
+ sum(len(item.users) for item in normalized.access.groups),
|
||||
module_count=sum(len(item.modules) for item in objects),
|
||||
layout_count=sum(len(item.layouts) for item in objects),
|
||||
movement_count=sum(len(item.movements) for item in objects),
|
||||
|
||||
@@ -1476,6 +1476,13 @@ def test_import_supports_structure_only_indexing(tmp_path: Path):
|
||||
<Role name="Менеджер" qualifiedName="Роль.Менеджер">
|
||||
<Right object="HTTPСервис.ПубличныйAPI" read="true" />
|
||||
</Role>
|
||||
<AccessProfile name="ПрофильМенеджера">
|
||||
<Role name="Менеджер" />
|
||||
</AccessProfile>
|
||||
<AccessGroup name="Менеджеры" profile="ПрофильМенеджера">
|
||||
<Member user="ivanov" />
|
||||
</AccessGroup>
|
||||
<InfobaseUser name="ivanov" fullName="Иванов Иван" />
|
||||
</Configuration>
|
||||
""",
|
||||
encoding="utf-8",
|
||||
@@ -1495,6 +1502,9 @@ def test_import_supports_structure_only_indexing(tmp_path: Path):
|
||||
assert payload["object_count"] >= 2
|
||||
assert payload["normalized_summary"]["group_count"] >= 5
|
||||
assert payload["normalized_summary"]["rights_count"] == 1
|
||||
assert payload["normalized_summary"]["access_profile_count"] == 1
|
||||
assert payload["normalized_summary"]["access_group_count"] == 1
|
||||
assert payload["normalized_summary"]["access_user_count"] == 1
|
||||
|
||||
setup = client.get(f"/projects/{project_id}/setup")
|
||||
assert setup.status_code == 200
|
||||
@@ -1525,6 +1535,14 @@ def test_import_supports_structure_only_indexing(tmp_path: Path):
|
||||
assert detail.json()["group_name"] == "Роли"
|
||||
assert detail.json()["object"]["rights"][0]["target"] == "HTTPСервис.ПубличныйAPI"
|
||||
|
||||
access = client.get(f"/projects/{project_id}/access")
|
||||
assert access.status_code == 200
|
||||
assert access.json()["profiles"][0]["roles"][0]["role_qualified_name"] == "Роль.Менеджер"
|
||||
|
||||
access_user = client.get(f"/projects/{project_id}/access/users/ivanov")
|
||||
assert access_user.status_code == 200
|
||||
assert access_user.json()["effective_roles"][0]["role_qualified_name"] == "Роль.Менеджер"
|
||||
|
||||
tree = client.get(f"/projects/{project_id}/metadata/tree")
|
||||
assert tree.status_code == 200
|
||||
root = tree.json()["root"]
|
||||
|
||||
@@ -245,6 +245,11 @@ def _request_fingerprint(request: RuntimeImportRequest) -> str:
|
||||
|
||||
def _mock_project(project_id: str | None) -> NormalizedProject:
|
||||
from one_c_normalizer import (
|
||||
AccessGroup,
|
||||
AccessModel,
|
||||
AccessProfile,
|
||||
AccessRoleAssignment,
|
||||
AccessUser,
|
||||
Command,
|
||||
ConfigurationRoot,
|
||||
Extension,
|
||||
@@ -258,6 +263,31 @@ def _mock_project(project_id: str | None) -> NormalizedProject:
|
||||
return NormalizedProject(
|
||||
project_id=project_id,
|
||||
source_path="mock://runtime-adapter",
|
||||
access=AccessModel(
|
||||
profiles=[
|
||||
AccessProfile(
|
||||
name="МенеджерПродаж",
|
||||
qualified_name="ПрофильГруппыДоступа.МенеджерПродаж",
|
||||
roles=[AccessRoleAssignment(role="Менеджер", role_qualified_name="Роль.Менеджер")],
|
||||
)
|
||||
],
|
||||
groups=[
|
||||
AccessGroup(
|
||||
name="ОтделПродаж",
|
||||
qualified_name="ГруппаДоступа.ОтделПродаж",
|
||||
profile="МенеджерПродаж",
|
||||
users=["demo.user"],
|
||||
)
|
||||
],
|
||||
users=[
|
||||
AccessUser(
|
||||
name="demo.user",
|
||||
qualified_name="Пользователь.demo.user",
|
||||
full_name="Demo User",
|
||||
groups=["ОтделПродаж"],
|
||||
)
|
||||
],
|
||||
),
|
||||
configuration=ConfigurationRoot(
|
||||
groups=[
|
||||
MetadataGroup(
|
||||
|
||||
Reference in New Issue
Block a user