propertyUniqueContextCache)
+ {
+ this.propertyUniqueContextCache = propertyUniqueContextCache;
+ }
+
+
/**
* Default constructor.
*
* This sets up the DAO accessors to bypass any caching to handle the case where the caches are not
* supplied in the setters.
*/
+ @SuppressWarnings({ "unchecked", "rawtypes" })
public AbstractPropertyValueDAOImpl()
{
this.propertyClassDaoCallback = new PropertyClassCallbackDAO();
@@ -147,6 +164,8 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
this.propertySerializableValueCache = new EntityLookupCache(propertySerializableValueCallback);
this.propertyValueCache = new EntityLookupCache(propertyValueCallback);
this.propertyCache = new EntityLookupCache(propertyCallback);
+
+ this.propertyUniqueContextCache = (SimpleCache)new NullCache();
}
/**
@@ -805,6 +824,9 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
Pair entityPair = propertyCache.getByKey(id);
if (entityPair == null)
{
+ // Remove from cache
+ propertyCache.removeByKey(id);
+
throw new DataIntegrityViolationException("No property value exists for ID " + id);
}
return entityPair.getSecond();
@@ -1085,7 +1107,63 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
//================================
// 'alf_prop_unique_ctx' accessors
//================================
-
+
+ private CachePucKey getPucKey(Long id1, Long id2, Long id3)
+ {
+ return new CachePucKey(id1, id2, id3);
+ }
+
+ /**
+ * Key for PropertyUniqueContext cache
+ */
+ public static class CachePucKey implements Serializable
+ {
+ private static final long serialVersionUID = -4294324585692613101L;
+
+ private final Long key1;
+ private final Long key2;
+ private final Long key3;
+
+ private final int hashCode;
+
+ private CachePucKey(Long key1, Long key2, Long key3)
+ {
+ this.key1 = key1;
+ this.key2 = key2;
+ this.key3 = key3;
+ this.hashCode = (key1 == null ? 0 : key1.hashCode()) + (key2 == null ? 0 : key2.hashCode()) + (key3 == null ? 0 : key3.hashCode());
+ }
+
+ @Override
+ public String toString()
+ {
+ return key1 + "." + key2 + "." + key3;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ else if (!(obj instanceof CachePucKey))
+ {
+ return false;
+ }
+ CachePucKey that = (CachePucKey) obj;
+ return EqualsHelper.nullSafeEquals(this.key1, that.key1) &&
+ EqualsHelper.nullSafeEquals(this.key2, that.key2) &&
+ EqualsHelper.nullSafeEquals(this.key3, that.key3);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return hashCode;
+ }
+ }
+
public Pair createPropertyUniqueContext(
Serializable value1, Serializable value2, Serializable value3,
Serializable propertyValue1)
@@ -1104,11 +1182,17 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
property1Id = createProperty(propertyValue1);
}
+ CachePucKey pucKey = getPucKey(id1, id2, id3);
+
Savepoint savepoint = controlDAO.createSavepoint("createPropertyUniqueContext");
try
{
PropertyUniqueContextEntity entity = createPropertyUniqueContext(id1, id2, id3, property1Id);
controlDAO.releaseSavepoint(savepoint);
+
+ // cache
+ propertyUniqueContextCache.put(pucKey, entity);
+
if (logger.isDebugEnabled())
{
logger.debug(
@@ -1116,10 +1200,14 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
" Values: " + value1 + "-" + value2 + "-" + value3 + "\n" +
" Result: " + entity);
}
+
return new Pair(entity.getId(), property1Id);
}
catch (Throwable e)
{
+ // Remove from cache
+ propertyUniqueContextCache.remove(pucKey);
+
controlDAO.rollbackToSavepoint(savepoint);
throw new PropertyUniqueConstraintViolation(value1, value2, value3, e);
}
@@ -1132,14 +1220,48 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
Pair pair2 = getPropertyValue(value2);
Pair pair3 = getPropertyValue(value3);
if (pair1 == null || pair2 == null || pair3 == null)
- {
+ {
// None of the values exist so no unique context values can exist
- return null;
- }
+ return null;
+ }
Long id1 = pair1.getFirst();
Long id2 = pair2.getFirst();
Long id3 = pair3.getFirst();
- PropertyUniqueContextEntity entity = getPropertyUniqueContextByValues(id1, id2, id3);
+
+ CachePucKey pucKey = getPucKey(id1, id2, id3);
+
+ // check cache
+ PropertyUniqueContextEntity entity = propertyUniqueContextCache.get(pucKey);
+ if (entity == null)
+ {
+ // Remove from cache
+ propertyUniqueContextCache.remove(pucKey);
+
+ // query DB
+ entity = getPropertyUniqueContextByValues(id1, id2, id3);
+
+ if (entity != null)
+ {
+ // cache
+ propertyUniqueContextCache.put(pucKey, entity);
+ }
+ }
+
+ if ((entity != null) && (entity.getPropertyId() != null))
+ {
+ try
+ {
+ // eager fetch - ignore return for now (could change API)
+ getPropertyById(entity.getPropertyId());
+ }
+ catch (DataIntegrityViolationException dive)
+ {
+ // Remove from cache
+ propertyUniqueContextCache.remove(pucKey);
+ throw dive;
+ }
+ }
+
// Done
if (logger.isDebugEnabled())
{
@@ -1168,7 +1290,10 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
}
valueIds[i] = valuePair.getFirst();
}
+
+ // not cached
getPropertyUniqueContextByValues(callback, valueIds);
+
// Done
if (logger.isDebugEnabled())
{
@@ -1177,8 +1302,12 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
" Values: " + Arrays.toString(values));
}
}
-
- public void updatePropertyUniqueContext(Long id, Serializable value1, Serializable value2, Serializable value3)
+
+ /*
+ * Update PUC keys - retain current property value
+ *
+ */
+ public void updatePropertyUniqueContextKeys(Long id, Serializable value1, Serializable value2, Serializable value3)
{
/*
* Use savepoints so that the PropertyUniqueConstraintViolation can be caught and handled in-transactioin
@@ -1189,19 +1318,30 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
Long id2 = getOrCreatePropertyValue(value2).getFirst();
Long id3 = getOrCreatePropertyValue(value3).getFirst();
+ CachePucKey pucKey = getPucKey(id1, id2, id3);
+
Savepoint savepoint = controlDAO.createSavepoint("updatePropertyUniqueContext");
try
{
PropertyUniqueContextEntity entity = getPropertyUniqueContextById(id);
if (entity == null)
{
+ // Remove from cache
+ propertyUniqueContextCache.remove(pucKey);
+
throw new DataIntegrityViolationException("No unique property context exists for id: " + id);
}
entity.setValue1PropId(id1);
entity.setValue2PropId(id2);
entity.setValue3PropId(id3);
- updatePropertyUniqueContext(entity);
+
+ entity = updatePropertyUniqueContext(entity);
+
controlDAO.releaseSavepoint(savepoint);
+
+ // cache
+ propertyUniqueContextCache.put(pucKey, entity);
+
// Done
if (logger.isDebugEnabled())
{
@@ -1214,43 +1354,83 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
}
catch (Throwable e)
{
+ // Remove from cache
+ propertyUniqueContextCache.remove(pucKey);
+
controlDAO.rollbackToSavepoint(savepoint);
throw new PropertyUniqueConstraintViolation(value1, value2, value3, e);
}
}
-
- public void updatePropertyUniqueContext(Long id, Serializable propertyValue)
+
+ /*
+ * Update property value by keys
+ */
+ public void updatePropertyUniqueContext(Serializable value1, Serializable value2, Serializable value3, Serializable propertyValue)
{
- PropertyUniqueContextEntity entity = getPropertyUniqueContextById(id);
- if (entity == null)
- {
- throw new DataIntegrityViolationException("No unique property context exists for id: " + id);
- }
- Long propertyIdToDelete = entity.getPropertyId();
-
- Long propertyId = null;
- if (propertyValue != null)
- {
- propertyId = createProperty(propertyValue);
- }
+ // Translate the properties. Null values are acceptable
+ Long id1 = getOrCreatePropertyValue(value1).getFirst();
+ Long id2 = getOrCreatePropertyValue(value2).getFirst();
+ Long id3 = getOrCreatePropertyValue(value3).getFirst();
- // Create a new property
- entity.setPropertyId(propertyId);
- updatePropertyUniqueContext(entity);
+ CachePucKey pucKey = getPucKey(id1, id2, id3);
- // Clean up the previous property, if present
- if (propertyIdToDelete != null)
+ try
{
- deleteProperty(propertyIdToDelete);
+ Pair entityPair = getPropertyUniqueContext(value1, value2, value3);
+ if (entityPair == null)
+ {
+ throw new DataIntegrityViolationException("No unique property context exists for values: " + value1 + "-" + value2 + "-" + value3);
+ }
+
+ long id = entityPair.getFirst();
+ PropertyUniqueContextEntity entity = getPropertyUniqueContextById(id);
+ if (entity == null)
+ {
+ throw new DataIntegrityViolationException("No unique property context exists for id: " + id);
+ }
+
+ Long propertyIdToDelete = entity.getPropertyId();
+
+ Long propertyId = null;
+ if (propertyValue != null)
+ {
+ propertyId = createProperty(propertyValue);
+ }
+
+ // Create a new property
+ entity.setPropertyId(propertyId);
+
+ entity = updatePropertyUniqueContext(entity);
+
+ // cache
+ propertyUniqueContextCache.put(pucKey, entity);
+
+ // Clean up the previous property, if present
+ if (propertyIdToDelete != null)
+ {
+ deleteProperty(propertyIdToDelete);
+ }
+
+ // Done
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ "Updated unique property context: \n" +
+ " ID: " + id + "\n" +
+ " Property: " + propertyId);
+ }
}
-
- // Done
- if (logger.isDebugEnabled())
+ catch (DataIntegrityViolationException e)
{
- logger.debug(
- "Updated unique property context: \n" +
- " ID: " + id + "\n" +
- " Property: " + propertyId);
+ // Remove from cache
+ propertyUniqueContextCache.remove(pucKey);
+ throw e;
+ }
+ catch (ConcurrencyFailureException e)
+ {
+ // Remove from cache
+ propertyUniqueContextCache.remove(pucKey);
+ throw e;
}
}
@@ -1272,6 +1452,20 @@ public abstract class AbstractPropertyValueDAOImpl implements PropertyValueDAO
valueIds[i] = valuePair.getFirst();
}
int deleted = deletePropertyUniqueContexts(valueIds);
+
+ CachePucKey pucKey = getPucKey(valueIds[0], (values.length > 1 ? valueIds[1] : null), (values.length > 2 ? valueIds[2] : null));
+
+ if (values.length == 3)
+ {
+ propertyUniqueContextCache.remove(pucKey);
+ }
+ else
+ {
+ // reasonable to clear for now (eg. only used by AVMLockingService.removeLocks*)
+ // note: in future, if we need to support mass removal based on specific key grouping then we need to use more intelligent cache (removal)
+ propertyUniqueContextCache.clear();
+ }
+
// Done
if (logger.isDebugEnabled())
{
diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java
index 3920a78471..be3b223aad 100644
--- a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java
+++ b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAO.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2010 Alfresco Software Limited.
+ * Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -325,15 +325,15 @@ public interface PropertyValueDAO
*
* @see #createPropertyUniqueContext(Serializable, Serializable, Serializable, Serializable)
*/
- void updatePropertyUniqueContext(
+ void updatePropertyUniqueContextKeys(
Long id,
Serializable value1, Serializable value2, Serializable value3);
/**
- * Update the property associated with a unique context.
+ * Update the property associated with a unique context (based on one, two or three context values).
*
* @see #createPropertyUniqueContext(Serializable, Serializable, Serializable, Serializable)
*/
- void updatePropertyUniqueContext(Long id, Serializable propertyValue1);
+ void updatePropertyUniqueContext(Serializable value1, Serializable value2, Serializable value3, Serializable propertyValue);
/**
* @see #createPropertyUniqueContext(Serializable, Serializable, Serializable, Serializable)
*/
diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java
index 7d999acde9..33ea3473bb 100644
--- a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java
+++ b/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2010 Alfresco Software Limited.
+ * Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -777,7 +777,7 @@ public class PropertyValueDAOTest extends TestCase
public Void execute() throws Throwable
{
// Now update it
- propertyValueDAO.updatePropertyUniqueContext(id, "A", "AA", bbb);
+ propertyValueDAO.updatePropertyUniqueContextKeys(id, "A", "AA", bbb);
// Should be able to create the previous one ...
propertyValueDAO.createPropertyUniqueContext("A", "AA", aaa, null);
// ... and fail to create the second one
@@ -851,7 +851,7 @@ public class PropertyValueDAOTest extends TestCase
{
public Pair execute() throws Throwable
{
- propertyValueDAO.updatePropertyUniqueContext(id, v1);
+ propertyValueDAO.updatePropertyUniqueContext(aaa, bbb, ccc, v1);
Pair pair = propertyValueDAO.getPropertyUniqueContext(aaa, bbb, ccc);
Serializable value = propertyValueDAO.getPropertyById(pair.getSecond());
return new Pair(pair.getFirst(), value);
@@ -863,7 +863,7 @@ public class PropertyValueDAOTest extends TestCase
{
public Pair execute() throws Throwable
{
- propertyValueDAO.updatePropertyUniqueContext(id, v2);
+ propertyValueDAO.updatePropertyUniqueContext(aaa, bbb, ccc, v2);
Pair pair = propertyValueDAO.getPropertyUniqueContext(aaa, bbb, ccc);
Serializable value = propertyValueDAO.getPropertyById(pair.getSecond());
return new Pair(pair.getFirst(), value);
diff --git a/source/java/org/alfresco/repo/jscript/People.java b/source/java/org/alfresco/repo/jscript/People.java
index 36978aa45b..b66f4ac426 100644
--- a/source/java/org/alfresco/repo/jscript/People.java
+++ b/source/java/org/alfresco/repo/jscript/People.java
@@ -30,7 +30,6 @@ import java.util.Set;
import java.util.StringTokenizer;
import org.alfresco.model.ContentModel;
-import org.alfresco.query.PagingRequest;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.UserNameGenerator;
@@ -56,6 +55,7 @@ import org.alfresco.service.cmr.usage.ContentUsageService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyMap;
+import org.alfresco.util.ScriptPagingDetails;
import org.alfresco.util.ValueDerivingMapFactory;
import org.alfresco.util.ValueDerivingMapFactory.ValueDeriver;
import org.apache.commons.logging.Log;
@@ -72,7 +72,7 @@ import org.springframework.extensions.surf.util.ParameterCheck;
* @author davidc
* @author kevinr
*/
-public final class People extends BaseScopableProcessorExtension implements InitializingBean
+public class People extends BaseScopableProcessorExtension implements InitializingBean
{
private static Log logger = LogFactory.getLog(People.class);
@@ -83,9 +83,10 @@ public final class People extends BaseScopableProcessorExtension implements Init
private PersonService personService;
private MutableAuthenticationService authenticationService;
private ContentUsageService contentUsageService;
- private TenantService tenantService;
private UserNameGenerator usernameGenerator;
private UserRegistrySynchronizer userRegistrySynchronizer;
+ protected TenantService tenantService;
+
private StoreRef storeRef;
private ValueDerivingMapFactory valueDerivingMapFactory;
private int numRetries = 10;
@@ -535,6 +536,26 @@ public final class People extends BaseScopableProcessorExtension implements Init
*/
public Scriptable getPeople(String filter, int maxResults, String sortBy, boolean sortAsc)
{
+ return getPeople(filter, new ScriptPagingDetails(maxResults, 0), null, null);
+ }
+
+ public Scriptable getPeople(String filter, ScriptPagingDetails pagingRequest, String sortBy, Boolean sortAsc)
+ {
+ List persons = getPeopleImpl(filter, pagingRequest, sortBy, sortAsc);
+
+ Object[] peopleRefs = new Object[persons.size()];
+ for (int i = 0; i < peopleRefs.length; i++)
+ {
+ peopleRefs[i] = persons.get(i).getNodeRef();
+ }
+
+ return Context.getCurrentContext().newArray(getScope(), peopleRefs);
+ }
+
+ protected List getPeopleImpl(String filter, ScriptPagingDetails pagingRequest, String sortBy, Boolean sortAsc)
+ {
+ ParameterCheck.mandatory("pagingRequest", pagingRequest);
+
boolean useCQ = false;
if (filter != null)
{
@@ -544,96 +565,124 @@ public final class People extends BaseScopableProcessorExtension implements Init
filter = filter.substring(0, filter.length()-HINT_CQ_SUFFIX.length());
}
}
+ else
+ {
+ filter = "*";
+ }
- Object[] people = null;
+ List persons = null;
+ int maxResults = pagingRequest.getMaxItems();
if ((maxResults <= 0) || (maxResults > defaultListMaxResults))
{
// remove open-ended query (eg cutoff at default/configurable max, eg. 5000 people)
maxResults = defaultListMaxResults;
+ pagingRequest.setMaxItems(maxResults);
}
- if ((filter == null || filter.length() == 0) || useCQ)
+ if (useCQ)
{
- people = getPeopleImplDB(filter, maxResults, sortBy, sortAsc);
+ persons = getPeopleImplDB(filter, pagingRequest, sortBy, sortAsc);
}
else
{
filter = filter.trim();
- if (filter.length() != 0)
+
+ String term = filter.replace("\\", "").replace("\"", "");
+ StringTokenizer t = new StringTokenizer(term, " ");
+ int propIndex = term.lastIndexOf(':');
+ int wildPosition = term.indexOf('*');
+
+ // simple filter - can use CQ if search fails
+ useCQ = ((t.countTokens() == 1) && (propIndex == -1) && ((wildPosition == -1) || (wildPosition == (term.length() - 1))));
+
+ try
{
- String term = filter.replace("\\", "").replace("\"", "");
- StringTokenizer t = new StringTokenizer(term, " ");
- int propIndex = term.lastIndexOf(':');
- int wildPosition = term.indexOf('*');
+ // FTS
+ List personRefs = getPeopleImplSearch(filter, pagingRequest, sortBy, sortAsc);
- // simple filter - can use CQ if search fails
- useCQ = ((t.countTokens() == 1) && (propIndex == -1) && ((wildPosition == -1) || (wildPosition == (term.length() - 1))));
-
- try
+ if (personRefs != null)
{
- people = getPeopleImplSearch(term, t, propIndex, maxResults, sortBy, sortAsc);
- }
- catch (Throwable err)
- {
- if (useCQ)
+ persons = new ArrayList(personRefs.size());
+ for (NodeRef personRef : personRefs)
{
- // search unavailable and/or parser exception - try CQ instead
- people = getPeopleImplDB(term, maxResults, sortBy, sortAsc);
+ persons.add(personService.getPerson(personRef));
}
}
}
+ catch (Throwable err)
+ {
+ if (useCQ)
+ {
+ // search unavailable and/or parser exception - try CQ instead
+ // simple non-FTS filter: firstname or lastname or username starting with term (ignoring case)
+ persons = getPeopleImplDB(filter, pagingRequest, sortBy, sortAsc);
+ }
+ }
}
- if (people == null)
- {
- people = new Object[0];
- }
-
- return Context.getCurrentContext().newArray(getScope(), people);
+ return (persons != null ? persons : new ArrayList(0));
}
// canned query
- private Object[] getPeopleImplDB(String term, int maxResults, final String sortBy, boolean sortAsc)
+ protected List getPeopleImplDB(String filter, ScriptPagingDetails pagingRequest, String sortBy, Boolean sortAsc)
{
- Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
+ List filterProps = null;
- List peopleRefs = new ArrayList();
-
- // simple non-FTS filter: firstname or lastname or username starting with term (ignoring case)
-
- List filterProps = new ArrayList(3);
- filterProps.add(ContentModel.PROP_FIRSTNAME);
- filterProps.add(ContentModel.PROP_LASTNAME);
- filterProps.add(ContentModel.PROP_USERNAME);
-
- List> sortProps = new ArrayList>(1);
- sortProps.add(new Pair(ContentModel.PROP_USERNAME, true));
-
- PagingRequest pagingRequest = new PagingRequest(maxResults, null);
- List persons = personService.getPeople(term, filterProps, sortProps, pagingRequest).getPage();
- for (int i=0; i 0))
{
- peopleRefs.add(persons.get(i).getNodeRef());
+ filter = filter.trim();
+ if (! filter.equals("*"))
+ {
+ filter = filter.replace("\\", "").replace("\"", "");
+
+ // simple non-FTS filter: firstname or lastname or username starting with term (ignoring case)
+
+ filterProps = new ArrayList(3);
+ filterProps.add(ContentModel.PROP_FIRSTNAME);
+ filterProps.add(ContentModel.PROP_LASTNAME);
+ filterProps.add(ContentModel.PROP_USERNAME);
+ }
}
- Object[] people = getSortedPeopleObjects(peopleRefs, sortBy, sortAsc);
-
- if (start != null)
+ // Build the sorting. The user controls the primary sort, we supply
+ // additional ones automatically
+ List> sort = new ArrayList>();
+ if ("lastName".equals(sortBy))
{
- logger.debug("getPeople: cq - "+people.length+" items (in "+(System.currentTimeMillis()-start)+" msecs)");
+ sort.add(new Pair(ContentModel.PROP_LASTNAME, sortAsc));
+ sort.add(new Pair(ContentModel.PROP_FIRSTNAME, sortAsc));
+ sort.add(new Pair(ContentModel.PROP_USERNAME, sortAsc));
}
-
- return people;
+ else if ("firstName".equals(sortBy))
+ {
+ sort.add(new Pair(ContentModel.PROP_FIRSTNAME, sortAsc));
+ sort.add(new Pair(ContentModel.PROP_LASTNAME, sortAsc));
+ sort.add(new Pair(ContentModel.PROP_USERNAME, sortAsc));
+ }
+ else
+ {
+ sort.add(new Pair(ContentModel.PROP_USERNAME, sortAsc));
+ sort.add(new Pair(ContentModel.PROP_FIRSTNAME, sortAsc));
+ sort.add(new Pair(ContentModel.PROP_LASTNAME, sortAsc));
+ }
+
+ return personService.getPeople(filter, filterProps, sort, pagingRequest).getPage();
}
// search query
- private Object[] getPeopleImplSearch(String term, StringTokenizer t, int propIndex, int maxResults, final String sortBy, boolean sortAsc) throws Throwable
+ protected List getPeopleImplSearch(String filter, ScriptPagingDetails pagingRequest, String sortBy, Boolean sortAsc) throws Throwable
{
+ List personRefs = null;
+
Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
- List peopleRefs = new ArrayList();
- Object[] people = null;
+ String term = filter.replace("\\", "").replace("\"", "");
+ StringTokenizer t = new StringTokenizer(term, " ");
+ int propIndex = term.indexOf(':');
+
+ int maxResults = pagingRequest.getMaxItems();
+ int skipCount = pagingRequest.getSkipCount();
SearchParameters params = new SearchParameters();
params.addQueryTemplate("_PERSON", "|%firstName OR |%lastName OR |%userName");
@@ -732,21 +781,57 @@ public final class People extends BaseScopableProcessorExtension implements Init
params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
params.addStore(this.storeRef);
params.setQuery(query.toString());
+
+ if (logger.isDebugEnabled())
+ {
+ if ((sortBy != null) && (! sortBy.isEmpty()))
+ {
+ logger.debug("getPeopleImplSearch: ignoring sortBy ("+sortBy+")- not yet supported by model for search");
+ }
+ }
+
+ /* not yet supported (default property index tokenisation mode = true)
+ if ("lastName".equals(sortBy))
+ {
+ params.addSort("@{http://www.alfresco.org/model/content/1.0}lastName", sortAsc);
+ params.addSort("@{http://www.alfresco.org/model/content/1.0}firstName", sortAsc);
+ params.addSort("@{http://www.alfresco.org/model/content/1.0}userName", sortAsc);
+ }
+ else if ("firstName".equals(sortBy))
+ {
+ params.addSort("@{http://www.alfresco.org/model/content/1.0}firstName", sortAsc);
+ params.addSort("@{http://www.alfresco.org/model/content/1.0}lastName", sortAsc);
+ params.addSort("@{http://www.alfresco.org/model/content/1.0}userName", sortAsc);
+ }
+ else
+ {
+ params.addSort("@{http://www.alfresco.org/model/content/1.0}userName", sortAsc);
+ params.addSort("@{http://www.alfresco.org/model/content/1.0}firstName", sortAsc);
+ params.addSort("@{http://www.alfresco.org/model/content/1.0}userName", sortAsc);
+ }
+ */
+
if (maxResults > 0)
{
params.setLimitBy(LimitBy.FINAL_SIZE);
params.setLimit(maxResults);
}
+ if (skipCount > 0)
+ {
+ params.setSkipCount(skipCount);
+ }
+
ResultSet results = null;
try
{
results = services.getSearchService().query(params);
- people = getSortedPeopleObjects(results.getNodeRefs(), sortBy, sortAsc);
+
+ personRefs = getSortedPeopleObjects(results.getNodeRefs(), sortBy, sortAsc);
if (start != null)
{
- logger.debug("getPeople: search - "+people.length+" items (in "+(System.currentTimeMillis()-start)+" msecs)");
+ logger.debug("getPeople: search - "+personRefs.size()+" items (in "+(System.currentTimeMillis()-start)+" msecs)");
}
}
catch (Throwable err)
@@ -766,10 +851,10 @@ public final class People extends BaseScopableProcessorExtension implements Init
}
}
- return people;
+ return personRefs;
}
- private Object[] getSortedPeopleObjects(List peopleRefs, final String sortBy, boolean sortAsc)
+ private List getSortedPeopleObjects(List peopleRefs, final String sortBy, boolean sortAsc)
{
final Collator col = Collator.getInstance(I18NUtil.getLocale());
final NodeService nodeService = services.getNodeService();
@@ -831,12 +916,9 @@ public final class People extends BaseScopableProcessorExtension implements Init
return result.toString();
}
- });
+ });
- Object[] people = new Object[peopleRefs.size()];
- peopleRefs.toArray(people);
-
- return people;
+ return peopleRefs;
}
/**
diff --git a/source/java/org/alfresco/repo/model/filefolder/HiddenAspect.java b/source/java/org/alfresco/repo/model/filefolder/HiddenAspect.java
index 1ec22b0f0d..88e2680cf8 100644
--- a/source/java/org/alfresco/repo/model/filefolder/HiddenAspect.java
+++ b/source/java/org/alfresco/repo/model/filefolder/HiddenAspect.java
@@ -1,3 +1,21 @@
+/*
+ * Copyright (C) 2005-2012 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
package org.alfresco.repo.model.filefolder;
import java.io.Serializable;
@@ -15,6 +33,7 @@ import org.alfresco.model.ContentModel;
import org.alfresco.query.PagingRequest;
import org.alfresco.query.PagingResults;
import org.alfresco.repo.search.impl.lucene.LuceneQueryParser;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -599,42 +618,44 @@ public class HiddenAspect
{
Visibility ret = Visibility.Visible;
- if(nodeService.hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN))
+ if (! AuthenticationUtil.isRunAsUserTheSystemUser())
{
- Integer visibilityMask = (Integer)nodeService.getProperty(nodeRef, ContentModel.PROP_VISIBILITY_MASK);
- if(visibilityMask != null)
+ if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_HIDDEN))
{
- if(visibilityMask.intValue() == 0)
+ Integer visibilityMask = (Integer)nodeService.getProperty(nodeRef, ContentModel.PROP_VISIBILITY_MASK);
+ if (visibilityMask != null)
{
- ret = Visibility.NotVisible;
- }
- else if(client == null)
- {
- ret = Visibility.NotVisible;
+ if(visibilityMask.intValue() == 0)
+ {
+ ret = Visibility.NotVisible;
+ }
+ else if(client == null)
+ {
+ ret = Visibility.NotVisible;
+ }
+ else
+ {
+ ret = getVisibility(visibilityMask.intValue(), client);
+ }
}
else
{
- ret = getVisibility(visibilityMask.intValue(), client);
- }
- }
- else
- {
- // no visibility mask property, so retain backwards compatibility with 3.4 hidden aspect behaviour
- if(client == Client.cifs)
- {
- ret = Visibility.HiddenAttribute;
- }
- else if(client == Client.webdav || client == Client.nfs || client == Client.imap)
- {
- ret = Visibility.Visible;
- }
- else
- {
- ret = Visibility.NotVisible;
+ // no visibility mask property, so retain backwards compatibility with 3.4 hidden aspect behaviour
+ if(client == Client.cifs)
+ {
+ ret = Visibility.HiddenAttribute;
+ }
+ else if(client == Client.webdav || client == Client.nfs || client == Client.imap)
+ {
+ ret = Visibility.Visible;
+ }
+ else
+ {
+ ret = Visibility.NotVisible;
+ }
}
}
}
-
return ret;
}
diff --git a/source/java/org/alfresco/repo/module/ModuleComponentHelper.java b/source/java/org/alfresco/repo/module/ModuleComponentHelper.java
index df19604f41..fbe647cb8b 100644
--- a/source/java/org/alfresco/repo/module/ModuleComponentHelper.java
+++ b/source/java/org/alfresco/repo/module/ModuleComponentHelper.java
@@ -87,6 +87,7 @@ public class ModuleComponentHelper
private RegistryService registryService;
private ModuleService moduleService;
private TenantAdminService tenantAdminService;
+ private boolean applyToTenants;
private Map> componentsByNameByModule;
/** Default constructor */
@@ -131,7 +132,12 @@ public class ModuleComponentHelper
{
this.tenantAdminService = tenantAdminService;
}
-
+
+ public void setApplyToTenants(boolean applyToTenants)
+ {
+ this.applyToTenants = applyToTenants;
+ }
+
/**
* Add a managed module component to the registry of components. These will be controlled
* by the {@link #startModules()} method.
@@ -197,40 +203,47 @@ public class ModuleComponentHelper
*/
AuthenticationUtil.runAs(new RunAsWork
+ */
+public class MessageDigestPasswordEncoder extends BaseDigestPasswordEncoder
+{
+
+ private final String algorithm;
+
+ /**
+ * The digest algorithm to use Supports the named Message Digest Algorithms in the Java environment.
+ *
+ * @param algorithm
+ */
+ public MessageDigestPasswordEncoder(String algorithm)
+ {
+ this(algorithm, false);
+ }
+
+ /**
+ * Convenience constructor for specifying the algorithm and whether or not
+ * to enable base64 encoding
+ *
+ * @param algorithm
+ * @param encodeHashAsBase64
+ * @throws IllegalArgumentException
+ * if an unknown
+ */
+ public MessageDigestPasswordEncoder(String algorithm, boolean encodeHashAsBase64) throws IllegalArgumentException
+ {
+ this.algorithm = algorithm;
+ setEncodeHashAsBase64(encodeHashAsBase64);
+ // Validity Check
+ getMessageDigest();
+ }
+
+ /**
+ * Encodes the rawPass using a MessageDigest. If a salt is specified it will
+ * be merged with the password before encoding.
+ *
+ * @param rawPass
+ * The plain text password
+ * @param salt
+ * The salt to sprinkle
+ * @return Hex string of password digest (or base64 encoded string if
+ * encodeHashAsBase64 is enabled.
+ */
+ public String encodePassword(String rawPass, Object salt)
+ {
+ String saltedPass = mergePasswordAndSalt(rawPass, salt, false);
+
+ MessageDigest messageDigest = getMessageDigest();
+
+ byte[] digest;
+
+ try
+ {
+ digest = messageDigest.digest(saltedPass.getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException e)
+ {
+ throw new IllegalStateException("UTF-8 not supported!");
+ }
+
+ if (getEncodeHashAsBase64())
+ {
+ return new String(Base64.encodeBase64(digest));
+ } else
+ {
+ return new String(Hex.encodeHex(digest));
+ }
+ }
+
+ /**
+ * Get a MessageDigest instance for the given algorithm. Throws an
+ * IllegalArgumentException if algorithm is unknown
+ *
+ * @return MessageDigest instance
+ * @throws IllegalArgumentException
+ * if NoSuchAlgorithmException is thrown
+ */
+ protected final MessageDigest getMessageDigest() throws IllegalArgumentException
+ {
+ try
+ {
+ return MessageDigest.getInstance(algorithm);
+ } catch (NoSuchAlgorithmException e)
+ {
+ throw new IllegalArgumentException("No such algorithm [" + algorithm + "]");
+ }
+ }
+
+ /**
+ * Takes a previously encoded password and compares it with a rawpassword
+ * after mixing in the salt and encoding that value
+ *
+ * @param encPass
+ * previously encoded password
+ * @param rawPass
+ * plain text password
+ * @param salt
+ * salt to mix into password
+ * @return true or false
+ */
+ public boolean isPasswordValid(String encPass, String rawPass, Object salt)
+ {
+ String pass1 = "" + encPass;
+ String pass2 = encodePassword(rawPass, salt);
+
+ return pass1.equals(pass2);
+ }
+
+ public String getAlgorithm()
+ {
+ return algorithm;
+ }
+}
diff --git a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java
index f80cbcd668..196b1aa7ab 100644
--- a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java
+++ b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java
@@ -52,6 +52,7 @@ import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.EqualsHelper;
+import org.alfresco.util.GUID;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
@@ -68,13 +69,13 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In
private static final Log logger = LogFactory.getLog(RepositoryAuthenticationDao.class);
- private AuthorityService authorityService;
-
- private NodeService nodeService;
- private TenantService tenantService;
- private NamespacePrefixResolver namespacePrefixResolver;
- private PasswordEncoder passwordEncoder;
- private PolicyComponent policyComponent;
+ protected AuthorityService authorityService;
+ protected NodeService nodeService;
+ protected TenantService tenantService;
+ protected NamespacePrefixResolver namespacePrefixResolver;
+ protected PasswordEncoder passwordEncoder;
+ protected PasswordEncoder sha256PasswordEncoder;
+ protected PolicyComponent policyComponent;
private TransactionService transactionService;
@@ -120,6 +121,11 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In
this.passwordEncoder = passwordEncoder;
}
+ public void setSha256PasswordEncoder(PasswordEncoder passwordEncoder)
+ {
+ this.sha256PasswordEncoder = passwordEncoder;
+ }
+
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
@@ -199,10 +205,22 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In
GrantedAuthority[] gas = new GrantedAuthority[1];
gas[0] = new GrantedAuthorityImpl("ROLE_AUTHENTICATED");
-
- return new CacheEntry(userRef, new User(userName, password, getEnabled(userName, properties),
- !getHasExpired(userName, properties), true, !getLocked(userName, properties), gas),
- getCredentialsExpiryDate(userName, properties));
+
+ boolean isAdminAuthority = authorityService.isAdminAuthority(userName);
+
+ Date credentialsExpiryDate = getCredentialsExpiryDate(userName, properties, isAdminAuthority);
+ boolean credentialsHaveNotExpired = (credentialsExpiryDate == null || credentialsExpiryDate.compareTo(new Date()) >= 0);
+
+ UserDetails ud = new User(
+ userName,
+ password,
+ getEnabled(userName, properties, isAdminAuthority),
+ !getHasExpired(userName, properties, isAdminAuthority),
+ credentialsHaveNotExpired,
+ !getLocked(userName, properties, isAdminAuthority),
+ gas);
+
+ return new CacheEntry(userRef, ud, credentialsExpiryDate);
}
return null;
}
@@ -252,9 +270,10 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In
NodeRef typesNode = getUserFolderLocation(caseSensitiveUserName);
Map properties = new HashMap();
properties.put(ContentModel.PROP_USER_USERNAME, caseSensitiveUserName);
- String salt = null; // GUID.generate();
+ String salt = GUID.generate();
properties.put(ContentModel.PROP_SALT, salt);
- properties.put(ContentModel.PROP_PASSWORD, passwordEncoder.encodePassword(new String(rawPassword), salt));
+ properties.put(ContentModel.PROP_PASSWORD, passwordEncoder.encodePassword(new String(rawPassword), null));
+ properties.put(ContentModel.PROP_PASSWORD_SHA256, sha256PasswordEncoder.encodePassword(new String(rawPassword), salt));
properties.put(ContentModel.PROP_ACCOUNT_EXPIRES, Boolean.valueOf(false));
properties.put(ContentModel.PROP_CREDENTIALS_EXPIRE, Boolean.valueOf(false));
properties.put(ContentModel.PROP_ENABLED, Boolean.valueOf(true));
@@ -308,11 +327,13 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In
throw new AuthenticationException("User name does not exist: " + userName);
}
Map properties = nodeService.getProperties(userRef);
- String salt = null; // GUID.generate();
+ String salt = GUID.generate();
properties.remove(ContentModel.PROP_SALT);
properties.put(ContentModel.PROP_SALT, salt);
properties.remove(ContentModel.PROP_PASSWORD);
- properties.put(ContentModel.PROP_PASSWORD, passwordEncoder.encodePassword(new String(rawPassword), salt));
+ properties.put(ContentModel.PROP_PASSWORD, passwordEncoder.encodePassword(new String(rawPassword), null));
+ properties.remove(ContentModel.PROP_PASSWORD_SHA256);
+ properties.put(ContentModel.PROP_PASSWORD_SHA256, sha256PasswordEncoder.encodePassword(new String(rawPassword), salt));
nodeService.setProperties(userRef, properties);
}
@@ -342,7 +363,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In
/**
* @return Returns the user properties or null if there are none
*/
- private Map getUserProperties(String userName)
+ protected Map getUserProperties(String userName)
{
NodeRef userNodeRef = getUserOrNull(userName);
if (userNodeRef == null)
@@ -396,16 +417,20 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In
@Override
public boolean getAccountHasExpired(String userName)
{
- return getHasExpired(userName, null);
+ return getHasExpired(userName, null, null);
}
/**
* @param userName the username
* @param properties user properties or null to fetch them
*/
- private boolean getHasExpired(String userName, Map properties)
+ protected boolean getHasExpired(String userName, Map properties, Boolean isAdminAuthority)
{
- if (authorityService.isAdminAuthority(userName))
+ if (isAdminAuthority == null)
+ {
+ isAdminAuthority = authorityService.isAdminAuthority(userName);
+ }
+ if (isAdminAuthority)
{
return false; // Admin never expires
}
@@ -434,26 +459,30 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In
return false;
}
}
-
+
@Override
public boolean getLocked(String userName)
{
- return getLocked(userName, null);
+ return getLocked(userName, null, null);
}
-
+
@Override
public boolean getAccountlocked(String userName)
{
- return getLocked(userName, null);
+ return getLocked(userName, null, null);
}
-
+
/**
* @param userName the username
* @param properties user properties or null to fetch them
*/
- private boolean getLocked(String userName, Map properties)
+ protected boolean getLocked(String userName, Map properties, Boolean isAdminAuthority)
{
- if (authorityService.isAdminAuthority(userName))
+ if (isAdminAuthority == null)
+ {
+ isAdminAuthority = authorityService.isAdminAuthority(userName);
+ }
+ if (isAdminAuthority)
{
return false; // Admin is never locked
}
@@ -486,7 +515,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In
* @param userName the username
* @param properties user properties or null to fetch them
*/
- private boolean getCredentialsExpire(String userName, Map properties)
+ protected boolean getCredentialsExpire(String userName, Map properties)
{
if (authorityService.isAdminAuthority(userName))
{
@@ -534,15 +563,20 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In
{
return !loadUserByUsername(userName).isCredentialsNonExpired();
}
-
+
/**
* @param userName the username (never null
* @param properties the properties associated with the user or null to get them
+ * @param isAdminAuthority is admin authority
* @return Date on which the credentials expire or null if they never expire
*/
- private Date getCredentialsExpiryDate(String userName, Map properties)
+ private Date getCredentialsExpiryDate(String userName, Map properties, Boolean isAdminAuthority)
{
- if (authorityService.isAdminAuthority(userName))
+ if (isAdminAuthority == null)
+ {
+ isAdminAuthority = authorityService.isAdminAuthority(userName);
+ }
+ if (isAdminAuthority)
{
return null; // Admin never expires
}
@@ -550,33 +584,30 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In
{
properties = getUserProperties(userName);
}
- if (properties == null)
- {
- return null;
- }
if (DefaultTypeConverter.INSTANCE.booleanValue(properties.get(ContentModel.PROP_CREDENTIALS_EXPIRE)))
{
return DefaultTypeConverter.INSTANCE.convert(Date.class, properties.get(ContentModel.PROP_CREDENTIALS_EXPIRY_DATE));
- }
- else
- {
- return null;
}
+ return null;
}
-
+
@Override
public boolean getEnabled(String userName)
{
- return getEnabled(userName, null);
+ return getEnabled(userName, null, null);
}
-
+
/**
* @param userName the username
* @param properties the user's properties or null
*/
- private boolean getEnabled(String userName, Map properties)
+ protected boolean getEnabled(String userName, Map properties, Boolean isAdminAuthority)
{
- if (authorityService.isAdminAuthority(userName))
+ if (isAdminAuthority == null)
+ {
+ isAdminAuthority = authorityService.isAdminAuthority(userName);
+ }
+ if (isAdminAuthority)
{
return true; // Admin is always enabled
}
diff --git a/source/java/org/alfresco/repo/security/authentication/ShaPasswordEncoderImpl.java b/source/java/org/alfresco/repo/security/authentication/ShaPasswordEncoderImpl.java
new file mode 100644
index 0000000000..c024861014
--- /dev/null
+++ b/source/java/org/alfresco/repo/security/authentication/ShaPasswordEncoderImpl.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2005-2013 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
+package org.alfresco.repo.security.authentication;
+
+/**
+ *
+ * SHA implementation of PasswordEncoder.
+ *
+ *
+ * If a null
password is presented, it will be treated as an empty
+ * String
("") password.
+ *
+ *
+ * As SHA is a one-way hash, the salt can contain any characters. The default
+ * strength for the SHA encoding is SHA-1. If you wish to use higher strengths
+ * use the argumented constructor. {@link #ShaPasswordEncoder(int strength)}
+ *
+ *
+ * The applicationContext example...
+ *
+ *
+ * <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
+ * <constructor-arg value="256"/>
+ * </bean>
+ *
+ */
+public class ShaPasswordEncoderImpl extends MessageDigestPasswordEncoder
+{
+
+ /**
+ * Initializes the ShaPasswordEncoder for SHA-1 strength
+ */
+ public ShaPasswordEncoderImpl()
+ {
+ this(1);
+ }
+
+ /**
+ * Initialize the ShaPasswordEncoder with a given SHA stength as supported
+ * by the JVM EX:
+ * ShaPasswordEncoder encoder = new ShaPasswordEncoder(256);
+ * initializes with SHA-256
+ *
+ * @param strength
+ * EX: 1, 256, 384, 512
+ */
+ public ShaPasswordEncoderImpl(int strength)
+ {
+ super("SHA-" + strength);
+ }
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/security/authentication/userModel.xml b/source/java/org/alfresco/repo/security/authentication/userModel.xml
index f6ac271b58..c1923f2e00 100644
--- a/source/java/org/alfresco/repo/security/authentication/userModel.xml
+++ b/source/java/org/alfresco/repo/security/authentication/userModel.xml
@@ -40,6 +40,9 @@
d:text
+
+ d:text
+
d:boolean
diff --git a/source/java/org/alfresco/repo/security/authority/script/ScriptAuthorityService.java b/source/java/org/alfresco/repo/security/authority/script/ScriptAuthorityService.java
index 83a732464f..31e3165e2d 100644
--- a/source/java/org/alfresco/repo/security/authority/script/ScriptAuthorityService.java
+++ b/source/java/org/alfresco/repo/security/authority/script/ScriptAuthorityService.java
@@ -486,6 +486,8 @@ public class ScriptAuthorityService extends BaseScopableProcessorExtension
* @param paging Paging object with max number to return, and items to skip
* @param sortBy What to sort on (firstName, lastName or userName)
* @return the users matching the query
+ *
+ * @deprecated see People.getPeople(String filter, ScriptPagingDetails pagingRequest, String sortBy)
*/
public ScriptUser[] searchUsers(String nameFilter, ScriptPagingDetails paging, String sortBy)
{
diff --git a/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryFactory.java b/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryFactory.java
index 5e1ab4822e..8e1282e760 100644
--- a/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryFactory.java
+++ b/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryFactory.java
@@ -97,7 +97,8 @@ public class GetPeopleCannedQueryFactory extends AbstractCannedQueryFactory> sortPairs = new ArrayList>(sortProps.size());
for (Pair sortProp : sortProps)
{
- sortPairs.add(new Pair(sortProp.getFirst(), (sortProp.getSecond() ? SortOrder.ASCENDING : SortOrder.DESCENDING)));
+ boolean sortAsc = ((sortProp.getSecond() == null) || sortProp.getSecond());
+ sortPairs.add(new Pair(sortProp.getFirst(), (sortAsc ? SortOrder.ASCENDING : SortOrder.DESCENDING)));
}
cqsd = new CannedQuerySortDetails(sortPairs);
diff --git a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java
index 64fc45e021..8aa20b1ce4 100644
--- a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java
+++ b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java
@@ -53,8 +53,6 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.PermissionServiceSPI;
import org.alfresco.repo.tenant.TenantDomainMismatchException;
import org.alfresco.repo.tenant.TenantService;
-import org.alfresco.repo.tenant.TenantUtil;
-import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
@@ -80,10 +78,12 @@ import org.alfresco.service.cmr.search.LimitBy;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
+import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.NoSuchPersonException;
+import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.NamespaceService;
@@ -165,7 +165,9 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
private JavaBehaviour beforeDeleteNodeValidationBehaviour;
private boolean homeFolderCreationEager;
-
+
+ private boolean homeFolderCreationDisabled = false; // if true then home folders creation is disabled (ie. home folders are not created - neither eagerly nor lazily)
+
static
{
Set props = new HashSet();
@@ -363,6 +365,14 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
this.homeFolderCreationEager = homeFolderCreationEager;
}
+ /**
+ * Indicates if home folder creation should be disabled.
+ */
+ public void setHomeFolderCreationDisabled(boolean homeFolderCreationDisabled)
+ {
+ this.homeFolderCreationDisabled = homeFolderCreationDisabled;
+ }
+
public void setAclDAO(AclDAO aclDao)
{
this.aclDao = aclDao;
@@ -426,7 +436,35 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
*/
public NodeRef getPersonOrNull(String userName)
{
- return getPerson(userName, false, false);
+ return getPersonImpl(userName, false, false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PersonInfo getPerson(NodeRef personRef) throws NoSuchPersonException
+ {
+ Map props = null;
+ try
+ {
+ props = nodeService.getProperties(personRef);
+ }
+ catch (InvalidNodeRefException inre)
+ {
+ throw new NoSuchPersonException(personRef.toString());
+ }
+
+ // belts-and-braces
+ String username = (String)props.get(ContentModel.PROP_USERNAME);
+ if (getPersonOrNull(username) == null)
+ {
+ throw new NoSuchPersonException(personRef.toString());
+ }
+
+ return new PersonInfo(personRef,
+ username,
+ (String)props.get(ContentModel.PROP_FIRSTNAME),
+ (String)props.get(ContentModel.PROP_LASTNAME));
}
/**
@@ -434,31 +472,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
*/
public NodeRef getPerson(final String userName, final boolean autoCreateHomeFolderAndMissingPersonIfAllowed)
{
- return getPerson(userName, autoCreateHomeFolderAndMissingPersonIfAllowed, true);
- }
-
- private NodeRef getPerson(
- final String userName,
- final boolean autoCreateHomeFolderAndMissingPersonIfAllowed,
- final boolean exceptionOrNull)
- {
- // MT share - for activity service system callback
- if (tenantService.isEnabled() && (AuthenticationUtil.SYSTEM_USER_NAME.equals(AuthenticationUtil.getRunAsUser())) && tenantService.isTenantUser(userName))
- {
- final String tenantDomain = tenantService.getUserDomain(userName);
-
- return TenantUtil.runAsSystemTenant(new TenantRunAsWork()
- {
- public NodeRef doWork() throws Exception
- {
- return getPersonImpl(userName, autoCreateHomeFolderAndMissingPersonIfAllowed, exceptionOrNull);
- }
- }, tenantDomain);
- }
- else
- {
- return getPersonImpl(userName, autoCreateHomeFolderAndMissingPersonIfAllowed, exceptionOrNull);
- }
+ return getPersonImpl(userName, autoCreateHomeFolderAndMissingPersonIfAllowed, true);
}
private NodeRef getPersonImpl(
@@ -500,7 +514,13 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
*/
public boolean personExists(String caseSensitiveUserName)
{
- return getPersonOrNullImpl(caseSensitiveUserName) != null;
+ NodeRef person = getPersonOrNull(caseSensitiveUserName);
+ if (person != null)
+ {
+ // re: THOR-293
+ return permissionServiceSPI.hasPermission(person, PermissionService.READ) == AccessStatus.ALLOWED;
+ }
+ return false;
}
private NodeRef getPersonOrNullImpl(String searchUserName)
@@ -840,7 +860,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
private void makeHomeFolderIfRequired(NodeRef person)
{
- if (person != null)
+ if ((person != null) && (homeFolderCreationDisabled == false))
{
NodeRef homeFolder = DefaultTypeConverter.INSTANCE.convert(NodeRef.class, nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER));
if (homeFolder == null)
@@ -1701,7 +1721,7 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
// Make sure there is an authority entry - with a DB constraint for uniqueness
// aclDao.createAuthority(username);
- if (homeFolderCreationEager)
+ if ((homeFolderCreationEager) && (homeFolderCreationDisabled == false))
{
makeHomeFolderAsSystem(childAssocRef);
}
diff --git a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java
index ae5d5545ff..815a48b97e 100644
--- a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java
+++ b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2012 Alfresco Software Limited.
+ * Copyright (C) 2005-2013 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -39,6 +39,8 @@ import junit.framework.TestCase;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.management.subsystems.ChildApplicationContextManager;
import org.alfresco.repo.security.authentication.AuthenticationContext;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.person.PersonServiceImpl;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
@@ -115,6 +117,10 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
this.authenticationContext = (AuthenticationContext) ChainingUserRegistrySynchronizerTest.context
.getBean("authenticationContext");
+
+
+ // this.authenticationContext.setSystemUserAsCurrentUser();
+ //AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
this.authenticationContext.setSystemUserAsCurrentUser();
this.retryingTransactionHelper = (RetryingTransactionHelper) ChainingUserRegistrySynchronizerTest.context
@@ -298,8 +304,16 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase
public Object execute() throws Throwable
{
-
- ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(false, false, false);
+ // re: THOR-293 - note: use runAs else security context is cleared (=> current system user becomes null and personExists fails)
+ AuthenticationUtil.runAsSystem(new RunAsWork()
+ {
+ public Void doWork() throws Exception
+ {
+ ChainingUserRegistrySynchronizerTest.this.synchronizer.synchronize(false, false, false);
+ return null;
+ }
+ });
+
// Stay in the same transaction
assertExists("Z1", "U1");
assertEmailEquals("U1", "changeofemail@alfresco.com");
diff --git a/source/java/org/alfresco/repo/site/SitesPermissionCleaner.java b/source/java/org/alfresco/repo/site/SitesPermissionCleaner.java
index 047ea155d4..5863763b5c 100644
--- a/source/java/org/alfresco/repo/site/SitesPermissionCleaner.java
+++ b/source/java/org/alfresco/repo/site/SitesPermissionCleaner.java
@@ -92,7 +92,7 @@ public class SitesPermissionCleaner
public void cleanSitePermissions(final NodeRef targetNode, SiteInfo containingSite)
{
- if (nodeService.exists(targetNode))
+ if (nodeDAO.exists(targetNode))
{
// We can calculate the containing site at the start of a recursive call & then reuse it on subsequent calls.
if (containingSite == null)
diff --git a/source/java/org/alfresco/repo/subscriptions/SubscriptionServiceImpl.java b/source/java/org/alfresco/repo/subscriptions/SubscriptionServiceImpl.java
index 3040ee482b..40d7d88832 100644
--- a/source/java/org/alfresco/repo/subscriptions/SubscriptionServiceImpl.java
+++ b/source/java/org/alfresco/repo/subscriptions/SubscriptionServiceImpl.java
@@ -28,10 +28,12 @@ import org.alfresco.model.ContentModel;
import org.alfresco.query.PagingRequest;
import org.alfresco.repo.action.executer.MailActionExecuter;
import org.alfresco.repo.activities.ActivityType;
+import org.alfresco.repo.dictionary.RepositoryLocation;
import org.alfresco.repo.domain.subscriptions.SubscriptionsDAO;
import org.alfresco.repo.search.SearcherException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.permissions.AccessDeniedException;
+import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.activities.ActivityService;
@@ -73,12 +75,14 @@ public class SubscriptionServiceImpl implements SubscriptionService
private static final String USER_USERNAME = "userUserName";
private static final String FOLLOWING_COUNT = "followingCount";
private static final String FOLLOWER_COUNT = "followerCount";
-
+
private static final String SUBSCRIBER_FIRSTNAME = "subscriberFirstName";
private static final String SUBSCRIBER_LASTNAME = "subscriberLastName";
private static final String SUBSCRIBER_USERNAME = "subscriberUserName";
private static final String NODE = "node";
-
+
+ private static final String TENANT_DOMAIN = "tenantDomain";
+
protected SubscriptionsDAO subscriptionsDAO;
protected NodeService nodeService;
protected PersonService personService;
@@ -90,6 +94,8 @@ public class SubscriptionServiceImpl implements SubscriptionService
protected FileFolderService fileFolderService;
protected boolean active;
+
+ private RepositoryLocation followingEmailTemplateLocation;
/**
* Sets the subscriptions DAO.
@@ -167,7 +173,13 @@ public class SubscriptionServiceImpl implements SubscriptionService
{
this.active = active;
}
-
+
+ public void setFollowingEmailTemplateLocation(RepositoryLocation followingEmailTemplateLocation)
+ {
+ this.followingEmailTemplateLocation = followingEmailTemplateLocation;
+ }
+
+
@Override
public PagingSubscriptionResults getSubscriptions(String userId, SubscriptionItemTypeEnum type,
PagingRequest pagingRequest)
@@ -472,7 +484,7 @@ public class SubscriptionServiceImpl implements SubscriptionService
return;
}
- NodeRef templateNodeRef = getEmailTemplateRef();
+ String templateNodeRef = getEmailTemplateRef();
if (templateNodeRef == null)
{
// we can't send an email without template
@@ -507,9 +519,16 @@ public class SubscriptionServiceImpl implements SubscriptionService
} catch (Exception e)
{
}
-
+
+ // Add tenant, if in context of tenant
+ String tenantDomain = TenantUtil.getCurrentDomain();
+ if (tenantDomain != null)
+ {
+ model.put(TENANT_DOMAIN, tenantDomain);
+ }
+
Action mail = actionService.createAction(MailActionExecuter.NAME);
-
+
mail.setParameterValue(MailActionExecuter.PARAM_TO, emailAddress);
mail.setParameterValue(MailActionExecuter.PARAM_SUBJECT, subjectText);
mail.setParameterValue(MailActionExecuter.PARAM_TEMPLATE, templateNodeRef);
@@ -523,32 +542,48 @@ public class SubscriptionServiceImpl implements SubscriptionService
* Returns the NodeRef of the email template or null
if the
* template coudln't be found.
*/
- protected NodeRef getEmailTemplateRef()
+ protected String getEmailTemplateRef()
{
- // Find the following email template
- String xpath = "app:company_home/app:dictionary/app:email_templates/app:following/cm:following-email.html.ftl";
- try
+ String locationType = followingEmailTemplateLocation.getQueryLanguage();
+
+ if (locationType.equals(SearchService.LANGUAGE_XPATH))
{
- NodeRef rootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
- List nodeRefs = searchService.selectNodes(rootNodeRef, xpath, null, namespaceService, false);
- if (nodeRefs.size() > 1)
+ // Find the following email template
+ StoreRef store = followingEmailTemplateLocation.getStoreRef();
+ String xpath = followingEmailTemplateLocation.getPath();
+
+ try
{
- logger.error("Found too many email templates using: " + xpath);
- nodeRefs = Collections.singletonList(nodeRefs.get(0));
- } else if (nodeRefs.size() == 0)
+ NodeRef rootNodeRef = nodeService.getRootNode(store);
+ List nodeRefs = searchService.selectNodes(rootNodeRef, xpath, null, namespaceService, false);
+ if (nodeRefs.size() > 1)
+ {
+ logger.error("Found too many email templates using: " + xpath);
+ nodeRefs = Collections.singletonList(nodeRefs.get(0));
+ }
+ else if (nodeRefs.size() == 0)
+ {
+ logger.error("Cannot find the email template using " + xpath);
+ return null;
+ }
+ // Now localise this
+ return fileFolderService.getLocalizedSibling(nodeRefs.get(0)).toString();
+ }
+ catch (SearcherException e)
{
- logger.error("Cannot find the email template using " + xpath);
- return null;
+ logger.error("Cannot find the email template!", e);
}
- // Now localise this
- NodeRef base = nodeRefs.get(0);
- NodeRef local = fileFolderService.getLocalizedSibling(base);
- return local;
- } catch (SearcherException e)
- {
- logger.error("Cannot find the email template!", e);
+
+ return null;
+ }
+ else if (locationType.equals(RepositoryLocation.LANGUAGE_CLASSPATH))
+ {
+ return followingEmailTemplateLocation.getPath();
+ }
+ else
+ {
+ logger.error("Unsupported location type: "+locationType);
+ return null;
}
-
- return null;
}
}
diff --git a/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java b/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java
index 6b3dcf7265..9abe8b30c4 100644
--- a/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java
+++ b/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java
@@ -19,7 +19,6 @@
package org.alfresco.repo.tenant;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
@@ -33,6 +32,8 @@ import org.alfresco.repo.domain.tenant.TenantAdminDAO;
import org.alfresco.repo.domain.tenant.TenantEntity;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -44,6 +45,8 @@ import org.springframework.context.ApplicationContextAware;
*/
public abstract class AbstractTenantRoutingContentStore extends AbstractRoutingContentStore implements ApplicationContextAware, TenantRoutingContentStore
{
+ private static Log logger = LogFactory.getLog(AbstractTenantRoutingContentStore.class);
+
private String defaultRootDirectory;
private TenantAdminDAO tenantAdminDAO;
protected TenantService tenantService;
@@ -102,14 +105,14 @@ public abstract class AbstractTenantRoutingContentStore extends AbstractRoutingC
@Override
public List getAllStores()
{
+ final List allEnabledStores = new ArrayList();
+
if (tenantService.isEnabled())
{
String currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
if ((currentUser == null) || (tenantService.getBaseNameUser(currentUser).equals(AuthenticationUtil.getSystemUserName())))
{
// return enabled stores across all tenants, if running as system/null user, for example, ContentStoreCleaner scheduled job
- final List allEnabledStores = new ArrayList();
-
List tenants = tenantAdminDAO.listTenants();
for (TenantEntity tenant : tenants)
{
@@ -127,16 +130,18 @@ public abstract class AbstractTenantRoutingContentStore extends AbstractRoutingC
}
}
- if (allEnabledStores.size() > 0)
+ if (logger.isDebugEnabled())
{
- allEnabledStores.add(getTenantContentStore());
- return allEnabledStores;
+ logger.debug("getAllStores called without tenant ctx ("+tenants.size()+" tenants)");
}
// drop through to ensure default content store has been init'ed
}
}
- return Arrays.asList(getTenantContentStore());
+
+ allEnabledStores.add(getTenantContentStore());
+
+ return allEnabledStores;
}
private ContentStore getTenantContentStore()
diff --git a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java
index e9ce9e0846..edf8f3a546 100644
--- a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java
+++ b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java
@@ -307,17 +307,27 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo
throw new AlfrescoRuntimeException("MT: cannot start tenants - TenantRoutingContentStore is not configured AND not all tenants use co-mingled content store");
}
+ String tenantDomain = tenant.getTenantDomain();
+
if (tenant.isEnabled())
{
- // this will also call tenant deployers registered so far ...
- enableTenant(tenant.getTenantDomain(), true);
+ // notify tenant deployers registered so far ...
+ notifyAfterEnableTenant(tenantDomain);
enabledCount++;
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Tenant enabled: " + tenantDomain);
+ }
}
else
{
- // explicitly disable, without calling disableTenant callback
- disableTenant(tenant.getTenantDomain(), false);
disabledCount++;
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Tenant disabled: " + tenantDomain);
+ }
}
}
@@ -377,7 +387,7 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo
/**
* @see TenantAdminService.createTenant()
*/
- public void createTenant(final String tenantDomainIn, final char[] tenantAdminRawPassword, String contentRootPath, String dbUrl)
+ public void createTenant(final String tenantDomainIn, final char[] tenantAdminRawPassword, String contentRootPath, final String dbUrl)
{
ParameterCheck.mandatory("tenantAdminRawPassword", tenantAdminRawPassword);
@@ -429,65 +439,72 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo
AuthenticationUtil.pushAuthentication();
AuthenticationUtil.setFullyAuthenticatedUser(getSystemUser(tenantDomain));
- dictionaryComponent.init();
-
- if (tenantFileContentStore instanceof TenantDeployer)
+ TenantUtil.runAsSystemTenant(new TenantRunAsWork