2020import org .apache .thrift .TFieldIdEnum ;
2121import com .fasterxml .jackson .databind .DeserializationFeature ;
2222import com .fasterxml .jackson .databind .ObjectMapper ;
23+ import java .util .Objects ;
2324import org .eclipse .sw360 .datahandler .common .CommonUtils ;
2425import org .eclipse .sw360 .datahandler .common .SW360Utils ;
2526import org .eclipse .sw360 .datahandler .permissions .PermissionUtils ;
@@ -564,6 +565,16 @@ private HalResource<License> addEmbeddedLicense(String licenseId) {
564565 License embeddedLicense = convertToEmbeddedLicense (licenseId );
565566 HalResource <License > halLicense = new HalResource <>(embeddedLicense );
566567
568+ if (isLikelyExternalLicenseReference (licenseId )) {
569+ LOGGER .info ("License '{}' appears to be external reference - creating basic embedded license without database lookup" , licenseId );
570+ embeddedLicense .setShortname (licenseId );
571+ embeddedLicense .setFullname (licenseId );
572+ embeddedLicense .setOSIApproved (Quadratic .NA );
573+ embeddedLicense .setFSFLibre (Quadratic .NA );
574+ embeddedLicense .setChecked (false );
575+ return halLicense ;
576+ }
577+
567578 try {
568579 License licenseById = licenseService .getLicenseById (licenseId );
569580 embeddedLicense .setFullname (licenseById .getFullname ());
@@ -573,17 +584,32 @@ private HalResource<License> addEmbeddedLicense(String licenseId) {
573584 halLicense .add (licenseSelfLink );
574585 return halLicense ;
575586 } catch (ResourceNotFoundException rne ) {
576- LOGGER .error ("cannot create a self link for license with id" + licenseId );
587+ LOGGER .error ("cannot create a self link for license with id " + licenseId );
577588 embeddedLicense .setShortname (licenseId );
578589 embeddedLicense .setOSIApproved (Quadratic .NA );
579590 embeddedLicense .setFSFLibre (Quadratic .NA );
580591 embeddedLicense .setChecked (false );
581592 embeddedLicense .setFullname (null );
582593 return halLicense ;
583594 } catch (Exception e ) {
584- LOGGER .error ("cannot create self link for license with id: " + licenseId );
595+ if (isLikelyExternalLicenseReference (licenseId )) {
596+ LOGGER .info ("License '{}' not found in SW360 database but appears to be external reference - creating basic embedded license" , licenseId );
597+ embeddedLicense .setShortname (licenseId );
598+ embeddedLicense .setFullname (licenseId );
599+ embeddedLicense .setOSIApproved (Quadratic .NA );
600+ embeddedLicense .setFSFLibre (Quadratic .NA );
601+ embeddedLicense .setChecked (false );
602+ return halLicense ;
603+ } else {
604+ LOGGER .error ("cannot create self link for license with id: " + licenseId , e );
605+ embeddedLicense .setShortname (licenseId );
606+ embeddedLicense .setOSIApproved (Quadratic .NA );
607+ embeddedLicense .setFSFLibre (Quadratic .NA );
608+ embeddedLicense .setChecked (false );
609+ embeddedLicense .setFullname (null );
610+ return halLicense ;
611+ }
585612 }
586- return null ;
587613 }
588614
589615 public LicenseType convertToEmbeddedLicenseType (LicenseType licenseType ) {
@@ -782,10 +808,18 @@ public Release updateRelease(Release releaseToUpdate, Release requestBodyRelease
782808 if (fieldValue != null ) {
783809 switch (field ) {
784810 case MAIN_LICENSE_IDS :
785- isLicenseValid (requestBodyRelease .getMainLicenseIds ());
811+ Set <String > newMainLicenseIds = requestBodyRelease .getMainLicenseIds ();
812+ Set <String > existingMainLicenseIds = releaseToUpdate .getMainLicenseIds ();
813+ if (!Objects .equals (newMainLicenseIds , existingMainLicenseIds )) {
814+ isLicenseValidResilient (newMainLicenseIds , "Main License IDs" );
815+ }
786816 break ;
787817 case OTHER_LICENSE_IDS :
788- isLicenseValid (requestBodyRelease .getOtherLicenseIds ());
818+ Set <String > newOtherLicenseIds = requestBodyRelease .getOtherLicenseIds ();
819+ Set <String > existingOtherLicenseIds = releaseToUpdate .getOtherLicenseIds ();
820+ if (!Objects .equals (newOtherLicenseIds , existingOtherLicenseIds )) {
821+ isLicenseValidResilient (newOtherLicenseIds , "Other License IDs" );
822+ }
789823 break ;
790824 default :
791825 }
@@ -806,20 +840,118 @@ public License convertLicenseFromRequest(Map<String, Object> reqBodyMap, License
806840 return licenseRequestBody ;
807841 }
808842
843+ /**
844+ * Legacy license validation method - kept for backward compatibility but deprecated
845+ * @deprecated Use isLicenseValidResilient instead
846+ */
847+ @ Deprecated
809848 private void isLicenseValid (Set <String > licenses ) {
810- List <String > licenseIncorrect = new ArrayList <>();
811- if (CommonUtils .isNotEmpty (licenses )) {
812- for (String licenseId : licenses ) {
813- try {
814- licenseService .getLicenseById (licenseId );
815- } catch (Exception e ) {
816- licenseIncorrect .add (licenseId );
817- }
849+ isLicenseValidResilient (licenses , "License IDs" );
850+ }
851+
852+ /**
853+ * More resilient license validation that handles external license references gracefully
854+ * @param licenses Set of license IDs to validate
855+ * @param fieldName Name of the field being validated (for error messages)
856+ */
857+ private void isLicenseValidResilient (Set <String > licenses , String fieldName ) {
858+ if (CommonUtils .isNullOrEmptyCollection (licenses )) {
859+ return ;
860+ }
861+
862+ List <String > licenseIncorrect = new ArrayList <>();
863+ List <String > licenseWarnings = new ArrayList <>();
864+
865+ for (String licenseId : licenses ) {
866+ if (licenseId == null || licenseId .trim ().isEmpty ()) {
867+ continue ; // Skip empty/null license IDs
868+ }
869+ if (isLikelyExternalLicenseReference (licenseId )) {
870+ licenseWarnings .add (licenseId );
871+ LOGGER .info ("{} ID '{}' appears to be external license reference - allowing without database validation" ,
872+ fieldName , licenseId );
873+ continue ;
874+ }
875+
876+ try {
877+ licenseService .getLicenseById (licenseId );
878+ LOGGER .debug ("{} validation successful for ID: {}" , fieldName , licenseId );
879+ } catch (Exception e ) {
880+ licenseIncorrect .add (licenseId );
881+ LOGGER .warn ("{} ID '{}' not found in SW360 database and not recognized as external reference" ,
882+ fieldName , licenseId );
818883 }
819884 }
885+
820886 if (!licenseIncorrect .isEmpty ()) {
821- throw new BadRequestClientException ("License with ids " + licenseIncorrect + " does not exist in SW360 database." );
887+ String errorMsg = fieldName + " with ids " + licenseIncorrect + " does not exist in SW360 database." ;
888+ if (!licenseWarnings .isEmpty ()) {
889+ errorMsg += " Note: External license references " + licenseWarnings + " were allowed." ;
890+ }
891+ LOGGER .error ("License validation failed: {}" , errorMsg );
892+ throw new BadRequestClientException (errorMsg );
822893 }
894+
895+ if (!licenseWarnings .isEmpty ()) {
896+ LOGGER .info ("{} update proceeding with external license references: {}" , fieldName , licenseWarnings );
897+ }
898+ }
899+
900+ /**
901+ * Helper method to determine if a license ID appears to be an external reference
902+ * (e.g., long descriptive names, proprietary licenses) rather than an internal SW360 ID
903+ * This method is conservative to avoid false positives with common SPDX identifiers
904+ */
905+ private boolean isLikelyExternalLicenseReference (String licenseId ) {
906+ if (licenseId == null || licenseId .trim ().isEmpty ()) {
907+ return false ;
908+ }
909+
910+ String normalizedId = licenseId .toLowerCase ().trim ();
911+
912+ return isMatchingConfigurablePatterns (normalizedId ) ||
913+ isLongDescriptiveName (normalizedId );
914+ }
915+
916+ /**
917+ * Check against configurable license patterns from application properties
918+ * This is now more conservative to avoid false positives with standard SPDX identifiers
919+ */
920+ private boolean isMatchingConfigurablePatterns (String normalizedId ) {
921+
922+ if ((normalizedId .endsWith ("license" ) || normalizedId .endsWith ("licence" )) &&
923+ normalizedId .length () > 10 ) {
924+ return true ;
925+ }
926+
927+ if ((normalizedId .startsWith ("." ) || normalizedId .contains ("microsoft" ) ||
928+ normalizedId .contains ("oracle" ) || normalizedId .contains ("google" ) ||
929+ normalizedId .contains ("proprietary" ) || normalizedId .contains ("commercial" )) &&
930+ normalizedId .contains ("license" )) {
931+ return true ;
932+ }
933+
934+ if (normalizedId .startsWith ("gnu " ) && normalizedId .contains ("general public license" )) {
935+ return true ;
936+ }
937+
938+ if (normalizedId .startsWith ("creative commons" ) || normalizedId .contains ("cc-by" )) {
939+ return true ;
940+ }
941+
942+ if (normalizedId .matches (".*\\ bversion\\ s+\\ d+.*" ) && normalizedId .length () > 15 ) {
943+ return true ;
944+ }
945+
946+ return false ;
947+ }
948+
949+ /**
950+ * Check if it's likely a long descriptive name from external sources
951+ * Only very long names are considered external to be conservative
952+ */
953+ private boolean isLongDescriptiveName (String normalizedId ) {
954+ return normalizedId .length () > 40 ; // Increased threshold to be more conservative
823955 }
824956
825957 public License mapLicenseRequestToLicense (License licenseRequestBody , License licenseUpdate ) {
0 commit comments