Repository: urlaubsverwaltung/urlaubsverwaltung
Pull request #4378 (2023-12-11 12:48:06)

If claim is missing the application should not return access denied a…
…nd loop

<!--

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 #.

-->



Patch:
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/api/RestApiSecurityConfig.java b/src/main/java/org/synyx/urlaubsverwaltung/api/RestApiSecurityConfig.java
index 90f356945..36a6888b7 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/api/RestApiSecurityConfig.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/api/RestApiSecurityConfig.java
@@ -25,8 +25,8 @@ class RestApiSecurityConfig {
         return http
             .securityMatcher("/api/**")
             .csrf(AbstractHttpConfigurer::disable)
-            .authorizeHttpRequests(requests ->
-                requests
+            .authorizeHttpRequests(authorizeHttpRequests ->
+                authorizeHttpRequests
                     .requestMatchers("/api/**").hasAuthority(USER.name())
                     .anyRequest().authenticated()
             ).sessionManagement(
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/dev/DemoDataCreationService.java b/src/main/java/org/synyx/urlaubsverwaltung/dev/DemoDataCreationService.java
index f60118fc7..8d312dfbf 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/dev/DemoDataCreationService.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/dev/DemoDataCreationService.java
@@ -142,6 +142,7 @@ public class DemoDataCreationService {
                 createDemoApplicationsAndSickNotes(EMAIL_DAMPF);
                 createDemoApplicationsAndSickNotes(EMAIL_SCHMIDT);
                 createDemoApplicationsAndSickNotes(EMAIL_SECOND_STAGE_AUTHORITY);
+
                 break;
             case EMAIL_OFFICE:
                 final Person office = personDataProvider.updateTestPerson(5, email, List.of(Role.USER, Role.OFFICE), PERSON_NOTIFICATIONS);
@@ -154,35 +155,35 @@ public class DemoDataCreationService {
                 createDemoApplicationsAndSickNotes(EMAIL_SECOND_STAGE_AUTHORITY);
                 break;
             case "admin@urlaubsverwaltung.cloud":
-                personDataProvider.updateTestPerson(5, email, List.of(Role.USER), List.of());
+                personDataProvider.updateTestPerson(5, email, List.of(Role.USER, Role.ADMIN), List.of());
                 break;
             case EMAIL_DAMPF:
-                final Person dampf = personDataProvider.updateTestPerson(6, email, List.of(Role.USER), PERSON_NOTIFICATIONS);
+                final Person dampf = personDataProvider.updateTestPerson(6, email, List.of(Role.USER, Role.USER), PERSON_NOTIFICATIONS);
                 departmentDataProvider.addDepartmentMember(DEPARTMENT_ADMINS, dampf);
                 createDemoApplicationsAndSickNotes(EMAIL_DAMPF);
                 break;
             case "baier@urlaubsverwaltung.cloud":
-                final Person baier = personDataProvider.updateTestPerson(7, email, List.of(Role.USER), PERSON_NOTIFICATIONS);
+                final Person baier = personDataProvider.updateTestPerson(7, email, List.of(Role.USER, Role.USER), PERSON_NOTIFICATIONS);
                 departmentDataProvider.addDepartmentMember(DEPARTMENT_MARKETING, baier);
                 break;
             case "schneider@urlaubsverwaltung.cloud":
-                final Person schneider = personDataProvider.updateTestPerson(8, email, List.of(Role.USER), PERSON_NOTIFICATIONS);
+                final Person schneider = personDataProvider.updateTestPerson(8, email, List.of(Role.USER, Role.USER), PERSON_NOTIFICATIONS);
                 departmentDataProvider.addDepartmentMember(DEPARTMENT_MARKETING, schneider);
                 break;
             case "haendel@urlaubsverwaltung.cloud":
-                final Person haendel = personDataProvider.updateTestPerson(9, email, List.of(Role.USER), PERSON_NOTIFICATIONS);
+                final Person haendel = personDataProvider.updateTestPerson(9, email, List.of(Role.USER, Role.USER), PERSON_NOTIFICATIONS);
                 departmentDataProvider.addDepartmentMember(DEPARTMENT_ADMINS, haendel);
                 break;
             case EMAIL_SCHMIDT:
-                final Person schmidt = personDataProvider.updateTestPerson(10, email, List.of(Role.USER), PERSON_NOTIFICATIONS);
+                final Person schmidt = personDataProvider.updateTestPerson(10, email, List.of(Role.USER, Role.USER), PERSON_NOTIFICATIONS);
                 departmentDataProvider.addDepartmentMember(DEPARTMENT_ENTWICKLUNG, schmidt);
                 createDemoApplicationsAndSickNotes(EMAIL_SCHMIDT);
                 break;
             case "hdieter@urlaubsverwaltung.cloud":
-                personDataProvider.updateTestPerson(11, email, List.of(Role.USER), PERSON_NOTIFICATIONS);
+                personDataProvider.updateTestPerson(11, email, List.of(Role.USER, Role.USER), PERSON_NOTIFICATIONS);
                 break;
             default:
-                personDataProvider.updateTestPerson(randomPersonnelNumber(), email, List.of(Role.USER), PERSON_NOTIFICATIONS);
+                personDataProvider.updateTestPerson(randomPersonnelNumber(), email, List.of(Role.USER, Role.USER), PERSON_NOTIFICATIONS);
                 break;
         }
     }
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/person/Role.java b/src/main/java/org/synyx/urlaubsverwaltung/person/Role.java
index 859e662f3..20cd40f2f 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/person/Role.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/person/Role.java
@@ -12,6 +12,7 @@ public enum Role {
     SECOND_STAGE_AUTHORITY,
     BOSS,
     OFFICE,
+    ADMIN,
     INACTIVE,
     APPLICATION_ADD,
     APPLICATION_CANCEL,
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsMapper.java b/src/main/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsMapper.java
index b189b46d2..67252ab26 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsMapper.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsMapper.java
@@ -40,6 +40,7 @@ final class PersonPermissionsMapper {
                 case SECOND_STAGE_AUTHORITY -> mappedToRoles.add(Role.SECOND_STAGE_AUTHORITY);
                 case BOSS -> mappedToRoles.add(Role.BOSS);
                 case OFFICE -> mappedToRoles.add(Role.OFFICE);
+                case ADMIN -> mappedToRoles.add(Role.ADMIN);
                 case INACTIVE -> mappedToRoles.add(Role.INACTIVE);
                 case SICK_NOTE_VIEW_ADD_EDIT -> {
                     mappedToRoles.add(Role.SICK_NOTE_VIEW);
@@ -68,6 +69,7 @@ final class PersonPermissionsMapper {
                 case SECOND_STAGE_AUTHORITY -> mappedToRolesDto.add(PersonPermissionsRoleDto.SECOND_STAGE_AUTHORITY);
                 case BOSS -> mappedToRolesDto.add(PersonPermissionsRoleDto.BOSS);
                 case OFFICE -> mappedToRolesDto.add(PersonPermissionsRoleDto.OFFICE);
+                case ADMIN -> mappedToRolesDto.add(PersonPermissionsRoleDto.ADMIN);
                 case INACTIVE -> mappedToRolesDto.add(PersonPermissionsRoleDto.INACTIVE);
                 case SICK_NOTE_VIEW, SICK_NOTE_ADD, SICK_NOTE_EDIT, SICK_NOTE_CANCEL, SICK_NOTE_COMMENT ->
                     mappedToRolesDto.add(PersonPermissionsRoleDto.SICK_NOTE_VIEW_ADD_EDIT);
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsRoleDto.java b/src/main/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsRoleDto.java
index d13815bb3..7813171c9 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsRoleDto.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsRoleDto.java
@@ -7,6 +7,7 @@ public enum PersonPermissionsRoleDto {
     SECOND_STAGE_AUTHORITY,
     BOSS,
     OFFICE,
+    ADMIN,
     INACTIVE,
     APPLICATION_ADD_CANCEL,
     SICK_NOTE_VIEW_ADD_EDIT
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsViewController.java b/src/main/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsViewController.java
index 66326cc14..9093ff409 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsViewController.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsViewController.java
@@ -26,6 +26,7 @@ import java.util.function.Predicate;
 
 import static java.util.function.Predicate.not;
 import static java.util.stream.Collectors.toList;
+import static org.synyx.urlaubsverwaltung.person.Role.ADMIN;
 import static org.synyx.urlaubsverwaltung.person.Role.INACTIVE;
 import static org.synyx.urlaubsverwaltung.person.Role.USER;
 import static org.synyx.urlaubsverwaltung.person.web.PersonPermissionsMapper.mapRoleToPermissionsDto;
@@ -96,7 +97,7 @@ public class PersonPermissionsViewController implements HasLaunchpad {
     private static List<Role> calculateAddedPermissions(Collection<Role> oldRoles, Person updatedPerson) {
         return updatedPerson.getPermissions().stream()
             .filter(not(oldRoles::contains))
-            .filter(role(INACTIVE).and(role(USER)))
+            .filter(role(INACTIVE).and(role(USER)).and(role(ADMIN)))
             .collect(toList());
     }
 
diff --git a/src/main/java/org/synyx/urlaubsverwaltung/security/SecurityWebConfiguration.java b/src/main/java/org/synyx/urlaubsverwaltung/security/SecurityWebConfiguration.java
index daa1928a7..64e2e6ff8 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/security/SecurityWebConfiguration.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/security/SecurityWebConfiguration.java
@@ -1,9 +1,10 @@
 package org.synyx.urlaubsverwaltung.security;
 
 import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
+import org.springframework.boot.actuate.health.HealthEndpoint;
+import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.core.annotation.Order;
 import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler;
@@ -38,21 +39,12 @@ class SecurityWebConfiguration {
         this.clientRegistrationRepository = clientRegistrationRepository;
     }
 
-    @Bean
-    @Order(2)
-    SecurityFilterChain actuatorSecurityFilterChain(final HttpSecurity http) throws Exception {
-        return http
-            .securityMatcher(EndpointRequest.toAnyEndpoint())
-            .authorizeHttpRequests(requests -> requests.anyRequest().permitAll())
-            .build();
-    }
-
     @Bean
     SecurityFilterChain webSecurityFilterChain(final HttpSecurity http, DelegatingSecurityContextRepository securityContextRepository) throws Exception {
 
         http
-            .authorizeHttpRequests(requests ->
-                requests
+            .authorizeHttpRequests(authorizeHttpRequests ->
+                authorizeHttpRequests
                     // Swagger API
                     .requestMatchers("/api", "/api/", "/swagger-ui/index.html").hasAuthority(USER.name())
                     // Assets
@@ -77,6 +69,10 @@ class SecurityWebConfiguration {
                     .requestMatchers("/web/person/**").hasAuthority(USER.name())
                     .requestMatchers("/web/sicknote/**").hasAuthority(USER.name())
                     .requestMatchers("/web/settings/**").hasAuthority(OFFICE.name())
+                    .requestMatchers(EndpointRequest.to(HealthEndpoint.class)).permitAll()
+                    .requestMatchers(EndpointRequest.to(PrometheusScrapeEndpoint.class)).permitAll()
+                    // TODO muss konfigurierbar werden!
+                    .requestMatchers(EndpointRequest.toAnyEndpoint()).hasAuthority("ADMIN")
                     .anyRequest().authenticated()
             );
 
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 7a5abbfc4..a2e0453ba 100644
--- a/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromGroupsClaimMapper.java
+++ b/src/main/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromGroupsClaimMapper.java
@@ -1,7 +1,6 @@
 package org.synyx.urlaubsverwaltung.security.oidc;
 
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.stereotype.Component;
 import org.synyx.urlaubsverwaltung.security.oidc.RolesFromClaimMappersProperties.GroupClaimMapperProperties;
@@ -46,13 +45,12 @@ class RolesFromGroupsClaimMapper implements RolesFromClaimMapper {
 
         final GroupClaimMapperProperties groupClaim = properties.getGroupClaim();
 
+        final String neededResourceAccessRole = properties.getRolePrefix().concat(USER.name().toLowerCase());
         if (!claims.containsKey(groupClaim.getClaimName())) {
-            throw new AccessDeniedException(format("claim=%s is missing!", 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());
-        final String neededResourceAccessRole = properties.getRolePrefix().concat(USER.name().toLowerCase());
-
         if (groups.stream().noneMatch(neededResourceAccessRole::equals)) {
             throw new MissingClaimAuthorityException(format("User has not required permission '%s' to access urlaubsverwaltung!", neededResourceAccessRole));
         }
diff --git a/src/main/resources/application-demodata.yaml b/src/main/resources/application-demodata.yaml
index 069ee43fb..2cf87420a 100644
--- a/src/main/resources/application-demodata.yaml
+++ b/src/main/resources/application-demodata.yaml
@@ -55,14 +55,3 @@ launchpad:
       en: urlaubsverwaltung.cloud
     url: https://urlaubsverwaltung.cloud
   name-default-locale: de
-
-management:
-  server:
-    port: 8081
-  endpoint:
-    prometheus:
-      enabled: true
-  endpoints:
-    web:
-      exposure:
-        include: info, health, prometheus, metrics
diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml
index 603e44c44..c4982f2a8 100644
--- a/src/main/resources/application.yaml
+++ b/src/main/resources/application.yaml
@@ -53,13 +53,13 @@ springdoc:
     disable-swagger-default-url: true
 
 management:
-  endpoint:
-    prometheus:
-      enabled: false
   endpoints:
     web:
       exposure:
-        include: info, health
+        include: health,info
+  endpoint:
+    prometheus:
+      enabled: false
 
 info:
   app:
diff --git a/src/main/resources/dbchangelogs/changelog-5.0.0-RC1-delete-admin-role.xml b/src/main/resources/dbchangelogs/changelog-5.0.0-RC1-delete-admin-role.xml
deleted file mode 100644
index 64f778065..000000000
--- a/src/main/resources/dbchangelogs/changelog-5.0.0-RC1-delete-admin-role.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.1" encoding="UTF-8" standalone="no"?>
-<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
-                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd">
-
-  <changeSet author="schneider" id="delete-admin-role">
-
-    <preConditions>
-      <tableExists tableName="person_permissions"/>
-    </preConditions>
-
-    <delete tableName="person_permissions">
-      <where>permissions = 'ADMIN'</where>
-    </delete>
-  </changeSet>
-</databaseChangeLog>
diff --git a/src/main/resources/dbchangelogs/changelogmaster.xml b/src/main/resources/dbchangelogs/changelogmaster.xml
index ec0e8d1f5..51ed28c78 100644
--- a/src/main/resources/dbchangelogs/changelogmaster.xml
+++ b/src/main/resources/dbchangelogs/changelogmaster.xml
@@ -6,5 +6,4 @@
   <include relativeToChangelogFile="true" file="changelog-5.0.0-M1-initial.xml"/>
   <include relativeToChangelogFile="true" file="changelog-5.0.0-M4-add-custom-vacation-type.xml"/>
   <include relativeToChangelogFile="true" file="changelog-5.0.0-unique-department-names.xml"/>
-  <include relativeToChangelogFile="true" file="changelog-5.0.0-RC1-delete-admin-role.xml"/>
 </databaseChangeLog>
diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties
index ea576eb8e..7b78b4e87 100644
--- a/src/main/resources/messages.properties
+++ b/src/main/resources/messages.properties
@@ -746,6 +746,8 @@ person.form.permissions.roles.BOSS=Chef
 person.form.permissions.roles.BOSS.description=darf Abwesenheiten aller Benutzer einsehen, genehmigen und ablehnen
 person.form.permissions.roles.OFFICE=Office
 person.form.permissions.roles.OFFICE.description=darf Einstellungen zur Anwendung vornehmen, die Daten aller Mitarbeiter verwalten, Abwesenheiten für alle Mitarbeiter beantragen/stornieren und Krankmeldungen pflegen
+person.form.permissions.roles.ADMIN=Admin
+person.form.permissions.roles.ADMIN.description=darf auf technische Metriken der Anwendung zugreifen
 person.form.permissions.roles.additional_roles.description=Zusätzliche Berechtigungen
 person.form.permissions.roles.SICK_NOTE_VIEW_ADD_EDIT=Pflege von Krankmeldungen
 person.form.permissions.roles.SICK_NOTE_VIEW_ADD_EDIT.description=darf Krankmeldungen aller Mitarbeitenden für die {0} verantwortlich ist, pflegen. Diese Funktionalität ist bereits in der Office-Berechtigung integriert.
diff --git a/src/main/resources/messages_de_AT.properties b/src/main/resources/messages_de_AT.properties
index 834d897c6..06c14592d 100644
--- a/src/main/resources/messages_de_AT.properties
+++ b/src/main/resources/messages_de_AT.properties
@@ -722,6 +722,8 @@ person.form.permissions.roles.BOSS=Chef
 person.form.permissions.roles.BOSS.description=darf Abwesenheiten aller Benutzer einsehen, genehmigen und ablehnen
 person.form.permissions.roles.OFFICE=Office
 person.form.permissions.roles.OFFICE.description=darf Einstellungen zur Anwendung vornehmen, die Daten aller Mitarbeiter verwalten, Abwesenheiten für alle Mitarbeiter beantragen/stornieren und Krankmeldungen pflegen
+person.form.permissions.roles.ADMIN=Admin
+person.form.permissions.roles.ADMIN.description=darf auf technische Metriken der Anwendung zugreifen
 person.form.permissions.roles.additional_roles.description=Zusätzliche Berechtigungen
 person.form.permissions.roles.SICK_NOTE_VIEW_ADD_EDIT=Pflege von Krankmeldungen
 person.form.permissions.roles.SICK_NOTE_VIEW_ADD_EDIT.description=darf Krankmeldungen aller Mitarbeitenden für die {0} verantwortlich ist, pflegen. Diese Funktionalität ist bereits in der Office-Berechtigung integriert.
diff --git a/src/main/resources/messages_el.properties b/src/main/resources/messages_el.properties
index 1662b5bbc..5b1458fa4 100644
--- a/src/main/resources/messages_el.properties
+++ b/src/main/resources/messages_el.properties
@@ -675,6 +675,8 @@ person.form.permissions.roles.BOSS=Αφεντικό
 person.form.permissions.roles.BOSS.description=μπορεί να δει, να εγκρίνει και να απορρίψει τις άδειες όλων των χρηστών
 person.form.permissions.roles.OFFICE=Γραφείο
 person.form.permissions.roles.OFFICE.description=μπορεί να διαμορφώσει ρυθμίσεις για την εφαρμογή, να διαχειρίζεται τα δεδομένα όλων υπαλλήλων, να ζητά / να ακυρώνει άδειες για όλους υπαλλήλους και να διατηρεί σημειώσεις ασθενείας
+person.form.permissions.roles.ADMIN=Διαχειριστής
+person.form.permissions.roles.ADMIN.description=μπορεί να έχει πρόσβαση σε τεχνικές μετρήσεις της εφαρμογής
 person.form.permissions.roles.additional_roles.description=Zusätzliche Berechtigungen
 person.form.permissions.roles.SICK_NOTE_VIEW_ADD_EDIT=Pflege von Krankmeldungen
 person.form.permissions.roles.SICK_NOTE_VIEW_ADD_EDIT.description=darf Krankmeldungen aller Mitarbeitenden für die {0} verantwortlich ist, pflegen. Diese Funktionalität ist bereits in der Office-Berechtigung integriert.
diff --git a/src/main/resources/messages_en.properties b/src/main/resources/messages_en.properties
index 247a52aa2..465dd5705 100644
--- a/src/main/resources/messages_en.properties
+++ b/src/main/resources/messages_en.properties
@@ -736,6 +736,8 @@ person.form.permissions.roles.BOSS=Boss
 person.form.permissions.roles.BOSS.description=may view, approve and deny absences of all users
 person.form.permissions.roles.OFFICE=Office
 person.form.permissions.roles.OFFICE.description=may configure settings for the application, manage the data of all employees, request / cancel absences for all employees and maintain sick notes
+person.form.permissions.roles.ADMIN=Admin
+person.form.permissions.roles.ADMIN.description=can access technical metrics of the application
 person.form.permissions.roles.additional_roles.description=Additional permissions
 person.form.permissions.roles.SICK_NOTE_VIEW_ADD_EDIT=Management of sick notes
 person.form.permissions.roles.SICK_NOTE_VIEW_ADD_EDIT.description=may manage sick notes for all employees for whom {0} is responsible. This functionality is already integrated in the Office permission.


Test patch:
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/absence/AbsenceApiControllerSecurityIT.java b/src/test/java/org/synyx/urlaubsverwaltung/absence/AbsenceApiControllerSecurityIT.java
index 454531dc3..9cc232387 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/absence/AbsenceApiControllerSecurityIT.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/absence/AbsenceApiControllerSecurityIT.java
@@ -165,8 +165,8 @@ class AbsenceApiControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"INACTIVE"})
-    void getAbsencesForOtherUserIsForbidden(final String role) throws Exception {
+    @ValueSource(strings = {"ADMIN", "INACTIVE"})
+    void getAbsencesAsAdminUserForOtherUserIsForbidden(final String role) throws Exception {
         perform(get("/api/persons/1/absences")
             .param("from", "2016-01-01")
             .param("to", "2016-12-31")
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/calendar/CalendarSharingViewControllerCompanyCalendarSecurityIT.java b/src/test/java/org/synyx/urlaubsverwaltung/calendar/CalendarSharingViewControllerCompanyCalendarSecurityIT.java
index 3af88e7a4..94cd479b1 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/calendar/CalendarSharingViewControllerCompanyCalendarSecurityIT.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/calendar/CalendarSharingViewControllerCompanyCalendarSecurityIT.java
@@ -99,7 +99,7 @@ class CalendarSharingViewControllerCompanyCalendarSecurityIT extends TestContain
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE"})
+    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "ADMIN", "INACTIVE"})
     void linkCompanyCalendarIsForbidden(final String role) throws Exception {
 
         perform(
@@ -180,7 +180,7 @@ class CalendarSharingViewControllerCompanyCalendarSecurityIT extends TestContain
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE"})
+    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "ADMIN", "INACTIVE"})
     void unlinkCompanyCalendarIsForbidden(final String role) throws Exception {
 
         perform(
@@ -243,7 +243,7 @@ class CalendarSharingViewControllerCompanyCalendarSecurityIT extends TestContain
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE"})
+    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "ADMIN", "INACTIVE"})
     void enableCompanyCalendarFeatureIsForbidden(final String role) throws Exception {
 
         perform(
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/calendar/CalendarSharingViewControllerDepartmentCalendarSecurityIT.java b/src/test/java/org/synyx/urlaubsverwaltung/calendar/CalendarSharingViewControllerDepartmentCalendarSecurityIT.java
index 0712fedfa..7c2cd9750 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/calendar/CalendarSharingViewControllerDepartmentCalendarSecurityIT.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/calendar/CalendarSharingViewControllerDepartmentCalendarSecurityIT.java
@@ -77,7 +77,7 @@ class CalendarSharingViewControllerDepartmentCalendarSecurityIT extends TestCont
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE"})
+    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "ADMIN", "INACTIVE"})
     void indexIsForbidden(final String role) throws Exception {
 
         perform(
@@ -212,7 +212,7 @@ class CalendarSharingViewControllerDepartmentCalendarSecurityIT extends TestCont
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE"})
+    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "ADMIN", "INACTIVE"})
     void linkDepartmentCalendarIsForbidden(final String role) throws Exception {
 
         perform(
@@ -299,7 +299,7 @@ class CalendarSharingViewControllerDepartmentCalendarSecurityIT extends TestCont
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE"})
+    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "ADMIN", "INACTIVE"})
     void unlinkDepartmentCalendarIsForbidden(final String role) throws Exception {
 
         perform(post("/web/calendars/share/persons/1/departments/2")
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/calendar/CalendarSharingViewControllerPersonCalendarSecurityIT.java b/src/test/java/org/synyx/urlaubsverwaltung/calendar/CalendarSharingViewControllerPersonCalendarSecurityIT.java
index 9d8b4b2a7..cc14c05d8 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/calendar/CalendarSharingViewControllerPersonCalendarSecurityIT.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/calendar/CalendarSharingViewControllerPersonCalendarSecurityIT.java
@@ -71,7 +71,7 @@ class CalendarSharingViewControllerPersonCalendarSecurityIT extends TestContaine
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE"})
+    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "ADMIN", "INACTIVE"})
     void ensureIndexAsWithIncorrectRoleIsForbidden(final String role) throws Exception {
         perform(get("/web/calendars/share/persons/1")
             .with(oidcLogin().authorities(new SimpleGrantedAuthority("USER"), new SimpleGrantedAuthority(role))))
@@ -124,7 +124,7 @@ class CalendarSharingViewControllerPersonCalendarSecurityIT extends TestContaine
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE"})
+    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "ADMIN", "INACTIVE"})
     void linkPrivateCalendarIsForbidden(final String role) throws Exception {
 
         perform(
@@ -136,7 +136,7 @@ class CalendarSharingViewControllerPersonCalendarSecurityIT extends TestContaine
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "OFFICE", "BOSS", "INACTIVE"})
+    @ValueSource(strings = {"USER", "OFFICE", "BOSS", "ADMIN", "INACTIVE"})
     void linkPrivateCalendarAsOfficeUserForOtherUserIsOk(final String role) throws Exception {
 
         final Person person = new Person();
@@ -181,7 +181,7 @@ class CalendarSharingViewControllerPersonCalendarSecurityIT extends TestContaine
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE"})
+    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "ADMIN", "INACTIVE"})
     void unlinkPrivateCalendarAsDepartmentHeadIsForbidden(final String role) throws Exception {
 
         perform(
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/department/api/DepartmentApiControllerSecurityIT.java b/src/test/java/org/synyx/urlaubsverwaltung/department/api/DepartmentApiControllerSecurityIT.java
index 22ad2c55a..62d1d21db 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/department/api/DepartmentApiControllerSecurityIT.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/department/api/DepartmentApiControllerSecurityIT.java
@@ -33,7 +33,7 @@ class DepartmentApiControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "BOSS", "INACTIVE"})
+    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "BOSS", "ADMIN", "INACTIVE"})
     void getDepartmentsWithBasicAuthIsNotOk(final String role) throws Exception {
         perform(
             get("/api/departments")
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/person/MailNotificationTest.java b/src/test/java/org/synyx/urlaubsverwaltung/person/MailNotificationTest.java
index 9ce6b8da2..242e5f3e8 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/person/MailNotificationTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/person/MailNotificationTest.java
@@ -303,7 +303,7 @@ class MailNotificationTest {
     }
 
     @ParameterizedTest
-    @EnumSource(value = Role.class, names = {"DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE", "APPLICATION_ADD", "APPLICATION_CANCEL", "APPLICATION_CANCELLATION_REQUESTED", "SICK_NOTE_VIEW", "SICK_NOTE_ADD", "SICK_NOTE_EDIT", "SICK_NOTE_CANCEL", "SICK_NOTE_COMMENT"})
+    @EnumSource(value = Role.class, names = {"DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "ADMIN", "INACTIVE", "APPLICATION_ADD", "APPLICATION_CANCEL", "APPLICATION_CANCELLATION_REQUESTED", "SICK_NOTE_VIEW", "SICK_NOTE_ADD", "SICK_NOTE_EDIT", "SICK_NOTE_CANCEL", "SICK_NOTE_COMMENT"})
     void ensureNOTIFICATION_EMAIL_PERSON_NEW_MANAGEMENT_ALL_IsNotValidWithRole(Role role) {
         assertThat(NOTIFICATION_EMAIL_PERSON_NEW_MANAGEMENT_ALL.isValidWith(List.of(USER, role))).isFalse();
     }
@@ -315,7 +315,7 @@ class MailNotificationTest {
     }
 
     @ParameterizedTest
-    @EnumSource(value = Role.class, names = {"INACTIVE", "APPLICATION_ADD", "APPLICATION_CANCEL", "APPLICATION_CANCELLATION_REQUESTED", "SICK_NOTE_VIEW", "SICK_NOTE_ADD", "SICK_NOTE_EDIT", "SICK_NOTE_CANCEL", "SICK_NOTE_COMMENT"})
+    @EnumSource(value = Role.class, names = {"ADMIN", "INACTIVE", "APPLICATION_ADD", "APPLICATION_CANCEL", "APPLICATION_CANCELLATION_REQUESTED", "SICK_NOTE_VIEW", "SICK_NOTE_ADD", "SICK_NOTE_EDIT", "SICK_NOTE_CANCEL", "SICK_NOTE_COMMENT"})
     void ensureNOTIFICATION_EMAIL_OVERTIME_MANAGEMENT_APPLIED_IsNotValidWithRole(Role role) {
         assertThat(NOTIFICATION_EMAIL_OVERTIME_MANAGEMENT_APPLIED.isValidWith(List.of(USER, role))).isFalse();
     }
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/person/api/PersonApiControllerSecurityIT.java b/src/test/java/org/synyx/urlaubsverwaltung/person/api/PersonApiControllerSecurityIT.java
index 65355eb6d..20161baaa 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/person/api/PersonApiControllerSecurityIT.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/person/api/PersonApiControllerSecurityIT.java
@@ -68,7 +68,7 @@ class PersonApiControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "INACTIVE"})
+    @ValueSource(strings = {"USER", "ADMIN", "INACTIVE"})
     void ensureAccessIsForbiddenForOtherUsersOnSpecificPerson(final String role) throws Exception {
         perform(get("/api/persons/1")
                 .with(oidcLogin().authorities(new SimpleGrantedAuthority("USER"), new SimpleGrantedAuthority(role)))
@@ -157,7 +157,7 @@ class PersonApiControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE"})
+    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "ADMIN", "INACTIVE"})
     void ensureAccessIsForbiddenForOtherOnPersonsUsers(final String role) throws Exception {
         perform(get("/api/persons")
             .with(oidcLogin().authorities(new SimpleGrantedAuthority("USER"), new SimpleGrantedAuthority(role)))
@@ -181,7 +181,7 @@ class PersonApiControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "BOSS", "INACTIVE"})
+    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "BOSS", "ADMIN", "INACTIVE"})
     void ensureAccessIsForbiddenForUserWithoutRolePersonAdd(final String role) throws Exception {
         perform(post("/api/persons")
             .with(oidcLogin().authorities(new SimpleGrantedAuthority("USER"), new SimpleGrantedAuthority(role)))
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/person/basedata/PersonBasedataViewControllerSecurityIT.java b/src/test/java/org/synyx/urlaubsverwaltung/person/basedata/PersonBasedataViewControllerSecurityIT.java
index 4f33ee8cb..07d7c279f 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/person/basedata/PersonBasedataViewControllerSecurityIT.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/person/basedata/PersonBasedataViewControllerSecurityIT.java
@@ -48,7 +48,7 @@ class PersonBasedataViewControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "BOSS", "INACTIVE", "USER"})
+    @ValueSource(strings = {"DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "BOSS", "ADMIN", "INACTIVE", "USER"})
     void ensuresAuthorizedPersonWithIncorrectRoleCannotAccess(final String role) throws Exception {
         perform(get("/web/person/1/basedata")
             .with(oidcLogin().authorities(new SimpleGrantedAuthority("USER"), new SimpleGrantedAuthority(role))))
@@ -84,7 +84,7 @@ class PersonBasedataViewControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "BOSS", "INACTIVE", "USER"})
+    @ValueSource(strings = {"DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "BOSS", "ADMIN", "INACTIVE", "USER"})
     void ensuresAuthorizedPersonWithoutRoleCannotPost(final String role) throws Exception {
         perform(
             post("/web/person/1/basedata")
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/person/web/PersonNotificationsViewControllerSecurityIT.java b/src/test/java/org/synyx/urlaubsverwaltung/person/web/PersonNotificationsViewControllerSecurityIT.java
index 30aee1a0c..22c775053 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/person/web/PersonNotificationsViewControllerSecurityIT.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/person/web/PersonNotificationsViewControllerSecurityIT.java
@@ -96,7 +96,7 @@ class PersonNotificationsViewControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE"})
+    @ValueSource(strings = {"DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "ADMIN", "INACTIVE"})
     void personNotificationIsForbidden(final String role) throws Exception {
         perform(
             get("/web/person/1/notifications")
@@ -196,7 +196,7 @@ class PersonNotificationsViewControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE"})
+    @ValueSource(strings = {"DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "ADMIN", "INACTIVE"})
     void personChangeNotificationIsForbidden(final String role) throws Exception {
         perform(
             post("/web/person/1/notifications")
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsMapperTest.java b/src/test/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsMapperTest.java
index 10d12fee9..13e01fafe 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsMapperTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/person/web/PersonPermissionsMapperTest.java
@@ -24,6 +24,7 @@ class PersonPermissionsMapperTest {
             arguments(PersonPermissionsRoleDto.SECOND_STAGE_AUTHORITY, List.of(Role.SECOND_STAGE_AUTHORITY)),
             arguments(PersonPermissionsRoleDto.BOSS, List.of(Role.BOSS)),
             arguments(PersonPermissionsRoleDto.OFFICE, List.of(Role.OFFICE)),
+            arguments(PersonPermissionsRoleDto.ADMIN, List.of(Role.ADMIN)),
             arguments(PersonPermissionsRoleDto.INACTIVE, List.of(Role.INACTIVE)),
             arguments(PersonPermissionsRoleDto.SICK_NOTE_VIEW_ADD_EDIT, List.of(
                 Role.SICK_NOTE_VIEW,
@@ -62,6 +63,7 @@ class PersonPermissionsMapperTest {
             arguments(Role.SECOND_STAGE_AUTHORITY, List.of(PersonPermissionsRoleDto.SECOND_STAGE_AUTHORITY)),
             arguments(Role.BOSS, List.of(PersonPermissionsRoleDto.BOSS)),
             arguments(Role.OFFICE, List.of(PersonPermissionsRoleDto.OFFICE)),
+            arguments(Role.ADMIN, List.of(PersonPermissionsRoleDto.ADMIN)),
             arguments(Role.INACTIVE, List.of(PersonPermissionsRoleDto.INACTIVE)),
             arguments(Role.SICK_NOTE_VIEW, List.of(PersonPermissionsRoleDto.SICK_NOTE_VIEW_ADD_EDIT)),
             arguments(Role.SICK_NOTE_ADD, List.of(PersonPermissionsRoleDto.SICK_NOTE_VIEW_ADD_EDIT)),
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/publicholiday/PublicHolidayApiControllerSecurityIT.java b/src/test/java/org/synyx/urlaubsverwaltung/publicholiday/PublicHolidayApiControllerSecurityIT.java
index 0646fc617..6d1c6c510 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/publicholiday/PublicHolidayApiControllerSecurityIT.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/publicholiday/PublicHolidayApiControllerSecurityIT.java
@@ -58,7 +58,7 @@ class PublicHolidayApiControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "BOSS", "OFFICE", "INACTIVE"})
+    @ValueSource(strings = {"USER", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "BOSS", "OFFICE", "ADMIN", "INACTIVE"})
     void ensureGetHolidaysForOtherUserIsOk(final String role) throws Exception {
         perform(
             get("/api/public-holidays")
@@ -78,7 +78,7 @@ class PublicHolidayApiControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "INACTIVE"})
+    @ValueSource(strings = {"USER", "ADMIN", "INACTIVE"})
     void ensuresPersonsPublicHolidaysForOtherUserIsForbidden(final String role) throws Exception {
         perform(
             get("/api/persons/1/public-holidays")
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 b05d96a2b..738cad414 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromGroupsClaimMapperTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/security/oidc/RolesFromGroupsClaimMapperTest.java
@@ -3,7 +3,6 @@ package org.synyx.urlaubsverwaltung.security.oidc;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.core.GrantedAuthority;
 
 import java.util.List;
@@ -95,7 +94,7 @@ class RolesFromGroupsClaimMapperTest {
         );
 
         assertThatThrownBy(() -> sut.mapClaimToRoles(claims))
-            .isInstanceOf(AccessDeniedException.class)
-            .hasMessageContaining("claim=groups is missing");
+            .isInstanceOf(MissingClaimAuthorityException.class)
+            .hasMessageContaining("User has not required permission 'urlaubsverwaltung_user' to access urlaubsverwaltung! The claim 'groups' is missing!");
     }
 }
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/sicknote/sicknote/SickNoteApiControllerSecurityIT.java b/src/test/java/org/synyx/urlaubsverwaltung/sicknote/sicknote/SickNoteApiControllerSecurityIT.java
index 4f978011b..fecd875de 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/sicknote/sicknote/SickNoteApiControllerSecurityIT.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/sicknote/sicknote/SickNoteApiControllerSecurityIT.java
@@ -56,7 +56,7 @@ class SickNoteApiControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "BOSS", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE"})
+    @ValueSource(strings = {"USER", "BOSS", "DEPARTMENT_HEAD", "SECOND_STAGE_AUTHORITY", "INACTIVE", "ADMIN"})
     void getSicknotesIsForbidden(final String role) throws Exception {
         final LocalDateTime now = LocalDateTime.now();
         perform(
@@ -140,7 +140,7 @@ class SickNoteApiControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "INACTIVE"})
+    @ValueSource(strings = {"USER", "ADMIN", "INACTIVE"})
     void personsSickNotesIsForbidden(final String role) throws Exception {
         LocalDateTime now = LocalDateTime.now();
         perform(
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/sicknote/sicknote/SickNoteValidatorTest.java b/src/test/java/org/synyx/urlaubsverwaltung/sicknote/sicknote/SickNoteValidatorTest.java
index 08ac7e281..a10e81bd0 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/sicknote/sicknote/SickNoteValidatorTest.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/sicknote/sicknote/SickNoteValidatorTest.java
@@ -34,6 +34,7 @@ import static org.synyx.urlaubsverwaltung.overlap.OverlapCase.NO_OVERLAPPING;
 import static org.synyx.urlaubsverwaltung.period.DayLength.FULL;
 import static org.synyx.urlaubsverwaltung.period.DayLength.MORNING;
 import static org.synyx.urlaubsverwaltung.period.DayLength.NOON;
+import static org.synyx.urlaubsverwaltung.person.Role.ADMIN;
 import static org.synyx.urlaubsverwaltung.person.Role.BOSS;
 import static org.synyx.urlaubsverwaltung.person.Role.DEPARTMENT_HEAD;
 import static org.synyx.urlaubsverwaltung.person.Role.OFFICE;
@@ -83,7 +84,7 @@ class SickNoteValidatorTest {
         final Person person = new Person("muster", "Muster", "Marlene", "muster@example.org");
 
         final Person applier = new Person("dh", "department", "head", "department@example.org");
-        applier.setPermissions(List.of(USER));
+        applier.setPermissions(List.of(USER, ADMIN));
 
         final SickNote sickNote = SickNote.builder()
             .person(person)
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/vacations/VacationApiControllerSecurityIT.java b/src/test/java/org/synyx/urlaubsverwaltung/vacations/VacationApiControllerSecurityIT.java
index 9ba2f2e1b..bb37212ba 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/vacations/VacationApiControllerSecurityIT.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/vacations/VacationApiControllerSecurityIT.java
@@ -114,7 +114,7 @@ class VacationApiControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "INACTIVE"})
+    @ValueSource(strings = {"USER", "ADMIN", "INACTIVE"})
     void getVacationsForOtherUserIsForbidden(final String role) throws Exception {
         final LocalDateTime now = LocalDateTime.now();
 
@@ -299,7 +299,7 @@ class VacationApiControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"INACTIVE"})
+    @ValueSource(strings = {"ADMIN", "INACTIVE"})
     void getVacationsOfDepartmentMembersAsAdminUserForOtherUserIsForbidden(final String role) throws Exception {
         final LocalDateTime now = LocalDateTime.now();
 
diff --git a/src/test/java/org/synyx/urlaubsverwaltung/workingtime/WorkingTimeCalendarApiControllerSecurityIT.java b/src/test/java/org/synyx/urlaubsverwaltung/workingtime/WorkingTimeCalendarApiControllerSecurityIT.java
index 1d01f23b6..5e9219ec6 100644
--- a/src/test/java/org/synyx/urlaubsverwaltung/workingtime/WorkingTimeCalendarApiControllerSecurityIT.java
+++ b/src/test/java/org/synyx/urlaubsverwaltung/workingtime/WorkingTimeCalendarApiControllerSecurityIT.java
@@ -98,7 +98,7 @@ class WorkingTimeCalendarApiControllerSecurityIT extends TestContainersBase {
     }
 
     @ParameterizedTest
-    @ValueSource(strings = {"USER", "INACTIVE"})
+    @ValueSource(strings = {"USER", "ADMIN", "INACTIVE"})
     void getWorkdaysIsForbiddenFor(final String role) throws Exception {
         when(workDaysCountService.getWorkDaysCount(any(), any(), any(), any())).thenReturn(BigDecimal.ONE);