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 
31 use Psr\Http\Message\ServerRequestInterface;
32 use Slim\Psr7\Factory\StreamFactory;
33 use Slim\Psr7\Request as SlimRequest;
34 use Slim\Psr7\UploadedFile as SlimUploadedFile;
35 use Symfony\Component\HttpFoundation\File\UploadedFile;
36 use Symfony\Component\HttpFoundation\Request;
37 
43 {
44 
49  private $reportsAllowed = array(
50  'dep5',
51  'spdx2',
52  'spdx2tv',
53  'readmeoss',
54  'unifiedreport',
55  'clixml',
56  'decisionexporter',
57  'cyclonedx'
58  );
59 
64  private $importAllowed = [
65  'decisionimporter',
66  'spdxrdf'
67  ];
68 
78  public function getReport($request, $response, $args)
79  {
80  $apiVersion = ApiVersion::getVersion($request);
81  $uploadId = null;
82  $reportFormat = null;
83  if ($apiVersion == ApiVersion::V2) {
84  $query = $request->getQueryParams();
85  $uploadId = $query['uploadId'];
86  $reportFormat = $query['reportFormat'];
87  } else {
88  $uploadId = $request->getHeaderLine('uploadId');
89  $reportFormat = $request->getHeaderLine('reportFormat');
90  }
91 
92  if (! in_array($reportFormat, $this->reportsAllowed)) {
93  throw new HttpBadRequestException(
94  "reportFormat must be from [" . implode(",", $this->reportsAllowed) .
95  "]");
96  }
97  $upload = $this->getUpload($uploadId);
98  if (get_class($upload) === Info::class) {
99  return $response->withJson($upload->getArray(), $upload->getCode());
100  }
101  $jobId = null;
102  $jobQueueId = null;
103 
104  switch ($reportFormat) {
105  case $this->reportsAllowed[0]:
106  case $this->reportsAllowed[1]:
107  case $this->reportsAllowed[2]:
109  $spdxGenerator = $this->restHelper->getPlugin('ui_spdx2');
110  list ($jobId, $jobQueueId, $error) = $spdxGenerator->scheduleAgent(
111  $this->restHelper->getGroupId(), $upload, $reportFormat);
112  break;
113  case $this->reportsAllowed[3]:
115  $readmeGenerator = $this->restHelper->getPlugin('ui_readmeoss');
116  list ($jobId, $jobQueueId, $error) = $readmeGenerator->scheduleAgent(
117  $this->restHelper->getGroupId(), $upload);
118  break;
119  case $this->reportsAllowed[4]:
121  $unifiedGenerator = $this->restHelper->getPlugin('agent_founifiedreport');
122  list ($jobId, $jobQueueId, $error) = $unifiedGenerator->scheduleAgent(
123  $this->restHelper->getGroupId(), $upload);
124  break;
125  case $this->reportsAllowed[5]:
127  $clixmlGenerator = $this->restHelper->getPlugin('ui_clixml');
128  list ($jobId, $jobQueueId) = $clixmlGenerator->scheduleAgent(
129  $this->restHelper->getGroupId(), $upload);
130  break;
131  case $this->reportsAllowed[6]:
133  $decisionExporter = $this->restHelper->getPlugin('agent_fodecisionexporter');
134  list($jobId, $jobQueueId) = $decisionExporter->scheduleAgent(
135  $this->restHelper->getGroupId(), $upload);
136  break;
137  case $this->reportsAllowed[7]:
139  $cyclonedxGenerator = $this->restHelper->getPlugin('ui_cyclonedx');
140  list ($jobId, $jobQueueId) = $cyclonedxGenerator->scheduleAgent(
141  $this->restHelper->getGroupId(), $upload);
142  break;
143  default:
144  throw new HttpInternalServerErrorException("Some error occured!");
145  }
146  $download_path = $this->buildDownloadPath($request, $jobId);
147  $info = new Info(201, $download_path, InfoType::INFO);
148  return $response->withJson($info->getArray(), $info->getCode());
149  }
150 
158  private function getUpload($uploadId)
159  {
160  if (empty($uploadId) || ! is_numeric($uploadId) || $uploadId <= 0) {
161  throw new HttpBadRequestException("uploadId must be a positive integer!");
162  }
163  $uploadDao = $this->restHelper->getUploadDao();
164  if (! $uploadDao->isAccessible($uploadId, $this->restHelper->getGroupId())) {
165  throw new HttpForbiddenException("Upload is not accessible!");
166  }
167  $upload = $uploadDao->getUpload($uploadId);
168  if ($upload === null) {
169  throw new HttpNotFoundException("Upload does not exists!");
170  }
171  return $upload;
172  }
173 
180  public static function buildDownloadPath($request, $jobId)
181  {
182  $url_parts = $request->getUri();
183  $download_path = "";
184  if (!empty($url_parts->getScheme())) {
185  $download_path .= $url_parts->getScheme() . "://";
186  }
187  if (!empty($url_parts->getHost())) {
188  $download_path .= $url_parts->getHost();
189  }
190  if (!empty($url_parts->getPort())) {
191  $download_path .= ':' . $url_parts->getPort();
192  }
193  $endpoint = substr($url_parts->getPath(), 0, strpos($url_parts->getPath(),
194  $GLOBALS["apiBasePath"]) + strlen($GLOBALS["apiBasePath"]));
195  if (substr($endpoint, -1) !== '/') {
196  $endpoint .= '/';
197  }
198  $endpoint .= "report/" . $jobId;
199  $download_path .= $endpoint;
200  return $download_path;
201  }
202 
212  public function downloadReport($request, $response, $args)
213  {
214  $id = $args['id'];
215  $this->checkReport($id);
217  $ui_download = $this->restHelper->getPlugin('download');
218  $responseFile = $ui_download->getReport($args['id']);
219  $responseContent = $responseFile->getFile();
220  $newResponse = $response->withHeader('Content-Description',
221  'File Transfer')
222  ->withHeader('Content-Type',
223  $responseContent->getMimeType())
224  ->withHeader('Content-Disposition',
225  $responseFile->headers->get('Content-Disposition'))
226  ->withHeader('Cache-Control', 'must-revalidate')
227  ->withHeader('Pragma', 'private')
228  ->withHeader('Content-Length', filesize($responseContent->getPathname()));
229  $sf = new StreamFactory();
230  return $newResponse->withBody(
231  $sf->createStreamFromFile($responseContent->getPathname())
232  );
233  }
234 
242  private function checkReport($id)
243  {
244  $dbManager = $this->dbHelper->getDbManager();
245  $row = $dbManager->getSingleRow(
246  'SELECT jq_type FROM jobqueue WHERE jq_job_fk = $1', array(
247  $id
248  ), "reportValidity");
249  if (! in_array($row['jq_type'], $this->reportsAllowed)) {
250  throw new HttpNotFoundException(
251  "No report scheduled with given job id.");
252  }
253  $row = $dbManager->getSingleRow('SELECT job_upload_fk FROM job WHERE job_pk = $1',
254  array($id), "reportFileUpload");
255  $uploadId = intval($row['job_upload_fk']);
256  $uploadDao = $this->restHelper->getUploadDao();
257  if (! $uploadDao->isAccessible($uploadId, $this->restHelper->getGroupId())) {
258  throw new HttpForbiddenException("Report is not accessible!");
259  }
260  $row = $dbManager->getSingleRow('SELECT * FROM reportgen WHERE job_fk = $1',
261  array($id), "reportFileName");
262  if (empty($row)) {
264  "Report is not ready. Retry after 10s."))
265  ->setHeaders(['Retry-After' => '10']);
266  }
267  // Everything went well
268  return true;
269  }
270 
280  public function importReport(ServerRequestInterface $request,
281  ResponseHelper $response, array $args): ResponseHelper
282  {
283  $query = $request->getQueryParams();
284  if (!array_key_exists("upload", $query)) {
285  throw new HttpBadRequestException("Missing query param 'upload'");
286  }
287  if (!array_key_exists("reportFormat", $query) ||
288  !in_array($query["reportFormat"], $this->importAllowed)) {
289  throw new HttpBadRequestException(
290  "Missing or wrong query param 'reportFormat'");
291  }
292  $upload_pk = intval($query['upload']);
293  // checking if the scheduler is running or not
294  $commu_status = fo_communicate_with_scheduler('status',
295  $response_from_scheduler, $error_info);
296  if (!$commu_status) {
297  throw new HttpServiceUnavailableException("Scheduler is not running!");
298  }
299  $files = $request->getUploadedFiles();
300 
301  $this->uploadAccessible($upload_pk);
302  if (empty($files['report'])) {
303  throw new HttpBadRequestException("No file uploaded");
304  }
306  $slimFile = $files['report'];
307 
308  $reportFormat = $query["reportFormat"];
309  switch ($reportFormat) {
310  case $this->importAllowed[0]:
311  $returnVal = $this->importDecisionJson($request, $response,
312  $upload_pk, $slimFile);
313  break;
314  case $this->importAllowed[1]:
315  $returnVal = $this->importSpdxReport($request, $response, $upload_pk,
316  $slimFile);
317  break;
318  default:
319  throw new HttpBadRequestException(
320  "Report format $reportFormat not supported. Supported formats are [" .
321  implode(", ", $this->importAllowed) . "]");
322  }
323  return $returnVal;
324  }
325 
336  private function importDecisionJson(ServerRequestInterface $request,
337  ResponseHelper $response, int $uploadId,
338  SlimUploadedFile $slimFile): ResponseHelper
339  {
340  $this->throwNotAdminException();
342  $decisionImporter = $this->restHelper->getPlugin("ui_fodecisionimporter");
343  $symfonyRequest = new Request();
344 
345  $reqBody = $this->getParsedBody($request);
346 
347  if (!array_key_exists("importerUser", $reqBody)) {
348  throw new HttpBadRequestException("Missing parameter 'importerUser'");
349  }
350 
351  $importerUser = intval($reqBody["importerUser"]);
352  if (empty($importerUser)) {
353  $importerUser = $this->restHelper->getUserId();
354  }
355 
356  $uploadedFile = new UploadedFile($slimFile->getFilePath(),
357  $slimFile->getClientFilename(), $slimFile->getClientMediaType());
358 
359  $symfonyRequest->files->set('report', $uploadedFile);
360  $symfonyRequest->request->set('uploadselect', $uploadId);
361  $symfonyRequest->request->set('userselect', $importerUser);
362 
363  $agentResp = $decisionImporter->handleRequest($symfonyRequest);
364 
365  if ($agentResp === false) {
366  throw new HttpBadRequestException("Missing required fields");
367  }
368  $info = new Info(201, intval($agentResp[0]), InfoType::INFO);
369  return $response->withJson($info->getArray(), $info->getCode());
370  }
371 
381  public function importSpdxReport(ServerRequestInterface $request,
382  ResponseHelper $response, int $upload_pk,
383  SlimUploadedFile $slimFile): ResponseHelper
384  {
385  $reqBody = $this->getParsedBody($request);
387  $reportImport = $this->restHelper->getPlugin('ui_reportImport');
388  $symfonyRequest = new Request();
389 
390  // moving the uploaded file to the ReportImport Directory
391  global $SysConf;
392  $fileBase = $SysConf['FOSSOLOGY']['path'] . "/ReportImport/";
393  if (!is_dir($fileBase)) {
394  mkdir($fileBase, 0755, true);
395  }
396  $targetFile = time() . '_' . rand() . '_' . $slimFile->getClientFilename();
397  $slimFile->moveTo($fileBase . $targetFile);
398 
399  // Get default values for parameters
400  $addNewLicensesAs = "candidate";
401  $addLicenseInfoFromInfoInFile = "true";
402  $addLicenseInfoFromConcluded = "false";
403  $addConcludedAsDecisions = "true";
404  $addConcludedAsDecisionsTBD = "true";
405  $addCopyrights = "false";
406  if (array_key_exists("addNewLicensesAs", $reqBody) &&
407  $reqBody["addNewLicensesAs"] === "license") {
408  $addNewLicensesAs = "license";
409  }
410  if (array_key_exists("addLicenseInfoFromInfoInFile", $reqBody) &&
411  !filter_var($reqBody["addLicenseInfoFromInfoInFile"],
412  FILTER_VALIDATE_BOOLEAN)) {
413  $addLicenseInfoFromInfoInFile = "false";
414  }
415  if (array_key_exists("addLicenseInfoFromConcluded", $reqBody) &&
416  filter_var($reqBody["addLicenseInfoFromConcluded"],
417  FILTER_VALIDATE_BOOLEAN)) {
418  $addLicenseInfoFromConcluded = "true";
419  }
420  if (array_key_exists("addConcludedAsDecisions", $reqBody) &&
421  !filter_var($reqBody["addConcludedAsDecisions"],
422  FILTER_VALIDATE_BOOLEAN)) {
423  $addConcludedAsDecisions = "false";
424  }
425  if (array_key_exists("addConcludedAsDecisionsTBD", $reqBody) &&
426  !filter_var($reqBody["addConcludedAsDecisionsTBD"],
427  FILTER_VALIDATE_BOOLEAN)) {
428  $addConcludedAsDecisionsTBD = "false";
429  }
430  if (array_key_exists("addCopyrights", $reqBody) &&
431  filter_var($reqBody["addCopyrights"],
432  FILTER_VALIDATE_BOOLEAN)) {
433  $addCopyrights = "true";
434  }
435 
436  // translating values for symfony request
437  $symfonyRequest->request->set('addNewLicensesAs', $addNewLicensesAs);
438  $symfonyRequest->request->set('addLicenseInfoFromInfoInFile',
439  $addLicenseInfoFromInfoInFile);
440  $symfonyRequest->request->set('addLicenseInfoFromConcluded',
441  $addLicenseInfoFromConcluded);
442  $symfonyRequest->request->set('addConcludedAsDecisions',
443  $addConcludedAsDecisions);
444  $symfonyRequest->request->set('addConcludedAsDecisionsTBD',
445  $addConcludedAsDecisionsTBD);
446  $symfonyRequest->request->set('addCopyrights', $addCopyrights);
447 
448  $agentResp = $reportImport->runImport($upload_pk, $targetFile, $symfonyRequest);
449  $returnVal = new Info(201, intval($agentResp[0]), InfoType::INFO);
450  return $response->withJson($returnVal->getArray(), $returnVal->getCode());
451  }
452 }
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.
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.