Repository: urlaubsverwaltung/urlaubsverwaltung
Pull request #4381 (2023-12-14 16:38:22)

Add authorityCheckEnabled for claim mappers
<!--

Thanks for contributing to the Urlaubsverwaltung.
Please review the following notes before submitting you pull request.

Please look for other issues or pull requests which already work on this topic. Is somebody already on it? Do you need to synchronize?

# Security Vulnerabilities

🛑 STOP! 🛑 If your contribution fixes a security vulnerability, please do not submit it.
Instead, please open a report of a security vulnerability via
[Report a security vulnerability](https://github.com/urlaubsverwaltung/urlaubsverwaltung/security/advisories/new)


# Describing Your Changes

If, having reviewed the notes above, you're ready to submit your pull request, please
provide a brief description of the proposed changes.

If they:
 🐞 fix a bug, please describe the broken behaviour and how the changes fix it.
    Please label with 'type: bug' and 'status: new'
    
 🎁 make an enhancement, please describe the new functionality and why you believe it's useful.
    Please label with 'type: enhancement' and 'status: new'
 
If your pull request relates to any existing issues,
please reference them by using the issue number prefixed with #.

-->


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4381) **Quality Gate passed**  
Kudos, no new issues were introduced!

[0 New issues](https://sonarcloud.io/project/issues?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4381&resolved=false&inNewCodePeriod=true)  
[0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4381&resolved=false&inNewCodePeriod=true)  
[100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4381&metric=new_coverage&view=list)  
[0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4381&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4381)



Patch:
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromClaimMappersProperties.java b/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromClaimMappersProperties.java
index e9d000802..071db7330 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromClaimMappersProperties.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromClaimMappersProperties.java
@@ -9,6 +9,15 @@ import org.springframework.validation.annotation.Validated;
 @ConfigurationProperties("uv.security.oidc.claim-mappers")
 public class RolesFromClaimMappersProperties {
 
+
+    /**
+     * Activates or deactivates the authority check of the claim mappers, if they are enabled.
+     * When the authority check is enabled, then you need to provide the {@link RolesFromClaimMappersProperties#rolePrefix}user authority in your iam,
+     * otherwise it can be done via the ui in the application.
+     *
+     */
+    private boolean authorityCheckEnabled = true;
+
     /**
      * Prefix to compare the defined roles/groups against. This role prefix is case-sensitive.
      */
@@ -21,6 +30,14 @@ public class RolesFromClaimMappersProperties {
     @NotNull
     private GroupClaimMapperProperties groupClaim = new GroupClaimMapperProperties();
 
+    public boolean isAuthorityCheckEnabled() {
+        return authorityCheckEnabled;
+    }
+
+    public void setAuthorityCheckEnabled(boolean authorityCheckEnabled) {
+        this.authorityCheckEnabled = authorityCheckEnabled;
+    }
+
     public String getRolePrefix() {
         return rolePrefix;
     }
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromGroupsClaimMapper.java b/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromGroupsClaimMapper.java
index a2e0453ba..937905a0d 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromGroupsClaimMapper.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromGroupsClaimMapper.java
@@ -46,12 +46,12 @@ class RolesFromGroupsClaimMapper implements RolesFromClaimMapper {
         final GroupClaimMapperProperties groupClaim = properties.getGroupClaim();
 
         final String neededResourceAccessRole = properties.getRolePrefix().concat(USER.name().toLowerCase());
-        if (!claims.containsKey(groupClaim.getClaimName())) {
+        if (properties.isAuthorityCheckEnabled() && !claims.containsKey(groupClaim.getClaimName())) {
             throw new MissingClaimAuthorityException(format("User has not required permission '%s' to access urlaubsverwaltung! The claim '%s' is missing!", neededResourceAccessRole, groupClaim.getClaimName()));
         }
 
         final List<String> groups = extractRolesFromClaimName(claims, groupClaim.getClaimName());
-        if (groups.stream().noneMatch(neededResourceAccessRole::equals)) {
+        if (properties.isAuthorityCheckEnabled() && groups.stream().noneMatch(neededResourceAccessRole::equals)) {
             throw new MissingClaimAuthorityException(format("User has not required permission '%s' to access urlaubsverwaltung!", neededResourceAccessRole));
         }
 
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromResourceAccessClaimMapper.java b/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromResourceAccessClaimMapper.java
index c4e6479d2..9baeda845 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromResourceAccessClaimMapper.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromResourceAccessClaimMapper.java
@@ -61,7 +61,7 @@ class RolesFromResourceAccessClaimMapper implements RolesFromClaimMapper {
         final List<String> resourceAccessRoles = extractRolesFromResourceApp(resourceApp, ROLES);
         final String neededResourceAccessRole = properties.getRolePrefix().concat(USER.name().toLowerCase());
 
-        if (resourceAccessRoles.stream().noneMatch(neededResourceAccessRole::equals)) {
+        if (properties.isAuthorityCheckEnabled() && resourceAccessRoles.stream().noneMatch(neededResourceAccessRole::equals)) {
             throw new MissingClaimAuthorityException(format("User has not required permission '%s' to access urlaubsverwaltung!", neededResourceAccessRole));
         }
 


Test patch:
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromClaimMappersPropertiesTest.java b/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromClaimMappersPropertiesTest.java
index 0138dad97..449dbe09b 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromClaimMappersPropertiesTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromClaimMappersPropertiesTest.java
@@ -30,6 +30,12 @@ class RolesFromClaimMappersPropertiesTest {
         assertThat(violations).isEmpty();
     }
 
+    @Test
+    void ensureDefaultAuthorityCheckEnabled() {
+        final RolesFromClaimMappersProperties properties = new RolesFromClaimMappersProperties();
+        assertThat(properties.isAuthorityCheckEnabled()).isTrue();
+    }
+
     @Test
     void ensureDefaultOfRolePrefix() {
         final RolesFromClaimMappersProperties properties = new RolesFromClaimMappersProperties();
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromGroupsClaimMapperTest.java b/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromGroupsClaimMapperTest.java
index 738cad414..6f14019e2 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromGroupsClaimMapperTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromGroupsClaimMapperTest.java
@@ -18,6 +18,26 @@ import static org.springframework.security.oauth2.core.oidc.StandardClaimNames.G
 @ExtendWith(MockitoExtension.class)
 class RolesFromGroupsClaimMapperTest {
 
+    @Test
+    void ensureToNotCheckClaimAndGroupIfDisabled() {
+
+        final RolesFromClaimMappersProperties properties = new RolesFromClaimMappersProperties();
+        properties.setAuthorityCheckEnabled(false);
+        final RolesFromClaimMapperConverter converter = new RolesFromClaimMapperConverter(properties);
+        final RolesFromGroupsClaimMapper sut = new RolesFromGroupsClaimMapper(converter, properties);
+
+        final Map<String, Object> claims = Map.of(
+            SUB, "uniqueID",
+            GIVEN_NAME, "givenName",
+            FAMILY_NAME, "familyNam",
+            EMAIL, "email"
+        );
+
+        final List<GrantedAuthority> authorities = sut.mapClaimToRoles(claims);
+        assertThat(authorities.stream().map(GrantedAuthority::getAuthority))
+            .isEmpty();
+    }
+
     @Test
     void ensureToMapClaimsFromGroupsClaim() {
 
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromResourceAccessClaimMapperTest.java b/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromResourceAccessClaimMapperTest.java
index 08679ba5a..25678e814 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromResourceAccessClaimMapperTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromResourceAccessClaimMapperTest.java
@@ -18,6 +18,26 @@ import static org.springframework.security.oauth2.core.oidc.StandardClaimNames.G
 @ExtendWith(MockitoExtension.class)
 class RolesFromResourceAccessClaimMapperTest {
 
+    @Test
+    void ensureToNotMapClaimsFromResourceAccessClaim() {
+
+        final RolesFromClaimMappersProperties properties = new RolesFromClaimMappersProperties();
+        properties.setAuthorityCheckEnabled(false);
+        final RolesFromClaimMapperConverter converter = new RolesFromClaimMapperConverter(properties);
+        final RolesFromResourceAccessClaimMapper sut = new RolesFromResourceAccessClaimMapper(converter, properties);
+
+        final Map<String, Object> claims = Map.of(
+            SUB, "uniqueID",
+            GIVEN_NAME, "givenName",
+            FAMILY_NAME, "familyNam",
+            EMAIL, "email"
+        );
+
+        final List<GrantedAuthority> authorities = sut.mapClaimToRoles(claims);
+        assertThat(authorities.stream().map(GrantedAuthority::getAuthority))
+            .isEmpty();
+    }
+
     @Test
     void ensureToMapClaimsFromResourceAccessClaim() {