diff options
Diffstat (limited to 'connector/src/main/java')
22 files changed, 1706 insertions, 0 deletions
| diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/MSSpecificeIDASNodeSpringResourceProvider.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/MSSpecificeIDASNodeSpringResourceProvider.java new file mode 100644 index 00000000..f64b6073 --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/MSSpecificeIDASNodeSpringResourceProvider.java @@ -0,0 +1,28 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector; + +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + +import at.gv.egiz.components.spring.api.SpringResourceProvider; + +public class MSSpecificeIDASNodeSpringResourceProvider implements SpringResourceProvider { + +	@Override +	public Resource[] getResourcesToLoad() { +		ClassPathResource mseIDASNode = new ClassPathResource("/specific_eIDAS_connector.beans.xml", MSSpecificeIDASNodeSpringResourceProvider.class);							 +		return new Resource[] {mseIDASNode}; +	} + +	@Override +	public String[] getPackagesToScan() { +		return null; +	} + +	@Override +	public String getName() { +		return "MS-specific eIDAS Node SpringResourceProvider"; +	} + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/MSeIDASNodeConstants.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/MSeIDASNodeConstants.java new file mode 100644 index 00000000..97defade --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/MSeIDASNodeConstants.java @@ -0,0 +1,53 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector; + +import at.gv.egiz.eaaf.core.api.data.EAAFConfigConstants; +import at.gv.egiz.eaaf.core.api.data.EAAFConstants; + +public class MSeIDASNodeConstants { +	//configuration properties +	public static final String PROP_CONFIG_APPLICATION_PREFIX = "eidas.ms.";	 +	public static final String PROP_CONFIG_APPLICATION_PUBLIC_URL_PREFIX =  "context.url.prefix"; +	 +	private static final String PROP_CONFIG_PVP2_PREFIX =  "pvp2."; +	public static final String PROP_CONFIG_PVP2_KEYSTORE_PATH = PROP_CONFIG_PVP2_PREFIX + "keystore.path";  +	public static final String PROP_CONFIG_PVP2_KEYSTORE_PASSWORD = PROP_CONFIG_PVP2_PREFIX + "keystore.password";	 +	public static final String PROP_CONFIG_PVP2_KEY_METADATA_ALIAS = PROP_CONFIG_PVP2_PREFIX + "key.metadata.alias";	 +	public static final String PROP_CONFIG_PVP2_KEY_METADATA_PASSWORD = PROP_CONFIG_PVP2_PREFIX + "key.metadata.password"; +	public static final String PROP_CONFIG_PVP2_KEY_SIGNING_ALIAS = PROP_CONFIG_PVP2_PREFIX + "key.signing.alias"; +	public static final String PROP_CONFIG_PVP2_KEY_SIGNING_PASSWORD = PROP_CONFIG_PVP2_PREFIX + "key.signing.password"; +	public static final String PROP_CONFIG_PVP2_METADATA_VALIDITY = PROP_CONFIG_PVP2_PREFIX + "metadata.validity";	 +	 +	public static final String PROP_CONFIG_SP_LIST_PREFIX = "sp.";  +	public static final String PROP_CONFIG_SP_UNIQUEIDENTIFIER = EAAFConfigConstants.SERVICE_UNIQUEIDENTIFIER; +	public static final String PROP_CONFIG_SP_FRIENDLYNAME = "friendlyName"; +	public static final String PROP_CONFIG_SP_PVP2_METADATA_URL = "pvp2.metadata.url"; +	public static final String PROP_CONFIG_SP_PVP2_METADATA_TRUSTSTORE = "pvp2.metadata.truststore"; +	public static final String PROP_CONFIG_SP_PVP2_METADATA_TRUSTSTORE_PASSWORD = "pvp2.metadata.truststore.password"; +	public static final String PROP_CONFIG_SP_POLICY_ALLOWED_TARGETS = "policy.allowed.requested.targets"; +	public static final String PROP_CONFIG_SP_POLICY_BASEIDTRANSFER_RESTRICTION = "policy.hasBaseIdTransferRestriction"; +	 +	public static final String PROP_CONFIG_PVP_SCHEME_VALIDATION = "configuration.pvp.scheme.validation"; +	public static final String PROP_CONFIG_PVP_ENABLE_ENTITYCATEGORIES = "configuration.pvp.enable.entitycategories"; +	 +	//default values +	public static final String POLICY_DEFAULT_ALLOWED_TARGETS =  +			EAAFConstants.URN_PREFIX_CDID.replaceAll(".", "\\.").replaceAll("+", "\\+") + ".*"; +	public static final int METADATA_SOCKED_TIMEOUT = 20 * 1000;  	//20 seconds metadata socked timeout +	public static final int DEFAULT_PVP_METADATA_VALIDITY = 24;		//24 hours +	 +	//application end-points +	public static final String ENDPOINT_PVP_METADATA = "/pvp/metadata"; +	public static final String ENDPOINT_PVP_POST = "/pvp/post"; +	public static final String ENDPOINT_PVP_REDIRECT = "/pvp/redirect"; +	 + +	//paths and templates +	public static final String CLASSPATH_TEMPLATE_DIR = "/templates/"; +	 +	public static final String TEMPLATE_HTML_ERROR = "error.html"; +	public static final String TEMPLATE_HTML_PVP_POSTBINDING = "pvp2_post_binding.html"; +	 +	 +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/SpringInitializer.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/SpringInitializer.java new file mode 100644 index 00000000..d5c2632c --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/SpringInitializer.java @@ -0,0 +1,166 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector; + +import java.util.Arrays; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.io.ClassPathResource; +import org.springframework.web.WebApplicationInitializer; +import org.springframework.web.context.ContextLoaderListener; +import org.springframework.web.context.request.RequestContextListener; +import org.springframework.web.context.support.GenericWebApplicationContext; +import org.springframework.web.context.support.ServletContextResource; +import org.springframework.web.servlet.DispatcherServlet; + +import at.gv.egiz.components.spring.api.SpringLoader; +import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.initialize.EAAFDefaultSAML2Bootstrap; + +/** + * Web application initializer + *  + * @author Thomas Lenz + */ +public class SpringInitializer implements WebApplicationInitializer { + +	private static final Logger log = LoggerFactory.getLogger(SpringInitializer.class); +	 +    private String[] rootServletContexts = null; +	private String[] servletContexts = null; +	private String[] activeProfiles = null; + +	public SpringInitializer() { +		this.rootServletContexts = null; +		this.servletContexts = new String[] { +				"/applicationContext.xml", +				 +		}; +		this.activeProfiles = null; +	} +     +     +	/* (non-Javadoc) +	 * @see org.springframework.web.WebApplicationInitializer#onStartup(javax.servlet.ServletContext) +	 */ +	@Override +	public void onStartup(ServletContext servletContext) throws ServletException { +		try {						 +			log.info("=============== Loading Config Root Context! ==============="); +			ApplicationContext cfgRootContext =  +			    	new ClassPathXmlApplicationContext(new String[] { +			    			"/applicationContext.xml" +			    			}); + + +			log.info("=============== Loading Root Context! ==============="); +			GenericWebApplicationContext rootContext = new GenericWebApplicationContext(); +			rootContext.setServletContext(servletContext); +			rootContext.setParent(cfgRootContext); +												 +//			log.info("=============== Setting active profiles! ==============="); +//			if (this.activeProfiles != null) { +//				for (String profile : this.activeProfiles) { +//					rootContext.getEnvironment().addActiveProfile(profile); +//				} +//			} +			 +			log.info("Spring-context was initialized with active profiles: " +  +					Arrays.asList(rootContext.getEnvironment().getActiveProfiles())); +			 +			log.info("=============== Loading Local Contexts! ==============="); +			XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader( +					rootContext); +			if (rootServletContexts != null) { +				for (String rootServletContext : rootServletContexts) { +					log.debug("Loading: "+ rootServletContext); +					xmlReader.loadBeanDefinitions(new ServletContextResource( +							servletContext, rootServletContext)); +				} +			} +			// Manage the lifecycle of the root application context +			servletContext.addListener(new ContextLoaderListener(rootContext)); + +			// log.debug("Beans after logAMQP in {}", rootContext); +			// dumpBeanDefinitions(rootContext); +			 +			log.info("=============== Loading SPI Context! ==============="); +			if (rootContext instanceof BeanDefinitionRegistry) { +				log.debug("Loading modules and components"); +				SpringLoader.loadSpringServices(rootContext); +				 +			} else +				log.warn("Failed to load external Spring since no BeanDefinitionRegistry"); +			 +			log.trace("Beans after SPI in "+ rootContext); +			dumpBeanDefinitions(rootContext); + +			log.debug("Loading servlet config in "+ rootContext); +			if (servletContexts != null) { +				for (String servletContextString : servletContexts) +					xmlReader.loadBeanDefinitions(new ClassPathResource(servletContextString, SpringInitializer.class)); +				 +			} +						 +			log.debug("Refreshing context "+ rootContext); +			rootContext.refresh(); + +			log.info("=============== Register Dispatcher Servlet! ==============="); + +			log.trace("Final Beans in "+ rootContext); +			dumpBeanDefinitions(rootContext); +											 +			log.info("Registering dispatcher configuration"); +			ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext)); +			if (dispatcher != null) {   +				dispatcher.setLoadOnStartup(1); +				dispatcher.addMapping("/"); +				dispatcher.setAsyncSupported(true); +				 +			} else +				log.error("Failed to register dispatcher server in servlet context!"); + +			 +			log.info("=============== Register RequestContextListener! ==============="); +			servletContext.addListener(new RequestContextListener()); +									 +			//TODO: integrate message provider!!!!							 +			//log.info(MOAIDMessageProvider.getInstance().getMessage("init.00", null)); +			 +			log.info("Bootstrap openSAML .... "); +			EAAFDefaultSAML2Bootstrap.bootstrap(); +			 +			log.info("Initialization of MS-specific eIDAS-connector finished."); +				 +						 +		} catch (Throwable e) { +			log.error("MS-specific eIDAS-connector initialization FAILED!", e); +			 +		} +				 +	} +	 +	private void dumpBeanDefinitions(GenericApplicationContext context) { +		log.trace("Registered Bean in context " + context.toString()); +		 +		String[] registeredBeans = context.getBeanDefinitionNames(); +		for (String registeredBean : registeredBeans) { +			BeanDefinition beanDefinition = context +					.getBeanDefinition(registeredBean); +			log.trace(registeredBean + " -> " + 	beanDefinition.getBeanClassName()); +			 +		} +		 +		log.trace("Registered Bean in context --"+ context); +	} +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/auth/AuthenticationManager.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/auth/AuthenticationManager.java new file mode 100644 index 00000000..e41bad28 --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/auth/AuthenticationManager.java @@ -0,0 +1,38 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.auth; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; +import at.gv.egiz.eaaf.core.api.idp.slo.ISLOInformationContainer; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.impl.idp.auth.AbstractAuthenticationManager; +import at.gv.egiz.eaaf.core.impl.idp.controller.protocols.RequestImpl; + +@Service("AuthenticationManager") +public class AuthenticationManager extends AbstractAuthenticationManager { +	private static final Logger log = LoggerFactory.getLogger(AuthenticationManager.class); +	 +	@Override +	public ISLOInformationContainer performSingleLogOut(HttpServletRequest httpReq, HttpServletResponse httpResp,  +			IRequest pendingReq, String internalSSOId) throws EAAFException {		 +		throw new RuntimeException("Single LogOut is NOT supported by this implementation"); +		 +	} + +	@Override +	protected void populateExecutionContext(ExecutionContext executionContext,  +			RequestImpl pendingReq, HttpServletRequest httpReq) +			throws EAAFException { +		log.trace("No implementation-specific population of execution-context required ... "); +		 +	} + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/builder/AuthenticationDataBuilder.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/builder/AuthenticationDataBuilder.java new file mode 100644 index 00000000..775e36f2 --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/builder/AuthenticationDataBuilder.java @@ -0,0 +1,64 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.builder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.w3c.dom.DOMException; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.idp.IAuthData; +import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; +import at.gv.egiz.eaaf.core.api.idp.auth.data.IAuthProcessDataContainer; +import at.gv.egiz.eaaf.core.exceptions.EAAFAuthenticationException; +import at.gv.egiz.eaaf.core.exceptions.EAAFBuilderException; +import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EAAFParserException; +import at.gv.egiz.eaaf.core.exceptions.XPathException; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.core.impl.idp.AuthenticationData; +import at.gv.egiz.eaaf.core.impl.idp.auth.builder.AbstractAuthenticationDataBuilder; +import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper; + +@Service("AuthenticationDataBuilder") +public class AuthenticationDataBuilder extends AbstractAuthenticationDataBuilder { +	private static final Logger log = LoggerFactory.getLogger(AuthenticationDataBuilder.class); +	 +	@Override +	public IAuthData buildAuthenticationData(IRequest pendingReq) throws EAAFAuthenticationException { + +		IAuthProcessDataContainer authProcessData = new AuthProcessDataWrapper(pendingReq.genericFullDataStorage()); +		AuthenticationData authData = new AuthenticationData(); +		 +		try { +			generateBasicAuthData(authData, pendingReq, authProcessData); +			 +		} catch (EAAFBuilderException | EAAFParserException | EAAFConfigurationException  +				| XPathException | DOMException e) { +			log.warn("Can not build authentication data from auth. process information"); +			throw new EAAFAuthenticationException("TODO", new Object[]{},					 +					"Can not build authentication data from auth. process information", e); + +		} +		  +		 +		 + +		return null; +	} + +	@Override +	protected Pair<String, String> getEncryptedbPKFromPVPAttribute(IAuthProcessDataContainer arg0, +			AuthenticationData arg1, ISPConfiguration arg2) throws EAAFBuilderException { +		return null; +		 +	} + +	@Override +	protected Pair<String, String> getbaseIDFromSZR(AuthenticationData arg0, String arg1, String arg2) { +		return null; +		 +	} + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/builder/PVPSubjectNameGenerator.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/builder/PVPSubjectNameGenerator.java new file mode 100644 index 00000000..d640539a --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/builder/PVPSubjectNameGenerator.java @@ -0,0 +1,19 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.builder; + +import at.gv.egiz.eaaf.core.api.idp.IAuthData; +import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; +import at.gv.egiz.eaaf.core.impl.data.Pair; +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2Exception; +import at.gv.egiz.eaaf.modules.pvp2.idp.api.builder.ISubjectNameIdGenerator; + +public class PVPSubjectNameGenerator implements ISubjectNameIdGenerator { + +	@Override +	public Pair<String, String> generateSubjectNameId(IAuthData authData, ISPConfiguration spConfig) throws PVP2Exception { +		//TODO: maybe update +		return Pair.newInstance(authData.getBPK(), authData.getBPKType()); +	} + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/config/BasicConfigurationProvider.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/config/BasicConfigurationProvider.java new file mode 100644 index 00000000..b898dfef --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/config/BasicConfigurationProvider.java @@ -0,0 +1,114 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.config; + +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; +import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.impl.idp.conf.AbstractConfigurationImpl; +import at.gv.egiz.eaaf.core.impl.utils.KeyValueUtils; +import at.gv.egiz.eidas.specific.connector.MSeIDASNodeConstants; + +@Service("BasicMSSpecificNodeConfig") +public class BasicConfigurationProvider extends AbstractConfigurationImpl{ +	private static final Logger log = LoggerFactory.getLogger(BasicConfigurationProvider.class); +	 +	private Map<String, ISPConfiguration> spConfigCache = new HashMap<String, ISPConfiguration>();  +	 +	public BasicConfigurationProvider(String configPath) throws EAAFConfigurationException { +		super(configPath); +		 +	} + +	@Override +	public ISPConfiguration getServiceProviderConfiguration(String entityId) throws EAAFConfigurationException { +		if (!spConfigCache.containsKey(entityId)) { +			log.debug("SP: " + entityId + " is NOT cached. Starting load operation ...  "); +			Map<String, String> allSPs = getBasicMOAIDConfigurationWithPrefix(MSeIDASNodeConstants.PROP_CONFIG_SP_LIST_PREFIX);	 +			for (String key : allSPs.keySet()) { +				if (key.endsWith(MSeIDASNodeConstants.PROP_CONFIG_SP_UNIQUEIDENTIFIER) &&  +						allSPs.get(key).equals(entityId)) { +					String listId = KeyValueUtils.getParentKey(key);					 +					log.trace("Find SP configuration with list-Id: " + listId + ". Extracting configuration elements ... "); +					Map<String, String> spConfig = KeyValueUtils.getSubSetWithPrefix(allSPs, listId + KeyValueUtils.KEY_DELIMITER); +					spConfigCache.put(entityId,  +							new ServiceProviderConfiguration(spConfig, this)); +					break; +				}				 +			} +			 +			if (spConfigCache.containsKey(entityId))  +				log.info("SP: " + entityId + " is loaded. Continuing auth. process ... ");			 +			else { +				log.warn("SP: " + entityId + " is NOT found in configuration. Stopping auth. process ... "); +				return null; +				 +			} +			 +		} else  +			log.trace("SP: " + entityId + " is already cached. Use configuration from there ... "); + +		 +		return spConfigCache.get(entityId); +	} + +	@Override +	public <T> T getServiceProviderConfiguration(String entityId, Class<T> decorator) throws EAAFConfigurationException { +		ISPConfiguration spConfig = getServiceProviderConfiguration(entityId); +		if (spConfig != null && decorator != null) { +			if (decorator.isInstance(spConfig)) +				return (T)spConfig; +			else +				log.error("SPConfig: " + spConfig.getClass().getName() + " is NOT instance of: " + decorator.getName()); +								 +		} +		 +		return null; +		 +	} + +	@Override +	public String validateIDPURL(URL url) throws EAAFException { +		log.trace("Validate requested URL: " + url); +		String urlPrefixFromConfig = getBasicConfiguration(MSeIDASNodeConstants.PROP_CONFIG_APPLICATION_PUBLIC_URL_PREFIX); +		if (StringUtils.isEmpty(urlPrefixFromConfig)) { +			log.warn("Application config containts NO URL prefix"); +			throw new EAAFConfigurationException("Application config containts NO URL prefix"); +			 +		} +		 +		//remove last slash +		if (urlPrefixFromConfig.endsWith("/")) +			urlPrefixFromConfig = urlPrefixFromConfig.substring(0, urlPrefixFromConfig.length()-1); +		 +		if (url != null && url.toExternalForm().startsWith(urlPrefixFromConfig)) +			return urlPrefixFromConfig; +		 +		 +		log.info("URL: " + url + " does NOT match to allowed application prefix: " + urlPrefixFromConfig); +		return null; +	} + +	@Override +	public String getApplicationSpecificKeyPrefix() {	 +		return MSeIDASNodeConstants.PROP_CONFIG_APPLICATION_PREFIX;	 +		 +	} + +	@Override +	protected String getBackupConfigPath() { +		return null; +		 +	} +	 + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/config/PVPEndPointConfiguration.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/config/PVPEndPointConfiguration.java new file mode 100644 index 00000000..21e46e10 --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/config/PVPEndPointConfiguration.java @@ -0,0 +1,68 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.config; + +import java.util.List; + +import org.opensaml.saml2.metadata.ContactPerson; +import org.opensaml.saml2.metadata.Organization; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.modules.pvp2.api.IPVP2BasicConfiguration; +import at.gv.egiz.eidas.specific.connector.MSeIDASNodeConstants; + +@Service("PVPEndPointConfiguration") +public class PVPEndPointConfiguration implements IPVP2BasicConfiguration { +	private static final Logger log = LoggerFactory.getLogger(PVPEndPointConfiguration.class); +	 +	@Autowired(required=true) IConfiguration basicConfiguration; +	 +	@Override +	public String getIDPEntityId(String authURL) throws EAAFException { +		return removePostFix(authURL) + MSeIDASNodeConstants.ENDPOINT_PVP_METADATA; +		 +	} + +	@Override +	public String getIDPSSOPostService(String authURL) throws EAAFException { +		return removePostFix(authURL) + MSeIDASNodeConstants.ENDPOINT_PVP_POST; +		 +	} + +	@Override +	public String getIDPSSORedirectService(String authURL) throws EAAFException { +		return removePostFix(authURL) + MSeIDASNodeConstants.ENDPOINT_PVP_REDIRECT; +		 +	} + +	@Override +	public Object getIDPSSOSOAPService(String extractAuthURLFromRequest) throws EAAFException { +		log.warn("PVP S-Profile End-Point does NOT support SOAP Binding"); +		return null; +		 +	} + +	@Override +	public List<ContactPerson> getIDPContacts() throws EAAFException { +		// TODO Auto-generated method stub +		return null; +	} + +	@Override +	public Organization getIDPOrganisation() throws EAAFException { +		// TODO Auto-generated method stub +		return null; +	} + +	private String removePostFix(String url) { +		if (url != null && url.endsWith("/")) +			return url.substring(0, url.length() - 1); +		else +			return url; +	} +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/config/PVPMetadataConfiguration.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/config/PVPMetadataConfiguration.java new file mode 100644 index 00000000..7d17baa1 --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/config/PVPMetadataConfiguration.java @@ -0,0 +1,240 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.config; + +import java.util.Arrays; +import java.util.List; + +import org.opensaml.saml2.core.Attribute; +import org.opensaml.saml2.core.NameIDType; +import org.opensaml.saml2.metadata.ContactPerson; +import org.opensaml.saml2.metadata.Organization; +import org.opensaml.saml2.metadata.RequestedAttribute; +import org.opensaml.xml.security.credential.Credential; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.modules.pvp2.api.IPVP2BasicConfiguration; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPVPMetadataBuilderConfiguration; +import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException; +import at.gv.egiz.eaaf.modules.pvp2.impl.builder.PVPAttributeBuilder; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.AbstractCredentialProvider; +import at.gv.egiz.eidas.specific.connector.MSeIDASNodeConstants; + +public class PVPMetadataConfiguration implements IPVPMetadataBuilderConfiguration{ +	private static final Logger log = LoggerFactory.getLogger(PVPMetadataConfiguration.class); +	 +	private IConfiguration basicConfig; +	private String authUrl; +	private AbstractCredentialProvider pvpIDPCredentials; +	private IPVP2BasicConfiguration pvpBasicConfig; +	 +	public PVPMetadataConfiguration(IConfiguration basicConfig, String authURL, IPVP2BasicConfiguration pvpBasicConfig, AbstractCredentialProvider pvpIDPCredentials) { +		this.authUrl = authURL; +		this.pvpIDPCredentials = pvpIDPCredentials; +		this.basicConfig = basicConfig; +		this.pvpBasicConfig = pvpBasicConfig; +		 +	} +	 +	@Override +	public String getSPNameForLogging() { +		return "PVP2 S-Profile IDP"; +	} + +	@Override +	public int getMetadataValidUntil() { +		return Integer.valueOf(basicConfig.getBasicConfiguration( +				MSeIDASNodeConstants.PROP_CONFIG_PVP2_METADATA_VALIDITY,  +				String.valueOf(MSeIDASNodeConstants.DEFAULT_PVP_METADATA_VALIDITY))); +		 +	} + +	@Override +	public boolean buildEntitiesDescriptorAsRootElement() { +		return false; +		 +	} + +	@Override +	public boolean buildIDPSSODescriptor() { +		return true; +		 +	} + +	@Override +	public boolean buildSPSSODescriptor() { +		return false; +		 +	} + +	@Override +	public String getEntityID() { +		try { +			return pvpBasicConfig.getIDPEntityId(authUrl); +			 +		} catch (EAAFException e) { +			log.error("Can NOT build PVP metadata configuration.", e); +			throw new RuntimeException("Can NOT build PVP metadata configuration."); +			 +		} +		 +	} + +	@Override +	public String getEntityFriendlyName() { +		return null; +		 +	} + +	@Override +	public List<ContactPerson> getContactPersonInformation() {		 +		try { +			return pvpBasicConfig.getIDPContacts(); +			 +		} catch (EAAFException e) { +			log.error("Can NOT build PVP metadata configuration.", e); +			throw new RuntimeException("Can NOT build PVP metadata configuration."); +			 +		} +		 +	} + +	@Override +	public Organization getOrgansiationInformation() { +		try { +			return pvpBasicConfig.getIDPOrganisation(); +			 +		} catch (EAAFException e) { +			log.error("Can NOT build PVP metadata configuration.", e); +			throw new RuntimeException("Can NOT build PVP metadata configuration."); +			 +		} +	} + +	@Override +	public Credential getMetadataSigningCredentials() throws CredentialsNotAvailableException { +		return pvpIDPCredentials.getIDPMetaDataSigningCredential(); +		 +	} + +	@Override +	public Credential getRequestorResponseSigningCredentials() throws CredentialsNotAvailableException { +		return pvpIDPCredentials.getIDPAssertionSigningCredential(); +		 +	} + +	@Override +	public Credential getEncryptionCredentials() throws CredentialsNotAvailableException { +		return null; +		 +		 +	} + +	@Override +	public String getIDPWebSSOPostBindingURL() { +		try { +			return pvpBasicConfig.getIDPSSOPostService(authUrl); +			 +		} catch (EAAFException e) { +			log.error("Can NOT build PVP metadata configuration.", e); +			throw new RuntimeException("Can NOT build PVP metadata configuration."); +			 +		} +		 +	} + +	@Override +	public String getIDPWebSSORedirectBindingURL() { +		try { +			return pvpBasicConfig.getIDPSSORedirectService(authUrl); +			 +		} catch (EAAFException e) { +			log.error("Can NOT build PVP metadata configuration.", e); +			throw new RuntimeException("Can NOT build PVP metadata configuration."); +			 +		} +	} + +	@Override +	public String getIDPSLOPostBindingURL() { +		return null; +		 +	} + +	@Override +	public String getIDPSLORedirectBindingURL() { +		return null; +		 +	} + +	@Override +	public String getSPAssertionConsumerServicePostBindingURL() { +		return null; +		 +	} + +	@Override +	public String getSPAssertionConsumerServiceRedirectBindingURL() { +		return null; +		 +	} + +	@Override +	public String getSPSLOPostBindingURL() { +		return null; +		 +	} + +	@Override +	public String getSPSLORedirectBindingURL() { +		return null; +		 +	} + +	@Override +	public String getSPSLOSOAPBindingURL() { +		return null; +		 +	} + +	@Override +	public List<Attribute> getIDPPossibleAttributes() { +		return PVPAttributeBuilder.buildSupportedEmptyAttributes(); +		 +	} + +	@Override +	public List<String> getIDPPossibleNameITTypes() { +		return Arrays.asList(NameIDType.PERSISTENT,  +				 NameIDType.TRANSIENT, +				 NameIDType.UNSPECIFIED); +	} + +	@Override +	public List<RequestedAttribute> getSPRequiredAttributes() { +		return null; +		 +	} + +	@Override +	public List<String> getSPAllowedNameITTypes() { +		return null; +		 +	} + +	@Override +	public boolean wantAssertionSigned() { +		return false; +		 +	} + +	@Override +	public boolean wantAuthnRequestSigned() { +		return true; +		 +	} + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/config/ServiceProviderConfiguration.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/config/ServiceProviderConfiguration.java new file mode 100644 index 00000000..3d8a3bdd --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/config/ServiceProviderConfiguration.java @@ -0,0 +1,105 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.config; + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.eaaf.core.api.data.EAAFConstants; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.impl.idp.conf.SPConfigurationImpl; +import at.gv.egiz.eidas.specific.connector.MSeIDASNodeConstants; + +public class ServiceProviderConfiguration extends SPConfigurationImpl {	 +	private static final long serialVersionUID = 1L; +	private static final Logger log = LoggerFactory.getLogger(ServiceProviderConfiguration.class); + +	private String minimumLoA = EAAFConstants.EIDAS_QAA_HIGH; +	private String bPKTargetIdentifier; +	 +	public ServiceProviderConfiguration(Map<String, String> spConfig, IConfiguration authConfig) { +		super(spConfig, authConfig); +		 +	} + +	@Override +	public boolean hasBaseIdInternalProcessingRestriction() { +		  return false; +		   +	} + +	@Override +	public boolean hasBaseIdTransferRestriction() {	   +		return isConfigurationValue( +				MSeIDASNodeConstants.PROP_CONFIG_SP_POLICY_BASEIDTRANSFER_RESTRICTION,  +				true); +		   +	} +	 +	@Override +	public String getMinimumLevelOfAssurence() { +		return minimumLoA; +		 +	} + + +	@Override +	public String getAreaSpecificTargetIdentifier() { +		return bPKTargetIdentifier; +	} + + +	@Override +	public String getFriendlyName() { +		return getConfigurationValue( +				MSeIDASNodeConstants.PROP_CONFIG_SP_FRIENDLYNAME,  +				"NO FRIENDLYNAME SET"); +		 +	} + +	/** +	 * Set the minimum level of eIDAS authentication for this SP +	 * <br> +	 * <b>Default:</b> http://eidas.europa.eu/LoA/high or  +	 *  +	 * @param minimumLoA eIDAS LoA URI +	 */ +	 +	public void setMinimumLoA(String minimumLoA) { +		this.minimumLoA = minimumLoA; +	} + +	 +	/** +	 * Set the bPK Target for this service provider +	 *  +	 * @param bPKTargetIdentifier +	 * @throws EAAFException If the bPKTargetIdentifier is NOT ALLOWED for this service provider  +	 */ +	public void setbPKTargetIdentifier(String bPKTargetIdentifier) throws EAAFException { +		String allowedTargetIdentifierRegExPattern = getConfigurationValue( +				MSeIDASNodeConstants.PROP_CONFIG_SP_POLICY_ALLOWED_TARGETS, +				MSeIDASNodeConstants.POLICY_DEFAULT_ALLOWED_TARGETS);		 +		log.trace("Use bPK-target regex pattern: " + allowedTargetIdentifierRegExPattern); +		 +		Pattern p = Pattern.compile(allowedTargetIdentifierRegExPattern); +		Matcher m = p.matcher(bPKTargetIdentifier); +		if (m.matches()) { +			log.debug("Requested bPK-target: " + bPKTargetIdentifier + " matches regex pattern"); +			this.bPKTargetIdentifier = bPKTargetIdentifier; +			 +		} else { +			log.warn("Requested bPK-target: " + bPKTargetIdentifier + " does NOT match regex pattern."); +			throw new EAAFException("TODO", new Object[] {bPKTargetIdentifier},  +					"Requested bPK-target: " + bPKTargetIdentifier + " does NOT match regex pattern."); +			 +		} +					 +	} +	 +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/controller/PVP2SProfileEndpoint.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/controller/PVP2SProfileEndpoint.java new file mode 100644 index 00000000..62092675 --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/controller/PVP2SProfileEndpoint.java @@ -0,0 +1,59 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.controller; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.modules.pvp2.idp.impl.AbstractPVP2XProtocol; +import at.gv.egiz.eaaf.modules.pvp2.idp.impl.PVPSProfilePendingRequest; +import at.gv.egiz.eidas.specific.connector.MSeIDASNodeConstants; + +@Controller +public class PVP2SProfileEndpoint extends AbstractPVP2XProtocol{ + +	public static final String NAME = PVP2SProfileEndpoint.class.getName(); +	public static final String PROTOCOL_ID = "pvp2-s"; +	 +	@RequestMapping(value = MSeIDASNodeConstants.ENDPOINT_PVP_METADATA, method = {RequestMethod.POST, RequestMethod.GET}) +	public void PVPMetadataRequest(HttpServletRequest req, HttpServletResponse resp) throws EAAFException {		 +		super.pvpMetadataRequest(req, resp); +		 +	} +	 +	@RequestMapping(value = MSeIDASNodeConstants.ENDPOINT_PVP_POST, method = {RequestMethod.POST}) +	public void PVPIDPPostRequest(HttpServletRequest req, HttpServletResponse resp) throws EAAFException { +		super.PVPIDPPostRequest(req, resp); +						 +	} +	 +	@RequestMapping(value = MSeIDASNodeConstants.ENDPOINT_PVP_REDIRECT, method = {RequestMethod.GET}) +	public void PVPIDPRedirecttRequest(HttpServletRequest req, HttpServletResponse resp) throws EAAFException { +		super.PVPIDPRedirecttRequest(req, resp); +				 +	} +	 +	 +	@Override +	public String getAuthProtocolIdentifier() { +		return PROTOCOL_ID; +	} + +	@Override +	public String getName() { +		return NAME; +	} + +	@Override +	protected boolean childPreProcess(HttpServletRequest arg0, HttpServletResponse arg1, PVPSProfilePendingRequest arg2) +			throws Throwable { +		return false; +	} + +	 +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/gui/DefaultGUIBuilderImpl.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/gui/DefaultGUIBuilderImpl.java new file mode 100644 index 00000000..e423b09a --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/gui/DefaultGUIBuilderImpl.java @@ -0,0 +1,44 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.gui; + +import java.io.InputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.gui.IGUIBuilderConfiguration; +import at.gv.egiz.eaaf.core.exceptions.GUIBuildException; +import at.gv.egiz.eaaf.core.impl.gui.AbstractGUIFormBuilderImpl; + +@Service("DefaultGUIBuilderImpl") +public class DefaultGUIBuilderImpl extends AbstractGUIFormBuilderImpl{ +	private static final Logger log = LoggerFactory.getLogger(DefaultGUIBuilderImpl.class); +	 +	private static final String CLASSPATH_HTMLTEMPLATES_DIR = "templates/"; +	 +	public DefaultGUIBuilderImpl() throws GUIBuildException { +		super(); +		 +	} + +	@Override +	protected InputStream getInternalTemplate(IGUIBuilderConfiguration config) throws GUIBuildException { +		String viewName = config.getViewName(); +		log.debug("GUI template:" + viewName + " is not found in configuration directory. " +				+ " Load template from project library ... ");					 +		String pathLocation = getInternalClasspathTemplateDir(config, CLASSPATH_HTMLTEMPLATES_DIR) + viewName; +		try  {			 +			InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(pathLocation);				 +			return is;						 +			 +		} catch (Exception e1) { +			log.error("GUI template:" + pathLocation + " is NOT loadable  from classpath!", e1); +			throw new GUIBuildException("GUI template:" + pathLocation + " is NOT loadable from classpath!", e1); +			 +		}			 +		 +	} + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/gui/GUIBuilderConfigurationFactory.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/gui/GUIBuilderConfigurationFactory.java new file mode 100644 index 00000000..8132c063 --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/gui/GUIBuilderConfigurationFactory.java @@ -0,0 +1,32 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.gui; + +import java.net.MalformedURLException; +import java.net.URI; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.gui.IGUIBuilderConfiguration; +import at.gv.egiz.eaaf.core.api.gui.IGUIBuilderConfigurationFactory; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eidas.specific.connector.MSeIDASNodeConstants; + +@Service("GUIBuilderConfigurationFactory") +public class GUIBuilderConfigurationFactory implements IGUIBuilderConfigurationFactory { +	@Autowired(required=true) private IConfiguration basicConfig;  +	 +	@Override +	public IGUIBuilderConfiguration getDefaultErrorGUI(String authURL) { +		return new StaticGuiBuilderConfiguration(basicConfig, authURL, MSeIDASNodeConstants.TEMPLATE_HTML_ERROR, null); +	} + +	@Override +	public IGUIBuilderConfiguration getSPSpecificSAML2PostConfiguration(IRequest pendingReq, String viewName, URI configRootContextDir) +			throws MalformedURLException {				 +		return new StaticGuiBuilderConfiguration(basicConfig, pendingReq,MSeIDASNodeConstants.TEMPLATE_HTML_PVP_POSTBINDING , null); +	} + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/gui/StaticGuiBuilderConfiguration.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/gui/StaticGuiBuilderConfiguration.java new file mode 100644 index 00000000..8dd3c580 --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/gui/StaticGuiBuilderConfiguration.java @@ -0,0 +1,91 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.gui; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang.StringEscapeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.impl.gui.AbstractGUIFormBuilderConfiguration; +import at.gv.egiz.eaaf.core.impl.utils.FileUtils; +import at.gv.egiz.eidas.specific.connector.MSeIDASNodeConstants; + +public class StaticGuiBuilderConfiguration extends AbstractGUIFormBuilderConfiguration { +	private static final Logger log = LoggerFactory.getLogger(StaticGuiBuilderConfiguration.class); +	 +	private IRequest pendingReq = null; +	private IConfiguration basicConfig = null; +	 +	public StaticGuiBuilderConfiguration(IConfiguration basicConfig, String authURL, String viewName, String formSubmitEndpoint) { +		super(authURL, viewName, formSubmitEndpoint); +		this.basicConfig = basicConfig; +	} + +	public StaticGuiBuilderConfiguration(IConfiguration basicConfig, IRequest pendingReq, String viewName, String formSubmitEndpoint) { +		super(pendingReq.getAuthURL(), viewName, formSubmitEndpoint); +		this.pendingReq = pendingReq; +		this.basicConfig = basicConfig; +		 +	} +	 +	@Override +	public String getClasspathTemplateDir() { +		return MSeIDASNodeConstants.CLASSPATH_TEMPLATE_DIR; +		 +	} + +	@Override +	public String getDefaultContentType() { +		return null; +		 +	} + +	@Override +	public InputStream getTemplate(String viewName) { +		String templateURL = MSeIDASNodeConstants.CLASSPATH_TEMPLATE_DIR + viewName; +		try {			 +			String absURL = FileUtils.makeAbsoluteURL(templateURL, this.basicConfig.getConfigurationRootDirectory());				 +			if (!absURL.startsWith("file:")) { +				log.warn("Path to template looks like NOT absolut: " + absURL + ". Template loading FAILED"); +			 +			} else { +				log.debug("Load template URL for view: " + viewName + " from: " + absURL); +				URI uri = new URL(absURL).toURI(); +				return new FileInputStream(new File(uri)); +				 +			} +			 +			 +		} catch (MalformedURLException | URISyntaxException | FileNotFoundException e) { +			log.warn("Can can build filesytem path to template: " + templateURL, e); +			 +		} +		 +		return null; +	} + +	@Override +	protected Map<String, Object> getSpecificViewParameters() { +		Map<String, Object> params =  new HashMap<String, Object>(); +		if (pendingReq != null) {							 +			params.put(PARAM_PENDINGREQUESTID, StringEscapeUtils.escapeHtml(pendingReq.getPendingRequestId())); +			 +		}	 +		 +		return params; +	} + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/mapper/LoALevelMapper.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/mapper/LoALevelMapper.java new file mode 100644 index 00000000..9432931e --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/mapper/LoALevelMapper.java @@ -0,0 +1,34 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.mapper; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.data.EAAFConstants; +import at.gv.egiz.eaaf.core.api.data.ILoALevelMapper; + +@Service("LoALevelMapper") +public class LoALevelMapper implements ILoALevelMapper{ +	private static final Logger log = LoggerFactory.getLogger(LoALevelMapper.class); +	 +	@Override +	public String mapToSecClass(String LoA) { +		log.info("Mapping to PVP SecClass is NOT supported"); +		return null; +	} + +	@Override +	public String mapToeIDASLoA(String LoA) { +		if (LoA.startsWith(EAAFConstants.EIDAS_QAA_PREFIX)) +			return LoA; +		 +		else +			log.info("Can NOT map '" + LoA + "' to eIDAS LoA"); +			 +		return null; +			 +	} + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/PVPEndPointCredentialProvider.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/PVPEndPointCredentialProvider.java new file mode 100644 index 00000000..cd86c79a --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/PVPEndPointCredentialProvider.java @@ -0,0 +1,92 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.provider; + +import java.net.MalformedURLException; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.impl.utils.FileUtils; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.AbstractCredentialProvider; +import at.gv.egiz.eidas.specific.connector.MSeIDASNodeConstants; + +public class PVPEndPointCredentialProvider extends AbstractCredentialProvider { +	private static final Logger log = LoggerFactory.getLogger(PVPEndPointCredentialProvider.class); +	 +	@Autowired(required=true) IConfiguration basicConfiguration; +	 +	@Override +	public String getFriendlyName() { +		return "PVP2 S-Profile EndPoint"; +	} + +	@Override +	public String getKeyStoreFilePath() throws EAAFException { +		try { +			String path = basicConfiguration.getBasicConfiguration(MSeIDASNodeConstants.PROP_CONFIG_PVP2_KEYSTORE_PATH); +			if (StringUtils.isEmpty(path)) { +				log.error(getFriendlyName() + " | Path to keyStore is NULL or EMPTY"); +				throw new EAAFConfigurationException(getFriendlyName() + " | Path to keyStore is NULL or EMPTY"); +				 +			} +			 +			return FileUtils.makeAbsoluteURL( +					path,  +					basicConfiguration.getConfigurationRootDirectory()); +			 +		} catch (MalformedURLException e) { +			log.error(getFriendlyName() + " | Path to keyStore NOT valid.", e); +			throw new EAAFConfigurationException(getFriendlyName() + " | Path to keyStore NOT valid.", e); +			 +		} +		 +	} + +	@Override +	public String getKeyStorePassword() { +		return basicConfiguration.getBasicConfiguration(MSeIDASNodeConstants.PROP_CONFIG_PVP2_KEYSTORE_PASSWORD); +		 +	} + +	@Override +	public String getMetadataKeyAlias() { +		return basicConfiguration.getBasicConfiguration(MSeIDASNodeConstants.PROP_CONFIG_PVP2_KEY_METADATA_ALIAS); +	} + +	@Override +	public String getMetadataKeyPassword() { +		return basicConfiguration.getBasicConfiguration(MSeIDASNodeConstants.PROP_CONFIG_PVP2_KEY_METADATA_PASSWORD); +		 +	} + +	@Override +	public String getSignatureKeyAlias() { +		return basicConfiguration.getBasicConfiguration(MSeIDASNodeConstants.PROP_CONFIG_PVP2_KEY_SIGNING_ALIAS); +		 +	} + +	@Override +	public String getSignatureKeyPassword() { +		return basicConfiguration.getBasicConfiguration(MSeIDASNodeConstants.PROP_CONFIG_PVP2_KEY_SIGNING_PASSWORD); +		 +	} + +	@Override +	public String getEncryptionKeyAlias() { +		return null; +		 +	} + +	@Override +	public String getEncryptionKeyPassword() { +		return null; +		 +	} + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/PVPMetadataConfigurationFactory.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/PVPMetadataConfigurationFactory.java new file mode 100644 index 00000000..c5d2f29c --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/PVPMetadataConfigurationFactory.java @@ -0,0 +1,28 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.provider; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.modules.pvp2.api.IPVP2BasicConfiguration; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPVPMetadataBuilderConfiguration; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPVPMetadataConfigurationFactory; +import at.gv.egiz.eaaf.modules.pvp2.impl.utils.AbstractCredentialProvider; +import at.gv.egiz.eidas.specific.connector.config.PVPMetadataConfiguration; + +@Service("PVPMetadataConfigurationFactory") +public class PVPMetadataConfigurationFactory implements IPVPMetadataConfigurationFactory { + +	@Autowired private IConfiguration basicConfig; +	@Autowired private IPVP2BasicConfiguration pvpBasicConfig; +	 +	@Override +	public IPVPMetadataBuilderConfiguration generateMetadataBuilderConfiguration(String authURL,  +			AbstractCredentialProvider pvpIDPCredentials) { +		return new PVPMetadataConfiguration(basicConfig, authURL, pvpBasicConfig, pvpIDPCredentials); +		 +	} + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/PVPMetadataProvider.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/PVPMetadataProvider.java new file mode 100644 index 00000000..0edc5fcd --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/PVPMetadataProvider.java @@ -0,0 +1,93 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.provider; + +import java.io.IOException; +import java.security.cert.CertificateException; +import java.util.List; + +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.params.HttpClientParams; +import org.opensaml.saml2.metadata.provider.MetadataProvider; +import org.opensaml.xml.parse.BasicParserPool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.idp.IConfiguration; +import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration; +import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException; +import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.AbstractChainingMetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.MetadataFilterChain; +import at.gv.egiz.eaaf.modules.pvp2.impl.validation.metadata.PVPEntityCategoryFilter; +import at.gv.egiz.eaaf.modules.pvp2.impl.validation.metadata.SchemaValidationFilter; +import at.gv.egiz.eidas.specific.connector.MSeIDASNodeConstants; + +@Service("PVPMetadataProvider") +public class PVPMetadataProvider extends AbstractChainingMetadataProvider{ +	private static final Logger log = LoggerFactory.getLogger(PVPMetadataProvider.class); +	 +	@Autowired(required=true) IConfiguration basicConfig; +	 +	@Override +	protected String getMetadataURL(String entityId) throws EAAFConfigurationException { +		ISPConfiguration spConfig = basicConfig.getServiceProviderConfiguration(entityId); +		if (spConfig != null) { +			return spConfig.getConfigurationValue(MSeIDASNodeConstants.PROP_CONFIG_SP_PVP2_METADATA_URL); +			 +		} else +			log.info("No ServiceProvider with entityId: " + entityId + " in configuration."); +		 +		return null; +	} + +	@Override +	protected MetadataProvider createNewMetadataProvider(String entityId) +			throws EAAFConfigurationException, IOException, CertificateException { +		ISPConfiguration spConfig = basicConfig.getServiceProviderConfiguration(entityId); +		if (spConfig != null) { +			String metadataURL = spConfig.getConfigurationValue(MSeIDASNodeConstants.PROP_CONFIG_SP_PVP2_METADATA_URL); +			String trustStoreUrl = spConfig.getConfigurationValue(MSeIDASNodeConstants.PROP_CONFIG_SP_PVP2_METADATA_TRUSTSTORE); +			return createNewSimpleMetadataProvider(metadataURL, 								  +					buildMetadataFilterChain(spConfig, metadataURL, trustStoreUrl),  +					spConfig.getConfigurationValue(MSeIDASNodeConstants.PROP_CONFIG_SP_UNIQUEIDENTIFIER), +					getTimer(), +					new BasicParserPool(), +					createHttpClient(metadataURL)); +			 +		} else +			log.info("No ServiceProvider with entityId: " + entityId + " in configuration."); +		 +		return null; +	} + +	@Override +	protected List<String> getAllMetadataURLsFromConfiguration() throws EAAFConfigurationException { +		// TODO Auto-generated method stub +		return null; +	} + +	private HttpClient createHttpClient(String metadataURL) {					 +		HttpClient httpClient = new HttpClient();		 +		HttpClientParams httpClientParams = new HttpClientParams(); +		httpClientParams.setSoTimeout(MSeIDASNodeConstants.METADATA_SOCKED_TIMEOUT); +		httpClient.setParams(httpClientParams);		 +		return httpClient; +				 +	} +	 +	private MetadataFilterChain buildMetadataFilterChain(ISPConfiguration oaParam, String metadataURL, String trustStoreUrl) throws CertificateException{ +		MetadataFilterChain filterChain = new MetadataFilterChain();		 +		filterChain.getFilters().add(new SchemaValidationFilter( +				basicConfig.getBasicMOAIDConfigurationBoolean(MSeIDASNodeConstants.PROP_CONFIG_PVP_SCHEME_VALIDATION, true))); +				 +		//TODO: add signature validation filter +		 +		 +		filterChain.getFilters().add(new PVPEntityCategoryFilter( +				basicConfig.getBasicMOAIDConfigurationBoolean(MSeIDASNodeConstants.PROP_CONFIG_PVP_ENABLE_ENTITYCATEGORIES, true))); +		 +		return filterChain;		 +	} +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/StatusMessageProvider.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/StatusMessageProvider.java new file mode 100644 index 00000000..6e3f45cc --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/provider/StatusMessageProvider.java @@ -0,0 +1,30 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.provider; + +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.IStatusMessager; + +@Service("StatusMessageProvider") +public class StatusMessageProvider implements IStatusMessager { + +	@Override +	public String getMessage(String messageId, Object[] parameters) { +		return "NOT IMPLEMENTED YET"; +		 +	} + +	@Override +	public String getResponseErrorCode(Throwable throwable) { +		return "NOT IMPLEMENTED YET"; +		 +	} + +	@Override +	public String mapInternalErrorToExternalError(String intErrorCode) { +		return "NOT IMPLEMENTED YET"; +		 +	} + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/storage/SimpleInMemoryTransactionStorage.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/storage/SimpleInMemoryTransactionStorage.java new file mode 100644 index 00000000..e4d02dae --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/storage/SimpleInMemoryTransactionStorage.java @@ -0,0 +1,138 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.storage; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage; +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.exceptions.EAAFStorageException; + +@Service("SimpleInMemoryTransactionStorage") +public class SimpleInMemoryTransactionStorage implements ITransactionStorage{ +	private static final Logger log = LoggerFactory.getLogger(SimpleInMemoryTransactionStorage.class); +	 +	private Map<String, TransactionStoreElement> storage = new ConcurrentHashMap<String, TransactionStoreElement>(); +	 +	@Override +	public void changeKey(String oldKey, String newKey, Object value) throws EAAFException { +		if (containsKey(oldKey)) { +			 +			 +		} else +			throw new EAAFStorageException("No element in TransactionStorage with key: " + oldKey); +		 +	} + +	@Override +	public List<String> clean(Date now, long dataTimeOut) { +		List<String> result = new ArrayList<String>(); +		Iterator<String> iterator = storage.keySet().iterator(); +		while (iterator.hasNext()) { +			String key = iterator.next();			 +			synchronized (storage) { +				if (storage.containsKey(key)) { +					TransactionStoreElement element = storage.get(key);				 +					if (now.getTime() - element.getCreated().getTime() > dataTimeOut) +						result.add(key); +				} +			}										 +		} +		 +		return result; +		 +	} + +	@Override +	public boolean containsKey(String key) { +		if (key != null) +			return storage.containsKey(key); +		else +			return false; +		 +	} + +	@Override +	public Object get(String key) throws EAAFException { +		if (key != null && containsKey(key)) { +			TransactionStoreElement element = storage.get(key); +			return element.getData(); +			 +		} else +			return null;	 +	} + +	@Override +	public <T> T get(String key, Class<T> type) throws EAAFException { +		return get(key, type, -1); +		 +	} + +	@Override +	public <T> T get(String key, Class<T> type, long dataTimeOut) throws EAAFException { +		if (key != null && containsKey(key)) { +			TransactionStoreElement value = storage.get(key);  +			 +			if (dataTimeOut > -1) { +				long now = new Date().getTime();				 +				if (now - value.getCreated().getTime() > dataTimeOut) { +					log.info("Transaction-Data with key: " + key + " is out of time."); +					throw new EAAFStorageException("Transaction-Data with key: " + key + " is out of time."); +					 +				}	 				  +			} +			 +			if (type.isAssignableFrom(value.getData().getClass())) { +				return (T) value.getData(); +				 +			} else +				log.warn("Can NOT cast '" + value.getClass() + "' to '" + type + "'"); +			 +		} +					 +		return null; +	} + +	@Override +	public Object getRaw(String key) throws EAAFException { +		return storage.get(key); +		 +	} + +	@Override +	public void put(String key, Object value, int dataTimeOut) throws EAAFException { +		TransactionStoreElement element = new TransactionStoreElement(); +		element.setKey(key); +		element.setData(value); +		storage.put(key, element); +		 +	} + +	@Override +	public void putRaw(String key, Object value) throws EAAFException { +		if (value instanceof TransactionStoreElement) +			storage.put(((TransactionStoreElement) value).getKey(), (TransactionStoreElement) value);		 +		else  +			log.info(value.getClass().getName() + " is NOT a RAW element of " + ITransactionStorage.class.getName()); +					 +	} + +	@Override +	public void remove(String key) { +		if (containsKey(key)) { +			log.debug("Remove element with key: " + key + " from " + ITransactionStorage.class.getName()); +			storage.remove(key); +			 +		} +	} + +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/storage/TransactionStoreElement.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/storage/TransactionStoreElement.java new file mode 100644 index 00000000..a0224813 --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/storage/TransactionStoreElement.java @@ -0,0 +1,34 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.storage; + +import java.util.Date; + +public class TransactionStoreElement { +	 +	private String key = null; +	private Object data = null; +	private Date created; +	 +	public String getKey() { +		return key; +	} +	public void setKey(String key) { +		this.key = key; +	} +	public Object getData() { +		return data; +	} +	public void setData(Object data) { +		this.data = data; +	} +	public Date getCreated() { +		return created; +	} +	public void setCreated(Date created) { +		this.created = created; +	} +	 +	 +		 +} diff --git a/connector/src/main/java/at/gv/egiz/eidas/specific/connector/verification/MetadataSignatureVerificationFilter.java b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/verification/MetadataSignatureVerificationFilter.java new file mode 100644 index 00000000..ed88091b --- /dev/null +++ b/connector/src/main/java/at/gv/egiz/eidas/specific/connector/verification/MetadataSignatureVerificationFilter.java @@ -0,0 +1,136 @@ +/******************************************************************************* + *******************************************************************************/ +package at.gv.egiz.eidas.specific.connector.verification; + +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +import org.opensaml.common.SignableSAMLObject; +import org.opensaml.saml2.metadata.EntitiesDescriptor; +import org.opensaml.saml2.metadata.EntityDescriptor; +import org.opensaml.security.SAMLSignatureProfileValidator; +import org.opensaml.xml.security.x509.BasicX509Credential; +import org.opensaml.xml.signature.SignatureValidator; +import org.opensaml.xml.validation.ValidationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.eaaf.core.exceptions.EAAFException; +import at.gv.egiz.eaaf.core.impl.utils.KeyStoreUtils; +import at.gv.egiz.eaaf.modules.pvp2.exception.PVP2MetadataException; +import at.gv.egiz.eaaf.modules.pvp2.idp.exception.SAMLRequestNotSignedException; +import at.gv.egiz.eaaf.modules.pvp2.impl.validation.metadata.AbstractMetadataSignatureFilter; + +public class MetadataSignatureVerificationFilter extends AbstractMetadataSignatureFilter{ +	private static final Logger log = LoggerFactory.getLogger(MetadataSignatureVerificationFilter.class); +	 +	private String metadataURL; +	private List<BasicX509Credential> trustedCredential = new ArrayList<BasicX509Credential>(); +	 +	public MetadataSignatureVerificationFilter(String trustStorePath, String trustStorePassword, String metadataURL)  +			throws PVP2MetadataException { +		this.metadataURL = metadataURL; +		 +		log.trace("Initialize metadata signature-verification filter with truststore: " + trustStorePath + " ... "); +		try { +			KeyStore keyStore = KeyStoreUtils.loadKeyStore(trustStorePath, trustStorePassword); +			if (keyStore != null) { +				//load trusted certificates +				Enumeration<String> aliases = keyStore.aliases(); +				while(aliases.hasMoreElements()) { +					String el = aliases.nextElement(); +					log.trace("Process TrustStoreEntry: " + el); +					if (keyStore.isCertificateEntry(el)) { +						Certificate cert = keyStore.getCertificate(el);  +						if (cert != null && cert instanceof X509Certificate) { +							BasicX509Credential trustedCert = new BasicX509Credential(); +							trustedCert.setEntityCertificate((X509Certificate) cert); +							this.trustedCredential.add(trustedCert); +							log.debug("Add cert: " + ((X509Certificate) cert).getSubjectDN() + " as trusted for metadata: " + metadataURL); +							 +						} else +							log.info("Can not process entry: " + el + ". Reason: " + cert.toString()); +						 +					} +				} +				 +				 +			} +			 +		} catch (KeyStoreException | IOException e) { +			log.warn("Can not open trustStore: " + trustStorePath + " for metadata: " + metadataURL + " Reason: " + e.getMessage(), e); +			throw new PVP2MetadataException("Can not open trustStore: " + trustStorePath + " for metadata", null, e); +			 +		} +		 +		 +	} +	 +	 +	@Override +	protected void verify(EntityDescriptor desc) throws PVP2MetadataException { +		try { +			internalVerify(desc); +			 +		} catch (EAAFException e) { +			log.info("Metadata verification FAILED for: " + metadataURL + " Reason: " +e.getMessage()); +			throw new PVP2MetadataException("Metadata verification FAILED for: " + metadataURL + " Reason: " +e.getMessage(), null, e);  +			 +		}		 +	} + +	@Override +	protected void verify(EntitiesDescriptor desc) throws PVP2MetadataException { +		throw new PVP2MetadataException("EntitiesDescritors are NOT supported", null); +		 +	} + +	@Override +	protected void verify(EntityDescriptor entity, EntitiesDescriptor desc) throws PVP2MetadataException { +		throw new PVP2MetadataException("EntitiesDescritors are NOT supported", null); +		 +	} +	 +	private void internalVerify(SignableSAMLObject signedElement) +			throws EAAFException { +		if (signedElement.getSignature() == null) { +			throw new SAMLRequestNotSignedException(); +		} + +		try { +			SAMLSignatureProfileValidator sigValidator = new SAMLSignatureProfileValidator(); +			sigValidator.validate(signedElement.getSignature()); +		} catch (ValidationException e) { +			log.error("Failed to validate Signature", e); +			throw new SAMLRequestNotSignedException(e); +		} + +		boolean isTrusted = false; +		for (BasicX509Credential cred : trustedCredential) { +			SignatureValidator sigValidator = new SignatureValidator(cred); +			try { +				sigValidator.validate(signedElement.getSignature()); +				isTrusted = true; +				 +			} catch (ValidationException e) { +				log.info("Failed to verfiy Signature with cert: " + cred.getEntityCertificate().getSubjectDN() +						+ " Reason: " + e.getMessage()); +				 +			}			 +		} +		 +		if (!isTrusted) { +			log.warn("PVP2 metadata: " + metadataURL + " are NOT trusted!"); +			throw new SAMLRequestNotSignedException(); +			 +		}  +		 +	} + +} | 
