Improvements for 2FA
This commit is contained in:
parent
67e969fd75
commit
eeb7dc2338
@ -258,9 +258,9 @@ public class TwoFaConfigController extends BaseController {
|
|||||||
ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
|
ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
|
||||||
@PostMapping("/settings")
|
@PostMapping("/settings")
|
||||||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
|
||||||
public void savePlatformTwoFaSettings(@ApiParam(value = "Settings value", required = true)
|
public PlatformTwoFaSettings savePlatformTwoFaSettings(@ApiParam(value = "Settings value", required = true)
|
||||||
@RequestBody PlatformTwoFaSettings twoFaSettings) throws ThingsboardException {
|
@RequestBody PlatformTwoFaSettings twoFaSettings) throws ThingsboardException {
|
||||||
twoFaConfigManager.savePlatformTwoFaSettings(getTenantId(), twoFaSettings);
|
return twoFaConfigManager.savePlatformTwoFaSettings(getTenantId(), twoFaSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -99,6 +99,9 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager {
|
|||||||
return newSettings;
|
return newSettings;
|
||||||
});
|
});
|
||||||
Map<TwoFaProviderType, TwoFaAccountConfig> configs = settings.getConfigs();
|
Map<TwoFaProviderType, TwoFaAccountConfig> configs = settings.getConfigs();
|
||||||
|
if (configs.isEmpty() && accountConfig.getProviderType() == TwoFaProviderType.BACKUP_CODE) {
|
||||||
|
throw new IllegalArgumentException("To use 2FA backup codes you first need to configure at least one provider");
|
||||||
|
}
|
||||||
if (accountConfig.isUseByDefault()) {
|
if (accountConfig.isUseByDefault()) {
|
||||||
configs.values().forEach(config -> config.setUseByDefault(false));
|
configs.values().forEach(config -> config.setUseByDefault(false));
|
||||||
}
|
}
|
||||||
@ -114,7 +117,11 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager {
|
|||||||
AccountTwoFaSettings settings = getAccountTwoFaSettings(tenantId, userId)
|
AccountTwoFaSettings settings = getAccountTwoFaSettings(tenantId, userId)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("2FA not configured"));
|
.orElseThrow(() -> new IllegalArgumentException("2FA not configured"));
|
||||||
settings.getConfigs().remove(providerType);
|
settings.getConfigs().remove(providerType);
|
||||||
if (!settings.getConfigs().isEmpty() && settings.getConfigs().values().stream().noneMatch(TwoFaAccountConfig::isUseByDefault)) {
|
if (settings.getConfigs().size() == 1) {
|
||||||
|
settings.getConfigs().remove(TwoFaProviderType.BACKUP_CODE);
|
||||||
|
}
|
||||||
|
if (!settings.getConfigs().isEmpty() && settings.getConfigs().values().stream()
|
||||||
|
.noneMatch(TwoFaAccountConfig::isUseByDefault)) {
|
||||||
settings.getConfigs().values().stream()
|
settings.getConfigs().values().stream()
|
||||||
.min(Comparator.comparing(TwoFaAccountConfig::getProviderType))
|
.min(Comparator.comparing(TwoFaAccountConfig::getProviderType))
|
||||||
.ifPresent(config -> config.setUseByDefault(true));
|
.ifPresent(config -> config.setUseByDefault(true));
|
||||||
@ -135,7 +142,7 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void savePlatformTwoFaSettings(TenantId tenantId, PlatformTwoFaSettings twoFactorAuthSettings) throws ThingsboardException {
|
public PlatformTwoFaSettings savePlatformTwoFaSettings(TenantId tenantId, PlatformTwoFaSettings twoFactorAuthSettings) throws ThingsboardException {
|
||||||
ConstraintValidator.validateFields(twoFactorAuthSettings);
|
ConstraintValidator.validateFields(twoFactorAuthSettings);
|
||||||
for (TwoFaProviderConfig providerConfig : twoFactorAuthSettings.getProviders()) {
|
for (TwoFaProviderConfig providerConfig : twoFactorAuthSettings.getProviders()) {
|
||||||
twoFactorAuthService.checkProvider(tenantId, providerConfig.getProviderType());
|
twoFactorAuthService.checkProvider(tenantId, providerConfig.getProviderType());
|
||||||
@ -149,6 +156,7 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager {
|
|||||||
});
|
});
|
||||||
settings.setJsonValue(JacksonUtil.valueToTree(twoFactorAuthSettings));
|
settings.setJsonValue(JacksonUtil.valueToTree(twoFactorAuthSettings));
|
||||||
adminSettingsService.saveAdminSettings(tenantId, settings);
|
adminSettingsService.saveAdminSettings(tenantId, settings);
|
||||||
|
return twoFactorAuthSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -39,7 +39,7 @@ public interface TwoFaConfigManager {
|
|||||||
|
|
||||||
Optional<PlatformTwoFaSettings> getPlatformTwoFaSettings(TenantId tenantId, boolean sysadminSettingsAsDefault);
|
Optional<PlatformTwoFaSettings> getPlatformTwoFaSettings(TenantId tenantId, boolean sysadminSettingsAsDefault);
|
||||||
|
|
||||||
void savePlatformTwoFaSettings(TenantId tenantId, PlatformTwoFaSettings twoFactorAuthSettings) throws ThingsboardException;
|
PlatformTwoFaSettings savePlatformTwoFaSettings(TenantId tenantId, PlatformTwoFaSettings twoFactorAuthSettings) throws ThingsboardException;
|
||||||
|
|
||||||
void deletePlatformTwoFaSettings(TenantId tenantId);
|
void deletePlatformTwoFaSettings(TenantId tenantId);
|
||||||
|
|
||||||
|
|||||||
@ -55,7 +55,9 @@ public class RestAwareAuthenticationSuccessHandler implements AuthenticationSucc
|
|||||||
|
|
||||||
if (authentication instanceof MfaAuthenticationToken) {
|
if (authentication instanceof MfaAuthenticationToken) {
|
||||||
int preVerificationTokenLifetime = twoFaConfigManager.getPlatformTwoFaSettings(securityUser.getTenantId(), true)
|
int preVerificationTokenLifetime = twoFaConfigManager.getPlatformTwoFaSettings(securityUser.getTenantId(), true)
|
||||||
.flatMap(settings -> Optional.ofNullable(settings.getTotalAllowedTimeForVerification())).orElse((int) TimeUnit.MINUTES.toSeconds(30));
|
.flatMap(settings -> Optional.ofNullable(settings.getTotalAllowedTimeForVerification())
|
||||||
|
.filter(time -> time > 0))
|
||||||
|
.orElse((int) TimeUnit.MINUTES.toSeconds(30));
|
||||||
tokenPair.setToken(tokenFactory.createPreVerificationToken(securityUser, preVerificationTokenLifetime).getToken());
|
tokenPair.setToken(tokenFactory.createPreVerificationToken(securityUser, preVerificationTokenLifetime).getToken());
|
||||||
tokenPair.setRefreshToken(null);
|
tokenPair.setRefreshToken(null);
|
||||||
tokenPair.setScope(Authority.PRE_VERIFICATION_TOKEN);
|
tokenPair.setScope(Authority.PRE_VERIFICATION_TOKEN);
|
||||||
|
|||||||
@ -172,11 +172,12 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (twoFaSettings.getMaxVerificationFailuresBeforeUserLockout() > 0
|
Integer maxVerificationFailures = twoFaSettings.getMaxVerificationFailuresBeforeUserLockout();
|
||||||
&& failedVerificationAttempts >= twoFaSettings.getMaxVerificationFailuresBeforeUserLockout()) {
|
if (maxVerificationFailures != null && maxVerificationFailures > 0
|
||||||
|
&& failedVerificationAttempts >= maxVerificationFailures) {
|
||||||
userService.setUserCredentialsEnabled(TenantId.SYS_TENANT_ID, userId, false);
|
userService.setUserCredentialsEnabled(TenantId.SYS_TENANT_ID, userId, false);
|
||||||
SecuritySettings securitySettings = self.getSecuritySettings(tenantId);
|
SecuritySettings securitySettings = self.getSecuritySettings(tenantId);
|
||||||
lockAccount(userId, securityUser.getEmail(), securitySettings.getUserLockoutNotificationEmail(), twoFaSettings.getMaxVerificationFailuresBeforeUserLockout());
|
lockAccount(userId, securityUser.getEmail(), securitySettings.getUserLockoutNotificationEmail(), maxVerificationFailures);
|
||||||
throw new LockedException("User account was locked due to exceeded 2FA verification attempts");
|
throw new LockedException("User account was locked due to exceeded 2FA verification attempts");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,7 +37,7 @@ public class PlatformTwoFaSettings {
|
|||||||
@Pattern(regexp = "[1-9]\\d*:[1-9]\\d*", message = "verification code check rate limit configuration is invalid")
|
@Pattern(regexp = "[1-9]\\d*:[1-9]\\d*", message = "verification code check rate limit configuration is invalid")
|
||||||
private String verificationCodeCheckRateLimit;
|
private String verificationCodeCheckRateLimit;
|
||||||
@Min(value = 0, message = "maximum number of verification failure before user lockout must be positive")
|
@Min(value = 0, message = "maximum number of verification failure before user lockout must be positive")
|
||||||
private int maxVerificationFailuresBeforeUserLockout;
|
private Integer maxVerificationFailuresBeforeUserLockout;
|
||||||
@Min(value = 1, message = "total amount of time allotted for verification must be greater than 0")
|
@Min(value = 1, message = "total amount of time allotted for verification must be greater than 0")
|
||||||
private Integer totalAllowedTimeForVerification;
|
private Integer totalAllowedTimeForVerification;
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import javax.validation.constraints.Min;
|
|||||||
@Data
|
@Data
|
||||||
public class BackupCodeTwoFaProviderConfig implements TwoFaProviderConfig {
|
public class BackupCodeTwoFaProviderConfig implements TwoFaProviderConfig {
|
||||||
|
|
||||||
@Min(0)
|
@Min(1)
|
||||||
private int codesQuantity;
|
private int codesQuantity;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user