package at.asitplus.eidas.specific.modules.auth.eidas.v2.test.tasks; import static at.asitplus.eidas.specific.connector.MsEidasNodeConstants.PROP_CONFIG_SP_NEW_EID_MODE; import static org.mockito.ArgumentMatchers.any; import static org.powermock.api.mockito.PowerMockito.when; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.security.KeyStore; import java.security.Provider; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import org.apache.commons.lang3.RandomStringUtils; import org.jetbrains.annotations.NotNull; import org.jose4j.jwa.AlgorithmConstraints; import org.jose4j.jwa.AlgorithmConstraints.ConstraintType; import org.jose4j.jws.AlgorithmIdentifiers; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.annotation.DirtiesContext; 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 com.skjolberg.mockito.soap.SoapServiceRule; import at.asitplus.eidas.specific.connector.MsEidasNodeConstants; import at.asitplus.eidas.specific.modules.auth.eidas.v2.Constants; import at.asitplus.eidas.specific.modules.auth.eidas.v2.exception.SzrCommunicationException; import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.AuthBlockSigningService; import at.asitplus.eidas.specific.modules.auth.eidas.v2.service.EidasAttributeRegistry; import at.asitplus.eidas.specific.modules.auth.eidas.v2.tasks.CreateIdentityLinkTask; import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.JoseUtils; import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.JoseUtils.JwsResult; 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.idp.IConfiguration; import at.gv.egiz.eaaf.core.api.idp.process.ExecutionContext; import at.gv.egiz.eaaf.core.exceptions.EaafException; import at.gv.egiz.eaaf.core.exceptions.EaafStorageException; import at.gv.egiz.eaaf.core.exceptions.TaskExecutionException; import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreFactory; import at.gv.egiz.eaaf.core.impl.credential.EaafKeyStoreUtils; import at.gv.egiz.eaaf.core.impl.credential.KeyStoreConfiguration; import at.gv.egiz.eaaf.core.impl.data.Pair; import at.gv.egiz.eaaf.core.impl.idp.auth.data.AuthProcessDataWrapper; import at.gv.egiz.eaaf.core.impl.idp.module.test.DummySpConfiguration; import at.gv.egiz.eaaf.core.impl.idp.module.test.TestRequestImpl; import at.gv.egiz.eaaf.core.impl.idp.process.ExecutionContextImpl; import eu.eidas.auth.commons.attribute.AttributeDefinition; import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; import eu.eidas.auth.commons.attribute.PersonType; import eu.eidas.auth.commons.protocol.impl.AuthenticationResponse; import lombok.val; import szrservices.SZR; import szrservices.SignContentEntry; import szrservices.SignContentResponseType; @RunWith(SpringJUnit4ClassRunner.class) //@RunWith(PowerMockRunner.class) //@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class) @PrepareForTest(CreateIdentityLinkTask.class) @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) @ContextConfiguration("/SpringTest-context_tasks_test.xml") public class CreateIdentityLinkTaskEidNewTest { @Autowired(required = true) private CreateIdentityLinkTask task; @Autowired(required = true) private IConfiguration basicConfig; @Autowired protected EidasAttributeRegistry attrRegistry; @Autowired EaafKeyStoreFactory keyStoreFactory; @Autowired private AuthBlockSigningService authBlockSigner; final ExecutionContext executionContext = new ExecutionContextImpl(); private MockHttpServletRequest httpReq; private MockHttpServletResponse httpResp; private TestRequestImpl pendingReq; private DummySpConfiguration oaParam; private SZR szrMock; private static final String PW = "f/+saJBc3a}*/T^s"; private static final String ALIAS = "connectorkeypair"; private static final List BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING = Collections.unmodifiableList(Arrays .asList(AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256, AlgorithmIdentifiers.ECDSA_USING_P521_CURVE_AND_SHA512, AlgorithmIdentifiers.RSA_PSS_USING_SHA256, AlgorithmIdentifiers.RSA_PSS_USING_SHA512)); @Rule public final SoapServiceRule soap = SoapServiceRule.newInstance(); /** * jUnit class initializer. * * @throws IOException In case of an error */ @BeforeClass public static void classInitializer() throws IOException { final String current = new java.io.File(".").toURI().toString(); System.setProperty("eidas.ms.configuration", current + "src/test/resources/config/junit_config_3.properties"); } /** * jUnit test set-up. */ @Before public void setUp() throws EaafStorageException, URISyntaxException { httpReq = new MockHttpServletRequest("POST", "https://localhost/authhandler"); httpResp = new MockHttpServletResponse(); RequestContextHolder.resetRequestAttributes(); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(httpReq, httpResp)); final Map spConfig = new HashMap<>(); spConfig.put(EaafConfigConstants.SERVICE_UNIQUEIDENTIFIER, "testSp"); spConfig.put("target", "urn:publicid:gv.at:cdid+XX"); spConfig.put(PROP_CONFIG_SP_NEW_EID_MODE, "true"); oaParam = new DummySpConfiguration(spConfig, basicConfig); pendingReq = new TestRequestImpl(); final AuthenticationResponse response = buildDummyAuthResponse(); pendingReq.getSessionData(AuthProcessDataWrapper.class) .setGenericDataToSession(Constants.DATA_FULL_EIDAS_RESPONSE, response); pendingReq.setSpConfig(oaParam); pendingReq.setPendingReqId(at.gv.egiz.eaaf.core.impl.utils.Random.nextProcessReferenceValue()); pendingReq.setAuthUrl("http://test.com/"); pendingReq.setTransactionId("avaasbav"); executionContext.put(MsEidasNodeConstants.REQ_PARAM_SELECTED_COUNTRY, "XX"); executionContext.put(EaafConstants.PROCESS_ENGINE_REQUIRES_NO_POSTAUTH_REDIRECT, true); szrMock = soap.mock(SZR.class, "http://localhost:1234/demoszr"); } @Test public void successfulProcess() throws Exception { //initialize test when(szrMock, "getStammzahlEncrypted", any(), any()).thenReturn(RandomStringUtils.randomNumeric(10)); val signContentResp = new SignContentResponseType(); final SignContentEntry signContentEntry = new SignContentEntry(); signContentEntry.setValue(RandomStringUtils.randomAlphanumeric(10)); signContentResp.getOut().add(signContentEntry); when(szrMock, "signContent", any(), any(), any()).thenReturn(signContentResp); //perform test task.execute(pendingReq, executionContext); //validate state final AuthProcessDataWrapper authProcessData = pendingReq.getSessionData(AuthProcessDataWrapper.class); Assert.assertNotNull("AuthProcessData", authProcessData); Assert.assertNotNull("eidasBind", authProcessData.getGenericDataFromSession(Constants.EIDAS_BIND, String.class)); String authBlock = authProcessData.getGenericDataFromSession(Constants.SZR_AUTHBLOCK, String.class); Assert.assertNotNull("AuthBlock", authBlock); //check authblock signature final AlgorithmConstraints constraints = new AlgorithmConstraints(ConstraintType.PERMIT, BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING.toArray(new String[BINDING_AUTH_ALGORITHM_WHITELIST_SIGNING.size()])); Pair keyStore = getKeyStore(); X509Certificate[] trustedCerts = EaafKeyStoreUtils .getPrivateKeyAndCertificates(keyStore.getFirst(), ALIAS, PW.toCharArray(), true, "junit").getSecond(); JwsResult result = JoseUtils.validateSignature(authBlock, Arrays.asList(trustedCerts), constraints); Assert.assertTrue("AuthBlock not valid", result.isValid()); } @Test public void getStammzahlEncryptedExceptionTest() throws Exception { try { when(szrMock, "getStammzahlEncrypted", any(), any()).thenReturn(null); task.execute(pendingReq, executionContext); } catch (TaskExecutionException e) { Assert.assertEquals("Incorrect exception thrown", e.getMessage(), "IdentityLink generation for foreign person " + "FAILED."); Assert.assertEquals("Incorrect exception thrown", ((SzrCommunicationException) e.getCause()).getErrorId(), "ernb.01"); Assert.assertTrue("Incorrect exception thrown", e.getCause().getMessage().contains("Stammzahl response empty")); } } @Test public void signContentExceptionTest() throws Exception { try { when(szrMock, "getStammzahlEncrypted", any(), any()).thenReturn(RandomStringUtils.randomNumeric(10)); when(szrMock, "signContent", any(), any(), any()).thenReturn(null); task.execute(pendingReq, executionContext); } catch (TaskExecutionException e) { Assert.assertEquals("Incorrect exception thrown", e.getMessage(), "IdentityLink generation for foreign person " + "FAILED."); Assert.assertEquals("Incorrect exception thrown", ((SzrCommunicationException) e.getCause()).getErrorId(), "ernb.01"); Assert.assertTrue("Incorrect exception thrown", e.getCause().getMessage().contains("BcBind response empty")); } } private Pair getKeyStore() throws EaafException { // read Connector wide config data TODO connector wide! String keyStoreName = basicConfig.getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEYSTORE_NAME); String keyStorePw = basicConfig.getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEYSTORE_PASSWORD); String keyStorePath = basicConfig.getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEYSTORE_PATH); String keyStoreType = basicConfig.getBasicConfiguration(MsEidasNodeConstants.PROP_CONFIG_AUTHBLOCK_KEYSTORE_TYPE); //build new KeyStore configuration KeyStoreConfiguration keyStoreConfiguration = new KeyStoreConfiguration(); keyStoreConfiguration.setFriendlyName("jUnit test"); keyStoreConfiguration.setSoftKeyStoreFilePath(keyStorePath); keyStoreConfiguration.setSoftKeyStorePassword(keyStorePw); keyStoreConfiguration.setKeyStoreType(KeyStoreConfiguration.KeyStoreType.fromString(keyStoreType)); keyStoreConfiguration.setKeyStoreName(keyStoreName); //build new KeyStore based on configuration return keyStoreFactory.buildNewKeyStore(keyStoreConfiguration); } @NotNull private AuthenticationResponse buildDummyAuthResponse() throws URISyntaxException { final AttributeDefinition attributeDef = AttributeDefinition.builder() .friendlyName(Constants.eIDAS_ATTR_PERSONALIDENTIFIER).nameUri(new URI("ad", "sd", "ff")) .personType(PersonType.LEGAL_PERSON).xmlType(new QName("http://saf", "as", "af")) .attributeValueMarshaller("eu.eidas.auth.commons.attribute.impl.LiteralStringAttributeValueMarshaller").build(); final AttributeDefinition attributeDef2 = AttributeDefinition.builder() .friendlyName(Constants.eIDAS_ATTR_CURRENTFAMILYNAME).nameUri(new URI("ad", "sd", "fff")) .personType(PersonType.LEGAL_PERSON).xmlType(new QName("http://saf", "as", "aff")) .attributeValueMarshaller("eu.eidas.auth.commons.attribute.impl.LiteralStringAttributeValueMarshaller").build(); final AttributeDefinition attributeDef3 = AttributeDefinition.builder() .friendlyName(Constants.eIDAS_ATTR_CURRENTGIVENNAME).nameUri(new URI("ad", "sd", "ffff")) .personType(PersonType.LEGAL_PERSON).xmlType(new QName("http://saf", "as", "afff")) .attributeValueMarshaller("eu.eidas.auth.commons.attribute.impl.LiteralStringAttributeValueMarshaller").build(); final AttributeDefinition attributeDef4 = AttributeDefinition.builder() .friendlyName(Constants.eIDAS_ATTR_DATEOFBIRTH).nameUri(new URI("ad", "sd", "fffff")) .personType(PersonType.LEGAL_PERSON).xmlType(new QName("http://saf", "as", "affff")) .attributeValueMarshaller("eu.eidas.auth.commons.attribute.impl.DateTimeAttributeValueMarshaller").build(); final ImmutableAttributeMap attributeMap = ImmutableAttributeMap.builder() .put(attributeDef, "de/st/" + RandomStringUtils.randomNumeric(64)) .put(attributeDef2, RandomStringUtils.randomAlphabetic(10)) .put(attributeDef3, RandomStringUtils.randomAlphabetic(10)).put(attributeDef4, "2001-01-01").build(); val b = new AuthenticationResponse.Builder(); return b.id("aasdf").issuer("asd").subject("asf").statusCode("200").inResponseTo("asdf").subjectNameIdFormat("afaf") .attributes(attributeMap).build(); } }