package at.asitplus.eidas.specific.modules.auth.idaustria.test.task; import static org.junit.Assert.assertThrows; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.Arrays; import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.UUID; import org.apache.commons.lang3.RandomStringUtils; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; import org.opensaml.core.xml.schema.XSString; import org.opensaml.core.xml.util.XMLObjectSupport; import org.opensaml.saml.common.xml.SAMLConstants; import org.opensaml.saml.saml2.core.AuthnRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import at.asitplus.eidas.specific.connector.config.ServiceProviderConfiguration; import at.asitplus.eidas.specific.connector.test.config.dummy.MsConnectorDummyConfigMap; import at.asitplus.eidas.specific.modules.auth.idaustria.IdAustriaAuthConstants; import at.asitplus.eidas.specific.modules.auth.idaustria.tasks.RequestIdAustriaSystemTask; import at.asitplus.eidas.specific.modules.auth.idaustria.utils.IdAustriaAuthMetadataProvider; import at.asitplus.eidas.specific.modules.msproxyservice.protocol.ProxyServicePendingRequest; import at.gv.egiz.eaaf.core.api.data.EaafConfigConstants; import at.gv.egiz.eaaf.core.api.data.EaafConstants; import at.gv.egiz.eaaf.core.api.gui.IVelocityGuiBuilderConfiguration; import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; import at.gv.egiz.eaaf.core.api.storage.ITransactionStorage; import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException; import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; import at.gv.egiz.eaaf.core.impl.idp.module.gui.DummyGuiBuilderConfigurationFactory; import at.gv.egiz.eaaf.core.impl.idp.process.ExecutionContextImpl; import at.gv.egiz.eaaf.modules.pvp2.api.reqattr.EaafRequestedAttributes; import at.gv.egiz.eaaf.modules.pvp2.exception.CredentialsNotAvailableException; import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2InternalErrorException; import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2MetadataException; import at.gv.egiz.eaaf.modules.pvp2.impl.message.PvpSProfileRequest; import at.gv.egiz.eaaf.modules.pvp2.impl.metadata.PvpMetadataResolverFactory; import at.gv.egiz.eaaf.modules.pvp2.impl.opensaml.initialize.EaafOpenSaml3xInitializer; import at.gv.egiz.eaaf.modules.pvp2.impl.utils.Saml2Utils; import at.gv.egiz.eaaf.modules.pvp2.impl.validation.TrustEngineFactory; import at.gv.egiz.eaaf.modules.pvp2.impl.verification.SamlVerificationEngine; import at.gv.egiz.eaaf.modules.pvp2.test.binding.PostBindingTest; import eu.eidas.auth.commons.light.impl.LightRequest; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "/spring/SpringTest-context_basic_mapConfig.xml", "/spring/SpringTest-context_basic_test.xml", }) public class RequestIdAustriaSystemTaskTest { private static final String METADATA_PATH = "classpath:/data/idp_metadata_classpath_entity.xml"; private static final String METADATA_SP_PATH = "classpath:/data/sp_metadata_junit.xml"; @Autowired ApplicationContext context; @Autowired MsConnectorDummyConfigMap config; @Autowired IdAustriaAuthMetadataProvider metadataProvider; @Autowired PvpMetadataResolverFactory metadataFactory; @Autowired DummyGuiBuilderConfigurationFactory guiBuilderConfigFactory; @Autowired SamlVerificationEngine samlVerifyEngine; @Autowired ITransactionStorage transactionStorage; final ExecutionContext executionContext = new ExecutionContextImpl(); private MockHttpServletRequest httpReq; private MockHttpServletResponse httpResp; private ProxyServicePendingRequest pendingReq; private ServiceProviderConfiguration oaParam; private Map spConfig; private RequestIdAustriaSystemTask task; /** * JUnit class initializer. * * @throws Exception In case of an OpenSAML3 initialization error */ @BeforeClass public static void initialize() throws Exception { EaafOpenSaml3xInitializer.eaafInitialize(); } /** * jUnit test set-up. * * @throws Exception In case of an set-up error */ @Before public void setUp() throws Exception { task = (RequestIdAustriaSystemTask) context.getBean("createIdAustriaAuthnRequestTask"); httpReq = new MockHttpServletRequest("POST", "https://localhost/authhandler"); httpResp = new MockHttpServletResponse(); RequestContextHolder.resetRequestAttributes(); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(httpReq, httpResp)); config.putConfigValue(IdAustriaAuthConstants.CONFIG_PROPS_SIGN_SIGNING_ALIAS, "sig"); config.putConfigValue(IdAustriaAuthConstants.CONFIG_PROPS_IDAUSTRIA_ENTITYID, METADATA_PATH); spConfig = new HashMap<>(); spConfig.put(EaafConfigConstants.SERVICE_UNIQUEIDENTIFIER, "http://test.com/test"); oaParam = new ServiceProviderConfiguration(spConfig, config); oaParam.setRequiredLoA(Arrays.asList(EaafConstants.EIDAS_LOA_HIGH)); String spCountryCode = RandomStringUtils.randomAlphabetic(2).toUpperCase(); oaParam.setBpkTargetIdentifier(EaafConstants.URN_PREFIX_EIDAS + "AT+" + spCountryCode); pendingReq = new ProxyServicePendingRequest(); pendingReq.initialize(httpReq, config); pendingReq.setPendingRequestId(RandomStringUtils.randomAlphanumeric(10)); pendingReq.setOnlineApplicationConfiguration(oaParam); metadataProvider.fullyDestroy(); guiBuilderConfigFactory.setVelocityBuilderConfig(createDummyGuiConfig()); } @Test public void missingIdAustriaSystemEntiryId() { config.removeConfigValue(IdAustriaAuthConstants.CONFIG_PROPS_IDAUSTRIA_ENTITYID); TaskExecutionException e = assertThrows(TaskExecutionException.class, () -> task.execute(pendingReq, executionContext)); Assert.assertNotNull(e.getPendingRequestID()); Assert.assertEquals(pendingReq.getPendingRequestId(), e.getPendingRequestID()); Assert.assertNotNull(e.getOriginalException()); org.springframework.util.Assert.isInstanceOf(EaafConfigurationException.class, e.getOriginalException()); Assert.assertEquals("module.idaustria.00", ((EaafConfigurationException) e.getOriginalException()).getErrorId()); } @Test public void noMetadataAvailableOnGlobalConfig() { config.putConfigValue(IdAustriaAuthConstants.CONFIG_PROPS_IDAUSTRIA_ENTITYID, RandomStringUtils.randomAlphabetic(10)); TaskExecutionException e = assertThrows(TaskExecutionException.class, () -> task.execute(pendingReq, executionContext)); Assert.assertNotNull(e.getPendingRequestID()); Assert.assertEquals(pendingReq.getPendingRequestId(), e.getPendingRequestID()); Assert.assertNotNull(e.getOriginalException()); org.springframework.util.Assert.isInstanceOf(EaafConfigurationException.class, e.getOriginalException()); Assert.assertEquals("module.idaustria.05", ((EaafConfigurationException) e.getOriginalException()).getErrorId()); } @Test public void noMetadataSigningKeyStore() throws Pvp2MetadataException { config.removeConfigValue("eidas.ms.modules.idaustriaauth.request.sign.alias"); metadataProvider.addMetadataResolverIntoChain( metadataFactory.createMetadataProvider(METADATA_PATH, null, "jUnitTest", null)); TaskExecutionException e = assertThrows(TaskExecutionException.class, () -> task.execute(pendingReq, executionContext)); Assert.assertNotNull(e.getPendingRequestID()); Assert.assertEquals(pendingReq.getPendingRequestId(), e.getPendingRequestID()); Assert.assertNotNull(e.getOriginalException()); org.springframework.util.Assert.isInstanceOf(CredentialsNotAvailableException.class, e.getOriginalException()); Assert.assertEquals("internal.pvp.01", ((CredentialsNotAvailableException) e.getOriginalException()).getErrorId()); } @Test public void successWithoutSpInfos() throws Pvp2InternalErrorException, SecurityException, Exception { metadataProvider.addMetadataResolverIntoChain( metadataFactory.createMetadataProvider(METADATA_PATH, null, "jUnitTest", null)); LightRequest.Builder eidasRequestBuilder = LightRequest.builder() .id(UUID.randomUUID().toString()) .issuer(RandomStringUtils.randomAlphabetic(10)) .citizenCountryCode(RandomStringUtils.randomAlphabetic(2).toUpperCase()) .levelOfAssurance(EaafConstants.EIDAS_LOA_HIGH) .spCountryCode(RandomStringUtils.randomAlphabetic(2).toUpperCase()) .spType("public"); pendingReq.setEidasRequest(eidasRequestBuilder.build()); //execute test task.execute(pendingReq, executionContext); //validate state final EaafRequestedAttributes reqAttr = validate(); Assert.assertEquals("#Req Attribute", 3, reqAttr.getAttributes().size()); Assert.assertEquals("Wrong req attr.", "urn:eidgvat:attributes.eidas.uniqueId", reqAttr.getAttributes().get(0).getName()); Assert.assertNotNull("Req. Attr value element", reqAttr.getAttributes().get(0).getAttributeValues()); Assert.assertEquals("#Req. Attr value", 1, reqAttr.getAttributes().get(0).getAttributeValues().size()); org.springframework.util.Assert.isInstanceOf(XSString.class, reqAttr.getAttributes().get(0).getAttributeValues().get(0), "Wrong requested Attributes Value type"); Assert.assertEquals("Req. Attr. Value", pendingReq.getServiceProviderConfiguration().getUniqueIdentifier(), ((XSString)reqAttr.getAttributes().get(0).getAttributeValues().get(0)).getValue()); Assert.assertEquals("Wrong req attr.", "urn:oid:1.2.40.0.10.2.1.1.261.34", reqAttr.getAttributes().get(1).getName()); Assert.assertNotNull("Req. Attr value element", reqAttr.getAttributes().get(1).getAttributeValues()); Assert.assertEquals("#Req. Attr value", 1, reqAttr.getAttributes().get(1).getAttributeValues().size()); org.springframework.util.Assert.isInstanceOf(XSString.class, reqAttr.getAttributes().get(1).getAttributeValues().get(0), "Wrong requested Attributes Value type"); Assert.assertEquals("Req. Attr. Value", oaParam.getAreaSpecificTargetIdentifier(), ((XSString)reqAttr.getAttributes().get(1).getAttributeValues().get(0)).getValue()); Assert.assertEquals("Wrong req attr.", "urn:oid:1.2.40.0.10.2.1.1.261.108", reqAttr.getAttributes().get(2).getName()); Assert.assertNotNull("Req. Attr value element", reqAttr.getAttributes().get(1).getAttributeValues()); Assert.assertEquals("#Req. Attr value", 1, reqAttr.getAttributes().get(2).getAttributeValues().size()); org.springframework.util.Assert.isInstanceOf(XSString.class, reqAttr.getAttributes().get(2).getAttributeValues().get(0), "Wrong requested Attributes Value type"); Assert.assertEquals("Req. Attr. Value", "http://eidas.europa.eu/LoA/high", ((XSString)reqAttr.getAttributes().get(2).getAttributeValues().get(0)).getValue()); } @Test public void successWithSpInfos() throws Pvp2InternalErrorException, SecurityException, Exception { metadataProvider.addMetadataResolverIntoChain( metadataFactory.createMetadataProvider(METADATA_PATH, null, "jUnitTest", null)); LightRequest.Builder eidasRequestBuilder = LightRequest.builder() .id(UUID.randomUUID().toString()) .issuer(RandomStringUtils.randomAlphabetic(10)) .citizenCountryCode(RandomStringUtils.randomAlphabetic(2).toUpperCase()) .levelOfAssurance(EaafConstants.EIDAS_LOA_HIGH) .spCountryCode(RandomStringUtils.randomAlphabetic(2).toUpperCase()) .spType("public") .requesterId(RandomStringUtils.randomAlphanumeric(10)) .providerName(RandomStringUtils.randomAlphanumeric(10)); LightRequest eidasReq = eidasRequestBuilder.build(); pendingReq.setEidasRequest(eidasReq); //execute test task.execute(pendingReq, executionContext); //validate state final EaafRequestedAttributes reqAttr = validate(); Assert.assertEquals("#Req Attribute", 5, reqAttr.getAttributes().size()); Assert.assertEquals("Wrong req attr.", "urn:eidgvat:attributes.ServiceProviderFriendlyName", reqAttr.getAttributes().get(3).getName()); Assert.assertNotNull("Req. Attr value element", reqAttr.getAttributes().get(1).getAttributeValues()); Assert.assertEquals("#Req. Attr value", 1, reqAttr.getAttributes().get(3).getAttributeValues().size()); org.springframework.util.Assert.isInstanceOf(XSString.class, reqAttr.getAttributes().get(3).getAttributeValues().get(0), "Wrong requested Attributes Value type"); Assert.assertEquals("Req. Attr. Value", eidasReq.getProviderName(), ((XSString)reqAttr.getAttributes().get(3).getAttributeValues().get(0)).getValue()); Assert.assertEquals("Wrong req attr.", "urn:eidgvat:attributes.ServiceProviderUniqueId", reqAttr.getAttributes().get(4).getName()); Assert.assertNotNull("Req. Attr value element", reqAttr.getAttributes().get(1).getAttributeValues()); Assert.assertEquals("#Req. Attr value", 1, reqAttr.getAttributes().get(4).getAttributeValues().size()); org.springframework.util.Assert.isInstanceOf(XSString.class, reqAttr.getAttributes().get(4).getAttributeValues().get(0), "Wrong requested Attributes Value type"); Assert.assertEquals("Req. Attr. Value", eidasReq.getRequesterId(), ((XSString)reqAttr.getAttributes().get(4).getAttributeValues().get(0)).getValue()); } private EaafRequestedAttributes validate() throws Pvp2InternalErrorException, SecurityException, Exception { Assert.assertEquals("HTTP Statuscode", 200, httpResp.getStatus()); Assert.assertEquals("ContentType", "text/html;charset=UTF-8", httpResp.getContentType()); Assert.assertEquals("ContentEncoding", "UTF-8", httpResp.getCharacterEncoding()); final String html = httpResp.getContentAsString(); Assert.assertNotNull("XML Metadata", html); final int startIndex = html.indexOf("SAMLRequest="); Assert.assertTrue("No SAMLRequest in html", startIndex >= 0); final String authnXml = html.substring(startIndex + "SAMLRequest=".length()); //check if relaystate was stored final int startIndexRelayState = html.indexOf("RelayState="); Assert.assertTrue("wrong RelayState in HTML", startIndexRelayState >= 0); String relayState = html.substring(startIndexRelayState + "RelayState=".length(), startIndex); String storedPendingReqId = transactionStorage.get(relayState, String.class); Assert.assertEquals("relayStore not map to pendingRequestId", pendingReq.getPendingRequestId(), storedPendingReqId); final AuthnRequest authnRequest = (AuthnRequest) XMLObjectSupport.unmarshallFromInputStream( XMLObjectProviderRegistrySupport.getParserPool(), new ByteArrayInputStream( Base64.getDecoder().decode(authnXml))); Assert.assertNotNull("AuthnReq", authnRequest); Assert.assertNotNull("Issuer", authnRequest.getIssuer()); Assert.assertEquals("EntityId", "http://localhost" + IdAustriaAuthConstants.ENDPOINT_METADATA, authnRequest.getIssuer().getValue()); //check XML scheme Saml2Utils.schemeValidation(authnRequest); //check signature final PvpSProfileRequest msg = new PvpSProfileRequest( authnRequest, SAMLConstants.SAML2_POST_BINDING_URI); msg.setEntityID(authnRequest.getIssuer().getValue()); metadataProvider.addMetadataResolverIntoChain( metadataFactory.createMetadataProvider(METADATA_SP_PATH, null, "jUnit SP", null)); samlVerifyEngine.verify(msg, TrustEngineFactory.getSignatureKnownKeysTrustEngine(metadataProvider)); //check other elements Assert.assertNotNull("Extensions", authnRequest.getExtensions()); Assert.assertFalse("No Requested attributes", authnRequest.getExtensions().getUnknownXMLObjects().isEmpty()); Assert.assertEquals("#ReqAttributes", 1, authnRequest.getExtensions().getUnknownXMLObjects().size()); org.springframework.util.Assert.isInstanceOf(EaafRequestedAttributes.class, authnRequest.getExtensions().getUnknownXMLObjects().get(0), "No Requested Attributes object"); return (EaafRequestedAttributes) authnRequest.getExtensions().getUnknownXMLObjects().get(0); } private IVelocityGuiBuilderConfiguration createDummyGuiConfig() { return new IVelocityGuiBuilderConfiguration() { @Override public Map getViewParameters() { return null; } @Override public String getViewName() { return "SAML2 Post-Binding"; } @Override public String getDefaultContentType() { return null; } @Override public InputStream getTemplate(String viewName) { return PostBindingTest.class.getResourceAsStream("/data/pvp_postbinding_template.html"); } @Override public String getClasspathTemplateDir() { return null; } }; } }