From adc1e32361da971841ac55b3cce4c0d1e505e821 Mon Sep 17 00:00:00 2001 From: charitha Date: Fri, 10 Aug 2018 15:22:01 +0530 Subject: [PATCH] Add siddhi extensions for operation and notification publishing --- .../org.wso2.extension.siddhi.device/pom.xml | 67 +++++- .../AddNotificationFunctionProcessor.java | 169 +++++++++++++ .../device/AddOperationFunctionProcessor.java | 223 ++++++++++++++++++ .../client/OAuthRequestInterceptor.java | 106 +++++++++ .../client/configs/SiddhiExtensionConfig.java | 70 ++++++ .../configs/SiddhiExtensionConfigReader.java | 95 ++++++++ .../device/client/dto/OAuthApplication.java | 50 ++++ .../device/client/dto/OperationRequest.java | 45 ++++ .../client/dto/RegistrationProfile.java | 63 +++++ .../client/exception/APIMClientException.java | 58 +++++ .../exception/APIMClientOAuthException.java | 58 +++++ .../InvalidConfigurationStateException.java | 78 ++++++ .../device/client/services/DCRService.java | 42 ++++ .../client/services/OperationService.java | 43 ++++ .../siddhi/device/utils/ClientUtils.java | 223 ++++++++++++++++++ .../siddhi/device/utils/DeviceUtils.java | 34 +++ .../src/main/resources/device.siddhiext | 2 + .../src/main/resources/build.properties | 1 + .../resources/conf/siddhi-integration.xml | 26 ++ .../src/main/resources/p2.inf | 2 + pom.xml | 19 ++ 21 files changed, 1473 insertions(+), 1 deletion(-) create mode 100644 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/AddNotificationFunctionProcessor.java create mode 100644 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/AddOperationFunctionProcessor.java create mode 100755 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/OAuthRequestInterceptor.java create mode 100644 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/configs/SiddhiExtensionConfig.java create mode 100644 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/configs/SiddhiExtensionConfigReader.java create mode 100644 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/dto/OAuthApplication.java create mode 100644 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/dto/OperationRequest.java create mode 100644 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/dto/RegistrationProfile.java create mode 100644 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/exception/APIMClientException.java create mode 100644 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/exception/APIMClientOAuthException.java create mode 100644 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/exception/InvalidConfigurationStateException.java create mode 100644 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/services/DCRService.java create mode 100644 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/services/OperationService.java create mode 100644 components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/utils/ClientUtils.java create mode 100644 features/extensions-feature/org.wso2.extension.siddhi.device.feature/src/main/resources/build.properties create mode 100644 features/extensions-feature/org.wso2.extension.siddhi.device.feature/src/main/resources/conf/siddhi-integration.xml create mode 100644 features/extensions-feature/org.wso2.extension.siddhi.device.feature/src/main/resources/p2.inf diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/pom.xml b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/pom.xml index 2246024c92..43570e8906 100644 --- a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/pom.xml +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/pom.xml @@ -55,6 +55,42 @@ org.json.wso2 json + + org.wso2.carbon.devicemgt + org.wso2.carbon.identity.jwt.client.extension + + + io.github.openfeign + feign-core + + + io.github.openfeign + feign-jaxrs + + + io.github.openfeign + feign-gson + + + io.github.openfeign + feign-slf4j + + + com.squareup.okhttp3 + okhttp + + + com.squareup.okio + okio + + + io.github.openfeign + feign-okhttp + + + javax.ws.rs + jsr311-api + com.h2database.wso2 h2-database-engine @@ -125,14 +161,43 @@ org.wso2.extension.siddhi.device.* + feign, + feign.codec, + feign.auth, + feign.gson, + feign.slf4j, + javax.net.ssl, + javax.xml, + javax.xml.bind, + javax.xml.bind.annotation, + javax.xml.parsers;resolution:=optional, + org.apache.commons.lang, + org.w3c.dom, + org.wso2.carbon.base, + org.wso2.carbon.utils, + org.wso2.carbon.user.api, org.json;version="${orbit.version.json.range}", org.wso2.siddhi.core.*, org.wso2.siddhi.query.api.*, org.wso2.carbon.device.mgt.core.*, org.wso2.carbon.device.mgt.common.*, + org.wso2.carbon.identity.jwt.client.*, org.apache.commons.logging, - org.wso2.carbon.context + org.wso2.carbon.context, + android.util;resolution:=optional, + javax.annotation;resolution:=optional, + javax.net;resolution:=optional, + javax.security.auth.x500;resolution:=optional, + javax.crypto;resolution:=optional, + javax.crypto.spec;resolution:=optional + + jsr311-api, + feign-jaxrs, + feign-okhttp, + okhttp, + okio + diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/AddNotificationFunctionProcessor.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/AddNotificationFunctionProcessor.java new file mode 100644 index 0000000000..cc48643b89 --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/AddNotificationFunctionProcessor.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2018, 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.extension.siddhi.device; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.DeviceIdentifier; +import org.wso2.carbon.device.mgt.common.notification.mgt.Notification; +import org.wso2.carbon.device.mgt.common.notification.mgt.NotificationManagementException; +import org.wso2.carbon.device.mgt.common.notification.mgt.NotificationManagementService; +import org.wso2.extension.siddhi.device.utils.DeviceUtils; +import org.wso2.siddhi.core.config.ExecutionPlanContext; +import org.wso2.siddhi.core.exception.ExecutionPlanRuntimeException; +import org.wso2.siddhi.core.executor.ExpressionExecutor; +import org.wso2.siddhi.core.query.processor.stream.function.StreamFunctionProcessor; +import org.wso2.siddhi.query.api.definition.AbstractDefinition; +import org.wso2.siddhi.query.api.definition.Attribute; +import org.wso2.siddhi.query.api.exception.ExecutionPlanValidationException; + +import java.util.ArrayList; +import java.util.List; + +public class AddNotificationFunctionProcessor extends StreamFunctionProcessor { + + private static final Log log = LogFactory.getLog(AddNotificationFunctionProcessor.class); + + /** + * The init method of the StreamProcessor, this method will be called before other methods + * + * @param abstractDefinition the incoming stream definition + * @param attributeExpressionExecutors the executors of each function parameters + * @param executionPlanContext the context of the execution plan + * @return the additional output attributes introduced by the function + */ + @Override + protected List init(AbstractDefinition abstractDefinition, + ExpressionExecutor[] attributeExpressionExecutors, + ExecutionPlanContext executionPlanContext) { + if (attributeExpressionExecutors.length != 3) { + throw new ExecutionPlanValidationException( + "Invalid no of arguments passed to device:addNotification() function, required 3 but found " + + attributeExpressionExecutors.length); + } + if (attributeExpressionExecutors[0].getReturnType() != Attribute.Type.STRING) { + throw new ExecutionPlanValidationException( + "Invalid parameter type found for the first argument (deviceIdentifier) of device:addNotification() " + + "function, required " + Attribute.Type.STRING + " as deviceIdentifier, but found " + + attributeExpressionExecutors[0].getReturnType().toString()); + } + if (attributeExpressionExecutors[1].getReturnType() != Attribute.Type.STRING) { + throw new ExecutionPlanValidationException( + "Invalid parameter type found for the second argument (deviceType) of device:addNotification() " + + "function, required " + Attribute.Type.STRING + " as deviceType, but found " + + attributeExpressionExecutors[1].getReturnType().toString()); + } + if (attributeExpressionExecutors[2].getReturnType() != Attribute.Type.STRING) { + throw new ExecutionPlanValidationException( + "Invalid parameter type found for the third argument (description) of device:addNotification() " + + "function, required " + Attribute.Type.STRING + " as description, but found " + + attributeExpressionExecutors[2].getReturnType().toString()); + } + ArrayList attributes = new ArrayList<>(); + attributes.add(new Attribute("notified", Attribute.Type.BOOL)); + return attributes; + } + + /** + * The process method of the StreamFunction, used when more than one function parameters are provided + * + * @param data the data values for the function parameters + * @return the data for additional output attributes introduced by the function + */ + @Override + protected Object[] process(Object[] data) { + if (data[0] == null || data[1] == null || data[2] == null) { + throw new ExecutionPlanRuntimeException("Invalid input given to device:addNotification() function. " + + "Neither of any three arguments cannot be null"); + } + boolean isNotified = false; + String deviceId = (String) data[0]; + String deviceType = (String) data[1]; + String description = (String) data[2]; + Notification notification = new Notification(); + notification.setStatus(Notification.Status.NEW.name()); + notification.setDeviceIdentifier(deviceId); + notification.setDeviceType(deviceType); + notification.setDescription(description); + + NotificationManagementService notificationManagementService = DeviceUtils.getNotificationManagementService(); + try { + isNotified = notificationManagementService.addNotification(new DeviceIdentifier(deviceId, deviceType), notification); + } catch (NotificationManagementException e) { + log.error("Error occurred while adding notification for " + deviceType + " device with id " + deviceId, e); + } + + return new Object[]{isNotified}; + } + + /** + * The process method of the StreamFunction, used when zero or one function parameter is provided + * + * @param data null if the function parameter count is zero or runtime data value of the function parameter + * @return the data for additional output attribute introduced by the function + */ + @Override + protected Object[] process(Object data) { + return new Object[0]; + } + + /** + * This will be called only once and this can be used to acquire + * required resources for the processing element. + * This will be called after initializing the system and before + * starting to process the events. + */ + @Override + public void start() { + + } + + /** + * This will be called only once and this can be used to release + * the acquired resources for processing. + * This will be called before shutting down the system. + */ + @Override + public void stop() { + + } + + /** + * Used to collect the serializable state of the processing element, that need to be + * persisted for the reconstructing the element to the same state on a different point of time + * + * @return stateful objects of the processing element as an array + */ + @Override + public Object[] currentState() { + return new Object[0]; + } + + /** + * Used to restore serialized state of the processing element, for reconstructing + * the element to the same state as if was on a previous point of time. + * + * @param objects the stateful objects of the element as an array on + * the same order provided by currentState(). + */ + @Override + public void restoreState(Object[] objects) { + //Since there's no need to maintain a state, nothing needs to be done here. + } +} diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/AddOperationFunctionProcessor.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/AddOperationFunctionProcessor.java new file mode 100644 index 0000000000..f3abc1a996 --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/AddOperationFunctionProcessor.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2018, 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.extension.siddhi.device; + +import feign.Feign; +import feign.Logger; +import feign.gson.GsonDecoder; +import feign.gson.GsonEncoder; +import feign.jaxrs.JAXRSContract; +import feign.okhttp.OkHttpClient; +import feign.slf4j.Slf4jLogger; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.wso2.carbon.device.mgt.common.operation.mgt.Activity; +import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; +import org.wso2.extension.siddhi.device.client.OAuthRequestInterceptor; +import org.wso2.extension.siddhi.device.client.configs.SiddhiExtensionConfigReader; +import org.wso2.extension.siddhi.device.client.dto.OperationRequest; +import org.wso2.extension.siddhi.device.client.services.OperationService; +import org.wso2.extension.siddhi.device.utils.ClientUtils; +import org.wso2.siddhi.core.config.ExecutionPlanContext; +import org.wso2.siddhi.core.exception.ExecutionPlanRuntimeException; +import org.wso2.siddhi.core.executor.ExpressionExecutor; +import org.wso2.siddhi.core.query.processor.stream.function.StreamFunctionProcessor; +import org.wso2.siddhi.query.api.definition.AbstractDefinition; +import org.wso2.siddhi.query.api.definition.Attribute; +import org.wso2.siddhi.query.api.exception.ExecutionPlanValidationException; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class AddOperationFunctionProcessor extends StreamFunctionProcessor { + + private static final Log log = LogFactory.getLog(AddOperationFunctionProcessor.class); + private static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss"; + private static final String DEVICE_MGT_BASE_CONTEXT = "/api/device-mgt/v1.0"; + private OperationService operationService; + + public AddOperationFunctionProcessor() { + operationService = Feign.builder().client(new OkHttpClient(ClientUtils.getSSLClient())) + .logger(new Slf4jLogger()) + .logLevel(Logger.Level.FULL).requestInterceptor(new OAuthRequestInterceptor()) + .contract(new JAXRSContract()).encoder(new GsonEncoder()).decoder(new GsonDecoder()) + .target(OperationService.class, ClientUtils.replaceProperties( + SiddhiExtensionConfigReader.getInstance().getConfig().getGatewayEndpoint() + + DEVICE_MGT_BASE_CONTEXT)); + } + + /** + * The init method of the StreamProcessor, this method will be called before other methods + * + * @param abstractDefinition the incoming stream definition + * @param attributeExpressionExecutors the executors of each function parameters + * @param executionPlanContext the context of the execution plan + * @return the additional output attributes introduced by the function + */ + @Override + protected List init(AbstractDefinition abstractDefinition, + ExpressionExecutor[] attributeExpressionExecutors, + ExecutionPlanContext executionPlanContext) { + if (attributeExpressionExecutors.length != 6) { + throw new ExecutionPlanValidationException( + "Invalid no of arguments passed to device:addOperation() function, required 3 but found " + + attributeExpressionExecutors.length); + } + if (attributeExpressionExecutors[0].getReturnType() != Attribute.Type.STRING) { + throw new ExecutionPlanValidationException( + "Invalid parameter type found for the first argument (deviceIdentifiers) of device:addOperation() " + + "function, required " + Attribute.Type.STRING + " as deviceIdentifiers, but found " + + attributeExpressionExecutors[0].getReturnType().toString()); + } + if (attributeExpressionExecutors[1].getReturnType() != Attribute.Type.STRING) { + throw new ExecutionPlanValidationException( + "Invalid parameter type found for the second argument (deviceType) of device:addOperation() " + + "function, required " + Attribute.Type.STRING + " as deviceType, but found " + + attributeExpressionExecutors[1].getReturnType().toString()); + } + if (attributeExpressionExecutors[2].getReturnType() != Attribute.Type.STRING) { + throw new ExecutionPlanValidationException( + "Invalid parameter type found for the third argument (code) of device:addOperation() " + + "function, required " + Attribute.Type.STRING + " as code, but found " + + attributeExpressionExecutors[2].getReturnType().toString()); + } + if (attributeExpressionExecutors[3].getReturnType() != Attribute.Type.STRING) { + throw new ExecutionPlanValidationException( + "Invalid parameter type found for the fourth argument (type) of device:addOperation() " + + "function, required " + Attribute.Type.STRING + " as type, but found " + + attributeExpressionExecutors[3].getReturnType().toString()); + } + if (attributeExpressionExecutors[4].getReturnType() != Attribute.Type.BOOL) { + throw new ExecutionPlanValidationException( + "Invalid parameter type found for the fifth argument (isEnabled) of device:addOperation() " + + "function, required " + Attribute.Type.BOOL + " as isEnabled, but found " + + attributeExpressionExecutors[4].getReturnType().toString()); + } + if (attributeExpressionExecutors[5].getReturnType() != Attribute.Type.STRING) { + throw new ExecutionPlanValidationException( + "Invalid parameter type found for the fifth argument (payLoad) of device:addOperation() " + + "function, required " + Attribute.Type.STRING + " as payLoad, but found " + + attributeExpressionExecutors[5].getReturnType().toString()); + } + ArrayList attributes = new ArrayList<>(); + attributes.add(new Attribute("activity_id", Attribute.Type.STRING)); + return attributes; + } + + /** + * The process method of the StreamFunction, used when more than one function parameters are provided + * + * @param data the data values for the function parameters + * @return the data for additional output attributes introduced by the function + */ + @Override + protected Object[] process(Object[] data) { + if (data[0] == null || data[1] == null || data[2] == null || data[3] == null || data[4] == null || data[5] == null) { + throw new ExecutionPlanRuntimeException("Invalid input given to device:addOperation() function. " + + "Neither of any three arguments cannot be null"); + } + + JSONArray deviceIds = new JSONArray((String) data[0]); + String deviceType = (String) data[1]; + List deviceIdentifiers = new ArrayList<>(); + for (int i = 0; i < deviceIds.length(); i++) { + deviceIdentifiers.add(deviceIds.getString(i)); + } + + Operation operation = new Operation(); + operation.setType(Operation.Type.valueOf((String) data[3])); + operation.setStatus(Operation.Status.PENDING); + operation.setCode((String) data[2]); + operation.setEnabled((Boolean) data[4]); + String payloadString = (String) data[5]; + operation.setPayLoad(payloadString.replaceAll("'", "\"")); + + String date = new SimpleDateFormat(DATE_FORMAT_NOW).format(new Date()); + operation.setCreatedTimeStamp(date); + + OperationRequest operationRequest = new OperationRequest(); + operationRequest.setDeviceIdentifiers(deviceIdentifiers); + operationRequest.setOperation(operation); + try { + Activity activity = operationService.addOperation(deviceType, operationRequest); + return new Object[]{activity.getActivityId()}; + } catch (Exception e) { + log.error("Error occurred while adding the operation " + operation.toString(), e); + return new Object[]{null}; + } + } + + /** + * The process method of the StreamFunction, used when zero or one function parameter is provided + * + * @param data null if the function parameter count is zero or runtime data value of the function parameter + * @return the data for additional output attribute introduced by the function + */ + @Override + protected Object[] process(Object data) { + return new Object[0]; + } + + /** + * This will be called only once and this can be used to acquire + * required resources for the processing element. + * This will be called after initializing the system and before + * starting to process the events. + */ + @Override + public void start() { + + } + + /** + * This will be called only once and this can be used to release + * the acquired resources for processing. + * This will be called before shutting down the system. + */ + @Override + public void stop() { + + } + + /** + * Used to collect the serializable state of the processing element, that need to be + * persisted for the reconstructing the element to the same state on a different point of time + * + * @return stateful objects of the processing element as an array + */ + @Override + public Object[] currentState() { + return new Object[0]; + } + + /** + * Used to restore serialized state of the processing element, for reconstructing + * the element to the same state as if was on a previous point of time. + * + * @param objects the stateful objects of the element as an array on + * the same order provided by currentState(). + */ + @Override + public void restoreState(Object[] objects) { + //Since there's no need to maintain a state, nothing needs to be done here. + } +} diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/OAuthRequestInterceptor.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/OAuthRequestInterceptor.java new file mode 100755 index 0000000000..7ef87863de --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/OAuthRequestInterceptor.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018, 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.extension.siddhi.device.client; + +import feign.Feign; +import feign.Logger; +import feign.RequestInterceptor; +import feign.RequestTemplate; +import feign.auth.BasicAuthRequestInterceptor; +import feign.gson.GsonDecoder; +import feign.gson.GsonEncoder; +import feign.jaxrs.JAXRSContract; +import feign.okhttp.OkHttpClient; +import feign.slf4j.Slf4jLogger; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.base.MultitenantConstants; +import org.wso2.carbon.context.PrivilegedCarbonContext; +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 org.wso2.extension.siddhi.device.client.configs.SiddhiExtensionConfigReader; +import org.wso2.extension.siddhi.device.client.dto.OAuthApplication; +import org.wso2.extension.siddhi.device.client.dto.RegistrationProfile; +import org.wso2.extension.siddhi.device.client.exception.APIMClientOAuthException; +import org.wso2.extension.siddhi.device.client.services.DCRService; +import org.wso2.extension.siddhi.device.utils.ClientUtils; +import org.wso2.extension.siddhi.device.utils.DeviceUtils; + +/** + * This is a request interceptor to add oauth token header. + */ +public class OAuthRequestInterceptor implements RequestInterceptor { + + private static final String APPLICATION_NAME = "siddhi_extension_client"; + private static final String REQUIRED_SCOPES = "perm:devices:operations"; + private static final String[] API_TAGS = {"device_management"}; + private DCRService dcrService; + private static OAuthApplication oAuthApplication; + private static final Log log = LogFactory.getLog(OAuthRequestInterceptor.class); + + /** + * Creates an interceptor that authenticates all requests. + */ + public OAuthRequestInterceptor() { + String username = SiddhiExtensionConfigReader.getInstance().getConfig().getUsername(); + String password = SiddhiExtensionConfigReader.getInstance().getConfig().getPassword(); + dcrService = Feign.builder().client(new OkHttpClient(ClientUtils.getSSLClient())).logger(new Slf4jLogger()) + .logLevel(Logger.Level.FULL) + .requestInterceptor(new BasicAuthRequestInterceptor(username, password)) + .contract(new JAXRSContract()).encoder(new GsonEncoder()).decoder(new GsonDecoder()) + .target(DCRService.class, ClientUtils.replaceProperties( + SiddhiExtensionConfigReader.getInstance().getConfig().getDcrEndpoint())); + } + + @Override + public void apply(RequestTemplate template) { + if (oAuthApplication == null) { + RegistrationProfile registrationProfile = new RegistrationProfile(); + registrationProfile.setApiApplicationName(APPLICATION_NAME); + registrationProfile.setIsAllowedToAllDomains(true); + registrationProfile.setTags(API_TAGS); + oAuthApplication = dcrService.register(registrationProfile); + } + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + try { + String username = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm().getRealmConfiguration().getAdminUserName(); + if (!tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) { + username = username + "@" + tenantDomain; + } + JWTClient jwtClient = DeviceUtils.getJWTClientManagerService().getJWTClient(); + AccessTokenInfo tenantBasedAccessTokenInfo = jwtClient.getAccessToken(oAuthApplication.getClientId(), + oAuthApplication.getClientSecret(), username, REQUIRED_SCOPES); + if (tenantBasedAccessTokenInfo.getAccessToken() != null) { + String headerValue = "Bearer " + tenantBasedAccessTokenInfo.getAccessToken(); + template.header("Authorization", headerValue); + } + } catch (JWTClientException e) { + String msg = "Failed to retrieve oauth token using jwt"; + log.error(msg, e); + throw new APIMClientOAuthException(msg, e); + } catch (UserStoreException e) { + String msg = "Unable to retrieve realm config for tenant " + tenantDomain; + log.error(msg, e); + throw new APIMClientOAuthException(msg, e); + } + } + +} diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/configs/SiddhiExtensionConfig.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/configs/SiddhiExtensionConfig.java new file mode 100644 index 0000000000..0021ca6909 --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/configs/SiddhiExtensionConfig.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2018, 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.extension.siddhi.device.client.configs; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * This holds the configuration api manager integration. + */ +@XmlRootElement(name = "SiddhiExtensionConfiguration") +public class SiddhiExtensionConfig { + + String dcrEndpoint; + String gatewayEndpoint; + String username; + String password; + + @XmlElement(name = "DCREndpoint", required = true) + public String getDcrEndpoint() { + return dcrEndpoint; + } + + public void setDcrEndpoint(String dcrEndpoint) { + this.dcrEndpoint = dcrEndpoint; + } + + @XmlElement(name = "GatewayEndpoint", required = true) + public String getGatewayEndpoint() { + return gatewayEndpoint; + } + + public void setGatewayEndpoint(String gatewayEndpoint) { + this.gatewayEndpoint = gatewayEndpoint; + } + + @XmlElement(name = "Username", required = true) + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @XmlElement(name = "Password", required = true) + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/configs/SiddhiExtensionConfigReader.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/configs/SiddhiExtensionConfigReader.java new file mode 100644 index 0000000000..5a22114632 --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/configs/SiddhiExtensionConfigReader.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2018, 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.extension.siddhi.device.client.configs; + +import org.w3c.dom.Document; +import org.wso2.carbon.utils.CarbonUtils; +import org.wso2.extension.siddhi.device.client.exception.APIMClientException; +import org.wso2.extension.siddhi.device.client.exception.InvalidConfigurationStateException; + +import javax.xml.XMLConstants; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.File; + +/** + * This holds the configuration parser for api integration.xml + */ +public class SiddhiExtensionConfigReader { + + private static SiddhiExtensionConfig config; + private static SiddhiExtensionConfigReader configReader = new SiddhiExtensionConfigReader(); + private static boolean isInitialized = false; + private static final String SIDDHI_INTEGRATION_CONFIG_PATH = + CarbonUtils.getCarbonConfigDirPath() + File.separator + "siddhi-integration.xml"; + + private SiddhiExtensionConfigReader() { + + } + + private static String apimIntegrationXmlFilePath = ""; + + //TOD file may be a part of another file + public static SiddhiExtensionConfigReader getInstance() { + if (!isInitialized) { + try { + init(); + } catch (APIMClientException e) { + throw new InvalidConfigurationStateException("Webapp Authenticator Configuration is not " + + "initialized properly"); + } + } + return configReader; + } + + public static void init() throws APIMClientException { + try { + File siddhiConfigFile = new File(SIDDHI_INTEGRATION_CONFIG_PATH); + Document doc = convertToDocument(siddhiConfigFile); + + JAXBContext ctx = JAXBContext.newInstance(SiddhiExtensionConfig.class); + Unmarshaller unmarshaller = ctx.createUnmarshaller(); + config = (SiddhiExtensionConfig) unmarshaller.unmarshal(doc); + isInitialized = true; + } catch (JAXBException e) { + throw new APIMClientException("Error occurred while un-marshalling SiddhiExtensionConfig", e); + } + } + + private static Document convertToDocument(File file) throws APIMClientException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + try { + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + DocumentBuilder docBuilder = factory.newDocumentBuilder(); + return docBuilder.parse(file); + } catch (Exception e) { + throw new APIMClientException("Error occurred while parsing file 'apim-integration.xml' to a org.w3c.dom.Document", e); + } + } + + public SiddhiExtensionConfig getConfig() { + return config; + } + +} diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/dto/OAuthApplication.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/dto/OAuthApplication.java new file mode 100644 index 0000000000..46c72e9e3c --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/dto/OAuthApplication.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018, 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.extension.siddhi.device.client.dto; + +public class OAuthApplication { + + private String client_id; + private String client_secret; + + public String getClientId() { + return client_id; + } + + public void setClientId(String clientId) { + this.client_id = clientId; + } + + public String getClientSecret() { + return client_secret; + } + + public void setClientSecret(String clientSecret) { + this.client_secret = clientSecret; + } + + @Override + public String toString() { + return "OAuthApplication {\n" + + " clientId: " + client_id + "\n" + + " clientSecret: " + client_secret + "\n" + + "}\n"; + } + +} diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/dto/OperationRequest.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/dto/OperationRequest.java new file mode 100644 index 0000000000..bd3d919e3e --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/dto/OperationRequest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018, 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.extension.siddhi.device.client.dto; + +import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; + +import java.util.List; + +public class OperationRequest { + + private List deviceIdentifiers; + private Operation operation; + + public List getDeviceIdentifiers() { + return deviceIdentifiers; + } + + public void setDeviceIdentifiers(List deviceIdentifiers) { + this.deviceIdentifiers = deviceIdentifiers; + } + + public Operation getOperation() { + return operation; + } + + public void setOperation(Operation operation) { + this.operation = operation; + } +} diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/dto/RegistrationProfile.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/dto/RegistrationProfile.java new file mode 100644 index 0000000000..ecb0086de3 --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/dto/RegistrationProfile.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018, 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.extension.siddhi.device.client.dto; + +/** + * DTO class to be used when registering an ApiM application. + */ + +public class RegistrationProfile { + + private String applicationName; + private String tags[]; + private boolean isAllowedToAllDomains; + private String validityPeriod; + + public String getApplicationName() { + return applicationName; + } + + public void setApiApplicationName(String apiApplicationName) { + this.applicationName = apiApplicationName; + } + + public String[] getTags() { + return tags; + } + + public void setTags(String[] tags) { + this.tags = tags; + } + + public boolean isAllowedToAllDomains() { + return isAllowedToAllDomains; + } + + public void setIsAllowedToAllDomains(boolean isAllowedToAllDomains) { + this.isAllowedToAllDomains = isAllowedToAllDomains; + } + + public String getValidityPeriod() { + return validityPeriod; + } + + public void setValidityPeriod(String validityPeriod) { + this.validityPeriod = validityPeriod; + } +} diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/exception/APIMClientException.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/exception/APIMClientException.java new file mode 100644 index 0000000000..98f3c460bf --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/exception/APIMClientException.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018, 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.extension.siddhi.device.client.exception; + +/** + * This holds api client exception. + */ +public class APIMClientException extends Exception { + + private static final long serialVersionUID = -3976392476319079281L; + private String responseReason; + private int responseStatus; + private String methodKey; + + APIMClientException(String methodKey, String reason, int status) { + super("Exception occurred while invoking " + methodKey + " status = " + status + " reason = " + reason); + this.methodKey = methodKey; + this.responseReason = reason; + this.responseStatus = status; + } + + APIMClientException(String message) { + super(message); + } + + public APIMClientException(String message, Exception e) { + super(message, e); + } + + public String getResponseReason() { + return responseReason; + } + + public int getResponseStatus() { + return responseStatus; + } + + public String getMethodKey() { + return methodKey; + } + +} \ No newline at end of file diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/exception/APIMClientOAuthException.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/exception/APIMClientOAuthException.java new file mode 100644 index 0000000000..731fa3c253 --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/exception/APIMClientOAuthException.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2018, 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.extension.siddhi.device.client.exception; + +/** + * This holds api client exception. + */ +public class APIMClientOAuthException extends RuntimeException { + + private static final long serialVersionUID = -3976392476319079281L; + private String responseReason; + private int responseStatus; + private String methodKey; + + APIMClientOAuthException(String methodKey, String reason, int status) { + super("Exception occurred while invoking " + methodKey + " status = " + status + " reason = " + reason); + this.methodKey = methodKey; + this.responseReason = reason; + this.responseStatus = status; + } + + public APIMClientOAuthException(String message) { + super(message); + } + + public APIMClientOAuthException(String message, Exception e) { + super(message, e); + } + + public String getResponseReason() { + return responseReason; + } + + public int getResponseStatus() { + return responseStatus; + } + + public String getMethodKey() { + return methodKey; + } + +} \ No newline at end of file diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/exception/InvalidConfigurationStateException.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/exception/InvalidConfigurationStateException.java new file mode 100644 index 0000000000..2656fd74b7 --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/exception/InvalidConfigurationStateException.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2018, 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.extension.siddhi.device.client.exception; + +/** + * This error is thrown when there is an issue with the client. + */ +public class InvalidConfigurationStateException extends RuntimeException { + + private static final long serialVersionUID = -3151279311329070397L; + + private String errorMessage; + private int errorCode; + + public InvalidConfigurationStateException(int errorCode, String message) { + super(message); + this.errorCode = errorCode; + } + + public InvalidConfigurationStateException(int errorCode, String message, Throwable cause) { + super(message, cause); + this.errorCode = errorCode; + } + + public int getErrorCode() { + return errorCode; + } + + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public InvalidConfigurationStateException(String msg, Exception nestedEx) { + super(msg, nestedEx); + setErrorMessage(msg); + } + + public InvalidConfigurationStateException(String message, Throwable cause) { + super(message, cause); + setErrorMessage(message); + } + + public InvalidConfigurationStateException(String msg) { + super(msg); + setErrorMessage(msg); + } + + public InvalidConfigurationStateException() { + super(); + } + + public InvalidConfigurationStateException(Throwable cause) { + super(cause); + } + +} diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/services/DCRService.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/services/DCRService.java new file mode 100644 index 0000000000..c170f9c5b8 --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/services/DCRService.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018, 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.extension.siddhi.device.client.services; + +import org.wso2.extension.siddhi.device.client.dto.OAuthApplication; +import org.wso2.extension.siddhi.device.client.dto.RegistrationProfile; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * DCR Rest resource. + */ +@Path("/") +public interface DCRService { + + // DCR APIs + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + OAuthApplication register(RegistrationProfile registrationProfile); + +} diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/services/OperationService.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/services/OperationService.java new file mode 100644 index 0000000000..9df62e75a2 --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/client/services/OperationService.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, 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.extension.siddhi.device.client.services; + +import org.wso2.carbon.device.mgt.common.operation.mgt.Activity; +import org.wso2.extension.siddhi.device.client.dto.OperationRequest; + +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * Add Operation REST resource. + */ +@Path("/devices") +public interface OperationService { + + @POST + @Path("/{type}/operations") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + Activity addOperation(@PathParam("type") String type, OperationRequest operationRequest); + +} diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/utils/ClientUtils.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/utils/ClientUtils.java new file mode 100644 index 0000000000..f51d9619ac --- /dev/null +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/utils/ClientUtils.java @@ -0,0 +1,223 @@ +/* +* Copyright (c) 2018, 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.extension.siddhi.device.utils; + +import okhttp3.OkHttpClient; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.base.ServerConfiguration; + +import javax.net.ssl.*; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.*; +import java.security.*; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ClientUtils { + + private static final Log log = LogFactory.getLog(ClientUtils.class); + + private static final String KEY_STORE_TYPE = "JKS"; + /** + * Default truststore type of the client + */ + private static final String TRUST_STORE_TYPE = "JKS"; + /** + * Default keymanager type of the client + */ + private static final String KEY_MANAGER_TYPE = "SunX509"; //Default Key Manager Type + /** + * Default trustmanager type of the client + */ + private static final String TRUST_MANAGER_TYPE = "SunX509"; //Default Trust Manager Type + + private static final String SSLV3 = "SSLv3"; + + private static final String DEFAULT_HOST = "localhost"; + + private static final String DEFAULT_HOST_IP = "127.0.0.1"; + + + //This method is only used if the mb features are within DAS. + public static String replaceProperties(String text) { + String regex = "\\$\\{(.*?)\\}"; + Pattern pattern = Pattern.compile(regex); + Matcher matchPattern = pattern.matcher(text); + while (matchPattern.find()) { + String sysPropertyName = matchPattern.group(1); + String sysPropertyValue = System.getProperty(sysPropertyName); + if (sysPropertyValue != null && !sysPropertyName.isEmpty()) { + text = text.replaceAll("\\$\\{(" + sysPropertyName + ")\\}", sysPropertyValue); + } + } + return text; + } + + public static OkHttpClient getSSLClient() { + + boolean isIgnoreHostnameVerification = Boolean.parseBoolean(System.getProperty("org.wso2" + + ".ignoreHostnameVerification")); + OkHttpClient okHttpClient; + final String proxyHost = System.getProperty("http.proxyHost"); + final String proxyPort = System.getProperty("http.proxyPort"); + final String nonProxyHostsValue = System.getProperty("http.nonProxyHosts"); + + final ProxySelector proxySelector = new ProxySelector() { + @Override + public List select(URI uri) { + List proxyList = new ArrayList<>(); + String host = uri.getHost(); + + if (!StringUtils.isEmpty(host)) { + if (host.startsWith(DEFAULT_HOST_IP) || host.startsWith(DEFAULT_HOST) || StringUtils + .isEmpty(nonProxyHostsValue) || StringUtils.contains(nonProxyHostsValue, host) || + StringUtils.isEmpty(proxyHost) || StringUtils.isEmpty(proxyPort)) { + proxyList.add(Proxy.NO_PROXY); + } else { + proxyList.add(new Proxy(Proxy.Type.HTTP, + new InetSocketAddress(proxyHost, Integer.parseInt(proxyPort)))); + } + } else { + log.error("Host is null. Host could not be empty or null"); + } + return proxyList; + } + + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + throw new UnsupportedOperationException("Not supported yet."); + } + }; + + X509TrustManager trustAllCerts = new X509TrustManager() { + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[0]; + } + public void checkClientTrusted( + java.security.cert.X509Certificate[] certs, String authType) { + } + public void checkServerTrusted( + java.security.cert.X509Certificate[] certs, String authType) { + } + }; + if(isIgnoreHostnameVerification) { + okHttpClient = new OkHttpClient.Builder() + .sslSocketFactory(getSimpleTrustedSSLSocketFactory(), trustAllCerts) + .hostnameVerifier(new HostnameVerifier() { + @Override + public boolean verify(String s, SSLSession sslSession) { + return true; + } + }).proxySelector(proxySelector).build(); + return okHttpClient; + }else { + SSLSocketFactory trustedSSLSocketFactory = getTrustedSSLSocketFactory(); + okHttpClient = new OkHttpClient.Builder().sslSocketFactory(trustedSSLSocketFactory) + .proxySelector(proxySelector).build(); + return okHttpClient; + } + } + + private static SSLSocketFactory getSimpleTrustedSSLSocketFactory() { + try { + TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + public void checkClientTrusted( + java.security.cert.X509Certificate[] certs, String authType) { + } + public void checkServerTrusted( + java.security.cert.X509Certificate[] certs, String authType) { + } + } + }; + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new java.security.SecureRandom()); + return sc.getSocketFactory(); + } catch (KeyManagementException | NoSuchAlgorithmException e) { + return null; + } + + } + + private static SSLSocketFactory getTrustedSSLSocketFactory() { + try { + String keyStorePassword = ServerConfiguration.getInstance().getFirstProperty("Security.KeyStore.Password"); + String keyStoreLocation = ServerConfiguration.getInstance().getFirstProperty("Security.KeyStore.Location"); + String trustStorePassword = ServerConfiguration.getInstance().getFirstProperty( + "Security.TrustStore.Password"); + String trustStoreLocation = ServerConfiguration.getInstance().getFirstProperty( + "Security.TrustStore.Location"); + KeyStore keyStore = loadKeyStore(keyStoreLocation,keyStorePassword,KEY_STORE_TYPE); + KeyStore trustStore = loadTrustStore(trustStoreLocation,trustStorePassword); + + return initSSLConnection(keyStore,keyStorePassword,trustStore); + } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException + |CertificateException | IOException | UnrecoverableKeyException e) { + log.error("Error while creating the SSL socket factory due to "+e.getMessage(),e); + return null; + } + + } + + private static SSLSocketFactory initSSLConnection(KeyStore keyStore,String keyStorePassword,KeyStore trustStore) throws NoSuchAlgorithmException, UnrecoverableKeyException, + KeyStoreException, KeyManagementException { + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KEY_MANAGER_TYPE); + keyManagerFactory.init(keyStore, keyStorePassword.toCharArray()); + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TRUST_MANAGER_TYPE); + trustManagerFactory.init(trustStore); + + // Create and initialize SSLContext for HTTPS communication + SSLContext sslContext = SSLContext.getInstance(SSLV3); + sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); + SSLContext.setDefault(sslContext); + return sslContext.getSocketFactory(); + } + + + private static KeyStore loadKeyStore(String keyStorePath, String ksPassword,String type) + throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException { + InputStream fileInputStream = null; + try { + char[] keypassChar = ksPassword.toCharArray(); + KeyStore keyStore = KeyStore.getInstance(type); + fileInputStream = new FileInputStream(keyStorePath); + keyStore.load(fileInputStream, keypassChar); + return keyStore; + } finally { + if (fileInputStream != null) { + fileInputStream.close(); + } + } + } + + private static KeyStore loadTrustStore(String trustStorePath, String tsPassword) + throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException { + return loadKeyStore(trustStorePath,tsPassword,TRUST_STORE_TYPE); + } +} \ No newline at end of file diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/utils/DeviceUtils.java b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/utils/DeviceUtils.java index 897e026c67..b7109c8b18 100644 --- a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/utils/DeviceUtils.java +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/java/org/wso2/extension/siddhi/device/utils/DeviceUtils.java @@ -20,8 +20,10 @@ package org.wso2.extension.siddhi.device.utils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.common.notification.mgt.NotificationManagementService; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.device.mgt.core.service.GroupManagementProviderService; +import org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService; /** * This class holds utility methods to retrieve data. @@ -31,6 +33,8 @@ public class DeviceUtils { private static Log log = LogFactory.getLog(DeviceUtils.class); private static DeviceManagementProviderService deviceManagementProviderService; private static GroupManagementProviderService groupManagementProviderService; + private static NotificationManagementService notificationManagementService; + private static JWTClientManagerService jwtClientManagerService; private DeviceUtils(){ } @@ -64,4 +68,34 @@ public class DeviceUtils { } return groupManagementProviderService; } + + public static NotificationManagementService getNotificationManagementService() { + if (notificationManagementService != null) { + return notificationManagementService; + } + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + notificationManagementService = + (NotificationManagementService) ctx.getOSGiService(NotificationManagementService.class, null); + if (notificationManagementService == null) { + String msg = "Notification Management service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return notificationManagementService; + } + + public static JWTClientManagerService getJWTClientManagerService() { + if (jwtClientManagerService != null) { + return jwtClientManagerService; + } + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + jwtClientManagerService = + (JWTClientManagerService) ctx.getOSGiService(JWTClientManagerService.class, null); + if (jwtClientManagerService == null) { + String msg = "JWTClient Manager service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + return jwtClientManagerService; + } } diff --git a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/resources/device.siddhiext b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/resources/device.siddhiext index 84dd50b95f..8b9d47d9f0 100644 --- a/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/resources/device.siddhiext +++ b/components/extensions/siddhi-extensions/org.wso2.extension.siddhi.device/src/main/resources/device.siddhiext @@ -22,3 +22,5 @@ hasDevicesOfUser=org.wso2.extension.siddhi.device.HasDevicesOfUserFunctionExecut getDevicesOfStatus=org.wso2.extension.siddhi.device.GetDevicesOfStatusFunctionExecutor hasDevicesOfStatus=org.wso2.extension.siddhi.device.HasDevicesOfStatusFunctionExecutor isEnrolled=org.wso2.extension.siddhi.device.IsEnrolledFunctionExecutor +addNotification=org.wso2.extension.siddhi.device.AddNotificationFunctionProcessor +addOperation=org.wso2.extension.siddhi.device.AddOperationFunctionProcessor \ No newline at end of file diff --git a/features/extensions-feature/org.wso2.extension.siddhi.device.feature/src/main/resources/build.properties b/features/extensions-feature/org.wso2.extension.siddhi.device.feature/src/main/resources/build.properties new file mode 100644 index 0000000000..9c86577d76 --- /dev/null +++ b/features/extensions-feature/org.wso2.extension.siddhi.device.feature/src/main/resources/build.properties @@ -0,0 +1 @@ +custom = true diff --git a/features/extensions-feature/org.wso2.extension.siddhi.device.feature/src/main/resources/conf/siddhi-integration.xml b/features/extensions-feature/org.wso2.extension.siddhi.device.feature/src/main/resources/conf/siddhi-integration.xml new file mode 100644 index 0000000000..8b762e3e4c --- /dev/null +++ b/features/extensions-feature/org.wso2.extension.siddhi.device.feature/src/main/resources/conf/siddhi-integration.xml @@ -0,0 +1,26 @@ + + + + https://${iot.gateway.host}:${iot.gateway.https.port}/api-application-registration/register + https://${iot.gateway.host}:${iot.gateway.https.port} + admin + admin + \ No newline at end of file diff --git a/features/extensions-feature/org.wso2.extension.siddhi.device.feature/src/main/resources/p2.inf b/features/extensions-feature/org.wso2.extension.siddhi.device.feature/src/main/resources/p2.inf new file mode 100644 index 0000000000..d55300665a --- /dev/null +++ b/features/extensions-feature/org.wso2.extension.siddhi.device.feature/src/main/resources/p2.inf @@ -0,0 +1,2 @@ +instructions.configure = \ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.extension.siddhi.device_${feature.version}/conf/siddhi-integration.xml,target:${installFolder}/../../conf/siddhi-integration.xml,overwrite:true);\ diff --git a/pom.xml b/pom.xml index 6c5de3eb21..8b357f257d 100644 --- a/pom.xml +++ b/pom.xml @@ -1171,6 +1171,21 @@ ${javassist.version} test + + com.squareup.okhttp3 + okhttp + ${squareup.okhttp3.version} + + + com.squareup.okio + okio + ${okio.version} + + + io.github.openfeign + feign-okhttp + ${github.openfeign.version} + @@ -1353,6 +1368,10 @@ 1.4.0.wso2v1 3.12.1.GA 1.7.0 + + 3.8.1 + 1.13.0 + 9.3.1