Merge branch 'master' into lwm2m_write_obj_19
This commit is contained in:
commit
edad8f4932
@ -14,3 +14,13 @@
|
||||
-- limitations under the License.
|
||||
--
|
||||
|
||||
ALTER TABLE user_credentials ADD COLUMN IF NOT EXISTS last_login_ts BIGINT;
|
||||
UPDATE user_credentials c SET last_login_ts = (SELECT (additional_info::json ->> 'lastLoginTs')::bigint FROM tb_user u WHERE u.id = c.user_id)
|
||||
WHERE last_login_ts IS NULL;
|
||||
|
||||
ALTER TABLE user_credentials ADD COLUMN IF NOT EXISTS failed_login_attempts INT;
|
||||
UPDATE user_credentials c SET failed_login_attempts = (SELECT (additional_info::json ->> 'failedLoginAttempts')::int FROM tb_user u WHERE u.id = c.user_id)
|
||||
WHERE failed_login_attempts IS NULL;
|
||||
|
||||
UPDATE tb_user SET additional_info = (additional_info::jsonb - 'lastLoginTs' - 'failedLoginAttempts' - 'userCredentialsEnabled')::text
|
||||
WHERE additional_info IS NOT NULL AND additional_info != 'null';
|
||||
|
||||
@ -38,6 +38,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
|
||||
import org.springframework.web.context.request.async.DeferredResult;
|
||||
import org.thingsboard.common.util.DonAsynchron;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.cluster.TbClusterService;
|
||||
import org.thingsboard.server.common.data.Customer;
|
||||
import org.thingsboard.server.common.data.Dashboard;
|
||||
@ -877,14 +878,18 @@ public abstract class BaseController {
|
||||
}
|
||||
|
||||
protected void checkUserInfo(User user) throws ThingsboardException {
|
||||
ObjectNode info;
|
||||
if (user.getAdditionalInfo() instanceof ObjectNode additionalInfo) {
|
||||
checkDashboardInfo(additionalInfo);
|
||||
|
||||
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
|
||||
if (userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) {
|
||||
additionalInfo.put("userCredentialsEnabled", true);
|
||||
}
|
||||
info = additionalInfo;
|
||||
checkDashboardInfo(info);
|
||||
} else {
|
||||
info = JacksonUtil.newObjectNode();
|
||||
user.setAdditionalInfo(info);
|
||||
}
|
||||
|
||||
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
|
||||
info.put("userCredentialsEnabled", userCredentials.isEnabled());
|
||||
info.put("lastLoginTs", userCredentials.getLastLoginTs());
|
||||
}
|
||||
|
||||
protected void checkDashboardInfo(JsonNode additionalInfo) throws ThingsboardException {
|
||||
|
||||
@ -78,7 +78,6 @@ import org.thingsboard.server.service.security.model.UserPrincipal;
|
||||
import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
|
||||
import org.thingsboard.server.service.security.permission.Operation;
|
||||
import org.thingsboard.server.service.security.permission.Resource;
|
||||
import org.thingsboard.server.service.security.system.SystemSecurityService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -123,7 +122,6 @@ public class UserController extends BaseController {
|
||||
|
||||
private final MailService mailService;
|
||||
private final JwtTokenFactory tokenFactory;
|
||||
private final SystemSecurityService systemSecurityService;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
private final TbUserService tbUserService;
|
||||
private final EntityQueryService entityQueryService;
|
||||
|
||||
@ -170,6 +170,7 @@ public class ThingsboardInstallService {
|
||||
log.info("Installing DataBase schema for entities...");
|
||||
|
||||
entityDatabaseSchemaService.createDatabaseSchema();
|
||||
entityDatabaseSchemaService.createSchemaVersion();
|
||||
|
||||
entityDatabaseSchemaService.createOrUpdateViewsAndFunctions();
|
||||
entityDatabaseSchemaService.createOrUpdateDeviceInfoView(persistToTelemetry);
|
||||
|
||||
@ -50,7 +50,6 @@ import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
|
||||
import org.thingsboard.server.dao.eventsourcing.RelationActionEvent;
|
||||
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
|
||||
import org.thingsboard.server.dao.tenant.TenantService;
|
||||
import org.thingsboard.server.dao.user.UserServiceImpl;
|
||||
|
||||
/**
|
||||
* This event listener does not support async event processing because relay on ThreadLocal
|
||||
@ -226,13 +225,10 @@ public class EdgeEventSourcingListener {
|
||||
}
|
||||
|
||||
private void cleanUpUserAdditionalInfo(User user) {
|
||||
// reset FAILED_LOGIN_ATTEMPTS and LAST_LOGIN_TS - edge is not interested in this information
|
||||
if (user.getAdditionalInfo() instanceof NullNode) {
|
||||
user.setAdditionalInfo(null);
|
||||
}
|
||||
if (user.getAdditionalInfo() instanceof ObjectNode additionalInfo) {
|
||||
additionalInfo.remove(UserServiceImpl.FAILED_LOGIN_ATTEMPTS);
|
||||
additionalInfo.remove(UserServiceImpl.LAST_LOGIN_TS);
|
||||
if (additionalInfo.isEmpty()) {
|
||||
user.setAdditionalInfo(null);
|
||||
} else {
|
||||
|
||||
@ -23,4 +23,6 @@ public interface EntityDatabaseSchemaService extends DatabaseSchemaService {
|
||||
|
||||
void createCustomerTitleUniqueConstraintIfNotExists();
|
||||
|
||||
void createSchemaVersion();
|
||||
|
||||
}
|
||||
|
||||
@ -16,7 +16,10 @@
|
||||
package org.thingsboard.server.service.install;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.info.BuildProperties;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@ -29,6 +32,11 @@ public class SqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaSer
|
||||
public static final String SCHEMA_ENTITIES_IDX_PSQL_ADDON_SQL = "schema-entities-idx-psql-addon.sql";
|
||||
public static final String SCHEMA_VIEWS_AND_FUNCTIONS_SQL = "schema-views-and-functions.sql";
|
||||
|
||||
@Autowired
|
||||
private BuildProperties buildProperties;
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
public SqlEntityDatabaseSchemaService() {
|
||||
super(SCHEMA_ENTITIES_SQL, SCHEMA_ENTITIES_IDX_SQL);
|
||||
}
|
||||
@ -59,4 +67,26 @@ public class SqlEntityDatabaseSchemaService extends SqlAbstractDatabaseSchemaSer
|
||||
"ALTER TABLE customer ADD CONSTRAINT customer_title_unq_key UNIQUE(tenant_id, title); END IF; END; $$;",
|
||||
"create 'customer_title_unq_key' constraint if it doesn't already exist!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createSchemaVersion() {
|
||||
try {
|
||||
Long schemaVersion = jdbcTemplate.queryForList("SELECT schema_version FROM tb_schema_settings", Long.class).stream().findFirst().orElse(null);
|
||||
if (schemaVersion == null) {
|
||||
jdbcTemplate.execute("INSERT INTO tb_schema_settings (schema_version) VALUES (" + getSchemaVersion() + ")");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to create schema version [{}]!", buildProperties.getVersion(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private int getSchemaVersion() {
|
||||
String[] versionParts = buildProperties.getVersion().replaceAll("[^\\d.]", "").split("\\.");
|
||||
|
||||
int major = Integer.parseInt(versionParts[0]);
|
||||
int minor = Integer.parseInt(versionParts[1]);
|
||||
int patch = versionParts.length > 2 ? Integer.parseInt(versionParts[2]) : 0;
|
||||
|
||||
return major * 1000000 + minor * 1000 + patch;
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.thingsboard.server.service.install;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.server.dao.util.SqlTsDao;
|
||||
@ -25,9 +24,6 @@ import org.thingsboard.server.dao.util.SqlTsDao;
|
||||
@Profile("install")
|
||||
public class SqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService implements TsDatabaseSchemaService {
|
||||
|
||||
@Value("${sql.postgres.ts_key_value_partitioning:MONTHS}")
|
||||
private String partitionType;
|
||||
|
||||
public SqlTsDatabaseSchemaService() {
|
||||
super("schema-ts-psql.sql", null);
|
||||
}
|
||||
|
||||
@ -265,7 +265,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
|
||||
}
|
||||
}
|
||||
if (actionType == ActionType.LOGIN && e == null) {
|
||||
userService.setLastLoginTs(user.getTenantId(), user.getId());
|
||||
userService.updateLastLoginTs(user.getTenantId(), user.getId());
|
||||
}
|
||||
auditLogService.logEntityAction(
|
||||
user.getTenantId(), user.getCustomerId(), user.getId(),
|
||||
|
||||
@ -27,6 +27,7 @@ import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.StringUtils;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
import org.thingsboard.server.common.data.UserActivationLink;
|
||||
import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.security.Authority;
|
||||
import org.thingsboard.server.common.data.security.UserCredentials;
|
||||
import org.thingsboard.server.common.data.security.model.SecuritySettings;
|
||||
@ -67,31 +68,30 @@ public class AuthControllerTest extends AbstractControllerTest {
|
||||
.andExpect(status().isUnauthorized());
|
||||
|
||||
loginSysAdmin();
|
||||
doGet("/api/auth/user")
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.authority", is(Authority.SYS_ADMIN.name())))
|
||||
.andExpect(jsonPath("$.email", is(SYS_ADMIN_EMAIL)));
|
||||
User user = getCurrentUser();
|
||||
assertThat(user.getAuthority()).isEqualTo(Authority.SYS_ADMIN);
|
||||
assertThat(user.getEmail()).isEqualTo(SYS_ADMIN_EMAIL);
|
||||
|
||||
loginTenantAdmin();
|
||||
doGet("/api/auth/user")
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.authority", is(Authority.TENANT_ADMIN.name())))
|
||||
.andExpect(jsonPath("$.email", is(TENANT_ADMIN_EMAIL)));
|
||||
user = getCurrentUser();
|
||||
assertThat(user.getAuthority()).isEqualTo(Authority.TENANT_ADMIN);
|
||||
assertThat(user.getEmail()).isEqualTo(TENANT_ADMIN_EMAIL);
|
||||
|
||||
loginCustomerUser();
|
||||
doGet("/api/auth/user")
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.authority", is(Authority.CUSTOMER_USER.name())))
|
||||
.andExpect(jsonPath("$.email", is(CUSTOMER_USER_EMAIL)));
|
||||
user = getCurrentUser();
|
||||
assertThat(user.getAuthority()).isEqualTo(Authority.CUSTOMER_USER);
|
||||
assertThat(user.getEmail()).isEqualTo(CUSTOMER_USER_EMAIL);
|
||||
user = getUser(customerUserId);
|
||||
assertThat(user.getAdditionalInfo().get("userCredentialsEnabled").asBoolean()).isTrue();
|
||||
assertThat(user.getAdditionalInfo().get("lastLoginTs").asLong()).isCloseTo(System.currentTimeMillis(), within(10000L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoginLogout() throws Exception {
|
||||
loginSysAdmin();
|
||||
doGet("/api/auth/user")
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.authority", is(Authority.SYS_ADMIN.name())))
|
||||
.andExpect(jsonPath("$.email", is(SYS_ADMIN_EMAIL)));
|
||||
User user = getCurrentUser();
|
||||
assertThat(user.getAuthority()).isEqualTo(Authority.SYS_ADMIN);
|
||||
assertThat(user.getEmail()).isEqualTo(SYS_ADMIN_EMAIL);
|
||||
|
||||
TimeUnit.SECONDS.sleep(1); //We need to make sure that event for invalidating token was successfully processed
|
||||
|
||||
@ -102,19 +102,45 @@ public class AuthControllerTest extends AbstractControllerTest {
|
||||
resetTokens();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailedLogin() throws Exception {
|
||||
int maxFailedLoginAttempts = 3;
|
||||
loginSysAdmin();
|
||||
updateSecuritySettings(securitySettings -> {
|
||||
securitySettings.setMaxFailedLoginAttempts(maxFailedLoginAttempts);
|
||||
});
|
||||
loginTenantAdmin();
|
||||
|
||||
for (int i = 0; i < maxFailedLoginAttempts; i++) {
|
||||
String error = getErrorMessage(doPost("/api/auth/login",
|
||||
new LoginRequest(CUSTOMER_USER_EMAIL, "IncorrectPassword"))
|
||||
.andExpect(status().isUnauthorized()));
|
||||
assertThat(error).containsIgnoringCase("invalid username or password");
|
||||
}
|
||||
|
||||
User user = getUser(customerUserId);
|
||||
assertThat(user.getAdditionalInfo().get("userCredentialsEnabled").asBoolean()).isTrue();
|
||||
|
||||
String error = getErrorMessage(doPost("/api/auth/login",
|
||||
new LoginRequest(CUSTOMER_USER_EMAIL, "IncorrectPassword4"))
|
||||
.andExpect(status().isUnauthorized()));
|
||||
assertThat(error).containsIgnoringCase("account is locked");
|
||||
|
||||
user = getUser(customerUserId);
|
||||
assertThat(user.getAdditionalInfo().get("userCredentialsEnabled").asBoolean()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshToken() throws Exception {
|
||||
loginSysAdmin();
|
||||
doGet("/api/auth/user")
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.authority", is(Authority.SYS_ADMIN.name())))
|
||||
.andExpect(jsonPath("$.email", is(SYS_ADMIN_EMAIL)));
|
||||
User user = getCurrentUser();
|
||||
assertThat(user.getAuthority()).isEqualTo(Authority.SYS_ADMIN);
|
||||
assertThat(user.getEmail()).isEqualTo(SYS_ADMIN_EMAIL);
|
||||
|
||||
refreshToken();
|
||||
doGet("/api/auth/user")
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.authority", is(Authority.SYS_ADMIN.name())))
|
||||
.andExpect(jsonPath("$.email", is(SYS_ADMIN_EMAIL)));
|
||||
user = getCurrentUser();
|
||||
assertThat(user.getAuthority()).isEqualTo(Authority.SYS_ADMIN);
|
||||
assertThat(user.getEmail()).isEqualTo(SYS_ADMIN_EMAIL);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -277,6 +303,14 @@ public class AuthControllerTest extends AbstractControllerTest {
|
||||
doPost("/api/admin/securitySettings", securitySettings).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
private User getCurrentUser() throws Exception {
|
||||
return doGet("/api/auth/user", User.class);
|
||||
}
|
||||
|
||||
private User getUser(UserId id) throws Exception {
|
||||
return doGet("/api/user/" + id, User.class);
|
||||
}
|
||||
|
||||
private String getActivationLink(User user) throws Exception {
|
||||
return doGet("/api/user/" + user.getId() + "/activationLink", String.class);
|
||||
}
|
||||
|
||||
@ -319,7 +319,9 @@ public class TwoFactorAuthTest extends AbstractControllerTest {
|
||||
assertThat(successfulLogInAuditLog.getActionStatus()).isEqualTo(ActionStatus.SUCCESS);
|
||||
assertThat(successfulLogInAuditLog.getUserName()).isEqualTo(username);
|
||||
});
|
||||
assertThat(userService.findUserById(tenantId, user.getId()).getAdditionalInfo()
|
||||
|
||||
loginTenantAdmin();
|
||||
assertThat(doGet("/api/user/" + user.getId(), User.class).getAdditionalInfo()
|
||||
.get("lastLoginTs").asLong())
|
||||
.isGreaterThan(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(3));
|
||||
}
|
||||
|
||||
@ -113,6 +113,7 @@ public class UserControllerTest extends AbstractControllerTest {
|
||||
Assert.assertEquals(email, savedUser.getEmail());
|
||||
|
||||
User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
|
||||
foundUser.setAdditionalInfo(savedUser.getAdditionalInfo());
|
||||
Assert.assertEquals(foundUser, savedUser);
|
||||
|
||||
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundUser, foundUser,
|
||||
@ -265,6 +266,7 @@ public class UserControllerTest extends AbstractControllerTest {
|
||||
User savedUser = doPost("/api/user", user, User.class);
|
||||
User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
|
||||
Assert.assertNotNull(foundUser);
|
||||
foundUser.setAdditionalInfo(savedUser.getAdditionalInfo());
|
||||
Assert.assertEquals(savedUser, foundUser);
|
||||
}
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
|
||||
@Test
|
||||
public void testCreateUpdateDeleteTenantUser() throws Exception {
|
||||
// create user
|
||||
edgeImitator.expectMessageAmount(6);
|
||||
edgeImitator.expectMessageAmount(3);
|
||||
User newTenantAdmin = new User();
|
||||
newTenantAdmin.setAuthority(Authority.TENANT_ADMIN);
|
||||
newTenantAdmin.setTenantId(tenantId);
|
||||
@ -55,9 +55,9 @@ public class UserEdgeTest extends AbstractEdgeTest {
|
||||
newTenantAdmin.setFirstName("Boris");
|
||||
newTenantAdmin.setLastName("Johnson");
|
||||
User savedTenantAdmin = createUser(newTenantAdmin, "tenant");
|
||||
Assert.assertTrue(edgeImitator.waitForMessages()); // wait 6 messages - x2 user update msg and x4 user credentials update msgs (create + authenticate user)
|
||||
Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size());
|
||||
Assert.assertEquals(4, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size());
|
||||
Assert.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - x1 user update msg and x2 user credentials update msgs (create + authenticate user)
|
||||
Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size());
|
||||
Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size());
|
||||
Optional<UserUpdateMsg> userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
|
||||
Assert.assertTrue(userUpdateMsgOpt.isPresent());
|
||||
UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get();
|
||||
@ -133,7 +133,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
|
||||
// create user
|
||||
edgeImitator.expectMessageAmount(6);
|
||||
edgeImitator.expectMessageAmount(3);
|
||||
User customerUser = new User();
|
||||
customerUser.setAuthority(Authority.CUSTOMER_USER);
|
||||
customerUser.setTenantId(tenantId);
|
||||
@ -142,9 +142,9 @@ public class UserEdgeTest extends AbstractEdgeTest {
|
||||
customerUser.setFirstName("John");
|
||||
customerUser.setLastName("Edwards");
|
||||
User savedCustomerUser = createUser(customerUser, "customer");
|
||||
Assert.assertTrue(edgeImitator.waitForMessages()); // wait 6 messages - x2 user update msg and x4 user credentials update msgs (create + authenticate user)
|
||||
Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size());
|
||||
Assert.assertEquals(4, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size());
|
||||
Assert.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - x1 user update msg and x2 user credentials update msgs (create + authenticate user)
|
||||
Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size());
|
||||
Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size());
|
||||
Optional<UserUpdateMsg> userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
|
||||
Assert.assertTrue(userUpdateMsgOpt.isPresent());
|
||||
UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get();
|
||||
|
||||
@ -114,6 +114,13 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
|
||||
CoapTestCallback callbackCoap = new TestCoapCallbackForRPC(client, false, protobuf);
|
||||
|
||||
CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap);
|
||||
String awaitAlias = "await Two Way Rpc (client.getObserveRelation)";
|
||||
await(awaitAlias)
|
||||
.atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
.until(() -> processTwoWayRpcTestWithAwait(callbackCoap, observeRelation, expectedResponseResult));
|
||||
}
|
||||
|
||||
private boolean processTwoWayRpcTestWithAwait(CoapTestCallback callbackCoap, CoapObserveRelation observeRelation, String expectedResponseResult) throws Exception {
|
||||
String awaitAlias = "await Two Way Rpc (client.getObserveRelation)";
|
||||
await(awaitAlias)
|
||||
.atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||
@ -146,7 +153,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
|
||||
validateTwoWayStateChangedNotification(callbackCoap, expectedResponseResult, actualResult);
|
||||
|
||||
observeRelation.proactiveCancel();
|
||||
assertTrue(observeRelation.isCanceled());
|
||||
return observeRelation.isCanceled();
|
||||
}
|
||||
|
||||
protected void processOnLoadResponse(CoapResponse response, CoapTestClient client) {
|
||||
|
||||
@ -97,7 +97,7 @@ public interface UserService extends EntityDaoService {
|
||||
|
||||
int increaseFailedLoginAttempts(TenantId tenantId, UserId userId);
|
||||
|
||||
void setLastLoginTs(TenantId tenantId, UserId userId);
|
||||
void updateLastLoginTs(TenantId tenantId, UserId userId);
|
||||
|
||||
void saveMobileSession(TenantId tenantId, UserId userId, String mobileToken, MobileSessionInfo sessionInfo);
|
||||
|
||||
|
||||
@ -40,6 +40,8 @@ public class UserCredentials extends BaseDataWithAdditionalInfo<UserCredentialsI
|
||||
private Long activateTokenExpTime;
|
||||
private String resetToken;
|
||||
private Long resetTokenExpTime;
|
||||
private Long lastLoginTs;
|
||||
private Integer failedLoginAttempts;
|
||||
|
||||
public UserCredentials() {
|
||||
super();
|
||||
|
||||
@ -82,7 +82,8 @@ public class ModelConstants {
|
||||
public static final String USER_CREDENTIALS_ACTIVATE_TOKEN_EXP_TIME_PROPERTY = "activate_token_exp_time";
|
||||
public static final String USER_CREDENTIALS_RESET_TOKEN_PROPERTY = "reset_token";
|
||||
public static final String USER_CREDENTIALS_RESET_TOKEN_EXP_TIME_PROPERTY = "reset_token_exp_time";
|
||||
public static final String USER_CREDENTIALS_ADDITIONAL_PROPERTY = "additional_info";
|
||||
public static final String USER_CREDENTIALS_LAST_LOGIN_TS_PROPERTY = "last_login_ts";
|
||||
public static final String USER_CREDENTIALS_FAILED_LOGIN_ATTEMPTS_PROPERTY = "failed_login_attempts";
|
||||
|
||||
/**
|
||||
* User settings constants.
|
||||
|
||||
@ -60,18 +60,21 @@ public final class UserCredentialsEntity extends BaseSqlEntity<UserCredentials>
|
||||
private Long resetTokenExpTime;
|
||||
|
||||
@Convert(converter = JsonConverter.class)
|
||||
@Column(name = ModelConstants.USER_CREDENTIALS_ADDITIONAL_PROPERTY)
|
||||
@Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY)
|
||||
private JsonNode additionalInfo;
|
||||
|
||||
@Column(name = ModelConstants.USER_CREDENTIALS_LAST_LOGIN_TS_PROPERTY)
|
||||
private Long lastLoginTs;
|
||||
|
||||
@Column(name = ModelConstants.USER_CREDENTIALS_FAILED_LOGIN_ATTEMPTS_PROPERTY)
|
||||
private Integer failedLoginAttempts;
|
||||
|
||||
public UserCredentialsEntity() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UserCredentialsEntity(UserCredentials userCredentials) {
|
||||
if (userCredentials.getId() != null) {
|
||||
this.setUuid(userCredentials.getId().getId());
|
||||
}
|
||||
this.setCreatedTime(userCredentials.getCreatedTime());
|
||||
super(userCredentials);
|
||||
if (userCredentials.getUserId() != null) {
|
||||
this.userId = userCredentials.getUserId().getId();
|
||||
}
|
||||
@ -82,6 +85,8 @@ public final class UserCredentialsEntity extends BaseSqlEntity<UserCredentials>
|
||||
this.resetToken = userCredentials.getResetToken();
|
||||
this.resetTokenExpTime = userCredentials.getResetTokenExpTime();
|
||||
this.additionalInfo = userCredentials.getAdditionalInfo();
|
||||
this.lastLoginTs = userCredentials.getLastLoginTs();
|
||||
this.failedLoginAttempts = userCredentials.getFailedLoginAttempts();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -98,6 +103,8 @@ public final class UserCredentialsEntity extends BaseSqlEntity<UserCredentials>
|
||||
userCredentials.setResetToken(resetToken);
|
||||
userCredentials.setResetTokenExpTime(resetTokenExpTime);
|
||||
userCredentials.setAdditionalInfo(additionalInfo);
|
||||
userCredentials.setLastLoginTs(lastLoginTs);
|
||||
userCredentials.setFailedLoginAttempts(failedLoginAttempts);
|
||||
return userCredentials;
|
||||
}
|
||||
|
||||
|
||||
@ -69,4 +69,19 @@ public class JpaUserCredentialsDao extends JpaAbstractDao<UserCredentialsEntity,
|
||||
userCredentialsRepository.removeByUserId(userId.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastLoginTs(TenantId tenantId, UserId userId, long lastLoginTs) {
|
||||
userCredentialsRepository.updateLastLoginTsByUserId(userId.getId(), lastLoginTs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int incrementFailedLoginAttempts(TenantId tenantId, UserId userId) {
|
||||
return userCredentialsRepository.incrementFailedLoginAttemptsByUserId(userId.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFailedLoginAttempts(TenantId tenantId, UserId userId, int failedLoginAttempts) {
|
||||
userCredentialsRepository.updateFailedLoginAttemptsByUserId(userId.getId(), failedLoginAttempts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
package org.thingsboard.server.dao.sql.user;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.thingsboard.server.dao.model.sql.UserCredentialsEntity;
|
||||
|
||||
@ -35,4 +37,19 @@ public interface UserCredentialsRepository extends JpaRepository<UserCredentials
|
||||
@Transactional
|
||||
void removeByUserId(UUID userId);
|
||||
|
||||
@Transactional
|
||||
@Modifying
|
||||
@Query("UPDATE UserCredentialsEntity SET lastLoginTs = :lastLoginTs WHERE userId = :userId")
|
||||
void updateLastLoginTsByUserId(UUID userId, long lastLoginTs);
|
||||
|
||||
@Transactional
|
||||
@Query(value = "UPDATE user_credentials SET failed_login_attempts = coalesce(failed_login_attempts, 0) + 1 " +
|
||||
"WHERE user_id = :userId RETURNING failed_login_attempts", nativeQuery = true)
|
||||
int incrementFailedLoginAttemptsByUserId(UUID userId);
|
||||
|
||||
@Transactional
|
||||
@Modifying
|
||||
@Query("UPDATE UserCredentialsEntity SET failedLoginAttempts = :failedLoginAttempts WHERE userId = :userId")
|
||||
void updateFailedLoginAttemptsByUserId(UUID userId, int failedLoginAttempts);
|
||||
|
||||
}
|
||||
|
||||
@ -61,4 +61,10 @@ public interface UserCredentialsDao extends Dao<UserCredentials> {
|
||||
|
||||
void removeByUserId(TenantId tenantId, UserId userId);
|
||||
|
||||
void setLastLoginTs(TenantId tenantId, UserId userId, long lastLoginTs);
|
||||
|
||||
int incrementFailedLoginAttempts(TenantId tenantId, UserId userId);
|
||||
|
||||
void setFailedLoginAttempts(TenantId tenantId, UserId userId, int failedLoginAttempts);
|
||||
|
||||
}
|
||||
|
||||
@ -17,9 +17,6 @@ package org.thingsboard.server.dao.user;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.BooleanNode;
|
||||
import com.fasterxml.jackson.databind.node.IntNode;
|
||||
import com.fasterxml.jackson.databind.node.LongNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -87,15 +84,10 @@ public class UserServiceImpl extends AbstractCachedEntityService<UserCacheKey, U
|
||||
|
||||
public static final String USER_PASSWORD_HISTORY = "userPasswordHistory";
|
||||
|
||||
public static final String LAST_LOGIN_TS = "lastLoginTs";
|
||||
public static final String FAILED_LOGIN_ATTEMPTS = "failedLoginAttempts";
|
||||
|
||||
private static final int DEFAULT_TOKEN_LENGTH = 30;
|
||||
public static final String INCORRECT_USER_ID = "Incorrect userId ";
|
||||
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
|
||||
|
||||
private static final String USER_CREDENTIALS_ENABLED = "userCredentialsEnabled";
|
||||
|
||||
@Value("${security.user_login_case_sensitive:true}")
|
||||
private boolean userLoginCaseSensitive;
|
||||
|
||||
@ -428,40 +420,28 @@ public class UserServiceImpl extends AbstractCachedEntityService<UserCacheKey, U
|
||||
customerUsersRemover.removeEntities(tenantId, customerId);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void setUserCredentialsEnabled(TenantId tenantId, UserId userId, boolean enabled) {
|
||||
log.trace("Executing setUserCredentialsEnabled [{}], [{}]", userId, enabled);
|
||||
validateId(userId, id -> INCORRECT_USER_ID + id);
|
||||
UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId());
|
||||
userCredentials.setEnabled(enabled);
|
||||
saveUserCredentials(tenantId, userCredentials);
|
||||
|
||||
User user = findUserById(tenantId, userId);
|
||||
user.setAdditionalInfoField(USER_CREDENTIALS_ENABLED, BooleanNode.valueOf(enabled));
|
||||
if (enabled) {
|
||||
resetFailedLoginAttempts(user);
|
||||
userCredentials.setFailedLoginAttempts(0);
|
||||
}
|
||||
saveUser(tenantId, user);
|
||||
saveUserCredentials(tenantId, userCredentials);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void resetFailedLoginAttempts(TenantId tenantId, UserId userId) {
|
||||
log.trace("Executing onUserLoginSuccessful [{}]", userId);
|
||||
User user = findUserById(tenantId, userId);
|
||||
resetFailedLoginAttempts(user);
|
||||
saveUser(tenantId, user);
|
||||
}
|
||||
|
||||
private void resetFailedLoginAttempts(User user) {
|
||||
user.setAdditionalInfoField(FAILED_LOGIN_ATTEMPTS, IntNode.valueOf(0));
|
||||
log.trace("Executing resetFailedLoginAttempts [{}]", userId);
|
||||
userCredentialsDao.setFailedLoginAttempts(tenantId, userId, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastLoginTs(TenantId tenantId, UserId userId) {
|
||||
User user = findUserById(tenantId, userId);
|
||||
user.setAdditionalInfoField(LAST_LOGIN_TS, new LongNode(System.currentTimeMillis()));
|
||||
saveUser(tenantId, user);
|
||||
public void updateLastLoginTs(TenantId tenantId, UserId userId) {
|
||||
userCredentialsDao.setLastLoginTs(tenantId, userId, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -502,18 +482,8 @@ public class UserServiceImpl extends AbstractCachedEntityService<UserCacheKey, U
|
||||
|
||||
@Override
|
||||
public int increaseFailedLoginAttempts(TenantId tenantId, UserId userId) {
|
||||
log.trace("Executing onUserLoginIncorrectCredentials [{}]", userId);
|
||||
User user = findUserById(tenantId, userId);
|
||||
int failedLoginAttempts = increaseFailedLoginAttempts(user);
|
||||
saveUser(tenantId, user);
|
||||
return failedLoginAttempts;
|
||||
}
|
||||
|
||||
private int increaseFailedLoginAttempts(User user) {
|
||||
int failedLoginAttempts = user.getAdditionalInfoField(FAILED_LOGIN_ATTEMPTS, JsonNode::asInt, 0);
|
||||
failedLoginAttempts++;
|
||||
user.setAdditionalInfoField(FAILED_LOGIN_ATTEMPTS, new IntNode(failedLoginAttempts));
|
||||
return failedLoginAttempts;
|
||||
log.trace("Executing increaseFailedLoginAttempts [{}]", userId);
|
||||
return userCredentialsDao.incrementFailedLoginAttempts(tenantId, userId);
|
||||
}
|
||||
|
||||
private void updatePasswordHistory(UserCredentials userCredentials) {
|
||||
|
||||
@ -20,18 +20,6 @@ CREATE TABLE IF NOT EXISTS tb_schema_settings
|
||||
CONSTRAINT tb_schema_settings_pkey PRIMARY KEY (schema_version)
|
||||
);
|
||||
|
||||
CREATE OR REPLACE PROCEDURE insert_tb_schema_settings()
|
||||
LANGUAGE plpgsql AS
|
||||
$$
|
||||
BEGIN
|
||||
IF (SELECT COUNT(*) FROM tb_schema_settings) = 0 THEN
|
||||
INSERT INTO tb_schema_settings (schema_version) VALUES (3006004);
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
|
||||
call insert_tb_schema_settings();
|
||||
|
||||
CREATE TABLE IF NOT EXISTS admin_settings (
|
||||
id uuid NOT NULL CONSTRAINT admin_settings_pkey PRIMARY KEY,
|
||||
tenant_id uuid NOT NULL,
|
||||
@ -497,7 +485,9 @@ CREATE TABLE IF NOT EXISTS user_credentials (
|
||||
reset_token varchar(255) UNIQUE,
|
||||
reset_token_exp_time BIGINT,
|
||||
user_id uuid UNIQUE,
|
||||
additional_info varchar DEFAULT '{}'
|
||||
additional_info varchar DEFAULT '{}',
|
||||
last_login_ts BIGINT,
|
||||
failed_login_attempts INT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS widget_type (
|
||||
|
||||
@ -220,8 +220,8 @@ export interface ScadaSymbolMetadata {
|
||||
|
||||
export const emptyMetadata = (width?: number, height?: number): ScadaSymbolMetadata => ({
|
||||
title: '',
|
||||
widgetSizeX: width ? width/100 : 3,
|
||||
widgetSizeY: height ? height/100 : 3,
|
||||
widgetSizeX: width ? Math.max(Math.round(width/100), 1) : 3,
|
||||
widgetSizeY: height ? Math.max(Math.round(height/100), 1) : 3,
|
||||
tags: [],
|
||||
behavior: [],
|
||||
properties: []
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user