Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@ on:
pull_request:
branches:
- main
paths-ignore:
- third-party/**
- Dockerfile
- docker_build.sh
- .github/sw360_container.yml
- .github/thrift_container.yml
merge_group:
branches:
- main
workflow_dispatch:

concurrency:
Expand Down
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"java.configuration.updateBuildConfiguration": "interactive",
"java.compile.nullAnalysis.mode": "automatic",
"java.jdt.ls.vmargs": "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx4G -Xms100m -Xlog:disable"
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
package org.eclipse.sw360.attachments;

import org.eclipse.sw360.datahandler.thrift.attachments.AttachmentService;
import org.apache.thrift.TException;

import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.THttpClient;

import java.io.IOException;


/**
* Small client for testing a service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,7 @@ public RequestSummary importAllSpdxLicenses(User user) {
RequestSummary requestSummary = new RequestSummary()
.setTotalAffectedElements(0)
.setMessage("");
RequestStatus requestStatus = RequestStatus.SUCCESS;
List<String> spdxIds = SpdxConnector.getAllSpdxLicenseIds();
Map<String,License> sw360Licenses = ThriftUtils.getIdMap(getLicenses());

Expand Down Expand Up @@ -1069,21 +1070,28 @@ public RequestSummary importAllSpdxLicenses(User user) {
}

try {
addOrOverwriteLicenses(newLicenses,user, false);
List<License> importedLicenses = addOrOverwriteLicenses(newLicenses, user, false);
if (importedLicenses == null) {
String msg = "Failed to import all SPDX licenses";
requestSummary.setMessage(msg);
requestStatus = RequestStatus.FAILURE;
log.error(msg);
}

if (mismatchedLicenses.size() > 0){
if (mismatchedLicenses.size() > 0 && requestStatus == RequestStatus.SUCCESS){
requestSummary.setMessage("The following licenses did not match their SPDX equivalent: " + COMMA_JOINER.join(mismatchedLicenses));
}
requestSummary.setTotalAffectedElements(newLicenses.size());
} catch (SW360Exception e) {
String msg = "Failed to import all SPDX licenses";
requestSummary.setMessage(msg);
requestStatus = RequestStatus.FAILURE;
log.error(msg, e);
}

return requestSummary
.setTotalElements(spdxIds.size())
.setRequestStatus(RequestStatus.SUCCESS);
.setRequestStatus(requestStatus);
}

public RequestSummary importAllOSADLLicenses(User user) {
Expand Down
5 changes: 5 additions & 0 deletions docker-compose.override.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
services:
couchdb:
environment:
- COUCHDB_USER=sw360
- COUCHDB_PASSWORD=sw360fossie
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#
# Test configuration for OrganizationMapperTest
#
enable.custom.mapping=true
match.prefix=true

mapping.1=ORG UNIT DEPT TEAM SUBTEAM
mapping.1.target=MAPPED_DEPT_A

mapping.2=ORG UNIT DEPT TEAM
mapping.2.target=MAPPED_DEPT_B

mapping.3=EXTERNAL_COMPANY
mapping.3.target=EXTERNAL

mapping.4=Engineering
mapping.4.target=ENG

mapping.5=UPPERCASE
mapping.5.target=UPPER_MAPPED

mapping.6=PREFIX
mapping.6.target=PREFIX_MAPPED
27 changes: 27 additions & 0 deletions keycloak/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,33 @@
<build>
<pluginManagement>
<plugins>
<!-- Prevent m2e from executing copy-dependencies during incremental IDE builds.
CLI Maven still runs this goal in the package phase. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<versionRange>[3.0.0,)</versionRange>
<goals>
<goal>copy-dependencies</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore/>
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import org.eclipse.sw360.rest.resourceserver.clearingrequest.Sw360ClearingRequestService;
import org.eclipse.sw360.rest.resourceserver.component.ComponentController;
import org.eclipse.sw360.rest.resourceserver.license.LicenseController;
import org.eclipse.sw360.rest.resourceserver.license.LicenseResolutionService;
import org.eclipse.sw360.rest.resourceserver.license.Sw360LicenseService;
import org.eclipse.sw360.rest.resourceserver.moderationrequest.EmbeddedModerationRequest;
import org.eclipse.sw360.rest.resourceserver.moderationrequest.ModerationRequestController;
Expand Down Expand Up @@ -142,6 +143,9 @@ public class RestControllerHelper<T> {
@NonNull
private final Sw360LicenseService licenseService;

@NonNull
private final LicenseResolutionService licenseResolutionService;

@NonNull
private final Sw360ObligationService obligationService;

Expand Down Expand Up @@ -826,12 +830,8 @@ private void isLicenseValid(Set<String> licenses) {
}

private void createMissingLicense(String licenseId) throws Exception {
License newLicense = new License();
newLicense.setId(licenseId);
newLicense.setShortname(licenseId);
newLicense.setFullname(licenseId);
User user = getSw360UserFromAuthentication();
licenseService.createLicense(newLicense, user);
licenseResolutionService.resolveOrCreateMissingLicense(licenseId, user);
}

public License mapLicenseRequestToLicense(License licenseRequestBody, License licenseUpdate) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ public void downloadLicenseArchive(
description = "Upload license archive.",
tags = {"Licenses"}
)
@PreAuthorize("hasAuthority('WRITE')")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "License archive uploaded successfully.")
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright Siemens AG, 2026. Part of the SW360 Portal Project.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/

package org.eclipse.sw360.rest.resourceserver.license;

import lombok.RequiredArgsConstructor;
import org.apache.thrift.TException;
import org.eclipse.sw360.datahandler.thrift.licenses.License;
import org.eclipse.sw360.datahandler.thrift.users.User;
import org.eclipse.sw360.rest.resourceserver.core.BadRequestClientException;
import org.eclipse.sw360.rest.resourceserver.license.licensedb.LicenseDbClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class LicenseResolutionService {

private final Sw360LicenseService sw360LicenseService;
private final LicenseSourcePolicy licenseSourcePolicy;
private final LicenseDbClient licenseDbClient;

public License resolveOrCreateMissingLicense(String licenseId, User sw360User) throws TException {
if (!licenseSourcePolicy.isLicenseDbOnlyMode()) {
return sw360LicenseService.createLicenseFromAuthoritativeSource(buildFallbackLicense(licenseId), sw360User);
}

Optional<License> licenseFromLicenseDb = licenseDbClient.fetchLicenseById(licenseId);
if (licenseFromLicenseDb.isEmpty()) {
throw new BadRequestClientException("License '" + licenseId
+ "' is unknown in SW360 and was not found in LicenseDB.");
}

License authoritativeLicense = licenseFromLicenseDb.get();
if (authoritativeLicense.getId() == null) {
authoritativeLicense.setId(licenseId);
}
if (authoritativeLicense.getShortname() == null) {
authoritativeLicense.setShortname(licenseId);
}
if (authoritativeLicense.getFullname() == null) {
authoritativeLicense.setFullname(authoritativeLicense.getShortname());
}

return sw360LicenseService.createLicenseFromAuthoritativeSource(authoritativeLicense, sw360User);
}

private License buildFallbackLicense(String licenseId) {
License fallbackLicense = new License();
fallbackLicense.setId(licenseId);
fallbackLicense.setShortname(licenseId);
fallbackLicense.setFullname(licenseId);
return fallbackLicense;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright Siemens AG, 2026. Part of the SW360 Portal Project.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/

package org.eclipse.sw360.rest.resourceserver.license;

public enum LicenseSourceMode {
LEGACY,
LICENSEDB_ONLY
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright Siemens AG, 2026. Part of the SW360 Portal Project.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/

package org.eclipse.sw360.rest.resourceserver.license;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Locale;

@Component
public class LicenseSourcePolicy {

@Value("${sw360.license.source.mode:LEGACY}")
private String sourceMode;

public LicenseSourceMode getSourceMode() {
try {
return LicenseSourceMode.valueOf(sourceMode.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
throw new IllegalStateException("Invalid sw360.license.source.mode: " + sourceMode, e);
}
}

public boolean isLicenseDbOnlyMode() {
return getSourceMode() == LicenseSourceMode.LICENSEDB_ONLY;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public class Sw360LicenseService {
private String thriftServerUrl;
private static final String CONTENT_TYPE = "application/zip";
LicenseType lType = new LicenseType();
private final LicenseSourcePolicy licenseSourcePolicy;

public List<License> getLicenses() throws TException {
LicenseService.Iface sw360LicenseClient = getThriftLicenseClient();
Expand Down Expand Up @@ -105,6 +106,11 @@ public void deleteAllLicenseInfo(User user) throws TException {
}

public License createLicense(License license, User sw360User) throws TException {
throwIfManualLicenseOperationsDisabled("Manual license creation is disabled");
return createLicenseFromAuthoritativeSource(license, sw360User);
}

public License createLicenseFromAuthoritativeSource(License license, User sw360User) throws TException {
LicenseService.Iface sw360LicenseClient = getThriftLicenseClient();
license.setId(license.getShortname());
List<License> licenses = sw360LicenseClient.addLicenses(Collections.singletonList(license), sw360User);
Expand Down Expand Up @@ -226,6 +232,7 @@ private LicenseService.Iface getThriftLicenseClient() throws TTransportException
return new LicenseService.Client(protocol);
}
public RequestSummary importSpdxInformation(User sw360User) throws TException {
throwIfManualLicenseOperationsDisabled("SPDX import is disabled");
LicenseService.Iface sw360LicenseClient = getThriftLicenseClient();
if (PermissionUtils.isUserAtLeast(UserGroup.ADMIN, sw360User)) {
RequestSummary allSPDXLicenseStatus = sw360LicenseClient.importAllSpdxLicenses(sw360User);
Expand Down Expand Up @@ -300,6 +307,7 @@ public void uploadLicense(User sw360User, MultipartFile file, boolean overwriteI
}

public RequestSummary importOsadlInformation(User sw360User) throws TException {
throwIfManualLicenseOperationsDisabled("OSADL import is disabled");
LicenseService.Iface sw360LicenseClient = getThriftLicenseClient();
if (PermissionUtils.isUserAtLeast(UserGroup.ADMIN, sw360User)) {
return sw360LicenseClient.importAllOSADLLicenses(sw360User);
Expand Down Expand Up @@ -404,4 +412,10 @@ public List<License> searchLicenses(String searchText) throws TException {
licenses.sort(Comparator.comparing(License::getFullname, String.CASE_INSENSITIVE_ORDER));
return licenses;
}

private void throwIfManualLicenseOperationsDisabled(String operationDescription) {
if (licenseSourcePolicy.isLicenseDbOnlyMode()) {
throw new AccessDeniedException(operationDescription + " in LICENSEDB_ONLY mode.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright Siemens AG, 2026. Part of the SW360 Portal Project.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/

package org.eclipse.sw360.rest.resourceserver.license.licensedb;

import org.eclipse.sw360.datahandler.thrift.licenses.License;

import java.util.Optional;

public interface LicenseDbClient {
Optional<License> fetchLicenseById(String licenseId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright Siemens AG, 2026. Part of the SW360 Portal Project.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/

package org.eclipse.sw360.rest.resourceserver.license.licensedb;

import org.eclipse.sw360.datahandler.thrift.licenses.License;
import org.springframework.stereotype.Component;

import java.util.Optional;

@Component
public class NoOpLicenseDbClient implements LicenseDbClient {

@Override
public Optional<License> fetchLicenseById(String licenseId) {
return Optional.empty();
}
}
Loading