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(); + + } + + } + +} |