Merge branch 'master' into lwm2m_write_obj_19
This commit is contained in:
commit
edad8f4932
@ -14,3 +14,13 @@
|
|||||||
-- limitations under the License.
|
-- 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.AsyncRequestTimeoutException;
|
||||||
import org.springframework.web.context.request.async.DeferredResult;
|
import org.springframework.web.context.request.async.DeferredResult;
|
||||||
import org.thingsboard.common.util.DonAsynchron;
|
import org.thingsboard.common.util.DonAsynchron;
|
||||||
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.server.cluster.TbClusterService;
|
import org.thingsboard.server.cluster.TbClusterService;
|
||||||
import org.thingsboard.server.common.data.Customer;
|
import org.thingsboard.server.common.data.Customer;
|
||||||
import org.thingsboard.server.common.data.Dashboard;
|
import org.thingsboard.server.common.data.Dashboard;
|
||||||
@ -877,14 +878,18 @@ public abstract class BaseController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void checkUserInfo(User user) throws ThingsboardException {
|
protected void checkUserInfo(User user) throws ThingsboardException {
|
||||||
|
ObjectNode info;
|
||||||
if (user.getAdditionalInfo() instanceof ObjectNode additionalInfo) {
|
if (user.getAdditionalInfo() instanceof ObjectNode additionalInfo) {
|
||||||
checkDashboardInfo(additionalInfo);
|
info = additionalInfo;
|
||||||
|
checkDashboardInfo(info);
|
||||||
UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId());
|
} else {
|
||||||
if (userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) {
|
info = JacksonUtil.newObjectNode();
|
||||||
additionalInfo.put("userCredentialsEnabled", true);
|
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 {
|
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.model.token.JwtTokenFactory;
|
||||||
import org.thingsboard.server.service.security.permission.Operation;
|
import org.thingsboard.server.service.security.permission.Operation;
|
||||||
import org.thingsboard.server.service.security.permission.Resource;
|
import org.thingsboard.server.service.security.permission.Resource;
|
||||||
import org.thingsboard.server.service.security.system.SystemSecurityService;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -123,7 +122,6 @@ public class UserController extends BaseController {
|
|||||||
|
|
||||||
private final MailService mailService;
|
private final MailService mailService;
|
||||||
private final JwtTokenFactory tokenFactory;
|
private final JwtTokenFactory tokenFactory;
|
||||||
private final SystemSecurityService systemSecurityService;
|
|
||||||
private final ApplicationEventPublisher eventPublisher;
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
private final TbUserService tbUserService;
|
private final TbUserService tbUserService;
|
||||||
private final EntityQueryService entityQueryService;
|
private final EntityQueryService entityQueryService;
|
||||||
|
|||||||
@ -170,6 +170,7 @@ public class ThingsboardInstallService {
|
|||||||
log.info("Installing DataBase schema for entities...");
|
log.info("Installing DataBase schema for entities...");
|
||||||
|
|
||||||
entityDatabaseSchemaService.createDatabaseSchema();
|
entityDatabaseSchemaService.createDatabaseSchema();
|
||||||
|
entityDatabaseSchemaService.createSchemaVersion();
|
||||||
|
|
||||||
entityDatabaseSchemaService.createOrUpdateViewsAndFunctions();
|
entityDatabaseSchemaService.createOrUpdateViewsAndFunctions();
|
||||||
entityDatabaseSchemaService.createOrUpdateDeviceInfoView(persistToTelemetry);
|
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.RelationActionEvent;
|
||||||
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
|
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
|
||||||
import org.thingsboard.server.dao.tenant.TenantService;
|
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
|
* 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) {
|
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) {
|
if (user.getAdditionalInfo() instanceof NullNode) {
|
||||||
user.setAdditionalInfo(null);
|
user.setAdditionalInfo(null);
|
||||||
}
|
}
|
||||||
if (user.getAdditionalInfo() instanceof ObjectNode additionalInfo) {
|
if (user.getAdditionalInfo() instanceof ObjectNode additionalInfo) {
|
||||||
additionalInfo.remove(UserServiceImpl.FAILED_LOGIN_ATTEMPTS);
|
|
||||||
additionalInfo.remove(UserServiceImpl.LAST_LOGIN_TS);
|
|
||||||
if (additionalInfo.isEmpty()) {
|
if (additionalInfo.isEmpty()) {
|
||||||
user.setAdditionalInfo(null);
|
user.setAdditionalInfo(null);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -23,4 +23,6 @@ public interface EntityDatabaseSchemaService extends DatabaseSchemaService {
|
|||||||
|
|
||||||
void createCustomerTitleUniqueConstraintIfNotExists();
|
void createCustomerTitleUniqueConstraintIfNotExists();
|
||||||
|
|
||||||
|
void createSchemaVersion();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,10 @@
|
|||||||
package org.thingsboard.server.service.install;
|
package org.thingsboard.server.service.install;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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.context.annotation.Profile;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@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_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";
|
public static final String SCHEMA_VIEWS_AND_FUNCTIONS_SQL = "schema-views-and-functions.sql";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BuildProperties buildProperties;
|
||||||
|
@Autowired
|
||||||
|
private JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
public SqlEntityDatabaseSchemaService() {
|
public SqlEntityDatabaseSchemaService() {
|
||||||
super(SCHEMA_ENTITIES_SQL, SCHEMA_ENTITIES_IDX_SQL);
|
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; $$;",
|
"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!");
|
"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;
|
package org.thingsboard.server.service.install;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.context.annotation.Profile;
|
import org.springframework.context.annotation.Profile;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.thingsboard.server.dao.util.SqlTsDao;
|
import org.thingsboard.server.dao.util.SqlTsDao;
|
||||||
@ -25,9 +24,6 @@ import org.thingsboard.server.dao.util.SqlTsDao;
|
|||||||
@Profile("install")
|
@Profile("install")
|
||||||
public class SqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService implements TsDatabaseSchemaService {
|
public class SqlTsDatabaseSchemaService extends SqlAbstractDatabaseSchemaService implements TsDatabaseSchemaService {
|
||||||
|
|
||||||
@Value("${sql.postgres.ts_key_value_partitioning:MONTHS}")
|
|
||||||
private String partitionType;
|
|
||||||
|
|
||||||
public SqlTsDatabaseSchemaService() {
|
public SqlTsDatabaseSchemaService() {
|
||||||
super("schema-ts-psql.sql", null);
|
super("schema-ts-psql.sql", null);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -265,7 +265,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (actionType == ActionType.LOGIN && e == null) {
|
if (actionType == ActionType.LOGIN && e == null) {
|
||||||
userService.setLastLoginTs(user.getTenantId(), user.getId());
|
userService.updateLastLoginTs(user.getTenantId(), user.getId());
|
||||||
}
|
}
|
||||||
auditLogService.logEntityAction(
|
auditLogService.logEntityAction(
|
||||||
user.getTenantId(), user.getCustomerId(), user.getId(),
|
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.StringUtils;
|
||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.UserActivationLink;
|
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.Authority;
|
||||||
import org.thingsboard.server.common.data.security.UserCredentials;
|
import org.thingsboard.server.common.data.security.UserCredentials;
|
||||||
import org.thingsboard.server.common.data.security.model.SecuritySettings;
|
import org.thingsboard.server.common.data.security.model.SecuritySettings;
|
||||||
@ -67,31 +68,30 @@ public class AuthControllerTest extends AbstractControllerTest {
|
|||||||
.andExpect(status().isUnauthorized());
|
.andExpect(status().isUnauthorized());
|
||||||
|
|
||||||
loginSysAdmin();
|
loginSysAdmin();
|
||||||
doGet("/api/auth/user")
|
User user = getCurrentUser();
|
||||||
.andExpect(status().isOk())
|
assertThat(user.getAuthority()).isEqualTo(Authority.SYS_ADMIN);
|
||||||
.andExpect(jsonPath("$.authority", is(Authority.SYS_ADMIN.name())))
|
assertThat(user.getEmail()).isEqualTo(SYS_ADMIN_EMAIL);
|
||||||
.andExpect(jsonPath("$.email", is(SYS_ADMIN_EMAIL)));
|
|
||||||
|
|
||||||
loginTenantAdmin();
|
loginTenantAdmin();
|
||||||
doGet("/api/auth/user")
|
user = getCurrentUser();
|
||||||
.andExpect(status().isOk())
|
assertThat(user.getAuthority()).isEqualTo(Authority.TENANT_ADMIN);
|
||||||
.andExpect(jsonPath("$.authority", is(Authority.TENANT_ADMIN.name())))
|
assertThat(user.getEmail()).isEqualTo(TENANT_ADMIN_EMAIL);
|
||||||
.andExpect(jsonPath("$.email", is(TENANT_ADMIN_EMAIL)));
|
|
||||||
|
|
||||||
loginCustomerUser();
|
loginCustomerUser();
|
||||||
doGet("/api/auth/user")
|
user = getCurrentUser();
|
||||||
.andExpect(status().isOk())
|
assertThat(user.getAuthority()).isEqualTo(Authority.CUSTOMER_USER);
|
||||||
.andExpect(jsonPath("$.authority", is(Authority.CUSTOMER_USER.name())))
|
assertThat(user.getEmail()).isEqualTo(CUSTOMER_USER_EMAIL);
|
||||||
.andExpect(jsonPath("$.email", is(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
|
@Test
|
||||||
public void testLoginLogout() throws Exception {
|
public void testLoginLogout() throws Exception {
|
||||||
loginSysAdmin();
|
loginSysAdmin();
|
||||||
doGet("/api/auth/user")
|
User user = getCurrentUser();
|
||||||
.andExpect(status().isOk())
|
assertThat(user.getAuthority()).isEqualTo(Authority.SYS_ADMIN);
|
||||||
.andExpect(jsonPath("$.authority", is(Authority.SYS_ADMIN.name())))
|
assertThat(user.getEmail()).isEqualTo(SYS_ADMIN_EMAIL);
|
||||||
.andExpect(jsonPath("$.email", is(SYS_ADMIN_EMAIL)));
|
|
||||||
|
|
||||||
TimeUnit.SECONDS.sleep(1); //We need to make sure that event for invalidating token was successfully processed
|
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();
|
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
|
@Test
|
||||||
public void testRefreshToken() throws Exception {
|
public void testRefreshToken() throws Exception {
|
||||||
loginSysAdmin();
|
loginSysAdmin();
|
||||||
doGet("/api/auth/user")
|
User user = getCurrentUser();
|
||||||
.andExpect(status().isOk())
|
assertThat(user.getAuthority()).isEqualTo(Authority.SYS_ADMIN);
|
||||||
.andExpect(jsonPath("$.authority", is(Authority.SYS_ADMIN.name())))
|
assertThat(user.getEmail()).isEqualTo(SYS_ADMIN_EMAIL);
|
||||||
.andExpect(jsonPath("$.email", is(SYS_ADMIN_EMAIL)));
|
|
||||||
|
|
||||||
refreshToken();
|
refreshToken();
|
||||||
doGet("/api/auth/user")
|
user = getCurrentUser();
|
||||||
.andExpect(status().isOk())
|
assertThat(user.getAuthority()).isEqualTo(Authority.SYS_ADMIN);
|
||||||
.andExpect(jsonPath("$.authority", is(Authority.SYS_ADMIN.name())))
|
assertThat(user.getEmail()).isEqualTo(SYS_ADMIN_EMAIL);
|
||||||
.andExpect(jsonPath("$.email", is(SYS_ADMIN_EMAIL)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -277,6 +303,14 @@ public class AuthControllerTest extends AbstractControllerTest {
|
|||||||
doPost("/api/admin/securitySettings", securitySettings).andExpect(status().isOk());
|
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 {
|
private String getActivationLink(User user) throws Exception {
|
||||||
return doGet("/api/user/" + user.getId() + "/activationLink", String.class);
|
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.getActionStatus()).isEqualTo(ActionStatus.SUCCESS);
|
||||||
assertThat(successfulLogInAuditLog.getUserName()).isEqualTo(username);
|
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())
|
.get("lastLoginTs").asLong())
|
||||||
.isGreaterThan(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(3));
|
.isGreaterThan(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(3));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -113,6 +113,7 @@ public class UserControllerTest extends AbstractControllerTest {
|
|||||||
Assert.assertEquals(email, savedUser.getEmail());
|
Assert.assertEquals(email, savedUser.getEmail());
|
||||||
|
|
||||||
User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
|
User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
|
||||||
|
foundUser.setAdditionalInfo(savedUser.getAdditionalInfo());
|
||||||
Assert.assertEquals(foundUser, savedUser);
|
Assert.assertEquals(foundUser, savedUser);
|
||||||
|
|
||||||
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundUser, foundUser,
|
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(foundUser, foundUser,
|
||||||
@ -265,6 +266,7 @@ public class UserControllerTest extends AbstractControllerTest {
|
|||||||
User savedUser = doPost("/api/user", user, User.class);
|
User savedUser = doPost("/api/user", user, User.class);
|
||||||
User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
|
User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
|
||||||
Assert.assertNotNull(foundUser);
|
Assert.assertNotNull(foundUser);
|
||||||
|
foundUser.setAdditionalInfo(savedUser.getAdditionalInfo());
|
||||||
Assert.assertEquals(savedUser, foundUser);
|
Assert.assertEquals(savedUser, foundUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -47,7 +47,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testCreateUpdateDeleteTenantUser() throws Exception {
|
public void testCreateUpdateDeleteTenantUser() throws Exception {
|
||||||
// create user
|
// create user
|
||||||
edgeImitator.expectMessageAmount(6);
|
edgeImitator.expectMessageAmount(3);
|
||||||
User newTenantAdmin = new User();
|
User newTenantAdmin = new User();
|
||||||
newTenantAdmin.setAuthority(Authority.TENANT_ADMIN);
|
newTenantAdmin.setAuthority(Authority.TENANT_ADMIN);
|
||||||
newTenantAdmin.setTenantId(tenantId);
|
newTenantAdmin.setTenantId(tenantId);
|
||||||
@ -55,9 +55,9 @@ public class UserEdgeTest extends AbstractEdgeTest {
|
|||||||
newTenantAdmin.setFirstName("Boris");
|
newTenantAdmin.setFirstName("Boris");
|
||||||
newTenantAdmin.setLastName("Johnson");
|
newTenantAdmin.setLastName("Johnson");
|
||||||
User savedTenantAdmin = createUser(newTenantAdmin, "tenant");
|
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.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - x1 user update msg and x2 user credentials update msgs (create + authenticate user)
|
||||||
Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size());
|
Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size());
|
||||||
Assert.assertEquals(4, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size());
|
Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size());
|
||||||
Optional<UserUpdateMsg> userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
|
Optional<UserUpdateMsg> userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
|
||||||
Assert.assertTrue(userUpdateMsgOpt.isPresent());
|
Assert.assertTrue(userUpdateMsgOpt.isPresent());
|
||||||
UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get();
|
UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get();
|
||||||
@ -133,7 +133,7 @@ public class UserEdgeTest extends AbstractEdgeTest {
|
|||||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||||
|
|
||||||
// create user
|
// create user
|
||||||
edgeImitator.expectMessageAmount(6);
|
edgeImitator.expectMessageAmount(3);
|
||||||
User customerUser = new User();
|
User customerUser = new User();
|
||||||
customerUser.setAuthority(Authority.CUSTOMER_USER);
|
customerUser.setAuthority(Authority.CUSTOMER_USER);
|
||||||
customerUser.setTenantId(tenantId);
|
customerUser.setTenantId(tenantId);
|
||||||
@ -142,9 +142,9 @@ public class UserEdgeTest extends AbstractEdgeTest {
|
|||||||
customerUser.setFirstName("John");
|
customerUser.setFirstName("John");
|
||||||
customerUser.setLastName("Edwards");
|
customerUser.setLastName("Edwards");
|
||||||
User savedCustomerUser = createUser(customerUser, "customer");
|
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.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - x1 user update msg and x2 user credentials update msgs (create + authenticate user)
|
||||||
Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size());
|
Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size());
|
||||||
Assert.assertEquals(4, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size());
|
Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size());
|
||||||
Optional<UserUpdateMsg> userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
|
Optional<UserUpdateMsg> userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
|
||||||
Assert.assertTrue(userUpdateMsgOpt.isPresent());
|
Assert.assertTrue(userUpdateMsgOpt.isPresent());
|
||||||
UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get();
|
UserUpdateMsg userUpdateMsg = userUpdateMsgOpt.get();
|
||||||
|
|||||||
@ -114,6 +114,13 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
|
|||||||
CoapTestCallback callbackCoap = new TestCoapCallbackForRPC(client, false, protobuf);
|
CoapTestCallback callbackCoap = new TestCoapCallbackForRPC(client, false, protobuf);
|
||||||
|
|
||||||
CoapObserveRelation observeRelation = client.getObserveRelation(callbackCoap);
|
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)";
|
String awaitAlias = "await Two Way Rpc (client.getObserveRelation)";
|
||||||
await(awaitAlias)
|
await(awaitAlias)
|
||||||
.atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
.atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
|
||||||
@ -146,7 +153,7 @@ public abstract class AbstractCoapServerSideRpcIntegrationTest extends AbstractC
|
|||||||
validateTwoWayStateChangedNotification(callbackCoap, expectedResponseResult, actualResult);
|
validateTwoWayStateChangedNotification(callbackCoap, expectedResponseResult, actualResult);
|
||||||
|
|
||||||
observeRelation.proactiveCancel();
|
observeRelation.proactiveCancel();
|
||||||
assertTrue(observeRelation.isCanceled());
|
return observeRelation.isCanceled();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processOnLoadResponse(CoapResponse response, CoapTestClient client) {
|
protected void processOnLoadResponse(CoapResponse response, CoapTestClient client) {
|
||||||
|
|||||||
@ -97,7 +97,7 @@ public interface UserService extends EntityDaoService {
|
|||||||
|
|
||||||
int increaseFailedLoginAttempts(TenantId tenantId, UserId userId);
|
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);
|
void saveMobileSession(TenantId tenantId, UserId userId, String mobileToken, MobileSessionInfo sessionInfo);
|
||||||
|
|
||||||
|
|||||||
@ -40,6 +40,8 @@ public class UserCredentials extends BaseDataWithAdditionalInfo<UserCredentialsI
|
|||||||
private Long activateTokenExpTime;
|
private Long activateTokenExpTime;
|
||||||
private String resetToken;
|
private String resetToken;
|
||||||
private Long resetTokenExpTime;
|
private Long resetTokenExpTime;
|
||||||
|
private Long lastLoginTs;
|
||||||
|
private Integer failedLoginAttempts;
|
||||||
|
|
||||||
public UserCredentials() {
|
public UserCredentials() {
|
||||||
super();
|
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_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_PROPERTY = "reset_token";
|
||||||
public static final String USER_CREDENTIALS_RESET_TOKEN_EXP_TIME_PROPERTY = "reset_token_exp_time";
|
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.
|
* User settings constants.
|
||||||
|
|||||||
@ -60,18 +60,21 @@ public final class UserCredentialsEntity extends BaseSqlEntity<UserCredentials>
|
|||||||
private Long resetTokenExpTime;
|
private Long resetTokenExpTime;
|
||||||
|
|
||||||
@Convert(converter = JsonConverter.class)
|
@Convert(converter = JsonConverter.class)
|
||||||
@Column(name = ModelConstants.USER_CREDENTIALS_ADDITIONAL_PROPERTY)
|
@Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY)
|
||||||
private JsonNode additionalInfo;
|
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() {
|
public UserCredentialsEntity() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserCredentialsEntity(UserCredentials userCredentials) {
|
public UserCredentialsEntity(UserCredentials userCredentials) {
|
||||||
if (userCredentials.getId() != null) {
|
super(userCredentials);
|
||||||
this.setUuid(userCredentials.getId().getId());
|
|
||||||
}
|
|
||||||
this.setCreatedTime(userCredentials.getCreatedTime());
|
|
||||||
if (userCredentials.getUserId() != null) {
|
if (userCredentials.getUserId() != null) {
|
||||||
this.userId = userCredentials.getUserId().getId();
|
this.userId = userCredentials.getUserId().getId();
|
||||||
}
|
}
|
||||||
@ -82,6 +85,8 @@ public final class UserCredentialsEntity extends BaseSqlEntity<UserCredentials>
|
|||||||
this.resetToken = userCredentials.getResetToken();
|
this.resetToken = userCredentials.getResetToken();
|
||||||
this.resetTokenExpTime = userCredentials.getResetTokenExpTime();
|
this.resetTokenExpTime = userCredentials.getResetTokenExpTime();
|
||||||
this.additionalInfo = userCredentials.getAdditionalInfo();
|
this.additionalInfo = userCredentials.getAdditionalInfo();
|
||||||
|
this.lastLoginTs = userCredentials.getLastLoginTs();
|
||||||
|
this.failedLoginAttempts = userCredentials.getFailedLoginAttempts();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -98,6 +103,8 @@ public final class UserCredentialsEntity extends BaseSqlEntity<UserCredentials>
|
|||||||
userCredentials.setResetToken(resetToken);
|
userCredentials.setResetToken(resetToken);
|
||||||
userCredentials.setResetTokenExpTime(resetTokenExpTime);
|
userCredentials.setResetTokenExpTime(resetTokenExpTime);
|
||||||
userCredentials.setAdditionalInfo(additionalInfo);
|
userCredentials.setAdditionalInfo(additionalInfo);
|
||||||
|
userCredentials.setLastLoginTs(lastLoginTs);
|
||||||
|
userCredentials.setFailedLoginAttempts(failedLoginAttempts);
|
||||||
return userCredentials;
|
return userCredentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -69,4 +69,19 @@ public class JpaUserCredentialsDao extends JpaAbstractDao<UserCredentialsEntity,
|
|||||||
userCredentialsRepository.removeByUserId(userId.getId());
|
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;
|
package org.thingsboard.server.dao.sql.user;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
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.springframework.transaction.annotation.Transactional;
|
||||||
import org.thingsboard.server.dao.model.sql.UserCredentialsEntity;
|
import org.thingsboard.server.dao.model.sql.UserCredentialsEntity;
|
||||||
|
|
||||||
@ -35,4 +37,19 @@ public interface UserCredentialsRepository extends JpaRepository<UserCredentials
|
|||||||
@Transactional
|
@Transactional
|
||||||
void removeByUserId(UUID userId);
|
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 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.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
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.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import lombok.RequiredArgsConstructor;
|
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 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;
|
private static final int DEFAULT_TOKEN_LENGTH = 30;
|
||||||
public static final String INCORRECT_USER_ID = "Incorrect userId ";
|
public static final String INCORRECT_USER_ID = "Incorrect userId ";
|
||||||
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
|
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
|
||||||
|
|
||||||
private static final String USER_CREDENTIALS_ENABLED = "userCredentialsEnabled";
|
|
||||||
|
|
||||||
@Value("${security.user_login_case_sensitive:true}")
|
@Value("${security.user_login_case_sensitive:true}")
|
||||||
private boolean userLoginCaseSensitive;
|
private boolean userLoginCaseSensitive;
|
||||||
|
|
||||||
@ -428,40 +420,28 @@ public class UserServiceImpl extends AbstractCachedEntityService<UserCacheKey, U
|
|||||||
customerUsersRemover.removeEntities(tenantId, customerId);
|
customerUsersRemover.removeEntities(tenantId, customerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public void setUserCredentialsEnabled(TenantId tenantId, UserId userId, boolean enabled) {
|
public void setUserCredentialsEnabled(TenantId tenantId, UserId userId, boolean enabled) {
|
||||||
log.trace("Executing setUserCredentialsEnabled [{}], [{}]", userId, enabled);
|
log.trace("Executing setUserCredentialsEnabled [{}], [{}]", userId, enabled);
|
||||||
validateId(userId, id -> INCORRECT_USER_ID + id);
|
validateId(userId, id -> INCORRECT_USER_ID + id);
|
||||||
UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId());
|
UserCredentials userCredentials = userCredentialsDao.findByUserId(tenantId, userId.getId());
|
||||||
userCredentials.setEnabled(enabled);
|
userCredentials.setEnabled(enabled);
|
||||||
saveUserCredentials(tenantId, userCredentials);
|
|
||||||
|
|
||||||
User user = findUserById(tenantId, userId);
|
|
||||||
user.setAdditionalInfoField(USER_CREDENTIALS_ENABLED, BooleanNode.valueOf(enabled));
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
resetFailedLoginAttempts(user);
|
userCredentials.setFailedLoginAttempts(0);
|
||||||
}
|
}
|
||||||
saveUser(tenantId, user);
|
saveUserCredentials(tenantId, userCredentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetFailedLoginAttempts(TenantId tenantId, UserId userId) {
|
public void resetFailedLoginAttempts(TenantId tenantId, UserId userId) {
|
||||||
log.trace("Executing onUserLoginSuccessful [{}]", userId);
|
log.trace("Executing resetFailedLoginAttempts [{}]", userId);
|
||||||
User user = findUserById(tenantId, userId);
|
userCredentialsDao.setFailedLoginAttempts(tenantId, userId, 0);
|
||||||
resetFailedLoginAttempts(user);
|
|
||||||
saveUser(tenantId, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resetFailedLoginAttempts(User user) {
|
|
||||||
user.setAdditionalInfoField(FAILED_LOGIN_ATTEMPTS, IntNode.valueOf(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLastLoginTs(TenantId tenantId, UserId userId) {
|
public void updateLastLoginTs(TenantId tenantId, UserId userId) {
|
||||||
User user = findUserById(tenantId, userId);
|
userCredentialsDao.setLastLoginTs(tenantId, userId, System.currentTimeMillis());
|
||||||
user.setAdditionalInfoField(LAST_LOGIN_TS, new LongNode(System.currentTimeMillis()));
|
|
||||||
saveUser(tenantId, user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -502,18 +482,8 @@ public class UserServiceImpl extends AbstractCachedEntityService<UserCacheKey, U
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int increaseFailedLoginAttempts(TenantId tenantId, UserId userId) {
|
public int increaseFailedLoginAttempts(TenantId tenantId, UserId userId) {
|
||||||
log.trace("Executing onUserLoginIncorrectCredentials [{}]", userId);
|
log.trace("Executing increaseFailedLoginAttempts [{}]", userId);
|
||||||
User user = findUserById(tenantId, userId);
|
return userCredentialsDao.incrementFailedLoginAttempts(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePasswordHistory(UserCredentials userCredentials) {
|
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)
|
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 (
|
CREATE TABLE IF NOT EXISTS admin_settings (
|
||||||
id uuid NOT NULL CONSTRAINT admin_settings_pkey PRIMARY KEY,
|
id uuid NOT NULL CONSTRAINT admin_settings_pkey PRIMARY KEY,
|
||||||
tenant_id uuid NOT NULL,
|
tenant_id uuid NOT NULL,
|
||||||
@ -497,7 +485,9 @@ CREATE TABLE IF NOT EXISTS user_credentials (
|
|||||||
reset_token varchar(255) UNIQUE,
|
reset_token varchar(255) UNIQUE,
|
||||||
reset_token_exp_time BIGINT,
|
reset_token_exp_time BIGINT,
|
||||||
user_id uuid UNIQUE,
|
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 (
|
CREATE TABLE IF NOT EXISTS widget_type (
|
||||||
|
|||||||
@ -220,8 +220,8 @@ export interface ScadaSymbolMetadata {
|
|||||||
|
|
||||||
export const emptyMetadata = (width?: number, height?: number): ScadaSymbolMetadata => ({
|
export const emptyMetadata = (width?: number, height?: number): ScadaSymbolMetadata => ({
|
||||||
title: '',
|
title: '',
|
||||||
widgetSizeX: width ? width/100 : 3,
|
widgetSizeX: width ? Math.max(Math.round(width/100), 1) : 3,
|
||||||
widgetSizeY: height ? height/100 : 3,
|
widgetSizeY: height ? Math.max(Math.round(height/100), 1) : 3,
|
||||||
tags: [],
|
tags: [],
|
||||||
behavior: [],
|
behavior: [],
|
||||||
properties: []
|
properties: []
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user