13 namespace Fossology\UI\Api\Controllers;
36 use Psr\Container\ContainerInterface;
37 use Psr\Http\Message\ServerRequestInterface as Request;
38 use Slim\Psr7\Factory\StreamFactory;
87 $this->licenseDao = $this->container->get(
'dao.license');
88 $this->adminLicenseAckDao = $this->container->get(
'dao.license.acknowledgement');
89 $this->licenseStdCommentDao = $this->container->get(
'dao.license.stdc');
103 $shortName = $args[
"shortname"];
105 if (empty($shortName)) {
109 $license = $this->licenseDao->getLicenseByShortName($shortName,
110 $this->restHelper->getGroupId());
112 if ($license ===
null) {
114 "No license found with short name '$shortName'.");
117 $obligations = $this->licenseDao->getLicenseObligations([$license->getId()],
119 $obligations = array_merge($obligations,
120 $this->licenseDao->getLicenseObligations([$license->getId()],
true));
121 $obligationList = [];
122 foreach ($obligations as $obligation) {
124 $obligation[
'ob_pk'],
125 $obligation[
'ob_topic'],
126 $obligation[
'ob_type'],
127 $obligation[
'ob_text'],
128 $obligation[
'ob_classification'],
129 $obligation[
'ob_comment']
135 $license->getShortName(),
136 $license->getFullName(),
143 return $response->withJson($returnVal->getArray(), 200);
158 $query = $request->getQueryParams();
159 if ($apiVersion == ApiVersion::V2) {
164 $page = $request->getHeaderLine(self::PAGE_PARAM);
165 $limit = $request->getHeaderLine(self::LIMIT_PARAM);
166 $onlyActive = $request->getHeaderLine(self::ACTIVE_PARAM);
168 if (! empty($limit)) {
169 $limit = filter_var($limit, FILTER_VALIDATE_INT);
172 "limit should be positive integer > 1");
179 if (array_key_exists(
"kind", $query) && !empty($query[
"kind"]) &&
180 in_array($query[
"kind"], [
"all",
"candidate",
"main"])) {
181 $kind = $query[
"kind"];
184 $totalPages = $this->dbHelper->getLicenseCount($kind,
185 $this->restHelper->getGroupId());
186 $totalPages = intval(ceil($totalPages / $limit));
188 if (! empty($page) || $page ==
"0") {
189 $page = filter_var($page, FILTER_VALIDATE_INT);
192 "page should be positive integer > 0");
194 if ($totalPages != 0 && $page > $totalPages) {
196 "Can not exceed total pages: $totalPages"))
197 ->setHeaders([
"X-Total-Pages" => $totalPages]);
202 if (! empty($onlyActive)) {
203 $onlyActive = filter_var($onlyActive, FILTER_VALIDATE_BOOLEAN);
208 $licenses = $this->dbHelper->getLicensesPaginated($page, $limit,
209 $kind, $this->restHelper->getGroupId(), $onlyActive);
212 foreach ($licenses as $license) {
215 $license[
'rf_shortname'],
216 $license[
'rf_fullname'],
221 $license[
'group_fk'] != 0
223 $licenseList[] = $newRow->getArray();
226 return $response->withHeader(
"X-Total-Pages", $totalPages)
227 ->withJson($licenseList, 200);
243 if ($newLicense === -1) {
245 "Input contains additional properties.");
247 if ($newLicense === -2) {
252 "non-candidate license.");
254 $tableName =
"license_ref";
256 "rf_shortname" => $newLicense->getShortName(),
257 "rf_fullname" => $newLicense->getFullName(),
258 "rf_text" => $newLicense->getText(),
259 "rf_md5" => md5($newLicense->getText()),
260 "rf_risk" => $newLicense->getRisk(),
261 "rf_url" => $newLicense->getUrl(),
262 "rf_detector_type" => 1
265 if ($newLicense->getIsCandidate()) {
266 $tableName =
"license_candidate";
267 $assocData[
"group_fk"] = $this->restHelper->getGroupId();
268 $assocData[
"rf_user_fk_created"] = $this->restHelper->getUserId();
269 $assocData[
"rf_user_fk_modified"] = $this->restHelper->getUserId();
270 $assocData[
"marydone"] = $newLicense->getMergeRequest();
271 $okToAdd = $this->
isNewLicense($newLicense->getShortName(),
272 $this->restHelper->getGroupId());
274 $okToAdd = $this->
isNewLicense($newLicense->getShortName());
278 $newLicense->getShortName() .
"' already exists!");
281 $rfPk = $this->dbHelper->getDbManager()->insertTableRow($tableName,
282 $assocData, __METHOD__ .
".newLicense",
"rf_pk");
283 $newInfo =
new Info(201, $rfPk, InfoType::INFO);
286 "License with same text already exists!", $e);
288 return $response->withJson($newInfo->getArray(), $newInfo->getCode());
303 $shortName = $args[
"shortname"];
304 if (empty($shortName)) {
308 $license = $this->licenseDao->getLicenseByShortName($shortName,
309 $this->restHelper->getGroupId());
311 if ($license ===
null) {
313 "No license found with short name '$shortName'.");
315 $isCandidate = $this->restHelper->getDbHelper()->doesIdExist(
316 "license_candidate",
"rf_pk", $license->getId());
319 "Need to be admin to edit non-candidate license.");
321 if ($isCandidate && ! $this->restHelper->getUserDao()->isAdvisorOrAdmin(
322 $this->restHelper->getUserId(), $this->restHelper->getGroupId())) {
324 "Operation not permitted for this group.");
328 if (array_key_exists(
'fullName', $newParams)) {
331 if (array_key_exists(
'text', $newParams)) {
334 if (array_key_exists(
'url', $newParams)) {
337 if (array_key_exists(
'risk', $newParams)) {
338 $assocData[
'rf_risk'] = intval($newParams[
'risk']);
340 if (empty($assocData)) {
344 $tableName =
"license_ref";
346 $tableName =
"license_candidate";
348 $this->dbHelper->getDbManager()->updateTableRow($tableName, $assocData,
349 "rf_pk", $license->getId(), __METHOD__ .
".updateLicense");
350 $newInfo =
new Info(200,
"License " . $license->getShortName() .
351 " updated.", InfoType::INFO);
352 return $response->withJson($newInfo->getArray(), $newInfo->getCode());
363 $tableName =
"ONLY license_ref";
365 $params = [$shortName];
366 $statement = __METHOD__;
368 $tableName =
"license_candidate";
369 $where =
"AND group_fk = $2";
370 $params[] = $groupId;
371 $statement .=
".candidate";
373 $sql =
"SELECT count(*) cnt FROM " .
374 "$tableName WHERE rf_shortname = $1 $where;";
375 $result = $this->dbHelper->getDbManager()->getSingleRow($sql, $params,
377 return $result[
"cnt"] == 0;
389 public function handleImportLicense($request, $response, $args)
393 $symReq = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
395 $adminLicenseFromCsv = $this->restHelper->getPlugin(
'admin_license_from_csv');
397 $uploadedFile = $symReq->files->get($adminLicenseFromCsv->getFileInputName($apiVersion),
403 if (array_key_exists(
"delimiter", $requestBody) && !empty($requestBody[
"delimiter"])) {
404 $delimiter = $requestBody[
"delimiter"];
406 if (array_key_exists(
"enclosure", $requestBody) && !empty($requestBody[
"enclosure"])) {
407 $enclosure = $requestBody[
"enclosure"];
410 $res = $adminLicenseFromCsv->handleFileUpload($uploadedFile, $delimiter,
414 throw new HttpBadRequestException($res[1]);
417 $newInfo =
new Info($res[2], $res[1], InfoType::INFO);
418 return $response->withJson($newInfo->getArray(), $newInfo->getCode());
430 public function getCandidates($request, $response, $args)
435 $adminLicenseCandidate = $this->restHelper->getPlugin(
"admin_license_candidate");
437 return $response->withJson($licenses, 200);
449 public function deleteAdminLicenseCandidate($request, $response, $args)
452 $id = intval($args[
'id']);
454 $adminLicenseCandidate = $this->restHelper->getPlugin(
'admin_license_candidate');
456 if (!$adminLicenseCandidate->getDataRow($id)) {
457 throw new HttpNotFoundException(
"License candidate not found.");
459 $res = $adminLicenseCandidate->doDeleteCandidate($id,
false);
460 $message = $res->getContent();
461 if ($res->getContent() !==
'true') {
462 throw new HttpConflictException(
463 "License used at following locations, can not delete: " .
466 $resInfo =
new Info(202,
"License candidate will be deleted.",
468 return $response->withJson($resInfo->getArray(), $resInfo->getCode());
484 $rawData = $this->adminLicenseAckDao->getAllAcknowledgements();
486 $acknowledgements = [];
487 foreach ($rawData as $ack) {
488 $acknowledgements[] =
new AdminAcknowledgement(intval($ack[
'la_pk']), $ack[
'name'], $ack[
'acknowledgement'], $ack[
'is_enabled'] ==
"t");
491 $res = array_map(fn($acknowledgement) => $acknowledgement->getArray($apiVersion), $acknowledgements);
492 return $response->withJson($res, 200);
513 if (!is_array($body)) {
516 foreach (array_keys($body) as $index) {
517 $ackReq = $body[$index];
518 if ((!$ackReq[
'update'] && empty($ackReq[
'name'])) || ($ackReq[
'update'] && empty($ackReq[
'name']) && !$ackReq[
'toggle'])) {
519 $error =
new Info(400,
"Acknowledgement name missing from the request #" . ($index + 1), InfoType::ERROR);
520 $errors[] = $error->getArray();
522 }
else if ((!$ackReq[
'update'] && empty($ackReq[
'ack'])) || ($ackReq[
'update'] && empty($ackReq[
'ack']) && !$ackReq[
'toggle'])) {
523 $error =
new Info(400,
"Acknowledgement text missing from the request #" . ($index + 1), InfoType::ERROR);
524 $errors[] = $error->getArray();
528 if ($ackReq[
'update']) {
530 if (empty($ackReq[
'id'])) {
531 $error =
new Info(400,
"Acknowledgement ID missing from the request #" . ($index + 1), InfoType::ERROR);
532 $errors[] = $error->getArray();
536 $sql =
"SELECT la_pk, name FROM license_std_acknowledgement WHERE la_pk = $1;";
537 $existingAck = $this->dbHelper->getDbManager()->getSingleRow($sql, [$ackReq[
'id']]);
539 if (empty($existingAck)) {
540 $error =
new Info(404,
"Acknowledgement not found for the request #" . ($index + 1), InfoType::ERROR);
541 $errors[] = $error->getArray();
543 }
else if ($existingAck[
"name"] != $ackReq[
"name"] && $this->dbHelper->doesIdExist(
"license_std_acknowledgement",
"name", $ackReq[
"name"])) {
544 $error =
new Info(400,
"Name already exists.", InfoType::ERROR);
545 $errors[] = $error->getArray();
549 if ($ackReq[
"name"] && $ackReq[
"ack"]) {
550 $this->adminLicenseAckDao->updateAcknowledgement($ackReq[
"id"], $ackReq[
"name"], $ackReq[
"ack"]);
553 if ($ackReq[
"toggle"]) {
554 $this->adminLicenseAckDao->toggleAcknowledgement($ackReq[
"id"]);
557 $info =
new Info(200,
"Successfully updated admin license acknowledgement with name '" . $existingAck[
"name"] .
"'", InfoType::INFO);
560 if ($this->dbHelper->doesIdExist(
"license_std_acknowledgement",
"name", $ackReq[
"name"])) {
561 $error =
new Info(400,
"Name already exists for the request #" . ($index + 1), InfoType::ERROR);
562 $errors[] = $error->getArray();
565 $res = $this->adminLicenseAckDao->insertAcknowledgement($ackReq[
"name"], $ackReq[
"ack"]);
567 $error =
new Info(500,
"Error while inserting new acknowledgement.", InfoType::ERROR);
568 $errors[] = $error->getArray();
571 $info =
new Info(201,
"Acknowledgement added successfully.", InfoType::INFO);
573 $success[] = $info->getArray();
575 return $response->withJson([
576 'success' => $success,
592 $rawData = $this->licenseStdCommentDao->getAllComments();
594 foreach ($rawData as $cmt) {
595 $comments[] =
new LicenseStandardComment(intval($cmt[
'lsc_pk']), $cmt[
'name'], $cmt[
'comment'], $cmt[
'is_enabled'] ==
"t");
597 $res = array_map(fn($comment) => $comment->getArray($apiVersion), $comments);
598 return $response->withJson($res, 200);
621 if (!is_array($body)) {
624 foreach (array_keys($body) as $index) {
625 $commentReq = $body[$index];
628 if ((!$commentReq[
'update'] && empty($commentReq[
'name']))) {
629 $error =
new Info(400,
"Comment name missing from the request #" . ($index + 1), InfoType::ERROR);
630 $errors[] = $error->getArray();
632 }
else if ((!$commentReq[
'update'] && empty($commentReq[
'comment']))) {
633 $error =
new Info(400,
"Comment text missing from the request #" . ($index + 1), InfoType::ERROR);
634 $errors[] = $error->getArray();
636 }
else if ($commentReq[
'update'] && empty($commentReq[
'name']) && empty($commentReq[
'comment']) && empty($commentReq[
'toggle'])) {
637 $error =
new Info(400,
"Comment name, text or toggle missing from the request #" . ($index + 1), InfoType::ERROR);
638 $errors[] = $error->getArray();
642 if ($commentReq[
'update']) {
644 if (empty($commentReq[
'id'])) {
645 $error =
new Info(400,
"Standard Comment ID missing from the request #" . ($index + 1), InfoType::ERROR);
646 $errors[] = $error->getArray();
650 $sql =
"SELECT lsc_pk, name, comment FROM license_std_comment WHERE lsc_pk = $1;";
651 $existingComment = $this->dbHelper->getDbManager()->getSingleRow($sql, [$commentReq[
'id']]);
653 if (empty($existingComment)) {
654 $error =
new Info(404,
"Standard comment not found for the request #" . ($index + 1), InfoType::ERROR);
655 $errors[] = $error->getArray();
658 }
else if ($existingComment[
"name"] != $commentReq[
"name"] && $this->dbHelper->doesIdExist(
"license_std_comment",
"name", $commentReq[
"name"])) {
659 $error =
new Info(400,
"Name already exists.", InfoType::ERROR);
660 $errors[] = $error->getArray();
665 if ($commentReq[
"name"] && $commentReq[
"comment"]) {
666 $this->licenseStdCommentDao->updateComment($commentReq[
"id"], $commentReq[
"name"], $commentReq[
"comment"]);
667 }
else if ($commentReq[
"name"]) {
668 $this->licenseStdCommentDao->updateComment($commentReq[
"id"], $commentReq[
"name"], $existingComment[
"comment"]);
669 }
else if ($commentReq[
"comment"]) {
670 $this->licenseStdCommentDao->updateComment($commentReq[
"id"], $existingComment[
"name"], $commentReq[
"comment"]);
673 if ($commentReq[
"toggle"]) {
674 $this->licenseStdCommentDao->toggleComment($commentReq[
"id"]);
677 $info =
new Info(200,
"Successfully updated standard comment", InfoType::INFO);
680 if ($this->dbHelper->doesIdExist(
"license_std_comment",
"name", $commentReq[
"name"])) {
681 $error =
new Info(400,
"Name already exists for the request #" . ($index + 1), InfoType::ERROR);
682 $errors[] = $error->getArray();
685 $res = $this->licenseStdCommentDao->insertComment($commentReq[
"name"], $commentReq[
"comment"]);
687 $error =
new Info(500,
"Error while inserting new comment.", InfoType::ERROR);
688 $errors[] = $error->getArray();
691 $info =
new Info(201,
"Comment with name '". $commentReq[
'name'] .
"' added successfully.", InfoType::INFO);
693 $success[] = $info->getArray();
695 return $response->withJson([
696 'success' => $success,
710 public function verifyLicense($request, $response, $args)
713 $licenseShortName = $args[
"shortname"];
715 $parentName = $body[
"parentShortname"];
717 if (empty($licenseShortName) || empty($parentName)) {
719 "License ShortName or Parent ShortName is missing.");
722 $license = $this->licenseDao->getLicenseByShortName($licenseShortName, $this->restHelper->getGroupId());
723 if ($licenseShortName != $parentName) {
724 $parentLicense = $this->licenseDao->getLicenseByShortName($parentName, $this->restHelper->getGroupId());
726 $parentLicense = $license;
729 if (empty($license) || empty($parentLicense)) {
730 throw new HttpNotFoundException(
"License not found.");
735 $adminLicenseCandidate = $this->restHelper->getPlugin(
'admin_license_candidate');
736 $ok = $adminLicenseCandidate->verifyCandidate($license->getId(), $licenseShortName, $parentLicense->getId());
737 }
catch (\Throwable $th) {
738 throw new HttpConflictException(
'The license text already exists.', $th);
742 throw new HttpBadRequestException(
'Short name must be unique');
744 $with = $parentLicense->getId() === $license->getId() ?
'' :
" as variant of ($parentName).";
745 $info =
new Info(200,
'Successfully verified candidate ('.$licenseShortName.
')'.$with, InfoType::INFO);
746 return $response->withJson($info->getArray(), $info->getCode());
758 public function mergeLicense($request, $response, $args)
761 $licenseShortName = $args[
"shortname"];
763 $parentName = $body[
"parentShortname"];
765 if (empty($licenseShortName) || empty($parentName)) {
766 throw new HttpBadRequestException(
767 "License ShortName or Parent ShortName is missing.");
769 if ($licenseShortName == $parentName) {
770 throw new HttpBadRequestException(
771 "License ShortName and Parent ShortName are same.");
774 $license = $this->licenseDao->getLicenseByShortName($licenseShortName, $this->restHelper->getGroupId());
775 $mergeLicense = $this->licenseDao->getLicenseByShortName($parentName, $this->restHelper->getGroupId());
777 if (empty($license) || empty($mergeLicense)) {
778 throw new HttpNotFoundException(
"License not found.");
782 $adminLicenseCandidate = $this->restHelper->getPlugin(
'admin_license_candidate');
783 $vars = $adminLicenseCandidate->getDataRow($license->getId());
785 throw new HttpNotFoundException(
"Candidate license not found.");
789 $vars[
'shortname'] = $vars[
'rf_shortname'];
790 $ok = $adminLicenseCandidate->mergeCandidate($license->getId(), $mergeLicense->getId(), $vars);
791 }
catch (\Throwable $th) {
792 throw new HttpConflictException(
'The license text already exists.', $th);
796 throw new HttpInternalServerErrorException(
"Please try again later.");
798 $info =
new Info(200,
"Successfully merged candidate ($parentName) into ($licenseShortName).", InfoType::INFO);
799 return $response->withJson($info->getArray(), $info->getCode());
811 public function getSuggestedLicense($request, $response, $args)
815 $rfText = $body[
"referenceText"];
816 if (empty($rfText)) {
817 throw new HttpBadRequestException(
"Reference text is missing.");
820 $adminLicenseCandidate = $this->restHelper->getPlugin(
'admin_license_candidate');
821 list ($suggestIds, $rendered) = $adminLicenseCandidate->suggestLicenseId($rfText,
true);
824 foreach ($rendered as $value) {
825 $highlights[] = $value->getArray();
828 if (! empty($suggestIds)) {
829 $suggest = $suggestIds[0];
830 $suggestLicense = $adminLicenseCandidate->getDataRow($suggest,
'ONLY license_ref');
832 'id' => intval($suggestLicense[
'rf_pk']),
833 'spdxName' => $suggestLicense[
'rf_spdx_id'],
834 'shortName' => $suggestLicense[
'rf_shortname'],
835 'fullName' => $suggestLicense[
'rf_fullname'],
836 'text' => $suggestLicense[
'rf_text'],
837 'url' => $suggestLicense[
'rf_url'],
838 'notes' => $suggestLicense[
'rf_notes'],
839 'risk' => intval($suggestLicense[
'rf_risk']),
840 'highlights' => $highlights,
843 if (empty($suggestLicense)) {
844 $suggestLicense = [];
846 return $response->withJson($suggestLicense, 200);
861 $query = $request->getQueryParams();
863 if (array_key_exists(
'id', $query)) {
864 $rf = intval($query[
'id']);
867 (! $this->dbHelper->doesIdExist(
"license_ref",
"rf_pk", $rf) &&
868 ! $this->dbHelper->doesIdExist(
"license_candidate",
"rf_pk", $rf))) {
871 $dbManager = $this->dbHelper->getDbManager();
873 $content = $licenseCsvExport->createCsv($rf);
874 $fileName =
"fossology-license-export-" . date(
"YMj-Gis");
875 $newResponse = $response->withHeader(
'Content-type',
'text/csv, charset=UTF-8')
876 ->withHeader(
'Content-Disposition',
'attachment; filename=' . $fileName .
'.csv')
877 ->withHeader(
'Pragma',
'no-cache')
878 ->withHeader(
'Cache-Control',
'no-cache, must-revalidate, maxage=1, post-check=0, pre-check=0')
879 ->withHeader(
'Expires',
'Expires: Thu, 19 Nov 1981 08:52:00 GMT');
880 $sf =
new StreamFactory();
881 return $newResponse->withBody(
882 $content ? $sf->createStream($content) : $sf->createStream(
'')
898 $query = $request->getQueryParams();
900 if (array_key_exists(
'id', $query)) {
901 $rf = intval($query[
'id']);
904 (! $this->dbHelper->doesIdExist(
"license_ref",
"rf_pk", $rf) &&
905 ! $this->dbHelper->doesIdExist(
"license_candidate",
"rf_pk", $rf))) {
908 $dbManager = $this->dbHelper->getDbManager();
910 $content = $licenseCsvExport->createCsv($rf,
false,
true);
911 $fileName =
"fossology-license-export-" . date(
"YMj-Gis");
912 $newResponse = $response->withHeader(
'Content-type',
'text/json, charset=UTF-8')
913 ->withHeader(
'Content-Disposition',
'attachment; filename=' . $fileName .
'.json')
914 ->withHeader(
'Pragma',
'no-cache')
915 ->withHeader(
'Cache-Control',
'no-cache, must-revalidate, maxage=1, post-check=0, pre-check=0')
916 ->withHeader(
'Expires',
'Expires: Thu, 19 Nov 1981 08:52:00 GMT');
917 $sf =
new StreamFactory();
918 return $newResponse->withBody(
919 $content ? $sf->createStream($content) : $sf->createStream(
'')
Helper class to export license list as a CSV from the DB.
Contains the constants and helpers for authentication of user.
static isAdmin()
Check if user is admin.
static replaceUnicodeControlChar($input, $replace="")
handleLicenseStandardComment($request, $response, $args)
handleAdminLicenseAcknowledgement($request, $response, $args)
isNewLicense($shortName, $groupId=0)
exportAdminLicenseToJSON($request, $response, $args)
createLicense($request, $response, $args)
updateLicense($request, $response, $args)
exportAdminLicenseToCSV($request, $response, $args)
getAllLicenseStandardComments($request, $response, $args)
const LICENSE_FETCH_LIMIT
getAllAdminAcknowledgements($request, $response, $args)
getAllLicenses($request, $response, $args)
getLicense($request, $response, $args)
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)
Different type of infos provided by REST.
Info model to contain general error and return values.
static convertDbArray($rows, $version=ApiVersion::V1)
static parseFromArray($inputLicense)