cartography 0.103.0rc1__py3-none-any.whl → 0.104.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 +97 -1
- cartography/config.py +28 -0
- cartography/graph/cleanupbuilder.py +151 -41
- cartography/intel/anthropic/__init__.py +62 -0
- cartography/intel/anthropic/apikeys.py +72 -0
- cartography/intel/anthropic/users.py +75 -0
- cartography/intel/anthropic/util.py +51 -0
- cartography/intel/anthropic/workspaces.py +95 -0
- cartography/intel/aws/cloudtrail.py +3 -38
- cartography/intel/aws/cloudwatch.py +93 -0
- cartography/intel/aws/ec2/load_balancer_v2s.py +4 -1
- cartography/intel/aws/resources.py +2 -0
- cartography/intel/aws/secretsmanager.py +150 -3
- cartography/intel/aws/ssm.py +71 -0
- cartography/intel/cloudflare/__init__.py +74 -0
- cartography/intel/cloudflare/accounts.py +57 -0
- cartography/intel/cloudflare/dnsrecords.py +64 -0
- cartography/intel/cloudflare/members.py +75 -0
- cartography/intel/cloudflare/roles.py +65 -0
- cartography/intel/cloudflare/zones.py +64 -0
- cartography/intel/entra/ou.py +21 -5
- cartography/intel/openai/__init__.py +86 -0
- cartography/intel/openai/adminapikeys.py +89 -0
- cartography/intel/openai/apikeys.py +96 -0
- cartography/intel/openai/projects.py +97 -0
- cartography/intel/openai/serviceaccounts.py +82 -0
- cartography/intel/openai/users.py +75 -0
- cartography/intel/openai/util.py +45 -0
- cartography/intel/tailscale/__init__.py +77 -0
- cartography/intel/tailscale/acls.py +146 -0
- cartography/intel/tailscale/devices.py +127 -0
- cartography/intel/tailscale/postureintegrations.py +81 -0
- cartography/intel/tailscale/tailnets.py +76 -0
- cartography/intel/tailscale/users.py +80 -0
- cartography/intel/tailscale/utils.py +132 -0
- cartography/models/anthropic/__init__.py +0 -0
- cartography/models/anthropic/apikey.py +90 -0
- cartography/models/anthropic/organization.py +19 -0
- cartography/models/anthropic/user.py +48 -0
- cartography/models/anthropic/workspace.py +90 -0
- cartography/models/aws/cloudtrail/trail.py +24 -0
- cartography/models/aws/cloudwatch/__init__.py +0 -0
- cartography/models/aws/cloudwatch/loggroup.py +52 -0
- cartography/models/aws/secretsmanager/__init__.py +0 -0
- cartography/models/aws/secretsmanager/secret_version.py +116 -0
- cartography/models/aws/ssm/parameters.py +84 -0
- cartography/models/cloudflare/__init__.py +0 -0
- cartography/models/cloudflare/account.py +25 -0
- cartography/models/cloudflare/dnsrecord.py +55 -0
- cartography/models/cloudflare/member.py +82 -0
- cartography/models/cloudflare/role.py +44 -0
- cartography/models/cloudflare/zone.py +59 -0
- cartography/models/core/nodes.py +15 -2
- cartography/models/openai/__init__.py +0 -0
- cartography/models/openai/adminapikey.py +90 -0
- cartography/models/openai/apikey.py +84 -0
- cartography/models/openai/organization.py +17 -0
- cartography/models/openai/project.py +89 -0
- cartography/models/openai/serviceaccount.py +50 -0
- cartography/models/openai/user.py +49 -0
- cartography/models/tailscale/__init__.py +0 -0
- cartography/models/tailscale/device.py +95 -0
- cartography/models/tailscale/group.py +86 -0
- cartography/models/tailscale/postureintegration.py +58 -0
- cartography/models/tailscale/tag.py +102 -0
- cartography/models/tailscale/tailnet.py +29 -0
- cartography/models/tailscale/user.py +52 -0
- cartography/sync.py +8 -0
- {cartography-0.103.0rc1.dist-info → cartography-0.104.0.dist-info}/METADATA +8 -4
- {cartography-0.103.0rc1.dist-info → cartography-0.104.0.dist-info}/RECORD +75 -19
- {cartography-0.103.0rc1.dist-info → cartography-0.104.0.dist-info}/WHEEL +1 -1
- {cartography-0.103.0rc1.dist-info → cartography-0.104.0.dist-info}/entry_points.txt +0 -0
- {cartography-0.103.0rc1.dist-info → cartography-0.104.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.103.0rc1.dist-info → cartography-0.104.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
from cloudflare import Cloudflare
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.models.cloudflare.dnsrecord import CloudflareDNSRecordSchema
|
|
12
|
+
from cartography.util import timeit
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
# Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
|
|
16
|
+
_TIMEOUT = (60, 60)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@timeit
|
|
20
|
+
def sync(
|
|
21
|
+
neo4j_session: neo4j.Session,
|
|
22
|
+
client: Cloudflare,
|
|
23
|
+
common_job_parameters: Dict[str, Any],
|
|
24
|
+
zone_id: str,
|
|
25
|
+
) -> None:
|
|
26
|
+
dnsrecords = get(client, zone_id)
|
|
27
|
+
load_dnsrecords(
|
|
28
|
+
neo4j_session,
|
|
29
|
+
dnsrecords,
|
|
30
|
+
zone_id,
|
|
31
|
+
common_job_parameters["UPDATE_TAG"],
|
|
32
|
+
)
|
|
33
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@timeit
|
|
37
|
+
def get(client: Cloudflare, zone_id: str) -> List[Dict[str, Any]]:
|
|
38
|
+
return [
|
|
39
|
+
dnsrecord.to_dict() for dnsrecord in client.dns.records.list(zone_id=zone_id)
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def load_dnsrecords(
|
|
44
|
+
neo4j_session: neo4j.Session,
|
|
45
|
+
data: List[Dict[str, Any]],
|
|
46
|
+
zone_id: str,
|
|
47
|
+
update_tag: int,
|
|
48
|
+
) -> None:
|
|
49
|
+
logger.info("Loading %d Cloudflare DNSRecords into Neo4j.", len(data))
|
|
50
|
+
load(
|
|
51
|
+
neo4j_session,
|
|
52
|
+
CloudflareDNSRecordSchema(),
|
|
53
|
+
data,
|
|
54
|
+
lastupdated=update_tag,
|
|
55
|
+
zone_id=zone_id,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def cleanup(
|
|
60
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
61
|
+
) -> None:
|
|
62
|
+
GraphJob.from_node_schema(CloudflareDNSRecordSchema(), common_job_parameters).run(
|
|
63
|
+
neo4j_session
|
|
64
|
+
)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
from cloudflare import Cloudflare
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.models.cloudflare.member import CloudflareMemberSchema
|
|
12
|
+
from cartography.util import timeit
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
# Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
|
|
16
|
+
_TIMEOUT = (60, 60)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@timeit
|
|
20
|
+
def sync(
|
|
21
|
+
neo4j_session: neo4j.Session,
|
|
22
|
+
client: Cloudflare,
|
|
23
|
+
common_job_parameters: Dict[str, Any],
|
|
24
|
+
account_id: str,
|
|
25
|
+
) -> None:
|
|
26
|
+
members = get(client, account_id)
|
|
27
|
+
transformed_members = transform_members(members)
|
|
28
|
+
load_members(
|
|
29
|
+
neo4j_session,
|
|
30
|
+
transformed_members,
|
|
31
|
+
account_id,
|
|
32
|
+
common_job_parameters["UPDATE_TAG"],
|
|
33
|
+
)
|
|
34
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@timeit
|
|
38
|
+
def get(client: Cloudflare, account_id: str) -> List[Dict[str, Any]]:
|
|
39
|
+
return [
|
|
40
|
+
member.to_dict()
|
|
41
|
+
for member in client.accounts.members.list(account_id=account_id)
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def load_members(
|
|
46
|
+
neo4j_session: neo4j.Session,
|
|
47
|
+
data: List[Dict[str, Any]],
|
|
48
|
+
account_id: str,
|
|
49
|
+
update_tag: int,
|
|
50
|
+
) -> None:
|
|
51
|
+
logger.info("Loading %d Cloudflare members into Neo4j.", len(data))
|
|
52
|
+
load(
|
|
53
|
+
neo4j_session,
|
|
54
|
+
CloudflareMemberSchema(),
|
|
55
|
+
data,
|
|
56
|
+
lastupdated=update_tag,
|
|
57
|
+
account_id=account_id,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def transform_members(data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
62
|
+
result: List[Dict[str, Any]] = []
|
|
63
|
+
for member in data:
|
|
64
|
+
member["roles_ids"] = [role["id"] for role in member.get("roles", [])]
|
|
65
|
+
member["policies_ids"] = [policy["id"] for policy in member.get("policies", [])]
|
|
66
|
+
result.append(member)
|
|
67
|
+
return result
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def cleanup(
|
|
71
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
72
|
+
) -> None:
|
|
73
|
+
GraphJob.from_node_schema(CloudflareMemberSchema(), common_job_parameters).run(
|
|
74
|
+
neo4j_session
|
|
75
|
+
)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
from cloudflare import Cloudflare
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.models.cloudflare.role import CloudflareRoleSchema
|
|
12
|
+
from cartography.util import timeit
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@timeit
|
|
18
|
+
def sync(
|
|
19
|
+
neo4j_session: neo4j.Session,
|
|
20
|
+
client: Cloudflare,
|
|
21
|
+
common_job_parameters: Dict[str, Any],
|
|
22
|
+
account_id: str,
|
|
23
|
+
) -> None:
|
|
24
|
+
roles = get(client, account_id)
|
|
25
|
+
load_roles(
|
|
26
|
+
neo4j_session,
|
|
27
|
+
roles,
|
|
28
|
+
account_id,
|
|
29
|
+
common_job_parameters["UPDATE_TAG"],
|
|
30
|
+
)
|
|
31
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@timeit
|
|
35
|
+
def get(
|
|
36
|
+
client: Cloudflare,
|
|
37
|
+
account_id: str,
|
|
38
|
+
) -> List[Dict[str, Any]]:
|
|
39
|
+
return [
|
|
40
|
+
role.to_dict() for role in client.accounts.roles.list(account_id=account_id)
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def load_roles(
|
|
45
|
+
neo4j_session: neo4j.Session,
|
|
46
|
+
data: List[Dict[str, Any]],
|
|
47
|
+
account_id: str,
|
|
48
|
+
update_tag: int,
|
|
49
|
+
) -> None:
|
|
50
|
+
logger.info("Loading %d Cloudflare roles into Neo4j.", len(data))
|
|
51
|
+
load(
|
|
52
|
+
neo4j_session,
|
|
53
|
+
CloudflareRoleSchema(),
|
|
54
|
+
data,
|
|
55
|
+
lastupdated=update_tag,
|
|
56
|
+
account_id=account_id,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def cleanup(
|
|
61
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
62
|
+
) -> None:
|
|
63
|
+
GraphJob.from_node_schema(CloudflareRoleSchema(), common_job_parameters).run(
|
|
64
|
+
neo4j_session
|
|
65
|
+
)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
from cloudflare import Cloudflare
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.models.cloudflare.zone import CloudflareZoneSchema
|
|
12
|
+
from cartography.util import timeit
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@timeit
|
|
18
|
+
def sync(
|
|
19
|
+
neo4j_session: neo4j.Session,
|
|
20
|
+
client: Cloudflare,
|
|
21
|
+
common_job_parameters: Dict[str, Any],
|
|
22
|
+
account_id: str,
|
|
23
|
+
) -> List[Dict]:
|
|
24
|
+
zones = get(client, account_id)
|
|
25
|
+
load_zones(
|
|
26
|
+
neo4j_session,
|
|
27
|
+
zones,
|
|
28
|
+
account_id,
|
|
29
|
+
common_job_parameters["UPDATE_TAG"],
|
|
30
|
+
)
|
|
31
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
32
|
+
return zones
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@timeit
|
|
36
|
+
def get(
|
|
37
|
+
client: Cloudflare,
|
|
38
|
+
account_id: str,
|
|
39
|
+
) -> List[Dict[str, Any]]:
|
|
40
|
+
return [zone.to_dict() for zone in client.zones.list(account=account_id)]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def load_zones(
|
|
44
|
+
neo4j_session: neo4j.Session,
|
|
45
|
+
data: List[Dict[str, Any]],
|
|
46
|
+
account_id: str,
|
|
47
|
+
update_tag: int,
|
|
48
|
+
) -> None:
|
|
49
|
+
logger.info("Loading %d Cloudflare zones into Neo4j.", len(data))
|
|
50
|
+
load(
|
|
51
|
+
neo4j_session,
|
|
52
|
+
CloudflareZoneSchema(),
|
|
53
|
+
data,
|
|
54
|
+
lastupdated=update_tag,
|
|
55
|
+
account_id=account_id,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def cleanup(
|
|
60
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
61
|
+
) -> None:
|
|
62
|
+
GraphJob.from_node_schema(CloudflareZoneSchema(), common_job_parameters).run(
|
|
63
|
+
neo4j_session
|
|
64
|
+
)
|
cartography/intel/entra/ou.py
CHANGED
|
@@ -22,12 +22,28 @@ async def get_entra_ous(client: GraphServiceClient) -> list[AdministrativeUnit]:
|
|
|
22
22
|
Get all OUs from Microsoft Graph API with pagination support
|
|
23
23
|
"""
|
|
24
24
|
all_units: list[AdministrativeUnit] = []
|
|
25
|
-
request = client.directory.administrative_units.request()
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
# Initialize first page request
|
|
27
|
+
current_request = client.directory.administrative_units
|
|
28
|
+
|
|
29
|
+
while current_request:
|
|
30
|
+
try:
|
|
31
|
+
response = await current_request.get()
|
|
32
|
+
if response and response.value:
|
|
33
|
+
all_units.extend(response.value)
|
|
34
|
+
|
|
35
|
+
# Handle next page using OData link
|
|
36
|
+
if response.odata_next_link:
|
|
37
|
+
current_request = client.directory.administrative_units.with_url(
|
|
38
|
+
response.odata_next_link
|
|
39
|
+
)
|
|
40
|
+
else:
|
|
41
|
+
current_request = None
|
|
42
|
+
else:
|
|
43
|
+
current_request = None
|
|
44
|
+
except Exception as e:
|
|
45
|
+
logger.error(f"Failed to retrieve administrative units: {str(e)}")
|
|
46
|
+
current_request = None
|
|
31
47
|
|
|
32
48
|
return all_units
|
|
33
49
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import neo4j
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
import cartography.intel.openai.adminapikeys
|
|
7
|
+
import cartography.intel.openai.apikeys
|
|
8
|
+
import cartography.intel.openai.projects
|
|
9
|
+
import cartography.intel.openai.serviceaccounts
|
|
10
|
+
import cartography.intel.openai.users
|
|
11
|
+
from cartography.config import Config
|
|
12
|
+
from cartography.util import timeit
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@timeit
|
|
18
|
+
def start_openai_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
|
|
19
|
+
"""
|
|
20
|
+
If this module is configured, perform ingestion of OpenAI data. Otherwise warn and exit
|
|
21
|
+
:param neo4j_session: Neo4J session for database interface
|
|
22
|
+
:param config: A cartography.config object
|
|
23
|
+
:return: None
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
if not config.openai_apikey or not config.openai_org_id:
|
|
27
|
+
logger.info(
|
|
28
|
+
"OpenAI import is not configured - skipping this module. "
|
|
29
|
+
"See docs to configure.",
|
|
30
|
+
)
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
# Create requests sessions
|
|
34
|
+
api_session = requests.session()
|
|
35
|
+
api_session.headers.update(
|
|
36
|
+
{
|
|
37
|
+
"Authorization": f"Bearer {config.openai_apikey}",
|
|
38
|
+
"OpenAI-Organization": config.openai_org_id,
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
common_job_parameters = {
|
|
43
|
+
"UPDATE_TAG": config.update_tag,
|
|
44
|
+
"BASE_URL": "https://api.openai.com/v1",
|
|
45
|
+
"ORG_ID": config.openai_org_id,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# Organization node is created during the users sync
|
|
49
|
+
cartography.intel.openai.users.sync(
|
|
50
|
+
neo4j_session,
|
|
51
|
+
api_session,
|
|
52
|
+
common_job_parameters,
|
|
53
|
+
ORG_ID=config.openai_org_id,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
for project in cartography.intel.openai.projects.sync(
|
|
57
|
+
neo4j_session,
|
|
58
|
+
api_session,
|
|
59
|
+
common_job_parameters,
|
|
60
|
+
ORG_ID=config.openai_org_id,
|
|
61
|
+
):
|
|
62
|
+
project_job_parameters = {
|
|
63
|
+
"UPDATE_TAG": config.update_tag,
|
|
64
|
+
"BASE_URL": "https://api.openai.com/v1",
|
|
65
|
+
"ORG_ID": config.openai_org_id,
|
|
66
|
+
"project_id": project["id"],
|
|
67
|
+
}
|
|
68
|
+
cartography.intel.openai.serviceaccounts.sync(
|
|
69
|
+
neo4j_session,
|
|
70
|
+
api_session,
|
|
71
|
+
project_job_parameters,
|
|
72
|
+
project_id=project["id"],
|
|
73
|
+
)
|
|
74
|
+
cartography.intel.openai.apikeys.sync(
|
|
75
|
+
neo4j_session,
|
|
76
|
+
api_session,
|
|
77
|
+
project_job_parameters,
|
|
78
|
+
project_id=project["id"],
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
cartography.intel.openai.adminapikeys.sync(
|
|
82
|
+
neo4j_session,
|
|
83
|
+
api_session,
|
|
84
|
+
common_job_parameters,
|
|
85
|
+
ORG_ID=config.openai_org_id,
|
|
86
|
+
)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.intel.openai.util import paginated_get
|
|
12
|
+
from cartography.models.openai.adminapikey import OpenAIAdminApiKeySchema
|
|
13
|
+
from cartography.util import timeit
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
# Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
|
|
17
|
+
_TIMEOUT = (60, 60)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@timeit
|
|
21
|
+
def sync(
|
|
22
|
+
neo4j_session: neo4j.Session,
|
|
23
|
+
api_session: requests.Session,
|
|
24
|
+
common_job_parameters: Dict[str, Any],
|
|
25
|
+
ORG_ID: str,
|
|
26
|
+
) -> None:
|
|
27
|
+
adminapikeys = get(
|
|
28
|
+
api_session,
|
|
29
|
+
common_job_parameters["BASE_URL"],
|
|
30
|
+
)
|
|
31
|
+
transformed_adminapikeys = transform(adminapikeys)
|
|
32
|
+
load_adminapikeys(
|
|
33
|
+
neo4j_session,
|
|
34
|
+
transformed_adminapikeys,
|
|
35
|
+
ORG_ID,
|
|
36
|
+
common_job_parameters["UPDATE_TAG"],
|
|
37
|
+
)
|
|
38
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@timeit
|
|
42
|
+
def get(
|
|
43
|
+
api_session: requests.Session,
|
|
44
|
+
base_url: str,
|
|
45
|
+
) -> List[Dict[str, Any]]:
|
|
46
|
+
return list(
|
|
47
|
+
paginated_get(
|
|
48
|
+
api_session, f"{base_url}/organization/admin_api_keys", timeout=_TIMEOUT
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def transform(
|
|
54
|
+
adminapikeys: List[Dict[str, Any]],
|
|
55
|
+
) -> List[Dict[str, Any]]:
|
|
56
|
+
result: List[Dict[str, Any]] = []
|
|
57
|
+
for adminapikey in adminapikeys:
|
|
58
|
+
if adminapikey["owner"]["type"] == "user":
|
|
59
|
+
adminapikey["owner_user_id"] = adminapikey["owner"]["id"]
|
|
60
|
+
else:
|
|
61
|
+
adminapikey["owner_sa_id"] = adminapikey["owner"]["id"]
|
|
62
|
+
result.append(adminapikey)
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@timeit
|
|
67
|
+
def load_adminapikeys(
|
|
68
|
+
neo4j_session: neo4j.Session,
|
|
69
|
+
data: List[Dict[str, Any]],
|
|
70
|
+
ORG_ID: str,
|
|
71
|
+
update_tag: int,
|
|
72
|
+
) -> None:
|
|
73
|
+
logger.info("Loading %d OpenAI AdminApiKey into Neo4j.", len(data))
|
|
74
|
+
load(
|
|
75
|
+
neo4j_session,
|
|
76
|
+
OpenAIAdminApiKeySchema(),
|
|
77
|
+
data,
|
|
78
|
+
lastupdated=update_tag,
|
|
79
|
+
ORG_ID=ORG_ID,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@timeit
|
|
84
|
+
def cleanup(
|
|
85
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
86
|
+
) -> None:
|
|
87
|
+
GraphJob.from_node_schema(OpenAIAdminApiKeySchema(), common_job_parameters).run(
|
|
88
|
+
neo4j_session
|
|
89
|
+
)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.intel.openai.util import paginated_get
|
|
12
|
+
from cartography.models.openai.apikey import OpenAIApiKeySchema
|
|
13
|
+
from cartography.util import timeit
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
# Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
|
|
17
|
+
_TIMEOUT = (60, 60)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@timeit
|
|
21
|
+
def sync(
|
|
22
|
+
neo4j_session: neo4j.Session,
|
|
23
|
+
api_session: requests.Session,
|
|
24
|
+
common_job_parameters: Dict[str, Any],
|
|
25
|
+
project_id: str,
|
|
26
|
+
) -> None:
|
|
27
|
+
apikeys = get(
|
|
28
|
+
api_session,
|
|
29
|
+
common_job_parameters["BASE_URL"],
|
|
30
|
+
project_id,
|
|
31
|
+
)
|
|
32
|
+
transformed_apikeys = transform(apikeys)
|
|
33
|
+
load_apikeys(
|
|
34
|
+
neo4j_session,
|
|
35
|
+
transformed_apikeys,
|
|
36
|
+
project_id,
|
|
37
|
+
common_job_parameters["UPDATE_TAG"],
|
|
38
|
+
)
|
|
39
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@timeit
|
|
43
|
+
def get(
|
|
44
|
+
api_session: requests.Session,
|
|
45
|
+
base_url: str,
|
|
46
|
+
project_id: str,
|
|
47
|
+
) -> List[Dict[str, Any]]:
|
|
48
|
+
return list(
|
|
49
|
+
paginated_get(
|
|
50
|
+
api_session,
|
|
51
|
+
"{base_url}/organization/projects/{project_id}/api_keys".format(
|
|
52
|
+
base_url=base_url,
|
|
53
|
+
project_id=project_id,
|
|
54
|
+
),
|
|
55
|
+
timeout=_TIMEOUT,
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def transform(
|
|
61
|
+
apikeys: List[Dict[str, Any]],
|
|
62
|
+
) -> List[Dict[str, Any]]:
|
|
63
|
+
result: List[Dict[str, Any]] = []
|
|
64
|
+
for apikey in apikeys:
|
|
65
|
+
if apikey["owner"]["type"] == "user":
|
|
66
|
+
apikey["owner_user_id"] = apikey["owner"]["user"]["id"]
|
|
67
|
+
else:
|
|
68
|
+
apikey["owner_sa_id"] = apikey["owner"]["service_account"]["id"]
|
|
69
|
+
result.append(apikey)
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@timeit
|
|
74
|
+
def load_apikeys(
|
|
75
|
+
neo4j_session: neo4j.Session,
|
|
76
|
+
data: List[Dict[str, Any]],
|
|
77
|
+
project_id: str,
|
|
78
|
+
update_tag: int,
|
|
79
|
+
) -> None:
|
|
80
|
+
logger.info("Loading %d OpenAI APIKey into Neo4j.", len(data))
|
|
81
|
+
load(
|
|
82
|
+
neo4j_session,
|
|
83
|
+
OpenAIApiKeySchema(),
|
|
84
|
+
data,
|
|
85
|
+
lastupdated=update_tag,
|
|
86
|
+
project_id=project_id,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@timeit
|
|
91
|
+
def cleanup(
|
|
92
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
93
|
+
) -> None:
|
|
94
|
+
GraphJob.from_node_schema(OpenAIApiKeySchema(), common_job_parameters).run(
|
|
95
|
+
neo4j_session
|
|
96
|
+
)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.intel.openai.util import paginated_get
|
|
12
|
+
from cartography.models.openai.project import OpenAIProjectSchema
|
|
13
|
+
from cartography.util import timeit
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
# Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
|
|
17
|
+
_TIMEOUT = (60, 60)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@timeit
|
|
21
|
+
def sync(
|
|
22
|
+
neo4j_session: neo4j.Session,
|
|
23
|
+
api_session: requests.Session,
|
|
24
|
+
common_job_parameters: Dict[str, Any],
|
|
25
|
+
ORG_ID: str,
|
|
26
|
+
) -> List[Dict]:
|
|
27
|
+
projects = get(
|
|
28
|
+
api_session,
|
|
29
|
+
common_job_parameters["BASE_URL"],
|
|
30
|
+
)
|
|
31
|
+
for project in projects:
|
|
32
|
+
project["users"] = []
|
|
33
|
+
project["admins"] = []
|
|
34
|
+
for user in get_project_users(
|
|
35
|
+
api_session,
|
|
36
|
+
common_job_parameters["BASE_URL"],
|
|
37
|
+
project["id"],
|
|
38
|
+
):
|
|
39
|
+
project["users"].append(user["id"])
|
|
40
|
+
if user["role"] == "owner":
|
|
41
|
+
project["admins"].append(user["id"])
|
|
42
|
+
load_projects(neo4j_session, projects, ORG_ID, common_job_parameters["UPDATE_TAG"])
|
|
43
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
44
|
+
return projects
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@timeit
|
|
48
|
+
def get(
|
|
49
|
+
api_session: requests.Session,
|
|
50
|
+
base_url: str,
|
|
51
|
+
) -> List[Dict[str, Any]]:
|
|
52
|
+
return list(
|
|
53
|
+
paginated_get(
|
|
54
|
+
api_session, f"{base_url}/organization/projects", timeout=_TIMEOUT
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@timeit
|
|
60
|
+
def get_project_users(
|
|
61
|
+
api_session: requests.Session,
|
|
62
|
+
base_url: str,
|
|
63
|
+
project_id: str,
|
|
64
|
+
) -> List[Dict[str, Any]]:
|
|
65
|
+
return list(
|
|
66
|
+
paginated_get(
|
|
67
|
+
api_session,
|
|
68
|
+
f"{base_url}/organization/projects/{project_id}/users",
|
|
69
|
+
timeout=_TIMEOUT,
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@timeit
|
|
75
|
+
def load_projects(
|
|
76
|
+
neo4j_session: neo4j.Session,
|
|
77
|
+
data: List[Dict[str, Any]],
|
|
78
|
+
ORG_ID: str,
|
|
79
|
+
update_tag: int,
|
|
80
|
+
) -> None:
|
|
81
|
+
logger.info("Loading %d OpenAI Projects into Neo4j.", len(data))
|
|
82
|
+
load(
|
|
83
|
+
neo4j_session,
|
|
84
|
+
OpenAIProjectSchema(),
|
|
85
|
+
data,
|
|
86
|
+
lastupdated=update_tag,
|
|
87
|
+
ORG_ID=ORG_ID,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@timeit
|
|
92
|
+
def cleanup(
|
|
93
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
94
|
+
) -> None:
|
|
95
|
+
GraphJob.from_node_schema(OpenAIProjectSchema(), common_job_parameters).run(
|
|
96
|
+
neo4j_session
|
|
97
|
+
)
|