From 8842e4ff602c5c7766c509d1c895b8e7e67fb732 Mon Sep 17 00:00:00 2001
From: Thomas <>
Date: Thu, 1 Jun 2023 16:36:34 +0200
Subject: fix(proxyservice): use requested SubjectNameIdFormat in eIDAS SAML2
 response

---
 .../protocol/ProxyServiceAuthenticationAction.java |  39 ++++-
 .../ProxyServiceAuthenticationActionTest.java      | 170 +++++++++++++++++++--
 2 files changed, 194 insertions(+), 15 deletions(-)

diff --git a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java
index d3c93421..8fc54e39 100644
--- a/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java
+++ b/modules/eidas_proxy-sevice/src/main/java/at/asitplus/eidas/specific/modules/msproxyservice/protocol/ProxyServiceAuthenticationAction.java
@@ -92,15 +92,17 @@ public class ProxyServiceAuthenticationAction implements IAction {
             .statusCode(EidasConstants.SUCCESS_URI)
             .build());
 
-        // TODO: check if we can use transient subjectNameIds
-        lightRespBuilder.subject(UUID.randomUUID().toString());
-        lightRespBuilder.subjectNameIdFormat(NameIDType.TRANSIENT);
+        // build eIDAS attribute result
+        ImmutableAttributeMap eidasAttributes = buildAttributesFromAuthData(authData, eidasReq);
+
+        injectSubjectNameId(lightRespBuilder, eidasAttributes, eidasReq);
 
         // TODO:
         lightRespBuilder.issuer(basicConfig.getBasicConfiguration(
             MsProxyServiceConstants.CONIG_PROPS_EIDAS_PROXY_NODE_ENTITYID));
         lightRespBuilder.levelOfAssurance(authData.getEidasQaaLevel());
-        lightRespBuilder.attributes(buildAttributesFromAuthData(authData, eidasReq));
+
+        lightRespBuilder.attributes(eidasAttributes);
 
         // set SLO response object of EAAF framework
         final SloInformationImpl sloInformation = new SloInformationImpl();
@@ -126,6 +128,7 @@ public class ProxyServiceAuthenticationAction implements IAction {
     }
   }
 
+
   @Override
   public boolean needAuthentication(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp) {
     return true;
@@ -422,4 +425,32 @@ public class ProxyServiceAuthenticationAction implements IAction {
     }     
   }
 
+  private void injectSubjectNameId(Builder lightRespBuilder, ImmutableAttributeMap eidasAttributes,
+      ILightRequest eidasReq) {
+    if (NameIDType.PERSISTENT.equals(eidasReq.getNameIdFormat())) {
+      lightRespBuilder.subjectNameIdFormat(NameIDType.PERSISTENT);      
+      final AttributeDefinition<?> attrDefPersonalId =
+          attrRegistry.getCoreRegistry().getCoreAttributeRegistry().getByFriendlyName(
+              EidasConstants.eIDAS_ATTR_PERSONALIDENTIFIER).first();
+      final AttributeDefinition<?> attrDefJurPersonalId =
+          attrRegistry.getCoreRegistry().getCoreAttributeRegistry().getByFriendlyName(
+              EidasConstants.eIDAS_ATTR_LEGALPERSONIDENTIFIER).first();
+      
+      // set SubjectNameId as same as PersonalIdentifier
+      String subjectNameId = (String) eidasAttributes.getFirstValue(attrDefPersonalId);
+      if (subjectNameId != null) {
+        lightRespBuilder.subject(subjectNameId);
+        
+      } else {
+        lightRespBuilder.subject((String) eidasAttributes.getFirstValue(attrDefJurPersonalId));
+        
+      }
+      
+    } else {
+      lightRespBuilder.subject(UUID.randomUUID().toString());
+      lightRespBuilder.subjectNameIdFormat(NameIDType.TRANSIENT);
+
+    }
+  }
+
 }
diff --git a/modules/eidas_proxy-sevice/src/test/java/at/asitplus/eidas/specific/modules/msproxyservice/test/protocol/ProxyServiceAuthenticationActionTest.java b/modules/eidas_proxy-sevice/src/test/java/at/asitplus/eidas/specific/modules/msproxyservice/test/protocol/ProxyServiceAuthenticationActionTest.java
index 50ce4033..333a823e 100644
--- a/modules/eidas_proxy-sevice/src/test/java/at/asitplus/eidas/specific/modules/msproxyservice/test/protocol/ProxyServiceAuthenticationActionTest.java
+++ b/modules/eidas_proxy-sevice/src/test/java/at/asitplus/eidas/specific/modules/msproxyservice/test/protocol/ProxyServiceAuthenticationActionTest.java
@@ -166,6 +166,36 @@ public class ProxyServiceAuthenticationActionTest {
   }
   
   @Test 
+  public void responseWithoutMandatePersistentNameId() throws EaafException, SpecificCommunicationException {
+    Map<String, Object> attr = new HashMap<>();
+    attr.put(PvpAttributeDefinitions.BPK_NAME, RandomStringUtils.randomAlphanumeric(10));
+    IAuthData authData = generateDummyAuthData(attr, EaafConstants.EIDAS_LOA_HIGH,
+        RandomStringUtils.randomAlphanumeric(10), RandomStringUtils.randomAlphanumeric(10), "1945-04-18",
+        false);
+
+    LightRequest.Builder eidasRequestBuilder = generateBasicLightRequest();
+    eidasRequestBuilder.nameIdFormat(NameIDType.PERSISTENT);
+    pendingReq.setEidasRequest(eidasRequestBuilder.build());
+
+    // perform test
+    SloInformationInterface result = action.processRequest(pendingReq, httpReq, httpResp, authData);
+
+    // validate state
+    Assert.assertNotNull("Result should be not null", result);
+
+    ImmutableAttributeMap respAttr = validateBasicEidasResponse(authData,
+        (String) attr.get(PvpAttributeDefinitions.BPK_NAME), NameIDType.PERSISTENT);
+    assertEquals("wrong attr. size", 4, respAttr.size());
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_PERSONALIDENTIFIER,
+        (String) attr.get(PvpAttributeDefinitions.BPK_NAME));
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_CURRENTFAMILYNAME, authData.getFamilyName());
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_CURRENTGIVENNAME, authData.getGivenName());
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_DATEOFBIRTH,
+        authData.getDateOfBirth());
+
+  }
+
+  @Test
   public void responseWithoutMandateAndOptionalAttributesExist() throws EaafException, SpecificCommunicationException {
     LightRequest.Builder eidasRequestBuilder = generateBasicLightRequest();
     eidasRequestBuilder.requestedAttributes(ImmutableAttributeMap.builder()
@@ -287,6 +317,56 @@ public class ProxyServiceAuthenticationActionTest {
   }
   
   @Test 
+  public void responseWithNatMandatePersistentNameId() throws EaafException, SpecificCommunicationException {
+    Map<String, Object> attr = new HashMap<>();
+    attr.put(PvpAttributeDefinitions.BPK_NAME,
+        "AT+XX:" + RandomStringUtils.randomAlphanumeric(10));
+    attr.put(PvpAttributeDefinitions.MANDATE_NAT_PER_BPK_NAME,
+        RandomStringUtils.randomAlphabetic(10));
+    attr.put(PvpAttributeDefinitions.MANDATE_NAT_PER_GIVEN_NAME_NAME,
+        RandomStringUtils.randomAlphabetic(10));
+    attr.put(PvpAttributeDefinitions.MANDATE_NAT_PER_FAMILY_NAME_NAME,
+        RandomStringUtils.randomAlphabetic(10));
+    attr.put(PvpAttributeDefinitions.MANDATE_NAT_PER_BIRTHDATE_NAME,
+        "1985-11-15");
+
+    LightRequest.Builder eidasRequestBuilder = generateBasicLightRequest();
+    eidasRequestBuilder.nameIdFormat(NameIDType.PERSISTENT);
+    pendingReq.setEidasRequest(eidasRequestBuilder.build());
+
+    IAuthData authData = generateDummyAuthData(attr, EaafConstants.EIDAS_LOA_HIGH,
+        RandomStringUtils.randomAlphanumeric(10), RandomStringUtils.randomAlphanumeric(10), "1945-04-18",
+        true);
+
+    // perform test
+    SloInformationInterface result = action.processRequest(pendingReq, httpReq, httpResp, authData);
+
+    // validate state
+    Assert.assertNotNull("Result should be not null", result);
+
+    ImmutableAttributeMap respAttr = validateBasicEidasResponse(authData,
+        (String) attr.get(PvpAttributeDefinitions.MANDATE_NAT_PER_BPK_NAME), NameIDType.PERSISTENT);
+    assertEquals("wrong attr. size", 8, respAttr.size());
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_REPRESENTATIVE_PERSONALIDENTIFIER,
+        (String) attr.get(PvpAttributeDefinitions.BPK_NAME));
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_REPRESENTATIVE_CURRENTFAMILYNAME, authData
+        .getFamilyName());
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_REPRESENTATIVE_CURRENTGIVENNAME, authData
+        .getGivenName());
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_REPRESENTATIVE_DATEOFBIRTH, authData.getDateOfBirth());
+
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_PERSONALIDENTIFIER,
+        (String) attr.get(PvpAttributeDefinitions.MANDATE_NAT_PER_BPK_NAME));
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_CURRENTFAMILYNAME,
+        (String) attr.get(PvpAttributeDefinitions.MANDATE_NAT_PER_FAMILY_NAME_NAME));
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_CURRENTGIVENNAME,
+        (String) attr.get(PvpAttributeDefinitions.MANDATE_NAT_PER_GIVEN_NAME_NAME));
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_DATEOFBIRTH,
+        (String) attr.get(PvpAttributeDefinitions.MANDATE_NAT_PER_BIRTHDATE_NAME));
+
+  }
+
+  @Test
   public void responseWithNatMandateButJurRequested() throws EaafException, SpecificCommunicationException {
     Map<String, Object> attr = new HashMap<>();
     attr.put(PvpAttributeDefinitions.BPK_NAME, 
@@ -437,6 +517,63 @@ public class ProxyServiceAuthenticationActionTest {
   }
   
   @Test 
+  public void responseWithJurMandatePersistentNameId() throws EaafException, SpecificCommunicationException {
+    Map<String, Object> attr = new HashMap<>();
+    attr.put(PvpAttributeDefinitions.BPK_NAME,
+        "AT+XX:" + RandomStringUtils.randomAlphanumeric(10));
+    attr.put(PvpAttributeDefinitions.MANDATE_LEG_PER_SOURCE_PIN_NAME,
+        RandomStringUtils.randomAlphabetic(10));
+    attr.put(PvpAttributeDefinitions.MANDATE_LEG_PER_FULL_NAME_NAME,
+        RandomStringUtils.randomAlphabetic(10));
+
+    IAuthData authData = generateDummyAuthData(attr, EaafConstants.EIDAS_LOA_HIGH,
+        RandomStringUtils.randomAlphanumeric(10), RandomStringUtils.randomAlphanumeric(10), "1945-04-18",
+        true);
+
+    LightRequest.Builder eidasRequestBuilder = generateBasicLightRequest();
+    eidasRequestBuilder.nameIdFormat(NameIDType.PERSISTENT);
+    eidasRequestBuilder.requestedAttributes(ImmutableAttributeMap.builder()
+        .put(attrRegistry.getCoreAttributeRegistry().getByFriendlyName(
+            EidasConstants.eIDAS_ATTR_LEGALPERSONIDENTIFIER).first())
+        .put(attrRegistry.getCoreAttributeRegistry().getByFriendlyName(EidasConstants.eIDAS_ATTR_LEGALNAME)
+            .first())
+        .build());
+    pendingReq.setEidasRequest(eidasRequestBuilder.build());
+
+    // perform test
+    SloInformationInterface result = action.processRequest(pendingReq, httpReq, httpResp, authData);
+
+    // validate state
+    Assert.assertNotNull("Result should be not null", result);
+
+    ImmutableAttributeMap respAttr = validateBasicEidasResponse(authData,
+        (String) attr.get(PvpAttributeDefinitions.MANDATE_LEG_PER_SOURCE_PIN_NAME), NameIDType.PERSISTENT);
+    assertEquals("wrong attr. size", 6, respAttr.size());
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_REPRESENTATIVE_PERSONALIDENTIFIER,
+        (String) attr.get(PvpAttributeDefinitions.BPK_NAME));
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_REPRESENTATIVE_CURRENTFAMILYNAME, authData
+        .getFamilyName());
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_REPRESENTATIVE_CURRENTGIVENNAME, authData
+        .getGivenName());
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_REPRESENTATIVE_DATEOFBIRTH, authData.getDateOfBirth());
+
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_LEGALPERSONIDENTIFIER,
+        (String) attr.get(PvpAttributeDefinitions.MANDATE_LEG_PER_SOURCE_PIN_NAME));
+    checkAttrValue(respAttr, EidasConstants.eIDAS_ATTR_LEGALNAME,
+        (String) attr.get(PvpAttributeDefinitions.MANDATE_LEG_PER_FULL_NAME_NAME));
+
+    assertNull("find nat. person subject: personalId",
+        getAttrValue(respAttr, EidasConstants.eIDAS_ATTR_PERSONALIDENTIFIER));
+    assertNull("find nat. person subject: familyName",
+        getAttrValue(respAttr, EidasConstants.eIDAS_ATTR_CURRENTFAMILYNAME));
+    assertNull("find nat. person subject: givenName",
+        getAttrValue(respAttr, EidasConstants.eIDAS_ATTR_CURRENTGIVENNAME));
+    assertNull("find nat. person subject: dateOfBirth",
+        getAttrValue(respAttr, EidasConstants.eIDAS_ATTR_DATEOFBIRTH));
+
+  }
+
+  @Test
   public void borisModeResponseWithJurMandate() throws EaafException, SpecificCommunicationException {
     Map<String, Object> attr = new HashMap<>();
     attr.put(PvpAttributeDefinitions.BPK_NAME, 
@@ -835,26 +972,37 @@ public class ProxyServiceAuthenticationActionTest {
   }
   
   private ImmutableAttributeMap validateBasicEidasResponse(IAuthData authData) throws SpecificCommunicationException {
+    return validateBasicEidasResponse(authData, null, NameIDType.TRANSIENT);
+    
+  }
+  
+  private ImmutableAttributeMap validateBasicEidasResponse(IAuthData authData, String subjectNameId,
+      String persistent) throws SpecificCommunicationException {
     assertNotNull("not redirct Header", httpResp.getHeader("Location"));
-    assertTrue("wrong redirect URL", httpResp.getHeader("Location").startsWith("http://eidas.proxy/endpoint?token="));    
+    assertTrue("wrong redirect URL", httpResp.getHeader("Location").startsWith(
+        "http://eidas.proxy/endpoint?token="));
     String token = httpResp.getHeader("Location").substring("http://eidas.proxy/endpoint?token=".length());
-    
-    ILightResponse resp = springManagedSpecificConnectorCommunicationService.getAndRemoveResponse(URLDecoder.decode(token), 
-        ImmutableSortedSet.copyOf(attrRegistry.getCoreAttributeRegistry().getAttributes()));    
-    
+
+    ILightResponse resp = springManagedSpecificConnectorCommunicationService.getAndRemoveResponse(URLDecoder
+        .decode(token),
+        ImmutableSortedSet.copyOf(attrRegistry.getCoreAttributeRegistry().getAttributes()));
+
     assertNotNull("responseId", resp.getId());
     assertEquals("inResponseTo", pendingReq.getEidasRequest().getId(), resp.getInResponseToId());
     assertEquals("relayState", pendingReq.getEidasRequest().getRelayState(), resp.getRelayState());
     assertEquals("LoA", authData.getEidasQaaLevel(), resp.getLevelOfAssurance());
-    
+
     assertNotNull("subjectNameId", resp.getSubject());
-    assertEquals("subjectNameIdFormat", NameIDType.TRANSIENT, resp.getSubjectNameIdFormat());
-    
-    assertFalse("not attributes", resp.getAttributes().isEmpty());    
+    if (subjectNameId != null) {
+      assertEquals("subjectNameId", subjectNameId, resp.getSubject());
+    }
+    assertEquals("subjectNameIdFormat", persistent, resp.getSubjectNameIdFormat());
+
+    assertFalse("not attributes", resp.getAttributes().isEmpty());
     return resp.getAttributes();
-    
+
   }
-  
+
   private Builder generateBasicLightRequest() {
     return LightRequest.builder()
         .id(UUID.randomUUID().toString())
-- 
cgit v1.2.3