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
8 changes: 8 additions & 0 deletions doc/release-notes/11161-do-not-assume-default-license.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
### New Feature Flag: do-not-assume-default-license

A new feature flag `do-not-assume-default-license` has been added. This flag controls the behavior when creating a dataset via API without providing a license or terms of use.

- By default (flag disabled), the default license will be automatically assigned if no license and no terms are provided in the input JSON, regardless of whether custom terms are allowed.
- When enabled, no license (and no terms) will be assigned if none are provided.

**Note:** Previously, if custom terms were allowed, the system would not assign a default license in this case. To retain that behavior, you must now enable this feature flag.
2 changes: 2 additions & 0 deletions doc/sphinx-guides/source/api/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ v6.12

- **/api/admin/index/perms/{id}**

- Dataset creation API calls may now behave differently when neither a license nor terms are provided, depending on the new :ref:`dataverse.feature.do-not-assume-default-license` feature flag.

v6.11
-----

Expand Down
9 changes: 9 additions & 0 deletions doc/sphinx-guides/source/installation/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4055,6 +4055,15 @@ In a future Dataverse version, the (currently) experimental response message sty

See also :ref:`dataverse.legacy.api-response-message-style`.

.. _dataverse.feature.do-not-assume-default-license:

dataverse.feature.do-not-assume-default-license
+++++++++++++++++++++++++++++++++++++++++++++++

When creating a dataset via API, if neither a license nor any terms of use are provided, the system normally assigns the default license. If this feature flag is enabled, no license is assigned (and no terms) in this case.

Defaults to ``false``.

.. _dataverse.feature.avoid-expensive-solr-join:

dataverse.feature.avoid-expensive-solr-join
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/TermsOfUseAndAccess.java
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ public String getTermsOfUse() {

public void setTermsOfUse(String termsOfUse) {
this.termsOfUse = termsOfUse;
if (termsOfUse != null) {
this.license = null;
}
}

public String getTermsOfAccess() {
Expand All @@ -177,6 +180,9 @@ public String getConfidentialityDeclaration() {

public void setConfidentialityDeclaration(String confidentialityDeclaration) {
this.confidentialityDeclaration = confidentialityDeclaration;
if (confidentialityDeclaration != null) {
this.license = null;
}
}

public String getSpecialPermissions() {
Expand All @@ -185,6 +191,9 @@ public String getSpecialPermissions() {

public void setSpecialPermissions(String specialPermissions) {
this.specialPermissions = specialPermissions;
if (specialPermissions != null) {
this.license = null;
}
}

public String getRestrictions() {
Expand All @@ -193,6 +202,9 @@ public String getRestrictions() {

public void setRestrictions(String restrictions) {
this.restrictions = restrictions;
if (restrictions != null) {
this.license = null;
}
}

public String getCitationRequirements() {
Expand All @@ -201,6 +213,9 @@ public String getCitationRequirements() {

public void setCitationRequirements(String citationRequirements) {
this.citationRequirements = citationRequirements;
if (citationRequirements != null) {
this.license = null;
}
}

public String getDepositorRequirements() {
Expand All @@ -209,6 +224,9 @@ public String getDepositorRequirements() {

public void setDepositorRequirements(String depositorRequirements) {
this.depositorRequirements = depositorRequirements;
if (depositorRequirements != null) {
this.license = null;
}
}

public String getConditions() {
Expand All @@ -217,6 +235,9 @@ public String getConditions() {

public void setConditions(String conditions) {
this.conditions = conditions;
if (conditions != null) {
this.license = null;
}
}

public String getDisclaimer() {
Expand All @@ -225,6 +246,9 @@ public String getDisclaimer() {

public void setDisclaimer(String disclaimer) {
this.disclaimer = disclaimer;
if (disclaimer != null) {
this.license = null;
}
}

public String getDataAccessPlace() {
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/edu/harvard/iq/dataverse/settings/FeatureFlags.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@ public enum FeatureFlags {
*/
API_BEARER_AUTH_USE_OAUTH_USER_ON_ID_MATCH("api-bearer-auth-use-oauth-user-on-id-match"),

/**
* When creating a dataset via API, if neither a license nor any terms of use are provided,
* the system normally assigns the default license.
*
* <p>If this feature flag is enabled, no license is assigned (and no terms) in this case.</p>
*
* @apiNote Raise flag by setting "dataverse.feature.do-not-assume-default-license"
* @since Dataverse 6.5
*/
DO_NOT_ASSUME_DEFAULT_LICENSE("do-not-assume-default-license"),

/**
* For published (public) objects, don't use a join when searching Solr.
* Experimental! Requires a reindex with the following feature flag enabled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ public enum JvmSettings {
// API: MDC Citation updates
SCOPE_API_MDC(SCOPE_API, "mdc"),
API_MDC_UPDATE_MIN_DELAY_MS(SCOPE_API_MDC, "min-delay-ms"),


// SIGNPOSTING SETTINGS
SCOPE_SIGNPOSTING(PREFIX, "signposting"),
Expand Down Expand Up @@ -321,7 +320,7 @@ public enum JvmSettings {
SCOPE_COARNOTIFY(SCOPE_LINKEDDATANOTIFICATION, "coar-notify"),
SCOPE_COARNOTIFY_RELATIONSHIP_ANNOUNCEMENT(SCOPE_COARNOTIFY, "relationship-announcement"),
COARNOTIFY_RELATIONSHIP_ANNOUNCEMENT_NOTIFY_SUPERUSERS_ONLY(SCOPE_COARNOTIFY_RELATIONSHIP_ANNOUNCEMENT, "notify-superusers-only"),
;
;

private static final String SCOPE_SEPARATOR = ".";
public static final String PLACEHOLDER_KEY = "%s";
Expand Down
101 changes: 55 additions & 46 deletions src/main/java/edu/harvard/iq/dataverse/util/json/JsonParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -470,27 +470,57 @@ public DatasetVersion parseDatasetVersion(JsonObject obj, DatasetVersion dsv) th

License license = null;

try {
// This method will attempt to parse the license in the format
// in which it appears in our json exports, as a compound
// field, for ex.:
// "license": {
// "name": "CC0 1.0",
// "uri": "http://creativecommons.org/publicdomain/zero/1.0"
// }
license = parseLicense(obj.getJsonObject("license"));
} catch (ClassCastException cce) {
logger.fine("class cast exception parsing the license section (will try parsing as a string)");
// attempt to parse as string:
// i.e. this is for backward compatibility, after the bug in #9155
// was fixed, with the old style of encoding the license info
// in input json, for ex.:
// "license" : "CC0 1.0"
license = parseLicense(obj.getString("license", null));
if (obj.containsKey("license")) {
try {
// This method will attempt to parse the license in the format
// in which it appears in our json exports, as a compound
// field, for ex.:
// "license": {
// "name": "CC0 1.0",
// "uri": "http://creativecommons.org/publicdomain/zero/1.0"
// }
license = parseLicense(obj.getJsonObject("license"));
} catch (ClassCastException cce) {
logger.fine("class cast exception parsing the license section (will try parsing as a string)");
// attempt to parse as string:
// i.e. this is for backward compatibility, after the bug in #9155
// was fixed, with the old style of encoding the license info
// in input json, for ex.:
// "license" : "CC0 1.0"
license = parseLicense(obj.getString("license", null));
}
}

//test to see if license exists in dataset type
//if not set it to null -

terms.setTermsOfUse(obj.getString("termsOfUse", null));
terms.setConfidentialityDeclaration(obj.getString("confidentialityDeclaration", null));
terms.setSpecialPermissions(obj.getString("specialPermissions", null));
terms.setRestrictions(obj.getString("restrictions", null));
terms.setCitationRequirements(obj.getString("citationRequirements", null));
terms.setDepositorRequirements(obj.getString("depositorRequirements", null));
terms.setConditions(obj.getString("conditions", null));
terms.setDisclaimer(obj.getString("disclaimer", null));

if (license == null) {
// If no license was provided or the provided license was invalid,
// we check if terms were provided.
boolean termsProvided = terms.getTermsOfUse() != null
|| terms.getConfidentialityDeclaration() != null
|| terms.getSpecialPermissions() != null
|| terms.getRestrictions() != null
|| terms.getCitationRequirements() != null
|| terms.getDepositorRequirements() != null
|| terms.getConditions() != null
|| terms.getDisclaimer() != null;

if (!FeatureFlags.DO_NOT_ASSUME_DEFAULT_LICENSE.enabled()) {
if (!termsProvided) {
license = licenseService.getDefault();
}
}
}

//test to see if license exists in dataset type
//if not set it to null -
//only test if Dataset has a type and if it has custom available licenses
if (dsv.getDataset() != null) {
DatasetType dst = dsv.getDataset().getDatasetType();
Expand All @@ -505,21 +535,9 @@ public DatasetVersion parseDatasetVersion(JsonObject obj, DatasetVersion dsv) th
license = null;
}
}
}

if (license == null) {
terms.setLicense(license);
terms.setTermsOfUse(obj.getString("termsOfUse", null));
terms.setConfidentialityDeclaration(obj.getString("confidentialityDeclaration", null));
terms.setSpecialPermissions(obj.getString("specialPermissions", null));
terms.setRestrictions(obj.getString("restrictions", null));
terms.setCitationRequirements(obj.getString("citationRequirements", null));
terms.setDepositorRequirements(obj.getString("depositorRequirements", null));
terms.setConditions(obj.getString("conditions", null));
terms.setDisclaimer(obj.getString("disclaimer", null));
} else {
terms.setLicense(license);
}

terms.setLicense(license);
terms.setTermsOfAccess(obj.getString("termsOfAccess", null));
terms.setDataAccessPlace(obj.getString("dataAccessPlace", null));
terms.setOriginalArchive(obj.getString("originalArchive", null));
Expand All @@ -530,6 +548,7 @@ public DatasetVersion parseDatasetVersion(JsonObject obj, DatasetVersion dsv) th
terms.setFileAccessRequest(obj.getBoolean("fileAccessRequest", false));
dsv.setTermsOfUseAndAccess(terms);
terms.setDatasetVersion(dsv);

JsonObject metadataBlocks = obj.getJsonObject("metadataBlocks");
if (metadataBlocks == null){
throw new JsonParseException(BundleUtil.getStringFromBundle("jsonparser.error.metadatablocks.not.found"));
Expand Down Expand Up @@ -700,12 +719,7 @@ public GuestbookResponse parseGuestbookResponse(JsonObject obj, GuestbookRespons

private edu.harvard.iq.dataverse.license.License parseLicense(String licenseNameOrUri) throws JsonParseException {
if (licenseNameOrUri == null){
boolean safeDefaultIfKeyNotFound = true;
if (settingsService.isTrueForKey(SettingsServiceBean.Key.AllowCustomTermsOfUse, safeDefaultIfKeyNotFound)){
return null;
} else {
return licenseService.getDefault();
}
return null;
}
License license = licenseService.getByNameOrUri(licenseNameOrUri);
if (license == null) throw new JsonParseException("Invalid license: " + licenseNameOrUri);
Expand All @@ -714,12 +728,7 @@ private edu.harvard.iq.dataverse.license.License parseLicense(String licenseName

private edu.harvard.iq.dataverse.license.License parseLicense(JsonObject licenseObj) throws JsonParseException {
if (licenseObj == null){
boolean safeDefaultIfKeyNotFound = true;
if (settingsService.isTrueForKey(SettingsServiceBean.Key.AllowCustomTermsOfUse, safeDefaultIfKeyNotFound)){
return null;
} else {
return licenseService.getDefault();
}
return null;
}

String licenseName = licenseObj.getString("name", null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package edu.harvard.iq.dataverse;

import org.junit.jupiter.api.Test;
import edu.harvard.iq.dataverse.license.License;
import static org.junit.jupiter.api.Assertions.*;

public class TermsOfUseAndAccessTest {

@Test
public void testLicenseAndTermsMutualExclusivity() {
License license = new License();
license.setName("CC0");

TermsOfUseAndAccess terms = new TermsOfUseAndAccess();

// 1. Setting license should clear terms
terms.setTermsOfUse("Some terms");
assertEquals("Some terms", terms.getTermsOfUse());
assertNull(terms.getLicense());

terms.setLicense(license);
assertNull(terms.getTermsOfUse());
assertEquals(license, terms.getLicense());

// 2. Setting terms should clear license
terms.setTermsOfUse("New terms");
assertNull(terms.getLicense());
assertEquals("New terms", terms.getTermsOfUse());

// 3. Test other fields clear license too
terms.setLicense(license);
terms.setConfidentialityDeclaration("Confidential");
assertNull(terms.getLicense());
assertEquals("Confidential", terms.getConfidentialityDeclaration());
}

@Test
public void testCopyTermsOfUseAndAccess() {
License license = new License();
license.setName("CC0");

TermsOfUseAndAccess terms = new TermsOfUseAndAccess();
terms.setLicense(license);

TermsOfUseAndAccess copy = terms.copyTermsOfUseAndAccess();
assertEquals(license, copy.getLicense());
assertNull(copy.getTermsOfUse());

terms.setTermsOfUse("Some terms");
TermsOfUseAndAccess copy2 = terms.copyTermsOfUseAndAccess();
assertNull(copy2.getLicense());
assertEquals("Some terms", copy2.getTermsOfUse());
}
}
Loading
Loading