diff --git a/doc/release-notes/12535-download-without-guestbook-response-for-preview-user2.md b/doc/release-notes/12535-download-without-guestbook-response-for-preview-user2.md new file mode 100644 index 00000000000..aabee9de5f1 --- /dev/null +++ b/doc/release-notes/12535-download-without-guestbook-response-for-preview-user2.md @@ -0,0 +1,2 @@ +## Bug ## +Preview URL users could not download files from the dataset being previewed if a guestbook was assigned to that dataset. This is now fixed. A similar issue with Locally FAIR content is also fixed. diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Access.java b/src/main/java/edu/harvard/iq/dataverse/api/Access.java index 7d12e5e8b3f..6642c120045 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Access.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Access.java @@ -12,10 +12,7 @@ import edu.harvard.iq.dataverse.authorization.DataverseRole; import edu.harvard.iq.dataverse.authorization.Permission; import edu.harvard.iq.dataverse.authorization.RoleAssignee; -import edu.harvard.iq.dataverse.authorization.users.ApiToken; -import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser; -import edu.harvard.iq.dataverse.authorization.users.GuestUser; -import edu.harvard.iq.dataverse.authorization.users.User; +import edu.harvard.iq.dataverse.authorization.users.*; import edu.harvard.iq.dataverse.dataaccess.*; import edu.harvard.iq.dataverse.datavariable.DataVariable; import edu.harvard.iq.dataverse.datavariable.VariableServiceBean; @@ -197,7 +194,7 @@ public BundleDownloadInstance datafileBundle(@Context ContainerRequestContext cr if (gbrecs != true && df.isReleased()) { // Write Guestbook record if not done previously and file is released - GuestbookResponse gbr = guestbookResponseService.initAPIGuestbookResponse(df.getOwner(), df, session, getRequestor(req.getUser())); + GuestbookResponse gbr = guestbookResponseService.initAPIGuestbookResponse(df.getOwner(), df, session, req.getUser()); guestbookResponseService.save(gbr); MakeDataCountEntry entry = new MakeDataCountEntry(uriInfo, headers, dvRequestService, df); mdcLogService.logEntry(entry); @@ -327,7 +324,7 @@ public Response datafile(@Context ContainerRequestContext crc, if (gbrecs != true && df.isReleased()){ // Write Guestbook record if not done previously and file is released - gbr = guestbookResponseService.initAPIGuestbookResponse(df.getOwner(), df, session, getRequestor(req.getUser())); + gbr = guestbookResponseService.initAPIGuestbookResponse(df.getOwner(), df, session, req.getUser()); } DownloadInfo dInfo = new DownloadInfo(df); @@ -1234,7 +1231,7 @@ private Response downloadDatafiles(ContainerRequestContext crc, String body, boo String customZipServiceUrl = settingsService.getValueForKey(SettingsServiceBean.Key.CustomZipDownloadServiceUrl); boolean useCustomZipService = customZipServiceUrl != null; - User user = getRequestor(getRequestUser(crc)); + User user = getRequestUser(crc); DataverseRequest req = createDataverseRequest(user); Boolean getOrig = false; @@ -2236,21 +2233,22 @@ public Response getUserPermissionsOnFile(@Context ContainerRequestContext crc, private boolean checkGuestbookRequiredResponse(User user, UriInfo uriInfo, DataFile df, String gbrids) throws WebApplicationException { // Check if guestbook response is required - Dataset d = df.getOwner(); - boolean required = df.getOwner().hasEnabledGuestbook() && !d.getEffectiveGuestbookEntryAtRequest(); + Dataset ds = df.getOwner(); + boolean required = ds.hasEnabledGuestbook() && !ds.getEffectiveGuestbookEntryAtRequest(); boolean wasWrittenInPost = false; if (required) { - User requestor = getRequestor(user); - if (requestor instanceof AuthenticatedUser && permissionService.userOn(requestor, df.getOwner()).has(Permission.EditDataset)) { - required = false; + checkAuthorization(user, df); + // PrivateUrlUsers are exempt from this requirement + if (user instanceof PrivateUrlUser) { + return false; } // Check if we are downloading a thumbnail image which doesn't require a guestbook response boolean imageThumb = uriInfo.getQueryParameters().containsKey("imageThumb"); if (imageThumb) { - required = false; + return false; } - if (required && gbrids != null && !gbrids.isEmpty()) { + if (gbrids != null && !gbrids.isEmpty()) { try { // verify that this id is good GuestbookResponse gbr = guestbookResponseService.findById(Long.valueOf(gbrids)); @@ -2258,7 +2256,7 @@ private boolean checkGuestbookRequiredResponse(User user, UriInfo uriInfo, DataF throw new NotFoundException("GuestbookResponse Not Found for id:" + gbrids); } Long delta = Instant.now().toEpochMilli() - gbr.getResponseTime().getTime(); - wasWrittenInPost = gbr.getDataset().getId().equals(df.getOwner().getId()) && delta <= (GUESTBOOK_RESPONSE_SIGNEDURL_TIMEOUT_MINUTES * 60000L); + wasWrittenInPost = gbr.getDataset().getId().equals(ds.getId()) && delta <= (GUESTBOOK_RESPONSE_SIGNEDURL_TIMEOUT_MINUTES * 60000L); } catch (NumberFormatException | DateTimeParseException ex) { throw new BadRequestException(ex.getMessage()); } @@ -2283,20 +2281,11 @@ private GuestbookResponse getGuestbookResponseFromBody(DataFile dataFile, String // checkAuthorization is a convenience method; it calls the boolean method // isAccessAuthorized(), the actual workhorse, and throws a 403 exception if not. - private void checkAuthorization(User initialUser, DataFile df) throws WebApplicationException { - User user = getRequestor(initialUser); - if (!isAccessAuthorized(user, df)) { + private void checkAuthorization(User requestUser, DataFile df) throws WebApplicationException { + if (!isAccessAuthorized(requestUser, df)) { throw new ForbiddenException(); } } - private User getRequestor(User user) { - // CompoundAuthMechanism should find the user by API Key/Token, Workflow, etc. And for SPA the Bearer Token - // For JSF check if CompoundAuthMechanism couldn't find the user then try to get it from the session - if (session!=null && user instanceof GuestUser) { - user = session.getUser(); - } - return user; - } private boolean isAccessAuthorized(User requestUser, DataFile df) { // First, check if the file belongs to a released Dataset version: diff --git a/src/main/java/edu/harvard/iq/dataverse/api/auth/SessionCookieAuthMechanism.java b/src/main/java/edu/harvard/iq/dataverse/api/auth/SessionCookieAuthMechanism.java index c1471c3f5b3..d53ea7476c1 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/auth/SessionCookieAuthMechanism.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/auth/SessionCookieAuthMechanism.java @@ -11,11 +11,19 @@ public class SessionCookieAuthMechanism implements AuthMechanism { @Inject DataverseSession session; + public static final String ACCESS_PATH_PREFIX = "access/"; + @Override public User findUserFromRequest(ContainerRequestContext containerRequestContext) throws WrappedAuthErrorResponse { - if (FeatureFlags.API_SESSION_AUTH.enabled()) { + if (FeatureFlags.API_SESSION_AUTH.enabled() || isAccessApi(containerRequestContext)) { return session.getUser(); } return null; } + + private boolean isAccessApi(ContainerRequestContext containerRequestContext) { + String requestPath = containerRequestContext.getUriInfo() != null ? containerRequestContext.getUriInfo().getPath() : ""; + return ("GET".equalsIgnoreCase(containerRequestContext.getMethod()) && + requestPath.startsWith(ACCESS_PATH_PREFIX)); + } }