-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
okr #914: tests for security package
- Loading branch information
1 parent
d2caa33
commit 67d2c12
Showing
6 changed files
with
386 additions
and
2 deletions.
There are no files selected for viewing
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
55 changes: 55 additions & 0 deletions
55
backend/src/test/java/ch/puzzle/okr/security/AuthenticationEventsTest.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,55 @@ | ||
package ch.puzzle.okr.security; | ||
|
||
import ch.puzzle.okr.multitenancy.TenantContext; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.oauth2.jwt.Jwt; | ||
|
||
import static ch.puzzle.okr.multitenancy.TenantContext.DEFAULT_TENANT_ID; | ||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.mockito.Mockito.*; | ||
|
||
public class AuthenticationEventsTest { | ||
|
||
public static final String TENANT_FROM_TOKEN = "pitc"; | ||
|
||
@DisplayName("onSuccess() puts Token from AuthenticationSuccessEvent in TenantContext") | ||
@Test | ||
void onSuccessPutsTokenFromAuthenticationSuccessEventInTenantContext() { | ||
// arrange | ||
Jwt tokenMock = mock(Jwt.class); | ||
|
||
AuthenticationSuccessEvent successEvent = new AuthenticationSuccessEvent(mock(Authentication.class)); | ||
when(successEvent.getAuthentication().getPrincipal()).thenReturn(tokenMock); | ||
|
||
JwtHelper jwtHelperMock = mock(JwtHelper.class); | ||
when(jwtHelperMock.getTenantFromToken(tokenMock)).thenReturn(TENANT_FROM_TOKEN); | ||
|
||
// pre-assert | ||
assertDefaultTenantIsInTenantContext(); | ||
|
||
// act | ||
AuthenticationEvents authenticationEvents = new AuthenticationEvents(jwtHelperMock); | ||
authenticationEvents.onSuccess(successEvent); | ||
|
||
// assert | ||
assertTenantFromTokenIsInTenantContext(); | ||
verifyGetTenantFromTokenIsCalledWithTokenFromAuthenticationSuccessEvent(jwtHelperMock, tokenMock); | ||
} | ||
|
||
private void assertDefaultTenantIsInTenantContext() { | ||
assertEquals(DEFAULT_TENANT_ID, TenantContext.getCurrentTenant()); | ||
} | ||
|
||
private void assertTenantFromTokenIsInTenantContext() { | ||
assertEquals(TENANT_FROM_TOKEN, TenantContext.getCurrentTenant()); | ||
} | ||
|
||
private void verifyGetTenantFromTokenIsCalledWithTokenFromAuthenticationSuccessEvent(JwtHelper jwtHelper, | ||
Jwt token) { | ||
verify(jwtHelper).getTenantFromToken(token); | ||
} | ||
|
||
} |
147 changes: 147 additions & 0 deletions
147
backend/src/test/java/ch/puzzle/okr/security/JwtHelperTest.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,147 @@ | ||
package ch.puzzle.okr.security; | ||
|
||
import ch.puzzle.okr.exception.OkrResponseStatusException; | ||
import ch.puzzle.okr.models.User; | ||
import ch.puzzle.okr.multitenancy.TenantConfigProvider; | ||
import com.nimbusds.jwt.JWTClaimsSet; | ||
import jakarta.persistence.EntityNotFoundException; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.security.oauth2.jwt.Jwt; | ||
|
||
import java.text.ParseException; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
import static org.springframework.http.HttpStatus.BAD_REQUEST; | ||
|
||
public class JwtHelperTest { | ||
|
||
private static final String TOKEN_CLAIMS_KEY_FIRSTNAME = "given_name"; | ||
private static final String TOKEN_CLAIMS_KEY_LASTNAME = "family_name"; | ||
private static final String TOKEN_CLAIMS_KEY_EMAIL = "email"; | ||
private static final String HANS = "Hans"; | ||
private static final String MUSTER = "Muster"; | ||
private static final String EMAIL = "[email protected]"; | ||
|
||
private static final String TOKEN_CLAIMS_KEY_TENANT = "tenant"; | ||
private static final String PITC = "pitc"; | ||
|
||
@DisplayName("getUserFromJwt() extracts User data from Token") | ||
@Test | ||
void getUserFromJwtExtractsUserDataFromToken() { | ||
// arrange | ||
Jwt tokenWithUserDataMock = mock(Jwt.class); | ||
when(tokenWithUserDataMock.getClaims()).thenReturn(Map.of( // | ||
TOKEN_CLAIMS_KEY_FIRSTNAME, HANS, // | ||
TOKEN_CLAIMS_KEY_LASTNAME, MUSTER, // | ||
TOKEN_CLAIMS_KEY_EMAIL, EMAIL // | ||
)); | ||
|
||
JwtHelper jwtHelper = new JwtHelper(null, // | ||
TOKEN_CLAIMS_KEY_FIRSTNAME, TOKEN_CLAIMS_KEY_LASTNAME, TOKEN_CLAIMS_KEY_EMAIL); | ||
|
||
// act | ||
User userFromToken = jwtHelper.getUserFromJwt(tokenWithUserDataMock); | ||
|
||
// assert | ||
assertEquals(HANS, userFromToken.getFirstname()); | ||
assertEquals(MUSTER, userFromToken.getLastname()); | ||
assertEquals(EMAIL, userFromToken.getEmail()); | ||
} | ||
|
||
@DisplayName("getUserFromJwt() throws Exception if Token not contains User data") | ||
@Test | ||
void getUserFromJwtThrowsExceptionIfTokenNotContainsUserData() { | ||
// arrange | ||
Jwt tokenWithNoUserDataMock = mock(Jwt.class); | ||
|
||
JwtHelper jwtHelper = new JwtHelper(null, // | ||
TOKEN_CLAIMS_KEY_FIRSTNAME, TOKEN_CLAIMS_KEY_LASTNAME, TOKEN_CLAIMS_KEY_EMAIL); | ||
|
||
// act + assert | ||
OkrResponseStatusException okrResponseStatusException = // | ||
assertThrows(OkrResponseStatusException.class, () -> jwtHelper.getUserFromJwt(tokenWithNoUserDataMock)); | ||
|
||
// assert | ||
assertEquals(BAD_REQUEST, okrResponseStatusException.getStatusCode()); | ||
} | ||
|
||
@DisplayName("getTenantFromToken() returns Tenant if Tenant found in TenantConfigProvider") | ||
@Test | ||
void getTenantFromTokenReturnsTenantIfTenantFoundInTenantConfigProvider() { | ||
// arrange | ||
Jwt tokenMock = mock(Jwt.class); | ||
when(tokenMock.getClaimAsString(TOKEN_CLAIMS_KEY_TENANT)).thenReturn(PITC); | ||
|
||
TenantConfigProvider tenantConfigProviderMock = mock(TenantConfigProvider.class); | ||
when(tenantConfigProviderMock.getTenantConfigById(PITC)).thenReturn(Optional.of( // | ||
new TenantConfigProvider.TenantConfig(PITC, // | ||
new String[] {}, "jwkSetUri", "issuerUrl", // | ||
"clientId", null) // | ||
)); | ||
|
||
JwtHelper jwtHelper = new JwtHelper(tenantConfigProviderMock, null, null, null); | ||
|
||
// act | ||
String tenantFromToken = jwtHelper.getTenantFromToken(tokenMock); | ||
|
||
// assert | ||
assertEquals(PITC, tenantFromToken); | ||
} | ||
|
||
@DisplayName("getTenantFromToken() throws Exception if Tenant not found in TenantConfigProvider") | ||
@Test | ||
void getTenantFromTokenThrowsExceptionIfTenantNotFoundInTenantConfigProvider() { | ||
// arrange | ||
Jwt tokenMock = mock(Jwt.class); | ||
when(tokenMock.getClaimAsString(TOKEN_CLAIMS_KEY_TENANT)).thenReturn(PITC); | ||
|
||
TenantConfigProvider emptyTenantConfigProviderMock = mock(TenantConfigProvider.class); | ||
|
||
JwtHelper jwtHelper = new JwtHelper(emptyTenantConfigProviderMock, null, null, null); | ||
|
||
// act + assert | ||
assertThrows(EntityNotFoundException.class, () -> jwtHelper.getTenantFromToken(tokenMock)); | ||
} | ||
|
||
@DisplayName("getTenantFromJWTClaimsSet() returns Tenant if Tenant found in TenantConfigProvider") | ||
@Test | ||
void getTenantFromJWTClaimsSetReturnsTenantIfTenantFoundInTenantConfigProvider() throws ParseException { | ||
// arrange | ||
JWTClaimsSet claimsSetMock = mock(JWTClaimsSet.class); | ||
when(claimsSetMock.getStringClaim(TOKEN_CLAIMS_KEY_TENANT)).thenReturn(PITC); | ||
|
||
TenantConfigProvider tenantConfigProviderWithDataMock = mock(TenantConfigProvider.class); | ||
when(tenantConfigProviderWithDataMock.getTenantConfigById(PITC)).thenReturn(Optional.of( // | ||
new TenantConfigProvider.TenantConfig(PITC, // | ||
new String[] {}, "jwkSetUri", "issuerUrl", // | ||
"clientId", null) // | ||
)); | ||
|
||
JwtHelper jwtHelper = new JwtHelper(tenantConfigProviderWithDataMock, null, null, null); | ||
|
||
// act | ||
String tenantFromToken = jwtHelper.getTenantFromJWTClaimsSet(claimsSetMock); | ||
|
||
// assert | ||
assertEquals(PITC, tenantFromToken); | ||
} | ||
|
||
@DisplayName("getTenantFromJWTClaimsSet() throws Exception if ClaimSet can not be parsed") | ||
@Test | ||
void getTenantFromJWTClaimsSetThrowsExceptionIfClaimSetCanNotBeParsed() throws ParseException { | ||
// arrange | ||
JWTClaimsSet claimsSetMock = mock(JWTClaimsSet.class); | ||
when(claimsSetMock.getStringClaim(TOKEN_CLAIMS_KEY_TENANT)).thenThrow(new ParseException("", 0)); | ||
|
||
JwtHelper jwtHelper = new JwtHelper(null, null, null, null); | ||
|
||
// act + assert | ||
assertThrows(RuntimeException.class, () -> jwtHelper.getTenantFromJWTClaimsSet(claimsSetMock)); | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
backend/src/test/java/ch/puzzle/okr/security/TenantJWSKeySelectorTest.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,99 @@ | ||
package ch.puzzle.okr.security; | ||
|
||
import ch.puzzle.okr.multitenancy.TenantConfigProvider; | ||
import com.nimbusds.jose.JWSHeader; | ||
import com.nimbusds.jose.KeySourceException; | ||
import com.nimbusds.jose.proc.JWSKeySelector; | ||
import com.nimbusds.jose.proc.SecurityContext; | ||
import com.nimbusds.jwt.JWTClaimsSet; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.security.Key; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import static org.junit.jupiter.api.Assertions.*; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
public class TenantJWSKeySelectorTest { | ||
private static final String PITC = "pitc"; | ||
private static final String JWK_SET_URI = "jwkSetUri"; | ||
private static final String MOCK_ALGORITHM = "mock_algorithm"; | ||
private static final String UNKNOWN_TENANT = "unknown tenant"; | ||
|
||
@DisplayName("selectKeys() throws Exception if JwkSetUri not found in TenantConfigProvider") | ||
@Test | ||
void selectKeysThrowsExceptionIfTenantConfigIsNotFound() { | ||
// arrange | ||
JWTClaimsSet jwtClaimsSetMock = mock(JWTClaimsSet.class); | ||
JWSHeader jwsHeaderMock = mock(JWSHeader.class); | ||
SecurityContext securityContext = new SecurityContext() { | ||
}; | ||
|
||
JwtHelper jwtHelperMock = mock(JwtHelper.class); | ||
when(jwtHelperMock.getTenantFromJWTClaimsSet(jwtClaimsSetMock)).thenReturn(PITC); | ||
|
||
TenantConfigProvider emptyTenantConfigProviderMock = mock(TenantConfigProvider.class); | ||
|
||
// act + assert | ||
TenantJWSKeySelector selector = new TenantJWSKeySelector(emptyTenantConfigProviderMock, jwtHelperMock); | ||
IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class, // | ||
() -> selector.selectKeys(jwsHeaderMock, jwtClaimsSetMock, securityContext)); | ||
|
||
assertEquals(UNKNOWN_TENANT, illegalArgumentException.getLocalizedMessage()); | ||
} | ||
|
||
@DisplayName("selectKeys() return Key with Mock Algorithm if JwkSetUri is found in TenantConfigProvider") | ||
@Test | ||
void selectKeysReturnKeyWithMockAlgorithmIfJwkSetUriIsFound() throws KeySourceException { | ||
// arrange | ||
JWTClaimsSet jwtClaimsSetMock = mock(JWTClaimsSet.class); | ||
JWSHeader jwsHeaderMock = mock(JWSHeader.class); | ||
SecurityContext securityContext = new SecurityContext() { | ||
}; | ||
|
||
JwtHelper jwtHelperMock = mock(JwtHelper.class); | ||
when(jwtHelperMock.getTenantFromJWTClaimsSet(jwtClaimsSetMock)).thenReturn(PITC); | ||
|
||
TenantConfigProvider tenantConfigProviderWithDataMock = mock(TenantConfigProvider.class); | ||
when(tenantConfigProviderWithDataMock.getJwkSetUri(PITC)).thenReturn(Optional.of(JWK_SET_URI)); | ||
|
||
TenantJWSKeySelector selector = new TenantJWSKeySelector(tenantConfigProviderWithDataMock, jwtHelperMock) { | ||
|
||
@Override | ||
JWSKeySelector<SecurityContext> fromUri(String uri) { | ||
return (jwsHeader, securityContext) -> List.of(new Key() { | ||
@Override | ||
public String getAlgorithm() { | ||
return MOCK_ALGORITHM; | ||
} | ||
|
||
@Override | ||
public String getFormat() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public byte[] getEncoded() { | ||
return new byte[0]; | ||
} | ||
}); | ||
} | ||
}; | ||
|
||
// act + assert | ||
List<? extends Key> keys = selector.selectKeys(jwsHeaderMock, jwtClaimsSetMock, securityContext); | ||
|
||
// assert | ||
assertListContainsSingleKeyWithMockAlgorithm(keys); | ||
} | ||
|
||
void assertListContainsSingleKeyWithMockAlgorithm(List<? extends Key> keys) { | ||
assertNotNull(keys); | ||
assertEquals(1, keys.size()); | ||
Key key = keys.get(0); | ||
assertEquals(MOCK_ALGORITHM, key.getAlgorithm()); | ||
} | ||
} |
Oops, something went wrong.