package com.devicehive.service;
/*
* #%L
* DeviceHive Java Server Common business logic
* %%
* Copyright (C) 2016 DataArt
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import com.devicehive.auth.HiveAuthentication;
import com.devicehive.auth.HivePrincipal;
import com.devicehive.configuration.Messages;
import com.devicehive.dao.NetworkDao;
import com.devicehive.exceptions.ActionNotAllowedException;
import com.devicehive.exceptions.IllegalParametersException;
import com.devicehive.model.rpc.ListNetworkRequest;
import com.devicehive.model.rpc.ListNetworkResponse;
import com.devicehive.model.updates.NetworkUpdate;
import com.devicehive.service.configuration.ConfigurationService;
import com.devicehive.service.helpers.ResponseConsumer;
import com.devicehive.shim.api.Request;
import com.devicehive.shim.api.Response;
import com.devicehive.shim.api.client.RpcClient;
import com.devicehive.util.HiveValidator;
import com.devicehive.vo.DeviceVO;
import com.devicehive.vo.NetworkVO;
import com.devicehive.vo.NetworkWithUsersAndDevicesVO;
import com.devicehive.vo.UserVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import static java.util.Optional.*;
@Component
public class NetworkService {
public static final String ALLOW_NETWORK_AUTO_CREATE = "allowNetworkAutoCreate";
private static final Logger logger = LoggerFactory.getLogger(NetworkService.class);
@Autowired
private UserService userService;
@Autowired
private ConfigurationService configurationService;
@Autowired
private HiveValidator hiveValidator;
@Autowired
private NetworkDao networkDao;
@Autowired
private RpcClient rpcClient;
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public NetworkWithUsersAndDevicesVO getWithDevicesAndDeviceClasses(@NotNull Long networkId, @NotNull HiveAuthentication hiveAuthentication) {
HivePrincipal principal = (HivePrincipal) hiveAuthentication.getPrincipal();
Set<Long> permittedNetworks = principal.getNetworkIds();
Set<String> permittedDevices = principal.getDeviceGuids();
Optional<NetworkWithUsersAndDevicesVO> result = of(principal)
.flatMap(pr -> {
if (pr.getUser() != null)
return of(pr.getUser());
else
return empty();
}).flatMap(user -> {
Long idForFiltering = user.isAdmin() ? null : user.getId();
List<NetworkWithUsersAndDevicesVO> found = networkDao.getNetworksByIdsAndUsers(idForFiltering,
Collections.singleton(networkId), permittedNetworks);
return found.stream().findFirst();
}).map(network -> {
//fixme - important, restore functionality once permission evaluator is switched to jwt
/*if (principal.getKey() != null) {
Set<AccessKeyPermissionVO> permissions = principal.getKey().getPermissions();
Set<AccessKeyPermissionVO> filtered = CheckPermissionsHelper
.filterPermissions(principal.getKey(), permissions, AccessKeyAction.GET_DEVICE,
details.getClientInetAddress(), details.getOrigin());
if (filtered.isEmpty()) {
network.setDevices(Collections.emptySet());
}
}*/
if (permittedDevices != null && !permittedDevices.isEmpty()) {
Set<DeviceVO> allowed = network.getDevices().stream()
.filter(device -> permittedDevices.contains(device.getGuid()))
.collect(Collectors.toSet());
network.setDevices(allowed);
}
return network;
});
return result.orElse(null);
}
@Transactional
public boolean delete(long id) {
logger.trace("About to execute named query \"Network.deleteById\" for ");
int result = networkDao.deleteById(id);
logger.debug("Deleted {} rows from Network table", result);
return result > 0;
}
@Transactional
public NetworkVO create(NetworkVO newNetwork) {
logger.debug("Creating network {}", newNetwork);
if (newNetwork.getId() != null) {
logger.error("Can't create network entity with id={} specified", newNetwork.getId());
throw new IllegalParametersException(Messages.ID_NOT_ALLOWED);
}
List<NetworkVO> existing = networkDao.findByName(newNetwork.getName());
if (!existing.isEmpty()) {
logger.error("Network with name {} already exists", newNetwork.getName());
throw new ActionNotAllowedException(Messages.DUPLICATE_NETWORK);
}
hiveValidator.validate(newNetwork);
networkDao.persist(newNetwork);
logger.info("Entity {} created successfully", newNetwork);
return newNetwork;
}
@Transactional
public NetworkVO update(@NotNull Long networkId, NetworkUpdate networkUpdate) {
NetworkVO existing = networkDao.find(networkId);
if (existing == null) {
throw new NoSuchElementException(String.format(Messages.NETWORK_NOT_FOUND, networkId));
}
if (networkUpdate.getKey() != null) {
existing.setKey(networkUpdate.getKey().orElse(null));
}
if (networkUpdate.getName() != null) {
existing.setName(networkUpdate.getName().orElse(null));
}
if (networkUpdate.getDescription() != null) {
existing.setDescription(networkUpdate.getDescription().orElse(null));
}
hiveValidator.validate(existing);
return networkDao.merge(existing);
}
//@Transactional(propagation = Propagation.NOT_SUPPORTED)
public CompletableFuture<List<NetworkVO>> list(String name,
String namePattern,
String sortField,
boolean sortOrderAsc,
Integer take,
Integer skip,
HivePrincipal principal) {
Optional<HivePrincipal> principalOpt = ofNullable(principal);
ListNetworkRequest request = new ListNetworkRequest();
request.setName(name);
request.setNamePattern(namePattern);
request.setSortField(sortField);
request.setSortOrderAsc(sortOrderAsc);
request.setTake(take);
request.setSkip(skip);
request.setPrincipal(principalOpt);
CompletableFuture<Response> future = new CompletableFuture<>();
rpcClient.call(Request.newBuilder().withBody(request).build(), new ResponseConsumer(future));
return future.thenApply(r -> ((ListNetworkResponse) r.getBody()).getNetworks());
}
@Transactional
public NetworkVO createOrVerifyNetwork(Optional<NetworkVO> networkNullable) {
//case network is not defined
if (networkNullable == null || networkNullable.orElse(null) == null) {
return null;
}
NetworkVO network = networkNullable.get();
Optional<NetworkVO> storedOpt = findNetworkByIdOrName(network);
if (storedOpt.isPresent()) {
return validateNetworkKey(storedOpt.get(), network);
} else {
if (network.getId() != null) {
throw new IllegalParametersException(Messages.INVALID_REQUEST_PARAMETERS);
}
boolean allowed = configurationService.getBoolean(ALLOW_NETWORK_AUTO_CREATE, false);
if (allowed) {
NetworkWithUsersAndDevicesVO newNetwork = new NetworkWithUsersAndDevicesVO(network);
networkDao.persist(newNetwork);
network.setId(newNetwork.getId());
}
return network;
}
}
@Transactional
public NetworkVO createOrUpdateNetworkByUser(Optional<NetworkVO> networkNullable, UserVO user) {
//case network is not defined
if (networkNullable == null || networkNullable.orElse(null) == null) {
return null;
}
NetworkVO network = networkNullable.orElse(null);
Optional<NetworkVO> storedOpt = findNetworkByIdOrName(network);
if (storedOpt.isPresent()) {
NetworkVO stored = validateNetworkKey(storedOpt.get(), network);
if (!userService.hasAccessToNetwork(user, stored)) {
throw new ActionNotAllowedException(Messages.NO_ACCESS_TO_NETWORK);
}
return stored;
} else {
if (network.getId() != null) {
throw new IllegalParametersException(Messages.INVALID_REQUEST_PARAMETERS);
}
boolean allowed = configurationService.getBoolean(ALLOW_NETWORK_AUTO_CREATE, false);
if (user.isAdmin() || allowed) {
NetworkWithUsersAndDevicesVO newNetwork = new NetworkWithUsersAndDevicesVO(network);
networkDao.persist(newNetwork);
network.setId(newNetwork.getId());
userService.assignNetwork(user.getId(), network.getId()); // Assign created network to user
} else {
throw new ActionNotAllowedException(Messages.NETWORK_CREATION_NOT_ALLOWED);
}
return network;
}
}
private Optional<NetworkVO> findNetworkByIdOrName(NetworkVO network) {
return ofNullable(network.getId())
.map(id -> ofNullable(networkDao.find(id)))
.orElseGet(() -> networkDao.findFirstByName(network.getName()));
}
private NetworkVO validateNetworkKey(NetworkVO stored, NetworkVO received) {
if (stored.getKey() != null && !stored.getKey().equals(received.getKey())) {
throw new ActionNotAllowedException(Messages.INVALID_NETWORK_KEY);
}
return stored;
}
}