cartography 0.103.0rc1__py3-none-any.whl → 0.104.0rc2__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.
- cartography/_version.py +2 -2
- cartography/cli.py +97 -1
- cartography/config.py +28 -0
- 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/cloudwatch.py +93 -0
- cartography/intel/aws/ec2/load_balancer_v2s.py +4 -1
- cartography/intel/aws/efs.py +93 -0
- cartography/intel/aws/resources.py +4 -0
- cartography/intel/aws/secretsmanager.py +136 -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/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/cloudwatch/__init__.py +0 -0
- cartography/models/aws/cloudwatch/loggroup.py +52 -0
- cartography/models/aws/efs/__init__.py +0 -0
- cartography/models/aws/efs/mount_target.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/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.0rc2.dist-info}/METADATA +8 -4
- {cartography-0.103.0rc1.dist-info → cartography-0.104.0rc2.dist-info}/RECORD +73 -14
- {cartography-0.103.0rc1.dist-info → cartography-0.104.0rc2.dist-info}/WHEEL +1 -1
- {cartography-0.103.0rc1.dist-info → cartography-0.104.0rc2.dist-info}/entry_points.txt +0 -0
- {cartography-0.103.0rc1.dist-info → cartography-0.104.0rc2.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.103.0rc1.dist-info → cartography-0.104.0rc2.dist-info}/top_level.txt +0 -0
|
@@ -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
|
+
)
|
|
@@ -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
|
+
)
|
|
@@ -0,0 +1,82 @@
|
|
|
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.serviceaccount import OpenAIServiceAccountSchema
|
|
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
|
+
serviceaccountss = get(
|
|
28
|
+
api_session,
|
|
29
|
+
common_job_parameters["BASE_URL"],
|
|
30
|
+
project_id,
|
|
31
|
+
)
|
|
32
|
+
load_serviceaccounts(
|
|
33
|
+
neo4j_session,
|
|
34
|
+
serviceaccountss,
|
|
35
|
+
project_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
|
+
project_id: str,
|
|
46
|
+
) -> List[Dict[str, Any]]:
|
|
47
|
+
return list(
|
|
48
|
+
paginated_get(
|
|
49
|
+
api_session,
|
|
50
|
+
"{base_url}/organization/projects/{project_id}/service_accounts".format(
|
|
51
|
+
base_url=base_url,
|
|
52
|
+
project_id=project_id,
|
|
53
|
+
),
|
|
54
|
+
timeout=_TIMEOUT,
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@timeit
|
|
60
|
+
def load_serviceaccounts(
|
|
61
|
+
neo4j_session: neo4j.Session,
|
|
62
|
+
data: List[Dict[str, Any]],
|
|
63
|
+
project_id: str,
|
|
64
|
+
update_tag: int,
|
|
65
|
+
) -> None:
|
|
66
|
+
logger.info("Loading %d OpenAI ServiceAccount into Neo4j.", len(data))
|
|
67
|
+
load(
|
|
68
|
+
neo4j_session,
|
|
69
|
+
OpenAIServiceAccountSchema(),
|
|
70
|
+
data,
|
|
71
|
+
lastupdated=update_tag,
|
|
72
|
+
project_id=project_id,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@timeit
|
|
77
|
+
def cleanup(
|
|
78
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
79
|
+
) -> None:
|
|
80
|
+
GraphJob.from_node_schema(OpenAIServiceAccountSchema(), common_job_parameters).run(
|
|
81
|
+
neo4j_session
|
|
82
|
+
)
|
|
@@ -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
|
+
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.organization import OpenAIOrganizationSchema
|
|
13
|
+
from cartography.models.openai.user import OpenAIUserSchema
|
|
14
|
+
from cartography.util import timeit
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
# Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
|
|
18
|
+
_TIMEOUT = (60, 60)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@timeit
|
|
22
|
+
def sync(
|
|
23
|
+
neo4j_session: neo4j.Session,
|
|
24
|
+
api_session: requests.Session,
|
|
25
|
+
common_job_parameters: Dict[str, Any],
|
|
26
|
+
ORG_ID: str,
|
|
27
|
+
) -> None:
|
|
28
|
+
users = get(
|
|
29
|
+
api_session,
|
|
30
|
+
common_job_parameters["BASE_URL"],
|
|
31
|
+
)
|
|
32
|
+
load_users(neo4j_session, users, ORG_ID, common_job_parameters["UPDATE_TAG"])
|
|
33
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@timeit
|
|
37
|
+
def get(
|
|
38
|
+
api_session: requests.Session,
|
|
39
|
+
base_url: str,
|
|
40
|
+
) -> List[Dict[str, Any]]:
|
|
41
|
+
return list(
|
|
42
|
+
paginated_get(api_session, f"{base_url}/organization/users", timeout=_TIMEOUT)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@timeit
|
|
47
|
+
def load_users(
|
|
48
|
+
neo4j_session: neo4j.Session,
|
|
49
|
+
data: List[Dict[str, Any]],
|
|
50
|
+
ORG_ID: str,
|
|
51
|
+
update_tag: int,
|
|
52
|
+
) -> None:
|
|
53
|
+
load(
|
|
54
|
+
neo4j_session,
|
|
55
|
+
OpenAIOrganizationSchema(),
|
|
56
|
+
[{"id": ORG_ID}],
|
|
57
|
+
lastupdated=update_tag,
|
|
58
|
+
)
|
|
59
|
+
logger.info("Loading %d OpenAI User into Neo4j.", len(data))
|
|
60
|
+
load(
|
|
61
|
+
neo4j_session,
|
|
62
|
+
OpenAIUserSchema(),
|
|
63
|
+
data,
|
|
64
|
+
lastupdated=update_tag,
|
|
65
|
+
ORG_ID=ORG_ID,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@timeit
|
|
70
|
+
def cleanup(
|
|
71
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
72
|
+
) -> None:
|
|
73
|
+
GraphJob.from_node_schema(OpenAIUserSchema(), common_job_parameters).run(
|
|
74
|
+
neo4j_session
|
|
75
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
from typing import Generator
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def paginated_get(
|
|
8
|
+
api_session: requests.Session,
|
|
9
|
+
url: str,
|
|
10
|
+
timeout: tuple[int, int],
|
|
11
|
+
after: str | None = None,
|
|
12
|
+
) -> Generator[dict[str, Any], None, None]:
|
|
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
|
+
"""
|
|
30
|
+
params = {"after": after} if after else {}
|
|
31
|
+
req = api_session.get(
|
|
32
|
+
url,
|
|
33
|
+
params=params,
|
|
34
|
+
timeout=timeout,
|
|
35
|
+
)
|
|
36
|
+
req.raise_for_status()
|
|
37
|
+
result = req.json()
|
|
38
|
+
yield from result.get("data", [])
|
|
39
|
+
if result.get("has_more"):
|
|
40
|
+
yield from paginated_get(
|
|
41
|
+
api_session,
|
|
42
|
+
url,
|
|
43
|
+
timeout=timeout,
|
|
44
|
+
after=result.get("last_id"),
|
|
45
|
+
)
|