FOSSology  4.7.1
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  'spdx2csv',
55  'readmeoss',
56  'unifiedreport',
57  'clixml',
58  'decisionexporter',
59  'cyclonedx',
60  'spdx3json',
61  'spdx3rdf',
62  'spdx3jsonld',
63  'spdx3tv'
64  );
65 
70  private $importAllowed = [
71  'decisionimporter',
72  'spdxrdf'
73  ];
74 
84  public function getReport($request, $response, $args)
85  {
86  $apiVersion = ApiVersion::getVersion($request);
87  $uploadId = null;
88  $reportFormat = null;
89  if ($apiVersion == ApiVersion::V2) {
90  $query = $request->getQueryParams();
91  $uploadId = $query['uploadId'];
92  $reportFormat = $query['reportFormat'];
93  } else {
94  $uploadId = $request->getHeaderLine('uploadId');
95  $reportFormat = $request->getHeaderLine('reportFormat');
96  }
97 
98  if (! in_array($reportFormat, $this->reportsAllowed)) {
99  throw new HttpBadRequestException(
100  "reportFormat must be from [" . implode(",", $this->reportsAllowed) .
101  "]");
102  }
103  $upload = $this->getUpload($uploadId);
104  if (get_class($upload) === Info::class) {
105  return $response->withJson($upload->getArray(), $upload->getCode());
106  }
107  $jobId = null;
108  $jobQueueId = null;
109 
110  switch ($reportFormat) {
111  case 'dep5':
112  case 'spdx2':
113  case 'spdx2tv':
114  case 'spdx2csv':
116  $spdxGenerator = $this->restHelper->getPlugin('ui_spdx2');
117  list ($jobId, $jobQueueId, $error) = $spdxGenerator->scheduleAgent(
118  $this->restHelper->getGroupId(), $upload, $reportFormat);
119  break;
120  case 'readmeoss':
122  $readmeGenerator = $this->restHelper->getPlugin('ui_readmeoss');
123  list ($jobId, $jobQueueId, $error) = $readmeGenerator->scheduleAgent(
124  $this->restHelper->getGroupId(), $upload);
125  break;
126  case 'unifiedreport':
128  $unifiedGenerator = $this->restHelper->getPlugin('agent_founifiedreport');
129  list ($jobId, $jobQueueId, $error) = $unifiedGenerator->scheduleAgent(
130  $this->restHelper->getGroupId(), $upload);
131  break;
132  case 'clixml':
134  $clixmlGenerator = $this->restHelper->getPlugin('ui_clixml');
135  list ($jobId, $jobQueueId) = $clixmlGenerator->scheduleAgent(
136  $this->restHelper->getGroupId(), $upload);
137  break;
138  case 'decisionexporter':
140  $decisionExporter = $this->restHelper->getPlugin('agent_fodecisionexporter');
141  list($jobId, $jobQueueId) = $decisionExporter->scheduleAgent(
142  $this->restHelper->getGroupId(), $upload);
143  break;
144  case 'cyclonedx':
146  $cyclonedxGenerator = $this->restHelper->getPlugin('ui_cyclonedx');
147  list ($jobId, $jobQueueId) = $cyclonedxGenerator->scheduleAgent(
148  $this->restHelper->getGroupId(), $upload);
149  break;
150  case 'spdx3json':
151  case 'spdx3rdf':
152  case 'spdx3jsonld':
153  case 'spdx3tv':
155  $spdx3Generator = $this->restHelper->getPlugin('ui_spdx3');
156  list ($jobId, $jobQueueId, $error) = $spdx3Generator->scheduleAgent(
157  $this->restHelper->getGroupId(), $upload, $reportFormat);
158  break;
159  default:
160  throw new HttpInternalServerErrorException("Some error occured!");
161  }
162  $download_path = $this->buildDownloadPath($request, $jobId);
163  $info = new Info(201, $download_path, InfoType::INFO);
164  return $response->withJson($info->getArray(), $info->getCode());
165  }
166 
174  private function getUpload($uploadId)
175  {
176  if (empty($uploadId) || ! is_numeric($uploadId) || $uploadId <= 0) {
177  throw new HttpBadRequestException("uploadId must be a positive integer!");
178  }
179  $uploadDao = $this->restHelper->getUploadDao();
180  if (! $uploadDao->isAccessible($uploadId, $this->restHelper->getGroupId())) {
181  throw new HttpForbiddenException("Upload is not accessible!");
182  }
183  $upload = $uploadDao->getUpload($uploadId);
184  if ($upload === null) {
185  throw new HttpNotFoundException("Upload does not exists!");
186  }
187  return $upload;
188  }
189 
196  public static function buildDownloadPath($request, $jobId)
197  {
198  $url_parts = $request->getUri();
199  $download_path = "";
200  if (!empty($url_parts->getScheme())) {
201  $download_path .= $url_parts->getScheme() . "://";
202  }
203  if (!empty($url_parts->getHost())) {
204  $download_path .= $url_parts->getHost();
205  }
206  if (!empty($url_parts->getPort())) {
207  $download_path .= ':' . $url_parts->getPort();
208  }
209  $endpoint = substr($url_parts->getPath(), 0, strpos($url_parts->getPath(),
210  $GLOBALS["apiBasePath"]) + strlen($GLOBALS["apiBasePath"]));
211  if (substr($endpoint, -1) !== '/') {
212  $endpoint .= '/';
213  }
214  $endpoint .= "report/" . $jobId;
215  $download_path .= $endpoint;
216  return $download_path;
217  }
218 
228  public function downloadReport($request, $response, $args)
229  {
230  $id = $args['id'];
231  $this->checkReport($id);
233  $ui_download = $this->restHelper->getPlugin('download');
234  $responseFile = $ui_download->getReport($args['id']);
235  $responseContent = $responseFile->getFile();
236  $newResponse = $response->withHeader('Content-Description',
237  'File Transfer')
238  ->withHeader('Content-Type',
239  $responseContent->getMimeType())
240  ->withHeader('Content-Disposition',
241  $responseFile->headers->get('Content-Disposition'))
242  ->withHeader('Cache-Control', 'must-revalidate')
243  ->withHeader('Pragma', 'private')
244  ->withHeader('Content-Length', filesize($responseContent->getPathname()));
245  $sf = new StreamFactory();
246  return $newResponse->withBody(
247  $sf->createStreamFromFile($responseContent->getPathname())
248  );
249  }
250 
258  private function checkReport($id)
259  {
260  $dbManager = $this->dbHelper->getDbManager();
261  $row = $dbManager->getSingleRow(
262  'SELECT jq_type FROM jobqueue WHERE jq_job_fk = $1', array(
263  $id
264  ), "reportValidity");
265  if (empty($row) || ! in_array($row['jq_type'], $this->reportsAllowed)) {
266  throw new HttpNotFoundException(
267  "No report scheduled with given job id.");
268  }
269  $row = $dbManager->getSingleRow('SELECT job_upload_fk FROM job WHERE job_pk = $1',
270  array($id), "reportFileUpload");
271  if (empty($row)) {
272  throw new HttpNotFoundException(
273  "No report scheduled with given job id.");
274  }
275  $uploadId = intval($row['job_upload_fk']);
276  $uploadDao = $this->restHelper->getUploadDao();
277  if (! $uploadDao->isAccessible($uploadId, $this->restHelper->getGroupId())) {
278  throw new HttpForbiddenException("Report is not accessible!");
279  }
280  $row = $dbManager->getSingleRow('SELECT * FROM reportgen WHERE job_fk = $1',
281  array($id), "reportFileName");
282  if (empty($row)) {
284  "Report is not ready. Retry after 10s."))
285  ->setHeaders(['Retry-After' => '10']);
286  }
287  // Everything went well
288  return true;
289  }
290 
300  public function importReport(ServerRequestInterface $request,
301  ResponseHelper $response, array $args): ResponseHelper
302  {
303  $query = $request->getQueryParams();
304  if (!array_key_exists("upload", $query)) {
305  throw new HttpBadRequestException("Missing query param 'upload'");
306  }
307  if (!array_key_exists("reportFormat", $query) ||
308  !in_array($query["reportFormat"], $this->importAllowed)) {
309  throw new HttpBadRequestException(
310  "Missing or wrong query param 'reportFormat'");
311  }
312  $upload_pk = intval($query['upload']);
313  // checking if the scheduler is running or not
314  $commu_status = fo_communicate_with_scheduler('status',
315  $response_from_scheduler, $error_info);
316  if (!$commu_status) {
317  throw new HttpServiceUnavailableException("Scheduler is not running!");
318  }
319  $files = $request->getUploadedFiles();
320 
321  $this->uploadAccessible($upload_pk);
322  if (empty($files['report'])) {
323  throw new HttpBadRequestException("No file uploaded");
324  }
326  $slimFile = $files['report'];
327 
328  $reportFormat = $query["reportFormat"];
329  switch ($reportFormat) {
330  case $this->importAllowed[0]:
331  $returnVal = $this->importDecisionJson($request, $response,
332  $upload_pk, $slimFile);
333  break;
334  case $this->importAllowed[1]:
335  $returnVal = $this->importSpdxReport($request, $response, $upload_pk,
336  $slimFile);
337  break;
338  default:
339  throw new HttpBadRequestException(
340  "Report format $reportFormat not supported. Supported formats are [" .
341  implode(", ", $this->importAllowed) . "]");
342  }
343  return $returnVal;
344  }
345 
356  private function importDecisionJson(ServerRequestInterface $request,
357  ResponseHelper $response, int $uploadId,
358  SlimUploadedFile $slimFile): ResponseHelper
359  {
360  $this->throwNotAdminException();
362  $decisionImporter = $this->restHelper->getPlugin("ui_fodecisionimporter");
363  $symfonyRequest = new Request();
364 
365  $reqBody = $this->getParsedBody($request);
366 
367  if (!array_key_exists("importerUser", $reqBody)) {
368  throw new HttpBadRequestException("Missing parameter 'importerUser'");
369  }
370 
371  $importerUser = intval($reqBody["importerUser"]);
372  if (empty($importerUser)) {
373  $importerUser = $this->restHelper->getUserId();
374  }
375 
376  $uploadedFile = new UploadedFile($slimFile->getFilePath(),
377  $slimFile->getClientFilename(), $slimFile->getClientMediaType());
378 
379  $symfonyRequest->files->set('report', $uploadedFile);
380  $symfonyRequest->request->set('uploadselect', $uploadId);
381  $symfonyRequest->request->set('userselect', $importerUser);
382 
383  $agentResp = $decisionImporter->handleRequest($symfonyRequest);
384 
385  if ($agentResp === false) {
386  throw new HttpBadRequestException("Missing required fields");
387  }
388  $info = new Info(201, intval($agentResp[0]), InfoType::INFO);
389  return $response->withJson($info->getArray(), $info->getCode());
390  }
391 
401  public function importSpdxReport(ServerRequestInterface $request,
402  ResponseHelper $response, int $upload_pk,
403  SlimUploadedFile $slimFile): ResponseHelper
404  {
405  $reqBody = $this->getParsedBody($request);
407  $reportImport = $this->restHelper->getPlugin('ui_reportImport');
408  $symfonyRequest = new Request();
409 
410  // moving the uploaded file to the ReportImport Directory
411  global $SysConf;
412  $fileBase = $SysConf['FOSSOLOGY']['path'] . "/ReportImport/";
413  if (!is_dir($fileBase)) {
414  mkdir($fileBase, 0755, true);
415  }
416  $targetFile = time() . '_' . rand() . '_' . $slimFile->getClientFilename();
417  $slimFile->moveTo($fileBase . $targetFile);
418 
419  // Get default values for parameters
420  $addNewLicensesAs = "candidate";
421  $addLicenseInfoFromInfoInFile = "true";
422  $addLicenseInfoFromConcluded = "false";
423  $addConcludedAsDecisions = "true";
424  $addConcludedAsDecisionsTBD = "true";
425  $addCopyrights = "false";
426  if (array_key_exists("addNewLicensesAs", $reqBody) &&
427  $reqBody["addNewLicensesAs"] === "license") {
428  $addNewLicensesAs = "license";
429  }
430  if (array_key_exists("addLicenseInfoFromInfoInFile", $reqBody) &&
431  !filter_var($reqBody["addLicenseInfoFromInfoInFile"],
432  FILTER_VALIDATE_BOOLEAN)) {
433  $addLicenseInfoFromInfoInFile = "false";
434  }
435  if (array_key_exists("addLicenseInfoFromConcluded", $reqBody) &&
436  filter_var($reqBody["addLicenseInfoFromConcluded"],
437  FILTER_VALIDATE_BOOLEAN)) {
438  $addLicenseInfoFromConcluded = "true";
439  }
440  if (array_key_exists("addConcludedAsDecisions", $reqBody) &&
441  !filter_var($reqBody["addConcludedAsDecisions"],
442  FILTER_VALIDATE_BOOLEAN)) {
443  $addConcludedAsDecisions = "false";
444  }
445  if (array_key_exists("addConcludedAsDecisionsTBD", $reqBody) &&
446  !filter_var($reqBody["addConcludedAsDecisionsTBD"],
447  FILTER_VALIDATE_BOOLEAN)) {
448  $addConcludedAsDecisionsTBD = "false";
449  }
450  if (array_key_exists("addCopyrights", $reqBody) &&
451  filter_var($reqBody["addCopyrights"],
452  FILTER_VALIDATE_BOOLEAN)) {
453  $addCopyrights = "true";
454  }
455 
456  // translating values for symfony request
457  $symfonyRequest->request->set('addNewLicensesAs', $addNewLicensesAs);
458  $symfonyRequest->request->set('addLicenseInfoFromInfoInFile',
459  $addLicenseInfoFromInfoInFile);
460  $symfonyRequest->request->set('addLicenseInfoFromConcluded',
461  $addLicenseInfoFromConcluded);
462  $symfonyRequest->request->set('addConcludedAsDecisions',
463  $addConcludedAsDecisions);
464  $symfonyRequest->request->set('addConcludedAsDecisionsTBD',
465  $addConcludedAsDecisionsTBD);
466  $symfonyRequest->request->set('addCopyrights', $addCopyrights);
467 
468  $agentResp = $reportImport->runImport($upload_pk, $targetFile, $symfonyRequest);
469  $returnVal = new Info(201, intval($agentResp[0]), InfoType::INFO);
470  return $response->withJson($returnVal->getArray(), $returnVal->getCode());
471  }
472 }
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.