@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.audit.AuditLog ;
import org.thingsboard.server.common.data.exception.ThingsboardException ;
import org.thingsboard.server.common.data.id.TenantId ;
import org.thingsboard.server.common.data.notification.targets.platform.TenantAdministratorsFilter ;
import org.thingsboard.server.common.data.page.PageLink ;
import org.thingsboard.server.common.data.page.SortOrder ;
import org.thingsboard.server.common.data.page.TimePageLink ;
@ -59,6 +60,7 @@ import java.time.Duration;
import java.util.Arrays ;
import java.util.List ;
import java.util.Map ;
import java.util.Set ;
import java.util.concurrent.TimeUnit ;
import java.util.function.Consumer ;
import java.util.stream.Collectors ;
@ -67,10 +69,6 @@ import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat ;
import static org.awaitility.Awaitility.await ;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow ;
import static org.junit.jupiter.api.Assertions.assertEquals ;
import static org.junit.jupiter.api.Assertions.assertNotNull ;
import static org.junit.jupiter.api.Assertions.assertNull ;
import static org.junit.jupiter.api.Assertions.assertTrue ;
import static org.junit.jupiter.api.Assertions.fail ;
import static org.mockito.ArgumentMatchers.any ;
import static org.mockito.ArgumentMatchers.eq ;
@ -121,7 +119,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
public void testTwoFa_totp ( ) throws Exception {
TotpTwoFaAccountConfig totpTwoFaAccountConfig = configureTotpTwoFa ( ) ;
logInWithPreVerification Token ( username , password ) ;
logInWithMfa Token ( username , password , Authority . PRE_VERIFICATION_TOKEN ) ;
doPost ( "/api/auth/2fa/verification/send?providerType=TOTP" )
. andExpect ( status ( ) . isOk ( ) ) ;
@ -141,7 +139,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
public void testTwoFa_sms ( ) throws Exception {
configureSmsTwoFa ( ) ;
logInWithPreVerification Token ( username , password ) ;
logInWithMfa Token ( username , password , Authority . PRE_VERIFICATION_TOKEN ) ;
doPost ( "/api/auth/2fa/verification/send?providerType=SMS" )
. andExpect ( status ( ) . isOk ( ) ) ;
@ -165,7 +163,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
twoFaSettings . setTotalAllowedTimeForVerification ( 65 ) ;
} ) ;
logInWithPreVerification Token ( username , password ) ;
logInWithMfa Token ( username , password , Authority . PRE_VERIFICATION_TOKEN ) ;
await ( "expiration of the pre-verification token" )
. atLeast ( Duration . ofSeconds ( 30 ) . plusMillis ( 500 ) )
@ -182,7 +180,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
twoFaSettings . setMaxVerificationFailuresBeforeUserLockout ( 10 ) ;
} ) ;
logInWithPreVerification Token ( username , password ) ;
logInWithMfa Token ( username , password , Authority . PRE_VERIFICATION_TOKEN ) ;
Stream . generate ( ( ) - > StringUtils . randomNumeric ( 6 ) )
. limit ( 9 )
@ -211,7 +209,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
twoFaSettings . setMinVerificationCodeSendPeriod ( 10 ) ;
} ) ;
logInWithPreVerification Token ( username , password ) ;
logInWithMfa Token ( username , password , Authority . PRE_VERIFICATION_TOKEN ) ;
doPost ( "/api/auth/2fa/verification/send?providerType=TOTP" )
. andExpect ( status ( ) . isOk ( ) ) ;
@ -235,7 +233,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
twoFaSettings . setVerificationCodeCheckRateLimit ( "3:10" ) ;
} ) ;
logInWithPreVerification Token ( username , password ) ;
logInWithMfa Token ( username , password , Authority . PRE_VERIFICATION_TOKEN ) ;
for ( int i = 0 ; i < 3 ; i + + ) {
String incorrectVerificationCodeError = getErrorMessage ( doPost ( "/api/auth/2fa/verification/check?providerType=TOTP&verificationCode=incorrect" )
@ -263,7 +261,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
@Test
public void testCheckVerificationCode_invalidVerificationCode ( ) throws Exception {
configureTotpTwoFa ( ) ;
logInWithPreVerification Token ( username , password ) ;
logInWithMfa Token ( username , password , Authority . PRE_VERIFICATION_TOKEN ) ;
for ( String invalidVerificationCode : new String [ ] { "1234567" , "ab1212" , "12311 " , "oewkriwejqf" } ) {
String errorMessage = getErrorMessage ( doPost ( "/api/auth/2fa/verification/check?providerType=TOTP&verificationCode=" + invalidVerificationCode )
@ -278,7 +276,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
smsTwoFaProviderConfig . setVerificationCodeLifetime ( 10 ) ;
} ) ;
logInWithPreVerification Token ( username , password ) ;
logInWithMfa Token ( username , password , Authority . PRE_VERIFICATION_TOKEN ) ;
ArgumentCaptor < String > verificationCodeCaptor = ArgumentCaptor . forClass ( String . class ) ;
doPost ( "/api/auth/2fa/verification/send?providerType=SMS" ) . andExpect ( status ( ) . isOk ( ) ) ;
@ -301,7 +299,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
public void testTwoFa_logLoginAction ( ) throws Exception {
TotpTwoFaAccountConfig totpTwoFaAccountConfig = configureTotpTwoFa ( ) ;
logInWithPreVerification Token ( username , password ) ;
logInWithMfa Token ( username , password , Authority . PRE_VERIFICATION_TOKEN ) ;
await ( "async audit log saving" ) . during ( 1 , TimeUnit . SECONDS ) ;
doPost ( "/api/auth/2fa/verification/check?providerType=TOTP&verificationCode=incorrect" )
@ -383,7 +381,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
emailTwoFaAccountConfig . setEmail ( twoFaUser . getEmail ( ) ) ;
twoFaConfigManager . saveTwoFaAccountConfig ( tenantId , twoFaUser . getId ( ) , emailTwoFaAccountConfig ) ;
logInWithPreVerification Token ( twoFaUser . getEmail ( ) , "12345678" ) ;
logInWithMfa Token ( twoFaUser . getEmail ( ) , "12345678" , Authority . PRE_VERIFICATION_TOKEN ) ;
Map < TwoFaProviderType , TwoFactorAuthController . TwoFaProviderInfo > providersInfos = readResponse ( doGet ( "/api/auth/2fa/providers" ) . andExpect ( status ( ) . isOk ( ) ) , new TypeReference < List < TwoFactorAuthController . TwoFaProviderInfo > > ( ) { } ) . stream ( )
. collect ( Collectors . toMap ( TwoFactorAuthController . TwoFaProviderInfo : : getType , v - > v ) ) ;
@ -401,7 +399,7 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
}
@Test
public void testEnforceTwoFactorSetting ( ) throws Exception {
public void testEnforceTwoFa ( ) throws Exception {
TotpTwoFaProviderConfig totpTwoFaProviderConfig = new TotpTwoFaProviderConfig ( ) ;
totpTwoFaProviderConfig . setIssuerName ( "tb" ) ;
@ -410,14 +408,13 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
twoFaSettings . setMinVerificationCodeSendPeriod ( 5 ) ;
twoFaSettings . setTotalAllowedTimeForVerification ( 100 ) ;
twoFaSettings . setEnforceTwoFa ( true ) ;
TenantAdministratorsFilter enforcedUsersFilter = new TenantAdministratorsFilter ( ) ;
enforcedUsersFilter . setTenantsIds ( Set . of ( tenantId . getId ( ) ) ) ;
twoFaSettings . setEnforcedUsersFilter ( enforcedUsersFilter ) ;
twoFaSettings = twoFaConfigManager . savePlatformTwoFaSettings ( TenantId . SYS_TENANT_ID , twoFaSettings ) ;
JsonNode node = readResponse ( doPost ( "/api/auth/login" , new LoginRequest ( username , password ) ) . andExpect ( status ( ) . isOk ( ) ) , JsonNode . class ) ;
assertNotNull ( node . get ( "token" ) . asText ( ) ) ;
assertNull ( node . get ( "refreshToken" ) ) ;
assertEquals ( node . get ( "scope" ) . asText ( ) , Authority . ENFORCE_MFA_TOKEN . name ( ) ) ;
logInWithMfaToken ( username , password , Authority . MFA_CONFIGURATION_TOKEN ) ;
this . token = node . get ( "token" ) . asText ( ) ;
TotpTwoFaAccountConfig totpTwoFaAccountConfig = ( TotpTwoFaAccountConfig ) twoFactorAuthService . generateNewAccountConfig ( user , totpTwoFaProviderConfig . getProviderType ( ) ) ;
String secret = UriComponentsBuilder . fromUriString ( totpTwoFaAccountConfig . getAuthUrl ( ) ) . build ( )
. getQueryParams ( ) . getFirst ( "secret" ) ;
@ -425,24 +422,27 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
readResponse ( doPost ( "/api/2fa/account/config?verificationCode=" + verificationCode , totpTwoFaAccountConfig ) . andExpect ( status ( ) . isOk ( ) ) , JsonNode . class ) ;
JwtPair tokenPair = readResponse ( doPost ( "/api/auth/2fa/login" ) . andExpect ( status ( ) . isOk ( ) ) , JwtPair . class ) ;
assertNotNull ( tokenPair ) ;
assertThat ( tokenPair . getToken ( ) ) . isNotEmpty ( ) ;
assertThat ( tokenPair . getRefreshToken ( ) ) . isNotEmpty ( ) ;
validateAndSetJwtToken ( tokenPair , username ) ;
this . token = tokenPair . getToken ( ) ;
this . refreshToken = tokenPair . getRefreshToken ( ) ;
doGet ( "/api/user/" + user . getId ( ) ) . andExpect ( status ( ) . isOk ( ) ) ;
// verifying enforced users filter
createDifferentTenant ( ) ;
doGet ( "/api/user/" + user . getId ( ) ) . andExpect ( status ( ) . isOk ( ) ) ;
twoFaSettings . setEnforceTwoFa ( false ) ;
twoFaConfigManager . savePlatformTwoFaSettings ( TenantId . SYS_TENANT_ID , twoFaSettings ) ;
}
private void logInWithPreVerification Token ( String username , String password ) throws Exception {
private void logInWithMfa Token ( String username , String password , Authority expectedScope ) throws Exception {
LoginRequest loginRequest = new LoginRequest ( username , password ) ;
JwtPair response = readResponse ( doPost ( "/api/auth/login" , loginRequest ) . andExpect ( status ( ) . isOk ( ) ) , JwtPair . class ) ;
assertThat ( response . getToken ( ) ) . isNotNull ( ) ;
assertThat ( response . getRefreshToken ( ) ) . isNull ( ) ;
assertThat ( response . getScope ( ) ) . isEqualTo ( Authority . PRE_VERIFICATION_TOKEN ) ;
assertThat ( response . getScope ( ) ) . isEqualTo ( expectedScope ) ;
this . token = response . getToken ( ) ;
}