FOSSology  4.7.0-rc1
Open Source License Compliance by Open Source Software
ReportController.php
Go to the documentation of this file.
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2018, 2021 Siemens AG
4  Author: Gaurav Mishra <mishra.gaurav@siemens.com>
5 
6  SPDX-License-Identifier: GPL-2.0-only
7 */
13 namespace Fossology\UI\Api\Controllers;
14 
32 use Psr\Http\Message\ServerRequestInterface;
33 use Slim\Psr7\Factory\StreamFactory;
34 use Slim\Psr7\Request as SlimRequest;
35 use Slim\Psr7\UploadedFile as SlimUploadedFile;
36 use Symfony\Component\HttpFoundation\File\UploadedFile;
37 use Symfony\Component\HttpFoundation\Request;
38 
44 {
45 
50  private $reportsAllowed = array(
51  'dep5',
52  'spdx2',
53  'spdx2tv',
54  'readmeoss',
55  'unifiedreport',
56  'clixml',
57  'decisionexporter',
58  'cyclonedx',
59  'spdx3json',
60  'spdx3rdf',
61  'spdx3jsonld'
62  );
63 
68  private $importAllowed = [
69  'decisionimporter',
70  'spdxrdf'
71  ];
72 
82  public function getReport($request, $response, $args)
83  {
84  $apiVersion = ApiVersion::getVersion($request);
85  $uploadId = null;
86  $reportFormat = null;
87  if ($apiVersion == ApiVersion::V2) {
88  $query = $request->getQueryParams();
89  $uploadId = $query['uploadId'];
90  $reportFormat = $query['reportFormat'];
91  } else {
92  $uploadId = $request->getHeaderLine('uploadId');
93  $reportFormat = $request->getHeaderLine('reportFormat');
94  }
95 
96  if (! in_array($reportFormat, $this->reportsAllowed)) {
97  throw new HttpBadRequestException(
98  "reportFormat must be from [" . implode(",", $this->reportsAllowed) .
99  "]");
100  }
101  $upload = $this->getUpload($uploadId);
102  if (get_class($upload) === Info::class) {
103  return $response->withJson($upload->getArray(), $upload->getCode());
104  }
105  $jobId = null;
106  $jobQueueId = null;
107 
108  switch ($reportFormat) {
109  case $this->reportsAllowed[0]:
110  case $this->reportsAllowed[1]:
111  case $this->reportsAllowed[2]:
113  $spdxGenerator = $this->restHelper->getPlugin('ui_spdx2');
114  list ($jobId, $jobQueueId, $error) = $spdxGenerator->scheduleAgent(
115  $this->restHelper->getGroupId(), $upload, $reportFormat);
116  break;
117  case $this->reportsAllowed[3]:
119  $readmeGenerator = $this->restHelper->getPlugin('ui_readmeoss');
120  list ($jobId, $jobQueueId, $error) = $readmeGenerator->scheduleAgent(
121  $this->restHelper->getGroupId(), $upload);
122  break;
123  case $this->reportsAllowed[4]:
125  $unifiedGenerator = $this->restHelper->getPlugin('agent_founifiedreport');
126  list ($jobId, $jobQueueId, $error) = $unifiedGenerator->scheduleAgent(
127  $this->restHelper->getGroupId(), $upload);
128  break;
129  case $this->reportsAllowed[5]:
131  $clixmlGenerator = $this->restHelper->getPlugin('ui_clixml');
132  list ($jobId, $jobQueueId) = $clixmlGenerator->scheduleAgent(
133  $this->restHelper->getGroupId(), $upload);
134  break;
135  case $this->reportsAllowed[6]:
137  $decisionExporter = $this->restHelper->getPlugin('agent_fodecisionexporter');
138  list($jobId, $jobQueueId) = $decisionExporter->scheduleAgent(
139  $this->restHelper->getGroupId(), $upload);
140  break;
141  case $this->reportsAllowed[7]:
143  $cyclonedxGenerator = $this->restHelper->getPlugin('ui_cyclonedx');
144  list ($jobId, $jobQueueId) = $cyclonedxGenerator->scheduleAgent(
145  $this->restHelper->getGroupId(), $upload);
146  break;
147  case $this->reportsAllowed[8]:
148  case $this->reportsAllowed[9]:
149  case $this->reportsAllowed[10]:
151  $spdx3Generator = $this->restHelper->getPlugin('ui_spdx3');
152  list ($jobId, $jobQueueId, $error) = $spdx3Generator->scheduleAgent(
153  $this->restHelper->getGroupId(), $upload, $reportFormat);
154  break;
155  default:
156  throw new HttpInternalServerErrorException("Some error occured!");
157  }
158  $download_path = $this->buildDownloadPath($request, $jobId);
159  $info = new Info(201, $download_path, InfoType::INFO);
160  return $response->withJson($info->getArray(), $info->getCode());
161  }
162 
170  private function getUpload($uploadId)
171  {
172  if (empty($uploadId) || ! is_numeric($uploadId) || $uploadId <= 0) {
173  throw new HttpBadRequestException("uploadId must be a positive integer!");
174  }
175  $uploadDao = $this->restHelper->getUploadDao();
176  if (! $uploadDao->isAccessible($uploadId, $this->restHelper->getGroupId())) {
177  throw new HttpForbiddenException("Upload is not accessible!");
178  }
179  $upload = $uploadDao->getUpload($uploadId);
180  if ($upload === null) {
181  throw new HttpNotFoundException("Upload does not exists!");
182  }
183  return $upload;
184  }
185 
192  public static function buildDownloadPath($request, $jobId)
193  {
194  $url_parts = $request->getUri();
195  $download_path = "";
196  if (!empty($url_parts->getScheme())) {
197  $download_path .= $url_parts->getScheme() . "://";
198  }
199  if (!empty($url_parts->getHost())) {
200  $download_path .= $url_parts->getHost();
201  }
202  if (!empty($url_parts->getPort())) {
203  $download_path .= ':' . $url_parts->getPort();
204  }
205  $endpoint = substr($url_parts->getPath(), 0, strpos($url_parts->getPath(),
206  $GLOBALS["apiBasePath"]) + strlen($GLOBALS["apiBasePath"]));
207  if (substr($endpoint, -1) !== '/') {
208  $endpoint .= '/';
209  }
210  $endpoint .= "report/" . $jobId;
211  $download_path .= $endpoint;
212  return $download_path;
213  }
214 
224  public function downloadReport($request, $response, $args)
225  {
226  $id = $args['id'];
227  $this->checkReport($id);
229  $ui_download = $this->restHelper->getPlugin('download');
230  $responseFile = $ui_download->getReport($args['id']);
231  $responseContent = $responseFile->getFile();
232  $newResponse = $response->withHeader('Content-Description',
233  'File Transfer')
234  ->withHeader('Content-Type',
235  $responseContent->getMimeType())
236  ->withHeader('Content-Disposition',
237  $responseFile->headers->get('Content-Disposition'))
238  ->withHeader('Cache-Control', 'must-revalidate')
239  ->withHeader('Pragma', 'private')
240  ->withHeader('Content-Length', filesize($responseContent->getPathname()));
241  $sf = new StreamFactory();
242  return $newResponse->withBody(
243  $sf->createStreamFromFile($responseContent->getPathname())
244  );
245  }
246 
254  private function checkReport($id)
255  {
256  $dbManager = $this->dbHelper->getDbManager();
257  $row = $dbManager->getSingleRow(
258  'SELECT jq_type FROM jobqueue WHERE jq_job_fk = $1', array(
259  $id
260  ), "reportValidity");
261  if (empty($row) || ! in_array($row['jq_type'], $this->reportsAllowed)) {
262  throw new HttpNotFoundException(
263  "No report scheduled with given job id.");
264  }
265  $row = $dbManager->getSingleRow('SELECT job_upload_fk FROM job WHERE job_pk = $1',
266  array($id), "reportFileUpload");
267  if (empty($row)) {
268  throw new HttpNotFoundException(
269  "No report scheduled with given job id.");
270  }
271  $uploadId = intval($row['job_upload_fk']);
272  $uploadDao = $this->restHelper->getUploadDao();
273  if (! $uploadDao->isAccessible($uploadId, $this->restHelper->getGroupId())) {
274  throw new HttpForbiddenException("Report is not accessible!");
275  }
276  $row = $dbManager->getSingleRow('SELECT * FROM reportgen WHERE job_fk = $1',
277  array($id), "reportFileName");
278  if (empty($row)) {
280  "Report is not ready. Retry after 10s."))
281  ->setHeaders(['Retry-After' => '10']);
282  }
283  // Everything went well
284  return true;
285  }
286 
296  public function importReport(ServerRequestInterface $request,
297  ResponseHelper $response, array $args): ResponseHelper
298  {
299  $query = $request->getQueryParams();
300  if (!array_key_exists("upload", $query)) {
301  throw new HttpBadRequestException("Missing query param 'upload'");
302  }
303  if (!array_key_exists("reportFormat", $query) ||
304  !in_array($query["reportFormat"], $this->importAllowed)) {
305  throw new HttpBadRequestException(
306  "Missing or wrong query param 'reportFormat'");
307  }
308  $upload_pk = intval($query['upload']);
309  // checking if the scheduler is running or not
310  $commu_status = fo_communicate_with_scheduler('status',
311  $response_from_scheduler, $error_info);
312  if (!$commu_status) {
313  throw new HttpServiceUnavailableException("Scheduler is not running!");
314  }
315  $files = $request->getUploadedFiles();
316 
317  $this->uploadAccessible($upload_pk);
318  if (empty($files['report'])) {
319  throw new HttpBadRequestException("No file uploaded");
320  }
322  $slimFile = $files['report'];
323 
324  $reportFormat = $query["reportFormat"];
325  switch ($reportFormat) {
326  case $this->importAllowed[0]:
327  $returnVal = $this->importDecisionJson($request, $response,
328  $upload_pk, $slimFile);
329  break;
330  case $this->importAllowed[1]:
331  $returnVal = $this->importSpdxReport($request, $response, $upload_pk,
332  $slimFile);
333  break;
334  default:
335  throw new HttpBadRequestException(
336  "Report format $reportFormat not supported. Supported formats are [" .
337  implode(", ", $this->importAllowed) . "]");
338  }
339  return $returnVal;
340  }
341 
352  private function importDecisionJson(ServerRequestInterface $request,
353  ResponseHelper $response, int $uploadId,
354  SlimUploadedFile $slimFile): ResponseHelper
355  {
356  $this->throwNotAdminException();
358  $decisionImporter = $this->restHelper->getPlugin("ui_fodecisionimporter");
359  $symfonyRequest = new Request();
360 
361  $reqBody = $this->getParsedBody($request);
362 
363  if (!array_key_exists("importerUser", $reqBody)) {
364  throw new HttpBadRequestException("Missing parameter 'importerUser'");
365  }
366 
367  $importerUser = intval($reqBody["importerUser"]);
368  if (empty($importerUser)) {
369  $importerUser = $this->restHelper->getUserId();
370  }
371 
372  $uploadedFile = new UploadedFile($slimFile->getFilePath(),
373  $slimFile->getClientFilename(), $slimFile->getClientMediaType());
374 
375  $symfonyRequest->files->set('report', $uploadedFile);
376  $symfonyRequest->request->set('uploadselect', $uploadId);
377  $symfonyRequest->request->set('userselect', $importerUser);
378 
379  $agentResp = $decisionImporter->handleRequest($symfonyRequest);
380 
381  if ($agentResp === false) {
382  throw new HttpBadRequestException("Missing required fields");
383  }
384  $info = new Info(201, intval($agentResp[0]), InfoType::INFO);
385  return $response->withJson($info->getArray(), $info->getCode());
386  }
387 
397  public function importSpdxReport(ServerRequestInterface $request,
398  ResponseHelper $response, int $upload_pk,
399  SlimUploadedFile $slimFile): ResponseHelper
400  {
401  $reqBody = $this->getParsedBody($request);
403  $reportImport = $this->restHelper->getPlugin('ui_reportImport');
404  $symfonyRequest = new Request();
405 
406  // moving the uploaded file to the ReportImport Directory
407  global $SysConf;
408  $fileBase = $SysConf['FOSSOLOGY']['path'] . "/ReportImport/";
409  if (!is_dir($fileBase)) {
410  mkdir($fileBase, 0755, true);
411  }
412  $targetFile = time() . '_' . rand() . '_' . $slimFile->getClientFilename();
413  $slimFile->moveTo($fileBase . $targetFile);
414 
415  // Get default values for parameters
416  $addNewLicensesAs = "candidate";
417  $addLicenseInfoFromInfoInFile = "true";
418  $addLicenseInfoFromConcluded = "false";
419  $addConcludedAsDecisions = "true";
420  $addConcludedAsDecisionsTBD = "true";
421  $addCopyrights = "false";
422  if (array_key_exists("addNewLicensesAs", $reqBody) &&
423  $reqBody["addNewLicensesAs"] === "license") {
424  $addNewLicensesAs = "license";
425  }
426  if (array_key_exists("addLicenseInfoFromInfoInFile", $reqBody) &&
427  !filter_var($reqBody["addLicenseInfoFromInfoInFile"],
428  FILTER_VALIDATE_BOOLEAN)) {
429  $addLicenseInfoFromInfoInFile = "false";
430  }
431  if (array_key_exists("addLicenseInfoFromConcluded", $reqBody) &&
432  filter_var($reqBody["addLicenseInfoFromConcluded"],
433  FILTER_VALIDATE_BOOLEAN)) {
434  $addLicenseInfoFromConcluded = "true";
435  }
436  if (array_key_exists("addConcludedAsDecisions", $reqBody) &&
437  !filter_var($reqBody["addConcludedAsDecisions"],
438  FILTER_VALIDATE_BOOLEAN)) {
439  $addConcludedAsDecisions = "false";
440  }
441  if (array_key_exists("addConcludedAsDecisionsTBD", $reqBody) &&
442  !filter_var($reqBody["addConcludedAsDecisionsTBD"],
443  FILTER_VALIDATE_BOOLEAN)) {
444  $addConcludedAsDecisionsTBD = "false";
445  }
446  if (array_key_exists("addCopyrights", $reqBody) &&
447  filter_var($reqBody["addCopyrights"],
448  FILTER_VALIDATE_BOOLEAN)) {
449  $addCopyrights = "true";
450  }
451 
452  // translating values for symfony request
453  $symfonyRequest->request->set('addNewLicensesAs', $addNewLicensesAs);
454  $symfonyRequest->request->set('addLicenseInfoFromInfoInFile',
455  $addLicenseInfoFromInfoInFile);
456  $symfonyRequest->request->set('addLicenseInfoFromConcluded',
457  $addLicenseInfoFromConcluded);
458  $symfonyRequest->request->set('addConcludedAsDecisions',
459  $addConcludedAsDecisions);
460  $symfonyRequest->request->set('addConcludedAsDecisionsTBD',
461  $addConcludedAsDecisionsTBD);
462  $symfonyRequest->request->set('addCopyrights', $addCopyrights);
463 
464  $agentResp = $reportImport->runImport($upload_pk, $targetFile, $symfonyRequest);
465  $returnVal = new Info(201, intval($agentResp[0]), InfoType::INFO);
466  return $response->withJson($returnVal->getArray(), $returnVal->getCode());
467  }
468 }
FOSSology Decision Exporter UI plugin.
Agent plugin for Readme_OSS agent.
Call SPDX3 agent to generate report from UI.
Base controller for REST calls.
getParsedBody(ServerRequestInterface $request)
Parse request body as JSON and return associative PHP array.
Override Slim response for withJson function.
static getVersion(ServerRequestInterface $request)
Definition: ApiVersion.php:29
Different type of infos provided by REST.
Definition: InfoType.php:16
Info model to contain general error and return values.
Definition: Info.php:19
fo_communicate_with_scheduler($input, &$output, &$error_msg)
Communicate with scheduler, send commands to the scheduler, then get the output.