7
7
# See https://aboutcode.org for more information about nexB OSS projects.
8
8
#
9
9
10
+ import json
11
+
12
+ from django .contrib .auth import get_user_model
13
+ from django .core import signing
10
14
from django .db import transaction
15
+ from django .http import Http404
16
+ from django .shortcuts import get_object_or_404
11
17
from django .utils import timezone
18
+ from django .views .decorators .csrf import csrf_exempt
19
+
12
20
from packageurl import PackageURL
13
21
from rest_framework import serializers , status , viewsets
14
- from rest_framework .decorators import action
22
+ from rest_framework .decorators import action , api_view
15
23
from rest_framework .permissions import IsAdminUser
16
24
from rest_framework .response import Response
17
25
21
29
from minecode import priority_router
22
30
from minecode .models import PriorityResourceURI , ResourceURI , ScannableURI
23
31
from minecode .permissions import IsScanQueueWorkerAPIUser
24
- from minecode .utils import validate_uuid
25
32
from minecode .utils import get_temp_file
33
+ from minecode .utils import get_webhook_url
26
34
27
35
28
36
class ResourceURISerializer (serializers .ModelSerializer ):
@@ -98,6 +106,7 @@ class ScannableURIViewSet(viewsets.ModelViewSet):
98
106
queryset = ScannableURI .objects .all ()
99
107
serializer_class = ScannableURISerializer
100
108
permission_classes = [IsScanQueueWorkerAPIUser | IsAdminUser ]
109
+ lookup_field = 'uuid'
101
110
102
111
@action (detail = False , methods = ['get' ])
103
112
def get_next_download_url (self , request , * args , ** kwargs ):
@@ -107,10 +116,13 @@ def get_next_download_url(self, request, *args, **kwargs):
107
116
with transaction .atomic ():
108
117
scannable_uri = ScannableURI .objects .get_next_scannable ()
109
118
if scannable_uri :
119
+ user = self .request .user
120
+ webhook_url = get_webhook_url ('index_package_scan' , user .id )
110
121
response = {
111
122
'scannable_uri_uuid' : scannable_uri .uuid ,
112
123
'download_url' : scannable_uri .uri ,
113
124
'pipelines' : scannable_uri .pipelines ,
125
+ 'webhook_url' : webhook_url ,
114
126
}
115
127
scannable_uri .scan_status = ScannableURI .SCAN_SUBMITTED
116
128
scannable_uri .scan_date = timezone .now ()
@@ -120,52 +132,28 @@ def get_next_download_url(self, request, *args, **kwargs):
120
132
'scannable_uri_uuid' : '' ,
121
133
'download_url' : '' ,
122
134
'pipelines' : [],
135
+ 'webhook_url' : '' ,
123
136
}
124
137
return Response (response )
125
138
126
- @action (detail = False , methods = ['post' ])
139
+ @action (detail = True , methods = ['post' ])
127
140
def update_status (self , request , * args , ** kwargs ):
128
141
"""
129
- Update the status of a ScannableURI with UUID of `scannable_uri_uuid`
130
- with `scan_status`
142
+ Update the status of a ScannableURI with `scan_status`
131
143
132
144
If `scan_status` is 'failed', then a `scan_log` string is expected and
133
145
should contain the error messages for that scan.
134
-
135
- If `scan_status` is 'scanned', then a `scan_results_file`,
136
- `scan_summary_file`, and `project_extra_data` mapping are expected.
137
- `scan_results_file`, `scan_summary_file`, and `project_extra_data` are
138
- then used to update Package data and its Resources.
139
146
"""
140
- scannable_uri_uuid = request .data .get ('scannable_uri_uuid' )
141
147
scan_status = request .data .get ('scan_status' )
142
- if not scannable_uri_uuid :
143
- response = {
144
- 'error' : 'missing scannable_uri_uuid'
145
- }
146
- return Response (response , status = status .HTTP_400_BAD_REQUEST )
147
-
148
148
if not scan_status :
149
149
response = {
150
150
'error' : 'missing scan_status'
151
151
}
152
152
return Response (response , status = status .HTTP_400_BAD_REQUEST )
153
153
154
- if not validate_uuid (scannable_uri_uuid ):
155
- response = {
156
- 'error' : f'invalid scannable_uri_uuid: { scannable_uri_uuid } '
157
- }
158
- return Response (response , status = status .HTTP_400_BAD_REQUEST )
159
-
160
- scannable_uri = ScannableURI .objects .get (uuid = scannable_uri_uuid )
154
+ scannable_uri = self .get_object ()
155
+ scannable_uri_uuid = scannable_uri .uuid
161
156
scannable_uri_status = ScannableURI .SCAN_STATUSES_BY_CODE .get (scannable_uri .scan_status )
162
- scan_status_code = ScannableURI .SCAN_STATUS_CODES_BY_SCAN_STATUS .get (scan_status )
163
-
164
- if not scan_status_code :
165
- msg = {
166
- 'error' : f'invalid scan_status: { scan_status } '
167
- }
168
- return Response (msg , status = status .HTTP_400_BAD_REQUEST )
169
157
170
158
if scannable_uri .scan_status in [
171
159
ScannableURI .SCAN_INDEXED ,
@@ -192,46 +180,65 @@ def update_status(self, request, *args, **kwargs):
192
180
scannable_uri .scan_status = ScannableURI .SCAN_FAILED
193
181
scannable_uri .wip_date = None
194
182
scannable_uri .save ()
195
- msg = {
183
+ response = {
196
184
'status' : f'updated scannable_uri { scannable_uri_uuid } scan_status to { scan_status } '
197
185
}
186
+ return Response (response )
198
187
199
- elif scan_status == 'scanned' :
200
- scan_results_file = request .data .get ('scan_results_file' )
201
- scan_summary_file = request .data .get ('scan_summary_file' )
202
- project_extra_data = request .data .get ('project_extra_data' )
203
-
204
- # Save results to temporary files
205
- scan_results_location = get_temp_file (
206
- file_name = 'scan_results' ,
207
- extension = '.json'
208
- )
209
- scan_summary_location = get_temp_file (
210
- file_name = 'scan_summary' ,
211
- extension = '.json'
212
- )
213
- with open (scan_results_location , 'wb' ) as f :
214
- f .write (scan_results_file .read ())
215
- with open (scan_summary_location , 'wb' ) as f :
216
- f .write (scan_summary_file .read ())
217
-
218
- scannable_uri .process_scan_results (
219
- scan_results_location = scan_results_location ,
220
- scan_summary_location = scan_summary_location ,
221
- project_extra_data = project_extra_data
222
- )
223
- msg = {
224
- 'status' : f'scan results for scannable_uri { scannable_uri_uuid } '
225
- 'have been queued for indexing'
226
- }
227
-
228
- return Response (msg )
229
-
230
- @action (detail = False , methods = ['get' ])
231
- def statistics (self , request , * args , ** kwargs ):
232
- """
233
- Return a scan queue statistics.
234
- """
235
- response = ScannableURI .objects .statistics ()
236
- return Response (response )
237
-
188
+ response = {
189
+ 'error' : f'invalid scan_status: { scan_status } '
190
+ }
191
+ return Response (response , status = status .HTTP_400_BAD_REQUEST )
192
+
193
+
194
+ @api_view (['POST' ])
195
+ @csrf_exempt
196
+ def index_package_scan (request , key ):
197
+ """
198
+ Given a `request` to the `/api/scan_queue/index_package_scan/<key>/`
199
+ endpoint, where `key` is the id of the purldb scan queue worker that has
200
+ been encoded as a secret, save the package scan results and summary to files
201
+ and create a new rq worker task to index the scan results and summary.
202
+ """
203
+ try :
204
+ json_data = json .loads (request .body .decode ("utf-8" ))
205
+ except json .JSONDecodeError :
206
+ raise Http404
207
+
208
+ user_id = signing .loads (key )
209
+ User = get_user_model ()
210
+ user = get_object_or_404 (User , id = user_id )
211
+
212
+ results = json_data .get ('results' )
213
+ summary = json_data .get ('summary' )
214
+ project_data = json_data .get ('project' )
215
+ extra_data = project_data .get ('extra_data' )
216
+ scannable_uri_uuid = extra_data .get ('scannable_uri_uuid' )
217
+
218
+ # Save results to temporary files
219
+ scan_results_location = get_temp_file (
220
+ file_name = 'scan_results' ,
221
+ extension = '.json'
222
+ )
223
+ scan_summary_location = get_temp_file (
224
+ file_name = 'scan_summary' ,
225
+ extension = '.json'
226
+ )
227
+
228
+ with open (scan_results_location , 'w' ) as f :
229
+ json .dump (results , f )
230
+
231
+ with open (scan_summary_location , 'w' ) as f :
232
+ json .dump (summary , f )
233
+
234
+ scannable_uri = get_object_or_404 (ScannableURI , uuid = scannable_uri_uuid )
235
+ scannable_uri .process_scan_results (
236
+ scan_results_location = scan_results_location ,
237
+ scan_summary_location = scan_summary_location ,
238
+ project_extra_data = extra_data
239
+ )
240
+ msg = {
241
+ 'status' : f'scan results for scannable_uri { scannable_uri .uuid } '
242
+ 'have been queued for indexing'
243
+ }
244
+ return Response (msg )
0 commit comments