Various changes to Invite Service

- added functionality to forbid a user from cancelling a pending invite, if he/she is not a Site Manager for the site associated with that invite
- added CancelInviteAction workflow action class, with functionality to clean up disabled invitee account and invitee person node when (upon cancellation) there are no outstanding invites for that invitee.
- added various unit tests to InviteServiceTest for above functionality
- added automatic redeployment of invite process definition in setup(...) method of InviteServiceTest class
- other minor refinements to InviteServiceTest
- some minor refactoring to Invite Service to prevent code duplication amongst various invite web scripts and workflow action classes


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@10894 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Glen Johnson
2008-09-15 00:28:21 +00:00
parent 7d25050a1e
commit a7076e1fde
10 changed files with 396 additions and 108 deletions

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.web.scripts.invite;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.repo.site.SiteModel;
import org.alfresco.repo.site.SiteService;
import org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.web.scripts.Status;
import org.alfresco.web.scripts.WebScriptException;
import org.jbpm.graph.exe.ExecutionContext;
import org.springframework.beans.factory.BeanFactory;
/**
* This class contains logic that gets executed when
* the wf:invitePendingTask in the invite workflow gets cancelled
* along the "cancel" transition
*
* @author glen johnson at alfresco com
*/
public class CancelInviteAction extends JBPMSpringActionHandler
{
private static final long serialVersionUID = 776961141883350908L;
private MutableAuthenticationDao mutableAuthenticationDao;
private PersonService personService;
private WorkflowService workflowService;
private SiteService siteService;
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory)
*/
@Override
protected void initialiseHandler(BeanFactory factory)
{
ServiceRegistry services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
mutableAuthenticationDao = (MutableAuthenticationDao) factory.getBean("authenticationDao");
personService = (PersonService) services.getPersonService();
workflowService = (WorkflowService) services.getWorkflowService();
siteService = (SiteService) services.getSiteService();
}
/* (non-Javadoc)
* @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext)
*/
@SuppressWarnings("unchecked")
public void execute(final ExecutionContext executionContext) throws Exception
{
// get the invitee user name and site short name variables off the execution context
final String inviteeUserName = (String) executionContext.getVariable(
InviteWorkflowModel.wfVarInviteeUserName);
final String siteShortName = (String) executionContext.getVariable(
InviteWorkflowModel.wfVarSiteShortName);
final String inviteId = (String) executionContext.getVariable(
InviteWorkflowModel.wfVarWorkflowInstanceId);
// throw http status 'forbidden' Web Script Exception if current user is not a Site Manager of the site
// associated with the invite (identified by inviteID)
String currentUserName = AuthenticationUtil.getCurrentUserName();
String currentUserSiteRole = this.siteService.getMembersRole(siteShortName, currentUserName);
if ((currentUserSiteRole == null) || (currentUserSiteRole.equals(SiteModel.SITE_MANAGER) == false))
{
throw new WebScriptException(Status.STATUS_FORBIDDEN,
"Current user '" + currentUserName + "' cannot cancel invite having ID '" + inviteId
+ "' because he\\she is not a Site Manager of the site with short name:'" + siteShortName
+ "'");
}
// clean up invitee's user account and person node if they are not in use i.e.
// account is still disabled and there are no pending invites outstanding for the
// invitee
InviteHelper.cleanUpStaleInviteeResources(inviteeUserName, mutableAuthenticationDao, personService,
workflowService);
}
}

View File

@@ -31,6 +31,8 @@ import java.util.Map;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.repo.security.authentication.PasswordGenerator;
import org.alfresco.repo.security.authentication.UserNameGenerator;
@@ -43,10 +45,10 @@ import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowException;
import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
@@ -400,13 +402,23 @@ public class Invite extends DeclarativeWebScript
// create a person node for the invitee with generated invitee user name
// and other provided person property values
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
final Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
properties.put(ContentModel.PROP_USERNAME, inviteeUserName);
properties.put(ContentModel.PROP_FIRSTNAME, inviteeFirstName);
properties.put(ContentModel.PROP_LASTNAME, inviteeLastName);
properties.put(ContentModel.PROP_EMAIL, inviteeEmail);
this.personService.createPerson(properties);
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
personService.createPerson(properties);
return null;
}
}, AuthenticationUtil.getSystemUserName());
return inviteeUserName;
}
@@ -676,8 +688,9 @@ public class Invite extends DeclarativeWebScript
}
/**
* Cancels pending invite. Note that only the Inviter should cancel the
* pending invite.
* Cancels pending invite. Note that only a Site Manager of the
* site associated with the pending invite should be able to cancel that
* invite
*
* @param model
* model to add objects to, which will be passed to the template
@@ -695,10 +708,31 @@ public class Invite extends DeclarativeWebScript
"Given invite ID " + inviteId + " null or empty");
}
// cancel the workflow with the given invite ID
// which is actually the workflow ID
this.workflowService.cancelWorkflow(inviteId);
try
{
// complete the wf:invitePendingTask along the 'cancel' transition because the invitation has been cancelled
InviteHelper.completeInviteTask(inviteId, InviteWorkflowModel.WF_INVITE_TASK_INVITE_PENDING,
InviteWorkflowModel.WF_TRANSITION_CANCEL, this.workflowService);
}
catch (WorkflowException wfe)
{
//
// If the indirect cause of workflow exception is a WebScriptException object
// then throw this directly, otherwise it will not be picked up by unit tests
//
Throwable indirectCause = wfe.getCause().getCause();
if (indirectCause instanceof WebScriptException)
{
WebScriptException wse = (WebScriptException) indirectCause;
throw wse;
}
else
{
throw wfe;
}
}
// add model properties for template to render
model.put(MODEL_PROP_KEY_ACTION, ACTION_CANCEL);
model.put(MODEL_PROP_KEY_INVITE_ID, inviteId);

View File

@@ -29,6 +29,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.site.SiteInfo;
import org.alfresco.repo.site.SiteService;
import org.alfresco.repo.template.TemplateNode;
@@ -95,7 +98,7 @@ public class InviteHelper
// set process name to "wf:invite" so that only tasks associated with
// invite workflow instances are returned by query
wfTaskQuery.setProcessName(QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "invite"));
wfTaskQuery.setProcessName(InviteWorkflowModel.WF_PROCESS_INVITE);
// set query to only pick up invite workflow instances
// associated with the given invitee user name
@@ -105,7 +108,7 @@ public class InviteHelper
// set query to only pick up in-progress invite pending tasks
wfTaskQuery.setTaskState(WorkflowTaskState.IN_PROGRESS);
wfTaskQuery.setTaskName(InviteWorkflowModel.WF_TASK_INVITE_PENDING);
wfTaskQuery.setTaskName(InviteWorkflowModel.WF_INVITE_TASK_INVITE_PENDING);
// query for invite workflow task associate
List<WorkflowTask> inviteStartTasks = workflowService
@@ -178,4 +181,88 @@ public class InviteHelper
return inviteInfo;
}
/**
* Clean up invitee user account and person node when no longer in use.
* They are deemed to no longer be in use when the invitee user account
* is still disabled and there are no outstanding pending invites for that invitee.
*
* @param inviteeUserName
* @param authenticationDao
* @param personService
* @param workflowService
*/
public static void cleanUpStaleInviteeResources(final String inviteeUserName,
final MutableAuthenticationDao authenticationDao, final PersonService personService,
final WorkflowService workflowService)
{
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
// see if there are any pending invites (invite workflow instances with invitePending task in-progress)
// outstanding for given invitee user name
List<WorkflowTask> pendingTasks = InviteHelper.findInvitePendingTasks(inviteeUserName, workflowService);
boolean invitesPending = (pendingTasks != null) && (pendingTasks.size() > 0);
// if invitee's user account is still disabled and there are no pending invites outstanding
// for the invitee, then remove the account and delete the invitee's person node
if ((authenticationDao.userExists(inviteeUserName))
&& (authenticationDao.getEnabled(inviteeUserName) == false)
&& (invitesPending == false))
{
// delete the invitee's user account
authenticationDao.deleteUser(inviteeUserName);
// delete the invitee's person node if one exists
if (personService.personExists(inviteeUserName))
{
personService.deletePerson(inviteeUserName);
}
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
/**
* Complete the specified Invite Workflow Task for the invite workflow
* instance associated with the given invite ID, and follow the given
* transition upon completing the task
*
* @param inviteId the invite ID of the invite workflow instance for which
* we want to complete the given task
* @param fullTaskName qualified name of invite workflow task to complete
* @param transitionId the task transition to take on completion of
* the task (or null, for the default transition)
*/
public static void completeInviteTask(String inviteId, QName fullTaskName, String transitionId,
final WorkflowService workflowService)
{
// create workflow task query
WorkflowTaskQuery wfTaskQuery = new WorkflowTaskQuery();
// set the given invite ID as the workflow process ID in the workflow query
wfTaskQuery.setProcessId(inviteId);
// find incomplete invite workflow tasks with given task name
wfTaskQuery.setActive(Boolean.TRUE);
wfTaskQuery.setTaskState(WorkflowTaskState.IN_PROGRESS);
wfTaskQuery.setTaskName(fullTaskName);
// set process name to "wf:invite" so that only
// invite workflow instances are considered by this query
wfTaskQuery.setProcessName(InviteWorkflowModel.WF_PROCESS_INVITE);
// query for invite workflow tasks with the constructed query
List<WorkflowTask> wf_invite_tasks = workflowService
.queryTasks(wfTaskQuery);
// end all tasks found with this name
for (WorkflowTask workflowTask : wf_invite_tasks)
{
workflowService.endTask(workflowTask.id, transitionId);
}
}
}

View File

@@ -25,14 +25,10 @@
package org.alfresco.repo.web.scripts.invite;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskQuery;
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.Status;
import org.alfresco.web.scripts.WebScriptException;
@@ -140,7 +136,8 @@ public class InviteResponse extends DeclarativeWebScript
InviteWorkflowModel.WF_PROP_SITE_SHORT_NAME);
// complete the wf:invitePendingTask along the 'accept' transition because the invitation has been accepted
completeInviteTask(inviteId, InviteWorkflowModel.WF_TASK_INVITE_PENDING, InviteWorkflowModel.WF_TRANSITION_ACCEPT);
InviteHelper.completeInviteTask(inviteId, InviteWorkflowModel.WF_INVITE_TASK_INVITE_PENDING,
InviteWorkflowModel.WF_TRANSITION_ACCEPT, this.workflowService);
// add model properties for template to render
model.put(MODEL_PROP_KEY_RESPONSE, RESPONSE_ACCEPT);
@@ -167,49 +164,11 @@ public class InviteResponse extends DeclarativeWebScript
InviteWorkflowModel.WF_PROP_SITE_SHORT_NAME);
// complete the wf:invitePendingTask task along the 'reject' transition because the invitation has been rejected
completeInviteTask(inviteId, InviteWorkflowModel.WF_TASK_INVITE_PENDING, InviteWorkflowModel.WF_TRANSITION_REJECT);
InviteHelper.completeInviteTask(inviteId, InviteWorkflowModel.WF_INVITE_TASK_INVITE_PENDING,
InviteWorkflowModel.WF_TRANSITION_REJECT, this.workflowService);
// add model properties for template to render
model.put(MODEL_PROP_KEY_RESPONSE, RESPONSE_REJECT);
model.put(MODEL_PROP_KEY_SITE_SHORT_NAME, siteShortName);
}
/**
* Complete the specified Invite Workflow Task for the invite workflow
* instance associated with the given invite ID, and follow the given
* transition upon completing the task
*
* @param inviteId the invite ID of the invite workflow instance for which
* we want to complete the given task
* @param fullTaskName qualified name of invite workflow task to complete
* @param transitionId the task transition to take on completion of
* the task (or null, for the default transition)
*/
private void completeInviteTask(String inviteId, QName fullTaskName, String transitionId)
{
// create workflow task query
WorkflowTaskQuery wfTaskQuery = new WorkflowTaskQuery();
// set the given invite ID as the workflow process ID in the workflow query
wfTaskQuery.setProcessId(inviteId);
// find incomplete invite workflow tasks with given task name
wfTaskQuery.setActive(Boolean.TRUE);
wfTaskQuery.setTaskState(WorkflowTaskState.IN_PROGRESS);
wfTaskQuery.setTaskName(fullTaskName);
// set process name to "wf:invite" so that only
// invite workflow instances are considered by this query
wfTaskQuery.setProcessName(InviteWorkflowModel.WF_PROCESS_INVITE);
// query for invite workflow tasks with the constructed query
List<WorkflowTask> wf_invite_tasks = this.workflowService
.queryTasks(wfTaskQuery);
// end all tasks found with this name
for (WorkflowTask workflowTask : wf_invite_tasks)
{
this.workflowService.endTask(workflowTask.id, transitionId);
}
}
}
}

View File

@@ -29,6 +29,7 @@ import java.util.List;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
@@ -36,6 +37,8 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.site.SiteInfo;
import org.alfresco.repo.site.SiteModel;
import org.alfresco.repo.site.SiteService;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.web.scripts.BaseWebScriptTest;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@@ -47,6 +50,8 @@ import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowInstance;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.PropertyMap;
import org.alfresco.util.URLEncoder;
import org.alfresco.web.scripts.Status;
@@ -55,6 +60,7 @@ import org.alfresco.web.scripts.TestWebScriptServer.PutRequest;
import org.alfresco.web.scripts.TestWebScriptServer.Response;
import org.apache.commons.lang.RandomStringUtils;
import org.json.JSONObject;
import org.springframework.core.io.ClassPathResource;
/**
* Unit Test to test Invite Web Script API
@@ -72,6 +78,8 @@ public class InviteServiceTest extends BaseWebScriptTest
private NodeService nodeService;
private WorkflowService workflowService;
private MutableAuthenticationDao mutableAuthenticationDao;
private NamespaceService namespaceService;
private TransactionService transactionService;
// stores invitee email addresses, one entry for each "start invite" operation
// invoked, so that resources created for each invitee for each test
@@ -116,7 +124,19 @@ public class InviteServiceTest extends BaseWebScriptTest
this.workflowService = (WorkflowService) getServer().getApplicationContext().getBean("WorkflowService");
this.mutableAuthenticationDao = (MutableAuthenticationDao) getServer().getApplicationContext()
.getBean("authenticationDao");
this.namespaceService = (NamespaceService) getServer().getApplicationContext().getBean("NamespaceService");
this.transactionService = (TransactionService) getServer().getApplicationContext()
.getBean("TransactionService");
// redeploy invite process definition in case it has been modified
WorkflowDefinition inviteWfDefinition = this.workflowService.getDefinitionByName(
"jbpm$" + InviteWorkflowModel.WF_PROCESS_INVITE.toPrefixString(this.namespaceService));
this.workflowService.undeployDefinition(inviteWfDefinition.id);
ClassPathResource inviteWfResource = new ClassPathResource(
"alfresco/workflow/invite_processdefinition.xml");
workflowService.deployDefinition(
"jbpm", inviteWfResource.getInputStream(), MimetypeMap.MIMETYPE_XML);
// Create new invitee email address list
this.inviteeEmailAddrs = new ArrayList<String>();
@@ -354,6 +374,26 @@ public class InviteServiceTest extends BaseWebScriptTest
return startInvite(inviteeFirstName, inviteeLastName, inviteeEmail, inviteeSiteRole, siteShortName,
expectedStatus);
}
private JSONObject cancelInvite(String inviteId, int expectedStatus) throws Exception
{
String cancelInviteUrl = URL_INVITE + "/"
+ INVITE_ACTION_CANCEL + "?inviteId=" + inviteId;
Response response = sendRequest(new GetRequest(cancelInviteUrl), expectedStatus);;
JSONObject result = new JSONObject(response.getContentAsString());
return result;
}
private JSONObject rejectInvite(String inviteId, String inviteTicket, int expectedStatus) throws Exception
{
// Invitee rejects invitation to a Site from Inviter
String rejectInviteUrl = URL_INVITE + "/" + inviteId + "/" + inviteTicket + "/reject";
Response response = sendRequest(new PutRequest(rejectInviteUrl, (byte[])null, null), expectedStatus);
JSONObject result = new JSONObject(response.getContentAsString());
return result;
}
private JSONObject getInvitesByInviteId(String inviteId, int expectedStatus)
throws Exception
@@ -465,7 +505,7 @@ public class InviteServiceTest extends BaseWebScriptTest
}, USER_INVITER);
JSONObject result = startInvite(INVITEE_FIRSTNAME, INVITEE_LASTNAME, inviteeEmailAddr, INVITEE_SITE_ROLE,
startInvite(INVITEE_FIRSTNAME, INVITEE_LASTNAME, inviteeEmailAddr, INVITEE_SITE_ROLE,
SITE_SHORT_NAME_INVITE_1, Status.STATUS_CONFLICT);
}
@@ -503,9 +543,7 @@ public class InviteServiceTest extends BaseWebScriptTest
String inviteId = result.getString("inviteId");
// Inviter cancels pending invitation
String cancelInviteUrl = URL_INVITE + "/"
+ INVITE_ACTION_CANCEL + "?inviteId=" + inviteId;
Response response = sendRequest(new GetRequest(cancelInviteUrl), Status.STATUS_OK);
cancelInvite(inviteId, Status.STATUS_OK);
}
public void testAcceptInvite() throws Exception
@@ -520,7 +558,7 @@ public class InviteServiceTest extends BaseWebScriptTest
// Invitee accepts invitation to a Site from Inviter
String acceptInviteUrl = URL_INVITE + "/" + inviteId + "/" + inviteTicket + "/accept";
Response response = sendRequest(new PutRequest(acceptInviteUrl, (byte[])null, null), Status.STATUS_OK);
sendRequest(new PutRequest(acceptInviteUrl, (byte[])null, null), Status.STATUS_OK);
//
// test that invitation represented by invite ID (of invitation started
@@ -548,9 +586,7 @@ public class InviteServiceTest extends BaseWebScriptTest
String inviteId = result.getString("inviteId");
String inviteTicket = result.getString("inviteTicket");
// Invitee rejects invitation to a Site from Inviter
String rejectInviteUrl = URL_INVITE + "/" + inviteId + "/" + inviteTicket + "/reject";
Response response = sendRequest(new PutRequest(rejectInviteUrl, (byte[])null, null), Status.STATUS_OK);
rejectInvite(inviteId, inviteTicket, Status.STATUS_OK);
//
// test that invite represented by invite ID (of invitation started
@@ -593,7 +629,7 @@ public class InviteServiceTest extends BaseWebScriptTest
public void testGetInvitesByInviterUserName() throws Exception
{
// inviter starts invite workflow
JSONObject startInviteResult = startInvite(INVITEE_FIRSTNAME,
startInvite(INVITEE_FIRSTNAME,
INVITEE_LASTNAME, INVITEE_SITE_ROLE, SITE_SHORT_NAME_INVITE_1, Status.STATUS_OK);
// get pending invites matching inviter user name used in invite started
@@ -658,11 +694,104 @@ public class InviteServiceTest extends BaseWebScriptTest
assertEquals(siteShortName, inviteJSONObj.getJSONObject("site").get("shortName"));
}
public void testInviteForbiddenWhenInviterNotSiteManager() throws Exception
public void testStartInviteForbiddenWhenInviterNotSiteManager() throws Exception
{
// inviter2 starts invite workflow, but he/she is not the site manager of the given site
AuthenticationUtil.setCurrentUser(USER_INVITER_2);
startInvite(INVITEE_FIRSTNAME,
INVITEE_LASTNAME, INVITEE_SITE_ROLE, SITE_SHORT_NAME_INVITE_3, Status.STATUS_FORBIDDEN);
}
public void testCancelInviteForbiddenWhenInviterNotSiteManager() throws Exception
{
// inviter (who is Site Manager of the given site) starts invite workflow
JSONObject result = startInvite(INVITEE_FIRSTNAME,
INVITEE_LASTNAME, INVITEE_SITE_ROLE, SITE_SHORT_NAME_INVITE_3, Status.STATUS_OK);
// get hold of invite ID of started invite
String inviteId = result.getString("inviteId");
// when inviter 2 (who is not Site Manager of the given site) tries to cancel invite
// http status FORBIDDEN must be returned
AuthenticationUtil.setCurrentUser(USER_INVITER_2);
cancelInvite(inviteId, Status.STATUS_FORBIDDEN);
}
public void testInviteeResourcesDeletedUponRejectWhenNoInvitePending() throws Exception
{
// inviter starts invite workflow
JSONObject result = startInvite(INVITEE_FIRSTNAME,
INVITEE_LASTNAME, INVITEE_SITE_ROLE, SITE_SHORT_NAME_INVITE_1, Status.STATUS_OK);
// get hold of properties of started invite
String inviteId = result.getString("inviteId");
String inviteTicket = result.getString("inviteTicket");
final String inviteeUserName = result.getString("inviteeUserName");
rejectInvite(inviteId, inviteTicket, Status.STATUS_OK);
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
assertEquals(false, mutableAuthenticationDao.userExists(inviteeUserName));
assertEquals(false, personService.personExists(inviteeUserName));
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
public void testInviteeResourcesNotDeletedUponRejectWhenInvitesPending() throws Exception
{
// inviter invites invitee to site 1
JSONObject result = startInvite(INVITEE_FIRSTNAME,
INVITEE_LASTNAME, INVITEE_SITE_ROLE, SITE_SHORT_NAME_INVITE_1, Status.STATUS_OK);
// get hold of properties of started invite
String invite1Id = result.getString("inviteId");
String invite1Ticket = result.getString("inviteTicket");
String inviteeEmail = result.getString("inviteeEmail");
final String inviteeUserName = result.getString("inviteeUserName");
// inviter invites invitee to site 2
startInvite(INVITEE_FIRSTNAME,
INVITEE_LASTNAME, inviteeEmail, INVITEE_SITE_ROLE, SITE_SHORT_NAME_INVITE_2, Status.STATUS_OK);
rejectInvite(invite1Id, invite1Ticket, Status.STATUS_OK);
boolean inviteeUserExists = AuthenticationUtil.runAs(new RunAsWork<Boolean>()
{
public Boolean doWork() throws Exception
{
RetryingTransactionHelper tranHelper = transactionService.getRetryingTransactionHelper();
Boolean result = tranHelper.doInTransaction(new RetryingTransactionCallback<Boolean>()
{
public Boolean execute() throws Throwable
{
Boolean result = mutableAuthenticationDao.userExists(inviteeUserName);
return result;
}
});
return result;
}
}, AuthenticationUtil.getSystemUserName());
// test that the invitee's user account still exists (has not been deleted
assertEquals(true, inviteeUserExists);
boolean inviteePersonExists = AuthenticationUtil.runAs(new RunAsWork<Boolean>()
{
public Boolean doWork() throws Exception
{
Boolean result = personService.personExists(inviteeUserName);
return result;
}
}, AuthenticationUtil.getSystemUserName());
assertEquals(true, inviteePersonExists);
}
}

View File

@@ -16,13 +16,12 @@ public interface InviteWorkflowModel {
public static final QName WF_INVITE_TASK_INVITE_PENDING = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "invitePendingTask");
public static final QName WF_TASK_ACCEPT_INVITE = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "acceptInviteTask");
public static final QName WF_TASK_REJECT_INVITE = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "rejectInviteTask");
public static final QName WF_TASK_INVITE_PENDING = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "invitePendingTask");
// transition names
public static final String WF_TRANSITION_SEND_INVITE = "sendInvite";
public static final String WF_TRANSITION_ACCEPT = "accept";
public static final String WF_TRANSITION_REJECT = "reject";
public static final String WF_TRANSITION_CANCEL = "cancel";
public static final String WF_TRANSITION_ACCEPT_INVITE_END = "end";
public static final String WF_TRANSITION_REJECT_INVITE_END = "end";
@@ -40,4 +39,8 @@ public interface InviteWorkflowModel {
public static final QName WF_PROP_SENT_INVITE_DATE = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "sentInviteDate");
public static final QName WF_PROP_INVITEE_GEN_PASSWORD = QName.createQName(NamespaceService.WORKFLOW_MODEL_1_0_URI, "inviteeGenPassword");
// workflow execution context variable names
public static final String wfVarInviteeUserName = "wf_inviteeUserName";
public static final String wfVarSiteShortName = "wf_siteShortName";
public static final String wfVarWorkflowInstanceId = "workflowinstanceid";
}

View File

@@ -24,16 +24,11 @@
*/
package org.alfresco.repo.web.scripts.invite;
import java.util.List;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.jbpm.graph.exe.ExecutionContext;
import org.springframework.beans.factory.BeanFactory;
@@ -71,35 +66,12 @@ public class RejectInviteAction extends JBPMSpringActionHandler
public void execute(final ExecutionContext executionContext) throws Exception
{
// get the invitee user name
final String inviteeUserName = (String) executionContext.getVariable("wf_inviteeUserName");
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
// see if there are any pending invites (invite workflow instances with invitePending task in-progress)
// outstanding for invitee who has responded here with an invite rejection
List<WorkflowTask> pendingTasks = InviteHelper.findInvitePendingTasks(inviteeUserName, workflowService);
boolean invitesPending = (pendingTasks != null) && (pendingTasks.size() > 0);
// if invitee's user account is still disabled and there are no pending invites outstanding
// for the invitee, then remove the account and delete the invitee's person node
if ((RejectInviteAction.this.mutableAuthenticationDao.userExists(inviteeUserName))
&& (RejectInviteAction.this.mutableAuthenticationDao.getEnabled(inviteeUserName) == false)
&& (invitesPending == false))
{
// delete the invitee's user account
mutableAuthenticationDao.deleteUser(inviteeUserName);
// delete the invitee's person node if one exists
if (personService.personExists(inviteeUserName))
{
personService.deletePerson(inviteeUserName);
}
}
final String inviteeUserName = (String) executionContext.getVariable(InviteWorkflowModel.wfVarInviteeUserName);
return null;
}
}, AuthenticationUtil.getSystemUserName());
// clean up invitee's user account and person node if they are not in use i.e.
// account is still disabled and there are no pending invites outstanding for the
// invitee
InviteHelper.cleanUpStaleInviteeResources(inviteeUserName, mutableAuthenticationDao, personService,
workflowService);
}
}