/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
*/
package org.olat.registration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import javax.mail.Address;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.olat.basesecurity.BaseSecurity;
import org.olat.core.commons.persistence.DB;
import org.olat.core.gui.translator.Translator;
import org.olat.core.helpers.Settings;
import org.olat.core.id.Identity;
import org.olat.core.id.User;
import org.olat.core.id.UserConstants;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.Encoder;
import org.olat.core.util.StringHelper;
import org.olat.core.util.Util;
import org.olat.core.util.WebappHelper;
import org.olat.core.util.i18n.I18nModule;
import org.olat.core.util.mail.MailManager;
import org.olat.core.util.mail.MailerResult;
import org.olat.properties.Property;
import org.olat.properties.PropertyManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Description:
*
* @author Sabina Jeger
*/
@Service("selfRegistrationManager")
public class RegistrationManager {
private static final OLog log = Tracing.createLoggerFor(RegistrationManager.class);
public static final String PW_CHANGE = "PW_CHANGE";
public static final String REGISTRATION = "REGISTRATION";
public static final String EMAIL_CHANGE = "EMAIL_CHANGE";
protected static final int REG_WORKFLOW_STEPS = 5;
@Autowired
private DB dbInstance;
@Autowired
private MailManager mailManager;
@Autowired
private BaseSecurity securityManager;
@Autowired
private PropertyManager propertyManager;
@Autowired
private RegistrationModule registrationModule;
public boolean validateEmailUsername(String email) {
List<String> whiteList = registrationModule.getDomainList();
if(whiteList.isEmpty()) {
return true;
}
if(!StringHelper.containsNonWhitespace(email)) {
return false;
}
int index = email.indexOf('@');
if(index < 0 || index+1 >= email.length()) {
return false;
}
String emailDomain = email.substring(index+1);
boolean valid = false;
for(String domain:whiteList) {
try {
String pattern = convertDomainPattern(domain);
if(emailDomain.matches(pattern)) {
valid = true;
break;
}
} catch (Exception e) {
log.error("Error matching an email adress", e);
}
}
return valid;
}
/**
* Validate the white list (prevent exception from regex matcher)
* @param list
* @return
*/
public List<String> validateWhiteList(List<String> list) {
if(list.isEmpty()) {
return Collections.emptyList();
}
String emailDomain = "openolat.org";
List<String> errors = new ArrayList<String>();
for(String domain:list) {
try {
String pattern = convertDomainPattern(domain);
emailDomain.matches(pattern);
} catch (Exception e) {
errors.add(domain);
log.error("Error matching an email adress", e);
}
}
return errors;
}
private String convertDomainPattern(String domain) {
if(domain.indexOf('*') >= 0) {
domain = domain.replace("*", ".*");
}
return domain;
}
/**
* creates a new user and identity with the data of the temporary key (email) and other
* supplied user data (within myUser)
*
* @param login Login name
* @param pwd Password
* @param myUser Not yet persisted user object
* @param tk Temporary key
* @return the newly created subject or null
*/
public Identity createNewUserAndIdentityFromTemporaryKey(String login, String pwd, User myUser, TemporaryKey tk) {
Identity identity = securityManager.createAndPersistIdentityAndUserWithDefaultProviderAndUserGroup(login, null, pwd, myUser);
if (identity == null) return null;
deleteTemporaryKey(tk);
return identity;
}
/**
* Send a notification messaged to the given notification email address about the registratoin of
* the given new identity.
* @param notificationMailAddress Email address who should be notified. MUST NOT BE NULL
* @param newIdentity The newly registered Identity
*/
public void sendNewUserNotificationMessage(String notificationMailAddress, Identity newIdentity) {
Address from;
Address[] to;
try {
// fxdiff: change from/replyto, see FXOLAT-74
from = new InternetAddress(WebappHelper.getMailConfig("mailReplyTo"));
to = new Address[] { new InternetAddress(notificationMailAddress)};
} catch (AddressException e) {
log.error("Could not send registration notification message, bad mail address", e);
return;
}
MailerResult result = new MailerResult();
User user = newIdentity.getUser();
Locale loc = I18nModule.getDefaultLocale();
String[] userParams = new String[] {newIdentity.getName(), user.getProperty(UserConstants.FIRSTNAME, loc), user.getProperty(UserConstants.LASTNAME, loc), user.getProperty(UserConstants.EMAIL, loc),
user.getPreferences().getLanguage(), Settings.getServerDomainName() + WebappHelper.getServletContextPath() };
Translator trans = Util.createPackageTranslator(RegistrationManager.class, loc);
String subject = trans.translate("reg.notiEmail.subject", userParams);
String body = trans.translate("reg.notiEmail.body", userParams);
MimeMessage msg = mailManager.createMimeMessage(from, to, null, null, body, subject, null, result);
mailManager.sendMessage(msg, result);
if (result.getReturnCode() != MailerResult.OK ) {
log.error("Could not send registration notification message, MailerResult was ::" + result.getReturnCode(), null);
}
}
/**
* A temporary key is created
*
* @param email address of new user
* @param ip address of new user
* @param action REGISTRATION or PWCHANGE
*
* @return TemporaryKey
*/
public TemporaryKey createTemporaryKeyByEmail(String email, String ip, String action) {
// check if the user is already registered
// we also try to find it in the temporarykey list
List<TemporaryKey> tks = dbInstance.getCurrentEntityManager()
.createNamedQuery("loadTemporaryKeyByEmailAddress", TemporaryKey.class)
.setParameter("email", email)
.getResultList();
TemporaryKey tk;
if ((tks == null) || (tks.size() != 1)) { // no user found, create a new one
tk = register(email, ip, action);
} else {
tk = tks.get(0);
}
return tk;
}
/**
* deletes a TemporaryKey
*
* @param key the temporary key to be deleted
*
* @return true if successfully deleted
*/
public void deleteTemporaryKey(TemporaryKey key) {
TemporaryKeyImpl reloadedKey = dbInstance.getCurrentEntityManager()
.getReference(TemporaryKeyImpl.class, key.getKey());
dbInstance.getCurrentEntityManager().remove(reloadedKey);
}
/**
* returns an existing TemporaryKey by a given email address or null if none
* found
*
* @param email
*
* @return the found temporary key or null if none is found
*/
public TemporaryKey loadTemporaryKeyByEmail(String email) {
List<TemporaryKey> tks = dbInstance.getCurrentEntityManager()
.createNamedQuery("loadTemporaryKeyByEmailAddress", TemporaryKey.class)
.setParameter("email", email)
.getResultList();
if (tks.size() == 1) {
return tks.get(0);
}
return null;
}
/**
* returns an existing list of TemporaryKey by a given action or null if none
* found
*
* @param action
*
* @return the found temporary key or null if none is found
*/
public List<TemporaryKey> loadTemporaryKeyByAction(String action) {
List<TemporaryKey> tks = dbInstance.getCurrentEntityManager()
.createNamedQuery("loadTemporaryKeyByRegAction", TemporaryKey.class)
.setParameter("action", action)
.getResultList();
if (tks.size() > 0) {
return tks;
} else {
return null;
}
}
/**
* Looks for a TemporaryKey by a given registrationkey
*
* @param regkey the encrypted registrationkey
*
* @return the found TemporaryKey or null if none is found
*/
public TemporaryKey loadTemporaryKeyByRegistrationKey(String regkey) {
List<TemporaryKey> tks = dbInstance.getCurrentEntityManager()
.createNamedQuery("loadTemporaryKeyByRegKey", TemporaryKey.class)
.setParameter("regkey", regkey)
.getResultList();
if (tks.size() == 1) {
return tks.get(0);
}
return null;
}
/**
* Creates a TemporaryKey and saves it permanently
*
* @param emailaddress
* @param ipaddress
* @param action REGISTRATION or PWCHANGE
*
* @return newly created temporary key
*/
public TemporaryKey register(String emailaddress, String ipaddress, String action) {
String today = new Date().toString();
String encryptMe = Encoder.md5hash(emailaddress + ipaddress + today);
TemporaryKeyImpl tk = new TemporaryKeyImpl();
tk.setCreationDate(new Date());
tk.setEmailAddress(emailaddress);
tk.setIpAddress(ipaddress);
tk.setRegistrationKey(encryptMe);
tk.setRegAction(action);
dbInstance.getCurrentEntityManager().persist(tk);
return tk;
}
/**
* Delete a temporary key.
* @param keyValue
*/
public void deleteTemporaryKeyWithId(String keyValue) {
TemporaryKey tKey = loadTemporaryKeyByRegistrationKey(keyValue);
if(tKey != null) {
deleteTemporaryKey(tKey);
}
}
/**
* Evaluates whether the given identity needs to accept a disclaimer before
* logging in or not.
*
* @param identity
* @return true: user must accept the disclaimer; false: user did already
* accept or must not accept a disclaimer
*/
public boolean needsToConfirmDisclaimer(Identity identity) {
boolean needsToConfirm = false; // default is not to confirm
if (registrationModule.isDisclaimerEnabled()) {
// don't use the discrete method to be more robust in case that more than one
// property is found
List<Property> disclaimerProperties = propertyManager.listProperties(identity, null, null, "user", "dislaimer_accepted");
needsToConfirm = ( disclaimerProperties.size() == 0);
}
return needsToConfirm;
}
/**
* Marks the given identity to have confirmed the disclaimer. Note that this
* method does not check if the disclaimer does already exist, do this by
* calling needsToConfirmDisclaimer() first!
*
* @param identity
*/
public void setHasConfirmedDislaimer(Identity identity) {
Property disclaimerProperty = propertyManager.createUserPropertyInstance(identity, "user", "dislaimer_accepted", null, 1l, null, null);
propertyManager.saveProperty(disclaimerProperty);
}
/**
* Remove all disclaimer confirmations. This means that every user on the
* system must accept the disclaimer again.
*/
public void revokeAllconfirmedDisclaimers() {
propertyManager.deleteProperties(null, null, null, "user", "dislaimer_accepted");
}
/**
* Remove the disclaimer confirmation for the specified identity. This means
* that this user must accept the disclaimer again.
*
* @param identity
*/
public void revokeConfirmedDisclaimer(Identity identity) {
propertyManager.deleteProperties(identity, null, null, "user", "dislaimer_accepted");
}
}