cartography 0.106.0rc1__py3-none-any.whl → 0.107.0__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.
- cartography/_version.py +2 -2
- cartography/cli.py +131 -2
- cartography/client/core/tx.py +62 -0
- cartography/config.py +42 -0
- cartography/driftdetect/cli.py +3 -2
- cartography/graph/cleanupbuilder.py +47 -0
- cartography/graph/job.py +42 -0
- cartography/graph/querybuilder.py +136 -2
- cartography/graph/statement.py +1 -1
- cartography/intel/airbyte/__init__.py +105 -0
- cartography/intel/airbyte/connections.py +120 -0
- cartography/intel/airbyte/destinations.py +81 -0
- cartography/intel/airbyte/organizations.py +59 -0
- cartography/intel/airbyte/sources.py +78 -0
- cartography/intel/airbyte/tags.py +64 -0
- cartography/intel/airbyte/users.py +106 -0
- cartography/intel/airbyte/util.py +122 -0
- cartography/intel/airbyte/workspaces.py +63 -0
- cartography/intel/aws/__init__.py +1 -0
- cartography/intel/aws/cloudtrail_management_events.py +364 -0
- cartography/intel/aws/cloudwatch.py +77 -0
- cartography/intel/aws/codebuild.py +132 -0
- cartography/intel/aws/ec2/subnets.py +1 -1
- cartography/intel/aws/ecs.py +17 -0
- cartography/intel/aws/efs.py +80 -0
- cartography/intel/aws/inspector.py +80 -61
- cartography/intel/aws/resources.py +4 -0
- cartography/intel/aws/sns.py +62 -2
- cartography/intel/entra/users.py +84 -42
- cartography/intel/scaleway/__init__.py +127 -0
- cartography/intel/scaleway/iam/__init__.py +0 -0
- cartography/intel/scaleway/iam/apikeys.py +71 -0
- cartography/intel/scaleway/iam/applications.py +71 -0
- cartography/intel/scaleway/iam/groups.py +71 -0
- cartography/intel/scaleway/iam/users.py +71 -0
- cartography/intel/scaleway/instances/__init__.py +0 -0
- cartography/intel/scaleway/instances/flexibleips.py +86 -0
- cartography/intel/scaleway/instances/instances.py +92 -0
- cartography/intel/scaleway/projects.py +79 -0
- cartography/intel/scaleway/storage/__init__.py +0 -0
- cartography/intel/scaleway/storage/snapshots.py +86 -0
- cartography/intel/scaleway/storage/volumes.py +84 -0
- cartography/intel/scaleway/utils.py +37 -0
- cartography/intel/sentinelone/__init__.py +69 -0
- cartography/intel/sentinelone/account.py +140 -0
- cartography/intel/sentinelone/agent.py +139 -0
- cartography/intel/sentinelone/api.py +113 -0
- cartography/intel/sentinelone/application.py +248 -0
- cartography/intel/sentinelone/utils.py +28 -0
- cartography/models/airbyte/__init__.py +0 -0
- cartography/models/airbyte/connection.py +138 -0
- cartography/models/airbyte/destination.py +75 -0
- cartography/models/airbyte/organization.py +19 -0
- cartography/models/airbyte/source.py +75 -0
- cartography/models/airbyte/stream.py +74 -0
- cartography/models/airbyte/tag.py +69 -0
- cartography/models/airbyte/user.py +111 -0
- cartography/models/airbyte/workspace.py +46 -0
- cartography/models/aws/cloudtrail/management_events.py +64 -0
- cartography/models/aws/cloudwatch/log_metric_filter.py +79 -0
- cartography/models/aws/codebuild/__init__.py +0 -0
- cartography/models/aws/codebuild/project.py +49 -0
- cartography/models/aws/ec2/networkinterfaces.py +2 -0
- cartography/models/aws/ec2/subnet_instance.py +2 -0
- cartography/models/aws/ec2/subnet_networkinterface.py +2 -0
- cartography/models/aws/ecs/containers.py +19 -0
- cartography/models/aws/ecs/task_definitions.py +38 -0
- cartography/models/aws/ecs/tasks.py +24 -1
- cartography/models/aws/efs/access_point.py +77 -0
- cartography/models/aws/inspector/findings.py +37 -0
- cartography/models/aws/inspector/packages.py +1 -31
- cartography/models/aws/sns/topic_subscription.py +74 -0
- cartography/models/core/common.py +1 -0
- cartography/models/core/relationships.py +44 -0
- cartography/models/entra/user.py +17 -51
- cartography/models/scaleway/__init__.py +0 -0
- cartography/models/scaleway/iam/__init__.py +0 -0
- cartography/models/scaleway/iam/apikey.py +96 -0
- cartography/models/scaleway/iam/application.py +52 -0
- cartography/models/scaleway/iam/group.py +95 -0
- cartography/models/scaleway/iam/user.py +60 -0
- cartography/models/scaleway/instance/__init__.py +0 -0
- cartography/models/scaleway/instance/flexibleip.py +52 -0
- cartography/models/scaleway/instance/instance.py +118 -0
- cartography/models/scaleway/organization.py +19 -0
- cartography/models/scaleway/project.py +48 -0
- cartography/models/scaleway/storage/__init__.py +0 -0
- cartography/models/scaleway/storage/snapshot.py +78 -0
- cartography/models/scaleway/storage/volume.py +51 -0
- cartography/models/sentinelone/__init__.py +1 -0
- cartography/models/sentinelone/account.py +40 -0
- cartography/models/sentinelone/agent.py +50 -0
- cartography/models/sentinelone/application.py +44 -0
- cartography/models/sentinelone/application_version.py +96 -0
- cartography/sync.py +11 -4
- {cartography-0.106.0rc1.dist-info → cartography-0.107.0.dist-info}/METADATA +20 -16
- {cartography-0.106.0rc1.dist-info → cartography-0.107.0.dist-info}/RECORD +101 -36
- {cartography-0.106.0rc1.dist-info → cartography-0.107.0.dist-info}/WHEEL +0 -0
- {cartography-0.106.0rc1.dist-info → cartography-0.107.0.dist-info}/entry_points.txt +0 -0
- {cartography-0.106.0rc1.dist-info → cartography-0.107.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.106.0rc1.dist-info → cartography-0.107.0.dist-info}/top_level.txt +0 -0
cartography/intel/entra/users.py
CHANGED
|
@@ -15,6 +15,51 @@ from cartography.util import timeit
|
|
|
15
15
|
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
17
17
|
|
|
18
|
+
# NOTE:
|
|
19
|
+
# Microsoft Graph imposes limits on the length of the $select clause as well as
|
|
20
|
+
# the number of properties that can be selected in a single request. In
|
|
21
|
+
# practice we have seen 400 Bad Request responses that bubble up as
|
|
22
|
+
# `Microsoft.SharePoint.Client.InvalidClientQueryException` once that limit is
|
|
23
|
+
# breached (Graph internally rewrites the next-link using a SharePoint style
|
|
24
|
+
# `id in (…)` filter which is then rejected).
|
|
25
|
+
#
|
|
26
|
+
# To avoid tripping this bug we only request a *core* subset of user attributes
|
|
27
|
+
# that are most commonly used in downstream analysis. The transform() function
|
|
28
|
+
# tolerates missing attributes (the generated MS Graph SDK simply returns
|
|
29
|
+
# `None` for properties that are not present in the payload), so fetching fewer
|
|
30
|
+
# fields is safe – we merely get more `null` values in the graph.
|
|
31
|
+
#
|
|
32
|
+
# If you need additional attributes in the future, append them here but keep the
|
|
33
|
+
# total character count of the comma-separated list comfortably below 500 and
|
|
34
|
+
# stay within the official v1.0 contract (beta-only fields cause similar
|
|
35
|
+
# failures). 20–25 fields is a good rule-of-thumb.
|
|
36
|
+
#
|
|
37
|
+
# References:
|
|
38
|
+
# • https://learn.microsoft.com/graph/query-parameters#select-parameter
|
|
39
|
+
# • https://learn.microsoft.com/graph/api/user-list?view=graph-rest-1.0
|
|
40
|
+
#
|
|
41
|
+
USER_SELECT_FIELDS = [
|
|
42
|
+
"id",
|
|
43
|
+
"userPrincipalName",
|
|
44
|
+
"displayName",
|
|
45
|
+
"givenName",
|
|
46
|
+
"surname",
|
|
47
|
+
"mail",
|
|
48
|
+
"mobilePhone",
|
|
49
|
+
"businessPhones",
|
|
50
|
+
"jobTitle",
|
|
51
|
+
"department",
|
|
52
|
+
"officeLocation",
|
|
53
|
+
"city",
|
|
54
|
+
"country",
|
|
55
|
+
"companyName",
|
|
56
|
+
"preferredLanguage",
|
|
57
|
+
"employeeId",
|
|
58
|
+
"employeeType",
|
|
59
|
+
"accountEnabled",
|
|
60
|
+
"ageGroup",
|
|
61
|
+
]
|
|
62
|
+
|
|
18
63
|
|
|
19
64
|
@timeit
|
|
20
65
|
async def get_tenant(client: GraphServiceClient) -> Organization:
|
|
@@ -27,14 +72,20 @@ async def get_tenant(client: GraphServiceClient) -> Organization:
|
|
|
27
72
|
|
|
28
73
|
@timeit
|
|
29
74
|
async def get_users(client: GraphServiceClient) -> list[User]:
|
|
75
|
+
"""Fetch all users with their manager reference in as few requests as possible.
|
|
76
|
+
|
|
77
|
+
We leverage `$expand=manager($select=id)` so the manager's *id* is hydrated
|
|
78
|
+
alongside every user record. This avoids making a second round-trip per
|
|
79
|
+
user – vastly reducing latency and eliminating the noisy 404s that occur
|
|
80
|
+
when a user has no manager assigned.
|
|
30
81
|
"""
|
|
31
|
-
|
|
32
|
-
"""
|
|
82
|
+
|
|
33
83
|
all_users: list[User] = []
|
|
34
84
|
request_configuration = client.users.UsersRequestBuilderGetRequestConfiguration(
|
|
35
85
|
query_parameters=client.users.UsersRequestBuilderGetQueryParameters(
|
|
36
|
-
# Request more items per page to reduce number of API calls
|
|
37
86
|
top=999,
|
|
87
|
+
select=USER_SELECT_FIELDS,
|
|
88
|
+
expand=["manager($select=id)"],
|
|
38
89
|
),
|
|
39
90
|
)
|
|
40
91
|
|
|
@@ -43,18 +94,32 @@ async def get_users(client: GraphServiceClient) -> list[User]:
|
|
|
43
94
|
all_users.extend(page.value)
|
|
44
95
|
if not page.odata_next_link:
|
|
45
96
|
break
|
|
46
|
-
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
page = await client.users.with_url(page.odata_next_link).get()
|
|
100
|
+
except Exception as e:
|
|
101
|
+
logger.error(
|
|
102
|
+
"Failed to fetch next page of Entra ID users – stopping pagination early: %s",
|
|
103
|
+
e,
|
|
104
|
+
)
|
|
105
|
+
break
|
|
47
106
|
|
|
48
107
|
return all_users
|
|
49
108
|
|
|
50
109
|
|
|
51
110
|
@timeit
|
|
111
|
+
# The manager reference is now embedded in the user objects courtesy of the
|
|
112
|
+
# `$expand` we added above, so we no longer need a separate `manager_map`.
|
|
52
113
|
def transform_users(users: list[User]) -> list[dict[str, Any]]:
|
|
53
|
-
"""
|
|
54
|
-
|
|
55
|
-
"""
|
|
114
|
+
"""Convert MS Graph SDK `User` models into dicts matching our schema."""
|
|
115
|
+
|
|
56
116
|
result: list[dict[str, Any]] = []
|
|
57
117
|
for user in users:
|
|
118
|
+
manager_id: str | None = None
|
|
119
|
+
if getattr(user, "manager", None) is not None:
|
|
120
|
+
# The SDK materialises `manager` as a DirectoryObject (or subclass)
|
|
121
|
+
manager_id = getattr(user.manager, "id", None)
|
|
122
|
+
|
|
58
123
|
transformed_user = {
|
|
59
124
|
"id": user.id,
|
|
60
125
|
"user_principal_name": user.user_principal_name,
|
|
@@ -62,47 +127,24 @@ def transform_users(users: list[User]) -> list[dict[str, Any]]:
|
|
|
62
127
|
"given_name": user.given_name,
|
|
63
128
|
"surname": user.surname,
|
|
64
129
|
"mail": user.mail,
|
|
65
|
-
"
|
|
66
|
-
"preferred_language": user.preferred_language,
|
|
67
|
-
"preferred_name": user.preferred_name,
|
|
68
|
-
"state": user.state,
|
|
69
|
-
"usage_location": user.usage_location,
|
|
70
|
-
"user_type": user.user_type,
|
|
71
|
-
"show_in_address_list": user.show_in_address_list,
|
|
72
|
-
"sign_in_sessions_valid_from_date_time": user.sign_in_sessions_valid_from_date_time,
|
|
73
|
-
"security_identifier": user.on_premises_security_identifier,
|
|
74
|
-
"account_enabled": user.account_enabled,
|
|
75
|
-
"age_group": user.age_group,
|
|
130
|
+
"mobile_phone": user.mobile_phone,
|
|
76
131
|
"business_phones": user.business_phones,
|
|
132
|
+
"job_title": user.job_title,
|
|
133
|
+
"department": user.department,
|
|
134
|
+
"office_location": user.office_location,
|
|
77
135
|
"city": user.city,
|
|
78
|
-
"
|
|
79
|
-
"consent_provided_for_minor": user.consent_provided_for_minor,
|
|
136
|
+
"state": user.state,
|
|
80
137
|
"country": user.country,
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"deleted_date_time": user.deleted_date_time,
|
|
84
|
-
"department": user.department,
|
|
138
|
+
"company_name": user.company_name,
|
|
139
|
+
"preferred_language": user.preferred_language,
|
|
85
140
|
"employee_id": user.employee_id,
|
|
86
141
|
"employee_type": user.employee_type,
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"is_management_restricted": user.is_management_restricted,
|
|
91
|
-
"is_resource_account": user.is_resource_account,
|
|
92
|
-
"job_title": user.job_title,
|
|
93
|
-
"last_password_change_date_time": user.last_password_change_date_time,
|
|
94
|
-
"mail_nickname": user.mail_nickname,
|
|
95
|
-
"office_location": user.office_location,
|
|
96
|
-
"on_premises_distinguished_name": user.on_premises_distinguished_name,
|
|
97
|
-
"on_premises_domain_name": user.on_premises_domain_name,
|
|
98
|
-
"on_premises_immutable_id": user.on_premises_immutable_id,
|
|
99
|
-
"on_premises_last_sync_date_time": user.on_premises_last_sync_date_time,
|
|
100
|
-
"on_premises_sam_account_name": user.on_premises_sam_account_name,
|
|
101
|
-
"on_premises_security_identifier": user.on_premises_security_identifier,
|
|
102
|
-
"on_premises_sync_enabled": user.on_premises_sync_enabled,
|
|
103
|
-
"on_premises_user_principal_name": user.on_premises_user_principal_name,
|
|
142
|
+
"account_enabled": user.account_enabled,
|
|
143
|
+
"age_group": user.age_group,
|
|
144
|
+
"manager_id": manager_id,
|
|
104
145
|
}
|
|
105
146
|
result.append(transformed_user)
|
|
147
|
+
|
|
106
148
|
return result
|
|
107
149
|
|
|
108
150
|
|
|
@@ -198,7 +240,7 @@ async def sync_entra_users(
|
|
|
198
240
|
credential, scopes=["https://graph.microsoft.com/.default"]
|
|
199
241
|
)
|
|
200
242
|
|
|
201
|
-
#
|
|
243
|
+
# Fetch tenant and users (with manager reference already populated by `$expand`)
|
|
202
244
|
tenant = await get_tenant(client)
|
|
203
245
|
users = await get_users(client)
|
|
204
246
|
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import neo4j
|
|
4
|
+
import scaleway
|
|
5
|
+
|
|
6
|
+
import cartography.intel.scaleway.iam.apikeys
|
|
7
|
+
import cartography.intel.scaleway.iam.applications
|
|
8
|
+
import cartography.intel.scaleway.iam.groups
|
|
9
|
+
import cartography.intel.scaleway.iam.users
|
|
10
|
+
import cartography.intel.scaleway.instances.flexibleips
|
|
11
|
+
import cartography.intel.scaleway.instances.instances
|
|
12
|
+
import cartography.intel.scaleway.projects
|
|
13
|
+
import cartography.intel.scaleway.storage.snapshots
|
|
14
|
+
import cartography.intel.scaleway.storage.volumes
|
|
15
|
+
from cartography.config import Config
|
|
16
|
+
from cartography.util import timeit
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@timeit
|
|
22
|
+
def start_scaleway_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
|
|
23
|
+
"""
|
|
24
|
+
If this module is configured, perform ingestion of Scaleway data. Otherwise warn and exit
|
|
25
|
+
:param neo4j_session: Neo4J session for database interface
|
|
26
|
+
:param config: A cartography.config object
|
|
27
|
+
:return: None
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
if (
|
|
31
|
+
not config.scaleway_access_key
|
|
32
|
+
or not config.scaleway_secret_key
|
|
33
|
+
or not config.scaleway_org
|
|
34
|
+
):
|
|
35
|
+
logger.info(
|
|
36
|
+
"Tailscale import is not configured - skipping this module. "
|
|
37
|
+
"See docs to configure.",
|
|
38
|
+
)
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
# Create client
|
|
42
|
+
client = scaleway.Client(
|
|
43
|
+
access_key=config.scaleway_access_key,
|
|
44
|
+
secret_key=config.scaleway_secret_key,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
common_job_parameters = {
|
|
48
|
+
"UPDATE_TAG": config.update_tag,
|
|
49
|
+
"ORG_ID": config.scaleway_org,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Organization level
|
|
53
|
+
projects = cartography.intel.scaleway.projects.sync(
|
|
54
|
+
neo4j_session,
|
|
55
|
+
client,
|
|
56
|
+
common_job_parameters,
|
|
57
|
+
org_id=config.scaleway_org,
|
|
58
|
+
update_tag=config.update_tag,
|
|
59
|
+
)
|
|
60
|
+
projects_id = [project["id"] for project in projects]
|
|
61
|
+
cartography.intel.scaleway.iam.users.sync(
|
|
62
|
+
neo4j_session,
|
|
63
|
+
client,
|
|
64
|
+
common_job_parameters,
|
|
65
|
+
org_id=config.scaleway_org,
|
|
66
|
+
update_tag=config.update_tag,
|
|
67
|
+
)
|
|
68
|
+
cartography.intel.scaleway.iam.applications.sync(
|
|
69
|
+
neo4j_session,
|
|
70
|
+
client,
|
|
71
|
+
common_job_parameters,
|
|
72
|
+
org_id=config.scaleway_org,
|
|
73
|
+
update_tag=config.update_tag,
|
|
74
|
+
)
|
|
75
|
+
cartography.intel.scaleway.iam.groups.sync(
|
|
76
|
+
neo4j_session,
|
|
77
|
+
client,
|
|
78
|
+
common_job_parameters,
|
|
79
|
+
org_id=config.scaleway_org,
|
|
80
|
+
update_tag=config.update_tag,
|
|
81
|
+
)
|
|
82
|
+
cartography.intel.scaleway.iam.apikeys.sync(
|
|
83
|
+
neo4j_session,
|
|
84
|
+
client,
|
|
85
|
+
common_job_parameters,
|
|
86
|
+
org_id=config.scaleway_org,
|
|
87
|
+
update_tag=config.update_tag,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Storage
|
|
91
|
+
cartography.intel.scaleway.storage.volumes.sync(
|
|
92
|
+
neo4j_session,
|
|
93
|
+
client,
|
|
94
|
+
common_job_parameters,
|
|
95
|
+
org_id=config.scaleway_org,
|
|
96
|
+
projects_id=projects_id,
|
|
97
|
+
update_tag=config.update_tag,
|
|
98
|
+
)
|
|
99
|
+
cartography.intel.scaleway.storage.snapshots.sync(
|
|
100
|
+
neo4j_session,
|
|
101
|
+
client,
|
|
102
|
+
common_job_parameters,
|
|
103
|
+
org_id=config.scaleway_org,
|
|
104
|
+
projects_id=projects_id,
|
|
105
|
+
update_tag=config.update_tag,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Instances
|
|
109
|
+
# DISABLED due to https://github.com/scaleway/scaleway-sdk-python/issues/1040
|
|
110
|
+
"""
|
|
111
|
+
cartography.intel.scaleway.instances.flexibleips.sync(
|
|
112
|
+
neo4j_session,
|
|
113
|
+
client,
|
|
114
|
+
common_job_parameters,
|
|
115
|
+
org_id=config.scaleway_org,
|
|
116
|
+
projects_id=projects_id,
|
|
117
|
+
update_tag=config.update_tag,
|
|
118
|
+
)
|
|
119
|
+
"""
|
|
120
|
+
cartography.intel.scaleway.instances.instances.sync(
|
|
121
|
+
neo4j_session,
|
|
122
|
+
client,
|
|
123
|
+
common_job_parameters,
|
|
124
|
+
org_id=config.scaleway_org,
|
|
125
|
+
projects_id=projects_id,
|
|
126
|
+
update_tag=config.update_tag,
|
|
127
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import neo4j
|
|
5
|
+
import scaleway
|
|
6
|
+
from scaleway.iam.v1alpha1 import APIKey
|
|
7
|
+
from scaleway.iam.v1alpha1 import IamV1Alpha1API
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.intel.scaleway.utils import scaleway_obj_to_dict
|
|
12
|
+
from cartography.models.scaleway.iam.apikey import ScalewayApiKeySchema
|
|
13
|
+
from cartography.util import timeit
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@timeit
|
|
19
|
+
def sync(
|
|
20
|
+
neo4j_session: neo4j.Session,
|
|
21
|
+
client: scaleway.Client,
|
|
22
|
+
common_job_parameters: dict[str, Any],
|
|
23
|
+
org_id: str,
|
|
24
|
+
update_tag: int,
|
|
25
|
+
) -> None:
|
|
26
|
+
apikeys = get(client, org_id)
|
|
27
|
+
formatted_apikeys = transform_apikeys(apikeys)
|
|
28
|
+
load_apikeys(neo4j_session, formatted_apikeys, org_id, update_tag)
|
|
29
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@timeit
|
|
33
|
+
def get(
|
|
34
|
+
client: scaleway.Client,
|
|
35
|
+
org_id: str,
|
|
36
|
+
) -> list[APIKey]:
|
|
37
|
+
api = IamV1Alpha1API(client)
|
|
38
|
+
return api.list_api_keys_all(organization_id=org_id)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def transform_apikeys(apikeys: list[APIKey]) -> list[dict[str, Any]]:
|
|
42
|
+
formatted_apikeys = []
|
|
43
|
+
for apikey in apikeys:
|
|
44
|
+
formatted_apikeys.append(scaleway_obj_to_dict(apikey))
|
|
45
|
+
return formatted_apikeys
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@timeit
|
|
49
|
+
def load_apikeys(
|
|
50
|
+
neo4j_session: neo4j.Session,
|
|
51
|
+
data: list[dict[str, Any]],
|
|
52
|
+
org_id: str,
|
|
53
|
+
update_tag: int,
|
|
54
|
+
) -> None:
|
|
55
|
+
logger.info("Loading %d Scaleway ApiKeys into Neo4j.", len(data))
|
|
56
|
+
load(
|
|
57
|
+
neo4j_session,
|
|
58
|
+
ScalewayApiKeySchema(),
|
|
59
|
+
data,
|
|
60
|
+
lastupdated=update_tag,
|
|
61
|
+
ORG_ID=org_id,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@timeit
|
|
66
|
+
def cleanup(
|
|
67
|
+
neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
|
|
68
|
+
) -> None:
|
|
69
|
+
GraphJob.from_node_schema(ScalewayApiKeySchema(), common_job_parameters).run(
|
|
70
|
+
neo4j_session
|
|
71
|
+
)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import neo4j
|
|
5
|
+
import scaleway
|
|
6
|
+
from scaleway.iam.v1alpha1 import Application
|
|
7
|
+
from scaleway.iam.v1alpha1 import IamV1Alpha1API
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.intel.scaleway.utils import scaleway_obj_to_dict
|
|
12
|
+
from cartography.models.scaleway.iam.application import ScalewayApplicationSchema
|
|
13
|
+
from cartography.util import timeit
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@timeit
|
|
19
|
+
def sync(
|
|
20
|
+
neo4j_session: neo4j.Session,
|
|
21
|
+
client: scaleway.Client,
|
|
22
|
+
common_job_parameters: dict[str, Any],
|
|
23
|
+
org_id: str,
|
|
24
|
+
update_tag: int,
|
|
25
|
+
) -> None:
|
|
26
|
+
applications = get(client, org_id)
|
|
27
|
+
formatted_applications = transform_applications(applications)
|
|
28
|
+
load_applications(neo4j_session, formatted_applications, org_id, update_tag)
|
|
29
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@timeit
|
|
33
|
+
def get(
|
|
34
|
+
client: scaleway.Client,
|
|
35
|
+
org_id: str,
|
|
36
|
+
) -> list[Application]:
|
|
37
|
+
api = IamV1Alpha1API(client)
|
|
38
|
+
return api.list_applications_all(organization_id=org_id)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def transform_applications(applications: list[Application]) -> list[dict[str, Any]]:
|
|
42
|
+
formatted_applications = []
|
|
43
|
+
for application in applications:
|
|
44
|
+
formatted_applications.append(scaleway_obj_to_dict(application))
|
|
45
|
+
return formatted_applications
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@timeit
|
|
49
|
+
def load_applications(
|
|
50
|
+
neo4j_session: neo4j.Session,
|
|
51
|
+
data: list[dict[str, Any]],
|
|
52
|
+
org_id: str,
|
|
53
|
+
update_tag: int,
|
|
54
|
+
) -> None:
|
|
55
|
+
logger.info("Loading %d Scaleway Applications into Neo4j.", len(data))
|
|
56
|
+
load(
|
|
57
|
+
neo4j_session,
|
|
58
|
+
ScalewayApplicationSchema(),
|
|
59
|
+
data,
|
|
60
|
+
lastupdated=update_tag,
|
|
61
|
+
ORG_ID=org_id,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@timeit
|
|
66
|
+
def cleanup(
|
|
67
|
+
neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
|
|
68
|
+
) -> None:
|
|
69
|
+
GraphJob.from_node_schema(ScalewayApplicationSchema(), common_job_parameters).run(
|
|
70
|
+
neo4j_session
|
|
71
|
+
)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import neo4j
|
|
5
|
+
import scaleway
|
|
6
|
+
from scaleway.iam.v1alpha1 import Group
|
|
7
|
+
from scaleway.iam.v1alpha1 import IamV1Alpha1API
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.intel.scaleway.utils import scaleway_obj_to_dict
|
|
12
|
+
from cartography.models.scaleway.iam.group import ScalewayGroupSchema
|
|
13
|
+
from cartography.util import timeit
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@timeit
|
|
19
|
+
def sync(
|
|
20
|
+
neo4j_session: neo4j.Session,
|
|
21
|
+
client: scaleway.Client,
|
|
22
|
+
common_job_parameters: dict[str, Any],
|
|
23
|
+
org_id: str,
|
|
24
|
+
update_tag: int,
|
|
25
|
+
) -> None:
|
|
26
|
+
groups = get(client, org_id)
|
|
27
|
+
formatted_groups = transform_groups(groups)
|
|
28
|
+
load_groups(neo4j_session, formatted_groups, org_id, update_tag)
|
|
29
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@timeit
|
|
33
|
+
def get(
|
|
34
|
+
client: scaleway.Client,
|
|
35
|
+
org_id: str,
|
|
36
|
+
) -> list[Group]:
|
|
37
|
+
api = IamV1Alpha1API(client)
|
|
38
|
+
return api.list_groups_all(organization_id=org_id)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def transform_groups(groups: list[Group]) -> list[dict[str, Any]]:
|
|
42
|
+
formatted_groups = []
|
|
43
|
+
for group in groups:
|
|
44
|
+
formatted_groups.append(scaleway_obj_to_dict(group))
|
|
45
|
+
return formatted_groups
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@timeit
|
|
49
|
+
def load_groups(
|
|
50
|
+
neo4j_session: neo4j.Session,
|
|
51
|
+
data: list[dict[str, Any]],
|
|
52
|
+
org_id: str,
|
|
53
|
+
update_tag: int,
|
|
54
|
+
) -> None:
|
|
55
|
+
logger.info("Loading %d Scaleway Groups into Neo4j.", len(data))
|
|
56
|
+
load(
|
|
57
|
+
neo4j_session,
|
|
58
|
+
ScalewayGroupSchema(),
|
|
59
|
+
data,
|
|
60
|
+
lastupdated=update_tag,
|
|
61
|
+
ORG_ID=org_id,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@timeit
|
|
66
|
+
def cleanup(
|
|
67
|
+
neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
|
|
68
|
+
) -> None:
|
|
69
|
+
GraphJob.from_node_schema(ScalewayGroupSchema(), common_job_parameters).run(
|
|
70
|
+
neo4j_session
|
|
71
|
+
)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import neo4j
|
|
5
|
+
import scaleway
|
|
6
|
+
from scaleway.iam.v1alpha1 import IamV1Alpha1API
|
|
7
|
+
from scaleway.iam.v1alpha1 import User
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.intel.scaleway.utils import scaleway_obj_to_dict
|
|
12
|
+
from cartography.models.scaleway.iam.user import ScalewayUserSchema
|
|
13
|
+
from cartography.util import timeit
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@timeit
|
|
19
|
+
def sync(
|
|
20
|
+
neo4j_session: neo4j.Session,
|
|
21
|
+
client: scaleway.Client,
|
|
22
|
+
common_job_parameters: dict[str, Any],
|
|
23
|
+
org_id: str,
|
|
24
|
+
update_tag: int,
|
|
25
|
+
) -> None:
|
|
26
|
+
users = get(client, org_id)
|
|
27
|
+
formatted_users = transform_users(users)
|
|
28
|
+
load_users(neo4j_session, formatted_users, org_id, update_tag)
|
|
29
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@timeit
|
|
33
|
+
def get(
|
|
34
|
+
client: scaleway.Client,
|
|
35
|
+
org_id: str,
|
|
36
|
+
) -> list[User]:
|
|
37
|
+
api = IamV1Alpha1API(client)
|
|
38
|
+
return api.list_users_all(organization_id=org_id)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def transform_users(users: list[User]) -> list[dict[str, Any]]:
|
|
42
|
+
formatted_users = []
|
|
43
|
+
for user in users:
|
|
44
|
+
formatted_users.append(scaleway_obj_to_dict(user))
|
|
45
|
+
return formatted_users
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@timeit
|
|
49
|
+
def load_users(
|
|
50
|
+
neo4j_session: neo4j.Session,
|
|
51
|
+
data: list[dict[str, Any]],
|
|
52
|
+
org_id: str,
|
|
53
|
+
update_tag: int,
|
|
54
|
+
) -> None:
|
|
55
|
+
logger.info("Loading %d Scaleway Users into Neo4j.", len(data))
|
|
56
|
+
load(
|
|
57
|
+
neo4j_session,
|
|
58
|
+
ScalewayUserSchema(),
|
|
59
|
+
data,
|
|
60
|
+
lastupdated=update_tag,
|
|
61
|
+
ORG_ID=org_id,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@timeit
|
|
66
|
+
def cleanup(
|
|
67
|
+
neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
|
|
68
|
+
) -> None:
|
|
69
|
+
GraphJob.from_node_schema(ScalewayUserSchema(), common_job_parameters).run(
|
|
70
|
+
neo4j_session
|
|
71
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import neo4j
|
|
5
|
+
import scaleway
|
|
6
|
+
from scaleway.instance.v1 import InstanceV1API
|
|
7
|
+
from scaleway.instance.v1 import Ip
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.intel.scaleway.utils import DEFAULT_ZONE
|
|
12
|
+
from cartography.intel.scaleway.utils import scaleway_obj_to_dict
|
|
13
|
+
from cartography.models.scaleway.instance.flexibleip import ScalewayFlexibleIpSchema
|
|
14
|
+
from cartography.util import timeit
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@timeit
|
|
20
|
+
def sync(
|
|
21
|
+
neo4j_session: neo4j.Session,
|
|
22
|
+
client: scaleway.Client,
|
|
23
|
+
common_job_parameters: dict[str, Any],
|
|
24
|
+
org_id: str,
|
|
25
|
+
projects_id: list[str],
|
|
26
|
+
update_tag: int,
|
|
27
|
+
) -> None:
|
|
28
|
+
flexibleips = get(client, org_id)
|
|
29
|
+
flexibleips_by_project = transform_flexibleips(flexibleips)
|
|
30
|
+
load_flexibleips(neo4j_session, flexibleips_by_project, update_tag)
|
|
31
|
+
cleanup(neo4j_session, projects_id, common_job_parameters)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@timeit
|
|
35
|
+
def get(
|
|
36
|
+
client: scaleway.Client,
|
|
37
|
+
org_id: str,
|
|
38
|
+
) -> list[Ip]:
|
|
39
|
+
api = InstanceV1API(client)
|
|
40
|
+
return api.list_ips_all(organization=org_id, zone=DEFAULT_ZONE)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def transform_flexibleips(
|
|
44
|
+
flexibleips: list[Ip],
|
|
45
|
+
) -> dict[str, list[dict[str, Any]]]:
|
|
46
|
+
result: dict[str, list[dict[str, Any]]] = {}
|
|
47
|
+
for flexibleip in flexibleips:
|
|
48
|
+
project_id = flexibleip.project
|
|
49
|
+
formatted_flexibleip = scaleway_obj_to_dict(flexibleip)
|
|
50
|
+
result.setdefault(project_id, []).append(formatted_flexibleip)
|
|
51
|
+
return result
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@timeit
|
|
55
|
+
def load_flexibleips(
|
|
56
|
+
neo4j_session: neo4j.Session,
|
|
57
|
+
data: dict[str, list[dict[str, Any]]],
|
|
58
|
+
update_tag: int,
|
|
59
|
+
) -> None:
|
|
60
|
+
for project_id, flexibleips in data.items():
|
|
61
|
+
logger.info(
|
|
62
|
+
"Loading %d Scaleway Flexible IPs in project '%s' into Neo4j.",
|
|
63
|
+
len(flexibleips),
|
|
64
|
+
project_id,
|
|
65
|
+
)
|
|
66
|
+
load(
|
|
67
|
+
neo4j_session,
|
|
68
|
+
ScalewayFlexibleIpSchema(),
|
|
69
|
+
flexibleips,
|
|
70
|
+
lastupdated=update_tag,
|
|
71
|
+
PROJECT_ID=project_id,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@timeit
|
|
76
|
+
def cleanup(
|
|
77
|
+
neo4j_session: neo4j.Session,
|
|
78
|
+
projects_id: list[str],
|
|
79
|
+
common_job_parameters: dict[str, Any],
|
|
80
|
+
) -> None:
|
|
81
|
+
for project_id in projects_id:
|
|
82
|
+
scopped_job_parameters = common_job_parameters.copy()
|
|
83
|
+
scopped_job_parameters["PROJECT_ID"] = project_id
|
|
84
|
+
GraphJob.from_node_schema(
|
|
85
|
+
ScalewayFlexibleIpSchema(), scopped_job_parameters
|
|
86
|
+
).run(neo4j_session)
|