diff --git a/config/alfresco/messages/site-service.properties b/config/alfresco/messages/site-service.properties
index 18e64db3d4..5b3939588a 100644
--- a/config/alfresco/messages/site-service.properties
+++ b/config/alfresco/messages/site-service.properties
@@ -1,6 +1,7 @@
# Site service externalised display strings
site_service.unable_to_create=Unable to create site because the site short name {0} is already in use. Site short names must be unique.
+site_service.visibility_group_missing=Unable to create site because the visibility group {0} does not exist.
site_service.can_not_update=Can not update site {0} because it does not exist.
site_service.can_not_delete=Can not delete site {0} because it does not exist.
site_service.site_no_exist=Site {0} does not exist.
diff --git a/config/alfresco/site-services-context.xml b/config/alfresco/site-services-context.xml
index b4aa1ab2e2..73ea4326e2 100644
--- a/config/alfresco/site-services-context.xml
+++ b/config/alfresco/site-services-context.xml
@@ -90,8 +90,7 @@
-
-
+
diff --git a/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter-context.xml b/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter-context.xml
index c8b9fd53ff..68d8596dca 100644
--- a/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter-context.xml
+++ b/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter-context.xml
@@ -38,6 +38,8 @@
${share.protocol}
+
+
diff --git a/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter.properties b/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter.properties
index 92ddefa30f..fecf5e867d 100644
--- a/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter.properties
+++ b/config/alfresco/subsystems/sysAdmin/default/sysadmin-parameter.properties
@@ -10,3 +10,8 @@ share.context=share
share.host=${localname}
share.port=8080
share.protocol=http
+
+# Share Site configuration.
+#
+# This property controls who has visibility of created share sites.
+site.public.group=GROUP_EVERYONE
diff --git a/source/java/org/alfresco/repo/admin/SysAdminParams.java b/source/java/org/alfresco/repo/admin/SysAdminParams.java
index fda30a2b5a..5a37bce23d 100644
--- a/source/java/org/alfresco/repo/admin/SysAdminParams.java
+++ b/source/java/org/alfresco/repo/admin/SysAdminParams.java
@@ -105,6 +105,15 @@ public interface SysAdminParams
*/
public String getShareProtocol();
+ /**
+ * Gets the group name used for public site visibility.
+ * Only members of this group will have SiteConsumer access to 'public' share sites.
+ *
+ * @return the name of the public site group.
+ * @since 3.4
+ */
+ public String getSitePublicGroup();
+
/**
* Expands the special ${localname} token within a host name using the resolved DNS name for the local host.
*
diff --git a/source/java/org/alfresco/repo/admin/SysAdminParamsImpl.java b/source/java/org/alfresco/repo/admin/SysAdminParamsImpl.java
index d07641dd63..cc83c22a79 100644
--- a/source/java/org/alfresco/repo/admin/SysAdminParamsImpl.java
+++ b/source/java/org/alfresco/repo/admin/SysAdminParamsImpl.java
@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
+import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.license.LicenseService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -75,6 +76,9 @@ public class SysAdminParamsImpl implements SysAdminParams, ApplicationContextAwa
/** Share protocol. */
private String shareProtocol = "http";
+
+ // The default is GROUP_EVERYONE, although this will likely be overridden by an injected value from spring.
+ private String sitePublicGroup = PermissionService.ALL_AUTHORITIES;
public SysAdminParamsImpl()
{
@@ -283,4 +287,19 @@ public class SysAdminParamsImpl implements SysAdminParams, ApplicationContextAwa
{
return hostName.replace(TOKEN_LOCAL_NAME, localName);
}
+
+ /*
+ * (non-Javadoc)
+ * @see org.alfresco.repo.admin.SysAdminParams#getSitePublicGroup()
+ */
+ public String getSitePublicGroup()
+ {
+ return this.sitePublicGroup;
+ }
+
+ public void setSitePublicGroup(String sitePublicGroup)
+ {
+ this.sitePublicGroup = sitePublicGroup;
+ }
+
}
diff --git a/source/java/org/alfresco/repo/site/SiteServiceImpl.java b/source/java/org/alfresco/repo/site/SiteServiceImpl.java
index f91c163319..aeaa309df5 100644
--- a/source/java/org/alfresco/repo/site/SiteServiceImpl.java
+++ b/source/java/org/alfresco/repo/site/SiteServiceImpl.java
@@ -33,6 +33,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.activities.ActivityType;
+import org.alfresco.repo.admin.SysAdminParams;
import org.alfresco.repo.search.QueryParameterDefImpl;
import org.alfresco.repo.search.impl.lucene.LuceneQueryParser;
import org.alfresco.repo.security.authentication.AuthenticationContext;
@@ -70,13 +71,13 @@ import org.alfresco.service.cmr.tagging.TaggingService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
+import org.alfresco.util.PropertyCheck;
import org.alfresco.util.PropertyMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.extensions.surf.util.ParameterCheck;
-import org.alfresco.util.PropertyCheck;
/**
* Site Service Implementation. Also bootstraps the site AVM and DM stores.
@@ -109,6 +110,7 @@ public class SiteServiceImpl implements SiteService, SiteModel
/** Messages */
private static final String MSG_UNABLE_TO_CREATE = "site_service.unable_to_create";
+ private static final String MSG_VISIBILITY_GROUP_MISSING = "site_service.visibility_group_missing";
private static final String MSG_CAN_NOT_UPDATE = "site_service.can_not_update";
private static final String MSG_CAN_NOT_DELETE = "site_service.can_not_delete";
private static final String MSG_SITE_NO_EXIST = "site_service.site_no_exist";
@@ -132,7 +134,8 @@ public class SiteServiceImpl implements SiteService, SiteModel
private TenantService tenantService;
private TenantAdminService tenantAdminService;
private RetryingTransactionHelper retryingTransactionHelper;
- private Comparator roleComparator ;
+ private Comparator roleComparator;
+ private SysAdminParams sysAdminParams;
/**
@@ -268,6 +271,11 @@ public class SiteServiceImpl implements SiteService, SiteModel
{
this.roleComparator = roleComparator;
}
+
+ public void setSysAdminParams(SysAdminParams sysAdminParams)
+ {
+ this.sysAdminParams = sysAdminParams;
+ }
public Comparator getRoleComparator()
{
@@ -398,7 +406,21 @@ public class SiteServiceImpl implements SiteService, SiteModel
// - add the current user to the site manager group
if (SiteVisibility.PUBLIC.equals(visibility) == true)
{
- permissionService.setPermission(siteNodeRef, PermissionService.ALL_AUTHORITIES, SITE_CONSUMER, true);
+ // From Alfresco 3.4 the 'site public' group is configurable. Out of the box it is
+ // GROUP_EVERYONE so unconfigured behaviour is unchanged. But from 3.4 admins
+ // can change the value of property site.public.group via JMX/properties files
+ // to be another group of their choosing.
+ // This then is the group that is given SiteConsumer access to newly created sites.
+ final String sitePublicGroup = sysAdminParams.getSitePublicGroup();
+
+ boolean groupExists = authorityService.authorityExists(sitePublicGroup);
+ if (!PermissionService.ALL_AUTHORITIES.equals(sitePublicGroup) && !groupExists)
+ {
+ // If the group does not exist, we cannot create the site.
+ throw new SiteServiceException(MSG_VISIBILITY_GROUP_MISSING, new Object[]{sitePublicGroup});
+ }
+
+ permissionService.setPermission(siteNodeRef, sitePublicGroup, SITE_CONSUMER, true);
}
else if (SiteVisibility.MODERATED.equals(visibility) == true)
{
diff --git a/source/java/org/alfresco/repo/site/SiteServiceImplTest.java b/source/java/org/alfresco/repo/site/SiteServiceImplTest.java
index cb8d6561be..1e6fee6d06 100644
--- a/source/java/org/alfresco/repo/site/SiteServiceImplTest.java
+++ b/source/java/org/alfresco/repo/site/SiteServiceImplTest.java
@@ -28,6 +28,7 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.model.ForumModel;
import org.alfresco.repo.jscript.ClasspathScriptLocation;
+import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.model.FileFolderService;
@@ -37,6 +38,7 @@ import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.service.cmr.repository.ScriptService;
+import org.alfresco.service.cmr.security.AccessPermission;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService;
@@ -148,6 +150,10 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest
}
}
+ /**
+ * This test method ensures that public sites can be created and that their site info is correct.
+ * It also tests that a duplicate site cannot be created.
+ */
public void testCreateSite() throws Exception
{
// Create a public site
@@ -203,10 +209,108 @@ public class SiteServiceImplTest extends BaseAlfrescoSpringTest
{
// Expected
}
-
-
}
-
+
+ /**
+ * This method tests https://issues.alfresco.com/jira/browse/ALF-3785 which allows 'public' sites
+ * to be only visible to members of a configured group, by default EVERYONE.
+ *
+ * @author Neil McErlean
+ * @since 3.4
+ */
+ @SuppressWarnings("deprecation")
+ public void testConfigurableSitePublicGroup() throws Exception
+ {
+ AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
+
+ // We'll be configuring a JMX managed bean (in this test method only).
+ ChildApplicationContextFactory sysAdminSubsystem = (ChildApplicationContextFactory) applicationContext.getBean("sysAdmin");
+ final String sitePublicGroupPropName = "site.public.group";
+ final String originalSitePublicGroup = "GROUP_EVERYONE";
+
+ try
+ {
+ // Firstly we'll ensure that the site.public.group has the correct (pristine) value.
+ String groupName = sysAdminSubsystem.getProperty(sitePublicGroupPropName);
+ assertEquals(sitePublicGroupPropName + " was not the pristine value",
+ originalSitePublicGroup, groupName);
+
+ // Create a 'normal', unconfigured site.
+ SiteInfo unconfiguredSite = siteService.createSite(TEST_SITE_PRESET, "unconfigured",
+ TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC);
+ assertTrue(containsConsumerPermission(originalSitePublicGroup, unconfiguredSite));
+
+
+ // Now set the managed bean's visibility group to something other than GROUP_EVERYONE.
+ // This is the group that will have visibility of subsequently created sites.
+ //
+ // We'll intentionally set it to a group that DOES NOT EXIST YET.
+ String newGroupName = this.getClass().getSimpleName() + System.currentTimeMillis();
+ String prefixedNewGroupName = PermissionService.GROUP_PREFIX + newGroupName;
+
+ sysAdminSubsystem.stop();
+ sysAdminSubsystem.setProperty(sitePublicGroupPropName, prefixedNewGroupName);
+ sysAdminSubsystem.start();
+
+ // Now create a site as before. It should fail as we're using a group that doesn't exist.
+ boolean expectedExceptionThrown = false;
+ try
+ {
+ siteService.createSite(TEST_SITE_PRESET, "thisShouldFail",
+ TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC);
+ }
+ catch (SiteServiceException expected)
+ {
+ expectedExceptionThrown = true;
+ }
+ if (!expectedExceptionThrown)
+ {
+ fail("Expected exception on createSite with non-existent group was not thrown.");
+ }
+
+
+ // Now we'll create the group used above.
+ authorityService.createAuthority(AuthorityType.GROUP, newGroupName);
+
+
+ // And create the site as before. This time it should succeed.
+ SiteInfo configuredSite = siteService.createSite(TEST_SITE_PRESET, "configured",
+ TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC);
+
+ // And check the permissions on the site.
+ assertTrue("The configured site should not have " + originalSitePublicGroup + " as SiteContributor",
+ !containsConsumerPermission(originalSitePublicGroup, configuredSite));
+ assertTrue("The configured site should have (newGroupName) as SiteContributor",
+ containsConsumerPermission(prefixedNewGroupName, configuredSite));
+ }
+ finally
+ {
+ // Reset the JMX bean to its out-of-the-box values.
+ sysAdminSubsystem.stop();
+ sysAdminSubsystem.setProperty(sitePublicGroupPropName, originalSitePublicGroup);
+ sysAdminSubsystem.start();
+ }
+ }
+
+ private boolean containsConsumerPermission(final String groupName,
+ SiteInfo unconfiguredSite)
+ {
+ boolean result = false;
+ Set perms = permissionService.getAllSetPermissions(unconfiguredSite.getNodeRef());
+ for (AccessPermission p : perms)
+ {
+ if (p.getAuthority().equals(groupName) &&
+ p.getPermission().equals(SiteModel.SITE_CONSUMER))
+ {
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method tests that admin and system users can set site membership for a site of which they are not SiteManagers.
+ */
public void testETHREEOH_15() throws Exception
{
SiteInfo siteInfo = this.siteService.createSite(TEST_SITE_PRESET, "mySiteTest", TEST_TITLE, TEST_DESCRIPTION, SiteVisibility.PUBLIC);