Skip to content

Commit 04fbfed

Browse files
authored
IFC-768 Implement attribute level permission report (#4616)
1 parent 6736814 commit 04fbfed

File tree

13 files changed

+208
-17
lines changed

13 files changed

+208
-17
lines changed

backend/infrahub/core/attribute.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ async def to_graphql(
460460
fields: Optional[dict] = None,
461461
related_node_ids: Optional[set] = None,
462462
filter_sensitive: bool = False,
463+
permissions: Optional[dict] = None,
463464
) -> dict:
464465
"""Generate GraphQL Payload for this attribute."""
465466
# pylint: disable=too-many-branches
@@ -486,6 +487,10 @@ async def to_graphql(
486487
response[field_name] = self.get_kind()
487488
continue
488489

490+
if field_name == "permissions":
491+
response[field_name] = {"update_value": permissions["update"]} if permissions else None
492+
continue
493+
489494
if field_name in ["source", "owner"]:
490495
node_attr_getter = getattr(self, f"get_{field_name}")
491496
node_attr = await node_attr_getter(db=db)

backend/infrahub/core/constants/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ class GlobalPermissions(InfrahubStringEnum):
6363

6464
class PermissionAction(InfrahubStringEnum):
6565
ANY = "any"
66-
ADD = "create"
67-
CHANGE = "update"
66+
CREATE = "create"
67+
UPDATE = "update"
6868
DELETE = "delete"
6969
VIEW = "view"
7070

backend/infrahub/core/node/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ async def to_graphql(
528528
fields: Optional[dict] = None,
529529
related_node_ids: Optional[set] = None,
530530
filter_sensitive: bool = False,
531+
permissions: Optional[dict] = None,
531532
) -> dict:
532533
"""Generate GraphQL Payload for all attributes
533534
@@ -579,11 +580,11 @@ async def to_graphql(
579580
fields=fields.get(field_name),
580581
related_node_ids=related_node_ids,
581582
filter_sensitive=filter_sensitive,
583+
permissions=permissions,
582584
)
583585
else:
584586
response[field_name] = await field.to_graphql(
585-
db=db,
586-
filter_sensitive=filter_sensitive,
587+
db=db, filter_sensitive=filter_sensitive, permissions=permissions
587588
)
588589

589590
return response

backend/infrahub/core/node/ipam.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ async def to_graphql(
1919
fields: Optional[dict] = None,
2020
related_node_ids: Optional[set] = None,
2121
filter_sensitive: bool = False,
22+
permissions: Optional[dict] = None,
2223
) -> dict:
2324
response = await super().to_graphql(
2425
db, fields=fields, related_node_ids=related_node_ids, filter_sensitive=filter_sensitive

backend/infrahub/core/node/permissions.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,14 @@ async def to_graphql(
1515
fields: Optional[dict] = None,
1616
related_node_ids: Optional[set] = None,
1717
filter_sensitive: bool = False,
18+
permissions: Optional[dict] = None,
1819
) -> dict:
1920
response = await super().to_graphql(
20-
db, fields=fields, related_node_ids=related_node_ids, filter_sensitive=filter_sensitive
21+
db,
22+
fields=fields,
23+
related_node_ids=related_node_ids,
24+
filter_sensitive=filter_sensitive,
25+
permissions=permissions,
2126
)
2227

2328
if fields:
@@ -34,9 +39,14 @@ async def to_graphql(
3439
fields: Optional[dict] = None,
3540
related_node_ids: Optional[set] = None,
3641
filter_sensitive: bool = False,
42+
permissions: Optional[dict] = None,
3743
) -> dict:
3844
response = await super().to_graphql(
39-
db, fields=fields, related_node_ids=related_node_ids, filter_sensitive=filter_sensitive
45+
db,
46+
fields=fields,
47+
related_node_ids=related_node_ids,
48+
filter_sensitive=filter_sensitive,
49+
permissions=permissions,
4050
)
4151

4252
if fields:

backend/infrahub/core/protocols_base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ async def to_graphql(
9393
fields: Optional[dict] = None,
9494
related_node_ids: Optional[set] = None,
9595
filter_sensitive: bool = False,
96+
permissions: Optional[dict] = None,
9697
) -> dict: ...
9798
async def render_display_label(self, db: Optional[InfrahubDatabase] = None) -> str: ...
9899
async def from_graphql(self, data: dict, db: InfrahubDatabase) -> bool: ...

backend/infrahub/core/schema/manager.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
GenericSchema,
2020
MainSchemaTypes,
2121
NodeSchema,
22+
ProfileSchema,
2223
RelationshipSchema,
2324
SchemaRoot,
2425
)
@@ -97,6 +98,15 @@ def get_node_schema(
9798

9899
raise ValueError("The selected node is not of type NodeSchema")
99100

101+
def get_profile_schema(
102+
self, name: str, branch: Optional[Union[Branch, str]] = None, duplicate: bool = True
103+
) -> ProfileSchema:
104+
schema = self.get(name=name, branch=branch, duplicate=duplicate)
105+
if isinstance(schema, ProfileSchema):
106+
return schema
107+
108+
raise ValueError("The selected node is not of type ProfileSchema")
109+
100110
def get_full(
101111
self, branch: Optional[Union[Branch, str]] = None, duplicate: bool = True
102112
) -> dict[str, MainSchemaTypes]:

backend/infrahub/graphql/permissions.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@ async def get_permissions(db: InfrahubDatabase, schema: MainSchemaTypes, context
1616
schema_objects = [schema]
1717
if isinstance(schema, GenericSchema):
1818
for node_name in schema.used_by:
19-
schema_objects.append(
20-
registry.schema.get_node_schema(name=node_name, branch=context.branch, duplicate=False)
21-
)
19+
schema_object: MainSchemaTypes
20+
try:
21+
schema_object = registry.schema.get_node_schema(name=node_name, branch=context.branch, duplicate=False)
22+
except ValueError:
23+
schema_object = registry.schema.get_profile_schema(
24+
name=node_name, branch=context.branch, duplicate=False
25+
)
26+
schema_objects.append(schema_object)
2227

2328
response: dict[str, Any] = {"count": len(schema_objects), "edges": []}
2429

backend/infrahub/graphql/resolver.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,15 @@ async def default_paginated_list_resolver(
139139
edges = fields.get("edges", {})
140140
node_fields = edges.get("node", {})
141141

142-
permissions = fields.get("permissions")
142+
permission_set: Optional[dict[str, Any]] = None
143+
permissions = await get_permissions(db=db, schema=schema, context=context) if context.account_session else None
144+
if fields.get("permissions"):
145+
response["permissions"] = permissions
146+
143147
if permissions:
144-
response["permissions"] = await get_permissions(db=db, schema=schema, context=context)
148+
for edge in permissions["edges"]:
149+
if edge["node"]["kind"] == schema.kind:
150+
permission_set = edge["node"]
145151

146152
objs = []
147153
if edges or "hfid" in filters:
@@ -175,7 +181,14 @@ async def default_paginated_list_resolver(
175181

176182
if objs:
177183
objects = [
178-
{"node": await obj.to_graphql(db=db, fields=node_fields, related_node_ids=context.related_node_ids)}
184+
{
185+
"node": await obj.to_graphql(
186+
db=db,
187+
fields=node_fields,
188+
related_node_ids=context.related_node_ids,
189+
permissions=permission_set,
190+
)
191+
}
179192
for obj in objs
180193
]
181194
response["edges"] = objects

backend/infrahub/graphql/types/attribute.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ class RelatedPrefixNodeInput(InputObjectType):
5555
_relation__source = String(required=False)
5656

5757

58+
class PermissionType(ObjectType):
59+
update_value = String(required=False)
60+
61+
5862
class AttributeInterface(InfrahubInterface):
5963
is_default = Field(Boolean)
6064
is_inherited = Field(Boolean)
@@ -70,6 +74,7 @@ class AttributeInterface(InfrahubInterface):
7074
class BaseAttribute(ObjectType):
7175
id = Field(String)
7276
is_from_profile = Field(Boolean)
77+
permissions = Field(PermissionType, required=False)
7378

7479
@classmethod
7580
def __init_subclass__(cls, **kwargs: dict[str, Any]) -> None:

0 commit comments

Comments
 (0)