Skip to content

Commit c6e9b82

Browse files
authored
Access Edges (#2195)
1 parent bce4ffc commit c6e9b82

29 files changed

+2011
-152
lines changed

.devcontainer/Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM mcr.microsoft.com/devcontainers/python:3.11
1+
FROM mcr.microsoft.com/devcontainers/python:3.12
22

33
ARG USERNAME=vscode
44

@@ -15,8 +15,8 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
1515
&& apt-get -y install --no-install-recommends vim wget
1616

1717

18-
RUN wget -qO- https://download.arangodb.com/arangodb310/DEBIAN/Release.key | apt-key add -
19-
RUN echo 'deb https://download.arangodb.com/arangodb310/DEBIAN/ /' | tee /etc/apt/sources.list.d/arangodb.list
18+
RUN wget -qO- https://download.arangodb.com/arangodb311/DEBIAN/Release.key | apt-key add -
19+
RUN echo 'deb https://download.arangodb.com/arangodb311/DEBIAN/ /' | tee /etc/apt/sources.list.d/arangodb.list
2020
RUN apt-get install -y apt-transport-https
2121
RUN apt-get update
2222
RUN apt-get install -y arangodb3-client

.devcontainer/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ services:
2323
# (Adding the "ports" property to this file will not forward from the container.)
2424

2525
arangodb:
26-
image: arangodb:3.10.3
26+
image: arangodb:3.11.11
2727
restart: unless-stopped
2828
# Uncomment the lines below in case you want to keep arangodb data in a separate volume
2929
# volumes:

.vscode/settings.json

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,17 @@
1010
"fixworker/test"
1111
],
1212
"python.testing.unittestEnabled": false,
13-
"python.testing.pytestEnabled": false,
14-
"python.formatting.provider": "black",
13+
"python.testing.pytestEnabled": true,
14+
"[python]": {
15+
"editor.defaultFormatter": "ms-python.black-formatter",
16+
"editor.formatOnSave": true
17+
},
18+
"black-formatter.args": [
19+
"--line-length",
20+
"120",
21+
"--target-version",
22+
"py39"
23+
],
1524
"search.exclude": {
1625
"**/*.code-search": true,
1726
"**/bower_components": true,
@@ -22,4 +31,4 @@
2231
"python.analysis.extraPaths": [
2332
"fixlib"
2433
]
25-
}
34+
}

fixlib/fixlib/baseresources.py

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@
66
from abc import ABC
77
from copy import deepcopy
88
from datetime import datetime, timezone, timedelta
9-
from enum import Enum, unique
9+
from enum import Enum, StrEnum, unique
1010
from functools import wraps, cached_property
1111
from typing import Dict, Iterator, List, ClassVar, Optional, TypedDict, Any, TypeVar, Type, Callable, Set, Tuple
1212
from collections import defaultdict
1313

1414
from attr import resolve_types
15-
from attrs import define, field, Factory
15+
from attrs import define, field, Factory, frozen, evolve
1616
from prometheus_client import Counter, Summary
1717

18-
from fixlib.json import from_json as _from_json, to_json as _to_json
18+
from fixlib.json import from_json as _from_json, to_json as _to_json, to_json_str
1919
from fixlib.logger import log
2020
from fixlib.types import Json
2121
from fixlib.utils import make_valid_timestamp, utc_str, utc
@@ -68,6 +68,7 @@ class ModelReference(TypedDict, total=False):
6868
class EdgeType(Enum):
6969
default = "default"
7070
delete = "delete"
71+
iam = "iam"
7172

7273
@staticmethod
7374
def from_value(value: Optional[str] = None) -> EdgeType:
@@ -1613,4 +1614,77 @@ def delete(self, graph: Any) -> bool:
16131614
return False
16141615

16151616

1617+
class PolicySourceKind(StrEnum):
1618+
principal = "principal" # e.g. IAM user, attached policy
1619+
group = "group" # policy comes from an IAM group
1620+
resource = "resource" # e.g. s3 bucket policy
1621+
1622+
1623+
ResourceConstraint = str
1624+
1625+
ConditionString = str
1626+
1627+
1628+
@frozen
1629+
class PolicySource:
1630+
kind: PolicySourceKind
1631+
uri: str
1632+
1633+
1634+
class HasResourcePolicy(ABC):
1635+
# returns a list of all policies that affects the resource (inline, attached, etc.)
1636+
def resource_policy(self, builder: Any) -> List[Tuple[PolicySource, Json]]:
1637+
raise NotImplementedError
1638+
1639+
1640+
@frozen
1641+
class PermissionCondition:
1642+
# if nonempty and any evals to true, access is granted, otherwise implicitly denied
1643+
allow: Optional[Tuple[ConditionString, ...]] = None
1644+
# if nonempty and any is evals to false, access is implicitly denied
1645+
boundary: Optional[Tuple[ConditionString, ...]] = None
1646+
# if nonempty and any evals to true, access is explicitly denied
1647+
deny: Optional[Tuple[ConditionString, ...]] = None
1648+
1649+
1650+
@frozen
1651+
class PermissionScope:
1652+
source: PolicySource
1653+
constraints: Tuple[ResourceConstraint, ...] # aka resource constraints
1654+
conditions: Optional[PermissionCondition] = None
1655+
1656+
def with_deny_conditions(self, deny_conditions: List[Json]) -> "PermissionScope":
1657+
c = self.conditions or PermissionCondition()
1658+
return evolve(self, conditions=evolve(c, deny=tuple([to_json_str(c) for c in deny_conditions])))
1659+
1660+
def with_boundary_conditions(self, boundary_conditions: List[Json]) -> "PermissionScope":
1661+
c = self.conditions or PermissionCondition()
1662+
return evolve(self, conditions=evolve(c, boundary=tuple([to_json_str(c) for c in boundary_conditions])))
1663+
1664+
def has_no_condititons(self) -> bool:
1665+
if self.conditions is None:
1666+
return True
1667+
1668+
if self.conditions.allow is None and self.conditions.boundary is None and self.conditions.deny is None:
1669+
return True
1670+
1671+
return False
1672+
1673+
1674+
class PermissionLevel(StrEnum):
1675+
list = "list"
1676+
read = "read"
1677+
tagging = "tagging"
1678+
write = "write"
1679+
permission_management = "permission"
1680+
unknown = "unknown" # in case a resource is not in the levels database
1681+
1682+
1683+
@frozen
1684+
class AccessPermission:
1685+
action: str
1686+
level: PermissionLevel
1687+
scopes: Tuple[PermissionScope, ...]
1688+
1689+
16161690
resolve_types(BaseResource) # noqa

fixlib/fixlib/graph/__init__.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def merge(self, graph: Graph, skip_deferred_edges: bool = False) -> None:
122122

123123
try:
124124
self._log_edge_creation = False
125-
self.update(edges=graph.edges, nodes=graph.nodes)
125+
self.update(edges=graph.edges(keys=True, data=True), nodes=graph.nodes)
126126
self.deferred_edges.extend(graph.deferred_edges)
127127
finally:
128128
self._log_edge_creation = True
@@ -647,17 +647,18 @@ def export_graph(self) -> None:
647647
if not self.found_replace_node:
648648
log.warning(f"No nodes of kind {self.graph_merge_kind.kind} found in graph")
649649
start_time = time()
650-
for edge in self.graph.edges:
651-
from_node = edge[0]
652-
to_node = edge[1]
650+
for from_node, to_node, key, data in self.graph.edges(keys=True, data=True):
653651
if not isinstance(from_node, BaseResource) or not isinstance(to_node, BaseResource):
654652
log.error(f"One of {from_node} and {to_node} is no base resource")
655653
continue
656654
edge_dict = {"from": from_node.chksum, "to": to_node.chksum}
657-
if len(edge) == 3:
658-
key = edge[2]
655+
if key:
659656
if isinstance(key, EdgeKey) and key.edge_type != EdgeType.default:
660657
edge_dict["edge_type"] = key.edge_type.value
658+
659+
if reported := data.get("reported"):
660+
edge_dict["reported"] = reported
661+
661662
edge_json = json.dumps(edge_dict) + "\n"
662663
self.tempfile.write(edge_json.encode())
663664
self.total_lines += 1

fixlib/tox.ini

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ commands = flake8 --verbose
2525
commands= pytest
2626

2727
[testenv:black]
28-
commands = black --line-length 120 --check --diff --target-version py39 .
28+
commands = black --line-length 120 --check --diff --target-version py311 .
2929

3030
[testenv:mypy]
31-
commands= mypy --install-types --non-interactive --python-version 3.9 --strict fixlib
31+
commands= mypy --install-types --non-interactive --python-version 3.11 --strict fixlib

0 commit comments

Comments
 (0)