diff --git a/config/alfresco/web-publishing-context.xml b/config/alfresco/web-publishing-context.xml index daa5a314b6..2a6b42b1c9 100644 --- a/config/alfresco/web-publishing-context.xml +++ b/config/alfresco/web-publishing-context.xml @@ -66,6 +66,7 @@ + @@ -75,6 +76,7 @@ + diff --git a/source/java/org/alfresco/repo/publishing/AbstractPublishingIntegrationTest.java b/source/java/org/alfresco/repo/publishing/AbstractPublishingIntegrationTest.java index e1b18ebd26..83387544f5 100644 --- a/source/java/org/alfresco/repo/publishing/AbstractPublishingIntegrationTest.java +++ b/source/java/org/alfresco/repo/publishing/AbstractPublishingIntegrationTest.java @@ -62,8 +62,6 @@ public abstract class AbstractPublishingIntegrationTest extends BaseSpringTest protected Environment environment; protected NodeRef docLib; - protected UserTransaction transaction; - @Override @Before public void onSetUp() throws Exception @@ -76,10 +74,6 @@ public abstract class AbstractPublishingIntegrationTest extends BaseSpringTest this.fileFolderService = serviceRegistry.getFileFolderService(); this.nodeService = serviceRegistry.getNodeService(); - transaction = serviceRegistry.getTransactionService().getUserTransaction(); - transaction.begin(); - transaction.setRollbackOnly(); - this.siteId = GUID.generate(); siteService.createSite("test", siteId, "Site created by publishing test", @@ -89,30 +83,21 @@ public abstract class AbstractPublishingIntegrationTest extends BaseSpringTest this.environment = rootObject.getEnvironment(); this.queue = rootObject.getPublishingQueue(); + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); } @After public void onTearDown() throws Exception { AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); - siteService.deleteSite(siteId); try { - if (transaction.getStatus() == Status.STATUS_MARKED_ROLLBACK) - { - transaction.rollback(); - } - else - { - transaction.commit(); - } + siteService.deleteSite(siteId); } - catch (Exception e) + finally { - e.printStackTrace(); - throw new RuntimeException(e); + super.onTearDown(); } - super.onTearDown(); } protected ChannelType mockChannelType() diff --git a/source/java/org/alfresco/repo/publishing/MutablePublishingPackageImpl.java b/source/java/org/alfresco/repo/publishing/MutablePublishingPackageImpl.java index f6cdeabb94..8767d02f5e 100644 --- a/source/java/org/alfresco/repo/publishing/MutablePublishingPackageImpl.java +++ b/source/java/org/alfresco/repo/publishing/MutablePublishingPackageImpl.java @@ -26,6 +26,8 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.transfer.manifest.TransferManifestNodeFactory; import org.alfresco.repo.transfer.manifest.TransferManifestNormalNode; import org.alfresco.service.cmr.publishing.MutablePublishingPackage; @@ -68,7 +70,20 @@ public class MutablePublishingPackageImpl implements MutablePublishingPackage /** * {@inheritDoc} */ - public void addNodesToPublish(Collection nodesToAdd) + public void addNodesToPublish(final Collection nodesToAdd) + { + AuthenticationUtil.runAs(new RunAsWork() + { + public Void doWork() throws Exception + { + versionNodes(nodesToAdd); + return null; + } + }, AuthenticationUtil.getSystemUserName()); + nodesToPublish.addAll(nodesToAdd); + } + + private void versionNodes(Collection nodesToAdd) { for (NodeRef nodeRef : nodesToAdd) { @@ -85,7 +100,6 @@ public class MutablePublishingPackageImpl implements MutablePublishingPackage entryMap.put(nodeRef, publishingPackage); } } - nodesToPublish.addAll(nodesToAdd); } /** diff --git a/source/java/org/alfresco/repo/publishing/PublishingEventHelper.java b/source/java/org/alfresco/repo/publishing/PublishingEventHelper.java index a5663d6999..842fefeacc 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingEventHelper.java +++ b/source/java/org/alfresco/repo/publishing/PublishingEventHelper.java @@ -59,6 +59,7 @@ import java.util.TimeZone; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.node.NodeUtils; +import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.workflow.WorkflowModel; import org.alfresco.service.cmr.publishing.PublishingEvent; import org.alfresco.service.cmr.publishing.PublishingEventFilter; @@ -72,6 +73,8 @@ import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.workflow.WorkflowDefinition; import org.alfresco.service.cmr.workflow.WorkflowPath; import org.alfresco.service.cmr.workflow.WorkflowService; @@ -98,6 +101,7 @@ public class PublishingEventHelper private ContentService contentService; private WorkflowService workflowService; private PublishingPackageSerializer serializer; + private PermissionService permissionService; private String workflowEngineId; @@ -143,6 +147,14 @@ public class PublishingEventHelper this.serializer = serializer; } + /** + * @param permissionService the permissionService to set + */ + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + public PublishingEvent getPublishingEvent(NodeRef eventNode) throws AlfrescoRuntimeException { if(eventNode == null) @@ -198,6 +210,12 @@ public class PublishingEventHelper public NodeRef createNode(NodeRef queueNode, PublishingPackage publishingPackage, String channelId, Calendar schedule, String comment, StatusUpdate statusUpdate) throws Exception { + checkChannelAccess(channelId); + if(statusUpdate != null && isEmpty(statusUpdate.getChannelIds())==false ) + for (String statusChannelId : statusUpdate.getChannelIds()) + { + checkChannelAccess(statusChannelId); + } if (schedule == null) { schedule = Calendar.getInstance(); @@ -214,6 +232,16 @@ public class PublishingEventHelper return eventNode; } + private void checkChannelAccess(String channelId) + { + NodeRef channelNode = new NodeRef(channelId); + AccessStatus accessStatus = permissionService.hasPermission(channelNode, PermissionService.ADD_CHILDREN); + if(AccessStatus.ALLOWED != accessStatus) + { + throw new AccessDeniedException("You do not have access to channel: " + channelId); + } + } + private Map buildPublishingEventProperties(PublishingPackage publishingPackage, String channelId, Calendar schedule, String comment, StatusUpdate statusUpdate, String name) { diff --git a/source/java/org/alfresco/repo/publishing/PublishingEventHelperTest.java b/source/java/org/alfresco/repo/publishing/PublishingEventHelperTest.java index 6e1a1b7f9d..fa20244e3f 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingEventHelperTest.java +++ b/source/java/org/alfresco/repo/publishing/PublishingEventHelperTest.java @@ -32,6 +32,7 @@ import static org.alfresco.repo.publishing.PublishingModel.PROP_PUBLISHING_EVENT import static org.alfresco.repo.publishing.PublishingModel.TYPE_PUBLISHING_EVENT; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyMap; +import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -59,6 +60,8 @@ import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; import org.junit.Test; import org.junit.runner.RunWith; @@ -84,6 +87,9 @@ public class PublishingEventHelperTest @Resource(name="ContentService") ContentService contentService; + @Resource(name="PermissionService") + PermissionService permissionService; + @Test public void testGetPublishingEventNode() throws Exception { @@ -168,6 +174,7 @@ public class PublishingEventHelperTest @Test public void testCreateNode() throws Exception { + when(permissionService.hasPermission(any(NodeRef.class), anyString())).thenReturn(AccessStatus.ALLOWED); // Mock serializer since this behaviour is already tested in PublishingPackageSerializerTest. ContentWriter writer = mock(ContentWriter.class); when(contentService.getWriter(any(NodeRef.class), eq(PROP_PUBLISHING_EVENT_PAYLOAD), eq(true))) @@ -186,7 +193,7 @@ public class PublishingEventHelperTest Map entires = Collections.emptyMap(); PublishingPackage pckg = new PublishingPackageImpl(entires); - String channelName = "The channel"; + String channelName = "test://channel/id"; Calendar schedule = Calendar.getInstance(); String comment = "The comment"; diff --git a/source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java b/source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java index 4e6d62531b..f8d7299139 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java +++ b/source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java @@ -32,8 +32,12 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.security.person.TestPersonManager; +import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.publishing.MutablePublishingPackage; import org.alfresco.service.cmr.publishing.PublishingEvent; import org.alfresco.service.cmr.publishing.PublishingPackage; @@ -41,11 +45,17 @@ import org.alfresco.service.cmr.publishing.PublishingPackageEntry; import org.alfresco.service.cmr.publishing.PublishingService; import org.alfresco.service.cmr.publishing.Status; import org.alfresco.service.cmr.publishing.StatusUpdate; +import org.alfresco.service.cmr.publishing.channels.Channel; +import org.alfresco.service.cmr.publishing.channels.ChannelType; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.workflow.WorkflowInstance; import org.alfresco.service.cmr.workflow.WorkflowPath; import org.alfresco.service.cmr.workflow.WorkflowService; import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; import org.junit.Test; /** @@ -58,10 +68,12 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest private static final String channelId = "test://channel/node"; private static final String comment = "The Comment"; - protected PublishingService publishingService; - + private PublishingService publishingService; private WorkflowService workflowService; - + private PermissionService permissionService; + private TestPersonManager personManager; + private ChannelServiceImpl channelService; + private String eventId; @Test @@ -142,7 +154,7 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest NodeRef firstNode = createContent("First"); NodeRef secondNode = createContent("Second"); - List channelNames = Arrays.asList("Channel1", "Channel2", "Channel3" ); + List channelNames = Arrays.asList("test://channel/Channel1", "test://channel/Channel2", "test://channel/Channel3" ); String message = "The message"; StatusUpdate update = queue.createStatusUpdate(message, secondNode, channelNames); @@ -162,6 +174,74 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest assertTrue(names.containsAll(channelNames)); } + @Test + public void testScheduleNewEventPermissions() throws Exception + { + // Create Channels as Admin + ChannelType channelType = mockChannelType(); + channelService.register(channelType); + Channel publishChannel = channelService.createChannel(channelType.getId(), "Channel1", null); + NodeRef publishChannelNode = new NodeRef(publishChannel.getId()); + Channel statusChannel = channelService.createChannel(channelType.getId(), "Channel2", null); + NodeRef statusChannelNode = new NodeRef(statusChannel.getId()); + + NodeRef firstNode = createContent("First"); + NodeRef secondNode = createContent("Second"); + + // Create User1, add read permissions and set as current user. + String user1 = GUID.generate(); + personManager.createPerson(user1); + permissionService.setPermission(publishChannelNode, user1, PermissionService.READ, true); + permissionService.setPermission(statusChannelNode, user1, PermissionService.READ, true); + personManager.setUser(user1); + + // Publish an event + MutablePublishingPackage publishingPackage = queue.createPublishingPackage(); + publishingPackage.addNodesToPublish(firstNode, secondNode); + Calendar schedule = Calendar.getInstance(); + schedule.add(Calendar.HOUR, 2); + try + { + this.eventId = queue.scheduleNewEvent(publishingPackage, publishChannel.getId(), schedule, comment, null); + fail("shceduleNewEvent should have thrown an AccessDeniedException!"); + } + catch(AlfrescoRuntimeException e) + { + //NOOP + } + + // Set Add Child permission on publish channel. + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + permissionService.setPermission(publishChannelNode, user1, PermissionService.ADD_CHILDREN, true); + personManager.setUser(user1); + + // Check publish works now. + this.eventId = queue.scheduleNewEvent(publishingPackage, publishChannel.getId(), schedule, comment, null); + assertNotNull(eventId); + publishingService.cancelPublishingEvent(eventId); + + String message = "The message"; + StatusUpdate update = queue.createStatusUpdate(message, secondNode, statusChannel.getId()); + try + { + this.eventId = queue.scheduleNewEvent(publishingPackage, publishChannel.getId(), schedule, comment, update); + fail("shceduleNewEvent with status update should have thrown an AccessDeniedException!"); + } + catch(AlfrescoRuntimeException e) + { + //NOOP + } + + // Set Add Child permission on status channel. + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + permissionService.setPermission(statusChannelNode, user1, PermissionService.ADD_CHILDREN, true); + personManager.setUser(user1); + + // Check publish works now. + this.eventId = queue.scheduleNewEvent(publishingPackage, publishChannel.getId(), schedule, comment, null); + assertNotNull(eventId); + } + private NodeRef createContent(String name) { return fileFolderService.create(docLib, name, ContentModel.TYPE_CONTENT).getNodeRef(); @@ -176,6 +256,13 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest super.onSetUp(); this.workflowService = serviceRegistry.getWorkflowService(); this.publishingService = (PublishingService) getApplicationContext().getBean("publishingService"); + this.channelService = (ChannelServiceImpl) getApplicationContext().getBean("channelService"); + + this.permissionService = (PermissionService) getApplicationContext().getBean(ServiceRegistry.PERMISSIONS_SERVICE.getLocalName()); + MutableAuthenticationService authenticationService= (MutableAuthenticationService) getApplicationContext().getBean(ServiceRegistry.AUTHENTICATION_SERVICE.getLocalName()); + PersonService personService= (PersonService) getApplicationContext().getBean(ServiceRegistry.PERSON_SERVICE.getLocalName()); + + this.personManager = new TestPersonManager(authenticationService, personService, nodeService); } /** @@ -185,6 +272,7 @@ public class PublishingQueueImplTest extends AbstractPublishingIntegrationTest @Override public void onTearDown() throws Exception { + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); if(eventId!=null) { try diff --git a/source/java/org/alfresco/repo/publishing/PublishingRootObject.java b/source/java/org/alfresco/repo/publishing/PublishingRootObject.java index b5b7529d61..92d6461bac 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingRootObject.java +++ b/source/java/org/alfresco/repo/publishing/PublishingRootObject.java @@ -38,6 +38,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; @@ -62,12 +63,11 @@ public class PublishingRootObject private VersionService versionService; private TransferManifestNodeFactory transferManifestNodeFactory; private RetryingTransactionHelper retryingTransactionHelper; -// private TenantAdminService tenantAdminService; + private PermissionService permissionService; private StoreRef publishingStore; private String publishingRootPath; -// private Map environments = new HashMap(); /** * @return the approprieate {@link Environment} for the current domain. @@ -75,15 +75,6 @@ public class PublishingRootObject */ public Environment getEnvironment() throws BeansException { -// String tenantDomain = tenantAdminService.getCurrentUserDomain(); -// Environment environment = environments.get(tenantDomain); -// if(environment != null) -// { -// return environment; -// } -// environment = createEnvironment(); -// environments.put(tenantDomain, environment); -// return environment; return createEnvironment(); } @@ -145,6 +136,7 @@ public class PublishingRootObject ASSOC_PUBLISHING_QUEUE, QName.createQName(NAMESPACE, "publishingQueue"), TYPE_PUBLISHING_QUEUE).getChildRef(); + permissionService.setPermission(queueNode, PermissionService.ALL_AUTHORITIES, PermissionService.ADD_CHILDREN, true); } return queueNode; } @@ -228,14 +220,6 @@ public class PublishingRootObject this.searchService = searchService; } -// /** -// * @param tenantAdminService the tenantAdminService to set -// */ -// public void setTenantAdminService(TenantAdminService tenantAdminService) -// { -// this.tenantAdminService = tenantAdminService; -// } - /** * @param transferManifestNodeFactory the transferManifestNodeFactory to set */ @@ -251,4 +235,12 @@ public class PublishingRootObject { this.versionService = versionService; } + + /** + * @param permissionService the permissionService to set + */ + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/publishing/PublishingRootObjectTest.java b/source/java/org/alfresco/repo/publishing/PublishingRootObjectTest.java index 9a59a32a8d..0578ab3ff5 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingRootObjectTest.java +++ b/source/java/org/alfresco/repo/publishing/PublishingRootObjectTest.java @@ -41,7 +41,6 @@ public class PublishingRootObjectTest extends AbstractPublishingIntegrationTest { super.onSetUp(); this.rootObject = (PublishingRootObject) getApplicationContext().getBean(PublishingRootObject.NAME); - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); } @Test diff --git a/source/java/org/alfresco/service/cmr/publishing/PublishingQueue.java b/source/java/org/alfresco/service/cmr/publishing/PublishingQueue.java index b0668dc4d4..c7269961ff 100644 --- a/source/java/org/alfresco/service/cmr/publishing/PublishingQueue.java +++ b/source/java/org/alfresco/service/cmr/publishing/PublishingQueue.java @@ -42,7 +42,7 @@ public interface PublishingQueue * @param channelId The name of the channel that the package is to be published to * @param schedule The time at which the new publishing event should be scheduled (optional - null indicates "as soon as possible") * @param comment A comment to be stored with this new event (optional - may be null) - * @param statusUpdate TODO + * @param statusUpdate defines the status update (if any). If null then no status update is sent. * @return The identifier of the newly scheduled event */ String scheduleNewEvent(PublishingPackage publishingPackage, String channelId, Calendar schedule, String comment, StatusUpdate statusUpdate);