Skip to content

Commit 256c80d

Browse files
datadog connector (#100)
datadog connector
1 parent 087344e commit 256c80d

File tree

4 files changed

+235
-1
lines changed

4 files changed

+235
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
*.log
33
.env
44
/Open-Connectors/Connectors/splunk/*.json
5+
/Open-Connectors/Connectors/datadog/*.json
56
/Open-Connectors/Connectors/Authomize_Docker_Image/docker-compose.dev.yml
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# schedule_config.py
22
scripts_to_schedule = [
33
{'cron': '0 */12 * * *', 'path': '/app/atlassian/atlassian.py'}, # cron for every 12 hours at 0000 % 1200.
4-
{'cron': '0 */12 * * *', 'path': '/app/splunk/splunk.py'}
4+
{'cron': '0 */12 * * *', 'path': '/app/splunk/splunk.py'}
5+
{'cron': '0 */12 * * *', 'path': '/app/datadog/datadog.py'}
56
# {'cron': '0 */12 * * *', 'path': 'path/to/your/script2.py'} # add other scripts here
67
]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Authomize DataDog Connector (Release - .9 Beta)
2+
- If you come across any issues or problems with a flow please [open an issue](https://github.com/authomize/Open-ITDR/issues).<br>
3+
4+
## Setup Environment Variables
5+
- Leave the path as `/app/.env` in datadog.py if using the Authomize Docker Container.
6+
- Else, to add environment variables create a `.env` file and edit the path in datadog.py file to match. Example `\<PATH>\<TO>\<YOUR>\.env` <br>
7+
- Add new lines in the following format for the environment file: ENV_VARIABLE='VALUE'<br>
8+
9+
# Required Authomize & DataDog Environment Variables
10+
DD_API_KEY = '<YOUR_DD_API_KEY_HERE>' <br>
11+
DD_APP_KEY = '<YOUR_DD_APP_KEY>' <br>
12+
AUTHOMIZE_DD_APPID = '<ENTER_YOUR_CONNECTOR_ID_HERE>' # The ID from your Rest API Connector at https://<YOUR_DOMAIN>.authomize.com/settings/integration/data-sources <br>
13+
AUTHOMIZE_DD_TOKEN = 'atm.........' # https://<YOUR_DOMAIN>.authomize.com/settings/api-tokens <br>
14+
authomize_batch_size = 1000 # Recommended to leave this at 1000. <br>
15+
16+
## Other Info
17+
- The created JSON files are only for troubleshooting purposes. These can be safely commented out.
18+
- This script does NOT utilize the DataDog Python client. `datadog-api-client`
19+
- For DataDog specific API questions refer to the DataDog API documentation here: https://docs.datadoghq.com/api/latest/
20+
- For Authomize specific API questions refer to the Authomize API documentation here: https://api.authomize.com/documentation
21+
22+
## Data Collected
23+
- Users
24+
- Roles
25+
- User to Role relationships
26+
- Privileges
27+
- Role to Privilege relationships
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import requests
2+
from datetime import datetime
3+
import logging
4+
import json
5+
import os
6+
from dotenv import load_dotenv
7+
8+
load_dotenv('/app/.env') #.env location
9+
10+
# DD API settings
11+
12+
DD_API_KEY = os.getenv('DD_API_KEY') # DD Token
13+
DD_APP_KEY = os.getenv('DD_APP_KEY') # DD Application Key
14+
15+
# Authomize API settings
16+
AUTHOMIZE_DD_TOKEN = os.getenv('AUTHOMIZE_DD_TOKEN') #Authomize API Token
17+
AUTHOMIZE_APP_ID = os.getenv('AUTHOMIZE_DD_APPID') #Authomize Connector ID (appId)
18+
authomize_batch_size = int(os.getenv('authomize_batch_size')) # Authomize Max POST Batch Size
19+
20+
#Authomize URLs
21+
user_url = f"https://api.authomize.com/v2/apps/{AUTHOMIZE_APP_ID}/accounts/users"
22+
role_url = f"https://api.authomize.com/v2/apps/{AUTHOMIZE_APP_ID}/access/grouping"
23+
user_to_role_url = f"https://api.authomize.com/v2/apps/{AUTHOMIZE_APP_ID}/association/accounts"
24+
create_privileges_url = f"https://api.authomize.com/v2/apps/{AUTHOMIZE_APP_ID}/privileges"
25+
permission_url = f"https://api.authomize.com/v2/apps/{AUTHOMIZE_APP_ID}/access/permissions"
26+
27+
# Headers for DD
28+
dd_headers = {
29+
"Accept": "application/json",
30+
"DD-API-KEY": f"{DD_API_KEY}",
31+
"DD-APPLICATION-KEY": f"{DD_APP_KEY}",
32+
}
33+
34+
# Authomize header
35+
authomize_headers = {
36+
'Content-Type': 'application/json',
37+
'Authorization': f'{AUTHOMIZE_DD_TOKEN}'
38+
}
39+
40+
logging.basicConfig(filename='/app/datadog/datadog.log', level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s", filemode='w')
41+
42+
dd_accounts_url = "https://api.datadoghq.com/api/v2/users"
43+
page_size = 10
44+
page_number = 0 #start from page 0
45+
total_filtered_count = None
46+
user_data = []
47+
role_data = []
48+
permission_data = []
49+
50+
while total_filtered_count is None or (page_number - 1) * page_size < total_filtered_count:
51+
params = {
52+
"page[size]": page_size,
53+
"page[number]": page_number,
54+
}
55+
response = requests.get(dd_accounts_url, headers=dd_headers, params=params)
56+
57+
if response.status_code == 200:
58+
json_data = response.json()
59+
60+
for user in json_data.get("data", []):
61+
if user['type'] == 'users':
62+
user_data.append(user)
63+
64+
for role in json_data.get("included", []):
65+
if role['type'] == 'roles':
66+
role_data.append(role)
67+
68+
for permission in json_data.get("included", []):
69+
if permission['type'] == 'permissions':
70+
permission_data.append(permission)
71+
72+
total_filtered_count = json_data["meta"]["page"]["total_filtered_count"]
73+
page_number += 1
74+
else:
75+
print(f"Error - Status Code: {response.status_code} Response Text: {response.text}")
76+
break
77+
78+
dd_users = []
79+
user_to_role = []
80+
81+
for user in user_data:
82+
user_info = {
83+
'uniqueId': user['id'],
84+
'originId': user['id'],
85+
'name': user['attributes']['name'],
86+
'email': user['attributes']['email'],
87+
}
88+
89+
if user['attributes']['disabled']:
90+
status = 'Disabled'
91+
elif user['attributes']['status'] == 'Active':
92+
status = 'Enabled'
93+
else:
94+
status = 'Unknown'
95+
96+
user_info['status'] = status
97+
dd_users.append(user_info)
98+
99+
for user_role in user['relationships']['roles']['data']:
100+
user_relationship = {
101+
'sourceId': user['id'],
102+
'targetId': user_role['id'],
103+
}
104+
user_to_role.append(user_relationship)
105+
106+
roles = []
107+
role_to_permission = []
108+
109+
for role in role_data:
110+
if role['type'] == 'roles':
111+
role_dict = {
112+
'uniqueId': role['id'],
113+
'originId': role['id'],
114+
'name': role['attributes']['name'],
115+
'originType': 'Role',
116+
'type': 'Group',
117+
'isRole': True
118+
}
119+
if role_dict not in roles:
120+
roles.append(role_dict)
121+
122+
for role_permission in role['relationships']['permissions']['data']:
123+
permission_dict = {
124+
'sourceUniqueId': role['id'],
125+
'sourceType': "Grouping",
126+
'privilegeId': role_permission['id']
127+
}
128+
if permission_dict not in role_to_permission:
129+
role_to_permission.append(permission_dict)
130+
131+
privileges = []
132+
133+
for privilege in permission_data:
134+
if privilege['type'] == 'permissions':
135+
uniqueId = privilege['id']
136+
originId = privilege['id']
137+
display_type = privilege['attributes']['display_type']
138+
if display_type == 'read':
139+
type = "Read"
140+
elif display_type == 'write':
141+
type = "Write"
142+
else:
143+
type = "Unknown"
144+
145+
originName = privilege['attributes']['display_name']
146+
privilege_info = {'uniqueId': uniqueId, 'originId': originId, 'type': type, 'originName': originName}
147+
148+
if privilege_info not in privileges:
149+
privileges.append(privilege_info)
150+
151+
accepted_timestamps = []
152+
153+
def post_authomize_data(url, data):
154+
for i in range(0, len(data), authomize_batch_size):
155+
batch = data[i:i + authomize_batch_size]
156+
payload = {
157+
'data': batch,
158+
'validateOnly': False
159+
}
160+
try:
161+
response = requests.post(url, headers=authomize_headers, json=payload)
162+
response.raise_for_status()
163+
# Log the response for both versions of the function
164+
logging.info("Authomize API Response for %s (Batch %d): %s", url, i // authomize_batch_size + 1, response.text)
165+
# Handle the accepted_timestamp only if it's present in the response
166+
response_json = response.json()
167+
accepted_timestamp = response_json.get("acceptedTimestamp")
168+
if accepted_timestamp:
169+
accepted_timestamps.append(accepted_timestamp)
170+
except requests.exceptions.RequestException as e:
171+
logging.error("Error occurred while sending data to Authomize API (Batch %d): %s", i // authomize_batch_size + 1, e)
172+
logging.error("Response body: %s", response.text) # Log the response body when an error occurs
173+
logging.error("Data causing the issue: %s", batch) # Add this line to log the problematic data
174+
175+
176+
# Post users and write to file for troubleshooting
177+
with open('/app/datadog/users_list.json', 'w') as file:
178+
file.write(json.dumps(dd_users))
179+
post_authomize_data(user_url, dd_users)
180+
181+
# Post roles and write to file for troubleshooting
182+
with open('/app/datadog/roles_list.json', 'w') as file:
183+
file.write(json.dumps(roles))
184+
post_authomize_data(role_url, roles)
185+
186+
# Post users to roles and write to file for troubleshooting
187+
with open('/app/datadog/user_to_role_list.json', 'w') as file:
188+
file.write(json.dumps(user_to_role))
189+
post_authomize_data(user_to_role_url, user_to_role)
190+
191+
# Post privileges and write to file for troubleshooting
192+
with open('/app/datadog/privilege_list.json', 'w') as file:
193+
file.write(json.dumps(privileges))
194+
post_authomize_data(create_privileges_url, privileges)
195+
196+
# Post privileges and write to file for troubleshooting
197+
with open('/app/datadog/permission_list.json', 'w') as file:
198+
file.write(json.dumps(role_to_permission))
199+
post_authomize_data(permission_url, role_to_permission)
200+
201+
# take first accepted timestamp
202+
timestamp = accepted_timestamps[0][:-6]
203+
delete_url = f'https://api.authomize.com/v2/apps/{AUTHOMIZE_APP_ID}/data?modifiedBefore={timestamp}'
204+
delete_response = requests.delete(delete_url, headers=authomize_headers)
205+
logging.info("Authomize Delete Response for: %s: %s", delete_url, delete_response.text)

0 commit comments

Comments
 (0)