FOSSology  4.4.0
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 
30 use Psr\Http\Message\ServerRequestInterface;
31 use Slim\Psr7\Factory\StreamFactory;
32 use Slim\Psr7\Request as SlimRequest;
33 use Slim\Psr7\UploadedFile as SlimUploadedFile;
34 use Symfony\Component\HttpFoundation\File\UploadedFile;
35 use Symfony\Component\HttpFoundation\Request;
36 
42 {
43 
48  private $reportsAllowed = array(
49  'dep5',
50  'spdx2',
51  'spdx2tv',
52  'readmeoss',
53  'unifiedreport',
54  'clixml',
55  'decisionexporter',
56  'cyclonedx'
57  );
58 
63  private $importAllowed = [
64  'decisionimporter',
65  'spdxrdf'
66  ];
67 
77  public function getReport($request, $response, $args)
78  {
79  $uploadId = $request->getHeaderLine('uploadId');
80  $reportFormat = $request->getHeaderLine('reportFormat');
81 
82  if (! in_array($reportFormat, $this->reportsAllowed)) {
83  throw new HttpBadRequestException(
84  "reportFormat must be from [" . implode(",", $this->reportsAllowed) .
85  "]");
86  }
87  $upload = $this->getUpload($uploadId);
88  if (get_class($upload) === Info::class) {
89  return $response->withJson($upload->getArray(), $upload->getCode());
90  }
91  $jobId = null;
92  $jobQueueId = null;
93 
94  switch ($reportFormat) {
95  case $this->reportsAllowed[0]:
96  case $this->reportsAllowed[1]:
97  case $this->reportsAllowed[2]:
99  $spdxGenerator = $this->restHelper->getPlugin('ui_spdx2');
100  list ($jobId, $jobQueueId, $error) = $spdxGenerator->scheduleAgent(
101  $this->restHelper->getGroupId(), $upload, $reportFormat);
102  break;
103  case $this->reportsAllowed[3]:
105  $readmeGenerator = $this->restHelper->getPlugin('ui_readmeoss');
106  list ($jobId, $jobQueueId, $error) = $readmeGenerator->scheduleAgent(
107  $this->restHelper->getGroupId(), $upload);
108  break;
109  case $this->reportsAllowed[4]:
111  $unifiedGenerator = $this->restHelper->getPlugin('agent_founifiedreport');
112  list ($jobId, $jobQueueId, $error) = $unifiedGenerator->scheduleAgent(
113  $this->restHelper->getGroupId(), $upload);
114  break;
115  case $this->reportsAllowed[5]:
117  $clixmlGenerator = $this->restHelper->getPlugin('ui_clixml');
118  list ($jobId, $jobQueueId) = $clixmlGenerator->scheduleAgent(
119  $this->restHelper->getGroupId(), $upload);
120  break;
121  case $this->reportsAllowed[6]:
123  $decisionExporter = $this->restHelper->getPlugin('agent_fodecisionexporter');
124  list($jobId, $jobQueueId) = $decisionExporter->scheduleAgent(
125  $this->restHelper->getGroupId(), $upload);
126  break;
127  case $this->reportsAllowed[7]:
129  $cyclonedxGenerator = $this->restHelper->getPlugin('ui_cyclonedx');
130  list ($jobId, $jobQueueId) = $cyclonedxGenerator->scheduleAgent(
131  $this->restHelper->getGroupId(), $upload);
132  break;
133  default:
134  throw new HttpInternalServerErrorException("Some error occured!");
135  }
136  $download_path = $this->buildDownloadPath($request, $jobId);
137  $info = new Info(201, $download_path, InfoType::INFO);
138  return $response->withJson($info->getArray(), $info->getCode());
139  }
140 
148  private function getUpload($uploadId)
149  {
150  if (empty($uploadId) || ! is_numeric($uploadId) || $uploadId <= 0) {
151  throw new HttpBadRequestException("uploadId must be a positive integer!");
152  }
153  $uploadDao = $this->restHelper->getUploadDao();
154  if (! $uploadDao->isAccessible($uploadId, $this->restHelper->getGroupId())) {
155  throw new HttpForbiddenException("Upload is not accessible!");
156  }
157  $upload = $uploadDao->getUpload($uploadId);
158  if ($upload === null) {
159  throw new HttpNotFoundException("Upload does not exists!");
160  }
161  return $upload;
162  }
163 
170  public static function buildDownloadPath($request, $jobId)
171  {
172  $url_parts = $request->getUri();
173  $download_path = "";
174  if (!empty($url_parts->getScheme())) {
175  $download_path .= $url_parts->getScheme() . "://";
176  }
177  if (!empty($url_parts->getHost())) {
178  $download_path .= $url_parts->getHost();
179  }
180  if (!empty($url_parts->getPort())) {
181  $download_path .= ':' . $url_parts->getPort();
182  }
183  $endpoint = substr($url_parts->getPath(), 0, strpos($url_parts->getPath(),
184  $GLOBALS["apiBasePath"]) + strlen($GLOBALS["apiBasePath"]));
185  if (substr($endpoint, -1) !== '/') {
186  $endpoint .= '/';
187  }
188  $endpoint .= "report/" . $jobId;
189  $download_path .= $endpoint;
190  return $download_path;
191  }
192 
202  public function downloadReport($request, $response, $args)
203  {
204  $id = $args['id'];
205  $this->checkReport($id);
207  $ui_download = $this->restHelper->getPlugin('download');
208  $responseFile = $ui_download->getReport($args['id']);
209  $responseContent = $responseFile->getFile();
210  $newResponse = $response->withHeader('Content-Description',
211  'File Transfer')
212  ->withHeader('Content-Type',
213  $responseContent->getMimeType())
214  ->withHeader('Content-Disposition',
215  $responseFile->headers->get('Content-Disposition'))
216  ->withHeader('Cache-Control', 'must-revalidate')
217  ->withHeader('Pragma', 'private')
218  ->withHeader('Content-Length', filesize($responseContent->getPathname()));
219  $sf = new StreamFactory();
220  return $newResponse->withBody(
221  $sf->createStreamFromFile($responseContent->getPathname())
222  );
223  }
224 
232  private function checkReport($id)
233  {
234  $dbManager = $this->dbHelper->getDbManager();
235  $row = $dbManager->getSingleRow(
236  'SELECT jq_type FROM jobqueue WHERE jq_job_fk = $1', array(
237  $id
238  ), "reportValidity");
239  if (! in_array($row['jq_type'], $this->reportsAllowed)) {
240  throw new HttpNotFoundException(
241  "No report scheduled with given job id.");
242  }
243  $row = $dbManager->getSingleRow('SELECT job_upload_fk FROM job WHERE job_pk = $1',
244  array($id), "reportFileUpload");
245  $uploadId = intval($row['job_upload_fk']);
246  $uploadDao = $this->restHelper->getUploadDao();
247  if (! $uploadDao->isAccessible($uploadId, $this->restHelper->getGroupId())) {
248  throw new HttpForbiddenException("Report is not accessible!");
249  }
250  $row = $dbManager->getSingleRow('SELECT * FROM reportgen WHERE job_fk = $1',
251  array($id), "reportFileName");
252  if (empty($row)) {
254  "Report is not ready. Retry after 10s."))
255  ->setHeaders(['Retry-After' => '10']);
256  }
257  // Everything went well
258  return true;
259  }
260 
270  public function importReport(ServerRequestInterface $request,
271  ResponseHelper $response, array $args): ResponseHelper
272  {
273  $query = $request->getQueryParams();
274  if (!array_key_exists("upload", $query)) {
275  throw new HttpBadRequestException("Missing query param 'upload'");
276  }
277  if (!array_key_exists("reportFormat", $query) ||
278  !in_array($query["reportFormat"], $this->importAllowed)) {
279  throw new HttpBadRequestException(
280  "Missing or wrong query param 'reportFormat'");
281  }
282  $upload_pk = intval($query['upload']);
283  // checking if the scheduler is running or not
284  $commu_status = fo_communicate_with_scheduler('status',
285  $response_from_scheduler, $error_info);
286  if (!$commu_status) {
287  throw new HttpServiceUnavailableException("Scheduler is not running!");
288  }
289  $files = $request->getUploadedFiles();
290 
291  $this->uploadAccessible($upload_pk);
292  if (empty($files['report'])) {
293  throw new HttpBadRequestException("No file uploaded");
294  }
296  $slimFile = $files['report'];
297 
298  $reportFormat = $query["reportFormat"];
299  switch ($reportFormat) {
300  case $this->importAllowed[0]:
301  $returnVal = $this->importDecisionJson($request, $response,
302  $upload_pk, $slimFile);
303  break;
304  case $this->importAllowed[1]:
305  $returnVal = $this->importSpdxReport($request, $response, $upload_pk,
306  $slimFile);
307  break;
308  default:
309  throw new HttpBadRequestException(
310  "Report format $reportFormat not supported. Supported formats are [" .
311  implode(", ", $this->importAllowed) . "]");
312  }
313  return $returnVal;
314  }
315 
326  private function importDecisionJson(ServerRequestInterface $request,
327  ResponseHelper $response, int $uploadId,
328  SlimUploadedFile $slimFile): ResponseHelper
329  {
330  $this->throwNotAdminException();
332  $decisionImporter = $this->restHelper->getPlugin("ui_fodecisionimporter");
333  $symfonyRequest = new Request();
334 
335  $reqBody = $this->getParsedBody($request);
336 
337  if (!array_key_exists("importerUser", $reqBody)) {
338  throw new HttpBadRequestException("Missing parameter 'importerUser'");
339  }
340 
341  $importerUser = intval($reqBody["importerUser"]);
342  if (empty($importerUser)) {
343  $importerUser = $this->restHelper->getUserId();
344  }
345 
346  $uploadedFile = new UploadedFile($slimFile->getFilePath(),
347  $slimFile->getClientFilename(), $slimFile->getClientMediaType());
348 
349  $symfonyRequest->files->set('report', $uploadedFile);
350  $symfonyRequest->request->set('uploadselect', $uploadId);
351  $symfonyRequest->request->set('userselect', $importerUser);
352 
353  $agentResp = $decisionImporter->handleRequest($symfonyRequest);
354 
355  if ($agentResp === false) {
356  throw new HttpBadRequestException("Missing required fields");
357  }
358  $info = new Info(201, intval($agentResp[0]), InfoType::INFO);
359  return $response->withJson($info->getArray(), $info->getCode());
360  }
361 
371  public function importSpdxReport(ServerRequestInterface $request,
372  ResponseHelper $response, int $upload_pk,
373  SlimUploadedFile $slimFile): ResponseHelper
374  {
375  $reqBody = $this->getParsedBody($request);
377  $reportImport = $this->restHelper->getPlugin('ui_reportImport');
378  $symfonyRequest = new Request();
379 
380  // moving the uploaded file to the ReportImport Directory
381  global $SysConf;
382  $fileBase = $SysConf['FOSSOLOGY']['path'] . "/ReportImport/";
383  if (!is_dir($fileBase)) {
384  mkdir($fileBase, 0755, true);
385  }
386  $targetFile = time() . '_' . rand() . '_' . $slimFile->getClientFilename();
387  $slimFile->moveTo($fileBase . $targetFile);
388 
389  // Get default values for parameters
390  $addNewLicensesAs = "candidate";
391  $addLicenseInfoFromInfoInFile = "true";
392  $addLicenseInfoFromConcluded = "false";
393  $addConcludedAsDecisions = "true";
394  $addConcludedAsDecisionsTBD = "true";
395  $addCopyrights = "false";
396  if (array_key_exists("addNewLicensesAs", $reqBody) &&
397  $reqBody["addNewLicensesAs"] === "license") {
398  $addNewLicensesAs = "license";
399  }
400  if (array_key_exists("addLicenseInfoFromInfoInFile", $reqBody) &&
401  !filter_var($reqBody["addLicenseInfoFromInfoInFile"],
402  FILTER_VALIDATE_BOOLEAN)) {
403  $addLicenseInfoFromInfoInFile = "false";
404  }
405  if (array_key_exists("addLicenseInfoFromConcluded", $reqBody) &&
406  filter_var($reqBody["addLicenseInfoFromConcluded"],
407  FILTER_VALIDATE_BOOLEAN)) {
408  $addLicenseInfoFromConcluded = "true";
409  }
410  if (array_key_exists("addConcludedAsDecisions", $reqBody) &&
411  !filter_var($reqBody["addConcludedAsDecisions"],
412  FILTER_VALIDATE_BOOLEAN)) {
413  $addConcludedAsDecisions = "false";
414  }
415  if (array_key_exists("addConcludedAsDecisionsTBD", $reqBody) &&
416  !filter_var($reqBody["addConcludedAsDecisionsTBD"],
417  FILTER_VALIDATE_BOOLEAN)) {
418  $addConcludedAsDecisionsTBD = "false";
419  }
420  if (array_key_exists("addCopyrights", $reqBody) &&
421  filter_var($reqBody["addCopyrights"],
422  FILTER_VALIDATE_BOOLEAN)) {
423  $addCopyrights = "true";
424  }
425 
426  // translating values for symfony request
427  $symfonyRequest->request->set('addNewLicensesAs', $addNewLicensesAs);
428  $symfonyRequest->request->set('addLicenseInfoFromInfoInFile',
429  $addLicenseInfoFromInfoInFile);
430  $symfonyRequest->request->set('addLicenseInfoFromConcluded',
431  $addLicenseInfoFromConcluded);
432  $symfonyRequest->request->set('addConcludedAsDecisions',
433  $addConcludedAsDecisions);
434  $symfonyRequest->request->set('addConcludedAsDecisionsTBD',
435  $addConcludedAsDecisionsTBD);
436  $symfonyRequest->request->set('addCopyrights', $addCopyrights);
437 
438  $agentResp = $reportImport->runImport($upload_pk, $targetFile, $symfonyRequest);
439  $returnVal = new Info(201, intval($agentResp[0]), InfoType::INFO);
440  return $response->withJson($returnVal->getArray(), $returnVal->getCode());
441  }
442 }
FOSSology Decision Exporter UI plugin.
Agent plugin for Readme_OSS agent.
Call SPDX2 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.
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.