-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding Springs AuditorAware capabilities
- Loading branch information
1 parent
2844251
commit e7be0a2
Showing
5 changed files
with
153 additions
and
14 deletions.
There are no files selected for viewing
29 changes: 29 additions & 0 deletions
29
server/wfprev-api/src/main/java/ca/bc/gov/nrs/wfprev/SpringSecurityAuditorAware.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package ca.bc.gov.nrs.wfprev; | ||
|
||
import org.springframework.data.domain.AuditorAware; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.Optional; | ||
|
||
@Component | ||
public class SpringSecurityAuditorAware implements AuditorAware<String> { | ||
|
||
@Override | ||
public Optional<String> getCurrentAuditor() { | ||
return Optional.ofNullable(SecurityContextHolder.getContext()) | ||
.map(context -> context.getAuthentication()) | ||
.filter(Authentication::isAuthenticated) | ||
.map(authentication -> { | ||
Object principal = authentication.getPrincipal(); | ||
if (principal instanceof DefaultOAuth2AuthenticatedPrincipal) { | ||
// Extract username or preferred identifier | ||
DefaultOAuth2AuthenticatedPrincipal oauthPrincipal = (DefaultOAuth2AuthenticatedPrincipal) principal; | ||
return (String) oauthPrincipal.getAttribute("preferred_username"); // Adjust key to match your provider | ||
} | ||
throw new IllegalStateException("Principal is not of type DefaultOAuth2AuthenticatedPrincipal"); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
server/wfprev-api/src/test/java/ca/bc/gov/nrs/wfprev/SpringSecurityAuditorAwareTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package ca.bc.gov.nrs.wfprev; | ||
|
||
import org.junit.jupiter.api.AfterEach; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.mockito.MockedStatic; | ||
import org.mockito.Mockito; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.context.SecurityContext; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
import org.springframework.security.oauth2.core.DefaultOAuth2AuthenticatedPrincipal; | ||
|
||
import java.util.Optional; | ||
|
||
import static org.junit.jupiter.api.Assertions.*; | ||
import static org.mockito.Mockito.*; | ||
|
||
class SpringSecurityAuditorAwareTest { | ||
|
||
private SpringSecurityAuditorAware auditorAware; | ||
|
||
private MockedStatic<SecurityContextHolder> securityContextHolderMock; | ||
|
||
@BeforeEach | ||
void setUp() { | ||
auditorAware = new SpringSecurityAuditorAware(); | ||
securityContextHolderMock = Mockito.mockStatic(SecurityContextHolder.class); | ||
} | ||
|
||
@AfterEach | ||
void tearDown() { | ||
securityContextHolderMock.close(); | ||
} | ||
|
||
@Test | ||
void getCurrentAuditor_authenticatedUser_returnsUsername() { | ||
// Given: A valid SecurityContext with an authenticated user | ||
SecurityContext mockSecurityContext = mock(SecurityContext.class); | ||
Authentication mockAuthentication = mock(Authentication.class); | ||
DefaultOAuth2AuthenticatedPrincipal mockPrincipal = mock(DefaultOAuth2AuthenticatedPrincipal.class); | ||
|
||
when(SecurityContextHolder.getContext()).thenReturn(mockSecurityContext); | ||
when(mockSecurityContext.getAuthentication()).thenReturn(mockAuthentication); | ||
when(mockAuthentication.isAuthenticated()).thenReturn(true); | ||
when(mockAuthentication.getPrincipal()).thenReturn(mockPrincipal); | ||
when(mockPrincipal.getAttribute("preferred_username")).thenReturn("test_user"); | ||
|
||
// When: getCurrentAuditor is called | ||
Optional<String> result = auditorAware.getCurrentAuditor(); | ||
|
||
// Then: The correct username is returned | ||
assertEquals(Optional.of("test_user"), result); | ||
} | ||
|
||
@Test | ||
void getCurrentAuditor_userNotAuthenticated_returnsEmptyOptional() { | ||
// Given: A valid SecurityContext with an unauthenticated user | ||
SecurityContext mockSecurityContext = mock(SecurityContext.class); | ||
Authentication mockAuthentication = mock(Authentication.class); | ||
|
||
when(SecurityContextHolder.getContext()).thenReturn(mockSecurityContext); | ||
when(mockSecurityContext.getAuthentication()).thenReturn(mockAuthentication); | ||
when(mockAuthentication.isAuthenticated()).thenReturn(false); | ||
|
||
// When: getCurrentAuditor is called | ||
Optional<String> result = auditorAware.getCurrentAuditor(); | ||
|
||
// Then: An empty Optional is returned | ||
assertEquals(Optional.empty(), result); | ||
} | ||
|
||
@Test | ||
void getCurrentAuditor_invalidPrincipalType_throwsIllegalStateException() { | ||
// Given: A valid SecurityContext with an invalid principal type | ||
SecurityContext mockSecurityContext = mock(SecurityContext.class); | ||
Authentication mockAuthentication = mock(Authentication.class); | ||
|
||
when(SecurityContextHolder.getContext()).thenReturn(mockSecurityContext); | ||
when(mockSecurityContext.getAuthentication()).thenReturn(mockAuthentication); | ||
when(mockAuthentication.isAuthenticated()).thenReturn(true); | ||
when(mockAuthentication.getPrincipal()).thenReturn("InvalidPrincipal"); | ||
|
||
// When & Then: getCurrentAuditor throws an IllegalStateException | ||
IllegalStateException exception = assertThrows(IllegalStateException.class, auditorAware::getCurrentAuditor); | ||
assertEquals("Principal is not of type DefaultOAuth2AuthenticatedPrincipal", exception.getMessage()); | ||
} | ||
|
||
@Test | ||
void getCurrentAuditor_nullSecurityContext_returnsEmptyOptional() { | ||
// Given: A null SecurityContext | ||
when(SecurityContextHolder.getContext()).thenReturn(null); | ||
|
||
// When: getCurrentAuditor is called | ||
Optional<String> result = auditorAware.getCurrentAuditor(); | ||
|
||
// Then: An empty Optional is returned | ||
assertEquals(Optional.empty(), result); | ||
} | ||
|
||
@Test | ||
void getCurrentAuditor_nullAuthentication_returnsEmptyOptional() { | ||
// Given: A valid SecurityContext with a null Authentication | ||
SecurityContext mockSecurityContext = mock(SecurityContext.class); | ||
|
||
when(SecurityContextHolder.getContext()).thenReturn(mockSecurityContext); | ||
when(mockSecurityContext.getAuthentication()).thenReturn(null); | ||
|
||
// When: getCurrentAuditor is called | ||
Optional<String> result = auditorAware.getCurrentAuditor(); | ||
|
||
// Then: An empty Optional is returned | ||
assertEquals(Optional.empty(), result); | ||
} | ||
} |