/** * 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.login.auth; import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; import org.olat.basesecurity.Authentication; import org.olat.basesecurity.BaseSecurity; import org.olat.core.commons.services.webdav.manager.WebDAVAuthManager; import org.olat.core.gui.translator.Translator; import org.olat.core.id.Identity; import org.olat.core.id.UserConstants; import org.olat.core.id.context.BusinessControlFactory; import org.olat.core.id.context.ContextEntry; import org.olat.core.logging.AssertException; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.manager.BasicManager; import org.olat.core.util.Encoder.Algorithm; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.core.util.WebappHelper; import org.olat.core.util.i18n.I18nManager; import org.olat.core.util.mail.MailBundle; import org.olat.core.util.mail.MailContext; import org.olat.core.util.mail.MailContextImpl; import org.olat.core.util.mail.MailHelper; import org.olat.core.util.mail.MailManager; import org.olat.core.util.resource.OresHelper; import org.olat.core.util.xml.XStreamHelper; import org.olat.ldap.LDAPError; import org.olat.ldap.LDAPLoginManager; import org.olat.ldap.LDAPLoginModule; import org.olat.ldap.ui.LDAPAuthenticationController; import org.olat.login.LoginModule; import org.olat.login.OLATAuthenticationController; import org.olat.registration.RegistrationManager; import org.olat.registration.TemporaryKey; import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.thoughtworks.xstream.XStream; /** * Initial Date: 26.09.2007 <br> * @author Felix Jost, http://www.goodsolutions.ch */ @Service("olatAuthenticationSpi") public class OLATAuthManager extends BasicManager implements AuthenticationSPI { private static final OLog log = Tracing.createLoggerFor(OLATAuthManager.class); @Autowired private UserManager userManager; @Autowired private BaseSecurity securityManager; @Autowired private MailManager mailManager; @Autowired private WebDAVAuthManager webDAVAuthManager; @Autowired private LoginModule loginModule; @Autowired private LDAPLoginModule ldapLoginModule; @Autowired private LDAPLoginManager ldapLoginManager; @Autowired private RegistrationManager registrationManager; /** * * @param identity * @param password * @param provider * @return */ @Override public Identity authenticate(Identity ident, String login, String password) { Authentication authentication; if (ident == null) { // check for email instead of username if ident is null if(loginModule.isAllowLoginUsingEmail()) { if (MailHelper.isValidEmailAddress(login)){ List<Identity> identities = userManager.findIdentitiesByEmail(Collections.singletonList(login)); // check for email changed with verification workflow if(identities.size() == 1) { ident = identities.get(0); } else if(identities.size() > 1) { logError("more than one identity found with email::" + login, null); } if (ident == null) { ident = findIdentInChangingEmailWorkflow(login); } } } if(ident == null) { authentication = securityManager.findAuthenticationByAuthusername(login, "OLAT"); } else { authentication = securityManager.findAuthentication(ident, "OLAT"); } } else { authentication = securityManager.findAuthentication(ident, "OLAT"); } if (authentication == null) { log.audit("Error authenticating user "+login+" via provider OLAT", OLATAuthenticationController.class.getName()); return null; } // find OLAT authentication provider if (securityManager.checkCredentials(authentication, password)) { Algorithm algorithm = Algorithm.find(authentication.getAlgorithm()); if(Algorithm.md5.equals(algorithm)) { Algorithm defAlgorithm = loginModule.getDefaultHashAlgorithm(); authentication = securityManager.updateCredentials(authentication, password, defAlgorithm); } Identity identity = authentication.getIdentity(); if(identity != null && webDAVAuthManager != null) { webDAVAuthManager.upgradePassword(identity, login, password); } return identity; } log.audit("Error authenticating user "+login+" via provider OLAT", OLATAuthenticationController.class.getName()); return null; } @Override public void upgradePassword(Identity identity, String login, String password) { //nothing to do } private Identity findIdentInChangingEmailWorkflow(String login){ XStream xml = XStreamHelper.createXStreamInstance(); List<TemporaryKey> tk = registrationManager.loadTemporaryKeyByAction(RegistrationManager.EMAIL_CHANGE); if (tk != null) { for (TemporaryKey temporaryKey : tk) { @SuppressWarnings("unchecked") Map<String, String> mails = (Map<String, String>)xml.fromXML(temporaryKey.getEmailAddress()); if (login.equals(mails.get("changedEMail"))) { return securityManager.findIdentityByName(mails.get("currentEMail")); } } } return null; } /** * Change the password of an identity. if the given identity is a LDAP-User, * the pw-change is propagated to LDAP (according to config) NOTE: caller of * this method should check if identity is allowed to change it's own pw [ * UserModule.isPwdchangeallowed(Identity ident) ], applies only if doer * equals identity * * @param doer * Identity who is changing the password * @param identity * Identity who's password is beeing changed. * @param newPwd * New password. * @return True upon success. */ public boolean changePassword(Identity doer, Identity identity, String newPwd) { if (doer==null) throw new AssertException("password changing identity cannot be undefined!"); if (identity.getKey() == null) throw new AssertException("cannot change password on a nonpersisted identity"); //o_clusterREVIEW identity = securityManager.loadIdentityByKey(identity.getKey()); boolean allOk = false; Authentication ldapAuth = securityManager.findAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP); if(ldapAuth != null) { if(ldapLoginModule.isPropagatePasswordChangedOnLdapServer()) { LDAPError ldapError = new LDAPError(); ldapLoginManager.changePassword(identity, newPwd, ldapError); log.audit(doer.getName() + " change the password on the LDAP server for identity: " + identity.getName()); allOk = ldapError.isEmpty(); if(allOk && ldapLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()) { allOk &= changeOlatPassword(doer, identity, identity.getName(), newPwd); } } } else { allOk = changeOlatPassword(doer, identity, identity.getName(), newPwd); } if(allOk) { sendConfirmationEmail(doer, identity); //remove try { loginModule.clearFailedLoginAttempts(identity.getName()); loginModule.clearFailedLoginAttempts(identity.getUser().getEmail()); } catch (Exception e) { log.error("", e); } } return allOk; } private void sendConfirmationEmail(Identity doer, Identity identity) { String prefsLanguage = identity.getUser().getPreferences().getLanguage(); Locale locale = I18nManager.getInstance().getLocaleOrDefault(prefsLanguage); Translator translator = Util.createPackageTranslator(OLATAuthenticationController.class, locale); ContextEntry ce = BusinessControlFactory.getInstance().createContextEntry(OresHelper.createOLATResourceableInstance("changepw", 0l)); String changePwUrl = BusinessControlFactory.getInstance().getAsURIString(Collections.singletonList(ce), false); String[] args = new String[] { identity.getName(),//0: changed users username identity.getUser().getProperty(UserConstants.EMAIL, locale),// 1: changed users email address userManager.getUserDisplayName(doer.getUser()),// 2: Name (first and last name) of user who changed the password WebappHelper.getMailConfig("mailSupport"), //3: configured support email address changePwUrl //4: direct link to change password workflow (e.g. https://xx.xx.xx/olat/url/changepw/0) }; String subject = translator.translate("mail.pwd.subject", args); String body = translator.translate("mail.pwd.body", args); MailContext context = new MailContextImpl(null, null, "[Identity:" + identity.getKey() + "]"); MailBundle bundle = new MailBundle(); bundle.setContext(context); bundle.setToId(identity); bundle.setContent(subject, body); mailManager.sendMessage(bundle); } /** * This update the OLAT and the HA1 passwords * @param doer * @param identity * @param newPwd * @return */ public boolean changeOlatPassword(Identity doer, Identity identity, String username, String newPwd) { Authentication auth = securityManager.findAuthentication(identity, "OLAT"); if (auth == null) { // create new authentication for provider OLAT auth = securityManager.createAndPersistAuthentication(identity, "OLAT", identity.getName(), newPwd, loginModule.getDefaultHashAlgorithm()); log.audit(doer.getName() + " created new authenticatin for identity: " + identity.getName()); } else { auth = securityManager.updateCredentials(auth, newPwd, loginModule.getDefaultHashAlgorithm()); log.audit(doer.getName() + " set new password for identity: " + identity.getName()); } if(identity != null && StringHelper.containsNonWhitespace(username) && webDAVAuthManager != null) { webDAVAuthManager.changeDigestPassword(doer, identity, username, newPwd); } return true; } public boolean synchronizeOlatPasswordAndUsername(Identity doer, Identity identity, String username, String newPwd) { Authentication auth = securityManager.findAuthentication(identity, "OLAT"); if (auth == null) { // create new authentication for provider OLAT auth = securityManager.createAndPersistAuthentication(identity, "OLAT", username, newPwd, loginModule.getDefaultHashAlgorithm()); log.audit(doer.getName() + " created new authenticatin for identity: " + identity.getName()); } else { //update credentials if(!securityManager.checkCredentials(auth, newPwd)) { auth = securityManager.updateCredentials(auth, newPwd, loginModule.getDefaultHashAlgorithm()); } if(!username.equals(auth.getAuthusername())) { auth.setAuthusername(username); auth = securityManager.updateAuthentication(auth); } log.audit(doer.getName() + " set new password for identity: " + identity.getName()); } if(identity != null && StringHelper.containsNonWhitespace(username) && webDAVAuthManager != null) { webDAVAuthManager.changeDigestPassword(doer, identity, username, newPwd); } return true; } /** * to change password without knowing exactly who is changing it -> change as admin * @param identity * @param newPwd * @return */ public boolean changePasswordAsAdmin(Identity identity, String newPwd) { Identity adminUserIdentity = securityManager.findIdentityByName("administrator"); return changePassword(adminUserIdentity, identity, newPwd); } /** * to change password by password forgotten link at login screen * @param identity * @param newPwd * @return */ public boolean changePasswordByPasswordForgottenLink(Identity identity, String newPwd) { return changePassword(identity, identity, newPwd); } }