Showing
9 changed files
with
161 additions
and
12 deletions
... | ... | @@ -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; | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
... | ... | @@ -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 | } | ... | ... |