Commit a1662d66429ce5c519fb0ed69e2add3bb068bd1f

Authored by Andrew Shvayka
1 parent da7955f9

Impl

... ... @@ -57,6 +57,7 @@ import org.thingsboard.server.dao.plugin.PluginService;
57 57 import org.thingsboard.server.dao.relation.RelationService;
58 58 import org.thingsboard.server.dao.rule.RuleChainService;
59 59 import org.thingsboard.server.dao.rule.RuleService;
  60 +import org.thingsboard.server.dao.tenant.TenantService;
60 61 import org.thingsboard.server.dao.user.UserService;
61 62 import org.thingsboard.server.dao.widget.WidgetTypeService;
62 63 import org.thingsboard.server.dao.widget.WidgetsBundleService;
... ... @@ -86,6 +87,9 @@ public abstract class BaseController {
86 87 private ThingsboardErrorResponseHandler errorResponseHandler;
87 88
88 89 @Autowired
  90 + protected TenantService tenantService;
  91 +
  92 + @Autowired
89 93 protected CustomerService customerService;
90 94
91 95 @Autowired
... ... @@ -602,5 +606,8 @@ public abstract class BaseController {
602 606 auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo);
603 607 }
604 608
  609 + protected static Exception toException(Throwable error) {
  610 + return Exception.class.isInstance(error) ? (Exception) error : new Exception(error);
  611 + }
605 612
606 613 }
... ...
... ... @@ -9,33 +9,49 @@ import org.springframework.beans.factory.annotation.Autowired;
9 9 import org.springframework.http.HttpStatus;
10 10 import org.springframework.http.ResponseEntity;
11 11 import org.springframework.security.access.prepost.PreAuthorize;
  12 +import org.springframework.util.StringUtils;
12 13 import org.springframework.web.bind.annotation.PathVariable;
13 14 import org.springframework.web.bind.annotation.RequestMapping;
14 15 import org.springframework.web.bind.annotation.RequestMethod;
  16 +import org.springframework.web.bind.annotation.RequestParam;
15 17 import org.springframework.web.bind.annotation.ResponseStatus;
16 18 import org.springframework.web.bind.annotation.RestController;
17 19 import org.springframework.web.context.request.async.DeferredResult;
18 20 import org.thingsboard.server.actors.plugin.ValidationResult;
  21 +import org.thingsboard.server.common.data.Customer;
19 22 import org.thingsboard.server.common.data.DataConstants;
20 23 import org.thingsboard.server.common.data.Device;
  24 +import org.thingsboard.server.common.data.Tenant;
  25 +import org.thingsboard.server.common.data.asset.Asset;
  26 +import org.thingsboard.server.common.data.audit.ActionType;
  27 +import org.thingsboard.server.common.data.id.AssetId;
  28 +import org.thingsboard.server.common.data.id.CustomerId;
21 29 import org.thingsboard.server.common.data.id.DeviceId;
22 30 import org.thingsboard.server.common.data.id.EntityId;
23 31 import org.thingsboard.server.common.data.id.EntityIdFactory;
  32 +import org.thingsboard.server.common.data.id.RuleChainId;
  33 +import org.thingsboard.server.common.data.id.TenantId;
  34 +import org.thingsboard.server.common.data.id.UUIDBased;
24 35 import org.thingsboard.server.common.data.kv.AttributeKvEntry;
25 36 import org.thingsboard.server.common.data.kv.KvEntry;
  37 +import org.thingsboard.server.common.data.kv.TsKvEntry;
  38 +import org.thingsboard.server.common.data.rule.RuleChain;
26 39 import org.thingsboard.server.dao.attributes.AttributesService;
27 40 import org.thingsboard.server.dao.timeseries.TimeseriesService;
28 41 import org.thingsboard.server.exception.ThingsboardException;
29 42 import org.thingsboard.server.extensions.api.exception.ToErrorResponseEntity;
30 43 import org.thingsboard.server.extensions.api.plugins.PluginConstants;
  44 +import org.thingsboard.server.extensions.core.plugin.telemetry.AttributeData;
31 45 import org.thingsboard.server.service.security.model.SecurityUser;
32 46
33 47 import javax.annotation.Nullable;
34 48 import javax.annotation.PreDestroy;
35 49 import java.util.ArrayList;
  50 +import java.util.Arrays;
36 51 import java.util.List;
37 52 import java.util.concurrent.ExecutorService;
38 53 import java.util.concurrent.Executors;
  54 +import java.util.function.BiConsumer;
39 55 import java.util.stream.Collectors;
40 56
41 57 /**
... ... @@ -74,61 +90,169 @@ public class TelemetryController extends BaseController {
74 90 @ResponseStatus(value = HttpStatus.OK)
75 91 public DeferredResult<ResponseEntity> getAttributeKeys(
76 92 @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
77   - DeferredResult<ResponseEntity> response = new DeferredResult<ResponseEntity>();
  93 + return validateEntityAndCallback(entityType, entityIdStr,
  94 + this::getAttributeKeysCallback,
  95 + (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR));
  96 + }
  97 +
  98 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  99 + @RequestMapping(value = "/{entityType}/{entityId}/keys/ATTRIBUTES/{scope}", method = RequestMethod.GET)
  100 + @ResponseStatus(value = HttpStatus.OK)
  101 + public DeferredResult<ResponseEntity> getAttributeKeysByScope(
  102 + @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr
  103 + , @PathVariable("scope") String scope) throws ThingsboardException {
  104 + return validateEntityAndCallback(entityType, entityIdStr,
  105 + (result, entityId) -> getAttributeKeysCallback(result, entityId, scope),
  106 + (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR));
  107 + }
  108 +
  109 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  110 + @RequestMapping(value = "/{entityType}/{entityId}/values/ATTRIBUTES", method = RequestMethod.GET)
  111 + @ResponseStatus(value = HttpStatus.OK)
  112 + public DeferredResult<ResponseEntity> getAttributes(
  113 + @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
  114 + @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
  115 + SecurityUser user = getCurrentUser();
  116 + return validateEntityAndCallback(entityType, entityIdStr,
  117 + (result, entityId) -> getAttributeValuesCallback(result, user, entityId, null, keysStr),
  118 + (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR));
  119 + }
  120 +
  121 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  122 + @RequestMapping(value = "/{entityType}/{entityId}/values/ATTRIBUTES/{scope}", method = RequestMethod.GET)
  123 + @ResponseStatus(value = HttpStatus.OK)
  124 + public DeferredResult<ResponseEntity> getAttributesByScope(
  125 + @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
  126 + @PathVariable("scope") String scope,
  127 + @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
  128 + SecurityUser user = getCurrentUser();
  129 + return validateEntityAndCallback(entityType, entityIdStr,
  130 + (result, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr),
  131 + (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR));
  132 + }
  133 +
  134 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  135 + @RequestMapping(value = "/{entityType}/{entityId}/keys/TIMESERIES", method = RequestMethod.GET)
  136 + @ResponseStatus(value = HttpStatus.OK)
  137 + public DeferredResult<ResponseEntity> getTimeseriesKeys(
  138 + @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr) throws ThingsboardException {
  139 + return validateEntityAndCallback(entityType, entityIdStr,
  140 + (result, entityId) -> {
  141 + Futures.addCallback(tsService.findAllLatest(entityId), getTsKeysToResponseCallback(result));
  142 + },
  143 + (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR));
  144 + }
  145 +
  146 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  147 + @RequestMapping(value = "/{entityType}/{entityId}/values/TIMESERIES", method = RequestMethod.GET)
  148 + @ResponseStatus(value = HttpStatus.OK)
  149 + public DeferredResult<ResponseEntity> getLatestTimeseries(
  150 + @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
  151 + @PathVariable("scope") String scope,
  152 + @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
  153 + SecurityUser user = getCurrentUser();
  154 +
  155 + return validateEntityAndCallback(entityType, entityIdStr,
  156 + (result, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr),
  157 + (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR));
  158 + }
  159 +
  160 +
  161 + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
  162 + @RequestMapping(value = "/{entityType}/{entityId}/values/TIMESERIES", method = RequestMethod.GET)
  163 + @ResponseStatus(value = HttpStatus.OK)
  164 + public DeferredResult<ResponseEntity> getLatestTimeseries(
  165 + @PathVariable("entityType") String entityType, @PathVariable("entityId") String entityIdStr,
  166 + @PathVariable("scope") String scope,
  167 + @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
  168 + SecurityUser user = getCurrentUser();
  169 +
  170 + return validateEntityAndCallback(entityType, entityIdStr,
  171 + (result, entityId) -> getAttributeValuesCallback(result, user, entityId, scope, keysStr),
  172 + (result, t) -> handleError(t, result, HttpStatus.INTERNAL_SERVER_ERROR));
  173 + }
  174 +
  175 + private DeferredResult<ResponseEntity> validateEntityAndCallback(String entityType, String entityIdStr,
  176 + BiConsumer<DeferredResult<ResponseEntity>, EntityId> onSuccess, BiConsumer<DeferredResult<ResponseEntity>, Throwable> onFailure) throws ThingsboardException {
  177 + final DeferredResult<ResponseEntity> response = new DeferredResult<>();
78 178 EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
79 179
80 180 validate(getCurrentUser(), entityId, new ValidationCallback(response,
81 181 new FutureCallback<DeferredResult<ResponseEntity>>() {
82 182 @Override
83 183 public void onSuccess(@Nullable DeferredResult<ResponseEntity> result) {
84   - List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
85   - for (String scope : DataConstants.allScopes()) {
86   - futures.add(attributesService.findAll(entityId, scope));
87   - }
88   -
89   - ListenableFuture<List<AttributeKvEntry>> future = Futures.transform(Futures.successfulAsList(futures),
90   - (Function<? super List<List<AttributeKvEntry>>, ? extends List<AttributeKvEntry>>) input -> {
91   - List<AttributeKvEntry> tmp = new ArrayList<>();
92   - if (input != null) {
93   - input.forEach(tmp::addAll);
94   - }
95   - return tmp;
96   - }, executor);
97   -
98   - Futures.addCallback(future, getAttributeKeysPluginCallback(result));
  184 + onSuccess.accept(response, entityId);
99 185 }
100 186
101 187 @Override
102 188 public void onFailure(Throwable t) {
103   - handleError(t, response, HttpStatus.INTERNAL_SERVER_ERROR);
  189 + onFailure.accept(response, t);
104 190 }
105 191 }));
106 192
107 193 return response;
108 194 }
109 195
110   - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
111   - @RequestMapping(value = "/{entityType}/{entityId}/keys/ATTRIBUTES/{scope}", method = RequestMethod.GET)
112   - @ResponseStatus(value = HttpStatus.OK)
113   - public DeferredResult<ResponseEntity> getAttributeKeysByScope() {
114   - return null;
  196 + private void getAttributeValuesCallback(@Nullable DeferredResult<ResponseEntity> result, SecurityUser user, EntityId entityId, String scope, String keys) {
  197 + List<String> keyList = null;
  198 + if (!StringUtils.isEmpty(keys)) {
  199 + keyList = Arrays.asList(keys.split(","));
  200 + }
  201 + FutureCallback<List<AttributeKvEntry>> callback = getAttributeValuesToResponseCallback(result, user, scope, entityId, keyList);
  202 + if (!StringUtils.isEmpty(scope)) {
  203 + if (keyList != null && !keyList.isEmpty()) {
  204 + Futures.addCallback(attributesService.find(entityId, scope, keyList), callback);
  205 + } else {
  206 + Futures.addCallback(attributesService.findAll(entityId, scope), callback);
  207 + }
  208 + } else {
  209 + List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
  210 + for (String tmpScope : DataConstants.allScopes()) {
  211 + if (keyList != null && !keyList.isEmpty()) {
  212 + futures.add(attributesService.find(entityId, tmpScope, keyList));
  213 + } else {
  214 + futures.add(attributesService.findAll(entityId, tmpScope));
  215 + }
  216 + }
  217 +
  218 + ListenableFuture<List<AttributeKvEntry>> future = mergeAllAttributesFutures(futures);
  219 +
  220 + Futures.addCallback(future, callback);
  221 + }
115 222 }
116 223
117   - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
118   - @RequestMapping(value = "/{entityType}/{entityId}/values/ATTRIBUTES", method = RequestMethod.GET)
119   - @ResponseStatus(value = HttpStatus.OK)
120   - public DeferredResult<ResponseEntity> getAttributeValues() {
121   - return null;
  224 + private void getAttributeKeysCallback(@Nullable DeferredResult<ResponseEntity> result, EntityId entityId, String scope) {
  225 + Futures.addCallback(attributesService.findAll(entityId, scope), getAttributeKeysToResponseCallback(result));
122 226 }
123 227
124   - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
125   - @RequestMapping(value = "/{entityType}/{entityId}/values/ATTRIBUTES", method = RequestMethod.GET)
126   - @ResponseStatus(value = HttpStatus.OK)
127   - public DeferredResult<ResponseEntity> getAttributeValuesByScope() {
128   - return null;
  228 + private void getAttributeKeysCallback(@Nullable DeferredResult<ResponseEntity> result, EntityId entityId) {
  229 + List<ListenableFuture<List<AttributeKvEntry>>> futures = new ArrayList<>();
  230 + for (String scope : DataConstants.allScopes()) {
  231 + futures.add(attributesService.findAll(entityId, scope));
  232 + }
  233 +
  234 + ListenableFuture<List<AttributeKvEntry>> future = mergeAllAttributesFutures(futures);
  235 +
  236 + Futures.addCallback(future, getAttributeKeysToResponseCallback(result));
  237 + }
  238 +
  239 + private FutureCallback<List<TsKvEntry>> getTsKeysToResponseCallback(final DeferredResult<ResponseEntity> response) {
  240 + return new FutureCallback<List<TsKvEntry>>() {
  241 + @Override
  242 + public void onSuccess(List<TsKvEntry> values) {
  243 + List<String> keys = values.stream().map(KvEntry::getKey).collect(Collectors.toList());
  244 + response.setResult(new ResponseEntity<>(keys, HttpStatus.OK));
  245 + }
  246 +
  247 + @Override
  248 + public void onFailure(Throwable e) {
  249 + log.error("Failed to fetch attributes", e);
  250 + handleError(e, response, HttpStatus.INTERNAL_SERVER_ERROR);
  251 + }
  252 + };
129 253 }
130 254
131   - private FutureCallback<List<AttributeKvEntry>> getAttributeKeysPluginCallback(final DeferredResult<ResponseEntity> response) {
  255 + private FutureCallback<List<AttributeKvEntry>> getAttributeKeysToResponseCallback(final DeferredResult<ResponseEntity> response) {
132 256 return new FutureCallback<List<AttributeKvEntry>>() {
133 257
134 258 @Override
... ... @@ -145,6 +269,40 @@ public class TelemetryController extends BaseController {
145 269 };
146 270 }
147 271
  272 + private FutureCallback<List<AttributeKvEntry>> getAttributeValuesToResponseCallback(final DeferredResult<ResponseEntity> response, final SecurityUser user, final String scope,
  273 + final EntityId entityId, final List<String> keyList) {
  274 + return new FutureCallback<List<AttributeKvEntry>>() {
  275 + @Override
  276 + public void onSuccess(List<AttributeKvEntry> attributes) {
  277 + List<AttributeData> values = attributes.stream().map(attribute -> new AttributeData(attribute.getLastUpdateTs(),
  278 + attribute.getKey(), attribute.getValue())).collect(Collectors.toList());
  279 + logAttributesRead(user, entityId, scope, keyList, null);
  280 + response.setResult(new ResponseEntity<>(values, HttpStatus.OK));
  281 + }
  282 +
  283 + @Override
  284 + public void onFailure(Throwable e) {
  285 + log.error("Failed to fetch attributes", e);
  286 + logAttributesRead(user, entityId, scope, keyList, e);
  287 + handleError(e, response, HttpStatus.INTERNAL_SERVER_ERROR);
  288 + }
  289 + };
  290 + }
  291 +
  292 + private void logAttributesRead(SecurityUser user, EntityId entityId, String scope, List<String> keys, Throwable e) {
  293 + auditLogService.logEntityAction(
  294 + user.getTenantId(),
  295 + user.getCustomerId(),
  296 + user.getId(),
  297 + user.getName(),
  298 + (UUIDBased & EntityId) entityId,
  299 + null,
  300 + ActionType.ATTRIBUTES_READ,
  301 + toException(e),
  302 + scope,
  303 + keys);
  304 + }
  305 +
148 306 private void handleError(Throwable e, final DeferredResult<ResponseEntity> response, HttpStatus defaultErrorStatus) {
149 307 ResponseEntity responseEntity;
150 308 if (e != null && e instanceof ToErrorResponseEntity) {
... ... @@ -162,24 +320,18 @@ public class TelemetryController extends BaseController {
162 320 case DEVICE:
163 321 validateDevice(currentUser, entityId, callback);
164 322 return;
165   -// case ASSET:
166   -// validateAsset(ctx, entityId, callback);
167   -// return;
168   -// case RULE:
169   -// validateRule(ctx, entityId, callback);
170   -// return;
171   -// case RULE_CHAIN:
172   -// validateRuleChain(ctx, entityId, callback);
173   -// return;
174   -// case PLUGIN:
175   -// validatePlugin(ctx, entityId, callback);
176   -// return;
177   -// case CUSTOMER:
178   -// validateCustomer(ctx, entityId, callback);
179   -// return;
180   -// case TENANT:
181   -// validateTenant(ctx, entityId, callback);
182   -// return;
  323 + case ASSET:
  324 + validateAsset(currentUser, entityId, callback);
  325 + return;
  326 + case RULE_CHAIN:
  327 + validateRuleChain(currentUser, entityId, callback);
  328 + return;
  329 + case CUSTOMER:
  330 + validateCustomer(currentUser, entityId, callback);
  331 + return;
  332 + case TENANT:
  333 + validateTenant(currentUser, entityId, callback);
  334 + return;
183 335 default:
184 336 //TODO: add support of other entities
185 337 throw new IllegalStateException("Not Implemented!");
... ... @@ -207,6 +359,89 @@ public class TelemetryController extends BaseController {
207 359 }
208 360 }
209 361
  362 + private void validateAsset(final SecurityUser currentUser, EntityId entityId, ValidationCallback callback) {
  363 + if (currentUser.isSystemAdmin()) {
  364 + callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
  365 + } else {
  366 + ListenableFuture<Asset> assetFuture = assetService.findAssetByIdAsync(new AssetId(entityId.getId()));
  367 + Futures.addCallback(assetFuture, getCallback(callback, asset -> {
  368 + if (asset == null) {
  369 + return ValidationResult.entityNotFound("Asset with requested id wasn't found!");
  370 + } else {
  371 + if (!asset.getTenantId().equals(currentUser.getTenantId())) {
  372 + return ValidationResult.accessDenied("Asset doesn't belong to the current Tenant!");
  373 + } else if (currentUser.isCustomerUser() && !asset.getCustomerId().equals(currentUser.getCustomerId())) {
  374 + return ValidationResult.accessDenied("Asset doesn't belong to the current Customer!");
  375 + } else {
  376 + return ValidationResult.ok();
  377 + }
  378 + }
  379 + }));
  380 + }
  381 + }
  382 +
  383 +
  384 + private void validateRuleChain(final SecurityUser currentUser, EntityId entityId, ValidationCallback callback) {
  385 + if (currentUser.isCustomerUser()) {
  386 + callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
  387 + } else {
  388 + ListenableFuture<RuleChain> ruleChainFuture = ruleChainService.findRuleChainByIdAsync(new RuleChainId(entityId.getId()));
  389 + Futures.addCallback(ruleChainFuture, getCallback(callback, ruleChain -> {
  390 + if (ruleChain == null) {
  391 + return ValidationResult.entityNotFound("Rule chain with requested id wasn't found!");
  392 + } else {
  393 + if (currentUser.isTenantAdmin() && !ruleChain.getTenantId().equals(currentUser.getTenantId())) {
  394 + return ValidationResult.accessDenied("Rule chain doesn't belong to the current Tenant!");
  395 + } else if (currentUser.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) {
  396 + return ValidationResult.accessDenied("Rule chain is not in system scope!");
  397 + } else {
  398 + return ValidationResult.ok();
  399 + }
  400 + }
  401 + }));
  402 + }
  403 + }
  404 +
  405 + private void validateCustomer(final SecurityUser currentUser, EntityId entityId, ValidationCallback callback) {
  406 + if (currentUser.isSystemAdmin()) {
  407 + callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
  408 + } else {
  409 + ListenableFuture<Customer> customerFuture = customerService.findCustomerByIdAsync(new CustomerId(entityId.getId()));
  410 + Futures.addCallback(customerFuture, getCallback(callback, customer -> {
  411 + if (customer == null) {
  412 + return ValidationResult.entityNotFound("Customer with requested id wasn't found!");
  413 + } else {
  414 + if (!customer.getTenantId().equals(currentUser.getTenantId())) {
  415 + return ValidationResult.accessDenied("Customer doesn't belong to the current Tenant!");
  416 + } else if (currentUser.isCustomerUser() && !customer.getId().equals(currentUser.getCustomerId())) {
  417 + return ValidationResult.accessDenied("Customer doesn't relate to the currently authorized customer user!");
  418 + } else {
  419 + return ValidationResult.ok();
  420 + }
  421 + }
  422 + }));
  423 + }
  424 + }
  425 +
  426 + private void validateTenant(final SecurityUser currentUser, EntityId entityId, ValidationCallback callback) {
  427 + if (currentUser.isCustomerUser()) {
  428 + callback.onSuccess(ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
  429 + } else if (currentUser.isSystemAdmin()) {
  430 + callback.onSuccess(ValidationResult.ok());
  431 + } else {
  432 + ListenableFuture<Tenant> tenantFuture = tenantService.findTenantByIdAsync(new TenantId(entityId.getId()));
  433 + Futures.addCallback(tenantFuture, getCallback(callback, tenant -> {
  434 + if (tenant == null) {
  435 + return ValidationResult.entityNotFound("Tenant with requested id wasn't found!");
  436 + } else if (!tenant.getId().equals(currentUser.getTenantId())) {
  437 + return ValidationResult.accessDenied("Tenant doesn't relate to the currently authorized user!");
  438 + } else {
  439 + return ValidationResult.ok();
  440 + }
  441 + }));
  442 + }
  443 + }
  444 +
210 445 private <T> FutureCallback<T> getCallback(ValidationCallback callback, Function<T, ValidationResult> transformer) {
211 446 return new FutureCallback<T>() {
212 447 @Override
... ... @@ -220,131 +455,16 @@ public class TelemetryController extends BaseController {
220 455 }
221 456 };
222 457 }
223   -//
224   -// private void validateAsset(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {
225   -// if (ctx.isSystemAdmin()) {
226   -// callback.onSuccess(this, ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
227   -// } else {
228   -// ListenableFuture<Asset> assetFuture = pluginCtx.assetService.findAssetByIdAsync(new AssetId(entityId.getId()));
229   -// Futures.addCallback(assetFuture, getCallback(callback, asset -> {
230   -// if (asset == null) {
231   -// return ValidationResult.entityNotFound("Asset with requested id wasn't found!");
232   -// } else {
233   -// if (!asset.getTenantId().equals(ctx.getTenantId())) {
234   -// return ValidationResult.accessDenied("Asset doesn't belong to the current Tenant!");
235   -// } else if (ctx.isCustomerUser() && !asset.getCustomerId().equals(ctx.getCustomerId())) {
236   -// return ValidationResult.accessDenied("Asset doesn't belong to the current Customer!");
237   -// } else {
238   -// return ValidationResult.ok();
239   -// }
240   -// }
241   -// }));
242   -// }
243   -// }
244   -//
245   -// private void validateRule(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {
246   -// if (ctx.isCustomerUser()) {
247   -// callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
248   -// } else {
249   -// ListenableFuture<RuleMetaData> ruleFuture = pluginCtx.ruleService.findRuleByIdAsync(new RuleId(entityId.getId()));
250   -// Futures.addCallback(ruleFuture, getCallback(callback, rule -> {
251   -// if (rule == null) {
252   -// return ValidationResult.entityNotFound("Rule with requested id wasn't found!");
253   -// } else {
254   -// if (ctx.isTenantAdmin() && !rule.getTenantId().equals(ctx.getTenantId())) {
255   -// return ValidationResult.accessDenied("Rule doesn't belong to the current Tenant!");
256   -// } else if (ctx.isSystemAdmin() && !rule.getTenantId().isNullUid()) {
257   -// return ValidationResult.accessDenied("Rule is not in system scope!");
258   -// } else {
259   -// return ValidationResult.ok();
260   -// }
261   -// }
262   -// }));
263   -// }
264   -// }
265   -//
266   -// private void validateRuleChain(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {
267   -// if (ctx.isCustomerUser()) {
268   -// callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
269   -// } else {
270   -// ListenableFuture<RuleChain> ruleChainFuture = pluginCtx.ruleChainService.findRuleChainByIdAsync(new RuleChainId(entityId.getId()));
271   -// Futures.addCallback(ruleChainFuture, getCallback(callback, ruleChain -> {
272   -// if (ruleChain == null) {
273   -// return ValidationResult.entityNotFound("Rule chain with requested id wasn't found!");
274   -// } else {
275   -// if (ctx.isTenantAdmin() && !ruleChain.getTenantId().equals(ctx.getTenantId())) {
276   -// return ValidationResult.accessDenied("Rule chain doesn't belong to the current Tenant!");
277   -// } else if (ctx.isSystemAdmin() && !ruleChain.getTenantId().isNullUid()) {
278   -// return ValidationResult.accessDenied("Rule chain is not in system scope!");
279   -// } else {
280   -// return ValidationResult.ok();
281   -// }
282   -// }
283   -// }));
284   -// }
285   -// }
286   -//
287   -//
288   -// private void validatePlugin(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {
289   -// if (ctx.isCustomerUser()) {
290   -// callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
291   -// } else {
292   -// ListenableFuture<PluginMetaData> pluginFuture = pluginCtx.pluginService.findPluginByIdAsync(new PluginId(entityId.getId()));
293   -// Futures.addCallback(pluginFuture, getCallback(callback, plugin -> {
294   -// if (plugin == null) {
295   -// return ValidationResult.entityNotFound("Plugin with requested id wasn't found!");
296   -// } else {
297   -// if (ctx.isTenantAdmin() && !plugin.getTenantId().equals(ctx.getTenantId())) {
298   -// return ValidationResult.accessDenied("Plugin doesn't belong to the current Tenant!");
299   -// } else if (ctx.isSystemAdmin() && !plugin.getTenantId().isNullUid()) {
300   -// return ValidationResult.accessDenied("Plugin is not in system scope!");
301   -// } else {
302   -// return ValidationResult.ok();
303   -// }
304   -// }
305   -// }));
306   -// }
307   -// }
308   -//
309   -// private void validateCustomer(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {
310   -// if (ctx.isSystemAdmin()) {
311   -// callback.onSuccess(this, ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
312   -// } else {
313   -// ListenableFuture<Customer> customerFuture = pluginCtx.customerService.findCustomerByIdAsync(new CustomerId(entityId.getId()));
314   -// Futures.addCallback(customerFuture, getCallback(callback, customer -> {
315   -// if (customer == null) {
316   -// return ValidationResult.entityNotFound("Customer with requested id wasn't found!");
317   -// } else {
318   -// if (!customer.getTenantId().equals(ctx.getTenantId())) {
319   -// return ValidationResult.accessDenied("Customer doesn't belong to the current Tenant!");
320   -// } else if (ctx.isCustomerUser() && !customer.getId().equals(ctx.getCustomerId())) {
321   -// return ValidationResult.accessDenied("Customer doesn't relate to the currently authorized customer user!");
322   -// } else {
323   -// return ValidationResult.ok();
324   -// }
325   -// }
326   -// }));
327   -// }
328   -// }
329   -//
330   -// private void validateTenant(final PluginApiCallSecurityContext ctx, EntityId entityId, ValidationCallback callback) {
331   -// if (ctx.isCustomerUser()) {
332   -// callback.onSuccess(this, ValidationResult.accessDenied(CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION));
333   -// } else if (ctx.isSystemAdmin()) {
334   -// callback.onSuccess(this, ValidationResult.ok());
335   -// } else {
336   -// ListenableFuture<Tenant> tenantFuture = pluginCtx.tenantService.findTenantByIdAsync(new TenantId(entityId.getId()));
337   -// Futures.addCallback(tenantFuture, getCallback(callback, tenant -> {
338   -// if (tenant == null) {
339   -// return ValidationResult.entityNotFound("Tenant with requested id wasn't found!");
340   -// } else if (!tenant.getId().equals(ctx.getTenantId())) {
341   -// return ValidationResult.accessDenied("Tenant doesn't relate to the currently authorized user!");
342   -// } else {
343   -// return ValidationResult.ok();
344   -// }
345   -// }));
346   -// }
347   -// }
348 458
  459 + private ListenableFuture<List<AttributeKvEntry>> mergeAllAttributesFutures(List<ListenableFuture<List<AttributeKvEntry>>> futures) {
  460 + return Futures.transform(Futures.successfulAsList(futures),
  461 + (Function<? super List<List<AttributeKvEntry>>, ? extends List<AttributeKvEntry>>) input -> {
  462 + List<AttributeKvEntry> tmp = new ArrayList<>();
  463 + if (input != null) {
  464 + input.forEach(tmp::addAll);
  465 + }
  466 + return tmp;
  467 + }, executor);
  468 + }
349 469
350 470 }
... ...