cartography 0.103.0__py3-none-any.whl → 0.104.0rc1__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.

Potentially problematic release.


This version of cartography might be problematic. Click here for more details.

Files changed (33) hide show
  1. cartography/_version.py +2 -2
  2. cartography/cli.py +21 -3
  3. cartography/config.py +4 -0
  4. cartography/intel/anthropic/__init__.py +62 -0
  5. cartography/intel/anthropic/apikeys.py +72 -0
  6. cartography/intel/anthropic/users.py +75 -0
  7. cartography/intel/anthropic/util.py +51 -0
  8. cartography/intel/anthropic/workspaces.py +95 -0
  9. cartography/intel/aws/ec2/load_balancer_v2s.py +4 -1
  10. cartography/intel/aws/secretsmanager.py +136 -3
  11. cartography/intel/aws/ssm.py +71 -0
  12. cartography/intel/openai/adminapikeys.py +1 -2
  13. cartography/intel/openai/apikeys.py +1 -1
  14. cartography/intel/openai/projects.py +4 -1
  15. cartography/intel/openai/serviceaccounts.py +1 -1
  16. cartography/intel/openai/users.py +0 -3
  17. cartography/intel/openai/util.py +17 -1
  18. cartography/models/anthropic/__init__.py +0 -0
  19. cartography/models/anthropic/apikey.py +90 -0
  20. cartography/models/anthropic/organization.py +19 -0
  21. cartography/models/anthropic/user.py +48 -0
  22. cartography/models/anthropic/workspace.py +90 -0
  23. cartography/models/aws/secretsmanager/__init__.py +0 -0
  24. cartography/models/aws/secretsmanager/secret_version.py +116 -0
  25. cartography/models/aws/ssm/parameters.py +84 -0
  26. cartography/models/openai/project.py +20 -1
  27. cartography/sync.py +2 -0
  28. {cartography-0.103.0.dist-info → cartography-0.104.0rc1.dist-info}/METADATA +4 -4
  29. {cartography-0.103.0.dist-info → cartography-0.104.0rc1.dist-info}/RECORD +33 -20
  30. {cartography-0.103.0.dist-info → cartography-0.104.0rc1.dist-info}/WHEEL +1 -1
  31. {cartography-0.103.0.dist-info → cartography-0.104.0rc1.dist-info}/entry_points.txt +0 -0
  32. {cartography-0.103.0.dist-info → cartography-0.104.0rc1.dist-info}/licenses/LICENSE +0 -0
  33. {cartography-0.103.0.dist-info → cartography-0.104.0rc1.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,6 @@
1
+ import json
1
2
  import logging
3
+ import re
2
4
  from typing import Any
3
5
  from typing import Dict
4
6
  from typing import List
@@ -10,6 +12,7 @@ from cartography.client.core.tx import load
10
12
  from cartography.graph.job import GraphJob
11
13
  from cartography.models.aws.ssm.instance_information import SSMInstanceInformationSchema
12
14
  from cartography.models.aws.ssm.instance_patch import SSMInstancePatchSchema
15
+ from cartography.models.aws.ssm.parameters import SSMParameterSchema
13
16
  from cartography.util import aws_handle_regions
14
17
  from cartography.util import dict_date_to_epoch
15
18
  from cartography.util import timeit
@@ -107,6 +110,42 @@ def transform_instance_patches(data_list: List[Dict[str, Any]]) -> List[Dict[str
107
110
  return data_list
108
111
 
109
112
 
113
+ @timeit
114
+ @aws_handle_regions
115
+ def get_ssm_parameters(
116
+ boto3_session: boto3.session.Session,
117
+ region: str,
118
+ ) -> List[Dict[str, Any]]:
119
+ client = boto3_session.client("ssm", region_name=region)
120
+ paginator = client.get_paginator("describe_parameters")
121
+ ssm_parameters_data: List[Dict[str, Any]] = []
122
+ for page in paginator.paginate(PaginationConfig={"PageSize": 50}):
123
+ ssm_parameters_data.extend(page.get("Parameters", []))
124
+ return ssm_parameters_data
125
+
126
+
127
+ def transform_ssm_parameters(
128
+ raw_parameters_data: List[Dict[str, Any]],
129
+ ) -> List[Dict[str, Any]]:
130
+ transformed_list: List[Dict[str, Any]] = []
131
+ for param in raw_parameters_data:
132
+ param["LastModifiedDate"] = dict_date_to_epoch(param, "LastModifiedDate")
133
+ param["PoliciesJson"] = json.dumps(param.get("Policies", []))
134
+ # KMSKey uses shorter UUID as their primary id
135
+ # SSM Parameters, when encrypted, reference KMS keys using their full ARNs in the KeyId field
136
+ # Adding a param to match on the id property of the target node
137
+ if param.get("Type") == "SecureString" and param.get("KeyId") is not None:
138
+ match = re.match(r".*key/(.*)$", param["KeyId"])
139
+ if match:
140
+ param["KMSKeyIdShort"] = match.group(1)
141
+ else:
142
+ param["KMSKeyIdShort"] = None
143
+ else:
144
+ param["KMSKeyIdShort"] = None
145
+ transformed_list.append(param)
146
+ return transformed_list
147
+
148
+
110
149
  @timeit
111
150
  def load_instance_information(
112
151
  neo4j_session: neo4j.Session,
@@ -143,6 +182,24 @@ def load_instance_patches(
143
182
  )
144
183
 
145
184
 
185
+ @timeit
186
+ def load_ssm_parameters(
187
+ neo4j_session: neo4j.Session,
188
+ data: List[Dict[str, Any]],
189
+ region: str,
190
+ current_aws_account_id: str,
191
+ aws_update_tag: int,
192
+ ) -> None:
193
+ load(
194
+ neo4j_session,
195
+ SSMParameterSchema(),
196
+ data,
197
+ lastupdated=aws_update_tag,
198
+ Region=region,
199
+ AWS_ID=current_aws_account_id,
200
+ )
201
+
202
+
146
203
  @timeit
147
204
  def cleanup_ssm(
148
205
  neo4j_session: neo4j.Session,
@@ -156,6 +213,9 @@ def cleanup_ssm(
156
213
  GraphJob.from_node_schema(SSMInstancePatchSchema(), common_job_parameters).run(
157
214
  neo4j_session,
158
215
  )
216
+ GraphJob.from_node_schema(SSMParameterSchema(), common_job_parameters).run(
217
+ neo4j_session,
218
+ )
159
219
 
160
220
 
161
221
  @timeit
@@ -193,4 +253,15 @@ def sync(
193
253
  current_aws_account_id,
194
254
  update_tag,
195
255
  )
256
+
257
+ data = get_ssm_parameters(boto3_session, region)
258
+ data = transform_ssm_parameters(data)
259
+ load_ssm_parameters(
260
+ neo4j_session,
261
+ data,
262
+ region,
263
+ current_aws_account_id,
264
+ update_tag,
265
+ )
266
+
196
267
  cleanup_ssm(neo4j_session, common_job_parameters)
@@ -23,7 +23,7 @@ def sync(
23
23
  api_session: requests.Session,
24
24
  common_job_parameters: Dict[str, Any],
25
25
  ORG_ID: str,
26
- ) -> List[Dict]:
26
+ ) -> None:
27
27
  adminapikeys = get(
28
28
  api_session,
29
29
  common_job_parameters["BASE_URL"],
@@ -36,7 +36,6 @@ def sync(
36
36
  common_job_parameters["UPDATE_TAG"],
37
37
  )
38
38
  cleanup(neo4j_session, common_job_parameters)
39
- return adminapikeys
40
39
 
41
40
 
42
41
  @timeit
@@ -77,7 +77,7 @@ def load_apikeys(
77
77
  project_id: str,
78
78
  update_tag: int,
79
79
  ) -> None:
80
- logger.info("Loading %d OpenAI Project APIKey into Neo4j.", len(data))
80
+ logger.info("Loading %d OpenAI APIKey into Neo4j.", len(data))
81
81
  load(
82
82
  neo4j_session,
83
83
  OpenAIApiKeySchema(),
@@ -30,12 +30,15 @@ def sync(
30
30
  )
31
31
  for project in projects:
32
32
  project["users"] = []
33
+ project["admins"] = []
33
34
  for user in get_project_users(
34
35
  api_session,
35
36
  common_job_parameters["BASE_URL"],
36
37
  project["id"],
37
38
  ):
38
39
  project["users"].append(user["id"])
40
+ if user["role"] == "owner":
41
+ project["admins"].append(user["id"])
39
42
  load_projects(neo4j_session, projects, ORG_ID, common_job_parameters["UPDATE_TAG"])
40
43
  cleanup(neo4j_session, common_job_parameters)
41
44
  return projects
@@ -75,7 +78,7 @@ def load_projects(
75
78
  ORG_ID: str,
76
79
  update_tag: int,
77
80
  ) -> None:
78
- logger.info("Loading %d OpenAIProjectSchema into Neo4j.", len(data))
81
+ logger.info("Loading %d OpenAI Projects into Neo4j.", len(data))
79
82
  load(
80
83
  neo4j_session,
81
84
  OpenAIProjectSchema(),
@@ -63,7 +63,7 @@ def load_serviceaccounts(
63
63
  project_id: str,
64
64
  update_tag: int,
65
65
  ) -> None:
66
- logger.info("Loading %d OpenAI ProjectServiceAccount into Neo4j.", len(data))
66
+ logger.info("Loading %d OpenAI ServiceAccount into Neo4j.", len(data))
67
67
  load(
68
68
  neo4j_session,
69
69
  OpenAIServiceAccountSchema(),
@@ -70,9 +70,6 @@ def load_users(
70
70
  def cleanup(
71
71
  neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
72
72
  ) -> None:
73
- GraphJob.from_node_schema(OpenAIOrganizationSchema(), common_job_parameters).run(
74
- neo4j_session
75
- )
76
73
  GraphJob.from_node_schema(OpenAIUserSchema(), common_job_parameters).run(
77
74
  neo4j_session
78
75
  )
@@ -10,7 +10,23 @@ def paginated_get(
10
10
  timeout: tuple[int, int],
11
11
  after: str | None = None,
12
12
  ) -> Generator[dict[str, Any], None, None]:
13
- # DOC
13
+ """Helper function to get paginated data from the OpenAI API.
14
+
15
+ This function handles the pagination of the API requests and returns
16
+ the results as a generator. It will continue to make requests until
17
+ all pages of data have been retrieved. The results are returned as a
18
+ list of dictionaries, where each dictionary represents a single
19
+ entity.
20
+
21
+ Args:
22
+ api_session (requests.Session): The requests session to use for making API calls.
23
+ url (str): The URL to make the API call to.
24
+ timeout (tuple[int, int]): The timeout for the API call.
25
+ after (str | None): The ID of the last item retrieved in the previous request.
26
+ If None, the first page of results will be retrieved.
27
+ Returns:
28
+ Generator[dict[str, Any], None, None]: A generator yielding dictionaries representing the results.
29
+ """
14
30
  params = {"after": after} if after else {}
15
31
  req = api_session.get(
16
32
  url,
File without changes
@@ -0,0 +1,90 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import OtherRelationships
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class AnthropicApiKeyNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef("id")
17
+ name: PropertyRef = PropertyRef("name")
18
+ status: PropertyRef = PropertyRef("status")
19
+ created_at: PropertyRef = PropertyRef("created_at")
20
+ last_used_at: PropertyRef = PropertyRef("last_used_at")
21
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class AnthropicApiKeyToOrganizationRelProperties(CartographyRelProperties):
26
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ # (:AnthropicOrganization)-[:RESOURCE]->(:AnthropicApiKey)
31
+ class AnthropicApiKeyToOrganizationRel(CartographyRelSchema):
32
+ target_node_label: str = "AnthropicOrganization"
33
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
34
+ {"id": PropertyRef("ORG_ID", set_in_kwargs=True)},
35
+ )
36
+ direction: LinkDirection = LinkDirection.INWARD
37
+ rel_label: str = "RESOURCE"
38
+ properties: AnthropicApiKeyToOrganizationRelProperties = (
39
+ AnthropicApiKeyToOrganizationRelProperties()
40
+ )
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class AnthropicApiKeyToUserRelProperties(CartographyRelProperties):
45
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ # (:AnthropicUser)-[:OWNS]->(:AnthropicApiKey)
50
+ class AnthropicApiKeyToUserRel(CartographyRelSchema):
51
+ target_node_label: str = "AnthropicUser"
52
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
53
+ {"id": PropertyRef("created_by.id")},
54
+ )
55
+ direction: LinkDirection = LinkDirection.INWARD
56
+ rel_label: str = "OWNS"
57
+ properties: AnthropicApiKeyToUserRelProperties = (
58
+ AnthropicApiKeyToUserRelProperties()
59
+ )
60
+
61
+
62
+ @dataclass(frozen=True)
63
+ class AnthropicApiKeyToWorkspaceRelProperties(CartographyRelProperties):
64
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
65
+
66
+
67
+ @dataclass(frozen=True)
68
+ # (:AnthropicWorkspace)-[:CONTAINS]->(:AnthropicApiKey)
69
+ class AnthropicApiKeyToWorkspaceRel(CartographyRelSchema):
70
+ target_node_label: str = "AnthropicWorkspace"
71
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
72
+ {"id": PropertyRef("workspace_id")},
73
+ )
74
+ direction: LinkDirection = LinkDirection.INWARD
75
+ rel_label: str = "CONTAINS"
76
+ properties: AnthropicApiKeyToWorkspaceRelProperties = (
77
+ AnthropicApiKeyToWorkspaceRelProperties()
78
+ )
79
+
80
+
81
+ @dataclass(frozen=True)
82
+ class AnthropicApiKeySchema(CartographyNodeSchema):
83
+ label: str = "AnthropicApiKey"
84
+ properties: AnthropicApiKeyNodeProperties = AnthropicApiKeyNodeProperties()
85
+ sub_resource_relationship: AnthropicApiKeyToOrganizationRel = (
86
+ AnthropicApiKeyToOrganizationRel()
87
+ )
88
+ other_relationships: OtherRelationships = OtherRelationships(
89
+ [AnthropicApiKeyToUserRel(), AnthropicApiKeyToWorkspaceRel()],
90
+ )
@@ -0,0 +1,19 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class AnthropicOrganizationNodeProperties(CartographyNodeProperties):
10
+ id: PropertyRef = PropertyRef("id")
11
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class AnthropicOrganizationSchema(CartographyNodeSchema):
16
+ label: str = "AnthropicOrganization"
17
+ properties: AnthropicOrganizationNodeProperties = (
18
+ AnthropicOrganizationNodeProperties()
19
+ )
@@ -0,0 +1,48 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import TargetNodeMatcher
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class AnthropicUserNodeProperties(CartographyNodeProperties):
15
+ id: PropertyRef = PropertyRef("id")
16
+ name: PropertyRef = PropertyRef("name")
17
+ email: PropertyRef = PropertyRef("email", extra_index=True)
18
+ role: PropertyRef = PropertyRef("role")
19
+ added_at: PropertyRef = PropertyRef("added_at")
20
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class AnthropicUserToOrganizationRelProperties(CartographyRelProperties):
25
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
26
+
27
+
28
+ @dataclass(frozen=True)
29
+ # (:AnthropicOrganization)-[:RESOURCE]->(:AnthropicUser)
30
+ class AnthropicUserToOrganizationRel(CartographyRelSchema):
31
+ target_node_label: str = "AnthropicOrganization"
32
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
33
+ {"id": PropertyRef("ORG_ID", set_in_kwargs=True)},
34
+ )
35
+ direction: LinkDirection = LinkDirection.INWARD
36
+ rel_label: str = "RESOURCE"
37
+ properties: AnthropicUserToOrganizationRelProperties = (
38
+ AnthropicUserToOrganizationRelProperties()
39
+ )
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class AnthropicUserSchema(CartographyNodeSchema):
44
+ label: str = "AnthropicUser"
45
+ properties: AnthropicUserNodeProperties = AnthropicUserNodeProperties()
46
+ sub_resource_relationship: AnthropicUserToOrganizationRel = (
47
+ AnthropicUserToOrganizationRel()
48
+ )
@@ -0,0 +1,90 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import OtherRelationships
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class AnthropicWorkspaceNodeProperties(CartographyNodeProperties):
16
+ id: PropertyRef = PropertyRef("id")
17
+ name: PropertyRef = PropertyRef("name")
18
+ created_at: PropertyRef = PropertyRef("created_at")
19
+ archived_at: PropertyRef = PropertyRef("archived_at")
20
+ display_color: PropertyRef = PropertyRef("display_color")
21
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
22
+
23
+
24
+ @dataclass(frozen=True)
25
+ class AnthropicWorkspaceToOrganizationRelProperties(CartographyRelProperties):
26
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
27
+
28
+
29
+ @dataclass(frozen=True)
30
+ # (:AnthropicOrganization)-[:RESOURCE]->(:AnthropicWorkspace)
31
+ class AnthropicWorkspaceToOrganizationRel(CartographyRelSchema):
32
+ target_node_label: str = "AnthropicOrganization"
33
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
34
+ {"id": PropertyRef("ORG_ID", set_in_kwargs=True)},
35
+ )
36
+ direction: LinkDirection = LinkDirection.INWARD
37
+ rel_label: str = "RESOURCE"
38
+ properties: AnthropicWorkspaceToOrganizationRelProperties = (
39
+ AnthropicWorkspaceToOrganizationRelProperties()
40
+ )
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class AnthropicWorkspaceToUserRelProperties(CartographyRelProperties):
45
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ # (:AnthropicUser)-[:MEMBER_OF]->(:AnthropicWorkspace)
50
+ class AnthropicWorkspaceToUserRel(CartographyRelSchema):
51
+ target_node_label: str = "AnthropicUser"
52
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
53
+ {"id": PropertyRef("users", one_to_many=True)},
54
+ )
55
+ direction: LinkDirection = LinkDirection.INWARD
56
+ rel_label: str = "MEMBER_OF"
57
+ properties: AnthropicWorkspaceToUserRelProperties = (
58
+ AnthropicWorkspaceToUserRelProperties()
59
+ )
60
+
61
+
62
+ @dataclass(frozen=True)
63
+ class AnthropicWorkspaceToUserAdminRelProperties(CartographyRelProperties):
64
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
65
+
66
+
67
+ @dataclass(frozen=True)
68
+ # (:AnthropicUser)-[:ADMIN_OF]->(:AnthropicWorkspace)
69
+ class AnthropicWorkspaceToUserAdminRel(CartographyRelSchema):
70
+ target_node_label: str = "AnthropicUser"
71
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
72
+ {"id": PropertyRef("admins", one_to_many=True)},
73
+ )
74
+ direction: LinkDirection = LinkDirection.INWARD
75
+ rel_label: str = "ADMIN_OF"
76
+ properties: AnthropicWorkspaceToUserAdminRelProperties = (
77
+ AnthropicWorkspaceToUserAdminRelProperties()
78
+ )
79
+
80
+
81
+ @dataclass(frozen=True)
82
+ class AnthropicWorkspaceSchema(CartographyNodeSchema):
83
+ label: str = "AnthropicWorkspace"
84
+ properties: AnthropicWorkspaceNodeProperties = AnthropicWorkspaceNodeProperties()
85
+ sub_resource_relationship: AnthropicWorkspaceToOrganizationRel = (
86
+ AnthropicWorkspaceToOrganizationRel()
87
+ )
88
+ other_relationships: OtherRelationships = OtherRelationships(
89
+ [AnthropicWorkspaceToUserRel(), AnthropicWorkspaceToUserAdminRel()],
90
+ )
File without changes
@@ -0,0 +1,116 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import OtherRelationships
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class SecretsManagerSecretVersionNodeProperties(CartographyNodeProperties):
16
+ """
17
+ Properties for AWS Secrets Manager Secret Version
18
+ """
19
+
20
+ # Align property names with the actual keys in the data
21
+ id: PropertyRef = PropertyRef("ARN")
22
+ arn: PropertyRef = PropertyRef("ARN", extra_index=True)
23
+ secret_id: PropertyRef = PropertyRef("SecretId")
24
+ version_id: PropertyRef = PropertyRef("VersionId")
25
+ version_stages: PropertyRef = PropertyRef("VersionStages")
26
+ created_date: PropertyRef = PropertyRef("CreatedDate")
27
+ region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
28
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
29
+ # Make KMS and tags properties without required=False parameter
30
+ kms_key_id: PropertyRef = PropertyRef("KmsKeyId")
31
+ tags: PropertyRef = PropertyRef("Tags")
32
+
33
+
34
+ @dataclass(frozen=True)
35
+ class SecretsManagerSecretVersionRelProperties(CartographyRelProperties):
36
+ """
37
+ Properties for relationships between Secret Version and other nodes
38
+ """
39
+
40
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
41
+
42
+
43
+ @dataclass(frozen=True)
44
+ class SecretsManagerSecretVersionToAWSAccountRel(CartographyRelSchema):
45
+ """
46
+ Relationship between Secret Version and AWS Account
47
+ """
48
+
49
+ target_node_label: str = "AWSAccount"
50
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
51
+ {"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
52
+ )
53
+ direction: LinkDirection = LinkDirection.INWARD
54
+ rel_label: str = "RESOURCE"
55
+ properties: SecretsManagerSecretVersionRelProperties = (
56
+ SecretsManagerSecretVersionRelProperties()
57
+ )
58
+
59
+
60
+ @dataclass(frozen=True)
61
+ class SecretsManagerSecretVersionToSecretRel(CartographyRelSchema):
62
+ """
63
+ Relationship between Secret Version and its parent Secret
64
+ """
65
+
66
+ target_node_label: str = "SecretsManagerSecret"
67
+ # Use only one matcher for the id field
68
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
69
+ {"id": PropertyRef("SecretId")},
70
+ )
71
+ direction: LinkDirection = LinkDirection.OUTWARD
72
+ rel_label: str = "VERSION_OF"
73
+ properties: SecretsManagerSecretVersionRelProperties = (
74
+ SecretsManagerSecretVersionRelProperties()
75
+ )
76
+
77
+
78
+ @dataclass(frozen=True)
79
+ class SecretsManagerSecretVersionToKMSKeyRel(CartographyRelSchema):
80
+ """
81
+ Relationship between Secret Version and its KMS key
82
+ Only created when KmsKeyId is present
83
+ """
84
+
85
+ target_node_label: str = "AWSKMSKey"
86
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
87
+ {"id": PropertyRef("KmsKeyId")},
88
+ )
89
+ direction: LinkDirection = LinkDirection.OUTWARD
90
+ rel_label: str = "ENCRYPTED_BY"
91
+ properties: SecretsManagerSecretVersionRelProperties = (
92
+ SecretsManagerSecretVersionRelProperties()
93
+ )
94
+ # Only create this relationship if KmsKeyId exists
95
+ conditional_match_property: str = "KmsKeyId"
96
+
97
+
98
+ @dataclass(frozen=True)
99
+ class SecretsManagerSecretVersionSchema(CartographyNodeSchema):
100
+ """
101
+ Schema for AWS Secrets Manager Secret Version
102
+ """
103
+
104
+ label: str = "SecretsManagerSecretVersion"
105
+ properties: SecretsManagerSecretVersionNodeProperties = (
106
+ SecretsManagerSecretVersionNodeProperties()
107
+ )
108
+ sub_resource_relationship: SecretsManagerSecretVersionToAWSAccountRel = (
109
+ SecretsManagerSecretVersionToAWSAccountRel()
110
+ )
111
+ other_relationships: OtherRelationships = OtherRelationships(
112
+ [
113
+ SecretsManagerSecretVersionToSecretRel(),
114
+ SecretsManagerSecretVersionToKMSKeyRel(),
115
+ ],
116
+ )
@@ -0,0 +1,84 @@
1
+ from dataclasses import dataclass
2
+
3
+ from cartography.models.core.common import PropertyRef
4
+ from cartography.models.core.nodes import CartographyNodeProperties
5
+ from cartography.models.core.nodes import CartographyNodeSchema
6
+ from cartography.models.core.relationships import CartographyRelProperties
7
+ from cartography.models.core.relationships import CartographyRelSchema
8
+ from cartography.models.core.relationships import LinkDirection
9
+ from cartography.models.core.relationships import make_target_node_matcher
10
+ from cartography.models.core.relationships import OtherRelationships
11
+ from cartography.models.core.relationships import TargetNodeMatcher
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class SSMParameterNodeProperties(CartographyNodeProperties):
16
+
17
+ arn: PropertyRef = PropertyRef("ARN", extra_index=True)
18
+ id: PropertyRef = PropertyRef("ARN")
19
+ name: PropertyRef = PropertyRef("Name")
20
+ description: PropertyRef = PropertyRef("Description")
21
+ type: PropertyRef = PropertyRef("Type")
22
+ keyid: PropertyRef = PropertyRef("KeyId")
23
+ kms_key_id_short: PropertyRef = PropertyRef("KMSKeyIdShort")
24
+ version: PropertyRef = PropertyRef("Version")
25
+ lastmodifieddate: PropertyRef = PropertyRef("LastModifiedDate")
26
+ tier: PropertyRef = PropertyRef("Tier")
27
+ lastmodifieduser: PropertyRef = PropertyRef("LastModifiedUser")
28
+ datatype: PropertyRef = PropertyRef("DataType")
29
+ allowedpattern: PropertyRef = PropertyRef("AllowedPattern")
30
+ policies_json: PropertyRef = PropertyRef("PoliciesJson")
31
+ region: PropertyRef = PropertyRef("Region", set_in_kwargs=True)
32
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
33
+
34
+
35
+ @dataclass(frozen=True)
36
+ class SSMParameterToAWSAccountRelProperties(CartographyRelProperties):
37
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class SSMParameterToAWSAccountRel(CartographyRelSchema):
42
+ target_node_label: str = "AWSAccount"
43
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
44
+ {"id": PropertyRef("AWS_ID", set_in_kwargs=True)},
45
+ )
46
+ direction: LinkDirection = LinkDirection.INWARD
47
+ rel_label: str = "RESOURCE"
48
+ properties: SSMParameterToAWSAccountRelProperties = (
49
+ SSMParameterToAWSAccountRelProperties()
50
+ )
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class SSMParameterToKMSKeyRelProperties(CartographyRelProperties):
55
+ lastupdated: PropertyRef = PropertyRef("lastupdated", set_in_kwargs=True)
56
+
57
+
58
+ @dataclass(frozen=True)
59
+ class SSMParameterToKMSKeyRel(CartographyRelSchema):
60
+ target_node_label: str = "KMSKey"
61
+ target_node_matcher: TargetNodeMatcher = make_target_node_matcher(
62
+ {
63
+ "id": PropertyRef("KMSKeyIdShort"),
64
+ }
65
+ )
66
+ direction: LinkDirection = LinkDirection.OUTWARD
67
+ rel_label: str = "ENCRYPTED_BY"
68
+ properties: SSMParameterToKMSKeyRelProperties = SSMParameterToKMSKeyRelProperties()
69
+
70
+
71
+ @dataclass(frozen=True)
72
+ class SSMParameterSchema(CartographyNodeSchema):
73
+
74
+ label: str = "SSMParameter"
75
+ properties: SSMParameterNodeProperties = SSMParameterNodeProperties()
76
+ sub_resource_relationship: SSMParameterToAWSAccountRel = (
77
+ SSMParameterToAWSAccountRel()
78
+ )
79
+
80
+ other_relationships: OtherRelationships = OtherRelationships(
81
+ [
82
+ SSMParameterToKMSKeyRel(),
83
+ ],
84
+ )