diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/SubscriptionManager.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/SubscriptionManager.java index 600da94b4f4..bc2eaabc47f 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/SubscriptionManager.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/SubscriptionManager.java @@ -24,8 +24,10 @@ import org.wso2.carbon.device.application.mgt.common.exception.SubscriptionManag import org.wso2.carbon.device.mgt.common.DeviceIdentifier; 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.App; import java.util.List; +import java.util.Properties; /** * This interface manages all the operations related with ApplicationDTO Subscription. @@ -33,22 +35,22 @@ import java.util.List; public interface SubscriptionManager { /** * Performs bulk subscription operation for a given application and a subscriber list. - * - * @param applicationUUID UUID of the application to subscribe/unsubscribe + * @param applicationUUID UUID of the application to subscribe/unsubscribe * @param params list of subscribers. This list can be of either - * {@link org.wso2.carbon.device.mgt.common.DeviceIdentifier} if {@param subType} is equal + * {@link DeviceIdentifier} if {@param subType} is equal * to DEVICE or * {@link String} if {@param subType} is USER, ROLE or GROUP * @param subType subscription type. E.g. DEVICE, USER, ROLE, GROUP {@see { - * @param action subscription action. E.g. INSTALL/UNINSTALL {@see { - * @param generic type of the method. - * @return {@link ApplicationInstallResponse} - * @throws ApplicationManagementException if error occurs when subscribing to the given application - * @link org.wso2.carbon.device.application.mgt.common.SubscriptionType}} - * @link org.wso2.carbon.device.application.mgt.common.SubAction}} + * @param action subscription action. E.g. INSTALL/UNINSTALL {@see { + * @param generic type of the method. + * @return {@link ApplicationInstallResponse} + * @throws ApplicationManagementException if error occurs when subscribing to the given application + * @link org.wso2.carbon.device.application.mgt.common.SubscriptionType}} + * @link org.wso2.carbon.device.application.mgt.common.SubAction}} + * @param properties */ ApplicationInstallResponse performBulkAppOperation(String applicationUUID, List params, String subType, - String action) throws ApplicationManagementException; + String action, Properties properties) throws ApplicationManagementException; /** * Create an entry related to the scheduled task in the database. @@ -121,7 +123,7 @@ public interface SubscriptionManager { * @throws ApplicationManagementException if error occurred while installing given applications into the given * device */ - void installAppsForDevice(DeviceIdentifier deviceIdentifier, List releaseUUID) + void installAppsForDevice(DeviceIdentifier deviceIdentifier, List apps) throws ApplicationManagementException; /*** diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java index 8219dbb4e76..2de1094c1ff 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java @@ -121,7 +121,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { @Override public ApplicationInstallResponse performBulkAppOperation(String applicationUUID, List params, - String subType, String action) throws ApplicationManagementException { + String subType, String action, Properties properties) throws ApplicationManagementException { if (log.isDebugEnabled()) { log.debug("Install application release which has UUID " + applicationUUID + " to " + params.size() + " users."); @@ -134,7 +134,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { params); ApplicationInstallResponse applicationInstallResponse = performActionOnDevices( applicationSubscriptionInfo.getAppSupportingDeviceTypeName(), applicationSubscriptionInfo.getDevices(), - applicationDTO, subType, applicationSubscriptionInfo.getSubscribers(), action); + applicationDTO, subType, applicationSubscriptionInfo.getSubscribers(), action, properties); applicationInstallResponse.setErrorDeviceIdentifiers(applicationSubscriptionInfo.getErrorDeviceIdentifiers()); return applicationInstallResponse; @@ -347,7 +347,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { } @Override - public void installAppsForDevice(DeviceIdentifier deviceIdentifier, List releaseUUIDs) + public void installAppsForDevice(DeviceIdentifier deviceIdentifier, List apps) throws ApplicationManagementException { int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); @@ -370,7 +370,8 @@ public class SubscriptionManagerImpl implements SubscriptionManager { List appInstallingDevices = new ArrayList<>(); - for (String releaseUUID : releaseUUIDs) { + for (App app : apps) { + String releaseUUID = app.getId(); try { ConnectionManagerUtil.openDBConnection(); ApplicationDTO applicationDTO = this.applicationDAO.getAppWithRelatedRelease(releaseUUID, tenantId); @@ -409,7 +410,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { if (!appInstallingDevices.isEmpty()) { performBulkAppOperation(releaseUUID, appInstallingDevices, SubscriptionType.DEVICE.toString(), - SubAction.INSTALL.toString()); + SubAction.INSTALL.toString(), app.getProperties()); } } } @@ -621,7 +622,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { * data. */ private ApplicationInstallResponse performActionOnDevices(String deviceType, List devices, - ApplicationDTO applicationDTO, String subType, List subscribers, String action) + ApplicationDTO applicationDTO, String subType, List subscribers, String action, Properties properties) throws ApplicationManagementException { //Get app subscribing info of each device @@ -667,11 +668,11 @@ public class SubscriptionManagerImpl implements SubscriptionManager { } for (Map.Entry> entry : deviceIdentifierMap.entrySet()) { Activity activity = addAppOperationOnDevices(applicationDTO, new ArrayList<>(entry.getValue()), - entry.getKey(), action); + entry.getKey(), action, properties); activityList.add(activity); } } else { - Activity activity = addAppOperationOnDevices(applicationDTO, deviceIdentifiers, deviceType, action); + Activity activity = addAppOperationOnDevices(applicationDTO, deviceIdentifiers, deviceType, action, properties); activityList.add(activity); } @@ -982,13 +983,13 @@ public class SubscriptionManagerImpl implements SubscriptionManager { * @throws ApplicationManagementException if found an invalid device. */ private Activity addAppOperationOnDevices(ApplicationDTO applicationDTO, - List deviceIdentifierList, String deviceType, String action) + List deviceIdentifierList, String deviceType, String action, Properties properties) throws ApplicationManagementException { DeviceManagementProviderService deviceManagementProviderService = HelperUtil .getDeviceManagementProviderService(); try { Application application = APIUtil.appDtoToAppResponse(applicationDTO); - Operation operation = generateOperationPayloadByDeviceType(deviceType, application, action); + Operation operation = generateOperationPayloadByDeviceType(deviceType, application, action, properties); return deviceManagementProviderService.addOperation(deviceType, operation, deviceIdentifierList); } catch (OperationManagementException e) { String msg = "Error occurred while adding the application install operation to devices"; @@ -1010,7 +1011,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { * @throws ApplicationManagementException if unknown application type is found to generate operation payload or * invalid action is found to generate operation payload. */ - private Operation generateOperationPayloadByDeviceType(String deviceType, Application application, String action) + private Operation generateOperationPayloadByDeviceType(String deviceType, Application application, String action, Properties properties) throws ApplicationManagementException { try { if (ApplicationType.CUSTOM.toString().equalsIgnoreCase(application.getType())) { @@ -1046,6 +1047,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { app.setLocation(application.getApplicationReleases().get(0).getInstallerPath()); app.setIdentifier(application.getPackageName()); app.setName(application.getName()); + app.setProperties(properties); if (SubAction.INSTALL.toString().equalsIgnoreCase(action)) { return MDMAndroidOperationUtil.createInstallAppOperation(app); } else { @@ -1067,7 +1069,6 @@ public class SubscriptionManagerImpl implements SubscriptionManager { app.setType(mobileAppType); app.setLocation(plistDownloadEndpoint); app.setIconImage(application.getApplicationReleases().get(0).getIconPath()); - Properties properties = new Properties(); properties.put(MDMAppConstants.IOSConstants.IS_PREVENT_BACKUP, true); properties.put(MDMAppConstants.IOSConstants.IS_REMOVE_APP, true); properties.put(MDMAppConstants.IOSConstants.I_TUNES_ID, application.getPackageName()); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java index 8a95efc0aaa..d8f491e37dc 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java @@ -37,6 +37,7 @@ import org.wso2.carbon.device.mgt.core.task.impl.RandomlyAssignedScheduleTask; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -45,6 +46,7 @@ public class ScheduledAppSubscriptionTask extends RandomlyAssignedScheduleTask { private static final String TASK_NAME = "SCHEDULE_APP_SUBSCRIPTION"; private SubscriptionManager subscriptionManager; + private String payload; private String subscribers; private String subscriptionType; private String application; @@ -57,6 +59,7 @@ public class ScheduledAppSubscriptionTask extends RandomlyAssignedScheduleTask { @Override public void setProperties(Map map) { this.subscribers = map.get(Constants.SUBSCRIBERS); + this.payload = map.get(Constants.PAYLOAD); this.subscriptionType = map.get(Constants.SUB_TYPE); this.application = map.get(Constants.APP_UUID); this.action = map.get(Constants.ACTION); @@ -89,8 +92,9 @@ public class ScheduledAppSubscriptionTask extends RandomlyAssignedScheduleTask { new TypeToken>() { }.getType()); try { + Properties properties = new Gson().fromJson(payload, Properties.class); subscriptionManager.performBulkAppOperation(this.application, deviceIdentifiers, - this.subscriptionType, this.action); + this.subscriptionType, this.action, properties); subscriptionDTO.setStatus(ExecutionStatus.EXECUTED); } catch (ApplicationManagementException e) { log.error( @@ -102,8 +106,9 @@ public class ScheduledAppSubscriptionTask extends RandomlyAssignedScheduleTask { List subscriberList = Pattern.compile(",").splitAsStream(this.subscribers).collect( Collectors.toList()); try { + Properties properties = new Gson().fromJson(payload, Properties.class); subscriptionManager.performBulkAppOperation(this.application, subscriberList, - this.subscriptionType, this.action); + this.subscriptionType, this.action, properties); subscriptionDTO.setStatus(ExecutionStatus.EXECUTED); } catch (ApplicationManagementException e) { log.error( diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTaskManager.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTaskManager.java index 326a358e1b9..b274b1850f3 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTaskManager.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTaskManager.java @@ -46,6 +46,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Properties; import java.util.stream.Collectors; public class ScheduledAppSubscriptionTaskManager { @@ -73,7 +74,7 @@ public class ScheduledAppSubscriptionTaskManager { * @throws ApplicationOperationTaskException if error occurred while scheduling the subscription */ public void scheduleAppSubscriptionTask(String applicationUUID, List subscribers, - SubscriptionType subscriptionType, SubAction action, long timestamp) + SubscriptionType subscriptionType, SubAction action, long timestamp, Properties properties) throws ApplicationOperationTaskException { Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date(timestamp * 1000)); @@ -106,7 +107,6 @@ public class ScheduledAppSubscriptionTaskManager { taskProperties.put(Constants.APP_UUID, applicationUUID); taskProperties.put(Constants.TENANT_DOMAIN, carbonContext.getTenantDomain(true)); taskProperties.put(Constants.SUBSCRIBER, carbonContext.getUsername()); - String subscribersString; if (SubscriptionType.DEVICE.equals(subscriptionType)) { subscribersString = new Gson().toJson(subscribers); @@ -115,6 +115,10 @@ public class ScheduledAppSubscriptionTaskManager { subscribersString = subscribers.stream().map(String.class::cast).collect(Collectors.joining(",")); taskProperties.put(Constants.SUBSCRIBERS, subscribersString); } + if(properties != null) { + String payload = new Gson().toJson(properties); + taskProperties.put(Constants.PAYLOAD, payload); + } if (log.isDebugEnabled()) { log.debug("Scheduling a task to " + action.toString() + " application: " + applicationUUID + " to/from the following " + subscriptionType.toString() + "S [" + subscribersString + "] at: " diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java index 866fbdd5241..27498f77bb7 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java @@ -58,6 +58,7 @@ public class Constants { public static final String SUB_TYPE = "SUBSCRIPTION_TYPE"; public static final String ACTION = "ACTION"; public static final String APP_UUID = "APP_UUID"; + public static final String APP_PROPERTIES = "APP_PROPERTIES"; public static final String SUBSCRIBER = "SUBSCRIBER"; public static final String TENANT_DOMAIN = "TENANT_DOMAIN"; public static final String TENANT_ID = "__TENANT_ID_PROP__"; diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/SubscriptionManagementAPI.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/SubscriptionManagementAPI.java index 84081e9f886..9c2bfcdf796 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/SubscriptionManagementAPI.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/SubscriptionManagementAPI.java @@ -133,7 +133,12 @@ public interface SubscriptionManagementAPI { name = "timestamp", value = "Timestamp of scheduled install/uninstall operation" ) - @QueryParam("timestamp") long timestamp + @QueryParam("timestamp") long timestamp, + @ApiParam( + name = "block-uninstall", + value = "App removal status of the install operation" + ) + @QueryParam("block-uninstall") Boolean isUninstallBlocked ); @POST @@ -186,7 +191,12 @@ public interface SubscriptionManagementAPI { name = "timestamp", value = "Timestamp of scheduled install/uninstall operation" ) - @QueryParam("timestamp") long timestamp + @QueryParam("timestamp") long timestamp, + @ApiParam( + name = "block-uninstall", + value = "App removal status of the install operation" + ) + @QueryParam("block-uninstall") Boolean isUninstallBlocked ); @POST diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/impl/SubscriptionManagementAPIImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/impl/SubscriptionManagementAPIImpl.java index a6324c8fdb6..4a51353997a 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/impl/SubscriptionManagementAPIImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/impl/SubscriptionManagementAPIImpl.java @@ -34,8 +34,6 @@ import org.wso2.carbon.device.application.mgt.common.BasicUserInfoList; import org.wso2.carbon.device.application.mgt.common.RoleList; import org.wso2.carbon.device.application.mgt.common.DeviceGroupList; import org.wso2.carbon.device.application.mgt.store.api.services.impl.util.RequestValidationUtil; -import org.wso2.carbon.device.mgt.common.PaginationRequest; -import org.wso2.carbon.device.mgt.common.PaginationResult; import org.wso2.carbon.device.application.mgt.core.exception.BadRequestException; import org.wso2.carbon.device.application.mgt.core.exception.ForbiddenException; import org.wso2.carbon.device.application.mgt.core.exception.NotFoundException; @@ -44,6 +42,9 @@ import org.wso2.carbon.device.application.mgt.core.util.APIUtil; import org.wso2.carbon.device.application.mgt.store.api.services.SubscriptionManagementAPI; import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.DeviceIdentifier; +import org.wso2.carbon.device.mgt.common.MDMAppConstants; +import org.wso2.carbon.device.mgt.common.PaginationRequest; +import org.wso2.carbon.device.mgt.common.PaginationResult; import javax.validation.Valid; import javax.ws.rs.Path; @@ -55,9 +56,8 @@ import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.core.Response; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.Properties; /** * Implementation of Subscription Management related APIs. @@ -75,16 +75,22 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{ @PathParam("uuid") String uuid, @PathParam("action") String action, @Valid List deviceIdentifiers, - @QueryParam("timestamp") long timestamp) { + @QueryParam("timestamp") long timestamp, + @QueryParam("block-uninstall") Boolean isUninstallBlocked + ) { + Properties properties = new Properties(); + if(isUninstallBlocked != null) { + properties.put(MDMAppConstants.AndroidConstants.IS_BLOCK_UNINSTALL, isUninstallBlocked); + } try { if (0 == timestamp) { SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager(); ApplicationInstallResponse response = subscriptionManager - .performBulkAppOperation(uuid, deviceIdentifiers, SubscriptionType.DEVICE.toString(), action); + .performBulkAppOperation(uuid, deviceIdentifiers, SubscriptionType.DEVICE.toString(), action, properties); return Response.status(Response.Status.OK).entity(response).build(); } else { return scheduleApplicationOperationTask(uuid, deviceIdentifiers, SubscriptionType.DEVICE, - SubAction.valueOf(action.toUpperCase()), timestamp); + SubAction.valueOf(action.toUpperCase()), timestamp, properties); } } catch (NotFoundException e) { String msg = "Couldn't found an application release for UUI: " + uuid; @@ -116,17 +122,23 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{ @PathParam("subType") String subType, @PathParam("action") String action, @Valid List subscribers, - @QueryParam("timestamp") long timestamp) { + @QueryParam("timestamp") long timestamp, + @QueryParam("block-uninstall") Boolean isUninstallBlocked + ) { + Properties properties = new Properties(); + if(isUninstallBlocked != null) { + properties.put(MDMAppConstants.AndroidConstants.IS_BLOCK_UNINSTALL, isUninstallBlocked); + } try { if (0 == timestamp) { SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager(); ApplicationInstallResponse response = subscriptionManager - .performBulkAppOperation(uuid, subscribers, subType, action); + .performBulkAppOperation(uuid, subscribers, subType, action, properties); return Response.status(Response.Status.OK).entity(response).build(); } else { return scheduleApplicationOperationTask(uuid, subscribers, SubscriptionType.valueOf(subType.toUpperCase()), SubAction.valueOf(action.toUpperCase()), - timestamp); + timestamp, properties); } } catch (NotFoundException e) { String msg = "Couldn't found an application release for UUID: " + uuid + ". Hence, verify the payload"; @@ -170,7 +182,7 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{ return Response.status(Response.Status.OK).entity(msg).build(); } else { return scheduleApplicationOperationTask(uuid, deviceIdentifiers, SubscriptionType.DEVICE, - SubAction.valueOf(SubAction.INSTALL.toString().toUpperCase()), timestamp); + SubAction.valueOf(SubAction.INSTALL.toString().toUpperCase()), timestamp, null); } } catch (NotFoundException e) { String msg = "Couldn't found an application release for UUI: " + uuid + " to perform ent app installation " @@ -216,7 +228,7 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{ } else { return scheduleApplicationOperationTask(uuid, subscribers, SubscriptionType.valueOf(subType.toUpperCase()), - SubAction.valueOf(SubAction.INSTALL.toString().toUpperCase()), timestamp); + SubAction.valueOf(SubAction.INSTALL.toString().toUpperCase()), timestamp, null); } } catch (NotFoundException e) { String msg = "Couldn't found an application release for UUID: " + uuid + ". Hence, verify the payload"; @@ -255,11 +267,11 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{ * @return {@link Response} of the operation */ private Response scheduleApplicationOperationTask(String applicationUUID, List subscribers, - SubscriptionType subType, SubAction subAction, long timestamp) { + SubscriptionType subType, SubAction subAction, long timestamp, Properties payload) { try { ScheduledAppSubscriptionTaskManager subscriptionTaskManager = new ScheduledAppSubscriptionTaskManager(); subscriptionTaskManager.scheduleAppSubscriptionTask(applicationUUID, subscribers, subType, subAction, - timestamp); + timestamp, payload); } catch (ApplicationOperationTaskException e) { String msg = "Error occurred while scheduling the application install operation"; log.error(msg, e); @@ -330,7 +342,7 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{ return Response.status(Response.Status.FORBIDDEN).entity(msg).build(); } catch (ApplicationManagementException e) { String msg = "Error occurred while getting application with the application release uuid: " - + uuid; + + uuid; log.error(msg, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); } @@ -385,17 +397,17 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{ return Response.status(Response.Status.NOT_FOUND).entity(msg).build(); } catch (BadRequestException e) { String msg = "Found invalid payload for getting application which has UUID: " + uuid - + ". Hence verify the payload"; + + ". Hence verify the payload"; log.error(msg, e); return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); } catch (ForbiddenException e) { String msg = "Application release is not in the installable state." - + "Hence you are not permitted to get the devices details."; + + "Hence you are not permitted to get the devices details."; log.error(msg, e); return Response.status(Response.Status.FORBIDDEN).entity(msg).build(); } catch (ApplicationManagementException e) { String msg = "Error occurred while getting application with the application " + - "release uuid: " + uuid; + "release uuid: " + uuid; 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/service/api/GroupManagementService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/GroupManagementService.java index fc2bfadc563..78b299346b8 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/GroupManagementService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/GroupManagementService.java @@ -15,6 +15,22 @@ * specific language governing permissions and limitations * under the License. * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.jaxrs.service.api; @@ -45,6 +61,7 @@ import org.wso2.carbon.device.mgt.jaxrs.util.Constants; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; +import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; @@ -257,6 +274,87 @@ public interface GroupManagementService { @QueryParam("requireGroupProps") boolean requireGroupProps); + + @GET + @Path("/hierarchy") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = HTTPConstants.HEADER_GET, + value = "Getting the List of Hierarchical Groups", + notes = "Returns all groups enrolled with the system hierarchically.", + tags = "Device Group Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:groups:groups") + }) + } + ) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. \n Successfully fetched the list of device hierarchical groups.", + response = DeviceGroupList.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 has been modified the last time.\n" + + "Used by caches, or in conditional requests."), + }), + @ApiResponse( + code = 304, + message = "Not Modified. \n Empty body because the client has already the latest version of " + + "the requested resource."), + @ApiResponse( + code = 406, + message = "Not Acceptable.\n The requested media type is not supported."), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Server error occurred while fetching the groups list.", + response = ErrorResponse.class) + }) + Response getGroupsWithHierarchy( + @ApiParam( + name = "name", + value = "Name of the group.") + @QueryParam("name") + String name, + @ApiParam( + name = "owner", + value = "Owner of the group.") + @QueryParam("owner") + String owner, + @ApiParam( + name = "requireGroupProps", + value = "Request group properties to include in the response", + defaultValue = "false") + @QueryParam("requireGroupProps") + boolean requireGroupProps, + @ApiParam( + name = "depth", + value = "Depth of the group hierarchy.") + @DefaultValue("3") + @QueryParam("depth") + int depth, + @ApiParam( + name = "offset", + value = "The starting pagination index for the complete list of qualified items.", + defaultValue = "0") + @DefaultValue("0") + @QueryParam("offset") + int offset, + @ApiParam( + name = "limit", + value = "Provide how many records require from the starting pagination index/offset.", + defaultValue = "5") + @DefaultValue("5") + @QueryParam("limit") + int limit); + @Path("/count") @GET @ApiOperation( @@ -426,7 +524,14 @@ public interface GroupManagementService { value = "Request group properties to include in the response", defaultValue = "false") @QueryParam("requireGroupProps") - boolean requireGroupProps); + boolean requireGroupProps, + @ApiParam( + name = "depth", + value = "Depth of the group hierarchy.", + defaultValue = "1") + @DefaultValue("1") + @QueryParam("depth") + int depth); @Path("/name/{groupName}") @GET @@ -485,7 +590,14 @@ public interface GroupManagementService { value = "Request group properties to include in the response", defaultValue = "false") @QueryParam("requireGroupProps") - boolean requireGroupProps); + boolean requireGroupProps, + @ApiParam( + name = "depth", + value = "Depth of the group hierarchy.", + defaultValue = "1") + @DefaultValue("1") + @QueryParam("depth") + int depth); @Path("/id/{groupId}") @PUT @@ -594,7 +706,12 @@ public interface GroupManagementService { name = "groupId", value = "ID of the group to be deleted.", required = true) - @PathParam("groupId") int groupId); + @PathParam("groupId") int groupId, + @ApiParam( + name = "isDeleteChildren", + value = "Is the children groups needs to be deleted.", + required = true) + @QueryParam("isDeleteChildren") boolean isDeleteChildren); @Path("/id/{groupId}/share") @POST diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/admin/GroupManagementAdminService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/admin/GroupManagementAdminService.java index e430611e90b..8fd956e9fcc 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/admin/GroupManagementAdminService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/admin/GroupManagementAdminService.java @@ -15,6 +15,22 @@ * specific language governing permissions and limitations * under the License. * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.jaxrs.service.api.admin; @@ -166,6 +182,90 @@ public interface GroupManagementAdminService { @QueryParam("requireGroupProps") boolean requireGroupProps); + @GET + @Path("hierarchy") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = HTTPConstants.HEADER_GET, + value = "Getting the List of Hierarchical Groups", + notes = "Returns all groups enrolled with the system hierarchically.", + tags = "Device Group Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:admin-groups:view") + }) + } + ) + @ApiResponses(value = { + @ApiResponse(code = 200, message = "OK. \n Successfully fetched the list of device groups hierarchically.", + response = DeviceGroupList.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 has been modified the last time.\n" + + "Used by caches, or in conditional requests."), + }), + @ApiResponse( + code = 304, + message = "Not Modified. \n Empty body because the client has already the latest version of " + + "the requested resource."), + @ApiResponse( + code = 406, + message = "Not Acceptable.\n The requested media type is not supported."), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Server error occurred while fetching the groups list.", + response = ErrorResponse.class) + }) + Response getGroupsWithHierarchy(@ApiParam( + name = "name", + value = "Name of the group.") + @QueryParam("name") + String name, + @ApiParam( + name = "owner", + value = "Owner of the group.") + @QueryParam("owner") + String owner, + @ApiParam( + name = "status", + value = "status of group to be retrieve.") + @QueryParam("status") + String status, + @ApiParam( + name = "requireGroupProps", + value = "Request group properties to include in the response", + defaultValue = "false") + @QueryParam("requireGroupProps") + boolean requireGroupProps, + @ApiParam( + name = "depth", + value = "Depth of the group hierarchy.") + @DefaultValue("3") + @QueryParam("depth") + int depth, + @ApiParam( + name = "offset", + value = "The starting pagination index for the complete list of qualified items.", + defaultValue = "0") + @DefaultValue("0") + @QueryParam("offset") + int offset, + @ApiParam( + name = "limit", + value = "Provide how many records require from the starting pagination index/offset.", + defaultValue = "5") + @DefaultValue("5") + @QueryParam("limit") + int limit); + @Path("/count") @GET @ApiOperation( 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 7648a102b8d..c0a5a4a6b4b 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 @@ -136,6 +136,7 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.ArrayList; +import java.util.Properties; @Path("/devices") public class DeviceManagementServiceImpl implements DeviceManagementService { @@ -915,7 +916,7 @@ public class DeviceManagementServiceImpl implements DeviceManagementService { if (UUID != null) { ApplicationInstallResponse response = subscriptionManager .performBulkAppOperation(UUID, deviceIdentifiers, SubscriptionType.DEVICE.toString(), - "uninstall"); + "uninstall", new Properties()); return Response.status(Response.Status.OK).entity(response).build(); //if the applications not installed via entgra store } else { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/GroupManagementServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/GroupManagementServiceImpl.java index 0502f37ab34..3fed8e75049 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/GroupManagementServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/GroupManagementServiceImpl.java @@ -15,6 +15,22 @@ * specific language governing permissions and limitations * under the License. * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.jaxrs.service.impl; @@ -36,7 +52,6 @@ import org.wso2.carbon.device.mgt.common.group.mgt.GroupAlreadyExistException; import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException; import org.wso2.carbon.device.mgt.common.group.mgt.GroupNotExistException; import org.wso2.carbon.device.mgt.common.group.mgt.RoleDoesNotExistException; -import org.wso2.carbon.device.mgt.common.policy.mgt.Policy; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.device.mgt.core.service.GroupManagementProviderService; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceGroupList; @@ -47,10 +62,12 @@ import org.wso2.carbon.device.mgt.jaxrs.service.api.GroupManagementService; import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil; import org.wso2.carbon.device.mgt.jaxrs.util.DeviceMgtAPIUtils; import org.wso2.carbon.policy.mgt.common.PolicyAdministratorPoint; -import org.wso2.carbon.policy.mgt.common.PolicyEvaluationException; -import org.wso2.carbon.policy.mgt.common.PolicyEvaluationPoint; import org.wso2.carbon.policy.mgt.common.PolicyManagementException; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import java.util.ArrayList; import java.util.List; @@ -89,11 +106,41 @@ public class GroupManagementServiceImpl implements GroupManagementService { } } + @GET + @Path("/hierarchy") + @Override + public Response getGroupsWithHierarchy( + @QueryParam("name") String name, + @QueryParam("owner") String owner, + @QueryParam("requireGroupProps") boolean requireGroupProps, + @DefaultValue("3") @QueryParam("depth") int depth, + @DefaultValue("0") @QueryParam("offset") int offset, + @DefaultValue("5") @QueryParam("limit") int limit) { + try { + RequestValidationUtil.validatePaginationParameters(offset, limit); + String currentUser = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + GroupPaginationRequest request = new GroupPaginationRequest(offset, limit); + request.setGroupName(name); + request.setOwner(owner); + request.setDepth(depth); + PaginationResult deviceGroupsResult = DeviceMgtAPIUtils.getGroupManagementProviderService() + .getGroupsWithHierarchy(currentUser, request, requireGroupProps); + DeviceGroupList deviceGroupList = new DeviceGroupList(); + deviceGroupList.setList(deviceGroupsResult.getData()); + deviceGroupList.setCount(deviceGroupsResult.getRecordsTotal()); + return Response.status(Response.Status.OK).entity(deviceGroupList).build(); + } catch (GroupManagementException e) { + String error = "Error occurred while retrieving groups with hierarchy."; + log.error(error, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(error).build(); + } + } + @Override public Response getGroupCount() { try { String currentUser = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); - int count = DeviceMgtAPIUtils.getGroupManagementProviderService().getGroupCount(currentUser); + int count = DeviceMgtAPIUtils.getGroupManagementProviderService().getGroupCount(currentUser, null); return Response.status(Response.Status.OK).entity(count).build(); } catch (GroupManagementException e) { String msg = "Error occurred while retrieving group count."; @@ -125,10 +172,10 @@ public class GroupManagementServiceImpl implements GroupManagementService { } @Override - public Response getGroup(int groupId, boolean requireGroupProps) { + public Response getGroup(int groupId, boolean requireGroupProps, int depth) { try { GroupManagementProviderService service = DeviceMgtAPIUtils.getGroupManagementProviderService(); - DeviceGroup deviceGroup = service.getGroup(groupId, requireGroupProps); + DeviceGroup deviceGroup = service.getGroup(groupId, requireGroupProps, depth); if (deviceGroup != null) { return Response.status(Response.Status.OK).entity(deviceGroup).build(); } else { @@ -142,7 +189,7 @@ public class GroupManagementServiceImpl implements GroupManagementService { } @Override - public Response getGroup(String groupName, boolean requireGroupProps) { + public Response getGroup(String groupName, boolean requireGroupProps, int depth) { try { GroupManagementProviderService service = DeviceMgtAPIUtils.getGroupManagementProviderService(); DeviceGroup deviceGroup = service.getGroup(groupName, requireGroupProps); @@ -178,9 +225,9 @@ public class GroupManagementServiceImpl implements GroupManagementService { } @Override - public Response deleteGroup(int groupId) { + public Response deleteGroup(int groupId, boolean isDeleteChildren) { try { - if (DeviceMgtAPIUtils.getGroupManagementProviderService().deleteGroup(groupId)) { + if (DeviceMgtAPIUtils.getGroupManagementProviderService().deleteGroup(groupId, isDeleteChildren)) { return Response.status(Response.Status.OK).build(); } else { return Response.status(Response.Status.NOT_FOUND).entity("Group not found.").build(); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/admin/GroupManagementAdminServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/admin/GroupManagementAdminServiceImpl.java index b742a1d8e19..a461b71b500 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/admin/GroupManagementAdminServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/admin/GroupManagementAdminServiceImpl.java @@ -15,6 +15,22 @@ * specific language governing permissions and limitations * under the License. * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.jaxrs.service.impl.admin; @@ -31,6 +47,10 @@ import org.wso2.carbon.device.mgt.jaxrs.service.api.admin.GroupManagementAdminSe import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil; import org.wso2.carbon.device.mgt.jaxrs.util.DeviceMgtAPIUtils; +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import java.util.ArrayList; @@ -74,6 +94,37 @@ public class GroupManagementAdminServiceImpl implements GroupManagementAdminServ } } + @GET + @Path("/hierarchy") + @Override + public Response getGroupsWithHierarchy( + @QueryParam("name") String name, + @QueryParam("owner") String owner, + @QueryParam("status") String status, + @QueryParam("requireGroupProps") boolean requireGroupProps, + @DefaultValue("3") @QueryParam("depth") int depth, + @DefaultValue("0") @QueryParam("offset") int offset, + @DefaultValue("5") @QueryParam("limit") int limit) { + try { + RequestValidationUtil.validatePaginationParameters(offset, limit); + GroupPaginationRequest request = new GroupPaginationRequest(offset, limit); + request.setGroupName(name); + request.setOwner(owner); + request.setStatus(status); + request.setDepth(depth); + PaginationResult deviceGroupsResult = DeviceMgtAPIUtils.getGroupManagementProviderService() + .getGroupsWithHierarchy(null, request, requireGroupProps); + DeviceGroupList deviceGroupList = new DeviceGroupList(); + deviceGroupList.setList(deviceGroupsResult.getData()); + deviceGroupList.setCount(deviceGroupsResult.getRecordsTotal()); + return Response.status(Response.Status.OK).entity(deviceGroupList).build(); + } catch (GroupManagementException e) { + String error = "Error occurred while retrieving groups with hierarchy."; + log.error(error, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(error).build(); + } + } + @Override public Response getGroupCount(String status) { try { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/test/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/GroupManagementServiceImplTest.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/test/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/GroupManagementServiceImplTest.java index 20136b172d7..2d430c09f6d 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/test/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/GroupManagementServiceImplTest.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/test/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/GroupManagementServiceImplTest.java @@ -15,11 +15,26 @@ * specific language governing permissions and limitations * under the License. * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.jaxrs.service.impl; -import org.mockito.Mock; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; @@ -130,13 +145,14 @@ public class GroupManagementServiceImplTest { .toReturn(groupManagementProviderService); PowerMockito.stub(PowerMockito.method(PrivilegedCarbonContext.class, "getThreadLocalCarbonContext")) .toReturn(context); - Mockito.doReturn(2).when(groupManagementProviderService).getGroupCount(Mockito.anyString()); + Mockito.doReturn(2).when(groupManagementProviderService) + .getGroupCount(Mockito.anyString(), Mockito.anyString()); Response response = groupManagementService.getGroupCount(); Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), "GetGroupCount request failed with valid parameters"); Mockito.reset(groupManagementProviderService); Mockito.doThrow(new GroupManagementException()).when(groupManagementProviderService) - .getGroupCount(Mockito.anyString()); + .getGroupCount(Mockito.anyString(), Mockito.anyString()); response = groupManagementService.getGroupCount(); Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), "GetGroupCount request succeeded with in-valid parameters"); @@ -174,16 +190,16 @@ public class GroupManagementServiceImplTest { public void testGetGroup() throws GroupManagementException { PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getGroupManagementProviderService")) .toReturn(groupManagementProviderService); - Mockito.doReturn(new DeviceGroup()).when(groupManagementProviderService).getGroup(1, false); - Mockito.doReturn(null).when(groupManagementProviderService).getGroup(2, false); - Mockito.doThrow(new GroupManagementException()).when(groupManagementProviderService).getGroup(3, false); - Response response = groupManagementService.getGroup(1, false); + Mockito.doReturn(new DeviceGroup()).when(groupManagementProviderService).getGroup(1, false, 1); + Mockito.doReturn(null).when(groupManagementProviderService).getGroup(2, false, 1); + Mockito.doThrow(new GroupManagementException()).when(groupManagementProviderService).getGroup(3, false, 1); + Response response = groupManagementService.getGroup(1, false, 1); Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), "getGroup request failed for a request with valid parameters"); - response = groupManagementService.getGroup(2, false); + response = groupManagementService.getGroup(2, false, 1); Assert.assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode(), "getGroup request returned a group for a non-existing group"); - response = groupManagementService.getGroup(3, false); + response = groupManagementService.getGroup(3, false, 1); Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), "getGroup request returned a group for a in-valid request"); } @@ -216,16 +232,16 @@ public class GroupManagementServiceImplTest { public void testDeleteGroup() throws GroupManagementException { PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getGroupManagementProviderService")) .toReturn(groupManagementProviderService); - Mockito.doReturn(true).when(groupManagementProviderService).deleteGroup(1); - Mockito.doReturn(false).when(groupManagementProviderService).deleteGroup(2); - Mockito.doThrow(new GroupManagementException()).when(groupManagementProviderService).deleteGroup(3); - Response response = groupManagementService.deleteGroup(1); + Mockito.doReturn(true).when(groupManagementProviderService).deleteGroup(1, false); + Mockito.doReturn(false).when(groupManagementProviderService).deleteGroup(2, false); + Mockito.doThrow(new GroupManagementException()).when(groupManagementProviderService).deleteGroup(3, false); + Response response = groupManagementService.deleteGroup(1, false); Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(), "delete group request failed for a request with valid parameters"); - response = groupManagementService.deleteGroup(2); + response = groupManagementService.deleteGroup(2, false); Assert.assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode(), "Non-existing group was successfully deleted"); - response = groupManagementService.deleteGroup(3); + response = groupManagementService.deleteGroup(3, false); Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), "Deletion succeeded with an erroneous condition."); } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/GroupPaginationRequest.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/GroupPaginationRequest.java index d7a74f199b1..9402d2ab374 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/GroupPaginationRequest.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/GroupPaginationRequest.java @@ -14,6 +14,23 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.common; @@ -28,6 +45,8 @@ public class GroupPaginationRequest { private String owner; private String groupName; private String status; + private String parentPath; + private int depth; public GroupPaginationRequest(int start, int rowCount) { this.startIndex = start; @@ -74,6 +93,22 @@ public class GroupPaginationRequest { this.groupName = groupName; } + public String getParentPath() { + return parentPath; + } + + public void setParentPath(String parentPath) { + this.parentPath = parentPath; + } + + public int getDepth() { + return depth; + } + + public void setDepth(int depth) { + this.depth = depth; + } + @Override public String toString() { return "Group Name '" + this.groupName + "' num of rows: " + this.rowCount + " start index: " + this.startIndex diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/MDMAppConstants.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/MDMAppConstants.java index e7f8f17df7d..40b647d6eab 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/MDMAppConstants.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/MDMAppConstants.java @@ -45,6 +45,7 @@ public class MDMAppConstants { private AndroidConstants() { throw new AssertionError(); } + public static final String IS_BLOCK_UNINSTALL = "isBlockUninstall"; public static final String OPCODE_INSTALL_APPLICATION = "INSTALL_APPLICATION"; public static final String OPCODE_UNINSTALL_APPLICATION = "UNINSTALL_APPLICATION"; public static final String UNMANAGED_APP_UNINSTALL= "UNMANAGED_APP_UNINSTALL"; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/AppStoreApplication.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/AppStoreApplication.java index 50126814ca3..71464070b72 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/AppStoreApplication.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/AppStoreApplication.java @@ -21,6 +21,7 @@ package org.wso2.carbon.device.mgt.common.app.mgt.android; import com.google.gson.Gson; import java.io.Serializable; +import java.util.Properties; /** * This class represents the Appstore AuthenticationImpl information. @@ -29,6 +30,7 @@ public class AppStoreApplication implements Serializable { private String type; private String appIdentifier; + private Properties properties; public String getType() { return type; @@ -51,4 +53,11 @@ public class AppStoreApplication implements Serializable { return gson.toJson(this); } + public Properties getProperties() { + return properties; + } + + public void setProperties(Properties properties) { + this.properties = properties; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/EnterpriseApplication.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/EnterpriseApplication.java index f087013a186..0e9bdb47da6 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/EnterpriseApplication.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/EnterpriseApplication.java @@ -21,6 +21,7 @@ package org.wso2.carbon.device.mgt.common.app.mgt.android; import com.google.gson.Gson; import java.io.Serializable; +import java.util.Properties; /** * This class represents the Enterprise AuthenticationImpl information. @@ -30,6 +31,7 @@ public class EnterpriseApplication implements Serializable { private String type; private String url; private String appIdentifier; + private Properties properties; public String getAppIdentifier() { return appIdentifier; @@ -60,4 +62,11 @@ public class EnterpriseApplication implements Serializable { return gson.toJson(this); } + public Properties getProperties() { + return properties; + } + + public void setProperties(Properties properties) { + this.properties = properties; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/WebApplication.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/WebApplication.java index 32574292694..70797dc8d97 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/WebApplication.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/WebApplication.java @@ -21,6 +21,7 @@ package org.wso2.carbon.device.mgt.common.app.mgt.android; import com.google.gson.Gson; import java.io.Serializable; +import java.util.Properties; /** * This class represents the Web AuthenticationImpl information. @@ -30,6 +31,7 @@ public class WebApplication implements Serializable { private String name; private String url; private String type; + private Properties properties; public String getName() { return name; @@ -60,4 +62,11 @@ public class WebApplication implements Serializable { return gson.toJson(this); } + public Properties getProperties() { + return properties; + } + + public void setProperties(Properties properties) { + this.properties = properties; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/group/mgt/DeviceGroup.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/group/mgt/DeviceGroup.java index 20ed01fb492..a3511033d54 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/group/mgt/DeviceGroup.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/group/mgt/DeviceGroup.java @@ -14,6 +14,23 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.common.group.mgt; @@ -21,6 +38,7 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.Serializable; +import java.util.List; import java.util.Map; /** @@ -47,6 +65,15 @@ public class DeviceGroup implements Serializable { @ApiModelProperty(name = "status", value = "The status of group that needs updating/retrieval.") private String status; + @ApiModelProperty(name = "parentGroupId", value = "Group ID of parent group") + private int parentGroupId; + + @ApiModelProperty(name = "parentPath", value = "Path of parent group") + private String parentPath; + + @ApiModelProperty(name = "childrenGroups", value = "Children groups") + private List childrenGroups; + public String getStatus() { return status; } @@ -103,4 +130,27 @@ public class DeviceGroup implements Serializable { this.groupProperties = groupProperties; } + public int getParentGroupId() { + return parentGroupId; + } + + public void setParentGroupId(int parentGroupId) { + this.parentGroupId = parentGroupId; + } + + public String getParentPath() { + return parentPath; + } + + public void setParentPath(String parentPath) { + this.parentPath = parentPath; + } + + public List getChildrenGroups() { + return childrenGroups; + } + + public void setChildrenGroups(List childrenGroups) { + this.childrenGroups = childrenGroups; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/group/mgt/DeviceGroupConstants.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/group/mgt/DeviceGroupConstants.java index 039219bfe88..b1a185d98f2 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/group/mgt/DeviceGroupConstants.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/group/mgt/DeviceGroupConstants.java @@ -84,4 +84,8 @@ public class DeviceGroupConstants { public static final String[] DEFAULT_VIEW_EVENTS_PERMISSIONS = {"/permission/device-mgt/user/groups/device_events"}; } + + public static final class HierarchicalGroup { + public static final String SEPERATOR = "/"; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/ui/UIConfiguration.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/ui/UIConfiguration.java index 1c4a1e91cdf..f494007330e 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/ui/UIConfiguration.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/ui/UIConfiguration.java @@ -32,6 +32,7 @@ public class UIConfiguration { private List scopes; private boolean isSsoEnable; private int sessionTimeOut; + private int loginCacheCapacity; @XmlElement(name = "AppRegistration", required=true) public AppRegistration getAppRegistration() { @@ -69,4 +70,13 @@ public class UIConfiguration { public void setSessionTimeOut(int sessionTimeOut) { this.sessionTimeOut = sessionTimeOut; } + + @XmlElement(name = "LoginCacheCapacity") + public int getLoginCacheCapacity() { + return loginCacheCapacity; + } + + public void setLoginCacheCapacity(int loginCacheCapacity) { + this.loginCacheCapacity = loginCacheCapacity; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/GroupDAO.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/GroupDAO.java index 79b536feaa5..9ba4e36283c 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/GroupDAO.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/GroupDAO.java @@ -14,6 +14,23 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.core.dao; @@ -74,6 +91,15 @@ public interface GroupDAO { */ void deleteAllGroupProperties(int groupId, int tenantId) throws GroupManagementDAOException; + /** + * Remove properties of device groups. + * + * @param groupIds to be deleted. + * @param tenantId of the group. + * @throws GroupManagementDAOException on error during deletion of group properties of groups + */ + void deleteAllGroupsProperties(List groupIds, int tenantId) throws GroupManagementDAOException; + /** * Retrives all properties stored against a group. * @@ -95,6 +121,15 @@ public interface GroupDAO { void updateGroup(DeviceGroup deviceGroup, int groupId, int tenantId) throws GroupManagementDAOException; + /** + * Update existing Device Groups. + * + * @param deviceGroups groups to update. + * @param tenantId of the group. + * @throws GroupManagementDAOException on error during updating of groups + */ + void updateGroups(List deviceGroups, int tenantId) throws GroupManagementDAOException; + /** * Delete an existing Device Group. * @@ -104,6 +139,24 @@ public interface GroupDAO { */ void deleteGroup(int groupId, int tenantId) throws GroupManagementDAOException; + /** + * Delete mappings of Device Groups. + * + * @param groupIds of Device Groups. + * @param tenantId of the group. + * @throws GroupManagementDAOException on error during deletion of mappings of groups + */ + void deleteGroupsMapping(List groupIds, int tenantId) throws GroupManagementDAOException; + + /** + * Delete existing Device Groups. + * + * @param groupIds of Device Groups. + * @param tenantId of the group. + * @throws GroupManagementDAOException on error during deletion of groups + */ + void deleteGroups(List groupIds, int tenantId) throws GroupManagementDAOException; + /** * Get device group by id. * @@ -114,6 +167,25 @@ public interface GroupDAO { */ DeviceGroup getGroup(int groupId, int tenantId) throws GroupManagementDAOException; + /** + * Get children groups by parent path. + * + * @param parentPath of parent group. + * @param tenantId of the group. + * @return {@link List} list of children device groups + * @throws GroupManagementDAOException on error during retrieval of children groups + */ + List getChildrenGroups(String parentPath, int tenantId) throws GroupManagementDAOException; + + /** + * Get root groups. + * + * @param tenantId of the group. + * @return {@link List} list of root device groups + * @throws GroupManagementDAOException on error during retrieval of root groups + */ + List getRootGroups(int tenantId) throws GroupManagementDAOException; + /** * Get the groups of device with device id provided * @param deviceId @@ -306,10 +378,11 @@ public interface GroupDAO { * * @param roles of the group. * @param tenantId of user's tenant. + * @param parentPath of the group. * @return count of device groups. * @throws GroupManagementDAOException */ - int getGroupsCount(String[] roles, int tenantId) throws GroupManagementDAOException; + int getGroupsCount(String[] roles, int tenantId, String parentPath) throws GroupManagementDAOException; /** * Get all device groups which owned by user. @@ -336,10 +409,11 @@ public interface GroupDAO { * * @param username of the owner. * @param tenantId of user's tenant. + * @param parentPath of the group. * @return count of device groups. * @throws GroupManagementDAOException */ - int getOwnGroupsCount(String username, int tenantId) throws GroupManagementDAOException; + int getOwnGroupsCount(String username, int tenantId, String parentPath) throws GroupManagementDAOException; /** * Get device Ids of devices which are assigned to groups. diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/AbstractGroupDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/AbstractGroupDAOImpl.java index 04d4bce5847..c3426e3521a 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/AbstractGroupDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/AbstractGroupDAOImpl.java @@ -14,18 +14,34 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.core.dao.impl; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.solr.common.StringUtils; import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.GroupPaginationRequest; import org.wso2.carbon.device.mgt.common.PaginationRequest; import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup; -import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.GroupDAO; import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOFactory; @@ -49,6 +65,127 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { private static final Log log = LogFactory.getLog(AbstractGroupDAOImpl.class); + @Override + public List getGroups(GroupPaginationRequest request, int tenantId) + throws GroupManagementDAOException { + try { + Connection conn = GroupManagementDAOFactory.getConnection(); + String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP " + + "WHERE TENANT_ID = ?"; + if (StringUtils.isNotBlank(request.getGroupName())) { + sql += " AND UPPER(GROUP_NAME) LIKE ?"; + } + if (StringUtils.isNotBlank(request.getOwner())) { + sql += " AND UPPER(OWNER) LIKE ?"; + } + if (StringUtils.isNotBlank(request.getStatus())) { + sql += " AND STATUS = ?"; + } + if (StringUtils.isNotBlank(request.getParentPath())) { + sql += " AND PARENT_PATH LIKE ?"; + } + if (request.getRowCount() != 0) { + sql += " LIMIT ? OFFSET ?"; + } + + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + int paramIndex = 1; + stmt.setInt(paramIndex++, tenantId); + if (StringUtils.isNotBlank(request.getGroupName())) { + stmt.setString(paramIndex++, request.getGroupName() + "%"); + } + if (StringUtils.isNotBlank(request.getOwner())) { + stmt.setString(paramIndex++, request.getOwner() + "%"); + } + if (StringUtils.isNotBlank(request.getStatus())) { + stmt.setString(paramIndex++, request.getStatus().toUpperCase()); + } + if (StringUtils.isNotBlank(request.getParentPath())) { + stmt.setString(paramIndex++, request.getParentPath()); + } + if (request.getRowCount() != 0) { + stmt.setInt(paramIndex++, request.getRowCount()); + stmt.setInt(paramIndex, request.getStartIndex()); + } + List deviceGroupList = new ArrayList<>(); + try (ResultSet resultSet = stmt.executeQuery()) { + while (resultSet.next()) { + deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet)); + } + } + return deviceGroupList; + } + } catch (SQLException e) { + String msg = "Error occurred while retrieving groups in tenant: " + tenantId; + log.error(msg); + throw new GroupManagementDAOException(msg, e); + } + } + + @Override + public List getGroups(GroupPaginationRequest request, List deviceGroupIds, + int tenantId) throws GroupManagementDAOException { + int deviceGroupIdsCount = deviceGroupIds.size(); + if (deviceGroupIdsCount == 0) { + return new ArrayList<>(); + } + + try { + Connection conn = GroupManagementDAOFactory.getConnection(); + String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP WHERE TENANT_ID = ?"; + if (StringUtils.isNotBlank(request.getGroupName())) { + sql += " AND GROUP_NAME LIKE ?"; + } + if (StringUtils.isNotBlank(request.getOwner())) { + sql += " AND OWNER LIKE ?"; + } + if (StringUtils.isNotBlank(request.getParentPath())) { + sql += " AND PARENT_PATH LIKE ?"; + } + sql += " AND ID IN ("; + for (int i = 0; i < deviceGroupIdsCount; i++) { + sql += (deviceGroupIdsCount - 1 != i) ? "?," : "?"; + } + sql += ")"; + if (request.getRowCount() != 0) { + sql += " LIMIT ? OFFSET ?"; + } + + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + int paramIndex = 1; + stmt.setInt(paramIndex++, tenantId); + if (StringUtils.isNotBlank(request.getGroupName())) { + stmt.setString(paramIndex++, request.getGroupName() + "%"); + } + if (StringUtils.isNotBlank(request.getOwner())) { + stmt.setString(paramIndex++, request.getOwner() + "%"); + } + if (StringUtils.isNotBlank(request.getParentPath())) { + stmt.setString(paramIndex++, request.getParentPath()); + } + for (Integer deviceGroupId : deviceGroupIds) { + stmt.setInt(paramIndex++, deviceGroupId); + } + if (request.getRowCount() != 0) { + stmt.setInt(paramIndex++, request.getRowCount()); + stmt.setInt(paramIndex, request.getStartIndex()); + } + List deviceGroupList = new ArrayList<>(); + try (ResultSet resultSet = stmt.executeQuery()) { + while (resultSet.next()) { + deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet)); + } + } + return deviceGroupList; + } + } catch (SQLException e) { + String msg = "Error occurred while retrieving groups of groups IDs " + deviceGroupIds.toString() + + " in tenant: " + tenantId; + log.error(msg); + throw new GroupManagementDAOException(msg, e); + } + } + @Override public int addGroup(DeviceGroup deviceGroup, int tenantId) throws GroupManagementDAOException { PreparedStatement stmt = null; @@ -59,9 +196,11 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { Connection conn = GroupManagementDAOFactory.getConnection(); String sql; if (deviceGroup.getStatus() == null || deviceGroup.getStatus().isEmpty()) { - sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID) VALUES (?, ?, ?, ?)"; + sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID, PARENT_PATH) " + + "VALUES (?, ?, ?, ?, ?)"; } else { - sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID, STATUS) VALUES (?, ?, ?, ?, ?)"; + sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID, PARENT_PATH, STATUS) " + + "VALUES (?, ?, ?, ?, ?, ?)"; hasStatus = true; } stmt = conn.prepareStatement(sql, new String[]{"ID"}); @@ -69,8 +208,9 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { stmt.setString(2, deviceGroup.getName()); stmt.setString(3, deviceGroup.getOwner()); stmt.setInt(4, tenantId); + stmt.setString(5, deviceGroup.getParentPath()); if (hasStatus) { - stmt.setString(5, deviceGroup.getStatus()); + stmt.setString(6, deviceGroup.getStatus()); } stmt.executeUpdate(); @@ -151,10 +291,12 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { try { Connection conn = GroupManagementDAOFactory.getConnection(); String sql = - "UPDATE DM_GROUP SET DESCRIPTION = ?, GROUP_NAME = ?, OWNER = ? WHERE ID = ? AND TENANT_ID = ?"; + "UPDATE DM_GROUP SET DESCRIPTION = ?, GROUP_NAME = ?, OWNER = ?, PARENT_PATH = ? WHERE ID = ? " + + "AND TENANT_ID = ?"; if (deviceGroup.getStatus() != null && !deviceGroup.getStatus().isEmpty()) { - sql = "UPDATE DM_GROUP SET DESCRIPTION = ?, GROUP_NAME = ?, OWNER = ?, STATUS = ? WHERE ID = ? AND TENANT_ID = ?"; + sql = "UPDATE DM_GROUP SET DESCRIPTION = ?, GROUP_NAME = ?, OWNER = ?, PARENT_PATH = ?, STATUS = ? " + + "WHERE ID = ? AND TENANT_ID = ?"; hasStatus = true; } stmt = conn.prepareStatement(sql); @@ -162,11 +304,12 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { stmt.setString(paramIndex++, deviceGroup.getDescription()); stmt.setString(paramIndex++, deviceGroup.getName()); stmt.setString(paramIndex++, deviceGroup.getOwner()); + stmt.setString(paramIndex++, deviceGroup.getParentPath()); if (hasStatus) { stmt.setString(paramIndex++, deviceGroup.getStatus()); } stmt.setInt(paramIndex++, groupId); - stmt.setInt(paramIndex++, tenantId); + stmt.setInt(paramIndex, tenantId); stmt.executeUpdate(); } catch (SQLException e) { throw new GroupManagementDAOException("Error occurred while updating deviceGroup '" + @@ -176,6 +319,32 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { } } + @Override + public void updateGroups(List deviceGroups, int tenantId) throws GroupManagementDAOException { + try { + Connection conn = GroupManagementDAOFactory.getConnection(); + String sql = "UPDATE DM_GROUP SET DESCRIPTION = ?, GROUP_NAME = ?, OWNER = ?, STATUS = ?, " + + "PARENT_PATH = ? WHERE ID = ? AND TENANT_ID = ?"; + try (PreparedStatement stmt = conn.prepareStatement(sql)){ + for (DeviceGroup deviceGroup : deviceGroups) { + stmt.setString(1, deviceGroup.getDescription()); + stmt.setString(2, deviceGroup.getName()); + stmt.setString(3, deviceGroup.getOwner()); + stmt.setString(4, deviceGroup.getStatus()); + stmt.setString(5, deviceGroup.getParentPath()); + stmt.setInt(6, deviceGroup.getGroupId()); + stmt.setInt(7, tenantId); + stmt.addBatch(); + } + stmt.executeBatch(); + } + } catch (SQLException e) { + String msg = "Error occurred while updating groups as batch"; + log.error(msg); + throw new GroupManagementDAOException(msg, e); + } + } + @Override public void deleteGroup(int groupId, int tenantId) throws GroupManagementDAOException { Connection conn; @@ -217,6 +386,64 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { } } + @Override + public void deleteGroupsMapping(List groupIds, int tenantId) throws GroupManagementDAOException { + try { + Connection conn = GroupManagementDAOFactory.getConnection(); + String sql = "DELETE FROM DM_ROLE_GROUP_MAP WHERE GROUP_ID = ? AND TENANT_ID = ?"; + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + for (int groupId : groupIds) { + stmt.setInt(1, groupId); + stmt.setInt(2, tenantId); + stmt.addBatch(); + } + stmt.executeBatch(); + } + sql = "DELETE FROM DM_DEVICE_GROUP_MAP WHERE GROUP_ID = ? AND TENANT_ID = ?"; + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + for (int groupId : groupIds) { + stmt.setInt(1, groupId); + stmt.setInt(2, tenantId); + stmt.addBatch(); + } + stmt.executeBatch(); + } + sql = "DELETE FROM DM_DEVICE_GROUP_POLICY WHERE DEVICE_GROUP_ID = ? AND TENANT_ID = ?"; + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + for (int groupId : groupIds) { + stmt.setInt(1, groupId); + stmt.setInt(2, tenantId); + stmt.addBatch(); + } + stmt.executeBatch(); + } + } catch (SQLException e) { + String msg = "Error occurred while removing mappings of groups as batches"; + log.error(msg); + throw new GroupManagementDAOException(msg, e); + } + } + + @Override + public void deleteGroups(List groupIds, int tenantId) throws GroupManagementDAOException { + try { + Connection conn = GroupManagementDAOFactory.getConnection(); + String sql = "DELETE FROM DM_GROUP WHERE ID = ? AND TENANT_ID = ?"; + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + for (int groupId : groupIds) { + stmt.setInt(1, groupId); + stmt.setInt(2, tenantId); + stmt.addBatch(); + } + stmt.executeBatch(); + } + } catch (SQLException e) { + String msg = "Error occurred while deleting groups as batches"; + log.error(msg); + throw new GroupManagementDAOException(msg, e); + } + } + public void deleteAllGroupProperties(int groupId, int tenantId) throws GroupManagementDAOException { PreparedStatement stmt = null; @@ -235,6 +462,25 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { } } + public void deleteAllGroupsProperties(List groupIds, int tenantId) throws GroupManagementDAOException { + try { + Connection conn = GroupManagementDAOFactory.getConnection(); + String sql = "DELETE FROM GROUP_PROPERTIES WHERE GROUP_ID = ? AND TENANT_ID = ?"; + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + for (Integer groupId : groupIds) { + stmt.setInt(1, groupId); + stmt.setInt(2, tenantId); + stmt.addBatch(); + } + stmt.executeUpdate(); + } + } catch (SQLException e) { + String msg = "Error occurred while deleting properties of groups as batches"; + log.error(msg); + throw new GroupManagementDAOException(msg, e); + } + } + public Map getAllGroupProperties(int groupId, int tenantId) throws GroupManagementDAOException { PreparedStatement stmt = null; @@ -266,7 +512,8 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { ResultSet resultSet = null; try { Connection conn = GroupManagementDAOFactory.getConnection(); - String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE ID = ? AND TENANT_ID = ?"; + String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP WHERE ID = ? " + + "AND TENANT_ID = ?"; stmt = conn.prepareStatement(sql); stmt.setInt(1, groupId); stmt.setInt(2, tenantId); @@ -284,6 +531,56 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { } } + @Override + public List getChildrenGroups(String parentPath, int tenantId) throws GroupManagementDAOException { + try { + Connection conn = GroupManagementDAOFactory.getConnection(); + String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP " + + "WHERE PARENT_PATH LIKE ? AND TENANT_ID = ?"; + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, parentPath + "%"); + stmt.setInt(2, tenantId); + List deviceGroupList = new ArrayList<>(); + try (ResultSet resultSet = stmt.executeQuery()) { + while (resultSet.next()) { + deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet)); + } + } + return deviceGroupList; + } + } catch (SQLException e) { + String msg = "Error occurred while retrieving children group having parent path '" + parentPath + + "' in tenant: " + tenantId; + log.error(msg); + throw new GroupManagementDAOException(msg, e); + } + } + + @Override + public List getRootGroups(int tenantId) throws GroupManagementDAOException { + try { + Connection conn = GroupManagementDAOFactory.getConnection(); + String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP " + + "WHERE PARENT_PATH LIKE ? AND TENANT_ID = ?"; + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, "/"); + stmt.setInt(2, tenantId); + List deviceGroupList = new ArrayList<>(); + try (ResultSet resultSet = stmt.executeQuery()) { + deviceGroupList = new ArrayList<>(); + while (resultSet.next()) { + deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet)); + } + } + return deviceGroupList; + } + } catch (SQLException e) { + String msg = "Error occurred while retrieving root groups in tenant: " + tenantId; + log.error(msg); + throw new GroupManagementDAOException(msg, e); + } + } + @Override public List getGroups(int deviceId, int tenantId) throws GroupManagementDAOException { PreparedStatement stmt = null; @@ -291,7 +588,7 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { List deviceGroupBuilders = new ArrayList<>(); try { Connection conn = GroupManagementDAOFactory.getConnection(); - String sql = "SELECT G.ID, G.GROUP_NAME, G.DESCRIPTION, G.OWNER, G.STATUS FROM DM_GROUP G " + + String sql = "SELECT G.ID, G.GROUP_NAME, G.DESCRIPTION, G.OWNER, G.STATUS, G.PARENT_PATH FROM DM_GROUP G " + "INNER JOIN DM_DEVICE_GROUP_MAP GM ON G.ID = GM.GROUP_ID " + "WHERE GM.DEVICE_ID = ? AND GM.TENANT_ID = ?"; stmt = conn.prepareStatement(sql); @@ -316,7 +613,8 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { List deviceGroupList = null; try { Connection conn = GroupManagementDAOFactory.getConnection(); - String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?"; + String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP " + + "WHERE TENANT_ID = ?"; stmt = conn.prepareStatement(sql); stmt.setInt(1, tenantId); resultSet = stmt.executeQuery(); @@ -389,6 +687,9 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { sql += " AND STATUS = ?"; hasStatus = true; } + if (StringUtils.isNotBlank(request.getParentPath())) { + sql += " AND PARENT_PATH = ?"; + } int paramIndex = 1; stmt = conn.prepareStatement(sql); @@ -397,10 +698,13 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { stmt.setString(paramIndex++, groupName + "%"); } if (hasOwner) { - stmt.setString(paramIndex, owner + "%"); + stmt.setString(paramIndex++, owner + "%"); } if (hasStatus) { - stmt.setString(paramIndex, request.getStatus()); + stmt.setString(paramIndex++, request.getStatus()); + } + if (StringUtils.isNotBlank(request.getParentPath())) { + stmt.setString(paramIndex, request.getParentPath()); } resultSet = stmt.executeQuery(); if (resultSet.next()) { @@ -422,7 +726,8 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { try { Connection conn = GroupManagementDAOFactory.getConnection(); String sql = - "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE GROUP_NAME = ? AND TENANT_ID = ?"; + "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP " + + "WHERE GROUP_NAME = ? AND TENANT_ID = ?"; stmt = conn.prepareStatement(sql); stmt.setString(1, groupName); stmt.setInt(2, tenantId); @@ -592,7 +897,7 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { List deviceGroupList = null; try { Connection conn = GroupManagementDAOFactory.getConnection(); - String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP g, " + + String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP g, " + "(SELECT GROUP_ID FROM DM_ROLE_GROUP_MAP WHERE ROLE IN ("; int index = 0; @@ -658,7 +963,7 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { } @Override - public int getGroupsCount(String[] roles, int tenantId) throws GroupManagementDAOException { + public int getGroupsCount(String[] roles, int tenantId, String parentPath) throws GroupManagementDAOException { int rolesCount = roles.length; if (rolesCount == 0) { return 0; @@ -672,14 +977,20 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { for (int i = 0; i < rolesCount; i++) { sql += (rolesCount - 1 != i) ? "?," : "?"; } - sql += ")) gr WHERE g.ID = gr.GROUP_ID AND TENANT_ID = ? GROUP BY g.ID"; - + sql += ")) gr WHERE g.ID = gr.GROUP_ID AND TENANT_ID = ? "; + if (StringUtils.isNotBlank(parentPath)) { + sql += " AND g.PARENT_PATH = ? "; + } + sql += "GROUP BY g.ID"; stmt = conn.prepareStatement(sql); int index = 0; while (index++ < rolesCount) { stmt.setString(index, roles[index - 1]); } - stmt.setInt(index, tenantId); + stmt.setInt(index++, tenantId); + if (StringUtils.isNotBlank(parentPath)) { + stmt.setString(index, parentPath); + } resultSet = stmt.executeQuery(); if (resultSet.next()) { return resultSet.getInt("GROUP_COUNT"); @@ -700,7 +1011,8 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { List deviceGroupList = null; try { Connection conn = GroupManagementDAOFactory.getConnection(); - String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE OWNER = ? AND TENANT_ID = ?"; + String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP " + + "WHERE OWNER = ? AND TENANT_ID = ?"; stmt = conn.prepareStatement(sql); stmt.setString(1, username); stmt.setInt(2, tenantId); @@ -744,15 +1056,21 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO { } @Override - public int getOwnGroupsCount(String username, int tenantId) throws GroupManagementDAOException { + public int getOwnGroupsCount(String username, int tenantId, String parentPath) throws GroupManagementDAOException { PreparedStatement stmt = null; ResultSet resultSet = null; try { Connection conn = GroupManagementDAOFactory.getConnection(); String sql = "SELECT COUNT(ID) AS GROUP_COUNT FROM DM_GROUP WHERE OWNER = ? AND TENANT_ID = ?"; + if (StringUtils.isNotBlank(parentPath)) { + sql += " AND PARENT_PATH = ?"; + } stmt = conn.prepareStatement(sql); stmt.setString(1, username); stmt.setInt(2, tenantId); + if (StringUtils.isNotBlank(parentPath)) { + stmt.setString(3, parentPath); + } resultSet = stmt.executeQuery(); if (resultSet.next()) { return resultSet.getInt("GROUP_COUNT"); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/GenericGroupDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/GenericGroupDAOImpl.java index d980e9e0ab9..96ca67edd04 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/GenericGroupDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/GenericGroupDAOImpl.java @@ -14,18 +14,32 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.core.dao.impl.group; import org.wso2.carbon.device.mgt.common.Device; -import org.wso2.carbon.device.mgt.common.GroupPaginationRequest; -import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup; import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.impl.AbstractGroupDAOImpl; import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil; -import org.wso2.carbon.device.mgt.core.dao.util.GroupManagementDAOUtil; import java.sql.Connection; import java.sql.PreparedStatement; @@ -38,134 +52,6 @@ import java.util.List; * This class represents implementation of GroupDAO */ public class GenericGroupDAOImpl extends AbstractGroupDAOImpl { - @Override - public List getGroups(GroupPaginationRequest request, int tenantId) - throws GroupManagementDAOException { - PreparedStatement stmt = null; - ResultSet resultSet = null; - List deviceGroupList = null; - - String groupName = request.getGroupName(); - boolean hasGroupName = false; - String owner = request.getOwner(); - String status = request.getStatus(); - boolean hasOwner = false; - boolean hasStatus = false; - boolean hasLimit = request.getRowCount() != 0; - - try { - Connection conn = GroupManagementDAOFactory.getConnection(); - String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?"; - if (groupName != null && !groupName.isEmpty()) { - sql += " AND UPPER(GROUP_NAME) LIKE ?"; - hasGroupName = true; - } - if (owner != null && !owner.isEmpty()) { - sql += " AND UPPER(OWNER) LIKE ?"; - hasOwner = true; - } - if (status != null && !status.isEmpty()) { - sql += " AND STATUS = ?"; - hasStatus = true; - } - if (hasLimit) { - sql += " LIMIT ?, ?"; - } - - int paramIndex = 1; - stmt = conn.prepareStatement(sql); - stmt.setInt(paramIndex++, tenantId); - if (hasGroupName) { - stmt.setString(paramIndex++, groupName + "%"); - } - if (hasOwner) { - stmt.setString(paramIndex++, owner + "%"); - } - if (hasStatus) { - stmt.setString(paramIndex++, status.toUpperCase()); - } - if (hasLimit) { - stmt.setInt(paramIndex++, request.getStartIndex()); - stmt.setInt(paramIndex, request.getRowCount()); - } - resultSet = stmt.executeQuery(); - deviceGroupList = new ArrayList<>(); - while (resultSet.next()) { - deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet)); - } - } catch (SQLException e) { - throw new GroupManagementDAOException("Error occurred while listing all groups in tenant: " + tenantId, e); - } finally { - GroupManagementDAOUtil.cleanupResources(stmt, resultSet); - } - return deviceGroupList; - } - - @Override - public List getGroups(GroupPaginationRequest request, List deviceGroupIds, - int tenantId) throws GroupManagementDAOException { - int deviceGroupIdsCount = deviceGroupIds.size(); - if (deviceGroupIdsCount == 0) { - return new ArrayList<>(); - } - PreparedStatement stmt = null; - ResultSet resultSet = null; - List deviceGroupList = null; - - String groupName = request.getGroupName(); - boolean hasGroupName = false; - String owner = request.getOwner(); - boolean hasOwner = false; - boolean hasLimit = request.getRowCount() != 0; - - try { - Connection conn = GroupManagementDAOFactory.getConnection(); - String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?"; - if (groupName != null && !groupName.isEmpty()) { - sql += " AND GROUP_NAME LIKE ?"; - hasGroupName = true; - } - if (owner != null && !owner.isEmpty()) { - sql += " AND OWNER LIKE ?"; - hasOwner = true; - } - sql += " AND ID IN ("; - for (int i = 0; i < deviceGroupIdsCount; i++) { - sql += (deviceGroupIdsCount - 1 != i) ? "?," : "?"; - } - sql += ")"; - if (hasLimit) { - sql += " LIMIT ?, ?"; - } - - int paramIndex = 1; - stmt = conn.prepareStatement(sql); - stmt.setInt(paramIndex++, tenantId); - if (hasGroupName) { - stmt.setString(paramIndex++, groupName + "%"); - } - if (hasOwner) { - stmt.setString(paramIndex++, owner + "%"); - } - for (Integer deviceGroupId : deviceGroupIds) { - stmt.setInt(paramIndex++, deviceGroupId); - } - if (hasLimit) { - stmt.setInt(paramIndex++, request.getStartIndex()); - stmt.setInt(paramIndex, request.getRowCount()); - } - resultSet = stmt.executeQuery(); - deviceGroupList = new ArrayList<>(); - while (resultSet.next()) { - deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet)); - } - } catch (SQLException e) { - throw new GroupManagementDAOException("Error occurred while listing all groups in tenant: " + tenantId, e); - } finally { - GroupManagementDAOUtil.cleanupResources(stmt, resultSet); - } - return deviceGroupList; - } @Override public List getDevices(int groupId, int startIndex, int rowCount, int tenantId) @@ -209,4 +95,4 @@ public class GenericGroupDAOImpl extends AbstractGroupDAOImpl { } return devices; } -} \ No newline at end of file +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/OracleGroupDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/OracleGroupDAOImpl.java index 4074840e0ff..e7473b6b53f 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/OracleGroupDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/OracleGroupDAOImpl.java @@ -14,10 +14,28 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.core.dao.impl.group; +import org.apache.commons.lang.StringUtils; import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.GroupPaginationRequest; import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup; @@ -56,7 +74,8 @@ public class OracleGroupDAOImpl extends AbstractGroupDAOImpl { try { Connection conn = GroupManagementDAOFactory.getConnection(); - String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?"; + String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP " + + "WHERE TENANT_ID = ?"; if (groupName != null && !groupName.isEmpty()) { sql += " AND GROUP_NAME LIKE ?"; hasGroupName = true; @@ -69,6 +88,9 @@ public class OracleGroupDAOImpl extends AbstractGroupDAOImpl { sql += " AND STATUS = ?"; hasStatus = true; } + if (StringUtils.isNotBlank(request.getParentPath())) { + sql += " AND PARENT_PATH LIKE ?"; + } if (hasLimit) { sql += " OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; } @@ -85,6 +107,9 @@ public class OracleGroupDAOImpl extends AbstractGroupDAOImpl { if (hasStatus) { stmt.setString(paramIndex++, status.toUpperCase()); } + if (StringUtils.isNotBlank(request.getParentPath())) { + stmt.setString(paramIndex++, request.getParentPath()); + } if (hasLimit) { stmt.setInt(paramIndex++, request.getStartIndex()); stmt.setInt(paramIndex, request.getRowCount()); @@ -121,7 +146,8 @@ public class OracleGroupDAOImpl extends AbstractGroupDAOImpl { try { Connection conn = GroupManagementDAOFactory.getConnection(); - String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?"; + String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP " + + "WHERE TENANT_ID = ?"; if (groupName != null && !groupName.isEmpty()) { sql += " AND GROUP_NAME LIKE ?"; hasGroupName = true; @@ -130,6 +156,9 @@ public class OracleGroupDAOImpl extends AbstractGroupDAOImpl { sql += " AND OWNER LIKE ?"; hasOwner = true; } + if (StringUtils.isNotBlank(request.getParentPath())) { + sql += " AND PARENT_PATH LIKE ?"; + } sql += " AND ID IN ("; for (int i = 0; i < deviceGroupIdsCount; i++) { sql += (deviceGroupIdsCount - 1 != i) ? "?," : "?"; @@ -148,6 +177,9 @@ public class OracleGroupDAOImpl extends AbstractGroupDAOImpl { if (hasOwner) { stmt.setString(paramIndex++, owner + "%"); } + if (StringUtils.isNotBlank(request.getParentPath())) { + stmt.setString(paramIndex++, request.getParentPath()); + } for (Integer deviceGroupId : deviceGroupIds) { stmt.setInt(paramIndex++, deviceGroupId); } @@ -210,4 +242,4 @@ public class OracleGroupDAOImpl extends AbstractGroupDAOImpl { } return devices; } -} \ No newline at end of file +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/PostgreSQLGroupDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/PostgreSQLGroupDAOImpl.java index 4140a5c48a0..a01ef5b6b87 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/PostgreSQLGroupDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/PostgreSQLGroupDAOImpl.java @@ -14,13 +14,29 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.core.dao.impl.group; import org.apache.solr.common.StringUtils; import org.wso2.carbon.device.mgt.common.Device; -import org.wso2.carbon.device.mgt.common.GroupPaginationRequest; import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup; import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOFactory; @@ -50,10 +66,10 @@ public class PostgreSQLGroupDAOImpl extends AbstractGroupDAOImpl { Connection conn = GroupManagementDAOFactory.getConnection(); String sql; if(StringUtils.isEmpty(deviceGroup.getStatus())) { - sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID) " + + sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID, PARENT_PATH) " + "VALUES (?, ?, ?, ?) RETURNING ID"; } else { - sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID, STATUS) " + + sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID, PARENT_PATH, STATUS) " + "VALUES (?, ?, ?, ?, ?) RETURNING ID"; hasStatus = true; } @@ -62,8 +78,9 @@ public class PostgreSQLGroupDAOImpl extends AbstractGroupDAOImpl { stmt.setString(2, deviceGroup.getName()); stmt.setString(3, deviceGroup.getOwner()); stmt.setInt(4, tenantId); + stmt.setString(5, deviceGroup.getParentPath()); if(hasStatus) { - stmt.setString(5, deviceGroup.getStatus()); + stmt.setString(6, deviceGroup.getStatus()); } stmt.execute(); rs = stmt.getGeneratedKeys(); @@ -79,135 +96,6 @@ public class PostgreSQLGroupDAOImpl extends AbstractGroupDAOImpl { } } - @Override - public List getGroups(GroupPaginationRequest request, int tenantId) - throws GroupManagementDAOException { - PreparedStatement stmt = null; - ResultSet resultSet = null; - List deviceGroupList = null; - - String groupName = request.getGroupName(); - boolean hasGroupName = false; - String owner = request.getOwner(); - String status = request.getStatus(); - boolean hasOwner = false; - boolean hasStatus = false; - boolean hasLimit = request.getRowCount() != 0; - - try { - Connection conn = GroupManagementDAOFactory.getConnection(); - String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?"; - if (groupName != null && !groupName.isEmpty()) { - sql += " AND GROUP_NAME LIKE ?"; - hasGroupName = true; - } - if (owner != null && !owner.isEmpty()) { - sql += " AND OWNER LIKE ?"; - hasOwner = true; - } - if (status != null && !status.isEmpty()) { - sql += " AND STATUS = ?"; - hasStatus = true; - } - if (hasLimit) { - sql += " LIMIT ? OFFSET ?"; - } - - int paramIndex = 1; - stmt = conn.prepareStatement(sql); - stmt.setInt(paramIndex++, tenantId); - if (hasGroupName) { - stmt.setString(paramIndex++, groupName + "%"); - } - if (hasOwner) { - stmt.setString(paramIndex++, owner + "%"); - } - if (hasStatus) { - stmt.setString(paramIndex++, status.toUpperCase()); - } - if (hasLimit) { - stmt.setInt(paramIndex++, request.getRowCount()); - stmt.setInt(paramIndex, request.getStartIndex()); - } - resultSet = stmt.executeQuery(); - deviceGroupList = new ArrayList<>(); - while (resultSet.next()) { - deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet)); - } - } catch (SQLException e) { - throw new GroupManagementDAOException("Error occurred while listing all groups in tenant: " + tenantId, e); - } finally { - GroupManagementDAOUtil.cleanupResources(stmt, resultSet); - } - return deviceGroupList; - } - - @Override - public List getGroups(GroupPaginationRequest request, List deviceGroupIds, - int tenantId) throws GroupManagementDAOException { - int deviceGroupIdsCount = deviceGroupIds.size(); - if (deviceGroupIdsCount == 0) { - return new ArrayList<>(); - } - PreparedStatement stmt = null; - ResultSet resultSet = null; - List deviceGroupList = null; - - String groupName = request.getGroupName(); - boolean hasGroupName = false; - String owner = request.getOwner(); - boolean hasOwner = false; - boolean hasLimit = request.getRowCount() != 0; - - try { - Connection conn = GroupManagementDAOFactory.getConnection(); - String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?"; - if (groupName != null && !groupName.isEmpty()) { - sql += " AND GROUP_NAME LIKE ?"; - hasGroupName = true; - } - if (owner != null && !owner.isEmpty()) { - sql += " AND OWNER LIKE ?"; - hasOwner = true; - } - sql += " AND ID IN ("; - for (int i = 0; i < deviceGroupIdsCount; i++) { - sql += (deviceGroupIdsCount - 1 != i) ? "?," : "?"; - } - sql += ")"; - if (hasLimit) { - sql += " LIMIT ? OFFSET ?"; - } - - int paramIndex = 1; - stmt = conn.prepareStatement(sql); - stmt.setInt(paramIndex++, tenantId); - if (hasGroupName) { - stmt.setString(paramIndex++, groupName + "%"); - } - if (hasOwner) { - stmt.setString(paramIndex++, owner + "%"); - } - for (Integer deviceGroupId : deviceGroupIds) { - stmt.setInt(paramIndex++, deviceGroupId); - } - if (hasLimit) { - stmt.setInt(paramIndex++, request.getRowCount()); - stmt.setInt(paramIndex, request.getStartIndex()); - } - resultSet = stmt.executeQuery(); - deviceGroupList = new ArrayList<>(); - while (resultSet.next()) { - deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet)); - } - } catch (SQLException e) { - throw new GroupManagementDAOException("Error occurred while listing all groups in tenant: " + tenantId, e); - } finally { - GroupManagementDAOUtil.cleanupResources(stmt, resultSet); - } - return deviceGroupList; - } - @Override public List getDevices(int groupId, int startIndex, int rowCount, int tenantId) throws GroupManagementDAOException { @@ -250,4 +138,4 @@ public class PostgreSQLGroupDAOImpl extends AbstractGroupDAOImpl { } return devices; } -} \ No newline at end of file +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/SQLServerGroupDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/SQLServerGroupDAOImpl.java index 879d4503496..5065628b087 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/SQLServerGroupDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/group/SQLServerGroupDAOImpl.java @@ -14,10 +14,28 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.core.dao.impl.group; +import org.apache.commons.lang.StringUtils; import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.GroupPaginationRequest; import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup; @@ -56,7 +74,8 @@ public class SQLServerGroupDAOImpl extends AbstractGroupDAOImpl { try { Connection conn = GroupManagementDAOFactory.getConnection(); - String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?"; + String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP " + + "WHERE TENANT_ID = ?"; if (groupName != null && !groupName.isEmpty()) { sql += " AND GROUP_NAME LIKE ?"; hasGroupName = true; @@ -69,6 +88,9 @@ public class SQLServerGroupDAOImpl extends AbstractGroupDAOImpl { sql += " AND STATUS = ?"; hasStatus = true; } + if (StringUtils.isNotBlank(request.getParentPath())) { + sql += " AND PARENT_PATH LIKE ?"; + } if (hasLimit) { sql += " ORDER BY ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; } @@ -85,6 +107,9 @@ public class SQLServerGroupDAOImpl extends AbstractGroupDAOImpl { if (hasStatus) { stmt.setString(paramIndex++, status.toUpperCase()); } + if (StringUtils.isNotBlank(request.getParentPath())) { + stmt.setString(paramIndex++, request.getParentPath()); + } if (hasLimit) { stmt.setInt(paramIndex++, request.getStartIndex()); stmt.setInt(paramIndex, request.getRowCount()); @@ -121,7 +146,8 @@ public class SQLServerGroupDAOImpl extends AbstractGroupDAOImpl { try { Connection conn = GroupManagementDAOFactory.getConnection(); - String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?"; + String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP " + + "WHERE TENANT_ID = ?"; if (groupName != null && !groupName.isEmpty()) { sql += " AND GROUP_NAME LIKE ?"; hasGroupName = true; @@ -130,6 +156,9 @@ public class SQLServerGroupDAOImpl extends AbstractGroupDAOImpl { sql += " AND OWNER LIKE ?"; hasOwner = true; } + if (StringUtils.isNotBlank(request.getParentPath())) { + sql += " AND PARENT_PATH LIKE ?"; + } sql += " AND ID IN ("; for (int i = 0; i < deviceGroupIdsCount; i++) { sql += (deviceGroupIdsCount - 1 != i) ? "?," : "?"; @@ -148,6 +177,9 @@ public class SQLServerGroupDAOImpl extends AbstractGroupDAOImpl { if (hasOwner) { stmt.setString(paramIndex++, owner + "%"); } + if (StringUtils.isNotBlank(request.getParentPath())) { + stmt.setString(paramIndex++, request.getParentPath()); + } for (Integer deviceGroupId : deviceGroupIds) { stmt.setInt(paramIndex++, deviceGroupId); } @@ -210,4 +242,4 @@ public class SQLServerGroupDAOImpl extends AbstractGroupDAOImpl { } return devices; } -} \ No newline at end of file +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/util/GroupManagementDAOUtil.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/util/GroupManagementDAOUtil.java index b3acd1d3139..3025fb6d0ac 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/util/GroupManagementDAOUtil.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/util/GroupManagementDAOUtil.java @@ -14,6 +14,22 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.core.dao.util; @@ -86,6 +102,7 @@ public final class GroupManagementDAOUtil { group.setName(resultSet.getString("GROUP_NAME")); group.setOwner(resultSet.getString("OWNER")); group.setStatus(resultSet.getString("STATUS")); + group.setParentPath(resultSet.getString("PARENT_PATH")); return group; } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderService.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderService.java index f729218314f..c5e7417c866 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderService.java @@ -14,6 +14,23 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.core.service; @@ -60,10 +77,11 @@ public interface GroupManagementProviderService { * Delete existing device group. * * @param groupId to be deleted. + * @param isDeleteChildren to delete the children groups or not. * @return status of the delete operation. * @throws GroupManagementException */ - boolean deleteGroup(int groupId) throws GroupManagementException; + boolean deleteGroup(int groupId, boolean isDeleteChildren) throws GroupManagementException; /** * Get the device group provided the device group id. @@ -75,6 +93,17 @@ public interface GroupManagementProviderService { */ DeviceGroup getGroup(int groupId, boolean requireGroupProps) throws GroupManagementException; + /** + * Get the device group provided the device group id and depth of children groups. + * + * @param groupId of the group. + * @param requireGroupProps to include group properties. + * @param depth of children groups to retrieve. + * @return {@link DeviceGroup} group with details. + * @throws GroupManagementException on error during retrieval of group + */ + DeviceGroup getGroup(int groupId, boolean requireGroupProps, int depth) throws GroupManagementException; + /** * Get the device group provided the device group name. * @@ -85,6 +114,17 @@ public interface GroupManagementProviderService { */ DeviceGroup getGroup(String groupName, boolean requireGroupProps) throws GroupManagementException; + /** + * Get the device group provided the device group id and depth of children groups. + * + * @param groupName of the group. + * @param requireGroupProps to include group properties. + * @param depth of children groups to retrieve. + * @return {@link DeviceGroup} group with details. + * @throws GroupManagementException on error during retrieval of group + */ + DeviceGroup getGroup(String groupName, boolean requireGroupProps, int depth) throws GroupManagementException; + /** * Get all device groups in tenant. * @@ -127,6 +167,18 @@ public interface GroupManagementProviderService { PaginationResult getGroups(String username, GroupPaginationRequest paginationRequest, boolean requireGroupProps) throws GroupManagementException; + /** + * Get device groups with children groups hierarchically which belongs to specified user with pagination. + * + * @param username of the user. + * @param request to filter results + * @param requireGroupProps to include group properties + * @return {@link PaginationResult} paginated groups. + * @throws GroupManagementException on error during retrieval of groups with hierarchy + */ + PaginationResult getGroupsWithHierarchy(String username, GroupPaginationRequest request, + boolean requireGroupProps) throws GroupManagementException; + /** * Get all device group count in tenant * @@ -147,10 +199,11 @@ public interface GroupManagementProviderService { * Get device group count of user * * @param username of the user + * @param parentPath of the group * @return group count * @throws GroupManagementException */ - int getGroupCount(String username) throws GroupManagementException; + int getGroupCount(String username, String parentPath) throws GroupManagementException; /** * Manage device group sharing with user with list of roles. diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceImpl.java index b28cbefe251..6a678c1fca0 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceImpl.java @@ -14,10 +14,28 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.core.service; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.CarbonConstants; @@ -36,14 +54,12 @@ import org.wso2.carbon.device.mgt.common.group.mgt.GroupAlreadyExistException; import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException; import org.wso2.carbon.device.mgt.common.group.mgt.GroupNotExistException; import org.wso2.carbon.device.mgt.common.group.mgt.RoleDoesNotExistException; -import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; import org.wso2.carbon.device.mgt.core.dao.DeviceDAO; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.GroupDAO; import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOFactory; -import org.wso2.carbon.device.mgt.core.event.config.EventOperationTaskConfiguration; import org.wso2.carbon.device.mgt.core.event.config.GroupAssignmentEventOperationExecutor; import org.wso2.carbon.device.mgt.core.geo.task.GeoFenceEventOperationManager; import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder; @@ -56,10 +72,11 @@ import org.wso2.carbon.user.api.UserStoreManager; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.stream.Collectors; public class GroupManagementProviderServiceImpl implements GroupManagementProviderService { @@ -95,6 +112,20 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid GroupManagementDAOFactory.beginTransaction(); DeviceGroup existingGroup = this.groupDAO.getGroup(deviceGroup.getName(), tenantId); if (existingGroup == null) { + if (deviceGroup.getParentGroupId() == 0) { + deviceGroup.setParentPath(DeviceGroupConstants.HierarchicalGroup.SEPERATOR); + } else { + DeviceGroup immediateParentGroup = groupDAO.getGroup(deviceGroup.getParentGroupId(), tenantId); + if (immediateParentGroup == null) { + String msg = "Parent group with group ID '" + deviceGroup.getParentGroupId() + + "' does not exist. Hence creating of group '" + deviceGroup.getName() + + "' was not success"; + log.error(msg); + throw new GroupManagementException(msg); + } + String parentPath = DeviceManagerUtil.createParentPath(immediateParentGroup); + deviceGroup.setParentPath(parentPath); + } int updatedGroupID = this.groupDAO.addGroup(deviceGroup, tenantId); if (deviceGroup.getGroupProperties() != null && deviceGroup.getGroupProperties().size() > 0) { this.groupDAO.addGroupProperties(deviceGroup, updatedGroupID, tenantId); @@ -146,7 +177,35 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid GroupManagementDAOFactory.beginTransaction(); DeviceGroup existingGroup = this.groupDAO.getGroup(groupId, tenantId); if (existingGroup != null) { - this.groupDAO.updateGroup(deviceGroup, groupId, tenantId); + List groupsToUpdate = new ArrayList<>(); + String immediateParentID = StringUtils.substringAfterLast(existingGroup.getParentPath(), DeviceGroupConstants.HierarchicalGroup.SEPERATOR); + String parentPath = ""; + if (deviceGroup.getParentGroupId() == 0) { + deviceGroup.setParentPath(DeviceGroupConstants.HierarchicalGroup.SEPERATOR); + } else { + DeviceGroup immediateParentGroup = groupDAO.getGroup(deviceGroup.getParentGroupId(), tenantId); + if (immediateParentGroup == null) { + String msg = "Parent group with group ID '" + deviceGroup.getParentGroupId() + + "' does not exist. Hence updating of group '" + groupId + + "' was not success"; + log.error(msg); + throw new GroupManagementException(msg); + } + parentPath = DeviceManagerUtil.createParentPath(immediateParentGroup); + deviceGroup.setParentPath(parentPath); + } + deviceGroup.setGroupId(groupId); + groupsToUpdate.add(deviceGroup); + if (StringUtils.isNotBlank(immediateParentID)) { + List childrenGroups = groupDAO.getChildrenGroups( + DeviceManagerUtil.createParentPath(existingGroup), tenantId); + for (DeviceGroup childrenGroup : childrenGroups) { + childrenGroup.setParentPath(childrenGroup.getParentPath() + .replace(existingGroup.getParentPath(), parentPath)); + groupsToUpdate.add(childrenGroup); + } + } + this.groupDAO.updateGroups(groupsToUpdate, tenantId); if (deviceGroup.getGroupProperties() != null && deviceGroup.getGroupProperties().size() > 0) { this.groupDAO.updateGroupProperties(deviceGroup, groupId, tenantId); } @@ -178,7 +237,7 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid * {@inheritDoc} */ @Override - public boolean deleteGroup(int groupId) throws GroupManagementException { + public boolean deleteGroup(int groupId, boolean isDeleteChildren) throws GroupManagementException { if (log.isDebugEnabled()) { log.debug("Delete group: " + groupId); } @@ -189,8 +248,37 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); try { GroupManagementDAOFactory.beginTransaction(); - this.groupDAO.deleteGroup(groupId, tenantId); - this.groupDAO.deleteAllGroupProperties(groupId, tenantId); + List childrenGroups = new ArrayList<>(); + List groupIdsToDelete = new ArrayList<>(); + if (deviceGroup.getChildrenGroups() != null && !deviceGroup.getChildrenGroups().isEmpty()) { + String parentPath = DeviceManagerUtil.createParentPath(deviceGroup); + childrenGroups = groupDAO.getChildrenGroups(parentPath, tenantId); + if (isDeleteChildren) { + groupIdsToDelete = childrenGroups.stream().map(DeviceGroup::getGroupId) + .collect(Collectors.toList()); + } else { + for (DeviceGroup childrenGroup : childrenGroups) { + String newParentPath = childrenGroup.getParentPath() + .replace(DeviceGroupConstants.HierarchicalGroup.SEPERATOR + deviceGroup.getGroupId(), ""); + if (StringUtils.isEmpty(newParentPath)) { + newParentPath = DeviceGroupConstants.HierarchicalGroup.SEPERATOR; + } + childrenGroup.setParentPath(newParentPath); + } + } + } + if (isDeleteChildren) { + groupIdsToDelete.add(groupId); + groupDAO.deleteGroupsMapping(groupIdsToDelete, tenantId); + groupDAO.deleteGroups(groupIdsToDelete, tenantId); + groupDAO.deleteAllGroupsProperties(groupIdsToDelete, tenantId); + } else { + groupDAO.deleteGroup(groupId, tenantId); + groupDAO.deleteAllGroupProperties(groupId, tenantId); + if (!childrenGroups.isEmpty()) { + groupDAO.updateGroups(childrenGroups, tenantId); + } + } GroupManagementDAOFactory.commitTransaction(); if (log.isDebugEnabled()) { log.debug("DeviceGroup " + deviceGroup.getName() + " removed."); @@ -219,6 +307,11 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid */ @Override public DeviceGroup getGroup(int groupId, boolean requireGroupProps) throws GroupManagementException { + return getGroup(groupId, requireGroupProps, 1); + } + + @Override + public DeviceGroup getGroup(int groupId, boolean requireGroupProps, int depth) throws GroupManagementException { if (log.isDebugEnabled()) { log.debug("Get group by id: " + groupId); } @@ -227,8 +320,13 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid try { GroupManagementDAOFactory.openConnection(); deviceGroup = this.groupDAO.getGroup(groupId, tenantId); - if (requireGroupProps) { - populateGroupProperties(deviceGroup, tenantId); + if (deviceGroup != null) { + String parentPath = DeviceManagerUtil.createParentPath(deviceGroup); + List childrenGroups = groupDAO.getChildrenGroups(parentPath, tenantId); + createGroupWithChildren(deviceGroup, childrenGroups, requireGroupProps, tenantId, depth, 0); + if (requireGroupProps) { + populateGroupProperties(deviceGroup, tenantId); + } } return deviceGroup; } catch (GroupManagementDAOException e) { @@ -236,11 +334,8 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid log.error(msg, e); throw new GroupManagementException(msg, e); } catch (SQLException e) { - String msg = "Error occurred while opening a connection to the data source."; - log.error(msg, e); - throw new GroupManagementException(msg, e); - } catch (Exception e) { - String msg = "Error occurred in getGroup for groupId: " + groupId; + String msg = "Error occurred while opening a connection to the data source to retrieve the group '" + + groupId + "'"; log.error(msg, e); throw new GroupManagementException(msg, e); } finally { @@ -253,6 +348,11 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid */ @Override public DeviceGroup getGroup(String groupName, boolean requireGroupProps) throws GroupManagementException { + return getGroup(groupName, requireGroupProps, 1); + } + + @Override + public DeviceGroup getGroup(String groupName, boolean requireGroupProps, int depth) throws GroupManagementException { if (groupName == null) { String msg = "Received empty groupName for getGroup"; log.error(msg); @@ -266,8 +366,13 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid try { GroupManagementDAOFactory.openConnection(); deviceGroup = this.groupDAO.getGroup(groupName, tenantId); - if (requireGroupProps) { - populateGroupProperties(deviceGroup, tenantId); + if (deviceGroup != null) { + String parentPath = DeviceManagerUtil.createParentPath(deviceGroup); + List childrenGroups = groupDAO.getChildrenGroups(parentPath, tenantId); + createGroupWithChildren(deviceGroup, childrenGroups, requireGroupProps, tenantId, depth , 0); + if (requireGroupProps) { + populateGroupProperties(deviceGroup, tenantId); + } } return deviceGroup; } catch (GroupManagementDAOException e) { @@ -275,11 +380,8 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid log.error(msg, e); throw new GroupManagementException(msg, e); } catch (SQLException e) { - String msg = "Error occurred while opening a connection to the data source."; - log.error(msg, e); - throw new GroupManagementException(msg, e); - } catch (Exception e) { - String msg = "Error occurred in getGroup with name " + groupName; + String msg = "Error occurred while opening a connection to the data source to retrieve group with name '" + + groupName + "'"; log.error(msg, e); throw new GroupManagementException(msg, e); } finally { @@ -296,12 +398,10 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid try { int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); GroupManagementDAOFactory.openConnection(); - deviceGroups = this.groupDAO.getGroups(tenantId); - if (requireGroupProps) { - if (deviceGroups != null && !deviceGroups.isEmpty()) { - for (DeviceGroup group : deviceGroups) { - populateGroupProperties(group, tenantId); - } + deviceGroups = groupDAO.getRootGroups(tenantId); + for (DeviceGroup deviceGroup : deviceGroups) { + if (requireGroupProps) { + populateGroupProperties(deviceGroup, tenantId); } } return deviceGroups; @@ -334,16 +434,15 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid log.debug("Get groups with pagination " + request.toString()); } request = DeviceManagerUtil.validateGroupListPageSize(request); - List deviceGroups; + List rootGroups; try { int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); GroupManagementDAOFactory.openConnection(); - deviceGroups = this.groupDAO.getGroups(request, tenantId); - if (requireGroupProps) { - if (deviceGroups != null && !deviceGroups.isEmpty()) { - for (DeviceGroup group : deviceGroups) { - populateGroupProperties(group, tenantId); - } + request.setParentPath(DeviceGroupConstants.HierarchicalGroup.SEPERATOR); + rootGroups = this.groupDAO.getGroups(request, tenantId); + for (DeviceGroup rootGroup : rootGroups) { + if (requireGroupProps) { + populateGroupProperties(rootGroup, tenantId); } } } catch (GroupManagementDAOException e) { @@ -362,11 +461,68 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid GroupManagementDAOFactory.closeConnection(); } PaginationResult groupResult = new PaginationResult(); - groupResult.setData(deviceGroups); + groupResult.setData(rootGroups); groupResult.setRecordsTotal(getGroupCount(request)); return groupResult; } + @Override + public PaginationResult getGroupsWithHierarchy(String username, GroupPaginationRequest request, + boolean requireGroupProps) throws GroupManagementException { + if (request == null) { + String msg = "Received incomplete data for retrieve groups with hierarchy"; + log.error(msg); + throw new GroupManagementException(msg); + } + if (log.isDebugEnabled()) { + log.debug("Get groups with hierarchy " + request.toString()); + } + DeviceManagerUtil.validateGroupListPageSize(request); + List rootGroups; + try { + int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); + request.setParentPath(DeviceGroupConstants.HierarchicalGroup.SEPERATOR); + if (StringUtils.isBlank(username)) { + GroupManagementDAOFactory.openConnection(); + rootGroups = groupDAO.getGroups(request, tenantId); + } else { + List allDeviceGroupIdsOfUser = getGroupIds(username); + GroupManagementDAOFactory.openConnection(); + rootGroups = this.groupDAO.getGroups(request, allDeviceGroupIdsOfUser, tenantId); + } + String parentPath; + List childrenGroups; + for (DeviceGroup rootGroup : rootGroups) { + parentPath = DeviceManagerUtil.createParentPath(rootGroup); + childrenGroups = groupDAO.getChildrenGroups(parentPath, tenantId); + createGroupWithChildren( + rootGroup, childrenGroups, requireGroupProps, tenantId, request.getDepth(), 0); + if (requireGroupProps) { + populateGroupProperties(rootGroup, tenantId); + } + } + } catch (GroupManagementDAOException e) { + String msg = "Error occurred while retrieving all groups with hierarchy"; + log.error(msg, e); + throw new GroupManagementException(msg, e); + } catch (SQLException e) { + String msg = "Error occurred while opening a connection to the data source to retrieve all groups " + + "with hierarchy"; + log.error(msg, e); + throw new GroupManagementException(msg, e); + } finally { + GroupManagementDAOFactory.closeConnection(); + } + PaginationResult groupResult = new PaginationResult(); + groupResult.setData(rootGroups); + if (StringUtils.isBlank(username)) { + groupResult.setRecordsTotal(getGroupCount(request)); + } else { + groupResult.setRecordsTotal(getGroupCount(username, request.getParentPath())); + } + return groupResult; + } + @Override public List getGroups(String username, boolean requireGroupProps) throws GroupManagementException { if (username == null || username.isEmpty()) { @@ -457,16 +613,15 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid } request = DeviceManagerUtil.validateGroupListPageSize(request); List allDeviceGroupIdsOfUser = getGroupIds(currentUser); - List allMatchingGroups; + List rootGroups; try { int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); GroupManagementDAOFactory.openConnection(); - allMatchingGroups = this.groupDAO.getGroups(request, allDeviceGroupIdsOfUser, tenantId); - if (requireGroupProps) { - if (allMatchingGroups != null && !allMatchingGroups.isEmpty()) { - for (DeviceGroup group : allMatchingGroups) { - populateGroupProperties(group, tenantId); - } + request.setParentPath(DeviceGroupConstants.HierarchicalGroup.SEPERATOR); + rootGroups = this.groupDAO.getGroups(request, allDeviceGroupIdsOfUser, tenantId); + for (DeviceGroup rootGroup : rootGroups) { + if (requireGroupProps) { + populateGroupProperties(rootGroup, tenantId); } } } catch (GroupManagementDAOException | SQLException e) { @@ -481,8 +636,8 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid GroupManagementDAOFactory.closeConnection(); } PaginationResult groupResult = new PaginationResult(); - groupResult.setData(allMatchingGroups); - groupResult.setRecordsTotal(getGroupCount(currentUser)); + groupResult.setData(rootGroups); + groupResult.setRecordsTotal(getGroupCount(currentUser, request.getParentPath())); return groupResult; } @@ -557,7 +712,7 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid * {@inheritDoc} */ @Override - public int getGroupCount(String username) throws GroupManagementException { + public int getGroupCount(String username, String parentPath) throws GroupManagementException { if (username == null || username.isEmpty()) { String msg = "Received empty user name for getGroupCount"; log.error(msg); @@ -574,8 +729,8 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid .getUserStoreManager(); String[] roleList = userStoreManager.getRoleListOfUser(username); GroupManagementDAOFactory.openConnection(); - count = groupDAO.getOwnGroupsCount(username, tenantId); - count += groupDAO.getGroupsCount(roleList, tenantId); + count = groupDAO.getOwnGroupsCount(username, tenantId, parentPath); + count += groupDAO.getGroupsCount(roleList, tenantId, parentPath); return count; } catch (UserStoreException e) { String msg = "Error occurred while retrieving role list of user '" + username + "'"; @@ -1131,4 +1286,40 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid } } } + + /** + * Recursive method to create group with children based on params to provide hierarchical grouping. + * @param parentGroup to which children group should be set. + * @param childrenGroups which are descendants of parent group. + * @param requireGroupProps to include device properties. + * @param tenantId of the group. + * @param depth of children groups set and when reaches recursive call returns to callee. + * @param counter to track the recursive calls and to stop when reaches the depth. + * @throws GroupManagementDAOException on error during population of group properties. + */ + private void createGroupWithChildren(DeviceGroup parentGroup, List childrenGroups, + boolean requireGroupProps, int tenantId, int depth, int counter) throws GroupManagementDAOException { + if (childrenGroups.isEmpty() || depth == counter) { + return; + } + List immediateChildrenGroups = new ArrayList<>(); + Iterator iterator = childrenGroups.iterator(); + while (iterator.hasNext()) { + DeviceGroup childGroup = iterator.next(); + int immediateParentID = Integer.parseInt(StringUtils.substringAfterLast( + childGroup.getParentPath(), DeviceGroupConstants.HierarchicalGroup.SEPERATOR)); + if (immediateParentID == parentGroup.getGroupId()) { + if (requireGroupProps) { + populateGroupProperties(childGroup, tenantId); + } + immediateChildrenGroups.add(childGroup); + iterator.remove(); + } + } + parentGroup.setChildrenGroups(immediateChildrenGroups); + counter++; + for (DeviceGroup nextParentGroup : immediateChildrenGroups) { + createGroupWithChildren(nextParentGroup, childrenGroups, requireGroupProps, tenantId, depth, counter); + } + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/DeviceManagerUtil.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/DeviceManagerUtil.java index 51ea9348900..8a88d53ac0d 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/DeviceManagerUtil.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/DeviceManagerUtil.java @@ -70,10 +70,10 @@ import org.wso2.carbon.device.mgt.common.exceptions.MetadataManagementException; import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException; import org.wso2.carbon.device.mgt.common.geo.service.GeofenceData; import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup; +import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroupConstants; import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException; import org.wso2.carbon.device.mgt.common.notification.mgt.NotificationManagementException; import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException; -import org.wso2.carbon.device.mgt.common.permission.mgt.Permission; import org.wso2.carbon.device.mgt.common.type.mgt.DeviceTypeMetaDefinition; import org.wso2.carbon.device.mgt.core.DeviceManagementConstants; import org.wso2.carbon.device.mgt.core.cache.DeviceCacheKey; @@ -131,8 +131,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.IntStream; -//import org.wso2.carbon.device.mgt.analytics.data.publisher.service.EventsPublisherService; - public final class DeviceManagerUtil { private static final Log log = LogFactory.getLog(DeviceManagerUtil.class); @@ -654,18 +652,6 @@ public final class DeviceManagerUtil { } } - public static void initializeAPIResourcePermissionCache() { - CacheManager manager = getCacheManager(); - if(!isAPIResourcePermissionCacheInitialized) { - isAPIResourcePermissionCacheInitialized = true; - if (manager != null) { - manager.getCache(DeviceManagementConstants.API_RESOURCE_PERMISSION_CACHE); - } else { - Caching.getCacheManager().getCache(DeviceManagementConstants.API_RESOURCE_PERMISSION_CACHE); - } - } - } - /** * Enable Geofence caching according to the configurations proviced by cdm-config.xml */ @@ -1218,4 +1204,18 @@ public final class DeviceManagerUtil { } return text; } + + /** + * Create the parent path that the children groups can have + * @param deviceGroup parent group + * @return created parent path + */ + public static String createParentPath(DeviceGroup deviceGroup) { + if (DeviceGroupConstants.HierarchicalGroup.SEPERATOR.equals(deviceGroup.getParentPath())) { + return deviceGroup.getParentPath() + deviceGroup.getGroupId(); + } else { + return deviceGroup.getParentPath() + DeviceGroupConstants.HierarchicalGroup.SEPERATOR + + deviceGroup.getGroupId(); + } + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/MDMAndroidOperationUtil.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/MDMAndroidOperationUtil.java index 14dbaea9ed0..99cacfd9506 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/MDMAndroidOperationUtil.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/MDMAndroidOperationUtil.java @@ -52,6 +52,7 @@ public class MDMAndroidOperationUtil { enterpriseApplication.setType(application.getType().toString()); enterpriseApplication.setUrl(application.getLocation()); enterpriseApplication.setAppIdentifier(application.getIdentifier()); + enterpriseApplication.setProperties(application.getProperties()); operation.setPayLoad(enterpriseApplication.toJSON()); break; case PUBLIC: @@ -59,6 +60,7 @@ public class MDMAndroidOperationUtil { new AppStoreApplication(); appStoreApplication.setType(application.getType().toString()); appStoreApplication.setAppIdentifier(application.getIdentifier()); + appStoreApplication.setProperties(application.getProperties()); operation.setPayLoad(appStoreApplication.toJSON()); break; case WEBAPP: @@ -67,6 +69,7 @@ public class MDMAndroidOperationUtil { webApplication.setUrl(application.getLocation()); webApplication.setName(application.getName()); webApplication.setType(application.getType().toString()); + webApplication.setProperties(application.getProperties()); operation.setPayLoad(webApplication.toJSON()); break; default: @@ -93,6 +96,7 @@ public class MDMAndroidOperationUtil { new EnterpriseApplication(); enterpriseApplication.setType(application.getType().toString()); enterpriseApplication.setAppIdentifier(application.getIdentifier()); + enterpriseApplication.setProperties(application.getProperties()); operation.setPayLoad(enterpriseApplication.toJSON()); break; case PUBLIC: diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceNegativeTest.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceNegativeTest.java index fd341650310..ec8004e475c 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceNegativeTest.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceNegativeTest.java @@ -8,6 +8,23 @@ * * 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. + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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 @@ -103,7 +120,7 @@ public class GroupManagementProviderServiceNegativeTest extends BaseDeviceManage expectedExceptions = {GroupManagementException.class}, expectedExceptionsMessageRegExp = "Error occurred " + "while retrieving group count of user.*") public void testGetGroupCountWithUserName() throws GroupManagementException { - groupManagementProviderService.getGroupCount("test"); + groupManagementProviderService.getGroupCount("test", null); } @@ -145,7 +162,7 @@ public class GroupManagementProviderServiceNegativeTest extends BaseDeviceManage expectedExceptions = {GroupManagementException.class}, expectedExceptionsMessageRegExp = "Received empty " + "user name for getGroupCount.*") public void testGetGroupCountWithUserName2() throws GroupManagementException { - groupManagementProviderService.getGroupCount(null); + groupManagementProviderService.getGroupCount(null, null); } @Test(description = "This method tests getGroups method under negative circumstances", diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceTest.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceTest.java index 4ffb675fe7d..b645eac9a0d 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceTest.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceTest.java @@ -14,6 +14,23 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * + * + * Copyright (c) 2021, Entgra (pvt) Ltd. (https://entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you 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. */ package org.wso2.carbon.device.mgt.core.service; @@ -119,13 +136,13 @@ public class GroupManagementProviderServiceTest extends BaseDeviceManagementTest @Test(dependsOnMethods = ("createGroup")) public void deleteGroup() throws GroupManagementException { DeviceGroup deviceGroup = groupManagementProviderService.getGroup(TestUtils.createDeviceGroup4().getName(), false); - Assert.assertTrue(groupManagementProviderService.deleteGroup(deviceGroup.getGroupId())); + Assert.assertTrue(groupManagementProviderService.deleteGroup(deviceGroup.getGroupId(), false)); } @Test(dependsOnMethods = ("createGroup")) public void deleteGroupNotExists() throws GroupManagementException { - groupManagementProviderService.deleteGroup(8); + groupManagementProviderService.deleteGroup(8, false); } @@ -190,7 +207,7 @@ public class GroupManagementProviderServiceTest extends BaseDeviceManagementTest @Test(dependsOnMethods = ("createGroup")) public void getGroupCountByUsername(String username) throws GroupManagementException { - int x = groupManagementProviderService.getGroupCount(username); + int x = groupManagementProviderService.getGroupCount(username, null); Assert.assertNotNull(x); } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql index 63af00f07f9..2c846cb1af6 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql @@ -14,6 +14,7 @@ CREATE TABLE IF NOT EXISTS DM_GROUP ( STATUS VARCHAR(50) DEFAULT NULL, DESCRIPTION TEXT DEFAULT NULL, OWNER VARCHAR(45) DEFAULT NULL, + PARENT_PATH VARCHAR(255) DEFAULT NULL, TENANT_ID INTEGER DEFAULT 0, PRIMARY KEY (ID) ); diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql index d830a6387ef..7238d37790e 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql @@ -18,6 +18,7 @@ CREATE TABLE IF NOT EXISTS DM_GROUP ( DATE_OF_CREATE BIGINT DEFAULT NULL, DATE_OF_LAST_UPDATE BIGINT DEFAULT NULL, OWNER VARCHAR(45) DEFAULT NULL, + PARENT_PATH VARCHAR(255) DEFAULT NULL, TENANT_ID INTEGER DEFAULT 0, PRIMARY KEY (ID) ); diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/InvokerHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/InvokerHandler.java index 7bf0861f72d..70004095f2d 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/InvokerHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/InvokerHandler.java @@ -85,7 +85,7 @@ public class InvokerHandler extends HttpServlet { ProxyResponse proxyResponse = HandlerUtil.execute(postRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = retryRequestWithRefreshedToken(req, resp, postRequest); + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, postRequest, apiEndpoint); if (proxyResponse == null) { return; } @@ -113,15 +113,19 @@ public class InvokerHandler extends HttpServlet { getRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(getRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = retryRequestWithRefreshedToken(req, resp, getRequest); + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, getRequest, apiEndpoint); if (proxyResponse == null) { return; } } if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { - log.error("Error occurred while invoking the GET API endpoint."); - HandlerUtil.handleError(resp, proxyResponse); - return; + if (proxyResponse.getCode() == HttpStatus.SC_UNAUTHORIZED) { + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, getRequest, apiEndpoint); + } else { + log.error("Error occurred while invoking the GET API endpoint."); + HandlerUtil.handleError(resp, proxyResponse); + return; + } } HandlerUtil.handleSuccess(resp, proxyResponse); } @@ -139,7 +143,7 @@ public class InvokerHandler extends HttpServlet { headRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(headRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = retryRequestWithRefreshedToken(req, resp, headRequest); + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, headRequest, apiEndpoint); if (proxyResponse == null) { return; } @@ -166,7 +170,7 @@ public class InvokerHandler extends HttpServlet { ProxyResponse proxyResponse = HandlerUtil.execute(putRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = retryRequestWithRefreshedToken(req, resp, putRequest); + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, putRequest, apiEndpoint); if (proxyResponse == null) { return; } @@ -194,7 +198,7 @@ public class InvokerHandler extends HttpServlet { deleteRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(deleteRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = retryRequestWithRefreshedToken(req, resp, deleteRequest); + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, deleteRequest, apiEndpoint); if (proxyResponse == null) { return; } @@ -305,7 +309,7 @@ public class InvokerHandler extends HttpServlet { apiEndpoint = System.getProperty("iot.reporting.webapp.host"); if (StringUtils.isBlank(apiEndpoint)){ log.error("Reporting Endpoint is not defined in the iot-server.sh properly."); - handleError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR); + HandlerUtil.handleError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR); return false; } } @@ -313,20 +317,20 @@ public class InvokerHandler extends HttpServlet { HttpSession session = req.getSession(false); if (session == null) { log.error("Unauthorized, You are not logged in. Please log in to the portal"); - handleError(resp, HttpStatus.SC_UNAUTHORIZED); + HandlerUtil.handleError(resp, HttpStatus.SC_UNAUTHORIZED); return false; } authData = (AuthData) session.getAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY); if (authData == null) { log.error("Unauthorized, Access token not found in the current session"); - handleError(resp, HttpStatus.SC_UNAUTHORIZED); + HandlerUtil.handleError(resp, HttpStatus.SC_UNAUTHORIZED); return false; } if (req.getMethod() == null) { log.error("Bad Request, Request method is empty"); - handleError(resp, HttpStatus.SC_BAD_REQUEST); + HandlerUtil.handleError(resp, HttpStatus.SC_BAD_REQUEST); return false; } return true; diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LoginHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LoginHandler.java index b1e34a99da3..5acceaabfd5 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LoginHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LoginHandler.java @@ -24,7 +24,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; import io.entgra.ui.request.interceptor.beans.AuthData; -import io.entgra.ui.request.interceptor.cache.LoginCacheManager; +import io.entgra.ui.request.interceptor.cache.LoginCache; import io.entgra.ui.request.interceptor.cache.OAuthApp; import io.entgra.ui.request.interceptor.cache.OAuthAppCacheKey; import io.entgra.ui.request.interceptor.exceptions.LoginException; @@ -39,7 +39,6 @@ import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.protocol.HTTP; import io.entgra.ui.request.interceptor.beans.ProxyResponse; -import org.json.JSONString; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; @@ -81,10 +80,9 @@ public class LoginHandler extends HttpServlet { httpSession.setMaxInactiveInterval(sessionTimeOut); // Check if OAuth app cache exists. If not create a new application. - LoginCacheManager loginCacheManager = new LoginCacheManager(); - loginCacheManager.initializeCacheManager(); + LoginCache loginCache = HandlerUtil.getLoginCache(httpSession); OAuthAppCacheKey oAuthAppCacheKey = new OAuthAppCacheKey(HandlerConstants.PUBLISHER_APPLICATION_NAME, username); - OAuthApp oAuthApp = loginCacheManager.getOAuthAppCache(oAuthAppCacheKey); + OAuthApp oAuthApp = loginCache.getOAuthAppCache(oAuthAppCacheKey); if (oAuthApp == null) { HttpPost apiRegEndpoint = new HttpPost(gatewayUrl + HandlerConstants.APP_REG_ENDPOINT); @@ -112,8 +110,6 @@ public class LoginHandler extends HttpServlet { clientSecret = jClientAppResultAsJsonObject.get("client_secret").getAsString(); encodedClientApp = Base64.getEncoder() .encodeToString((clientId + HandlerConstants.COLON + clientSecret).getBytes()); - - oAuthAppCacheKey = new OAuthAppCacheKey(HandlerConstants.PUBLISHER_APPLICATION_NAME, username); oAuthApp = new OAuthApp( HandlerConstants.PUBLISHER_APPLICATION_NAME, username, @@ -121,7 +117,7 @@ public class LoginHandler extends HttpServlet { clientSecret, encodedClientApp ); - loginCacheManager.addOAuthAppToCache(oAuthAppCacheKey, oAuthApp); + loginCache.addOAuthAppToCache(oAuthAppCacheKey, oAuthApp); } if (getTokenAndPersistInSession(req, resp, clientId, clientSecret, encodedClientApp, scopes)) { diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginHandler.java index 6ae4c55ed7c..7b93b9cad82 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginHandler.java @@ -23,7 +23,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.google.gson.JsonSyntaxException; -import io.entgra.ui.request.interceptor.cache.LoginCacheManager; +import io.entgra.ui.request.interceptor.cache.LoginCache; import io.entgra.ui.request.interceptor.cache.OAuthApp; import io.entgra.ui.request.interceptor.cache.OAuthAppCacheKey; import io.entgra.ui.request.interceptor.util.HandlerConstants; @@ -81,9 +81,9 @@ public class SsoLoginHandler extends HttpServlet { private JsonObject uiConfigJsonObject; private HttpSession httpSession; - - private LoginCacheManager loginCacheManager; + private LoginCache loginCache; private OAuthApp oAuthApp; + private OAuthAppCacheKey oAuthAppCacheKey; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { @@ -99,13 +99,23 @@ public class SsoLoginHandler extends HttpServlet { baseContextPath = req.getContextPath(); applicationName = baseContextPath.substring(1, baseContextPath.indexOf("-ui-request-handler")); - // Check if oauth app cache is available - loginCacheManager = new LoginCacheManager(); - loginCacheManager.initializeCacheManager(); - oAuthApp = loginCacheManager.getOAuthAppCache( - new OAuthAppCacheKey(applicationName, adminUsername) - ); + String iotsCorePort = System.getProperty(HandlerConstants.IOT_CORE_HTTPS_PORT_ENV_VAR); + if (HandlerConstants.HTTP_PROTOCOL.equals(req.getScheme())) { + iotsCorePort = System.getProperty(HandlerConstants.IOT_CORE_HTTP_PORT_ENV_VAR); + } + gatewayUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_GW_HOST_ENV_VAR) + + HandlerConstants.COLON + HandlerUtil.getGatewayPort(req.getScheme()); + iotsCoreUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR) + + HandlerConstants.COLON + iotsCorePort; + + // Fetch ui config and persists in session + String uiConfigUrl = iotsCoreUrl + HandlerConstants.UI_CONFIG_ENDPOINT; + uiConfigJsonObject = HandlerUtil.getUIConfigAndPersistInSession(uiConfigUrl, gatewayUrl, httpSession, resp); + // Retrieving login cache and do a DCR if the cache is not available. + loginCache = HandlerUtil.getLoginCache(httpSession); + oAuthAppCacheKey = new OAuthAppCacheKey(applicationName, adminUsername); + oAuthApp = loginCache.getOAuthAppCache(oAuthAppCacheKey); if (oAuthApp == null) { dynamicClientRegistration(req, resp); } @@ -143,19 +153,6 @@ public class SsoLoginHandler extends HttpServlet { */ private void dynamicClientRegistration(HttpServletRequest req, HttpServletResponse resp) { try { - String iotsCorePort = System.getProperty(HandlerConstants.IOT_CORE_HTTPS_PORT_ENV_VAR); - - if (HandlerConstants.HTTP_PROTOCOL.equals(req.getScheme())) { - iotsCorePort = System.getProperty(HandlerConstants.IOT_CORE_HTTP_PORT_ENV_VAR); - } - - gatewayUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_GW_HOST_ENV_VAR) - + HandlerConstants.COLON + HandlerUtil.getGatewayPort(req.getScheme()); - iotsCoreUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR) - + HandlerConstants.COLON + iotsCorePort; - String uiConfigUrl = iotsCoreUrl + HandlerConstants.UI_CONFIG_ENDPOINT; - - uiConfigJsonObject = HandlerUtil.getUIConfigAndPersistInSession(uiConfigUrl, gatewayUrl, httpSession, resp); JsonArray tags = uiConfigJsonObject.get("appRegistration").getAsJsonObject().get("tags").getAsJsonArray(); JsonArray scopes = uiConfigJsonObject.get("scopes").getAsJsonArray(); sessionTimeOut = Integer.parseInt(String.valueOf(uiConfigJsonObject.get("sessionTimeOut"))); @@ -191,9 +188,8 @@ public class SsoLoginHandler extends HttpServlet { } // cache the oauth app credentials - OAuthAppCacheKey oAuthAppCacheKey = new OAuthAppCacheKey(applicationName, adminUsername); oAuthApp = new OAuthApp(applicationName, adminUsername, clientId, clientSecret, encodedClientApp); - loginCacheManager.addOAuthAppToCache(oAuthAppCacheKey, oAuthApp); + loginCache.addOAuthAppToCache(oAuthAppCacheKey, oAuthApp); } // Get the details of the registered application diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/UserHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/UserHandler.java index c545567ed87..ef334b89407 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/UserHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/UserHandler.java @@ -85,9 +85,13 @@ public class UserHandler extends HttpServlet { ProxyResponse tokenStatus = HandlerUtil.execute(tokenEndpoint); if (tokenStatus.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { - log.error("Error occurred while invoking the API to get token status."); - HandlerUtil.handleError(resp, tokenStatus); - return; + if (tokenStatus.getCode() == HttpStatus.SC_UNAUTHORIZED) { + tokenStatus = HandlerUtil.retryRequestWithRefreshedToken(req, resp, tokenEndpoint, serverUrl); + } else { + log.error("Error occurred while invoking the API to get token status."); + HandlerUtil.handleError(resp, tokenStatus); + return; + } } String tokenData = tokenStatus.getData(); if (tokenData == null) { diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/cache/LoginCacheManager.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/cache/LoginCache.java similarity index 67% rename from components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/cache/LoginCacheManager.java rename to components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/cache/LoginCache.java index 3ecd741350b..06ca1cef6b9 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/cache/LoginCacheManager.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/cache/LoginCache.java @@ -18,25 +18,19 @@ package io.entgra.ui.request.interceptor.cache; -import io.entgra.ui.request.interceptor.util.HandlerConstants; - -import javax.cache.Cache; -import javax.cache.CacheManager; -import javax.cache.Caching; +import java.util.LinkedHashMap; /** * Contains necessary functions to manage oAuth app cache during login handling */ -public class LoginCacheManager { +public class LoginCache { - private CacheManager cacheManager = null; - private Cache cache = null; + private final LinkedHashMap cache; + private final int capacity; - /** - * Initialize the cache manager if it is not already initialized - */ - public void initializeCacheManager() { - cacheManager = Caching.getCacheManagerFactory().getCacheManager(HandlerConstants.LOGIN_CACHE); + public LoginCache(int capacity) { + this.capacity = capacity; + this.cache = new LinkedHashMap<>(capacity); } /** @@ -46,7 +40,9 @@ public class LoginCacheManager { * @param oAuthApp - The value of the cache which contains OAuth app data */ public void addOAuthAppToCache(OAuthAppCacheKey oAuthAppCacheKey, OAuthApp oAuthApp) { - cache = cacheManager.getCache(HandlerConstants.LOGIN_CACHE); + if (cache.size() == capacity) { + cache.remove(cache.entrySet().iterator().next().getKey()); + } cache.put(oAuthAppCacheKey, oAuthApp); } @@ -57,7 +53,13 @@ public class LoginCacheManager { * @return - Returns OAuthApp object */ public OAuthApp getOAuthAppCache(OAuthAppCacheKey oAuthAppCacheKey) { - cache = cacheManager.getCache(HandlerConstants.LOGIN_CACHE); - return cache.get(oAuthAppCacheKey); + OAuthApp oAuthApp = cache.get(oAuthAppCacheKey); + if (oAuthApp != null) { + if (cache.size() == capacity) { + cache.remove(oAuthAppCacheKey); + cache.put(oAuthAppCacheKey, oAuthApp); + } + } + return oAuthApp; } } diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerConstants.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerConstants.java index 4c864c81e52..f2303aba474 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerConstants.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerConstants.java @@ -55,7 +55,7 @@ public class HandlerConstants { public static final String PASSWORD_GRANT_TYPE = "password"; public static final String JWT_BEARER_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer"; public static final String PRODUCTION_KEY = "PRODUCTION"; - public static final String LOGIN_CACHE = "LOGIN_CACHE"; + public static final String LOGIN_CACHE_CAPACITY_KEY = "loginCacheCapacity"; public static final String SCHEME_SEPARATOR = "://"; public static final String COLON = ":"; diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerUtil.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerUtil.java index 5acec832ce3..ab702eb9ad0 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerUtil.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerUtil.java @@ -23,13 +23,17 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import io.entgra.ui.request.interceptor.beans.AuthData; +import io.entgra.ui.request.interceptor.cache.LoginCache; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.Consts; +import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.entity.ContentType; @@ -60,6 +64,9 @@ import java.io.StringWriter; public class HandlerUtil { private static final Log log = LogFactory.getLog(HandlerUtil.class); + private static LoginCache loginCache = null; + private static boolean isLoginCacheInitialized = false; + private static AuthData authData; /*** * @@ -166,9 +173,11 @@ public class HandlerUtil { /*** + * Handle error requests. * * @param resp {@link HttpServletResponse} - * Return Error Response. + * @param proxyResponse {@link ProxyResponse} + * @throws IOException If error occurred when trying to send the error response. */ public static void handleError(HttpServletResponse resp, ProxyResponse proxyResponse) throws IOException { Gson gson = new Gson(); @@ -188,6 +197,22 @@ public class HandlerUtil { } } + /** + * Handle error requests with custom error codes. + * + * @param resp {@link HttpServletResponse} + * @param errorCode HTTP error status code + * @throws IOException If error occurred when trying to send the error response. + */ + public static void handleError(HttpServletResponse resp, int errorCode) + throws IOException { + ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setCode(errorCode); + proxyResponse.setExecutorResponse( + HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(errorCode)); + HandlerUtil.handleError(resp, proxyResponse); + } + /*** * * @param resp {@link HttpServletResponse} @@ -400,4 +425,136 @@ public class HandlerUtil { return stringOutput; } + + /*** + * Search a key from a given json string object. + * + * @param jsonObjectString - json object in string format. + * @param key - the key to be searched. + * @return string value of the key value. + */ + private static String searchFromJsonObjectString(String jsonObjectString, String key) { + JsonParser jsonParser = new JsonParser(); + JsonElement jsonElement = jsonParser.parse(jsonObjectString); + JsonObject jsonObject = jsonElement.getAsJsonObject(); + return jsonObject.get(key).getAsString(); + } + + /*** + * Initializes the login cache. + * + * @param httpSession - current active HttpSession. + */ + private static void initializeLoginCache(HttpSession httpSession) { + String uiConfig = httpSession.getAttribute(HandlerConstants.UI_CONFIG_KEY).toString(); + int capacity = Integer.parseInt(searchFromJsonObjectString(uiConfig, HandlerConstants.LOGIN_CACHE_CAPACITY_KEY)); + loginCache = new LoginCache(capacity); + } + + /*** + * Retrieves login cache and initializes if its not done already. + * + * @param httpSession - current active HttpSession. + */ + public static LoginCache getLoginCache(HttpSession httpSession) { + if (!isLoginCacheInitialized || loginCache == null) { + isLoginCacheInitialized = true; + initializeLoginCache(httpSession); + } + return loginCache; + } + + /** + * Retry request again after refreshing the access token + * + * @param req incoming {@link HttpServletRequest} + * @param resp resp {@link HttpServletResponse} + * @param httpRequest subclass of {@link HttpRequestBase} related to the current request. + * @return {@link ProxyResponse} if successful and null if failed. + * @throws IOException If an error occurs when try to retry the request. + */ + public static ProxyResponse retryRequestWithRefreshedToken(HttpServletRequest req, HttpServletResponse resp, + HttpRequestBase httpRequest, String apiEndpoint) throws IOException { + if (refreshToken(req, resp, apiEndpoint)) { + HttpSession session = req.getSession(false); + if (session == null) { + log.error("Unauthorized, You are not logged in. Please log in to the portal"); + handleError(resp, HttpStatus.SC_UNAUTHORIZED); + return null; + } + httpRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); + ProxyResponse proxyResponse = HandlerUtil.execute(httpRequest); + if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { + log.error("Error occurred while invoking the API after refreshing the token."); + HandlerUtil.handleError(resp, proxyResponse); + return null; + } + return proxyResponse; + } + return null; + } + + /*** + * This method is responsible to get the refresh token + * + * @param req {@link HttpServletRequest} + * @param resp {@link HttpServletResponse} + * @return If successfully renew tokens, returns TRUE otherwise return FALSE + * @throws IOException If an error occurs while witting error response to client side or invoke token renewal API + */ + private static boolean refreshToken(HttpServletRequest req, HttpServletResponse resp, String gatewayUrl) + throws IOException { + if (log.isDebugEnabled()) { + log.debug("refreshing the token"); + } + HttpPost tokenEndpoint = new HttpPost( + gatewayUrl + HandlerConstants.TOKEN_ENDPOINT); + HttpSession session = req.getSession(false); + if (session == null) { + log.error("Couldn't find a session, hence it is required to login and proceed."); + handleError(resp, HttpStatus.SC_UNAUTHORIZED); + return false; + } + + authData = (AuthData) session.getAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY); + StringEntity tokenEndpointPayload = new StringEntity( + "grant_type=refresh_token&refresh_token=" + authData.getRefreshToken() + "&scope=PRODUCTION", + ContentType.APPLICATION_FORM_URLENCODED); + + tokenEndpoint.setEntity(tokenEndpointPayload); + String encodedClientApp = authData.getEncodedClientApp(); + tokenEndpoint.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC + + encodedClientApp); + tokenEndpoint.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.toString()); + + ProxyResponse tokenResultResponse = HandlerUtil.execute(tokenEndpoint); + if (tokenResultResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { + log.error("Error occurred while refreshing access token."); + HandlerUtil.handleError(resp, tokenResultResponse); + return false; + } + + JsonParser jsonParser = new JsonParser(); + JsonElement jTokenResult = jsonParser.parse(tokenResultResponse.getData()); + + if (jTokenResult.isJsonObject()) { + JsonObject jTokenResultAsJsonObject = jTokenResult.getAsJsonObject(); + AuthData newAuthData = new AuthData(); + + newAuthData.setAccessToken(jTokenResultAsJsonObject.get("access_token").getAsString()); + newAuthData.setRefreshToken(jTokenResultAsJsonObject.get("refresh_token").getAsString()); + newAuthData.setScope(jTokenResultAsJsonObject.get("scope").getAsString()); + newAuthData.setClientId(authData.getClientId()); + newAuthData.setClientSecret(authData.getClientSecret()); + newAuthData.setEncodedClientApp(authData.getEncodedClientApp()); + newAuthData.setUsername(authData.getUsername()); + authData = newAuthData; + session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, newAuthData); + return true; + } + + log.error("Error Occurred in token renewal process."); + handleError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR); + return false; + } } diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/mdm-ui-config.xml b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/mdm-ui-config.xml index 36fe0250343..aa3f169a33d 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/mdm-ui-config.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/mdm-ui-config.xml @@ -22,6 +22,8 @@ true 3600 + + 10000 application_management diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql index bda86bc93b8..f6894eb4ac1 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql @@ -23,6 +23,7 @@ CREATE TABLE IF NOT EXISTS DM_GROUP ( STATUS VARCHAR(50) DEFAULT NULL, DESCRIPTION TEXT DEFAULT NULL, OWNER VARCHAR(255) DEFAULT NULL, + PARENT_PATH VARCHAR(255) DEFAULT NULL, TENANT_ID INTEGER DEFAULT 0, PRIMARY KEY (ID) ); diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql index 247d796c52e..c27f8914452 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql @@ -32,6 +32,7 @@ IF NOT EXISTS(SELECT * STATUS VARCHAR(50) DEFAULT NULL, DESCRIPTION VARCHAR(MAX) DEFAULT NULL, OWNER VARCHAR(255) DEFAULT NULL, + PARENT_PATH VARCHAR(255) DEFAULT NULL, TENANT_ID INTEGER DEFAULT 0, PRIMARY KEY (ID) ); diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql index cc9e8f12733..a69b79b7832 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql @@ -27,6 +27,7 @@ CREATE TABLE IF NOT EXISTS DM_GROUP ( STATUS VARCHAR(50) DEFAULT NULL, DESCRIPTION TEXT DEFAULT NULL, OWNER VARCHAR(255) DEFAULT NULL, + PARENT_PATH VARCHAR(255) DEFAULT NULL, TENANT_ID INTEGER DEFAULT 0, PRIMARY KEY (ID) ) diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql index 007a7008f06..e2e4693bd23 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql @@ -50,6 +50,7 @@ CREATE TABLE DM_GROUP ( GROUP_NAME VARCHAR2(100) DEFAULT NULL, STATUS VARCHAR2(50) DEFAULT NULL, OWNER VARCHAR2(255) DEFAULT NULL, + PARENT_PATH VARCHAR2(255) DEFAULT NULL, TENANT_ID NUMBER(10) DEFAULT 0, CONSTRAINT PK_DM_GROUP PRIMARY KEY (ID) ) diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql index db431497e8a..bd8eb8771ac 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql @@ -21,6 +21,7 @@ CREATE TABLE IF NOT EXISTS DM_GROUP ( GROUP_NAME VARCHAR(100) DEFAULT NULL, DESCRIPTION TEXT DEFAULT NULL, OWNER VARCHAR(45) DEFAULT NULL, + PARENT_PATH VARCHAR(255) DEFAULT NULL, TENANT_ID INTEGER DEFAULT 0, PRIMARY KEY (ID) ) @@ -732,4 +733,4 @@ CREATE TABLE IF NOT EXISTS DM_GEOFENCE ( PRIMARY KEY (ID) ); --- END OF DM_GEOFENCE TABLE-- \ No newline at end of file +-- END OF DM_GEOFENCE TABLE--