Browse Source

Remove platform 2FA settings for tenant authority

pull/6235/head
Viacheslav Klimov 4 years ago
parent
commit
d3863de094
  1. 7
      application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java
  2. 62
      application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java
  3. 81
      application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java
  4. 7
      application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java
  5. 1
      common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java

7
application/src/main/java/org/thingsboard/server/controller/TwoFaConfigController.java

@ -206,15 +206,13 @@ public class TwoFaConfigController extends BaseController {
"If 2FA is not configured, then an empty response will be returned." +
ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@GetMapping("/settings")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
public PlatformTwoFaSettings getPlatformTwoFaSettings() throws ThingsboardException {
return twoFaConfigManager.getPlatformTwoFaSettings(getTenantId(), false).orElse(null);
}
@ApiOperation(value = "Save platform 2FA settings (savePlatformTwoFaSettings)",
notes = "Save 2FA settings for platform. The settings have following properties:\n" +
"- `useSystemTwoFactorAuthSettings` - option for tenant admins to use 2FA settings configured by sysadmin. " +
"If this param is set to true, then the settings will not be validated for constraints (if it is a tenant admin; for sysadmin this param is ignored).\n" +
"- `providers` - the list of 2FA providers' configs. Users will only be allowed to use 2FA providers from this list. \n\n" +
"- `verificationCodeSendRateLimit` - rate limit configuration for verification code sending. " +
"The format is standard: 'amountOfRequests:periodInSeconds'. The value of '1:60' would limit verification " +
@ -233,7 +231,6 @@ public class TwoFaConfigController extends BaseController {
"- `verificationCodeLifetime` - the same as for SMS." + NEW_LINE +
"Example of the settings:\n" +
"```\n{\n" +
" \"useSystemTwoFactorAuthSettings\": false,\n" +
" \"providers\": [\n" +
" {\n" +
" \"providerType\": \"TOTP\",\n" +
@ -256,7 +253,7 @@ public class TwoFaConfigController extends BaseController {
"}\n```" +
ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PostMapping("/settings")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
public void savePlatformTwoFaSettings(@ApiParam(value = "Settings value", required = true)
@RequestBody PlatformTwoFaSettings twoFaSettings) throws ThingsboardException {
twoFaConfigManager.savePlatformTwoFaSettings(getTenantId(), twoFaSettings);

62
application/src/main/java/org/thingsboard/server/service/security/auth/mfa/config/DefaultTwoFaConfigManager.java

@ -16,18 +16,14 @@
package org.thingsboard.server.service.security.auth.mfa.config;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.JsonDataEntry;
import org.thingsboard.server.common.data.security.UserAuthSettings;
import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings;
import org.thingsboard.server.common.data.security.model.mfa.account.AccountTwoFaSettings;
@ -41,11 +37,9 @@ import org.thingsboard.server.dao.settings.AdminSettingsService;
import org.thingsboard.server.dao.user.UserAuthSettingsDao;
import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
@Service
@RequiredArgsConstructor
@ -54,7 +48,6 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager {
private final UserAuthSettingsDao userAuthSettingsDao;
private final AdminSettingsService adminSettingsService;
private final AdminSettingsDao adminSettingsDao;
private final AttributesService attributesService;
@Autowired @Lazy
private TwoFactorAuthService twoFactorAuthService;
@ -132,60 +125,33 @@ public class DefaultTwoFaConfigManager implements TwoFaConfigManager {
.flatMap(twoFaSettings -> twoFaSettings.getProviderConfig(providerType));
}
@SneakyThrows({InterruptedException.class, ExecutionException.class})
@Override
public Optional<PlatformTwoFaSettings> getPlatformTwoFaSettings(TenantId tenantId, boolean sysadminSettingsAsDefault) {
if (tenantId.equals(TenantId.SYS_TENANT_ID)) {
return Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, TWO_FACTOR_AUTH_SETTINGS_KEY))
.map(adminSettings -> JacksonUtil.treeToValue(adminSettings.getJsonValue(), PlatformTwoFaSettings.class));
} else {
Optional<PlatformTwoFaSettings> tenantTwoFaSettings = attributesService.find(TenantId.SYS_TENANT_ID, tenantId,
DataConstants.SERVER_SCOPE, TWO_FACTOR_AUTH_SETTINGS_KEY).get()
.map(adminSettingsAttribute -> JacksonUtil.fromString(adminSettingsAttribute.getJsonValue().get(), PlatformTwoFaSettings.class));
if (sysadminSettingsAsDefault) {
if (tenantTwoFaSettings.isEmpty() || tenantTwoFaSettings.get().isUseSystemTwoFactorAuthSettings()) {
return getPlatformTwoFaSettings(TenantId.SYS_TENANT_ID, false);
}
}
return tenantTwoFaSettings;
}
return Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, TWO_FACTOR_AUTH_SETTINGS_KEY))
.map(adminSettings -> JacksonUtil.treeToValue(adminSettings.getJsonValue(), PlatformTwoFaSettings.class));
}
@SneakyThrows({InterruptedException.class, ExecutionException.class})
@Override
public void savePlatformTwoFaSettings(TenantId tenantId, PlatformTwoFaSettings twoFactorAuthSettings) throws ThingsboardException {
if (tenantId.equals(TenantId.SYS_TENANT_ID) || !twoFactorAuthSettings.isUseSystemTwoFactorAuthSettings()) {
ConstraintValidator.validateFields(twoFactorAuthSettings);
}
ConstraintValidator.validateFields(twoFactorAuthSettings);
for (TwoFaProviderConfig providerConfig : twoFactorAuthSettings.getProviders()) {
twoFactorAuthService.checkProvider(tenantId, providerConfig.getProviderType());
}
if (tenantId.equals(TenantId.SYS_TENANT_ID)) {
AdminSettings settings = Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(tenantId, TWO_FACTOR_AUTH_SETTINGS_KEY))
.orElseGet(() -> {
AdminSettings newSettings = new AdminSettings();
newSettings.setKey(TWO_FACTOR_AUTH_SETTINGS_KEY);
return newSettings;
});
settings.setJsonValue(JacksonUtil.valueToTree(twoFactorAuthSettings));
adminSettingsService.saveAdminSettings(tenantId, settings);
} else {
attributesService.save(TenantId.SYS_TENANT_ID, tenantId, DataConstants.SERVER_SCOPE, Collections.singletonList(
new BaseAttributeKvEntry(new JsonDataEntry(TWO_FACTOR_AUTH_SETTINGS_KEY, JacksonUtil.toString(twoFactorAuthSettings)), System.currentTimeMillis())
)).get();
}
AdminSettings settings = Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(tenantId, TWO_FACTOR_AUTH_SETTINGS_KEY))
.orElseGet(() -> {
AdminSettings newSettings = new AdminSettings();
newSettings.setKey(TWO_FACTOR_AUTH_SETTINGS_KEY);
return newSettings;
});
settings.setJsonValue(JacksonUtil.valueToTree(twoFactorAuthSettings));
adminSettingsService.saveAdminSettings(tenantId, settings);
}
@SneakyThrows({InterruptedException.class, ExecutionException.class})
@Override
public void deletePlatformTwoFaSettings(TenantId tenantId) {
if (tenantId.equals(TenantId.SYS_TENANT_ID)) {
Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(tenantId, TWO_FACTOR_AUTH_SETTINGS_KEY))
.ifPresent(adminSettings -> adminSettingsDao.removeById(tenantId, adminSettings.getId().getId()));
} else {
attributesService.removeAll(TenantId.SYS_TENANT_ID, tenantId, DataConstants.SERVER_SCOPE,
Collections.singletonList(TWO_FACTOR_AUTH_SETTINGS_KEY)).get();
}
Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(tenantId, TWO_FACTOR_AUTH_SETTINGS_KEY))
.ifPresent(adminSettings -> adminSettingsDao.removeById(tenantId, adminSettings.getId().getId()));
}
}

81
application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthConfigTest.java

@ -30,10 +30,8 @@ import org.springframework.web.util.UriComponentsBuilder;
import org.thingsboard.rule.engine.api.SmsService;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.security.model.mfa.account.AccountTwoFaSettings;
import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService;
import org.thingsboard.server.service.security.auth.mfa.config.TwoFaConfigManager;
import org.thingsboard.server.common.data.security.model.mfa.PlatformTwoFaSettings;
import org.thingsboard.server.common.data.security.model.mfa.account.AccountTwoFaSettings;
import org.thingsboard.server.common.data.security.model.mfa.account.SmsTwoFaAccountConfig;
import org.thingsboard.server.common.data.security.model.mfa.account.TotpTwoFaAccountConfig;
import org.thingsboard.server.common.data.security.model.mfa.account.TwoFaAccountConfig;
@ -41,6 +39,8 @@ import org.thingsboard.server.common.data.security.model.mfa.provider.SmsTwoFaPr
import org.thingsboard.server.common.data.security.model.mfa.provider.TotpTwoFaProviderConfig;
import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderConfig;
import org.thingsboard.server.common.data.security.model.mfa.provider.TwoFaProviderType;
import org.thingsboard.server.service.security.auth.mfa.TwoFactorAuthService;
import org.thingsboard.server.service.security.auth.mfa.config.TwoFaConfigManager;
import org.thingsboard.server.service.security.auth.mfa.provider.impl.OtpBasedTwoFaProvider;
import org.thingsboard.server.service.security.auth.mfa.provider.impl.TotpTwoFaProvider;
@ -85,15 +85,9 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest {
@Test
public void testSavePlatformTwoFaSettingsForDifferentAuthorities() throws Exception {
public void testSavePlatformTwoFaSettings() throws Exception {
loginSysAdmin();
testSavePlatformTwoFaSettings();
loginTenantAdmin();
testSavePlatformTwoFaSettings();
}
private void testSavePlatformTwoFaSettings() throws Exception {
TotpTwoFaProviderConfig totpTwoFaProviderConfig = new TotpTwoFaProviderConfig();
totpTwoFaProviderConfig.setIssuerName("tb");
SmsTwoFaProviderConfig smsTwoFaProviderConfig = new SmsTwoFaProviderConfig();
@ -117,7 +111,7 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest {
@Test
public void testSavePlatformTwoFaSettings_validationError() throws Exception {
loginTenantAdmin();
loginSysAdmin();
PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings();
twoFaSettings.setProviders(Collections.emptyList());
@ -135,71 +129,6 @@ public abstract class TwoFactorAuthConfigTest extends AbstractControllerTest {
"maximum number of verification failure before user lockout must be positive",
"total amount of time allotted for verification must be greater than 0"
);
twoFaSettings.setUseSystemTwoFactorAuthSettings(true);
doPost("/api/2fa/settings", twoFaSettings)
.andExpect(status().isOk());
twoFaSettings.setVerificationCodeSendRateLimit(null);
twoFaSettings.setVerificationCodeCheckRateLimit(null);
twoFaSettings.setMaxVerificationFailuresBeforeUserLockout(0);
twoFaSettings.setTotalAllowedTimeForVerification(null);
doPost("/api/2fa/settings", twoFaSettings)
.andExpect(status().isOk());
}
@Test
public void testGetPlatformTwoFaSettings_useSysadminSettingsAsDefault() throws Exception {
loginSysAdmin();
PlatformTwoFaSettings sysadminTwoFaSettings = new PlatformTwoFaSettings();
TotpTwoFaProviderConfig totpTwoFaProviderConfig = new TotpTwoFaProviderConfig();
totpTwoFaProviderConfig.setIssuerName("tb");
sysadminTwoFaSettings.setProviders(Collections.singletonList(totpTwoFaProviderConfig));
sysadminTwoFaSettings.setMaxVerificationFailuresBeforeUserLockout(25);
doPost("/api/2fa/settings", sysadminTwoFaSettings).andExpect(status().isOk());
loginTenantAdmin();
PlatformTwoFaSettings tenantTwoFaSettings = new PlatformTwoFaSettings();
tenantTwoFaSettings.setUseSystemTwoFactorAuthSettings(true);
tenantTwoFaSettings.setProviders(Collections.emptyList());
doPost("/api/2fa/settings", tenantTwoFaSettings).andExpect(status().isOk());
PlatformTwoFaSettings twoFaSettings = readResponse(doGet("/api/2fa/settings").andExpect(status().isOk()), PlatformTwoFaSettings.class);
assertThat(twoFaSettings).isEqualTo(tenantTwoFaSettings);
doPost("/api/2fa/account/config/generate?providerType=TOTP")
.andExpect(status().isOk());
tenantTwoFaSettings.setUseSystemTwoFactorAuthSettings(false);
tenantTwoFaSettings.setProviders(Collections.emptyList());
tenantTwoFaSettings.setMaxVerificationFailuresBeforeUserLockout(10);
doPost("/api/2fa/settings", tenantTwoFaSettings).andExpect(status().isOk());
twoFaSettings = readResponse(doGet("/api/2fa/settings").andExpect(status().isOk()), PlatformTwoFaSettings.class);
assertThat(twoFaSettings).isEqualTo(tenantTwoFaSettings);
assertThat(getErrorMessage(doPost("/api/2fa/account/config/generate?providerType=TOTP")
.andExpect(status().isBadRequest()))).containsIgnoringCase("provider is not configured");
loginSysAdmin();
sysadminTwoFaSettings.setProviders(Collections.emptyList());
doPost("/api/2fa/settings", sysadminTwoFaSettings).andExpect(status().isOk());
loginTenantAdmin();
tenantTwoFaSettings.setUseSystemTwoFactorAuthSettings(true);
tenantTwoFaSettings.setProviders(Collections.singletonList(totpTwoFaProviderConfig));
doPost("/api/2fa/settings", tenantTwoFaSettings).andExpect(status().isOk());
assertThat(getErrorMessage(doPost("/api/2fa/account/config/generate?providerType=TOTP")
.andExpect(status().isBadRequest()))).containsIgnoringCase("provider is not configured");
tenantTwoFaSettings.setUseSystemTwoFactorAuthSettings(false);
doPost("/api/2fa/settings", tenantTwoFaSettings).andExpect(status().isOk());
doPost("/api/2fa/account/config/generate?providerType=TOTP")
.andExpect(status().isOk());
loginSysAdmin();
twoFaSettings = readResponse(doGet("/api/2fa/settings").andExpect(status().isOk()), PlatformTwoFaSettings.class);
assertThat(twoFaSettings).isEqualTo(sysadminTwoFaSettings);
}
@Test

7
application/src/test/java/org/thingsboard/server/controller/TwoFactorAuthTest.java

@ -345,7 +345,6 @@ public abstract class TwoFactorAuthTest extends AbstractControllerTest {
@Test
public void testTwoFa_multipleProviders() throws Exception {
PlatformTwoFaSettings platformTwoFaSettings = new PlatformTwoFaSettings();
platformTwoFaSettings.setUseSystemTwoFactorAuthSettings(true);
TotpTwoFaProviderConfig totpTwoFaProviderConfig = new TotpTwoFaProviderConfig();
totpTwoFaProviderConfig.setIssuerName("TB");
@ -411,10 +410,9 @@ public abstract class TwoFactorAuthTest extends AbstractControllerTest {
totpTwoFaProviderConfig.setIssuerName("tb");
PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings();
twoFaSettings.setUseSystemTwoFactorAuthSettings(false);
twoFaSettings.setProviders(Arrays.stream(new TwoFaProviderConfig[]{totpTwoFaProviderConfig}).collect(Collectors.toList()));
Arrays.stream(customizer).forEach(c -> c.accept(twoFaSettings));
twoFaConfigManager.savePlatformTwoFaSettings(tenantId, twoFaSettings);
twoFaConfigManager.savePlatformTwoFaSettings(TenantId.SYS_TENANT_ID, twoFaSettings);
TotpTwoFaAccountConfig totpTwoFaAccountConfig = (TotpTwoFaAccountConfig) twoFactorAuthService.generateNewAccountConfig(user, TwoFaProviderType.TOTP);
twoFaConfigManager.saveTwoFaAccountConfig(tenantId, user.getId(), totpTwoFaAccountConfig);
@ -428,9 +426,8 @@ public abstract class TwoFactorAuthTest extends AbstractControllerTest {
Arrays.stream(customizer).forEach(c -> c.accept(smsTwoFaProviderConfig));
PlatformTwoFaSettings twoFaSettings = new PlatformTwoFaSettings();
twoFaSettings.setUseSystemTwoFactorAuthSettings(false);
twoFaSettings.setProviders(Arrays.stream(new TwoFaProviderConfig[]{smsTwoFaProviderConfig}).collect(Collectors.toList()));
twoFaConfigManager.savePlatformTwoFaSettings(tenantId, twoFaSettings);
twoFaConfigManager.savePlatformTwoFaSettings(TenantId.SYS_TENANT_ID, twoFaSettings);
SmsTwoFaAccountConfig smsTwoFaAccountConfig = new SmsTwoFaAccountConfig();
smsTwoFaAccountConfig.setPhoneNumber("+38050505050");

1
common/data/src/main/java/org/thingsboard/server/common/data/security/model/mfa/PlatformTwoFaSettings.java

@ -28,7 +28,6 @@ import java.util.Optional;
@Data
public class PlatformTwoFaSettings {
private boolean useSystemTwoFactorAuthSettings;
@Valid
private List<TwoFaProviderConfig> providers;

Loading…
Cancel
Save