Browse Source

Merge pull request #14352 from volodymyr-babak/support-user-sync

Sync User & UserCredentials from Edge to Cloud
pull/14454/head
Viacheslav Klimov 11 months ago
committed by GitHub
parent
commit
0b60099308
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 5
      application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java
  2. 24
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  3. 155
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/BaseUserProcessor.java
  4. 101
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java
  5. 31
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserProcessor.java
  6. 393
      application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java
  7. 6
      common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java
  8. 2
      common/edge-api/src/main/proto/edge.proto
  9. 38
      dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java

5
application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java

@ -75,6 +75,7 @@ import org.thingsboard.server.service.edge.rpc.processor.resource.ResourceEdgePr
import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.rule.RuleChainMetadataEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.telemetry.TelemetryEdgeProcessor;
import org.thingsboard.server.service.edge.rpc.processor.user.UserProcessor;
import org.thingsboard.server.service.edge.rpc.sync.EdgeRequestsService;
import org.thingsboard.server.service.executors.GrpcCallbackExecutorService;
@ -265,9 +266,13 @@ public class EdgeContextComponent {
@Autowired
private AiModelService aiModelService;
@Autowired
private AiModelProcessor aiModelProcessor;
@Autowired
private UserProcessor userProcessor;
public EdgeProcessor getProcessor(EdgeEventType edgeEventType) {
EdgeProcessor processor = processorMap.get(edgeEventType);
if (processor == null) {

24
application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java

@ -84,6 +84,8 @@ import org.thingsboard.server.gen.edge.v1.SyncCompletedMsg;
import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.gen.edge.v1.UplinkResponseMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
import org.thingsboard.server.gen.edge.v1.WidgetBundleTypesRequestMsg;
import org.thingsboard.server.service.edge.EdgeContextComponent;
import org.thingsboard.server.service.edge.EdgeMsgConstructorUtils;
@ -101,6 +103,7 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiConsumer;
@ -122,6 +125,7 @@ public abstract class EdgeGrpcSession implements Closeable {
private final EdgeSessionState sessionState = new EdgeSessionState();
private final ReentrantLock downlinkMsgLock = new ReentrantLock();
private final Lock sequenceDependencyLock = new ReentrantLock();
protected EdgeContextComponent ctx;
protected Edge edge;
@ -940,6 +944,26 @@ public abstract class EdgeGrpcSession implements Closeable {
result.add(ctx.getAiModelProcessor().processAiModelMsgFromEdge(edge.getTenantId(), edge, aiModelUpdateMsg));
}
}
if (uplinkMsg.getUserUpdateMsgCount() > 0) {
for (UserUpdateMsg userUpdateMsg : uplinkMsg.getUserUpdateMsgList()) {
sequenceDependencyLock.lock();
try {
result.add(ctx.getUserProcessor().processUserMsgFromEdge(edge.getTenantId(), edge, userUpdateMsg));
} finally {
sequenceDependencyLock.unlock();
}
}
}
if (uplinkMsg.getUserCredentialsUpdateMsgCount() > 0) {
for (UserCredentialsUpdateMsg userCredentialsUpdateMsg : uplinkMsg.getUserCredentialsUpdateMsgList()) {
sequenceDependencyLock.lock();
try {
result.add(ctx.getUserProcessor().processUserCredentialsMsgFromEdge(edge.getTenantId(), edge, userCredentialsUpdateMsg));
} finally {
sequenceDependencyLock.unlock();
}
}
}
} catch (Exception e) {
String failureMsg = String.format("Can't process uplink msg [%s] from edge", uplinkMsg);
log.trace("[{}][{}] Can't process uplink msg [{}]", tenantId, edge.getId(), uplinkMsg, e);

155
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/BaseUserProcessor.java

@ -0,0 +1,155 @@
/**
* Copyright © 2016-2025 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor.user;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.util.Pair;
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.edge.Edge;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
@Slf4j
public abstract class BaseUserProcessor extends BaseEdgeProcessor {
@Autowired
private DataValidator<User> userValidator;
protected Pair<Boolean, Boolean> saveOrUpdateUser(TenantId tenantId, UserId userId, UserUpdateMsg userUpdateMsg) {
boolean isCreated = false;
boolean userEmailUpdated = false;
try {
User user = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);
if (user == null) {
throw new IllegalArgumentException(String.format("[%s] Failed to parse User from UserUpdateMsg: %s", tenantId, userUpdateMsg));
}
User userById = edgeCtx.getUserService().findUserById(tenantId, userId);
if (userById == null) {
isCreated = true;
user.setId(null);
} else {
user.setId(userId);
}
String userEmail = user.getEmail();
User existing = edgeCtx.getUserService().findUserByTenantIdAndEmail(tenantId, user.getEmail());
if (existing != null && !existing.getId().equals(user.getId())) {
String[] splitEmail = userEmail.split("@");
userEmail = splitEmail[0] + "_" + StringUtils.randomAlphanumeric(15) + "@" + splitEmail[1];
log.warn("[{}] User with email {} already exists. Renaming User email to {}",
tenantId, user.getEmail(), userEmail);
userEmailUpdated = true;
}
user.setEmail(userEmail);
setCustomerId(tenantId, isCreated ? null : userById.getCustomerId(), user, userUpdateMsg);
userValidator.validate(user, User::getTenantId);
if (isCreated) {
user.setId(userId);
}
edgeCtx.getUserService().saveUser(tenantId, user, false);
} catch (Exception e) {
log.error("[{}] Failed to process user update msg [{}]", tenantId, userUpdateMsg, e);
throw e;
}
return Pair.of(isCreated, userEmailUpdated);
}
protected void deleteUserAndPushEntityDeletedEventToRuleEngine(TenantId tenantId, UserId userId) {
deleteUserAndPushEntityDeletedEventToRuleEngine(tenantId, userId, null);
}
protected void deleteUserAndPushEntityDeletedEventToRuleEngine(TenantId tenantId, UserId userId, Edge edge) {
User removedUser = deleteUser(tenantId, userId);
if (removedUser == null) {
return;
}
CustomerId userCustomerId = removedUser.getCustomerId();
String userAsString = JacksonUtil.toString(removedUser);
TbMsgMetaData msgMetaData = edge == null ? new TbMsgMetaData() : getEdgeActionTbMsgMetaData(edge, userCustomerId);
addRemovedUserMetadata(msgMetaData, removedUser);
pushEntityEventToRuleEngine(tenantId, userId, userCustomerId, TbMsgType.ENTITY_DELETED, userAsString, msgMetaData);
}
private User deleteUser(TenantId tenantId, UserId userId) {
User userById = edgeCtx.getUserService().findUserById(tenantId, userId);
if (userById == null) {
log.trace("[{}] User with id {} does not exist", tenantId, userId);
return null;
}
edgeCtx.getUserService().deleteUser(tenantId, userById);
return userById;
}
protected void updateUserCredentials(TenantId tenantId, UserCredentialsUpdateMsg updateMsg) {
UserCredentials userCredentials = JacksonUtil.fromString(updateMsg.getEntity(), UserCredentials.class, true);
if (userCredentials == null) {
throw new IllegalArgumentException(String.format("[%s] Failed to parse UserCredentials from updateMsg: %s", tenantId, updateMsg));
}
User user = edgeCtx.getUserService().findUserById(tenantId, userCredentials.getUserId());
if (user == null) {
log.warn("[{}] Can't find user by id [{}] skipping credentials update. UserCredentialsUpdateMsg [{}]",
tenantId, userCredentials.getUserId(), updateMsg);
return;
}
log.debug("[{}] Updating user credentials for user [{}]. New credentials Id [{}], enabled [{}]",
tenantId, user.getName(), userCredentials.getId(), userCredentials.isEnabled());
try {
UserCredentials userCredentialsByUserId = edgeCtx.getUserService().findUserCredentialsByUserId(tenantId, user.getId());
if (userCredentialsByUserId != null && !userCredentialsByUserId.getId().equals(userCredentials.getId())) {
edgeCtx.getUserService().deleteUserCredentials(tenantId, userCredentialsByUserId);
}
edgeCtx.getUserService().saveUserCredentials(tenantId, userCredentials, false);
} catch (Exception e) {
log.error("[{}] Can't update user credentials for user [{}], userCredentialsUpdateMsg [{}]",
tenantId, user.getName(), updateMsg, e);
throw new RuntimeException(e);
}
}
private void addRemovedUserMetadata(TbMsgMetaData metaData, User removedUser) {
metaData.putValue("userId", removedUser.getId().toString());
metaData.putValue("userName", removedUser.getName());
metaData.putValue("userEmail", removedUser.getEmail());
if (removedUser.getFirstName() != null) {
metaData.putValue("userFirstName", removedUser.getFirstName());
}
if (removedUser.getLastName() != null) {
metaData.putValue("userLastName", removedUser.getLastName());
}
}
protected abstract void setCustomerId(TenantId tenantId, CustomerId customerId, User user, UserUpdateMsg userUpdateMsg);
}

101
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserEdgeProcessor.java

@ -15,26 +15,110 @@
*/
package org.thingsboard.server.service.edge.rpc.processor.user;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.edge.EdgeEvent;
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.msg.TbMsgType;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.EdgeVersion;
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.edge.EdgeMsgConstructorUtils;
import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
import java.util.UUID;
@Slf4j
@Component
@TbCoreComponent
public class UserEdgeProcessor extends BaseEdgeProcessor {
public class UserEdgeProcessor extends BaseUserProcessor implements UserProcessor {
@Override
public ListenableFuture<Void> processUserMsgFromEdge(TenantId tenantId, Edge edge, UserUpdateMsg userUpdateMsg) {
log.trace("[{}] executing processUserMsgFromEdge [{}] from edge [{}]", tenantId, userUpdateMsg, edge.getId());
UserId userId = new UserId(new UUID(userUpdateMsg.getIdMSB(), userUpdateMsg.getIdLSB()));
try {
edgeSynchronizationManager.getEdgeId().set(edge.getId());
return switch (userUpdateMsg.getMsgType()) {
case ENTITY_CREATED_RPC_MESSAGE, ENTITY_UPDATED_RPC_MESSAGE -> {
saveOrUpdateUser(tenantId, userId, userUpdateMsg, edge);
yield Futures.immediateFuture(null);
}
case ENTITY_DELETED_RPC_MESSAGE -> {
deleteUserAndPushEntityDeletedEventToRuleEngine(tenantId, userId, edge);
yield Futures.immediateFuture(null);
}
default -> handleUnsupportedMsgType(userUpdateMsg.getMsgType());
};
} catch (DataValidationException e) {
if (e.getMessage().contains("limit reached")) {
log.warn("[{}] Number of allowed users violated {}", tenantId, userUpdateMsg, e);
return Futures.immediateFuture(null);
} else {
return Futures.immediateFailedFuture(e);
}
} finally {
edgeSynchronizationManager.getEdgeId().remove();
}
}
@Override
public ListenableFuture<Void> processUserCredentialsMsgFromEdge(TenantId tenantId, Edge edge, UserCredentialsUpdateMsg userCredentialsUpdateMsg) {
log.debug("[{}] Executing processUserCredentialsMsgFromEdge, userCredentialsUpdateMsg [{}]", tenantId, userCredentialsUpdateMsg);
try {
edgeSynchronizationManager.getEdgeId().set(edge.getId());
super.updateUserCredentials(tenantId, userCredentialsUpdateMsg);
} finally {
edgeSynchronizationManager.getEdgeId().remove();
}
return Futures.immediateFuture(null);
}
private void saveOrUpdateUser(TenantId tenantId, UserId userId, UserUpdateMsg userUpdateMsg, Edge edge) {
Pair<Boolean, Boolean> resultPair = super.saveOrUpdateUser(tenantId, userId, userUpdateMsg);
boolean isCreated = resultPair.getFirst();
if (isCreated) {
createRelationFromEdge(tenantId, edge.getId(), userId);
pushUserCreatedEventToRuleEngine(tenantId, edge, userId);
}
boolean userEmailUpdated = resultPair.getSecond();
if (userEmailUpdated) {
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER, EdgeEventActionType.UPDATED, userId, null);
}
}
private void pushUserCreatedEventToRuleEngine(TenantId tenantId, Edge edge, UserId userId) {
try {
User user = edgeCtx.getUserService().findUserById(tenantId, userId);
if (user != null) {
String userAsString = JacksonUtil.toString(user);
TbMsgMetaData msgMetaData = getEdgeActionTbMsgMetaData(edge, user.getCustomerId());
pushEntityEventToRuleEngine(tenantId, userId, user.getCustomerId(), TbMsgType.ENTITY_CREATED, userAsString, msgMetaData);
}
} catch (Exception e) {
log.warn("[{}][{}] Failed to push user action to rule engine: {}", tenantId, userId, TbMsgType.ENTITY_CREATED.name(), e);
}
}
@Override
public DownlinkMsg convertEdgeEventToDownlink(EdgeEvent edgeEvent, EdgeVersion edgeVersion) {
@ -48,7 +132,7 @@ public class UserEdgeProcessor extends BaseEdgeProcessor {
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addUserUpdateMsg(EdgeMsgConstructorUtils.constructUserUpdatedMsg(msgType, user));
UserCredentials userCredentialsByUserId = edgeCtx.getUserService().findUserCredentialsByUserId(edgeEvent.getTenantId(), userId);
if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) {
if (userCredentialsByUserId != null) {
builder.addUserCredentialsUpdateMsg(EdgeMsgConstructorUtils.constructUserCredentialsUpdatedMsg(userCredentialsByUserId));
}
return builder.build();
@ -62,11 +146,10 @@ public class UserEdgeProcessor extends BaseEdgeProcessor {
}
case CREDENTIALS_UPDATED -> {
UserCredentials userCredentialsByUserId = edgeCtx.getUserService().findUserCredentialsByUserId(edgeEvent.getTenantId(), userId);
if (userCredentialsByUserId != null && userCredentialsByUserId.isEnabled()) {
UserCredentialsUpdateMsg userCredentialsUpdateMsg = EdgeMsgConstructorUtils.constructUserCredentialsUpdatedMsg(userCredentialsByUserId);
if (userCredentialsByUserId != null) {
return DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addUserCredentialsUpdateMsg(userCredentialsUpdateMsg)
.addUserCredentialsUpdateMsg(EdgeMsgConstructorUtils.constructUserCredentialsUpdatedMsg(userCredentialsByUserId))
.build();
}
}
@ -79,4 +162,10 @@ public class UserEdgeProcessor extends BaseEdgeProcessor {
return EdgeEventType.USER;
}
@Override
protected void setCustomerId(TenantId tenantId, CustomerId customerId, User user, UserUpdateMsg userUpdateMsg) {
CustomerId customerUUID = user.getCustomerId() != null ? user.getCustomerId() : customerId;
user.setCustomerId(customerUUID);
}
}

31
application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/user/UserProcessor.java

@ -0,0 +1,31 @@
/**
* Copyright © 2016-2025 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.edge.rpc.processor.user;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
import org.thingsboard.server.service.edge.rpc.processor.EdgeProcessor;
public interface UserProcessor extends EdgeProcessor {
ListenableFuture<Void> processUserMsgFromEdge(TenantId tenantId, Edge edge, UserUpdateMsg userUpdateMsg);
ListenableFuture<Void> processUserCredentialsMsgFromEdge(TenantId tenantId, Edge edge, UserCredentialsUpdateMsg userCredentialsUpdateMsg);
}

393
application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java

@ -15,15 +15,22 @@
*/
package org.thingsboard.server.edge;
import com.google.protobuf.AbstractMessage;
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.test.web.servlet.ResultMatcher;
import org.testcontainers.shaded.org.awaitility.Awaitility;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.EdgeUtils;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.UserCredentialsId;
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.dao.service.DaoSqlTest;
@ -32,11 +39,15 @@ import org.thingsboard.server.gen.edge.v1.UplinkMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsRequestMsg;
import org.thingsboard.server.gen.edge.v1.UserCredentialsUpdateMsg;
import org.thingsboard.server.gen.edge.v1.UserUpdateMsg;
import org.thingsboard.server.service.edge.EdgeMsgConstructorUtils;
import org.thingsboard.server.service.security.model.ChangePasswordRequest;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.user.UserServiceImpl.DEFAULT_TOKEN_LENGTH;
@DaoSqlTest
public class UserEdgeTest extends AbstractEdgeTest {
@ -44,216 +55,304 @@ public class UserEdgeTest extends AbstractEdgeTest {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
private static final String DEFAULT_FIRST_NAME = "Boris";
private static final String DEFAULT_LAST_NAME = "Johnson";
private static final String UPDATED_LAST_NAME = "Borisov";
private static final String DEFAULT_TENANT_ADMIN_EMAIL = "tenantAdmin@thingsboard.org";
private static final String DEFAULT_CUSTOMER_USER_EMAIL = "customerUser@thingsboard.org";
@Test
public void testCreateUpdateDeleteTenantUser() throws Exception {
// create user
edgeImitator.expectMessageAmount(3);
User newTenantAdmin = new User();
newTenantAdmin.setAuthority(Authority.TENANT_ADMIN);
newTenantAdmin.setTenantId(tenantId);
newTenantAdmin.setEmail("tenantAdmin@thingsboard.org");
newTenantAdmin.setFirstName("Boris");
newTenantAdmin.setLastName("Johnson");
User savedTenantAdmin = createUser(newTenantAdmin, "tenant");
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();
User userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);
Assert.assertNotNull(userMsg);
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, userUpdateMsg.getMsgType());
Assert.assertEquals(savedTenantAdmin.getId(), userMsg.getId());
Assert.assertEquals(savedTenantAdmin.getAuthority(), userMsg.getAuthority());
Assert.assertEquals(savedTenantAdmin.getEmail(), userMsg.getEmail());
Assert.assertEquals(savedTenantAdmin.getFirstName(), userMsg.getFirstName());
Assert.assertEquals(savedTenantAdmin.getLastName(), userMsg.getLastName());
Optional<UserCredentialsUpdateMsg> userCredentialsUpdateMsgOpt = edgeImitator.findMessageByType(UserCredentialsUpdateMsg.class);
Assert.assertTrue(userCredentialsUpdateMsgOpt.isPresent());
User newTenantAdmin = buildUser(Authority.TENANT_ADMIN, null);
User savedTenantAdmin = createAndVerifyUserOnEdge(newTenantAdmin);
// update user
edgeImitator.expectMessageAmount(2);
savedTenantAdmin.setLastName("Borisov");
savedTenantAdmin = doPost("/api/user", savedTenantAdmin, User.class);
Assert.assertTrue(edgeImitator.waitForMessages());
userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
Assert.assertTrue(userUpdateMsgOpt.isPresent());
userUpdateMsg = userUpdateMsgOpt.get();
userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);
Assert.assertNotNull(userMsg);
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, userUpdateMsg.getMsgType());
Assert.assertEquals(savedTenantAdmin.getLastName(), userMsg.getLastName());
updateAndVerifyUserLastName(savedTenantAdmin);
// update user credentials
login(savedTenantAdmin.getEmail(), "tenant");
updateAndVerifyUserCredentials(savedTenantAdmin);
loginTenantAdmin();
edgeImitator.expectMessageAmount(1);
ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest();
changePasswordRequest.setCurrentPassword("tenant");
changePasswordRequest.setNewPassword("newTenant");
doPost("/api/auth/changePassword", changePasswordRequest);
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg);
UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage;
UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true);
Assert.assertNotNull(userCredentialsMsg);
Assert.assertEquals(savedTenantAdmin.getId(), userCredentialsMsg.getUserId());
Assert.assertTrue(passwordEncoder.matches(changePasswordRequest.getNewPassword(), userCredentialsMsg.getPassword()));
// delete user
deleteAndVerifyUser(savedTenantAdmin);
}
@Test
public void testCreateUpdateDeleteCustomerUser() throws Exception {
// create customer
Customer savedCustomer = createAndAssignCustomerToEdge();
// create user
User customerUser = buildUser(Authority.CUSTOMER_USER, savedCustomer.getId());
User savedCustomerUser = createAndVerifyUserOnEdge(customerUser);
// update user
updateAndVerifyUserLastName(savedCustomerUser);
// update user credentials
login(savedCustomerUser.getEmail(), "customer");
updateAndVerifyUserCredentials(savedCustomerUser);
loginTenantAdmin();
// delete user
deleteAndVerifyUser(savedCustomerUser);
}
@Test
public void testSendUserToCloudFromEdge() throws Exception {
// create customer
Customer savedCustomer = createAndAssignCustomerToEdge();
// create uplinkMsg with user and userCredentials
UserId userId = new UserId(UUID.randomUUID());
UserCredentialsId userCredentialsId = new UserCredentialsId(UUID.randomUUID());
UplinkMsg uplinkMsg = buildUserUplinkMsg(userId, savedCustomer.getId(), userCredentialsId);
User userFromCloud = verifyMsgOnCloud(uplinkMsg, userId, false);
assertUserCredentialsFlags(userFromCloud, false, false);
// create uplinkMsg with enabled userCredentials
UplinkMsg uplinkMsgWithEnabledCredentials = constructUserCredentialsUplinkMsg(userCredentialsId, userId);
User cloudUserWithCredentials = verifyMsgOnCloud(uplinkMsgWithEnabledCredentials, userId, false);
assertUserCredentialsFlags(cloudUserWithCredentials, true, true);
// create uplinkMsg with user the same email
UserId secondUserId = new UserId(UUID.randomUUID());
UserCredentialsId secondCredentialsId = new UserCredentialsId(UUID.randomUUID());
UplinkMsg uplinkMsgForUserExistingEmail = buildUserUplinkMsg(secondUserId, savedCustomer.getId(), secondCredentialsId);
verifyMsgOnCloud(uplinkMsgForUserExistingEmail, secondUserId, true);
}
@Test
public void testSendUserCredentialsRequestToCloud() throws Exception {
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
UserCredentialsRequestMsg.Builder userCredentialsRequestMsgBuilder = UserCredentialsRequestMsg.newBuilder();
userCredentialsRequestMsgBuilder.setUserIdMSB(tenantAdminUserId.getId().getMostSignificantBits());
userCredentialsRequestMsgBuilder.setUserIdLSB(tenantAdminUserId.getId().getLeastSignificantBits());
testAutoGeneratedCodeByProtobuf(userCredentialsRequestMsgBuilder);
uplinkMsgBuilder.addUserCredentialsRequestMsg(userCredentialsRequestMsgBuilder.build());
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
edgeImitator.expectResponsesAmount(1);
edgeImitator.expectMessageAmount(1);
doDelete("/api/user/" + savedTenantAdmin.getUuidId())
.andExpect(status().isOk());
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses());
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof UserUpdateMsg);
userUpdateMsg = (UserUpdateMsg) latestMessage;
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, userUpdateMsg.getMsgType());
Assert.assertEquals(savedTenantAdmin.getUuidId().getMostSignificantBits(), userUpdateMsg.getIdMSB());
Assert.assertEquals(savedTenantAdmin.getUuidId().getLeastSignificantBits(), userUpdateMsg.getIdLSB());
UserCredentialsUpdateMsg userCredentialsUpdateMsg = getLatestUserCredentialsUpdateMsg();
UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true);
Assert.assertNotNull(userCredentialsMsg);
Assert.assertEquals(tenantAdminUserId, userCredentialsMsg.getUserId());
testAutoGeneratedCodeByProtobuf(userCredentialsUpdateMsg);
}
@Test
public void testCreateUpdateDeleteCustomerUser() throws Exception {
public void testSendUserDeleteFromEdgeToCloud() throws Exception {
// create customer
Customer savedCustomer = createAndAssignCustomerToEdge();
// create user
User customerUser = buildUser(Authority.CUSTOMER_USER, savedCustomer.getId());
User savedCustomerUser = createAndVerifyUserOnEdge(customerUser);
// simulate user removal event from edge to cloud
UserUpdateMsg.Builder userUpdateMsg = UserUpdateMsg.newBuilder().setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE)
.setIdMSB(savedCustomerUser.getUuidId().getMostSignificantBits())
.setIdLSB(savedCustomerUser.getUuidId().getLeastSignificantBits());
UplinkMsg uplink = UplinkMsg.newBuilder()
.setUplinkMsgId(EdgeUtils.nextPositiveInt())
.addUserUpdateMsg(userUpdateMsg).build();
testAutoGeneratedCodeByProtobuf(userUpdateMsg);
// expect edge message sent & cloud message response
edgeImitator.expectResponsesAmount(1);
edgeImitator.sendUplinkMsg(uplink);
Assert.assertTrue(edgeImitator.waitForResponses());
loginTenantAdmin();
Awaitility.await().atMost(10, TimeUnit.SECONDS)
.until(() -> {
try {
doGet("/api/user/" + savedCustomerUser.getId(), User.class, status().isNotFound());
return true;
} catch (Throwable ex) {
return false;
}
});
}
private Customer createAndAssignCustomerToEdge() throws Exception {
edgeImitator.expectMessageAmount(1);
Customer customer = new Customer();
customer.setTitle("Edge Customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
Assert.assertFalse(edgeImitator.waitForMessages(5));
// assign edge to customer
edgeImitator.expectMessageAmount(2);
doPost("/api/customer/" + savedCustomer.getUuidId()
+ "/edge/" + edge.getUuidId(), Edge.class);
doPost("/api/customer/" + savedCustomer.getUuidId() + "/edge/" + edge.getUuidId(), Edge.class);
Assert.assertTrue(edgeImitator.waitForMessages());
// create user
return savedCustomer;
}
private User buildUser(Authority authority, CustomerId customerId) {
User user = new User();
user.setAuthority(authority);
user.setTenantId(tenantId);
user.setCustomerId(customerId);
user.setEmail(authority == Authority.TENANT_ADMIN ? DEFAULT_TENANT_ADMIN_EMAIL : DEFAULT_CUSTOMER_USER_EMAIL);
user.setFirstName(DEFAULT_FIRST_NAME);
user.setLastName(DEFAULT_LAST_NAME);
return user;
}
private User createAndVerifyUserOnEdge(User user) throws Exception {
String password = user.getAuthority() == Authority.TENANT_ADMIN ? "tenant" : "customer";
// wait 3 messages - x1 user update msg and x2 user credentials update msgs (create + authenticate user)
edgeImitator.expectMessageAmount(3);
User customerUser = new User();
customerUser.setAuthority(Authority.CUSTOMER_USER);
customerUser.setTenantId(tenantId);
customerUser.setCustomerId(savedCustomer.getId());
customerUser.setEmail("customerUser@thingsboard.org");
customerUser.setFirstName("John");
customerUser.setLastName("Edwards");
User savedCustomerUser = createUser(customerUser, "customer");
Assert.assertTrue(edgeImitator.waitForMessages()); // wait 3 messages - x1 user update msg and x2 user credentials update msgs (create + authenticate user)
User savedUser = createUser(user, password);
Assert.assertTrue(edgeImitator.waitForMessages());
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();
UserUpdateMsg userUpdateMsg = getLatestUserUpdateMsg();
User userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);
Assert.assertNotNull(userMsg);
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, userUpdateMsg.getMsgType());
Assert.assertEquals(savedCustomerUser.getId(), userMsg.getId());
Assert.assertEquals(savedCustomerUser.getCustomerId(), userMsg.getCustomerId());
Assert.assertEquals(savedCustomerUser.getAuthority(), userMsg.getAuthority());
Assert.assertEquals(savedCustomerUser.getEmail(), userMsg.getEmail());
Assert.assertEquals(savedCustomerUser.getFirstName(), userMsg.getFirstName());
Assert.assertEquals(savedCustomerUser.getLastName(), userMsg.getLastName());
Assert.assertEquals(savedUser.getId(), userMsg.getId());
Assert.assertEquals(savedUser.getCustomerId(), userMsg.getCustomerId());
Assert.assertEquals(savedUser.getAuthority(), userMsg.getAuthority());
Assert.assertEquals(savedUser.getEmail(), userMsg.getEmail());
Assert.assertEquals(savedUser.getFirstName(), userMsg.getFirstName());
Assert.assertEquals(savedUser.getLastName(), userMsg.getLastName());
return savedUser;
}
private void updateAndVerifyUserLastName(User user) throws Exception {
user.setLastName(UPDATED_LAST_NAME);
// update user
edgeImitator.expectMessageAmount(2);
savedCustomerUser.setLastName("Addams");
savedCustomerUser = doPost("/api/user", savedCustomerUser, User.class);
doPost("/api/user", user, User.class);
Assert.assertTrue(edgeImitator.waitForMessages());
userUpdateMsgOpt = edgeImitator.findMessageByType(UserUpdateMsg.class);
Assert.assertTrue(userUpdateMsgOpt.isPresent());
userUpdateMsg = userUpdateMsgOpt.get();
userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);
Assert.assertNotNull(userMsg);
UserUpdateMsg userUpdateMsg = getLatestUserUpdateMsg();
User userFromMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);
Assert.assertNotNull(userFromMsg);
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, userUpdateMsg.getMsgType());
Assert.assertEquals(savedCustomerUser.getLastName(), userMsg.getLastName());
Assert.assertEquals(UPDATED_LAST_NAME, userFromMsg.getLastName());
}
// update user credentials
login(savedCustomerUser.getEmail(), "customer");
private void updateAndVerifyUserCredentials(User user) throws Exception {
String password = user.getAuthority() == Authority.TENANT_ADMIN ? "tenant" : "customer";
String newPassword = "new" + password;
edgeImitator.expectMessageAmount(1);
ChangePasswordRequest changePasswordRequest = new ChangePasswordRequest();
changePasswordRequest.setCurrentPassword("customer");
changePasswordRequest.setNewPassword("newCustomer");
changePasswordRequest.setCurrentPassword(password);
changePasswordRequest.setNewPassword(newPassword);
doPost("/api/auth/changePassword", changePasswordRequest);
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg);
UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage;
UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true);
Assert.assertNotNull(userCredentialsMsg);
Assert.assertEquals(savedCustomerUser.getId(), userCredentialsMsg.getUserId());
Assert.assertTrue(passwordEncoder.matches(changePasswordRequest.getNewPassword(), userCredentialsMsg.getPassword()));
loginTenantAdmin();
UserCredentialsUpdateMsg msg = getLatestUserCredentialsUpdateMsg();
UserCredentials creds = JacksonUtil.fromString(msg.getEntity(), UserCredentials.class, true);
Assert.assertNotNull(creds);
Assert.assertEquals(user.getId(), creds.getUserId());
Assert.assertTrue(passwordEncoder.matches(newPassword, creds.getPassword()));
}
// delete user
private void deleteAndVerifyUser(User savedTenantAdmin) throws Exception {
edgeImitator.expectMessageAmount(1);
doDelete("/api/user/" + savedCustomerUser.getUuidId())
doDelete("/api/user/" + savedTenantAdmin.getUuidId())
.andExpect(status().isOk());
Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof UserUpdateMsg);
userUpdateMsg = (UserUpdateMsg) latestMessage;
UserUpdateMsg userUpdateMsg = getLatestUserUpdateMsg();
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, userUpdateMsg.getMsgType());
Assert.assertEquals(savedCustomerUser.getUuidId().getMostSignificantBits(), userUpdateMsg.getIdMSB());
Assert.assertEquals(savedCustomerUser.getUuidId().getLeastSignificantBits(), userUpdateMsg.getIdLSB());
Assert.assertEquals(savedTenantAdmin.getUuidId().getMostSignificantBits(), userUpdateMsg.getIdMSB());
Assert.assertEquals(savedTenantAdmin.getUuidId().getLeastSignificantBits(), userUpdateMsg.getIdLSB());
}
private UplinkMsg buildUserUplinkMsg(UserId userId, CustomerId customerId, UserCredentialsId userCredentialsUuid) {
User customerUser = buildUser(Authority.CUSTOMER_USER, customerId);
customerUser.setId(userId);
UserCredentials userCredentials = buildCredentials(userCredentialsUuid, userId, false);
userCredentials.setActivateToken(StringUtils.randomAlphanumeric(DEFAULT_TOKEN_LENGTH));
UserUpdateMsg userUpdateMsg = EdgeMsgConstructorUtils.constructUserUpdatedMsg(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, customerUser);
UserCredentialsUpdateMsg userCredentialsMsg = EdgeMsgConstructorUtils.constructUserCredentialsUpdatedMsg(userCredentials);
return UplinkMsg.newBuilder()
.addUserUpdateMsg(userUpdateMsg)
.addUserCredentialsUpdateMsg(userCredentialsMsg)
.build();
}
@Test
public void testSendUserCredentialsRequestToCloud() throws Exception {
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
UserCredentialsRequestMsg.Builder userCredentialsRequestMsgBuilder = UserCredentialsRequestMsg.newBuilder();
userCredentialsRequestMsgBuilder.setUserIdMSB(tenantAdminUserId.getId().getMostSignificantBits());
userCredentialsRequestMsgBuilder.setUserIdLSB(tenantAdminUserId.getId().getLeastSignificantBits());
testAutoGeneratedCodeByProtobuf(userCredentialsRequestMsgBuilder);
uplinkMsgBuilder.addUserCredentialsRequestMsg(userCredentialsRequestMsgBuilder.build());
private UserCredentials buildCredentials(UserCredentialsId userCredentialsUuid, UserId userId, boolean enabled) {
UserCredentials userCredentials = new UserCredentials();
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
userCredentials.setId(userCredentialsUuid);
userCredentials.setUserId(userId);
userCredentials.setEnabled(enabled);
userCredentials.setAdditionalInfo(JacksonUtil.newObjectNode());
return userCredentials;
}
private User verifyMsgOnCloud(UplinkMsg uplinkMsg, UserId userId, boolean emailExist) throws Exception {
edgeImitator.expectResponsesAmount(1);
edgeImitator.expectMessageAmount(1);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
edgeImitator.sendUplinkMsg(uplinkMsg);
Assert.assertTrue(edgeImitator.waitForResponses());
Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg);
UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage;
UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true);
Assert.assertNotNull(userCredentialsMsg);
Assert.assertEquals(tenantAdminUserId, userCredentialsMsg.getUserId());
User userFromCloud = doGet("/api/user/" + userId, User.class);
Assert.assertNotNull(userFromCloud);
if (emailExist) {
Assert.assertNotEquals(DEFAULT_CUSTOMER_USER_EMAIL, userFromCloud.getEmail());
} else {
Assert.assertEquals(DEFAULT_CUSTOMER_USER_EMAIL, userFromCloud.getEmail());
}
return userFromCloud;
}
@Test
public void sendUserCredentialsRequest() throws Exception {
UplinkMsg.Builder uplinkMsgBuilder = UplinkMsg.newBuilder();
UserCredentialsRequestMsg.Builder userCredentialsRequestMsgBuilder = UserCredentialsRequestMsg.newBuilder();
userCredentialsRequestMsgBuilder.setUserIdMSB(tenantAdminUserId.getId().getMostSignificantBits());
userCredentialsRequestMsgBuilder.setUserIdLSB(tenantAdminUserId.getId().getLeastSignificantBits());
testAutoGeneratedCodeByProtobuf(userCredentialsRequestMsgBuilder);
uplinkMsgBuilder.addUserCredentialsRequestMsg(userCredentialsRequestMsgBuilder.build());
private UplinkMsg constructUserCredentialsUplinkMsg(UserCredentialsId userCredentialsUuid, UserId userId) {
UserCredentials userCredentials = buildCredentials(userCredentialsUuid, userId, true);
userCredentials.setPassword("password");
UserCredentialsUpdateMsg credsMsg = EdgeMsgConstructorUtils.constructUserCredentialsUpdatedMsg(userCredentials);
testAutoGeneratedCodeByProtobuf(uplinkMsgBuilder);
return UplinkMsg.newBuilder()
.addUserCredentialsUpdateMsg(credsMsg)
.build();
}
edgeImitator.expectResponsesAmount(1);
edgeImitator.expectMessageAmount(1);
edgeImitator.sendUplinkMsg(uplinkMsgBuilder.build());
Assert.assertTrue(edgeImitator.waitForResponses());
Assert.assertTrue(edgeImitator.waitForMessages());
private void assertUserCredentialsFlags(User user, boolean enabled, boolean activated) {
JsonNode info = user.getAdditionalInfo();
Assert.assertNotNull(info);
Assert.assertEquals(enabled, info.get("userCredentialsEnabled").asBoolean());
Assert.assertEquals(activated, info.get("userActivated").asBoolean());
}
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof UserCredentialsUpdateMsg);
UserCredentialsUpdateMsg userCredentialsUpdateMsg = (UserCredentialsUpdateMsg) latestMessage;
UserCredentials userCredentialsMsg = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true);
Assert.assertNotNull(userCredentialsMsg);
Assert.assertEquals(tenantAdminUserId, userCredentialsMsg.getUserId());
private UserUpdateMsg getLatestUserUpdateMsg() {
Optional<UserUpdateMsg> opt = edgeImitator.findMessageByType(UserUpdateMsg.class);
Assert.assertTrue(opt.isPresent());
return opt.get();
}
testAutoGeneratedCodeByProtobuf(userCredentialsUpdateMsg);
private UserCredentialsUpdateMsg getLatestUserCredentialsUpdateMsg() {
Optional<UserCredentialsUpdateMsg> opt = edgeImitator.findMessageByType(UserCredentialsUpdateMsg.class);
Assert.assertTrue(opt.isPresent());
return opt.get();
}
}

6
common/dao-api/src/main/java/org/thingsboard/server/dao/user/UserService.java

@ -48,6 +48,8 @@ public interface UserService extends EntityDaoService {
User saveUser(TenantId tenantId, User user);
User saveUser(TenantId tenantId, User user, boolean doValidate);
UserCredentials findUserCredentialsByUserId(TenantId tenantId, UserId userId);
UserCredentials findUserCredentialsByActivateToken(TenantId tenantId, String activateToken);
@ -56,6 +58,8 @@ public interface UserService extends EntityDaoService {
UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials);
UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials, boolean doValidate);
UserCredentials activateUserCredentials(TenantId tenantId, String activateToken, String password);
UserCredentials requestPasswordReset(TenantId tenantId, String email);
@ -70,6 +74,8 @@ public interface UserService extends EntityDaoService {
UserCredentials replaceUserCredentials(TenantId tenantId, UserCredentials userCredentials);
void deleteUserCredentials(TenantId tenantId, UserCredentials userCredentials);
void deleteUser(TenantId tenantId, User user);
PageData<User> findUsersByTenantId(TenantId tenantId, PageLink pageLink);

2
common/edge-api/src/main/proto/edge.proto

@ -449,6 +449,8 @@ message UplinkMsg {
repeated CalculatedFieldUpdateMsg calculatedFieldUpdateMsg = 25;
repeated CalculatedFieldRequestMsg calculatedFieldRequestMsg = 26;
repeated AiModelUpdateMsg aiModelUpdateMsg = 27;
repeated UserUpdateMsg userUpdateMsg = 28;
repeated UserCredentialsUpdateMsg userCredentialsUpdateMsg = 29;
}
message UplinkResponseMsg {

38
dao/src/main/java/org/thingsboard/server/dao/user/UserServiceImpl.java

@ -96,8 +96,9 @@ public class UserServiceImpl extends AbstractCachedEntityService<UserCacheKey, U
public static final String USER_PASSWORD_HISTORY = "userPasswordHistory";
private static final int DEFAULT_TOKEN_LENGTH = 30;
public static final int DEFAULT_TOKEN_LENGTH = 30;
public static final String INCORRECT_USER_ID = "Incorrect userId ";
public static final String INCORRECT_USER_CREDENTIALS_ID = "Incorrect userCredentialsId ";
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
@Value("${security.user_login_case_sensitive:true}")
@ -173,12 +174,23 @@ public class UserServiceImpl extends AbstractCachedEntityService<UserCacheKey, U
@Override
@Transactional
public User saveUser(TenantId tenantId, User user) {
return saveEntity(user, () -> doSaveUser(tenantId, user));
return saveUser(tenantId, user, true);
}
private User doSaveUser(TenantId tenantId, User user) {
@Override
@Transactional
public User saveUser(TenantId tenantId, User user, boolean doValidate) {
return saveEntity(user, () -> doSaveUser(tenantId, user, doValidate));
}
private User doSaveUser(TenantId tenantId, User user, boolean doValidate) {
log.trace("Executing saveUser [{}]", user);
User oldUser = userValidator.validate(user, User::getTenantId);
User oldUser = null;
if (doValidate) {
oldUser = userValidator.validate(user, User::getTenantId);
} else if (user.getId() != null) {
oldUser = findUserById(user.getTenantId(), user.getId());
}
if (!userLoginCaseSensitive) {
user.setEmail(user.getEmail().toLowerCase());
}
@ -233,8 +245,15 @@ public class UserServiceImpl extends AbstractCachedEntityService<UserCacheKey, U
@Override
public UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials) {
return saveUserCredentials(tenantId, userCredentials, true);
}
@Override
public UserCredentials saveUserCredentials(TenantId tenantId, UserCredentials userCredentials, boolean doValidate) {
log.trace("Executing saveUserCredentials [{}]", userCredentials);
userCredentialsValidator.validate(userCredentials, data -> tenantId);
if (doValidate) {
userCredentialsValidator.validate(userCredentials, data -> tenantId);
}
UserCredentials result = userCredentialsDao.save(tenantId, userCredentials);
eventPublisher.publishEvent(ActionEntityEvent.builder()
.tenantId(tenantId)
@ -337,6 +356,15 @@ public class UserServiceImpl extends AbstractCachedEntityService<UserCacheKey, U
return result;
}
@Override
public void deleteUserCredentials(TenantId tenantId, UserCredentials userCredentials) {
Objects.requireNonNull(userCredentials, "UserCredentials is null");
UserCredentialsId userCredentialsId = userCredentials.getId();
log.trace("[{}] Executing deleteUserCredentials [{}]", tenantId, userCredentialsId);
validateId(userCredentialsId, id -> INCORRECT_USER_CREDENTIALS_ID + id);
userCredentialsDao.removeById(tenantId, userCredentialsId.getId());
}
@Override
@Transactional
public void deleteUser(TenantId tenantId, User user) {

Loading…
Cancel
Save