ALF-8968 - Extend ScriptAuthorityService to also allow the fetching of users, so that the JS layer can find people without needing lucene queries

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@28423 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Nick Burch
2011-06-16 11:10:58 +00:00
parent 9dd2d98dd8
commit 5aacf7e84e
5 changed files with 542 additions and 63 deletions

View File

@@ -100,7 +100,9 @@
<property name="extensionName"> <property name="extensionName">
<value>groups</value> <value>groups</value>
</property> </property>
<property name="authorityService" ref="AuthorityService"/> <property name="serviceRegistry">
<ref bean="ServiceRegistry" />
</property>
</bean> </bean>
</beans> </beans>

View File

@@ -20,13 +20,23 @@ package org.alfresco.repo.security.authority.script;
import static org.alfresco.repo.security.authority.script.ScriptGroup.makeScriptGroups; import static org.alfresco.repo.security.authority.script.ScriptGroup.makeScriptGroups;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.jscript.BaseScopableProcessorExtension; import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
import org.alfresco.repo.security.authority.UnknownAuthorityException; import org.alfresco.repo.security.authority.UnknownAuthorityException;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.NoSuchPersonException;
import org.alfresco.service.cmr.security.PagingPersonResults;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.alfresco.util.ScriptPagingDetails; import org.alfresco.util.ScriptPagingDetails;
/** /**
@@ -38,12 +48,18 @@ import org.alfresco.util.ScriptPagingDetails;
*/ */
public class ScriptAuthorityService extends BaseScopableProcessorExtension public class ScriptAuthorityService extends BaseScopableProcessorExtension
{ {
/** The service */ /** The group/authority service */
private AuthorityService authorityService; private AuthorityService authorityService;
/** The person service */
private PersonService personService;
public void setAuthorityService(AuthorityService authorityService) private ServiceRegistry serviceRegistry;
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{ {
this.authorityService = authorityService; this.serviceRegistry = serviceRegistry;
this.authorityService = serviceRegistry.getAuthorityService();
this.personService = serviceRegistry.getPersonService();
} }
public AuthorityService getAuthorityService() public AuthorityService getAuthorityService()
@@ -307,4 +323,82 @@ public class ScriptAuthorityService extends BaseScopableProcessorExtension
} }
return makeScriptGroups(authorities, paging, sortBy, authorityService); return makeScriptGroups(authorities, paging, sortBy, authorityService);
} }
/**
* Get a user given their username
* @param username, the username of the user
* @return the user or null if they can't be found
*/
public ScriptUser getUser(String username)
{
try
{
NodeRef person = personService.getPerson(username, false);
return new ScriptUser(username, person, serviceRegistry);
}
catch(NoSuchPersonException e)
{
return null;
}
}
/**
* Search for users
* Includes paging parameters to limit size of results returned.
*
* @param nameFilter partial match of the name (username, first name, last name). If empty then matches everyone.
* @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
*/
public ScriptUser[] searchUsers(String nameFilter, ScriptPagingDetails paging, String sortBy)
{
// Build the filter
List<Pair<QName,String>> filter = new ArrayList<Pair<QName,String>>();
filter.add(new Pair<QName, String>(ContentModel.PROP_FIRSTNAME, nameFilter));
filter.add(new Pair<QName, String>(ContentModel.PROP_LASTNAME, nameFilter));
filter.add(new Pair<QName, String>(ContentModel.PROP_USERNAME, nameFilter));
// Build the sorting. The user controls the primary sort, we supply
// additional ones automatically
List<Pair<QName,Boolean>> sort = new ArrayList<Pair<QName,Boolean>>();
if("lastName".equals(sortBy))
{
sort.add(new Pair<QName, Boolean>(ContentModel.PROP_LASTNAME, true));
sort.add(new Pair<QName, Boolean>(ContentModel.PROP_FIRSTNAME, true));
sort.add(new Pair<QName, Boolean>(ContentModel.PROP_USERNAME, true));
}
else if("firstName".equals(sortBy))
{
sort.add(new Pair<QName, Boolean>(ContentModel.PROP_FIRSTNAME, true));
sort.add(new Pair<QName, Boolean>(ContentModel.PROP_LASTNAME, true));
sort.add(new Pair<QName, Boolean>(ContentModel.PROP_USERNAME, true));
}
else
{
sort.add(new Pair<QName, Boolean>(ContentModel.PROP_USERNAME, true));
sort.add(new Pair<QName, Boolean>(ContentModel.PROP_FIRSTNAME, true));
sort.add(new Pair<QName, Boolean>(ContentModel.PROP_LASTNAME, true));
}
// Do the search
PagingPersonResults results = personService.getPeople(filter, true, sort, paging);
// Record the size of the results
paging.setTotalItems(results);
// Now wrap up the users
List<NodeRef> nodes = results.getPage();
ScriptUser[] users = new ScriptUser[nodes.size()];
for(int i=0; i<users.length; i++)
{
NodeRef node = nodes.get(i);
String username = (String)serviceRegistry.getNodeService().getProperty(
node, ContentModel.PROP_USERNAME
);
users[i] = new ScriptUser(username, node, serviceRegistry);
}
return users;
}
} }

View File

@@ -0,0 +1,319 @@
/*
* Copyright (C) 2005-2010 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.security.authority.script;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import javax.transaction.Status;
import javax.transaction.UserTransaction;
import junit.framework.TestCase;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.domain.permissions.AclDAO;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
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.PersonService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.ScriptPagingDetails;
import org.springframework.context.ApplicationContext;
/**
* Tests for the Script wrapper for the Authority Service,
* ScriptAuthorityService
*/
public class ScriptAuthorityServiceTest extends TestCase
{
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private static final String GROUP_A = "testGroupA";
private static final String GROUP_A_FULL = "GROUP_testGroupA";
private static final String GROUP_B = "testGroupB";
private static final String GROUP_B_FULL = "GROUP_testGroupB";
private static final String GROUP_C = "testGroupC";
private static final String GROUP_C_FULL = "GROUP_testGroupC";
private static final String USER_A = "testUserA";
private static final String USER_B = "testUserB";
private static final String USER_C = "testUserC";
private AuthenticationComponent authenticationComponentImpl;
private MutableAuthenticationService authenticationService;
private MutableAuthenticationDao authenticationDAO;
private AuthorityService authorityService;
private AuthorityService pubAuthorityService;
private PersonService personService;
private UserTransaction tx;
private AclDAO aclDaoComponent;
private NodeService nodeService;
private ScriptAuthorityService service;
public ScriptAuthorityServiceTest()
{
super();
}
public void setUp() throws Exception
{
if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_NONE)
{
throw new AlfrescoRuntimeException(
"A previous tests did not clean up transaction: " +
AlfrescoTransactionSupport.getTransactionId());
}
authenticationComponentImpl = (AuthenticationComponent) ctx.getBean("authenticationComponent");
authenticationService = (MutableAuthenticationService) ctx.getBean("authenticationService");
authorityService = (AuthorityService) ctx.getBean("authorityService");
pubAuthorityService = (AuthorityService) ctx.getBean("AuthorityService");
personService = (PersonService) ctx.getBean("personService");
authenticationDAO = (MutableAuthenticationDao) ctx.getBean("authenticationDao");
aclDaoComponent = (AclDAO) ctx.getBean("aclDAO");
nodeService = (NodeService) ctx.getBean("nodeService");
service = (ScriptAuthorityService) ctx.getBean("authorityServiceScript");
authenticationComponentImpl.setSystemUserAsCurrentUser();
// Clean up the users if they're already there
TransactionService transactionService = (TransactionService) ctx.getBean(ServiceRegistry.TRANSACTION_SERVICE.getLocalName());
tx = transactionService.getUserTransaction();
tx.begin();
for (String user : new String[] { USER_A, USER_B, USER_C })
{
if (personService.personExists(user))
{
NodeRef person = personService.getPerson(user);
NodeRef hf = DefaultTypeConverter.INSTANCE.convert(NodeRef.class, nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER));
if (hf != null)
{
nodeService.deleteNode(hf);
}
aclDaoComponent.deleteAccessControlEntries(user);
personService.deletePerson(user);
}
if (authenticationDAO.userExists(user))
{
authenticationDAO.deleteUser(user);
}
}
// And the group
if(authorityService.authorityExists(GROUP_A_FULL))
{
authorityService.deleteAuthority(GROUP_A_FULL);
}
if(authorityService.authorityExists(GROUP_B_FULL))
{
authorityService.deleteAuthority(GROUP_B_FULL);
}
if(authorityService.authorityExists(GROUP_C_FULL))
{
authorityService.deleteAuthority(GROUP_C_FULL);
}
tx.commit();
// Now re-create them
tx = transactionService.getUserTransaction();
tx.begin();
authorityService.createAuthority(AuthorityType.GROUP, GROUP_A);
authorityService.createAuthority(AuthorityType.GROUP, GROUP_B);
authorityService.createAuthority(AuthorityType.GROUP, GROUP_C);
for (String user : new String[] { USER_A, USER_B, USER_C })
{
if (!authenticationDAO.userExists(user))
{
authenticationService.createAuthentication(user, user.toCharArray());
}
Map<QName,Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_USERNAME, user);
props.put(ContentModel.PROP_FIRSTNAME, user);
props.put(ContentModel.PROP_LASTNAME, "Last_" + user);
personService.createPerson(props);
}
}
@Override
protected void tearDown() throws Exception
{
if ((tx.getStatus() == Status.STATUS_ACTIVE) || (tx.getStatus() == Status.STATUS_MARKED_ROLLBACK))
{
tx.rollback();
}
AuthenticationUtil.clearCurrentSecurityContext();
super.tearDown();
}
public void testBasics()
{
// Should return the same count for the root groups
int count = pubAuthorityService.getAllRootAuthorities(AuthorityType.GROUP).size();
ScriptGroup[] groups = service.getAllRootGroups();
assertEquals(count, groups.length);
// And our test ones are in there
ScriptGroup groupA = null;
ScriptGroup groupB = null;
ScriptGroup groupC = null;
for(ScriptGroup group : groups)
{
if(group.getShortName().equals(GROUP_A)) groupA = group;
if(group.getShortName().equals(GROUP_B)) groupB = group;
if(group.getShortName().equals(GROUP_C)) groupC = group;
}
assertNotNull(GROUP_A + " not found in " + groups, groupA);
assertNotNull(GROUP_B + " not found in " + groups, groupB);
assertNotNull(GROUP_C + " not found in " + groups, groupC);
// Check group A in more detail
assertEquals(GROUP_A, groupA.getShortName());
assertEquals(GROUP_A, groupA.getDisplayName());
assertEquals(GROUP_A_FULL, groupA.getFullName());
}
public void testZones()
{
}
public void testFindGroups()
{
}
public void testGroupUsers()
{
}
public void testUsers()
{
// Getting by username
ScriptUser userA = service.getUser(USER_A);
ScriptUser userB = service.getUser(USER_B);
ScriptUser userC = service.getUser(USER_C);
ScriptUser userNA = service.getUser("DOESnotEXISTinTHEsystem");
assertNotNull(userA);
assertNotNull(userB);
assertNotNull(userC);
assertNull(userNA);
// Check the details on one user
assertEquals(USER_A, userA.getFullName());
assertEquals(USER_A, userA.getShortName());
assertEquals(USER_A, userA.getDisplayName());
NodeRef nodeA = personService.getPerson(USER_A, false);
assertNotNull(nodeA);
// Check the person
assertEquals(nodeA, userA.getPersonNodeRef());
assertEquals(nodeA, userA.getPerson().getNodeRef());
assertEquals(USER_A, userA.getPerson().getProperties().get("userName"));
assertEquals(USER_A, userA.getPerson().getProperties().get("firstName"));
}
public void testFindUsers()
{
// Try to find admin
ScriptUser[] users = service.searchUsers(
AuthenticationUtil.getAdminUserName(),
new ScriptPagingDetails(10, 0),
"userName"
);
assertTrue("Admin not found", users.length > 0);
// Try to find our test users
users = service.searchUsers(
USER_A.substring(0, USER_A.length()-1),
new ScriptPagingDetails(10, 0),
"userName"
);
assertEquals("Users count wrong " + users, 3, users.length);
// Check on the username sorting
assertEquals(USER_A, users[0].getPerson().getProperties().get("userName"));
assertEquals(USER_B, users[1].getPerson().getProperties().get("userName"));
assertEquals(USER_C, users[2].getPerson().getProperties().get("userName"));
// Tweak names and re-check
ScriptUser userA = users[0];
ScriptUser userB = users[1];
ScriptUser userC = users[2];
nodeService.setProperty(userB.getPersonNodeRef(), ContentModel.PROP_FIRSTNAME, "bbbbFIRST");
nodeService.setProperty(userC.getPersonNodeRef(), ContentModel.PROP_LASTNAME, "ccccLAST");
users = service.searchUsers(
USER_A.substring(0, USER_A.length()-1),
new ScriptPagingDetails(10, 0),
"userName"
);
assertEquals("Users count wrong " + users, 3, users.length);
assertEquals(USER_A, users[0].getPerson().getProperties().get("userName"));
assertEquals(USER_B, users[1].getPerson().getProperties().get("userName"));
assertEquals(USER_C, users[2].getPerson().getProperties().get("userName"));
// Check sorting on firstname
users = service.searchUsers(
USER_A.substring(0, USER_A.length()-1),
new ScriptPagingDetails(10, 0),
"firstName"
);
assertEquals("Users count wrong " + users, 3, users.length);
assertEquals(USER_B, users[0].getPerson().getProperties().get("userName"));
assertEquals(USER_A, users[1].getPerson().getProperties().get("userName"));
assertEquals(USER_C, users[2].getPerson().getProperties().get("userName"));
// And lastname
users = service.searchUsers(
USER_A.substring(0, USER_A.length()-1),
new ScriptPagingDetails(10, 0),
"lastName"
);
assertEquals("Users count wrong " + users, 3, users.length);
assertEquals(USER_C, users[0].getPerson().getProperties().get("userName"));
assertEquals(USER_A, users[1].getPerson().getProperties().get("userName"));
assertEquals(USER_B, users[2].getPerson().getProperties().get("userName"));
}
}

View File

@@ -20,8 +20,10 @@ package org.alfresco.repo.security.authority.script;
import java.io.Serializable; import java.io.Serializable;
import org.alfresco.repo.jscript.ScriptNode;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
/** /**
* The Script user is a USER authority exposed to the scripting API * The Script user is a USER authority exposed to the scripting API
@@ -30,51 +32,104 @@ import org.alfresco.service.cmr.security.AuthorityType;
*/ */
public class ScriptUser implements Authority, Serializable public class ScriptUser implements Authority, Serializable
{ {
private static final long serialVersionUID = 7865300693011208293L;
private transient ServiceRegistry serviceRegistry;
private transient AuthorityService authorityService; private transient AuthorityService authorityService;
private ScriptAuthorityType authorityType = ScriptAuthorityType.USER; private ScriptAuthorityType authorityType = ScriptAuthorityType.USER;
private String userName;
private String shortName; private String shortName;
private String fullName;
private String displayName; private String displayName;
private NodeRef personNodeRef;
public ScriptUser(String fullName, AuthorityService authorityService) public ScriptUser(String userName, NodeRef personNodeRef, ServiceRegistry serviceRegistry)
{ {
this.authorityService = authorityService; this.serviceRegistry = serviceRegistry;
this.fullName = fullName; this.authorityService = serviceRegistry.getAuthorityService();
shortName = authorityService.getShortName(fullName);
displayName = authorityService.getAuthorityDisplayName(fullName); this.personNodeRef = personNodeRef;
//isInternal = authorityService. this.userName = userName;
shortName = authorityService.getShortName(userName);
displayName = authorityService.getAuthorityDisplayName(userName);
} }
public void setAuthorityType(ScriptAuthorityType authorityType) { /**
* @deprecated The ServiceRegistry is now needed
*/
public ScriptUser(String userName, AuthorityService authorityService)
{
this.authorityService = authorityService;
this.userName = userName;
shortName = authorityService.getShortName(userName);
displayName = authorityService.getAuthorityDisplayName(userName);
}
public void setAuthorityType(ScriptAuthorityType authorityType)
{
this.authorityType = authorityType; this.authorityType = authorityType;
} }
public ScriptAuthorityType getAuthorityType() { public ScriptAuthorityType getAuthorityType()
{
return authorityType; return authorityType;
} }
public void setShortName(String shortName) { public void setShortName(String shortName)
{
this.shortName = shortName; this.shortName = shortName;
} }
public String getShortName() { public String getShortName()
{
return shortName; return shortName;
} }
public void setFullName(String fullName) { public void setFullName(String fullName)
this.fullName = fullName; {
this.userName = fullName;
} }
public String getFullName() { public String getFullName()
return fullName; {
return userName;
} }
public void setDisplayName(String displayName) { /**
* Return the User Name, also known as the
* Authority Full Name
*/
public String getUserName()
{
return userName;
}
public void setDisplayName(String displayName)
{
this.displayName = displayName; this.displayName = displayName;
} }
public String getDisplayName() { public String getDisplayName()
{
return displayName; return displayName;
} }
/**
* Return the NodeRef of the person
*/
public NodeRef getPersonNodeRef()
{
if (personNodeRef == null)
{
// Lazy lookup for Authority based creation
personNodeRef = authorityService.getAuthorityNodeRef(userName);
}
return personNodeRef;
}
/**
* Return a ScriptNode wrapping the person
*/
public ScriptNode getPerson()
{
return new ScriptNode(getPersonNodeRef(), serviceRegistry);
}
} }

View File

@@ -82,10 +82,18 @@ public class ScriptPagingDetails extends PagingRequest
*/ */
public <R> void setTotalItems(PagingResults<R> results) public <R> void setTotalItems(PagingResults<R> results)
{ {
if(results.getTotalResultCount() == null)
{
// No count calculated
this.totalItems = -1;
this.confidence = ItemsSizeConfidence.UNKNOWN;
}
else
{
// Get the total count and confidence
Integer min = results.getTotalResultCount().getFirst(); Integer min = results.getTotalResultCount().getFirst();
Integer max = results.getTotalResultCount().getSecond(); Integer max = results.getTotalResultCount().getSecond();
// Get the total count and confidence
if(min == null) if(min == null)
{ {
this.totalItems = -1; this.totalItems = -1;
@@ -107,6 +115,7 @@ public class ScriptPagingDetails extends PagingRequest
this.totalItemsRangeMax = max; this.totalItemsRangeMax = max;
this.confidence = ItemsSizeConfidence.RANGE; this.confidence = ItemsSizeConfidence.RANGE;
} }
}
// Finally record the query execution ID // Finally record the query execution ID
setQueryExecutionId(results.getQueryExecutionId()); setQueryExecutionId(results.getQueryExecutionId());