All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pl.edu.icm.unity.saml.idp.web.SamlConsentScreen Maven / Gradle / Ivy

There is a newer version: 4.0.5
Show newest version
/*
 * Copyright (c) 2018 Bixbit - Krzysztof Benedyczak All rights reserved.
 * See LICENCE.txt file for licensing information.
 */
package pl.edu.icm.unity.saml.idp.web;

import com.google.common.base.Strings;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.HtmlComponent;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.Image;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import io.imunity.vaadin.endpoint.common.WebLogoutHandler;
import io.imunity.vaadin.endpoint.common.consent_utils.*;
import io.imunity.vaadin.endpoint.common.forms.VaadinLogoImageLoader;
import io.imunity.vaadin.endpoint.common.plugins.attributes.AttributeHandlerRegistry;
import org.apache.logging.log4j.Logger;
import pl.edu.icm.unity.base.attribute.Attribute;
import pl.edu.icm.unity.base.attribute.AttributeType;
import pl.edu.icm.unity.base.exceptions.EngineException;
import pl.edu.icm.unity.base.identity.IdentityParam;
import pl.edu.icm.unity.base.message.MessageSource;
import pl.edu.icm.unity.base.utils.Log;
import pl.edu.icm.unity.engine.api.PreferencesManagement;
import pl.edu.icm.unity.engine.api.attributes.AttributeTypeSupport;
import pl.edu.icm.unity.engine.api.attributes.DynamicAttribute;
import pl.edu.icm.unity.engine.api.identity.IdentityTypeSupport;
import pl.edu.icm.unity.saml.idp.ctx.SAMLAuthnContext;
import pl.edu.icm.unity.saml.idp.preferences.SamlPreferences;
import pl.edu.icm.unity.saml.idp.preferences.SamlPreferences.SPSettings;
import xmlbeans.org.oasis.saml2.assertion.NameIDType;
import xmlbeans.org.oasis.saml2.protocol.AuthnRequestType;

import java.time.Instant;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static pl.edu.icm.unity.saml.idp.web.SamlAuthVaadinEndpoint.SAML_CONSENT_DECIDER_SERVLET_PATH;

/**
 * Consent screen of the SAML web IdP. Fairly simple: shows who asks, what is going to be sent,
 * and optionally allows for some customization.
 */
class SamlConsentScreen extends VerticalLayout
{
	private static final Logger log = Log.getLogger(Log.U_SERVER_SAML, SamlConsentScreen.class);
	protected final MessageSource msg;
	protected final AttributeHandlerRegistry handlersRegistry;
	protected final IdentityTypeSupport identityTypeSupport;
	protected final PreferencesManagement preferencesMan;
	protected final WebLogoutHandler authnProcessor;
	protected final AttributeTypeSupport aTypeSupport;
	protected final VaadinLogoImageLoader imageAccessService;

	protected final List validIdentities;
	protected final Collection attributes;
	protected final Map attributeTypes;
	
	protected final Runnable declineHandler;
	protected final ConfirmationConsumer acceptHandler;
	
	protected IdentitySelectorComponent idSelector;
	protected SelectableAttributesComponent attrsPresenter;
	protected SamlResponseHandler samlResponseHandler;
	protected Checkbox rememberCB;

	public SamlConsentScreen(MessageSource msg, VaadinLogoImageLoader imageAccessService,
			AttributeHandlerRegistry handlersRegistry,
			PreferencesManagement preferencesMan,
			WebLogoutHandler authnProcessor,
			IdentityTypeSupport identityTypeSupport, 
			AttributeTypeSupport aTypeSupport,
			List validIdentities,
			Collection attributes,
			Map attributeTypes,
			Runnable declineHandler,
			ConfirmationConsumer acceptHandler)
	{
		this.msg = msg;
		this.imageAccessService = imageAccessService;
		this.handlersRegistry = handlersRegistry;
		this.preferencesMan = preferencesMan;
		this.authnProcessor = authnProcessor;
		this.identityTypeSupport = identityTypeSupport;
		this.aTypeSupport = aTypeSupport;
		this.validIdentities = validIdentities;
		this.attributes = attributes;
		this.attributeTypes = attributeTypes;
		this.declineHandler = declineHandler;
		this.acceptHandler = acceptHandler;
		initUI();
	}

	protected void initUI()
	{
		SAMLAuthnContext samlCtx = SamlSessionService.getVaadinContext();
		
		VerticalLayout contents = new VerticalLayout();
		contents.addClassName("u-consentMainColumn");

		createInfoPart(samlCtx, contents);

		Component exposedInfoPanel = createExposedDataPart(samlCtx);
		contents.add(exposedInfoPanel);
		createRememberMeCheckbox(contents);
		
		createButtonsPart(samlCtx, contents);

		add(contents);
		setAlignItems(Alignment.CENTER);

		loadPreferences(samlCtx);
	}

	private void createInfoPart(SAMLAuthnContext samlCtx, VerticalLayout contents)
	{
		AuthnRequestType request = samlCtx.getRequest();

		String samlRequester = request.getIssuer().getStringValue();
		String returnAddress = samlCtx.getSamlConfiguration().getReturnAddressForRequester(request);
		String displayedName = samlCtx.getSamlConfiguration().getDisplayedNameForRequester(request.getIssuer(), msg);
		Image logo = samlCtx.getSamlConfiguration().getLogoForRequesterOrNull(request.getIssuer(), msg,
				imageAccessService).orElse(null);

		SPInfoComponent spInfo = new SPInfoComponent(msg, logo,
				Strings.isNullOrEmpty(displayedName) ? samlRequester : displayedName,
				returnAddress);

		contents.add(spInfo);
	}

	protected Component createExposedDataPart(SAMLAuthnContext samlCtx)
	{
		Div exposedInfoPanel = new Div();
		exposedInfoPanel.setClassName("u-consent-screen");

		VerticalLayout eiLayout = new VerticalLayout();
		eiLayout.setWidthFull();
		exposedInfoPanel.add(eiLayout);
		idSelector = new IdentitySelectorComponent(msg, identityTypeSupport, validIdentities);

		Span info1 = new Span(msg.getMessage("SamlIdPWebUI.allowForSignInInfo"));
		Span info2 = new Span(msg.getMessage("SamlIdPWebUI.allowForReadingUserProfile"));
		eiLayout.add(info1, info2);
		
		if (validIdentities.size() > 1)
			eiLayout.add(idSelector);
		
		eiLayout.add(new HtmlComponent("br"));
		
		boolean userCanEditConsent = samlCtx.getSamlConfiguration().userCanEditConsent;
		Optional selectedIdentity = Optional.ofNullable(validIdentities.size() == 1 ? validIdentities.get(0) : null); 
		attrsPresenter = userCanEditConsent ? 
				new ExposedSelectableAttributesComponent(msg, identityTypeSupport, handlersRegistry,
						aTypeSupport, attributes, selectedIdentity) :
				new ROExposedAttributesComponent(msg, identityTypeSupport, attributes, handlersRegistry, 
						selectedIdentity);
		eiLayout.add((Component)attrsPresenter);
		return exposedInfoPanel;
	}

	protected void createRememberMeCheckbox(VerticalLayout layout)
	{
		rememberCB = new Checkbox(msg.getMessage("SamlIdPWebUI.rememberSettings"));
		rememberCB.addClassName("u-consent-screen-checkbox");
		layout.add(rememberCB);
	}
	
	private void createButtonsPart(final SAMLAuthnContext samlCtx, VerticalLayout contents)
	{
		IdPButtonsBar buttons = new IdPButtonsBar(msg, authnProcessor, SAML_CONSENT_DECIDER_SERVLET_PATH, action ->
		{
			if (IdPButtonsBar.Action.ACCEPT == action)
				confirm();
			else if (IdPButtonsBar.Action.DENY == action)
				decline();
		});
		
		contents.add(buttons);
		contents.setAlignItems(Alignment.CENTER);
		buttons.setClassName("u-consent-screen-buttons");
	}
	
	
	protected void loadPreferences(SAMLAuthnContext samlCtx)
	{
		try
		{
			SamlPreferences preferences = SamlPreferences.getPreferences(preferencesMan);
			SPSettings settings = preferences.getSPSettings(samlCtx.getRequest().getIssuer());
			updateUIFromPreferences(settings, samlCtx);
		} catch (Exception e)
		{
			log.error("Engine problem when processing stored preferences", e);
			//we kill the session as the user may want to log as different user if has access to several entities.
			samlResponseHandler.handleExceptionNotThrowing(e, true);
		}
	}
	
	protected void updateUIFromPreferences(SPSettings settings, SAMLAuthnContext samlCtx)
	{
		if (settings == null)
			return;
		Map attribtues = settings.getHiddenAttribtues();
		attrsPresenter.setInitialState(attribtues);
		String selId = settings.getSelectedIdentity();
		idSelector.setSelected(selId);

		if (settings.isDoNotAsk())
		{
			if (settings.isDefaultAccept())
				confirm();
			else
				decline();
		}
	}
	
	/**
	 * Applies UI selected values to the given preferences object
	 */
	protected void updatePreferencesFromUI(SamlPreferences preferences, SAMLAuthnContext samlCtx, boolean defaultAccept) 
			throws EngineException
	{
		if (!rememberCB.getValue())
			return;
		NameIDType reqIssuer = samlCtx.getRequest().getIssuer();
		SPSettings settings = preferences.getSPSettings(reqIssuer);
		settings.setDefaultAccept(defaultAccept);
		settings.setDoNotAsk(true);
		settings.setHiddenAttribtues(attrsPresenter.getHiddenAttributes());

		String identityValue = idSelector.getSelectedIdentityForPreferences();
		if (identityValue != null)
			settings.setSelectedIdentity(identityValue);
		settings.setTimestamp(Instant.now());
		preferences.setSPSettings(reqIssuer, settings);
	}
	
	protected void storePreferences(boolean defaultAccept)
	{
		try
		{
			SAMLAuthnContext samlCtx = SamlSessionService.getVaadinContext();
			SamlPreferences preferences = SamlPreferences.getPreferences(preferencesMan);
			updatePreferencesFromUI(preferences, samlCtx, defaultAccept);
			SamlPreferences.savePreferences(preferencesMan, preferences);
		} catch (EngineException e)
		{
			log.error("Unable to store user's preferences", e);
		}
	}

	protected void decline()
	{
		storePreferences(false);
		declineHandler.run();
	}
	
	protected void confirm()
	{
		storePreferences(true);
		acceptHandler.onAccepted(idSelector.getSelectedIdentity(), 
				getExposedAttributes());
	}
	
	protected Collection getExposedAttributes()
	{
		return attrsPresenter.getUserFilteredAttributes();
	}
	
	public interface ConfirmationConsumer
	{
		void onAccepted(IdentityParam selectedIdentity, Collection attributes);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy