infrahub-server 1.1.1__py3-none-any.whl → 1.1.3__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.
- infrahub/api/__init__.py +13 -5
- infrahub/api/artifact.py +9 -15
- infrahub/api/auth.py +7 -1
- infrahub/api/dependencies.py +15 -2
- infrahub/api/diff/diff.py +13 -7
- infrahub/api/file.py +5 -10
- infrahub/api/internal.py +19 -6
- infrahub/api/menu.py +8 -6
- infrahub/api/oauth2.py +25 -10
- infrahub/api/oidc.py +26 -10
- infrahub/api/query.py +2 -2
- infrahub/api/schema.py +48 -59
- infrahub/api/storage.py +8 -8
- infrahub/api/transformation.py +6 -5
- infrahub/auth.py +1 -26
- infrahub/cli/__init__.py +1 -1
- infrahub/cli/context.py +5 -8
- infrahub/cli/db.py +6 -6
- infrahub/cli/git_agent.py +1 -1
- infrahub/computed_attribute/models.py +1 -1
- infrahub/computed_attribute/tasks.py +1 -1
- infrahub/config.py +5 -5
- infrahub/core/account.py +2 -10
- infrahub/core/attribute.py +22 -0
- infrahub/core/branch/models.py +1 -1
- infrahub/core/branch/tasks.py +4 -3
- infrahub/core/diff/calculator.py +14 -0
- infrahub/core/diff/combiner.py +6 -2
- infrahub/core/diff/conflicts_enricher.py +2 -2
- infrahub/core/diff/coordinator.py +296 -87
- infrahub/core/diff/data_check_synchronizer.py +33 -4
- infrahub/core/diff/enricher/cardinality_one.py +3 -3
- infrahub/core/diff/enricher/hierarchy.py +4 -1
- infrahub/core/diff/merger/merger.py +11 -1
- infrahub/core/diff/merger/serializer.py +5 -29
- infrahub/core/diff/model/path.py +88 -4
- infrahub/core/diff/query/field_specifiers.py +35 -0
- infrahub/core/diff/query/roots_metadata.py +48 -0
- infrahub/core/diff/query/save.py +1 -0
- infrahub/core/diff/query_parser.py +27 -11
- infrahub/core/diff/repository/deserializer.py +7 -3
- infrahub/core/diff/repository/repository.py +100 -9
- infrahub/core/diff/tasks.py +1 -1
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/integrity/object_conflict/conflict_recorder.py +6 -1
- infrahub/core/ipam/utilization.py +6 -1
- infrahub/core/manager.py +8 -0
- infrahub/core/merge.py +6 -1
- infrahub/core/migrations/graph/__init__.py +2 -0
- infrahub/core/migrations/graph/m014_remove_index_attr_value.py +1 -1
- infrahub/core/migrations/graph/m015_diff_format_update.py +1 -1
- infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +1 -1
- infrahub/core/migrations/graph/m018_uniqueness_nulls.py +101 -0
- infrahub/core/migrations/query/attribute_add.py +5 -5
- infrahub/core/migrations/schema/tasks.py +2 -2
- infrahub/core/migrations/shared.py +3 -3
- infrahub/core/node/__init__.py +8 -2
- infrahub/core/node/constraints/grouped_uniqueness.py +9 -2
- infrahub/core/query/__init__.py +5 -2
- infrahub/core/query/diff.py +32 -19
- infrahub/core/query/ipam.py +30 -22
- infrahub/core/query/node.py +91 -40
- infrahub/core/schema/generated/attribute_schema.py +2 -2
- infrahub/core/schema/generated/base_node_schema.py +2 -2
- infrahub/core/schema/generated/relationship_schema.py +1 -1
- infrahub/core/schema/schema_branch_computed.py +1 -1
- infrahub/core/task/task_log.py +1 -1
- infrahub/core/validators/attribute/kind.py +1 -1
- infrahub/core/validators/interface.py +1 -2
- infrahub/core/validators/models/violation.py +1 -14
- infrahub/core/validators/shared.py +2 -2
- infrahub/core/validators/tasks.py +7 -4
- infrahub/core/validators/uniqueness/index.py +2 -4
- infrahub/database/index.py +1 -1
- infrahub/dependencies/builder/constraint/schema/aggregated.py +2 -0
- infrahub/dependencies/builder/constraint/schema/attribute_kind.py +8 -0
- infrahub/dependencies/builder/diff/data_check_synchronizer.py +2 -0
- infrahub/git/base.py +3 -3
- infrahub/git/integrator.py +1 -1
- infrahub/graphql/api/endpoints.py +12 -3
- infrahub/graphql/app.py +2 -2
- infrahub/graphql/auth/query_permission_checker/default_branch_checker.py +2 -17
- infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py +1 -12
- infrahub/graphql/auth/query_permission_checker/object_permission_checker.py +6 -40
- infrahub/graphql/auth/query_permission_checker/super_admin_checker.py +5 -8
- infrahub/graphql/enums.py +2 -2
- infrahub/graphql/initialization.py +27 -8
- infrahub/graphql/manager.py +9 -3
- infrahub/graphql/models.py +6 -0
- infrahub/graphql/mutations/account.py +14 -10
- infrahub/graphql/mutations/computed_attribute.py +11 -22
- infrahub/graphql/mutations/diff.py +2 -0
- infrahub/graphql/mutations/main.py +5 -16
- infrahub/graphql/mutations/proposed_change.py +11 -20
- infrahub/graphql/mutations/resource_manager.py +6 -3
- infrahub/graphql/mutations/schema.py +8 -7
- infrahub/graphql/mutations/tasks.py +1 -1
- infrahub/graphql/permissions.py +3 -4
- infrahub/graphql/queries/account.py +2 -11
- infrahub/graphql/queries/resource_manager.py +21 -10
- infrahub/graphql/query.py +3 -1
- infrahub/graphql/resolvers/resolver.py +5 -1
- infrahub/graphql/types/task.py +14 -2
- infrahub/menu/generator.py +6 -18
- infrahub/message_bus/messages/event_node_mutated.py +2 -2
- infrahub/message_bus/operations/check/repository.py +2 -4
- infrahub/message_bus/operations/event/branch.py +2 -4
- infrahub/message_bus/operations/requests/proposed_change.py +1 -1
- infrahub/message_bus/operations/requests/repository.py +3 -5
- infrahub/message_bus/types.py +1 -1
- infrahub/permissions/__init__.py +12 -3
- infrahub/permissions/backend.py +2 -17
- infrahub/permissions/constants.py +12 -8
- infrahub/permissions/local_backend.py +5 -102
- infrahub/permissions/manager.py +135 -0
- infrahub/permissions/report.py +14 -25
- infrahub/permissions/types.py +6 -0
- infrahub/proposed_change/tasks.py +1 -1
- infrahub/task_manager/models.py +34 -5
- infrahub/task_manager/task.py +14 -6
- infrahub/visuals.py +1 -3
- infrahub_sdk/client.py +204 -43
- infrahub_sdk/ctl/cli_commands.py +106 -6
- infrahub_sdk/data.py +3 -2
- infrahub_sdk/graphql.py +5 -0
- infrahub_sdk/node.py +21 -2
- infrahub_sdk/queries.py +69 -0
- infrahub_sdk/schema/main.py +1 -0
- infrahub_sdk/testing/schemas/animal.py +1 -0
- infrahub_sdk/types.py +6 -0
- infrahub_sdk/utils.py +17 -0
- {infrahub_server-1.1.1.dist-info → infrahub_server-1.1.3.dist-info}/METADATA +1 -1
- {infrahub_server-1.1.1.dist-info → infrahub_server-1.1.3.dist-info}/RECORD +136 -131
- infrahub/core/diff/query/empty_roots.py +0 -33
- {infrahub_server-1.1.1.dist-info → infrahub_server-1.1.3.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.1.1.dist-info → infrahub_server-1.1.3.dist-info}/WHEEL +0 -0
- {infrahub_server-1.1.1.dist-info → infrahub_server-1.1.3.dist-info}/entry_points.txt +0 -0
|
@@ -13,10 +13,9 @@ from infrahub.core.constants import (
|
|
|
13
13
|
)
|
|
14
14
|
from infrahub.core.manager import NodeManager
|
|
15
15
|
from infrahub.core.node import Node
|
|
16
|
-
from infrahub.core.registry import registry
|
|
17
16
|
from infrahub.core.schema import NodeSchema
|
|
18
17
|
from infrahub.database import InfrahubDatabase, retry_db_transaction
|
|
19
|
-
from infrahub.exceptions import BranchNotFoundError, ValidationError
|
|
18
|
+
from infrahub.exceptions import BranchNotFoundError, PermissionDeniedError, ValidationError
|
|
20
19
|
from infrahub.graphql.mutations.main import InfrahubMutationMixin
|
|
21
20
|
from infrahub.graphql.types.enums import CheckType as GraphQLCheckType
|
|
22
21
|
from infrahub.message_bus import messages
|
|
@@ -92,22 +91,6 @@ class InfrahubProposedChangeMutation(InfrahubMutationMixin, Mutation):
|
|
|
92
91
|
):
|
|
93
92
|
context: GraphqlContext = info.context
|
|
94
93
|
|
|
95
|
-
has_merge_permission = False
|
|
96
|
-
if context.account_session:
|
|
97
|
-
for permission_backend in registry.permission_backends:
|
|
98
|
-
if has_merge_permission := await permission_backend.has_permission(
|
|
99
|
-
db=context.db,
|
|
100
|
-
account_session=context.active_account_session,
|
|
101
|
-
permission=GlobalPermission(
|
|
102
|
-
action=GlobalPermissions.MERGE_PROPOSED_CHANGE.value,
|
|
103
|
-
decision=PermissionDecision.ALLOW_ALL.value,
|
|
104
|
-
),
|
|
105
|
-
branch=branch,
|
|
106
|
-
):
|
|
107
|
-
break
|
|
108
|
-
else:
|
|
109
|
-
has_merge_permission = True
|
|
110
|
-
|
|
111
94
|
obj = await NodeManager.get_one_by_id_or_default_filter(
|
|
112
95
|
db=context.db,
|
|
113
96
|
kind=cls._meta.schema.kind,
|
|
@@ -125,8 +108,16 @@ class InfrahubProposedChangeMutation(InfrahubMutationMixin, Mutation):
|
|
|
125
108
|
state.validate_state_transition(updated_state)
|
|
126
109
|
|
|
127
110
|
# Check before starting a transaction, stopping in the middle of the transaction seems to break with memgraph
|
|
128
|
-
if updated_state == ProposedChangeState.MERGED and
|
|
129
|
-
|
|
111
|
+
if updated_state == ProposedChangeState.MERGED and context.account_session:
|
|
112
|
+
try:
|
|
113
|
+
context.active_permissions.raise_for_permission(
|
|
114
|
+
permission=GlobalPermission(
|
|
115
|
+
action=GlobalPermissions.MERGE_PROPOSED_CHANGE.value,
|
|
116
|
+
decision=PermissionDecision.ALLOW_ALL.value,
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
except PermissionDeniedError as exc:
|
|
120
|
+
raise ValidationError(str(exc)) from exc
|
|
130
121
|
|
|
131
122
|
if updated_state == ProposedChangeState.MERGED:
|
|
132
123
|
data["state"]["value"] = ProposedChangeState.MERGING.value
|
|
@@ -10,6 +10,7 @@ from infrahub.core import registry
|
|
|
10
10
|
from infrahub.core.constants import InfrahubKind
|
|
11
11
|
from infrahub.core.ipam.constants import PrefixMemberType
|
|
12
12
|
from infrahub.core.schema import NodeSchema
|
|
13
|
+
from infrahub.database import retry_db_transaction
|
|
13
14
|
from infrahub.exceptions import QueryValidationError, SchemaNotFoundError, ValidationError
|
|
14
15
|
|
|
15
16
|
from ..queries.resource_manager import PoolAllocatedNode
|
|
@@ -32,7 +33,7 @@ class IPPrefixPoolGetResourceInput(InputObjectType):
|
|
|
32
33
|
hfid = InputField(String(required=False), description="HFID of the pool to allocate from")
|
|
33
34
|
identifier = InputField(String(required=False), description="Identifier for the allocated resource")
|
|
34
35
|
prefix_length = InputField(Int(required=False), description="Size of the prefix to allocate")
|
|
35
|
-
member_type = InputField(String(required=False), description="
|
|
36
|
+
member_type = InputField(String(required=False), description="Type of members for the newly created prefix")
|
|
36
37
|
prefix_type = InputField(String(required=False), description="Kind of prefix to allocate")
|
|
37
38
|
data = InputField(GenericScalar(required=False), description="Additional data to pass to the newly created prefix")
|
|
38
39
|
|
|
@@ -44,9 +45,9 @@ class IPAddressPoolGetResourceInput(InputObjectType):
|
|
|
44
45
|
prefix_length = InputField(
|
|
45
46
|
Int(required=False), description="Size of the prefix mask to allocate on the new IP address"
|
|
46
47
|
)
|
|
47
|
-
address_type = InputField(String(required=False), description="Kind of
|
|
48
|
+
address_type = InputField(String(required=False), description="Kind of IP address to allocate")
|
|
48
49
|
data = InputField(
|
|
49
|
-
GenericScalar(required=False), description="Additional data to pass to the newly created
|
|
50
|
+
GenericScalar(required=False), description="Additional data to pass to the newly created IP address"
|
|
50
51
|
)
|
|
51
52
|
|
|
52
53
|
|
|
@@ -167,6 +168,7 @@ class InfrahubNumberPoolMutation(InfrahubMutationMixin, Mutation):
|
|
|
167
168
|
super().__init_subclass_with_meta__(_meta=_meta, **options)
|
|
168
169
|
|
|
169
170
|
@classmethod
|
|
171
|
+
@retry_db_transaction(name="resource_manager_create")
|
|
170
172
|
async def mutate_create(
|
|
171
173
|
cls,
|
|
172
174
|
info: GraphQLResolveInfo,
|
|
@@ -196,6 +198,7 @@ class InfrahubNumberPoolMutation(InfrahubMutationMixin, Mutation):
|
|
|
196
198
|
return await super().mutate_create(info=info, data=data, branch=branch)
|
|
197
199
|
|
|
198
200
|
@classmethod
|
|
201
|
+
@retry_db_transaction(name="resource_manager_update")
|
|
199
202
|
async def mutate_update(
|
|
200
203
|
cls,
|
|
201
204
|
info: GraphQLResolveInfo,
|
|
@@ -126,7 +126,7 @@ class SchemaDropdownRemove(Mutation):
|
|
|
126
126
|
validate_kind_dropdown(kind=kind, attribute=attribute)
|
|
127
127
|
dropdown = str(data.dropdown)
|
|
128
128
|
nodes_with_dropdown = await NodeManager.query(
|
|
129
|
-
db=context.db, schema=kind.kind, filters={f"{attribute}__value": dropdown}
|
|
129
|
+
db=context.db, schema=kind.kind, filters={f"{attribute}__value": dropdown}, branch=context.branch
|
|
130
130
|
)
|
|
131
131
|
if nodes_with_dropdown:
|
|
132
132
|
raise ValidationError(f"There are still {kind.kind} objects using this dropdown")
|
|
@@ -208,14 +208,15 @@ class SchemaEnumRemove(Mutation):
|
|
|
208
208
|
data: SchemaEnumInput,
|
|
209
209
|
) -> dict[str, bool]:
|
|
210
210
|
context: GraphqlContext = info.context
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
kind = db.schema.get(name=str(data.kind), branch=branch.name)
|
|
211
|
+
|
|
212
|
+
kind = context.db.schema.get(name=str(data.kind), branch=context.branch.name)
|
|
214
213
|
|
|
215
214
|
attribute = str(data.attribute)
|
|
216
215
|
enum = str(data.enum)
|
|
217
216
|
validate_kind_enum(kind=kind, attribute=attribute)
|
|
218
|
-
nodes_with_enum = await NodeManager.query(
|
|
217
|
+
nodes_with_enum = await NodeManager.query(
|
|
218
|
+
db=context.db, schema=kind.kind, filters={f"{attribute}__value": enum}, branch=context.branch
|
|
219
|
+
)
|
|
219
220
|
if nodes_with_enum:
|
|
220
221
|
raise ValidationError(f"There are still {kind.kind} objects using this enum")
|
|
221
222
|
|
|
@@ -231,8 +232,8 @@ class SchemaEnumRemove(Mutation):
|
|
|
231
232
|
|
|
232
233
|
await update_registry(
|
|
233
234
|
kind=kind,
|
|
234
|
-
branch=branch,
|
|
235
|
-
db=db,
|
|
235
|
+
branch=context.branch,
|
|
236
|
+
db=context.db,
|
|
236
237
|
account_id=context.active_account_session.account_id,
|
|
237
238
|
service=context.active_service,
|
|
238
239
|
)
|
|
@@ -31,7 +31,7 @@ async def merge_branch_mutation(branch: str) -> None:
|
|
|
31
31
|
diff_coordinator = await component_registry.get_component(DiffCoordinator, db=db, branch=obj)
|
|
32
32
|
diff_repository = await component_registry.get_component(DiffRepository, db=db, branch=obj)
|
|
33
33
|
diff_merger = await component_registry.get_component(DiffMerger, db=db, branch=obj)
|
|
34
|
-
enriched_diff = await diff_coordinator.
|
|
34
|
+
enriched_diff = await diff_coordinator.update_branch_diff_and_return(base_branch=base_branch, diff_branch=obj)
|
|
35
35
|
if enriched_diff.get_all_conflicts():
|
|
36
36
|
raise ValidationError(
|
|
37
37
|
f"Branch {obj.name} contains conflicts with the default branch."
|
infrahub/graphql/permissions.py
CHANGED
|
@@ -4,15 +4,14 @@ from typing import TYPE_CHECKING, Any
|
|
|
4
4
|
|
|
5
5
|
from infrahub.core.registry import registry
|
|
6
6
|
from infrahub.core.schema import GenericSchema
|
|
7
|
-
from infrahub.permissions
|
|
7
|
+
from infrahub.permissions import report_schema_permissions
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from infrahub.core.schema import MainSchemaTypes
|
|
11
|
-
from infrahub.database import InfrahubDatabase
|
|
12
11
|
from infrahub.graphql.initialization import GraphqlContext
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
async def get_permissions(
|
|
14
|
+
async def get_permissions(schema: MainSchemaTypes, context: GraphqlContext) -> dict[str, Any]:
|
|
16
15
|
schema_objects = [schema]
|
|
17
16
|
if isinstance(schema, GenericSchema):
|
|
18
17
|
for node_name in schema.used_by:
|
|
@@ -28,7 +27,7 @@ async def get_permissions(db: InfrahubDatabase, schema: MainSchemaTypes, context
|
|
|
28
27
|
response: dict[str, Any] = {"count": len(schema_objects), "edges": []}
|
|
29
28
|
|
|
30
29
|
nodes = await report_schema_permissions(
|
|
31
|
-
|
|
30
|
+
branch=context.branch, permission_manager=context.active_permissions, schemas=schema_objects
|
|
32
31
|
)
|
|
33
32
|
response["edges"] = [{"node": node} for node in nodes]
|
|
34
33
|
|
|
@@ -5,7 +5,6 @@ from typing import TYPE_CHECKING, Any
|
|
|
5
5
|
from graphene import Field, Int, List, ObjectType, String
|
|
6
6
|
from infrahub_sdk.utils import extract_fields_first_node
|
|
7
7
|
|
|
8
|
-
from infrahub.core import registry
|
|
9
8
|
from infrahub.core.manager import NodeManager
|
|
10
9
|
from infrahub.core.protocols import InternalAccountToken
|
|
11
10
|
from infrahub.exceptions import PermissionDeniedError
|
|
@@ -14,7 +13,6 @@ if TYPE_CHECKING:
|
|
|
14
13
|
from graphql import GraphQLResolveInfo
|
|
15
14
|
|
|
16
15
|
from infrahub.graphql.initialization import GraphqlContext
|
|
17
|
-
from infrahub.permissions.constants import AssignedPermissions
|
|
18
16
|
|
|
19
17
|
|
|
20
18
|
class AccountTokenNode(ObjectType):
|
|
@@ -120,17 +118,10 @@ async def resolve_account_permissions(
|
|
|
120
118
|
raise ValueError("An account_session is mandatory to execute this query")
|
|
121
119
|
|
|
122
120
|
fields = await extract_fields_first_node(info)
|
|
123
|
-
permissions: AssignedPermissions = {"global_permissions": [], "object_permissions": []}
|
|
124
|
-
for permission_backend in registry.permission_backends:
|
|
125
|
-
backend_permissions = await permission_backend.load_permissions(
|
|
126
|
-
db=context.db, account_session=context.account_session, branch=context.branch
|
|
127
|
-
)
|
|
128
|
-
permissions["global_permissions"].extend(backend_permissions["global_permissions"])
|
|
129
|
-
permissions["object_permissions"].extend(backend_permissions["object_permissions"])
|
|
130
121
|
|
|
131
122
|
response: dict[str, dict[str, Any]] = {}
|
|
132
123
|
if "global_permissions" in fields:
|
|
133
|
-
global_list = permissions["global_permissions"]
|
|
124
|
+
global_list = context.active_permissions.permissions["global_permissions"]
|
|
134
125
|
response["global_permissions"] = {"count": len(global_list)}
|
|
135
126
|
response["global_permissions"]["edges"] = [
|
|
136
127
|
{
|
|
@@ -145,7 +136,7 @@ async def resolve_account_permissions(
|
|
|
145
136
|
for obj in global_list
|
|
146
137
|
]
|
|
147
138
|
if "object_permissions" in fields:
|
|
148
|
-
object_list = permissions["object_permissions"]
|
|
139
|
+
object_list = context.active_permissions.permissions["object_permissions"]
|
|
149
140
|
response["object_permissions"] = {"count": len(object_list)}
|
|
150
141
|
response["object_permissions"]["edges"] = [
|
|
151
142
|
{
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING, Any
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
4
|
|
|
5
5
|
from graphene import Field, Float, Int, List, ObjectType, String
|
|
6
6
|
from infrahub_sdk.utils import extract_fields_first_node
|
|
@@ -57,7 +57,7 @@ class PoolAllocatedEdge(ObjectType):
|
|
|
57
57
|
node = Field(PoolAllocatedNode, required=True)
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
def _validate_pool_type(pool_id: str, pool:
|
|
60
|
+
def _validate_pool_type(pool_id: str, pool: CoreNode | None = None) -> CoreNode:
|
|
61
61
|
if not pool or pool.get_kind() not in [
|
|
62
62
|
InfrahubKind.IPADDRESSPOOL,
|
|
63
63
|
InfrahubKind.IPPREFIXPOOL,
|
|
@@ -81,15 +81,21 @@ class PoolAllocated(ObjectType):
|
|
|
81
81
|
limit: int = 10,
|
|
82
82
|
) -> dict:
|
|
83
83
|
context: GraphqlContext = info.context
|
|
84
|
-
pool:
|
|
84
|
+
pool: CoreNode | None = await NodeManager.get_one(id=pool_id, db=context.db, branch=context.branch)
|
|
85
85
|
|
|
86
86
|
fields = await extract_fields_first_node(info=info)
|
|
87
87
|
|
|
88
|
+
allocated_kinds: list[str] = []
|
|
88
89
|
pool = _validate_pool_type(pool_id=pool_id, pool=pool)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
match pool.get_kind():
|
|
91
|
+
case InfrahubKind.NUMBERPOOL:
|
|
92
|
+
return await resolve_number_pool_allocation(
|
|
93
|
+
db=context.db, context=context, pool=pool, fields=fields, offset=offset, limit=limit
|
|
94
|
+
)
|
|
95
|
+
case InfrahubKind.IPPREFIXPOOL:
|
|
96
|
+
allocated_kinds.append(InfrahubKind.IPPREFIX)
|
|
97
|
+
case InfrahubKind.IPADDRESSPOOL:
|
|
98
|
+
allocated_kinds.append(InfrahubKind.IPADDRESS)
|
|
93
99
|
|
|
94
100
|
resources = await pool.resources.get_peers(db=context.db) # type: ignore[attr-defined,union-attr]
|
|
95
101
|
if resource_id not in resources:
|
|
@@ -100,7 +106,12 @@ class PoolAllocated(ObjectType):
|
|
|
100
106
|
resource = resources[resource_id]
|
|
101
107
|
|
|
102
108
|
query = await IPPrefixUtilization.init(
|
|
103
|
-
db=context.db,
|
|
109
|
+
db=context.db,
|
|
110
|
+
at=context.at,
|
|
111
|
+
ip_prefixes=[resource],
|
|
112
|
+
allocated_kinds=allocated_kinds,
|
|
113
|
+
offset=offset,
|
|
114
|
+
limit=limit,
|
|
104
115
|
)
|
|
105
116
|
response: dict[str, Any] = {}
|
|
106
117
|
if "count" in fields:
|
|
@@ -170,7 +181,7 @@ class PoolUtilization(ObjectType):
|
|
|
170
181
|
) -> dict:
|
|
171
182
|
context: GraphqlContext = info.context
|
|
172
183
|
db: InfrahubDatabase = context.db
|
|
173
|
-
pool:
|
|
184
|
+
pool: CoreNode | None = await NodeManager.get_one(id=pool_id, db=db, branch=context.branch)
|
|
174
185
|
pool = _validate_pool_type(pool_id=pool_id, pool=pool)
|
|
175
186
|
if pool.get_kind() == "CoreNumberPool":
|
|
176
187
|
return await resolve_number_pool_utilization(db=db, context=context, pool=pool)
|
|
@@ -212,7 +223,7 @@ class PoolUtilization(ObjectType):
|
|
|
212
223
|
for resource_id, resource_node in resources_map.items():
|
|
213
224
|
resource_total = None
|
|
214
225
|
default_branch_total = None
|
|
215
|
-
node_response: dict[str,
|
|
226
|
+
node_response: dict[str, str | float | int] = {}
|
|
216
227
|
if "id" in node_fields:
|
|
217
228
|
node_response["id"] = resource_id
|
|
218
229
|
if "kind" in node_fields:
|
infrahub/graphql/query.py
CHANGED
|
@@ -37,7 +37,9 @@ async def execute_query(
|
|
|
37
37
|
if not graphql_query:
|
|
38
38
|
raise ValueError(f"Unable to find the {InfrahubKind.GRAPHQLQUERY} {name}")
|
|
39
39
|
|
|
40
|
-
gql_params = prepare_graphql_params(
|
|
40
|
+
gql_params = await prepare_graphql_params(
|
|
41
|
+
branch=branch, db=db, at=at, include_mutation=False, include_subscription=False
|
|
42
|
+
)
|
|
41
43
|
|
|
42
44
|
result = await graphql(
|
|
43
45
|
schema=gql_params.schema,
|
|
@@ -9,6 +9,7 @@ from infrahub.core.manager import NodeManager
|
|
|
9
9
|
from infrahub.core.query.node import NodeGetHierarchyQuery
|
|
10
10
|
from infrahub.exceptions import NodeNotFoundError
|
|
11
11
|
|
|
12
|
+
from ..models import OrderModel
|
|
12
13
|
from ..parser import extract_selection
|
|
13
14
|
from ..permissions import get_permissions
|
|
14
15
|
from ..types import RELATIONS_PROPERTY_MAP, RELATIONS_PROPERTY_MAP_REVERSED
|
|
@@ -33,6 +34,7 @@ async def account_resolver(
|
|
|
33
34
|
filters={"ids": [context.account_session.account_id]},
|
|
34
35
|
fields=fields,
|
|
35
36
|
db=db,
|
|
37
|
+
order=OrderModel(disable=True),
|
|
36
38
|
)
|
|
37
39
|
if results:
|
|
38
40
|
account_profile = await results[0].to_graphql(db=db, fields=fields)
|
|
@@ -132,6 +134,7 @@ async def default_paginated_list_resolver(
|
|
|
132
134
|
info: GraphQLResolveInfo,
|
|
133
135
|
offset: int | None = None,
|
|
134
136
|
limit: int | None = None,
|
|
137
|
+
order: OrderModel | None = None,
|
|
135
138
|
partial_match: bool = False,
|
|
136
139
|
**kwargs: dict[str, Any],
|
|
137
140
|
) -> dict[str, Any]:
|
|
@@ -149,7 +152,7 @@ async def default_paginated_list_resolver(
|
|
|
149
152
|
node_fields = edges.get("node", {})
|
|
150
153
|
|
|
151
154
|
permission_set: Optional[dict[str, Any]] = None
|
|
152
|
-
permissions = await get_permissions(
|
|
155
|
+
permissions = await get_permissions(schema=schema, context=context) if context.permissions else None
|
|
153
156
|
if fields.get("permissions"):
|
|
154
157
|
response["permissions"] = permissions
|
|
155
158
|
|
|
@@ -173,6 +176,7 @@ async def default_paginated_list_resolver(
|
|
|
173
176
|
include_source=True,
|
|
174
177
|
include_owner=True,
|
|
175
178
|
partial_match=partial_match,
|
|
179
|
+
order=order,
|
|
176
180
|
)
|
|
177
181
|
|
|
178
182
|
if "count" in fields:
|
infrahub/graphql/types/task.py
CHANGED
|
@@ -28,9 +28,21 @@ class Task(ObjectType):
|
|
|
28
28
|
start_time = String(required=False)
|
|
29
29
|
|
|
30
30
|
|
|
31
|
+
class TaskRelatedNode(ObjectType):
|
|
32
|
+
id = String(required=True)
|
|
33
|
+
kind = String(required=True)
|
|
34
|
+
|
|
35
|
+
|
|
31
36
|
class TaskNode(Task):
|
|
32
|
-
related_node = String(
|
|
33
|
-
|
|
37
|
+
related_node = String(
|
|
38
|
+
required=False,
|
|
39
|
+
deprecation_reason="This field is deprecated and it will be removed in a future release, use related_nodes instead",
|
|
40
|
+
)
|
|
41
|
+
related_node_kind = String(
|
|
42
|
+
required=False,
|
|
43
|
+
deprecation_reason="This field is deprecated and it will be removed in a future release, use related_nodes instead",
|
|
44
|
+
)
|
|
45
|
+
related_nodes = List(TaskRelatedNode)
|
|
34
46
|
logs = Field(TaskLogEdge)
|
|
35
47
|
|
|
36
48
|
|
infrahub/menu/generator.py
CHANGED
|
@@ -5,16 +5,14 @@ from typing import TYPE_CHECKING
|
|
|
5
5
|
from infrahub.core import registry
|
|
6
6
|
from infrahub.core.protocols import CoreMenuItem
|
|
7
7
|
from infrahub.log import get_logger
|
|
8
|
-
from infrahub.permissions.constants import AssignedPermissions
|
|
9
|
-
from infrahub.permissions.local_backend import LocalPermissionBackend
|
|
10
8
|
|
|
11
9
|
from .constants import FULL_DEFAULT_MENU
|
|
12
10
|
from .models import MenuDict, MenuItemDict
|
|
13
11
|
|
|
14
12
|
if TYPE_CHECKING:
|
|
15
|
-
from infrahub.auth import AccountSession
|
|
16
13
|
from infrahub.core.branch import Branch
|
|
17
14
|
from infrahub.database import InfrahubDatabase
|
|
15
|
+
from infrahub.permissions import PermissionManager
|
|
18
16
|
|
|
19
17
|
log = get_logger()
|
|
20
18
|
|
|
@@ -24,28 +22,18 @@ def get_full_name(obj: CoreMenuItem) -> str:
|
|
|
24
22
|
|
|
25
23
|
|
|
26
24
|
async def generate_restricted_menu(
|
|
27
|
-
db: InfrahubDatabase, branch: Branch, menu_items: list[CoreMenuItem],
|
|
25
|
+
db: InfrahubDatabase, branch: Branch, menu_items: list[CoreMenuItem], account_permissions: PermissionManager | None
|
|
28
26
|
) -> MenuDict:
|
|
29
27
|
menu = await generate_menu(db=db, branch=branch, menu_items=menu_items)
|
|
30
28
|
|
|
31
|
-
permissions = AssignedPermissions(global_permissions=[], object_permissions=[])
|
|
32
|
-
perm_backend = LocalPermissionBackend()
|
|
33
|
-
|
|
34
|
-
if account:
|
|
35
|
-
permissions = await perm_backend.load_permissions(db=db, account_session=account, branch=branch)
|
|
36
|
-
|
|
37
29
|
for item in menu.data.values():
|
|
38
|
-
has_permission
|
|
30
|
+
has_permission = True
|
|
39
31
|
for permission in item.get_global_permissions():
|
|
40
|
-
has_permission =
|
|
41
|
-
|
|
32
|
+
has_permission = account_permissions is not None and account_permissions.has_permission(
|
|
33
|
+
permission=permission
|
|
42
34
|
)
|
|
43
|
-
if has_permission:
|
|
44
|
-
has_permission = True
|
|
45
|
-
elif has_permission is None:
|
|
46
|
-
has_permission = False
|
|
47
35
|
|
|
48
|
-
if has_permission
|
|
36
|
+
if not has_permission:
|
|
49
37
|
item.hidden = True
|
|
50
38
|
|
|
51
39
|
return menu
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Any
|
|
2
2
|
|
|
3
3
|
from pydantic import Field
|
|
4
4
|
|
|
@@ -12,4 +12,4 @@ class EventNodeMutated(InfrahubMessage):
|
|
|
12
12
|
kind: str = Field(..., description="The type of object modified")
|
|
13
13
|
node_id: str = Field(..., description="The ID of the mutated node")
|
|
14
14
|
action: str = Field(..., description="The action taken on the node")
|
|
15
|
-
data:
|
|
15
|
+
data: dict[str, Any] = Field(..., description="Data on modified object")
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
|
|
3
1
|
from infrahub_sdk.uuidt import UUIDT
|
|
4
2
|
from prefect import flow
|
|
5
3
|
from prefect.logging import get_run_logger
|
|
@@ -29,10 +27,10 @@ async def check_definition(message: messages.CheckRepositoryCheckDefinition, ser
|
|
|
29
27
|
)
|
|
30
28
|
proposed_change = await service.client.get(kind=InfrahubKind.PROPOSEDCHANGE, id=message.proposed_change)
|
|
31
29
|
validator_execution_id = str(UUIDT())
|
|
32
|
-
check_execution_ids:
|
|
30
|
+
check_execution_ids: list[str] = []
|
|
33
31
|
await proposed_change.validations.fetch()
|
|
34
32
|
validator = None
|
|
35
|
-
events:
|
|
33
|
+
events: list[InfrahubMessage] = []
|
|
36
34
|
|
|
37
35
|
for relationship in proposed_change.validations.peers:
|
|
38
36
|
existing_validator = relationship.peer
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
|
|
3
1
|
from prefect import flow
|
|
4
2
|
|
|
5
3
|
from infrahub.core import registry
|
|
@@ -24,14 +22,14 @@ async def merge(message: messages.EventBranchMerge, service: InfrahubServices) -
|
|
|
24
22
|
async with service.database.start_session() as db:
|
|
25
23
|
log.info("Branch merged", source_branch=message.source_branch, target_branch=message.target_branch)
|
|
26
24
|
|
|
27
|
-
events:
|
|
25
|
+
events: list[InfrahubMessage] = [
|
|
28
26
|
messages.RefreshRegistryBranches(),
|
|
29
27
|
]
|
|
30
28
|
component_registry = get_component_registry()
|
|
31
29
|
default_branch = registry.get_branch_from_registry()
|
|
32
30
|
diff_repository = await component_registry.get_component(DiffRepository, db=db, branch=default_branch)
|
|
33
31
|
# send diff update requests for every branch-tracking diff
|
|
34
|
-
branch_diff_roots = await diff_repository.
|
|
32
|
+
branch_diff_roots = await diff_repository.get_roots_metadata(base_branch_names=[message.target_branch])
|
|
35
33
|
|
|
36
34
|
await service.workflow.submit_workflow(
|
|
37
35
|
workflow=TRIGGER_ARTIFACT_DEFINITION_GENERATE,
|
|
@@ -26,7 +26,7 @@ from infrahub.proposed_change.models import (
|
|
|
26
26
|
RequestProposedChangeSchemaIntegrity,
|
|
27
27
|
RequestProposedChangeUserTests,
|
|
28
28
|
)
|
|
29
|
-
from infrahub.services import InfrahubServices # noqa:
|
|
29
|
+
from infrahub.services import InfrahubServices # noqa: TC001
|
|
30
30
|
from infrahub.workflows.catalogue import (
|
|
31
31
|
REQUEST_PROPOSED_CHANGE_DATA_INTEGRITY,
|
|
32
32
|
REQUEST_PROPOSED_CHANGE_REPOSITORY_CHECKS,
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
|
|
3
1
|
from infrahub_sdk.uuidt import UUIDT
|
|
4
2
|
from prefect import flow
|
|
5
3
|
from prefect.logging import get_run_logger
|
|
@@ -18,7 +16,7 @@ async def checks(message: messages.RequestRepositoryChecks, service: InfrahubSer
|
|
|
18
16
|
await add_tags(branches=[message.source_branch], nodes=[message.proposed_change])
|
|
19
17
|
log = get_run_logger()
|
|
20
18
|
|
|
21
|
-
events:
|
|
19
|
+
events: list[InfrahubMessage] = []
|
|
22
20
|
|
|
23
21
|
repository = await service.client.get(
|
|
24
22
|
kind=InfrahubKind.GENERICREPOSITORY, id=message.repository, branch=message.source_branch
|
|
@@ -26,7 +24,7 @@ async def checks(message: messages.RequestRepositoryChecks, service: InfrahubSer
|
|
|
26
24
|
proposed_change = await service.client.get(kind=InfrahubKind.PROPOSEDCHANGE, id=message.proposed_change)
|
|
27
25
|
|
|
28
26
|
validator_execution_id = str(UUIDT())
|
|
29
|
-
check_execution_ids:
|
|
27
|
+
check_execution_ids: list[str] = []
|
|
30
28
|
await proposed_change.validations.fetch()
|
|
31
29
|
await repository.checks.fetch()
|
|
32
30
|
|
|
@@ -106,7 +104,7 @@ async def user_checks(message: messages.RequestRepositoryUserChecks, service: In
|
|
|
106
104
|
await add_tags(branches=[message.source_branch], nodes=[message.proposed_change])
|
|
107
105
|
log = get_run_logger()
|
|
108
106
|
|
|
109
|
-
events:
|
|
107
|
+
events: list[InfrahubMessage] = []
|
|
110
108
|
|
|
111
109
|
repository = await service.client.get(
|
|
112
110
|
kind=InfrahubKind.GENERICREPOSITORY, id=message.repository_id, branch=message.source_branch, fragment=True
|
infrahub/message_bus/types.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import re
|
|
4
4
|
from enum import Enum
|
|
5
5
|
|
|
6
|
-
from infrahub_sdk.diff import NodeDiff # noqa:
|
|
6
|
+
from infrahub_sdk.diff import NodeDiff # noqa: TC002
|
|
7
7
|
from pydantic import BaseModel, Field
|
|
8
8
|
|
|
9
9
|
from infrahub.core.constants import InfrahubKind, RepositoryInternalStatus
|
infrahub/permissions/__init__.py
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
from .backend import PermissionBackend
|
|
2
|
-
from .local_backend import LocalPermissionBackend
|
|
1
|
+
from infrahub.permissions.backend import PermissionBackend
|
|
2
|
+
from infrahub.permissions.local_backend import LocalPermissionBackend
|
|
3
|
+
from infrahub.permissions.manager import PermissionManager
|
|
4
|
+
from infrahub.permissions.report import report_schema_permissions
|
|
5
|
+
from infrahub.permissions.types import AssignedPermissions
|
|
3
6
|
|
|
4
|
-
__all__ = [
|
|
7
|
+
__all__ = [
|
|
8
|
+
"AssignedPermissions",
|
|
9
|
+
"LocalPermissionBackend",
|
|
10
|
+
"PermissionBackend",
|
|
11
|
+
"PermissionManager",
|
|
12
|
+
"report_schema_permissions",
|
|
13
|
+
]
|
infrahub/permissions/backend.py
CHANGED
|
@@ -5,28 +5,13 @@ from typing import TYPE_CHECKING
|
|
|
5
5
|
|
|
6
6
|
if TYPE_CHECKING:
|
|
7
7
|
from infrahub.auth import AccountSession
|
|
8
|
-
from infrahub.core.account import GlobalPermission, ObjectPermission
|
|
9
8
|
from infrahub.core.branch import Branch
|
|
10
9
|
from infrahub.database import InfrahubDatabase
|
|
11
|
-
from infrahub.permissions.
|
|
10
|
+
from infrahub.permissions.types import AssignedPermissions
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class PermissionBackend(ABC):
|
|
15
14
|
@abstractmethod
|
|
16
15
|
async def load_permissions(
|
|
17
|
-
self, db: InfrahubDatabase,
|
|
16
|
+
self, db: InfrahubDatabase, branch: Branch, account_session: AccountSession
|
|
18
17
|
) -> AssignedPermissions: ...
|
|
19
|
-
|
|
20
|
-
@abstractmethod
|
|
21
|
-
def report_object_permission(
|
|
22
|
-
self, permissions: list[ObjectPermission], namespace: str, name: str, action: str
|
|
23
|
-
) -> PermissionDecisionFlag: ...
|
|
24
|
-
|
|
25
|
-
@abstractmethod
|
|
26
|
-
async def has_permission(
|
|
27
|
-
self,
|
|
28
|
-
db: InfrahubDatabase,
|
|
29
|
-
account_session: AccountSession,
|
|
30
|
-
permission: GlobalPermission | ObjectPermission,
|
|
31
|
-
branch: Branch,
|
|
32
|
-
) -> bool: ...
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from enum import IntFlag, StrEnum, auto
|
|
4
|
-
from typing import TYPE_CHECKING, TypedDict
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
from infrahub.core.account import GlobalPermission, ObjectPermission
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class AssignedPermissions(TypedDict):
|
|
11
|
-
global_permissions: list[GlobalPermission]
|
|
12
|
-
object_permissions: list[ObjectPermission]
|
|
5
|
+
from infrahub.core.constants import GlobalPermissions
|
|
13
6
|
|
|
14
7
|
|
|
15
8
|
class PermissionDecisionFlag(IntFlag):
|
|
@@ -26,3 +19,14 @@ class BranchRelativePermissionDecision(StrEnum):
|
|
|
26
19
|
ALLOW = auto()
|
|
27
20
|
ALLOW_DEFAULT = auto()
|
|
28
21
|
ALLOW_OTHER = auto()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
GLOBAL_PERMISSION_DENIAL_MESSAGE = {
|
|
25
|
+
GlobalPermissions.EDIT_DEFAULT_BRANCH.value: "You are not allowed to change data in the default branch",
|
|
26
|
+
GlobalPermissions.MERGE_BRANCH.value: "You are not allowed to merge a branch",
|
|
27
|
+
GlobalPermissions.MERGE_PROPOSED_CHANGE.value: "You are not allowed to merge proposed changes",
|
|
28
|
+
GlobalPermissions.MANAGE_SCHEMA.value: "You are not allowed to manage the schema",
|
|
29
|
+
GlobalPermissions.MANAGE_ACCOUNTS.value: "You are not allowed to manage user accounts, groups or roles",
|
|
30
|
+
GlobalPermissions.MANAGE_PERMISSIONS.value: "You are not allowed to manage permissions",
|
|
31
|
+
GlobalPermissions.MANAGE_REPOSITORIES.value: "You are not allowed to manage repositories",
|
|
32
|
+
}
|