Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
LDAPAuthenticationController.java 11.37 KiB
/**
 * <a href="http://www.openolat.org">
 * OpenOLAT - Online Learning and Training</a><br>
 * <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 the
 * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
 * <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>
 * Initial code contributed and copyrighted by<br>
 * frentix GmbH, http://www.frentix.com
 * <p>
 */
package org.olat.ldap.ui;

import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.naming.directory.Attributes;

import org.olat.admin.user.delete.service.UserDeletionManager;
import org.olat.basesecurity.AuthHelper;
import org.olat.basesecurity.Authentication;
import org.olat.basesecurity.BaseSecurity;
import org.olat.basesecurity.BaseSecurityManager;
import org.olat.basesecurity.BaseSecurityModule;
import org.olat.core.CoreSpringFactory;
import org.olat.core.commons.persistence.DBFactory;
import org.olat.core.dispatcher.DispatcherAction;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.link.LinkFactory;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.id.Identity;
import org.olat.core.id.context.ContextEntry;
import org.olat.core.id.context.StateEntry;
import org.olat.core.logging.OLATRuntimeException;
import org.olat.core.logging.OLATSecurityException;
import org.olat.core.util.Encoder;
import org.olat.core.util.Util;
import org.olat.core.util.WebappHelper;
import org.olat.ldap.LDAPError;
import org.olat.ldap.LDAPLoginManager;
import org.olat.ldap.LDAPLoginModule;
import org.olat.login.LoginModule;
import org.olat.login.OLATAuthenticationController;
import org.olat.login.auth.AuthenticationController;
import org.olat.login.auth.OLATAuthentcationForm;
import org.olat.registration.DisclaimerController;
import org.olat.registration.PwChangeController;
import org.olat.registration.RegistrationManager;
import org.olat.user.UserModule;

import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
import org.olat.core.gui.control.generic.dtabs.Activateable2;

public class LDAPAuthenticationController extends AuthenticationController implements Activateable2 {
	public static final String PROVIDER_LDAP = "LDAP";
	
	private VelocityContainer loginComp;
	private Link pwLink;
	private Link anoLink;
	private Controller subController;
	private OLATAuthentcationForm loginForm; 
	private DisclaimerController disclaimerCtr;
	private Identity authenticatedIdentity;
	private String provider = null;

	private CloseableModalController cmc;

	public LDAPAuthenticationController(UserRequest ureq, WindowControl control) {
		// use fallback translator to login and registration package
		super(ureq, control, Util.createPackageTranslator(LoginModule.class, ureq.getLocale(), Util.createPackageTranslator(RegistrationManager.class, ureq.getLocale())));
		
		loginComp = createVelocityContainer("ldaplogin");
		
		if(UserModule.isPwdchangeallowed(null) && LDAPLoginModule.isPropagatePasswordChangedOnLdapServer()) {
			pwLink = LinkFactory.createLink("_olat_login_change_pwd", "menu.pw", loginComp, this);
			pwLink.setCustomEnabledLinkCSS("o_login_pwd  b_with_small_icon_left");
		}
		if (LoginModule.isGuestLoginLinksEnabled()) {
			anoLink = LinkFactory.createLink("_olat_login_guest", "menu.guest", loginComp, this);
			anoLink.setCustomEnabledLinkCSS("o_login_guests  b_with_small_icon_left");
		}
		
		// Use the standard OLAT login form but with our LDAP translator
		loginForm = new OLATAuthentcationForm(ureq, control, "ldap_login", getTranslator());
		listenTo(loginForm);
		
		loginComp.put("ldapForm", loginForm.getInitialComponent());
		
		putInitialPanel(loginComp);
	}

	@Override
	public void changeLocale(Locale newLocale) {
		setLocale(newLocale, true);
	}

	
@Override
protected void event(UserRequest ureq, Component source, Event event) {
	if (source == pwLink) {
		openChangePassword(ureq, null);	//fxdiff FXOLAT-113: business path in DMZ
	} else if (source == anoLink) {
			if (AuthHelper.doAnonymousLogin(ureq, ureq.getLocale()) == AuthHelper.LOGIN_OK) return;
			else showError("login.error", WebappHelper.getMailConfig("mailSupport"));
		}
	}
	//fxdiff FXOLAT-113: business path in DMZ
	protected void openChangePassword(UserRequest ureq, String initialEmail) {
		// double-check if allowed first
		if (!UserModule.isPwdchangeallowed(ureq.getIdentity()) || !LDAPLoginModule.isPropagatePasswordChangedOnLdapServer())
			throw new OLATSecurityException("chose password to be changed, but disallowed by config");

		
		removeAsListenerAndDispose(subController);
		subController = new PwChangeController(ureq, getWindowControl(), initialEmail);
		listenTo(subController);
		
		removeAsListenerAndDispose(cmc);
		cmc = new CloseableModalController(getWindowControl(), translate("close"), subController.getInitialComponent());
		listenTo(cmc);
		
		cmc.activate();
	}
	
	protected void event(UserRequest ureq, Controller source, Event event) {
		
		LDAPError ldapError = new LDAPError();
		
		if (source == loginForm && event == Event.DONE_EVENT) {

			String login = loginForm.getLogin();
			String pass = loginForm.getPass();

			authenticatedIdentity= authenticate(login, pass, ldapError);

			if (authenticatedIdentity != null) {
				provider = LDAPAuthenticationController.PROVIDER_LDAP;
			} else {
				// try fallback to OLAT provider if configured
				if (LDAPLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()) {
					authenticatedIdentity = OLATAuthenticationController.authenticate(login, pass);
				}
				if (authenticatedIdentity != null) {
					provider = BaseSecurityModule.getDefaultAuthProviderIdentifier();
				}
			}
			// Still not found? register for hacking attempts
			if (authenticatedIdentity == null) {
				if (LoginModule.registerFailedLoginAttempt(login)) {
					logAudit("Too many failed login attempts for " + login + ". Login blocked.", null);
					showError("login.blocked", LoginModule.getAttackPreventionTimeoutMin().toString());
					return;
				} else {
					showError("login.error", ldapError.get());
					return;
				}
			}

			LoginModule.clearFailedLoginAttempts(login);

			// Check if disclaimer has been accepted
			if (RegistrationManager.getInstance().needsToConfirmDisclaimer(authenticatedIdentity)) {
				// accept disclaimer first
				
				removeAsListenerAndDispose(disclaimerCtr);
				disclaimerCtr = new DisclaimerController(ureq, getWindowControl());
				listenTo(disclaimerCtr);
				
				removeAsListenerAndDispose(cmc);
				cmc = new CloseableModalController(getWindowControl(), translate("close"), disclaimerCtr.getInitialComponent());
				listenTo(cmc);

				cmc.activate();	

			} else {
				// disclaimer acceptance not required
				doLoginAndRegister(authenticatedIdentity, ureq, provider);
			}
		}
		
		if (source == subController) {
			if (event == Event.CANCELLED_EVENT || event == Event.DONE_EVENT) {
				cmc.deactivate();
			}
		} else if (source == disclaimerCtr) {
			cmc.deactivate();
			if (event == Event.DONE_EVENT) {
				// User accepted disclaimer, do login now
				RegistrationManager.getInstance().setHasConfirmedDislaimer(authenticatedIdentity);
				doLoginAndRegister(authenticatedIdentity, ureq, provider);
			} else if (event == Event.CANCELLED_EVENT) {
				// User did not accept, workflow ends here
				showWarning("disclaimer.form.cancelled");
			}
		} else if (source == cmc) {
			// User did close disclaimer window, workflow ends here
			showWarning("disclaimer.form.cancelled");			
		}
	}
	
	@Override
	//fxdiff FXOLAT-113: business path in DMZ
	public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
		if(entries == null || entries.isEmpty()) return;
		
		String type = entries.get(0).getOLATResourceable().getResourceableTypeName();
		if("changepw".equals(type)) {
			String email = null;
			if(entries.size() > 1) {
				email = entries.get(1).getOLATResourceable().getResourceableTypeName();
			}
			openChangePassword(ureq, email);
		}
	}
	
	public static Identity authenticate(String username, String pwd, LDAPError ldapError) {
		
		LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class);
		Attributes attrs = ldapManager.bindUser(username, pwd, ldapError);
		
		if (ldapError.isEmpty() && attrs != null) { 
			Identity identity = ldapManager.findIdentyByLdapAuthentication(username, ldapError);
			if (!ldapError.isEmpty()) return null;
			if (identity == null) {
				// User authenticated but not yet existing - create as new OLAT user
				ldapManager.createAndPersistUser(attrs);
				identity = ldapManager.findIdentyByLdapAuthentication(username, ldapError);
			} else {
				// User does already exist - just sync attributes
				Map<String, String> olatProToSync = ldapManager.prepareUserPropertyForSync(attrs, identity);
				if (olatProToSync != null) {
					ldapManager.syncUser(olatProToSync, identity);
				}
			}
			// Add or update an OLAT authentication token for this user if configured in the module
			if (identity != null && LDAPLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()) {
				BaseSecurity secMgr = BaseSecurityManager.getInstance();
				Authentication auth = secMgr.findAuthentication(identity, BaseSecurityModule.getDefaultAuthProviderIdentifier());
				if (auth == null) {
					// Create new authentication token
					secMgr.createAndPersistAuthentication(identity, BaseSecurityModule.getDefaultAuthProviderIdentifier(), username, Encoder.encrypt(pwd));
				} else {
					// Reuse existing authentication token
					auth.setCredential(Encoder.encrypt(pwd));
					DBFactory.getInstance().updateObject(auth);
				}				
			}
			return identity;
		} 
		return null;
	}
	
	/**
	 * Internal helper to perform the real login code and do all necessary steps to
	 * register the user session
	 * 
	 * @param authenticatedIdentity
	 * @param ureq
	 * @param myProvider The provider that identified the user
	 */
	private void doLoginAndRegister(Identity authenticatedIdentity, UserRequest ureq, String myProvider) {
		if (provider.equals(PROVIDER_LDAP)) {
			// prepare redirects to home etc, set status
			int loginStatus = AuthHelper.doLogin(authenticatedIdentity, myProvider, ureq);
			if (loginStatus == AuthHelper.LOGIN_OK) {
				//update last login date and register active user
				UserDeletionManager.getInstance().setIdentityAsActiv(authenticatedIdentity);
			} else if (loginStatus == AuthHelper.LOGIN_NOTAVAILABLE){
				DispatcherAction.redirectToServiceNotAvailable( ureq.getHttpResp() );
			} else {
				getWindowControl().setError(translate("login.error", WebappHelper.getMailConfig("mailSupport")));
			}
		} else if (provider.equals(BaseSecurityModule.getDefaultAuthProviderIdentifier())) {
			// delegate login process to OLAT authentication controller
			authenticated(ureq, authenticatedIdentity);		
		} else {
			throw new OLATRuntimeException("Unknown login provider::" + myProvider, null);
		}
	}
	
	@Override
	protected void doDispose() {
		//
	}
}