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,77 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import neo4j
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
import cartography.intel.tailscale.acls
|
|
7
|
+
import cartography.intel.tailscale.devices
|
|
8
|
+
import cartography.intel.tailscale.postureintegrations
|
|
9
|
+
import cartography.intel.tailscale.tailnets
|
|
10
|
+
import cartography.intel.tailscale.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_tailscale_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
|
|
19
|
+
"""
|
|
20
|
+
If this module is configured, perform ingestion of Tailscale 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.tailscale_token or not config.tailscale_org:
|
|
27
|
+
logger.info(
|
|
28
|
+
"Tailscale 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({"Authorization": f"Bearer {config.tailscale_token}"})
|
|
36
|
+
|
|
37
|
+
common_job_parameters = {
|
|
38
|
+
"UPDATE_TAG": config.update_tag,
|
|
39
|
+
"BASE_URL": config.tailscale_base_url,
|
|
40
|
+
"org": config.tailscale_org,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
cartography.intel.tailscale.tailnets.sync(
|
|
44
|
+
neo4j_session,
|
|
45
|
+
api_session,
|
|
46
|
+
common_job_parameters,
|
|
47
|
+
org=config.tailscale_org,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
users = cartography.intel.tailscale.users.sync(
|
|
51
|
+
neo4j_session,
|
|
52
|
+
api_session,
|
|
53
|
+
common_job_parameters,
|
|
54
|
+
org=config.tailscale_org,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
cartography.intel.tailscale.devices.sync(
|
|
58
|
+
neo4j_session,
|
|
59
|
+
api_session,
|
|
60
|
+
common_job_parameters,
|
|
61
|
+
org=config.tailscale_org,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
cartography.intel.tailscale.postureintegrations.sync(
|
|
65
|
+
neo4j_session,
|
|
66
|
+
api_session,
|
|
67
|
+
common_job_parameters,
|
|
68
|
+
org=config.tailscale_org,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
cartography.intel.tailscale.acls.sync(
|
|
72
|
+
neo4j_session,
|
|
73
|
+
api_session,
|
|
74
|
+
common_job_parameters,
|
|
75
|
+
org=config.tailscale_org,
|
|
76
|
+
users=users,
|
|
77
|
+
)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
from typing import Tuple
|
|
6
|
+
|
|
7
|
+
import neo4j
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from cartography.client.core.tx import load
|
|
11
|
+
from cartography.graph.job import GraphJob
|
|
12
|
+
from cartography.intel.tailscale.utils import ACLParser
|
|
13
|
+
from cartography.intel.tailscale.utils import role_to_group
|
|
14
|
+
from cartography.models.tailscale.group import TailscaleGroupSchema
|
|
15
|
+
from cartography.models.tailscale.tag import TailscaleTagSchema
|
|
16
|
+
from cartography.util import timeit
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
# Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
|
|
20
|
+
_TIMEOUT = (60, 60)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@timeit
|
|
24
|
+
def sync(
|
|
25
|
+
neo4j_session: neo4j.Session,
|
|
26
|
+
api_session: requests.Session,
|
|
27
|
+
common_job_parameters: Dict[str, Any],
|
|
28
|
+
org: str,
|
|
29
|
+
users: List[Dict[str, Any]],
|
|
30
|
+
) -> None:
|
|
31
|
+
raw_acl = get(
|
|
32
|
+
api_session,
|
|
33
|
+
common_job_parameters["BASE_URL"],
|
|
34
|
+
org,
|
|
35
|
+
)
|
|
36
|
+
groups, tags = transform(raw_acl, users)
|
|
37
|
+
load_groups(
|
|
38
|
+
neo4j_session,
|
|
39
|
+
groups,
|
|
40
|
+
common_job_parameters["UPDATE_TAG"],
|
|
41
|
+
org,
|
|
42
|
+
)
|
|
43
|
+
load_tags(
|
|
44
|
+
neo4j_session,
|
|
45
|
+
tags,
|
|
46
|
+
org,
|
|
47
|
+
common_job_parameters["UPDATE_TAG"],
|
|
48
|
+
)
|
|
49
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@timeit
|
|
53
|
+
def get(
|
|
54
|
+
api_session: requests.Session,
|
|
55
|
+
base_url: str,
|
|
56
|
+
org: str,
|
|
57
|
+
) -> str:
|
|
58
|
+
req = api_session.get(
|
|
59
|
+
f"{base_url}/tailnet/{org}/acl",
|
|
60
|
+
timeout=_TIMEOUT,
|
|
61
|
+
)
|
|
62
|
+
req.raise_for_status()
|
|
63
|
+
return req.text
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def transform(
|
|
67
|
+
raw_acl: str,
|
|
68
|
+
users: List[Dict[str, Any]],
|
|
69
|
+
) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
|
|
70
|
+
transformed_groups: Dict[str, Dict[str, Any]] = {}
|
|
71
|
+
transformed_tags: Dict[str, Dict[str, Any]] = {}
|
|
72
|
+
|
|
73
|
+
parser = ACLParser(raw_acl)
|
|
74
|
+
# Extract groups from the ACL
|
|
75
|
+
for group in parser.get_groups():
|
|
76
|
+
for dom in group["domain_members"]:
|
|
77
|
+
for user in users:
|
|
78
|
+
if user["loginName"].endswith(f"@{dom}"):
|
|
79
|
+
group["members"].append(user["loginName"])
|
|
80
|
+
# Ensure domain members are unique
|
|
81
|
+
group["domain_members"] = list(set(group["domain_members"]))
|
|
82
|
+
transformed_groups[group["id"]] = group
|
|
83
|
+
# Extract tags from the ACL
|
|
84
|
+
for tag in parser.get_tags():
|
|
85
|
+
for dom in tag["domain_owners"]:
|
|
86
|
+
for user in users:
|
|
87
|
+
if user["loginName"].endswith(f"@{dom}"):
|
|
88
|
+
tag["owners"].append(user["loginName"])
|
|
89
|
+
# Ensure domain owners are unique
|
|
90
|
+
tag["owners"] = list(set(tag["owners"]))
|
|
91
|
+
transformed_tags[tag["id"]] = tag
|
|
92
|
+
|
|
93
|
+
# Add autogroups based on user roles
|
|
94
|
+
for user in users:
|
|
95
|
+
for g in role_to_group(user["role"]):
|
|
96
|
+
if g not in transformed_groups:
|
|
97
|
+
transformed_groups[g] = {
|
|
98
|
+
"id": g,
|
|
99
|
+
"name": g.split(":")[-1],
|
|
100
|
+
"members": [],
|
|
101
|
+
"sub_groups": [],
|
|
102
|
+
"domain_members": [],
|
|
103
|
+
}
|
|
104
|
+
transformed_groups[g]["members"].append(user["loginName"])
|
|
105
|
+
|
|
106
|
+
return list(transformed_groups.values()), list(transformed_tags.values())
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@timeit
|
|
110
|
+
def load_groups(
|
|
111
|
+
neo4j_session: neo4j.Session,
|
|
112
|
+
groups: List[Dict[str, Any]],
|
|
113
|
+
update_tag: str,
|
|
114
|
+
org: str,
|
|
115
|
+
) -> None:
|
|
116
|
+
logger.info(f"Loading {len(groups)} Tailscale Groups to the graph")
|
|
117
|
+
load(neo4j_session, TailscaleGroupSchema(), groups, lastupdated=update_tag, org=org)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@timeit
|
|
121
|
+
def load_tags(
|
|
122
|
+
neo4j_session: neo4j.Session,
|
|
123
|
+
data: List[Dict[str, Any]],
|
|
124
|
+
org: str,
|
|
125
|
+
update_tag: int,
|
|
126
|
+
) -> None:
|
|
127
|
+
logger.info(f"Loading {len(data)} Tailscale Tags to the graph")
|
|
128
|
+
load(
|
|
129
|
+
neo4j_session,
|
|
130
|
+
TailscaleTagSchema(),
|
|
131
|
+
data,
|
|
132
|
+
lastupdated=update_tag,
|
|
133
|
+
org=org,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@timeit
|
|
138
|
+
def cleanup(
|
|
139
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
140
|
+
) -> None:
|
|
141
|
+
GraphJob.from_node_schema(TailscaleGroupSchema(), common_job_parameters).run(
|
|
142
|
+
neo4j_session
|
|
143
|
+
)
|
|
144
|
+
GraphJob.from_node_schema(TailscaleTagSchema(), common_job_parameters).run(
|
|
145
|
+
neo4j_session
|
|
146
|
+
)
|
|
@@ -0,0 +1,127 @@
|
|
|
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.models.tailscale.device import TailscaleDeviceSchema
|
|
12
|
+
from cartography.models.tailscale.tag import TailscaleTagSchema
|
|
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: str,
|
|
26
|
+
) -> List[Dict]:
|
|
27
|
+
devices = get(
|
|
28
|
+
api_session,
|
|
29
|
+
common_job_parameters["BASE_URL"],
|
|
30
|
+
org,
|
|
31
|
+
)
|
|
32
|
+
tags = transform(devices)
|
|
33
|
+
load_devices(
|
|
34
|
+
neo4j_session,
|
|
35
|
+
devices,
|
|
36
|
+
org,
|
|
37
|
+
common_job_parameters["UPDATE_TAG"],
|
|
38
|
+
)
|
|
39
|
+
load_tags(
|
|
40
|
+
neo4j_session,
|
|
41
|
+
tags,
|
|
42
|
+
org,
|
|
43
|
+
common_job_parameters["UPDATE_TAG"],
|
|
44
|
+
)
|
|
45
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
46
|
+
return devices
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@timeit
|
|
50
|
+
def get(
|
|
51
|
+
api_session: requests.Session,
|
|
52
|
+
base_url: str,
|
|
53
|
+
org: str,
|
|
54
|
+
) -> List[Dict[str, Any]]:
|
|
55
|
+
results: List[Dict[str, Any]] = []
|
|
56
|
+
req = api_session.get(
|
|
57
|
+
f"{base_url}/tailnet/{org}/devices",
|
|
58
|
+
timeout=_TIMEOUT,
|
|
59
|
+
)
|
|
60
|
+
req.raise_for_status()
|
|
61
|
+
results = req.json()["devices"]
|
|
62
|
+
return results
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def transform(
|
|
66
|
+
raw_data: List[Dict[str, Any]],
|
|
67
|
+
) -> List[Dict[str, Any]]:
|
|
68
|
+
"""Extracts tags from the raw data and returns a list of dictionaries"""
|
|
69
|
+
transformed_tags: Dict[str, Dict[str, Any]] = {}
|
|
70
|
+
# Transform the raw data into the format expected by the load function
|
|
71
|
+
for device in raw_data:
|
|
72
|
+
for raw_tag in device.get("tags", []):
|
|
73
|
+
if raw_tag not in transformed_tags:
|
|
74
|
+
transformed_tags[raw_tag] = {
|
|
75
|
+
"id": raw_tag,
|
|
76
|
+
"name": raw_tag.split(":")[-1],
|
|
77
|
+
"devices": [device["nodeId"]],
|
|
78
|
+
}
|
|
79
|
+
else:
|
|
80
|
+
transformed_tags[raw_tag]["devices"].append(device["nodeId"])
|
|
81
|
+
return list(transformed_tags.values())
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@timeit
|
|
85
|
+
def load_devices(
|
|
86
|
+
neo4j_session: neo4j.Session,
|
|
87
|
+
data: List[Dict[str, Any]],
|
|
88
|
+
org: str,
|
|
89
|
+
update_tag: int,
|
|
90
|
+
) -> None:
|
|
91
|
+
logger.info(f"Loading {len(data)} Tailscale Devices to the graph")
|
|
92
|
+
load(
|
|
93
|
+
neo4j_session,
|
|
94
|
+
TailscaleDeviceSchema(),
|
|
95
|
+
data,
|
|
96
|
+
lastupdated=update_tag,
|
|
97
|
+
org=org,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@timeit
|
|
102
|
+
def load_tags(
|
|
103
|
+
neo4j_session: neo4j.Session,
|
|
104
|
+
data: List[Dict[str, Any]],
|
|
105
|
+
org: str,
|
|
106
|
+
update_tag: int,
|
|
107
|
+
) -> None:
|
|
108
|
+
logger.info(f"Loading {len(data)} Tailscale Tags to the graph")
|
|
109
|
+
load(
|
|
110
|
+
neo4j_session,
|
|
111
|
+
TailscaleTagSchema(),
|
|
112
|
+
data,
|
|
113
|
+
lastupdated=update_tag,
|
|
114
|
+
org=org,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@timeit
|
|
119
|
+
def cleanup(
|
|
120
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
121
|
+
) -> None:
|
|
122
|
+
GraphJob.from_node_schema(TailscaleDeviceSchema(), common_job_parameters).run(
|
|
123
|
+
neo4j_session
|
|
124
|
+
)
|
|
125
|
+
GraphJob.from_node_schema(TailscaleTagSchema(), common_job_parameters).run(
|
|
126
|
+
neo4j_session
|
|
127
|
+
)
|
|
@@ -0,0 +1,81 @@
|
|
|
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.models.tailscale.postureintegration import (
|
|
12
|
+
TailscalePostureIntegrationSchema,
|
|
13
|
+
)
|
|
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: str,
|
|
27
|
+
) -> None:
|
|
28
|
+
postureintegrations = get(
|
|
29
|
+
api_session,
|
|
30
|
+
common_job_parameters["BASE_URL"],
|
|
31
|
+
org,
|
|
32
|
+
)
|
|
33
|
+
load_postureintegrations(
|
|
34
|
+
neo4j_session,
|
|
35
|
+
postureintegrations,
|
|
36
|
+
org,
|
|
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
|
+
org: str,
|
|
47
|
+
) -> List[Dict[str, Any]]:
|
|
48
|
+
results: List[Dict[str, Any]] = []
|
|
49
|
+
req = api_session.get(
|
|
50
|
+
f"{base_url}/tailnet/{org}/posture/integrations",
|
|
51
|
+
timeout=_TIMEOUT,
|
|
52
|
+
)
|
|
53
|
+
req.raise_for_status()
|
|
54
|
+
results = req.json()["integrations"]
|
|
55
|
+
return results
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@timeit
|
|
59
|
+
def load_postureintegrations(
|
|
60
|
+
neo4j_session: neo4j.Session,
|
|
61
|
+
data: List[Dict[str, Any]],
|
|
62
|
+
org: str,
|
|
63
|
+
update_tag: int,
|
|
64
|
+
) -> None:
|
|
65
|
+
logger.info(f"Loading {len(data)} Tailscale PostureIntegrations to the graph")
|
|
66
|
+
load(
|
|
67
|
+
neo4j_session,
|
|
68
|
+
TailscalePostureIntegrationSchema(),
|
|
69
|
+
data,
|
|
70
|
+
lastupdated=update_tag,
|
|
71
|
+
org=org,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@timeit
|
|
76
|
+
def cleanup(
|
|
77
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
78
|
+
) -> None:
|
|
79
|
+
GraphJob.from_node_schema(
|
|
80
|
+
TailscalePostureIntegrationSchema(), common_job_parameters
|
|
81
|
+
).run(neo4j_session)
|
|
@@ -0,0 +1,76 @@
|
|
|
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.models.tailscale.tailnet import TailscaleTailnetSchema
|
|
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
|
+
api_session: requests.Session,
|
|
23
|
+
common_job_parameters: Dict[str, Any],
|
|
24
|
+
org: str,
|
|
25
|
+
) -> None:
|
|
26
|
+
tailnet = get(
|
|
27
|
+
api_session,
|
|
28
|
+
common_job_parameters["BASE_URL"],
|
|
29
|
+
org,
|
|
30
|
+
)
|
|
31
|
+
load_tailnets(
|
|
32
|
+
neo4j_session,
|
|
33
|
+
[tailnet],
|
|
34
|
+
org,
|
|
35
|
+
common_job_parameters["UPDATE_TAG"],
|
|
36
|
+
)
|
|
37
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@timeit
|
|
41
|
+
def get(
|
|
42
|
+
api_session: requests.Session,
|
|
43
|
+
base_url: str,
|
|
44
|
+
org: str,
|
|
45
|
+
) -> Dict[str, Any]:
|
|
46
|
+
req = api_session.get(
|
|
47
|
+
f"{base_url}/tailnet/{org}/settings",
|
|
48
|
+
timeout=_TIMEOUT,
|
|
49
|
+
)
|
|
50
|
+
req.raise_for_status()
|
|
51
|
+
return req.json()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@timeit
|
|
55
|
+
def load_tailnets(
|
|
56
|
+
neo4j_session: neo4j.Session,
|
|
57
|
+
data: List[Dict[str, Any]],
|
|
58
|
+
org: str,
|
|
59
|
+
update_tag: int,
|
|
60
|
+
) -> None:
|
|
61
|
+
load(
|
|
62
|
+
neo4j_session,
|
|
63
|
+
TailscaleTailnetSchema(),
|
|
64
|
+
data,
|
|
65
|
+
lastupdated=update_tag,
|
|
66
|
+
org=org,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@timeit
|
|
71
|
+
def cleanup(
|
|
72
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
73
|
+
) -> None:
|
|
74
|
+
GraphJob.from_node_schema(TailscaleTailnetSchema(), common_job_parameters).run(
|
|
75
|
+
neo4j_session
|
|
76
|
+
)
|
|
@@ -0,0 +1,80 @@
|
|
|
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.models.tailscale.user import TailscaleUserSchema
|
|
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
|
+
api_session: requests.Session,
|
|
23
|
+
common_job_parameters: Dict[str, Any],
|
|
24
|
+
org: str,
|
|
25
|
+
) -> List[Dict]:
|
|
26
|
+
users = get(
|
|
27
|
+
api_session,
|
|
28
|
+
common_job_parameters["BASE_URL"],
|
|
29
|
+
org,
|
|
30
|
+
)
|
|
31
|
+
load_users(
|
|
32
|
+
neo4j_session,
|
|
33
|
+
users,
|
|
34
|
+
org,
|
|
35
|
+
common_job_parameters["UPDATE_TAG"],
|
|
36
|
+
)
|
|
37
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
38
|
+
return users
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@timeit
|
|
42
|
+
def get(
|
|
43
|
+
api_session: requests.Session,
|
|
44
|
+
base_url: str,
|
|
45
|
+
org: str,
|
|
46
|
+
) -> List[Dict[str, Any]]:
|
|
47
|
+
results: List[Dict[str, Any]] = []
|
|
48
|
+
req = api_session.get(
|
|
49
|
+
f"{base_url}/tailnet/{org}/users",
|
|
50
|
+
timeout=_TIMEOUT,
|
|
51
|
+
)
|
|
52
|
+
req.raise_for_status()
|
|
53
|
+
results = req.json()["users"]
|
|
54
|
+
return results
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@timeit
|
|
58
|
+
def load_users(
|
|
59
|
+
neo4j_session: neo4j.Session,
|
|
60
|
+
data: List[Dict[str, Any]],
|
|
61
|
+
org: str,
|
|
62
|
+
update_tag: int,
|
|
63
|
+
) -> None:
|
|
64
|
+
logger.info(f"Loading {len(data)} Tailscale Users to the graph")
|
|
65
|
+
load(
|
|
66
|
+
neo4j_session,
|
|
67
|
+
TailscaleUserSchema(),
|
|
68
|
+
data,
|
|
69
|
+
lastupdated=update_tag,
|
|
70
|
+
org=org,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@timeit
|
|
75
|
+
def cleanup(
|
|
76
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
77
|
+
) -> None:
|
|
78
|
+
GraphJob.from_node_schema(TailscaleUserSchema(), common_job_parameters).run(
|
|
79
|
+
neo4j_session
|
|
80
|
+
)
|