From 0fc1ef187e509c478f96865aed939a81771b1cf0 Mon Sep 17 00:00:00 2001 From: lbownik Date: Tue, 10 Dec 2024 16:01:03 +0100 Subject: [PATCH] Closes #2540 Create double-blind-review-ready Anonymized Private URL --- .../persistence/user/AuthenticatedUser.java | 11 + .../persistence/user/DataverseRole.java | 2 - .../dataverse/persistence/user/GuestUser.java | 12 ++ .../persistence/user/PrivateUrlUser.java | 45 +++-- .../persistence/user/RoleAssignment.java | 40 +++- .../user/RoleAssignmentRepository.java | 59 +++++- .../iq/dataverse/persistence/user/User.java | 7 +- .../src/main/resources/Bundle_en.properties | 5 + .../src/main/resources/Bundle_pl.properties | 5 + ...dd_anonimized_column_to_roleassignment.sql | 3 + .../dataverse/AnonymizedPrivateUrlDialog.java | 102 ++++++++++ .../edu/harvard/iq/dataverse/DatasetPage.java | 191 ++++++++---------- .../iq/dataverse/DataverseHeaderFragment.java | 7 + .../iq/dataverse/DataverseSession.java | 81 ++++---- .../iq/dataverse/PrivateUrlDialog.java | 102 ++++++++++ .../harvard/iq/dataverse/api/Datasets.java | 55 ++++- .../iq/dataverse/citation/Citation.java | 4 - .../command/impl/AssignRoleCommand.java | 8 +- .../command/impl/CreatePrivateUrlCommand.java | 23 +-- .../impl/DeleteDatasetVersionCommand.java | 11 +- .../command/impl/DeletePrivateUrlCommand.java | 30 +-- .../command/impl/GetPrivateUrlCommand.java | 35 ++-- .../privateurl/PrivateUrlServiceBean.java | 72 ++----- .../dataverse/privateurl/PrivateUrlUtil.java | 2 +- .../webapp/dataset-privateUrlDialog.xhtml | 85 ++++++++ .../src/main/webapp/dataset.xhtml | 93 +++------ .../src/main/webapp/dataverse_header.xhtml | 2 +- .../impl/CreatePrivateUrlCommandTest.java | 10 +- .../impl/DeletePrivateUrlCommandTest.java | 4 +- .../impl/GetPrivateUrlCommandTest.java | 6 +- .../privateurl/PrivateUrlUtilTest.java | 2 +- 31 files changed, 738 insertions(+), 376 deletions(-) create mode 100644 dataverse-persistence/src/main/resources/db/migration/V80__add_anonimized_column_to_roleassignment.sql create mode 100644 dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/AnonymizedPrivateUrlDialog.java create mode 100644 dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/PrivateUrlDialog.java create mode 100644 dataverse-webapp/src/main/webapp/dataset-privateUrlDialog.xhtml diff --git a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUser.java b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUser.java index 93c5b95614..cee5516ed9 100644 --- a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUser.java +++ b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/AuthenticatedUser.java @@ -1,5 +1,6 @@ package edu.harvard.iq.dataverse.persistence.user; +import edu.harvard.iq.dataverse.persistence.DvObject; import edu.harvard.iq.dataverse.persistence.JpaEntity; import edu.harvard.iq.dataverse.persistence.config.LocaleConverter; import edu.harvard.iq.dataverse.persistence.config.ValidateEmail; @@ -194,6 +195,16 @@ public List getAcceptedConsents() { public boolean isSuperuser() { return superuser; } + + @Override + public boolean isAnonymized() { + return false; + } + + @Override + public boolean isAffiliatedWith(final DvObject object) { + return object.getReleaseUser().equals(this); + } public AuthenticatedUserLookup getAuthenticatedUserLookup() { return authenticatedUserLookup; diff --git a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/DataverseRole.java b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/DataverseRole.java index de6de7cc23..bce306ba00 100644 --- a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/DataverseRole.java +++ b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/DataverseRole.java @@ -254,6 +254,4 @@ public boolean doesDvObjectClassHavePermissionForObject(Class { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue(strategy = IDENTITY) private Long id; @Column(nullable = false) private String assigneeIdentifier; - @ManyToOne(cascade = {CascadeType.MERGE}) + @ManyToOne(cascade = {MERGE}) @JoinColumn(nullable = false) private DataverseRole role; - @ManyToOne(cascade = {CascadeType.MERGE}) + @ManyToOne(cascade = {MERGE}) @JoinColumn(nullable = false) private DvObject definitionPoint; @Column(nullable = true) private String privateUrlToken; + + private boolean anonymized; public RoleAssignment() { } - public RoleAssignment(DataverseRole aRole, RoleAssignee anAssignee, DvObject aDefinitionPoint, String privateUrlToken) { + public RoleAssignment(DataverseRole aRole, RoleAssignee anAssignee, + DvObject aDefinitionPoint, String privateUrlToken) { + this(aRole, anAssignee, aDefinitionPoint, privateUrlToken, false); + } + + public RoleAssignment(DataverseRole aRole, RoleAssignee anAssignee, + DvObject aDefinitionPoint, String privateUrlToken, boolean anonymized) { role = aRole; assigneeIdentifier = anAssignee.getIdentifier(); definitionPoint = aDefinitionPoint; this.privateUrlToken = privateUrlToken; + this.anonymized = anonymized; } public Long getId() { @@ -113,6 +125,14 @@ public String getPrivateUrlToken() { return privateUrlToken; } + public boolean isAnonymized() { + return this.anonymized; + } + + public void setAnonymized(final boolean anonymized) { + this.anonymized = anonymized; + } + @Override public int hashCode() { int hash = 7; diff --git a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/RoleAssignmentRepository.java b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/RoleAssignmentRepository.java index 39a7bd7360..3b7adcb153 100644 --- a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/RoleAssignmentRepository.java +++ b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/RoleAssignmentRepository.java @@ -1,18 +1,20 @@ package edu.harvard.iq.dataverse.persistence.user; import edu.harvard.iq.dataverse.persistence.JpaRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import edu.harvard.iq.dataverse.persistence.dataset.Dataset; import javax.ejb.Singleton; +import javax.persistence.NoResultException; +import javax.persistence.NonUniqueResultException; import javax.persistence.Query; + +import static java.util.stream.Collectors.joining; + import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; @Singleton public class RoleAssignmentRepository extends JpaRepository { - private static final Logger logger = LoggerFactory.getLogger(RoleAssignmentRepository.class); // -------------------- CONSTRUCTORS -------------------- @@ -69,9 +71,54 @@ public List findDataversesWithUserPermitted(List identifiers) { String query = "SELECT id FROM dvobject WHERE dtype = 'Dataverse' " + "and id in (select definitionpoint_id from roleassignment " + "where assigneeidentifier in (" - + identifiers.stream().map(i -> "'" + i + "'").collect(Collectors.joining(",")) + "));"; - logger.info("query: {}", query); + + identifiers.stream().map(i -> "'" + i + "'").collect(joining(",")) + "));"; Query nativeQuery = em.createNativeQuery(query); return (List) nativeQuery.getResultList(); } + + /** + * @return A RoleAssignment or null. + * @todo This might be a good place for Optional. + */ + public RoleAssignment getRoleAssignmentFromPrivateUrlToken( + final String privateUrlToken) { + if (privateUrlToken == null) { + return null; + } else { + try { + return this.em + .createNamedQuery("RoleAssignment.listByPrivateUrlToken", + RoleAssignment.class) + .setParameter("privateUrlToken", privateUrlToken) + .getSingleResult(); + } catch (final NoResultException | NonUniqueResultException ex) { + return null; + } + } + } + + /** + * @param dataset A non-null dataset; + * @return A role assignment for a Private URL, if found, or null. + * @todo This might be a good place for Optional. + */ + public RoleAssignment getPrivateUrlRoleAssignmentFromDataset( + final Dataset dataset, final boolean anonymized) { + if (dataset == null) { + return null; + } else { + try { + return this.em.createNamedQuery( + "RoleAssignment.listByAssigneeIdentifier_DefinitionPointId", + RoleAssignment.class) + .setParameter("assigneeIdentifier", + new PrivateUrlUser(dataset.getId()).getIdentifier()) + .setParameter("definitionPointId", dataset.getId()) + .setParameter("anonymized", anonymized) + .getSingleResult(); + } catch (final NoResultException | NonUniqueResultException ex) { + return null; + } + } + } } diff --git a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/User.java b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/User.java index 633f650bce..e2c1083a5f 100644 --- a/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/User.java +++ b/dataverse-persistence/src/main/java/edu/harvard/iq/dataverse/persistence/user/User.java @@ -2,6 +2,8 @@ import java.io.Serializable; +import edu.harvard.iq.dataverse.persistence.DvObject; + /** * A user of the dataverse system. Intuitively a single real person in real * life, but some corner cases exist (e.g. {@link GuestUser}, who stands for @@ -12,5 +14,8 @@ public interface User extends RoleAssignee, Serializable { boolean isAuthenticated(); boolean isSuperuser(); - + + boolean isAnonymized(); + + boolean isAffiliatedWith(DvObject object); } diff --git a/dataverse-persistence/src/main/resources/Bundle_en.properties b/dataverse-persistence/src/main/resources/Bundle_en.properties index c2d265940b..9955f636fb 100755 --- a/dataverse-persistence/src/main/resources/Bundle_en.properties +++ b/dataverse-persistence/src/main/resources/Bundle_en.properties @@ -1473,6 +1473,7 @@ dataset.editBtn.itemLabel.selectGuestbook=Select Guestbook dataset.editBtn.itemLabel.permissions=Permissions dataset.editBtn.itemLabel.thumbnailsAndWidgets=Thumbnails + Widgets dataset.editBtn.itemLabel.privateUrl=Private URL +dataset.editBtn.itemLabel.privateAnonymizedUrl=Private anonymized URL dataset.editBtn.itemLabel.embargo=Embargo dataset.editBtn.itemLabel.permissionsDataset=Dataset dataset.editBtn.itemLabel.permissionsFile=Restricted Files @@ -1746,6 +1747,7 @@ dataset.mixedSelectedFilesForDownload=The restricted file(s) selected may not be dataset.downloadUnrestricted=Click Continue to download the files you have access to download. dataset.requestAccessToRestrictedFiles=You may request access to the restricted file(s) by clicking the Request Access button. dataset.privateurl.infoMessageAuthor.title=Dataset Private URL +dataset.privateurl.anonymized.infoMessageAuthor.title=Dataset Anonymized Private URL dataset.privateurl.infoMessageAuthor.details=Privately share this dataset before it is publicly available: {0} dataset.privateurl.infoMessageReviewer=Dataset Private URL - This dataset is being privately shared. You will not be able to access it when logged into your Dataverse account. dataset.privateurl.infoMessageReviewer.title=Dataset Private URL @@ -1761,6 +1763,9 @@ dataset.privateurl.roleassigeeTitle=Private URL Enabled dataset.privateurl.createdSuccess=Success! dataset.privateurl.disabledSuccess=You have successfully disabled the Private URL for this dataset. dataset.privateurl.noPermToCreate=To create a Private URL you must have the following permissions: {0}. +dataset.anonymized.privateurl.header=Dataset Anonymized Private URL +dataset.anonymized.privateurl.tip=Use a Anonymized Private URL to allow those without Dataverse accounts to access your dataset. For more information about the Private URL feature, please refer to the User Guide. +dataset.anonymized.privateurl.warning="WARNING. This dataset has at least one published version. Those who have access to the Anonymized Private URL for this dataset may be able to use its accessible metadata to look up the full, not anonymized version of this dataset. dataset.embargo.datasetSummary.title=Embargo dataset.embargo.datasetSummary.tip=In an embargoed dataset files remain temporarily unavailable. dataset.embargo.datasetSummary.message=Files in this dataset will be available from {0}. diff --git a/dataverse-persistence/src/main/resources/Bundle_pl.properties b/dataverse-persistence/src/main/resources/Bundle_pl.properties index ffe0ffab5d..ae9c404808 100644 --- a/dataverse-persistence/src/main/resources/Bundle_pl.properties +++ b/dataverse-persistence/src/main/resources/Bundle_pl.properties @@ -1455,6 +1455,7 @@ dataset.editBtn.itemLabel.selectGuestbook=Przypisz Ksi\u0119g\u0119 Go\u015Bci dataset.editBtn.itemLabel.permissions=Uprawnienia dataset.editBtn.itemLabel.thumbnailsAndWidgets=Miniaturka + Wid\u017Cety dataset.editBtn.itemLabel.privateUrl=Prywatny adres URL +dataset.editBtn.itemLabel.privateAnonymizedUrl=Prywatny anonimowy adres URL dataset.editBtn.itemLabel.embargo=Embargo dataset.editBtn.itemLabel.permissionsDataset=Zbi\u00F3r danych dataset.editBtn.itemLabel.permissionsFile=Zastrze\u017Cone pliki @@ -1726,6 +1727,7 @@ dataset.noValidSelectedFilesForDownload=Nie mo\u017Cna pobra\u0107 wybranych zas dataset.mixedSelectedFilesForDownload=Nie mo\u017Cna pobra\u0107 wybranych zastrze\u017Conych plik\u00F3w, poniewa\u017C nie przyznano Ci do nich dost\u0119pu. dataset.downloadUnrestricted=Naci\u015Bnij "Dalej" aby pobra\u0107 pliki, do pobrania kt\u00F3rych posiadasz odpowiednie uprawnienia. dataset.privateurl.infoMessageAuthor.title=Prywatny URL zbioru danych +dataset.privateurl.anonymized.infoMessageAuthor.title=Prywatny anonimizowany URL zbioru danych dataset.privateurl.infoMessageAuthor.details=Udost\u0119pnij prywatnie ten zbi\u00F3r danych jeszcze zanim stanie si\u0119 publicznie dost\u0119pny: {0} dataset.privateurl.infoMessageReviewer.title=Prywatny URL zbioru danych dataset.privateurl.infoMessageReviewer.details=Ten zbi\u00F3r danych jest udost\u0119pniany prywatnie. Nie b\u0119dziesz mie\u0107 do niego dost\u0119pu po zalogowaniu na konto w repozytorium. @@ -1740,6 +1742,9 @@ dataset.privateurl.roleassigeeTitle=Prywatny URL dataset.privateurl.createdSuccess=Operacja zako\u0144czona powodzeniem. dataset.privateurl.disabledSuccess=Uda\u0142o Ci si\u0119 dezaktywowa\u0107 prywatny URL tego zbioru danych. dataset.privateurl.noPermToCreate=Aby stworzy\u0107 prywatny URL, musisz mie\u0107 nast\u0119puj\u0105ce uprawnienia: {0}. +dataset.anonymized.privateurl.header=Prywatny Anonimowy URL zbioru danych +dataset.anonymized.privateurl.tip=Skorzystaj z prywatnego URL by osobom nieposiadaj\u0105cym konta w repozytorium umo\u017Cliwi\u0107 dost\u0119p do Twojego zbioru danych. Wi\u0119cej informacji na temat prywatnego URL mo\u017Cna znale\u017A\u0107 w "Poradniku u\u017Cytkownika". +dataset.anonymized.privateurl.warning="UWAGA. Tena zbiór danych posiada opublikowan\u0105 wersj\u0119. Osoby u\u017Cywaj\u0105ce Zanonymizowanego Prywatnego adresu URL mog\u0105 by\u0107 w stanie uzyska\u0107 pe\u0142ny, niezanonimizowany dost\u0119p do tego zbioru. dataset.embargo.datasetSummary.title=Embargo dataset.embargo.datasetSummary.tip=W zbiorze obj\u0119tym embargiem pliki pozostaj\u0105 czasowo niedost\u0119pne. dataset.embargo.datasetSummary.message=Pliki w tym zbiorze danych b\u0119d\u0105 dost\u0119pne od {0}. diff --git a/dataverse-persistence/src/main/resources/db/migration/V80__add_anonimized_column_to_roleassignment.sql b/dataverse-persistence/src/main/resources/db/migration/V80__add_anonimized_column_to_roleassignment.sql new file mode 100644 index 0000000000..74f3d136da --- /dev/null +++ b/dataverse-persistence/src/main/resources/db/migration/V80__add_anonimized_column_to_roleassignment.sql @@ -0,0 +1,3 @@ +alter table roleassignment add column if not exists anonymized boolean not null default false; +ALTER TABLE roleassignment DROP CONSTRAINT unq_roleassignment_0; +alter table roleassignment add CONSTRAINT unq_roleassignment_0 UNIQUE (assigneeidentifier, role_id, definitionpoint_id, anonymized); \ No newline at end of file diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/AnonymizedPrivateUrlDialog.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/AnonymizedPrivateUrlDialog.java new file mode 100644 index 0000000000..a34674fde9 --- /dev/null +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/AnonymizedPrivateUrlDialog.java @@ -0,0 +1,102 @@ +package edu.harvard.iq.dataverse; + +import static edu.harvard.iq.dataverse.common.BundleUtil.getStringFromBundle; + +import javax.annotation.PostConstruct; +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.inject.Named; + +import edu.harvard.iq.dataverse.privateurl.PrivateUrl; +import edu.harvard.iq.dataverse.settings.SettingsWrapper; + +@Named +@RequestScoped +public class AnonymizedPrivateUrlDialog { + + private final SettingsWrapper settingsWrapper; + private final DatasetPage datasetPage; + + private PrivateUrl url; + private boolean displaySuccess = false; + + @Inject + public AnonymizedPrivateUrlDialog(final SettingsWrapper settingsWrapper, + final DatasetPage datasetPage) { + this.settingsWrapper = settingsWrapper; + this.datasetPage = datasetPage; + } + + @PostConstruct + public void init() { + this.url = datasetPage.getPrivateUrl(true); + } + + public String getHelpUrl() { + return this.settingsWrapper.getGuidesBaseUrl() + "/" + + this.settingsWrapper.getGuidesVersion() + + "/user/dataset-management.html#private-url-for-reviewing-an-unpublished-dataset"; + } + + public String getName() { + return "anonPrvUrlDlg"; + } + + public String getPanelName() { + return "anonPrvUrlDlgPanel"; + } + public String getConfirmationName() { + return "anonPrvUrlConfirmDlg"; + } + + public String getHeaderText() { + return getStringFromBundle("dataset.anonymized.privateurl.header"); + } + + public String getTipText() { + return getStringFromBundle("dataset.anonymized.privateurl.tip"); + } + + public String getUrl() { + return this.url.getLink(); + } + + public boolean isUrlGenerated() { + return this.url != null; + } + + public boolean displaySuccessMessage() { + return this.displaySuccess; + } + + public boolean displayUrlAbsentMessage() { + return ! isUrlGenerated(); + } + + public boolean displayUrl() { + return isUrlGenerated(); + } + + public boolean displayGenerateButton() { + return ! isUrlGenerated(); + } + + public boolean displayDisableButton() { + return isUrlGenerated(); + } + + public boolean displayPublishedWarning() { + return this.datasetPage.isExistReleasedVersion(); + } + + public void generateUrl() { + this.url = this.datasetPage.createPrivateUrl(true); + this.displaySuccess = isUrlGenerated(); + } + + public void disableUrl() { + this.datasetPage.disablePrivateUrl(true); + this.url = null; + this.displaySuccess = false; + } +} diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java index 7810d9e412..1d580ede5b 100644 --- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java @@ -1,6 +1,47 @@ package edu.harvard.iq.dataverse; +import static edu.harvard.iq.dataverse.common.BundleUtil.getStringFromBundle; +import static edu.harvard.iq.dataverse.util.FileUtil.getResourceAsStream; +import static edu.harvard.iq.dataverse.util.JsfHelper.addErrorMessage; +import static org.apache.commons.lang3.StringUtils.isNotEmpty; + +import java.io.IOException; +import java.io.Serializable; +import java.text.SimpleDateFormat; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.faces.application.FacesMessage; +import javax.faces.component.UIComponent; +import javax.faces.component.UIInput; +import javax.faces.context.FacesContext; +import javax.faces.event.ActionEvent; +import javax.faces.validator.ValidatorException; +import javax.inject.Inject; +import javax.inject.Named; + +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; +import org.jsoup.Jsoup; +import org.omnifaces.cdi.ViewScoped; +import org.primefaces.model.DefaultStreamedContent; +import org.primefaces.model.StreamedContent; + import com.google.common.collect.Lists; + import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean; import edu.harvard.iq.dataverse.citation.CitationFactory; import edu.harvard.iq.dataverse.common.BundleUtil; @@ -53,48 +94,13 @@ import edu.harvard.iq.dataverse.privateurl.PrivateUrl; import edu.harvard.iq.dataverse.privateurl.PrivateUrlUtil; import edu.harvard.iq.dataverse.settings.SettingsServiceBean; +import edu.harvard.iq.dataverse.settings.SettingsWrapper; import edu.harvard.iq.dataverse.util.ArchiverUtil; import edu.harvard.iq.dataverse.util.JsfHelper; import edu.harvard.iq.dataverse.util.SystemConfig; import io.vavr.control.Either; import io.vavr.control.Option; import io.vavr.control.Try; -import org.apache.commons.lang.StringEscapeUtils; -import org.apache.commons.lang3.StringUtils; -import org.jsoup.Jsoup; -import org.omnifaces.cdi.ViewScoped; -import org.primefaces.model.DefaultStreamedContent; -import org.primefaces.model.StreamedContent; - -import javax.faces.application.FacesMessage; -import javax.faces.component.UIComponent; -import javax.faces.component.UIInput; -import javax.faces.context.FacesContext; -import javax.faces.event.ActionEvent; -import javax.faces.validator.ValidatorException; -import javax.inject.Inject; -import javax.inject.Named; - -import static edu.harvard.iq.dataverse.util.FileUtil.getResourceAsStream; - -import java.io.IOException; -import java.io.Serializable; -import java.text.SimpleDateFormat; -import java.time.Clock; -import java.time.Instant; -import java.time.ZoneOffset; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; /** * @author gdurand @@ -178,7 +184,8 @@ public DatasetPage(DataverseSession session, EjbDataverseEngine commandEngine, DatasetThumbnailService datasetThumbnailService, DatasetSummaryService datasetSummaryService, GuestbookResponseServiceBean guestbookResponseService, ConfirmEmailServiceBean confirmEmailService, AuthenticationServiceBean authenticationService, DatasetPageFacade datasetPageFacade, - CitationFactory citationFactory, UningestInfoService uningestInfoService) { + CitationFactory citationFactory, UningestInfoService uningestInfoService, + SettingsWrapper settingsWrapper) { this.session = session; this.commandEngine = commandEngine; this.permissionsWrapper = permissionsWrapper; @@ -378,9 +385,13 @@ public void setVersion(String version) { public String getDisplayCitation() { return displayCitation; } - - public void setDisplayCitation(String displayCitation) { - this.displayCitation = displayCitation; + + public boolean displayCitation() { + return ! this.session.isViewedFromAnonymizedPrivateUrl(this.dataset); + } + + public boolean displayVersionsTab() { + return ! this.session.isViewedFromAnonymizedPrivateUrl(this.dataset); } public Dataset getDataset() { @@ -609,11 +620,6 @@ private String init(boolean initFull) { // populate MapLayerMetadata loadMapLayerMetadataLookup(); // A DataFile may have a related MapLayerMetadata object } - try { - privateUrl = commandEngine.submit(new GetPrivateUrlCommand(dvRequestService.getDataverseRequest(), dataset)); - } catch (CommandException ex) { - // No big deal. The user simply doesn't have access to create or delete a Private URL. - } if (session.getUser() instanceof AuthenticatedUser) { AuthenticatedUser replyToUser = (AuthenticatedUser) session.getUser(); @@ -622,17 +628,22 @@ private String init(boolean initFull) { return null; } + + public boolean displayPrivateUrl(final boolean anonymized) { + return getPrivateUrl(anonymized) != null && !isViewedFromPrivateUrl(); + } + + PrivateUrl getPrivateUrl(final boolean anonymized) { + return this.commandEngine.submit(new GetPrivateUrlCommand( + this.dvRequestService.getDataverseRequest(), this.dataset, anonymized)); + } public void fetchMetricsDownloadCount() { datasetDownloadCount = guestbookResponseService.getCountGuestbookResponsesByDatasetId(dataset.getId()); } public boolean isViewedFromPrivateUrl() { - if (session.getUser() instanceof PrivateUrlUser) { - PrivateUrlUser privateUrlUser = (PrivateUrlUser) session.getUser(); - return dataset != null && dataset.getId().equals(privateUrlUser.getDatasetId()); - } - return false; + return this.session.isViewedFromPrivateUrl(this.dataset); } public boolean isDoiReserved() { @@ -799,37 +810,29 @@ public String updateCurrentVersion() { public String deleteDataset() { - DestroyDatasetCommand cmd; - boolean deleteCommandSuccess = false; Map deleteStorageLocations = datafileService.getPhysicalFilesToDelete(dataset); try { - cmd = new DestroyDatasetCommand(dataset, dvRequestService.getDataverseRequest()); + DestroyDatasetCommand cmd = new DestroyDatasetCommand(dataset, dvRequestService.getDataverseRequest()); commandEngine.submit(cmd); - deleteCommandSuccess = true; /* - need to figure out what to do Update notification in Delete Dataset Method for (UserNotification und : userNotificationService.findByDvObject(dataset.getId())){ userNotificationService.delete(und); } */ + datafileService.finalizeFileDeletes(deleteStorageLocations); + JsfHelper.addFlashSuccessMessage(BundleUtil.getStringFromBundle("dataset.message.deleteSuccess")); } catch (CommandException ex) { JsfHelper.addFlashErrorMessage(BundleUtil.getStringFromBundle("dataset.message.deleteFailure"), ""); logger.severe(ex.getMessage()); } - if (deleteCommandSuccess) { - datafileService.finalizeFileDeletes(deleteStorageLocations); - JsfHelper.addFlashSuccessMessage(BundleUtil.getStringFromBundle("dataset.message.deleteSuccess")); - } - - return "/dataverse.xhtml?alias=" + dataset.getOwner().getAlias() + "&faces-redirect=true"; + return "/dataverse.xhtml?faces-redirect=true&alias=".concat(dataset.getOwner().getAlias()); } public String deleteDatasetVersion() { - DeleteDatasetVersionCommand cmd; try { - cmd = new DeleteDatasetVersionCommand(dvRequestService.getDataverseRequest(), dataset); - commandEngine.submit(cmd); + commandEngine.submit(new DeleteDatasetVersionCommand(dvRequestService.getDataverseRequest(), dataset)); JsfHelper.addFlashSuccessMessage(BundleUtil.getStringFromBundle("datasetVersion.message.deleteSuccess")); } catch (CommandException ex) { JsfHelper.addFlashErrorMessage(BundleUtil.getStringFromBundle("dataset.message.deleteFailure"), ""); @@ -888,8 +891,10 @@ public void saveLinkingDataverses(ActionEvent evt) { } - public String getPrivateUrlMessageDetails() { - return BundleUtil.getStringFromBundle("dataset.privateurl.infoMessageAuthor.details", getPrivateUrlLink(privateUrl)); + public String getPrivateUrlMessageDetails(final boolean anonymized) { + return getStringFromBundle( + "dataset.privateurl.infoMessageAuthor.details", + getPrivateUrl(anonymized).getLink()); } private String linkingDataverseErrorMessage = ""; @@ -1044,59 +1049,29 @@ public String getDatasetPublishCustomText() { public Boolean isDatasetPublishPopupCustomTextOnAllVersions() { return settingsService.isTrueForKey(SettingsServiceBean.Key.DatasetPublishPopupCustomTextOnAllVersions); } - - private PrivateUrl privateUrl; - - public PrivateUrl getPrivateUrl() { - return privateUrl; - } - - public void setPrivateUrl(PrivateUrl privateUrl) { - this.privateUrl = privateUrl; - } - - public void initPrivateUrlPopUp() { - if (privateUrl != null) { - setPrivateUrlJustCreatedToFalse(); - } - } - - private boolean privateUrlWasJustCreated; - - public boolean isPrivateUrlWasJustCreated() { - return privateUrlWasJustCreated; - } - - public void setPrivateUrlJustCreatedToFalse() { - privateUrlWasJustCreated = false; - } - - public void createPrivateUrl() { + + PrivateUrl createPrivateUrl(final boolean anonymized) { try { - PrivateUrl createdPrivateUrl = commandEngine.submit(new CreatePrivateUrlCommand(dvRequestService.getDataverseRequest(), dataset)); - privateUrl = createdPrivateUrl; - privateUrlWasJustCreated = true; - } catch (CommandException ex) { - String msg = BundleUtil.getStringFromBundle("dataset.privateurl.noPermToCreate", PrivateUrlUtil.getRequiredPermissions(ex)); - logger.info("Unable to create a Private URL for dataset id " + dataset.getId() + ". Message to user: " + msg + " Exception: " + ex); - JsfHelper.addErrorMessage(msg); + return this.commandEngine.submit(new CreatePrivateUrlCommand( + this.dvRequestService.getDataverseRequest(), this.dataset, anonymized)); + } catch (final CommandException ex) { + String msg = getStringFromBundle( + "dataset.privateurl.noPermToCreate", + PrivateUrlUtil.getRequiredPermissions(ex)); + addErrorMessage(msg); + return null; } } - - public void disablePrivateUrl() { + + void disablePrivateUrl(final boolean anonymized) { try { - commandEngine.submit(new DeletePrivateUrlCommand(dvRequestService.getDataverseRequest(), dataset)); - privateUrl = null; + commandEngine.submit(new DeletePrivateUrlCommand(dvRequestService.getDataverseRequest(), dataset, anonymized)); JsfHelper.addFlashSuccessMessage(BundleUtil.getStringFromBundle("dataset.privateurl.disabledSuccess")); } catch (CommandException ex) { logger.info("CommandException caught calling DeletePrivateUrlCommand: " + ex); } } - public String getPrivateUrlLink(PrivateUrl privateUrl) { - return privateUrl.getLink(); - } - /** * dataset title diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DataverseHeaderFragment.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DataverseHeaderFragment.java index 4653c89c32..e6f307635b 100644 --- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DataverseHeaderFragment.java +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DataverseHeaderFragment.java @@ -51,6 +51,7 @@ public class DataverseHeaderFragment implements Serializable { private List breadcrumbs = new ArrayList<>(); private Long unreadNotificationCount; + private DvObject currentDvObject; // -------------------- CONSTRUCTORS -------------------- @@ -84,6 +85,7 @@ public List getBreadcrumbs() { // -------------------- LOGIC -------------------- public void initBreadcrumbs(DvObject dvObject) { + this.currentDvObject = dvObject; if (dvObject == null) { return; } @@ -99,6 +101,11 @@ public void initBreadcrumbsForFileMetadata(FileMetadata fmd) { initBreadcrumbsForFileMetadata(fmd, null); } + + public boolean displayBreadcrumbs() { + return getBreadcrumbs().size() > 1 + && ! this.dataverseSession.isViewedFromAnonymizedPrivateUrl(this.currentDvObject); + } public void initBreadcrumbsForDataFile(DataFile datafile, String subPage) { Dataset dataset = datafile.getOwner(); diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DataverseSession.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DataverseSession.java index 8f7cfe2ddb..1c93261270 100644 --- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DataverseSession.java +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/DataverseSession.java @@ -1,31 +1,33 @@ package edu.harvard.iq.dataverse; -import edu.harvard.iq.dataverse.actionlogging.ActionLogServiceBean; -import edu.harvard.iq.dataverse.persistence.ActionLogRecord; -import edu.harvard.iq.dataverse.persistence.user.GuestUser; -import edu.harvard.iq.dataverse.persistence.user.User; -import edu.harvard.iq.dataverse.util.SystemConfig; +import static edu.harvard.iq.dataverse.persistence.ActionLogRecord.ActionType.SessionManagement; -import javax.ejb.EJB; -import javax.enterprise.context.SessionScoped; -import javax.faces.context.FacesContext; -import javax.inject.Inject; -import javax.inject.Named; import java.io.Serializable; import java.util.Locale; import java.util.Set; import java.util.UUID; -import java.util.logging.Logger; + +import javax.enterprise.context.SessionScoped; +import javax.faces.context.FacesContext; +import javax.inject.Inject; +import javax.inject.Named; + +import edu.harvard.iq.dataverse.actionlogging.ActionLogServiceBean; +import edu.harvard.iq.dataverse.persistence.ActionLogRecord; +import edu.harvard.iq.dataverse.persistence.DvObject; +import edu.harvard.iq.dataverse.persistence.user.GuestUser; +import edu.harvard.iq.dataverse.persistence.user.PrivateUrlUser; +import edu.harvard.iq.dataverse.persistence.user.User; +import edu.harvard.iq.dataverse.util.SystemConfig; /** * @author gdurand */ +@SuppressWarnings("serial") @Named @SessionScoped public class DataverseSession implements Serializable { - private static final Logger logger = Logger.getLogger(DataverseSession.class.getCanonicalName()); - private ActionLogServiceBean logSvc; private SystemConfig systemConfig; @@ -56,7 +58,6 @@ public User getUser() { if (user == null) { user = GuestUser.get(); } - return user; } @@ -69,38 +70,40 @@ public boolean isStatusDismissed() { } public String getLocaleCode() { - if (localeCode == null) { - localeCode = initLocale(); - - logger.fine("init: locale set to " + localeCode); + if (this.localeCode == null) { + this.localeCode = initLocale(); } - return localeCode; + return this.localeCode; } public Locale getLocale() { - String localeCode = getLocaleCode(); - return Locale.forLanguageTag(localeCode); + return Locale.forLanguageTag(getLocaleCode()); } public String getLocaleTitle() { - if (localeCode == null) { - localeCode = initLocale(); - - logger.fine("init: locale set to " + localeCode); - } - return systemConfig.getConfiguredLocales().get(localeCode); + return this.systemConfig.getConfiguredLocales().get(getLocaleCode()); } public int getFilesPerPage() { - return filesPerPage; + return this.filesPerPage; } + public boolean isViewedFromPrivateUrl(final DvObject dataset) { + return getUser() instanceof PrivateUrlUser && getUser().isAffiliatedWith(dataset); + } + + public boolean isViewedFromAnonymizedPrivateUrl(final DvObject dataset) { + return getUser().isAnonymized() && isViewedFromPrivateUrl(dataset); + } + + // -------------------- LOGIC -------------------- public String initLocale() { - Set dataverseLanguages = systemConfig.getConfiguredLocales().keySet(); - - return dataverseLanguages.contains(getBrowserLanguage()) ? getBrowserLanguage() : "en"; + final Set dataverseLanguages = this.systemConfig + .getConfiguredLocales().keySet(); + final String browserLagunage = getBrowserLanguage(); + return dataverseLanguages.contains(browserLagunage) ? browserLagunage : "en"; } public void updateLocaleInViewRootForReload(String code) { @@ -109,11 +112,12 @@ public void updateLocaleInViewRootForReload(String code) { } public void updateLocaleInViewRoot() { - if (localeCode != null - && FacesContext.getCurrentInstance() != null - && FacesContext.getCurrentInstance().getViewRoot() != null - && !localeCode.equals(FacesContext.getCurrentInstance().getViewRoot().getLocale().getLanguage())) { - FacesContext.getCurrentInstance().getViewRoot().setLocale(new Locale(localeCode)); + final FacesContext context = FacesContext.getCurrentInstance(); + if (this.localeCode != null + && context != null + && context.getViewRoot() != null + && !this.localeCode.equals(context.getViewRoot().getLocale().getLanguage())) { + context.getViewRoot().setLocale(new Locale(this.localeCode)); } } @@ -127,14 +131,15 @@ public boolean canEditDashboard() { * @return Top browser locale which is taken from 'Accept-Language header'. */ private String getBrowserLanguage() { - return FacesContext.getCurrentInstance().getExternalContext().getRequestLocale().getLanguage(); + return FacesContext.getCurrentInstance().getExternalContext() + .getRequestLocale().getLanguage(); } // -------------------- SETTERS -------------------- public void setUser(User aUser) { logSvc.log( - new ActionLogRecord(ActionLogRecord.ActionType.SessionManagement, (aUser == null) ? "logout" : "login") + new ActionLogRecord(SessionManagement, (aUser == null) ? "logout" : "login") .setUserIdentifier((aUser != null) ? aUser.getIdentifier() : (user != null ? user.getIdentifier() : ""))); this.user = aUser; } diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/PrivateUrlDialog.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/PrivateUrlDialog.java new file mode 100644 index 0000000000..dcbb76e1ac --- /dev/null +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/PrivateUrlDialog.java @@ -0,0 +1,102 @@ +package edu.harvard.iq.dataverse; + +import static edu.harvard.iq.dataverse.common.BundleUtil.getStringFromBundle; + +import javax.annotation.PostConstruct; +import javax.enterprise.context.RequestScoped; +import javax.inject.Inject; +import javax.inject.Named; + +import edu.harvard.iq.dataverse.privateurl.PrivateUrl; +import edu.harvard.iq.dataverse.settings.SettingsWrapper; + +@Named +@RequestScoped +public class PrivateUrlDialog { + + private final SettingsWrapper settingsWrapper; + private final DatasetPage datasetPage; + + private PrivateUrl url; + private boolean displaySuccess = false; + + @Inject + public PrivateUrlDialog(final SettingsWrapper settingsWrapper, + final DatasetPage datasetPage) { + this.settingsWrapper = settingsWrapper; + this.datasetPage = datasetPage; + } + + @PostConstruct + public void init() { + this.url = datasetPage.getPrivateUrl(false); + } + + public String getHelpUrl() { + return this.settingsWrapper.getGuidesBaseUrl() + "/" + + this.settingsWrapper.getGuidesVersion() + + "/user/dataset-management.html#private-url-for-reviewing-an-unpublished-dataset"; + } + + public String getName() { + return "prvUrlDlg"; + } + + public String getPanelName() { + return "prvUrlDlgPanel"; + } + public String getConfirmationName() { + return "prvUrlConfirmDlg"; + } + + public String getHeaderText() { + return getStringFromBundle("dataset.privateurl.header"); + } + + public String getTipText() { + return getStringFromBundle("dataset.privateurl.tip"); + } + + public String getUrl() { + return this.url.getLink(); + } + + public boolean isUrlGenerated() { + return this.url != null; + } + + public boolean displaySuccessMessage() { + return this.displaySuccess; + } + + public boolean displayUrlAbsentMessage() { + return ! isUrlGenerated(); + } + + public boolean displayUrl() { + return isUrlGenerated(); + } + + public boolean displayGenerateButton() { + return ! isUrlGenerated(); + } + + public boolean displayDisableButton() { + return isUrlGenerated(); + } + + public boolean displayPublishedWarning() { + return false; + } + + public void generateUrl() { + this.url = this.datasetPage.createPrivateUrl(false); + this.displaySuccess = isUrlGenerated(); + } + + public void disableUrl() { + this.datasetPage.disablePrivateUrl(false); + this.url = null; + this.displaySuccess = false; + } +} diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java index 212c9aeeba..181bd838b0 100644 --- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/api/Datasets.java @@ -141,6 +141,9 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import javax.ws.rs.core.UriInfo; + +import static javax.ws.rs.core.Response.Status.NOT_FOUND; + import java.io.IOException; import java.io.InputStream; import java.io.StringReader; @@ -1030,12 +1033,7 @@ public Response getAssignments(@PathParam("identifier") String id) { @GET @Path("{id}/privateUrl") public Response getPrivateUrlData(@PathParam("id") String idSupplied) { - return response(req -> { - PrivateUrl privateUrl = execCommand(new GetPrivateUrlCommand(req, findDatasetOrDie(idSupplied))); - return privateUrl != null - ? ok(new PrivateUrlDTO.Converter().convert(privateUrl)) - : error(Response.Status.NOT_FOUND, "Private URL not found."); - }); + return getAnonymizedPrivateUrlData(idSupplied, false); } @POST @@ -1043,19 +1041,56 @@ public Response getPrivateUrlData(@PathParam("id") String idSupplied) { @Path("{id}/privateUrl") public Response createPrivateUrl(@PathParam("id") String idSupplied) { return response(req -> ok( - new PrivateUrlDTO.Converter().convert( - execCommand(new CreatePrivateUrlCommand(req, findDatasetOrDie(idSupplied)))))); + toDTO(execCommand(new CreatePrivateUrlCommand(req, findDatasetOrDie(idSupplied), false))))); } @DELETE @ApiWriteOperation @Path("{id}/privateUrl") public Response deletePrivateUrl(@PathParam("id") String idSupplied) { + return deleteAnonymizedPrivateUrl(idSupplied, false); + } + + private PrivateUrlDTO toDTO(final PrivateUrl url) { + return new PrivateUrlDTO.Converter().convert(url); + } + + @GET + @Path("{id}/anonymizedPrivateUrl") + public Response getAnonymizedPrivateUrlData(@PathParam("id") String idSupplied) { + return getAnonymizedPrivateUrlData(idSupplied, true); + } + + private Response getAnonymizedPrivateUrlData(final String idSupplied, final boolean anonymized) { + return response(req -> { + PrivateUrl privateUrl = execCommand(new GetPrivateUrlCommand(req, findDatasetOrDie(idSupplied), anonymized)); + return privateUrl != null + ? ok(toDTO(privateUrl)) + : error(NOT_FOUND, "Private URL not found."); + }); + } + + @POST + @ApiWriteOperation + @Path("{id}/anonymizedPrivateUrl") + public Response createAnonymizedPrivateUrl(@PathParam("id") String idSupplied) { + return response(req -> ok( + toDTO(execCommand(new CreatePrivateUrlCommand(req, findDatasetOrDie(idSupplied), true))))); + } + + @DELETE + @ApiWriteOperation + @Path("{id}/anonymizedPrivateUrl") + public Response deleteAnonymizedPrivateUrl(@PathParam("id") String idSupplied) { + return deleteAnonymizedPrivateUrl(idSupplied, true); + } + + private Response deleteAnonymizedPrivateUrl(final String idSupplied, final boolean anonymized) { return response(req -> { Dataset dataset = findDatasetOrDie(idSupplied); - PrivateUrl privateUrl = execCommand(new GetPrivateUrlCommand(req, dataset)); + PrivateUrl privateUrl = execCommand(new GetPrivateUrlCommand(req, dataset, anonymized)); if (privateUrl != null) { - execCommand(new DeletePrivateUrlCommand(req, dataset)); + execCommand(new DeletePrivateUrlCommand(req, dataset, false)); return ok("Private URL deleted."); } else { return notFound("No Private URL to delete."); diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/citation/Citation.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/citation/Citation.java index 83a0ab2f0d..b88596791a 100644 --- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/citation/Citation.java +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/citation/Citation.java @@ -1,12 +1,8 @@ package edu.harvard.iq.dataverse.citation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.Locale; public class Citation { - private static final Logger logger = LoggerFactory.getLogger(Citation.class); private CitationData data; diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/AssignRoleCommand.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/AssignRoleCommand.java index a8feb8d7fe..b2f86132a4 100644 --- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/AssignRoleCommand.java +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/AssignRoleCommand.java @@ -33,6 +33,7 @@ public class AssignRoleCommand extends AbstractCommand implement private final RoleAssignee grantee; private final DvObject defPoint; private final String privateUrlToken; + private final boolean anonymized; /** * @param anAssignee The user being granted the role @@ -42,18 +43,23 @@ public class AssignRoleCommand extends AbstractCommand implement * @param privateUrlToken An optional token used by the Private Url feature. */ public AssignRoleCommand(RoleAssignee anAssignee, DataverseRole aRole, DvObject assignmentPoint, DataverseRequest aRequest, String privateUrlToken) { + this(anAssignee, aRole, assignmentPoint, aRequest, privateUrlToken, false); + } + + public AssignRoleCommand(RoleAssignee anAssignee, DataverseRole aRole, DvObject assignmentPoint, DataverseRequest aRequest, String privateUrlToken, boolean anonymized) { // for data file check permission on owning dataset super(aRequest, assignmentPoint instanceof DataFile ? assignmentPoint.getOwner() : assignmentPoint); role = aRole; grantee = anAssignee; defPoint = assignmentPoint; this.privateUrlToken = privateUrlToken; + this.anonymized = anonymized; } @Override public RoleAssignment execute(CommandContext ctxt) { // TODO make sure the role is defined on the dataverse. - RoleAssignment roleAssignment = new RoleAssignment(role, grantee, defPoint, privateUrlToken); + RoleAssignment roleAssignment = new RoleAssignment(role, grantee, defPoint, privateUrlToken, anonymized); return ctxt.roles().save(roleAssignment); } diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CreatePrivateUrlCommand.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CreatePrivateUrlCommand.java index 52254d4761..6dc54b3315 100644 --- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CreatePrivateUrlCommand.java +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/CreatePrivateUrlCommand.java @@ -1,5 +1,7 @@ package edu.harvard.iq.dataverse.engine.command.impl; +import java.util.UUID; + import edu.harvard.iq.dataverse.engine.command.AbstractCommand; import edu.harvard.iq.dataverse.engine.command.CommandContext; import edu.harvard.iq.dataverse.engine.command.DataverseRequest; @@ -13,45 +15,40 @@ import edu.harvard.iq.dataverse.persistence.user.RoleAssignment; import edu.harvard.iq.dataverse.privateurl.PrivateUrl; -import java.util.UUID; -import java.util.logging.Logger; - +@SuppressWarnings("serial") @RequiredPermissions(value = {Permission.ManageDatasetPermissions, Permission.ManageMinorDatasetPermissions}, isAllPermissionsRequired = false) public class CreatePrivateUrlCommand extends AbstractCommand { - private static final Logger logger = Logger.getLogger(CreatePrivateUrlCommand.class.getCanonicalName()); - final Dataset dataset; - - public CreatePrivateUrlCommand(DataverseRequest dataverseRequest, Dataset theDataset) { + private final boolean anonymized; + + public CreatePrivateUrlCommand(DataverseRequest dataverseRequest, Dataset theDataset, boolean anonymized) { super(dataverseRequest, theDataset); - dataset = theDataset; + this.dataset = theDataset; + this.anonymized = anonymized; } @Override public PrivateUrl execute(CommandContext ctxt) { - logger.fine("Executing CreatePrivateUrlCommand..."); if (dataset == null) { /** * @todo Internationalize this. */ String message = "Can't create Private URL. Dataset is null."; - logger.info(message); throw new IllegalCommandException(message, this); } - PrivateUrl existing = ctxt.privateUrl().getPrivateUrlFromDatasetId(dataset.getId()); + PrivateUrl existing = ctxt.privateUrl().getPrivateUrlFromDatasetId(dataset.getId(), this.anonymized); if (existing != null) { /** * @todo Internationalize this. */ String message = "Private URL already exists for dataset id " + dataset.getId() + "."; - logger.info(message); throw new IllegalCommandException(message, this); } PrivateUrlUser privateUrlUser = new PrivateUrlUser(dataset.getId()); DataverseRole memberRole = ctxt.roles().findBuiltinRoleByAlias(BuiltInRole.MEMBER); final String privateUrlToken = UUID.randomUUID().toString(); - RoleAssignment roleAssignment = ctxt.engine().submit(new AssignRoleCommand(privateUrlUser, memberRole, dataset, getRequest(), privateUrlToken)); + RoleAssignment roleAssignment = ctxt.engine().submit(new AssignRoleCommand(privateUrlUser, memberRole, dataset, getRequest(), privateUrlToken, this.anonymized)); PrivateUrl privateUrl = new PrivateUrl(roleAssignment, dataset, ctxt.systemConfig().getDataverseSiteUrl()); return privateUrl; } diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteDatasetVersionCommand.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteDatasetVersionCommand.java index 971c78c02d..5131f94503 100644 --- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteDatasetVersionCommand.java +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeleteDatasetVersionCommand.java @@ -84,7 +84,7 @@ protected void executeImpl(CommandContext ctxt) { * Contributor who does NOT have ManageDatasetPermissions can * still successfully delete a Private URL. */ - PrivateUrl privateUrl = ctxt.privateUrl().getPrivateUrlFromDatasetId(doomed.getId()); + PrivateUrl privateUrl = ctxt.privateUrl().getPrivateUrlFromDatasetId(doomed.getId(), false); if (privateUrl != null) { logger.fine("Deleting Private URL for dataset id " + doomed.getId()); PrivateUrlUser privateUrlUser = new PrivateUrlUser(doomed.getId()); @@ -93,6 +93,15 @@ protected void executeImpl(CommandContext ctxt) { ctxt.roles().revoke(roleAssignment); } } + privateUrl = ctxt.privateUrl().getPrivateUrlFromDatasetId(doomed.getId(), true); + if (privateUrl != null) { + logger.fine("Deleting Anonymized Private URL for dataset id " + doomed.getId()); + PrivateUrlUser privateUrlUser = new PrivateUrlUser(doomed.getId()); + List roleAssignments = ctxt.roles().directRoleAssignments(privateUrlUser, doomed); + for (RoleAssignment roleAssignment : roleAssignments) { + ctxt.roles().revoke(roleAssignment); + } + } boolean doNormalSolrDocCleanUp = true; removeLockAndUpdateDataset(ctxt); ctxt.index().indexDataset(doomed, doNormalSolrDocCleanUp); diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeletePrivateUrlCommand.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeletePrivateUrlCommand.java index 93adfdbef9..805a15d729 100644 --- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeletePrivateUrlCommand.java +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/DeletePrivateUrlCommand.java @@ -1,46 +1,48 @@ package edu.harvard.iq.dataverse.engine.command.impl; +import static edu.harvard.iq.dataverse.persistence.user.Permission.ManageDatasetPermissions; +import static edu.harvard.iq.dataverse.persistence.user.Permission.ManageMinorDatasetPermissions; + +import java.util.List; + import edu.harvard.iq.dataverse.engine.command.AbstractVoidCommand; import edu.harvard.iq.dataverse.engine.command.CommandContext; import edu.harvard.iq.dataverse.engine.command.DataverseRequest; import edu.harvard.iq.dataverse.engine.command.RequiredPermissions; -import edu.harvard.iq.dataverse.engine.command.exception.CommandException; import edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException; import edu.harvard.iq.dataverse.persistence.dataset.Dataset; -import edu.harvard.iq.dataverse.persistence.user.Permission; import edu.harvard.iq.dataverse.persistence.user.PrivateUrlUser; import edu.harvard.iq.dataverse.persistence.user.RoleAssignment; -import java.util.List; -import java.util.logging.Logger; - -@RequiredPermissions(value = {Permission.ManageDatasetPermissions, Permission.ManageMinorDatasetPermissions}, isAllPermissionsRequired = false) +@SuppressWarnings("serial") +@RequiredPermissions(value = {ManageDatasetPermissions, ManageMinorDatasetPermissions}, isAllPermissionsRequired = false) public class DeletePrivateUrlCommand extends AbstractVoidCommand { - private static final Logger logger = Logger.getLogger(DeletePrivateUrlCommand.class.getCanonicalName()); - - final Dataset dataset; + private final Dataset dataset; + private final boolean anonymized; - public DeletePrivateUrlCommand(DataverseRequest aRequest, Dataset theDataset) { + + public DeletePrivateUrlCommand(DataverseRequest aRequest, Dataset theDataset, boolean anonymized) { super(aRequest, theDataset); - dataset = theDataset; + this.dataset = theDataset; + this.anonymized = anonymized; } @Override protected void executeImpl(CommandContext ctxt) { - logger.fine("Executing DeletePrivateUrlCommand...."); if (dataset == null) { /** * @todo Internationalize this. */ String message = "Can't delete Private URL. Dataset is null."; - logger.info(message); throw new IllegalCommandException(message, this); } PrivateUrlUser privateUrlUser = new PrivateUrlUser(dataset.getId()); List roleAssignments = ctxt.roles().directRoleAssignments(privateUrlUser, dataset); for (RoleAssignment roleAssignment : roleAssignments) { - ctxt.engine().submit(new RevokeRoleCommand(roleAssignment, getRequest())); + if(roleAssignment.isAnonymized() == this.anonymized) { + ctxt.engine().submit(new RevokeRoleCommand(roleAssignment, getRequest())); + } } } diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetPrivateUrlCommand.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetPrivateUrlCommand.java index 276cf51145..3ed38b2d0f 100644 --- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetPrivateUrlCommand.java +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/GetPrivateUrlCommand.java @@ -1,36 +1,39 @@ package edu.harvard.iq.dataverse.engine.command.impl; +import static edu.harvard.iq.dataverse.persistence.user.Permission.ViewUnpublishedDataset; +import static edu.harvard.iq.dataverse.persistence.user.Permission.ViewUnpublishedDataverse; + import edu.harvard.iq.dataverse.engine.command.AbstractCommand; import edu.harvard.iq.dataverse.engine.command.CommandContext; import edu.harvard.iq.dataverse.engine.command.DataverseRequest; import edu.harvard.iq.dataverse.engine.command.RequiredPermissions; import edu.harvard.iq.dataverse.persistence.dataset.Dataset; -import edu.harvard.iq.dataverse.persistence.user.Permission; import edu.harvard.iq.dataverse.privateurl.PrivateUrl; -import java.util.logging.Logger; - -@RequiredPermissions(value = {Permission.ManageDatasetPermissions, Permission.ManageMinorDatasetPermissions}, isAllPermissionsRequired = false) +@SuppressWarnings("serial") +@RequiredPermissions(value = { ViewUnpublishedDataset, + ViewUnpublishedDataverse }, isAllPermissionsRequired = false) public class GetPrivateUrlCommand extends AbstractCommand { - private static final Logger logger = Logger.getLogger(GetPrivateUrlCommand.class.getCanonicalName()); - private final Dataset dataset; + private final boolean anonymized; - public GetPrivateUrlCommand(DataverseRequest aRequest, Dataset theDataset) { - super(aRequest, theDataset); - dataset = theDataset; + public GetPrivateUrlCommand(final DataverseRequest request, + final Dataset dataset, final boolean anonymized) { + super(request, dataset); + this.dataset = dataset; + this.anonymized = anonymized; } @Override - public PrivateUrl execute(CommandContext ctxt) { - logger.fine("GetPrivateUrlCommand called"); - Long datasetId = dataset.getId(); - if (datasetId == null) { - // Perhaps a dataset is being created in the GUI. - return null; + public PrivateUrl execute(final CommandContext ctxt) { + final Long datasetId = dataset.getId(); + if (datasetId != null) { + return ctxt.privateUrl().getPrivateUrlFromDatasetId(datasetId, + this.anonymized); + } else { + return null; // Perhaps a dataset is being created in the GUI. } - return ctxt.privateUrl().getPrivateUrlFromDatasetId(datasetId); } } diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/privateurl/PrivateUrlServiceBean.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/privateurl/PrivateUrlServiceBean.java index a1e74e1ef8..32c66b1705 100644 --- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/privateurl/PrivateUrlServiceBean.java +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/privateurl/PrivateUrlServiceBean.java @@ -1,34 +1,28 @@ package edu.harvard.iq.dataverse.privateurl; +import java.io.Serializable; + +import javax.ejb.EJB; +import javax.ejb.Stateless; + import edu.harvard.iq.dataverse.DatasetDao; -import edu.harvard.iq.dataverse.persistence.dataset.Dataset; import edu.harvard.iq.dataverse.persistence.user.PrivateUrlUser; import edu.harvard.iq.dataverse.persistence.user.RoleAssignment; +import edu.harvard.iq.dataverse.persistence.user.RoleAssignmentRepository; import edu.harvard.iq.dataverse.util.SystemConfig; -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.persistence.EntityManager; -import javax.persistence.NoResultException; -import javax.persistence.NonUniqueResultException; -import javax.persistence.PersistenceContext; -import javax.persistence.TypedQuery; -import java.io.Serializable; -import java.util.logging.Logger; - /** * PrivateUrlServiceBean depends on Glassfish and Postgres being available and * it is tested with API tests in DatasetIT. Code that can execute without any * runtime dependencies should be put in PrivateUrlUtil so it can be unit * tested. */ +@SuppressWarnings("serial") @Stateless public class PrivateUrlServiceBean implements Serializable { - private static final Logger logger = Logger.getLogger(PrivateUrlServiceBean.class.getCanonicalName()); - - @PersistenceContext(unitName = "VDCNet-ejbPU") - private EntityManager em; + @EJB + private RoleAssignmentRepository roleAssignmentRepo; @EJB DatasetDao datasetDao; @@ -39,8 +33,8 @@ public class PrivateUrlServiceBean implements Serializable { /** * @return A PrivateUrl if the dataset has one or null. */ - public PrivateUrl getPrivateUrlFromDatasetId(long datasetId) { - RoleAssignment roleAssignment = getPrivateUrlRoleAssignmentFromDataset(datasetDao.find(datasetId)); + public PrivateUrl getPrivateUrlFromDatasetId(long datasetId, final boolean anonymized) { + RoleAssignment roleAssignment = this.roleAssignmentRepo.getPrivateUrlRoleAssignmentFromDataset(datasetDao.find(datasetId), anonymized); return PrivateUrlUtil.getPrivateUrlFromRoleAssignment(roleAssignment, systemConfig.getDataverseSiteUrl()); } @@ -48,7 +42,7 @@ public PrivateUrl getPrivateUrlFromDatasetId(long datasetId) { * @return A PrivateUrlUser if one can be found using the token or null. */ public PrivateUrlUser getPrivateUrlUserFromToken(String token) { - return PrivateUrlUtil.getPrivateUrlUserFromRoleAssignment(getRoleAssignmentFromPrivateUrlToken(token)); + return PrivateUrlUtil.getPrivateUrlUserFromRoleAssignment(this.roleAssignmentRepo.getRoleAssignmentFromPrivateUrlToken(token)); } /** @@ -56,49 +50,9 @@ public PrivateUrlUser getPrivateUrlUserFromToken(String token) { * null. */ public PrivateUrlRedirectData getPrivateUrlRedirectDataFromToken(String token) { - return PrivateUrlUtil.getPrivateUrlRedirectData(getRoleAssignmentFromPrivateUrlToken(token)); + return PrivateUrlUtil.getPrivateUrlRedirectData(this.roleAssignmentRepo.getRoleAssignmentFromPrivateUrlToken(token)); } - /** - * @return A RoleAssignment or null. - * @todo This might be a good place for Optional. - */ - private RoleAssignment getRoleAssignmentFromPrivateUrlToken(String privateUrlToken) { - if (privateUrlToken == null) { - return null; - } - TypedQuery query = em.createNamedQuery( - "RoleAssignment.listByPrivateUrlToken", - RoleAssignment.class); - query.setParameter("privateUrlToken", privateUrlToken); - try { - RoleAssignment roleAssignment = query.getSingleResult(); - return roleAssignment; - } catch (NoResultException | NonUniqueResultException ex) { - return null; - } - } - /** - * @param dataset A non-null dataset; - * @return A role assignment for a Private URL, if found, or null. - * @todo This might be a good place for Optional. - */ - private RoleAssignment getPrivateUrlRoleAssignmentFromDataset(Dataset dataset) { - if (dataset == null) { - return null; - } - TypedQuery query = em.createNamedQuery( - "RoleAssignment.listByAssigneeIdentifier_DefinitionPointId", - RoleAssignment.class); - PrivateUrlUser privateUrlUser = new PrivateUrlUser(dataset.getId()); - query.setParameter("assigneeIdentifier", privateUrlUser.getIdentifier()); - query.setParameter("definitionPointId", dataset.getId()); - try { - return query.getSingleResult(); - } catch (NoResultException | NonUniqueResultException ex) { - return null; - } - } } diff --git a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/privateurl/PrivateUrlUtil.java b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/privateurl/PrivateUrlUtil.java index 959c2efa78..c7889151b3 100644 --- a/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/privateurl/PrivateUrlUtil.java +++ b/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/privateurl/PrivateUrlUtil.java @@ -93,7 +93,7 @@ static public PrivateUrlUser getPrivateUrlUserFromRoleAssignment(RoleAssignment } Dataset dataset = getDatasetFromRoleAssignment(roleAssignment); if (dataset != null) { - PrivateUrlUser privateUrlUser = new PrivateUrlUser(dataset.getId()); + PrivateUrlUser privateUrlUser = new PrivateUrlUser(dataset.getId(), roleAssignment.isAnonymized()); return privateUrlUser; } return null; diff --git a/dataverse-webapp/src/main/webapp/dataset-privateUrlDialog.xhtml b/dataverse-webapp/src/main/webapp/dataset-privateUrlDialog.xhtml new file mode 100644 index 0000000000..97bda9419f --- /dev/null +++ b/dataverse-webapp/src/main/webapp/dataset-privateUrlDialog.xhtml @@ -0,0 +1,85 @@ + + + + + + +

+ + + +

+ +
+

+ #{bundle['dataset.privateurl.absent']}

+

+ + #{bundle['dataset.anonymized.privateurl.warning']} +

+
+

+ + #{bundle['dataset.privateurl.createdSuccess']} +

+

#{dialog.getUrl()}

+
+
+
+ + + +
+
+
+ + + +

+ + #{bundle['dataset.privateurl.disableConfirmationText']} +

+
+ + +
+
+
+ \ No newline at end of file diff --git a/dataverse-webapp/src/main/webapp/dataset.xhtml b/dataverse-webapp/src/main/webapp/dataset.xhtml index f9f4304d5c..e19de5fe37 100644 --- a/dataverse-webapp/src/main/webapp/dataset.xhtml +++ b/dataverse-webapp/src/main/webapp/dataset.xhtml @@ -132,10 +132,18 @@

- - - -

- - - - -

-
-
-

#{bundle['dataset.privateurl.absent']}

-
-
-

#{bundle['dataset.privateurl.createdSuccess']} -

- -

- #{privateUrlLink} -

-
-
-
- - - -
-
-
- -

#{bundle['dataset.privateurl.disableConfirmationText']} -

-
- - -
-
+ + + + + + diff --git a/dataverse-webapp/src/main/webapp/dataverse_header.xhtml b/dataverse-webapp/src/main/webapp/dataverse_header.xhtml index 7de27a8919..18191bacb8 100644 --- a/dataverse-webapp/src/main/webapp/dataverse_header.xhtml +++ b/dataverse-webapp/src/main/webapp/dataverse_header.xhtml @@ -443,7 +443,7 @@ -