Load 1C access profiles groups and users
CI / python (push) Has been cancelled
CI / rust (push) Has been cancelled

This commit is contained in:
2026-05-21 18:17:27 +03:00
parent 3c7b1825c4
commit d0b74c05be
11 changed files with 599 additions and 0 deletions
@@ -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),