mirror of https://github.com/apache/cloudstack.git
bug 12399: introduced periodic thread that expires Project invitations
status 12399: resolved fixed
This commit is contained in:
parent
21c2979341
commit
0731dac370
|
|
@ -970,14 +970,18 @@ public class ApiServer implements HttpRequestHandler {
|
|||
if (errorCode == BaseCmd.UNSUPPORTED_ACTION_ERROR || apiCommandParams == null || apiCommandParams.isEmpty()) {
|
||||
responseName = "errorresponse";
|
||||
} else {
|
||||
String cmdName = ((String[]) apiCommandParams.get("command"))[0];
|
||||
cmdClassName = _apiCommands.getProperty(cmdName);
|
||||
if (cmdClassName != null) {
|
||||
Class<?> claz = Class.forName(cmdClassName);
|
||||
responseName = ((BaseCmd) claz.newInstance()).getCommandName();
|
||||
} else {
|
||||
responseName = "errorresponse";
|
||||
}
|
||||
Object cmdObj = apiCommandParams.get("command");
|
||||
//cmd name can be null when "command" parameter is missing in the request
|
||||
if (cmdObj != null) {
|
||||
String cmdName = ((String[])cmdObj) [0];
|
||||
cmdClassName = _apiCommands.getProperty(cmdName);
|
||||
if (cmdClassName != null) {
|
||||
Class<?> claz = Class.forName(cmdClassName);
|
||||
responseName = ((BaseCmd) claz.newInstance()).getCommandName();
|
||||
} else {
|
||||
responseName = "errorresponse";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExceptionResponse apiResponse = new ExceptionResponse();
|
||||
|
|
|
|||
|
|
@ -18,11 +18,15 @@
|
|||
package com.cloud.projects;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.sql.Date;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.ejb.Local;
|
||||
import javax.mail.Authenticator;
|
||||
|
|
@ -63,9 +67,11 @@ import com.cloud.user.DomainManager;
|
|||
import com.cloud.user.ResourceLimitService;
|
||||
import com.cloud.user.UserContext;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.utils.DateUtil;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.component.Inject;
|
||||
import com.cloud.utils.component.Manager;
|
||||
import com.cloud.utils.concurrency.NamedThreadFactory;
|
||||
import com.cloud.utils.db.DB;
|
||||
import com.cloud.utils.db.Filter;
|
||||
import com.cloud.utils.db.JoinBuilder;
|
||||
|
|
@ -106,8 +112,10 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
|
|||
private ProjectInvitationDao _projectInvitationDao;
|
||||
|
||||
protected boolean _invitationRequired = false;
|
||||
protected long _invitationTimeOut = 86400;
|
||||
protected long _invitationTimeOut = 86400000;
|
||||
protected boolean _allowUserToCreateProjet = true;
|
||||
protected ScheduledExecutorService _executor;
|
||||
protected int _projectCleanupExpInvInterval = 60; //Interval defining how often project invitation cleanup thread is running
|
||||
|
||||
|
||||
@Override
|
||||
|
|
@ -116,7 +124,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
|
|||
|
||||
Map<String, String> configs = _configDao.getConfiguration(params);
|
||||
_invitationRequired = Boolean.valueOf(configs.get(Config.ProjectInviteRequired.key()));
|
||||
_invitationTimeOut = Long.valueOf(configs.get(Config.ProjectInvitationExpirationTime.key()));
|
||||
_invitationTimeOut = Long.valueOf(configs.get(Config.ProjectInvitationExpirationTime.key()))*1000;
|
||||
_allowUserToCreateProjet = Boolean.valueOf(configs.get(Config.AllowUserToCreateProject.key()));
|
||||
|
||||
|
||||
|
|
@ -136,12 +144,14 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
|
|||
}
|
||||
|
||||
_emailInvite = new EmailInvite(smtpHost, smtpPort, useAuth, smtpUsername, smtpPassword, emailSender, smtpDebug);
|
||||
_executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Project-ExpireInvitations"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start() {
|
||||
_executor.scheduleWithFixedDelay(new ExpiredInvitationsCleanup(), _projectCleanupExpInvInterval, _projectCleanupExpInvInterval, TimeUnit.SECONDS);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -671,36 +681,54 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
|
|||
return _projectAccountDao.search(sc, searchFilter);
|
||||
}
|
||||
|
||||
public ProjectInvitation createAccountInvitation(Project project, Long accountId) {
|
||||
//verify if the invitation was already generated
|
||||
ProjectInvitationVO invite = _projectInvitationDao.findPendingByAccountIdProjectId(accountId, project.getId());
|
||||
public ProjectInvitation createAccountInvitation(Project project, Long accountId) {
|
||||
if (activeInviteExists(project, accountId, null)) {
|
||||
throw new InvalidParameterValueException("There is already a pending invitation for account id=" + accountId + " to the project id=" + project);
|
||||
}
|
||||
|
||||
ProjectInvitation invitation= _projectInvitationDao.persist(new ProjectInvitationVO(project.getId(), accountId, project.getDomainId(), null, null));
|
||||
|
||||
return invitation;
|
||||
}
|
||||
|
||||
@DB
|
||||
public boolean activeInviteExists(Project project, Long accountId, String email) {
|
||||
Transaction txn = Transaction.currentTxn();
|
||||
txn.start();
|
||||
//verify if the invitation was already generated
|
||||
ProjectInvitationVO invite = null;
|
||||
if (accountId != null) {
|
||||
invite = _projectInvitationDao.findByAccountIdProjectId(accountId, project.getId());
|
||||
} else if (email != null) {
|
||||
invite = _projectInvitationDao.findByEmailAndProjectId(email, project.getId());
|
||||
}
|
||||
|
||||
if (invite != null) {
|
||||
if (_projectInvitationDao.isActive(invite.getId(), _invitationTimeOut)) {
|
||||
throw new InvalidParameterValueException("There is already a pending invitation for account id=" + accountId + " to the project id=" + project);
|
||||
if (invite.getState() == ProjectInvitation.State.Completed || _projectInvitationDao.isActive(invite.getId(), _invitationTimeOut)) {
|
||||
return true;
|
||||
} else {
|
||||
if (invite.getState() == ProjectInvitation.State.Pending) {
|
||||
expireInvitation(invite);
|
||||
}
|
||||
//remove the expired/declined invitation
|
||||
if (accountId != null) {
|
||||
s_logger.debug("Removing invitation in state " + invite.getState() + " for account id=" + accountId + " to project " + project);
|
||||
} else if (email != null) {
|
||||
s_logger.debug("Removing invitation in state " + invite.getState() + " for email " + email + " to project " + project);
|
||||
}
|
||||
|
||||
_projectInvitationDao.expunge(invite.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return _projectInvitationDao.persist(new ProjectInvitationVO(project.getId(), accountId, project.getDomainId(), null, null));
|
||||
}
|
||||
txn.commit();
|
||||
return false;
|
||||
}
|
||||
|
||||
public ProjectInvitation generateTokenBasedInvitation(Project project, String email, String token) {
|
||||
//verify if the invitation was already generated
|
||||
ProjectInvitationVO invite = _projectInvitationDao.findPendingByEmailAndProjectId(email, project.getId());
|
||||
|
||||
if (invite != null) {
|
||||
if (_projectInvitationDao.isActive(invite.getId(), _invitationTimeOut)) {
|
||||
throw new InvalidParameterValueException("There is already a pending invitation for email=" + email + " to the project id=" + project);
|
||||
} else {
|
||||
if (invite.getState() == ProjectInvitation.State.Pending) {
|
||||
expireInvitation(invite);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (activeInviteExists(project, null, email)) {
|
||||
throw new InvalidParameterValueException("There is already a pending invitation for email " + email + " to the project id=" + project);
|
||||
}
|
||||
|
||||
ProjectInvitation projectInvitation = _projectInvitationDao.persist(new ProjectInvitationVO(project.getId(), null, project.getDomainId(), email, token));
|
||||
try {
|
||||
|
|
@ -794,7 +822,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
|
|||
|
||||
if (activeOnly) {
|
||||
sc.setParameters("state", ProjectInvitation.State.Pending);
|
||||
sc.setParameters("created", new Date((System.currentTimeMillis() >> 10) - _invitationTimeOut));
|
||||
sc.setParameters("created", new Date((DateUtil.currentGMTTime().getTime()) - _invitationTimeOut));
|
||||
}
|
||||
|
||||
return _projectInvitationDao.search(sc, searchFilter);
|
||||
|
|
@ -837,9 +865,9 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
|
|||
//check that invitation exists
|
||||
ProjectInvitationVO invite = null;
|
||||
if (token == null) {
|
||||
invite = _projectInvitationDao.findPendingByAccountIdProjectId(accountId, projectId);
|
||||
invite = _projectInvitationDao.findByAccountIdProjectId(accountId, projectId, ProjectInvitation.State.Pending);
|
||||
} else {
|
||||
invite = _projectInvitationDao.findPendingByTokenAndProjectId(token, projectId);
|
||||
invite = _projectInvitationDao.findPendingByTokenAndProjectId(token, projectId, ProjectInvitation.State.Pending);
|
||||
}
|
||||
|
||||
if (invite != null) {
|
||||
|
|
@ -1034,7 +1062,7 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
|
|||
msg.setFrom(new InternetAddress(_emailSender, _emailSender));
|
||||
msg.addRecipient(RecipientType.TO, address);
|
||||
msg.setSubject("You are invited to join the cloud stack project id=" + projectId);
|
||||
msg.setSentDate(new Date(System.currentTimeMillis() >> 10));
|
||||
msg.setSentDate(new Date(DateUtil.currentGMTTime().getTime() >> 10));
|
||||
msg.setContent(content, "text/plain");
|
||||
msg.saveChanges();
|
||||
|
||||
|
|
@ -1076,4 +1104,24 @@ public class ProjectManagerImpl implements ProjectManager, Manager{
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class ExpiredInvitationsCleanup implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
TimeZone.getDefault();
|
||||
List<ProjectInvitationVO> invitationsToExpire = _projectInvitationDao.listInvitationsToExpire(_invitationTimeOut);
|
||||
if (!invitationsToExpire.isEmpty()) {
|
||||
s_logger.debug("Found " + invitationsToExpire.size() + " projects to expire");
|
||||
for (ProjectInvitationVO invitationToExpire : invitationsToExpire) {
|
||||
invitationToExpire.setState(ProjectInvitation.State.Expired);
|
||||
_projectInvitationDao.update(invitationToExpire.getId(), invitationToExpire);
|
||||
s_logger.trace("Expired project invitation id=" + invitationToExpire.getId());
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
s_logger.warn("Exception while running expired invitations cleanup", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,16 +19,18 @@ package com.cloud.projects.dao;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import com.cloud.projects.ProjectInvitation.State;
|
||||
import com.cloud.projects.ProjectInvitationVO;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
|
||||
public interface ProjectInvitationDao extends GenericDao<ProjectInvitationVO, Long>{
|
||||
ProjectInvitationVO findPendingByAccountIdProjectId(long accountId, long projectId);
|
||||
ProjectInvitationVO findByAccountIdProjectId(long accountId, long projectId, State... inviteState);
|
||||
List<ProjectInvitationVO> listExpiredInvitations();
|
||||
boolean expirePendingInvitations(long timeOut);
|
||||
boolean isActive(long id, long timeout);
|
||||
ProjectInvitationVO findPendingByEmailAndProjectId(String email, long projectId);
|
||||
ProjectInvitationVO findPendingByTokenAndProjectId(String token, long projectId);
|
||||
ProjectInvitationVO findByEmailAndProjectId(String email, long projectId, State... inviteState);
|
||||
ProjectInvitationVO findPendingByTokenAndProjectId(String token, long projectId, State... inviteState);
|
||||
void cleanupInvitations(long projectId);
|
||||
ProjectInvitationVO findPendingById(long id);
|
||||
List<ProjectInvitationVO> listInvitationsToExpire(long timeOut);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import org.apache.log4j.Logger;
|
|||
|
||||
import com.cloud.projects.ProjectInvitation.State;
|
||||
import com.cloud.projects.ProjectInvitationVO;
|
||||
import com.cloud.utils.DateUtil;
|
||||
import com.cloud.utils.db.GenericDaoBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
|
|
@ -25,7 +26,7 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase<ProjectInvitationVO
|
|||
AllFieldsSearch.and("projectId", AllFieldsSearch.entity().getProjectId(), SearchCriteria.Op.EQ);
|
||||
AllFieldsSearch.and("created", AllFieldsSearch.entity().getCreated(), SearchCriteria.Op.EQ);
|
||||
AllFieldsSearch.and("projectAccountId", AllFieldsSearch.entity().getState(), SearchCriteria.Op.EQ);
|
||||
AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), SearchCriteria.Op.EQ);
|
||||
AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), SearchCriteria.Op.IN);
|
||||
AllFieldsSearch.and("email", AllFieldsSearch.entity().getEmail(), SearchCriteria.Op.EQ);
|
||||
AllFieldsSearch.and("token", AllFieldsSearch.entity().getToken(), SearchCriteria.Op.EQ);
|
||||
AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), SearchCriteria.Op.EQ);
|
||||
|
|
@ -42,12 +43,14 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase<ProjectInvitationVO
|
|||
|
||||
|
||||
@Override
|
||||
public ProjectInvitationVO findPendingByAccountIdProjectId(long accountId, long projectId) {
|
||||
public ProjectInvitationVO findByAccountIdProjectId(long accountId, long projectId, State... inviteState) {
|
||||
SearchCriteria<ProjectInvitationVO> sc = AllFieldsSearch.create();
|
||||
sc.setParameters("accountId", accountId);
|
||||
sc.setParameters("projectId", projectId);
|
||||
sc.setParameters("state", State.Pending);
|
||||
|
||||
if (inviteState != null && inviteState.length > 0) {
|
||||
sc.setParameters("state", (Object[])inviteState);
|
||||
}
|
||||
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +67,7 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase<ProjectInvitationVO
|
|||
boolean success = true;
|
||||
|
||||
SearchCriteria<ProjectInvitationVO> sc = InactiveSearch.create();
|
||||
sc.setParameters("created", new Date((System.currentTimeMillis() >> 10) - timeout));
|
||||
sc.setParameters("created", new Date((DateUtil.currentGMTTime().getTime() >> 10) - timeout));
|
||||
sc.setParameters("state", State.Pending);
|
||||
|
||||
List<ProjectInvitationVO> invitationsToExpire = listBy(sc);
|
||||
|
|
@ -75,10 +78,17 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase<ProjectInvitationVO
|
|||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProjectInvitationVO> listInvitationsToExpire (long timeOut) {
|
||||
SearchCriteria<ProjectInvitationVO> sc = InactiveSearch.create();
|
||||
sc.setParameters("created", new Date((DateUtil.currentGMTTime().getTime()) - timeOut));
|
||||
sc.setParameters("state", State.Pending);
|
||||
return listBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive(long id, long timeout) {
|
||||
SearchCriteria<ProjectInvitationVO> sc = InactiveSearch.create();
|
||||
|
|
@ -90,7 +100,7 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase<ProjectInvitationVO
|
|||
return false;
|
||||
}
|
||||
|
||||
sc.setParameters("created", new Date((System.currentTimeMillis() >> 10) - timeout));
|
||||
sc.setParameters("created", new Date((DateUtil.currentGMTTime().getTime()) - timeout));
|
||||
|
||||
if (findOneBy(sc) == null) {
|
||||
return true;
|
||||
|
|
@ -100,21 +110,25 @@ public class ProjectInvitationDaoImpl extends GenericDaoBase<ProjectInvitationVO
|
|||
}
|
||||
|
||||
@Override
|
||||
public ProjectInvitationVO findPendingByEmailAndProjectId(String email, long projectId) {
|
||||
public ProjectInvitationVO findByEmailAndProjectId(String email, long projectId, State... inviteState) {
|
||||
SearchCriteria<ProjectInvitationVO> sc = AllFieldsSearch.create();
|
||||
sc.setParameters("email", email);
|
||||
sc.setParameters("projectId", projectId);
|
||||
sc.setParameters("state", State.Pending);
|
||||
if (inviteState != null && inviteState.length > 0) {
|
||||
sc.setParameters("state", (Object[])inviteState);
|
||||
}
|
||||
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectInvitationVO findPendingByTokenAndProjectId(String token, long projectId) {
|
||||
public ProjectInvitationVO findPendingByTokenAndProjectId(String token, long projectId, State... inviteState) {
|
||||
SearchCriteria<ProjectInvitationVO> sc = AllFieldsSearch.create();
|
||||
sc.setParameters("token", token);
|
||||
sc.setParameters("projectId", projectId);
|
||||
sc.setParameters("state", State.Pending);
|
||||
if (inviteState != null && inviteState.length > 0) {
|
||||
sc.setParameters("state", (Object[])inviteState);
|
||||
}
|
||||
|
||||
return findOneBy(sc);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,14 +18,13 @@
|
|||
|
||||
package com.cloud.utils;
|
||||
|
||||
import java.net.URI;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
public class DateUtil {
|
||||
|
|
|
|||
Loading…
Reference in New Issue