From a38e91499efa19614ac497ce88b7d39290010690 Mon Sep 17 00:00:00 2001 From: Kaveesha Date: Sun, 25 Oct 2020 23:30:45 +0530 Subject: [PATCH 1/2] Add API for get devices history snapshots --- .../service/api/DeviceManagementService.java | 90 +++++++ .../impl/DeviceManagementServiceImpl.java | 250 +++++++++--------- .../mgt/jaxrs/util/DeviceMgtAPIUtils.java | 161 ++++++++++- .../wso2/carbon/device/mgt/common/Device.java | 16 ++ .../device/mgt/common/DeviceIdentifier.java | 1 + 5 files changed, 384 insertions(+), 134 deletions(-) diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java index 2166e5c5872..361de7bbbc6 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java @@ -393,6 +393,96 @@ public interface DeviceManagementService { @QueryParam("limit") int limit); + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/{groupId}/location-history") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "Getting the Location Details of Devices in the group", + notes = "Get the location details of devices in the group during a define time period.", + response = Response.class, + tags = "Device Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:details") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully fetched the details of the device.", + response = Device.class, + responseHeaders = { + @ResponseHeader( + name = "Content-Type", + description = "The content type of the body"), + @ResponseHeader( + name = "ETag", + description = "Entity Tag of the response resource.\n" + + "Used by caches, or in conditional requests."), + @ResponseHeader( + name = "Last-Modified", + description = "Date and time the resource was last modified.\n" + + "Used by caches, or in conditional requests."), + }), + @ApiResponse( + code = 304, + message = "Not Modified. Empty body because the client already has the latest version" + + " of the requested resource.\n"), + @ApiResponse( + code = 400, + message = "Bad Request. \n Invalid request or validation error.", + response = ErrorResponse.class), + @ApiResponse( + code = 404, + message = "Not Found. \n Location history details for the devices with the specified group id was not found.", + response = ErrorResponse.class), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n " + + "Server error occurred while retrieving the devices location history details.", + response = ErrorResponse.class) + }) + Response getDevicesGroupLocationInfo( + @ApiParam( + name = "groupId", + value = "The group ID.", + required = true) + @PathParam("groupId") int groupId, + @ApiParam( + name = "from", + value = "Define the time to start getting the geo location history of the device in " + + "milliseconds.", + required = true) + @QueryParam("from") long from, + @ApiParam( + name = "to", + value = "Define the time to finish getting the geo location history of the device in " + + "milliseconds.", + required = true) + @QueryParam("to") long to, + @ApiParam( + name = "type", + value = "Defines how the output should be.", + required = true) + @QueryParam("type") String type, + @ApiParam( + name = "offset", + value = "The starting pagination index for the complete list of qualified items.", + required = false, + defaultValue = "0") + @QueryParam("offset") int offset, + @ApiParam( + name = "limit", + value = "Provide how many device details you require from the starting pagination index/offset.", + required = false, + defaultValue = "100") + @QueryParam("limit") int limit + ); + @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{type}/{id}") diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java index 731b4131b7c..42717744b28 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java @@ -36,9 +36,6 @@ package org.wso2.carbon.device.mgt.jaxrs.service.impl; -import java.util.LinkedList; -import java.util.Queue; - import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; @@ -51,8 +48,6 @@ import org.wso2.carbon.device.mgt.common.DeviceIdentifier; import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.common.Feature; import org.wso2.carbon.device.mgt.common.FeatureManager; -import org.wso2.carbon.device.mgt.common.MonitoringOperation; -import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig; import org.wso2.carbon.device.mgt.common.PaginationRequest; import org.wso2.carbon.device.mgt.common.PaginationResult; import org.wso2.carbon.device.mgt.common.app.mgt.Application; @@ -62,15 +57,13 @@ import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorization import org.wso2.carbon.device.mgt.common.device.details.DeviceData; import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo; import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation; -import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistory; -import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistorySnapshot; import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistorySnapshotWrapper; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.InvalidConfigurationException; import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException; import org.wso2.carbon.device.mgt.common.exceptions.BadRequestException; -import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup; +import org.wso2.carbon.device.mgt.common.exceptions.UnAuthorizedException; import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException; import org.wso2.carbon.device.mgt.common.operation.mgt.Activity; import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; @@ -92,6 +85,7 @@ import org.wso2.carbon.device.mgt.core.operation.mgt.ProfileOperation; import org.wso2.carbon.device.mgt.core.search.mgt.SearchManagerService; import org.wso2.carbon.device.mgt.core.search.mgt.SearchMgtException; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; +import org.wso2.carbon.device.mgt.core.service.GroupManagementProviderService; import org.wso2.carbon.device.mgt.core.util.DeviceManagerUtil; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceCompliance; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList; @@ -112,7 +106,6 @@ import org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerSer import org.wso2.carbon.policy.mgt.common.PolicyManagementException; import org.wso2.carbon.policy.mgt.core.PolicyManagerService; import org.wso2.carbon.user.api.UserStoreException; -import org.wso2.carbon.user.api.UserStoreManager; import org.wso2.carbon.utils.multitenancy.MultitenantUtils; import javax.validation.Valid; @@ -219,29 +212,7 @@ public class DeviceManagementServiceImpl implements DeviceManagementService { if (groupId != 0) { try { - int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); - UserStoreManager userStoreManager = DeviceMgtAPIUtils.getRealmService() - .getTenantUserRealm(tenantId).getUserStoreManager(); - String[] userRoles = userStoreManager.getRoleListOfUser(authorizedUser); - boolean isPermitted = false; - if (deviceAccessAuthorizationService.isDeviceAdminUser()) { - isPermitted = true; - } else { - List roles = DeviceMgtAPIUtils.getGroupManagementProviderService().getRoles(groupId); - for (String userRole : userRoles) { - if (roles.contains(userRole)) { - isPermitted = true; - break; - } - } - if (!isPermitted) { - DeviceGroup deviceGroup = DeviceMgtAPIUtils.getGroupManagementProviderService() - .getGroup(groupId, false); - if (deviceGroup != null && authorizedUser.equals(deviceGroup.getOwner())) { - isPermitted = true; - } - } - } + boolean isPermitted = DeviceMgtAPIUtils.checkPermission(groupId, authorizedUser); if (isPermitted) { request.setGroupId(groupId); } else { @@ -384,6 +355,103 @@ public class DeviceManagementServiceImpl implements DeviceManagementService { } } + /** + * Validate group Id and group Id greater than 0 and exist. + * + * @param groupId Group ID of the group + * @param from time to start getting DeviceLocationHistorySnapshotWrapper in milliseconds + * @param to time to end getting DeviceLocationHistorySnapshotWrapper in milliseconds + */ + private static void validateGroupId(int groupId, long from, long to) throws GroupManagementException, BadRequestException { + if (from == 0 || to == 0) { + String msg = "Invalid values for from/to"; + log.error(msg); + throw new BadRequestException(msg); + } + if (groupId <= 0) { + String msg = "Invalid group ID '" + groupId + "'"; + log.error(msg); + throw new BadRequestException(msg); + } + GroupManagementProviderService service = DeviceMgtAPIUtils.getGroupManagementProviderService(); + if (service.getGroup(groupId, false) == null) { + String msg = "Invalid group ID '" + groupId + "'"; + log.error(msg); + throw new BadRequestException(msg); + } + } + + @GET + @Override + @Path("/{groupId}/location-history") + public Response getDevicesGroupLocationInfo(@PathParam("groupId") int groupId, + @QueryParam("from") long from, + @QueryParam("to") long to, + @QueryParam("type") String type, + @DefaultValue("0") @QueryParam("offset") int offset, + @DefaultValue("100") @QueryParam("limit") int limit){ + try { + RequestValidationUtil.validatePaginationParameters(offset, limit); + DeviceManagementProviderService dms = DeviceMgtAPIUtils.getDeviceManagementService(); + PaginationRequest request = new PaginationRequest(offset, limit); + DeviceList devices = new DeviceList(); + + // this is the user who initiates the request + String authorizedUser = CarbonContext.getThreadLocalCarbonContext().getUsername(); + try { + validateGroupId(groupId, from, to); + boolean isPermitted = DeviceMgtAPIUtils.checkPermission(groupId, authorizedUser); + if (isPermitted) { + request.setGroupId(groupId); + } else { + String msg = "Current user '" + authorizedUser + + "' doesn't have enough privileges to list devices of group '" + + groupId + "'"; + log.error(msg); + return Response.status(Response.Status.FORBIDDEN).entity(msg).build(); + } + } catch (GroupManagementException e) { + String msg = "Error occurred while getting the data using '" + groupId + "'"; + log.error(msg); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } catch (UserStoreException e){ + String msg = "Error occurred while retrieving role list of user '" + authorizedUser + "'"; + log.error(msg); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } + + PaginationResult result = dms.getAllDevices(request, false); + + if(!result.getData().isEmpty()){ + devices.setList((List) result.getData()); + + for (Device device : devices.getList()) { + DeviceLocationHistorySnapshotWrapper snapshotWrapper = DeviceMgtAPIUtils.getDeviceHistorySnapshots( + device.getType(), device.getDeviceIdentifier(), authorizedUser, from, to, type, + dms); + device.setHistorySnapshot(snapshotWrapper); + } + } + return Response.status(Response.Status.OK).entity(devices).build(); + } catch (BadRequestException e) { + String msg = "Invalid type, use either 'path' or 'full'"; + log.error(msg, e); + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } catch (UnAuthorizedException e) { + String msg = "Current user doesn't have enough privileges to list devices of group '" + groupId + "'"; + log.error(msg, e); + return Response.status(Response.Status.FORBIDDEN).entity(msg).build(); + } catch (DeviceManagementException e) { + String msg = "Error occurred while fetching the device information."; + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } catch (DeviceAccessAuthorizationException e) { + String msg = "Error occurred while checking device access authorization"; + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } + } + @DELETE @Override @Path("/type/{deviceType}/id/{deviceId}") @@ -514,115 +582,33 @@ public class DeviceManagementServiceImpl implements DeviceManagementService { @QueryParam("to") long to, @QueryParam("type") String type) { try { - RequestValidationUtil.validateDeviceIdentifier(deviceType, deviceId); DeviceManagementProviderService dms = DeviceMgtAPIUtils.getDeviceManagementService(); - DeviceAccessAuthorizationService deviceAccessAuthorizationService = - DeviceMgtAPIUtils.getDeviceAccessAuthorizationService(); String authorizedUser = CarbonContext.getThreadLocalCarbonContext().getUsername(); - DeviceIdentifier deviceIdentifier = new DeviceIdentifier(deviceId, deviceType); - - if (!deviceAccessAuthorizationService.isUserAuthorized(deviceIdentifier, authorizedUser)) { - String msg = "User '" + authorizedUser + "' is not authorized to retrieve the given device id '" + - deviceId + "'"; - log.error(msg); - return Response.status(Response.Status.UNAUTHORIZED).entity(new ErrorResponse.ErrorResponseBuilder() - .setCode(Response.Status.UNAUTHORIZED.getStatusCode()).setMessage(msg).build()).build(); - } - if (from == 0 || to == 0) { - String msg = "Invalid values for from/to"; - log.error(msg); - return Response.status(Response.Status.BAD_REQUEST).entity(new ErrorResponse.ErrorResponseBuilder() - .setCode(Response.Status.BAD_REQUEST.getStatusCode()).setMessage(msg)).build(); - } - - // Get the location history snapshots for the given period - List deviceLocationHistorySnapshots = dms - .getDeviceLocationInfo(deviceIdentifier, from, to); - - OperationMonitoringTaskConfig operationMonitoringTaskConfig = dms.getDeviceMonitoringConfig(deviceType); - int taskFrequency = operationMonitoringTaskConfig.getFrequency(); - int operationRecurrentTimes = 0; - - List monitoringOperations = operationMonitoringTaskConfig.getMonitoringOperation(); - for (MonitoringOperation monitoringOperation : monitoringOperations) { - if (monitoringOperation.getTaskName().equals("DEVICE_LOCATION")) { - operationRecurrentTimes = monitoringOperation.getRecurrentTimes(); - break; - } - } - - // Device Location operation frequency in milliseconds. Adding 100000 ms as an error - long operationFrequency = taskFrequency * operationRecurrentTimes + 100000; - Queue deviceLocationHistorySnapshotsQueue = new LinkedList<>( - deviceLocationHistorySnapshots); - List> locationHistorySnapshotList = new ArrayList<>(); - - List pathsArray = new ArrayList<>(); - DeviceLocationHistorySnapshotWrapper snapshotWrapper = new DeviceLocationHistorySnapshotWrapper(); - while (!deviceLocationHistorySnapshotsQueue.isEmpty()) { - List snapshots = new ArrayList<>(); - // Make a copy of remaining snapshots - List cachedSnapshots = new ArrayList<>( - deviceLocationHistorySnapshotsQueue); - - List locationPoint = new ArrayList<>(); - for (int i = 0; i < cachedSnapshots.size(); i++) { - DeviceLocationHistorySnapshot currentSnapshot = deviceLocationHistorySnapshotsQueue.poll(); - snapshots.add(currentSnapshot); - if (currentSnapshot != null) { - locationPoint.add(currentSnapshot.getLatitude()); - locationPoint.add(currentSnapshot.getLongitude()); - locationPoint.add(currentSnapshot.getUpdatedTime()); - pathsArray.add(new ArrayList<>(locationPoint)); - locationPoint.clear(); - } - if (!deviceLocationHistorySnapshotsQueue.isEmpty()) { - DeviceLocationHistorySnapshot nextSnapshot = deviceLocationHistorySnapshotsQueue.peek(); - locationPoint.add(nextSnapshot.getLatitude()); - locationPoint.add(nextSnapshot.getLongitude()); - locationPoint.add(nextSnapshot.getUpdatedTime()); - pathsArray.add(new ArrayList<>(locationPoint)); - locationPoint.clear(); - if (nextSnapshot.getUpdatedTime().getTime() - currentSnapshot.getUpdatedTime().getTime() - > operationFrequency) { - break; - } - } - } - locationHistorySnapshotList.add(snapshots); - } - DeviceLocationHistory deviceLocationHistory = new DeviceLocationHistory(); - deviceLocationHistory.setLocationHistorySnapshots(locationHistorySnapshotList); - if (type != null) { - if (type.equals("path")) { - snapshotWrapper.setPathSnapshot(pathsArray); - } else if (type.equals("full")) { - snapshotWrapper.setFullSnapshot(deviceLocationHistory); - } else { - String msg = "Invalid type, use either 'path' or 'full'"; - log.error(msg); - return Response.status(Response.Status.BAD_REQUEST).entity(new ErrorResponse.ErrorResponseBuilder() - .setCode(Response.Status.BAD_REQUEST.getStatusCode()).setMessage(msg)).build(); - } - } else { - snapshotWrapper.setFullSnapshot(deviceLocationHistory); - } + DeviceLocationHistorySnapshotWrapper snapshotWrapper = DeviceMgtAPIUtils.getDeviceHistorySnapshots( + deviceType, deviceId, authorizedUser, from, to, type, + dms); return Response.status(Response.Status.OK).entity(snapshotWrapper).build(); + } catch (BadRequestException e) { + String msg = "Invalid type, use either 'path' or 'full'"; + log.error(msg, e); + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } catch (UnAuthorizedException e) { + String msg = "Current user doesn't have enough privileges to retrieve the given device id '" + + deviceId + "'"; + log.error(msg, e); + return Response.status(Response.Status.FORBIDDEN).entity(msg).build(); } catch (DeviceManagementException e) { String msg = "Error occurred while fetching the device information."; log.error(msg, e); - return Response.serverError().entity(new ErrorResponse.ErrorResponseBuilder() - .setCode(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).setMessage(msg).build()).build(); - } catch (DeviceAccessAuthorizationException e) { - String msg = "Error occurred while checking the device authorization."; - log.error(msg, e); - return Response.serverError().entity(new ErrorResponse.ErrorResponseBuilder() - .setCode(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).setMessage(msg).build()).build(); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); } catch (InputValidationException e) { String msg = "Invalid device Id or device type"; log.error(msg, e); - return Response.status(Response.Status.BAD_REQUEST).entity(new ErrorResponse.ErrorResponseBuilder() - .setCode(Response.Status.BAD_REQUEST.getStatusCode()).setMessage(msg)).build(); + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } catch (DeviceAccessAuthorizationException e) { + String msg = "Error occurred while checking device access authorization"; + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java index 56f2a76e367..e652311aeda 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java @@ -53,16 +53,25 @@ import org.wso2.carbon.context.CarbonContext; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.core.util.Utils; import org.wso2.carbon.device.mgt.analytics.data.publisher.service.EventsPublisherService; -import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.DeviceIdentifier; +import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; +import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig; +import org.wso2.carbon.device.mgt.common.MonitoringOperation; +import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationException; +import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistory; +import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistorySnapshot; +import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistorySnapshotWrapper; import org.wso2.carbon.device.mgt.common.exceptions.BadRequestException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; -import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationService; import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationEntry; import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration; import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfigurationManagementService; +import org.wso2.carbon.device.mgt.common.exceptions.UnAuthorizedException; import org.wso2.carbon.device.mgt.common.geo.service.GeoLocationProviderService; +import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup; +import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException; import org.wso2.carbon.device.mgt.common.metadata.mgt.MetadataManagementService; import org.wso2.carbon.device.mgt.common.notification.mgt.NotificationManagementService; import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; @@ -82,6 +91,7 @@ import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse; import org.wso2.carbon.device.mgt.jaxrs.beans.OperationStatusBean; import org.wso2.carbon.device.mgt.jaxrs.beans.analytics.EventAttributeList; import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.InputValidationException; +import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil; import org.wso2.carbon.event.processor.stub.EventProcessorAdminServiceStub; import org.wso2.carbon.event.publisher.stub.EventPublisherAdminServiceStub; import org.wso2.carbon.event.receiver.stub.EventReceiverAdminServiceStub; @@ -123,7 +133,9 @@ import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; +import java.util.Queue; /** * MDMAPIUtils class provides utility function used by CDM REST-API classes. @@ -940,4 +952,149 @@ public class DeviceMgtAPIUtils { claimPropertyDTO.setPropertyValue(propertyValue); return claimPropertyDTO; } + + /** + * Getting Device History Snapshots for given Device Type and Identifier. + * + * @param deviceType Device type of the device + * @param identifier Device identifier of the device + * @param authorizedUser user who initiates the request + * @param from time to start getting DeviceLocationHistorySnapshotWrapper in milliseconds + * @param to time to end getting DeviceLocationHistorySnapshotWrapper in milliseconds + * @param type output type should be for DeviceLocationHistorySnapshotWrapper + * @param dms DeviceManagementService instance + * + * @return DeviceLocationHistorySnapshotWrapper instance + * @throws DeviceManagementException if device information cannot be fetched + * @throws DeviceAccessAuthorizationException if device authorization get failed + */ + public static DeviceLocationHistorySnapshotWrapper getDeviceHistorySnapshots(String deviceType, + String identifier, + String authorizedUser, + long from, + long to, + String type, + DeviceManagementProviderService dms) + throws DeviceManagementException, DeviceAccessAuthorizationException { + RequestValidationUtil.validateDeviceIdentifier(deviceType, identifier); + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(identifier, deviceType); + + if (!getDeviceAccessAuthorizationService().isUserAuthorized(deviceIdentifier, authorizedUser)) { + String msg = "User '" + authorizedUser + "' is not authorized to retrieve the given device id '" + + identifier + "'"; + log.error(msg); + throw new UnAuthorizedException(msg); + } + + // Get the location history snapshots for the given period + List deviceLocationHistorySnapshots = dms.getDeviceLocationInfo(deviceIdentifier, from, to); + + OperationMonitoringTaskConfig operationMonitoringTaskConfig = dms.getDeviceMonitoringConfig(deviceType); + int taskFrequency = operationMonitoringTaskConfig.getFrequency(); + int operationRecurrentTimes = 0; + + List monitoringOperations = operationMonitoringTaskConfig.getMonitoringOperation(); + for (MonitoringOperation monitoringOperation : monitoringOperations) { + if (monitoringOperation.getTaskName().equals("DEVICE_LOCATION")) { + operationRecurrentTimes = monitoringOperation.getRecurrentTimes(); + break; + } + } + + // Device Location operation frequency in milliseconds. Adding 100000 ms as an error + long operationFrequency = taskFrequency * operationRecurrentTimes + 100000; + Queue deviceLocationHistorySnapshotsQueue = new LinkedList<>( + deviceLocationHistorySnapshots); + List> locationHistorySnapshotList = new ArrayList<>(); + + List pathsArray = new ArrayList<>(); + DeviceLocationHistorySnapshotWrapper snapshotWrapper = new DeviceLocationHistorySnapshotWrapper(); + while (!deviceLocationHistorySnapshotsQueue.isEmpty()) { + List snapshots = new ArrayList<>(); + // Make a copy of remaining snapshots + List cachedSnapshots = new ArrayList<>( + deviceLocationHistorySnapshotsQueue); + + List locationPoint = new ArrayList<>(); + for (int i = 0; i < cachedSnapshots.size(); i++) { + DeviceLocationHistorySnapshot currentSnapshot = deviceLocationHistorySnapshotsQueue.poll(); + snapshots.add(currentSnapshot); + if (currentSnapshot != null) { + locationPoint.add(currentSnapshot.getLatitude()); + locationPoint.add(currentSnapshot.getLongitude()); + locationPoint.add(currentSnapshot.getUpdatedTime()); + pathsArray.add(new ArrayList<>(locationPoint)); + locationPoint.clear(); + } + if (!deviceLocationHistorySnapshotsQueue.isEmpty()) { + DeviceLocationHistorySnapshot nextSnapshot = deviceLocationHistorySnapshotsQueue.peek(); + locationPoint.add(nextSnapshot.getLatitude()); + locationPoint.add(nextSnapshot.getLongitude()); + locationPoint.add(nextSnapshot.getUpdatedTime()); + pathsArray.add(new ArrayList<>(locationPoint)); + locationPoint.clear(); + if (nextSnapshot.getUpdatedTime().getTime() - currentSnapshot.getUpdatedTime().getTime() + > operationFrequency) { + break; + } + } + } + locationHistorySnapshotList.add(snapshots); + } + DeviceLocationHistory deviceLocationHistory = new DeviceLocationHistory(); + deviceLocationHistory.setLocationHistorySnapshots(locationHistorySnapshotList); + if (type != null) { + if (type.equals("path")) { + snapshotWrapper.setPathSnapshot(pathsArray); + } else if (type.equals("full")) { + snapshotWrapper.setFullSnapshot(deviceLocationHistory); + } else { + String msg = "Invalid type, use either 'path' or 'full'"; + log.error(msg); + throw new BadRequestException(msg); + } + } else { + snapshotWrapper.setFullSnapshot(deviceLocationHistory); + } + return snapshotWrapper; + } + + /** + * Check user who initiates the request has permission to list devices from given group Id. + * + * @param groupId Group ID of the group + * @param authorizedUser user who initiates the request + * + * @return boolean instance + * @throws UserStoreException if roles list of authorizedUser cannot be fetched + * @throws DeviceAccessAuthorizationException if device authorization get failed. + * @throws GroupManagementException if group or roles cannot be fetched using groupId + */ + public static boolean checkPermission(int groupId, String authorizedUser) throws UserStoreException, DeviceAccessAuthorizationException, GroupManagementException { + int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); + UserStoreManager userStoreManager = DeviceMgtAPIUtils.getRealmService() + .getTenantUserRealm(tenantId).getUserStoreManager(); + String[] userRoles = userStoreManager.getRoleListOfUser(authorizedUser); + boolean isPermitted = false; + if (getDeviceAccessAuthorizationService().isDeviceAdminUser()) { + isPermitted = true; + } else { + List roles = DeviceMgtAPIUtils.getGroupManagementProviderService().getRoles(groupId); + for (String userRole : userRoles) { + if (roles.contains(userRole)) { + isPermitted = true; + break; + } + } + if (!isPermitted) { + DeviceGroup deviceGroup = DeviceMgtAPIUtils.getGroupManagementProviderService() + .getGroup(groupId, false); + if (deviceGroup != null && authorizedUser.equals(deviceGroup.getOwner())) { + isPermitted = true; + } + } + } + return isPermitted; + } + } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/Device.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/Device.java index 9ab36b2dc35..df86fd3575e 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/Device.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/Device.java @@ -20,8 +20,10 @@ package org.wso2.carbon.device.mgt.common; import com.google.gson.Gson; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; +import com.fasterxml.jackson.annotation.JsonProperty; import org.wso2.carbon.device.mgt.common.app.mgt.Application; import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo; +import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistorySnapshotWrapper; import java.io.Serializable; import java.util.List; @@ -70,6 +72,12 @@ public class Device implements Serializable { required = false) private List applications; + @ApiModelProperty( + name = "historySnapshot", + value = "device history snapshots") + @JsonProperty(value = "historySnapshot") + private DeviceLocationHistorySnapshotWrapper historySnapshot; + public Device() { } @@ -164,6 +172,14 @@ public class Device implements Serializable { this.applications = applications; } + public DeviceLocationHistorySnapshotWrapper getHistorySnapshot() { + return historySnapshot; + } + + public void setHistorySnapshot(DeviceLocationHistorySnapshotWrapper historySnapshot) { + this.historySnapshot = historySnapshot; + } + public static class Property { private String name; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceIdentifier.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceIdentifier.java index 04504ccdc91..9fc3401a366 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceIdentifier.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceIdentifier.java @@ -58,6 +58,7 @@ public class DeviceIdentifier implements Serializable{ public void setType(String type) { this.type = type.trim(); } + public String getId() { return id; } From c8218087971a25ddb5e10b6332ff45ae77ec8d06 Mon Sep 17 00:00:00 2001 From: Shamalka Navod Date: Mon, 9 Nov 2020 19:29:15 +0000 Subject: [PATCH 2/2] Fix store app loading issue --- .../dao/impl/application/GenericApplicationDAOImpl.java | 6 +++--- .../dao/impl/application/OracleApplicationDAOImpl.java | 7 +++---- .../dao/impl/application/SQLServerApplicationDAOImpl.java | 7 +++---- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/GenericApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/GenericApplicationDAOImpl.java index 1059fe45803..845c4626d51 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/GenericApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/GenericApplicationDAOImpl.java @@ -173,14 +173,14 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic if (deviceTypeId != -1) { sql += "AND AP_APP.DEVICE_TYPE_ID = ? "; } - sql += "GROUP BY AP_APP.ID "; + sql += "GROUP BY AP_APP.ID ORDER BY AP_APP.ID "; if (StringUtils.isNotEmpty(filter.getSortBy())) { - sql += "ORDER BY ID " + filter.getSortBy() +" "; + sql += filter.getSortBy() +" "; } if (filter.getLimit() != -1) { sql += "LIMIT ? OFFSET ? "; } - sql += ") AS app_data ON app_data.ID = AP_APP.ID WHERE AP_APP.TENANT_ID = ?"; + sql += ") AS app_data ON app_data.ID = AP_APP.ID WHERE AP_APP.TENANT_ID = ? ORDER BY AP_APP.ID"; try { Connection conn = this.getDBConnection(); try (PreparedStatement stmt = conn.prepareStatement(sql)) { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/OracleApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/OracleApplicationDAOImpl.java index b570c10fabf..c57ef8a07d7 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/OracleApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/OracleApplicationDAOImpl.java @@ -118,15 +118,14 @@ public class OracleApplicationDAOImpl extends GenericApplicationDAOImpl { if (deviceTypeId != -1) { sql += "AND AP_APP.DEVICE_TYPE_ID = ? "; } - sql += "GROUP BY AP_APP.ID "; + sql += "GROUP BY AP_APP.ID ORDER BY AP_APP.ID "; if (StringUtils.isNotEmpty(filter.getSortBy())) { - sql += "ORDER BY ID " + filter.getSortBy() + " "; + sql += filter.getSortBy() +" "; } if (filter.getLimit() != -1) { sql += "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY "; } - sql += ") AS app_data ON app_data.ID = AP_APP.ID " + - "WHERE AP_APP.TENANT_ID = ?"; + sql += ") AS app_data ON app_data.ID = AP_APP.ID WHERE AP_APP.TENANT_ID = ? ORDER BY AP_APP.ID"; try { Connection conn = this.getDBConnection(); try (PreparedStatement stmt = conn.prepareStatement(sql)) { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/SQLServerApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/SQLServerApplicationDAOImpl.java index 8825b205797..ee7c78f8bac 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/SQLServerApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/SQLServerApplicationDAOImpl.java @@ -117,15 +117,14 @@ public class SQLServerApplicationDAOImpl extends GenericApplicationDAOImpl { if (deviceTypeId != -1) { sql += "AND AP_APP.DEVICE_TYPE_ID = ? "; } - sql += "GROUP BY AP_APP.ID "; + sql += "GROUP BY AP_APP.ID ORDER BY AP_APP.ID "; if (StringUtils.isNotEmpty(filter.getSortBy())) { - sql += "ORDER BY ID " + filter.getSortBy() + " "; + sql += filter.getSortBy() +" "; } if (filter.getLimit() != -1) { sql += "ORDER BY ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY "; } - sql += ") AS app_data ON app_data.ID = AP_APP.ID " + - "WHERE AP_APP.TENANT_ID = ?"; + sql += ") AS app_data ON app_data.ID = AP_APP.ID WHERE AP_APP.TENANT_ID = ? ORDER BY AP_APP.ID"; try { Connection conn = this.getDBConnection(); try (PreparedStatement stmt = conn.prepareStatement(sql)) {