Repository: urlaubsverwaltung/urlaubsverwaltung
Pull request #4346 (2023-12-01 15:50:17)

Combine db and oidc authorities in OidcPersonAuthoritiesMapper
<!--

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]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&resolved=false&types=CODE_SMELL)

[![100.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/100-16px.png '100.0%')](https://sonarcloud.io/component_measures?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&metric=new_coverage&view=list) [100.0% Coverage](https://sonarcloud.io/component_measures?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&metric=new_coverage&view=list)  
[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&metric=new_duplicated_lines_density&view=list) [0.0% Duplication](https://sonarcloud.io/component_measures?id=urlaubsverwaltung_urlaubsverwaltung&pullRequest=4346&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/OidcPersonAuthoritiesMapper.java b/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/OidcPersonAuthoritiesMapper.java
index e11768082..6f211b71b 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/OidcPersonAuthoritiesMapper.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/OidcPersonAuthoritiesMapper.java
@@ -15,6 +15,7 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
 import java.util.function.Supplier;
+import java.util.stream.Stream;
 
 import static java.lang.String.format;
 import static java.lang.invoke.MethodHandles.lookup;
@@ -38,13 +39,16 @@ class OidcPersonAuthoritiesMapper implements GrantedAuthoritiesMapper {
 
     @Override
     public Collection<? extends GrantedAuthority> mapAuthorities(Collection<? extends GrantedAuthority> authorities) {
-        return authorities
+
+        final Collection<? extends GrantedAuthority> applicationAuthorities = authorities
             .stream()
             .filter(OidcUserAuthority.class::isInstance)
             .findFirst()
             .map(OidcUserAuthority.class::cast)
             .map(this::mapAuthorities)
             .orElseThrow(() -> new OidcPersonMappingException("oidc: The granted authority was not a 'OidcUserAuthority' and the user cannot be mapped."));
+
+        return Stream.concat(applicationAuthorities.stream(), authorities.stream()).toList();
     }
 
     private Collection<? extends GrantedAuthority> mapAuthorities(OidcUserAuthority oidcUserAuthority) {


Test patch:
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/OidcPersonAuthoritiesMapperTest.java b/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/OidcPersonAuthoritiesMapperTest.java
index 823d328d6..62335fff6 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/OidcPersonAuthoritiesMapperTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/OidcPersonAuthoritiesMapperTest.java
@@ -48,6 +48,26 @@ class OidcPersonAuthoritiesMapperTest {
         sut = new OidcPersonAuthoritiesMapper(personService);
     }
 
+
+    @Test
+    void ensureToCombineAuthoritiesFromOIDCWithDatabaseAuthorities() {
+        final String uniqueID = "uniqueID";
+        final String email = "test.me@example.com";
+
+        final OidcUserAuthority oidcUserAuthority = getOidcUserAuthority(Map.of(
+            SUB, uniqueID,
+            EMAIL, email
+        ));
+
+        final Person personForLogin = new Person();
+        personForLogin.setPermissions(List.of(USER));
+        final Optional<Person> person = Optional.of(personForLogin);
+        when(personService.getPersonByUsername(uniqueID)).thenReturn(person);
+
+        final Collection<? extends GrantedAuthority> grantedAuthorities = sut.mapAuthorities(List.of(oidcUserAuthority, new SimpleGrantedAuthority("OFFICE")));
+        assertThat(grantedAuthorities.stream().map(GrantedAuthority::getAuthority)).containsOnly(USER.name(), "OIDC_USER", OFFICE.name());
+    }
+
     @Test
     void mapAuthoritiesFromIdToken() {
         final String uniqueID = "uniqueID";
@@ -64,7 +84,7 @@ class OidcPersonAuthoritiesMapperTest {
         when(personService.getPersonByUsername(uniqueID)).thenReturn(person);
 
         final Collection<? extends GrantedAuthority> grantedAuthorities = sut.mapAuthorities(List.of(oidcUserAuthority));
-        assertThat(grantedAuthorities.stream().map(GrantedAuthority::getAuthority)).containsOnly(USER.name());
+        assertThat(grantedAuthorities.stream().map(GrantedAuthority::getAuthority)).containsOnly(USER.name(), "OIDC_USER");
     }
 
     @Test
@@ -85,7 +105,7 @@ class OidcPersonAuthoritiesMapperTest {
         when(personService.getPersonByMailAddress(email)).thenReturn(person);
 
         final Collection<? extends GrantedAuthority> grantedAuthorities = sut.mapAuthorities(List.of(oidcUserAuthority));
-        assertThat(grantedAuthorities.stream().map(GrantedAuthority::getAuthority)).containsOnly(USER.name());
+        assertThat(grantedAuthorities.stream().map(GrantedAuthority::getAuthority)).containsOnly(USER.name(), "OIDC_USER");
     }
 
     @Test