Commit 6cdb508c71041fb15757ef2e7d9182e48ea18601
Committed by
GitHub
1 parent
fcb0c041
Claiming devices implementation
Showing
30 changed files
with
743 additions
and
109 deletions
@@ -28,7 +28,16 @@ import org.springframework.security.core.Authentication; | @@ -28,7 +28,16 @@ import org.springframework.security.core.Authentication; | ||
28 | import org.springframework.security.core.context.SecurityContextHolder; | 28 | import org.springframework.security.core.context.SecurityContextHolder; |
29 | import org.springframework.web.bind.annotation.ExceptionHandler; | 29 | import org.springframework.web.bind.annotation.ExceptionHandler; |
30 | import org.thingsboard.server.actors.service.ActorService; | 30 | import org.thingsboard.server.actors.service.ActorService; |
31 | -import org.thingsboard.server.common.data.*; | 31 | +import org.thingsboard.server.common.data.Customer; |
32 | +import org.thingsboard.server.common.data.Dashboard; | ||
33 | +import org.thingsboard.server.common.data.DashboardInfo; | ||
34 | +import org.thingsboard.server.common.data.DataConstants; | ||
35 | +import org.thingsboard.server.common.data.Device; | ||
36 | +import org.thingsboard.server.common.data.EntityType; | ||
37 | +import org.thingsboard.server.common.data.EntityView; | ||
38 | +import org.thingsboard.server.common.data.HasName; | ||
39 | +import org.thingsboard.server.common.data.Tenant; | ||
40 | +import org.thingsboard.server.common.data.User; | ||
32 | import org.thingsboard.server.common.data.alarm.Alarm; | 41 | import org.thingsboard.server.common.data.alarm.Alarm; |
33 | import org.thingsboard.server.common.data.alarm.AlarmId; | 42 | import org.thingsboard.server.common.data.alarm.AlarmId; |
34 | import org.thingsboard.server.common.data.alarm.AlarmInfo; | 43 | import org.thingsboard.server.common.data.alarm.AlarmInfo; |
@@ -36,7 +45,19 @@ import org.thingsboard.server.common.data.asset.Asset; | @@ -36,7 +45,19 @@ import org.thingsboard.server.common.data.asset.Asset; | ||
36 | import org.thingsboard.server.common.data.audit.ActionType; | 45 | import org.thingsboard.server.common.data.audit.ActionType; |
37 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; | 46 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
38 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 47 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
39 | -import org.thingsboard.server.common.data.id.*; | 48 | +import org.thingsboard.server.common.data.id.AssetId; |
49 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
50 | +import org.thingsboard.server.common.data.id.DashboardId; | ||
51 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
52 | +import org.thingsboard.server.common.data.id.EntityId; | ||
53 | +import org.thingsboard.server.common.data.id.EntityIdFactory; | ||
54 | +import org.thingsboard.server.common.data.id.EntityViewId; | ||
55 | +import org.thingsboard.server.common.data.id.RuleChainId; | ||
56 | +import org.thingsboard.server.common.data.id.RuleNodeId; | ||
57 | +import org.thingsboard.server.common.data.id.TenantId; | ||
58 | +import org.thingsboard.server.common.data.id.UserId; | ||
59 | +import org.thingsboard.server.common.data.id.WidgetTypeId; | ||
60 | +import org.thingsboard.server.common.data.id.WidgetsBundleId; | ||
40 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 61 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
41 | import org.thingsboard.server.common.data.kv.DataType; | 62 | import org.thingsboard.server.common.data.kv.DataType; |
42 | import org.thingsboard.server.common.data.page.TextPageLink; | 63 | import org.thingsboard.server.common.data.page.TextPageLink; |
@@ -45,7 +66,6 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor; | @@ -45,7 +66,6 @@ import org.thingsboard.server.common.data.plugin.ComponentDescriptor; | ||
45 | import org.thingsboard.server.common.data.plugin.ComponentType; | 66 | import org.thingsboard.server.common.data.plugin.ComponentType; |
46 | import org.thingsboard.server.common.data.rule.RuleChain; | 67 | import org.thingsboard.server.common.data.rule.RuleChain; |
47 | import org.thingsboard.server.common.data.rule.RuleNode; | 68 | import org.thingsboard.server.common.data.rule.RuleNode; |
48 | -import org.thingsboard.server.common.data.security.Authority; | ||
49 | import org.thingsboard.server.common.data.widget.WidgetType; | 69 | import org.thingsboard.server.common.data.widget.WidgetType; |
50 | import org.thingsboard.server.common.data.widget.WidgetsBundle; | 70 | import org.thingsboard.server.common.data.widget.WidgetsBundle; |
51 | import org.thingsboard.server.common.msg.TbMsg; | 71 | import org.thingsboard.server.common.msg.TbMsg; |
@@ -53,13 +73,13 @@ import org.thingsboard.server.common.msg.TbMsgDataType; | @@ -53,13 +73,13 @@ import org.thingsboard.server.common.msg.TbMsgDataType; | ||
53 | import org.thingsboard.server.common.msg.TbMsgMetaData; | 73 | import org.thingsboard.server.common.msg.TbMsgMetaData; |
54 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; | 74 | import org.thingsboard.server.common.msg.cluster.SendToClusterMsg; |
55 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; | 75 | import org.thingsboard.server.common.msg.system.ServiceToRuleEngineMsg; |
56 | -import org.thingsboard.server.common.msg.tools.TbRateLimitsException; | ||
57 | import org.thingsboard.server.dao.alarm.AlarmService; | 76 | import org.thingsboard.server.dao.alarm.AlarmService; |
58 | import org.thingsboard.server.dao.asset.AssetService; | 77 | import org.thingsboard.server.dao.asset.AssetService; |
59 | import org.thingsboard.server.dao.attributes.AttributesService; | 78 | import org.thingsboard.server.dao.attributes.AttributesService; |
60 | import org.thingsboard.server.dao.audit.AuditLogService; | 79 | import org.thingsboard.server.dao.audit.AuditLogService; |
61 | import org.thingsboard.server.dao.customer.CustomerService; | 80 | import org.thingsboard.server.dao.customer.CustomerService; |
62 | import org.thingsboard.server.dao.dashboard.DashboardService; | 81 | import org.thingsboard.server.dao.dashboard.DashboardService; |
82 | +import org.thingsboard.server.dao.device.ClaimDevicesService; | ||
63 | import org.thingsboard.server.dao.device.DeviceCredentialsService; | 83 | import org.thingsboard.server.dao.device.DeviceCredentialsService; |
64 | import org.thingsboard.server.dao.device.DeviceService; | 84 | import org.thingsboard.server.dao.device.DeviceService; |
65 | import org.thingsboard.server.dao.entityview.EntityViewService; | 85 | import org.thingsboard.server.dao.entityview.EntityViewService; |
@@ -162,6 +182,9 @@ public abstract class BaseController { | @@ -162,6 +182,9 @@ public abstract class BaseController { | ||
162 | @Autowired | 182 | @Autowired |
163 | protected AttributesService attributesService; | 183 | protected AttributesService attributesService; |
164 | 184 | ||
185 | + @Autowired | ||
186 | + protected ClaimDevicesService claimDevicesService; | ||
187 | + | ||
165 | @Value("${server.log_controller_error_stack_trace}") | 188 | @Value("${server.log_controller_error_stack_trace}") |
166 | @Getter | 189 | @Getter |
167 | private boolean logControllerErrorStackTrace; | 190 | private boolean logControllerErrorStackTrace; |
@@ -15,8 +15,11 @@ | @@ -15,8 +15,11 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.controller; | 16 | package org.thingsboard.server.controller; |
17 | 17 | ||
18 | +import com.google.common.util.concurrent.FutureCallback; | ||
19 | +import com.google.common.util.concurrent.Futures; | ||
18 | import com.google.common.util.concurrent.ListenableFuture; | 20 | import com.google.common.util.concurrent.ListenableFuture; |
19 | import org.springframework.http.HttpStatus; | 21 | import org.springframework.http.HttpStatus; |
22 | +import org.springframework.http.ResponseEntity; | ||
20 | import org.springframework.security.access.prepost.PreAuthorize; | 23 | import org.springframework.security.access.prepost.PreAuthorize; |
21 | import org.springframework.web.bind.annotation.PathVariable; | 24 | import org.springframework.web.bind.annotation.PathVariable; |
22 | import org.springframework.web.bind.annotation.RequestBody; | 25 | import org.springframework.web.bind.annotation.RequestBody; |
@@ -26,27 +29,31 @@ import org.springframework.web.bind.annotation.RequestParam; | @@ -26,27 +29,31 @@ import org.springframework.web.bind.annotation.RequestParam; | ||
26 | import org.springframework.web.bind.annotation.ResponseBody; | 29 | import org.springframework.web.bind.annotation.ResponseBody; |
27 | import org.springframework.web.bind.annotation.ResponseStatus; | 30 | import org.springframework.web.bind.annotation.ResponseStatus; |
28 | import org.springframework.web.bind.annotation.RestController; | 31 | import org.springframework.web.bind.annotation.RestController; |
32 | +import org.springframework.web.context.request.async.DeferredResult; | ||
29 | import org.thingsboard.server.common.data.Customer; | 33 | import org.thingsboard.server.common.data.Customer; |
34 | +import org.thingsboard.server.common.data.DataConstants; | ||
30 | import org.thingsboard.server.common.data.Device; | 35 | import org.thingsboard.server.common.data.Device; |
31 | import org.thingsboard.server.common.data.EntitySubtype; | 36 | import org.thingsboard.server.common.data.EntitySubtype; |
32 | import org.thingsboard.server.common.data.EntityType; | 37 | import org.thingsboard.server.common.data.EntityType; |
33 | import org.thingsboard.server.common.data.audit.ActionType; | 38 | import org.thingsboard.server.common.data.audit.ActionType; |
34 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; | 39 | import org.thingsboard.server.common.data.device.DeviceSearchQuery; |
35 | -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; | ||
36 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 40 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
37 | import org.thingsboard.server.common.data.id.CustomerId; | 41 | import org.thingsboard.server.common.data.id.CustomerId; |
38 | import org.thingsboard.server.common.data.id.DeviceId; | 42 | import org.thingsboard.server.common.data.id.DeviceId; |
39 | import org.thingsboard.server.common.data.id.TenantId; | 43 | import org.thingsboard.server.common.data.id.TenantId; |
40 | import org.thingsboard.server.common.data.page.TextPageData; | 44 | import org.thingsboard.server.common.data.page.TextPageData; |
41 | import org.thingsboard.server.common.data.page.TextPageLink; | 45 | import org.thingsboard.server.common.data.page.TextPageLink; |
42 | -import org.thingsboard.server.common.data.security.Authority; | ||
43 | import org.thingsboard.server.common.data.security.DeviceCredentials; | 46 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
47 | +import org.thingsboard.server.controller.claim.data.ClaimRequest; | ||
48 | +import org.thingsboard.server.dao.device.claim.ClaimResponse; | ||
44 | import org.thingsboard.server.dao.exception.IncorrectParameterException; | 49 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
45 | import org.thingsboard.server.dao.model.ModelConstants; | 50 | import org.thingsboard.server.dao.model.ModelConstants; |
46 | import org.thingsboard.server.service.security.model.SecurityUser; | 51 | import org.thingsboard.server.service.security.model.SecurityUser; |
47 | import org.thingsboard.server.service.security.permission.Operation; | 52 | import org.thingsboard.server.service.security.permission.Operation; |
48 | import org.thingsboard.server.service.security.permission.Resource; | 53 | import org.thingsboard.server.service.security.permission.Resource; |
49 | 54 | ||
55 | +import javax.annotation.Nullable; | ||
56 | +import java.io.IOException; | ||
50 | import java.util.ArrayList; | 57 | import java.util.ArrayList; |
51 | import java.util.List; | 58 | import java.util.List; |
52 | import java.util.stream.Collectors; | 59 | import java.util.stream.Collectors; |
@@ -55,7 +62,8 @@ import java.util.stream.Collectors; | @@ -55,7 +62,8 @@ import java.util.stream.Collectors; | ||
55 | @RequestMapping("/api") | 62 | @RequestMapping("/api") |
56 | public class DeviceController extends BaseController { | 63 | public class DeviceController extends BaseController { |
57 | 64 | ||
58 | - public static final String DEVICE_ID = "deviceId"; | 65 | + private static final String DEVICE_ID = "deviceId"; |
66 | + private static final String DEVICE_NAME = "deviceName"; | ||
59 | 67 | ||
60 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | 68 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
61 | @RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET) | 69 | @RequestMapping(value = "/device/{deviceId}", method = RequestMethod.GET) |
@@ -379,4 +387,91 @@ public class DeviceController extends BaseController { | @@ -379,4 +387,91 @@ public class DeviceController extends BaseController { | ||
379 | throw handleException(e); | 387 | throw handleException(e); |
380 | } | 388 | } |
381 | } | 389 | } |
390 | + | ||
391 | + @PreAuthorize("hasAnyAuthority('CUSTOMER_USER')") | ||
392 | + @RequestMapping(value = "/customer/device/{deviceName}/claim", method = RequestMethod.POST) | ||
393 | + @ResponseBody | ||
394 | + public DeferredResult<ResponseEntity> claimDevice(@PathVariable(DEVICE_NAME) String deviceName, | ||
395 | + @RequestBody(required = false) ClaimRequest claimRequest) throws ThingsboardException { | ||
396 | + checkParameter(DEVICE_NAME, deviceName); | ||
397 | + try { | ||
398 | + final DeferredResult<ResponseEntity> deferredResult = new DeferredResult<>(); | ||
399 | + | ||
400 | + SecurityUser user = getCurrentUser(); | ||
401 | + TenantId tenantId = user.getTenantId(); | ||
402 | + CustomerId customerId = user.getCustomerId(); | ||
403 | + | ||
404 | + Device device = checkNotNull(deviceService.findDeviceByTenantIdAndName(tenantId, deviceName)); | ||
405 | + accessControlService.checkPermission(user, Resource.DEVICE, Operation.CLAIM_DEVICES, | ||
406 | + device.getId(), device); | ||
407 | + String secretKey = getSecretKey(claimRequest); | ||
408 | + | ||
409 | + ListenableFuture<ClaimResponse> future = claimDevicesService.claimDevice(device, customerId, secretKey); | ||
410 | + Futures.addCallback(future, new FutureCallback<ClaimResponse>() { | ||
411 | + @Override | ||
412 | + public void onSuccess(@Nullable ClaimResponse result) { | ||
413 | + HttpStatus status; | ||
414 | + if (result.equals(ClaimResponse.SUCCESS)) { | ||
415 | + status = HttpStatus.OK; | ||
416 | + } else { | ||
417 | + status = HttpStatus.BAD_REQUEST; | ||
418 | + } | ||
419 | + deferredResult.setResult(new ResponseEntity<>(result, status)); | ||
420 | + } | ||
421 | + | ||
422 | + @Override | ||
423 | + public void onFailure(Throwable t) { | ||
424 | + deferredResult.setErrorResult(t); | ||
425 | + } | ||
426 | + }); | ||
427 | + return deferredResult; | ||
428 | + } catch (Exception e) { | ||
429 | + throw handleException(e); | ||
430 | + } | ||
431 | + } | ||
432 | + | ||
433 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | ||
434 | + @RequestMapping(value = "/customer/device/{deviceName}/claim", method = RequestMethod.DELETE) | ||
435 | + @ResponseStatus(value = HttpStatus.OK) | ||
436 | + public DeferredResult<ResponseEntity> reClaimDevice(@PathVariable(DEVICE_NAME) String deviceName) throws ThingsboardException { | ||
437 | + checkParameter(DEVICE_NAME, deviceName); | ||
438 | + try { | ||
439 | + final DeferredResult<ResponseEntity> deferredResult = new DeferredResult<>(); | ||
440 | + | ||
441 | + SecurityUser user = getCurrentUser(); | ||
442 | + TenantId tenantId = user.getTenantId(); | ||
443 | + | ||
444 | + Device device = checkNotNull(deviceService.findDeviceByTenantIdAndName(tenantId, deviceName)); | ||
445 | + accessControlService.checkPermission(user, Resource.DEVICE, Operation.CLAIM_DEVICES, | ||
446 | + device.getId(), device); | ||
447 | + | ||
448 | + ListenableFuture<List<Void>> future = claimDevicesService.reClaimDevice(tenantId, device); | ||
449 | + Futures.addCallback(future, new FutureCallback<List<Void>>() { | ||
450 | + @Override | ||
451 | + public void onSuccess(@Nullable List<Void> result) { | ||
452 | + if (result != null) { | ||
453 | + deferredResult.setResult(new ResponseEntity(HttpStatus.OK)); | ||
454 | + } else { | ||
455 | + deferredResult.setResult(new ResponseEntity(HttpStatus.BAD_REQUEST)); | ||
456 | + } | ||
457 | + } | ||
458 | + | ||
459 | + @Override | ||
460 | + public void onFailure(Throwable t) { | ||
461 | + deferredResult.setErrorResult(t); | ||
462 | + } | ||
463 | + }); | ||
464 | + return deferredResult; | ||
465 | + } catch (Exception e) { | ||
466 | + throw handleException(e); | ||
467 | + } | ||
468 | + } | ||
469 | + | ||
470 | + private String getSecretKey(ClaimRequest claimRequest) throws IOException { | ||
471 | + String secretKey = claimRequest.getSecretKey(); | ||
472 | + if (secretKey != null) { | ||
473 | + return secretKey; | ||
474 | + } | ||
475 | + return DataConstants.DEFAULT_SECRET_KEY; | ||
476 | + } | ||
382 | } | 477 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2019 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.controller.claim.data; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | + | ||
20 | +@Data | ||
21 | +public class ClaimRequest { | ||
22 | + | ||
23 | + private final String secretKey; | ||
24 | + | ||
25 | +} |
application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPermissions.java
renamed from
application/src/main/java/org/thingsboard/server/service/security/permission/CustomerUserPremissions.java
@@ -16,20 +16,20 @@ | @@ -16,20 +16,20 @@ | ||
16 | package org.thingsboard.server.service.security.permission; | 16 | package org.thingsboard.server.service.security.permission; |
17 | 17 | ||
18 | import org.springframework.stereotype.Component; | 18 | import org.springframework.stereotype.Component; |
19 | -import org.thingsboard.server.common.data.*; | 19 | +import org.thingsboard.server.common.data.DashboardInfo; |
20 | +import org.thingsboard.server.common.data.HasCustomerId; | ||
21 | +import org.thingsboard.server.common.data.HasTenantId; | ||
22 | +import org.thingsboard.server.common.data.User; | ||
20 | import org.thingsboard.server.common.data.id.DashboardId; | 23 | import org.thingsboard.server.common.data.id.DashboardId; |
21 | import org.thingsboard.server.common.data.id.EntityId; | 24 | import org.thingsboard.server.common.data.id.EntityId; |
22 | -import org.thingsboard.server.common.data.id.TenantId; | ||
23 | import org.thingsboard.server.common.data.id.UserId; | 25 | import org.thingsboard.server.common.data.id.UserId; |
24 | import org.thingsboard.server.common.data.security.Authority; | 26 | import org.thingsboard.server.common.data.security.Authority; |
25 | import org.thingsboard.server.service.security.model.SecurityUser; | 27 | import org.thingsboard.server.service.security.model.SecurityUser; |
26 | 28 | ||
27 | -import java.util.HashMap; | 29 | +@Component(value = "customerUserPermissions") |
30 | +public class CustomerUserPermissions extends AbstractPermissions { | ||
28 | 31 | ||
29 | -@Component(value="customerUserPermissions") | ||
30 | -public class CustomerUserPremissions extends AbstractPermissions { | ||
31 | - | ||
32 | - public CustomerUserPremissions() { | 32 | + public CustomerUserPermissions() { |
33 | super(); | 33 | super(); |
34 | put(Resource.ALARM, TenantAdminPermissions.tenantEntityPermissionChecker); | 34 | put(Resource.ALARM, TenantAdminPermissions.tenantEntityPermissionChecker); |
35 | put(Resource.ASSET, customerEntityPermissionChecker); | 35 | put(Resource.ASSET, customerEntityPermissionChecker); |
@@ -44,26 +44,26 @@ public class CustomerUserPremissions extends AbstractPermissions { | @@ -44,26 +44,26 @@ public class CustomerUserPremissions extends AbstractPermissions { | ||
44 | 44 | ||
45 | private static final PermissionChecker customerEntityPermissionChecker = | 45 | private static final PermissionChecker customerEntityPermissionChecker = |
46 | new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_CREDENTIALS, | 46 | new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_CREDENTIALS, |
47 | - Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY, Operation.RPC_CALL) { | 47 | + Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY, Operation.RPC_CALL, Operation.CLAIM_DEVICES) { |
48 | 48 | ||
49 | - @Override | ||
50 | - public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { | 49 | + @Override |
50 | + public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { | ||
51 | 51 | ||
52 | - if (!super.hasPermission(user, operation, entityId, entity)) { | ||
53 | - return false; | ||
54 | - } | ||
55 | - if (!user.getTenantId().equals(entity.getTenantId())) { | ||
56 | - return false; | ||
57 | - } | ||
58 | - if (!(entity instanceof HasCustomerId)) { | ||
59 | - return false; | ||
60 | - } | ||
61 | - if (!user.getCustomerId().equals(((HasCustomerId)entity).getCustomerId())) { | ||
62 | - return false; | ||
63 | - } | ||
64 | - return true; | ||
65 | - } | ||
66 | - }; | 52 | + if (!super.hasPermission(user, operation, entityId, entity)) { |
53 | + return false; | ||
54 | + } | ||
55 | + if (!user.getTenantId().equals(entity.getTenantId())) { | ||
56 | + return false; | ||
57 | + } | ||
58 | + if (!(entity instanceof HasCustomerId)) { | ||
59 | + return false; | ||
60 | + } | ||
61 | + if (!operation.equals(Operation.CLAIM_DEVICES) && !user.getCustomerId().equals(((HasCustomerId) entity).getCustomerId())) { | ||
62 | + return false; | ||
63 | + } | ||
64 | + return true; | ||
65 | + } | ||
66 | + }; | ||
67 | 67 | ||
68 | private static final PermissionChecker customerPermissionChecker = | 68 | private static final PermissionChecker customerPermissionChecker = |
69 | new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) { | 69 | new PermissionChecker.GenericPermissionChecker(Operation.READ, Operation.READ_ATTRIBUTES, Operation.READ_TELEMETRY) { |
@@ -18,6 +18,6 @@ package org.thingsboard.server.service.security.permission; | @@ -18,6 +18,6 @@ package org.thingsboard.server.service.security.permission; | ||
18 | public enum Operation { | 18 | public enum Operation { |
19 | 19 | ||
20 | ALL, CREATE, READ, WRITE, DELETE, ASSIGN_TO_CUSTOMER, UNASSIGN_FROM_CUSTOMER, RPC_CALL, | 20 | ALL, CREATE, READ, WRITE, DELETE, ASSIGN_TO_CUSTOMER, UNASSIGN_FROM_CUSTOMER, RPC_CALL, |
21 | - READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY | 21 | + READ_CREDENTIALS, WRITE_CREDENTIALS, READ_ATTRIBUTES, WRITE_ATTRIBUTES, READ_TELEMETRY, WRITE_TELEMETRY, CLAIM_DEVICES |
22 | 22 | ||
23 | } | 23 | } |
@@ -22,11 +22,31 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | @@ -22,11 +22,31 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
22 | import org.springframework.stereotype.Service; | 22 | import org.springframework.stereotype.Service; |
23 | import org.thingsboard.rule.engine.api.util.DonAsynchron; | 23 | import org.thingsboard.rule.engine.api.util.DonAsynchron; |
24 | import org.thingsboard.server.actors.ActorSystemContext; | 24 | import org.thingsboard.server.actors.ActorSystemContext; |
25 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
26 | +import org.thingsboard.server.common.data.id.TenantId; | ||
25 | import org.thingsboard.server.common.msg.cluster.ServerAddress; | 27 | import org.thingsboard.server.common.msg.cluster.ServerAddress; |
26 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 28 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
27 | import org.thingsboard.server.common.transport.service.AbstractTransportService; | 29 | import org.thingsboard.server.common.transport.service.AbstractTransportService; |
30 | +import org.thingsboard.server.dao.device.ClaimDevicesService; | ||
28 | import org.thingsboard.server.gen.transport.TransportProtos; | 31 | import org.thingsboard.server.gen.transport.TransportProtos; |
29 | -import org.thingsboard.server.gen.transport.TransportProtos.*; | 32 | +import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; |
33 | +import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg; | ||
34 | +import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; | ||
35 | +import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; | ||
36 | +import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; | ||
37 | +import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; | ||
38 | +import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; | ||
39 | +import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg; | ||
40 | +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | ||
41 | +import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; | ||
42 | +import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg; | ||
43 | +import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; | ||
44 | +import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg; | ||
45 | +import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; | ||
46 | +import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; | ||
47 | +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; | ||
48 | +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; | ||
49 | +import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; | ||
30 | import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; | 50 | import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; |
31 | import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; | 51 | import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; |
32 | import org.thingsboard.server.service.encoding.DataDecodingEncodingService; | 52 | import org.thingsboard.server.service.encoding.DataDecodingEncodingService; |
@@ -35,6 +55,7 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra | @@ -35,6 +55,7 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra | ||
35 | import javax.annotation.PostConstruct; | 55 | import javax.annotation.PostConstruct; |
36 | import javax.annotation.PreDestroy; | 56 | import javax.annotation.PreDestroy; |
37 | import java.util.Optional; | 57 | import java.util.Optional; |
58 | +import java.util.UUID; | ||
38 | import java.util.function.Consumer; | 59 | import java.util.function.Consumer; |
39 | 60 | ||
40 | /** | 61 | /** |
@@ -58,6 +79,8 @@ public class LocalTransportService extends AbstractTransportService implements R | @@ -58,6 +79,8 @@ public class LocalTransportService extends AbstractTransportService implements R | ||
58 | private ClusterRpcService rpcService; | 79 | private ClusterRpcService rpcService; |
59 | @Autowired | 80 | @Autowired |
60 | private DataDecodingEncodingService encodingService; | 81 | private DataDecodingEncodingService encodingService; |
82 | + @Autowired | ||
83 | + private ClaimDevicesService claimDevicesService; | ||
61 | 84 | ||
62 | @PostConstruct | 85 | @PostConstruct |
63 | public void init() { | 86 | public void init() { |
@@ -151,6 +174,23 @@ public class LocalTransportService extends AbstractTransportService implements R | @@ -151,6 +174,23 @@ public class LocalTransportService extends AbstractTransportService implements R | ||
151 | } | 174 | } |
152 | 175 | ||
153 | @Override | 176 | @Override |
177 | + protected void registerClaimingInfo(SessionInfoProto sessionInfo, ClaimDeviceMsg msg, TransportServiceCallback<Void> callback) { | ||
178 | + TransportToDeviceActorMsg toDeviceActorMsg = TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo).setClaimDevice(msg).build(); | ||
179 | + | ||
180 | + TransportToDeviceActorMsgWrapper wrapper = new TransportToDeviceActorMsgWrapper(toDeviceActorMsg); | ||
181 | + Optional<ServerAddress> address = routingService.resolveById(wrapper.getDeviceId()); | ||
182 | + if (address.isPresent()) { | ||
183 | + rpcService.tell(encodingService.convertToProtoDataMessage(address.get(), wrapper)); | ||
184 | + callback.onSuccess(null); | ||
185 | + } else { | ||
186 | + TenantId tenantId = new TenantId(new UUID(sessionInfo.getTenantIdMSB(), sessionInfo.getTenantIdLSB())); | ||
187 | + DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); | ||
188 | + DonAsynchron.withCallback(claimDevicesService.registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs()), | ||
189 | + callback::onSuccess, callback::onError); | ||
190 | + } | ||
191 | + } | ||
192 | + | ||
193 | + @Override | ||
154 | public void process(String nodeId, DeviceActorToTransportMsg msg) { | 194 | public void process(String nodeId, DeviceActorToTransportMsg msg) { |
155 | process(nodeId, msg, null, null); | 195 | process(nodeId, msg, null, null); |
156 | } | 196 | } |
@@ -103,6 +103,11 @@ security: | @@ -103,6 +103,11 @@ security: | ||
103 | user_token_access_enabled: "${SECURITY_USER_TOKEN_ACCESS_ENABLED:true}" | 103 | user_token_access_enabled: "${SECURITY_USER_TOKEN_ACCESS_ENABLED:true}" |
104 | # Enable/disable case-sensitive username login | 104 | # Enable/disable case-sensitive username login |
105 | user_login_case_sensitive: "${SECURITY_USER_LOGIN_CASE_SENSITIVE:true}" | 105 | user_login_case_sensitive: "${SECURITY_USER_LOGIN_CASE_SENSITIVE:true}" |
106 | + claim: | ||
107 | + # Enable/disable claiming devices, if false -> the device's [claimingAllowed] SERVER_SCOPE attribute must be set to [true] to allow claiming specific device | ||
108 | + allowClaimingByDefault: "${SECURITY_CLAIM_ALLOW_CLAIMING_BY_DEFAULT:true}" | ||
109 | + # Time allowed to claim the device in milliseconds | ||
110 | + duration: "${SECURITY_CLAIM_DURATION:60000}" # 1 minute, note this value must equal claimDevices.timeToLiveInMinutes value | ||
106 | 111 | ||
107 | # Dashboard parameters | 112 | # Dashboard parameters |
108 | dashboard: | 113 | dashboard: |
@@ -261,6 +266,9 @@ caffeine: | @@ -261,6 +266,9 @@ caffeine: | ||
261 | entityViews: | 266 | entityViews: |
262 | timeToLiveInMinutes: 1440 | 267 | timeToLiveInMinutes: 1440 |
263 | maxSize: 100000 | 268 | maxSize: 100000 |
269 | + claimDevices: | ||
270 | + timeToLiveInMinutes: 1 | ||
271 | + maxSize: 100000 | ||
264 | 272 | ||
265 | redis: | 273 | redis: |
266 | # standalone or cluster | 274 | # standalone or cluster |
@@ -22,4 +22,5 @@ public class CacheConstants { | @@ -22,4 +22,5 @@ public class CacheConstants { | ||
22 | public static final String SESSIONS_CACHE = "sessions"; | 22 | public static final String SESSIONS_CACHE = "sessions"; |
23 | public static final String ASSET_CACHE = "assets"; | 23 | public static final String ASSET_CACHE = "assets"; |
24 | public static final String ENTITY_VIEW_CACHE = "entityViews"; | 24 | public static final String ENTITY_VIEW_CACHE = "entityViews"; |
25 | + public static final String CLAIM_DEVICES_CACHE = "claimDevices"; | ||
25 | } | 26 | } |
@@ -59,4 +59,8 @@ public class DataConstants { | @@ -59,4 +59,8 @@ public class DataConstants { | ||
59 | 59 | ||
60 | public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE"; | 60 | public static final String RPC_CALL_FROM_SERVER_TO_DEVICE = "RPC_CALL_FROM_SERVER_TO_DEVICE"; |
61 | 61 | ||
62 | + public static final String DEFAULT_SECRET_KEY = ""; | ||
63 | + public static final String SECRET_KEY_FIELD_NAME = "secretKey"; | ||
64 | + public static final String DURATION_MS_FIELD_NAME = "durationMs"; | ||
65 | + | ||
62 | } | 66 | } |
@@ -28,7 +28,9 @@ public enum SessionMsgType { | @@ -28,7 +28,9 @@ public enum SessionMsgType { | ||
28 | 28 | ||
29 | RULE_ENGINE_ERROR, | 29 | RULE_ENGINE_ERROR, |
30 | 30 | ||
31 | - SESSION_OPEN, SESSION_CLOSE; | 31 | + SESSION_OPEN, SESSION_CLOSE, |
32 | + | ||
33 | + CLAIM_REQUEST(); | ||
32 | 34 | ||
33 | private final boolean requiresRulesProcessing; | 35 | private final boolean requiresRulesProcessing; |
34 | 36 |
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
@@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j; | @@ -19,7 +19,6 @@ import lombok.extern.slf4j.Slf4j; | ||
19 | import org.eclipse.californium.core.CoapResource; | 19 | import org.eclipse.californium.core.CoapResource; |
20 | import org.eclipse.californium.core.coap.CoAP.ResponseCode; | 20 | import org.eclipse.californium.core.coap.CoAP.ResponseCode; |
21 | import org.eclipse.californium.core.coap.Request; | 21 | import org.eclipse.californium.core.coap.Request; |
22 | -import org.eclipse.californium.core.coap.Response; | ||
23 | import org.eclipse.californium.core.network.Exchange; | 22 | import org.eclipse.californium.core.network.Exchange; |
24 | import org.eclipse.californium.core.network.ExchangeObserver; | 23 | import org.eclipse.californium.core.network.ExchangeObserver; |
25 | import org.eclipse.californium.core.server.resources.CoapExchange; | 24 | import org.eclipse.californium.core.server.resources.CoapExchange; |
@@ -122,6 +121,9 @@ public class CoapTransportResource extends CoapResource { | @@ -122,6 +121,9 @@ public class CoapTransportResource extends CoapResource { | ||
122 | processRequest(exchange, SessionMsgType.TO_SERVER_RPC_REQUEST); | 121 | processRequest(exchange, SessionMsgType.TO_SERVER_RPC_REQUEST); |
123 | } | 122 | } |
124 | break; | 123 | break; |
124 | + case CLAIM: | ||
125 | + processRequest(exchange, SessionMsgType.CLAIM_REQUEST); | ||
126 | + break; | ||
125 | } | 127 | } |
126 | } | 128 | } |
127 | } | 129 | } |
@@ -153,6 +155,11 @@ public class CoapTransportResource extends CoapResource { | @@ -153,6 +155,11 @@ public class CoapTransportResource extends CoapResource { | ||
153 | transportContext.getAdaptor().convertToPostTelemetry(sessionId, request), | 155 | transportContext.getAdaptor().convertToPostTelemetry(sessionId, request), |
154 | new CoapOkCallback(exchange)); | 156 | new CoapOkCallback(exchange)); |
155 | break; | 157 | break; |
158 | + case CLAIM_REQUEST: | ||
159 | + transportService.process(sessionInfo, | ||
160 | + transportContext.getAdaptor().convertToClaimDevice(sessionId, request, sessionInfo), | ||
161 | + new CoapOkCallback(exchange)); | ||
162 | + break; | ||
156 | case SUBSCRIBE_ATTRIBUTES_REQUEST: | 163 | case SUBSCRIBE_ATTRIBUTES_REQUEST: |
157 | advanced.setObserver(new CoapExchangeObserverProxy((ExchangeObserver) observerField.get(advanced), | 164 | advanced.setObserver(new CoapExchangeObserverProxy((ExchangeObserver) observerField.get(advanced), |
158 | registerAsyncCoapSession(exchange, request, sessionInfo, sessionId))); | 165 | registerAsyncCoapSession(exchange, request, sessionInfo, sessionId))); |
@@ -319,7 +326,7 @@ public class CoapTransportResource extends CoapResource { | @@ -319,7 +326,7 @@ public class CoapTransportResource extends CoapResource { | ||
319 | 326 | ||
320 | @Override | 327 | @Override |
321 | public void onSuccess(Void msg) { | 328 | public void onSuccess(Void msg) { |
322 | - exchange.respond(ResponseCode.VALID); | 329 | + exchange.respond(ResponseCode.VALID); |
323 | } | 330 | } |
324 | 331 | ||
325 | @Override | 332 | @Override |
@@ -22,7 +22,6 @@ import org.thingsboard.server.gen.transport.TransportProtos; | @@ -22,7 +22,6 @@ import org.thingsboard.server.gen.transport.TransportProtos; | ||
22 | import org.thingsboard.server.transport.coap.CoapTransportResource; | 22 | import org.thingsboard.server.transport.coap.CoapTransportResource; |
23 | 23 | ||
24 | import java.util.UUID; | 24 | import java.util.UUID; |
25 | -import java.util.Optional; | ||
26 | 25 | ||
27 | public interface CoapTransportAdaptor { | 26 | public interface CoapTransportAdaptor { |
28 | 27 | ||
@@ -36,6 +35,8 @@ public interface CoapTransportAdaptor { | @@ -36,6 +35,8 @@ public interface CoapTransportAdaptor { | ||
36 | 35 | ||
37 | TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException; | 36 | TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException; |
38 | 37 | ||
38 | + TransportProtos.ClaimDeviceMsg convertToClaimDevice(UUID sessionId, Request inbound, TransportProtos.SessionInfoProto sessionInfo) throws AdaptorException; | ||
39 | + | ||
39 | Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException; | 40 | Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException; |
40 | 41 | ||
41 | Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; | 42 | Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.AttributeUpdateNotificationMsg notificationMsg) throws AdaptorException; |
@@ -15,33 +15,36 @@ | @@ -15,33 +15,36 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.transport.coap.adaptors; | 16 | package org.thingsboard.server.transport.coap.adaptors; |
17 | 17 | ||
18 | -import java.util.*; | ||
19 | - | ||
20 | import com.google.gson.JsonElement; | 18 | import com.google.gson.JsonElement; |
21 | import com.google.gson.JsonObject; | 19 | import com.google.gson.JsonObject; |
20 | +import com.google.gson.JsonParser; | ||
21 | +import com.google.gson.JsonSyntaxException; | ||
22 | import lombok.extern.slf4j.Slf4j; | 22 | import lombok.extern.slf4j.Slf4j; |
23 | import org.eclipse.californium.core.coap.CoAP; | 23 | import org.eclipse.californium.core.coap.CoAP; |
24 | import org.eclipse.californium.core.coap.Request; | 24 | import org.eclipse.californium.core.coap.Request; |
25 | import org.eclipse.californium.core.coap.Response; | 25 | import org.eclipse.californium.core.coap.Response; |
26 | +import org.springframework.stereotype.Component; | ||
26 | import org.springframework.util.StringUtils; | 27 | import org.springframework.util.StringUtils; |
27 | -import org.thingsboard.server.common.msg.kv.AttributesKVMsg; | ||
28 | -import org.thingsboard.server.common.msg.session.SessionContext; | 28 | +import org.thingsboard.server.common.data.id.DeviceId; |
29 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; | 29 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
30 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; | 30 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
31 | -import org.springframework.stereotype.Component; | ||
32 | - | ||
33 | -import com.google.gson.JsonParser; | ||
34 | -import com.google.gson.JsonSyntaxException; | ||
35 | import org.thingsboard.server.gen.transport.TransportProtos; | 31 | import org.thingsboard.server.gen.transport.TransportProtos; |
36 | import org.thingsboard.server.transport.coap.CoapTransportResource; | 32 | import org.thingsboard.server.transport.coap.CoapTransportResource; |
37 | 33 | ||
34 | +import java.util.Arrays; | ||
35 | +import java.util.HashSet; | ||
36 | +import java.util.List; | ||
37 | +import java.util.Optional; | ||
38 | +import java.util.Set; | ||
39 | +import java.util.UUID; | ||
40 | + | ||
38 | @Component("JsonCoapAdaptor") | 41 | @Component("JsonCoapAdaptor") |
39 | @Slf4j | 42 | @Slf4j |
40 | public class JsonCoapAdaptor implements CoapTransportAdaptor { | 43 | public class JsonCoapAdaptor implements CoapTransportAdaptor { |
41 | 44 | ||
42 | @Override | 45 | @Override |
43 | public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound) throws AdaptorException { | 46 | public TransportProtos.PostTelemetryMsg convertToPostTelemetry(UUID sessionId, Request inbound) throws AdaptorException { |
44 | - String payload = validatePayload(sessionId, inbound); | 47 | + String payload = validatePayload(sessionId, inbound, false); |
45 | try { | 48 | try { |
46 | return JsonConverter.convertToTelemetryProto(new JsonParser().parse(payload)); | 49 | return JsonConverter.convertToTelemetryProto(new JsonParser().parse(payload)); |
47 | } catch (IllegalStateException | JsonSyntaxException ex) { | 50 | } catch (IllegalStateException | JsonSyntaxException ex) { |
@@ -51,7 +54,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { | @@ -51,7 +54,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { | ||
51 | 54 | ||
52 | @Override | 55 | @Override |
53 | public TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound) throws AdaptorException { | 56 | public TransportProtos.PostAttributeMsg convertToPostAttributes(UUID sessionId, Request inbound) throws AdaptorException { |
54 | - String payload = validatePayload(sessionId, inbound); | 57 | + String payload = validatePayload(sessionId, inbound, false); |
55 | try { | 58 | try { |
56 | return JsonConverter.convertToAttributesProto(new JsonParser().parse(payload)); | 59 | return JsonConverter.convertToAttributesProto(new JsonParser().parse(payload)); |
57 | } catch (IllegalStateException | JsonSyntaxException ex) { | 60 | } catch (IllegalStateException | JsonSyntaxException ex) { |
@@ -79,7 +82,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { | @@ -79,7 +82,7 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { | ||
79 | @Override | 82 | @Override |
80 | public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound) throws AdaptorException { | 83 | public TransportProtos.ToDeviceRpcResponseMsg convertToDeviceRpcResponse(UUID sessionId, Request inbound) throws AdaptorException { |
81 | Optional<Integer> requestId = CoapTransportResource.getRequestId(inbound); | 84 | Optional<Integer> requestId = CoapTransportResource.getRequestId(inbound); |
82 | - String payload = validatePayload(sessionId, inbound); | 85 | + String payload = validatePayload(sessionId, inbound, false); |
83 | JsonObject response = new JsonParser().parse(payload).getAsJsonObject(); | 86 | JsonObject response = new JsonParser().parse(payload).getAsJsonObject(); |
84 | return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId.orElseThrow(() -> new AdaptorException("Request id is missing!"))) | 87 | return TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId.orElseThrow(() -> new AdaptorException("Request id is missing!"))) |
85 | .setPayload(response.toString()).build(); | 88 | .setPayload(response.toString()).build(); |
@@ -87,11 +90,22 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { | @@ -87,11 +90,22 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { | ||
87 | 90 | ||
88 | @Override | 91 | @Override |
89 | public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException { | 92 | public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(UUID sessionId, Request inbound) throws AdaptorException { |
90 | - String payload = validatePayload(sessionId, inbound); | 93 | + String payload = validatePayload(sessionId, inbound, false); |
91 | return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), 0); | 94 | return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), 0); |
92 | } | 95 | } |
93 | 96 | ||
94 | @Override | 97 | @Override |
98 | + public TransportProtos.ClaimDeviceMsg convertToClaimDevice(UUID sessionId, Request inbound, TransportProtos.SessionInfoProto sessionInfo) throws AdaptorException { | ||
99 | + DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); | ||
100 | + String payload = validatePayload(sessionId, inbound, true); | ||
101 | + try { | ||
102 | + return JsonConverter.convertToClaimDeviceProto(deviceId, payload); | ||
103 | + } catch (IllegalStateException | JsonSyntaxException ex) { | ||
104 | + throw new AdaptorException(ex); | ||
105 | + } | ||
106 | + } | ||
107 | + | ||
108 | + @Override | ||
95 | public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.AttributeUpdateNotificationMsg msg) throws AdaptorException { | 109 | public Response convertToPublish(CoapTransportResource.CoapSessionListener session, TransportProtos.AttributeUpdateNotificationMsg msg) throws AdaptorException { |
96 | return getObserveNotification(session.getNextSeqNumber(), JsonConverter.toJson(msg)); | 110 | return getObserveNotification(session.getNextSeqNumber(), JsonConverter.toJson(msg)); |
97 | } | 111 | } |
@@ -128,11 +142,13 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { | @@ -128,11 +142,13 @@ public class JsonCoapAdaptor implements CoapTransportAdaptor { | ||
128 | return response; | 142 | return response; |
129 | } | 143 | } |
130 | 144 | ||
131 | - private String validatePayload(UUID sessionId, Request inbound) throws AdaptorException { | 145 | + private String validatePayload(UUID sessionId, Request inbound, boolean isEmptyPayloadAllowed) throws AdaptorException { |
132 | String payload = inbound.getPayloadString(); | 146 | String payload = inbound.getPayloadString(); |
133 | if (payload == null) { | 147 | if (payload == null) { |
134 | log.warn("[{}] Payload is empty!", sessionId); | 148 | log.warn("[{}] Payload is empty!", sessionId); |
135 | - throw new AdaptorException(new IllegalArgumentException("Payload is empty!")); | 149 | + if (!isEmptyPayloadAllowed) { |
150 | + throw new AdaptorException(new IllegalArgumentException("Payload is empty!")); | ||
151 | + } | ||
136 | } | 152 | } |
137 | return payload; | 153 | return payload; |
138 | } | 154 | } |
@@ -20,7 +20,6 @@ import com.google.gson.JsonParser; | @@ -20,7 +20,6 @@ import com.google.gson.JsonParser; | ||
20 | import lombok.extern.slf4j.Slf4j; | 20 | import lombok.extern.slf4j.Slf4j; |
21 | import org.springframework.beans.factory.annotation.Autowired; | 21 | import org.springframework.beans.factory.annotation.Autowired; |
22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | 22 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
23 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
24 | import org.springframework.http.HttpStatus; | 23 | import org.springframework.http.HttpStatus; |
25 | import org.springframework.http.ResponseEntity; | 24 | import org.springframework.http.ResponseEntity; |
26 | import org.springframework.util.StringUtils; | 25 | import org.springframework.util.StringUtils; |
@@ -31,6 +30,7 @@ import org.springframework.web.bind.annotation.RequestMethod; | @@ -31,6 +30,7 @@ import org.springframework.web.bind.annotation.RequestMethod; | ||
31 | import org.springframework.web.bind.annotation.RequestParam; | 30 | import org.springframework.web.bind.annotation.RequestParam; |
32 | import org.springframework.web.bind.annotation.RestController; | 31 | import org.springframework.web.bind.annotation.RestController; |
33 | import org.springframework.web.context.request.async.DeferredResult; | 32 | import org.springframework.web.context.request.async.DeferredResult; |
33 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
34 | import org.thingsboard.server.common.transport.SessionMsgListener; | 34 | import org.thingsboard.server.common.transport.SessionMsgListener; |
35 | import org.thingsboard.server.common.transport.TransportContext; | 35 | import org.thingsboard.server.common.transport.TransportContext; |
36 | import org.thingsboard.server.common.transport.TransportService; | 36 | import org.thingsboard.server.common.transport.TransportService; |
@@ -119,6 +119,20 @@ public class DeviceApiController { | @@ -119,6 +119,20 @@ public class DeviceApiController { | ||
119 | return responseWriter; | 119 | return responseWriter; |
120 | } | 120 | } |
121 | 121 | ||
122 | + @RequestMapping(value = "/{deviceToken}/claim", method = RequestMethod.POST) | ||
123 | + public DeferredResult<ResponseEntity> claimDevice(@PathVariable("deviceToken") String deviceToken, | ||
124 | + @RequestBody(required = false) String json, HttpServletRequest request) { | ||
125 | + DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>(); | ||
126 | + transportContext.getTransportService().process(ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), | ||
127 | + new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { | ||
128 | + TransportService transportService = transportContext.getTransportService(); | ||
129 | + DeviceId deviceId = new DeviceId(new UUID(sessionInfo.getDeviceIdMSB(), sessionInfo.getDeviceIdLSB())); | ||
130 | + transportService.process(sessionInfo, JsonConverter.convertToClaimDeviceProto(deviceId, json), | ||
131 | + new HttpOkCallback(responseWriter)); | ||
132 | + })); | ||
133 | + return responseWriter; | ||
134 | + } | ||
135 | + | ||
122 | @RequestMapping(value = "/{deviceToken}/rpc", method = RequestMethod.GET, produces = "application/json") | 136 | @RequestMapping(value = "/{deviceToken}/rpc", method = RequestMethod.GET, produces = "application/json") |
123 | public DeferredResult<ResponseEntity> subscribeToCommands(@PathVariable("deviceToken") String deviceToken, | 137 | public DeferredResult<ResponseEntity> subscribeToCommands(@PathVariable("deviceToken") String deviceToken, |
124 | @RequestParam(value = "timeout", required = false, defaultValue = "0") long timeout, | 138 | @RequestParam(value = "timeout", required = false, defaultValue = "0") long timeout, |
@@ -29,6 +29,7 @@ public class MqttTopics { | @@ -29,6 +29,7 @@ public class MqttTopics { | ||
29 | public static final String DEVICE_ATTRIBUTES_RESPONSES_TOPIC = DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + "+"; | 29 | public static final String DEVICE_ATTRIBUTES_RESPONSES_TOPIC = DEVICE_ATTRIBUTES_RESPONSE_TOPIC_PREFIX + "+"; |
30 | public static final String DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC + "/attributes/request/"; | 30 | public static final String DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC + "/attributes/request/"; |
31 | public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + "/telemetry"; | 31 | public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + "/telemetry"; |
32 | + public static final String DEVICE_CLAIM_TOPIC = BASE_DEVICE_API_TOPIC + "/claim"; | ||
32 | public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + "/attributes"; | 33 | public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + "/attributes"; |
33 | 34 | ||
34 | public static final String BASE_GATEWAY_API_TOPIC = "v1/gateway"; | 35 | public static final String BASE_GATEWAY_API_TOPIC = "v1/gateway"; |
@@ -36,6 +37,7 @@ public class MqttTopics { | @@ -36,6 +37,7 @@ public class MqttTopics { | ||
36 | public static final String GATEWAY_DISCONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + "/disconnect"; | 37 | public static final String GATEWAY_DISCONNECT_TOPIC = BASE_GATEWAY_API_TOPIC + "/disconnect"; |
37 | public static final String GATEWAY_ATTRIBUTES_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes"; | 38 | public static final String GATEWAY_ATTRIBUTES_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes"; |
38 | public static final String GATEWAY_TELEMETRY_TOPIC = BASE_GATEWAY_API_TOPIC + "/telemetry"; | 39 | public static final String GATEWAY_TELEMETRY_TOPIC = BASE_GATEWAY_API_TOPIC + "/telemetry"; |
40 | + public static final String GATEWAY_CLAIM_TOPIC = BASE_GATEWAY_API_TOPIC + "/claim"; | ||
39 | public static final String GATEWAY_RPC_TOPIC = BASE_GATEWAY_API_TOPIC + "/rpc"; | 41 | public static final String GATEWAY_RPC_TOPIC = BASE_GATEWAY_API_TOPIC + "/rpc"; |
40 | public static final String GATEWAY_ATTRIBUTES_REQUEST_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes/request"; | 42 | public static final String GATEWAY_ATTRIBUTES_REQUEST_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes/request"; |
41 | public static final String GATEWAY_ATTRIBUTES_RESPONSE_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes/response"; | 43 | public static final String GATEWAY_ATTRIBUTES_RESPONSE_TOPIC = BASE_GATEWAY_API_TOPIC + "/attributes/response"; |
@@ -38,11 +38,11 @@ import io.netty.util.concurrent.Future; | @@ -38,11 +38,11 @@ import io.netty.util.concurrent.Future; | ||
38 | import io.netty.util.concurrent.GenericFutureListener; | 38 | import io.netty.util.concurrent.GenericFutureListener; |
39 | import lombok.extern.slf4j.Slf4j; | 39 | import lombok.extern.slf4j.Slf4j; |
40 | import org.springframework.util.StringUtils; | 40 | import org.springframework.util.StringUtils; |
41 | +import org.thingsboard.server.common.msg.EncryptionUtil; | ||
41 | import org.thingsboard.server.common.transport.SessionMsgListener; | 42 | import org.thingsboard.server.common.transport.SessionMsgListener; |
42 | import org.thingsboard.server.common.transport.TransportService; | 43 | import org.thingsboard.server.common.transport.TransportService; |
43 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 44 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
44 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; | 45 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
45 | -import org.thingsboard.server.common.msg.EncryptionUtil; | ||
46 | import org.thingsboard.server.common.transport.service.AbstractTransportService; | 46 | import org.thingsboard.server.common.transport.service.AbstractTransportService; |
47 | import org.thingsboard.server.gen.transport.TransportProtos; | 47 | import org.thingsboard.server.gen.transport.TransportProtos; |
48 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; | 48 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; |
@@ -183,6 +183,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -183,6 +183,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
183 | case MqttTopics.GATEWAY_TELEMETRY_TOPIC: | 183 | case MqttTopics.GATEWAY_TELEMETRY_TOPIC: |
184 | gatewaySessionHandler.onDeviceTelemetry(mqttMsg); | 184 | gatewaySessionHandler.onDeviceTelemetry(mqttMsg); |
185 | break; | 185 | break; |
186 | + case MqttTopics.GATEWAY_CLAIM_TOPIC: | ||
187 | + gatewaySessionHandler.onDeviceClaim(mqttMsg); | ||
188 | + break; | ||
186 | case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC: | 189 | case MqttTopics.GATEWAY_ATTRIBUTES_TOPIC: |
187 | gatewaySessionHandler.onDeviceAttributes(mqttMsg); | 190 | gatewaySessionHandler.onDeviceAttributes(mqttMsg); |
188 | break; | 191 | break; |
@@ -221,6 +224,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | @@ -221,6 +224,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement | ||
221 | } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC)) { | 224 | } else if (topicName.startsWith(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC)) { |
222 | TransportProtos.ToServerRpcRequestMsg rpcRequestMsg = adaptor.convertToServerRpcRequest(deviceSessionCtx, mqttMsg); | 225 | TransportProtos.ToServerRpcRequestMsg rpcRequestMsg = adaptor.convertToServerRpcRequest(deviceSessionCtx, mqttMsg); |
223 | transportService.process(sessionInfo, rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg)); | 226 | transportService.process(sessionInfo, rpcRequestMsg, getPubAckCallback(ctx, msgId, rpcRequestMsg)); |
227 | + } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) { | ||
228 | + TransportProtos.ClaimDeviceMsg claimDeviceMsg = adaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg); | ||
229 | + transportService.process(sessionInfo, claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg)); | ||
224 | } | 230 | } |
225 | } catch (AdaptorException e) { | 231 | } catch (AdaptorException e) { |
226 | log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); | 232 | log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); |
@@ -57,7 +57,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | @@ -57,7 +57,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | ||
57 | 57 | ||
58 | @Override | 58 | @Override |
59 | public TransportProtos.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | 59 | public TransportProtos.PostTelemetryMsg convertToPostTelemetry(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { |
60 | - String payload = validatePayload(ctx.getSessionId(), inbound.payload()); | 60 | + String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); |
61 | try { | 61 | try { |
62 | return JsonConverter.convertToTelemetryProto(new JsonParser().parse(payload)); | 62 | return JsonConverter.convertToTelemetryProto(new JsonParser().parse(payload)); |
63 | } catch (IllegalStateException | JsonSyntaxException ex) { | 63 | } catch (IllegalStateException | JsonSyntaxException ex) { |
@@ -67,7 +67,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | @@ -67,7 +67,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | ||
67 | 67 | ||
68 | @Override | 68 | @Override |
69 | public TransportProtos.PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | 69 | public TransportProtos.PostAttributeMsg convertToPostAttributes(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { |
70 | - String payload = validatePayload(ctx.getSessionId(), inbound.payload()); | 70 | + String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); |
71 | try { | 71 | try { |
72 | return JsonConverter.convertToAttributesProto(new JsonParser().parse(payload)); | 72 | return JsonConverter.convertToAttributesProto(new JsonParser().parse(payload)); |
73 | } catch (IllegalStateException | JsonSyntaxException ex) { | 73 | } catch (IllegalStateException | JsonSyntaxException ex) { |
@@ -114,7 +114,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | @@ -114,7 +114,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | ||
114 | @Override | 114 | @Override |
115 | public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | 115 | public TransportProtos.ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { |
116 | String topicName = inbound.variableHeader().topicName(); | 116 | String topicName = inbound.variableHeader().topicName(); |
117 | - String payload = validatePayload(ctx.getSessionId(), inbound.payload()); | 117 | + String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); |
118 | try { | 118 | try { |
119 | Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC.length())); | 119 | Integer requestId = Integer.valueOf(topicName.substring(MqttTopics.DEVICE_RPC_REQUESTS_TOPIC.length())); |
120 | return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), requestId); | 120 | return JsonConverter.convertToServerRpcRequest(new JsonParser().parse(payload), requestId); |
@@ -124,6 +124,16 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | @@ -124,6 +124,16 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | ||
124 | } | 124 | } |
125 | 125 | ||
126 | @Override | 126 | @Override |
127 | + public TransportProtos.ClaimDeviceMsg convertToClaimDevice(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { | ||
128 | + String payload = validatePayload(ctx.getSessionId(), inbound.payload(), true); | ||
129 | + try { | ||
130 | + return JsonConverter.convertToClaimDeviceProto(ctx.getDeviceId(), payload); | ||
131 | + } catch (IllegalStateException | JsonSyntaxException ex) { | ||
132 | + throw new AdaptorException(ex); | ||
133 | + } | ||
134 | + } | ||
135 | + | ||
136 | + @Override | ||
127 | public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { | 137 | public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, TransportProtos.GetAttributeResponseMsg responseMsg) throws AdaptorException { |
128 | if (!StringUtils.isEmpty(responseMsg.getError())) { | 138 | if (!StringUtils.isEmpty(responseMsg.getError())) { |
129 | throw new AdaptorException(responseMsg.getError()); | 139 | throw new AdaptorException(responseMsg.getError()); |
@@ -193,7 +203,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | @@ -193,7 +203,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | ||
193 | } | 203 | } |
194 | 204 | ||
195 | public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { | 205 | public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { |
196 | - String payload = validatePayload(sessionId, payloadData); | 206 | + String payload = validatePayload(sessionId, payloadData, false); |
197 | try { | 207 | try { |
198 | return new JsonParser().parse(payload); | 208 | return new JsonParser().parse(payload); |
199 | } catch (JsonSyntaxException ex) { | 209 | } catch (JsonSyntaxException ex) { |
@@ -201,12 +211,14 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | @@ -201,12 +211,14 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { | ||
201 | } | 211 | } |
202 | } | 212 | } |
203 | 213 | ||
204 | - private static String validatePayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { | 214 | + private static String validatePayload(UUID sessionId, ByteBuf payloadData, boolean isEmptyPayloadAllowed) throws AdaptorException { |
205 | try { | 215 | try { |
206 | String payload = payloadData.toString(UTF8); | 216 | String payload = payloadData.toString(UTF8); |
207 | if (payload == null) { | 217 | if (payload == null) { |
208 | log.warn("[{}] Payload is empty!", sessionId); | 218 | log.warn("[{}] Payload is empty!", sessionId); |
209 | - throw new AdaptorException(new IllegalArgumentException("Payload is empty!")); | 219 | + if (!isEmptyPayloadAllowed) { |
220 | + throw new AdaptorException(new IllegalArgumentException("Payload is empty!")); | ||
221 | + } | ||
210 | } | 222 | } |
211 | return payload; | 223 | return payload; |
212 | } finally { | 224 | } finally { |
@@ -19,6 +19,7 @@ import io.netty.handler.codec.mqtt.MqttMessage; | @@ -19,6 +19,7 @@ import io.netty.handler.codec.mqtt.MqttMessage; | ||
19 | import io.netty.handler.codec.mqtt.MqttPublishMessage; | 19 | import io.netty.handler.codec.mqtt.MqttPublishMessage; |
20 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; | 20 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
21 | import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; | 21 | import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; |
22 | +import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; | ||
22 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; | 23 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; |
23 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; | 24 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; |
24 | import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; | 25 | import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; |
@@ -46,6 +47,8 @@ public interface MqttTransportAdaptor { | @@ -46,6 +47,8 @@ public interface MqttTransportAdaptor { | ||
46 | 47 | ||
47 | ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException; | 48 | ToServerRpcRequestMsg convertToServerRpcRequest(MqttDeviceAwareSessionContext ctx, MqttPublishMessage mqttMsg) throws AdaptorException; |
48 | 49 | ||
50 | + ClaimDeviceMsg convertToClaimDevice(MqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException; | ||
51 | + | ||
49 | Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, GetAttributeResponseMsg responseMsg) throws AdaptorException; | 52 | Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, GetAttributeResponseMsg responseMsg) throws AdaptorException; |
50 | 53 | ||
51 | Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, GetAttributeResponseMsg responseMsg) throws AdaptorException; | 54 | Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, GetAttributeResponseMsg responseMsg) throws AdaptorException; |
@@ -59,4 +62,5 @@ public interface MqttTransportAdaptor { | @@ -59,4 +62,5 @@ public interface MqttTransportAdaptor { | ||
59 | Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException; | 62 | Optional<MqttMessage> convertToGatewayPublish(MqttDeviceAwareSessionContext ctx, String deviceName, ToDeviceRpcRequestMsg rpcRequest) throws AdaptorException; |
60 | 63 | ||
61 | Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ToServerRpcResponseMsg rpcResponse) throws AdaptorException; | 64 | Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ToServerRpcResponseMsg rpcResponse) throws AdaptorException; |
65 | + | ||
62 | } | 66 | } |
@@ -30,6 +30,7 @@ import io.netty.handler.codec.mqtt.MqttMessage; | @@ -30,6 +30,7 @@ import io.netty.handler.codec.mqtt.MqttMessage; | ||
30 | import io.netty.handler.codec.mqtt.MqttPublishMessage; | 30 | import io.netty.handler.codec.mqtt.MqttPublishMessage; |
31 | import lombok.extern.slf4j.Slf4j; | 31 | import lombok.extern.slf4j.Slf4j; |
32 | import org.springframework.util.StringUtils; | 32 | import org.springframework.util.StringUtils; |
33 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
33 | import org.thingsboard.server.common.transport.TransportService; | 34 | import org.thingsboard.server.common.transport.TransportService; |
34 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 35 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
35 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; | 36 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
@@ -183,7 +184,42 @@ public class GatewaySessionHandler { | @@ -183,7 +184,42 @@ public class GatewaySessionHandler { | ||
183 | 184 | ||
184 | @Override | 185 | @Override |
185 | public void onFailure(Throwable t) { | 186 | public void onFailure(Throwable t) { |
186 | - log.debug("[{}] Failed to process device teleemtry command: {}", sessionId, deviceName, t); | 187 | + log.debug("[{}] Failed to process device telemetry command: {}", sessionId, deviceName, t); |
188 | + } | ||
189 | + }, context.getExecutor()); | ||
190 | + } | ||
191 | + } else { | ||
192 | + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); | ||
193 | + } | ||
194 | + } | ||
195 | + | ||
196 | + public void onDeviceClaim(MqttPublishMessage mqttMsg) throws AdaptorException { | ||
197 | + JsonElement json = JsonMqttAdaptor.validateJsonPayload(sessionId, mqttMsg.payload()); | ||
198 | + int msgId = mqttMsg.variableHeader().packetId(); | ||
199 | + if (json.isJsonObject()) { | ||
200 | + JsonObject jsonObj = json.getAsJsonObject(); | ||
201 | + for (Map.Entry<String, JsonElement> deviceEntry : jsonObj.entrySet()) { | ||
202 | + String deviceName = deviceEntry.getKey(); | ||
203 | + Futures.addCallback(checkDeviceConnected(deviceName), | ||
204 | + new FutureCallback<GatewayDeviceSessionCtx>() { | ||
205 | + @Override | ||
206 | + public void onSuccess(@Nullable GatewayDeviceSessionCtx deviceCtx) { | ||
207 | + if (!deviceEntry.getValue().isJsonObject()) { | ||
208 | + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + json); | ||
209 | + } | ||
210 | + try { | ||
211 | + DeviceId deviceId = deviceCtx.getDeviceId(); | ||
212 | + TransportProtos.ClaimDeviceMsg claimDeviceMsg = JsonConverter.convertToClaimDeviceProto(deviceId, deviceEntry.getValue()); | ||
213 | + transportService.process(deviceCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(channel, deviceName, msgId, claimDeviceMsg)); | ||
214 | + } catch (Throwable e) { | ||
215 | + UUID gatewayId = new UUID(gateway.getDeviceIdMSB(), gateway.getDeviceIdLSB()); | ||
216 | + log.warn("[{}][{}] Failed to convert claim message: {}", gatewayId, deviceName, deviceEntry.getValue(), e); | ||
217 | + } | ||
218 | + } | ||
219 | + | ||
220 | + @Override | ||
221 | + public void onFailure(Throwable t) { | ||
222 | + log.debug("[{}] Failed to process device claiming command: {}", sessionId, deviceName, t); | ||
187 | } | 223 | } |
188 | }, context.getExecutor()); | 224 | }, context.getExecutor()); |
189 | } | 225 | } |
@@ -209,6 +245,7 @@ public class GatewaySessionHandler { | @@ -209,6 +245,7 @@ public class GatewaySessionHandler { | ||
209 | TransportProtos.PostAttributeMsg postAttributeMsg = JsonConverter.convertToAttributesProto(deviceEntry.getValue().getAsJsonObject()); | 245 | TransportProtos.PostAttributeMsg postAttributeMsg = JsonConverter.convertToAttributesProto(deviceEntry.getValue().getAsJsonObject()); |
210 | transportService.process(deviceCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(channel, deviceName, msgId, postAttributeMsg)); | 246 | transportService.process(deviceCtx.getSessionInfo(), postAttributeMsg, getPubAckCallback(channel, deviceName, msgId, postAttributeMsg)); |
211 | } | 247 | } |
248 | + | ||
212 | @Override | 249 | @Override |
213 | public void onFailure(Throwable t) { | 250 | public void onFailure(Throwable t) { |
214 | log.debug("[{}] Failed to process device attributes command: {}", sessionId, deviceName, t); | 251 | log.debug("[{}] Failed to process device attributes command: {}", sessionId, deviceName, t); |
@@ -16,18 +16,19 @@ | @@ -16,18 +16,19 @@ | ||
16 | package org.thingsboard.server.common.transport; | 16 | package org.thingsboard.server.common.transport; |
17 | 17 | ||
18 | import org.thingsboard.server.gen.transport.TransportProtos; | 18 | import org.thingsboard.server.gen.transport.TransportProtos; |
19 | -import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg; | ||
20 | -import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; | ||
21 | -import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; | ||
22 | -import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg; | ||
23 | -import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | 19 | +import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; |
20 | +import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; | ||
24 | import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; | 21 | import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; |
25 | import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; | 22 | import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; |
26 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg; | 23 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg; |
24 | +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | ||
25 | +import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; | ||
26 | +import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg; | ||
27 | +import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; | ||
28 | +import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg; | ||
27 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; | 29 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; |
28 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; | 30 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; |
29 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; | 31 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; |
30 | -import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; | ||
31 | 32 | ||
32 | /** | 33 | /** |
33 | * Created by ashvayka on 04.10.18. | 34 | * Created by ashvayka on 04.10.18. |
@@ -63,6 +64,8 @@ public interface TransportService { | @@ -63,6 +64,8 @@ public interface TransportService { | ||
63 | 64 | ||
64 | void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscriptionInfoProto msg, TransportServiceCallback<Void> callback); | 65 | void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.SubscriptionInfoProto msg, TransportServiceCallback<Void> callback); |
65 | 66 | ||
67 | + void process(SessionInfoProto sessionInfo, ClaimDeviceMsg msg, TransportServiceCallback<Void> callback); | ||
68 | + | ||
66 | void registerAsyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener); | 69 | void registerAsyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener); |
67 | 70 | ||
68 | void registerSyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout); | 71 | void registerSyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener, long timeout); |
@@ -24,6 +24,8 @@ import com.google.gson.JsonPrimitive; | @@ -24,6 +24,8 @@ import com.google.gson.JsonPrimitive; | ||
24 | import com.google.gson.JsonSyntaxException; | 24 | import com.google.gson.JsonSyntaxException; |
25 | import org.apache.commons.lang3.math.NumberUtils; | 25 | import org.apache.commons.lang3.math.NumberUtils; |
26 | import org.springframework.util.StringUtils; | 26 | import org.springframework.util.StringUtils; |
27 | +import org.thingsboard.server.common.data.DataConstants; | ||
28 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
27 | import org.thingsboard.server.common.data.kv.AttributeKey; | 29 | import org.thingsboard.server.common.data.kv.AttributeKey; |
28 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; | 30 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
29 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | 31 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
@@ -35,6 +37,7 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; | @@ -35,6 +37,7 @@ import org.thingsboard.server.common.data.kv.StringDataEntry; | ||
35 | import org.thingsboard.server.common.msg.kv.AttributesKVMsg; | 37 | import org.thingsboard.server.common.msg.kv.AttributesKVMsg; |
36 | import org.thingsboard.server.gen.transport.TransportProtos; | 38 | import org.thingsboard.server.gen.transport.TransportProtos; |
37 | import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; | 39 | import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; |
40 | +import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; | ||
38 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; | 41 | import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; |
39 | import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; | 42 | import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; |
40 | import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; | 43 | import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType; |
@@ -63,23 +66,69 @@ public class JsonConverter { | @@ -63,23 +66,69 @@ public class JsonConverter { | ||
63 | 66 | ||
64 | private static int maxStringValueLength = 0; | 67 | private static int maxStringValueLength = 0; |
65 | 68 | ||
66 | - public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonObject) throws JsonSyntaxException { | ||
67 | - long systemTs = System.currentTimeMillis(); | 69 | + public static PostTelemetryMsg convertToTelemetryProto(JsonElement jsonElement) throws JsonSyntaxException { |
68 | PostTelemetryMsg.Builder builder = PostTelemetryMsg.newBuilder(); | 70 | PostTelemetryMsg.Builder builder = PostTelemetryMsg.newBuilder(); |
69 | - if (jsonObject.isJsonObject()) { | ||
70 | - parseObject(builder, systemTs, jsonObject); | ||
71 | - } else if (jsonObject.isJsonArray()) { | ||
72 | - jsonObject.getAsJsonArray().forEach(je -> { | 71 | + convertToTelemetry(jsonElement, System.currentTimeMillis(), null, builder); |
72 | + return builder.build(); | ||
73 | + } | ||
74 | + | ||
75 | + private static void convertToTelemetry(JsonElement jsonElement, long systemTs, Map<Long, List<KvEntry>> result, PostTelemetryMsg.Builder builder) { | ||
76 | + if (jsonElement.isJsonObject()) { | ||
77 | + parseObject(systemTs, result, builder, jsonElement.getAsJsonObject()); | ||
78 | + } else if (jsonElement.isJsonArray()) { | ||
79 | + jsonElement.getAsJsonArray().forEach(je -> { | ||
73 | if (je.isJsonObject()) { | 80 | if (je.isJsonObject()) { |
74 | - parseObject(builder, systemTs, je.getAsJsonObject()); | 81 | + parseObject(systemTs, result, builder, je.getAsJsonObject()); |
75 | } else { | 82 | } else { |
76 | throw new JsonSyntaxException(CAN_T_PARSE_VALUE + je); | 83 | throw new JsonSyntaxException(CAN_T_PARSE_VALUE + je); |
77 | } | 84 | } |
78 | }); | 85 | }); |
79 | } else { | 86 | } else { |
80 | - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonObject); | 87 | + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonElement); |
81 | } | 88 | } |
82 | - return builder.build(); | 89 | + } |
90 | + | ||
91 | + private static void parseObject(long systemTs, Map<Long, List<KvEntry>> result, PostTelemetryMsg.Builder builder, JsonObject jo) { | ||
92 | + if (result != null) { | ||
93 | + parseObject(result, systemTs, jo); | ||
94 | + } else { | ||
95 | + parseObject(builder, systemTs, jo); | ||
96 | + } | ||
97 | + } | ||
98 | + | ||
99 | + public static ClaimDeviceMsg convertToClaimDeviceProto(DeviceId deviceId, String json) { | ||
100 | + long durationMs = 0L; | ||
101 | + if (json != null && !json.isEmpty()) { | ||
102 | + return convertToClaimDeviceProto(deviceId, new JsonParser().parse(json)); | ||
103 | + } | ||
104 | + return buildClaimDeviceMsg(deviceId, DataConstants.DEFAULT_SECRET_KEY, durationMs); | ||
105 | + } | ||
106 | + | ||
107 | + public static ClaimDeviceMsg convertToClaimDeviceProto(DeviceId deviceId, JsonElement jsonElement) { | ||
108 | + String secretKey = DataConstants.DEFAULT_SECRET_KEY; | ||
109 | + long durationMs = 0L; | ||
110 | + if (jsonElement.isJsonObject()) { | ||
111 | + JsonObject jo = jsonElement.getAsJsonObject(); | ||
112 | + if (jo.has(DataConstants.SECRET_KEY_FIELD_NAME)) { | ||
113 | + secretKey = jo.get(DataConstants.SECRET_KEY_FIELD_NAME).getAsString(); | ||
114 | + } | ||
115 | + if (jo.has(DataConstants.DURATION_MS_FIELD_NAME)) { | ||
116 | + durationMs = jo.get(DataConstants.DURATION_MS_FIELD_NAME).getAsLong(); | ||
117 | + } | ||
118 | + } else { | ||
119 | + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonElement); | ||
120 | + } | ||
121 | + return buildClaimDeviceMsg(deviceId, secretKey, durationMs); | ||
122 | + } | ||
123 | + | ||
124 | + private static ClaimDeviceMsg buildClaimDeviceMsg(DeviceId deviceId, String secretKey, long durationMs) { | ||
125 | + ClaimDeviceMsg.Builder result = ClaimDeviceMsg.newBuilder(); | ||
126 | + return result | ||
127 | + .setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) | ||
128 | + .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) | ||
129 | + .setSecretKey(secretKey) | ||
130 | + .setDurationMs(durationMs) | ||
131 | + .build(); | ||
83 | } | 132 | } |
84 | 133 | ||
85 | public static PostAttributeMsg convertToAttributesProto(JsonElement jsonObject) throws JsonSyntaxException { | 134 | public static PostAttributeMsg convertToAttributesProto(JsonElement jsonObject) throws JsonSyntaxException { |
@@ -103,8 +152,7 @@ public class JsonConverter { | @@ -103,8 +152,7 @@ public class JsonConverter { | ||
103 | return result; | 152 | return result; |
104 | } | 153 | } |
105 | 154 | ||
106 | - private static void parseObject(PostTelemetryMsg.Builder builder, long systemTs, JsonElement jsonObject) { | ||
107 | - JsonObject jo = jsonObject.getAsJsonObject(); | 155 | + private static void parseObject(PostTelemetryMsg.Builder builder, long systemTs, JsonObject jo) { |
108 | if (jo.has("ts") && jo.has("values")) { | 156 | if (jo.has("ts") && jo.has("values")) { |
109 | parseWithTs(builder, jo); | 157 | parseWithTs(builder, jo); |
110 | } else { | 158 | } else { |
@@ -137,7 +185,7 @@ public class JsonConverter { | @@ -137,7 +185,7 @@ public class JsonConverter { | ||
137 | String message = String.format("String value length [%d] for key [%s] is greater than maximum allowed [%d]", value.getAsString().length(), valueEntry.getKey(), maxStringValueLength); | 185 | String message = String.format("String value length [%d] for key [%s] is greater than maximum allowed [%d]", value.getAsString().length(), valueEntry.getKey(), maxStringValueLength); |
138 | throw new JsonSyntaxException(message); | 186 | throw new JsonSyntaxException(message); |
139 | } | 187 | } |
140 | - if(isTypeCastEnabled && NumberUtils.isParsable(value.getAsString())) { | 188 | + if (isTypeCastEnabled && NumberUtils.isParsable(value.getAsString())) { |
141 | try { | 189 | try { |
142 | result.add(buildNumericKeyValueProto(value, valueEntry.getKey())); | 190 | result.add(buildNumericKeyValueProto(value, valueEntry.getKey())); |
143 | } catch (RuntimeException th) { | 191 | } catch (RuntimeException th) { |
@@ -400,7 +448,7 @@ public class JsonConverter { | @@ -400,7 +448,7 @@ public class JsonConverter { | ||
400 | String message = String.format("String value length [%d] for key [%s] is greater than maximum allowed [%d]", value.getAsString().length(), valueEntry.getKey(), maxStringValueLength); | 448 | String message = String.format("String value length [%d] for key [%s] is greater than maximum allowed [%d]", value.getAsString().length(), valueEntry.getKey(), maxStringValueLength); |
401 | throw new JsonSyntaxException(message); | 449 | throw new JsonSyntaxException(message); |
402 | } | 450 | } |
403 | - if(isTypeCastEnabled && NumberUtils.isParsable(value.getAsString())) { | 451 | + if (isTypeCastEnabled && NumberUtils.isParsable(value.getAsString())) { |
404 | try { | 452 | try { |
405 | parseNumericValue(result, valueEntry, value); | 453 | parseNumericValue(result, valueEntry, value); |
406 | } catch (RuntimeException th) { | 454 | } catch (RuntimeException th) { |
@@ -423,26 +471,13 @@ public class JsonConverter { | @@ -423,26 +471,13 @@ public class JsonConverter { | ||
423 | return result; | 471 | return result; |
424 | } | 472 | } |
425 | 473 | ||
426 | - public static Map<Long, List<KvEntry>> convertToTelemetry(JsonElement jsonObject, long systemTs) throws JsonSyntaxException { | 474 | + public static Map<Long, List<KvEntry>> convertToTelemetry(JsonElement jsonElement, long systemTs) throws JsonSyntaxException { |
427 | Map<Long, List<KvEntry>> result = new HashMap<>(); | 475 | Map<Long, List<KvEntry>> result = new HashMap<>(); |
428 | - if (jsonObject.isJsonObject()) { | ||
429 | - parseObject(result, systemTs, jsonObject); | ||
430 | - } else if (jsonObject.isJsonArray()) { | ||
431 | - jsonObject.getAsJsonArray().forEach(je -> { | ||
432 | - if (je.isJsonObject()) { | ||
433 | - parseObject(result, systemTs, je.getAsJsonObject()); | ||
434 | - } else { | ||
435 | - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + je); | ||
436 | - } | ||
437 | - }); | ||
438 | - } else { | ||
439 | - throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonObject); | ||
440 | - } | 476 | + convertToTelemetry(jsonElement, systemTs, result, null); |
441 | return result; | 477 | return result; |
442 | } | 478 | } |
443 | 479 | ||
444 | - private static void parseObject(Map<Long, List<KvEntry>> result, long systemTs, JsonElement jsonObject) { | ||
445 | - JsonObject jo = jsonObject.getAsJsonObject(); | 480 | + private static void parseObject(Map<Long, List<KvEntry>> result, long systemTs, JsonObject jo) { |
446 | if (jo.has("ts") && jo.has("values")) { | 481 | if (jo.has("ts") && jo.has("values")) { |
447 | parseWithTs(result, jo); | 482 | parseWithTs(result, jo); |
448 | } else { | 483 | } else { |
@@ -28,7 +28,12 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; | @@ -28,7 +28,12 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; | ||
28 | import org.thingsboard.server.gen.transport.TransportProtos; | 28 | import org.thingsboard.server.gen.transport.TransportProtos; |
29 | 29 | ||
30 | import java.util.UUID; | 30 | import java.util.UUID; |
31 | -import java.util.concurrent.*; | 31 | +import java.util.concurrent.ConcurrentHashMap; |
32 | +import java.util.concurrent.ConcurrentMap; | ||
33 | +import java.util.concurrent.ExecutorService; | ||
34 | +import java.util.concurrent.Executors; | ||
35 | +import java.util.concurrent.ScheduledExecutorService; | ||
36 | +import java.util.concurrent.TimeUnit; | ||
32 | 37 | ||
33 | /** | 38 | /** |
34 | * Created by ashvayka on 17.10.18. | 39 | * Created by ashvayka on 17.10.18. |
@@ -128,6 +133,12 @@ public abstract class AbstractTransportService implements TransportService { | @@ -128,6 +133,12 @@ public abstract class AbstractTransportService implements TransportService { | ||
128 | } | 133 | } |
129 | 134 | ||
130 | @Override | 135 | @Override |
136 | + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ClaimDeviceMsg msg, | ||
137 | + TransportServiceCallback<Void> callback) { | ||
138 | + registerClaimingInfo(sessionInfo, msg, callback); | ||
139 | + } | ||
140 | + | ||
141 | + @Override | ||
131 | public void reportActivity(TransportProtos.SessionInfoProto sessionInfo) { | 142 | public void reportActivity(TransportProtos.SessionInfoProto sessionInfo) { |
132 | reportActivityInternal(sessionInfo); | 143 | reportActivityInternal(sessionInfo); |
133 | } | 144 | } |
@@ -148,6 +159,8 @@ public abstract class AbstractTransportService implements TransportService { | @@ -148,6 +159,8 @@ public abstract class AbstractTransportService implements TransportService { | ||
148 | 159 | ||
149 | protected abstract void doProcess(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback); | 160 | protected abstract void doProcess(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToServerRpcRequestMsg msg, TransportServiceCallback<Void> callback); |
150 | 161 | ||
162 | + protected abstract void registerClaimingInfo(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ClaimDeviceMsg msg, TransportServiceCallback<Void> callback); | ||
163 | + | ||
151 | private SessionMetaData reportActivityInternal(TransportProtos.SessionInfoProto sessionInfo) { | 164 | private SessionMetaData reportActivityInternal(TransportProtos.SessionInfoProto sessionInfo) { |
152 | UUID sessionId = toId(sessionInfo); | 165 | UUID sessionId = toId(sessionInfo); |
153 | SessionMetaData sessionMetaData = sessions.get(sessionId); | 166 | SessionMetaData sessionMetaData = sessions.get(sessionId); |
@@ -24,34 +24,40 @@ import org.apache.kafka.clients.producer.RecordMetadata; | @@ -24,34 +24,40 @@ import org.apache.kafka.clients.producer.RecordMetadata; | ||
24 | import org.springframework.beans.factory.annotation.Autowired; | 24 | import org.springframework.beans.factory.annotation.Autowired; |
25 | import org.springframework.beans.factory.annotation.Value; | 25 | import org.springframework.beans.factory.annotation.Value; |
26 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | 26 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
27 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
28 | import org.springframework.stereotype.Service; | 27 | import org.springframework.stereotype.Service; |
29 | -import org.thingsboard.server.common.transport.SessionMsgListener; | ||
30 | -import org.thingsboard.server.common.transport.TransportService; | ||
31 | import org.thingsboard.server.common.transport.TransportServiceCallback; | 28 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
32 | -import org.thingsboard.server.gen.transport.TransportProtos; | ||
33 | -import org.thingsboard.server.gen.transport.TransportProtos.*; | 29 | +import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; |
30 | +import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg; | ||
34 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; | 31 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; |
35 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; | 32 | import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayResponseMsg; |
36 | import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; | 33 | import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg; |
37 | import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; | 34 | import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; |
38 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg; | 35 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg; |
39 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | 36 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; |
37 | +import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToAttributeUpdatesMsg; | ||
38 | +import org.thingsboard.server.gen.transport.TransportProtos.SubscribeToRPCMsg; | ||
39 | +import org.thingsboard.server.gen.transport.TransportProtos.SubscriptionInfoProto; | ||
40 | +import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcResponseMsg; | ||
41 | +import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; | ||
42 | +import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcRequestMsg; | ||
40 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; | 43 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; |
41 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; | 44 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiRequestMsg; |
42 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; | 45 | import org.thingsboard.server.gen.transport.TransportProtos.TransportApiResponseMsg; |
43 | -import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; | 46 | +import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; |
44 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; | 47 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg; |
45 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; | 48 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceTokenRequestMsg; |
46 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; | 49 | import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceX509CertRequestMsg; |
47 | -import org.thingsboard.server.kafka.*; | 50 | +import org.thingsboard.server.kafka.AsyncCallbackTemplate; |
51 | +import org.thingsboard.server.kafka.TBKafkaAdmin; | ||
52 | +import org.thingsboard.server.kafka.TBKafkaConsumerTemplate; | ||
53 | +import org.thingsboard.server.kafka.TBKafkaProducerTemplate; | ||
54 | +import org.thingsboard.server.kafka.TbKafkaRequestTemplate; | ||
55 | +import org.thingsboard.server.kafka.TbKafkaSettings; | ||
56 | +import org.thingsboard.server.kafka.TbNodeIdProvider; | ||
48 | 57 | ||
49 | import javax.annotation.PostConstruct; | 58 | import javax.annotation.PostConstruct; |
50 | import javax.annotation.PreDestroy; | 59 | import javax.annotation.PreDestroy; |
51 | import java.time.Duration; | 60 | import java.time.Duration; |
52 | -import java.util.UUID; | ||
53 | -import java.util.concurrent.ConcurrentHashMap; | ||
54 | -import java.util.concurrent.ConcurrentMap; | ||
55 | import java.util.concurrent.ExecutorService; | 61 | import java.util.concurrent.ExecutorService; |
56 | import java.util.concurrent.Executors; | 62 | import java.util.concurrent.Executors; |
57 | 63 | ||
@@ -305,6 +311,15 @@ public class RemoteTransportService extends AbstractTransportService { | @@ -305,6 +311,15 @@ public class RemoteTransportService extends AbstractTransportService { | ||
305 | send(sessionInfo, toRuleEngineMsg, callback); | 311 | send(sessionInfo, toRuleEngineMsg, callback); |
306 | } | 312 | } |
307 | 313 | ||
314 | + @Override | ||
315 | + protected void registerClaimingInfo(SessionInfoProto sessionInfo, ClaimDeviceMsg msg, TransportServiceCallback<Void> callback) { | ||
316 | + ToRuleEngineMsg toRuleEngineMsg = ToRuleEngineMsg.newBuilder().setToDeviceActorMsg( | ||
317 | + TransportToDeviceActorMsg.newBuilder().setSessionInfo(sessionInfo) | ||
318 | + .setClaimDevice(msg).build() | ||
319 | + ).build(); | ||
320 | + send(sessionInfo, toRuleEngineMsg, callback); | ||
321 | + } | ||
322 | + | ||
308 | private static class TransportCallbackAdaptor implements Callback { | 323 | private static class TransportCallbackAdaptor implements Callback { |
309 | private final TransportServiceCallback<Void> callback; | 324 | private final TransportServiceCallback<Void> callback; |
310 | 325 |
@@ -172,6 +172,13 @@ message ToServerRpcResponseMsg { | @@ -172,6 +172,13 @@ message ToServerRpcResponseMsg { | ||
172 | string error = 3; | 172 | string error = 3; |
173 | } | 173 | } |
174 | 174 | ||
175 | +message ClaimDeviceMsg { | ||
176 | + int64 deviceIdMSB = 1; | ||
177 | + int64 deviceIdLSB = 2; | ||
178 | + string secretKey = 3; | ||
179 | + int64 durationMs = 4; | ||
180 | +} | ||
181 | + | ||
175 | //Used to report session state to tb-node and persist this state in the cache on the tb-node level. | 182 | //Used to report session state to tb-node and persist this state in the cache on the tb-node level. |
176 | message SubscriptionInfoProto { | 183 | message SubscriptionInfoProto { |
177 | int64 lastActivityTime = 1; | 184 | int64 lastActivityTime = 1; |
@@ -199,6 +206,7 @@ message TransportToDeviceActorMsg { | @@ -199,6 +206,7 @@ message TransportToDeviceActorMsg { | ||
199 | ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 8; | 206 | ToDeviceRpcResponseMsg toDeviceRPCCallResponse = 8; |
200 | ToServerRpcRequestMsg toServerRPCCallRequest = 9; | 207 | ToServerRpcRequestMsg toServerRPCCallRequest = 9; |
201 | SubscriptionInfoProto subscriptionInfo = 10; | 208 | SubscriptionInfoProto subscriptionInfo = 10; |
209 | + ClaimDeviceMsg claimDevice = 11; | ||
202 | } | 210 | } |
203 | 211 | ||
204 | message DeviceActorToTransportMsg { | 212 | message DeviceActorToTransportMsg { |
1 | +/** | ||
2 | + * Copyright © 2016-2019 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.device; | ||
17 | + | ||
18 | +import com.google.common.util.concurrent.ListenableFuture; | ||
19 | +import org.thingsboard.server.common.data.Device; | ||
20 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
21 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
22 | +import org.thingsboard.server.common.data.id.TenantId; | ||
23 | +import org.thingsboard.server.dao.device.claim.ClaimResponse; | ||
24 | + | ||
25 | +import java.util.List; | ||
26 | + | ||
27 | +public interface ClaimDevicesService { | ||
28 | + | ||
29 | + ListenableFuture<Void> registerClaimingInfo(TenantId tenantId, DeviceId deviceId, String secretKey, long durationMs); | ||
30 | + | ||
31 | + ListenableFuture<ClaimResponse> claimDevice(Device device, CustomerId customerId, String secretKey); | ||
32 | + | ||
33 | + ListenableFuture<List<Void>> reClaimDevice(TenantId tenantId, Device device); | ||
34 | + | ||
35 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2019 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.device; | ||
17 | + | ||
18 | +import com.google.common.util.concurrent.Futures; | ||
19 | +import com.google.common.util.concurrent.ListenableFuture; | ||
20 | +import lombok.extern.slf4j.Slf4j; | ||
21 | +import org.springframework.beans.factory.annotation.Autowired; | ||
22 | +import org.springframework.beans.factory.annotation.Value; | ||
23 | +import org.springframework.cache.Cache; | ||
24 | +import org.springframework.cache.CacheManager; | ||
25 | +import org.springframework.stereotype.Service; | ||
26 | +import org.thingsboard.server.common.data.DataConstants; | ||
27 | +import org.thingsboard.server.common.data.Device; | ||
28 | +import org.thingsboard.server.common.data.id.CustomerId; | ||
29 | +import org.thingsboard.server.common.data.id.DeviceId; | ||
30 | +import org.thingsboard.server.common.data.id.TenantId; | ||
31 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | ||
32 | +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | ||
33 | +import org.thingsboard.server.common.data.kv.BooleanDataEntry; | ||
34 | +import org.thingsboard.server.dao.attributes.AttributesService; | ||
35 | +import org.thingsboard.server.dao.device.claim.ClaimData; | ||
36 | +import org.thingsboard.server.dao.device.claim.ClaimResponse; | ||
37 | +import org.thingsboard.server.dao.model.ModelConstants; | ||
38 | + | ||
39 | +import java.util.Collections; | ||
40 | +import java.util.List; | ||
41 | +import java.util.Optional; | ||
42 | + | ||
43 | +import static org.thingsboard.server.common.data.CacheConstants.CLAIM_DEVICES_CACHE; | ||
44 | + | ||
45 | +@Service | ||
46 | +@Slf4j | ||
47 | +public class ClaimDevicesServiceImpl implements ClaimDevicesService { | ||
48 | + | ||
49 | + private static final String CLAIM_ATTRIBUTE_NAME = "claimingAllowed"; | ||
50 | + | ||
51 | + @Autowired | ||
52 | + private DeviceService deviceService; | ||
53 | + @Autowired | ||
54 | + private AttributesService attributesService; | ||
55 | + @Autowired | ||
56 | + private CacheManager cacheManager; | ||
57 | + | ||
58 | + @Value("${security.claim.allowClaimingByDefault}") | ||
59 | + private boolean isAllowedClaimingByDefault; | ||
60 | + | ||
61 | + @Value("${security.claim.duration}") | ||
62 | + private long systemDurationMs; | ||
63 | + | ||
64 | + @Override | ||
65 | + public ListenableFuture<Void> registerClaimingInfo(TenantId tenantId, DeviceId deviceId, String secretKey, long durationMs) { | ||
66 | + ListenableFuture<Device> deviceFuture = deviceService.findDeviceByIdAsync(tenantId, deviceId); | ||
67 | + return Futures.transformAsync(deviceFuture, device -> { | ||
68 | + Cache cache = cacheManager.getCache(CLAIM_DEVICES_CACHE); | ||
69 | + List<Object> key = constructCacheKey(device.getId()); | ||
70 | + | ||
71 | + if (isAllowedClaimingByDefault) { | ||
72 | + if (device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | ||
73 | + persistInCache(secretKey, durationMs, cache, key); | ||
74 | + return Futures.immediateFuture(null); | ||
75 | + } | ||
76 | + log.warn("The device [{}] has been already claimed!", device.getName()); | ||
77 | + throw new IllegalArgumentException(); | ||
78 | + } else { | ||
79 | + ListenableFuture<List<AttributeKvEntry>> claimingAllowedFuture = attributesService.find(tenantId, device.getId(), | ||
80 | + DataConstants.SERVER_SCOPE, Collections.singletonList(CLAIM_ATTRIBUTE_NAME)); | ||
81 | + return Futures.transform(claimingAllowedFuture, list -> { | ||
82 | + if (list != null && !list.isEmpty()) { | ||
83 | + Optional<Boolean> claimingAllowedOptional = list.get(0).getBooleanValue(); | ||
84 | + if (claimingAllowedOptional.isPresent() && claimingAllowedOptional.get() | ||
85 | + && device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | ||
86 | + persistInCache(secretKey, durationMs, cache, key); | ||
87 | + return null; | ||
88 | + } | ||
89 | + } | ||
90 | + log.warn("Failed to find claimingAllowed attribute for device or it is already claimed![{}]", device.getName()); | ||
91 | + throw new IllegalArgumentException(); | ||
92 | + }); | ||
93 | + } | ||
94 | + }); | ||
95 | + } | ||
96 | + | ||
97 | + @Override | ||
98 | + public ListenableFuture<ClaimResponse> claimDevice(Device device, CustomerId customerId, String secretKey) { | ||
99 | + List<Object> key = constructCacheKey(device.getId()); | ||
100 | + Cache cache = cacheManager.getCache(CLAIM_DEVICES_CACHE); | ||
101 | + ClaimData claimData = cache.get(key, ClaimData.class); | ||
102 | + if (claimData != null) { | ||
103 | + long currTs = System.currentTimeMillis(); | ||
104 | + if (currTs > claimData.getExpirationTime() || !secretKey.equals(claimData.getSecretKey())) { | ||
105 | + log.warn("The claiming timeout occurred or wrong 'secretKey' provided for the device [{}]", device.getName()); | ||
106 | + cache.evict(key); | ||
107 | + return Futures.immediateFuture(ClaimResponse.FAILURE); | ||
108 | + } else { | ||
109 | + if (device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | ||
110 | + device.setCustomerId(customerId); | ||
111 | + deviceService.saveDevice(device); | ||
112 | + return Futures.transform(removeClaimingSavedData(cache, key, device), result -> ClaimResponse.SUCCESS); | ||
113 | + } | ||
114 | + return Futures.transform(removeClaimingSavedData(cache, key, device), result -> ClaimResponse.CLAIMED); | ||
115 | + } | ||
116 | + } else { | ||
117 | + log.warn("Failed to find the device's claiming message![{}]", device.getName()); | ||
118 | + return Futures.immediateFuture(ClaimResponse.CLAIMED); | ||
119 | + } | ||
120 | + } | ||
121 | + | ||
122 | + @Override | ||
123 | + public ListenableFuture<List<Void>> reClaimDevice(TenantId tenantId, Device device) { | ||
124 | + if (!device.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { | ||
125 | + cacheEviction(device.getId()); | ||
126 | + | ||
127 | + device.setCustomerId(null); | ||
128 | + deviceService.saveDevice(device); | ||
129 | + if (isAllowedClaimingByDefault) { | ||
130 | + return Futures.immediateFuture(Collections.emptyList()); | ||
131 | + } | ||
132 | + return attributesService.save(tenantId, device.getId(), DataConstants.SERVER_SCOPE, Collections.singletonList( | ||
133 | + new BaseAttributeKvEntry(new BooleanDataEntry(CLAIM_ATTRIBUTE_NAME, true), | ||
134 | + System.currentTimeMillis()))); | ||
135 | + } | ||
136 | + cacheEviction(device.getId()); | ||
137 | + return Futures.immediateFuture(Collections.emptyList()); | ||
138 | + } | ||
139 | + | ||
140 | + private List<Object> constructCacheKey(DeviceId deviceId) { | ||
141 | + return Collections.singletonList(deviceId); | ||
142 | + } | ||
143 | + | ||
144 | + private void persistInCache(String secretKey, long durationMs, Cache cache, List<Object> key) { | ||
145 | + ClaimData claimData = new ClaimData(secretKey, | ||
146 | + System.currentTimeMillis() + validateDurationMs(durationMs)); | ||
147 | + cache.putIfAbsent(key, claimData); | ||
148 | + } | ||
149 | + | ||
150 | + private long validateDurationMs(long durationMs) { | ||
151 | + if (durationMs > 0L) { | ||
152 | + return durationMs; | ||
153 | + } | ||
154 | + return systemDurationMs; | ||
155 | + } | ||
156 | + | ||
157 | + private ListenableFuture<List<Void>> removeClaimingSavedData(Cache cache, List<Object> key, Device device) { | ||
158 | + cache.evict(key); | ||
159 | + if (isAllowedClaimingByDefault) { | ||
160 | + return Futures.immediateFuture(null); | ||
161 | + } | ||
162 | + return attributesService.removeAll(device.getTenantId(), | ||
163 | + device.getId(), DataConstants.SERVER_SCOPE, Collections.singletonList(CLAIM_ATTRIBUTE_NAME)); | ||
164 | + } | ||
165 | + | ||
166 | + private void cacheEviction(DeviceId deviceId) { | ||
167 | + Cache cache = cacheManager.getCache(CLAIM_DEVICES_CACHE); | ||
168 | + cache.evict(constructCacheKey(deviceId)); | ||
169 | + } | ||
170 | + | ||
171 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2019 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.device.claim; | ||
17 | + | ||
18 | +import lombok.AllArgsConstructor; | ||
19 | +import lombok.Data; | ||
20 | + | ||
21 | +@AllArgsConstructor | ||
22 | +@Data | ||
23 | +public class ClaimData { | ||
24 | + | ||
25 | + private final String secretKey; | ||
26 | + private final long expirationTime; | ||
27 | + | ||
28 | +} |
1 | +/** | ||
2 | + * Copyright © 2016-2019 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.dao.device.claim; | ||
17 | + | ||
18 | +public enum ClaimResponse { | ||
19 | + | ||
20 | + SUCCESS, | ||
21 | + FAILURE, | ||
22 | + CLAIMED | ||
23 | + | ||
24 | +} |
@@ -27,11 +27,16 @@ caffeine.specs.assets.maxSize=100000 | @@ -27,11 +27,16 @@ caffeine.specs.assets.maxSize=100000 | ||
27 | caffeine.specs.entityViews.timeToLiveInMinutes=1440 | 27 | caffeine.specs.entityViews.timeToLiveInMinutes=1440 |
28 | caffeine.specs.entityViews.maxSize=100000 | 28 | caffeine.specs.entityViews.maxSize=100000 |
29 | 29 | ||
30 | +caffeine.specs.claimDevices.timeToLiveInMinutes=1440 | ||
31 | +caffeine.specs.claimDevices.maxSize=100000 | ||
32 | + | ||
30 | redis.connection.host=localhost | 33 | redis.connection.host=localhost |
31 | redis.connection.port=6379 | 34 | redis.connection.port=6379 |
32 | redis.connection.db=0 | 35 | redis.connection.db=0 |
33 | redis.connection.password= | 36 | redis.connection.password= |
34 | 37 | ||
35 | security.user_login_case_sensitive=true | 38 | security.user_login_case_sensitive=true |
39 | +security.claim.allowClaimingByDefault=true | ||
40 | +security.claim.duration=60000 | ||
36 | 41 | ||
37 | -database.ts_max_intervals=700 | ||
42 | +database.ts_max_intervals=700 |