Commit 461da688f41a802fda2159a1edefab14d331e589

Authored by Andrii Shvaika
2 parents a5edc309 c2a2b8cc

Merge with 2.5.5

... ... @@ -166,7 +166,8 @@ public class AuthController extends BaseController {
166 166 try {
167 167 String email = resetPasswordByEmailRequest.get("email").asText();
168 168 UserCredentials userCredentials = userService.requestPasswordReset(TenantId.SYS_TENANT_ID, email);
169   - String baseUrl = MiscUtils.constructBaseUrl(request);
  169 + User user = userService.findUserById(TenantId.SYS_TENANT_ID, userCredentials.getUserId());
  170 + String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request);
170 171 String resetUrl = String.format("%s/api/noauth/resetPassword?resetToken=%s", baseUrl,
171 172 userCredentials.getResetToken());
172 173
... ... @@ -214,7 +215,7 @@ public class AuthController extends BaseController {
214 215 User user = userService.findUserById(TenantId.SYS_TENANT_ID, credentials.getUserId());
215 216 UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail());
216 217 SecurityUser securityUser = new SecurityUser(user, credentials.isEnabled(), principal);
217   - String baseUrl = MiscUtils.constructBaseUrl(request);
  218 + String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request);
218 219 String loginUrl = String.format("%s/login", baseUrl);
219 220 String email = user.getEmail();
220 221
... ... @@ -261,7 +262,7 @@ public class AuthController extends BaseController {
261 262 User user = userService.findUserById(TenantId.SYS_TENANT_ID, userCredentials.getUserId());
262 263 UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, user.getEmail());
263 264 SecurityUser securityUser = new SecurityUser(user, userCredentials.isEnabled(), principal);
264   - String baseUrl = MiscUtils.constructBaseUrl(request);
  265 + String baseUrl = systemSecurityService.getBaseUrl(user.getTenantId(), user.getCustomerId(), request);
265 266 String loginUrl = String.format("%s/login", baseUrl);
266 267 String email = user.getEmail();
267 268 mailService.sendPasswordWasResetEmail(loginUrl, email);
... ...
... ... @@ -52,6 +52,7 @@ import org.thingsboard.server.service.security.model.token.JwtToken;
52 52 import org.thingsboard.server.service.security.model.token.JwtTokenFactory;
53 53 import org.thingsboard.server.service.security.permission.Operation;
54 54 import org.thingsboard.server.service.security.permission.Resource;
  55 +import org.thingsboard.server.service.security.system.SystemSecurityService;
55 56 import org.thingsboard.server.utils.MiscUtils;
56 57
57 58 import javax.servlet.http.HttpServletRequest;
... ... @@ -78,6 +79,9 @@ public class UserController extends BaseController {
78 79 @Autowired
79 80 private RefreshTokenRepository refreshTokenRepository;
80 81
  82 + @Autowired
  83 + private SystemSecurityService systemSecurityService;
  84 +
81 85
82 86 @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
83 87 @RequestMapping(value = "/user/{userId}", method = RequestMethod.GET)
... ... @@ -146,7 +150,7 @@ public class UserController extends BaseController {
146 150 if (sendEmail) {
147 151 SecurityUser authUser = getCurrentUser();
148 152 UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), savedUser.getId());
149   - String baseUrl = MiscUtils.constructBaseUrl(request);
  153 + String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request);
150 154 String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl,
151 155 userCredentials.getActivateToken());
152 156 String email = savedUser.getEmail();
... ... @@ -186,7 +190,7 @@ public class UserController extends BaseController {
186 190
187 191 UserCredentials userCredentials = userService.findUserCredentialsByUserId(getCurrentUser().getTenantId(), user.getId());
188 192 if (!userCredentials.isEnabled()) {
189   - String baseUrl = MiscUtils.constructBaseUrl(request);
  193 + String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request);
190 194 String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl,
191 195 userCredentials.getActivateToken());
192 196 mailService.sendActivationEmail(activateUrl, email);
... ... @@ -211,7 +215,7 @@ public class UserController extends BaseController {
211 215 SecurityUser authUser = getCurrentUser();
212 216 UserCredentials userCredentials = userService.findUserCredentialsByUserId(authUser.getTenantId(), user.getId());
213 217 if (!userCredentials.isEnabled()) {
214   - String baseUrl = MiscUtils.constructBaseUrl(request);
  218 + String baseUrl = systemSecurityService.getBaseUrl(getTenantId(), getCurrentUser().getCustomerId(), request);
215 219 String activateUrl = String.format(ACTIVATE_URL_PATTERN, baseUrl,
216 220 userCredentials.getActivateToken());
217 221 return activateUrl;
... ...
... ... @@ -173,6 +173,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
173 173 generalSettings.setKey("general");
174 174 ObjectNode node = objectMapper.createObjectNode();
175 175 node.put("baseUrl", "http://localhost:8080");
  176 + node.put("prohibitDifferentUrl", true);
176 177 generalSettings.setJsonValue(node);
177 178 adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, generalSettings);
178 179
... ...
... ... @@ -30,7 +30,7 @@ import java.nio.charset.StandardCharsets;
30 30
31 31 @Component(value = "oauth2AuthenticationFailureHandler")
32 32 @ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true")
33   -public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
  33 +public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
34 34
35 35 @Override
36 36 public void onAuthenticationFailure(HttpServletRequest request,
... ...
... ... @@ -63,7 +63,6 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
63 63 public void onAuthenticationSuccess(HttpServletRequest request,
64 64 HttpServletResponse response,
65 65 Authentication authentication) throws IOException {
66   -
67 66 String baseUrl = MiscUtils.constructBaseUrl(request);
68 67 try {
69 68 OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
... ...
... ... @@ -40,17 +40,20 @@ import org.thingsboard.rule.engine.api.MailService;
40 40 import org.thingsboard.server.common.data.AdminSettings;
41 41 import org.thingsboard.server.common.data.User;
42 42 import org.thingsboard.server.common.data.exception.ThingsboardException;
  43 +import org.thingsboard.server.common.data.id.CustomerId;
43 44 import org.thingsboard.server.common.data.id.TenantId;
44 45 import org.thingsboard.server.common.data.security.UserCredentials;
  46 +import org.thingsboard.server.common.data.security.model.SecuritySettings;
  47 +import org.thingsboard.server.common.data.security.model.UserPasswordPolicy;
45 48 import org.thingsboard.server.dao.exception.DataValidationException;
46 49 import org.thingsboard.server.dao.settings.AdminSettingsService;
47 50 import org.thingsboard.server.dao.user.UserService;
48 51 import org.thingsboard.server.dao.user.UserServiceImpl;
49 52 import org.thingsboard.server.service.security.exception.UserPasswordExpiredException;
50   -import org.thingsboard.server.common.data.security.model.SecuritySettings;
51   -import org.thingsboard.server.common.data.security.model.UserPasswordPolicy;
  53 +import org.thingsboard.server.utils.MiscUtils;
52 54
53 55 import javax.annotation.Resource;
  56 +import javax.servlet.http.HttpServletRequest;
54 57 import java.util.ArrayList;
55 58 import java.util.List;
56 59 import java.util.Map;
... ... @@ -146,7 +149,7 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
146 149 if (isPositiveInteger(securitySettings.getPasswordPolicy().getPasswordExpirationPeriodDays())) {
147 150 if ((userCredentials.getCreatedTime()
148 151 + TimeUnit.DAYS.toMillis(securitySettings.getPasswordPolicy().getPasswordExpirationPeriodDays()))
149   - < System.currentTimeMillis()) {
  152 + < System.currentTimeMillis()) {
150 153 userCredentials = userService.requestExpiredPasswordReset(tenantId, userCredentials.getId());
151 154 throw new UserPasswordExpiredException("User password expired!", userCredentials.getResetToken());
152 155 }
... ... @@ -197,6 +200,21 @@ public class DefaultSystemSecurityService implements SystemSecurityService {
197 200 }
198 201 }
199 202
  203 + @Override
  204 + public String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest) {
  205 + String baseUrl;
  206 + AdminSettings generalSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "general");
  207 +
  208 + JsonNode prohibitDifferentUrl = generalSettings.getJsonValue().get("prohibitDifferentUrl");
  209 +
  210 + if (prohibitDifferentUrl != null && prohibitDifferentUrl.asBoolean()) {
  211 + baseUrl = generalSettings.getJsonValue().get("baseUrl").asText();
  212 + } else {
  213 + baseUrl = MiscUtils.constructBaseUrl(httpServletRequest);
  214 + }
  215 + return baseUrl;
  216 + }
  217 +
200 218 private static boolean isPositiveInteger(Integer val) {
201 219 return val != null && val.intValue() > 0;
202 220 }
... ...
... ... @@ -16,11 +16,14 @@
16 16 package org.thingsboard.server.service.security.system;
17 17
18 18 import org.springframework.security.core.AuthenticationException;
  19 +import org.thingsboard.server.common.data.id.CustomerId;
19 20 import org.thingsboard.server.common.data.id.TenantId;
20 21 import org.thingsboard.server.common.data.security.UserCredentials;
21 22 import org.thingsboard.server.dao.exception.DataValidationException;
22 23 import org.thingsboard.server.common.data.security.model.SecuritySettings;
23 24
  25 +import javax.servlet.http.HttpServletRequest;
  26 +
24 27 public interface SystemSecurityService {
25 28
26 29 SecuritySettings getSecuritySettings(TenantId tenantId);
... ... @@ -31,4 +34,6 @@ public interface SystemSecurityService {
31 34
32 35 void validatePassword(TenantId tenantId, String password, UserCredentials userCredentials) throws DataValidationException;
33 36
  37 + String getBaseUrl(TenantId tenantId, CustomerId customerId, HttpServletRequest httpServletRequest);
  38 +
34 39 }
... ...
... ... @@ -20,11 +20,11 @@ import com.fasterxml.jackson.databind.JsonNode;
20 20 import com.fasterxml.jackson.databind.node.ObjectNode;
21 21 import com.google.common.util.concurrent.ListenableFuture;
22 22 import lombok.extern.slf4j.Slf4j;
  23 +import org.apache.commons.collections.CollectionUtils;
23 24 import org.apache.commons.lang3.StringUtils;
24 25 import org.hibernate.exception.ConstraintViolationException;
25 26 import org.springframework.beans.factory.annotation.Autowired;
26 27 import org.springframework.stereotype.Service;
27   -import org.springframework.util.CollectionUtils;
28 28 import org.thingsboard.server.common.data.BaseData;
29 29 import org.thingsboard.server.common.data.EntityType;
30 30 import org.thingsboard.server.common.data.Tenant;
... ... @@ -53,10 +53,12 @@ import org.thingsboard.server.dao.tenant.TenantDao;
53 53
54 54 import java.util.ArrayList;
55 55 import java.util.HashMap;
  56 +import java.util.HashSet;
56 57 import java.util.Iterator;
57 58 import java.util.List;
58 59 import java.util.Map;
59 60 import java.util.Optional;
  61 +import java.util.Set;
60 62 import java.util.concurrent.ExecutionException;
61 63 import java.util.stream.Collectors;
62 64
... ... @@ -136,6 +138,10 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
136 138 return null;
137 139 }
138 140
  141 + if (CollectionUtils.isNotEmpty(ruleChainMetaData.getConnections())) {
  142 + validateCircles(ruleChainMetaData.getConnections());
  143 + }
  144 +
139 145 List<RuleNode> nodes = ruleChainMetaData.getNodes();
140 146 List<RuleNode> toAddOrUpdate = new ArrayList<>();
141 147 List<RuleNode> toDelete = new ArrayList<>();
... ... @@ -218,6 +224,31 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC
218 224 return loadRuleChainMetaData(tenantId, ruleChainMetaData.getRuleChainId());
219 225 }
220 226
  227 + private void validateCircles(List<NodeConnectionInfo> connectionInfos) {
  228 + Map<Integer, Set<Integer>> connectionsMap = new HashMap<>();
  229 + for (NodeConnectionInfo nodeConnection : connectionInfos) {
  230 + if (nodeConnection.getFromIndex() == nodeConnection.getToIndex()) {
  231 + throw new DataValidationException("Can't create the relation to yourself.");
  232 + }
  233 + connectionsMap
  234 + .computeIfAbsent(nodeConnection.getFromIndex(), from -> new HashSet<>())
  235 + .add(nodeConnection.getToIndex());
  236 + }
  237 + connectionsMap.keySet().forEach(key -> validateCircles(key, connectionsMap.get(key), connectionsMap));
  238 + }
  239 +
  240 + private void validateCircles(int from, Set<Integer> toList, Map<Integer, Set<Integer>> connectionsMap) {
  241 + if (toList == null) {
  242 + return;
  243 + }
  244 + for (Integer to : toList) {
  245 + if (from == to) {
  246 + throw new DataValidationException("Can't create circling relations in rule chain.");
  247 + }
  248 + validateCircles(from, connectionsMap.get(to), connectionsMap);
  249 + }
  250 + }
  251 +
221 252 @Override
222 253 public RuleChainMetaData loadRuleChainMetaData(TenantId tenantId, RuleChainId ruleChainId) {
223 254 Validator.validateId(ruleChainId, "Incorrect rule chain id.");
... ...
... ... @@ -317,6 +317,16 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest {
317 317 ruleChainService.deleteRuleChainById(tenantId, savedRuleChainMetaData.getRuleChainId());
318 318 }
319 319
  320 + @Test(expected = DataValidationException.class)
  321 + public void testUpdateRuleChainMetaDataWithCirclingRelation() throws Exception {
  322 + ruleChainService.saveRuleChainMetaData(tenantId, createRuleChainMetadataWithCirclingRelation());
  323 + }
  324 +
  325 + @Test(expected = DataValidationException.class)
  326 + public void testUpdateRuleChainMetaDataWithCirclingRelation2() throws Exception {
  327 + ruleChainService.saveRuleChainMetaData(tenantId, createRuleChainMetadataWithCirclingRelation2());
  328 + }
  329 +
320 330 private RuleChainMetaData createRuleChainMetadata() throws Exception {
321 331 RuleChain ruleChain = new RuleChain();
322 332 ruleChain.setName("My RuleChain");
... ... @@ -357,5 +367,85 @@ public abstract class BaseRuleChainServiceTest extends AbstractServiceTest {
357 367 return ruleChainService.saveRuleChainMetaData(tenantId, ruleChainMetaData);
358 368 }
359 369
  370 + private RuleChainMetaData createRuleChainMetadataWithCirclingRelation() throws Exception {
  371 + RuleChain ruleChain = new RuleChain();
  372 + ruleChain.setName("My RuleChain");
  373 + ruleChain.setTenantId(tenantId);
  374 + RuleChain savedRuleChain = ruleChainService.saveRuleChain(ruleChain);
  375 +
  376 + RuleChainMetaData ruleChainMetaData = new RuleChainMetaData();
  377 + ruleChainMetaData.setRuleChainId(savedRuleChain.getId());
  378 +
  379 + ObjectMapper mapper = new ObjectMapper();
  380 +
  381 + RuleNode ruleNode1 = new RuleNode();
  382 + ruleNode1.setName("name1");
  383 + ruleNode1.setType("type1");
  384 + ruleNode1.setConfiguration(mapper.readTree("\"key1\": \"val1\""));
  385 +
  386 + RuleNode ruleNode2 = new RuleNode();
  387 + ruleNode2.setName("name2");
  388 + ruleNode2.setType("type2");
  389 + ruleNode2.setConfiguration(mapper.readTree("\"key2\": \"val2\""));
  390 +
  391 + RuleNode ruleNode3 = new RuleNode();
  392 + ruleNode3.setName("name3");
  393 + ruleNode3.setType("type3");
  394 + ruleNode3.setConfiguration(mapper.readTree("\"key3\": \"val3\""));
  395 +
  396 + List<RuleNode> ruleNodes = new ArrayList<>();
  397 + ruleNodes.add(ruleNode1);
  398 + ruleNodes.add(ruleNode2);
  399 + ruleNodes.add(ruleNode3);
  400 + ruleChainMetaData.setFirstNodeIndex(0);
  401 + ruleChainMetaData.setNodes(ruleNodes);
  402 +
  403 + ruleChainMetaData.addConnectionInfo(0,1,"success");
  404 + ruleChainMetaData.addConnectionInfo(0,2,"fail");
  405 + ruleChainMetaData.addConnectionInfo(1,2,"success");
  406 + ruleChainMetaData.addConnectionInfo(2,2,"success");
  407 +
  408 + return ruleChainMetaData;
  409 + }
  410 +
  411 + private RuleChainMetaData createRuleChainMetadataWithCirclingRelation2() throws Exception {
  412 + RuleChain ruleChain = new RuleChain();
  413 + ruleChain.setName("My RuleChain");
  414 + ruleChain.setTenantId(tenantId);
  415 + RuleChain savedRuleChain = ruleChainService.saveRuleChain(ruleChain);
  416 +
  417 + RuleChainMetaData ruleChainMetaData = new RuleChainMetaData();
  418 + ruleChainMetaData.setRuleChainId(savedRuleChain.getId());
  419 +
  420 + ObjectMapper mapper = new ObjectMapper();
  421 +
  422 + RuleNode ruleNode1 = new RuleNode();
  423 + ruleNode1.setName("name1");
  424 + ruleNode1.setType("type1");
  425 + ruleNode1.setConfiguration(mapper.readTree("\"key1\": \"val1\""));
  426 +
  427 + RuleNode ruleNode2 = new RuleNode();
  428 + ruleNode2.setName("name2");
  429 + ruleNode2.setType("type2");
  430 + ruleNode2.setConfiguration(mapper.readTree("\"key2\": \"val2\""));
  431 +
  432 + RuleNode ruleNode3 = new RuleNode();
  433 + ruleNode3.setName("name3");
  434 + ruleNode3.setType("type3");
  435 + ruleNode3.setConfiguration(mapper.readTree("\"key3\": \"val3\""));
  436 +
  437 + List<RuleNode> ruleNodes = new ArrayList<>();
  438 + ruleNodes.add(ruleNode1);
  439 + ruleNodes.add(ruleNode2);
  440 + ruleNodes.add(ruleNode3);
  441 + ruleChainMetaData.setFirstNodeIndex(0);
  442 + ruleChainMetaData.setNodes(ruleNodes);
  443 +
  444 + ruleChainMetaData.addConnectionInfo(0,1,"success");
  445 + ruleChainMetaData.addConnectionInfo(0,2,"fail");
  446 + ruleChainMetaData.addConnectionInfo(1,2,"success");
  447 + ruleChainMetaData.addConnectionInfo(2,0,"success");
360 448
  449 + return ruleChainMetaData;
  450 + }
361 451 }
... ...