From 4d0c0d2af994718024ba81fd05b3fd747724598e Mon Sep 17 00:00:00 2001 From: Ace Date: Fri, 4 Nov 2016 19:16:44 +0530 Subject: [PATCH] restructuring iot-plugins removing mobile/iot base plugins and other restructuring --- pom.xml | 263 ++++++++++++ .../service/impl/VirtualFireAlarmService.java | 73 ++++ .../impl/VirtualFireAlarmServiceImpl.java | 347 ++++++++++++++++ .../service/impl/dto/SensorRecord.java | 68 +++ .../exception/VirtualFireAlarmException.java | 35 ++ .../service/impl/util/APIUtil.java | 198 +++++++++ .../util/VirtualFireAlarmServiceUtils.java | 51 +++ .../util/VirtualFireAlarmUtilConstants.java | 26 ++ .../service/impl/util/ZipArchive.java | 43 ++ .../service/impl/util/ZipUtil.java | 387 ++++++++++++++++++ .../service/impl/util/util/Utils.java | 61 +++ src/main/webapp/META-INF/permissions.xml | 52 +++ .../webapp/META-INF/webapp-classloading.xml | 33 ++ src/main/webapp/WEB-INF/cxf-servlet.xml | 38 ++ src/main/webapp/WEB-INF/web.xml | 42 ++ 15 files changed, 1717 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmService.java create mode 100644 src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmServiceImpl.java create mode 100644 src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/dto/SensorRecord.java create mode 100644 src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/exception/VirtualFireAlarmException.java create mode 100644 src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/APIUtil.java create mode 100644 src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/VirtualFireAlarmServiceUtils.java create mode 100644 src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/VirtualFireAlarmUtilConstants.java create mode 100644 src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/ZipArchive.java create mode 100644 src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/ZipUtil.java create mode 100644 src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/util/Utils.java create mode 100644 src/main/webapp/META-INF/permissions.xml create mode 100644 src/main/webapp/META-INF/webapp-classloading.xml create mode 100644 src/main/webapp/WEB-INF/cxf-servlet.xml create mode 100644 src/main/webapp/WEB-INF/web.xml diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..83eefe902 --- /dev/null +++ b/pom.xml @@ -0,0 +1,263 @@ + + + + + + virtual-fire-alarm-plugin + org.wso2.carbon.devicemgt-plugins + 2.2.5-SNAPSHOT + ../pom.xml + + + 4.0.0 + org.wso2.carbon.device.mgt.iot.virtualfirealarm.api + war + WSO2 Carbon - IoT Server VirtualFireAlarm API + WSO2 Carbon - Virtual FireAlarm Service Management API Implementation + http://wso2.org + + + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.common + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.core + provided + + + org.apache.axis2.wso2 + axis2-client + + + + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.analytics.data.publisher + provided + + + org.apache.axis2.wso2 + axis2-client + + + + + + org.wso2.carbon.devicemgt + org.wso2.carbon.certificate.mgt.core + provided + + + commons-codec + commons-codec + + + + + + + + org.apache.cxf + cxf-rt-frontend-jaxws + provided + + + org.apache.cxf + cxf-rt-frontend-jaxrs + provided + + + org.apache.cxf + cxf-rt-transports-http + provided + + + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + provided + + + + + org.apache.httpcomponents + httpasyncclient + 4.1 + provided + + + + + org.codehaus.jackson + jackson-core-asl + + + org.codehaus.jackson + jackson-jaxrs + + + javax + javaee-web-api + provided + + + javax.ws.rs + jsr311-api + provided + + + commons-httpclient.wso2 + commons-httpclient + provided + + + + org.wso2.carbon + org.wso2.carbon.utils + provided + + + org.bouncycastle.wso2 + bcprov-jdk15on + + + org.wso2.carbon + org.wso2.carbon.user.api + + + org.wso2.carbon + org.wso2.carbon.queuing + + + org.wso2.carbon + org.wso2.carbon.base + + + org.apache.axis2.wso2 + axis2 + + + org.igniterealtime.smack.wso2 + smack + + + org.igniterealtime.smack.wso2 + smackx + + + jaxen + jaxen + + + commons-fileupload.wso2 + commons-fileupload + + + org.apache.ant.wso2 + ant + + + org.apache.ant.wso2 + ant + + + commons-httpclient.wso2 + commons-httpclient + + + org.eclipse.equinox + javax.servlet + + + org.wso2.carbon + org.wso2.carbon.registry.api + + + + + commons-codec + commons-codec + + + org.wso2.carbon.devicemgt + org.wso2.carbon.apimgt.annotations + provided + + + org.igniterealtime.smack.wso2 + smack + provided + + + org.igniterealtime.smack.wso2 + smackx + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.identity.jwt.client.extension + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.apimgt.application.extension + provided + + + org.wso2.carbon.analytics + org.wso2.carbon.analytics.api + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.extensions + provided + + + org.wso2.carbon.devicemgt-plugins + org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin + provided + + + + + + maven-compiler-plugin + + UTF-8 + ${wso2.maven.compiler.source} + ${wso2.maven.compiler.target} + + + + maven-war-plugin + + virtual_firealarm + + + + + + diff --git a/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmService.java b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmService.java new file mode 100644 index 000000000..9d89050fb --- /dev/null +++ b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmService.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.iot.virtualfirealarm.service.impl; + +import org.wso2.carbon.apimgt.annotations.api.API; +import org.wso2.carbon.apimgt.annotations.api.Scope; +import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.DeviceType; +import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.Feature; + +import javax.ws.rs.*; +import javax.ws.rs.core.Response; + +/** + * This class consists the functions/APIs specific to the "actions" of the VirtualFirealarm device-type. These APIs + * include the ones that are used by the [Device] to contact the server (i.e: Enrollment & Publishing Data) and the + * ones used by the [Server/Owner] to contact the [Device] (i.e: sending control signals). This class also initializes + * the transport 'Connectors' [XMPP & MQTT] specific to the VirtualFirealarm device-type in order to communicate with + * such devices and to receive messages form it. + */ +@API(name = "virtual_firealarm", version = "1.0.0", context = "/virtual_firealarm", tags = {"virtual_firealarm"}) +@DeviceType(value = "virtual_firealarm") +public interface VirtualFireAlarmService { + + /** + * This is an API called/used from within the Server(Front-End) or by a device Owner. It sends a control command to + * the VirtualFirealarm device to switch `ON` or `OFF` its buzzer. The method also takes in the protocol to be used + * to connect-to and send the command to the device. + * + * @param deviceId the ID of the VirtualFirealarm device on which the buzzer needs to switched `ON` or `OFF`. + * @param state the state to which the buzzer on the device needs to be changed. Either "ON" or "OFF". + * (Case-Insensitive String) + */ + @POST + @Path("device/{deviceId}/buzz") + @Scope(key = "device:firealarm:enroll", name = "", description = "") + @Feature(code = "buzz", name = "Buzzer On / Off", description = "Switch on/off Virtual Fire Alarm Buzzer. (On / Off)") + Response switchBuzzer(@PathParam("deviceId") String deviceId, + @FormParam("state") String state); + + /** + * Retrieve Sensor data for the device type + */ + @Path("device/stats/{deviceId}") + @GET + @Scope(key = "device:firealarm:enroll", name = "", description = "") + @Consumes("application/json") + @Produces("application/json") + Response getVirtualFirealarmStats(@PathParam("deviceId") String deviceId, @QueryParam("from") long from, + @QueryParam("to") long to); + + @Path("device/download") + @GET + @Produces("application/zip") + @Scope(key = "device:firealarm:enroll", name = "", description = "") + Response downloadSketch(@QueryParam("deviceName") String deviceName, @QueryParam("sketchType") String sketchType); + +} diff --git a/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmServiceImpl.java b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmServiceImpl.java new file mode 100644 index 000000000..ce54d9537 --- /dev/null +++ b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/VirtualFireAlarmServiceImpl.java @@ -0,0 +1,347 @@ +/* +* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. +* +* WSO2 Inc. 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.iot.virtualfirealarm.service.impl; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.FileUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.analytics.dataservice.commons.SortByField; +import org.wso2.carbon.analytics.dataservice.commons.SortType; +import org.wso2.carbon.analytics.datasource.commons.exception.AnalyticsException; +import org.wso2.carbon.apimgt.application.extension.APIManagementProviderService; +import org.wso2.carbon.apimgt.application.extension.dto.ApiApplicationKey; +import org.wso2.carbon.apimgt.application.extension.exception.APIManagerException; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.common.*; +import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationException; +import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroupConstants; +import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; +import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException; +import org.wso2.carbon.device.mgt.core.operation.mgt.CommandOperation; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.constants.VirtualFireAlarmConstants; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.exception.VirtualFirealarmDeviceMgtPluginException; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.impl.VirtualFirealarmSecurityManager; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.xmpp.XmppAccount; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.xmpp.XmppConfig; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.xmpp.XmppServerClient; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.dto.SensorRecord; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.exception.VirtualFireAlarmException; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.util.APIUtil; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.util.VirtualFireAlarmServiceUtils; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.util.ZipArchive; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.util.ZipUtil; +import org.wso2.carbon.identity.jwt.client.extension.JWTClient; +import org.wso2.carbon.identity.jwt.client.extension.dto.AccessTokenInfo; +import org.wso2.carbon.identity.jwt.client.extension.exception.JWTClientException; +import org.wso2.carbon.user.api.UserStoreException; + +import javax.ws.rs.Consumes; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.PrivateKey; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.UUID; + +public class VirtualFireAlarmServiceImpl implements VirtualFireAlarmService { + + private static final String XMPP_PROTOCOL = "XMPP"; + private static final String MQTT_PROTOCOL = "MQTT"; + private static final String KEY_TYPE = "PRODUCTION"; + private static ApiApplicationKey apiApplicationKey; + private static final String DEVICE_MGT_SCOPE_IDENTIFIER = "device-mgt"; + private static Log log = LogFactory.getLog(VirtualFireAlarmServiceImpl.class); + + @POST + @Path("device/{deviceId}/buzz") + public Response switchBuzzer(@PathParam("deviceId") String deviceId, @FormParam("state") String state) { + if (state == null || state.isEmpty()) { + log.error("State is not defined for the buzzer operation"); + return Response.status(Response.Status.BAD_REQUEST).build(); + } + String switchToState = state.toUpperCase(); + if (!switchToState.equals(VirtualFireAlarmConstants.STATE_ON) && !switchToState.equals( + VirtualFireAlarmConstants.STATE_OFF)) { + log.error("The requested state change shoud be either - 'ON' or 'OFF'"); + return Response.status(Response.Status.BAD_REQUEST).build(); + } + try { + if (!APIUtil.getDeviceAccessAuthorizationService().isUserAuthorized( + new DeviceIdentifier(deviceId, VirtualFireAlarmConstants.DEVICE_TYPE), + DeviceGroupConstants.Permissions.DEFAULT_OPERATOR_PERMISSIONS)) { + return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build(); + } + String resource = VirtualFireAlarmConstants.BULB_CONTEXT.replace("/", ""); + PrivateKey serverPrivateKey = VirtualFirealarmSecurityManager.getServerPrivateKey(); + String actualMessage = resource + ":" + switchToState; + String encryptedMsg = VirtualFireAlarmServiceUtils.prepareSecurePayLoad(actualMessage, + serverPrivateKey); + String publishTopic = APIUtil.getTenantDomainOftheUser() + "/" + + VirtualFireAlarmConstants.DEVICE_TYPE + "/" + deviceId; + + Operation commandOp = new CommandOperation(); + commandOp.setCode("buzz"); + commandOp.setType(Operation.Type.COMMAND); + commandOp.setEnabled(true); + commandOp.setPayLoad(encryptedMsg); + + Properties props = new Properties(); + props.setProperty(VirtualFireAlarmConstants.MQTT_ADAPTER_TOPIC_PROPERTY_NAME, publishTopic); + props.setProperty(VirtualFireAlarmConstants.CLIENT_JID_PROPERTY_KEY, deviceId + "@" + XmppConfig + .getInstance().getServerName()); + props.setProperty(VirtualFireAlarmConstants.SUBJECT_PROPERTY_KEY, "CONTROL-REQUEST"); + props.setProperty(VirtualFireAlarmConstants.MESSAGE_TYPE_PROPERTY_KEY, + VirtualFireAlarmConstants.CHAT_PROPERTY_KEY); + commandOp.setProperties(props); + + List deviceIdentifiers = new ArrayList<>(); + deviceIdentifiers.add(new DeviceIdentifier(deviceId, VirtualFireAlarmConstants.DEVICE_TYPE)); + APIUtil.getDeviceManagementService().addOperation(VirtualFireAlarmConstants.DEVICE_TYPE, commandOp, + deviceIdentifiers); + return Response.ok().build(); + } catch (InvalidDeviceException e) { + String msg = "Error occurred while executing command operation to send keywords"; + log.error(msg, e); + return Response.status(Response.Status.BAD_REQUEST).build(); + } catch (DeviceAccessAuthorizationException e) { + log.error(e.getErrorMessage(), e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } catch (VirtualFireAlarmException e) { + String errorMsg = "Preparing Secure payload failed for device - [" + deviceId + "]"; + log.error(errorMsg); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } catch (OperationManagementException e) { + String msg = "Error occurred while executing command operation upon ringing the buzzer"; + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } + + @PUT + @Path("device/{deviceId}/policy") + public Response updatePolicy(@PathParam("deviceId") String deviceId, @QueryParam("protocol") String protocol, + @FormParam("policy") String policy) { + String protocolString = protocol.toUpperCase(); + if (log.isDebugEnabled()) { + log.debug("Sending request to update-policy of device [" + deviceId + "] via " + protocolString); + } + try { + if (!APIUtil.getDeviceAccessAuthorizationService().isUserAuthorized( + new DeviceIdentifier(deviceId, VirtualFireAlarmConstants.DEVICE_TYPE), + DeviceGroupConstants.Permissions.DEFAULT_MANAGE_POLICIES_PERMISSIONS)) { + return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build(); + } + PrivateKey serverPrivateKey = VirtualFirealarmSecurityManager.getServerPrivateKey(); + String actualMessage = VirtualFireAlarmConstants.POLICY_CONTEXT + ":" + policy; + String encryptedMsg = VirtualFireAlarmServiceUtils.prepareSecurePayLoad(actualMessage, + serverPrivateKey); + Map dynamicProperties = new HashMap<>(); + switch (protocolString) { + case XMPP_PROTOCOL: + dynamicProperties.put(VirtualFireAlarmConstants.JID_PROPERTY_KEY, + deviceId + "@" + XmppConfig.getInstance().getServerName()); + dynamicProperties.put(VirtualFireAlarmConstants.SUBJECT_PROPERTY_KEY, "POLICTY-REQUEST"); + dynamicProperties.put(VirtualFireAlarmConstants.MESSAGE_TYPE_PROPERTY_KEY, + VirtualFireAlarmConstants.CHAT_PROPERTY_KEY); + APIUtil.getOutputEventAdapterService().publish(VirtualFireAlarmConstants.XMPP_ADAPTER_NAME, + dynamicProperties, encryptedMsg); + break; + default: + + String publishTopic = APIUtil.getTenantDomainOftheUser() + "/" + + VirtualFireAlarmConstants.DEVICE_TYPE + "/" + deviceId; + dynamicProperties.put(VirtualFireAlarmConstants.ADAPTER_TOPIC_PROPERTY, publishTopic); + APIUtil.getOutputEventAdapterService().publish(VirtualFireAlarmConstants.MQTT_ADAPTER_NAME, + dynamicProperties, encryptedMsg); + break; + } + return Response.ok().build(); + } catch (DeviceAccessAuthorizationException e) { + log.error(e.getErrorMessage(), e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } catch (VirtualFireAlarmException e) { + log.error(e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } + + + @Path("device/stats/{deviceId}") + @GET + @Consumes("application/json") + @Produces("application/json") + public Response getVirtualFirealarmStats(@PathParam("deviceId") String deviceId, @QueryParam("from") long from, + @QueryParam("to") long to) { + String fromDate = String.valueOf(from); + String toDate = String.valueOf(to); + String query = "deviceId:" + deviceId + " AND deviceType:" + + VirtualFireAlarmConstants.DEVICE_TYPE + " AND time : [" + fromDate + " TO " + toDate + "]"; + String sensorTableName = VirtualFireAlarmConstants.TEMPERATURE_EVENT_TABLE; + try { + if (!APIUtil.getDeviceAccessAuthorizationService().isUserAuthorized( + new DeviceIdentifier(deviceId, VirtualFireAlarmConstants.DEVICE_TYPE), + DeviceGroupConstants.Permissions.DEFAULT_STATS_MONITOR_PERMISSIONS)) { + return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build(); + } + List sortByFields = new ArrayList<>(); + SortByField sortByField = new SortByField("time", SortType.ASC); + sortByFields.add(sortByField); + List sensorRecords = APIUtil.getAllEventsForDevice(sensorTableName, query, sortByFields); + return Response.status(Response.Status.OK.getStatusCode()).entity(sensorRecords).build(); + } catch (AnalyticsException e) { + String errorMsg = "Error on retrieving stats on table " + sensorTableName + " with query " + query; + log.error(errorMsg); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).entity(errorMsg).build(); + } catch (DeviceAccessAuthorizationException e) { + log.error(e.getErrorMessage(), e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + } + + @Path("device/download") + @GET + @Produces("application/zip") + public Response downloadSketch(@QueryParam("deviceName") String deviceName, + @QueryParam("sketchType") String sketchType) { + try { + ZipArchive zipFile = createDownloadFile(APIUtil.getAuthenticatedUser(), deviceName, sketchType); + Response.ResponseBuilder response = Response.ok(FileUtils.readFileToByteArray(zipFile.getZipFile())); + response.status(Response.Status.OK); + response.type("application/zip"); + response.header("Content-Disposition", "attachment; filename=\"" + zipFile.getFileName() + "\""); + Response resp = response.build(); + zipFile.getZipFile().delete(); + return resp; + } catch (IllegalArgumentException ex) { + return Response.status(400).entity(ex.getMessage()).build();//bad request + } catch (DeviceManagementException ex) { + log.error(ex.getMessage(), ex); + return Response.status(500).entity(ex.getMessage()).build(); + } catch (JWTClientException ex) { + log.error(ex.getMessage(), ex); + return Response.status(500).entity(ex.getMessage()).build(); + } catch (APIManagerException ex) { + log.error(ex.getMessage(), ex); + return Response.status(500).entity(ex.getMessage()).build(); + } catch (IOException ex) { + log.error(ex.getMessage(), ex); + return Response.status(500).entity(ex.getMessage()).build(); + } catch (UserStoreException ex) { + log.error(ex.getMessage(), ex); + return Response.status(500).entity(ex.getMessage()).build(); + } catch (VirtualFirealarmDeviceMgtPluginException ex) { + log.error(ex.getMessage(), ex); + return Response.status(500).entity(ex.getMessage()).build(); + } + } + + private boolean register(String deviceId, String name) { + try { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(); + deviceIdentifier.setId(deviceId); + deviceIdentifier.setType(VirtualFireAlarmConstants.DEVICE_TYPE); + if (APIUtil.getDeviceManagementService().isEnrolled(deviceIdentifier)) { + return false; + } + Device device = new Device(); + device.setDeviceIdentifier(deviceId); + EnrolmentInfo enrolmentInfo = new EnrolmentInfo(); + enrolmentInfo.setDateOfEnrolment(new Date().getTime()); + enrolmentInfo.setDateOfLastUpdate(new Date().getTime()); + enrolmentInfo.setStatus(EnrolmentInfo.Status.ACTIVE); + enrolmentInfo.setOwnership(EnrolmentInfo.OwnerShip.BYOD); + device.setName(name); + device.setType(VirtualFireAlarmConstants.DEVICE_TYPE); + enrolmentInfo.setOwner(APIUtil.getAuthenticatedUser()); + device.setEnrolmentInfo(enrolmentInfo); + return APIUtil.getDeviceManagementService().enrollDevice(device); + } catch (DeviceManagementException e) { + log.error(e.getMessage(), e); + return false; + } + } + + private ZipArchive createDownloadFile(String owner, String deviceName, String sketchType) + throws DeviceManagementException, APIManagerException, JWTClientException, + UserStoreException, VirtualFirealarmDeviceMgtPluginException { + //create new device id + String deviceId = shortUUID(); + boolean status = register(deviceId, deviceName); + if (!status) { + String msg = "Error occurred while registering the device with " + "id: " + deviceId + " owner:" + owner; + throw new DeviceManagementException(msg); + } + if (apiApplicationKey == null) { + String applicationUsername = + PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm().getRealmConfiguration() + .getAdminUserName(); + APIManagementProviderService apiManagementProviderService = APIUtil.getAPIManagementProviderService(); + String[] tags = {VirtualFireAlarmConstants.DEVICE_TYPE}; + apiApplicationKey = apiManagementProviderService.generateAndRetrieveApplicationKeys( + VirtualFireAlarmConstants.DEVICE_TYPE, tags, KEY_TYPE, applicationUsername, true); + } + JWTClient jwtClient = APIUtil.getJWTClientManagerService().getJWTClient(); + String device = "{ \"scope\":\"mqtt-publisher mqtt-subscriber\", \"deviceIdentifiers\":[{\"id\":\""+deviceId+"\", " + + "\"type\":\""+VirtualFireAlarmConstants.DEVICE_TYPE+"\"}]}"; + Map params = new HashMap(); + params.put("device", Base64.encodeBase64String(device.getBytes())); + AccessTokenInfo accessTokenInfo = jwtClient.getAccessToken(apiApplicationKey.getConsumerKey(), + apiApplicationKey.getConsumerSecret(), owner, + null, params); + String accessToken = accessTokenInfo.getAccessToken(); + String refreshToken = accessTokenInfo.getRefreshToken(); + XmppAccount newXmppAccount = new XmppAccount(); + newXmppAccount.setAccountName(deviceId); + newXmppAccount.setUsername(deviceId); + newXmppAccount.setPassword(accessToken); + newXmppAccount.setEmail(deviceId + "@" + APIUtil.getTenantDomainOftheUser()); + + status = XmppServerClient.createAccount(newXmppAccount); + if (!status) { + String msg = "XMPP Account was not created for device - " + deviceId + " of owner - " + owner + + ".XMPP might have been disabled in org.wso2.carbon.device.mgt.iot" + + ".common.config.server.configs"; + throw new DeviceManagementException(msg); + } + ZipUtil ziputil = new ZipUtil(); + return ziputil.createZipFile(owner, sketchType, deviceId, deviceName, apiApplicationKey.toString(), + accessToken, refreshToken); + } + + private static String shortUUID() { + UUID uuid = UUID.randomUUID(); + long l = ByteBuffer.wrap(uuid.toString().getBytes(StandardCharsets.UTF_8)).getLong(); + return Long.toString(l, Character.MAX_RADIX); + } +} diff --git a/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/dto/SensorRecord.java b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/dto/SensorRecord.java new file mode 100644 index 000000000..e1f46d2d7 --- /dev/null +++ b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/dto/SensorRecord.java @@ -0,0 +1,68 @@ +package org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.dto; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@XmlRootElement +/** + * This stores sensor event data for android sense. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class SensorRecord { + + @XmlElementWrapper(required = true, name = "values") + private Map values; + + /** The id. */ + @XmlElement(required = false, name = "id") + private String id; + + /** + * Gets the values. + * @return the values + */ + public Map getValues() { + return values; + } + + /** + * Sets the values. + * @param values the values + */ + public void setValues(Map values) { + this.values = values; + } + + /** + * Sets the id. + * @param id the new id + */ + public void setId(String id) { + this.id = id; + } + + /** + * Gets the id. + * @return the id + */ + public String getId() { + return id; + } + + @Override + public String toString(){ + List valueList = new ArrayList(); + for (Map.Entry entry : values.entrySet()) { + valueList.add(entry.getKey() + ":" + entry.getValue()); + } + return valueList.toString(); + + } + +} diff --git a/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/exception/VirtualFireAlarmException.java b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/exception/VirtualFireAlarmException.java new file mode 100644 index 000000000..4305ec7c0 --- /dev/null +++ b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/exception/VirtualFireAlarmException.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.iot.virtualfirealarm.service.impl.exception; + +public class VirtualFireAlarmException extends Exception { + private static final long serialVersionUID = 118512086957330189L; + + public VirtualFireAlarmException(String errorMessage) { + super(errorMessage); + } + + public VirtualFireAlarmException(String errorMessage, Throwable throwable) { + super(errorMessage, throwable); + } + + public VirtualFireAlarmException(Throwable cause) { + super(cause); + } +} diff --git a/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/APIUtil.java b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/APIUtil.java new file mode 100644 index 000000000..0509eb80b --- /dev/null +++ b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/APIUtil.java @@ -0,0 +1,198 @@ +package org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.analytics.api.AnalyticsDataAPI; +import org.wso2.carbon.analytics.dataservice.commons.AnalyticsDataResponse; +import org.wso2.carbon.analytics.dataservice.commons.AnalyticsDrillDownRequest; +import org.wso2.carbon.analytics.dataservice.commons.SearchResultEntry; +import org.wso2.carbon.analytics.dataservice.commons.SortByField; +import org.wso2.carbon.analytics.dataservice.core.AnalyticsDataServiceUtils; +import org.wso2.carbon.analytics.datasource.commons.Record; +import org.wso2.carbon.analytics.datasource.commons.exception.AnalyticsException; +import org.wso2.carbon.apimgt.application.extension.APIManagementProviderService; +import org.wso2.carbon.context.CarbonContext; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationService; +import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfigurationManagementService; +import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.dto.SensorRecord; +import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterService; +import org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This class provides utility functions used by REST-API. + */ +public class APIUtil { + + private static Log log = LogFactory.getLog(APIUtil.class); + + public static String getAuthenticatedUser() { + PrivilegedCarbonContext threadLocalCarbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + String username = threadLocalCarbonContext.getUsername(); + String tenantDomain = threadLocalCarbonContext.getTenantDomain(); + if (username.endsWith(tenantDomain)) { + return username.substring(0, username.lastIndexOf("@")); + } + return username; + } + + public static DeviceManagementProviderService getDeviceManagementService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + DeviceManagementProviderService deviceManagementProviderService = + (DeviceManagementProviderService) ctx.getOSGiService(DeviceManagementProviderService.class, null); + if (deviceManagementProviderService == null) { + String msg = "Device Management service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return deviceManagementProviderService; + } + + public static AnalyticsDataAPI getAnalyticsDataAPI() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + AnalyticsDataAPI analyticsDataAPI = + (AnalyticsDataAPI) ctx.getOSGiService(AnalyticsDataAPI.class, null); + if (analyticsDataAPI == null) { + String msg = "Analytics api service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return analyticsDataAPI; + } + + public static List getAllEventsForDevice(String tableName, String query, + List sortByFields) throws AnalyticsException { + int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); + AnalyticsDataAPI analyticsDataAPI = getAnalyticsDataAPI(); + int eventCount = analyticsDataAPI.searchCount(tenantId, tableName, query); + if (eventCount == 0) { + return null; + } + List resultEntries = analyticsDataAPI.search(tenantId, tableName, query, 0, eventCount, + sortByFields); + List recordIds = getRecordIds(resultEntries); + AnalyticsDataResponse response = analyticsDataAPI.get(tenantId, tableName, 1, null, recordIds); + Map sensorDatas = createSensorData(AnalyticsDataServiceUtils.listRecords( + analyticsDataAPI, response)); + List sortedSensorData = getSortedSensorData(sensorDatas, resultEntries); + return sortedSensorData; + } + + private static List getRecordIds(List searchResults) { + List ids = new ArrayList<>(); + for (SearchResultEntry searchResult : searchResults) { + ids.add(searchResult.getId()); + } + return ids; + } + + public static List getSortedSensorData(Map sensorDatas, + List searchResults) { + List sortedRecords = new ArrayList<>(); + for (SearchResultEntry searchResultEntry : searchResults) { + sortedRecords.add(sensorDatas.get(searchResultEntry.getId())); + } + return sortedRecords; + } + + /** + * Creates the SensorDatas from records. + * + * @param records the records + * @return the Map of SensorRecord + */ + public static Map createSensorData(List records) { + Map sensorDatas = new HashMap<>(); + for (Record record : records) { + SensorRecord sensorData = createSensorData(record); + sensorDatas.put(sensorData.getId(), sensorData); + } + return sensorDatas; + } + + /** + * Create a SensorRecord object out of a Record object + * + * @param record the record object + * @return SensorRecord object + */ + public static SensorRecord createSensorData(Record record) { + SensorRecord recordBean = new SensorRecord(); + recordBean.setId(record.getId()); + recordBean.setValues(record.getValues()); + return recordBean; + } + + public static APIManagementProviderService getAPIManagementProviderService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + APIManagementProviderService apiManagementProviderService = + (APIManagementProviderService) ctx.getOSGiService(APIManagementProviderService.class, null); + if (apiManagementProviderService == null) { + String msg = "API management provider service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return apiManagementProviderService; + } + + public static JWTClientManagerService getJWTClientManagerService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + JWTClientManagerService jwtClientManagerService = + (JWTClientManagerService) ctx.getOSGiService(JWTClientManagerService.class, null); + if (jwtClientManagerService == null) { + String msg = "JWT Client manager service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return jwtClientManagerService; + } + + public static String getTenantDomainOftheUser() { + PrivilegedCarbonContext threadLocalCarbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + return threadLocalCarbonContext.getTenantDomain(); + } + + public static DeviceAccessAuthorizationService getDeviceAccessAuthorizationService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + DeviceAccessAuthorizationService deviceAccessAuthorizationService = + (DeviceAccessAuthorizationService) ctx.getOSGiService(DeviceAccessAuthorizationService.class, null); + if (deviceAccessAuthorizationService == null) { + String msg = "Device Authorization service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return deviceAccessAuthorizationService; + } + + public static OutputEventAdapterService getOutputEventAdapterService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + OutputEventAdapterService outputEventAdapterService = + (OutputEventAdapterService) ctx.getOSGiService(OutputEventAdapterService.class, null); + if (outputEventAdapterService == null) { + String msg = "Device Authorization service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return outputEventAdapterService; + } + + public static PlatformConfigurationManagementService getTenantConfigurationManagementService() { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + PlatformConfigurationManagementService tenantConfigurationManagementService = + (PlatformConfigurationManagementService) ctx.getOSGiService(PlatformConfigurationManagementService.class, null); + if (tenantConfigurationManagementService == null) { + String msg = "Tenant configuration Management service not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return tenantConfigurationManagementService; + } + +} diff --git a/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/VirtualFireAlarmServiceUtils.java b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/VirtualFireAlarmServiceUtils.java new file mode 100644 index 000000000..277000e0f --- /dev/null +++ b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/VirtualFireAlarmServiceUtils.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.iot.virtualfirealarm.service.impl.util; + +import org.apache.commons.codec.binary.Base64; +import org.json.JSONObject; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.exception.VirtualFirealarmDeviceMgtPluginException; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.impl.VirtualFirealarmSecurityManager; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.exception.VirtualFireAlarmException; + +import java.lang.*; +import java.security.PrivateKey; + +/** + * + */ +public class VirtualFireAlarmServiceUtils { + + private static final String JSON_MESSAGE_KEY = "Msg"; + private static final String JSON_SIGNATURE_KEY = "Sig"; + + public static String prepareSecurePayLoad(String message, PrivateKey signatureKey) throws VirtualFireAlarmException { + try { + message = Base64.encodeBase64String(message.getBytes()); + String signedPayload = VirtualFirealarmSecurityManager.signMessage(message, signatureKey); + JSONObject jsonPayload = new JSONObject(); + jsonPayload.put(JSON_MESSAGE_KEY, message); + jsonPayload.put(JSON_SIGNATURE_KEY, signedPayload); + return jsonPayload.toString(); + } catch (VirtualFirealarmDeviceMgtPluginException e) { + throw new VirtualFireAlarmException(e); + } + + } +} diff --git a/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/VirtualFireAlarmUtilConstants.java b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/VirtualFireAlarmUtilConstants.java new file mode 100644 index 000000000..fae96848d --- /dev/null +++ b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/VirtualFireAlarmUtilConstants.java @@ -0,0 +1,26 @@ +package org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.util; + +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.xmpp.XmppConfig; + +public class VirtualFireAlarmUtilConstants { + + public static final String TENANT_DOMAIN = "TENANT_DOMAIN"; + public static final String DEVICE_OWNER = "DEVICE_OWNER"; + public static final String DEVICE_ID = "DEVICE_ID"; + public static final String DEVICE_NAME = "DEVICE_NAME"; + public static final String HTTPS_EP = "HTTPS_EP"; + public static final String HTTP_EP = "HTTP_EP"; + public static final String APIM_EP = "APIM_EP"; + public static final String MQTT_EP = "MQTT_EP"; + public static final String XMPP_EP = "XMPP_EP"; + public static final String VIRTUAL_FIREALARM_HTTPS_EP = "VIRTUAL_FIREALARM_HTTPS_EP"; + public static final String VIRTUAL_FIREALARM_HTTP_EP = "VIRTUAL_FIREALARM_HTTP_EP"; + public static final String VIRTUAL_FIREALARM_APIM_EP = "VIRTUAL_FIREALARM_APIM_EP"; + public static final String VIRTUAL_FIREALARM_MQTT_EP = "VIRTUAL_FIREALARM_MQTT_EP"; + public static final String VIRTUAL_FIREALARM_XMPP_EP = "VIRTUAL_FIREALARM_XMPP_EP"; + public static final String API_APPLICATION_KEY = "API_APPLICATION_KEY"; + public static final String DEVICE_TOKEN = "DEVICE_TOKEN"; + public static final String DEVICE_REFRESH_TOKEN = "DEVICE_REFRESH_TOKEN"; + public static final String SERVER_NAME = "SERVER_NAME"; + public static final String SERVER_JID = "SERVER_JID"; +} diff --git a/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/ZipArchive.java b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/ZipArchive.java new file mode 100644 index 000000000..22fda92b3 --- /dev/null +++ b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/ZipArchive.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.iot.virtualfirealarm.service.impl.util; + +import java.io.File; + +/** + * This is an utility class to hold zip files. + */ +public class ZipArchive { + + private File zipFile = null; + private String fileName = null; + + public ZipArchive(String fileName, File zipFile) { + this.fileName = fileName; + this.zipFile = zipFile; + } + + public File getZipFile() { + return zipFile; + } + + public String getFileName() { + return fileName; + } +} diff --git a/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/ZipUtil.java b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/ZipUtil.java new file mode 100644 index 000000000..feb8135db --- /dev/null +++ b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/ZipUtil.java @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.iot.virtualfirealarm.service.impl.util; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONObject; +import org.wso2.carbon.apimgt.application.extension.constants.ApiApplicationConstants; +import org.wso2.carbon.base.ServerConfiguration; +import org.wso2.carbon.device.mgt.common.DeviceManagementException; +import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationEntry; +import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationManagementException; +import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.plugin.xmpp.XmppConfig; +import org.wso2.carbon.utils.CarbonUtils; +import org.wso2.carbon.utils.NetworkUtils; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.SocketException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * This is used to create a zip file that includes the necessary configuration required for the agent. + */ +public class ZipUtil { + + private static final Log log = LogFactory.getLog(ZipUtil.class); + private static final String HTTPS_PORT_PROPERTY = "httpsPort"; + private static final String HTTP_PORT_PROPERTY = "httpPort"; + + private static final String LOCALHOST = "localhost"; + private static final String HTTPS_PROTOCOL_APPENDER = "https://"; + private static final String HTTP_PROTOCOL_APPENDER = "http://"; + private static final String CONFIG_TYPE = "general"; + private static final String DEFAULT_MQTT_ENDPOINT = "tcp://localhost:1883"; + public static final String HOST_NAME = "HostName"; + + public ZipArchive createZipFile(String owner, String deviceType, String deviceId, String deviceName, + String apiApplicationKey, String token, String refreshToken) + throws DeviceManagementException { + + String sketchFolder = "repository" + File.separator + "resources" + File.separator + "sketches"; + String archivesPath = + CarbonUtils.getCarbonHome() + File.separator + sketchFolder + File.separator + "archives" + + File.separator + deviceId; + String templateSketchPath = sketchFolder + File.separator + deviceType; + String iotServerIP; + + try { + iotServerIP = getServerUrl(); + String httpsServerPort = System.getProperty(HTTPS_PORT_PROPERTY); + String httpServerPort = System.getProperty(HTTP_PORT_PROPERTY); + String httpsServerEP = HTTPS_PROTOCOL_APPENDER + iotServerIP + ":" + httpsServerPort; + String httpServerEP = HTTP_PROTOCOL_APPENDER + iotServerIP + ":" + httpServerPort; + String mqttEndpoint = DEFAULT_MQTT_ENDPOINT; + if (mqttEndpoint.contains(LOCALHOST)) { + mqttEndpoint = mqttEndpoint.replace(LOCALHOST, iotServerIP); + } + + String xmppEndpoint = ""; + if (XmppConfig.getInstance().isEnabled()) { + xmppEndpoint = XmppConfig.getInstance().getHost() + ":" + XmppConfig.getInstance().getPort(); + if (xmppEndpoint.contains(LOCALHOST)) { + xmppEndpoint = xmppEndpoint.replace(LOCALHOST, iotServerIP); + } + } + PlatformConfiguration configuration = APIUtil.getTenantConfigurationManagementService().getConfiguration( + CONFIG_TYPE); + if (configuration != null && configuration.getConfiguration() != null && configuration + .getConfiguration().size() > 0) { + List configurations = configuration.getConfiguration(); + for (ConfigurationEntry configurationEntry : configurations) { + switch (configurationEntry.getName()) { + case VirtualFireAlarmUtilConstants.VIRTUAL_FIREALARM_HTTPS_EP: + httpsServerEP = (String)configurationEntry.getValue(); + break; + case VirtualFireAlarmUtilConstants.VIRTUAL_FIREALARM_HTTP_EP: + httpServerEP = (String)configurationEntry.getValue(); + break; + case VirtualFireAlarmUtilConstants.VIRTUAL_FIREALARM_MQTT_EP: + mqttEndpoint = (String)configurationEntry.getValue(); + break; + case VirtualFireAlarmUtilConstants.VIRTUAL_FIREALARM_XMPP_EP: + xmppEndpoint = (String)configurationEntry.getValue(); + break; + } + } + } + String base64EncodedApplicationKey = getBase64EncodedAPIAppKey(apiApplicationKey).trim(); + + Map contextParams = new HashMap<>(); + contextParams.put(VirtualFireAlarmUtilConstants.TENANT_DOMAIN, APIUtil.getTenantDomainOftheUser()); + contextParams.put(VirtualFireAlarmUtilConstants.DEVICE_OWNER, owner); + contextParams.put(VirtualFireAlarmUtilConstants.DEVICE_ID, deviceId); + contextParams.put(VirtualFireAlarmUtilConstants.DEVICE_NAME, deviceName); + contextParams.put(VirtualFireAlarmUtilConstants.HTTPS_EP, httpsServerEP); + contextParams.put(VirtualFireAlarmUtilConstants.HTTP_EP, httpServerEP); + contextParams.put(VirtualFireAlarmUtilConstants.APIM_EP, httpServerEP); + contextParams.put(VirtualFireAlarmUtilConstants.MQTT_EP, mqttEndpoint); + contextParams.put(VirtualFireAlarmUtilConstants.XMPP_EP, "XMPP:" + xmppEndpoint); + contextParams.put(VirtualFireAlarmUtilConstants.API_APPLICATION_KEY, base64EncodedApplicationKey); + contextParams.put(VirtualFireAlarmUtilConstants.DEVICE_TOKEN, token); + contextParams.put(VirtualFireAlarmUtilConstants.DEVICE_REFRESH_TOKEN, refreshToken); + contextParams.put(VirtualFireAlarmUtilConstants.SERVER_NAME, XmppConfig.getInstance().getServerName() == null + ? "" : XmppConfig.getInstance().getServerName()); + contextParams.put(VirtualFireAlarmUtilConstants.SERVER_JID, XmppConfig.getInstance().getJid() == null + ? "" : XmppConfig.getInstance().getJid()); + + ZipArchive zipFile; + zipFile = getSketchArchive(archivesPath, templateSketchPath, contextParams, deviceName); + return zipFile; + } catch (IOException e) { + throw new DeviceManagementException("Zip File Creation Failed", e); + } catch (ConfigurationManagementException e) { + throw new DeviceManagementException("Failed to retrieve configuration", e); + } + } + + private String getBase64EncodedAPIAppKey(String apiAppCredentialsAsJSONString) { + + JSONObject jsonObject = new JSONObject(apiAppCredentialsAsJSONString); + String consumerKey = jsonObject.get(ApiApplicationConstants.OAUTH_CLIENT_ID).toString(); + String consumerSecret = jsonObject.get(ApiApplicationConstants.OAUTH_CLIENT_SECRET).toString(); + String stringToEncode = consumerKey + ":" + consumerSecret; + return Base64.encodeBase64String(stringToEncode.getBytes()); + } + + public static String getServerUrl() { + String hostName = ServerConfiguration.getInstance().getFirstProperty(HOST_NAME); + try { + if (hostName == null) { + hostName = NetworkUtils.getLocalHostname(); + } + } catch (SocketException e) { + hostName = "localhost"; + log.warn("Failed retrieving the hostname, therefore set to localhost", e); + } + return hostName; + } + + public static ZipArchive getSketchArchive(String archivesPath, String templateSketchPath, Map contextParams + , String zipFileName) + throws DeviceManagementException, IOException { + String sketchPath = CarbonUtils.getCarbonHome() + File.separator + templateSketchPath; + FileUtils.deleteDirectory(new File(archivesPath));//clear directory + FileUtils.deleteDirectory(new File(archivesPath + ".zip"));//clear zip + if (!new File(archivesPath).mkdirs()) { //new dir + String message = "Could not create directory at path: " + archivesPath; + log.error(message); + throw new DeviceManagementException(message); + } + zipFileName = zipFileName + ".zip"; + try { + Map> properties = getProperties(sketchPath + File.separator + "sketch" + ".properties"); + List templateFiles = properties.get("templates"); + + for (String templateFile : templateFiles) { + parseTemplate(templateSketchPath + File.separator + templateFile, archivesPath + File.separator + templateFile, + contextParams); + } + + templateFiles.add("sketch.properties"); // ommit copying the props file + copyFolder(new File(sketchPath), new File(archivesPath), templateFiles); + createZipArchive(archivesPath); + FileUtils.deleteDirectory(new File(archivesPath)); + File zip = new File(archivesPath + ".zip"); + return new org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.util.ZipArchive(zipFileName, zip); + } catch (IOException ex) { + throw new DeviceManagementException( + "Error occurred when trying to read property " + "file sketch.properties", ex); + } + } + + private static Map> getProperties(String propertyFilePath) throws IOException { + Properties prop = new Properties(); + InputStream input = null; + + try { + + input = new FileInputStream(propertyFilePath); + + // load a properties file + prop.load(input); + Map> properties = new HashMap>(); + + String templates = prop.getProperty("templates"); + List list = new ArrayList(Arrays.asList(templates.split(","))); + properties.put("templates", list); + + final String filename = prop.getProperty("zipfilename"); + list = new ArrayList() {{ + add(filename); + }}; + properties.put("zipfilename", list); + return properties; + + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + log.error("Failed closing connection", e); + } + } + } + } + + private static void parseTemplate(String srcFile, String dstFile, Map contextParams) throws IOException { + //read from file + FileInputStream inputStream = null; + FileOutputStream outputStream = null; + try { + inputStream = new FileInputStream(srcFile); + outputStream = new FileOutputStream(dstFile); + String content = IOUtils.toString(inputStream, StandardCharsets.UTF_8.toString()); + Iterator iterator = contextParams.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry mapEntry = (Map.Entry) iterator.next(); + content = content.replaceAll("\\$\\{" + mapEntry.getKey() + "\\}", mapEntry.getValue().toString()); + } + IOUtils.write(content, outputStream, StandardCharsets.UTF_8.toString()); + } finally { + if (inputStream != null) { + inputStream.close(); + } + if (outputStream != null) { + outputStream.close(); + } + } + } + + private static void copyFolder(File src, File dest, List excludeFileNames) throws IOException { + + if (src.isDirectory()) { + //if directory not exists, create it + if (!dest.exists() && !dest.mkdirs()) { + String message = "Could not create directory at path: " + dest; + log.error(message); + throw new IOException(message); + } + //list all the directory contents + String files[] = src.list(); + + if (files == null) { + log.warn("There are no files insides the directory " + src.getAbsolutePath()); + return; + } + + for (String file : files) { + //construct the src and dest file structure + File srcFile = new File(src, file); + File destFile = new File(dest, file); + //recursive copy + copyFolder(srcFile, destFile, excludeFileNames); + } + + } else { + for (String fileName : excludeFileNames) { + if (src.getName().equals(fileName)) { + return; + } + } + //if file, then copy it + //Use bytes stream to support all file types + InputStream in = null; + OutputStream out = null; + + try { + in = new FileInputStream(src); + out = new FileOutputStream(dest); + + byte[] buffer = new byte[1024]; + + int length; + //copy the file content in bytes + while ((length = in.read(buffer)) > 0) { + out.write(buffer, 0, length); + } + } finally { + if (in != null) { + in.close(); + } + if (out != null) { + out.close(); + } + } + } + } + + private static boolean createZipArchive(String srcFolder) throws IOException { + BufferedInputStream origin = null; + ZipOutputStream out = null; + + try { + final int BUFFER = 2048; + FileOutputStream dest = new FileOutputStream(new File(srcFolder + ".zip")); + out = new ZipOutputStream(new BufferedOutputStream(dest)); + byte data[] = new byte[BUFFER]; + File subDir = new File(srcFolder); + String subdirList[] = subDir.list(); + if (subdirList == null) { + log.warn("The sub directory " + subDir.getAbsolutePath() + " is empty"); + return false; + } + for (String sd : subdirList) { + // get a list of files from current directory + File f = new File(srcFolder + "/" + sd); + if (f.isDirectory()) { + String files[] = f.list(); + + if (files == null) { + log.warn("The current directory " + f.getAbsolutePath() + " is empty. Has no files"); + return false; + } + + for (int i = 0; i < files.length; i++) { + FileInputStream fi = new FileInputStream(srcFolder + "/" + sd + "/" + files[i]); + origin = new BufferedInputStream(fi, BUFFER); + ZipEntry entry = new ZipEntry(sd + "/" + files[i]); + out.putNextEntry(entry); + int count; + while ((count = origin.read(data, 0, BUFFER)) != -1) { + out.write(data, 0, count); + out.flush(); + } + + } + } else //it is just a file + { + FileInputStream fi = new FileInputStream(f); + origin = new BufferedInputStream(fi, BUFFER); + ZipEntry entry = new ZipEntry(sd); + out.putNextEntry(entry); + int count; + while ((count = origin.read(data, 0, BUFFER)) != -1) { + out.write(data, 0, count); + out.flush(); + } + } + } + out.flush(); + } finally { + if (origin != null) { + origin.close(); + } + if (out != null) { + out.close(); + } + } + return true; + } +} diff --git a/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/util/Utils.java b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/util/Utils.java new file mode 100644 index 000000000..586de223c --- /dev/null +++ b/src/main/java/org/wso2/carbon/device/mgt/iot/virtualfirealarm/service/impl/util/util/Utils.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. 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.iot.virtualfirealarm.service.impl.util.util; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.base.ServerConfiguration; +import org.wso2.carbon.device.mgt.common.DeviceManagementException; +import org.wso2.carbon.device.mgt.iot.virtualfirealarm.service.impl.util.ZipArchive; +import org.wso2.carbon.utils.CarbonUtils; +import org.wso2.carbon.utils.NetworkUtils; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.SocketException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * Provides utility methods required by the device type plugins. + */ +public class Utils { + + public static final String HOST_NAME = "HostName"; + private static final Log log = LogFactory.getLog(Utils.class); + + + +} diff --git a/src/main/webapp/META-INF/permissions.xml b/src/main/webapp/META-INF/permissions.xml new file mode 100644 index 000000000..fbad43f95 --- /dev/null +++ b/src/main/webapp/META-INF/permissions.xml @@ -0,0 +1,52 @@ + + + + + + + + + + Download device + /device-mgt/user/devices + /device/download + GET + virtual_firealarm_user + + + Control Buzz + /device-mgt/user/operation + /device/*/buzz + POST + virtual_firealarm_user + + + Get Stats + /device-mgt/user/stats + /device/stats/* + GET + virtual_firealarm_user + + \ No newline at end of file diff --git a/src/main/webapp/META-INF/webapp-classloading.xml b/src/main/webapp/META-INF/webapp-classloading.xml new file mode 100644 index 000000000..fa4461919 --- /dev/null +++ b/src/main/webapp/META-INF/webapp-classloading.xml @@ -0,0 +1,33 @@ + + + + + + + + + false + + + CXF,Carbon + diff --git a/src/main/webapp/WEB-INF/cxf-servlet.xml b/src/main/webapp/WEB-INF/cxf-servlet.xml new file mode 100644 index 000000000..37a69c268 --- /dev/null +++ b/src/main/webapp/WEB-INF/cxf-servlet.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..2d1c6e26b --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,42 @@ + + + WSO2 IoT Server + WSO2 IoT Server + + + CXFServlet + org.apache.cxf.transport.servlet.CXFServlet + 1 + + + CXFServlet + /* + + + isAdminService + false + + + doAuthentication + true + + + providerTenantDomain + carbon.super + + + + + managed-api-enabled + true + + + managed-api-owner + admin + + + \ No newline at end of file