/**
* $Id: ValidationLogicImpl.java 106391 2012-03-30 07:45:31Z david.horwitz@uct.ac.za $
* $URL: https://source.sakaiproject.org/svn/reset-pass/trunk/account-validator-impl/src/java/org/sakaiproject/accountvalidator/logic/impl/ValidationLogicImpl.java $
*
**************************************************************************
* Copyright (c) 2008, 2009 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sakaiproject.accountvalidator.logic.impl;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.sakaiproject.accountvalidator.logic.ValidationException;
import org.sakaiproject.accountvalidator.logic.ValidationLogic;
import org.sakaiproject.accountvalidator.logic.dao.ValidationDao;
import org.sakaiproject.accountvalidator.model.ValidationAccount;
import org.sakaiproject.authz.api.AuthzGroup;
import org.sakaiproject.authz.api.AuthzGroupService;
import org.sakaiproject.authz.api.AuthzPermissionException;
import org.sakaiproject.authz.api.GroupNotDefinedException;
import org.sakaiproject.authz.api.GroupProvider;
import org.sakaiproject.authz.api.Member;
import org.sakaiproject.authz.api.SecurityAdvisor;
import org.sakaiproject.authz.api.SecurityService;
import org.sakaiproject.component.api.ServerConfigurationService;
import org.sakaiproject.component.cover.ComponentManager;
import org.sakaiproject.emailtemplateservice.service.EmailTemplateService;
import org.sakaiproject.emailtemplateservice.model.EmailTemplate;
import org.sakaiproject.entitybroker.DeveloperHelperService;
import org.sakaiproject.entitybroker.EntityReference;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.genericdao.api.search.Restriction;
import org.sakaiproject.genericdao.api.search.Search;
import org.sakaiproject.id.api.IdManager;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.tool.api.Session;
import org.sakaiproject.tool.api.SessionManager;
import org.sakaiproject.user.api.User;
import org.sakaiproject.user.api.UserDirectoryService;
import org.sakaiproject.user.api.UserEdit;
import org.sakaiproject.user.api.UserLockedException;
import org.sakaiproject.user.api.UserNotDefinedException;
import org.sakaiproject.user.api.UserPermissionException;
public class ValidationLogicImpl implements ValidationLogic {
private static final String TEMPLATE_KEY_EXISTINGUSER = "validate.existinguser";
private static final String TEMPLATE_KEY_NEW_USER = "validate.newUser";
private static final String TEMPLATE_KEY_LEGACYUSER = "validate.legacyuser";
private static final String TEMPLATE_KEY_PASSWORDRESET = "validate.passwordreset";
private static final int VALIDATION_PERIOD_MONTHS = -36;
private static Log log = LogFactory.getLog(ValidationLogicImpl.class);
private static final String ADMIN = "admin";
public void init(){
log.info("init()");
//need to populate the templates
loadTemplate("validate_newUser.xml", TEMPLATE_KEY_NEW_USER);
loadTemplate("validate_existingUser.xml", TEMPLATE_KEY_EXISTINGUSER);
loadTemplate("validate_legacyUser.xml", TEMPLATE_KEY_LEGACYUSER);
loadTemplate("validate_newPassword.xml", TEMPLATE_KEY_PASSWORDRESET);
//seeing the GroupProvider is optional we need to load it here
if (groupProvider == null) {
groupProvider = (GroupProvider) ComponentManager.get(GroupProvider.class.getName());
}
}
private void loadTemplate(String fileName, String templateKey) {
//we need a user session to avoid potential NPE's
Session sakaiSession = sessionManager.getCurrentSession();
try {
sakaiSession.setUserId(ADMIN);
sakaiSession.setUserEid(ADMIN);
InputStream in = ValidationLogicImpl.class.getClassLoader().getResourceAsStream(fileName);
if(in == null) {
log.warn("Could not load resource from '" + fileName + "'. Skipping ...");
return;
}
Document document = new SAXBuilder( ).build(in);
List<Element> it = document.getRootElement().getChildren("emailTemplate");
for (int i =0; i < it.size(); i++) {
Element xmlTemplate = (Element)it.get(i);
xmlToTemplate(xmlTemplate, templateKey);
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally
{
sakaiSession.setUserId(null);
sakaiSession.setUserEid(null);
}
}
private IdManager idManager;
public void setIdManager(IdManager idm) {
idManager = idm;
}
private ValidationDao dao;
public void setDao(ValidationDao dao) {
this.dao = dao;
}
private EmailTemplateService emailTemplateService;
public void setEmailTemplateService(EmailTemplateService emailTemplateService) {
this.emailTemplateService = emailTemplateService;
}
private UserDirectoryService userDirectoryService;
public void setUserDirectoryService(UserDirectoryService userDirectoryService) {
this.userDirectoryService = userDirectoryService;
}
private AuthzGroupService authzGroupService;
public void setAuthzGroupService(AuthzGroupService authzGroupService) {
this.authzGroupService = authzGroupService;
}
private SiteService siteService;
public void setSiteService(SiteService siteService) {
this.siteService = siteService;
}
private DeveloperHelperService developerHelperService;
public void setDeveloperHelperService(
DeveloperHelperService developerHelperService) {
this.developerHelperService = developerHelperService;
}
private ServerConfigurationService serverConfigurationService;
public void setServerConfigurationService(
ServerConfigurationService serverConfigurationService) {
this.serverConfigurationService = serverConfigurationService;
}
private SecurityService securityService;
public void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
private SessionManager sessionManager;
public void setSessionManager(SessionManager sessionManager) {
this.sessionManager = sessionManager;
}
private GroupProvider groupProvider;
public void setGroupProvider(GroupProvider groupProvider) {
this.groupProvider = groupProvider;
}
public ValidationAccount getVaLidationAcountById(Long id) {
Search search = new Search();
Restriction rest = new Restriction("id", id);
search.addRestriction(rest);
List<ValidationAccount> l = dao.findBySearch(ValidationAccount.class, search);
if (l.size() >0 )
return (ValidationAccount)l.get(0);
return null;
}
public ValidationAccount getVaLidationAcountBytoken(String token) {
Search search = new Search();
Restriction rest = new Restriction("validationToken", token);
search.addRestriction(rest);
List<ValidationAccount> l = dao.findBySearch(ValidationAccount.class, search);
if (l.size() >0 )
return (ValidationAccount)l.get(0);
return null;
}
public boolean isAccountValidated(String userId) {
//this is a basic rule need to account for validations expiring
log.debug("validating" + userId);
ValidationAccount va = this.getVaLidationAcountByUserId(userId);
Calendar cal = new GregorianCalendar();
cal.add(Calendar.MONTH, VALIDATION_PERIOD_MONTHS);
//a time validation time in the past
Date validationDeadline = cal.getTime();
if (va == null) {
log.debug("no account found!");
return false;
} else {
if(ValidationAccount.STATUS_EXPIRED.equals((va.getStatus()))){
return true;
}else if (va.getValidationReceived() == null && va.getValidationSent().after(validationDeadline)) {
log.debug("validation sent still awaiting reply");
return true;
} else if (va.getValidationReceived() == null && va.getValidationSent().before(validationDeadline)) {
log.debug("validation sent but no reply received");
//what should we do in this case?
return true;
}
log.debug("got an item of staus " + va.getStatus());
if (ValidationAccount.STATUS_CONFIRMED.equals(va.getStatus())) {
log.info("account is validated");
return true;
}
}
log.debug("no conditions met assuming account is not validated");
return false;
}
public ValidationAccount getVaLidationAcountByUserId(String userId) {
Search search = new Search();
Restriction rest = new Restriction("userId", userId);
search.addRestriction(rest);
List<ValidationAccount> l = dao.findBySearch(ValidationAccount.class, search);
if (l.size() >0 )
return (ValidationAccount)l.get(0);
return null;
}
public List<ValidationAccount> getValidationAccountsByStatus(Integer status) {
Search search = new Search();
Restriction rest = new Restriction("status", status);
search.addRestriction(rest);
List<ValidationAccount> l = dao.findBySearch(ValidationAccount.class, search);
if (l.size() >0 )
return l;
return new ArrayList<ValidationAccount>();
}
public ValidationAccount createValidationAccount(String userRef) {
return createValidationAccount(userRef, false);
}
public ValidationAccount createValidationAccount(String UserId, boolean newAccount) {
Integer status = ValidationAccount.ACCOUNT_STATUS_EXISITING;
if (newAccount) {
status = ValidationAccount.ACCOUNT_STATUS_NEW;
}
return createValidationAccount(UserId, status);
}
public ValidationAccount createValidationAccount(String userRef,
Integer accountStatus) {
log.debug("createValidationAccount(" + userRef + ", " + accountStatus);
//TODO creating a new Validation should clear old ones for the user
ValidationAccount v = new ValidationAccount();
v.setUserId(userRef);
v.setValidationToken(idManager.createUuid());
v.setValidationsSent(1);
if (accountStatus == null) {
accountStatus = ValidationAccount.ACCOUNT_STATUS_NEW;
} else {
v.setAccountStatus(accountStatus);
}
//new send the validation
List<String> userReferences = new ArrayList<String>();
userReferences.add(userRef);
Map<String, String> replacementValues = new HashMap<String, String>();
replacementValues.put("validationToken", v.getValidationToken());
//get the url
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("tokenId", v.getValidationToken());
///we want a direct tool url
String serverUrl = serverConfigurationService.getServerUrl();
String url = serverUrl + "/accountvalidator/faces/validate?tokenId=" + v.getValidationToken();
replacementValues.put("url", url);
//add some details about the user
String userId = EntityReference.getIdFromRef(userRef);
String userFirstName = "";
String userLastName = "";
String userDisplayName = "";
String userEid = "";
try {
User u = userDirectoryService.getUser(userId);
if (u.getFirstName() != null)
userFirstName = u.getFirstName();
if (u.getLastName() != null)
userLastName = u.getLastName();
userDisplayName = u.getDisplayName();
userEid = u.getEid();
//information about the user that added them
User added = u.getCreatedBy();
replacementValues.put("addedBy", added.getDisplayName());
replacementValues.put("addedByEmail", added.getEmail());
} catch (UserNotDefinedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//information about the site(s) they have been added to
Set<String> groups = authzGroupService.getAuthzGroupsIsAllowed(userId, SiteService.SITE_VISIT, null);
log.info("got a list of: " + groups.size());
Iterator<String> itg = groups.iterator();
StringBuilder sb = new StringBuilder();
int siteCount = 0;
while (itg.hasNext()) {
String groupRef = itg.next();
String siteId = developerHelperService.getLocationIdFromRef(groupRef);
try {
Site s = siteService.getSite(siteId);
if (siteCount > 0) {
sb.append(", ");
}
log.info("adding site: " + s.getTitle());
sb.append(s.getTitle());
siteCount++;
} catch (IdUnusedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
replacementValues.put("memberSites", sb.toString());
replacementValues.put("displayName", userDisplayName);
replacementValues.put("userEid", userEid);
replacementValues.put("support.email", serverConfigurationService.getString("support.email"));
replacementValues.put("institution", serverConfigurationService.getString("ui.institution"));
String templateKey = getTemplateKey(accountStatus);
emailTemplateService.sendRenderedMessages(templateKey , userReferences, replacementValues, serverConfigurationService.getString("support.email"), serverConfigurationService.getString("support.email"));
v.setValidationSent(new Date());
v.setStatus(ValidationAccount.STATUS_SENT);
/*if (ValidationAccount.ACCOUNT_STATUS_PASSWORD_RESET == accountStatus.intValue()) {
//A password reset doesn't invalidate confirmation
v.setStatus(ValidationAccount.STATUS_CONFIRMED);
} else {
v.setStatus(ValidationAccount.STATUS_SENT);
}*/
v.setFirstName(userFirstName);
v.setSurname(userLastName);
dao.save(v);
return v;
}
private String getTemplateKey(Integer accountStatus) {
log.info("getTemplateKey( " + accountStatus.intValue());
String templateKey = TEMPLATE_KEY_NEW_USER;
if ( (ValidationAccount.ACCOUNT_STATUS_EXISITING == accountStatus.intValue())) {
templateKey = TEMPLATE_KEY_EXISTINGUSER;
} else if ( (ValidationAccount.ACCOUNT_STATUS_LEGACY == accountStatus.intValue() || ValidationAccount.ACCOUNT_STATUS_LEGACY_NOPASS == accountStatus.intValue())) {
templateKey = TEMPLATE_KEY_LEGACYUSER;
} else if ( (ValidationAccount.ACCOUNT_STATUS_PASSWORD_RESET == accountStatus.intValue())) {
templateKey = TEMPLATE_KEY_PASSWORDRESET;
}
return templateKey;
}
public void mergeAccounts(String oldUserReference, String newUserReference) throws ValidationException {
log.debug("merge account: " + oldUserReference + ", " + newUserReference + ")");
UserEdit olduser = null;
try {
String oldUserId = EntityReference.getIdFromRef(oldUserReference);
String newuserId = EntityReference.getIdFromRef(newUserReference);
//we need a security advisor
SecurityAdvisor secAdvice = new SecurityAdvisor() {
public SecurityAdvice isAllowed(String userId, String function, String reference) {
log.debug("isAllowed( " + userId + ", " + function + ", " + reference);
if (UserDirectoryService.SECURE_UPDATE_USER_ANY.equals(function)) {
return SecurityAdvice.ALLOWED;
} else if (AuthzGroupService.SECURE_UPDATE_AUTHZ_GROUP.equals(function)){
return SecurityAdvice.ALLOWED;
} else if (UserDirectoryService.SECURE_REMOVE_USER.equals(function)) {
log.debug("advising user can delete users");
return SecurityAdvice.ALLOWED;
} else {
return SecurityAdvice.NOT_ALLOWED;
}
}
};
securityService.pushAdvisor(secAdvice);
log.debug("pushed security avisor: " + secAdvice);
olduser = userDirectoryService.editUser(oldUserId);
//get the old users realm memberships
Set<String> groups = authzGroupService.getAuthzGroupsIsAllowed(EntityReference.getIdFromRef(oldUserReference), "site.visit", null);
Iterator<String> it = groups.iterator();
while (it.hasNext()) {
AuthzGroup group = authzGroupService.getAuthzGroup(it.next());
Member member = group.getMember(oldUserId);
//TODO we need to check if olduser and if so resolve the highest role
Member exisiting = group.getMember(newuserId);
String preferedRole = member.getRole().getId();
//the groupProvider is optional it may not be set
if (exisiting != null && groupProvider != null) {
preferedRole = groupProvider.preferredRole(preferedRole, exisiting.getRole().getId());
}
//add the new user
group.addMember(newuserId, preferedRole, true, false);
//remove the old user
group.removeMember(oldUserId);
authzGroupService.save(group);
}
//remove the old user
userDirectoryService.removeUser(olduser);
} catch (UserNotDefinedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UserPermissionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
if (olduser != null) {
userDirectoryService.cancelEdit(olduser);
}
//throw new ValidationException("don't have persmission", e);
} catch (UserLockedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (GroupNotDefinedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AuthzPermissionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
SecurityAdvisor sa = securityService.popAdvisor();
if (sa == null) {
log.warn("Something cleared our advisor!");
}
}
}
public void deleteValidationAccount(ValidationAccount toDelete) {
dao.delete(toDelete);
}
public void save(ValidationAccount toSave) {
dao.save(toSave);
}
private void xmlToTemplate(Element xmlTemplate, String key) {
String subject = xmlTemplate.getChildText("subject");
String body = xmlTemplate.getChildText("message");
String bodyHtml = xmlTemplate.getChildText("messagehtml");
String locale = xmlTemplate.getChildText("locale");
String versionString = xmlTemplate.getChildText("version");
if (emailTemplateService.getEmailTemplate(key, new Locale(locale)) == null)
{
EmailTemplate template = new EmailTemplate();
template.setSubject(subject);
template.setMessage(body);
if (bodyHtml != null) {
String decodedHtml;
try {
decodedHtml = URLDecoder.decode(bodyHtml, "utf8");
} catch (UnsupportedEncodingException e) {
decodedHtml = bodyHtml;
e.printStackTrace();
}
template.setHtmlMessage(decodedHtml);
}
template.setLocale(locale);
template.setKey(key);
template.setVersion(Integer.valueOf(1));//setVersion(versionString != null ? Integer.valueOf(versionString) : Integer.valueOf(0)); // set version
template.setOwner("admin");
template.setLastModified(new Date());
this.emailTemplateService.saveTemplate(template);
log.info(this + " user notification tempalte " + key + " added");
}
/*
else
{
EmailTemplate existingTemplate = this.emailTemplateService.getEmailTemplate(key, new Locale(locale));
String oVersionString = existingTemplate.getVersion() != null ? existingTemplate.getVersion().toString():null;
if ((oVersionString == null && versionString != null) || (oVersionString != null && versionString != null && !oVersionString.equals(versionString)))
{
existingTemplate.setSubject(subject);
existingTemplate.setMessage(body);
existingTemplate.setLocale(locale);
existingTemplate.setKey(key);
existingTemplate.setVersion(versionString != null ? Integer.valueOf(versionString) : Integer.valueOf(0)); // set version
existingTemplate.setOwner("admin");
existingTemplate.setLastModified(new Date());
this.emailTemplateService.updateTemplate(existingTemplate);
log.info(this + " user notification tempalte " + key + " updated to newer version");
}
}
*/
}
public void resendValidation(String token) {
ValidationAccount account = this.getVaLidationAcountBytoken(token);
if (account == null) {
throw new IllegalArgumentException("no such account: " + token);
}
account.setValidationSent(new Date());
account.setValidationsSent(account.getValidationsSent() + 1);
account.setStatus(ValidationAccount.STATUS_RESENT);
save(account);
//new send the validation
List<String> userReferences = new ArrayList<String>();
userReferences.add(userDirectoryService.userReference(account.getUserId()));
Map<String, String> replacementValues = new HashMap<String, String>();
replacementValues.put("validationToken", account.getValidationToken());
//get the url
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("tokenId", account.getValidationToken());
///we want a direct tool url
String serverUrl = serverConfigurationService.getServerUrl();
String url = serverUrl + "/accountvalidator/faces/validate?tokenId=" + account.getValidationToken();
replacementValues.put("url", url);
//add some details about the user
String userId = EntityReference.getIdFromRef(account.getUserId());
String userDisplayName = "";
String userEid = "";
try {
User u = userDirectoryService.getUser(userId);
userDisplayName = u.getDisplayName();
userEid = u.getEid();
//information about the user that added them
User added = u.getCreatedBy();
replacementValues.put("addedBy", added.getDisplayName());
replacementValues.put("addedByEmail", added.getEmail());
replacementValues.put("displayName", userDisplayName);
replacementValues.put("userEid", userEid);
replacementValues.put("support.email", serverConfigurationService.getString("support.email"));
replacementValues.put("institution", serverConfigurationService.getString("ui.institution"));
} catch (UserNotDefinedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//information about the site(s) they have been added to
Set<String> groups = authzGroupService.getAuthzGroupsIsAllowed(userId, SiteService.SITE_VISIT, null);
log.debug("got a list of: " + groups.size());
Iterator<String> itg = groups.iterator();
StringBuilder sb = new StringBuilder();
int siteCount = 0;
while (itg.hasNext()) {
String groupRef = itg.next();
String siteId = developerHelperService.getLocationIdFromRef(groupRef);
try {
Site s = siteService.getSite(siteId);
if (siteCount > 0) {
sb.append(", ");
}
log.debug("adding site: " + s.getTitle());
sb.append(s.getTitle());
siteCount++;
} catch (IdUnusedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
replacementValues.put("memberSites", sb.toString());
String templateKey = getTemplateKey(account.getAccountStatus());
emailTemplateService.sendRenderedMessages(templateKey , userReferences, replacementValues, serverConfigurationService.getString("support.email"), serverConfigurationService.getString("support.email"));
}
}