diff --git a/dataverse-persistence/src/main/resources/Bundle_en.properties b/dataverse-persistence/src/main/resources/Bundle_en.properties
index c3856607f8b..cb3da7470ee 100755
--- a/dataverse-persistence/src/main/resources/Bundle_en.properties
+++ b/dataverse-persistence/src/main/resources/Bundle_en.properties
@@ -3078,7 +3078,8 @@ uningest.table.unf=UNF of ingested file
uningest.table.actions=Action
uningest.table.uningest.button=Uningest
uningest.dialog.title=Confirm uningest
-uningest.dialog.text=Do you really want to uningest the file named "{0}"?
+uningest.dialog.text=Do you really want to uningest the selected files?
+uningest.error=Could not uningest the following files
add.dataset.button=Add dataset
add.dataset.dialog.header=Select dataverse
diff --git a/dataverse-persistence/src/main/resources/Bundle_pl.properties b/dataverse-persistence/src/main/resources/Bundle_pl.properties
index 83c9c3283fa..e5ab93944fa 100644
--- a/dataverse-persistence/src/main/resources/Bundle_pl.properties
+++ b/dataverse-persistence/src/main/resources/Bundle_pl.properties
@@ -2334,8 +2334,8 @@ mydataFragment.errorMessage.NoUserSelected=Przykro mi! U\u017Cytkownik nie zosta
mydataFragment.errorMessage.UnknownType.Prefix=Przykro mi! Typ '
mydataFragment.errorMessage.UnknownType.Suffix=' jest nieznany.
-file.provenance=Pochodzenie
-file.editProvenanceDialog=Pochodzenie
+file.provenance=Proweniencja
+file.editProvenanceDialog=Proweniencja
file.editProvenanceDialog.tip=Proweniencja to zapis \u017Ar\u00F3d\u0142a Twojego pliku danych i wszelkich transformacji, jakie przeszed\u0142. Prze\u015Blij plik JSON z narz\u0119dzia do przechwytywania proweniencji w celu wygenerowania grafu proweniencji swoich danych. Wi\u0119cej informacji mo\u017Cna znale\u017A\u0107 w Podr\u0119czniku u\u017Cytkownika.
file.editProvenanceDialog.upload.invalidFile=Nie mo\u017Cna przes\u0142a\u0107 pliku. Spr\u00F3buj ponownie u\u017Cywaj\u0105c pliku w formacie JSON.
file.editProvenanceDialog.uploadSuccess=Przesy\u0142anie uko\u0144czone
@@ -2344,7 +2344,7 @@ file.editProvenanceDialog.noEntitiesError=Przes\u0142any plik z rejestrem prowen
file.editProvenanceDialog.invalidSchemaError=Przes\u0142any plik z rejestrem proweniencji nie jest zgodny ze standardem PROV W3C.
file.editProvenanceDialog.bundleFile=Plik z rejestrem proweniencji
file.editProvenanceDialog.bundleFile.instructions=Plik musi by\u0107 w formacie JSON i spe\u0142nia\u0107 wymogi standardu W3C.
-file.editProvenanceDialog.bundleFile.alreadyPublished=Ten plik z rejestrem pochodzenia zosta\u0142 opublikowany i nie mo\u017Cna go zast\u0105pi\u0107 ani usun\u0105\u0107.
+file.editProvenanceDialog.bundleFile.alreadyPublished=Ten plik z rejestrem proweniencji zosta\u0142 opublikowany i nie mo\u017Cna go zast\u0105pi\u0107 ani usun\u0105\u0107.
file.editProvenanceDialog.bundleEntity=Jednostka (entity) pliku danych
file.editProvenanceDialog.bundleEntity.placeholder=Po\u0142\u0105cz jednostk\u0119 (entity)\u2026
file.editProvenanceDialog.bundleEntity.requiredValidation=Warto\u015B\u0107 jest wymagana
@@ -2354,10 +2354,10 @@ file.editProvenanceDialog.bundleEntity.typeHeader=Typ
file.editProvenanceDialog.bundleEntity.entityHeader=Jednostka (entity)
file.editProvenanceDialog.selectToAddBtn=Wybierz plik
file.editProvenanceDialog.description.tip=Mo\u017Cesz r\u00F3wnie\u017C doda\u0107 dokumentacj\u0119 historii Twojego pliku danych, zawieraj\u0105c\u0105 informacje o tym, jak powsta\u0142, jak si\u0119 zmienia\u0142 i kto nad nim pracowa\u0142.
-file.editProvenanceDialog.description=Opis pochodzenia
-file.editProvenanceDialog.description.placeholder=Dodaj opis pochodzenia
-file.confirmProvenanceDialog=Pochodzenie
-file.confirmProvenanceDialog.tip1=Po opublikowaniu tego pliku danych, Twojego pliku z rejestrem pochodzenia nie b\u0119dzie mo\u017Cna edytowa\u0107, ani zast\u0105pi\u0107.
+file.editProvenanceDialog.description=Opis proweniencji
+file.editProvenanceDialog.description.placeholder=Dodaj opis proweniencji
+file.confirmProvenanceDialog=Proweniencja
+file.confirmProvenanceDialog.tip1=Po opublikowaniu tego pliku danych, Twojego pliku z rejestrem proweniencji nie b\u0119dzie mo\u017Cna edytowa\u0107, ani zast\u0105pi\u0107.
file.confirmProvenanceDialog.tip2=Wybierz "Anuluj" by powr\u00F3ci\u0107 do poprzedniej strony, gdzie mo\u017Cesz obejrze\u0107 podgl\u0105d swojego pliku z rejestrem proweniencji, by potwierdzi\u0107 jego poprawno\u015B\u0107.
file.metadataTab.provenance.header=Proweniencja pliku
file.metadataTab.provenance.body=Informacja o proweniencji pliku, kt\u00F3ra pojawi si\u0119 w p\u00F3\u017Aniejszej wersji\u2026
@@ -2370,8 +2370,8 @@ file.provConfirm.empty=Nie dokonano \u017Cadnych zmian.
file.provAlert.published.json=Zmiany w Twoim pliku z rejestrem proweniencji zosta\u0142y zapisane w zbiorze danych.
file.provAlert.unpublished.json=Zmiany w Twoim pliku z rejestrem proweniencji zostan\u0105 zapisane w tej wersji zbioru danych po naci\u015Bni\u0119ciu przycisku "Zapisz zmiany".
file.provAlert.freeform=Zmiany w opisie proweniencji zostan\u0105 zapisane w tej wersji zbioru danych po naci\u015Bni\u0119ciu przycisku "Zapisz zmiany".
-file.provAlert.filePage.published.json=Zmiany w Twoim pliku z rejestrem pochodzenia zosta\u0142y zapisane w zbiorze danych.
-file.provAlert.filePage.unpublished.json=Zmiany w Twoim pliku z rejestrem pochodzenia zosta\u0142y zapisane w tej wersji zbioru danych.
+file.provAlert.filePage.published.json=Zmiany w Twoim pliku z rejestrem proweniencji zosta\u0142y zapisane w zbiorze danych.
+file.provAlert.filePage.unpublished.json=Zmiany w Twoim pliku z rejestrem proweniencji zosta\u0142y zapisane w tej wersji zbioru danych.
file.provAlert.filePage.freeform=Zmiany w opisie proweniencji zosta\u0142y zapisane w tej wersji zbioru danych.
api.prov.provJsonSaved=Zapisano rejestr proweniencji PROV-JSON dla pliku danych:
@@ -3029,7 +3029,8 @@ uningest.table.unf=UNF zanalizowanego pliku
uningest.table.actions=Akcje
uningest.table.uningest.button=Cofnij analiz\u0119
uningest.dialog.title=Potwierd\u017A cofni\u0119cie analizy
-uningest.dialog.text=Czy na pewno chcesz cofn\u0105\u0107 analiz\u0119 pliku o nazwie "{0}"?
+uningest.dialog.text=Czy na pewno chcesz cofn\u0105\u0107 analiz\u0119 dla zaznaczonych plik\u00F3w?
+uningest.error=Nie uda\u0142o si\u0119 cofn\u0105\u0107 analiz\u0119 dla nast\u0119puj\u0105cych plik\u00F3w
add.dataset.button=Dodaj zbi\u00F3r danych
add.dataset.dialog.header=Wybierz kolekcj\u0119
diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DataverseSessionConfigListener.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DataverseSessionConfigListener.java
new file mode 100644
index 00000000000..ae296c8cd72
--- /dev/null
+++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DataverseSessionConfigListener.java
@@ -0,0 +1,47 @@
+package edu.harvard.iq.dataverse;
+
+import edu.harvard.iq.dataverse.util.SystemConfig;
+
+import javax.inject.Inject;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.annotation.WebListener;
+import javax.servlet.SessionCookieConfig;
+import java.util.logging.Logger;
+
+@WebListener
+public class DataverseSessionConfigListener implements ServletContextListener {
+ private final SystemConfig systemConfig;
+ private static final Logger logger = Logger.getLogger(DataverseSessionConfigListener.class.getCanonicalName());
+
+ @Inject
+ public DataverseSessionConfigListener(SystemConfig systemConfig) {
+ this.systemConfig = systemConfig;
+ }
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+ logger.info("Initializing session cookie configuration");
+ SessionCookieConfig sessionCookieConfig = sce.getServletContext().getSessionCookieConfig();
+ String cookieName = systemConfig.getCookieName();
+ if (cookieName != null && !cookieName.isEmpty()) {
+ logger.info("Setting session cookie name to " + cookieName);
+ sessionCookieConfig.setName(cookieName);
+ }
+ String cookieDomain = systemConfig.getCookieDomain();
+ if (cookieDomain != null && !cookieName.isEmpty()) {
+ logger.info("Setting session cookie domain to " + cookieDomain);
+ sessionCookieConfig.setDomain(cookieDomain);
+ }
+ Boolean cookieSecure = systemConfig.getCookieSecure();
+ if (cookieSecure != null && !cookieName.isEmpty()) {
+ logger.info("Setting session cookie secure to " + cookieSecure);
+ sessionCookieConfig.setSecure(cookieSecure);
+ }
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent servletContextEvent) {
+ // nothing to do here
+ }
+}
diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/LoginPage.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/LoginPage.java
index 99baaf0a84a..b2b4d51ee7f 100644
--- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/LoginPage.java
+++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/LoginPage.java
@@ -22,6 +22,7 @@
import javax.faces.validator.ValidatorException;
import javax.inject.Inject;
import javax.inject.Named;
+import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
@@ -31,6 +32,7 @@
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
+import java.util.regex.Pattern;
/**
* @author xyang
@@ -184,7 +186,12 @@ public String login() {
}
logger.log(Level.FINE, "Sending user to = {0}", redirectPage);
- return redirectPage + (!redirectPage.contains("?") ? "?" : "&") + "faces-redirect=true";
+
+ if(validateIsRedirectUrlAnExternalResource(redirectPage)) {
+ return redirectToExternalResource();
+ } else {
+ return redirectPage + (!redirectPage.contains("?") ? "?" : "&") + "faces-redirect=true";
+ }
} catch (AuthenticationFailedException ex) {
numFailedLoginAttempts++;
op1 = random.nextInt(10);
@@ -212,6 +219,15 @@ public String login() {
}
}
+ boolean validateIsRedirectUrlAnExternalResource(String urlToValidate) {
+ boolean result = Pattern.compile("^(https?)://[^\\s/$.?#].[^\\s]*$",
+ Pattern.CASE_INSENSITIVE).matcher(urlToValidate).matches();
+ if(!result) {
+ logger.severe("Invalid redirect URL: " + urlToValidate + ". Redirect URL must start with http:// or https://");
+ }
+ return result;
+ }
+
public void resetFilledCredentials(AjaxBehaviorEvent event) {
if (selectedCredentialsProvider() == null) {
return;
@@ -278,6 +294,28 @@ private String redirectToRoot() {
return "dataverse.xhtml?alias=" + dataverseDao.findRootDataverse().getAlias();
}
+ private String redirectToExternalResource() {
+ try {
+ logger.info("Trying to redirect to external page: " + redirectPage);
+ if(systemConfig.getAllowedExternalRedirectionUrl() == null || systemConfig.getAllowedExternalRedirectionUrl().isEmpty()) {
+ logger.severe("External redirection not allowed.");
+ } else if(redirectPage.startsWith(systemConfig.getAllowedExternalRedirectionUrl())) {
+ FacesContext.getCurrentInstance().getExternalContext().redirect(redirectPage);
+ } else {
+ logger.severe("Chosen redirect page " + redirectPage + " is not allowed. " +
+ "Allowed pages: " + systemConfig.getAllowedExternalRedirectionUrl());
+ }
+ } catch (IOException e) {
+ logger.severe("Unable to redirect to external page "+ e.getMessage());
+ }
+ // Internal Redirection: Uses navigation handling in JSF, where returning
+ // a string tells JSF which page to navigate to next.
+ // External Redirection: Directly interacts with the HTTP response to send a redirect.
+ // No string return is necessary because the redirection is handled immediately
+ // by the ExternalContext.redirect() method.
+ return "";
+ }
+
// -------------------- SETTERS --------------------
public void setSelectedSamlIdpId(Long selectedSamlIdpId) {
diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/UningestPage.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/UningestPage.java
index 5ca876b95ab..8753ba4998c 100644
--- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/UningestPage.java
+++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/UningestPage.java
@@ -1,5 +1,6 @@
package edu.harvard.iq.dataverse;
+import edu.harvard.iq.dataverse.common.BundleUtil;
import edu.harvard.iq.dataverse.ingest.UningestInfoService;
import edu.harvard.iq.dataverse.ingest.UningestService;
import edu.harvard.iq.dataverse.persistence.datafile.DataFile;
@@ -11,7 +12,11 @@
import edu.harvard.iq.dataverse.util.SystemConfig;
import org.apache.commons.lang.StringUtils;
import org.omnifaces.cdi.ViewScoped;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import javax.faces.application.FacesMessage;
+import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.Serializable;
@@ -23,6 +28,7 @@
@Named("UningestPage")
public class UningestPage implements Serializable {
+ private static final Logger log = LoggerFactory.getLogger(UningestPage.class);
private UningestService uningestService;
private DatasetRepository datasetRepository;
private DataverseSession dataverseSession;
@@ -33,7 +39,7 @@ public class UningestPage implements Serializable {
private UningestInfoService uningestInfoService;
private List
+
-