1
1
import hashlib
2
2
import hmac
3
+ import logging
4
+
5
+ import requests
6
+ from django .conf import settings
7
+ from django_rq import job
8
+ from jinja2 .exceptions import TemplateError
9
+
10
+ from .constants import WEBHOOK_EVENT_TYPES
11
+
12
+ logger = logging .getLogger ('netbox.webhooks' )
3
13
4
14
5
15
def generate_signature (request_body , secret ):
@@ -12,3 +22,79 @@ def generate_signature(request_body, secret):
12
22
digestmod = hashlib .sha512
13
23
)
14
24
return hmac_prep .hexdigest ()
25
+
26
+
27
+ @job ('default' )
28
+ def send_webhook (event_rule , model_name , event , data , timestamp , username , request_id = None , snapshots = None ):
29
+ """
30
+ Make a POST request to the defined Webhook
31
+ """
32
+ webhook = event_rule .action_object
33
+
34
+ # Prepare context data for headers & body templates
35
+ context = {
36
+ 'event' : WEBHOOK_EVENT_TYPES [event ],
37
+ 'timestamp' : timestamp ,
38
+ 'model' : model_name ,
39
+ 'username' : username ,
40
+ 'request_id' : request_id ,
41
+ 'data' : data ,
42
+ }
43
+ if snapshots :
44
+ context .update ({
45
+ 'snapshots' : snapshots
46
+ })
47
+
48
+ # Build the headers for the HTTP request
49
+ headers = {
50
+ 'Content-Type' : webhook .http_content_type ,
51
+ }
52
+ try :
53
+ headers .update (webhook .render_headers (context ))
54
+ except (TemplateError , ValueError ) as e :
55
+ logger .error (f"Error parsing HTTP headers for webhook { webhook } : { e } " )
56
+ raise e
57
+
58
+ # Render the request body
59
+ try :
60
+ body = webhook .render_body (context )
61
+ except TemplateError as e :
62
+ logger .error (f"Error rendering request body for webhook { webhook } : { e } " )
63
+ raise e
64
+
65
+ # Prepare the HTTP request
66
+ params = {
67
+ 'method' : webhook .http_method ,
68
+ 'url' : webhook .render_payload_url (context ),
69
+ 'headers' : headers ,
70
+ 'data' : body .encode ('utf8' ),
71
+ }
72
+ logger .info (
73
+ f"Sending { params ['method' ]} request to { params ['url' ]} ({ context ['model' ]} { context ['event' ]} )"
74
+ )
75
+ logger .debug (params )
76
+ try :
77
+ prepared_request = requests .Request (** params ).prepare ()
78
+ except requests .exceptions .RequestException as e :
79
+ logger .error (f"Error forming HTTP request: { e } " )
80
+ raise e
81
+
82
+ # If a secret key is defined, sign the request with a hash of the key and its content
83
+ if webhook .secret != '' :
84
+ prepared_request .headers ['X-Hook-Signature' ] = generate_signature (prepared_request .body , webhook .secret )
85
+
86
+ # Send the request
87
+ with requests .Session () as session :
88
+ session .verify = webhook .ssl_verification
89
+ if webhook .ca_file_path :
90
+ session .verify = webhook .ca_file_path
91
+ response = session .send (prepared_request , proxies = settings .HTTP_PROXIES )
92
+
93
+ if 200 <= response .status_code <= 299 :
94
+ logger .info (f"Request succeeded; response status { response .status_code } " )
95
+ return f"Status { response .status_code } returned, webhook successfully processed."
96
+ else :
97
+ logger .warning (f"Request failed; response status { response .status_code } : { response .content } " )
98
+ raise requests .exceptions .RequestException (
99
+ f"Status { response .status_code } returned with content '{ response .content } ', webhook FAILED to process."
100
+ )
0 commit comments