diff --git a/data-model/src/main/java/org/alfresco/repo/content/ContentStore.java b/data-model/src/main/java/org/alfresco/repo/content/ContentStore.java index ccded57adb..4344a15c87 100644 --- a/data-model/src/main/java/org/alfresco/repo/content/ContentStore.java +++ b/data-model/src/main/java/org/alfresco/repo/content/ContentStore.java @@ -2,7 +2,7 @@ * #%L * Alfresco Data model classes * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -25,6 +25,8 @@ */ package org.alfresco.repo.content; +import java.util.Date; + import org.alfresco.api.AlfrescoPublicApi; import org.alfresco.service.cmr.repository.ContentAccessor; import org.alfresco.service.cmr.repository.ContentIOException; @@ -32,8 +34,8 @@ import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentStreamListener; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.DirectAccessUrl; +import org.alfresco.service.cmr.repository.NodeRef; -import java.util.Date; /** * Provides low-level retrieval of content @@ -239,6 +241,58 @@ public interface ContentStore */ public boolean delete(String contentUrl); + /** + * Checks if the store supports the retrieving of direct access URLs. + * + * @return {@code true} if direct access URLs retrieving is supported, {@code false} otherwise + */ + default boolean isContentDirectUrlEnabled() + { + return false; + } + + /** + * Checks if the store supports the retrieving of a direct access URL for the given node. + * + * @return {@code true} if direct access URLs retrieving is supported for the node, {@code false} otherwise + */ + default boolean isContentDirectUrlEnabled(NodeRef nodeRef) + { + return false; + } + + /** + * Gets a presigned URL to directly access the content. It is up to the actual store + * implementation if it can fulfil this request with an expiry time or not. + * + * @param contentUrl A content store {@code URL} + * @param attachment {@code true} if an attachment URL is requested, {@code false} for an embedded {@code URL}. + * @param fileName File name of the content + * @return A direct access {@code URL} object for the content + * @throws UnsupportedOperationException if the store is unable to provide the information + */ + default DirectAccessUrl requestContentDirectUrl(String contentUrl, boolean attachment, String fileName) + { + return requestContentDirectUrl(contentUrl, attachment, fileName, null); + } + + /** + * Gets a presigned URL to directly access the content. It is up to the actual store + * implementation if it can fulfil this request with an expiry time or not. + * + * @param contentUrl A content store {@code URL} + * @param attachment {@code true} if an attachment URL is requested, {@code false} for an embedded {@code URL}. + * @param fileName File name of the content + * @param validFor The time at which the direct access {@code URL} will expire. + * @return A direct access {@code URL} object for the content. + * @throws UnsupportedOperationException if the store is unable to provide the information + */ + default DirectAccessUrl requestContentDirectUrl(String contentUrl, boolean attachment, String fileName, Long validFor) + { + throw new UnsupportedOperationException( + "Retrieving direct access URLs is not supported by this content store."); + } + /** * Gets a presigned URL to directly access a binary content. It is up to the actual store * implementation if it can fulfil this request with an expiry time or not. @@ -248,10 +302,11 @@ public interface ContentStore * @return A direct access URL object for a binary content * @throws UnsupportedOperationException if the store is unable to provide the information */ + @Deprecated default DirectAccessUrl getDirectAccessUrl(String contentUrl, Date expiresAt) { throw new UnsupportedOperationException( - "Retrieving direct access URLs is not supported by this content store."); + "Retrieving direct access URLs is not supported by this content store."); } /** @@ -259,6 +314,7 @@ public interface ContentStore * * @return true if direct access URLs retrieving is supported, false otherwise */ + @Deprecated default boolean isDirectAccessSupported() { return false; diff --git a/data-model/src/main/java/org/alfresco/service/cmr/repository/DirectAccessUrl.java b/data-model/src/main/java/org/alfresco/service/cmr/repository/DirectAccessUrl.java index 0df9532f7a..365f441bb2 100644 --- a/data-model/src/main/java/org/alfresco/service/cmr/repository/DirectAccessUrl.java +++ b/data-model/src/main/java/org/alfresco/service/cmr/repository/DirectAccessUrl.java @@ -2,7 +2,7 @@ * #%L * Alfresco Data model classes * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -27,6 +27,7 @@ package org.alfresco.service.cmr.repository; import java.io.Serializable; import java.util.Date; +import java.util.Objects; import org.alfresco.api.AlfrescoPublicApi; @@ -36,7 +37,8 @@ public class DirectAccessUrl implements Serializable private static final long serialVersionUID = -881676208224414139L; private String contentUrl; - private Date expiresAt; + private Date expiryTime; + private boolean attachment; public String getContentUrl() { @@ -48,13 +50,38 @@ public class DirectAccessUrl implements Serializable this.contentUrl = contentUrl; } - public Date getExpiresAt() + public Date getExpiryTime() { - return expiresAt; + return expiryTime; } - public void setExpiresAt(Date expiresAt) + public void setExpiryTime(Date expiryTime) { - this.expiresAt = expiresAt; + this.expiryTime = expiryTime; + } + + public boolean isAttachment() + { + return attachment; + } + + public void setAttachment(boolean attachment) + { + this.attachment = attachment; + } + + @Override public boolean equals(Object obj) + { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + + DirectAccessUrl that = (DirectAccessUrl) obj; + return attachment == that.attachment && Objects.equals(contentUrl, + that.contentUrl) && Objects.equals(expiryTime, that.expiryTime); + } + + @Override public int hashCode() + { + return Objects.hash(contentUrl, expiryTime, attachment); } } diff --git a/repository/src/main/java/org/alfresco/repo/content/ContentServiceImpl.java b/repository/src/main/java/org/alfresco/repo/content/ContentServiceImpl.java index 86e58e6527..158306bed0 100644 --- a/repository/src/main/java/org/alfresco/repo/content/ContentServiceImpl.java +++ b/repository/src/main/java/org/alfresco/repo/content/ContentServiceImpl.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2019 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -25,12 +25,21 @@ */ package org.alfresco.repo.content; +import java.io.Serializable; +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.ContentServicePolicies.OnContentPropertyUpdatePolicy; import org.alfresco.repo.content.ContentServicePolicies.OnContentReadPolicy; import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy; import org.alfresco.repo.content.cleanup.EagerContentStoreCleaner; +import org.alfresco.repo.content.directurl.DirectAccessUrlDisabledException; +import org.alfresco.repo.content.directurl.SystemWideDirectUrlConfig; import org.alfresco.repo.content.filestore.FileContentStore; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.ClassPolicyDelegate; @@ -47,6 +56,7 @@ import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.DirectAccessUrl; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.MimetypeServiceAware; import org.alfresco.service.cmr.repository.NodeRef; @@ -62,13 +72,6 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.extensions.surf.util.I18NUtil; -import java.io.Serializable; -import java.util.Collection; -import java.util.Date; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - /** * Service implementation acting as a level of indirection between the client * and the underlying content store. @@ -82,7 +85,7 @@ import java.util.Set; */ public class ContentServiceImpl implements ContentService, ApplicationContextAware { - private static Log logger = LogFactory.getLog(ContentServiceImpl.class); + private static final Log logger = LogFactory.getLog(ContentServiceImpl.class); private DictionaryService dictionaryService; private NodeService nodeService; @@ -99,6 +102,8 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa /** Should we consider zero byte content to be the same as no content? */ private boolean ignoreEmptyContent; + private SystemWideDirectUrlConfig systemWideDirectUrlConfig; + /** * The policy component */ @@ -140,7 +145,12 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa { this.store = store; } - + + public void setSystemWideDirectUrlConfig(SystemWideDirectUrlConfig systemWideDirectUrlConfig) + { + this.systemWideDirectUrlConfig = systemWideDirectUrlConfig; + } + public void setPolicyComponent(PolicyComponent policyComponent) { this.policyComponent = policyComponent; @@ -510,23 +520,10 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa return tempStore.getWriter(ContentContext.NULL_CONTEXT); } - @Override + @Deprecated public DirectAccessUrl getDirectAccessUrl(NodeRef nodeRef, Date expiresAt) { - ContentData contentData = getContentData(nodeRef, ContentModel.PROP_CONTENT); - - // check that the URL is available - if (contentData == null || contentData.getContentUrl() == null) - { - throw new IllegalArgumentException("The supplied nodeRef " + nodeRef + " has no content."); - } - - if (store.isDirectAccessSupported()) - { - return store.getDirectAccessUrl(contentData.getContentUrl(), expiresAt); - } - - return null; + return requestContentDirectUrl(nodeRef, true, null); } /** @@ -586,4 +583,104 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa } } } + + /** + * {@inheritDoc} + */ + @Override + public boolean isContentDirectUrlEnabled() + { + return systemWideDirectUrlConfig.isEnabled() && store.isContentDirectUrlEnabled(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isContentDirectUrlEnabled(NodeRef nodeRef) + { + boolean contentDirectUrlEnabled = false; + + // TODO: update this + if (systemWideDirectUrlConfig.isEnabled()) + { + ContentData contentData = getContentData(nodeRef, ContentModel.PROP_CONTENT); + + // check that the URL is available + if (contentData == null || contentData.getContentUrl() == null) + { + throw new IllegalArgumentException("The supplied nodeRef " + nodeRef + " has no content."); + } + + contentDirectUrlEnabled = (store.isContentDirectUrlEnabled(nodeRef)); + } + + return contentDirectUrlEnabled; + } + + /** + * {@inheritDoc} + */ + public DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, boolean attachment, Long validFor) + { + if (!systemWideDirectUrlConfig.isEnabled()) + { + throw new DirectAccessUrlDisabledException("Direct access url isn't available."); + } + + String contentUrl = getContentUrl(nodeRef); + String fileName = getFileName(nodeRef); + validFor = adjustValidFor(validFor); + + DirectAccessUrl directAccessUrl = null; + if (store.isContentDirectUrlEnabled()) + { + try + { + directAccessUrl = store.requestContentDirectUrl(contentUrl, attachment, fileName, validFor); + } + catch (UnsupportedOperationException ex) + { + // expected exception + } + } + return directAccessUrl; + } + + protected String getContentUrl(NodeRef nodeRef) + { + ContentData contentData = getContentData(nodeRef, ContentModel.PROP_CONTENT); + + // check that the URL is available + if (contentData == null || contentData.getContentUrl() == null) + { + throw new IllegalArgumentException("The supplied nodeRef " + nodeRef + " has no content."); + } + + return contentData.getContentUrl(); + } + + protected String getFileName(NodeRef nodeRef) + { + String fileName = null; + + try + { + fileName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + } + catch (InvalidNodeRefException ex) + { + } + + return fileName; + } + + private Long adjustValidFor(Long validFor) + { + if (validFor == null || validFor > systemWideDirectUrlConfig.getDefaultExpiryTimeInSec()) + { + validFor = systemWideDirectUrlConfig.getDefaultExpiryTimeInSec(); + } + return validFor; + } } \ No newline at end of file diff --git a/repository/src/main/java/org/alfresco/repo/content/caching/CachingContentStore.java b/repository/src/main/java/org/alfresco/repo/content/caching/CachingContentStore.java index 5cad001984..77e7851547 100644 --- a/repository/src/main/java/org/alfresco/repo/content/caching/CachingContentStore.java +++ b/repository/src/main/java/org/alfresco/repo/content/caching/CachingContentStore.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -25,7 +25,6 @@ */ package org.alfresco.repo.content.caching; -import java.util.Date; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; @@ -41,6 +40,7 @@ import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentStreamListener; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.DirectAccessUrl; +import org.alfresco.service.cmr.repository.NodeRef; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanNameAware; @@ -103,7 +103,7 @@ public class CachingContentStore implements ContentStore, ApplicationEventPublis { eventPublisher.publishEvent(new CachingContentStoreCreatedEvent(this)); } - + @Override public boolean isContentUrlSupported(String contentUrl) { @@ -137,7 +137,7 @@ public class CachingContentStore implements ContentStore, ApplicationEventPublis /** * {@inheritDoc} *

- * For {@link #SPOOF_PROTOCOL spoofed} URLs, the URL always exists. + * For {@link FileContentStore#SPOOF_PROTOCOL spoofed} URLs, the URL always exists. */ @Override public boolean exists(String contentUrl) @@ -478,13 +478,35 @@ public class CachingContentStore implements ContentStore, ApplicationEventPublis return this.beanName; } - public boolean isDirectAccessSupported() + /** + * {@inheritDoc} + */ + public boolean isContentDirectUrlEnabled() { - return backingStore.isDirectAccessSupported(); + return backingStore.isContentDirectUrlEnabled(); } - public DirectAccessUrl getDirectAccessUrl(String contentUrl, Date expiresAt) + /** + * {@inheritDoc} + */ + public boolean isContentDirectUrlEnabled(NodeRef nodeRef) { - return backingStore.getDirectAccessUrl(contentUrl, expiresAt); + return backingStore.isContentDirectUrlEnabled(nodeRef); + } + + /** + * {@inheritDoc} + */ + public DirectAccessUrl requestContentDirectUrl(String contentUrl, boolean attachment, String fileName) + { + return backingStore.requestContentDirectUrl(contentUrl, attachment, fileName); + } + + /** + * {@inheritDoc} + */ + public DirectAccessUrl requestContentDirectUrl(String contentUrl, boolean attachment, String fileName, Long validFor) + { + return backingStore.requestContentDirectUrl(contentUrl, attachment, fileName, validFor); } } diff --git a/repository/src/main/java/org/alfresco/repo/content/directurl/DirectAccessUrlDisabledException.java b/repository/src/main/java/org/alfresco/repo/content/directurl/DirectAccessUrlDisabledException.java new file mode 100644 index 0000000000..d0702b398d --- /dev/null +++ b/repository/src/main/java/org/alfresco/repo/content/directurl/DirectAccessUrlDisabledException.java @@ -0,0 +1,44 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2021 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.content.directurl; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * Runtime exception thrown when direct access URLs are disabled. + * + * @author Sara Aspery + */ +public class DirectAccessUrlDisabledException extends AlfrescoRuntimeException +{ + + private static final long serialVersionUID = -6506082117146782993L; + + public DirectAccessUrlDisabledException(String msg) + { + super(msg); + } +} diff --git a/repository/src/main/java/org/alfresco/repo/content/replication/AggregatingContentStore.java b/repository/src/main/java/org/alfresco/repo/content/replication/AggregatingContentStore.java index 15cc9c5246..6c700e0572 100644 --- a/repository/src/main/java/org/alfresco/repo/content/replication/AggregatingContentStore.java +++ b/repository/src/main/java/org/alfresco/repo/content/replication/AggregatingContentStore.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -25,7 +25,6 @@ */ package org.alfresco.repo.content.replication; -import java.util.Date; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; @@ -41,6 +40,7 @@ import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.DirectAccessUrl; +import org.alfresco.service.cmr.repository.NodeRef; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -65,7 +65,7 @@ import org.apache.commons.logging.LogFactory; */ public class AggregatingContentStore extends AbstractContentStore { - private static Log logger = LogFactory.getLog(AggregatingContentStore.class); + private static final Log logger = LogFactory.getLog(AggregatingContentStore.class); private ContentStore primaryStore; private List secondaryStores; @@ -266,33 +266,58 @@ public class AggregatingContentStore extends AbstractContentStore } /** - * @return Returns true if at least one store supports direct access + * @return Returns {@code true} if at least one store supports direct access URLs */ - public boolean isDirectAccessSupported() + public boolean isContentDirectUrlEnabled() { // Check the primary store - boolean isDirectAccessSupported = primaryStore.isDirectAccessSupported(); + boolean isContentDirectUrlEnabled = primaryStore.isContentDirectUrlEnabled(); - if (!isDirectAccessSupported) + if (!isContentDirectUrlEnabled) { // Direct access is not supported by the primary store so we have to check the // other stores for (ContentStore store : secondaryStores) { + isContentDirectUrlEnabled = store.isContentDirectUrlEnabled(); - isDirectAccessSupported = store.isDirectAccessSupported(); - - if (isDirectAccessSupported) + if (isContentDirectUrlEnabled) { break; } } } - return isDirectAccessSupported; + return isContentDirectUrlEnabled; } - public DirectAccessUrl getDirectAccessUrl(String contentUrl, Date expiresAt) + /** + * @return Returns {@code true} if at least one store supports direct access URL for node + */ + public boolean isContentDirectUrlEnabled(NodeRef nodeRef) + { + // Check the primary store + boolean isContentDirectUrlEnabled = primaryStore.isContentDirectUrlEnabled(nodeRef); + + if (!isContentDirectUrlEnabled) + { + // Direct access is not supported by the primary store so we have to check the + // other stores + for (ContentStore store : secondaryStores) + { + isContentDirectUrlEnabled = store.isContentDirectUrlEnabled(nodeRef); + + if (isContentDirectUrlEnabled) + { + break; + } + } + } + + return isContentDirectUrlEnabled; + } + + public DirectAccessUrl requestContentDirectUrl(String contentUrl, boolean attachment, String fileName, Long validFor) { if (primaryStore == null) { @@ -312,13 +337,13 @@ public class AggregatingContentStore extends AbstractContentStore // Check the primary store try { - directAccessUrl = primaryStore.getDirectAccessUrl(contentUrl, expiresAt); + directAccessUrl = primaryStore.requestContentDirectUrl(contentUrl, attachment, fileName, validFor); } catch (UnsupportedOperationException e) { // The store does not support direct access URL directAccessUrlSupported = false; - } + } catch (UnsupportedContentUrlException e) { // The store can't handle the content URL @@ -335,7 +360,7 @@ public class AggregatingContentStore extends AbstractContentStore { try { - directAccessUrl = store.getDirectAccessUrl(contentUrl, expiresAt); + directAccessUrl = store.requestContentDirectUrl(contentUrl, attachment, fileName, validFor); } catch (UnsupportedOperationException e) { diff --git a/repository/src/main/java/org/alfresco/service/cmr/repository/ContentService.java b/repository/src/main/java/org/alfresco/service/cmr/repository/ContentService.java index 81622ff41a..ce73035374 100644 --- a/repository/src/main/java/org/alfresco/service/cmr/repository/ContentService.java +++ b/repository/src/main/java/org/alfresco/service/cmr/repository/ContentService.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2019 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -25,13 +25,13 @@ */ package org.alfresco.service.cmr.repository; +import java.util.Date; + import org.alfresco.api.AlfrescoPublicApi; import org.alfresco.service.Auditable; import org.alfresco.service.cmr.dictionary.InvalidTypeException; import org.alfresco.service.namespace.QName; -import java.util.Date; - /** * Provides methods for accessing and transforming content. *

@@ -168,6 +168,48 @@ public interface ContentService * @return A direct access URL object for a binary content or returns null if not supported * @throws IllegalArgumentException if there is no binary content for the node */ + @Deprecated @Auditable(parameters = {"nodeRef", "expiresAt"}) public DirectAccessUrl getDirectAccessUrl(NodeRef nodeRef, Date expiresAt); + + /** + * Checks if the system and at least one store supports the retrieving of direct access URLs. + * + * @return {@code true} if direct access URLs retrieving is supported, {@code false} otherwise + */ + boolean isContentDirectUrlEnabled(); + + /** + * Checks if the system and store supports the retrieving of a direct access {@code URL} for the given node. + * + * @return {@code true} if direct access URLs retrieving is supported for the node, {@code false} otherwise + */ + boolean isContentDirectUrlEnabled(NodeRef nodeRef); + + /** + * Gets a presigned URL to directly access the content. It is up to the actual store + * implementation if it can fulfil this request with an expiry time or not. + * + * @param nodeRef Node ref for which to obtain the direct access {@code URL}. + * @param attachment {@code true} if an attachment URL is requested, {@code false} for an embedded {@code URL}. + * @return A direct access {@code URL} object for the content. + * @throws UnsupportedOperationException if the store is unable to provide the information. + */ + default DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, boolean attachment) + { + return requestContentDirectUrl(nodeRef, attachment, null); + } + + /** + * Gets a presigned URL to directly access the content. It is up to the actual store + * implementation if it can fulfil this request with an expiry time or not. + * + * @param nodeRef Node ref for which to obtain the direct access {@code URL}. + * @param attachment {@code true} if an attachment URL is requested, {@code false} for an embedded {@code URL}. + * @param validFor The time at which the direct access {@code URL} will expire. + * @return A direct access {@code URL} object for the content. + * @throws UnsupportedOperationException if the store is unable to provide the information. + */ + @Auditable(parameters = {"nodeRef", "validFor"}) + DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, boolean attachment, Long validFor); } diff --git a/repository/src/main/resources/alfresco/content-services-context.xml b/repository/src/main/resources/alfresco/content-services-context.xml index ab02159381..a086db2e41 100644 --- a/repository/src/main/resources/alfresco/content-services-context.xml +++ b/repository/src/main/resources/alfresco/content-services-context.xml @@ -161,6 +161,9 @@ ${policy.content.update.ignoreEmpty} + + + diff --git a/repository/src/main/resources/alfresco/repository.properties b/repository/src/main/resources/alfresco/repository.properties index 4979b06b90..b95cbf72ae 100644 --- a/repository/src/main/resources/alfresco/repository.properties +++ b/repository/src/main/resources/alfresco/repository.properties @@ -1291,7 +1291,7 @@ connector.s3.directAccessUrl.maxExpiryTimeInSec=300 # Configure the REST API configuration settings for direct access urls. # # Controls whether direct access url requests via the REST API are enabled. -restApi.directAccessUrl.enabled=true +restApi.directAccessUrl.enabled=false # Sets the expiry time for all the direct access urls requested via a REST call. # Its value cannot exceed the system-wide max expiry time configuration, it can only be equal or lower (REST API DAUs # disabled otherwise). diff --git a/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java b/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java index 2d8cc48e74..7836b7cbec 100644 --- a/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java +++ b/repository/src/test/java/org/alfresco/AllUnitTestsSuite.java @@ -180,6 +180,7 @@ import org.junit.runners.Suite; org.alfresco.repo.audit.AuditableAnnotationTest.class, org.alfresco.repo.audit.PropertyAuditFilterTest.class, org.alfresco.repo.audit.access.NodeChangeTest.class, + org.alfresco.repo.content.ContentServiceImplUnitTest.class, org.alfresco.repo.content.directurl.SystemWideDirectUrlConfigUnitTest.class, org.alfresco.repo.content.directurl.ContentStoreDirectUrlConfigUnitTest.class, org.alfresco.repo.content.LimitedStreamCopierTest.class, diff --git a/repository/src/test/java/org/alfresco/repo/content/ContentServiceImplUnitTest.java b/repository/src/test/java/org/alfresco/repo/content/ContentServiceImplUnitTest.java new file mode 100644 index 0000000000..63e6530715 --- /dev/null +++ b/repository/src/test/java/org/alfresco/repo/content/ContentServiceImplUnitTest.java @@ -0,0 +1,160 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2021 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.content; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.directurl.DirectAccessUrlDisabledException; +import org.alfresco.repo.content.directurl.SystemWideDirectUrlConfig; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.DirectAccessUrl; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +/** + * Unit tests for content service implementation. + * + * @author Sara Aspery + */ +public class ContentServiceImplUnitTest +{ + private static final Boolean ENABLED = Boolean.TRUE; + private static final Boolean DISABLED = Boolean.FALSE; + + private static final Long SYS_DEFAULT_EXPIRY_TIME_IN_SECS = 30L; + private static final Long SYS_MAX_EXPIRY_TIME_IN_SECS = 300L; + + private static final NodeRef NODE_REF = new NodeRef("content://Node/Ref"); + + @InjectMocks + private ContentServiceImpl contentService; + + @Mock + private ContentStore mockContentStore; + + @Mock + private NodeService mockNodeService; + + @Mock + private ContentData mockContentData; + + @Before + public void setup() + { + openMocks(this); + when(mockNodeService.getProperty(NODE_REF, ContentModel.PROP_CONTENT)).thenReturn(mockContentData); + when(mockContentData.getContentUrl()).thenReturn("someContentUrl"); + when(mockNodeService.getProperty(NODE_REF, ContentModel.PROP_NAME)).thenReturn("someFilename"); + } + + @Test + public void testIsContentDirectUrlEnabled_SystemWideIsDisabled() + { + setupSystemWideDirectAccessConfig(DISABLED); + assertFalse("Expected contentDirectUrl to be disabled", contentService.isContentDirectUrlEnabled()); + verify(mockContentStore, never()).isContentDirectUrlEnabled(); + } + + @Test + public void testIsContentDirectUrlEnabled_SystemWideIsEnabledButStoreIsDisabled() + { + setupSystemWideDirectAccessConfig(ENABLED); + when(mockContentStore.isContentDirectUrlEnabled()).thenReturn(DISABLED); + assertFalse("Expected contentDirectUrl to be disabled", contentService.isContentDirectUrlEnabled()); + } + + @Test + public void testIsContentDirectUrlEnabled_SystemWideIsEnabledAndStoreIsEnabled() + { + setupSystemWideDirectAccessConfig(ENABLED); + when(mockContentStore.isContentDirectUrlEnabled()).thenReturn(ENABLED); + assertTrue("Expected contentDirectUrl to be enabled", contentService.isContentDirectUrlEnabled()); + } + + @Test + public void testRequestContentDirectUrl_SystemWideIsDisabled() + { + setupSystemWideDirectAccessConfig(DISABLED); + try + { + contentService.requestContentDirectUrl(NODE_REF, true, 20L); + fail("Expected DirectAccessUrlDisabledException"); + } + catch (DirectAccessUrlDisabledException ex) + { + verify(mockContentStore, never()).isContentDirectUrlEnabled(); + } + } + + @Test + public void testRequestContentDirectUrl_SystemWideIsEnabledButStoreIsDisabled() + { + setupSystemWideDirectAccessConfig(ENABLED); + when(mockContentStore.isContentDirectUrlEnabled()).thenReturn(DISABLED); + + DirectAccessUrl directAccessUrl = contentService.requestContentDirectUrl(NODE_REF, true, 20L); + assertNull(directAccessUrl); + verify(mockContentStore, never()).requestContentDirectUrl(anyString(), eq(true), anyString(), anyLong()); + } + + @Test + public void testRequestContentDirectUrl_StoreIsEnabledButNotImplemented() + { + setupSystemWideDirectAccessConfig(ENABLED); + when(mockContentStore.isContentDirectUrlEnabled()).thenReturn(ENABLED); + + DirectAccessUrl directAccessUrl = contentService.requestContentDirectUrl(NODE_REF, true, 20L); + assertNull(directAccessUrl); + verify(mockContentStore, times(1)).requestContentDirectUrl(anyString(), eq(true), anyString(), anyLong()); + } + + /* Helper method to set system-wide direct access url configuration settings */ + private void setupSystemWideDirectAccessConfig(Boolean isEnabled) + { + SystemWideDirectUrlConfig sysConfig = new SystemWideDirectUrlConfig(); + sysConfig.setEnabled(isEnabled); + sysConfig.setDefaultExpiryTimeInSec(SYS_DEFAULT_EXPIRY_TIME_IN_SECS); + sysConfig.setMaxExpiryTimeInSec(SYS_MAX_EXPIRY_TIME_IN_SECS); + sysConfig.validate(); + contentService.setSystemWideDirectUrlConfig(sysConfig); + } +} diff --git a/repository/src/test/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java b/repository/src/test/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java index ffac9eea8b..f35d0b08d4 100644 --- a/repository/src/test/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java +++ b/repository/src/test/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java @@ -35,6 +35,7 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; @@ -493,10 +494,10 @@ public class CachingContentStoreTest @Test public void isDirectAccessSupported() { - assertFalse(cachingStore.isDirectAccessSupported()); + assertFalse(cachingStore.isContentDirectUrlEnabled()); - when(backingStore.isDirectAccessSupported()).thenReturn(true); - assertTrue(cachingStore.isDirectAccessSupported()); + when(backingStore.isContentDirectUrlEnabled()).thenReturn(true); + assertTrue(cachingStore.isContentDirectUrlEnabled()); } @Test @@ -504,8 +505,8 @@ public class CachingContentStoreTest { try { - when(backingStore.getDirectAccessUrl(anyString(), any())).thenThrow(new UnsupportedOperationException()); - cachingStore.getDirectAccessUrl("url", null); + when(backingStore.requestContentDirectUrl(anyString(), eq(true), anyString(), anyLong())).thenThrow(new UnsupportedOperationException()); + cachingStore.requestContentDirectUrl("url", true,"someFile", 30L); fail(); } catch (UnsupportedOperationException e) @@ -517,7 +518,7 @@ public class CachingContentStoreTest @Test public void getDirectAccessUrl() { - when(backingStore.getDirectAccessUrl(anyString(), any())).thenReturn(new DirectAccessUrl()); - cachingStore.getDirectAccessUrl("url", null); + when(backingStore.requestContentDirectUrl(anyString(), eq(true), anyString(), anyLong())).thenReturn(new DirectAccessUrl()); + cachingStore.requestContentDirectUrl("url", true,"someFile", 30L); } } diff --git a/repository/src/test/java/org/alfresco/repo/content/replication/AggregatingContentStoreTest.java b/repository/src/test/java/org/alfresco/repo/content/replication/AggregatingContentStoreTest.java index ecfaf17823..585843f155 100644 --- a/repository/src/test/java/org/alfresco/repo/content/replication/AggregatingContentStoreTest.java +++ b/repository/src/test/java/org/alfresco/repo/content/replication/AggregatingContentStoreTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -56,9 +56,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** @@ -191,7 +189,7 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes } @Test - public void testIsDirectAccessSupported() + public void testIsContentDirectUrlEnabled() { // Create the aggregating store AggregatingContentStore aggStore = new AggregatingContentStore(); @@ -199,21 +197,21 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes aggStore.setSecondaryStores(List.of(secondaryStoreMock)); // By default it is unsupported - assertFalse(aggStore.isDirectAccessSupported()); + assertFalse(aggStore.isContentDirectUrlEnabled()); // Supported if at least one store supports direct access { - when(primaryStoreMock.isDirectAccessSupported()).thenReturn(false); - when(secondaryStoreMock.isDirectAccessSupported()).thenReturn(true); - assertTrue(aggStore.isDirectAccessSupported()); + when(primaryStoreMock.isContentDirectUrlEnabled()).thenReturn(false); + when(secondaryStoreMock.isContentDirectUrlEnabled()).thenReturn(true); + assertTrue(aggStore.isContentDirectUrlEnabled()); - when(primaryStoreMock.isDirectAccessSupported()).thenReturn(true); - when(secondaryStoreMock.isDirectAccessSupported()).thenReturn(true); - assertTrue(aggStore.isDirectAccessSupported()); + when(primaryStoreMock.isContentDirectUrlEnabled()).thenReturn(true); + when(secondaryStoreMock.isContentDirectUrlEnabled()).thenReturn(true); + assertTrue(aggStore.isContentDirectUrlEnabled()); - when(primaryStoreMock.isDirectAccessSupported()).thenReturn(true); - when(secondaryStoreMock.isDirectAccessSupported()).thenReturn(false); - assertTrue(aggStore.isDirectAccessSupported()); + when(primaryStoreMock.isContentDirectUrlEnabled()).thenReturn(true); + when(secondaryStoreMock.isContentDirectUrlEnabled()).thenReturn(false); + assertTrue(aggStore.isContentDirectUrlEnabled()); } } @@ -229,15 +227,15 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes UnsupportedContentUrlException unsupportedContentUrlExc = new UnsupportedContentUrlException(aggStore, ""); // By default it is unsupported - DirectAccessUrl directAccessUrl = aggStore.getDirectAccessUrl("url", null); + DirectAccessUrl directAccessUrl = aggStore.requestContentDirectUrl("url", true, "anyfilename", 30L); assertNull(directAccessUrl); // Direct access not supported try { - when(primaryStoreMock.getDirectAccessUrl(eq("urlDANotSupported"), any())).thenThrow(unsupportedExc); - when(secondaryStoreMock.getDirectAccessUrl(eq("urlDANotSupported"), any())).thenThrow(unsupportedExc); - aggStore.getDirectAccessUrl("urlDANotSupported", null); + when(primaryStoreMock.requestContentDirectUrl(eq("urlDANotSupported"), any(), any(), any())).thenThrow(unsupportedExc); + when(secondaryStoreMock.requestContentDirectUrl(eq("urlDANotSupported"), any(), any(), any())).thenThrow(unsupportedExc); + aggStore.requestContentDirectUrl(eq("urlDANotSupported"), true, "anyfilename", 30L); fail(); } catch (UnsupportedOperationException e) @@ -247,9 +245,9 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes try { - when(primaryStoreMock.getDirectAccessUrl(eq("urlDANotSupported"), any())).thenThrow(unsupportedContentUrlExc); - when(secondaryStoreMock.getDirectAccessUrl(eq("urlDANotSupported"), any())).thenThrow(unsupportedExc); - aggStore.getDirectAccessUrl("urlDANotSupported", null); + when(primaryStoreMock.requestContentDirectUrl(eq("urlDANotSupported"), any(), any(), any())).thenThrow(unsupportedContentUrlExc); + when(secondaryStoreMock.requestContentDirectUrl(eq("urlDANotSupported"), any(), any(), any())).thenThrow(unsupportedExc); + aggStore.requestContentDirectUrl("urlDANotSupported", true, "anyfilename", 30L); fail(); } catch (UnsupportedOperationException e) @@ -259,9 +257,9 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes try { - when(primaryStoreMock.getDirectAccessUrl(eq("urlDANotSupported"), any())).thenThrow(unsupportedExc); - when(secondaryStoreMock.getDirectAccessUrl(eq("urlDANotSupported"), any())).thenThrow(unsupportedContentUrlExc); - aggStore.getDirectAccessUrl("urlDANotSupported", null); + when(primaryStoreMock.requestContentDirectUrl(eq("urlDANotSupported"), any(), any(), any())).thenThrow(unsupportedExc); + when(secondaryStoreMock.requestContentDirectUrl(eq("urlDANotSupported"), any(), any(), any())).thenThrow(unsupportedContentUrlExc); + aggStore.requestContentDirectUrl("urlDANotSupported", true, "anyfilename", 30L); fail(); } catch (UnsupportedOperationException e) @@ -272,9 +270,9 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes // Content url not supported try { - when(primaryStoreMock.getDirectAccessUrl(eq("urlNotSupported"), any())).thenThrow(unsupportedContentUrlExc); - when(secondaryStoreMock.getDirectAccessUrl(eq("urlNotSupported"), any())).thenThrow(unsupportedContentUrlExc); - aggStore.getDirectAccessUrl("urlNotSupported", null); + when(primaryStoreMock.requestContentDirectUrl(eq("urlNotSupported"), any(), any(), any())).thenThrow(unsupportedContentUrlExc); + when(secondaryStoreMock.requestContentDirectUrl(eq("urlNotSupported"), any(), any(), any())).thenThrow(unsupportedContentUrlExc); + aggStore.requestContentDirectUrl("urlNotSupported", true, "anyfilename", 30L); fail(); } catch (UnsupportedContentUrlException e) @@ -282,31 +280,31 @@ public class AggregatingContentStoreTest extends AbstractWritableContentStoreTes // Expected } - when(primaryStoreMock.getDirectAccessUrl(eq("urlPriSupported"), any())).thenReturn(new DirectAccessUrl()); - when(secondaryStoreMock.getDirectAccessUrl(eq("urlPriSupported"), any())).thenThrow(unsupportedExc); - directAccessUrl = aggStore.getDirectAccessUrl("urlPriSupported", null); + when(primaryStoreMock.requestContentDirectUrl(eq("urlPriSupported"), any(), any(), any())).thenReturn(new DirectAccessUrl()); + when(secondaryStoreMock.requestContentDirectUrl(eq("urlPriSupported"), any(), any(), any())).thenThrow(unsupportedExc); + directAccessUrl = aggStore.requestContentDirectUrl("urlPriSupported", true, "anyfilename", 30L); assertNotNull(directAccessUrl); - when(primaryStoreMock.getDirectAccessUrl(eq("urlPriSupported"), any())).thenReturn(new DirectAccessUrl()); - when(secondaryStoreMock.getDirectAccessUrl(eq("urlPriSupported"), any())).thenThrow(unsupportedContentUrlExc); - directAccessUrl = aggStore.getDirectAccessUrl("urlPriSupported", null); + when(primaryStoreMock.requestContentDirectUrl(eq("urlPriSupported"), any(), any(), any())).thenReturn(new DirectAccessUrl()); + when(secondaryStoreMock.requestContentDirectUrl(eq("urlPriSupported"), any(), any(), any())).thenThrow(unsupportedContentUrlExc); + directAccessUrl = aggStore.requestContentDirectUrl("urlPriSupported", true, "anyfilename", 30L); assertNotNull(directAccessUrl); - when(primaryStoreMock.getDirectAccessUrl(eq("urlSecSupported"), any())).thenThrow(unsupportedExc); - when(secondaryStoreMock.getDirectAccessUrl(eq("urlSecSupported"), any())).thenReturn(new DirectAccessUrl()); - directAccessUrl = aggStore.getDirectAccessUrl("urlSecSupported", null); + when(primaryStoreMock.requestContentDirectUrl(eq("urlSecSupported"), any(), any(), any())).thenThrow(unsupportedExc); + when(secondaryStoreMock.requestContentDirectUrl(eq("urlSecSupported"), any(), any(), any())).thenReturn(new DirectAccessUrl()); + directAccessUrl = aggStore.requestContentDirectUrl("urlSecSupported", true, "anyfilename", 30L); assertNotNull(directAccessUrl); - when(primaryStoreMock.getDirectAccessUrl(eq("urlSecSupported"), any())).thenThrow(unsupportedContentUrlExc); - when(secondaryStoreMock.getDirectAccessUrl(eq("urlSecSupported"), any())).thenReturn(new DirectAccessUrl()); - directAccessUrl = aggStore.getDirectAccessUrl("urlSecSupported", null); + when(primaryStoreMock.requestContentDirectUrl(eq("urlSecSupported"), any(), any(), any())).thenThrow(unsupportedContentUrlExc); + when(secondaryStoreMock.requestContentDirectUrl(eq("urlSecSupported"), any(), any(), any())).thenReturn(new DirectAccessUrl()); + directAccessUrl = aggStore.requestContentDirectUrl("urlSecSupported", true, "anyfilename", 30L); assertNotNull(directAccessUrl); - when(primaryStoreMock.getDirectAccessUrl(eq("urlPriSupported"), any())).thenReturn(new DirectAccessUrl()); - when(secondaryStoreMock.getDirectAccessUrl(eq("urlSecSupported"), any())).thenReturn(new DirectAccessUrl()); - directAccessUrl = aggStore.getDirectAccessUrl("urlPriSupported", null); + when(primaryStoreMock.requestContentDirectUrl(eq("urlPriSupported"), any(), any(), any())).thenReturn(new DirectAccessUrl()); + when(secondaryStoreMock.requestContentDirectUrl(eq("urlSecSupported"), any(), any(), any())).thenReturn(new DirectAccessUrl()); + directAccessUrl = aggStore.requestContentDirectUrl("urlPriSupported", true, "anyfilename", 30L); assertNotNull(directAccessUrl); - directAccessUrl = aggStore.getDirectAccessUrl("urlSecSupported", null); + directAccessUrl = aggStore.requestContentDirectUrl("urlSecSupported", true, "anyfilename", 30L); assertNotNull(directAccessUrl); } } diff --git a/repository/src/test/java/org/alfresco/repo/version/ContentServiceImplTest.java b/repository/src/test/java/org/alfresco/repo/version/ContentServiceImplTest.java index 5cebb8acaf..5423fd3077 100644 --- a/repository/src/test/java/org/alfresco/repo/version/ContentServiceImplTest.java +++ b/repository/src/test/java/org/alfresco/repo/version/ContentServiceImplTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -25,18 +25,16 @@ */ package org.alfresco.repo.version; -import org.alfresco.error.AlfrescoRuntimeException; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.openMocks; + import org.alfresco.model.ContentModel; import org.alfresco.repo.content.ContentStore; -import org.alfresco.repo.content.EmptyContentReader; -import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.repo.content.MimetypeMapTest; +import org.alfresco.repo.content.directurl.SystemWideDirectUrlConfig; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.cmr.repository.NoTransformerException; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.TransformationOptions; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.namespace.QName; import org.alfresco.test_category.OwnJVMTestsCategory; @@ -44,10 +42,11 @@ import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.mockito.InjectMocks; +import org.mockito.Mock; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.transaction.annotation.Transactional; -import java.util.Date; /** * Tests for getting content readers and writers. @@ -57,7 +56,9 @@ import java.util.Date; @Category(OwnJVMTestsCategory.class) @Transactional public class ContentServiceImplTest extends BaseVersionStoreTest -{ +{ + private static final Boolean ENABLED = Boolean.TRUE; + /** * Test content data */ @@ -66,9 +67,14 @@ public class ContentServiceImplTest extends BaseVersionStoreTest /** * The version content store */ + @InjectMocks private ContentService contentService; + private ContentStore contentStore; + @Mock + private SystemWideDirectUrlConfig mockSystemWideDirectUrlConfig; + @Before public void before() throws Exception { @@ -139,15 +145,17 @@ public class ContentServiceImplTest extends BaseVersionStoreTest } @Test - public void testWhenGetDirectAccessUrlIsNotSupported() + public void testWhenRequestContentDirectUrlIsNotSupported() { - assertFalse(contentStore.isDirectAccessSupported()); + openMocks(this); + when(mockSystemWideDirectUrlConfig.isEnabled()).thenReturn(ENABLED); + when(mockSystemWideDirectUrlConfig.getDefaultExpiryTimeInSec()).thenReturn(30L); + when(mockSystemWideDirectUrlConfig.getMaxExpiryTimeInSec()).thenReturn(300L); + + assertFalse(contentStore.isContentDirectUrlEnabled()); // Set the presigned URL to expire after one minute. - Date expiresAt = new Date(); - long expTimeMillis = expiresAt.getTime(); - expTimeMillis += 1000 * 60; - expiresAt.setTime(expTimeMillis); + Long validFor = 60L; try { @@ -155,7 +163,7 @@ public class ContentServiceImplTest extends BaseVersionStoreTest NodeRef nodeRef = this.dbNodeService .createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}MyNoContentNode"), TEST_TYPE_QNAME, this.nodeProperties).getChildRef(); - assertEquals(null, contentService.getDirectAccessUrl(nodeRef, expiresAt)); + assertNull(contentService.requestContentDirectUrl(nodeRef, true, validFor)); fail("nodeRef has no content"); } catch (IllegalArgumentException e) @@ -165,7 +173,7 @@ public class ContentServiceImplTest extends BaseVersionStoreTest try { - assertEquals(null, contentService.getDirectAccessUrl(null, null)); + assertNull(contentService.requestContentDirectUrl(null, true, null)); fail("nodeRef is null"); } catch (IllegalArgumentException e) @@ -176,7 +184,7 @@ public class ContentServiceImplTest extends BaseVersionStoreTest // Create a node with content NodeRef nodeRef = createNewVersionableNode(); - assertEquals(null, contentService.getDirectAccessUrl(nodeRef, null)); - assertEquals(null, contentService.getDirectAccessUrl(nodeRef, expiresAt)); + assertNull(contentService.requestContentDirectUrl(nodeRef, true, null)); + assertNull(contentService.requestContentDirectUrl(nodeRef, true, validFor)); } }