diff --git a/auth-api/src/main/java/edu/unc/lib/boxc/auth/api/services/DatastreamPermissionUtil.java b/auth-api/src/main/java/edu/unc/lib/boxc/auth/api/services/DatastreamPermissionUtil.java
index ff69854f65..81a72d3da0 100644
--- a/auth-api/src/main/java/edu/unc/lib/boxc/auth/api/services/DatastreamPermissionUtil.java
+++ b/auth-api/src/main/java/edu/unc/lib/boxc/auth/api/services/DatastreamPermissionUtil.java
@@ -30,8 +30,8 @@ public class DatastreamPermissionUtil {
DS_PERMISSION_MAP.put(DatastreamType.ORIGINAL_FILE, Permission.viewOriginal);
DS_PERMISSION_MAP.put(DatastreamType.TECHNICAL_METADATA, Permission.viewHidden);
DS_PERMISSION_MAP.put(DatastreamType.TECHNICAL_METADATA_HISTORY, Permission.viewHidden);
- DS_PERMISSION_MAP.put(DatastreamType.THUMBNAIL_SMALL, Permission.viewMetadata);
- DS_PERMISSION_MAP.put(DatastreamType.THUMBNAIL_LARGE, Permission.viewMetadata);
+ DS_PERMISSION_MAP.put(DatastreamType.THUMBNAIL_SMALL, Permission.viewAccessCopies);
+ DS_PERMISSION_MAP.put(DatastreamType.THUMBNAIL_LARGE, Permission.viewAccessCopies);
}
private DatastreamPermissionUtil() {
diff --git a/static/js/vue-cdr-access/src/components/full_record/fileList.vue b/static/js/vue-cdr-access/src/components/full_record/fileList.vue
index d9ee7435f5..7cdfc09f17 100644
--- a/static/js/vue-cdr-access/src/components/full_record/fileList.vue
+++ b/static/js/vue-cdr-access/src/components/full_record/fileList.vue
@@ -135,7 +135,7 @@ export default {
render: (data, type, row) => {
let img;
- if ('thumbnail_url' in row) {
+ if ('thumbnail_url' in row && this.hasPermission(row,'viewAccessCopies')) {
const thumbnail_title = this.$t('full_record.thumbnail_title', { title: row.title })
img = ``;
diff --git a/static/js/vue-cdr-access/src/components/full_record/thumbnail.vue b/static/js/vue-cdr-access/src/components/full_record/thumbnail.vue
index 27c8718719..8ec1150b82 100644
--- a/static/js/vue-cdr-access/src/components/full_record/thumbnail.vue
+++ b/static/js/vue-cdr-access/src/components/full_record/thumbnail.vue
@@ -105,7 +105,7 @@ export default {
},
src() {
- if (this.objectData.thumbnail_url !== undefined) {
+ if (this.objectData.thumbnail_url !== undefined && this.hasPermission(this.objectData, 'viewAccessCopies')) {
return this.objectData.thumbnail_url;
}
diff --git a/static/js/vue-cdr-access/tests/unit/thumbnail.spec.js b/static/js/vue-cdr-access/tests/unit/thumbnail.spec.js
index 0d1f4f0f24..313c46f57f 100644
--- a/static/js/vue-cdr-access/tests/unit/thumbnail.spec.js
+++ b/static/js/vue-cdr-access/tests/unit/thumbnail.spec.js
@@ -128,12 +128,21 @@ describe('thumbnail.vue', () => {
});
});
- it('displays a thumbnail, if present', () => {
+ it('displays a thumbnail, if present and user has viewAccessCopies permissions', () => {
expect(wrapper.find('.thumbnail .thumbnail-viewer').exists()).toBe(true);
+ expect(wrapper.find('i.placeholder').exists()).toBe(false);
expect(wrapper.find('a').attributes('class'))
.toEqual('thumbnail thumbnail-size-large has_tooltip')
});
+ it('does not display a thumbnail if user does not have viewAccessCopies permissions', async () => {
+ let updatedRecordData = cloneDeep(recordData);
+ updatedRecordData.briefObject.permissions = ['viewMetadata'];
+ await wrapper.setProps({ thumbnailData: updatedRecordData });
+ expect(wrapper.find('i.placeholder').exists()).toBe(true);
+ expect(wrapper.find('.thumbnail .thumbnail-viewer').exists()).toBe(false);
+ });
+
it('displays a placeholder, if no thumbnail', async () => {
let updatedRecordData = cloneDeep(recordData);
updatedRecordData.briefObject.thumbnail_url = undefined;
diff --git a/web-common/src/main/java/edu/unc/lib/boxc/web/common/services/AccessCopiesService.java b/web-common/src/main/java/edu/unc/lib/boxc/web/common/services/AccessCopiesService.java
index 235ee9504c..c96eb0a3b0 100644
--- a/web-common/src/main/java/edu/unc/lib/boxc/web/common/services/AccessCopiesService.java
+++ b/web-common/src/main/java/edu/unc/lib/boxc/web/common/services/AccessCopiesService.java
@@ -130,12 +130,17 @@ public String getDownloadUrl(ContentObjectRecord contentObjectRecord, AccessGrou
*/
public String getThumbnailId(ContentObjectRecord contentObjectRecord, AccessGroupSet principals,
boolean checkChildren) {
+ if (!permissionsHelper.hasThumbnailAccess(principals, contentObjectRecord)) {
+ return null;
+ }
+
// Find thumbnail datastream recorded directly on the object, if present
var thumbId = DatastreamUtil.getThumbnailOwnerId(contentObjectRecord);
if (thumbId != null) {
log.debug("Found thumbnail object directly assigned to object {}", thumbId);
return thumbId;
}
+
// Don't need to check any further if object isn't a work or doesn't contain files with thumbnails
if (!ResourceType.Work.name().equals(contentObjectRecord.getResourceType())
|| contentObjectRecord.getFileFormatCategory() == null
diff --git a/web-common/src/main/java/edu/unc/lib/boxc/web/common/services/PermissionsHelper.java b/web-common/src/main/java/edu/unc/lib/boxc/web/common/services/PermissionsHelper.java
index 29218f80b4..5f1b3ba2c5 100644
--- a/web-common/src/main/java/edu/unc/lib/boxc/web/common/services/PermissionsHelper.java
+++ b/web-common/src/main/java/edu/unc/lib/boxc/web/common/services/PermissionsHelper.java
@@ -8,6 +8,7 @@
import static edu.unc.lib.boxc.model.api.DatastreamType.JP2_ACCESS_COPY;
import static edu.unc.lib.boxc.model.api.DatastreamType.MD_DESCRIPTIVE;
import static edu.unc.lib.boxc.model.api.DatastreamType.ORIGINAL_FILE;
+import static edu.unc.lib.boxc.model.api.DatastreamType.THUMBNAIL_LARGE;
import static edu.unc.lib.boxc.model.api.DatastreamType.THUMBNAIL_SMALL;
import static org.springframework.util.Assert.notNull;
@@ -57,7 +58,8 @@ public boolean hasOriginalAccess(AccessGroupSet principals, ContentObjectRecord
* @return
*/
public boolean hasThumbnailAccess(AccessGroupSet principals, ContentObjectRecord metadata) {
- return hasDatastreamAccess(principals, THUMBNAIL_SMALL, metadata);
+ return hasThumbnailPreviewAccess(principals, THUMBNAIL_SMALL, metadata) ||
+ hasThumbnailPreviewAccess(principals, THUMBNAIL_LARGE, metadata);
}
/**
@@ -107,6 +109,16 @@ public boolean hasDatastreamAccess(AccessGroupSet principals, DatastreamType dat
return accessControlService.hasAccess(metadata.getPid(), principals, permission);
}
+ public boolean hasThumbnailPreviewAccess(AccessGroupSet principals, DatastreamType datastream,
+ ContentObjectRecord metadata) {
+ notNull(principals, "Requires agent principals");
+ notNull(datastream, "Requires datastream type");
+ notNull(metadata, "Requires metadata object");
+
+ Permission permission = getPermissionForDatastream(datastream);
+ return accessControlService.hasAccess(metadata.getPid(), principals, permission);
+ }
+
private static boolean containsDatastream(ContentObjectRecord metadata, String datastream) {
return metadata.getDatastreamObjects().stream()
.anyMatch(d -> d.getName().equals(datastream));
diff --git a/web-common/src/test/java/edu/unc/lib/boxc/web/common/services/AccessCopiesServiceTest.java b/web-common/src/test/java/edu/unc/lib/boxc/web/common/services/AccessCopiesServiceTest.java
index fc17173f61..772d6fcce9 100644
--- a/web-common/src/test/java/edu/unc/lib/boxc/web/common/services/AccessCopiesServiceTest.java
+++ b/web-common/src/test/java/edu/unc/lib/boxc/web/common/services/AccessCopiesServiceTest.java
@@ -27,6 +27,7 @@
import java.util.List;
import java.util.UUID;
+import static edu.unc.lib.boxc.auth.api.Permission.viewAccessCopies;
import static edu.unc.lib.boxc.auth.api.Permission.viewOriginal;
import static edu.unc.lib.boxc.model.api.DatastreamType.ORIGINAL_FILE;
import static edu.unc.lib.boxc.model.api.DatastreamType.TECHNICAL_METADATA;
@@ -137,6 +138,7 @@ private ContentObjectSolrRecord createImgObject(ResourceType resourceType) {
List imgDatastreams = Arrays.asList(
ORIGINAL_FILE.getId() + "|image/png|file.png|png|766|urn:sha1:checksum|",
DatastreamType.THUMBNAIL_LARGE.getId() + "|image/png|thumb|png|55||",
+ DatastreamType.THUMBNAIL_SMALL.getId() + "|image/png|thumb|png|15||",
DatastreamType.JP2_ACCESS_COPY.getId() + "|image/jp2|thumb|jp2|555||");
mdObjectImg.setFileFormatCategory(Collections.singletonList(ContentCategory.image.getDisplayName()));
mdObjectImg.setFileFormatType(Collections.singletonList("image/png"));
@@ -236,6 +238,14 @@ public void primaryObjThumbnail() {
assertEquals(mdObjectImg.getId(), accessCopiesService.getThumbnailId(mdObjectImg, principals, true));
}
+ @Test
+ public void primaryObjThumbnailMetadataOnlyPermissions() {
+ hasPermissions(mdObjectImg, true, false);
+
+ assertNull(accessCopiesService.getThumbnailId(mdObjectImg, principals, false));
+ assertNull(accessCopiesService.getThumbnailId(mdObjectImg, principals, true));
+ }
+
@Test
public void noPrimaryObjThumbnailMultipleFiles() {
hasPermissions(noOriginalFileObj, true);
@@ -253,6 +263,21 @@ public void noPrimaryObjThumbnailMultipleFiles() {
assertSortType("default");
}
+ @Test
+ public void noPrimaryObjThumbnailMultipleFilesMetadataOnlyPermissions() {
+ hasPermissions(noOriginalFileObj, true, false);
+ hasPermissions(mdObjectXml, false);
+ hasPermissions(mdObjectImg, false);
+ noOriginalFileObj.setFileFormatCategory(Collections.singletonList(ContentCategory.image.getDisplayName()));
+ noOriginalFileObj.setFileFormatType(Collections.singletonList("png"));
+ populateResultList(mdObjectImg);
+ when(searchResultResponse.getResultCount()).thenReturn(2l);
+
+ assertNull(accessCopiesService.getThumbnailId(noOriginalFileObj, principals, false));
+ // Gets the ID of the specific child with a thumbnail
+ assertNull(accessCopiesService.getThumbnailId(noOriginalFileObj, principals, true));
+ }
+
@Test
public void noPrimaryObjNoThumbnail() {
hasPermissions(noOriginalFileObj, true);
@@ -273,7 +298,8 @@ public void getThumbnailIdNoPrimaryMultipleImages() {
mdObjectImg2.setId(UUID.randomUUID().toString());
var imgDatastreams = Arrays.asList(
ORIGINAL_FILE.getId() + "|image/jpg|file2.png|png|555|urn:sha1:checksum|",
- DatastreamType.THUMBNAIL_LARGE.getId() + "|image/png|thumb|png|55||");
+ DatastreamType.THUMBNAIL_LARGE.getId() + "|image/png|thumb|png|55||",
+ DatastreamType.THUMBNAIL_SMALL.getId() + "|image/png|thumb|png|15||");
mdObjectImg2.setFileFormatCategory(Collections.singletonList(ContentCategory.image.getDisplayName()));
mdObjectImg2.setFileFormatType(Collections.singletonList("png"));
mdObjectImg2.setDatastream(imgDatastreams);
@@ -294,6 +320,33 @@ public void getThumbnailIdNoPrimaryMultipleImages() {
assertSortType("default");
}
+ @Test
+ public void getThumbnailIdNoPrimaryMultipleImagesMetadataOnlyPermissions() {
+ var mdObjectImg2 = new ContentObjectSolrRecord();
+ mdObjectImg2.setResourceType(ResourceType.File.name());
+ mdObjectImg2.setId(UUID.randomUUID().toString());
+ var imgDatastreams = Arrays.asList(
+ ORIGINAL_FILE.getId() + "|image/jpg|file2.png|png|555|urn:sha1:checksum|",
+ DatastreamType.THUMBNAIL_LARGE.getId() + "|image/png|thumb|png|55||",
+ DatastreamType.THUMBNAIL_SMALL.getId() + "|image/png|thumb|png|15||");
+ mdObjectImg2.setFileFormatCategory(Collections.singletonList(ContentCategory.image.getDisplayName()));
+ mdObjectImg2.setFileFormatType(Collections.singletonList("png"));
+ mdObjectImg2.setDatastream(imgDatastreams);
+
+ hasPermissions(noOriginalFileObj, true, false);
+ hasPermissions(mdObjectImg2, true, false);
+ hasPermissions(mdObjectImg, true, false);
+ noOriginalFileObj.setFileFormatCategory(Collections.singletonList(ContentCategory.image.getDisplayName()));
+ noOriginalFileObj.setFileFormatType(Collections.singletonList("png"));
+ populateResultList(mdObjectImg2);
+ when(searchResultResponse.getResultCount()).thenReturn(2l);
+
+ assertNull(accessCopiesService.getThumbnailId(noOriginalFileObj, principals, false));
+
+ // Gets the ID of the specific child with a thumbnail
+ assertNull(accessCopiesService.getThumbnailId(noOriginalFileObj, principals, true));
+ }
+
private void assertRequestedDatastreamFilter(DatastreamType expectedType) {
var searchState = searchRequestCaptor.getValue().getSearchState();
var queryFilter = (NamedDatastreamFilter) searchState.getFilters().get(0);
@@ -366,6 +419,15 @@ public void hasViewableFilesImageWorkTest() {
private void hasPermissions(ContentObjectSolrRecord contentObject, boolean hasAccess) {
when(accessControlService.hasAccess(contentObject.getPid(), principals, viewOriginal)).thenReturn(hasAccess);
+ when(accessControlService.hasAccess(contentObject.getPid(), principals, viewAccessCopies)).thenReturn(hasAccess);
+ }
+
+ private void hasPermissions(ContentObjectSolrRecord contentObject, boolean hasAccessOriginal,
+ boolean hasAccessThumb) {
+ when(accessControlService.hasAccess(contentObject.getPid(), principals, viewOriginal))
+ .thenReturn(hasAccessOriginal);
+ when(accessControlService.hasAccess(contentObject.getPid(), principals, viewAccessCopies))
+ .thenReturn(hasAccessThumb);
}
private void populateResultList(ContentObjectRecord... objects) {
diff --git a/web-common/src/test/java/edu/unc/lib/boxc/web/common/services/PermissionsHelperTest.java b/web-common/src/test/java/edu/unc/lib/boxc/web/common/services/PermissionsHelperTest.java
index 68ec7c9622..9ca62e8454 100644
--- a/web-common/src/test/java/edu/unc/lib/boxc/web/common/services/PermissionsHelperTest.java
+++ b/web-common/src/test/java/edu/unc/lib/boxc/web/common/services/PermissionsHelperTest.java
@@ -6,6 +6,8 @@
import static edu.unc.lib.boxc.auth.api.Permission.viewOriginal;
import static edu.unc.lib.boxc.model.api.DatastreamType.JP2_ACCESS_COPY;
import static edu.unc.lib.boxc.model.api.DatastreamType.ORIGINAL_FILE;
+import static edu.unc.lib.boxc.model.api.DatastreamType.THUMBNAIL_LARGE;
+import static edu.unc.lib.boxc.model.api.DatastreamType.THUMBNAIL_SMALL;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Matchers.any;
@@ -61,7 +63,9 @@ public void init() {
mdObject.setRoleGroup(roleGroups);
List datastreams = Arrays.asList(
ORIGINAL_FILE.getId() + "|application/pdf|file.pdf|pdf|766|urn:sha1:checksum|",
- JP2_ACCESS_COPY.getId() + "|application/jp2|file.jp2|jp2|884||");
+ JP2_ACCESS_COPY.getId() + "|application/jp2|file.jp2|jp2|884||",
+ THUMBNAIL_LARGE.getId() + "|image/png|thumb|png|55||",
+ THUMBNAIL_SMALL.getId() + "|image/png|thumb|png|15||");
mdObject.setDatastream(datastreams);
principals = new AccessGroupSetImpl("group");
@@ -110,6 +114,20 @@ public void testPermitDerivativeAccess() {
assertTrue(helper.hasDatastreamAccess(principals, JP2_ACCESS_COPY, mdObject));
}
+ @Test
+ public void testAllowsThumbnailAccess() {
+ assignPermission(viewAccessCopies, true);
+
+ assertTrue(helper.hasThumbnailAccess(principals, mdObject), "Thumbnail should have full patron access");
+ }
+
+ @Test
+ public void testDisAllowsThumbnailAccess() {
+ assignPermission(viewMetadata, true);
+
+ assertFalse(helper.hasThumbnailAccess(principals, mdObject), "Thumbnail must not have full patron access");
+ }
+
@Test
public void testDenyOriginalAccess() {
assignPermission(viewOriginal, false);