main
py 406 lines 14 KB
Raw
1 import json
2 from datetime import datetime
3
4 from loguru import logger
5 from sqlalchemy import and_
6 from sqlalchemy import update
7 from sqlalchemy.ext.asyncio import AsyncSession
8
9 from app.connectors.grafana.schema.dashboards import DashboardProvisionRequest
10 from app.connectors.grafana.schema.dashboards import MimecastDashboard
11 from app.connectors.grafana.services.dashboards import provision_dashboards
12 from app.connectors.grafana.utils.universal import create_grafana_client
13 from app.connectors.graylog.services.management import start_stream
14 from app.connectors.graylog.utils.universal import send_post_request
15 from app.connectors.graylog.utils.universal import send_post_request_create_entity
16 from app.customer_provisioning.schema.grafana import GrafanaDatasource
17 from app.customer_provisioning.schema.grafana import GrafanaDataSourceCreationResponse
18 from app.customer_provisioning.schema.graylog import GraylogIndexSetCreationResponse
19 from app.customer_provisioning.schema.graylog import StreamCreationResponse
20 from app.customer_provisioning.schema.graylog import TimeBasedIndexSet
21 from app.customer_provisioning.services.grafana import create_grafana_folder
22 from app.customer_provisioning.services.grafana import get_opensearch_version
23 from app.customers.routes.customers import get_customer
24 from app.customers.routes.customers import get_customer_meta
25 from app.integrations.mimecast.schema.provision import MimecastEventStream
26 from app.integrations.mimecast.schema.provision import ProvisionMimecastRequest
27 from app.integrations.mimecast.schema.provision import ProvisionMimecastResponse
28 from app.integrations.models.customer_integration_settings import CustomerIntegrations
29 from app.integrations.routes import create_integration_meta
30 from app.integrations.schema import CustomerIntegrationsMetaSchema
31 from app.utils import get_connector_attribute
32
33
34 ################## ! GRAYLOG ! ##################
35 async def build_index_set_config(
36 customer_code: str,
37 session: AsyncSession,
38 ) -> TimeBasedIndexSet:
39 """
40 Build the configuration for a time-based index set.
41
42 Args:
43 request (ProvisionNewCustomer): The request object containing customer information.
44
45 Returns:
46 TimeBasedIndexSet: The configured time-based index set.
47 """
48 return TimeBasedIndexSet(
49 title=f"{(await get_customer(customer_code, session)).customer.customer_name} - Mimecast",
50 description=f"{customer_code} - Mimecast",
51 index_prefix=f"mimecast_{customer_code}",
52 rotation_strategy_class="org.graylog2.indexer.rotation.strategies.TimeBasedRotationStrategy",
53 rotation_strategy={
54 "type": "org.graylog2.indexer.rotation.strategies.TimeBasedRotationStrategyConfig",
55 "rotation_period": "P1D",
56 "rotate_empty_index_set": False,
57 "max_rotation_period": None,
58 },
59 retention_strategy_class="org.graylog2.indexer.retention.strategies.DeletionRetentionStrategy",
60 retention_strategy={
61 "type": "org.graylog2.indexer.retention.strategies.DeletionRetentionStrategyConfig",
62 "max_number_of_indices": 30,
63 },
64 creation_date=datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
65 index_analyzer="standard",
66 shards=1,
67 replicas=0,
68 index_optimization_max_num_segments=1,
69 index_optimization_disabled=False,
70 writable=True,
71 field_type_refresh_interval=5000,
72 )
73
74
75 # Function to send the POST request and handle the response
76 async def send_index_set_creation_request(
77 index_set: TimeBasedIndexSet,
78 ) -> GraylogIndexSetCreationResponse:
79 """
80 Sends a request to create an index set in Graylog.
81
82 Args:
83 index_set (TimeBasedIndexSet): The index set to be created.
84
85 Returns:
86 GraylogIndexSetCreationResponse: The response from Graylog after creating the index set.
87 """
88 json_index_set = json.dumps(index_set.model_dump())
89 logger.info(f"json_index_set set: {json_index_set}")
90 response_json = await send_post_request(
91 endpoint="/api/system/indices/index_sets",
92 data=index_set.model_dump(),
93 )
94 return GraylogIndexSetCreationResponse(**response_json)
95
96
97 async def create_index_set(
98 customer_code: str,
99 session: AsyncSession,
100 ) -> GraylogIndexSetCreationResponse:
101 """
102 Creates an index set for a new customer.
103
104 Args:
105 request (ProvisionNewCustomer): The request object containing the customer information.
106
107 Returns:
108 GraylogIndexSetCreationResponse: The response object containing the result of the index set creation.
109 """
110 logger.info(f"Creating index set for customer {customer_code}")
111 index_set_config = await build_index_set_config(customer_code, session)
112 return await send_index_set_creation_request(index_set_config)
113
114
115 # ! Event STREAMS ! #
116 # Function to create event stream configuration
117 async def build_event_stream_config(
118 customer_code: str,
119 index_set_id: str,
120 session: AsyncSession,
121 ) -> MimecastEventStream:
122 """
123 Builds the configuration for a Mimecast event stream.
124
125 Args:
126 customer_code (str): The customer code.
127 index_set_id (str): The index set ID.
128 session (AsyncSession): The async session.
129
130 Returns:
131 MimecastEventStream: The configured Mimecast event stream.
132 """
133 return MimecastEventStream(
134 title=f"{(await get_customer(customer_code, session)).customer.customer_name} - Mimecast",
135 description=f"{(await get_customer(customer_code, session)).customer.customer_name} - Mimecast",
136 index_set_id=index_set_id,
137 rules=[
138 {
139 "field": "integration",
140 "type": 1,
141 "inverted": False,
142 "value": "mimecast",
143 },
144 {
145 "field": "customer_code",
146 "type": 1,
147 "inverted": False,
148 "value": f"{customer_code}",
149 },
150 ],
151 matching_type="AND",
152 remove_matches_from_default_stream=True,
153 content_pack=None,
154 )
155
156
157 async def send_event_stream_creation_request(
158 event_stream: MimecastEventStream,
159 ) -> StreamCreationResponse:
160 """
161 Sends a request to create an event stream.
162
163 Args:
164 event_stream (MimecastEventStream): The event stream to be created.
165
166 Returns:
167 StreamCreationResponse: The response containing the created event stream.
168 """
169 json_event_stream = json.dumps(event_stream.model_dump())
170 logger.info(f"json_event_stream set: {json_event_stream}")
171 response_json = await send_post_request_create_entity(
172 endpoint="/api/streams",
173 entity=event_stream.model_dump(),
174 )
175 return StreamCreationResponse(**response_json)
176
177
178 async def create_event_stream(
179 customer_code: str,
180 index_set_id: str,
181 session: AsyncSession,
182 ) -> StreamCreationResponse:
183 """
184 Creates an event stream for a customer.
185
186 Args:
187 request (ProvisionNewCustomer): The request object containing customer information.
188 index_set_id (str): The ID of the index set.
189
190 Returns:
191 The result of the event stream creation request.
192 """
193 event_stream_config = await build_event_stream_config(
194 customer_code,
195 index_set_id,
196 session,
197 )
198 return await send_event_stream_creation_request(event_stream_config)
199
200
201 #### ! GRAFANA ! ####
202 async def create_grafana_datasource(
203 customer_code: str,
204 session: AsyncSession,
205 ) -> GrafanaDataSourceCreationResponse:
206 """
207 Creates a Grafana datasource for the specified customer.
208
209 Args:
210 customer_code (str): The customer code.
211 session (AsyncSession): The async session.
212
213 Returns:
214 GrafanaDataSourceCreationResponse: The response containing the created datasource details.
215 """
216 logger.info("Creating Grafana datasource")
217 grafana_client = await create_grafana_client("Grafana")
218 # Switch to the newly created organization
219 grafana_client.user.switch_actual_user_organisation(
220 (await get_customer_meta(customer_code, session)).customer_meta.customer_meta_grafana_org_id,
221 )
222 datasource_payload = GrafanaDatasource(
223 name="MIMECAST",
224 type="grafana-opensearch-datasource",
225 typeName="OpenSearch",
226 access="proxy",
227 url=await get_connector_attribute(
228 connector_id=1,
229 column_name="connector_url",
230 session=session,
231 ),
232 database=f"mimecast_{customer_code}*",
233 basicAuth=True,
234 basicAuthUser=await get_connector_attribute(
235 connector_id=1,
236 column_name="connector_username",
237 session=session,
238 ),
239 secureJsonData={
240 "basicAuthPassword": await get_connector_attribute(
241 connector_id=1,
242 column_name="connector_password",
243 session=session,
244 ),
245 },
246 isDefault=False,
247 jsonData={
248 "database": f"mimecast_{customer_code}*",
249 "flavor": "opensearch",
250 "includeFrozen": False,
251 "logLevelField": "syslog_level",
252 "logMessageField": "rule_description",
253 "maxConcurrentShardRequests": 5,
254 "pplEnabled": True,
255 "timeField": "timestamp",
256 "tlsSkipVerify": True,
257 "version": await get_opensearch_version(),
258 },
259 readOnly=True,
260 )
261 results = grafana_client.datasource.create_datasource(
262 datasource=datasource_payload.model_dump(),
263 )
264 return GrafanaDataSourceCreationResponse(**results)
265
266
267 async def provision_mimecast(
268 provision_mimecast_request: ProvisionMimecastRequest,
269 session: AsyncSession,
270 ) -> ProvisionMimecastResponse:
271 """
272 Provisions Mimecast integration for a customer.
273
274 Args:
275 provision_mimecast_request (ProvisionMimecastRequest): The request object containing the necessary information for provisioning.
276 session (AsyncSession, optional): The database session. Defaults to Depends(get_db).
277
278 Returns:
279 ProvisionMimecastResponse: The response object containing the result of the provisioning.
280 """
281 logger.info(
282 f"Provisioning Mimecast integration for customer {provision_mimecast_request.customer_code}.",
283 )
284
285 # Create Index Set
286 index_set_id = (
287 await create_index_set(
288 customer_code=provision_mimecast_request.customer_code,
289 session=session,
290 )
291 ).data.id
292 logger.info(f"Index set: {index_set_id}")
293 # Create event stream
294 stream_id = (
295 await create_event_stream(
296 provision_mimecast_request.customer_code,
297 index_set_id,
298 session,
299 )
300 ).data.stream_id
301 # Start stream
302 await start_stream(stream_id=stream_id)
303
304 # Grafana Deployment
305 mimecast_datasource_uid = (
306 await create_grafana_datasource(
307 customer_code=provision_mimecast_request.customer_code,
308 session=session,
309 )
310 ).datasource.uid
311 grafana_mimecast_folder_id = (
312 await create_grafana_folder(
313 organization_id=(
314 await get_customer_meta(
315 provision_mimecast_request.customer_code,
316 session,
317 )
318 ).customer_meta.customer_meta_grafana_org_id,
319 folder_title="MIMECAST",
320 )
321 ).id
322 await provision_dashboards(
323 DashboardProvisionRequest(
324 dashboards=[dashboard.name for dashboard in MimecastDashboard],
325 organizationId=(
326 await get_customer_meta(
327 provision_mimecast_request.customer_code,
328 session,
329 )
330 ).customer_meta.customer_meta_grafana_org_id,
331 folderId=grafana_mimecast_folder_id,
332 datasourceUid=mimecast_datasource_uid,
333 ),
334 )
335 await create_integration_meta_entry(
336 CustomerIntegrationsMetaSchema(
337 customer_code=provision_mimecast_request.customer_code,
338 integration_name="Mimecast",
339 graylog_input_id=None,
340 graylog_index_id=index_set_id,
341 graylog_stream_id=stream_id,
342 grafana_org_id=(
343 await get_customer_meta(
344 provision_mimecast_request.customer_code,
345 session,
346 )
347 ).customer_meta.customer_meta_grafana_org_id,
348 grafana_dashboard_folder_id=grafana_mimecast_folder_id,
349 grafana_datasource_uid=mimecast_datasource_uid,
350 ),
351 session,
352 )
353 await update_customer_integration_table(
354 provision_mimecast_request.customer_code,
355 session,
356 )
357
358 return ProvisionMimecastResponse(
359 success=True,
360 message="Mimecast integration provisioned.",
361 )
362
363
364 ############## ! WRITE TO DB ! ##############
365 async def create_integration_meta_entry(
366 customer_integration_meta: CustomerIntegrationsMetaSchema,
367 session: AsyncSession,
368 ) -> None:
369 """
370 Creates an entry for the customer integration meta in the database.
371
372 Args:
373 customer_integration_meta (CustomerIntegrationsMetaSchema): The customer integration meta object.
374 session (AsyncSession): The async session object for database operations.
375 """
376 await create_integration_meta(customer_integration_meta, session)
377 logger.info(
378 f"Integration meta entry created for customer {customer_integration_meta.customer_code}.",
379 )
380
381
382 async def update_customer_integration_table(
383 customer_code: str,
384 session: AsyncSession,
385 ) -> None:
386 """
387 Updates the `customer_integrations` table to set the `deployed` column to True where the `customer_code`
388 matches the given customer code and the `integration_service_name` is "Mimecast".
389
390 Args:
391 customer_code (str): The customer code.
392 session (AsyncSession): The async session object for making HTTP requests.
393 """
394 await session.execute(
395 update(CustomerIntegrations)
396 .where(
397 and_(
398 CustomerIntegrations.customer_code == customer_code,
399 CustomerIntegrations.integration_service_name == "Mimecast",
400 ),
401 )
402 .values(deployed=True),
403 )
404 await session.commit()
405
406 return None