Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
ShibbolethRegistrationController.java 16.33 KiB
/**
* 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.shibboleth;

import java.util.Locale;

import javax.servlet.http.HttpServletRequest;

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.chiefcontrollers.LanguageChangedEvent;
import org.olat.core.commons.fullWebApp.LayoutMain3ColsController;
import org.olat.core.dispatcher.DispatcherModule;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.ChiefController;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.ControllerEventListener;
import org.olat.core.gui.control.DefaultController;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.LocaleChangedEvent;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.translator.Translator;
import org.olat.core.id.Identity;
import org.olat.core.id.UserConstants;
import org.olat.core.logging.AssertException;
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.session.UserSessionManager;
import org.olat.dispatcher.LocaleNegotiator;
import org.olat.registration.DisclaimerController;
import org.olat.registration.LanguageChooserController;
import org.olat.registration.RegistrationManager;
import org.olat.registration.RegistrationModule;
import org.olat.registration.UserNameCreationInterceptor;
import org.olat.shibboleth.manager.ShibbolethAttributes;
import org.olat.user.UserManager;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Initial Date:  09.08.2004
 *
 * @author Mike Stock
 *
 * Comment:
 * User wants ShibbolethAuthentication
 * - Basic flow:
 * System asks User for username and create olataccount with ShibbolethAuthentication
 * Branches:
 * 1. no email in shibbolethAttributes
 * 		- System asks for emailaddress (no institutionalEmail is set !!!)
 * 2. no email in shibbolethAttributes and User already exists in System
 * 		- System asks for password (no institutionalEmail is set !!!)
 *
 */

public class ShibbolethRegistrationController extends DefaultController implements ControllerEventListener {

	private static final String VELOCITY_ROOT = Util.getPackageVelocityRoot(ShibbolethModule.class);
	private static final String KEY_SHIBATTRIBUTES = "shibattr";
	private static final String KEY_SHIBUNIQUEID = "shibuid";

	private VelocityContainer mainContainer;
	private ShibbolethRegistrationForm regForm;
	private ShibbolethMigrationForm migrationForm;
	private ShibbolethRegistrationUserPropertiesFrom regWithUserPropForm;
	private DisclaimerController dclController;
	private LanguageChooserController languageChooserController;

	private Translator translator;
	private ShibbolethAttributes shibbolethAttributes;
	private String shibbolethUniqueID;

	private int state = STATE_UNDEFINED;
	private static final int STATE_UNDEFINED = 0;
	private static final int STATE_NEW_SHIB_USER = 1;
	private static final int STATE_MIGRATED_SHIB_USER = 2;
	private String proposedUsername;
	Locale locale;

	@Autowired
	private ShibbolethModule shibbolethModule;
	@Autowired
	private ShibbolethManager shibbolethManager;
	@Autowired
	private RegistrationModule registrationModule;

	/**
	 * Implements the shibboleth registration workflow.
	 * @param ureq
	 * @param wControl
	 */
	public ShibbolethRegistrationController(UserRequest ureq, WindowControl wControl) {
		super(wControl);

		translator = Util.createPackageTranslator(ShibbolethModule.class, ureq.getLocale());
		shibbolethAttributes = (ShibbolethAttributes)ureq.getUserSession().getEntry(KEY_SHIBATTRIBUTES);
		shibbolethUniqueID = (String)ureq.getUserSession().getEntry(KEY_SHIBUNIQUEID);

		if (shibbolethUniqueID == null) {
			ChiefController msgcc = MessageWindowController.createMessageChiefController(ureq,
					new AssertException("ShibbolethRegistrationController was unable to fetch ShibbolethUniqueID from session."), translator.translate("error.shibboleth.generic"), null);
			msgcc.getWindow().dispatchRequest(ureq, true);
			return;
		}

		locale = (Locale)ureq.getUserSession().getEntry(LocaleNegotiator.NEGOTIATED_LOCALE);
		if(locale == null) {
			String preferedLanguage = shibbolethAttributes.getPreferredLanguage();
			if(preferedLanguage == null) {
				locale = LocaleNegotiator.getPreferedLocale(ureq);
			} else {
				locale = LocaleNegotiator.getNegotiatedLocale(preferedLanguage);
				if(locale == null) {
					locale = LocaleNegotiator.getPreferedLocale(ureq);
				}
			}
		}
		ureq.getUserSession().setLocale(locale);
		I18nManager.updateLocaleInfoToThread(ureq.getUserSession());
		ureq.getUserSession().putEntry(LocaleNegotiator.NEGOTIATED_LOCALE, locale);

		translator = Util.createPackageTranslator(ShibbolethModule.class, ureq.getLocale());
		mainContainer = new VelocityContainer("main", VELOCITY_ROOT + "/langchooser.html", translator, this);

		languageChooserController = new LanguageChooserController(ureq, wControl, false);
		languageChooserController.addControllerListener(this);
		mainContainer.put("select.language", languageChooserController.getInitialComponent());
		mainContainer.contextPut("languageCode", locale.getLanguage());

		if(registrationModule.getUsernamePresetBean() != null) {
			UserNameCreationInterceptor interceptor = registrationModule.getUsernamePresetBean();
			proposedUsername = interceptor.getUsernameFor(shibbolethAttributes.toMap());
			if(proposedUsername == null) {
				if(interceptor.allowChangeOfUsername()) {
					setRegistrationForm(ureq, wControl, proposedUsername);
				} else {
					setErrorPage("sm.error.no_username", wControl);
				}
			} else {
				Identity identity = BaseSecurityManager.getInstance().findIdentityByName(proposedUsername);
				if(identity != null) {
					if(interceptor.allowChangeOfUsername()) {
						setRegistrationForm(ureq, wControl, proposedUsername);
					} else {
						setErrorPage("sm.error.username_in_use", wControl);
					}
				} else if(interceptor.allowChangeOfUsername()) {
					setRegistrationForm(ureq, wControl, proposedUsername);
				} else {
					if(areMandatoryUserPropertiesAvailable()) {
						state = STATE_NEW_SHIB_USER;
						mainContainer.setPage(VELOCITY_ROOT + "/disclaimer.html");
					} else {
						regWithUserPropForm = new ShibbolethRegistrationUserPropertiesFrom(ureq, wControl, shibbolethAttributes);
						regWithUserPropForm.addControllerListener(this);
						mainContainer.put("getUserPropsForm", regWithUserPropForm.getInitialComponent());
						mainContainer.setPage(VELOCITY_ROOT + "/register_user_props.html");
					}
				}
			}
		} else {
			setRegistrationForm(ureq, wControl, null);
		}

		dclController = new DisclaimerController(ureq, getWindowControl());
		dclController.addControllerListener(this);
		mainContainer.put("dclComp", dclController.getInitialComponent());

		// load view in layout
		LayoutMain3ColsController layoutCtr = new LayoutMain3ColsController(ureq, getWindowControl(), null, mainContainer, null);
		setInitialComponent(layoutCtr.getInitialComponent());
	}

	private void setErrorPage(String errorKey, WindowControl wControl) {
		String error = translator.translate(errorKey);
		wControl.setError(error);
		mainContainer.contextPut("error_msg", error);
		mainContainer.setPage(VELOCITY_ROOT + "/error.html");
	}

	private void setRegistrationForm(UserRequest ureq, WindowControl wControl, String proposedUsername) {
		regForm = new ShibbolethRegistrationForm(ureq, wControl, proposedUsername);
		regForm.addControllerListener(this);
		mainContainer.put("regForm", regForm.getInitialComponent());
	}

	/**
	 * Put shibboleth attributes map in reqest for later usage.
	 * @param req
	 * @param attributes
	 */
	public static final void putShibAttributes(HttpServletRequest req, ShibbolethAttributes attributes) {
		CoreSpringFactory.getImpl(UserSessionManager.class).getUserSession(req).putEntry(KEY_SHIBATTRIBUTES, attributes);
	}

	/**
	 * Put shibboleth unique identifier in request for later usage.
	 * @param req
	 * @param uniqueID
	 */
	public static final void putShibUniqueID(HttpServletRequest req, String uniqueID) {
		CoreSpringFactory.getImpl(UserSessionManager.class).getUserSession(req).putEntry(KEY_SHIBUNIQUEID, uniqueID);
	}

	@Override
	public void event(UserRequest ureq, Component source, Event event) {
		if (event instanceof LocaleChangedEvent) {
			LocaleChangedEvent lce = (LocaleChangedEvent)event;
			Locale newLocale = lce.getNewLocale();
			translator.setLocale(newLocale);
			dclController.changeLocale(newLocale);
		}
	}

	@Override
	public void event(UserRequest ureq, Controller source, Event event) {
		if (source == migrationForm) {
			if (event == Event.CANCELLED_EVENT) {
				mainContainer.setPage(VELOCITY_ROOT + "/register.html");
			} else if (event == Event.DONE_EVENT) {
				state = STATE_MIGRATED_SHIB_USER;
				mainContainer.setPage(VELOCITY_ROOT + "/disclaimer.html");
			}
		} else if (source == regWithUserPropForm){
			if (event == Event.CANCELLED_EVENT) {
				mainContainer.setPage(VELOCITY_ROOT + "/register.html");
			} else if (event == Event.DONE_EVENT) {
				state = STATE_NEW_SHIB_USER;
				mainContainer.setPage(VELOCITY_ROOT + "/disclaimer.html");
			}
		} else if (source == regForm) {
			if (event == Event.DONE_EVENT) {
				String choosenLogin = regForm.getLogin();
				BaseSecurity secMgr = BaseSecurityManager.getInstance();
				Identity identity = secMgr.findIdentityByName(choosenLogin);


				if (identity == null) { // ok, create new user
					if (isMandatoryUserPropertyMissing()){
						regWithUserPropForm = new ShibbolethRegistrationUserPropertiesFrom(ureq, getWindowControl(), shibbolethAttributes);
						regWithUserPropForm.addControllerListener(this);
						mainContainer.put("getUserPropsForm", regWithUserPropForm.getInitialComponent());
						mainContainer.setPage(VELOCITY_ROOT + "/register_user_props.html");
					} else {
						state = STATE_NEW_SHIB_USER;
						mainContainer.setPage(VELOCITY_ROOT + "/disclaimer.html");
					}
				} else { // offer identity migration, if OLAT provider exists
					Authentication auth = secMgr.findAuthentication(identity, BaseSecurityModule.getDefaultAuthProviderIdentifier());
					if (auth == null) { // no OLAT provider, migration not possible...
						getWindowControl().setError(translator.translate("sr.error.loginexists", new String[] {WebappHelper.getMailConfig("mailSupport")}));
					}	else { // OLAT provider exists, offer migration...
						migrationForm = new ShibbolethMigrationForm(ureq, getWindowControl(), auth);
						migrationForm.addControllerListener(this);
						mainContainer.put("migrationForm", migrationForm.getInitialComponent());
						mainContainer.setPage(VELOCITY_ROOT + "/migration.html");
					}
				}
			}
		} else if (source == languageChooserController) {
			if (event == Event.DONE_EVENT) { // language choosed
				mainContainer.setPage(VELOCITY_ROOT + "/register.html");
				ureq.getUserSession().removeEntry(LocaleNegotiator.NEGOTIATED_LOCALE);
			} else if (event instanceof LanguageChangedEvent) {
				LanguageChangedEvent lcev = (LanguageChangedEvent)event;
				translator.setLocale(lcev.getNewLocale());
				dclController.changeLocale(lcev.getNewLocale());
			}
		} else if (source == dclController) {
			if (event == Event.DONE_EVENT) { // disclaimer accepted...
				if (state == STATE_NEW_SHIB_USER) { // ...proceed and create user
					String choosenLogin;
					if(regForm == null ) {
						choosenLogin = proposedUsername;
					} else {
						choosenLogin = regForm.getLogin();
					}

					// check if login has been taken by another user in the meantime...
					BaseSecurity secMgr = BaseSecurityManager.getInstance();

					// check if login has been taken by another user in the meantime...
					Identity identity = secMgr.findIdentityByName(choosenLogin);
					if (identity != null) {
						getWindowControl().setError(translator.translate("sr.login.meantimetaken"));
						mainContainer.setPage(VELOCITY_ROOT + "/register.html");
						state = STATE_UNDEFINED;
						return;
					}

					String email = shibbolethAttributes.getValueForUserPropertyName(UserConstants.EMAIL);
					if (!UserManager.getInstance().isEmailAllowed(email)) {
						// error, email already exists. should actually not happen if OLAT Authenticator has
						// been set after removing shibboleth authenticator
						getWindowControl().setError(translator.translate("sr.error.emailexists", new String[] {WebappHelper.getMailConfig("mailSupport")}));
						mainContainer.setPage(VELOCITY_ROOT + "/register.html");
						state = STATE_UNDEFINED;
						return;
					}

					identity = shibbolethManager.createUser(choosenLogin, shibbolethUniqueID, locale.getLanguage(), shibbolethAttributes);

					// tell system that this user did accept the disclaimer
					CoreSpringFactory.getImpl(RegistrationManager.class).setHasConfirmedDislaimer(identity);
					doLogin(identity, ureq);
					return;
				} else if (state == STATE_MIGRATED_SHIB_USER) { // ...proceed and migrate user
					// create additional authentication
					Authentication auth = migrationForm.getAuthentication();
					Identity authenticationedIdentity = auth.getIdentity();
					BaseSecurity secMgr = BaseSecurityManager.getInstance();
					secMgr.createAndPersistAuthentication(authenticationedIdentity, ShibbolethDispatcher.PROVIDER_SHIB, shibbolethUniqueID, null, null);

					// update user profile
					shibbolethManager.syncUser(authenticationedIdentity, shibbolethAttributes);

					doLogin(authenticationedIdentity, ureq);
					return;
				}
			} else if (event == Event.CANCELLED_EVENT) {
				mainContainer.setPage(VELOCITY_ROOT + "/register.html");
				getWindowControl().setError(translator.translate("sr.error.disclaimer"));
			}
		}
	}

	private boolean isMandatoryUserPropertyMissing() {
		return !areMandatoryUserPropertiesAvailable();
	}

	private boolean areMandatoryUserPropertiesAvailable() {
		for (String userPropertyName: shibbolethModule.getMandatoryUserProperties()) {
			String value = shibbolethAttributes.getValueForUserPropertyName(userPropertyName);
			if (!StringHelper.containsNonWhitespace(value)) {
				return false;
			}
		}
		return true;
	}

	private void doLogin(Identity identity, UserRequest ureq) {
		int loginStatus = AuthHelper.doLogin(identity, ShibbolethDispatcher.PROVIDER_SHIB, ureq);
		if (loginStatus != AuthHelper.LOGIN_OK) {
			//REVIEW:2010-01-11:revisited:pb: do not redirect if already MediaResource is set before
			//ureq.getDispatchResult().setResultingMediaResource(resultingMediaResource);
			//instead set the media resource accordingly
			//pb -> provide a DispatcherAction.getDefaultDispatcherRedirectMediaresource();
			//to be used here. (and some more places like CatalogController.
			DispatcherModule.redirectToDefaultDispatcher(ureq.getHttpResp()); // error, redirect to login screen
			return;
		}
		// successfull login
		ureq.getUserSession().getIdentityEnvironment().addAttributes(
				shibbolethModule.getAttributeTranslator().translateAttributesMap(shibbolethAttributes.toMap()));
	}

	/**
	 *
	 * @see org.olat.core.gui.control.DefaultController#doDispose(boolean)
	 */
	@Override
	protected void doDispose() {
		if (dclController != null) {
			dclController.dispose();
			dclController = null;
		}

		if (languageChooserController != null) {
			languageChooserController.dispose();
			languageChooserController = null;
		}
	}

}