diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/pom.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/pom.xml new file mode 100644 index 0000000000..65b46b32a3 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/pom.xml @@ -0,0 +1,338 @@ + + + + + + + org.wso2.carbon.devicemgt + grafana-mgt + 5.0.3-SNAPSHOT + ../pom.xml + + + 4.0.0 + io.entgra.analytics.mgt.grafana.proxy.api + war + Entgra - Grafana Proxy API + Entgra - Grafana Proxy API + http://entgra.io + + + + + maven-compiler-plugin + + 1.8 + 1.8 + + + + maven-war-plugin + + WEB-INF/lib/*cxf*.jar + api#grafana-mgt#v1.0 + + + + + + + + deploy + + compile + + + org.apache.maven.plugins + maven-antrun-plugin + 1.7 + + + compile + + run + + + + + + + + + + + + + + + + + + + + + org.testng + testng + test + + + org.springframework + spring-web + provided + + + org.apache.cxf + cxf-bundle + 3.0.0-milestone2 + test + + + org.apache.cxf + cxf-bundle-jaxrs + provided + + + commons-httpclient.wso2 + commons-httpclient + provided + + + org.wso2.carbon + org.wso2.carbon.utils + provided + + + org.wso2.carbon.identity.framework + org.wso2.carbon.user.mgt + provided + + + org.slf4j + slf4j-api + + + org.slf4j + jcl-over-slf4j + + + + + org.wso2.carbon + org.wso2.carbon.logging + provided + + + org.wso2.carbon.identity.inbound.auth.oauth2 + org.wso2.carbon.identity.oauth.stub + provided + + + org.apache.axis2.wso2 + axis2-client + + + + + org.json.wso2 + json + + + commons-codec.wso2 + commons-codec + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.certificate.mgt.core + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.core + + + + + io.swagger + swagger-annotations + + + io.swagger + swagger-core + + + com.fasterxml.jackson.module + jackson-module-jaxb-annotations + + + org.slf4j + slf4j-api + + + org.wso2.orbit.com.fasterxml.jackson.core + jackson-core + + + + + io.swagger + swagger-jaxrs + + + com.fasterxml.jackson.module + jackson-module-jaxb-annotations + + + org.slf4j + slf4j-api + + + org.wso2.orbit.com.fasterxml.jackson.core + jackson-core + + + + + javax.servlet + javax.servlet-api + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.apimgt.annotations + provided + + + org.wso2.orbit.com.fasterxml.jackson.core + jackson-annotations + + + org.hibernate + hibernate-validator + + + javax.ws.rs + javax.ws.rs-api + provided + + + javax.ws.rs + jsr311-api + provided + + + org.wso2.carbon.commons + org.wso2.carbon.application.mgt.stub + provided + + + org.wso2.carbon.analytics + org.wso2.carbon.analytics.api + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.identity.jwt.client.extension + provided + + + org.wso2.carbon + org.wso2.carbon.registry.core + provided + + + org.wso2.carbon.registry + org.wso2.carbon.registry.resource + provided + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.user.store.count + ${carbon.identity.framework.version} + provided + + + + + + + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.receiver.stub + provided + + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.stream.stub + provided + + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.publisher.stub + provided + + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.stream.persistence.stub + provided + + + org.powermock + powermock-module-testng + test + + + org.powermock + powermock-api-mockito + test + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.common + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.extensions + provided + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.claim.metadata.mgt + ${carbon.identity.framework.version} + provided + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.core + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.core + provided + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.common + provided + + + + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/GrafanaAPIProxyService.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/GrafanaAPIProxyService.java new file mode 100644 index 0000000000..3921e6fc34 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/GrafanaAPIProxyService.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.api.service; + +import com.google.gson.JsonObject; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.Extension; +import io.swagger.annotations.ExtensionProperty; +import io.swagger.annotations.Info; +import io.swagger.annotations.SwaggerDefinition; +import io.swagger.annotations.Tag; +import org.wso2.carbon.apimgt.annotations.api.Scope; +import org.wso2.carbon.apimgt.annotations.api.Scopes; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +@SwaggerDefinition( + info = @Info( + version = "1.0.0", + title = "Grafana API Proxy Service", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = "name", value = "GrafanaAPIManagement"), + @ExtensionProperty(name = "context", value = "/api/grafana-mgt/v1.0/api"), + }) + } + ), + tags = { + @Tag(name = "analytics_management", description = "") + } +) +@Scopes( + scopes = { + @Scope( + name = "Using Grafana APIs required for Grafana iframes", + description = "Grafana API proxy to validate requests.", + key = "perm:grafana:api:view", + roles = {"Internal/grafanamgt-user"}, + permissions = {"/analytics-mgt/grafana-mgt/api/view"} + ) + } +) + +@Path("/api") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.MEDIA_TYPE_WILDCARD) +@Api(value = "Grafana API Management", description = "Grafana api related operations can be found here.") +public interface GrafanaAPIProxyService { + + String SCOPE = "scope"; + + @POST + @Path("/ds/query") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "POST", + value = "Grafana query API proxy", + tags = "Analytics", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:grafana:api:view") + }) + } + ) + Response queryDatasource(JsonObject body, @Context HttpHeaders headers, @Context UriInfo requestUriInfo); + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/frontend-metrics") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "POST", + value = "Grafana frontend-metric API proxy", + tags = "Analytics", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:grafana:api:view") + }) + } + ) + Response frontendMetrics(JsonObject body, @Context HttpHeaders headers, @Context UriInfo requestUriInfo); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/dashboards/uid/{uid}") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "POST", + value = "Grafana dashboard details API proxy", + tags = "Analytics", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:grafana:api:view") + }) + } + ) + Response getDashboard(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) throws ClassNotFoundException; + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/annotations") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "Grafana annotations API proxy", + tags = "Analytics", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:grafana:api:view") + }) + } + ) + Response getAnnotations(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) throws ClassNotFoundException; + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/alerts/states-for-dashboard") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "POST", + value = "Get Grafana alert states for dashboard details API proxy", + tags = "Analytics", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:grafana:api:view") + }) + } + ) + Response getAlertStateForDashboards(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) throws ClassNotFoundException; +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/ApiOriginFilter.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/ApiOriginFilter.java new file mode 100644 index 0000000000..5562c40ad0 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/ApiOriginFilter.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.api.service.addons; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class ApiOriginFilter implements Filter { + + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + HttpServletResponse res = (HttpServletResponse) response; + res.addHeader("Access-Control-Allow-Origin", "*"); + res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); + res.addHeader("Access-Control-Allow-Headers", "Content-Type"); + chain.doFilter(request, response); + } + + public void destroy() { + //do nothing + } + + public void init(FilterConfig filterConfig) throws ServletException { + //do nothing + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/GsonMessageBodyHandler.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/GsonMessageBodyHandler.java new file mode 100644 index 0000000000..f786cf645e --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/GsonMessageBodyHandler.java @@ -0,0 +1,91 @@ +/* + * 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 io.entgra.analytics.mgt.grafana.proxy.api.service.addons; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import java.io.*; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +@Provider +@Produces(APPLICATION_JSON) +@Consumes(APPLICATION_JSON) +public class GsonMessageBodyHandler implements MessageBodyWriter, MessageBodyReader { + + public static final String DATE_FORMAT = "EEE, d MMM yyyy HH:mm:ss Z"; + private Gson gson; + private static final String UTF_8 = "UTF-8"; + + public boolean isReadable(Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { + return true; + } + + private Gson getGson() { + if (gson == null) { + final GsonBuilder gsonBuilder = new GsonBuilder(); + gson = gsonBuilder.setDateFormat(DATE_FORMAT).create(); + } + return gson; + } + + public Object readFrom(Class objectClass, Type type, Annotation[] annotations, MediaType mediaType, + MultivaluedMap stringStringMultivaluedMap, InputStream entityStream) + throws IOException, WebApplicationException { + + InputStreamReader reader = new InputStreamReader(entityStream, "UTF-8"); + + try { + return getGson().fromJson(reader, type); + } finally { + reader.close(); + } + } + + public boolean isWriteable(Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { + return true; + } + + public long getSize(Object o, Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { + return -1; + } + + public void writeTo(Object object, Class aClass, Type type, Annotation[] annotations, MediaType mediaType, + MultivaluedMap stringObjectMultivaluedMap, OutputStream entityStream) + throws IOException, WebApplicationException { + + OutputStreamWriter writer = new OutputStreamWriter(entityStream, UTF_8); + try { + getGson().toJson(object, type, writer); + } finally { + writer.close(); + } + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/ValidationInterceptor.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/ValidationInterceptor.java new file mode 100644 index 0000000000..3ff1928ef6 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/ValidationInterceptor.java @@ -0,0 +1,121 @@ +/* Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.api.service.addons; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.cxf.interceptor.Fault; +import org.apache.cxf.jaxrs.lifecycle.ResourceProvider; +import org.apache.cxf.jaxrs.model.ClassResourceInfo; +import org.apache.cxf.jaxrs.model.OperationResourceInfo; +import org.apache.cxf.message.Message; +import org.apache.cxf.message.MessageContentsList; +import org.apache.cxf.phase.AbstractPhaseInterceptor; +import org.apache.cxf.phase.Phase; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.executable.ExecutableValidator; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; + +public class ValidationInterceptor extends AbstractPhaseInterceptor { + private Log log = LogFactory.getLog(getClass()); + private Validator validator = null; //validator interface is thread-safe + + public ValidationInterceptor() { + super(Phase.PRE_INVOKE); + ValidatorFactory defaultFactory = Validation.buildDefaultValidatorFactory(); + validator = defaultFactory.getValidator(); + if (validator == null) { + log.warn("Bean Validation provider could not be found, no validation will be performed"); + } else { + log.debug("Validation In-Interceptor initialized successfully"); + } + } + + @Override + public void handleMessage(Message message) throws Fault { + final OperationResourceInfo operationResource = message.getExchange().get(OperationResourceInfo.class); + if (operationResource == null) { + log.info("OperationResourceInfo is not available, skipping validation"); + return; + } + + final ClassResourceInfo classResource = operationResource.getClassResourceInfo(); + if (classResource == null) { + log.info("ClassResourceInfo is not available, skipping validation"); + return; + } + + final ResourceProvider resourceProvider = classResource.getResourceProvider(); + if (resourceProvider == null) { + log.info("ResourceProvider is not available, skipping validation"); + return; + } + + final List arguments = MessageContentsList.getContentsList(message); + final Method method = operationResource.getAnnotatedMethod(); + final Object instance = resourceProvider.getInstance(message); + if (method != null && arguments != null) { + //validate the parameters(arguments) over the invoked method + validate(method, arguments.toArray(), instance); + + //validate the fields of each argument + for (Object arg : arguments) { + if (arg != null) + validate(arg); + } + } + + } + + public void validate(final Method method, final Object[] arguments, final T instance) { + if (validator == null) { + log.warn("Bean Validation provider could not be found, no validation will be performed"); + return; + } + + ExecutableValidator methodValidator = validator.forExecutables(); + Set> violations = methodValidator.validateParameters(instance, + method, arguments); + + if (!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } + } + + public void validate(final T object) { + if (validator == null) { + log.warn("Bean Validation provider could be found, no validation will be performed"); + return; + } + + Set> violations = validator.validate(object); + + if (!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } + } + + public void handleFault(org.apache.cxf.message.Message messageParam) { + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/bean/ErrorListItem.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/bean/ErrorListItem.java new file mode 100644 index 0000000000..aca4e22b56 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/bean/ErrorListItem.java @@ -0,0 +1,77 @@ +/* + * 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 io.entgra.analytics.mgt.grafana.proxy.api.service.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; + +@ApiModel(description = "") +public class ErrorListItem { + + @NotNull + private String code = null; + @NotNull + private String message = null; + + @ApiModelProperty(required = true, value = "") + @JsonProperty("code") + public String getCode() { + return code; + } + public void setCode(String code) { + this.code = code; + } + + public ErrorListItem() {} + + public ErrorListItem(String code, String msg) { + this.code = code; + this.message = msg; + } + + + /** + * Description about individual errors occurred + **/ + @ApiModelProperty(required = true, value = "Description about individual errors occurred") + @JsonProperty("message") + public String getMessage() { + return message; + } + public void setMessage(String message) { + this.message = message; + } + + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("errorItem {\n"); + + sb.append(" code: ").append(code).append("\n"); + sb.append(" message: ").append(message).append("\n"); + sb.append("}\n"); + return sb.toString(); + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/bean/ErrorResponse.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/bean/ErrorResponse.java new file mode 100644 index 0000000000..1287ce5f76 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/bean/ErrorResponse.java @@ -0,0 +1,157 @@ +/* + * 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 io.entgra.analytics.mgt.grafana.proxy.api.service.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import java.util.ArrayList; +import java.util.List; + +@ApiModel(description = "") +public class ErrorResponse { + + private Long code = null; + private String message = null; + private String description = null; + private String moreInfo = null; + private List errorItems = new ArrayList<>(); + + public ErrorResponse() { + } + + @JsonProperty(value = "code") + @ApiModelProperty(required = true, value = "") + public Long getCode() { + return code; + } + + public void setCode(Long code) { + this.code = code; + } + + @JsonProperty(value = "message") + @ApiModelProperty(required = true, value = "ErrorResponse message.") + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @JsonProperty(value = "description") + @ApiModelProperty(value = "A detail description about the error message.") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @JsonProperty(value = "moreInfo") + @ApiModelProperty(value = "Preferably an url with more details about the error.") + public String getMoreInfo() { + return moreInfo; + } + + public void setMoreInfo(String moreInfo) { + this.moreInfo = moreInfo; + } + + public void addErrorListItem(ErrorListItem item) { + this.errorItems.add(item); + } + + /** + * If there are more than one error list them out. \nFor example, list out validation errors by each field. + */ + @JsonProperty(value = "errorItems") + @ApiModelProperty(value = "If there are more than one error list them out. \n" + + "For example, list out validation errors by each field.") + public List getErrorItems() { + return errorItems; + } + + public void setErrorItems(List error) { + this.errorItems = error; + } + + @Override + public String toString() { + return null; + } + + public static class ErrorResponseBuilder { + + private Long code = null; + private String message = null; + private String description = null; + private String moreInfo = null; + private List error; + + + public ErrorResponseBuilder() { + this.error = new ArrayList<>(); + } + + public ErrorResponseBuilder setCode(long code) { + this.code = code; + return this; + } + + public ErrorResponseBuilder setMessage(String message) { + this.message = message; + return this; + } + + public ErrorResponseBuilder setDescription(String description) { + this.description = description; + return this; + } + + public ErrorResponseBuilder setMoreInfo(String moreInfo) { + this.moreInfo = moreInfo; + return this; + } + + public ErrorResponseBuilder addErrorItem(String code, String msg) { + ErrorListItem item = new ErrorListItem(); + item.setCode(code); + item.setMessage(msg); + this.error.add(item); + return this; + } + + public ErrorResponse build() { + ErrorResponse errorResponse = new ErrorResponse(); + errorResponse.setCode(code); + errorResponse.setMessage(message); + errorResponse.setErrorItems(error); + errorResponse.setDescription(description); + errorResponse.setMoreInfo(moreInfo); + return errorResponse; + } + } + +} + + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/exception/RefererNotValid.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/exception/RefererNotValid.java new file mode 100644 index 0000000000..2e39f3d62e --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/exception/RefererNotValid.java @@ -0,0 +1,7 @@ +package io.entgra.analytics.mgt.grafana.proxy.api.service.exception; + +public class RefererNotValid extends Exception { + public RefererNotValid(String msg) { + super(msg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/impl/GrafanaAPIProxyServiceImpl.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/impl/GrafanaAPIProxyServiceImpl.java new file mode 100644 index 0000000000..ee227b3f18 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/impl/GrafanaAPIProxyServiceImpl.java @@ -0,0 +1,116 @@ +package io.entgra.analytics.mgt.grafana.proxy.api.service.impl; + +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.api.service.GrafanaAPIProxyService; +import io.entgra.analytics.mgt.grafana.proxy.api.service.bean.ErrorResponse; +import io.entgra.analytics.mgt.grafana.proxy.api.service.exception.RefererNotValid; +import io.entgra.analytics.mgt.grafana.proxy.api.service.impl.util.GrafanaRequestHandlerUtil; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.bean.GrafanaPanelIdentifier; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.MaliciousQueryAttempt; +import io.entgra.analytics.mgt.grafana.proxy.core.internal.GrafanaMgtDataHolder; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import java.io.IOException; +import java.sql.SQLException; + +@Path("/api") +public class GrafanaAPIProxyServiceImpl implements GrafanaAPIProxyService { + + private static final Log log = LogFactory.getLog(GrafanaAPIProxyServiceImpl.class); + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/ds/query") + @Override + public Response queryDatasource(JsonObject body, @Context HttpHeaders headers, @Context UriInfo requestUriInfo) { + try { + GrafanaPanelIdentifier panelIdentifier = GrafanaRequestHandlerUtil.getPanelIdentifier(headers); + GrafanaMgtDataHolder.getInstance().getGrafanaQueryService(). + buildSafeQuery(body, panelIdentifier.getDashboardId(), panelIdentifier.getPanelId(), requestUriInfo.getRequestUri()); + return GrafanaRequestHandlerUtil.proxyPassPostRequest(body, requestUriInfo, panelIdentifier.getOrgId()); + } catch (MaliciousQueryAttempt e) { + return Response.status(Response.Status.BAD_REQUEST).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(e.getMessage()).build()).build(); + } catch (GrafanaManagementException e) { + return GrafanaRequestHandlerUtil.constructInternalServerError(e, e.getMessage()); + } catch (RefererNotValid e) { + return GrafanaRequestHandlerUtil.constructInvalidReferer(); + } catch (SQLException | IOException | DBConnectionException | + io.entgra.application.mgt.common.exception.DBConnectionException e) { + log.error(e); + return GrafanaRequestHandlerUtil.constructInternalServerError(e, e.getMessage()); + } + } + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/frontend-metrics") + @Override + public Response frontendMetrics(JsonObject body, @Context HttpHeaders headers, @Context UriInfo requestUriInfo) { + return proxyPassPostRequest(body, headers, requestUriInfo); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/dashboards/uid/{uid}") + @Override + public Response getDashboard(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) { + return proxyPassGetRequest(headers, requestUriInfo); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/annotations") + @Override + public Response getAnnotations(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) { + return proxyPassGetRequest(headers, requestUriInfo); + } + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/alerts/states-for-dashboard") + @Override + public Response getAlertStateForDashboards(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) { + return proxyPassGetRequest(headers, requestUriInfo); + } + + public Response proxyPassGetRequest(HttpHeaders headers, UriInfo requestUriInfo) { + try { + GrafanaPanelIdentifier panelIdentifier = GrafanaRequestHandlerUtil.getPanelIdentifier(headers); + return GrafanaRequestHandlerUtil.proxyPassGetRequest(requestUriInfo, panelIdentifier.getOrgId()); + } catch (RefererNotValid e) { + return GrafanaRequestHandlerUtil.constructInvalidReferer(); + } catch (GrafanaManagementException e) { + return GrafanaRequestHandlerUtil.constructInternalServerError(e, e.getMessage()); + } + } + + public Response proxyPassPostRequest(JsonObject body, HttpHeaders headers, UriInfo requestUriInfo) { + try { + GrafanaPanelIdentifier panelIdentifier = GrafanaRequestHandlerUtil.getPanelIdentifier(headers); + return GrafanaRequestHandlerUtil.proxyPassPostRequest(body, requestUriInfo, panelIdentifier.getOrgId()); + } catch (RefererNotValid e) { + return GrafanaRequestHandlerUtil.constructInvalidReferer(); + } catch (GrafanaManagementException e) { + return GrafanaRequestHandlerUtil.constructInternalServerError(e, e.getMessage()); + } + } + + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/impl/util/GrafanaRequestHandlerUtil.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/impl/util/GrafanaRequestHandlerUtil.java new file mode 100644 index 0000000000..064ea037f4 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/impl/util/GrafanaRequestHandlerUtil.java @@ -0,0 +1,166 @@ +/* + * 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 io.entgra.analytics.mgt.grafana.proxy.api.service.impl.util; + +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.api.service.bean.ErrorResponse; +import io.entgra.analytics.mgt.grafana.proxy.api.service.exception.RefererNotValid; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.bean.GrafanaPanelIdentifier; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.GrafanaEnvVariablesNotDefined; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.wso2.carbon.device.mgt.core.common.util.HttpUtil; +import org.wso2.carbon.device.mgt.core.report.mgt.Constants; + +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.util.List; +import java.util.Map; + +public class GrafanaRequestHandlerUtil { + + private static final Log log = LogFactory.getLog(GrafanaRequestHandlerUtil.class); + + public static Response proxyPassGetRequest(UriInfo requestUriInfo, String orgId) throws GrafanaEnvVariablesNotDefined { + HttpGet grafanaGetReq = new HttpGet(); + return forwardRequestToGrafanaEndpoint(grafanaGetReq, requestUriInfo, orgId); + } + + public static Response proxyPassPostRequest(JsonObject body, UriInfo requestUriInfo, String orgId) + throws GrafanaEnvVariablesNotDefined { + HttpPost grafanaPostReq = new HttpPost(); + try { + setRequestEntity(grafanaPostReq, body); + } catch (UnsupportedEncodingException e) { + String errorMsg = "Error occurred while parsing body"; + log.error(errorMsg, e); + return Response.status(Response.Status.BAD_REQUEST).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(errorMsg).build()).build(); + } + return forwardRequestToGrafanaEndpoint(grafanaPostReq, requestUriInfo, orgId); + } + + private static Response forwardRequestToGrafanaEndpoint(HttpRequestBase requestBase, UriInfo requestUriInfo, String orgId) + throws GrafanaEnvVariablesNotDefined { + URI grafanaUri = generateGrafanaUri(requestUriInfo); + requestBase.setURI(grafanaUri); + requestBase.setHeader(GrafanaConstants.X_GRAFANA_ORG_ID_HEADER, orgId); + try(CloseableHttpClient client = HttpClients.createDefault()) { + HttpResponse grafanaResponse = invokeGrafanaAPI(client, requestBase); + String grafanaResponseBody = HttpUtil.getResponseString(grafanaResponse); + return Response.status(Response.Status.OK).entity(grafanaResponseBody). + header(HttpHeaders.CONTENT_TYPE, HttpUtil.getContentType(grafanaResponse)).build(); + } catch (IOException e) { + String msg = "Error occurred while calling Grafana API"; + log.error(msg, e); + return Response.serverError().entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); + } catch (GrafanaManagementException e) { + String err = "Error occurred while retrieving Grafana configuration"; + log.error(err, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(err).build()).build(); + } + } + + public static HttpResponse invokeGrafanaAPI(HttpClient client, HttpRequestBase request) throws IOException, GrafanaManagementException { + setBasicAuthHeader(request); + return client.execute(request); + } + + public static void setBasicAuthHeader(HttpRequestBase request) throws GrafanaManagementException { + String basicAuth = GrafanaUtil.getBasicAuthBase64Header(); + request.setHeader(HttpHeaders.AUTHORIZATION, basicAuth); + } + + public static URI generateGrafanaUri(UriInfo requestUriInfo) throws GrafanaEnvVariablesNotDefined { + String base = GrafanaUtil.getGrafanaHTTPBase(requestUriInfo.getRequestUri().getScheme()); + String grafanaRequestPath = getGrafanaRequestPathWQuery(requestUriInfo); + return HttpUtil.createURI(GrafanaUtil.generateGrafanaUrl(grafanaRequestPath, base)); + } + + public static String getGrafanaRequestPathWQuery(UriInfo requestUriInfo) { + String path = requestUriInfo.getPath(); + String queryParam = requestUriInfo.getRequestUri().getRawQuery(); + if (queryParam != null) { + path += Constants.URI_QUERY_SEPARATOR + queryParam; + } + return path; + } + + public static GrafanaPanelIdentifier getPanelIdentifier(HttpHeaders headers) throws RefererNotValid { + String referer = headers.getHeaderString(GrafanaConstants.REFERER_HEADER); + if(referer == null) { + String errMsg = "Request does not contain Referer header"; + log.error(errMsg); + throw new RefererNotValid(errMsg); + } + GrafanaPanelIdentifier panelIdentifier = GrafanaUtil.getPanelIdentifierFromReferer(referer); + if(panelIdentifier.getDashboardId() == null || + panelIdentifier.getPanelId() == null || panelIdentifier.getOrgId() == null) { + String errMsg = "Referer must contain dashboardId, panelId and orgId"; + log.error(errMsg); + throw new RefererNotValid(errMsg); + } + return panelIdentifier; + } + + public static void setRequestEntity(HttpPost postRequest, JsonObject body) throws UnsupportedEncodingException { + StringEntity bodyEntity = new StringEntity(body.toString()); + bodyEntity.setContentType(MediaType.APPLICATION_JSON); + postRequest.setEntity(bodyEntity); + } + + public static Response constructInvalidReferer() { + String errorMsg = "Request does not contain a valid Referer header"; + return Response.status(Response.Status.BAD_REQUEST).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(errorMsg).build()).build(); + } + public static Response constructInternalServerError(Exception e, String errMsg) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(errMsg).build()).build(); + } + + public static void copyHeadersToGrafanaRequest(HttpRequestBase grafanaRequest, HttpHeaders headers) { + Map> headerValues = headers.getRequestHeaders(); + for (String key : headerValues.keySet()) { + if (!key.equals(HttpHeaders.AUTHORIZATION) && !key.equals(HttpHeaders.CONTENT_LENGTH)) { + for (String value : headerValues.get(key)) { + grafanaRequest.setHeader(key, value); + } + } + } + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/META-INF/permissions.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/META-INF/permissions.xml new file mode 100644 index 0000000000..190a634a12 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/META-INF/permissions.xml @@ -0,0 +1,32 @@ + + + + + + + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/META-INF/webapp-classloading.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/META-INF/webapp-classloading.xml new file mode 100644 index 0000000000..9f50930c4f --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/META-INF/webapp-classloading.xml @@ -0,0 +1,35 @@ + + + + + + + + + false + + + CXF3,Carbon + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/WEB-INF/cxf-servlet.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/WEB-INF/cxf-servlet.xml new file mode 100644 index 0000000000..0f6ec36cf0 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/WEB-INF/cxf-servlet.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/WEB-INF/web.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..6ef280f763 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,113 @@ + + + + Grafana-API-Proxy-Webapp + + JAX-WS/JAX-RS Grafana API Management Endpoint + JAX-WS/JAX-RS Servlet + CXFServlet + + org.apache.cxf.transport.servlet.CXFServlet + + + + swagger.security.filter + ApiAuthorizationFilterImpl + + 1 + + + CXFServlet + /* + + + 60 + + + + doAuthentication + true + + + + nonSecuredEndPoints + + + + + + managed-api-enabled + true + + + managed-api-owner + admin + + + isSharedWithAllTenants + true + + + + ApiOriginFilter + io.entgra.analytics.mgt.grafana.proxy.api.service.addons.ApiOriginFilter + + + + ApiOriginFilter + /* + + + + HttpHeaderSecurityFilter + org.apache.catalina.filters.HttpHeaderSecurityFilter + + hstsEnabled + false + + + + + ContentTypeBasedCachePreventionFilter + org.wso2.carbon.ui.filters.cache.ContentTypeBasedCachePreventionFilter + + patterns + text/html" ,application/json" ,text/plain + + + filterAction + enforce + + + httpHeaders + Cache-Control: no-store, no-cache, must-revalidate, private + + + + + HttpHeaderSecurityFilter + /* + + + + ContentTypeBasedCachePreventionFilter + /* + + + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.common/pom.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.common/pom.xml new file mode 100644 index 0000000000..4780a7f221 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.common/pom.xml @@ -0,0 +1,107 @@ + + + + + + + org.wso2.carbon.devicemgt + grafana-mgt + 5.0.3-SNAPSHOT + ../pom.xml + + + 4.0.0 + io.entgra.analytics.mgt.grafana.proxy.common + bundle + Entgra - Grafana API Handler Common + Entgra - Grafana API Handler Common + http://entgra.io + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.artifactId} + ${project.artifactId} + ${carbon.device.mgt.version} + Grafana Management Common Bundle + + io.entgra.analytics.mgt.grafana.proxy.common.* + + + javax.xml.bind.annotation; version="${javax.xml.bind.imp.pkg.version}", + com.fasterxml.jackson.annotation;version="${jackson-annotations.version}", + io.swagger.annotations; version="${swagger.annotations.version}"; resolution:=optional, + com.google.gson + + + + + + org.jacoco + jacoco-maven-plugin + + ${basedir}/target/coverage-reports/jacoco-unit.exec + + + + jacoco-initialize + + prepare-agent + + + + jacoco-site + test + + report + + + ${basedir}/target/coverage-reports/jacoco-unit.exec + ${basedir}/target/coverage-reports/site + + + + + + + + + + + io.swagger + swagger-annotations + provided + + + org.wso2.orbit.com.fasterxml.jackson.core + jackson-annotations + + + com.google.code.gson + gson + + + + + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.common/src/main/java/io/entgra/analytics/mgt/grafana/proxy/common/exception/GrafanaManagementException.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.common/src/main/java/io/entgra/analytics/mgt/grafana/proxy/common/exception/GrafanaManagementException.java new file mode 100644 index 0000000000..64ec9b2731 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.common/src/main/java/io/entgra/analytics/mgt/grafana/proxy/common/exception/GrafanaManagementException.java @@ -0,0 +1,31 @@ +/* + * 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 io.entgra.analytics.mgt.grafana.proxy.common.exception; + +public class GrafanaManagementException extends Exception{ + + private static final long serialVersionUID = -3922279312829079297L; + + public GrafanaManagementException(String msg, Exception nestedEx) { + super(msg, nestedEx); + } + public GrafanaManagementException(String msg) { + super(msg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/pom.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/pom.xml new file mode 100644 index 0000000000..05d8e9d54a --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/pom.xml @@ -0,0 +1,329 @@ + + + + + + + org.wso2.carbon.devicemgt + grafana-mgt + 5.0.3-SNAPSHOT + ../pom.xml + + + 4.0.0 + io.entgra.analytics.mgt.grafana.proxy.core + bundle + Entgra - Grafana API Handler Core + Entgra - Grafana API Handler Core + http://entgra.io + + + + + org.apache.felix + maven-scr-plugin + + + org.jacoco + jacoco-maven-plugin + + ${basedir}/target/coverage-reports/jacoco-unit.exec + + + + jacoco-initialize + + prepare-agent + + + + jacoco-site + test + + report + + + ${basedir}/target/coverage-reports/jacoco-unit.exec + ${basedir}/target/coverage-reports/site + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.artifactId} + ${project.artifactId} + ${carbon.device.mgt.version} + Grafana API Management Core Bundle + io.entgra.analytics.mgt.grafana.proxy.core.internal + + io.entgra.analytics.mgt.grafana.proxy.common.*, + javax.xml.parsers;version="${javax.xml.parsers.import.pkg.version}";resolution:=optional, + javax.xml.bind.annotation, + javax.xml.bind, + org.apache.commons.lang, + org.wso2.carbon, + org.wso2.carbon.device.mgt.common.*, + org.wso2.carbon.device.mgt.core.* + io.entgra.application.mgt.core.* + + + !org.wso2.carbon.email.sender.core.internal, + io.entgra.analytics.mgt.grafana.proxy.core.* + + + scribe;scope=compile|runtime;inline=false, + + * + + + + + + + + + org.eclipse.osgi + org.eclipse.osgi + + + org.eclipse.osgi + org.eclipse.osgi.services + + + org.wso2.orbit.org.scannotation + scannotation + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.common + + + org.wso2.carbon + org.wso2.carbon.utils + + + org.wso2.carbon + org.wso2.carbon.logging + provided + + + org.wso2.carbon + org.wso2.carbon.core + provided + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.common + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.core + + + org.wso2.carbon.devicemgt + io.entgra.application.mgt.core + + + org.apache.httpcomponents.wso2 + httpclient + 4.1.1.wso2v1 + compile + + + org.apache.httpcomponents + httpclient + + + org.wso2.carbon.devicemgt + org.wso2.carbon.identity.jwt.client.extension + provided + + + javax.ws.rs + javax.ws.rs-api + + + javax.ws.rs + jsr311-api + + + org.javassist + javassist + + + org.powermock + powermock-api-mockito + + + org.wso2.carbon + org.wso2.carbon.ndatasource.core + + + commons-lang + commons-lang + + + + + commons-codec.wso2 + commons-codec + provided + + + com.h2database.wso2 + h2-database-engine + test + + + org.testng + testng + + + org.powermock + powermock-module-testng + test + + + org.wso2.carbon + org.wso2.carbon.user.core + + + org.wso2.carbon + org.wso2.carbon.user.api + + + org.wso2.carbon + org.wso2.carbon.registry.api + + + org.wso2.carbon + org.wso2.carbon.registry.core + + + org.apache.tomcat.wso2 + jdbc-pool + + + org.wso2.carbon + org.wso2.carbon.base + + + org.wso2.carbon.governance + org.wso2.carbon.governance.api + + + org.apache.axis2.transport + axis2-transport-mail + + + org.apache.ws.commons.axiom.wso2 + axiom + + + + org.apache.axis2.wso2 + axis2 + + + org.wso2.carbon.identity.inbound.auth.oauth2 + org.wso2.carbon.identity.oauth.stub + + + org.wso2.tomcat + tomcat + + + org.wso2.tomcat + tomcat-servlet-api + + + + + org.wso2.carbon.commons + org.wso2.carbon.ntask.core + + + + + commons-collections.wso2 + commons-collections + + + + org.wso2.carbon.devicemgt + org.wso2.carbon.email.sender.core + + + + mysql + mysql-connector-java + test + + + + com.google.code.gson + gson + + + + io.swagger + swagger-annotations + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.apimgt.annotations + + + org.wso2.carbon.event-processing + org.wso2.carbon.event.processor.stub + + + org.wso2.carbon.multitenancy + org.wso2.carbon.tenant.mgt + + + commons-validator + commons-validator + + + org.wso2.carbon.devicemgt + io.entgra.server.bootup.heartbeat.beacon + + + com.google.guava + guava + + + + + + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/bean/GrafanaPanelIdentifier.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/bean/GrafanaPanelIdentifier.java new file mode 100644 index 0000000000..4ff90e25b0 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/bean/GrafanaPanelIdentifier.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.bean; + +public class GrafanaPanelIdentifier { + + private String orgId; + private String dashboardUID; + private String panelId; + + public GrafanaPanelIdentifier(String orgId, String dashboardUID, String panelId) { + this.orgId = orgId; + this.dashboardUID = dashboardUID; + this.panelId = panelId; + } + + public String getOrgId() { + return orgId; + } + + public void setOrgId(String orgId) { + this.orgId = orgId; + } + + public String getDashboardId() { + return dashboardUID; + } + + public void setDashboardId(String dashboardId) { + this.dashboardUID = dashboardId; + } + + public String getPanelId() { + return panelId; + } + + public void setPanelId(String panelId) { + this.panelId = panelId; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/GrafanaConfiguration.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/GrafanaConfiguration.java new file mode 100644 index 0000000000..72ab1545fe --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/GrafanaConfiguration.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.config; + +import io.entgra.analytics.mgt.grafana.proxy.core.config.xml.bean.CacheConfiguration; +import io.entgra.analytics.mgt.grafana.proxy.core.config.xml.bean.User; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +@XmlRootElement(name = "GrafanaConfiguration") +public class GrafanaConfiguration { + + private User adminUser; + private List caches; + + @XmlElement(name = "AdminUser") + public User getAdminUser() { + return adminUser; + } + + public void setAdminUser(User user) { + this.adminUser = user; + } + + + @XmlElementWrapper(name = "Cache") + @XmlElement(name = "CacheConfiguration") + public List getCaches() { + return caches; + } + + public CacheConfiguration getCacheByName(String cacheName) { + for (CacheConfiguration cache : caches) { + if (cache.getName().equals(cacheName)) { + return cache; + } + } + return null; + } + + public void setCaches(List caches) { + this.caches = caches; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/GrafanaConfigurationManager.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/GrafanaConfigurationManager.java new file mode 100644 index 0000000000..a8ce50fb5f --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/GrafanaConfigurationManager.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.config; + +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Document; +import org.wso2.carbon.utils.CarbonUtils; + +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; + +public class GrafanaConfigurationManager { + + private static final Log log = LogFactory.getLog(GrafanaConfigurationManager.class); + private static GrafanaConfigurationManager grafanaConfigurationManager; + private GrafanaConfiguration grafanaConfiguration; + private static final String GRAFANA_CONFIG_PATH = CarbonUtils.getCarbonConfigDirPath() + File.separator + + GrafanaConstants.CONFIG_XML_NAME; + + public static GrafanaConfigurationManager getInstance() { + if (grafanaConfigurationManager == null) { + synchronized (GrafanaConfigurationManager.class) { + if (grafanaConfigurationManager == null) { + grafanaConfigurationManager = new GrafanaConfigurationManager(); + } + } + } + return grafanaConfigurationManager; + } + + /** + * Initialize the Grafana Configuration through the provided configuration location + * @param configLocation has the path of the configuration file + * @throws GrafanaManagementException throws when there are any errors during the initialization of + * Grafana configuration + */ + public synchronized void initConfig(String configLocation) throws GrafanaManagementException { + try { + File smsConfig = new File(configLocation); + Document doc = convertXMLToDocument(smsConfig); + + /* Un-marshaling Grafana configuration */ + JAXBContext smsContext = JAXBContext.newInstance(GrafanaConfiguration.class); + Unmarshaller unmarshaller = smsContext.createUnmarshaller(); + this.grafanaConfiguration = (GrafanaConfiguration) unmarshaller.unmarshal(doc); + } catch (JAXBException e) { + String msg = "Error occurred while initializing Grafana config '" + configLocation + "'"; + log.error(msg, e); + throw new GrafanaManagementException(msg, e); + } + } + + /** + * Initialize the Grafana Configuration through the grafana-config.xml file in the GRAFANA_CONFIG_PATH + * @throws GrafanaManagementException throws when there are any errors during the initialization of config + */ + public void initConfig() throws GrafanaManagementException { + this.initConfig(GRAFANA_CONFIG_PATH); + } + + public GrafanaConfiguration getGrafanaConfiguration() throws GrafanaManagementException { + if (grafanaConfiguration != null) { + return grafanaConfiguration; + } + initConfig(); + return grafanaConfiguration; + } + + private static Document convertXMLToDocument(File file) throws GrafanaManagementException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + try { + factory.setFeature(GrafanaConstants.XML.FEATURES_DISALLOW_DOCTYPE_DECL, true); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + DocumentBuilder docBuilder = factory.newDocumentBuilder(); + return docBuilder.parse(file); + } catch (Exception e) { + String errMsg = "Error occurred while parsing file, while converting to a org.w3c.dom.Document"; + log.error(errMsg, e); + throw new GrafanaManagementException(errMsg, e); + } + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/xml/bean/CacheConfiguration.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/xml/bean/CacheConfiguration.java new file mode 100644 index 0000000000..def5fb3476 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/xml/bean/CacheConfiguration.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.config.xml.bean; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; + +public class CacheConfiguration { + + private String name; + private int capacity; + + @XmlAttribute(name = "name") + public String getName() { + return name; + } + + @XmlElement(name = "Capacity") + public int getCapacity() { + return capacity; + } + + public void setName(String name) { + this.name = name; + } + + public void setCapacity(int capacity) { + this.capacity = capacity; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/xml/bean/User.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/xml/bean/User.java new file mode 100644 index 0000000000..3e8b662175 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/xml/bean/User.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.config.xml.bean; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name="AdminUser") +public class User { + private String username; + private String password; + + @XmlElement(name="Username") + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @XmlElement(name="Password") + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/DashboardNotFound.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/DashboardNotFound.java new file mode 100644 index 0000000000..c052d107d1 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/DashboardNotFound.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.exception; + +public class DashboardNotFound extends GrafanaAPIException { + + private static final long serialVersionUID = -2111271331930066297L; + + public DashboardNotFound(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/DatasourceNotFound.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/DatasourceNotFound.java new file mode 100644 index 0000000000..3d750bff35 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/DatasourceNotFound.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.exception; + +public class DatasourceNotFound extends GrafanaAPIException { + + private static final long serialVersionUID = -3171333332936220227L; + + public DatasourceNotFound(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/GrafanaAPIException.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/GrafanaAPIException.java new file mode 100644 index 0000000000..df926061f0 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/GrafanaAPIException.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.exception; + +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; + +public class GrafanaAPIException extends GrafanaManagementException { + + private static final long serialVersionUID = -3121249462130274227L; + + public GrafanaAPIException(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/GrafanaEnvVariablesNotDefined.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/GrafanaEnvVariablesNotDefined.java new file mode 100644 index 0000000000..acb836680c --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/GrafanaEnvVariablesNotDefined.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.exception; + + +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; + +public class GrafanaEnvVariablesNotDefined extends GrafanaManagementException { + + private static final long serialVersionUID = -3424429462350570237L; + + public GrafanaEnvVariablesNotDefined(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/MaliciousQueryAttempt.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/MaliciousQueryAttempt.java new file mode 100644 index 0000000000..ff91dd6a6e --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/MaliciousQueryAttempt.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.exception; + + +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; + +public class MaliciousQueryAttempt extends GrafanaManagementException { + + private static final long serialVersionUID = -3171279331530570257L; + + public MaliciousQueryAttempt(String msg) { + super(msg); + } + + public MaliciousQueryAttempt(String msg, Exception nestedException) { + super(msg, nestedException); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/PanelNotFound.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/PanelNotFound.java new file mode 100644 index 0000000000..f8cead9df8 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/PanelNotFound.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.exception; + +public class PanelNotFound extends GrafanaAPIException { + + private static final long serialVersionUID = -3471379441938070287L; + public PanelNotFound(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/QueryMisMatch.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/QueryMisMatch.java new file mode 100644 index 0000000000..0d011ddd14 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/QueryMisMatch.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.exception; + +public class QueryMisMatch extends MaliciousQueryAttempt { + + private static final long serialVersionUID = -3171277334737076294L; + + public QueryMisMatch(String msg) { + super(msg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/QueryNotFound.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/QueryNotFound.java new file mode 100644 index 0000000000..1bff687b14 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/QueryNotFound.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.exception; + +public class QueryNotFound extends GrafanaAPIException { + + private static final long serialVersionUID = -3151259311910070297L; + public QueryNotFound(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/TemplateNotFound.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/TemplateNotFound.java new file mode 100644 index 0000000000..e5347eee10 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/TemplateNotFound.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.exception; + +public class TemplateNotFound extends GrafanaAPIException { + + private static final long serialVersionUID = -3481878481835562209L; + public TemplateNotFound(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/internal/GrafanaManagementServiceComponent.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/internal/GrafanaManagementServiceComponent.java new file mode 100644 index 0000000000..f976e2ef51 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/internal/GrafanaManagementServiceComponent.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.internal; + +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaAPIService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaQueryService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.impl.GrafanaAPIServiceImpl; +import io.entgra.analytics.mgt.grafana.proxy.core.service.impl.GrafanaQueryServiceImpl; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.framework.BundleContext; +import org.osgi.service.component.ComponentContext; +import org.wso2.carbon.context.PrivilegedCarbonContext; + +/** + * @scr.component name="io.entgra.analytics.mgt.grafana.proxy.grafanamanagementservicecomponent" immediate="true" + */ +public class GrafanaManagementServiceComponent { + + private static Log log = LogFactory.getLog(GrafanaManagementServiceComponent.class); + + @SuppressWarnings("unused") + protected void activate(ComponentContext componentContext) { + try { + if (log.isDebugEnabled()) { + log.debug("Initializing grafana proxy management core bundle"); + } + + BundleContext bundleContext = componentContext.getBundleContext(); + + GrafanaAPIService grafanaAPIService = new GrafanaAPIServiceImpl(); + GrafanaQueryService grafanaQueryService = new GrafanaQueryServiceImpl(grafanaAPIService); + bundleContext.registerService(GrafanaAPIService.class.getName(), grafanaAPIService, null); + GrafanaMgtDataHolder.getInstance().setGrafanaAPIService(grafanaAPIService); + bundleContext.registerService(GrafanaQueryService.class.getName(), grafanaQueryService, null); + GrafanaMgtDataHolder.getInstance().setGrafanaQueryService(grafanaQueryService); + + if (log.isDebugEnabled()) { + log.debug("Grafana management core bundle has been successfully initialized"); + } + } catch (Throwable e) { + log.error("Error occurred while initializing grafana management core bundle", e); + } + } + + @SuppressWarnings("unused") + protected void deactivate(ComponentContext componentContext) { + if (log.isDebugEnabled()) { + log.debug("De-activating Grafana Management Service Component"); + } + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/internal/GrafanaMgtDataHolder.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/internal/GrafanaMgtDataHolder.java new file mode 100644 index 0000000000..10d43d7a23 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/internal/GrafanaMgtDataHolder.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.internal; + +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaAPIService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaQueryService; + +public class GrafanaMgtDataHolder { + + private GrafanaAPIService grafanaAPIService; + private GrafanaQueryService grafanaQueryService; + + private GrafanaMgtDataHolder() { + + } + + public GrafanaAPIService getGrafanaAPIService() { + return grafanaAPIService; + } + + public void setGrafanaAPIService(GrafanaAPIService grafanaAPIService) { + this.grafanaAPIService = grafanaAPIService; + } + + public GrafanaQueryService getGrafanaQueryService() { + return grafanaQueryService; + } + + public void setGrafanaQueryService(GrafanaQueryService grafanaQueryService) { + this.grafanaQueryService = grafanaQueryService; + } + + public static class InstanceHolder { + public static GrafanaMgtDataHolder instance = new GrafanaMgtDataHolder(); + } + + public static GrafanaMgtDataHolder getInstance() { + return InstanceHolder.instance; + } + + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/GrafanaAPIService.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/GrafanaAPIService.java new file mode 100644 index 0000000000..91776c50b9 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/GrafanaAPIService.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.service; + +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.service.bean.Datasource; + +import java.io.IOException; + +public interface GrafanaAPIService { + + /** + * Get predefined query template from grafana for given panel/variable + * @param dashboardUID + * @param panelId + * @param refId + * @param requestScheme + * @return query of the given panel/variable + * @throws IOException + * @throws GrafanaManagementException + */ + String getQueryTemplate(String dashboardUID, String panelId, String refId, + String requestScheme) throws IOException, GrafanaManagementException; + + /** + * Get panel details by dashboard uid and panel Id + * @param dashboardUID + * @param panelId + * @param requestScheme + * @return Panel details + * @throws IOException + * @throws GrafanaManagementException + */ + JsonObject getPanelDetails(String dashboardUID, String panelId, String requestScheme) throws + IOException, GrafanaManagementException; + + /** + * Get dashboard details by uid + * @param dashboardUID + * @param requestScheme + * @return Dashboard details + * @throws IOException + * @throws GrafanaManagementException + */ + JsonObject getDashboardDetails(String dashboardUID, String requestScheme) throws IOException, + GrafanaManagementException; + + /** + * Get datasource details by id + * @param datasourceId + * @param requestScheme + * @return Datasource details + * @throws IOException + * @throws GrafanaManagementException + */ + Datasource getDatasource(int datasourceId, String requestScheme) throws IOException, + GrafanaManagementException; +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/GrafanaQueryService.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/GrafanaQueryService.java new file mode 100644 index 0000000000..a1c43baf52 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/GrafanaQueryService.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.service; + +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; + +import java.io.IOException; +import java.net.URI; +import java.sql.SQLException; + +public interface GrafanaQueryService { + + void buildSafeQuery(JsonObject queryRequestBody, String dashboardUID, String panelId, + URI requestUri) throws IOException, SQLException, GrafanaManagementException, + DBConnectionException, io.entgra.application.mgt.common.exception.DBConnectionException; + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/bean/Datasource.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/bean/Datasource.java new file mode 100644 index 0000000000..43a11fdda4 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/bean/Datasource.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.service.bean; + +public class Datasource { + private int id; + private String url; + private String name; + private String type; + private String database; + + public Datasource(int id, String url, String name, String type, String database) { + this.id = id; + this.url = url; + this.name = name; + this.type = type; + this.database = database; + } + + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDatabase() { + return database; + } + + public void setDatabase(String database) { + this.database = database; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } +} \ No newline at end of file diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/cache/CacheManager.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/cache/CacheManager.java new file mode 100644 index 0000000000..b56a1041bd --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/cache/CacheManager.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.service.cache; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.config.GrafanaConfiguration; +import io.entgra.analytics.mgt.grafana.proxy.core.config.GrafanaConfigurationManager; +import io.entgra.analytics.mgt.grafana.proxy.core.config.xml.bean.CacheConfiguration; +import io.entgra.analytics.mgt.grafana.proxy.core.service.bean.Datasource; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + +public class CacheManager { + private static final Log log = LogFactory.getLog(CacheManager.class); + + private Cache datasourceAPICache; + private Cache queryTemplateAPICache; + private Cache encodedQueryCache; + + private CacheManager() { + initCache(); + } + + private static final class CacheManagerHolder { + static final CacheManager cacheManager = new CacheManager(); + } + + public static CacheManager getInstance() { + return CacheManagerHolder.cacheManager; + } + + public Cache getEncodedQueryCache() { + return encodedQueryCache; + } + + public Cache getQueryTemplateAPICache() { + return queryTemplateAPICache; + } + + + public Cache getDatasourceAPICache() { + return datasourceAPICache; + } + + + private void initCache() { + this.datasourceAPICache = buildDatasourceCache(); + this.queryTemplateAPICache = buildQueryCacheByName(GrafanaConstants.QUERY_API_CACHE_NAME); + this.encodedQueryCache = buildQueryCacheByName(GrafanaConstants.ENCODED_QUERY_CACHE_NAME); + } + + private Cache buildDatasourceCache() { + return CacheBuilder.newBuilder().build(); + } + + private Cache buildQueryCacheByName(String cacheName) { + int capacity = getCacheCapacity(cacheName); + return CacheBuilder.newBuilder().maximumSize(capacity).build(); + } + + + + private static int getCacheCapacity(String cacheName) { + try { + GrafanaConfiguration configuration = GrafanaConfigurationManager.getInstance().getGrafanaConfiguration(); + CacheConfiguration cacheConfig = configuration.getCacheByName(cacheName); + if (cacheConfig == null) { + log.error("CacheConfiguration config not defined for " + cacheName); + throw new GrafanaManagementException("Query API CacheConfiguration configuration not properly defined"); + } + return cacheConfig.getCapacity(); + } catch (GrafanaManagementException e) { + String errMsg = "Error occurred while initializing cache capacity for " + cacheName; + log.error(errMsg); + throw new RuntimeException(errMsg, e); + } + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/cache/QueryTemplateCacheKey.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/cache/QueryTemplateCacheKey.java new file mode 100644 index 0000000000..175fe3ae28 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/cache/QueryTemplateCacheKey.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.service.cache; + +import java.util.Objects; + +public class QueryTemplateCacheKey { + + private final String dashboardUID; + private final String panelId; + private final String refId; + private volatile int hashCode; + + public QueryTemplateCacheKey(String dashboardUID, String panelId, String refId) { + this.dashboardUID = dashboardUID; + this.panelId = panelId; + this.refId = refId; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj instanceof QueryTemplateCacheKey) { + final QueryTemplateCacheKey other = (QueryTemplateCacheKey) obj; + String thisId = this.dashboardUID + this.panelId + this.refId; + String otherId = other.dashboardUID + other.panelId + other.refId; + return thisId.equals(otherId); + } + return false; + } + + @Override + public int hashCode() { + if (hashCode == 0) { + hashCode = Objects.hash(dashboardUID, panelId, refId); + } + return hashCode; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/impl/GrafanaAPIServiceImpl.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/impl/GrafanaAPIServiceImpl.java new file mode 100644 index 0000000000..ae3e91a0c7 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/impl/GrafanaAPIServiceImpl.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.service.impl; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.DashboardNotFound; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.DatasourceNotFound; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.GrafanaEnvVariablesNotDefined; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.PanelNotFound; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.QueryNotFound; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.TemplateNotFound; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaAPIService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.bean.Datasource; +import io.entgra.analytics.mgt.grafana.proxy.core.service.cache.CacheManager; +import io.entgra.analytics.mgt.grafana.proxy.core.service.cache.QueryTemplateCacheKey; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.wso2.carbon.device.mgt.core.common.util.HttpUtil; +import org.wso2.carbon.device.mgt.core.report.mgt.Constants; + +import javax.ws.rs.core.HttpHeaders; +import java.io.IOException; + +public class GrafanaAPIServiceImpl implements GrafanaAPIService { + + private static final Log log = LogFactory.getLog(GrafanaAPIServiceImpl.class); + + public String getQueryTemplate(String dashboardUID, String panelId, String refId, + String requestScheme) throws IOException, GrafanaManagementException { + try { + return getPanelQuery(dashboardUID, panelId, refId, requestScheme); + } catch (QueryNotFound e) { + return getTemplateQuery(dashboardUID, refId, requestScheme); + } + } + + /** + * Get predefined query template from grafana for given panel + * @param dashboardUID + * @param panelId + * @param refId + * @param requestScheme + * @return query of the given panel + * @throws IOException + * @throws GrafanaManagementException + */ + public String getPanelQuery(String dashboardUID, String panelId, String refId, + String requestScheme) throws IOException, GrafanaManagementException { + JsonObject panel = getPanelDetails(dashboardUID, panelId, requestScheme); + JsonArray queries = panel.getAsJsonArray(GrafanaConstants.DASHBOARD_PANEL_DETAIL_QUERIES_KEY); + JsonObject query = getQueryByRefId(queries, refId); + if (query == null) { + throw new QueryNotFound("No query exists for {dashboard: " + dashboardUID + + ", panelId: " + panelId + ", refId: " + refId + "}"); + } + String queryTemplate = query.get(GrafanaConstants.RAW_SQL_KEY).getAsString(); + CacheManager.getInstance().getQueryTemplateAPICache(). + put(new QueryTemplateCacheKey(dashboardUID, panelId, refId), queryTemplate); + return queryTemplate; + } + + /** + * Get predefined query template from grafana for given variable (template variable) + * Note: Here template query means the grafana template variable queries + * @param dashboardUID + * @param refId + * @param requestScheme + * @return query of grafana template variable + * @throws IOException + * @throws GrafanaManagementException + */ + // Here template query means the grafana template variable queries + public String getTemplateQuery(String dashboardUID, String refId, + String requestScheme) throws IOException, GrafanaManagementException { + JsonObject template = getTemplatingDetails(dashboardUID, refId, requestScheme); + JsonElement queryElement = template.get(GrafanaConstants.TEMPLATE_QUERY_KEY); + if (queryElement == null) { + throw new QueryNotFound("No template query exists for {dashboard: " + dashboardUID + + ", refId: " + refId + "}"); + } + String query = queryElement.getAsString(); + CacheManager.getInstance().getQueryTemplateAPICache(). + put(new QueryTemplateCacheKey(dashboardUID, null, refId), query); + return query; + } + + /** + * Get grafana template variable details of given dashboard uid + * @param dashboardUID + * @param refId + * @param requestScheme + * @return Template variable details of given dashboard uid + * @throws IOException + * @throws GrafanaManagementException + */ + public JsonObject getTemplatingDetails(String dashboardUID, String refId, String requestScheme) throws + IOException, GrafanaManagementException { + JsonObject dashboard = getDashboardDetails(dashboardUID, requestScheme); + JsonObject template = getTemplateByRefId(dashboard, refId); + if (template == null) { + String errorMsg = "Template for {refId: " + refId + ", dashboard: " + dashboardUID + "} is not found"; + log.error(errorMsg); + throw new TemplateNotFound(errorMsg); + } + return template; + } + + public JsonObject getPanelDetails(String dashboardUID, String panelId, String requestScheme) throws + IOException, GrafanaManagementException { + JsonObject dashboard = getDashboardDetails(dashboardUID, requestScheme); + JsonObject panel = getPanelById(dashboard, panelId); + if (panel == null) { + String errorMsg = "Panel " + panelId + " for dashboard " + dashboardUID + " is not found"; + log.error(errorMsg); + throw new PanelNotFound(errorMsg); + } + return panel; + } + + public JsonObject getDashboardDetails(String dashboardUID, String requestScheme) throws IOException, + GrafanaManagementException { + String dashboardAPI = generateGrafanaAPIBaseUri(GrafanaConstants.DASHBOARD_API, requestScheme) + dashboardUID; + HttpGet request = new HttpGet(dashboardAPI); + JsonObject dashboardResponseJsonBody = getGrafanaAPIJsonResponse(request); + JsonObject dashboardDetails = dashboardResponseJsonBody.getAsJsonObject(GrafanaConstants.DASHBOARD_KEY); + if (dashboardDetails == null) { + throw new DashboardNotFound("Grafana response: " + dashboardResponseJsonBody); + } + return dashboardResponseJsonBody.getAsJsonObject(GrafanaConstants.DASHBOARD_KEY); + } + + public Datasource getDatasource(int datasourceId, String requestScheme) throws IOException, GrafanaManagementException { + String datasourceAPI = generateGrafanaAPIBaseUri(GrafanaConstants.DATASOURCE_API, requestScheme) + datasourceId; + HttpGet request = new HttpGet(datasourceAPI); + JsonObject datasourceDetails = getGrafanaAPIJsonResponse(request); + if (datasourceDetails.get(GrafanaConstants.DATASOURCE_NAME_KEY) == null) { + throw new DatasourceNotFound("Grafana response: " + datasourceDetails); + } + String url = datasourceDetails.get(GrafanaConstants.DATASOURCE_URL_KEY).getAsString(); + String name = datasourceDetails.get(GrafanaConstants.DATASOURCE_NAME_KEY).getAsString(); + String type = datasourceDetails.get(GrafanaConstants.DATASOURCE_TYPE_KEY).getAsString(); + String database = datasourceDetails.get(GrafanaConstants.DATASOURCE_DB_KEY).getAsString(); + Datasource ds = new Datasource(datasourceId, url, name, type, database); + CacheManager.getInstance().getDatasourceAPICache().put(datasourceId, ds); + return ds; + } + + private String generateGrafanaAPIBaseUri(String api, String requestScheme) throws GrafanaEnvVariablesNotDefined { + return GrafanaUtil.getGrafanaHTTPBase(requestScheme) + api + Constants.URI_SEPARATOR; + } + + + private JsonObject getGrafanaAPIJsonResponse(HttpRequestBase request) throws IOException, + GrafanaManagementException { + try(CloseableHttpClient client = HttpClients.createDefault()) { + HttpResponse response = invokeAPI(client, request); + return GrafanaUtil.getJsonBody(HttpUtil.getResponseString(response)); + } + } + + private HttpResponse invokeAPI(HttpClient client, HttpRequestBase request) throws IOException, GrafanaManagementException { + setBasicAuthHeader(request); + return client.execute(request); + } + + private void setBasicAuthHeader(HttpRequestBase request) throws GrafanaManagementException { + String basicAuth = GrafanaUtil.getBasicAuthBase64Header(); + request.setHeader(HttpHeaders.AUTHORIZATION, basicAuth); + } + + private JsonObject getQueryByRefId(JsonArray queries, String refId) { + for (int i = 0; i < queries.size(); i++) { + JsonObject query = queries.get(i).getAsJsonObject(); + String queryRefId = query.get(GrafanaConstants.QUERY_REF_ID_KEY).getAsString(); + if (queryRefId.equals(refId)) { + return query; + } + } + return null; + } + + private JsonObject getTemplateByRefId(JsonObject dashboard, String refId) { + JsonArray templates = dashboard.getAsJsonObject(GrafanaConstants.TEMPLATING_KEY). + getAsJsonArray(GrafanaConstants.TEMPLATING_LIST_KEY); + for (int i = 0; i < templates.size(); i++) { + JsonObject templateObj = templates.get(i).getAsJsonObject(); + String name = templateObj.get(GrafanaConstants.TEMPLATING_NAME_KEY).getAsString(); + // RefId in query body corresponds to template name + if (refId.equals(name)) { + return templateObj; + } + } + return null; + } + + private JsonObject getPanelById(JsonObject dashboard, String panelId) { + JsonArray panels = dashboard.getAsJsonArray(GrafanaConstants.PANEL_KEY); + for (int i = 0; i < panels.size(); i++) { + JsonObject panelObj = panels.get(i).getAsJsonObject(); + String id = panelObj.get(GrafanaConstants.ID_KEY).getAsString(); + if (id.equals(panelId)) { + return panelObj; + } + } + return null; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/impl/GrafanaQueryServiceImpl.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/impl/GrafanaQueryServiceImpl.java new file mode 100644 index 0000000000..b15ebed76a --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/impl/GrafanaQueryServiceImpl.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.service.impl; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.MaliciousQueryAttempt; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.QueryMisMatch; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.QueryNotFound; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaAPIService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaQueryService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.bean.Datasource; +import io.entgra.analytics.mgt.grafana.proxy.core.service.cache.CacheManager; +import io.entgra.analytics.mgt.grafana.proxy.core.service.cache.QueryTemplateCacheKey; +import io.entgra.analytics.mgt.grafana.proxy.core.sql.query.GrafanaPreparedQueryBuilder; +import io.entgra.analytics.mgt.grafana.proxy.core.sql.query.PreparedQuery; +import io.entgra.analytics.mgt.grafana.proxy.core.sql.query.encoder.QueryEncoderFactory; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; + +import java.io.IOException; +import java.net.URI; +import java.sql.SQLException; + +public class GrafanaQueryServiceImpl implements GrafanaQueryService { + + private static final Log log = LogFactory.getLog(GrafanaQueryServiceImpl.class); + private final GrafanaAPIService grafanaAPIService; + + public GrafanaQueryServiceImpl(GrafanaAPIService grafanaAPIService) { + this.grafanaAPIService = grafanaAPIService; + } + + public void buildSafeQuery(JsonObject queryRequestBody, String dashboardUID, String panelId, URI requestUri) + throws IOException, SQLException, GrafanaManagementException, DBConnectionException, io.entgra.application.mgt.common.exception.DBConnectionException { + JsonArray queries = queryRequestBody.getAsJsonArray(GrafanaConstants.QUERY_BODY_QUERIES_KEY); + for (int i = 0; i < queries.size(); i++) { + JsonObject queryObj = queries.get(i).getAsJsonObject(); + JsonElement refIdJson = queryObj.get(GrafanaConstants.QUERY_REF_ID_KEY); + JsonElement rawSqlJson = queryObj.get(GrafanaConstants.RAW_SQL_KEY); + JsonElement datasourceIdJson = queryObj.get(GrafanaConstants.DATASOURCE_ID_KEY); + if (refIdJson == null || rawSqlJson == null || datasourceIdJson == null) { + String errMsg = "Query json body: refId, rawSql and datasourceId cannot be null"; + log.error(errMsg); + throw new MaliciousQueryAttempt(errMsg); + } + String refId = refIdJson.getAsString(); + String rawSql = rawSqlJson.getAsString(); + int datasourceId = datasourceIdJson.getAsInt(); + CacheManager cacheManager = CacheManager.getInstance(); + String encodedQuery = cacheManager.getEncodedQueryCache().getIfPresent(rawSql); + if (cacheManager.getEncodedQueryCache().getIfPresent(rawSql) != null) { + queryObj.addProperty(GrafanaConstants.RAW_SQL_KEY, encodedQuery); + return; + } + Datasource datasource = cacheManager.getDatasourceAPICache().getIfPresent(datasourceId); + if (datasource == null) { + datasource = grafanaAPIService.getDatasource(datasourceId, requestUri.getScheme()); + } + String queryTemplate = cacheManager.getQueryTemplateAPICache(). + getIfPresent(new QueryTemplateCacheKey(dashboardUID, panelId, refId)); + try { + if (queryTemplate != null) { + try { + encodeQuery(queryObj, datasource, queryTemplate, rawSql); + } catch (QueryMisMatch e) { + log.error("Error occurred while encoding query, " + + "retrying to encode by getting the query template from api instead of cache", e); + queryTemplate = grafanaAPIService.getQueryTemplate(dashboardUID, panelId, refId, requestUri.getScheme()); + encodeQuery(queryObj, datasource, queryTemplate, rawSql); + } + } else { + queryTemplate = grafanaAPIService.getQueryTemplate(dashboardUID, panelId, refId, requestUri.getScheme()); + encodeQuery(queryObj, datasource, queryTemplate, rawSql); + } + } catch (QueryNotFound e) { + String errMsg = "No query exists for {dashboard: " + dashboardUID + + ", panelId: " + panelId + ", refId: " + refId + "}"; + log.error(errMsg); + throw new QueryNotFound(errMsg); + } + } + } + + private void encodeQuery(JsonObject queryObj, Datasource datasource, String queryTemplate, String rawSql) + throws SQLException, GrafanaManagementException, DBConnectionException, + io.entgra.application.mgt.common.exception.DBConnectionException { + PreparedQuery pq = GrafanaPreparedQueryBuilder.build(queryTemplate, rawSql); + String encodedQuery = QueryEncoderFactory.createEncoder(datasource.getType(), datasource.getName()).encode(pq); + CacheManager.getInstance().getEncodedQueryCache().put(rawSql, encodedQuery); + if(log.isDebugEnabled()) { + log.debug("Encoded query: " + encodedQuery); + } + queryObj.addProperty(GrafanaConstants.RAW_SQL_KEY, encodedQuery); + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/connection/GrafanaDatasourceConnectionFactory.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/connection/GrafanaDatasourceConnectionFactory.java new file mode 100644 index 0000000000..808b859038 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/connection/GrafanaDatasourceConnectionFactory.java @@ -0,0 +1,66 @@ +package io.entgra.analytics.mgt.grafana.proxy.core.sql.connection; + +import io.entgra.application.mgt.core.config.Configuration; +import io.entgra.application.mgt.core.config.ConfigurationManager; +import io.entgra.application.mgt.core.util.ConnectionManagerUtil; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; +import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; +import org.wso2.carbon.device.mgt.core.config.DeviceManagementConfig; +import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; +import org.wso2.carbon.device.mgt.core.report.mgt.config.ReportMgtConfiguration; +import org.wso2.carbon.device.mgt.core.report.mgt.config.ReportMgtConfigurationManager; +import org.wso2.carbon.device.mgt.core.report.mgt.dao.common.ReportMgtConnectionManager; + +import java.sql.Connection; +import java.sql.SQLException; + +public class GrafanaDatasourceConnectionFactory { + + private static final ReportMgtConfiguration reportMgtConfiguration= ReportMgtConfigurationManager.getInstance(). + getConfiguration(); + private static final DeviceManagementConfig deviceManagementConfig = DeviceConfigurationManager.getInstance(). + getDeviceManagementConfig(); + + private static final Configuration applicationMgtConfig = ConfigurationManager.getInstance().getConfiguration(); + + public static Connection getConnection(String databaseName) throws SQLException, DBConnectionException, + io.entgra.application.mgt.common.exception.DBConnectionException { + if(databaseName.equals(getReportManagementDatasourceName())) { + ReportMgtConnectionManager.openDBConnection(); + return ReportMgtConnectionManager.getDBConnection(); + } else if (databaseName.equals(getDeviceManagementDatasourceName())) { + DeviceManagementDAOFactory.openConnection(); + return DeviceManagementDAOFactory.getConnection(); + } else if (databaseName.equals(getApplicationManagementDatasourceName())) { + ConnectionManagerUtil.openDBConnection(); + return ConnectionManagerUtil.getDBConnection(); + } else { + throw new RuntimeException("No such datasource with the name: " + databaseName); + } + } + public static void closeConnection(String databaseName) { + if(databaseName.equals(getReportManagementDatasourceName())) { + ReportMgtConnectionManager.closeDBConnection(); + } else if (databaseName.equals(getDeviceManagementDatasourceName())) { + DeviceManagementDAOFactory.closeConnection(); + } else if (databaseName.equals(getApplicationManagementDatasourceName())) { + ConnectionManagerUtil.closeDBConnection(); + } else { + throw new RuntimeException("No such datasource with the name: " + databaseName); + } + } + + private static String getReportManagementDatasourceName() { + return reportMgtConfiguration.getDatasourceName(); + } + + private static String getDeviceManagementDatasourceName() { + return deviceManagementConfig.getDeviceManagementConfigRepository().getDataSourceConfig(). + getJndiLookupDefinition().getJndiName(); + } + + private static String getApplicationManagementDatasourceName() { + return applicationMgtConfig.getDatasourceName(); + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/GrafanaPreparedQueryBuilder.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/GrafanaPreparedQueryBuilder.java new file mode 100644 index 0000000000..8b8606dacb --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/GrafanaPreparedQueryBuilder.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.sql.query; + +import io.entgra.analytics.mgt.grafana.proxy.core.exception.QueryMisMatch; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class GrafanaPreparedQueryBuilder { + + private static final Log log = LogFactory.getLog(GrafanaPreparedQueryBuilder.class); + + private static final String QUERY_MISMATCH_EXCEPTION_MESSAGE = "Grafana query api request rawSql does not match relevant query template"; + private static final String COMMA_SEPARATOR = ","; + private static final String VAR_PARAM_TEMPLATE = "$param"; + private static final String GRAFANA_QUOTED_VAR_REGEX = "('\\$(\\d|\\w|_)+')|('\\$\\{.*?\\}')|(\"\\$(\\d|\\w|_)+\")|(\"\\$\\{.*?\\}\")"; + private static final String GRAFANA_VAR_REGEX = "(\\$(\\d|\\w|_)+)|(\\$\\{.*?\\})"; + + + public static PreparedQuery build(String queryTemplate, String rawQuery) throws QueryMisMatch { + // In Grafana, a variable can coexist with a hardcoded parameter (ie: '${__from:date:YYYY-MM-DD} 00:00:00') + // hence this function templates the whole parameter in order to make valid prepared statement + queryTemplate = templateParamWGrafanaVariables(queryTemplate); + + String queryUpToVarStart = queryTemplate; + String rawQueryUpToVarStart = rawQuery; + + // Pattern matcher to get query string before the first grafana variable + Pattern toVarStart = Pattern.compile("([^\\$]+(?=\"\\$))|([^\\$]+(?='\\$))|([^\\$]+(?=\\$))"); + Matcher matchToVarStart = toVarStart.matcher(queryTemplate); + + if (matchToVarStart.find()) { + queryUpToVarStart = matchToVarStart.group(); + if(rawQuery.length() < queryUpToVarStart.length()) { + throw new QueryMisMatch(QUERY_MISMATCH_EXCEPTION_MESSAGE); + } + rawQueryUpToVarStart = rawQuery.substring(0, queryUpToVarStart.length()); + } + if(!queryUpToVarStart.equals(rawQueryUpToVarStart)){ + throw new QueryMisMatch(QUERY_MISMATCH_EXCEPTION_MESSAGE); + } + StringBuilder preparedQueryBuilder = new StringBuilder().append(queryUpToVarStart); + + String queryFromVarStart = queryTemplate.substring(queryUpToVarStart.length()); + String rawQueryFromVarStart = rawQuery.substring(queryUpToVarStart.length()); + queryTemplate = queryFromVarStart; + rawQuery = rawQueryFromVarStart; + Pattern varPattern = Pattern.compile(GRAFANA_QUOTED_VAR_REGEX + "|" + GRAFANA_VAR_REGEX); + Matcher varMatch = varPattern.matcher(queryTemplate); + List parameters = new ArrayList<>(); + while(varMatch.find()) { + String currentVar = varMatch.group(); + // Pattern matcher to get query string between grafana current variable and next variable + matchToVarStart = toVarStart.matcher(queryTemplate.substring(currentVar.length())); + + String matchToLookBehindRawQuery; + if (matchToVarStart.find()) { + matchToLookBehindRawQuery = matchToVarStart.group(); + } else { + // If next variable does not exist get query string after the current variable + matchToLookBehindRawQuery = queryTemplate.substring(currentVar.length()); + } + String currentVarInput; + if (matchToLookBehindRawQuery.isEmpty()) { + // If there is no string after the current variable, then remaining segment of rawQuery is the + // current variable input (rawQuery is sliced up to the next variable) + currentVarInput = rawQuery; + } else { + Matcher lookBehindRQ = Pattern.compile("(.*?)(?=" + Pattern.quote(matchToLookBehindRawQuery) + ")").matcher(rawQuery); + if (!lookBehindRQ.find()) { + throw new QueryMisMatch(QUERY_MISMATCH_EXCEPTION_MESSAGE); + } + currentVarInput = lookBehindRQ.group(); + } + if (isTenantIdVar(currentVar)) { + preparedQueryBuilder.append(GrafanaUtil.getTenantId()); + } else { + // Grafana variable input can be multi-valued, which are separated by comma by default + String[] varValues = splitByComma(currentVarInput); + List preparedStatementPlaceHolders = new ArrayList<>(); + for (String v : varValues) { + String param = unQuoteString(v); + if (isSafeVariableInput(param)) { + preparedStatementPlaceHolders.add(v); + } else { + parameters.add(param); + preparedStatementPlaceHolders.add(PreparedQuery.PREPARED_SQL_PARAM_PLACEHOLDER); + } + } + preparedQueryBuilder.append(String.join(COMMA_SEPARATOR, preparedStatementPlaceHolders)); + } + preparedQueryBuilder.append(matchToLookBehindRawQuery); + // Get template and raw query string from next variable + queryTemplate = queryTemplate.substring(currentVar.length() + matchToLookBehindRawQuery.length()); + rawQuery = rawQuery.substring(currentVarInput.length() + matchToLookBehindRawQuery.length()); + + varMatch = varPattern.matcher(queryTemplate); + } + if (!queryTemplate.equals(rawQuery)) { + throw new QueryMisMatch(QUERY_MISMATCH_EXCEPTION_MESSAGE); + } + return new PreparedQuery(preparedQueryBuilder.toString(), parameters); + } + + private static String[] splitByComma(String str) { + // Using regex to avoid splitting by comma inside quotes + return str.split("(\\s|\\t)*,(\\s|\\t)*(?=(?:[^'\"]*['|\"][^'\"]*['|\"])*[^'\"]*$)"); + } + + private static String templateParamWGrafanaVariables(String queryTemplate) { + // TODO: handle escaped quotes and special characters + Pattern quotedStringPattern = Pattern.compile("(\"(.+?)\")|('(.+?)')"); + Matcher quotedStringMatch = quotedStringPattern.matcher(queryTemplate); + while(quotedStringMatch.find()) { + String quotedString = quotedStringMatch.group(); + Matcher varMatcher = Pattern.compile(GRAFANA_VAR_REGEX).matcher(quotedString); + // If grafana variable exists in single quoted string + if(varMatcher.find()) { + String var = varMatcher.group(); + if (!isTenantIdVar(var)) { + String templatedQuotedString = templateQuotedString(quotedString); + // escape any special characters + templatedQuotedString = Matcher.quoteReplacement(templatedQuotedString); + queryTemplate = queryTemplate.replaceFirst(Pattern.quote(quotedString), templatedQuotedString); + } + } + } + return queryTemplate; + } + + private static String templateQuotedString(String quotedString) { + return quotedString.replaceFirst("[^']+|[^\"]+", + Matcher.quoteReplacement(VAR_PARAM_TEMPLATE)); + } + + private static boolean isSafeVariableInput(String currentVarInput) { + if (StringUtils.isEmpty(currentVarInput)) { + return true; + } + return currentVarInput.matches("\\$?[a-zA-Z0-9-_\\.]+|^\"[a-zA-Z0-9-_\\.\\s]+\"$|^'[a-zA-Z0-9-_\\.\\s]+'$"); + } + + private static String unQuoteString(String str) { + if (isQuoted(str)) { + int firstCharIndex = 0; + int lastCharIndex = str.length() - 1; + return str.substring(firstCharIndex + 1, lastCharIndex); + } + return str; + } + + private static boolean isQuoted(String str) { + if (StringUtils.isEmpty(str)) { + return false; + } + int firstCharIndex = 0; + int lastCharIndex = str.length() - 1; + return (str.charAt(firstCharIndex) == '\'' && str.charAt(lastCharIndex) == '\'') || + (str.charAt(firstCharIndex) == '"' && str.charAt(lastCharIndex) == '"'); + } + + private static boolean isTenantIdVar(String var) { + return var.equals(GrafanaConstants.TENANT_ID_VAR) || var.equals(singleQuoteString(GrafanaConstants.TENANT_ID_VAR)) + || var.equals(doubleQuoteString(GrafanaConstants.TENANT_ID_VAR)); + } + + private static String doubleQuoteString(String str) { + return "\"" + str + "\""; + } + + private static String singleQuoteString(String str) { + return "'" + str + "'"; + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/PreparedQuery.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/PreparedQuery.java new file mode 100644 index 0000000000..9540841061 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/PreparedQuery.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.sql.query; + +import java.util.List; + +public class PreparedQuery { + + public static final String PREPARED_SQL_PARAM_PLACEHOLDER = "?"; + + private String preparedSQL; + private List parameters; + + public PreparedQuery(String preparedSQL, List parameters) { + this.preparedSQL = preparedSQL; + this.parameters = parameters; + } + + public String getPreparedSQL() { + return preparedSQL; + } + + public void setPreparedSQL(String preparedSQL) { + this.preparedSQL = preparedSQL; + } + + public List getParameters() { + return parameters; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/GenericQueryEncoder.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/GenericQueryEncoder.java new file mode 100644 index 0000000000..7b6693689f --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/GenericQueryEncoder.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.sql.query.encoder; + +import io.entgra.analytics.mgt.grafana.proxy.core.sql.query.PreparedQuery; +import org.apache.commons.lang.StringEscapeUtils; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class GenericQueryEncoder implements QueryEncoder { + + public GenericQueryEncoder() { + } + + @Override + public String encode(PreparedQuery preparedQuery) throws SQLException { + return generateEncodedSQL(preparedQuery.getPreparedSQL(), preparedQuery.getParameters()); + } + + private String generateEncodedSQL(String preparedSQL, List parameters) throws SQLException { + List escapedParams = escapeParameters(parameters); + Matcher placeHolderMatcher = Pattern.compile(Pattern.quote(PreparedQuery.PREPARED_SQL_PARAM_PLACEHOLDER)).matcher(preparedSQL); + StringBuffer sb = new StringBuffer(); + for (String param: escapedParams) { + if(placeHolderMatcher.find()) { + placeHolderMatcher.appendReplacement(sb, Matcher.quoteReplacement(param)); + } else { + String errMsg = "Given parameter count doesn't match parameters available in SQL"; + throw new SQLException(errMsg); + } + } + return sb.toString(); + } + + private List escapeParameters(List parameters) { + List escapedParams = new ArrayList<>(); + for (String param : parameters) { + String escapedParam = StringEscapeUtils.escapeSql(param); + escapedParams.add(escapedParam); + } + return escapedParams; + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/MySQLQueryEncoder.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/MySQLQueryEncoder.java new file mode 100644 index 0000000000..faa6977b78 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/MySQLQueryEncoder.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.sql.query.encoder; + +import io.entgra.analytics.mgt.grafana.proxy.core.sql.connection.GrafanaDatasourceConnectionFactory; +import io.entgra.analytics.mgt.grafana.proxy.core.sql.query.PreparedQuery; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +public class MySQLQueryEncoder implements QueryEncoder { + + private static final Log log = LogFactory.getLog(MySQLQueryEncoder.class); + private static final String PREPARED_STATEMENT_STRING_OBJECT_ID_SEPARATOR = ": "; + + private final String databaseName; + + public MySQLQueryEncoder(String databaseName) { + this.databaseName = databaseName; + } + + @Override + public String encode(PreparedQuery preparedQuery) throws SQLException, DBConnectionException, + io.entgra.application.mgt.common.exception.DBConnectionException { + try { + Connection con = GrafanaDatasourceConnectionFactory.getConnection(databaseName); + PreparedStatement stmt = con.prepareStatement(preparedQuery.getPreparedSQL()); + setParameters(stmt, preparedQuery.getParameters()); + return generateQueryFromPreparedStatement(stmt); + } finally { + GrafanaDatasourceConnectionFactory.closeConnection(databaseName); + } + } + + public void setParameters(PreparedStatement stmt, List parameters) + throws SQLException { + int i = 0; + for (String p : parameters) { + stmt.setObject(++i, p); + } + } + + private String generateQueryFromPreparedStatement(PreparedStatement stmt) { + String query = stmt.toString().substring(stmt.toString().indexOf(PREPARED_STATEMENT_STRING_OBJECT_ID_SEPARATOR) + + PREPARED_STATEMENT_STRING_OBJECT_ID_SEPARATOR.length()); + // Below is how generated string of prepared statement may look like + // org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy[Proxy=1138452482; Query=; Delegate=com.mysql.cj.jdbc.ClientPreparedStatement: ] + // This removes unnecessary "]" char at the end that may remain after extracting the query + if (query.charAt(query.length() - 1) == ']') { + query = query.substring(0, query.length() - 1); + } + return query; + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/QueryEncoder.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/QueryEncoder.java new file mode 100644 index 0000000000..81d7735a81 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/QueryEncoder.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.sql.query.encoder; + +import io.entgra.analytics.mgt.grafana.proxy.core.sql.query.PreparedQuery; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; + +import java.sql.SQLException; + +public interface QueryEncoder { + + String encode(PreparedQuery preparedQuery) throws SQLException, DBConnectionException, io.entgra.application.mgt.common.exception.DBConnectionException; + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/QueryEncoderFactory.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/QueryEncoderFactory.java new file mode 100644 index 0000000000..fe426f1f20 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/QueryEncoderFactory.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.sql.query.encoder; + +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; + +public class QueryEncoderFactory { + + public static QueryEncoder createEncoder(String datasourceType, String databaseName) { + if (datasourceType.equals(GrafanaConstants.DATASOURCE_TYPE.MYSQL)) { + return new MySQLQueryEncoder(databaseName); + } else { + return new GenericQueryEncoder(); + } + + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/util/GrafanaConstants.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/util/GrafanaConstants.java new file mode 100644 index 0000000000..65865ad619 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/util/GrafanaConstants.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014, 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 io.entgra.analytics.mgt.grafana.proxy.core.util; + +public class GrafanaConstants { + + public static final String QUERY_API_CACHE_NAME = "queryAPICache"; + public static final String ENCODED_QUERY_CACHE_NAME = "encodedQueryCache"; + public static final String REFERER_HEADER = "Referer"; + public static final String CONFIG_XML_NAME = "grafana-config.xml"; + public static final String X_GRAFANA_ORG_ID_HEADER = "x-org-grafana-id"; + + public static class XML { + public static final String FEATURES_DISALLOW_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl"; + } + + public static class DATASOURCE_TYPE { + public static final String MYSQL = "mysql"; + public static final String POSTGRESQL = "postgresql"; + public static final String MICROSOFT_SQL_SERVER = "mssql"; + public static final String ORACLE = "oracle"; + } + + public static final int IFRAME_URL_DASHBOARD_UID_INDEX = 1; + + public static final String TENANT_ID_VAR_NAME = "tenantId"; + public static final String VAR_PREFIX = "$"; + public static final String TENANT_ID_VAR = VAR_PREFIX + TENANT_ID_VAR_NAME; + public static final String QUERY_PARAM_VAR_PREFIX = "var-"; + public static final String TENANT_ID_VAR_QUERY_PARAM = QUERY_PARAM_VAR_PREFIX + TENANT_ID_VAR_NAME; + public static final String API_PATH = "/api"; + public static final String DASHBOARD_KEY = "dashboard"; + public static final String DATASOURCE_TYPE_KEY = "type"; + public static final String DATASOURCE_DB_KEY = "database"; + public static final String DATASOURCE_ID_KEY = "datasourceId"; + public static final String DATASOURCE_NAME_KEY = "name"; + public static final String DATASOURCE_URL_KEY = "name"; + public static final String PANEL_KEY = "panels"; + public static final String TEMPLATING_KEY = "templating"; + public static final String TEMPLATING_NAME_KEY = "name"; + public static final String TEMPLATE_QUERY_KEY = "query"; + public static final String TEMPLATING_LIST_KEY = "list"; + public static final String RAW_SQL_KEY = "rawSql"; + public static final String QUERY_BODY_QUERIES_KEY = "queries"; + public static final String DASHBOARD_PANEL_DETAIL_QUERIES_KEY = "targets"; + public static final String QUERY_REF_ID_KEY = "refId"; + public static final String PANEL_ID_QUERY_PARAM = "panelId"; + public static final String ORG_ID_QUERY_PARAM = "panelId"; + public static final String ID_KEY = "id"; + + public static final String WS_LIVE_API = "/api/live/ws"; + public static final String DASHBOARD_API = "/api/dashboards/uid"; + public static final String DATASOURCE_API = "/api/datasources"; + public static final String HTTP_HOST_ENV_VAR = "iot.grafana.http.host"; + public static final String HTTPS_HOST_ENV_VAR = "iot.grafana.https.host"; +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/util/GrafanaUtil.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/util/GrafanaUtil.java new file mode 100644 index 0000000000..40117cee60 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/util/GrafanaUtil.java @@ -0,0 +1,117 @@ +/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.util; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.bean.GrafanaPanelIdentifier; +import io.entgra.analytics.mgt.grafana.proxy.core.config.GrafanaConfiguration; +import io.entgra.analytics.mgt.grafana.proxy.core.config.GrafanaConfigurationManager; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.GrafanaEnvVariablesNotDefined; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaAPIService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaQueryService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.impl.GrafanaAPIServiceImpl; +import io.entgra.analytics.mgt.grafana.proxy.core.service.impl.GrafanaQueryServiceImpl; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.core.common.util.HttpUtil; +import org.wso2.carbon.device.mgt.core.report.mgt.Constants; + +import java.net.URI; +import java.util.List; +import java.util.Map; + +public class GrafanaUtil { + + private static final Log log = LogFactory.getLog(GrafanaUtil.class); + + public static boolean isGrafanaAPI(String uri) { + return uri.contains(GrafanaConstants.API_PATH); + } + + public static String generateGrafanaUrl(String requestPath, String base) { + base += Constants.URI_SEPARATOR; + return base + requestPath; + } + public static String getGrafanaWebSocketBase(String requestScheme) throws GrafanaEnvVariablesNotDefined { + return getGrafanaBase(requestScheme, Constants.WS_PROTOCOL, Constants.WSS_PROTOCOL); + } + public static String getGrafanaHTTPBase(String requestScheme) throws GrafanaEnvVariablesNotDefined { + return getGrafanaBase(requestScheme, Constants.HTTP_PROTOCOL, Constants.HTTPS_PROTOCOL); + } + + public static String getGrafanaBase(String requestScheme, String httpProtocol, String httpsProtocol) + throws GrafanaEnvVariablesNotDefined { + String grafanaHost = System.getProperty(GrafanaConstants.HTTPS_HOST_ENV_VAR); + String scheme = httpsProtocol; + if (Constants.HTTP_PROTOCOL.equals(requestScheme) || StringUtils.isEmpty(grafanaHost)){ + grafanaHost = System.getProperty(GrafanaConstants.HTTP_HOST_ENV_VAR); + scheme = httpProtocol; + } + if(StringUtils.isEmpty(grafanaHost)) { + String errMsg = "Grafana host is not defined in the iot-server.sh properly."; + log.error(errMsg); + throw new GrafanaEnvVariablesNotDefined(errMsg); + } + return scheme + Constants.SCHEME_SEPARATOR + grafanaHost; + } + + public static String getPanelId(URI iframeURL) { + Map> queryMap = HttpUtil.getQueryMap(iframeURL); + return HttpUtil.getFirstQueryValue(GrafanaConstants.PANEL_ID_QUERY_PARAM, queryMap); + } + public static String getOrgId(URI iframeURL) { + Map> queryMap = HttpUtil.getQueryMap(iframeURL); + return HttpUtil.getFirstQueryValue(GrafanaConstants.ORG_ID_QUERY_PARAM, queryMap); + } + + public static String getDashboardUID(URI iframeURL) { + return HttpUtil.getRequestSubPathFromEnd(iframeURL, GrafanaConstants.IFRAME_URL_DASHBOARD_UID_INDEX); + } + + public static JsonObject getJsonBody(String body) { + return new Gson().fromJson(body, JsonObject.class); + } + + public static String getBasicAuthBase64Header() throws GrafanaManagementException { + GrafanaConfiguration configuration = GrafanaConfigurationManager.getInstance().getGrafanaConfiguration(); + String username = configuration.getAdminUser().getUsername(); + String password = configuration.getAdminUser().getPassword(); + return Constants.BASIC_AUTH_HEADER_PREFIX + GrafanaUtil.getBase64Encode(username, password); + } + + public static String getBase64Encode(String key, String value) { + return new String(Base64.encodeBase64((key + ":" + value).getBytes())); + } + + public static GrafanaPanelIdentifier getPanelIdentifierFromReferer(String referer) { + URI refererUri = HttpUtil.createURI(referer); + String orgId = GrafanaUtil.getOrgId(refererUri); + String dashboardUID = GrafanaUtil.getDashboardUID(refererUri); + String panelId = GrafanaUtil.getPanelId(refererUri); + return new GrafanaPanelIdentifier(orgId, dashboardUID, panelId); + } + + public static int getTenantId() { + return PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); + } +} diff --git a/components/analytics-mgt/grafana-mgt/pom.xml b/components/analytics-mgt/grafana-mgt/pom.xml new file mode 100644 index 0000000000..7657d60461 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/pom.xml @@ -0,0 +1,42 @@ + + + + + + + org.wso2.carbon.devicemgt + analytics-mgt + 5.0.3-SNAPSHOT + ../pom.xml + + + 4.0.0 + grafana-mgt + pom + Entgra - Grafana API Handler Component + http://entgra.io + + + + io.entgra.analytics.mgt.grafana.proxy.core + io.entgra.analytics.mgt.grafana.proxy.common + io.entgra.analytics.mgt.grafana.proxy.api + + + diff --git a/components/analytics-mgt/pom.xml b/components/analytics-mgt/pom.xml new file mode 100644 index 0000000000..49f16fdd54 --- /dev/null +++ b/components/analytics-mgt/pom.xml @@ -0,0 +1,20 @@ + + + + carbon-devicemgt + org.wso2.carbon.devicemgt + 5.0.3-SNAPSHOT + ../../pom.xml + + + 4.0.0 + analytics-mgt + pom + Entgra - Analytics Management Component + http://entgra.io + + + grafana-mgt + + + diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/pom.xml b/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/pom.xml index 7fb7e2ff3e..0ab9138c8d 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/pom.xml +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/pom.xml @@ -22,7 +22,7 @@ apimgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/pom.xml b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/pom.xml index 58141d635e..1d20bd67ee 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/pom.xml +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/pom.xml @@ -21,7 +21,7 @@ apimgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/pom.xml b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/pom.xml index 8961e7446e..5ce50a53eb 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/pom.xml +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/pom.xml @@ -22,7 +22,7 @@ apimgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/pom.xml b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/pom.xml index 415b41e932..e6dac63e65 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/pom.xml +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/pom.xml @@ -22,7 +22,7 @@ apimgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/apimgt-extensions/pom.xml b/components/apimgt-extensions/pom.xml index 565482a552..853fd05aa7 100644 --- a/components/apimgt-extensions/pom.xml +++ b/components/apimgt-extensions/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/components/application-mgt/io.entgra.application.mgt.addons/pom.xml b/components/application-mgt/io.entgra.application.mgt.addons/pom.xml index c2f9a31f7d..8d264d59be 100644 --- a/components/application-mgt/io.entgra.application.mgt.addons/pom.xml +++ b/components/application-mgt/io.entgra.application.mgt.addons/pom.xml @@ -20,7 +20,7 @@ application-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/application-mgt/io.entgra.application.mgt.api/pom.xml b/components/application-mgt/io.entgra.application.mgt.api/pom.xml index 2c2928887d..777c10b817 100644 --- a/components/application-mgt/io.entgra.application.mgt.api/pom.xml +++ b/components/application-mgt/io.entgra.application.mgt.api/pom.xml @@ -22,7 +22,7 @@ application-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/application-mgt/io.entgra.application.mgt.common/pom.xml b/components/application-mgt/io.entgra.application.mgt.common/pom.xml index 276bac4b98..25e0944d26 100644 --- a/components/application-mgt/io.entgra.application.mgt.common/pom.xml +++ b/components/application-mgt/io.entgra.application.mgt.common/pom.xml @@ -21,7 +21,7 @@ org.wso2.carbon.devicemgt application-mgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/application-mgt/io.entgra.application.mgt.core/pom.xml b/components/application-mgt/io.entgra.application.mgt.core/pom.xml index f3458ef553..ffd0b0ddb6 100644 --- a/components/application-mgt/io.entgra.application.mgt.core/pom.xml +++ b/components/application-mgt/io.entgra.application.mgt.core/pom.xml @@ -21,7 +21,7 @@ org.wso2.carbon.devicemgt application-mgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/application-mgt/io.entgra.application.mgt.publisher.api/pom.xml b/components/application-mgt/io.entgra.application.mgt.publisher.api/pom.xml index b63528235d..ebeb1f6343 100644 --- a/components/application-mgt/io.entgra.application.mgt.publisher.api/pom.xml +++ b/components/application-mgt/io.entgra.application.mgt.publisher.api/pom.xml @@ -22,7 +22,7 @@ application-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/application-mgt/io.entgra.application.mgt.store.api/pom.xml b/components/application-mgt/io.entgra.application.mgt.store.api/pom.xml index 9b46f642fc..bb118b1912 100644 --- a/components/application-mgt/io.entgra.application.mgt.store.api/pom.xml +++ b/components/application-mgt/io.entgra.application.mgt.store.api/pom.xml @@ -22,7 +22,7 @@ application-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/application-mgt/pom.xml b/components/application-mgt/pom.xml index 8dc8ef23dc..f0ba6dc5d7 100644 --- a/components/application-mgt/pom.xml +++ b/components/application-mgt/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.api/pom.xml b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.api/pom.xml index 19d9eba0f5..be5d49d1a8 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.api/pom.xml +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.api/pom.xml @@ -22,7 +22,7 @@ certificate-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/pom.xml b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/pom.xml index 0cf1becb84..e4fcceb66b 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/pom.xml +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/pom.xml @@ -22,7 +22,7 @@ certificate-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/pom.xml b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/pom.xml index 0aac7f27a2..970249d34a 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/pom.xml +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/pom.xml @@ -38,7 +38,7 @@ org.wso2.carbon.devicemgt certificate-mgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/certificate-mgt/pom.xml b/components/certificate-mgt/pom.xml index db23fa09fe..283e977bb7 100644 --- a/components/certificate-mgt/pom.xml +++ b/components/certificate-mgt/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer/pom.xml b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer/pom.xml index fffb7bebde..64c5c6131d 100644 --- a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer/pom.xml +++ b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer/pom.xml @@ -22,7 +22,7 @@ device-mgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.pull.notification/pom.xml b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.pull.notification/pom.xml index b00f13f3ff..2acd8b41ce 100644 --- a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.pull.notification/pom.xml +++ b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.pull.notification/pom.xml @@ -22,7 +22,7 @@ device-mgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm/pom.xml b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm/pom.xml index ac1089637c..644a9d19d8 100644 --- a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm/pom.xml +++ b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm/pom.xml @@ -22,7 +22,7 @@ device-mgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http/pom.xml b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http/pom.xml index 8c26406263..eee2b7a6a8 100644 --- a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http/pom.xml +++ b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http/pom.xml @@ -22,7 +22,7 @@ device-mgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt/pom.xml b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt/pom.xml index f8d0edf3c5..b3cda0ad5c 100644 --- a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt/pom.xml +++ b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt/pom.xml @@ -22,7 +22,7 @@ device-mgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp/pom.xml b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp/pom.xml index d59e6fb6e9..e65bd2a136 100644 --- a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp/pom.xml +++ b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp/pom.xml @@ -22,7 +22,7 @@ device-mgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/device-mgt-extensions/pom.xml b/components/device-mgt-extensions/pom.xml index f19e334e91..ac3dd2a6fe 100644 --- a/components/device-mgt-extensions/pom.xml +++ b/components/device-mgt-extensions/pom.xml @@ -22,7 +22,7 @@ carbon-devicemgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/components/device-mgt/io.entgra.carbon.device.mgt.config.api/pom.xml b/components/device-mgt/io.entgra.carbon.device.mgt.config.api/pom.xml index 0501fdc394..be26831d54 100644 --- a/components/device-mgt/io.entgra.carbon.device.mgt.config.api/pom.xml +++ b/components/device-mgt/io.entgra.carbon.device.mgt.config.api/pom.xml @@ -22,7 +22,7 @@ device-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/pom.xml b/components/device-mgt/org.wso2.carbon.device.mgt.api/pom.xml index 0f0d22e475..af068895c3 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/pom.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/pom.xml @@ -22,7 +22,7 @@ device-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java index c19f050ffd..4f38198cfe 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java @@ -1899,6 +1899,141 @@ public interface DeviceManagementService { @Size(max = 45) String id); + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/{type}/{id}/getstatushistory") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "Get Device status history", + notes = "Get a list of status history associated with the device type and id", + tags = "Device Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:view") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully fetched the status history of matching devices.", + response = List.class, + responseHeaders = { + @ResponseHeader( + name = "Content-Type", + description = "The content type of the body"), + @ResponseHeader( + name = "ETag", + description = "Entity Tag of the response resource.\n" + + "Used by caches, or in conditional requests."), + @ResponseHeader( + name = "Last-Modified", + description = "Date and time the resource was last modified.\n" + + "Used by caches, or in conditional requests."), + }), + @ApiResponse( + code = 304, + message = "Not Modified. Empty body because the client already has the latest version" + + " of the requested resource.\n"), + @ApiResponse( + code = 400, + message = "Bad Request. \n Invalid request or validation error.", + response = ErrorResponse.class), + @ApiResponse( + code = 404, + message = "Not Found. \n A device with the specified device type and id was not found.", + response = ErrorResponse.class), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n " + + "Server error occurred while retrieving the device details.", + response = ErrorResponse.class) + }) + Response getDeviceStatusHistory( + @ApiParam( + name = "type", + value = "The device type, such as ios, android, or windows.", + required = true) + @PathParam("type") + @Size(max = 45) + String type, + @ApiParam( + name = "id", + value = "Device ID.", + required = true) + @PathParam("id") + @Size(max = 45) + String id); + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/{type}/{id}/getenrolmentstatushistory") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "Get Device Current Enrolment status history", + notes = "Get a list of status history associated with the device type and id for the current enrolment", + tags = "Device Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:view") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully fetched the status history of matching devices.", + response = List.class, + responseHeaders = { + @ResponseHeader( + name = "Content-Type", + description = "The content type of the body"), + @ResponseHeader( + name = "ETag", + description = "Entity Tag of the response resource.\n" + + "Used by caches, or in conditional requests."), + @ResponseHeader( + name = "Last-Modified", + description = "Date and time the resource was last modified.\n" + + "Used by caches, or in conditional requests."), + }), + @ApiResponse( + code = 304, + message = "Not Modified. Empty body because the client already has the latest version" + + " of the requested resource.\n"), + @ApiResponse( + code = 400, + message = "Bad Request. \n Invalid request or validation error.", + response = ErrorResponse.class), + @ApiResponse( + code = 404, + message = "Not Found. \n A device with the specified device type and id was not found.", + response = ErrorResponse.class), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n " + + "Server error occurred while retrieving the device details.", + response = ErrorResponse.class) + }) + Response getCurrentEnrolmentDeviceStatusHistory( + @ApiParam( + name = "type", + value = "The device type, such as ios, android, or windows.", + required = true) + @PathParam("type") + @Size(max = 45) + String type, + @ApiParam( + name = "id", + value = "Device ID.", + required = true) + @PathParam("id") + @Size(max = 45) + String id); + @PUT @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ReportManagementService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ReportManagementService.java index ee3e9354dd..cbec6b47c9 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ReportManagementService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ReportManagementService.java @@ -17,6 +17,7 @@ */ package org.wso2.carbon.device.mgt.jaxrs.service.api; +import com.google.gson.JsonObject; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; @@ -37,12 +38,16 @@ import org.wso2.carbon.device.mgt.jaxrs.util.Constants; import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.POST; 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.Context; +import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; import java.util.List; @SwaggerDefinition( diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java index e1c9e8fb15..f3f580dc5d 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java @@ -85,6 +85,7 @@ import org.wso2.carbon.device.mgt.common.policy.mgt.monitor.NonComplianceData; import org.wso2.carbon.device.mgt.common.policy.mgt.monitor.PolicyComplianceException; import org.wso2.carbon.device.mgt.common.search.PropertyMap; import org.wso2.carbon.device.mgt.common.search.SearchContext; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; import org.wso2.carbon.device.mgt.core.app.mgt.ApplicationManagementProviderService; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceDetailsMgtException; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager; @@ -1186,6 +1187,70 @@ public class DeviceManagementServiceImpl implements DeviceManagementService { } } + /** + * List device status history + * + * @param type Device type + * @param id Device id + * @return {@link Response} object + */ + @GET + @Path("/{type}/{id}/getstatushistory") + public Response getDeviceStatusHistory(@PathParam("type") @Size(max = 45) String type, + @PathParam("id") @Size(max = 45) String id) { + //TODO check authorization for this + RequestValidationUtil.validateDeviceIdentifier(type, id); + DeviceManagementProviderService deviceManagementProviderService = + DeviceMgtAPIUtils.getDeviceManagementService(); + try { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(id, type); + Device persistedDevice = deviceManagementProviderService.getDevice(deviceIdentifier, false); + if (persistedDevice == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + List deviceStatusHistory = deviceManagementProviderService.getDeviceStatusHistory(persistedDevice); + return Response.status(Response.Status.OK).entity(deviceStatusHistory).build(); + } catch (DeviceManagementException e) { + String msg = "Error occurred while retreiving device status history for device of type : " + type + " and " + + "device id : " + id; + log.error(msg); + return Response.status(Response.Status.BAD_REQUEST).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); + } + } + + /** + * List device status history for the current enrolment + * + * @param type Device type + * @param id Device id + * @return {@link Response} object + */ + @GET + @Path("/{type}/{id}/getenrolmentstatushistory") + public Response getCurrentEnrolmentDeviceStatusHistory(@PathParam("type") @Size(max = 45) String type, + @PathParam("id") @Size(max = 45) String id) { + //TODO check authorization for this or current enrolment should be based on for the enrolment associated with the user + RequestValidationUtil.validateDeviceIdentifier(type, id); + DeviceManagementProviderService deviceManagementProviderService = + DeviceMgtAPIUtils.getDeviceManagementService(); + try { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(id, type); + Device persistedDevice = deviceManagementProviderService.getDevice(deviceIdentifier, false); + if (persistedDevice == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + List deviceStatusHistory = deviceManagementProviderService.getDeviceCurrentEnrolmentStatusHistory(persistedDevice); + return Response.status(Response.Status.OK).entity(deviceStatusHistory).build(); + } catch (DeviceManagementException e) { + String msg = "Error occurred while retreiving device status history for device of type : " + type + " and " + + "device id : " + id; + log.error(msg); + return Response.status(Response.Status.BAD_REQUEST).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); + } + } + @POST @Path("/{type}/operations") public Response addOperation(@PathParam("type") String type, @Valid OperationRequest operationRequest) { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/util/RequestValidationUtil.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/util/RequestValidationUtil.java index 116c715eb2..1308dfa970 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/util/RequestValidationUtil.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/util/RequestValidationUtil.java @@ -610,10 +610,12 @@ public class RequestValidationUtil { || Constants.OperationStatus.NOTNOW.toUpperCase().equals(status.get(i)) || Constants.OperationStatus.REPEATED.toUpperCase().equals(status.get(i)) || Constants.OperationStatus.PENDING.toUpperCase().equals(status.get(i)) - || Constants.OperationStatus.IN_PROGRESS.toUpperCase().equals(status.get(i))) { + || Constants.OperationStatus.IN_PROGRESS.toUpperCase().equals(status.get(i)) + || Constants.OperationStatus.REQUIRED_CONFIRMATION.toUpperCase().equals(status.get(i)) + || Constants.OperationStatus.CONFIRMED.toUpperCase().equals(status.get(i))) { } else { String msg = "Invalid status type: " + status + ". \nValid status types are COMPLETED | ERROR | " + - "IN_PROGRESS | NOTNOW | PENDING | REPEATED"; + "IN_PROGRESS | NOTNOW | PENDING | REPEATED | REQUIRED_CONFIRMATION | CONFIRMED"; log.error(msg); throw new InputValidationException(new ErrorResponse.ErrorResponseBuilder() .setCode(HttpStatus.SC_BAD_REQUEST) diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java index 7042bb8610..d7462993a9 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java @@ -68,6 +68,8 @@ public class Constants { public static final String PENDING = "pending"; public static final String NOTNOW = "notnow"; public static final String REPEATED = "repeated"; + public static final String REQUIRED_CONFIRMATION = "required_confirmation"; + public static final String CONFIRMED = "confirmed"; } public static final String DEVICES = "devices"; public static final String ATTRIBUTE_DISPLAY_NAME = "DisplayName"; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java index c26a1f49a4..8861833f5a 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java @@ -959,9 +959,15 @@ public class DeviceMgtAPIUtils { case Constants.OperationStatus.REPEATED: operation.setStatus(Operation.Status.REPEATED); break; + case Constants.OperationStatus.REQUIRED_CONFIRMATION: + operation.setStatus(Operation.Status.REQUIRED_CONFIRMATION); + break; + case Constants.OperationStatus.CONFIRMED: + operation.setStatus(Operation.Status.CONFIRMED); + break; default: String msg = "Invalid operation status. Valid operations: " + - "[IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW]"; + "[IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW, REQUIRED_CONFIRMATION, CONFIRMED]"; log.error(msg); throw new BadRequestException(msg); } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/pom.xml b/components/device-mgt/org.wso2.carbon.device.mgt.common/pom.xml index 5ae45654ef..5429879fde 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/pom.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/pom.xml @@ -21,7 +21,7 @@ device-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceManagementConstants.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceManagementConstants.java index 62590a821c..154594678c 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceManagementConstants.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceManagementConstants.java @@ -142,4 +142,9 @@ public final class DeviceManagementConstants { public static final String GEOFENCE = "GEOFENCE"; } + public static final class MaintenanceProperties { + private MaintenanceProperties() {throw new AssertionError();} + public static final String MAINTENANCE_USER = "system"; + } + } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/ActivityStatus.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/ActivityStatus.java index 234aa084c2..c07621a610 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/ActivityStatus.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/ActivityStatus.java @@ -30,7 +30,7 @@ import java.util.List; public class ActivityStatus { public enum Status { - IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, INVALID, UNAUTHORIZED, NOTNOW + IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, INVALID, UNAUTHORIZED, NOTNOW, REQUIRED_CONFIRMATION, CONFIRMED } @ApiModelProperty( diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/Operation.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/Operation.java index b3b863a5f3..4c6dd9256e 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/Operation.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/Operation.java @@ -37,7 +37,7 @@ public class Operation implements Serializable { } public enum Status { - IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW + IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW, REQUIRED_CONFIRMATION, CONFIRMED } public enum Control { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java index 63333de165..ec00986a47 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java @@ -154,4 +154,6 @@ public interface OperationManager { */ boolean isOperationExist(DeviceIdentifier deviceId, int operationId) throws OperationManagementException; + List getActivities(List deviceTypes, String operationCode, long updatedSince, String operationStatus) + throws OperationManagementException; } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/type/mgt/DeviceStatus.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/type/mgt/DeviceStatus.java new file mode 100644 index 0000000000..a91c4947b1 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/type/mgt/DeviceStatus.java @@ -0,0 +1,61 @@ +package org.wso2.carbon.device.mgt.common.type.mgt; + +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; + +import java.util.Date; + +public class DeviceStatus { + private EnrolmentInfo.Status status; + private Date updateTime; + private int enrolmentId; + private int deviceId; + private String changedBy; + + public DeviceStatus(int enrolmentId, int deviceId, EnrolmentInfo.Status status, Date updateTime, String changedBy) { + this.status = status; + this.updateTime = updateTime; + this.enrolmentId = enrolmentId; + this.deviceId = deviceId; + this.changedBy = changedBy; + } + + public String getChangedBy() { + return changedBy; + } + + public void setChangedBy(String changedBy) { + this.changedBy = changedBy; + } + + public EnrolmentInfo.Status getStatus() { + return status; + } + + public void setStatus(EnrolmentInfo.Status status) { + this.status = status; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public int getEnrolmentId() { + return enrolmentId; + } + + public void setEnrolmentId(int enrolmentId) { + this.enrolmentId = enrolmentId; + } + + public int getDeviceId() { + return deviceId; + } + + public void setDeviceId(int deviceId) { + this.deviceId = deviceId; + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/pom.xml b/components/device-mgt/org.wso2.carbon.device.mgt.core/pom.xml index 04b1e73c79..e481eca242 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/pom.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml @@ -338,6 +338,10 @@ org.wso2.carbon.devicemgt io.entgra.server.bootup.heartbeat.beacon + + com.google.guava + guava + diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/common/util/HttpUtil.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/common/util/HttpUtil.java new file mode 100644 index 0000000000..5a21aaa122 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/common/util/HttpUtil.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.common.util; + +import org.apache.commons.lang.StringUtils; +import org.apache.http.HttpResponse; +import org.apache.http.entity.ContentType; +import org.apache.http.util.EntityUtils; +import org.wso2.carbon.device.mgt.core.report.mgt.Constants; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class HttpUtil { + + public static URI createURI(String uriString) { + uriString = uriString.replace(" ", "%20"); + return URI.create(uriString); + } + + public static String getRequestSubPathFromEnd(URI requestUri, int position) { + if (requestUri.getPath() != null) { + String[] pathList = requestUri.getPath().split("/"); + if (pathList.length - 1 >= position) { + return pathList[pathList.length - 1 - position]; + } + } + return null; + } + + public static String getRequestSubPath(String fullPath, int position) { + String[] pathList = fullPath.split("/"); + if (pathList.length - 1 > position) { + String path = pathList[position + 1]; + if(path.contains(Constants.URI_QUERY_SEPARATOR)) { + path = path.substring(0, path.indexOf(Constants.URI_QUERY_SEPARATOR)); + } + return path; + } + return null; + } + + public static String getResponseString(HttpResponse response) throws IOException { + return EntityUtils.toString(response.getEntity()); + } + + public static boolean isQueryParamExist(String param, URI request) { + Map> queryMap = getQueryMap(request); + return queryMap.containsKey(param); + } + public static String getFirstQueryValue(String param, Map> queryMap) { + List valueList = queryMap.get(param); + String firstValue = null; + if(valueList != null) { + firstValue = valueList.get(0); + } + return firstValue; + } + public static Map> getQueryMap(String uri) { + String query = getQueryFromURIPath(uri); + Map> map = new HashMap<>(); + if (query != null) { + String[] params = query.split("&"); + for (String param : params) { + String[] paramArr = param.split("="); + if (paramArr.length == 2) { + String name = paramArr[0]; + String value = paramArr[1]; + if (!map.containsKey(name)) { + List valueList = new ArrayList<>(); + map.put(name, valueList); + } + map.get(name).add(value); + } + } + } + return map; + } + public static Map> getQueryMap(URI request) { + String query = request.getQuery(); + Map> map = new HashMap<>(); + if (query != null) { + String[] params = query.split("&"); + for (String param : params) { + String[] paramArr = param.split("="); + if (paramArr.length == 2) { + String name = paramArr[0]; + String value = paramArr[1]; + if (!map.containsKey(name)) { + List valueList = new ArrayList<>(); + map.put(name, valueList); + } + map.get(name).add(value); + } + } + } + return map; + } + public static String getQueryFromURIPath(String uri) { + String query = null; + if (uri.length() > "?".length() && uri.contains("?")) { + query = uri.substring(uri.lastIndexOf("?") + "?".length()); + } + if (StringUtils.isEmpty(query)) { + query = null; + } + return query; + } + + public static String getContentType(HttpResponse response) { + ContentType contentType = ContentType.getOrDefault(response.getEntity()); + return contentType.getMimeType(); + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/DeviceManagementConfig.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/DeviceManagementConfig.java index 4b8adcc357..ebdfa35bfc 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/DeviceManagementConfig.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/DeviceManagementConfig.java @@ -24,6 +24,7 @@ import org.wso2.carbon.device.mgt.core.config.archival.ArchivalConfiguration; import org.wso2.carbon.device.mgt.core.config.cache.CertificateCacheConfiguration; import org.wso2.carbon.device.mgt.core.config.cache.DeviceCacheConfiguration; import org.wso2.carbon.device.mgt.core.config.cache.GeoFenceCacheConfiguration; +import org.wso2.carbon.device.mgt.core.config.operation.timeout.OperationTimeoutConfiguration; import org.wso2.carbon.device.mgt.core.event.config.EventOperationTaskConfiguration; import org.wso2.carbon.device.mgt.core.config.geo.location.GeoLocationConfiguration; import org.wso2.carbon.device.mgt.core.config.identity.IdentityConfigurations; @@ -66,6 +67,7 @@ public final class DeviceManagementConfig { private ArchivalConfiguration archivalConfiguration; private EnrollmentNotificationConfiguration enrollmentNotificationConfiguration; private DefaultRoles defaultRoles; + private OperationTimeoutConfiguration operationTimeoutConfiguration; @XmlElement(name = "ManagementRepository", required = true) public DeviceManagementConfigRepository getDeviceManagementConfigRepository() { @@ -243,5 +245,14 @@ public final class DeviceManagementConfig { public DefaultRoles getDefaultRoles() { return defaultRoles; } public void setDefaultRoles(DefaultRoles defaultRoles) { this.defaultRoles = defaultRoles; } + + @XmlElement(name = "OperationTimeoutConfigurations", required = true) + public OperationTimeoutConfiguration getOperationTimeoutConfiguration() { + return operationTimeoutConfiguration; + } + + public void setOperationTimeoutConfiguration(OperationTimeoutConfiguration operationTimeoutConfiguration) { + this.operationTimeoutConfiguration = operationTimeoutConfiguration; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/operation/timeout/OperationTimeout.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/operation/timeout/OperationTimeout.java new file mode 100644 index 0000000000..f9f9302770 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/operation/timeout/OperationTimeout.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.config.operation.timeout; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +@XmlRootElement(name = "OperationTimeout") +public class OperationTimeout { + + private String code; + private int timeout; + private List deviceTypes; + private String initialStatus; + private String nextStatus; + + public String getCode() { + return code; + } + + @XmlElement(name = "Code", required = true) + public void setCode(String code) { + this.code = code; + } + + public int getTimeout() { + return timeout; + } + + @XmlElement(name = "Timeout", required = true) + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public List getDeviceTypes() { + return deviceTypes; + } + + @XmlElementWrapper(name = "DeviceTypes", required = true) + @XmlElement(name = "DeviceType", required = true) + public void setDeviceTypes(List deviceTypes) { + this.deviceTypes = deviceTypes; + } + + public String getInitialStatus() { + return initialStatus; + } + + @XmlElement(name = "InitialStatus", required = true) + public void setInitialStatus(String initialStatus) { + this.initialStatus = initialStatus; + } + + public String getNextStatus() { + return nextStatus; + } + + @XmlElement(name = "NextStatus", required = true) + public void setNextStatus(String nextStatus) { + this.nextStatus = nextStatus; + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/operation/timeout/OperationTimeoutConfiguration.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/operation/timeout/OperationTimeoutConfiguration.java new file mode 100644 index 0000000000..a2777eda40 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/operation/timeout/OperationTimeoutConfiguration.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.config.operation.timeout; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +@XmlRootElement(name = "OperationTimeoutConfigurations") +public class OperationTimeoutConfiguration { + private List operationTimeoutList; + + public List getOperationTimeoutList() { + return operationTimeoutList; + } + + @XmlElementWrapper(name = "OperationTimeouts", required = true) + @XmlElement(name = "OperationTimeout", required = false) + public void setOperationTimeoutList(List operationTimeoutList) { + this.operationTimeoutList = operationTimeoutList; + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java index c5c778ca23..348f256016 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java @@ -26,11 +26,8 @@ import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementExcepti import org.wso2.carbon.device.mgt.common.exceptions.UnsupportedDatabaseEngineException; import org.wso2.carbon.device.mgt.core.config.datasource.DataSourceConfig; import org.wso2.carbon.device.mgt.core.config.datasource.JNDILookupDefinition; -import org.wso2.carbon.device.mgt.core.dao.impl.ApplicationDAOImpl; -import org.wso2.carbon.device.mgt.core.dao.impl.DeviceTypeDAOImpl; -import org.wso2.carbon.device.mgt.core.dao.impl.EnrollmentDAOImpl; +import org.wso2.carbon.device.mgt.core.dao.impl.*; import org.wso2.carbon.device.mgt.core.dao.impl.event.GenericEventConfigDAOImpl; -import org.wso2.carbon.device.mgt.core.dao.impl.GeofenceDAOImpl; import org.wso2.carbon.device.mgt.core.dao.impl.device.GenericDeviceDAOImpl; import org.wso2.carbon.device.mgt.core.dao.impl.device.OracleDeviceDAOImpl; import org.wso2.carbon.device.mgt.core.dao.impl.device.PostgreSQLDeviceDAOImpl; @@ -127,7 +124,9 @@ public class DeviceManagementDAOFactory { public static EnrollmentDAO getEnrollmentDAO() { return new EnrollmentDAOImpl(); } - + public static DeviceStatusDAO getDeviceStatusDAO() { + return new DeviceStatusDAOImpl(); + } public static ApplicationDAO getApplicationDAO() { if (databaseEngine != null) { switch (databaseEngine) { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusDAO.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusDAO.java new file mode 100644 index 0000000000..6d9cb18e58 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusDAO.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.core.dao; + +import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; +import org.wso2.carbon.device.mgt.common.EnrolmentInfo.Status; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; + +import java.util.Date; +import java.util.List; + +public interface DeviceStatusDAO { + +// boolean updateStatus(int deviceId, Status status, int tenantId) throws DeviceManagementDAOException; + +// boolean updateStatus(int enrolmentId, Status status) throws DeviceManagementDAOException; + + List getStatus(int deviceId, int tenantId) throws DeviceManagementDAOException; + + List getStatus(int deviceId, int tenantId, Date fromDate, Date toDate) throws DeviceManagementDAOException; + + List getStatus(int enrolmentId) throws DeviceManagementDAOException; + + List getStatus(int enrolmentId, Date fromDate, Date toDate) throws DeviceManagementDAOException; + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/EnrollmentDAO.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/EnrollmentDAO.java index 6613b3044a..be621eab34 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/EnrollmentDAO.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/EnrollmentDAO.java @@ -34,8 +34,11 @@ public interface EnrollmentDAO { int removeEnrollment(int deviceId, String currentOwner, int tenantId) throws DeviceManagementDAOException; + @Deprecated boolean setStatus(String currentOwner, Status status, int tenantId) throws DeviceManagementDAOException; + boolean setStatusAllDevices(String currentOwner, Status status, int tenantId) throws DeviceManagementDAOException; + boolean setStatus(int enrolmentId, Status status, int tenantId) throws DeviceManagementDAOException; Status getStatus(int deviceId, String currentOwner, int tenantId) throws DeviceManagementDAOException; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/DeviceStatusDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/DeviceStatusDAOImpl.java new file mode 100644 index 0000000000..0305f83114 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/DeviceStatusDAOImpl.java @@ -0,0 +1,89 @@ +package org.wso2.carbon.device.mgt.core.dao.impl; + +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; +import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; +import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; +import org.wso2.carbon.device.mgt.core.dao.DeviceStatusDAO; +import org.wso2.carbon.device.mgt.core.dao.EnrollmentDAO; +import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil; + +import java.sql.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class DeviceStatusDAOImpl implements DeviceStatusDAO { + + private List getStatus(int id, Date fromDate, Date toDate, boolean isDeviceId) throws DeviceManagementDAOException { + List result = new ArrayList<>(); + Connection conn; + PreparedStatement stmt = null; + ResultSet rs = null; + EnrolmentInfo.Status status = null; + try { + conn = this.getConnection(); + // either we list all status values for the device using the device id or only get status values for the given enrolment id + String idType = isDeviceId ? "DEVICE_ID" : "ENROLMENT_ID"; + String sql = "SELECT ENROLMENT_ID, DEVICE_ID, UPDATE_TIME, STATUS, CHANGED_BY FROM DM_DEVICE_STATUS WHERE " + idType + " = ?"; + // filter the data based on a date range if specified + if (fromDate != null){ + sql += " AND UPDATE_TIME >= ?"; + } + if (toDate != null){ + sql += " AND UPDATE_TIME <= ?"; + } + + stmt = conn.prepareStatement(sql); + + int i = 1; + stmt.setInt(i++, id); + if (fromDate != null){ + Timestamp fromTime = new Timestamp(fromDate.getTime()); + stmt.setTimestamp(i++, fromTime); + } + if (toDate != null){ + Timestamp toTime = new Timestamp(toDate.getTime()); + stmt.setTimestamp(i++, toTime); + } + rs = stmt.executeQuery(); + + while (rs.next()) { + DeviceStatus ds = new DeviceStatus(rs.getInt("ENROLMENT_ID"), rs.getInt("DEVICE_ID"), + EnrolmentInfo.Status.valueOf(rs.getString("STATUS")), + new Date(rs.getTimestamp("UPDATE_TIME").getTime()), rs.getString("CHANGED_BY")); + result.add(ds); + } + } catch (SQLException e) { + throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment", e); + } finally { + DeviceManagementDAOUtil.cleanupResources(stmt, rs); + } + return result; + } + + @Override + public List getStatus(int enrolmentId, Date fromDate, Date toDate) throws DeviceManagementDAOException { + return getStatus(enrolmentId, fromDate, toDate, false); + } + + @Override + public List getStatus(int deviceId, int tenantId) throws DeviceManagementDAOException { + return getStatus(deviceId, tenantId, null, null); + } + + @Override + public List getStatus(int deviceId, int tenantId, Date fromDate, Date toDate) throws DeviceManagementDAOException { + return getStatus(deviceId, fromDate, toDate, true); + } + + @Override + public List getStatus(int enrolmentId) throws DeviceManagementDAOException { + return getStatus(enrolmentId, null, null); + } + + + private Connection getConnection() throws SQLException { + return DeviceManagementDAOFactory.getConnection(); + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/EnrollmentDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/EnrollmentDAOImpl.java index 186b3b4dd6..1472518b1c 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/EnrollmentDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/EnrollmentDAOImpl.java @@ -18,7 +18,9 @@ */ package org.wso2.carbon.device.mgt.core.dao.impl; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.device.mgt.common.DeviceManagementConstants; import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; @@ -39,7 +41,7 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { @Override public EnrolmentInfo addEnrollment(int deviceId, EnrolmentInfo enrolmentInfo, - int tenantId) throws DeviceManagementDAOException { + int tenantId) throws DeviceManagementDAOException { Connection conn; PreparedStatement stmt = null; ResultSet rs = null; @@ -64,10 +66,12 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { enrolmentInfo.setId(enrolmentId); enrolmentInfo.setDateOfEnrolment(enrollmentTime.getTime()); enrolmentInfo.setDateOfLastUpdate(enrollmentTime.getTime()); + addDeviceStatus(enrolmentId, enrolmentInfo.getStatus()); return enrolmentInfo; } return null; } catch (SQLException e) { + e.printStackTrace(); throw new DeviceManagementDAOException("Error occurred while adding enrolment configuration", e); } finally { DeviceManagementDAOUtil.cleanupResources(stmt, rs); @@ -89,7 +93,11 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { stmt.setTimestamp(3, new Timestamp(new Date().getTime())); stmt.setInt(4, enrolmentInfo.getId()); stmt.setInt(5, tenantId); - return stmt.executeUpdate(); + int updatedCount = stmt.executeUpdate(); + if (updatedCount == 1){ + addDeviceStatus(enrolmentInfo.getId(), enrolmentInfo.getStatus()); + } + return updatedCount; } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while updating enrolment configuration", e); } finally { @@ -124,6 +132,9 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { } if (updateStatus > 0) { status = true; + for (EnrolmentInfo enrolmentInfo : enrolmentInfos) { + addDeviceStatus(enrolmentInfo); + } } } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while updating enrolment status of given device-list.", e); @@ -189,23 +200,31 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { @Override public boolean setStatus(String currentOwner, EnrolmentInfo.Status status, int tenantId) throws DeviceManagementDAOException { + return setStatusAllDevices(currentOwner, status, tenantId); + } + + @Override + public boolean setStatusAllDevices(String currentOwner, EnrolmentInfo.Status status, int tenantId) + throws DeviceManagementDAOException{ Connection conn; PreparedStatement stmt = null; + Timestamp updateTime = new Timestamp(new Date().getTime()); if(getCountOfDevicesOfOwner(currentOwner, tenantId) > 0){ try { conn = this.getConnection(); - String sql = "UPDATE DM_ENROLMENT SET STATUS = ? WHERE OWNER = ? AND TENANT_ID = ?"; + String sql = "UPDATE DM_ENROLMENT SET STATUS = ?, DATE_OF_LAST_UPDATE = ? WHERE OWNER = ? AND TENANT_ID = ?"; stmt = conn.prepareStatement(sql); stmt.setString(1, status.toString()); - stmt.setString(2, currentOwner); - stmt.setInt(3, tenantId); + stmt.setTimestamp(2, updateTime); + stmt.setString(3, currentOwner); + stmt.setInt(4, tenantId); stmt.executeUpdate(); } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment", e); } finally { DeviceManagementDAOUtil.cleanupResources(stmt, null); } - return true; + return addDeviceStatus(currentOwner, status, tenantId); } else { return false; } @@ -215,14 +234,22 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { public boolean setStatus(int enrolmentID, EnrolmentInfo.Status status, int tenantId) throws DeviceManagementDAOException { Connection conn; PreparedStatement stmt = null; + Timestamp updateTime = new Timestamp(new Date().getTime()); try { conn = this.getConnection(); - String sql = "UPDATE DM_ENROLMENT SET STATUS = ? WHERE ID = ? AND TENANT_ID = ?"; + String sql = "UPDATE DM_ENROLMENT SET STATUS = ?, DATE_OF_LAST_UPDATE = ? WHERE ID = ? AND TENANT_ID = ?"; stmt = conn.prepareStatement(sql); stmt.setString(1, status.toString()); - stmt.setInt(2, enrolmentID); - stmt.setInt(3, tenantId); - stmt.executeUpdate(); + stmt.setTimestamp(2, updateTime); + stmt.setInt(3, enrolmentID); + stmt.setInt(4, tenantId); + int updatedRowCount = stmt.executeUpdate(); + if (updatedRowCount != 1){ + throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment: "+ + updatedRowCount + " rows were updated instead of one row!!!"); + } + // save the device status history + addDeviceStatus(enrolmentID, status); } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment", e); } finally { @@ -231,6 +258,133 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { return true; } + private boolean addDeviceStatus(EnrolmentInfo config) throws DeviceManagementDAOException { + return addDeviceStatus(config.getId(), config.getStatus()); + } + + private boolean addDeviceStatus(String currentOwner, EnrolmentInfo.Status status, int tenantId) throws DeviceManagementDAOException { + Connection conn; + String changedBy = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + if (changedBy == null){ + changedBy = DeviceManagementConstants.MaintenanceProperties.MAINTENANCE_USER; + } + PreparedStatement stmt = null; + ResultSet rs = null; + List enrolmentInfoList = new ArrayList<>(); + try { + conn = this.getConnection(); + String sql = "SELECT ID, DEVICE_ID, OWNER, OWNERSHIP, STATUS, IS_TRANSFERRED, DATE_OF_ENROLMENT, " + + "DATE_OF_LAST_UPDATE, TENANT_ID FROM DM_ENROLMENT WHERE OWNER = ? AND TENANT_ID = ?"; + stmt = conn.prepareStatement(sql); + stmt.setString(1, currentOwner); + stmt.setInt(2, tenantId); + rs = stmt.executeQuery(); + while (rs.next()) { + int enrolmentId = rs.getInt("ID"); + int deviceId = rs.getInt("DEVICE_ID"); + enrolmentInfoList.add(new int[]{enrolmentId, deviceId}); + } + DeviceManagementDAOUtil.cleanupResources(stmt, rs); + Timestamp updateTime = new Timestamp(new Date().getTime()); + sql = "INSERT INTO DM_DEVICE_STATUS (ENROLMENT_ID, DEVICE_ID, STATUS, UPDATE_TIME, CHANGED_BY) VALUES(?, ?, ?, ?, ?)"; + try (PreparedStatement ps = conn.prepareStatement(sql)) { + if (conn.getMetaData().supportsBatchUpdates()) { + for(int[] info: enrolmentInfoList){ + ps.setInt(1, info[0]); + ps.setInt(2, info[1]); + ps.setString(3, status.toString()); + ps.setTimestamp(4, updateTime); + ps.setString(5, changedBy); + ps.addBatch(); + } + int[] batchResult = ps.executeBatch(); + for (int i : batchResult) { + if (i == 0 || i == Statement.SUCCESS_NO_INFO || i == Statement.EXECUTE_FAILED) { + return false; + } + } + } else { + for(int[] info: enrolmentInfoList){ + ps.setInt(1, info[0]); + ps.setInt(2, info[1]); + ps.setString(3, status.toString()); + ps.setTimestamp(4, updateTime); + ps.setString(5, changedBy); + ps.execute(); + } + + } + } + + } catch (SQLException e) { + throw new DeviceManagementDAOException("Error occurred while retrieving the enrolments " + + "information of owner '" + currentOwner + "'", e); + } finally { + DeviceManagementDAOUtil.cleanupResources(stmt, rs); + } + return true; + } + + private boolean addDeviceStatus(int enrolmentId, EnrolmentInfo.Status status) throws DeviceManagementDAOException { + Connection conn; + String changedBy = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + if (changedBy == null){ + changedBy = DeviceManagementConstants.MaintenanceProperties.MAINTENANCE_USER; + } + PreparedStatement stmt = null; + try { + conn = this.getConnection(); + // get the device id and last udpated status from the device status table + String sql = "SELECT DEVICE_ID, STATUS FROM DM_DEVICE_STATUS WHERE ENROLMENT_ID = ? ORDER BY UPDATE_TIME DESC LIMIT 1"; + stmt = conn.prepareStatement(sql); + stmt.setInt(1, enrolmentId); + ResultSet rs = stmt.executeQuery(); + int deviceId = -1; + EnrolmentInfo.Status previousStatus = null; + if (rs.next()) { + // if there is a record corresponding to the enrolment we save the status and the device id + previousStatus = EnrolmentInfo.Status.valueOf(rs.getString("STATUS")); + deviceId = rs.getInt("DEVICE_ID"); + } + DeviceManagementDAOUtil.cleanupResources(stmt, null); + // if there was no record for the enrolment or the previous status is not the same as the current status + // we'll add a record + if (previousStatus == null || previousStatus != status){ + if (deviceId == -1) { + // we need the device id in order to add a new record, therefore we get it from the enrolment table + sql = "SELECT DEVICE_ID FROM DM_ENROLMENT WHERE ID = ?"; + stmt = conn.prepareStatement(sql); + stmt.setInt(1, enrolmentId); + rs = stmt.executeQuery(); + if (rs.next()) { + deviceId = rs.getInt("DEVICE_ID"); + } else { + // if there were no records corresponding to the enrolment id this is a problem. i.e. enrolment + // id is invalid + throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment: no record for enrolment id " + enrolmentId); + } + DeviceManagementDAOUtil.cleanupResources(stmt, null); + } + + sql = "INSERT INTO DM_DEVICE_STATUS (ENROLMENT_ID, DEVICE_ID, STATUS, UPDATE_TIME, CHANGED_BY) VALUES(?, ?, ?, ?, ?)"; + stmt = conn.prepareStatement(sql); + Timestamp updateTime = new Timestamp(new Date().getTime()); + stmt.setInt(1, enrolmentId); + stmt.setInt(2, deviceId); + stmt.setString(3, status.toString()); + stmt.setTimestamp(4, updateTime); + stmt.setString(5, changedBy); + stmt.execute(); + } else { + // no need to update status since the last recorded status is the same as the current status + } + } catch (SQLException e) { + throw new DeviceManagementDAOException("Error occurred while setting the status of device", e); + } finally { + DeviceManagementDAOUtil.cleanupResources(stmt, null); + } + return true; + } @Override public EnrolmentInfo.Status getStatus(int deviceId, String currentOwner, int tenantId) throws DeviceManagementDAOException { @@ -323,7 +477,7 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { try { conn = this.getConnection(); String sql = "SELECT ID, DEVICE_ID, OWNER, OWNERSHIP, STATUS, IS_TRANSFERRED, DATE_OF_ENROLMENT, " + - "DATE_OF_LAST_UPDATE, TENANT_ID FROM DM_ENROLMENT WHERE DEVICE_ID = ? AND OWNER = ? AND TENANT_ID = ?"; + "DATE_OF_LAST_UPDATE, TENANT_ID FROM DM_ENROLMENT WHERE DEVICE_ID = ? AND OWNER = ? AND TENANT_ID = ?"; stmt = conn.prepareStatement(sql); stmt.setInt(1, deviceId); stmt.setString(2, user); @@ -336,7 +490,7 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { return enrolmentInfos; } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while retrieving the enrolments " + - "information of user '" + user + "' upon device '" + deviceId + "'", e); + "information of user '" + user + "' upon device '" + deviceId + "'", e); } finally { DeviceManagementDAOUtil.cleanupResources(stmt, rs); } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dto/operation/mgt/Operation.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dto/operation/mgt/Operation.java index 03acfa9a11..74bf820ca7 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dto/operation/mgt/Operation.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dto/operation/mgt/Operation.java @@ -30,7 +30,7 @@ public class Operation implements Serializable { } public enum Status { - IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW + IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW, REQUIRED_CONFIRMATION, CONFIRMED } public enum Control { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementDataHolder.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementDataHolder.java index 818df70b49..5cb1a1c9ab 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementDataHolder.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementDataHolder.java @@ -34,6 +34,7 @@ import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManag import org.wso2.carbon.device.mgt.core.dto.DeviceType; import org.wso2.carbon.device.mgt.core.dto.DeviceTypeServiceIdentifier; import org.wso2.carbon.device.mgt.core.geo.task.GeoFenceEventOperationManager; +import org.wso2.carbon.device.mgt.core.operation.timeout.task.OperationTimeoutTaskManagerService; import org.wso2.carbon.device.mgt.core.privacy.PrivacyComplianceProvider; import org.wso2.carbon.device.mgt.core.push.notification.mgt.PushNotificationProviderRepository; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; @@ -81,6 +82,7 @@ public class DeviceManagementDataHolder { private GeoLocationProviderService geoLocationProviderService; private GeoFenceEventOperationManager geoFenceEventOperationManager; private ExecutorService eventConfigExecutors; + private OperationTimeoutTaskManagerService operationTimeoutTaskManagerService; private final Map deviceStatusTaskPluginConfigs = Collections.synchronizedMap( new HashMap<>()); @@ -338,4 +340,13 @@ public class DeviceManagementDataHolder { public void setEventConfigExecutors(ExecutorService eventConfigExecutors) { this.eventConfigExecutors = eventConfigExecutors; } + + public OperationTimeoutTaskManagerService getOperationTimeoutTaskManagerService() { + return operationTimeoutTaskManagerService; + } + + public void setOperationTimeoutTaskManagerService( + OperationTimeoutTaskManagerService operationTimeoutTaskManagerService) { + this.operationTimeoutTaskManagerService = operationTimeoutTaskManagerService; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceTaskManagerServiceComponent.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceTaskManagerServiceComponent.java index 489a29c72c..a7cba70a35 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceTaskManagerServiceComponent.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceTaskManagerServiceComponent.java @@ -26,7 +26,12 @@ import org.wso2.carbon.device.mgt.common.DeviceStatusTaskPluginConfig; import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig; import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; import org.wso2.carbon.device.mgt.core.config.DeviceManagementConfig; +import org.wso2.carbon.device.mgt.core.config.operation.timeout.OperationTimeout; +import org.wso2.carbon.device.mgt.core.config.operation.timeout.OperationTimeoutConfiguration; import org.wso2.carbon.device.mgt.core.dto.DeviceType; +import org.wso2.carbon.device.mgt.core.operation.timeout.task.OperationTimeoutTaskException; +import org.wso2.carbon.device.mgt.core.operation.timeout.task.OperationTimeoutTaskManagerService; +import org.wso2.carbon.device.mgt.core.operation.timeout.task.impl.OperationTimeoutTaskManagerServiceImpl; import org.wso2.carbon.device.mgt.core.status.task.DeviceStatusTaskException; import org.wso2.carbon.device.mgt.core.status.task.DeviceStatusTaskManagerService; import org.wso2.carbon.device.mgt.core.status.task.impl.DeviceStatusTaskManagerServiceImpl; @@ -66,6 +71,10 @@ public class DeviceTaskManagerServiceComponent { if (deviceManagementConfig != null && deviceManagementConfig.getDeviceStatusTaskConfig().isEnabled()) { startDeviceStatusMonitoringTask(componentContext.getBundleContext()); } + + if (deviceManagementConfig != null && deviceManagementConfig.getOperationTimeoutConfiguration() != null) { + startOperationTimeoutTask(componentContext.getBundleContext()); + } } catch (Throwable e) { log.error("Error occurred while initializing device task manager service.", e); } @@ -102,6 +111,27 @@ public class DeviceTaskManagerServiceComponent { } } + private void startOperationTimeoutTask(BundleContext bundleContext) { + OperationTimeoutTaskManagerService operationTimeoutTaskManagerService = + new OperationTimeoutTaskManagerServiceImpl(); + DeviceManagementDataHolder.getInstance().setOperationTimeoutTaskManagerService( + operationTimeoutTaskManagerService); + bundleContext.registerService(OperationTimeoutTaskManagerService.class, + operationTimeoutTaskManagerService, null); + + OperationTimeoutConfiguration configuration = deviceManagementConfig.getOperationTimeoutConfiguration(); + + for (OperationTimeout operationTimeout : configuration.getOperationTimeoutList()) { + try { + operationTimeoutTaskManagerService.startTask(operationTimeout); + } catch (OperationTimeoutTaskException e) { + log.error("Error while starting the operation timeout task for device type (s) : " + + operationTimeout.getDeviceTypes() + ", operation code : " + + operationTimeout.getInitialStatus()); + } + } + } + @SuppressWarnings("unused") protected void deactivate(ComponentContext componentContext) { try { @@ -109,6 +139,9 @@ public class DeviceTaskManagerServiceComponent { if (deviceManagementConfig != null && deviceManagementConfig.getDeviceStatusTaskConfig().isEnabled()) { stopDeviceStatusMonitoringTask(); } + if (deviceManagementConfig != null && deviceManagementConfig.getOperationTimeoutConfiguration() != null) { + stopOperationTimeoutTask(); + } } catch (Throwable e) { log.error("Error occurred while shutting down device task manager service.", e); } @@ -143,6 +176,22 @@ public class DeviceTaskManagerServiceComponent { } } + private void stopOperationTimeoutTask() { + OperationTimeoutTaskManagerService operationTimeoutTaskManagerService = + DeviceManagementDataHolder.getInstance().getOperationTimeoutTaskManagerService(); + OperationTimeoutConfiguration configuration = deviceManagementConfig.getOperationTimeoutConfiguration(); + + for (OperationTimeout operationTimeout : configuration.getOperationTimeoutList()) { + try { + operationTimeoutTaskManagerService.stopTask(operationTimeout); + } catch (OperationTimeoutTaskException e) { + log.error("Error while stopping the operation timeout task for device type (s) : " + + operationTimeout.getDeviceTypes() + ", operation code : " + + operationTimeout.getInitialStatus()); + } + } + } + protected void setTaskService(TaskService taskService) { if (log.isDebugEnabled()) { log.debug("Setting the task service."); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java index 32615eb5e2..27882f4fbd 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java @@ -1587,4 +1587,19 @@ public class OperationManagerImpl implements OperationManager { OperationManagementDAOFactory.closeConnection(); } } + + @Override + public List getActivities(List deviceTypes, String operationCode, long updatedSince, String operationStatus) + throws OperationManagementException { + try { + OperationManagementDAOFactory.openConnection(); + return operationDAO.getActivities(deviceTypes, operationCode, updatedSince, operationStatus); + } catch (SQLException e) { + throw new OperationManagementException("Error occurred while opening a connection to the data source.", e); + } catch (OperationManagementDAOException e) { + throw new OperationManagementException("Error occurred while getting the activity list.", e); + } finally { + OperationManagementDAOFactory.closeConnection(); + } + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationDAO.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationDAO.java index 26e4f20ef8..6d54790c02 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationDAO.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationDAO.java @@ -101,6 +101,9 @@ public interface OperationDAO { Map> getOperationMappingsByStatus(Operation.Status opStatus, Operation.PushNotificationStatus pushNotificationStatus, int limit) throws OperationManagementDAOException; + List getActivities(List deviceTypes, String operationCode, long updatedSince, String operationStatus) + throws OperationManagementDAOException; + List getActivities(ActivityPaginationRequest activityPaginationRequest) throws OperationManagementDAOException; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/GenericOperationDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/GenericOperationDAOImpl.java index 55fb0d16ae..f6f7e4b0cc 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/GenericOperationDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/GenericOperationDAOImpl.java @@ -1173,10 +1173,10 @@ public class GenericOperationDAOImpl implements OperationDAO { Operation operation = null; try { Connection conn = OperationManagementDAOFactory.getConnection(); - String sql = "SELECT o.ID, o.TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, om.STATUS, o.OPERATION_CODE, " + - "om.ID AS OM_MAPPING_ID, " + + String sql = "SELECT o.ID, o.TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, om.STATUS, " + + "o.OPERATION_CODE, o.INITIATED_BY, om.ID AS OM_MAPPING_ID, " + "om.UPDATED_TIMESTAMP FROM (SELECT ID, TYPE, CREATED_TIMESTAMP, RECEIVED_TIMESTAMP," + - "OPERATION_CODE FROM DM_OPERATION WHERE id = ?) o INNER JOIN (SELECT * FROM " + + "OPERATION_CODE, INITIATED_BY FROM DM_OPERATION WHERE id = ?) o INNER JOIN (SELECT * FROM " + "DM_ENROLMENT_OP_MAPPING dm where dm.OPERATION_ID = ? AND dm.ENROLMENT_ID = ?) om " + "ON o.ID = om.OPERATION_ID "; stmt = conn.prepareStatement(sql); @@ -1198,6 +1198,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); OperationDAOUtil.setActivityId(operation, rs.getInt("ID")); } } catch (SQLException e) { @@ -1215,11 +1216,11 @@ public class GenericOperationDAOImpl implements OperationDAO { PreparedStatement stmt = null; ResultSet rs = null; Operation operation; - List operations = new ArrayList(); + List operations = new ArrayList<>(); try { Connection conn = OperationManagementDAOFactory.getConnection(); - String sql = "SELECT o.ID, TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, o.OPERATION_CODE, om.ID AS OM_MAPPING_ID," + - "om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + + String sql = "SELECT o.ID, TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, o.OPERATION_CODE, " + + "o.INITIATED_BY, om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + "INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + "WHERE dm.ENROLMENT_ID = ? AND dm.STATUS = ?) om ON o.ID = om.OPERATION_ID ORDER BY o.CREATED_TIMESTAMP DESC"; stmt = conn.prepareStatement(sql); @@ -1239,6 +1240,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); operation.setStatus(status); OperationDAOUtil.setActivityId(operation, rs.getInt("ID")); operations.add(operation); @@ -1263,7 +1265,7 @@ public class GenericOperationDAOImpl implements OperationDAO { try { Connection conn = OperationManagementDAOFactory.getConnection(); String sql = "SELECT o.ID, TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, o.OPERATION_CODE, " + - "om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + + "o.INITIATED_BY, om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + "INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + "WHERE dm.ENROLMENT_ID = ? AND dm.STATUS = ?) om ON o.ID = om.OPERATION_ID ORDER BY " + "o.CREATED_TIMESTAMP DESC LIMIT ?,?"; @@ -1286,6 +1288,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); operation.setStatus(status); OperationDAOUtil.setActivityId(operation, rs.getInt("OM_MAPPING_ID")); operations.add(operation); @@ -1309,8 +1312,8 @@ public class GenericOperationDAOImpl implements OperationDAO { try { Connection conn = OperationManagementDAOFactory.getConnection(); String sql = "SELECT o.ID, o.TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, " + - "o.OPERATION_CODE, om.STATUS, om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + - "INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + + "o.OPERATION_CODE, o.INITIATED_BY, om.STATUS, om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP " + + "FROM DM_OPERATION o INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + "WHERE dm.ENROLMENT_ID = ?) om ON o.ID = om.OPERATION_ID " + "ORDER BY o.CREATED_TIMESTAMP DESC, o.ID DESC"; stmt = conn.prepareStatement(sql); @@ -1329,6 +1332,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); operation.setStatus(Operation.Status.valueOf(rs.getString("STATUS"))); operations.add(operation); } @@ -1345,7 +1349,7 @@ public class GenericOperationDAOImpl implements OperationDAO { public List getOperationsForDevice(int enrolmentId, PaginationRequest request) throws OperationManagementDAOException { Operation operation; - List operations = new ArrayList(); + List operations = new ArrayList<>(); String createdTo = null; String createdFrom = null; DateFormat simple = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); @@ -1363,74 +1367,75 @@ public class GenericOperationDAOImpl implements OperationDAO { Long updatedTo = request.getOperationLogFilters().getUpdatedDayTo(); List operationCode = request.getOperationLogFilters().getOperationCode(); List status = request.getOperationLogFilters().getStatus(); - String sql = "SELECT " + - "o.ID, " + - "TYPE, " + - "o.CREATED_TIMESTAMP, " + - "o.RECEIVED_TIMESTAMP, " + - "o.OPERATION_CODE, " + - "om.STATUS, " + - "om.ID AS OM_MAPPING_ID, " + - "om.UPDATED_TIMESTAMP " + - "FROM " + - "DM_OPERATION o " + - "INNER JOIN " + - "(SELECT dm.OPERATION_ID, " + - "dm.ID, " + - "dm.STATUS, " + - "dm.UPDATED_TIMESTAMP " + - "FROM " + - "DM_ENROLMENT_OP_MAPPING dm " + - "WHERE " + - "dm.ENROLMENT_ID = ?"; + StringBuilder sql = new StringBuilder("SELECT " + + "o.ID, " + + "TYPE, " + + "o.CREATED_TIMESTAMP, " + + "o.RECEIVED_TIMESTAMP, " + + "o.OPERATION_CODE, " + + "o.INITIATED_BY, " + + "om.STATUS, " + + "om.ID AS OM_MAPPING_ID, " + + "om.UPDATED_TIMESTAMP " + + "FROM " + + "DM_OPERATION o " + + "INNER JOIN " + + "(SELECT dm.OPERATION_ID, " + + "dm.ID, " + + "dm.STATUS, " + + "dm.UPDATED_TIMESTAMP " + + "FROM " + + "DM_ENROLMENT_OP_MAPPING dm " + + "WHERE " + + "dm.ENROLMENT_ID = ?"); if (updatedFrom != null && updatedFrom != 0 && updatedTo != null && updatedTo != 0) { - sql = sql + " AND dm.UPDATED_TIMESTAMP BETWEEN ? AND ?"; + sql.append(" AND dm.UPDATED_TIMESTAMP BETWEEN ? AND ?"); isUpdatedDayProvided = true; } - sql = sql + ") om ON o.ID = om.OPERATION_ID "; + sql.append(") om ON o.ID = om.OPERATION_ID "); if (createdFrom != null && !createdFrom.isEmpty() && createdTo != null && !createdTo.isEmpty()) { - sql = sql + " WHERE o.CREATED_TIMESTAMP BETWEEN ? AND ?"; + sql.append(" WHERE o.CREATED_TIMESTAMP BETWEEN ? AND ?"); isCreatedDayProvided = true; } if ((isCreatedDayProvided) && (status != null && !status.isEmpty())) { int size = status.size(); - sql = sql + " AND (om.STATUS = ? "; + sql.append(" AND (om.STATUS = ? "); for (int i = 0; i < size - 1; i++) { - sql = sql + " OR om.STATUS = ?"; + sql.append(" OR om.STATUS = ?"); } - sql = sql + ")"; + sql.append(")"); isStatusProvided = true; } else if ((!isCreatedDayProvided) && (status != null && !status.isEmpty())) { int size = status.size(); - sql = sql + " WHERE (om.STATUS = ? "; + sql.append(" WHERE (om.STATUS = ? "); for (int i = 0; i < size - 1; i++) { - sql = sql + " OR om.STATUS = ?"; + sql.append(" OR om.STATUS = ?"); } - sql = sql + ")"; + sql.append(")"); isStatusProvided = true; } if ((isCreatedDayProvided || isStatusProvided) && (operationCode != null && !operationCode.isEmpty())) { int size = operationCode.size(); - sql = sql + " AND (o.OPERATION_CODE = ? "; + sql.append(" AND (o.OPERATION_CODE = ? "); for (int i = 0; i < size - 1; i++) { - sql = sql + " OR o.OPERATION_CODE = ?"; + sql.append(" OR o.OPERATION_CODE = ?"); } - sql = sql + ")"; + sql.append(")"); isOperationCodeProvided = true; } else if ((!isCreatedDayProvided && !isStatusProvided) && (operationCode != null && !operationCode.isEmpty())) { int size = operationCode.size(); - sql = sql + " WHERE (o.OPERATION_CODE = ? "; + sql.append(" WHERE (o.OPERATION_CODE = ? "); for (int i = 0; i < size - 1; i++) { - sql = sql + " OR o.OPERATION_CODE = ?"; + sql.append(" OR o.OPERATION_CODE = ?"); } - sql = sql + ")"; + sql.append(")"); isOperationCodeProvided = true; } - sql = sql + " ORDER BY o.CREATED_TIMESTAMP DESC LIMIT ?,?"; + sql.append(" ORDER BY o.CREATED_TIMESTAMP DESC LIMIT ?,?"); try { Connection conn = OperationManagementDAOFactory.getConnection(); - try (PreparedStatement stmt = conn.prepareStatement(sql)) { + try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) { int paramIndex = 1; stmt.setInt(paramIndex++, enrolmentId); if (isUpdatedDayProvided) { @@ -1442,15 +1447,13 @@ public class GenericOperationDAOImpl implements OperationDAO { stmt.setString(paramIndex++, createdTo); } if (isStatusProvided) { - int size = status.size(); - for (int i = 0; i < size; i++) { - stmt.setString(paramIndex++, status.get(i)); + for (String s : status) { + stmt.setString(paramIndex++, s); } } if (isOperationCodeProvided) { - int size = operationCode.size(); - for (int i = 0; i < size; i++) { - stmt.setString(paramIndex++, operationCode.get(i)); + for (String s : operationCode) { + stmt.setString(paramIndex++, s); } } stmt.setInt(paramIndex++, request.getStartIndex()); @@ -1468,6 +1471,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); operation.setStatus(Operation.Status.valueOf(rs.getString("STATUS"))); OperationDAOUtil.setActivityId(operation, rs.getInt("ID")); operations.add(operation); @@ -1594,7 +1598,7 @@ public class GenericOperationDAOImpl implements OperationDAO { try { Connection connection = OperationManagementDAOFactory.getConnection(); stmt = connection.prepareStatement("SELECT o.ID, o.TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, " + - "o.OPERATION_CODE, om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + + "o.OPERATION_CODE, o.INITIATED_BY, om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + "INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + "WHERE dm.ENROLMENT_ID = ? AND dm.STATUS = ?) om ON o.ID = om.OPERATION_ID " + "ORDER BY om.UPDATED_TIMESTAMP ASC, om.ID ASC LIMIT 1"); @@ -1616,6 +1620,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); operation.setStatus(Operation.Status.PENDING); OperationDAOUtil.setActivityId(operation, rs.getInt("ID")); } @@ -1636,10 +1641,10 @@ public class GenericOperationDAOImpl implements OperationDAO { List operations = new ArrayList<>(); try { Connection conn = OperationManagementDAOFactory.getConnection(); - String sql = "SELECT o.ID, TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, OPERATION_CODE, om.ID AS OM_MAPPING_ID, " + - "om.UPDATED_TIMESTAMP FROM (SELECT o.ID, TYPE, CREATED_TIMESTAMP, RECEIVED_TIMESTAMP, OPERATION_CODE " + - "FROM DM_OPERATION o WHERE o.TYPE = ?) o " + - "INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + + String sql = "SELECT o.ID, TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, OPERATION_CODE, o.INITIATED_BY," + + " om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM " + + "(SELECT o.ID, TYPE, CREATED_TIMESTAMP, RECEIVED_TIMESTAMP, OPERATION_CODE, INITIATED_BY " + + "FROM DM_OPERATION o WHERE o.TYPE = ?) o INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + "WHERE dm.ENROLMENT_ID = ? AND dm.STATUS = ?) om ON o.ID = om.OPERATION_ID ORDER BY o.CREATED_TIMESTAMP ASC"; stmt = conn.prepareStatement(sql); @@ -1660,6 +1665,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); OperationDAOUtil.setActivityId(operation, rs.getInt("ID")); operations.add(operation); } @@ -1717,6 +1723,141 @@ public class GenericOperationDAOImpl implements OperationDAO { return operationMappingsTenantMap; } + + public List getActivities(List deviceTypes, String operationCode, long updatedSince, String operationStatus) + throws OperationManagementDAOException { + try { + + Connection conn = OperationManagementDAOFactory.getConnection(); + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + StringBuilder sql = new StringBuilder("SELECT " + + " eom.ENROLMENT_ID," + + " eom.CREATED_TIMESTAMP," + + " eom.UPDATED_TIMESTAMP," + + " eom.OPERATION_ID," + + " eom.OPERATION_CODE," + + " eom.INITIATED_BY," + + " eom.TYPE," + + " eom.STATUS," + + " eom.DEVICE_ID," + + " eom.DEVICE_IDENTIFICATION," + + " eom.DEVICE_TYPE," + + " opr.ID AS OP_RES_ID," + + " opr.RECEIVED_TIMESTAMP," + + " opr.OPERATION_RESPONSE," + + " opr.IS_LARGE_RESPONSE " + + "FROM " + + " DM_ENROLMENT_OP_MAPPING eom " + + "LEFT JOIN " + + " DM_DEVICE_OPERATION_RESPONSE opr ON opr.EN_OP_MAP_ID = eom.ID " + + "INNER JOIN " + + " (SELECT DISTINCT OPERATION_ID FROM DM_ENROLMENT_OP_MAPPING WHERE TENANT_ID = ? "); + + + if (deviceTypes != null && !deviceTypes.isEmpty()) { + sql.append("AND DEVICE_TYPE IN ("); + for (int i = 0; i < deviceTypes.size() - 1; i++) { + sql.append("?, "); + } + sql.append("?) "); + } + + if (operationCode != null) { + sql.append("AND OPERATION_CODE = ? "); + } + + if (updatedSince != 0) { + sql.append("AND UPDATED_TIMESTAMP < ? "); + } + + if (operationStatus != null) { + sql.append("AND STATUS = ? "); + } + + sql.append("ORDER BY OPERATION_ID ASC ) eom_ordered " + + "ON eom_ordered.OPERATION_ID = eom.OPERATION_ID WHERE eom.TENANT_ID = ? "); + + if (deviceTypes != null && !deviceTypes.isEmpty()) { + sql.append("AND DEVICE_TYPE IN ("); + for (int i = 0; i < deviceTypes.size() - 1; i++) { + sql.append("?, "); + } + sql.append("?) "); + } + + if (operationCode != null) { + sql.append("AND eom.OPERATION_CODE = ? "); + } + + if (updatedSince != 0) { + sql.append("AND eom.UPDATED_TIMESTAMP < ? "); + } + + if (operationStatus != null) { + sql.append("AND eom.STATUS = ? "); + } + + sql.append("ORDER BY eom.OPERATION_ID, eom.UPDATED_TIMESTAMP"); + + int index = 1; + try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) { + stmt.setInt(index++, tenantId); + + if (deviceTypes != null && !deviceTypes.isEmpty()) { + for (String deviceId : deviceTypes) { + stmt.setString(index++, deviceId); + } + } + + if (operationCode != null) { + stmt.setString(index++, operationCode); + } + + if (updatedSince != 0) { + stmt.setLong(index++, updatedSince); + } + + if (operationStatus != null) { + stmt.setString(index++, operationStatus); + } + + stmt.setInt(index++, tenantId); + + if (deviceTypes != null && !deviceTypes.isEmpty()) { + for (String deviceId : deviceTypes) { + stmt.setString(index++, deviceId); + } + } + + if (operationCode != null) { + stmt.setString(index++, operationCode); + } + + if (updatedSince != 0) { + stmt.setLong(index++, updatedSince); + } + + if (operationStatus != null) { + stmt.setString(index, operationStatus); + } + + try (ResultSet rs = stmt.executeQuery()) { + ActivityHolder activityHolder = OperationDAOUtil.getActivityHolder(rs); + List largeResponseIDs = activityHolder.getLargeResponseIDs(); + List activities = activityHolder.getActivityList(); + if (!largeResponseIDs.isEmpty()) { + populateLargeOperationResponses(activities, largeResponseIDs); + } + return activities; + } + } + } catch (SQLException e) { + String msg = "Error occurred while getting the operation details from the database."; + log.error(msg, e); + throw new OperationManagementDAOException(msg, e); + } + } + @Override public List getActivities(ActivityPaginationRequest activityPaginationRequest) throws OperationManagementDAOException { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/OperationTimeoutTaskException.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/OperationTimeoutTaskException.java new file mode 100644 index 0000000000..c6a561ca3b --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/OperationTimeoutTaskException.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.device.mgt.core.operation.timeout.task; + +/** + * This exception class defines the custom exceptions thrown by the OperationTimeoutTask related components. + */ +public class OperationTimeoutTaskException extends Exception { + + private static final long serialVersionUID = -31222242646464497L; + + private String errorMessage; + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public OperationTimeoutTaskException(String msg, Exception nestedEx) { + super(msg, nestedEx); + setErrorMessage(msg); + } + + public OperationTimeoutTaskException(String message, Throwable cause) { + super(message, cause); + setErrorMessage(message); + } + + public OperationTimeoutTaskException(String msg) { + super(msg); + setErrorMessage(msg); + } + + public OperationTimeoutTaskException() { + super(); + } + + public OperationTimeoutTaskException(Throwable cause) { + super(cause); + } + +} + diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/OperationTimeoutTaskManagerService.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/OperationTimeoutTaskManagerService.java new file mode 100644 index 0000000000..f605fd3ee6 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/OperationTimeoutTaskManagerService.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.device.mgt.core.operation.timeout.task; + +import org.wso2.carbon.device.mgt.core.config.operation.timeout.OperationTimeout; + +/** + * This interface defines the methods that should be implemented by the management service of OperationTimeoutTask + */ +public interface OperationTimeoutTaskManagerService { + + /** + * This method will start the task + * @param config + * @throws OperationTimeoutTaskException + */ + void startTask(OperationTimeout config) + throws OperationTimeoutTaskException; + + /** + * This method will stop the task. + * @param config + * @throws OperationTimeoutTaskException + */ + void stopTask(OperationTimeout config) + throws OperationTimeoutTaskException; + + /** + * This will update the task frequency which it runs. + * + * @param config + * @throws OperationTimeoutTaskException + */ + void updateTask(OperationTimeout config) + throws OperationTimeoutTaskException; + + /** + * This will check weather the task is scheduled. + * @return + * @throws OperationTimeoutTaskException + */ + boolean isTaskScheduled(OperationTimeout config) throws OperationTimeoutTaskException; +} \ No newline at end of file diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/impl/OperationTimeoutTask.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/impl/OperationTimeoutTask.java new file mode 100644 index 0000000000..2ee7e32032 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/impl/OperationTimeoutTask.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.operation.timeout.task.impl; + +import com.google.gson.Gson; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; +import org.wso2.carbon.device.mgt.common.operation.mgt.Activity; +import org.wso2.carbon.device.mgt.common.operation.mgt.ActivityStatus; +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.config.operation.timeout.OperationTimeout; +import org.wso2.carbon.device.mgt.core.dto.DeviceType; +import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder; +import org.wso2.carbon.device.mgt.core.task.impl.DynamicPartitionedScheduleTask; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class OperationTimeoutTask extends DynamicPartitionedScheduleTask { + + private static final Log log = LogFactory.getLog(OperationTimeoutTask.class); + private OperationTimeout operationTimeoutConfig; + + @Override + public void setProperties(Map properties) { + super.setProperties(properties); + String operationTimeoutTaskConfigStr = properties + .get(OperationTimeoutTaskManagerServiceImpl.OPERATION_TIMEOUT_TASK_CONFIG); + Gson gson = new Gson(); + operationTimeoutConfig = gson.fromJson(operationTimeoutTaskConfigStr, OperationTimeout.class); + } + + @Override + public String getProperty(String name) { + return super.getProperty(name); + } + + @Override + public void refreshContext() { + super.refreshContext(); + } + + @Override + protected void setup() { + + } + + @Override + protected void executeDynamicTask() { + try { + + long timeMillis = System.currentTimeMillis() - operationTimeoutConfig.getTimeout() * 60 * 1000; + List deviceTypes = new ArrayList<>(); + if (operationTimeoutConfig.getDeviceTypes().size() == 1 && + "ALL".equals(operationTimeoutConfig.getDeviceTypes().get( 0))) { + try { + List deviceTypeList = DeviceManagementDataHolder.getInstance() + .getDeviceManagementProvider().getDeviceTypes(); + for (DeviceType deviceType : deviceTypeList) { + deviceTypes.add(deviceType.getName()); + } + } catch (DeviceManagementException e) { + log.error("Error occurred while reading device types", e); + } + } else { + deviceTypes = operationTimeoutConfig.getDeviceTypes(); + } + List activities = DeviceManagementDataHolder.getInstance().getOperationManager() + .getActivities(deviceTypes, operationTimeoutConfig.getCode(), timeMillis, + operationTimeoutConfig.getInitialStatus()); + for (Activity activity : activities) { + for (ActivityStatus activityStatus : activity.getActivityStatus()) { + String operationId = activity.getActivityId().replace("ACTIVITY_", ""); + Operation operation = DeviceManagementDataHolder.getInstance().getOperationManager() + .getOperation(Integer.parseInt(operationId)); + operation.setStatus(Operation.Status.valueOf(operationTimeoutConfig.getNextStatus())); + DeviceManagementDataHolder.getInstance().getOperationManager() + .updateOperation(activityStatus.getDeviceIdentifier(), operation); + } + } + + } catch (OperationManagementException e) { + String msg = "Error occurred while retrieving operations."; + log.error(msg, e); + } + } + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/impl/OperationTimeoutTaskManagerServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/impl/OperationTimeoutTaskManagerServiceImpl.java new file mode 100644 index 0000000000..b438b9215a --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/impl/OperationTimeoutTaskManagerServiceImpl.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.operation.timeout.task.impl; + +import com.google.gson.Gson; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.core.config.operation.timeout.OperationTimeout; +import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder; +import org.wso2.carbon.device.mgt.core.operation.timeout.task.OperationTimeoutTaskException; +import org.wso2.carbon.device.mgt.core.operation.timeout.task.OperationTimeoutTaskManagerService; +import org.wso2.carbon.ntask.common.TaskException; +import org.wso2.carbon.ntask.core.TaskInfo; +import org.wso2.carbon.ntask.core.TaskManager; +import org.wso2.carbon.ntask.core.service.TaskService; + +import java.util.HashMap; +import java.util.Map; + +public class OperationTimeoutTaskManagerServiceImpl implements OperationTimeoutTaskManagerService { + + private static final Log log = LogFactory.getLog(OperationTimeoutTaskManagerServiceImpl.class); + + public static final String OPERATION_TIMEOUT_TASK = "OPERATION_TIMEOUT_TASK"; + static final String DEVICE_TYPES = "DEVICE_TYPES"; + static final String OPERATION_TIMEOUT_TASK_CONFIG = "OPERATION_TIMEOUT_TASK_CONFIG"; + static final String INITIAL_STATUS = "INITIAL_STATUS"; + private static final String TASK_CLASS = OperationTimeoutTask.class.getName(); + + @Override + public void startTask(OperationTimeout config) + throws OperationTimeoutTaskException { + log.info("Operation timeout task adding for device type(s) : " + config.getDeviceTypes() + + ", operation code : " + config.getInitialStatus()); + + try { + TaskService taskService = DeviceManagementDataHolder.getInstance().getTaskService(); + taskService.registerTaskType(OPERATION_TIMEOUT_TASK); + + if (log.isDebugEnabled()) { + log.debug("Operation timeout task is started for the device type(s) : " + config.getDeviceTypes() + + ", operation code : " + config.getInitialStatus()); + log.debug( + "Operation timeout task is at frequency of : " + config.getTimeout() + " minutes"); + } + + TaskManager taskManager = taskService.getTaskManager(OPERATION_TIMEOUT_TASK); + + TaskInfo.TriggerInfo triggerInfo = new TaskInfo.TriggerInfo(); + //Convert to milli seconds + triggerInfo.setIntervalMillis(config.getTimeout() * 60 * 1000); + triggerInfo.setRepeatCount(-1); + + Gson gson = new Gson(); + String operationTimeoutConfig = gson.toJson(config); + + Map properties = new HashMap<>(); + + String deviceTypes = StringUtils.join(config.getDeviceTypes(), "_"); + properties.put(DEVICE_TYPES, deviceTypes); + properties.put(INITIAL_STATUS, config.getInitialStatus()); + properties.put(OPERATION_TIMEOUT_TASK_CONFIG, operationTimeoutConfig); + + String taskName = OPERATION_TIMEOUT_TASK + "_" + config.getInitialStatus() + "_" + deviceTypes; + + if (!taskManager.isTaskScheduled(taskName)) { + TaskInfo taskInfo = new TaskInfo(taskName, TASK_CLASS, properties, triggerInfo); + taskManager.registerTask(taskInfo); + taskManager.rescheduleTask(taskInfo.getName()); + } else { + throw new OperationTimeoutTaskException( + "Operation Timeout task is already started for the device type(s) : " + config.getDeviceTypes() + + ", operation code : " + config.getInitialStatus()); + } + } catch (TaskException e) { + throw new OperationTimeoutTaskException("Error occurred while creating the Operation timeout task " + + "for the device type(s) : " + config.getDeviceTypes() + ", operation code : " + config + .getInitialStatus(), e); + } + } + + @Override + public void stopTask(OperationTimeout config) + throws OperationTimeoutTaskException { + try { + TaskService taskService = DeviceManagementDataHolder.getInstance().getTaskService(); + String deviceTypes = StringUtils.join(config.getDeviceTypes(), "_"); + String taskName = OPERATION_TIMEOUT_TASK + "_" + config.getInitialStatus() + "_" + deviceTypes; + if (taskService != null && taskService.isServerInit()) { + TaskManager taskManager = taskService.getTaskManager(OPERATION_TIMEOUT_TASK); + taskManager.deleteTask(taskName); + } + } catch (TaskException e) { + throw new OperationTimeoutTaskException("Error occurred while deleting the Operation timeout task " + + "for the device type(s) : " + config.getDeviceTypes() + ", operation code : " + config + .getInitialStatus(), e); + } + } + + @Override + public void updateTask(OperationTimeout config) + throws OperationTimeoutTaskException { + try { + TaskService taskService = DeviceManagementDataHolder.getInstance().getTaskService(); + TaskManager taskManager = taskService.getTaskManager(OPERATION_TIMEOUT_TASK); + String deviceTypes = StringUtils.join(config.getDeviceTypes(), "_"); + String taskName = OPERATION_TIMEOUT_TASK + "_" + config.getInitialStatus() + "_" + deviceTypes; + + if (taskManager.isTaskScheduled(taskName)) { + taskManager.deleteTask(taskName); + TaskInfo.TriggerInfo triggerInfo = new TaskInfo.TriggerInfo(); + triggerInfo.setIntervalMillis(config.getTimeout() * 60 * 1000); + triggerInfo.setRepeatCount(-1); + + Map properties = new HashMap<>(); + properties.put(DEVICE_TYPES, deviceTypes); + properties.put(INITIAL_STATUS, config.getInitialStatus()); + + Gson gson = new Gson(); + String deviceStatusTaskConfigs = gson.toJson(config); + properties.put(OPERATION_TIMEOUT_TASK_CONFIG, deviceStatusTaskConfigs); + + TaskInfo taskInfo = new TaskInfo(taskName, TASK_CLASS, properties, triggerInfo); + + taskManager.registerTask(taskInfo); + taskManager.rescheduleTask(taskInfo.getName()); + } else { + throw new OperationTimeoutTaskException( + "Operation timeout task has not been started for this device-type the device type(s) : " + + config.getDeviceTypes() + ", operation code : " + config.getInitialStatus() + + ". Please start the task first."); + } + + } catch (TaskException e) { + throw new OperationTimeoutTaskException("Error occurred while updating the Operation timeout task " + + "for the device type(s) : " + config.getDeviceTypes() + ", operation code : " + + config.getInitialStatus(), e); + } + } + + @Override + public boolean isTaskScheduled(OperationTimeout config) throws OperationTimeoutTaskException { + String deviceTypes = StringUtils.join(config.getDeviceTypes(), "_"); + String taskName = OPERATION_TIMEOUT_TASK + "_" + config.getInitialStatus() + "_" + deviceTypes; + TaskService taskService = DeviceManagementDataHolder.getInstance().getTaskService(); + TaskManager taskManager; + try { + taskManager = taskService.getTaskManager(OPERATION_TIMEOUT_TASK); + return taskManager.isTaskScheduled(taskName); + } catch (TaskException e) { + throw new OperationTimeoutTaskException("Error occurred while checking the task schedule status " + + "of the Operation timeout task for the device type(s) : " + config.getDeviceTypes() + + ", operation code : " + config.getInitialStatus(), e); + } + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/Constants.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/Constants.java index 2be36d0acf..962082ce3a 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/Constants.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/Constants.java @@ -18,7 +18,14 @@ package org.wso2.carbon.device.mgt.core.report.mgt; +import org.wso2.carbon.utils.CarbonUtils; + +import java.io.File; + public class Constants { + public static final String DEFAULT_CONFIG_FILE_LOCATION = CarbonUtils.getCarbonConfigDirPath() + File.separator + + Constants.REPORT_MGT_CONFIG_XML_FILE; + public static final String REPORT_MGT_CONFIG_XML_FILE = "reporting-mgt.xml"; // device types public static final String ANDROID = "android"; public static final String IOS = "ios"; @@ -29,5 +36,14 @@ public class Constants { // OS version value generating properties public static final int NUM_OF_OS_VERSION_DIGITS= 5; public static final int NUM_OF_OS_VERSION_POSITIONS = 3; + public static final String BASIC_AUTH_HEADER_PREFIX = "Basic "; + public static final String HTTP_PROTOCOL = "http"; + public static final String HTTPS_PROTOCOL = "https"; + public static final String WSS_PROTOCOL = "wss"; + public static final String WS_PROTOCOL = "ws"; + public static final String SCHEME_SEPARATOR = "://"; + public static final String URI_SEPARATOR = "/"; + public static final String URI_QUERY_SEPARATOR = "?"; + public static final String COLON = ":"; } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/config/ReportMgtConfiguration.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/config/ReportMgtConfiguration.java new file mode 100644 index 0000000000..6c736e8679 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/config/ReportMgtConfiguration.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.report.mgt.config; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Represents the Application Management Configuration. + */ +@XmlRootElement(name = "ReportManagementConfiguration") +public class ReportMgtConfiguration { + + private String datasourceName; + + @XmlElement(name = "DatasourceName", required = true) + public String getDatasourceName() { + return datasourceName; + } + + public void setDatasourceName(String datasourceName) { + this.datasourceName = datasourceName; + } + +} + diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/config/ReportMgtConfigurationManager.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/config/ReportMgtConfigurationManager.java new file mode 100644 index 0000000000..e47ba7cc84 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/config/ReportMgtConfigurationManager.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.report.mgt.config; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.exceptions.InvalidConfigurationException; +import org.wso2.carbon.device.mgt.common.exceptions.ReportManagementException; +import org.wso2.carbon.device.mgt.core.report.mgt.Constants; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Unmarshaller; +import java.io.File; + +/** + * ConfigurationManager is responsible for the managing Application Management related configurations. + */ +public class ReportMgtConfigurationManager { + + private static final Log log = LogFactory.getLog(ReportMgtConfigurationManager.class); + + private ReportMgtConfiguration configuration; + + private static String configPath; + + private static volatile ReportMgtConfigurationManager configurationManager; + + private ReportMgtConfigurationManager() { + + } + + public static ReportMgtConfigurationManager getInstance() { + if (configurationManager == null) { + synchronized (ReportMgtConfigurationManager.class) { + if (configurationManager == null) { + configurationManager = new ReportMgtConfigurationManager(); + try { + configurationManager.initConfig(); + } catch (ReportManagementException e) { + log.error(e); + } + } + } + } + return configurationManager; + } + + public static synchronized void setConfigLocation(String configPath) throws InvalidConfigurationException { + if (ReportMgtConfigurationManager.configPath == null) { + ReportMgtConfigurationManager.configPath = configPath; + } else { + throw new InvalidConfigurationException("Configuration path " + configPath + " is already defined"); + } + } + + private void initConfig() throws ReportManagementException { + try { + JAXBContext jaxbContext = JAXBContext.newInstance(ReportMgtConfiguration.class); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + if (configPath == null) { + configPath = Constants.DEFAULT_CONFIG_FILE_LOCATION; + } + //TODO: Add validation for the configurations + this.configuration = (ReportMgtConfiguration) unmarshaller.unmarshal(new File(configPath)); + } catch (Exception e) { + log.error(e); + throw new InvalidConfigurationException("Error occurred while initializing application config: " + + configPath, e); + } + } + + public ReportMgtConfiguration getConfiguration() { + return configuration; + } + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/dao/common/ReportMgtConnectionManager.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/dao/common/ReportMgtConnectionManager.java new file mode 100644 index 0000000000..0d11461ef0 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/dao/common/ReportMgtConnectionManager.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.report.mgt.dao.common; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; +import org.wso2.carbon.device.mgt.common.exceptions.IllegalTransactionStateException; +import org.wso2.carbon.device.mgt.core.report.mgt.config.ReportMgtConfigurationManager; + +import javax.naming.InitialContext; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +/** + * This class intends to act as the primary entity that hides all DAO instantiation related complexities and logic so + * that the business objection handling layer doesn't need to be aware of the same providing seamless plug-ability of + * different data sources, connection acquisition mechanisms as well as different forms of DAO implementations to the + * high-level implementations that require Application management related metadata persistence. + */ +public class ReportMgtConnectionManager { + + private static final Log log = LogFactory.getLog(ReportMgtConnectionManager.class); + + private static final ThreadLocal currentConnection = new ThreadLocal<>(); + private static DataSource dataSource; + + static { + String dataSourceName = ReportMgtConfigurationManager.getInstance().getConfiguration().getDatasourceName(); + init(dataSourceName); + } + + public static void init(String datasourceName) { + resolveDataSource(datasourceName); + } + + public static String getDatabaseType() { + try { + return dataSource.getConnection().getMetaData().getDatabaseProductName(); + } catch (SQLException e) { + log.error("Error occurred while retrieving config.datasource connection", e); + } + return null; + } + + /** + * Resolve the datasource from the datasource definition. + * + * @param dataSourceName Name of the datasource + * @return DataSource resolved by the datasource name + */ + public static DataSource resolveDataSource(String dataSourceName) { + try { + dataSource = InitialContext.doLookup(dataSourceName); + } catch (Exception e) { + throw new RuntimeException("Error in looking up data source: " + e.getMessage(), e); + } + return dataSource; + } + + public static void openDBConnection() throws DBConnectionException { + Connection conn = currentConnection.get(); + if (conn != null) { + throw new IllegalTransactionStateException("Database connection has already been obtained."); + } + try { + conn = dataSource.getConnection(); + } catch (SQLException e) { + throw new DBConnectionException("Failed to get a database connection.", e); + } + currentConnection.set(conn); + } + + public static Connection getDBConnection() throws DBConnectionException { + if (dataSource == null) { + String dataSourceName = ReportMgtConfigurationManager.getInstance().getConfiguration().getDatasourceName(); + init(dataSourceName); + } + Connection conn = currentConnection.get(); + if (conn == null) { + try { + conn = dataSource.getConnection(); + currentConnection.set(conn); + } catch (SQLException e) { + throw new DBConnectionException("Failed to get database connection.", e); + } + } + return conn; + } + + + public static void closeDBConnection() { + Connection conn = currentConnection.get(); + if (conn == null) { + throw new IllegalTransactionStateException("Database connection is not active."); + } + try { + conn.close(); + } catch (SQLException e) { + log.error("Error occurred while closing the connection", e); + } + currentConnection.remove(); + } + + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java index 15d0fc2c4c..d7df2b286d 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java @@ -62,6 +62,7 @@ import org.wso2.carbon.device.mgt.common.policy.mgt.PolicyMonitoringManager; import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationExecutionFailedException; import org.wso2.carbon.device.mgt.common.push.notification.NotificationStrategy; import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dto.DeviceType; import org.wso2.carbon.device.mgt.core.dto.DeviceTypeVersion; @@ -702,6 +703,14 @@ public interface DeviceManagementProviderService { boolean setStatus(String currentOwner, EnrolmentInfo.Status status) throws DeviceManagementException; + List getDeviceStatusHistory(Device device) throws DeviceManagementException; + + List getDeviceStatusHistory(Device device, Date fromDate, Date toDate) throws DeviceManagementException; + + List getDeviceCurrentEnrolmentStatusHistory(Device device) throws DeviceManagementException; + + List getDeviceCurrentEnrolmentStatusHistory(Device device, Date fromDate, Date toDate) throws DeviceManagementException; + void notifyOperationToDevices(Operation operation, List deviceIds) throws DeviceManagementException; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java index 30d39b0ea1..c61871cae9 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java @@ -95,6 +95,7 @@ import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationExecu import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationSubscriber; import org.wso2.carbon.device.mgt.common.push.notification.NotificationStrategy; import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; import org.wso2.carbon.device.mgt.common.type.mgt.DeviceTypePlatformDetails; import org.wso2.carbon.device.mgt.common.type.mgt.DeviceTypePlatformVersion; import org.wso2.carbon.device.mgt.core.DeviceManagementConstants; @@ -103,12 +104,7 @@ import org.wso2.carbon.device.mgt.core.cache.DeviceCacheKey; import org.wso2.carbon.device.mgt.core.cache.impl.DeviceCacheManagerImpl; import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; import org.wso2.carbon.device.mgt.core.config.DeviceManagementConfig; -import org.wso2.carbon.device.mgt.core.dao.ApplicationDAO; -import org.wso2.carbon.device.mgt.core.dao.DeviceDAO; -import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; -import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; -import org.wso2.carbon.device.mgt.core.dao.DeviceTypeDAO; -import org.wso2.carbon.device.mgt.core.dao.EnrollmentDAO; +import org.wso2.carbon.device.mgt.core.dao.*; import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceDetailsMgtException; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager; @@ -158,6 +154,7 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv private final EnrollmentDAO enrollmentDAO; private final ApplicationDAO applicationDAO; private MetadataDAO metadataDAO; + private final DeviceStatusDAO deviceStatusDAO; public DeviceManagementProviderServiceImpl() { this.pluginRepository = new DeviceManagementPluginRepository(); @@ -166,6 +163,7 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv this.deviceTypeDAO = DeviceManagementDAOFactory.getDeviceTypeDAO(); this.enrollmentDAO = DeviceManagementDAOFactory.getEnrollmentDAO(); this.metadataDAO = MetadataManagementDAOFactory.getMetadataDAO(); + this.deviceStatusDAO = DeviceManagementDAOFactory.getDeviceStatusDAO(); /* Registering a listener to retrieve events when some device management service plugin is installed after * the component is done getting initialized */ @@ -1853,6 +1851,64 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv DeviceManagementDAOFactory.closeConnection(); } } + @Override + public List getDeviceStatusHistory(Device device, Date fromDate, Date toDate) throws DeviceManagementException{ + if (log.isDebugEnabled()) { + log.debug("get status history of device: " + device.getDeviceIdentifier()); + } + try { + int tenantId = this.getTenantId(); + return deviceStatusDAO.getStatus(device.getId(), tenantId, fromDate, toDate); + } catch (DeviceManagementDAOException e) { + DeviceManagementDAOFactory.rollbackTransaction(); + String msg = "Error occurred while retrieving status history"; + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } catch (Exception e) { + String msg = "Error occurred in retrieving status history for device :" + device.getDeviceIdentifier(); + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + } + + @Override + public List getDeviceCurrentEnrolmentStatusHistory(Device device, Date fromDate, Date toDate) throws DeviceManagementException{ + if (log.isDebugEnabled()) { + log.debug("get status history of device: " + device.getDeviceIdentifier()); + } + try { + int tenantId = this.getTenantId(); + EnrolmentInfo enrolmentInfo = device.getEnrolmentInfo(); + if (enrolmentInfo == null) { + enrolmentInfo = enrollmentDAO.getEnrollment(device.getId(), tenantId); + if (enrolmentInfo == null) { + String msg = "Error occurred in getting enrollment for device :" + device.getDeviceIdentifier(); + log.error(msg); + throw new DeviceManagementException(msg); + } + } + return deviceStatusDAO.getStatus(enrolmentInfo.getId(), fromDate, toDate); + } catch (DeviceManagementDAOException e) { + DeviceManagementDAOFactory.rollbackTransaction(); + String msg = "Error occurred while retrieving status history"; + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } catch (Exception e) { + String msg = "Error occurred in retrieving status history for current enrolment of device : " + device.getDeviceIdentifier(); + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + } + + @Override + public List getDeviceStatusHistory(Device device) throws DeviceManagementException{ + return getDeviceStatusHistory(device, null, null); + } + + @Override + public List getDeviceCurrentEnrolmentStatusHistory(Device device) throws DeviceManagementException{ + return getDeviceCurrentEnrolmentStatusHistory(device, null, null); + } @Override public boolean setStatus(String currentOwner, EnrolmentInfo.Status status) throws DeviceManagementException { @@ -1863,7 +1919,7 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv boolean success; int tenantId = this.getTenantId(); DeviceManagementDAOFactory.beginTransaction(); - success = enrollmentDAO.setStatus(currentOwner, status, tenantId); + success = enrollmentDAO.setStatusAllDevices(currentOwner, status, tenantId); DeviceManagementDAOFactory.commitTransaction(); return success; } catch (DeviceManagementDAOException e) { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/HttpReportingUtil.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/HttpReportingUtil.java index 920b4e4972..54877fdaa1 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/HttpReportingUtil.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/HttpReportingUtil.java @@ -17,6 +17,8 @@ package org.wso2.carbon.device.mgt.core.util; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; @@ -26,11 +28,11 @@ import org.apache.http.impl.client.HttpClients; import org.apache.http.protocol.HTTP; import org.wso2.carbon.device.mgt.common.exceptions.EventPublishingException; import org.wso2.carbon.device.mgt.core.DeviceManagementConstants; - import java.io.IOException; public class HttpReportingUtil { + private static final Log log = LogFactory.getLog(HttpReportingUtil.class); private static final String IS_EVENT_PUBLISHING_ENABLED = "isEventPublishingEnabled"; public static String getReportingHost() { @@ -52,6 +54,7 @@ public class HttpReportingUtil { } } + public static boolean isPublishingEnabledForTenant() { Object configuration = DeviceManagerUtil.getConfiguration(IS_EVENT_PUBLISHING_ENABLED); if (configuration != null) { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/common/BaseDeviceManagementTest.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/common/BaseDeviceManagementTest.java index d991f92957..2fc9e4122e 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/common/BaseDeviceManagementTest.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/common/BaseDeviceManagementTest.java @@ -158,6 +158,7 @@ public abstract class BaseDeviceManagementTest { PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(MultitenantConstants .SUPER_TENANT_DOMAIN_NAME); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(MultitenantConstants.SUPER_TENANT_ID); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername("admin"); } protected DataSourceConfig readDataSourceConfig(String configLocation) throws DeviceManagementException { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusPersistenceTests.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusPersistenceTests.java new file mode 100644 index 0000000000..65c22e1161 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusPersistenceTests.java @@ -0,0 +1,254 @@ +/* + * 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.core.dao; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.testng.Assert; +import org.testng.annotations.Test; +import org.testng.annotations.BeforeClass; +import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; +import org.wso2.carbon.device.mgt.core.common.BaseDeviceManagementTest; +import org.wso2.carbon.device.mgt.core.common.TestDataHolder; + +import java.sql.SQLException; +import java.util.*; +import java.util.List; +import java.util.stream.Stream; + +import static org.wso2.carbon.device.mgt.common.EnrolmentInfo.Status.*; + +public class DeviceStatusPersistenceTests extends BaseDeviceManagementTest { + + private static final Log log = LogFactory.getLog(DeviceStatusPersistenceTests.class); + private EnrollmentDAO enrollmentDAO = DeviceManagementDAOFactory.getEnrollmentDAO(); + private DeviceStatusDAO deviceStatusDAO = DeviceManagementDAOFactory.getDeviceStatusDAO(); + + /** + * Validate that the list of statuses received match the statuses + * @param device + * @param receivedStatus + * @param statuses + */ + private void validateDeviceStatus(Device device, List receivedStatus, EnrolmentInfo.Status[] statuses){ + Assert.assertEquals(receivedStatus.size(), statuses.length); + for(int i = 0; i < statuses.length; i++) { + Assert.assertEquals(receivedStatus.get(i).getDeviceId(), device.getId()); + Assert.assertEquals(receivedStatus.get(i).getStatus(), statuses[i]); + } + } + + /** + * Validate the list of statuses corresponds to different enrolments and their statuses + * @param device + * @param receivedStatus + * @param statuses + */ + private void validateDeviceStatus(Device device, List receivedStatus, EnrolmentInfo.Status[][] statuses){ + Map> statusMap = new HashMap<>(); + for (DeviceStatus deviceStatus: receivedStatus) { + Assert.assertEquals(deviceStatus.getDeviceId(), device.getId()); + if (!statusMap.containsKey(deviceStatus.getEnrolmentId())){ + statusMap.put(deviceStatus.getEnrolmentId(), new ArrayList<>()); + } + statusMap.get(deviceStatus.getEnrolmentId()).add(deviceStatus); + } + Assert.assertEquals(statusMap.size(), statuses.length); + Integer[] keys = (new TreeSet<>(statusMap.keySet())).toArray(new Integer[]{}); + for(int i = 0; i < keys.length; i++){ + validateDeviceStatus(device, statusMap.get(keys[i]), statuses[i]); + } + } + + @Test + public void testSingleDeviceOneEnrolmentOneStatus(){ + try { + this.initDataSource(); + Device device = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + addDevice(device); + + EnrolmentInfo.Status[] statuses = {ACTIVE}; + int enrolmentId = createNewEnrolmentAddStatuses(device, "admin", statuses); + +// DeviceManagementDAOFactory.openConnection(); + validateDeviceStatus(device, deviceStatusDAO.getStatus(enrolmentId), statuses); + validateDeviceStatus(device, deviceStatusDAO.getStatus(device.getId(), TestDataHolder.SUPER_TENANT_ID), + new EnrolmentInfo.Status[][]{statuses}); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while getting enrolment status", e); + } catch (Exception e) { + log.error("Error occurred while initializing datasource", e); +// } finally{ +// DeviceManagementDAOFactory.closeConnection(); + } + } + + @Test + public void testSingleDeviceOneEnrolmentMultipleStatus(){ + try { + this.initDataSource(); + Device device = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + addDevice(device); + EnrolmentInfo.Status[] statuses = {ACTIVE, ASSIGNED, CONFIGURED, READY_TO_CONNECT}; + int enrolmentId = createNewEnrolmentAddStatuses(device, "admin", statuses); +// DeviceManagementDAOFactory.openConnection(); + validateDeviceStatus(device, deviceStatusDAO.getStatus(enrolmentId),statuses); + validateDeviceStatus(device, deviceStatusDAO.getStatus(device.getId(), TestDataHolder.SUPER_TENANT_ID), + new EnrolmentInfo.Status[][]{statuses}); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while getting enrolment status", e); + } catch (Exception e) { + log.error("Error occurred while initializing datasource", e); +// } finally{ +// DeviceManagementDAOFactory.closeConnection(); + } + } + + @Test + public void testSingleDeviceMultipleEnrolmentMultipleStatus(){ + try { + this.initDataSource(); + Device device = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + addDevice(device); + + EnrolmentInfo.Status[] statuses1 = {ACTIVE, ASSIGNED, CONFIGURED, READY_TO_CONNECT}; + int enrolmentId1 = createNewEnrolmentAddStatuses(device, "admin1", statuses1); + + EnrolmentInfo.Status[] statuses2 = {CREATED, SUSPENDED, BLOCKED, DEFECTIVE, REMOVED, WARRANTY_REPLACED, BLOCKED}; + int enrolmentId2 = createNewEnrolmentAddStatuses(device, "admin2", statuses2); + + EnrolmentInfo.Status[] statuses3 = {READY_TO_CONNECT, ASSIGNED}; + addStatus(enrolmentId1, statuses3); + + EnrolmentInfo.Status[] statuses1_3 = Stream.concat(Arrays.stream(statuses1), Arrays.stream(statuses3)).toArray(EnrolmentInfo.Status[]::new); + + // introducing a delay so that data is committed to the database before we try to retrieve for validation +// Thread.sleep(5000); + +// DeviceManagementDAOFactory.openConnection(); + validateDeviceStatus(device, deviceStatusDAO.getStatus(enrolmentId1), statuses1_3); + validateDeviceStatus(device, deviceStatusDAO.getStatus(enrolmentId2), statuses2); + + validateDeviceStatus(device, deviceStatusDAO.getStatus(device.getId(), TestDataHolder.SUPER_TENANT_ID), + new EnrolmentInfo.Status[][]{statuses1_3, statuses2}); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while getting enrolment status", e); + } catch (Exception e) { + log.error("Error occurred while initializing data source", e); +// } finally{ +// DeviceManagementDAOFactory.closeConnection(); + } + } + + @Test + public void testSettingAllDeviceStatusOfSingleUser(){ + try { + this.initDataSource(); + Device device1 = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + addDevice(device1); + + Device device2 = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + addDevice(device2); + + EnrolmentInfo.Status[] statuses1 = {ACTIVE, ASSIGNED, CONFIGURED, READY_TO_CONNECT}; + int enrolmentId1 = createNewEnrolmentAddStatuses(device1, "admin1", statuses1); + + EnrolmentInfo.Status[] statuses2 = {CREATED, SUSPENDED, BLOCKED, DEFECTIVE, REMOVED, WARRANTY_REPLACED, BLOCKED}; + int enrolmentId2 = createNewEnrolmentAddStatuses(device1, "admin2", statuses2); + + EnrolmentInfo.Status[] statuses3 = {READY_TO_CONNECT, ASSIGNED}; + int enrolmentId3 = createNewEnrolmentAddStatuses(device2, "admin1", statuses3); + + enrollmentDAO.setStatusAllDevices("admin1", REMOVED, TestDataHolder.SUPER_TENANT_ID); + + EnrolmentInfo.Status[] statuses1_ = Stream.concat(Arrays.stream(statuses1), Arrays.stream(new EnrolmentInfo.Status[] {REMOVED})).toArray(EnrolmentInfo.Status[]::new); + EnrolmentInfo.Status[] statuses3_ = Stream.concat(Arrays.stream(statuses3), Arrays.stream(new EnrolmentInfo.Status[] {REMOVED})).toArray(EnrolmentInfo.Status[]::new); + + validateDeviceStatus(device1, deviceStatusDAO.getStatus(enrolmentId1), statuses1_); + validateDeviceStatus(device2, deviceStatusDAO.getStatus(enrolmentId3), statuses3_); + + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while getting enrolment status", e); + } catch (Exception e) { + log.error("Error occurred while initializing data source", e); + } + } + private int addDevice(Device device) throws DeviceManagementDAOException { + try { + DeviceManagementDAOFactory.openConnection(); + DeviceDAO deviceDAO = DeviceManagementDAOFactory.getDeviceDAO(); + int deviceId = deviceDAO.addDevice(TestDataHolder.initialTestDeviceType.getId(), device, TestDataHolder.SUPER_TENANT_ID); + device.setId(deviceId); + return deviceId; + } catch (SQLException e) { + throw new DeviceManagementDAOException("Error while adding device", e); + }finally{ + DeviceManagementDAOFactory.closeConnection(); + } + + } + private int addEnrolment(Device device, String owner, int tenantId, EnrolmentInfo.Status initialStatus) { + int deviceId = device.getId(); + EnrolmentInfo source = new EnrolmentInfo(owner, EnrolmentInfo.OwnerShip.BYOD, initialStatus); + try { + DeviceManagementDAOFactory.openConnection(); + EnrolmentInfo config = enrollmentDAO.addEnrollment(deviceId, source, tenantId); + device.setEnrolmentInfo(config); + return config.getId(); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while adding enrollment", e); + } finally { + DeviceManagementDAOFactory.closeConnection(); + } + return -1; + } + + private int createNewEnrolmentAddStatuses(Device device, String user, EnrolmentInfo.Status[] statuses) { + int enrolmentId = this.addEnrolment(device, user, TestDataHolder.SUPER_TENANT_ID, statuses[0]); + Assert.assertNotEquals(enrolmentId, -1); + addStatus(enrolmentId, Arrays.copyOfRange(statuses, 1, statuses.length)); + return enrolmentId; + } + + private void addStatus(int enrolmentId, EnrolmentInfo.Status[] statuses) { + for(int i = 0; i < statuses.length; i++) { + Assert.assertTrue(addStatus(enrolmentId, statuses[i], TestDataHolder.SUPER_TENANT_ID)); + } + } + + private boolean addStatus(int enrolmentId, EnrolmentInfo.Status status, int tenentId){ + try { + DeviceManagementDAOFactory.openConnection(); + return enrollmentDAO.setStatus(enrolmentId, status, tenentId); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while setting status", e); + } finally { + DeviceManagementDAOFactory.closeConnection(); + } + return false; + } + + @BeforeClass + @Override + public void init() throws Exception { + + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/EnrolmentPersistenceTests.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/EnrolmentPersistenceTests.java index 92647cea28..9497324798 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/EnrolmentPersistenceTests.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/EnrolmentPersistenceTests.java @@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.core.common.BaseDeviceManagementTest; import org.wso2.carbon.device.mgt.core.common.TestDataHolder; @@ -36,7 +37,7 @@ public class EnrolmentPersistenceTests extends BaseDeviceManagementTest { @Test public void testAddEnrolment() { - int deviceId = TestDataHolder.initialTestDevice.getId(); + int deviceId = -1; String owner = "admin"; /* Initializing source enrolment configuration bean to be tested */ @@ -47,6 +48,10 @@ public class EnrolmentPersistenceTests extends BaseDeviceManagementTest { /* Adding dummy enrolment configuration to the device management metadata store */ try { DeviceManagementDAOFactory.openConnection(); + Device device = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + DeviceDAO deviceDAO = DeviceManagementDAOFactory.getDeviceDAO(); + deviceId = deviceDAO.addDevice(TestDataHolder.initialTestDeviceType.getId(), device, TestDataHolder.SUPER_TENANT_ID); + device.setId(deviceId); enrollmentDAO.addEnrollment(deviceId, source, TestDataHolder.SUPER_TENANT_ID); } catch (DeviceManagementDAOException | SQLException e) { log.error("Error occurred while adding enrollment", e); @@ -54,16 +59,18 @@ public class EnrolmentPersistenceTests extends BaseDeviceManagementTest { DeviceManagementDAOFactory.closeConnection(); } /* Retrieving the enrolment associated with the given deviceId and owner */ - EnrolmentInfo target = null; - try { - target = this.getEnrolmentConfig(deviceId, owner, TestDataHolder.SUPER_TENANT_ID); - } catch (DeviceManagementDAOException e) { - String msg = "Error occurred while retrieving application info"; - log.error(msg, e); - Assert.fail(msg, e); - } + if (deviceId != -1) { + EnrolmentInfo target = null; + try { + target = this.getEnrolmentConfig(deviceId, owner, TestDataHolder.SUPER_TENANT_ID); + } catch (DeviceManagementDAOException e) { + String msg = "Error occurred while retrieving application info"; + log.error(msg, e); + Assert.fail(msg, e); + } - Assert.assertEquals(target, source, "Enrolment configuration added is not as same as what's retrieved"); + Assert.assertEquals(target, source, "Enrolment configuration added is not as same as what's retrieved"); + } } private EnrolmentInfo getEnrolmentConfig(int deviceId, String currentOwner, diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceTest.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceTest.java index 80183bbcff..443c0f8499 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceTest.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceTest.java @@ -74,6 +74,8 @@ import org.wso2.carbon.device.mgt.core.authorization.DeviceAccessAuthorizationSe import org.wso2.carbon.device.mgt.core.common.BaseDeviceManagementTest; import org.wso2.carbon.device.mgt.core.common.TestDataHolder; import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; +import org.wso2.carbon.device.mgt.core.dao.DeviceDAO; +import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsDAO; import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsMgtDAOException; @@ -885,6 +887,28 @@ public class DeviceManagementProviderServiceTest extends BaseDeviceManagementTes public void testUpdateDevicesStatusWithDeviceID() throws DeviceManagementException { if (!isMock()) { Device device = TestDataHolder.generateDummyDeviceData("abc"); + try { + int tenantId = TestDataHolder.SUPER_TENANT_ID; + DeviceManagementDAOFactory.beginTransaction(); + DeviceDAO deviceDAO = DeviceManagementDAOFactory.getDeviceDAO(); + int deviceId = deviceDAO.addDevice(TestDataHolder.initialTestDeviceType.getId(), device, tenantId); + device.setId(deviceId); + int enrolmentId = deviceDAO.addEnrollment(device, tenantId); + device.getEnrolmentInfo().setId(enrolmentId); + DeviceManagementDAOFactory.commitTransaction(); + } catch (DeviceManagementDAOException e) { + DeviceManagementDAOFactory.rollbackTransaction(); + String msg = "Error occurred while adding '" + device.getType() + "' device with the identifier '" + + device.getDeviceIdentifier() + "'"; + log.error(msg, e); + Assert.fail(msg, e); + } catch (TransactionManagementException e) { + String msg = "Error occurred while initiating transaction"; + log.error(msg, e); + Assert.fail(msg, e); + } finally { + DeviceManagementDAOFactory.closeConnection(); + } boolean status = deviceMgtService.setStatus(device, EnrolmentInfo.Status.ACTIVE); Assert.assertTrue(status); } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql index cc69537e90..ea5bd1dd33 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql @@ -99,7 +99,19 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT uk_dm_device_enrolment UNIQUE (DEVICE_ID, OWNER, OWNERSHIP, TENANT_ID) ); - +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING ( ID INTEGER AUTO_INCREMENT NOT NULL, ENROLMENT_ID INTEGER NOT NULL, diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/testng.xml b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/testng.xml index 889a6238b7..dc0dd4d939 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/testng.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/testng.xml @@ -33,6 +33,8 @@ + + diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.extensions/pom.xml b/components/device-mgt/org.wso2.carbon.device.mgt.extensions/pom.xml index 6428a95367..97b53a14e1 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.extensions/pom.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.extensions/pom.xml @@ -22,7 +22,7 @@ device-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/test/resources/sql-files/h2.sql b/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/test/resources/sql-files/h2.sql index ef684a6685..d99e112c0c 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/test/resources/sql-files/h2.sql +++ b/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/test/resources/sql-files/h2.sql @@ -120,7 +120,19 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT uk_dm_device_enrolment UNIQUE (DEVICE_ID, OWNER, OWNERSHIP, TENANT_ID) ); - +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING ( ID INTEGER AUTO_INCREMENT NOT NULL, ENROLMENT_ID INTEGER NOT NULL, diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.url.printer/pom.xml b/components/device-mgt/org.wso2.carbon.device.mgt.url.printer/pom.xml index a36f167e83..3f5620c737 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.url.printer/pom.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.url.printer/pom.xml @@ -23,7 +23,7 @@ device-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/device-mgt/pom.xml b/components/device-mgt/pom.xml index f5c20388c5..2d117ceeeb 100644 --- a/components/device-mgt/pom.xml +++ b/components/device-mgt/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/pom.xml b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/pom.xml index e263aafe5a..3d6215077a 100644 --- a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/pom.xml +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt heartbeat-management - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/heartbeat-management/pom.xml b/components/heartbeat-management/pom.xml index 0e98a77795..a22d908db3 100644 --- a/components/heartbeat-management/pom.xml +++ b/components/heartbeat-management/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml index 9cc0e926d7..30e6d3919c 100644 --- a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt identity-extensions - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/identity-extensions/org.wso2.carbon.identity.jwt.client.extension/pom.xml b/components/identity-extensions/org.wso2.carbon.identity.jwt.client.extension/pom.xml index ba3450e72c..b10c9823ee 100644 --- a/components/identity-extensions/org.wso2.carbon.identity.jwt.client.extension/pom.xml +++ b/components/identity-extensions/org.wso2.carbon.identity.jwt.client.extension/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt identity-extensions - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/identity-extensions/pom.xml b/components/identity-extensions/pom.xml index 89396a40c2..9cbd0ce470 100644 --- a/components/identity-extensions/pom.xml +++ b/components/identity-extensions/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/components/policy-mgt/org.wso2.carbon.policy.decision.point/pom.xml b/components/policy-mgt/org.wso2.carbon.policy.decision.point/pom.xml index f93cf53b3b..530bec31ba 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.decision.point/pom.xml +++ b/components/policy-mgt/org.wso2.carbon.policy.decision.point/pom.xml @@ -3,7 +3,7 @@ org.wso2.carbon.devicemgt policy-mgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/policy-mgt/org.wso2.carbon.policy.information.point/pom.xml b/components/policy-mgt/org.wso2.carbon.policy.information.point/pom.xml index af52a0c72e..761c2a8564 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.information.point/pom.xml +++ b/components/policy-mgt/org.wso2.carbon.policy.information.point/pom.xml @@ -3,7 +3,7 @@ org.wso2.carbon.devicemgt policy-mgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.common/pom.xml b/components/policy-mgt/org.wso2.carbon.policy.mgt.common/pom.xml index e2b5c457f1..4485663232 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.common/pom.xml +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.common/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt policy-mgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/pom.xml b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/pom.xml index 427034bc6c..9068a6a304 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/pom.xml +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt policy-mgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/BasePolicyManagementDAOTest.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/BasePolicyManagementDAOTest.java index 450313aac0..b790106c68 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/BasePolicyManagementDAOTest.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/BasePolicyManagementDAOTest.java @@ -162,6 +162,8 @@ public abstract class BasePolicyManagementDAOTest { .SUPER_TENANT_DOMAIN_NAME); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(MultitenantConstants.SUPER_TENANT_ID); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername("admin"); + } private DataSource getDataSource(DataSourceConfig config) { diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql index 4c59adb953..0c5a71aa73 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql @@ -107,7 +107,20 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( CONSTRAINT fk_dm_device_enrolment FOREIGN KEY (DEVICE_ID) REFERENCES DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); - +DROP TABLE IF EXISTS DM_DEVICE_STATUS; +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); DROP TABLE IF EXISTS DM_ENROLMENT_OP_MAPPING; CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING ( ID INTEGER AUTO_INCREMENT NOT NULL, diff --git a/components/policy-mgt/pom.xml b/components/policy-mgt/pom.xml index b653b61a19..5dca677890 100644 --- a/components/policy-mgt/pom.xml +++ b/components/policy-mgt/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/components/transport-mgt/email-sender/org.wso2.carbon.email.sender.core/pom.xml b/components/transport-mgt/email-sender/org.wso2.carbon.email.sender.core/pom.xml index 40d44a8500..beb952d68d 100644 --- a/components/transport-mgt/email-sender/org.wso2.carbon.email.sender.core/pom.xml +++ b/components/transport-mgt/email-sender/org.wso2.carbon.email.sender.core/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt email-sender - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/transport-mgt/email-sender/pom.xml b/components/transport-mgt/email-sender/pom.xml index 6bae00732f..41f105b461 100644 --- a/components/transport-mgt/email-sender/pom.xml +++ b/components/transport-mgt/email-sender/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt transport-mgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/transport-mgt/pom.xml b/components/transport-mgt/pom.xml index cabafa2cf5..1adbd1abf1 100644 --- a/components/transport-mgt/pom.xml +++ b/components/transport-mgt/pom.xml @@ -3,7 +3,7 @@ carbon-devicemgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api/pom.xml b/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api/pom.xml index 2a216e425d..55466f7f9d 100644 --- a/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api/pom.xml +++ b/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt sms-handler - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.common/pom.xml b/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.common/pom.xml index bf6029c1d6..343d9a9c32 100644 --- a/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.common/pom.xml +++ b/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.common/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt sms-handler - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.core/pom.xml b/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.core/pom.xml index f1b5a2e63c..3ce5f78c91 100644 --- a/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.core/pom.xml +++ b/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.core/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt sms-handler - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/transport-mgt/sms-handler/pom.xml b/components/transport-mgt/sms-handler/pom.xml index 4cf46974df..2940603826 100644 --- a/components/transport-mgt/sms-handler/pom.xml +++ b/components/transport-mgt/sms-handler/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt transport-mgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/pom.xml b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/pom.xml index 4fb0ecaf79..af0efe5674 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/pom.xml +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/pom.xml @@ -21,7 +21,7 @@ ui-request-interceptor io.entgra.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT 4.0.0 @@ -102,6 +102,17 @@ org.wso2.carbon.apimgt.application.extension provided + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.core + provided + + + org.apache.httpcomponents.wso2 + httpclient + + + org.wso2.carbon.devicemgt org.wso2.carbon.device.mgt.core diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/DefaultTokenHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/DefaultTokenHandler.java index 9667c25000..6c182b9e1d 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/DefaultTokenHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/DefaultTokenHandler.java @@ -140,6 +140,7 @@ public class DefaultTokenHandler extends HttpServlet { ProxyResponse proxyResponse = new ProxyResponse(); proxyResponse.setCode(HttpStatus.SC_OK); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); proxyResponse.setData(payload); return proxyResponse; } diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/GrafanaHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/GrafanaHandler.java new file mode 100644 index 0000000000..3ac99b1044 --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/GrafanaHandler.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.ui.request.interceptor; + +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.GrafanaEnvVariablesNotDefined; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import io.entgra.ui.request.interceptor.beans.AuthData; +import io.entgra.ui.request.interceptor.beans.ProxyResponse; +import io.entgra.ui.request.interceptor.util.GrafanaHandlerUtil; +import io.entgra.ui.request.interceptor.util.HandlerConstants; +import io.entgra.ui.request.interceptor.util.HandlerUtil; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.*; +import org.apache.http.impl.client.CloseableHttpClient; +import org.wso2.carbon.device.mgt.core.common.util.HttpUtil; + +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.ws.rs.ProcessingException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; + +@MultipartConfig +@WebServlet( + name = "GrafanaRequestHandler", + description = "This servlet intercepts the iframe requests initiated from the user interface and validate before" + + " forwarding to the backend", + urlPatterns = { + "/grafana/*" + } +) +public class GrafanaHandler extends HttpServlet { + private static final Log log = LogFactory.getLog(GrafanaHandler.class); + private static final long serialVersionUID = -6508020875358160165L; + private static AuthData authData; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + try { + if (validateRequest(req, resp)) { + HttpGet grafanaRequest = new HttpGet(); + HandlerUtil.copyRequestHeaders(req, grafanaRequest, true); + if (!GrafanaUtil.isGrafanaAPI(req.getRequestURI())) { + proxyPassGrafanaRequest(grafanaRequest, resp, req); + return; + } + grafanaRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); + ProxyResponse grafanaAPIResponse = executeGrafanaAPIRequest(grafanaRequest, req); + String keyManagerUrl = HandlerUtil.getKeyManagerUrl(req.getScheme()); + if (HandlerConstants.TOKEN_IS_EXPIRED.equals(grafanaAPIResponse.getExecutorResponse())) { + grafanaAPIResponse = HandlerUtil.retryRequestWithRefreshedToken(req, grafanaRequest, keyManagerUrl); + if (!HandlerUtil.isResponseSuccessful(grafanaAPIResponse)) { + HandlerUtil.handleError(resp, grafanaAPIResponse); + return; + } + } + if (grafanaAPIResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { + if (grafanaAPIResponse.getCode() == HttpStatus.SC_UNAUTHORIZED) { + grafanaAPIResponse = HandlerUtil.retryRequestWithRefreshedToken(req, grafanaRequest, keyManagerUrl); + if (!HandlerUtil.isResponseSuccessful(grafanaAPIResponse)) { + HandlerUtil.handleError(resp, grafanaAPIResponse); + return; + } + } else { + log.error("Error occurred while invoking the GET API endpoint."); + HandlerUtil.handleError(resp, grafanaAPIResponse); + return; + } + } + handleSuccess(resp, grafanaAPIResponse); + } + } catch (ProcessingException e) { + String msg = "Grafana server is down or invalid grafana dashboard url provided"; + log.error(msg, e); + } + catch (IOException e) { + log.error("Error occurred when processing Iframe request.", e); + } + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) { + try { + if (validateRequest(req, resp)) { + HttpPost grafanaRequest = new HttpPost(); + HandlerUtil.generateRequestEntity(req, grafanaRequest); + HandlerUtil.copyRequestHeaders(req, grafanaRequest, true); + if (!GrafanaUtil.isGrafanaAPI(req.getRequestURI())) { + proxyPassGrafanaRequest(grafanaRequest, resp, req); + return; + } + grafanaRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); + ProxyResponse grafanaAPIResponse = executeGrafanaAPIRequest(grafanaRequest, req); + if (HandlerConstants.TOKEN_IS_EXPIRED.equals(grafanaAPIResponse.getExecutorResponse())) { + String keyManagerUrl = HandlerUtil.getKeyManagerUrl(req.getScheme()); + grafanaAPIResponse = HandlerUtil.retryRequestWithRefreshedToken(req, grafanaRequest, keyManagerUrl); + if (!HandlerUtil.isResponseSuccessful(grafanaAPIResponse)) { + handleError(resp, grafanaAPIResponse); + return; + } + } + if (grafanaAPIResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { + log.error("Error occurred while invoking the POST API endpoint."); + handleError(resp, grafanaAPIResponse); + return; + } + handleSuccess(resp, grafanaAPIResponse); + } + } catch (FileUploadException e) { + log.error("Error occurred when processing Multipart POST request.", e); + } catch (ProcessingException e) { + String msg = "Grafana server is down or invalid grafana dashboard url provided"; + log.error(msg, e); + } catch (IOException e) { + log.error("Error occurred when processing Iframe request.", e); + } + } + + private void handleSuccess(HttpServletResponse resp, ProxyResponse grafanaAPIResponse) throws IOException{ + String contentType = HandlerUtil.getHeaderValue(HttpHeaders.CONTENT_TYPE, grafanaAPIResponse.getHeaders()); + resp.setContentType(contentType); + resp.setStatus(grafanaAPIResponse.getCode()); + addXFrameOptionsHeaders(resp); + resp.getWriter().print(grafanaAPIResponse.getData()); + } + + private void handleError(HttpServletResponse resp, int errCode, String errMsg) throws IOException { + resp.sendError(errCode, errMsg); + } + + private void handleError(HttpServletResponse resp, ProxyResponse proxyResponse) throws IOException { + resp.sendError(proxyResponse.getCode()); + } + + /*** + * Validates the incoming request. + * + * @param req {@link HttpServletRequest} + * @param resp {@link HttpServletResponse} + * @return If request is a valid one, returns TRUE, otherwise return FALSE + * @throws IOException If and error occurs while witting error response to client side + */ + private boolean validateRequest(HttpServletRequest req, HttpServletResponse resp) + throws IOException { + + if (req.getMethod() == null) { + String errMsg = "Bad Request, Request method is empty"; + log.error(errMsg); + handleError(resp, HttpStatus.SC_BAD_REQUEST, errMsg); + return false; + } + if (HandlerUtil.isPropertyDefined(HandlerConstants.IOT_REPORTING_WEBAPP_HOST_ENV_VAR)) { + String errMsg = "Reporting Endpoint is not defined in the iot-server.sh properly."; + log.error(errMsg); + resp.sendError(500, errMsg); + return false; + } + + HttpSession session = req.getSession(false); + if (session == null) { + String errMsg = "Unauthorized, You are not logged in. Please log in to the portal"; + log.error(errMsg); + handleError(resp, HttpStatus.SC_UNAUTHORIZED, errMsg); + return false; + } + + authData = (AuthData) session.getAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY); + if (authData == null) { + String errMsg = "Unauthorized, Access token not found in the current session"; + log.error(errMsg); + handleError(resp, HttpStatus.SC_UNAUTHORIZED, errMsg); + return false; + } + + return true; + } + + private ProxyResponse executeGrafanaAPIRequest(HttpRequestBase requestBase, HttpServletRequest request) + throws IOException { + URI grafanaUri = HttpUtil.createURI(generateGrafanaAPIUrl(request)); + requestBase.setURI(grafanaUri); + return HandlerUtil.execute(requestBase); + } + + private String generateGrafanaAPIUrl(HttpServletRequest request) { + String apiBase = generateGrafanaAPIBase(request); + String grafanaUri = getURIWithQuery(request); + return apiBase + grafanaUri; + } + + private String generateGrafanaAPIBase(HttpServletRequest request) { + return HandlerUtil.getIOTGatewayBase(request) + HandlerConstants.GRAFANA_API; + } + + private String getURIWithQuery(HttpServletRequest request) { + String uri = request.getPathInfo(); + if (request.getQueryString() != null) { + uri += HandlerConstants.URI_QUERY_SEPARATOR + request.getQueryString(); + } + return uri; + } + private void proxyPassGrafanaRequest(HttpRequestBase requestBase, HttpServletResponse response, + HttpServletRequest request) throws IOException { + try (CloseableHttpClient client = HandlerUtil.getHttpClient()) { + String grafanaUriStr = GrafanaHandlerUtil.generateGrafanaUrl(HttpUtil.createURI(getURIWithQuery(request)), + GrafanaUtil.getGrafanaHTTPBase(request.getScheme())); + URI grafanaURI = HttpUtil.createURI(grafanaUriStr); + requestBase.setURI(grafanaURI); + HttpResponse grafanaResponse = invokeGrafanaAPI(client, requestBase); + forwardGrafanaResponse(grafanaResponse, response); + } catch (GrafanaEnvVariablesNotDefined e) { + handleError(response, HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } catch (GrafanaManagementException e) { + String errMsg = "Error occurred while retrieving grafana user credentials"; + log.error(errMsg, e); + handleError(response, HttpStatus.SC_INTERNAL_SERVER_ERROR, errMsg); + } + } + + private HttpResponse invokeGrafanaAPI(HttpClient client, HttpRequestBase request) throws IOException, GrafanaManagementException { + setBasicAuthHeader(request); + return client.execute(request); + } + + private void setBasicAuthHeader(HttpRequestBase request) throws GrafanaManagementException { + String basicAuth = GrafanaUtil.getBasicAuthBase64Header(); + request.setHeader(HttpHeaders.AUTHORIZATION, basicAuth); + } + + private void forwardGrafanaResponse(HttpResponse grafanaResponse, HttpServletResponse response) throws IOException { + InputStream responseContent = grafanaResponse.getEntity().getContent(); + String grafanaContentType = HandlerUtil.getMemeType(grafanaResponse); + response.setHeader(HttpHeaders.CONTENT_TYPE, grafanaContentType); + addXFrameOptionsHeaders(response); + byte[] buffer = new byte[10240]; + try (InputStream input = responseContent; OutputStream output = response.getOutputStream()) { + for (int length = 0; (length = input.read(buffer)) > 0;) { + output.write(buffer, 0, length); + } + } + + } + + private void addXFrameOptionsHeaders(HttpServletResponse response) { + response.setHeader(HandlerConstants.X_FRAME_OPTIONS, HandlerConstants.X_FRAME_OPTIONS_SAMEORIGIN); + } + +} diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/InvokerHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/InvokerHandler.java index 7cfe352548..045aac0b5a 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/InvokerHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/InvokerHandler.java @@ -18,37 +18,21 @@ package io.entgra.ui.request.interceptor; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import io.entgra.ui.request.interceptor.beans.AuthData; import io.entgra.ui.request.interceptor.util.HandlerConstants; import io.entgra.ui.request.interceptor.util.HandlerUtil; -import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpHeaders; import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpHead; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.cookie.SM; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.InputStreamEntity; -import org.apache.http.entity.StringEntity; -import org.apache.http.entity.mime.HttpMultipartMode; -import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.entity.mime.content.InputStreamBody; import io.entgra.ui.request.interceptor.beans.ProxyResponse; - import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; @@ -56,8 +40,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; -import java.util.Enumeration; -import java.util.List; @MultipartConfig @WebServlet( @@ -79,14 +61,15 @@ public class InvokerHandler extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) { try { if (validateRequest(req, resp)) { - HttpPost postRequest = new HttpPost(generateBackendRequestURL(req)); - generateRequestEntity(req, postRequest); + HttpPost postRequest = new HttpPost(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); + HandlerUtil.generateRequestEntity(req, postRequest); postRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(postRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, postRequest, kmManagerUrl); - if (proxyResponse == null) { + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, postRequest, kmManagerUrl); + if (!HandlerUtil.isResponseSuccessful(proxyResponse)) { + HandlerUtil.handleError(resp, proxyResponse); return; } } @@ -108,19 +91,24 @@ public class InvokerHandler extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) { try { if (validateRequest(req, resp)) { - HttpGet getRequest = new HttpGet(generateBackendRequestURL(req)); - copyRequestHeaders(req, getRequest, false); + HttpGet getRequest = new HttpGet(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); + HandlerUtil.copyRequestHeaders(req, getRequest, false); getRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(getRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, getRequest, kmManagerUrl); - if (proxyResponse == null) { + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, getRequest, kmManagerUrl); + if (!HandlerUtil.isResponseSuccessful(proxyResponse)) { + HandlerUtil.handleError(resp, proxyResponse); return; } } if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { if (proxyResponse.getCode() == HttpStatus.SC_UNAUTHORIZED) { - proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, getRequest, kmManagerUrl); + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, getRequest, kmManagerUrl); + if (!HandlerUtil.isResponseSuccessful(proxyResponse)) { + HandlerUtil.handleError(resp, proxyResponse); + return; + } } else { log.error("Error occurred while invoking the GET API endpoint."); HandlerUtil.handleError(resp, proxyResponse); @@ -138,13 +126,14 @@ public class InvokerHandler extends HttpServlet { protected void doHead(HttpServletRequest req, HttpServletResponse resp) { try { if (validateRequest(req, resp)) { - HttpHead headRequest = new HttpHead(generateBackendRequestURL(req)); - copyRequestHeaders(req, headRequest, false); + HttpHead headRequest = new HttpHead(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); + HandlerUtil.copyRequestHeaders(req, headRequest, false); headRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(headRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, headRequest, kmManagerUrl); - if (proxyResponse == null) { + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, headRequest, kmManagerUrl); + if (!HandlerUtil.isResponseSuccessful(proxyResponse)) { + HandlerUtil.handleError(resp, proxyResponse); return; } } @@ -164,14 +153,15 @@ public class InvokerHandler extends HttpServlet { protected void doPut(HttpServletRequest req, HttpServletResponse resp) { try { if (validateRequest(req, resp)) { - HttpPut putRequest = new HttpPut(generateBackendRequestURL(req)); - generateRequestEntity(req, putRequest); + HttpPut putRequest = new HttpPut(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); + HandlerUtil.generateRequestEntity(req, putRequest); putRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(putRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, putRequest, kmManagerUrl); - if (proxyResponse == null) { + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, putRequest, kmManagerUrl); + if (!HandlerUtil.isResponseSuccessful(proxyResponse)) { + HandlerUtil.handleError(resp, proxyResponse); return; } } @@ -193,13 +183,14 @@ public class InvokerHandler extends HttpServlet { protected void doDelete(HttpServletRequest req, HttpServletResponse resp) { try { if (validateRequest(req, resp)) { - HttpDelete deleteRequest = new HttpDelete(generateBackendRequestURL(req)); - copyRequestHeaders(req, deleteRequest, false); + HttpDelete deleteRequest = new HttpDelete(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); + HandlerUtil.copyRequestHeaders(req, deleteRequest, false); deleteRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(deleteRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, deleteRequest, kmManagerUrl); - if (proxyResponse == null) { + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, deleteRequest, kmManagerUrl); + if (!HandlerUtil.isResponseSuccessful(proxyResponse)) { + HandlerUtil.handleError(resp, proxyResponse); return; } } @@ -215,83 +206,6 @@ public class InvokerHandler extends HttpServlet { } } - /** - * Generate te request entity for POST and PUT requests from the incoming request. - * - * @param req incoming {@link HttpServletRequest}. - * @param proxyRequest proxy request instance. - * @throws FileUploadException If unable to parse the incoming request for multipart content extraction. - * @throws IOException If error occurred while generating the request body. - */ - private void generateRequestEntity(HttpServletRequest req, HttpEntityEnclosingRequestBase proxyRequest) - throws FileUploadException, IOException { - if (ServletFileUpload.isMultipartContent(req)) { - ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory()); - List fileItemList = servletFileUpload.parseRequest(req); - MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create(); - entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); - for (FileItem item: fileItemList) { - if (!item.isFormField()) { - entityBuilder.addPart(item.getFieldName(), new InputStreamBody(item.getInputStream(), - ContentType.create(item.getContentType()), item.getName())); - } else { - entityBuilder.addTextBody(item.getFieldName(), item.getString()); - } - } - proxyRequest.setEntity(entityBuilder.build()); - copyRequestHeaders(req, proxyRequest, false); - } else { - if (StringUtils.isNotEmpty(req.getHeader(HttpHeaders.CONTENT_LENGTH)) || - StringUtils.isNotEmpty(req.getHeader(HttpHeaders.TRANSFER_ENCODING))) { - InputStreamEntity entity = new InputStreamEntity(req.getInputStream(), - Long.parseLong(req.getHeader(HttpHeaders.CONTENT_LENGTH))); - proxyRequest.setEntity(entity); - } - copyRequestHeaders(req, proxyRequest, true); - } - } - - /** - * Generates the target URL for the proxy request. - * - * @param req incoming {@link HttpServletRequest} - * @return Target URL - */ - private String generateBackendRequestURL(HttpServletRequest req) { - StringBuilder urlBuilder = new StringBuilder(); - urlBuilder.append(apiEndpoint).append(HandlerConstants.API_COMMON_CONTEXT) - .append(req.getPathInfo().replace(" ", "%20")); - if (StringUtils.isNotEmpty(req.getQueryString())) { - urlBuilder.append("?").append(req.getQueryString()); - } - return urlBuilder.toString(); - } - - /** - * Copy incoming request headers to the proxy request. - * - * @param req incoming {@link HttpServletRequest} - * @param httpRequest proxy request instance. - * @param preserveContentType true if content type header needs to be preserved. - * This should be set to false when handling multipart requests as Http - * client will generate the Content-Type header automatically. - */ - private void copyRequestHeaders(HttpServletRequest req, HttpRequestBase httpRequest, boolean preserveContentType) { - Enumeration headerNames = req.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - if (headerName.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH) || - headerName.equalsIgnoreCase(SM.COOKIE) || - (!preserveContentType && headerName.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE))) { - continue; - } - Enumeration headerValues = req.getHeaders(headerName); - while (headerValues.hasMoreElements()) { - httpRequest.setHeader(headerName, headerValues.nextElement()); - } - } - } - /*** * Validates the incoming request. * @@ -305,8 +219,7 @@ public class InvokerHandler extends HttpServlet { apiEndpoint = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_GW_HOST_ENV_VAR) + HandlerConstants.COLON + HandlerUtil.getGatewayPort(req.getScheme()); - kmManagerUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) - + HandlerConstants.COLON + HandlerUtil.getKeymanagerPort(req.getScheme()); + kmManagerUrl = HandlerUtil.getKeyManagerUrl(req.getScheme()); if (HandlerConstants.REPORTS.equalsIgnoreCase(req.getHeader(HandlerConstants.APP_NAME))){ apiEndpoint = System.getProperty("iot.reporting.webapp.host"); @@ -339,107 +252,4 @@ public class InvokerHandler extends HttpServlet { return true; } - /** - * Retry request again after refreshing the access token - * - * @param req incoming {@link HttpServletRequest} - * @param resp resp {@link HttpServletResponse} - * @param httpRequest subclass of {@link HttpRequestBase} related to the current request. - * @return {@link ProxyResponse} if successful and null if failed. - * @throws IOException If an error occurs when try to retry the request. - */ - private ProxyResponse retryRequestWithRefreshedToken(HttpServletRequest req, HttpServletResponse resp, - HttpRequestBase httpRequest) throws IOException { - if (refreshToken(req, resp)) { - httpRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); - ProxyResponse proxyResponse = HandlerUtil.execute(httpRequest); - if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { - log.error("Error occurred while invoking the API after refreshing the token."); - HandlerUtil.handleError(resp, proxyResponse); - return null; - } - return proxyResponse; - } - return null; - } - - /*** - * This method is responsible to get the refresh token - * - * @param req {@link HttpServletRequest} - * @param resp {@link HttpServletResponse} - * @return If successfully renew tokens, returns TRUE otherwise return FALSE - * @throws IOException If an error occurs while witting error response to client side or invoke token renewal API - */ - private static boolean refreshToken(HttpServletRequest req, HttpServletResponse resp) - throws IOException { - if (log.isDebugEnabled()) { - log.debug("refreshing the token"); - } - - HttpPost tokenEndpoint = new HttpPost( - kmManagerUrl + HandlerConstants.TOKEN_ENDPOINT); - HttpSession session = req.getSession(false); - if (session == null) { - log.error("Couldn't find a session, hence it is required to login and proceed."); - handleError(resp, HttpStatus.SC_UNAUTHORIZED); - return false; - } - - StringEntity tokenEndpointPayload = new StringEntity( - "grant_type=refresh_token&refresh_token=" + authData.getRefreshToken() + "&scope=PRODUCTION", - ContentType.APPLICATION_FORM_URLENCODED); - - tokenEndpoint.setEntity(tokenEndpointPayload); - String encodedClientApp = authData.getEncodedClientApp(); - tokenEndpoint.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC + - encodedClientApp); - tokenEndpoint.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.toString()); - - ProxyResponse tokenResultResponse = HandlerUtil.execute(tokenEndpoint); - if (tokenResultResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { - log.error("Error occurred while refreshing access token."); - HandlerUtil.handleError(resp, tokenResultResponse); - return false; - } - - JsonParser jsonParser = new JsonParser(); - JsonElement jTokenResult = jsonParser.parse(tokenResultResponse.getData()); - - if (jTokenResult.isJsonObject()) { - JsonObject jTokenResultAsJsonObject = jTokenResult.getAsJsonObject(); - AuthData newAuthData = new AuthData(); - - newAuthData.setAccessToken(jTokenResultAsJsonObject.get("access_token").getAsString()); - newAuthData.setRefreshToken(jTokenResultAsJsonObject.get("refresh_token").getAsString()); - newAuthData.setScope(jTokenResultAsJsonObject.get("scope").getAsString()); - newAuthData.setClientId(authData.getClientId()); - newAuthData.setClientSecret(authData.getClientSecret()); - newAuthData.setEncodedClientApp(authData.getEncodedClientApp()); - newAuthData.setUsername(authData.getUsername()); - authData = newAuthData; - session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, newAuthData); - return true; - } - - log.error("Error Occurred in token renewal process."); - handleError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR); - return false; - } - - /** - * Handle error requests - * - * @param resp {@link HttpServletResponse} - * @param errorCode HTTP error status code - * @throws IOException If error occurred when trying to send the error response. - */ - private static void handleError(HttpServletResponse resp, int errorCode) - throws IOException { - ProxyResponse proxyResponse = new ProxyResponse(); - proxyResponse.setCode(errorCode); - proxyResponse.setExecutorResponse( - HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(errorCode)); - HandlerUtil.handleError(resp, proxyResponse); - } } diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LoginHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LoginHandler.java index 953fbf5a5c..f41f94a764 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LoginHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LoginHandler.java @@ -123,6 +123,7 @@ public class LoginHandler extends HttpServlet { if (getTokenAndPersistInSession(req, resp, clientId, clientSecret, encodedClientApp, scopes)) { ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); proxyResponse.setCode(HttpStatus.SC_OK); HandlerUtil.handleSuccess(resp, proxyResponse); return; @@ -210,7 +211,7 @@ public class LoginHandler extends HttpServlet { + HandlerConstants.COLON + HandlerUtil.getCorePort(req.getScheme()); uiConfigUrl = iotCoreUrl + HandlerConstants.UI_CONFIG_ENDPOINT; kmManagerUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) - + HandlerConstants.COLON + HandlerUtil.getKeymanagerPort(req.getScheme()); + + HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(req.getScheme()); if (username == null || password == null) { String msg = "Invalid login request. Username or Password is not received for login request."; diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LogoutHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LogoutHandler.java index 6cb7aed757..bbf82164a2 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LogoutHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LogoutHandler.java @@ -45,6 +45,7 @@ public class LogoutHandler extends HttpServlet { } ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); proxyResponse.setCode(HttpStatus.SC_OK); try { HandlerUtil.handleSuccess(resp, proxyResponse); diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/OTPInvokerHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/OTPInvokerHandler.java index c852cce259..44dbe7882e 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/OTPInvokerHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/OTPInvokerHandler.java @@ -20,37 +20,22 @@ package io.entgra.ui.request.interceptor; import io.entgra.ui.request.interceptor.util.HandlerConstants; import io.entgra.ui.request.interceptor.util.HandlerUtil; -import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.http.HttpHeaders; import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpHead; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.cookie.SM; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.InputStreamEntity; -import org.apache.http.entity.mime.HttpMultipartMode; -import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.entity.mime.content.InputStreamBody; import io.entgra.ui.request.interceptor.beans.ProxyResponse; - import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.util.Enumeration; -import java.util.List; @WebServlet( name = "OTPRequestHandlerServlet", @@ -70,7 +55,7 @@ public class OTPInvokerHandler extends HttpServlet { try { if (validateRequest(req, resp)) { HttpPost postRequest = new HttpPost(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); - generateRequestEntity(req, postRequest); + HandlerUtil.generateRequestEntity(req, postRequest); ProxyResponse proxyResponse = HandlerUtil.execute(postRequest); if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { @@ -92,7 +77,7 @@ public class OTPInvokerHandler extends HttpServlet { try { if (validateRequest(req, resp)) { HttpGet getRequest = new HttpGet(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); - copyRequestHeaders(req, getRequest, false); + HandlerUtil.copyRequestHeaders(req, getRequest, false); ProxyResponse proxyResponse = HandlerUtil.execute(getRequest); if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { @@ -112,7 +97,7 @@ public class OTPInvokerHandler extends HttpServlet { try { if (validateRequest(req, resp)) { HttpHead headRequest = new HttpHead(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); - copyRequestHeaders(req, headRequest, false); + HandlerUtil.copyRequestHeaders(req, headRequest, false); ProxyResponse proxyResponse = HandlerUtil.execute(headRequest); if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { @@ -132,7 +117,7 @@ public class OTPInvokerHandler extends HttpServlet { try { if (validateRequest(req, resp)) { HttpPut putRequest = new HttpPut(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); - generateRequestEntity(req, putRequest); + HandlerUtil.generateRequestEntity(req, putRequest); ProxyResponse proxyResponse = HandlerUtil.execute(putRequest); if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { @@ -154,7 +139,7 @@ public class OTPInvokerHandler extends HttpServlet { try { if (validateRequest(req, resp)) { HttpDelete deleteRequest = new HttpDelete(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); - copyRequestHeaders(req, deleteRequest, false); + HandlerUtil.copyRequestHeaders(req, deleteRequest, false); ProxyResponse proxyResponse = HandlerUtil.execute(deleteRequest); if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { @@ -169,67 +154,6 @@ public class OTPInvokerHandler extends HttpServlet { } } - /** - * Generate te request entity for POST and PUT requests from the incoming request. - * - * @param req incoming {@link HttpServletRequest}. - * @param proxyRequest proxy request instance. - * @throws FileUploadException If unable to parse the incoming request for multipart content extraction. - * @throws IOException If error occurred while generating the request body. - */ - private void generateRequestEntity(HttpServletRequest req, HttpEntityEnclosingRequestBase proxyRequest) - throws FileUploadException, IOException { - if (ServletFileUpload.isMultipartContent(req)) { - ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory()); - List fileItemList = servletFileUpload.parseRequest(req); - MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create(); - entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); - for (FileItem item: fileItemList) { - if (!item.isFormField()) { - entityBuilder.addPart(item.getFieldName(), new InputStreamBody(item.getInputStream(), - ContentType.create(item.getContentType()), item.getName())); - } else { - entityBuilder.addTextBody(item.getFieldName(), item.getString()); - } - } - proxyRequest.setEntity(entityBuilder.build()); - copyRequestHeaders(req, proxyRequest, false); - } else { - if (StringUtils.isNotEmpty(req.getHeader(HttpHeaders.CONTENT_LENGTH)) || - StringUtils.isNotEmpty(req.getHeader(HttpHeaders.TRANSFER_ENCODING))) { - InputStreamEntity entity = new InputStreamEntity(req.getInputStream(), - Long.parseLong(req.getHeader(HttpHeaders.CONTENT_LENGTH))); - proxyRequest.setEntity(entity); - } - copyRequestHeaders(req, proxyRequest, true); - } - } - - /** - * Copy incoming request headers to the proxy request. - * - * @param req incoming {@link HttpServletRequest} - * @param httpRequest proxy request instance. - * @param preserveContentType true if content type header needs to be preserved. - * This should be set to false when handling multipart requests as Http - * client will generate the Content-Type header automatically. - */ - private void copyRequestHeaders(HttpServletRequest req, HttpRequestBase httpRequest, boolean preserveContentType) { - Enumeration headerNames = req.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - if (headerName.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH) || - headerName.equalsIgnoreCase(SM.COOKIE) || - (!preserveContentType && headerName.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE))) { - continue; - } - Enumeration headerValues = req.getHeaders(headerName); - while (headerValues.hasMoreElements()) { - httpRequest.setHeader(headerName, headerValues.nextElement()); - } - } - } - /*** * Validates the incoming request. * @@ -246,25 +170,10 @@ public class OTPInvokerHandler extends HttpServlet { if (StringUtils.isBlank(req.getHeader(HandlerConstants.OTP_HEADER))) { log.error("Unauthorized, Please provide OTP token."); - handleError(resp, HttpStatus.SC_UNAUTHORIZED); + HandlerUtil.handleError(resp, HttpStatus.SC_UNAUTHORIZED); return false; } return true; } - /** - * Handle error requests - * - * @param resp {@link HttpServletResponse} - * @param errorCode HTTP error status code - * @throws IOException If error occurred when trying to send the error response. - */ - private static void handleError(HttpServletResponse resp, int errorCode) - throws IOException { - ProxyResponse proxyResponse = new ProxyResponse(); - proxyResponse.setCode(errorCode); - proxyResponse.setExecutorResponse( - HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(errorCode)); - HandlerUtil.handleError(resp, proxyResponse); - } } diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/PermissionScopeHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/PermissionScopeHandler.java index 9ccb10c796..b5309492c7 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/PermissionScopeHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/PermissionScopeHandler.java @@ -57,6 +57,7 @@ public class PermissionScopeHandler extends HttpServlet { JSONObject jsonObject = new JSONObject(); jsonObject.put(HandlerConstants.USER_SCOPES, authData.getScope()); proxyResponse.setCode(HttpStatus.SC_OK); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); proxyResponse.setData(jsonObject.toString()); HandlerUtil.handleSuccess(resp, proxyResponse); return; diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginCallbackHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginCallbackHandler.java index d402f4795e..6fc5afa8ef 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginCallbackHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginCallbackHandler.java @@ -53,7 +53,7 @@ public class SsoLoginCallbackHandler extends HttpServlet { String iotsCoreUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR) + HandlerConstants.COLON + HandlerUtil.getCorePort(req.getScheme()); String keyManagerUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) - + HandlerConstants.COLON + HandlerUtil.getKeymanagerPort(req.getScheme()); + + HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(req.getScheme()); if (session == null) { String baseContextPath = req.getContextPath(); diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginHandler.java index f76f72a958..f6180c0a37 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginHandler.java @@ -108,7 +108,7 @@ public class SsoLoginHandler extends HttpServlet { apiMgtUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_APIM_HOST_ENV_VAR) + HandlerConstants.COLON + HandlerUtil.getAPIManagerPort(req.getScheme()); keyManagerUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) - + HandlerConstants.COLON + HandlerUtil.getKeymanagerPort(req.getScheme()); + + HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(req.getScheme()); // Fetch ui config and persists in session String uiConfigUrl = iotsCoreUrl + HandlerConstants.UI_CONFIG_ENDPOINT; diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLogoutHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLogoutHandler.java index 92d14735ae..0e46e759f6 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLogoutHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLogoutHandler.java @@ -45,6 +45,7 @@ public class SsoLogoutHandler extends HttpServlet { } removeCookie(HandlerConstants.COMMON_AUTH_ID_KEY, "/", resp); ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); proxyResponse.setCode(HttpStatus.SC_OK); HttpSession session = req.getSession(false); diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/UserHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/UserHandler.java index 170030f399..4e1f120734 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/UserHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/UserHandler.java @@ -57,7 +57,7 @@ public class UserHandler extends HttpServlet { String keymanagerUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) - + HandlerConstants.COLON + HandlerUtil.getKeymanagerPort(req.getScheme()); + + HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(req.getScheme()); HttpSession httpSession = req.getSession(false); if (httpSession == null) { HandlerUtil.sendUnAuthorizeResponse(resp); @@ -86,7 +86,11 @@ public class UserHandler extends HttpServlet { if (tokenStatus.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { if (tokenStatus.getCode() == HttpStatus.SC_UNAUTHORIZED) { - tokenStatus = HandlerUtil.retryRequestWithRefreshedToken(req, resp, tokenEndpoint, keymanagerUrl); + tokenStatus = HandlerUtil.retryRequestWithRefreshedToken(req, tokenEndpoint, keymanagerUrl); + if(!HandlerUtil.isResponseSuccessful(tokenStatus)) { + HandlerUtil.handleError(resp, tokenStatus); + return; + } } else { log.error("Error occurred while invoking the API to get token status."); HandlerUtil.handleError(resp, tokenStatus); @@ -108,6 +112,7 @@ public class UserHandler extends HttpServlet { return; } ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); proxyResponse.setCode(HttpStatus.SC_OK); proxyResponse.setData( jTokenResultAsJsonObject.get("username").getAsString().replaceAll("@carbon.super", "")); diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/beans/ProxyResponse.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/beans/ProxyResponse.java index ea1a550cfb..32c7ecb117 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/beans/ProxyResponse.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/beans/ProxyResponse.java @@ -17,11 +17,20 @@ */ package io.entgra.ui.request.interceptor.beans; +import org.apache.http.Header; + public class ProxyResponse { + public static class Status { + public static int SUCCESS = 1; + public static int ERROR = 0; + } + private int code; private String data; private String executorResponse; + private int status; + private Header[] headers; public int getCode() { return code; } @@ -34,4 +43,21 @@ public class ProxyResponse { public String getExecutorResponse() { return executorResponse; } public void setExecutorResponse(String executorResponse) { this.executorResponse = executorResponse; } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public Header[] getHeaders() { + return headers; + } + + public void setHeaders(Header[] headers) { + this.headers = headers; + } + } diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/GrafanaHandlerUtil.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/GrafanaHandlerUtil.java new file mode 100644 index 0000000000..3ba09f8d0d --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/GrafanaHandlerUtil.java @@ -0,0 +1,20 @@ +package io.entgra.ui.request.interceptor.util; + +import org.apache.commons.lang.StringUtils; + +import java.net.URI; + +public class GrafanaHandlerUtil { + public static String getGrafanaUri(URI req) { + StringBuilder grafanaUriBuilder = new StringBuilder(); + grafanaUriBuilder.append(req.getPath().replace(" ", "%20")); + if (StringUtils.isNotEmpty(req.getQuery())) { + grafanaUriBuilder.append("?").append(req.getQuery()); + } + return grafanaUriBuilder.toString(); + } + + public static String generateGrafanaUrl(URI request, String base) { + return base + GrafanaHandlerUtil.getGrafanaUri(request); + } +} diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerConstants.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerConstants.java index ac97931bf2..df0e085b0a 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerConstants.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerConstants.java @@ -31,6 +31,11 @@ public class HandlerConstants { public static final String SSO_LOGIN_CALLBACK = "/ssoLoginCallback"; public static final String BASIC = "Basic "; public static final String BEARER = "Bearer "; + public static final String X_FRAME_OPTIONS = "X-Frame-Options"; + public static final String X_FRAME_OPTIONS_SAMEORIGIN = "SAMEORIGIN"; + public static final String UPGRADE = "Upgrade"; + public static final String WEB_SOCKET = "websocket"; + public static final String HTTP_UPGRADE = "HTTP/1.1"; public static final String TAGS_KEY = "tags"; public static final String APP_NAME_KEY = "applicationName"; public static final String SESSION_AUTH_DATA_KEY = "authInfo"; @@ -58,10 +63,13 @@ public class HandlerConstants { public static final String LOGIN_CACHE_CAPACITY_KEY = "loginCacheCapacity"; public static final String SCHEME_SEPARATOR = "://"; + public static final String URI_SEPARATOR = "/"; + public static final String QUERY_PARAM_KEY_VALUE_SEP = "="; public static final String COLON = ":"; public static final String HTTP_PROTOCOL = "http"; public static final String HTTPS_PROTOCOL = "https"; public static final String UNDERSCORE = "_"; + public static final String URI_QUERY_SEPARATOR = "?"; public static final int INTERNAL_ERROR_CODE = 500; public static final long TIMEOUT = 1200; @@ -71,9 +79,12 @@ public class HandlerConstants { public static final String AX_PREFIX = "ax2251:"; public static final String PAYLOADS_DIR = "repository/resources/payloads"; public static final String SOAP_ACTION_HEADER = "SOAPAction"; + public static final String REFERER_HEADER = "Referer"; public static final String WSS_PROTOCOL = "wss"; + public static final String WS_PROTOCOL = "ws"; public static final String REMOTE_SESSION_CONTEXT = "/remote/session/clients"; + public static final String GRAFANA_API = "/api/device-mgt/v1.0/reports/grafana"; public static final String IOT_CORE_HOST_ENV_VAR = "iot.core.host"; public static final String IOT_CORE_HTTP_PORT_ENV_VAR = "iot.core.http.port"; @@ -89,5 +100,6 @@ public class HandlerConstants { public static final String IOT_REMOTE_SESSION_HOST_ENV_VAR = "iot.remotesession.server.host"; public static final String IOT_REMOTE_SESSION_HTTPS_PORT_ENV_VAR = "iot.remotesession.server.https.port"; public static final String IOT_GW_HTTPS_PORT_ENV_VAR = "iot.gateway.https.port"; + public static final String IOT_REPORTING_WEBAPP_HOST_ENV_VAR = "iot.reporting.webapp.host"; public static final String USER_SCOPES = "userScopes"; } diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerUtil.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerUtil.java index 48812430f4..64f8a3e221 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerUtil.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerUtil.java @@ -25,19 +25,26 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import io.entgra.ui.request.interceptor.beans.AuthData; import io.entgra.ui.request.interceptor.cache.LoginCache; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.http.Consts; -import org.apache.http.HttpHeaders; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; +import org.apache.http.*; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.cookie.SM; import org.apache.http.entity.ContentType; +import org.apache.http.entity.InputStreamEntity; import org.apache.http.entity.StringEntity; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.entity.mime.content.InputStreamBody; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.xml.serialize.OutputFormat; @@ -46,6 +53,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.w3c.dom.Document; import io.entgra.ui.request.interceptor.beans.ProxyResponse; +import org.wso2.carbon.device.mgt.core.common.util.HttpUtil; import org.xml.sax.SAXException; import javax.servlet.http.HttpServletRequest; @@ -60,6 +68,8 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.Enumeration; +import java.util.List; public class HandlerUtil { @@ -83,54 +93,64 @@ public class HandlerUtil { log.error("Received null response for http request : " + httpRequest.getMethod() + " " + httpRequest .getURI().toString()); proxyResponse.setCode(HandlerConstants.INTERNAL_ERROR_CODE); + proxyResponse.setStatus(ProxyResponse.Status.ERROR); proxyResponse.setExecutorResponse(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + getStatusKey( HandlerConstants.INTERNAL_ERROR_CODE)); return proxyResponse; } else { int statusCode = response.getStatusLine().getStatusCode(); - try (BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) { - StringBuilder result = new StringBuilder(); - String line; - while ((line = rd.readLine()) != null) { - result.append(line); - } - - String jsonString = result.toString(); - if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_CREATED) { + String jsonString = getResponseString(response); + if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_CREATED) { + proxyResponse.setCode(statusCode); + proxyResponse.setData(jsonString); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); + proxyResponse.setExecutorResponse("SUCCESS"); + proxyResponse.setHeaders(response.getAllHeaders()); + return proxyResponse; + } else if (statusCode == HttpStatus.SC_UNAUTHORIZED) { + if (isTokenExpired(jsonString)) { + proxyResponse.setCode(statusCode); + proxyResponse.setStatus(ProxyResponse.Status.ERROR); + proxyResponse.setExecutorResponse(HandlerConstants.TOKEN_IS_EXPIRED); + } else { + log.error( + "Received " + statusCode + " response for http request : " + httpRequest.getMethod() + + " " + httpRequest.getURI().toString() + ". Error message: " + jsonString); proxyResponse.setCode(statusCode); proxyResponse.setData(jsonString); - proxyResponse.setExecutorResponse("SUCCESS"); - return proxyResponse; - } else if (statusCode == HttpStatus.SC_UNAUTHORIZED) { - if (jsonString.contains("Access token expired") || jsonString - .contains("Invalid input. Access token validation failed")) { - proxyResponse.setCode(statusCode); - proxyResponse.setExecutorResponse(HandlerConstants.TOKEN_IS_EXPIRED); - return proxyResponse; - } else { - log.error( - "Received " + statusCode + " response for http request : " + httpRequest.getMethod() - + " " + httpRequest.getURI().toString() + ". Error message: " + jsonString); - proxyResponse.setCode(statusCode); - proxyResponse.setData(jsonString); - proxyResponse.setExecutorResponse( - HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + getStatusKey(statusCode)); - return proxyResponse; - } + proxyResponse.setStatus(ProxyResponse.Status.ERROR); + proxyResponse.setExecutorResponse( + HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + getStatusKey(statusCode)); } - log.error("Received " + statusCode + - " response for http request : " + httpRequest.getMethod() + " " + httpRequest.getURI() - .toString() + ". Error message: " + jsonString); - proxyResponse.setCode(statusCode); - proxyResponse.setData(jsonString); - proxyResponse - .setExecutorResponse(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + getStatusKey(statusCode)); return proxyResponse; } + log.error("Received " + statusCode + + " response for http request : " + httpRequest.getMethod() + " " + httpRequest.getURI() + .toString() + ". Error message: " + jsonString); + proxyResponse.setCode(statusCode); + proxyResponse.setData(jsonString); + proxyResponse.setStatus(ProxyResponse.Status.ERROR); + proxyResponse + .setExecutorResponse(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + getStatusKey(statusCode)); + return proxyResponse; } } } + public static boolean isTokenExpired(String jsonBody) { + return jsonBody.contains("Access token expired") || jsonBody + .contains("Invalid input. Access token validation failed"); + } + + public static String getMemeType(HttpResponse response) { + String memeType = ""; + Header contentType = response.getEntity().getContentType(); + if (contentType != null) { + memeType = contentType.getValue().split(";")[0].trim(); + } + return memeType; + } + /*** * * @param statusCode Provide status code, e.g:- 400, 401, 500 etc @@ -172,6 +192,7 @@ public class HandlerUtil { } + /*** * Handle error requests. * @@ -183,6 +204,7 @@ public class HandlerUtil { Gson gson = new Gson(); if (proxyResponse == null) { proxyResponse = new ProxyResponse(); + proxyResponse.setStatus(ProxyResponse.Status.ERROR); proxyResponse.setCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); proxyResponse.setExecutorResponse(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil .getStatusKey(HandlerConstants.INTERNAL_ERROR_CODE)); @@ -200,17 +222,27 @@ public class HandlerUtil { /** * Handle error requests with custom error codes. * - * @param resp {@link HttpServletResponse} + * @param resp {@link HttpServletResponse} * @param errorCode HTTP error status code * @throws IOException If error occurred when trying to send the error response. */ public static void handleError(HttpServletResponse resp, int errorCode) throws IOException { + ProxyResponse proxyResponse = constructProxyResponseByErrorCode(errorCode); + HandlerUtil.handleError(resp, proxyResponse); + } + + public static ProxyResponse constructProxyResponseByErrorCode(int errorCode) { ProxyResponse proxyResponse = new ProxyResponse(); proxyResponse.setCode(errorCode); + proxyResponse.setStatus(ProxyResponse.Status.ERROR); proxyResponse.setExecutorResponse( HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(errorCode)); - HandlerUtil.handleError(resp, proxyResponse); + return proxyResponse; + } + + public static boolean isResponseSuccessful(ProxyResponse response) { + return response.getStatus() == ProxyResponse.Status.SUCCESS; } /*** @@ -265,12 +297,17 @@ public class HandlerUtil { * @param scheme https or https * @return {@link String} keymanager port */ - public static String getKeymanagerPort(String scheme) { - String keymanagerPort = System.getProperty(HandlerConstants.IOT_KM_HTTPS_PORT_ENV_VAR); + public static String getKeyManagerPort(String scheme) { + String keyManagerPort = System.getProperty(HandlerConstants.IOT_KM_HTTPS_PORT_ENV_VAR); if (HandlerConstants.HTTP_PROTOCOL.equals(scheme)) { - keymanagerPort = System.getProperty(HandlerConstants.IOT_KM_HTTP_PORT_ENV_VAR); + keyManagerPort = System.getProperty(HandlerConstants.IOT_KM_HTTP_PORT_ENV_VAR); } - return keymanagerPort; + return keyManagerPort; + } + + public static String getKeyManagerUrl(String scheme) { + return scheme + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) + + HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(scheme); } /** @@ -324,6 +361,7 @@ public class HandlerUtil { public static void sendUnAuthorizeResponse(HttpServletResponse resp) throws IOException { ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setStatus(ProxyResponse.Status.ERROR); proxyResponse.setCode(HttpStatus.SC_UNAUTHORIZED); proxyResponse.setExecutorResponse( HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(HttpStatus.SC_UNAUTHORIZED)); @@ -333,7 +371,7 @@ public class HandlerUtil { /** * Generates the target URL for the proxy request. * - * @param req incoming {@link HttpServletRequest} + * @param req incoming {@link HttpServletRequest} * @param apiEndpoint API Endpoint URL * @return Target URL */ @@ -347,6 +385,47 @@ public class HandlerUtil { return urlBuilder.toString(); } + public static String getIOTGatewayBase(HttpServletRequest req) { + return req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_GW_HOST_ENV_VAR) + + HandlerConstants.COLON + HandlerUtil.getGatewayPort(req.getScheme()); + } + + /** + * Generate te request entity for POST and PUT requests from the incoming request. + * + * @param req incoming {@link HttpServletRequest}. + * @param proxyRequest proxy request instance. + * @throws FileUploadException If unable to parse the incoming request for multipart content extraction. + * @throws IOException If error occurred while generating the request body. + */ + public static void generateRequestEntity(HttpServletRequest req, HttpEntityEnclosingRequestBase proxyRequest) + throws FileUploadException, IOException { + if (ServletFileUpload.isMultipartContent(req)) { + ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory()); + List fileItemList = servletFileUpload.parseRequest(req); + MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create(); + entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); + for (FileItem item : fileItemList) { + if (!item.isFormField()) { + entityBuilder.addPart(item.getFieldName(), new InputStreamBody(item.getInputStream(), + ContentType.create(item.getContentType()), item.getName())); + } else { + entityBuilder.addTextBody(item.getFieldName(), item.getString()); + } + } + proxyRequest.setEntity(entityBuilder.build()); + HandlerUtil.copyRequestHeaders(req, proxyRequest, false); + } else { + if (StringUtils.isNotEmpty(req.getHeader(HttpHeaders.CONTENT_LENGTH)) || + StringUtils.isNotEmpty(req.getHeader(HttpHeaders.TRANSFER_ENCODING))) { + InputStreamEntity entity = new InputStreamEntity(req.getInputStream(), + Long.parseLong(req.getHeader(HttpHeaders.CONTENT_LENGTH))); + proxyRequest.setEntity(entity); + } + HandlerUtil.copyRequestHeaders(req, proxyRequest, true); + } + } + /*** * Constructs the application registration payload for DCR. * @@ -376,7 +455,7 @@ public class HandlerUtil { * @return {@link JsonObject} of UI configurations */ public static JsonObject getUIConfigAndPersistInSession(String uiConfigUrl, String gatewayUrl, HttpSession httpSession, - HttpServletResponse resp) throws IOException { + HttpServletResponse resp) throws IOException { HttpGet uiConfigEndpoint = new HttpGet(uiConfigUrl); ProxyResponse uiConfigResponse = HandlerUtil.execute(uiConfigEndpoint); String executorResponse = uiConfigResponse.getExecutorResponse(); @@ -448,7 +527,7 @@ public class HandlerUtil { log.error("Error occurred while sending the response into the socket. ", e); } catch (ParserConfigurationException e) { log.error("Error while creating the document builder."); - } catch ( SAXException e) { + } catch (SAXException e) { log.error("Error while parsing xml file.", e); } @@ -496,56 +575,76 @@ public class HandlerUtil { /** * Retry request again after refreshing the access token * - * @param req incoming {@link HttpServletRequest} - * @param resp resp {@link HttpServletResponse} + * @param req incoming {@link HttpServletRequest} * @param httpRequest subclass of {@link HttpRequestBase} related to the current request. * @return {@link ProxyResponse} if successful and null if failed. * @throws IOException If an error occurs when try to retry the request. */ - public static ProxyResponse retryRequestWithRefreshedToken(HttpServletRequest req, HttpServletResponse resp, - HttpRequestBase httpRequest, String apiEndpoint) throws IOException { - if (refreshToken(req, resp, apiEndpoint)) { + public static ProxyResponse retryRequestWithRefreshedToken(HttpServletRequest req, HttpRequestBase httpRequest, + String apiEndpoint) throws IOException { + ProxyResponse retryResponse = refreshToken(req, apiEndpoint); + if (isResponseSuccessful(retryResponse)) { HttpSession session = req.getSession(false); if (session == null) { log.error("Unauthorized, You are not logged in. Please log in to the portal"); - handleError(resp, HttpStatus.SC_UNAUTHORIZED); - return null; + return constructProxyResponseByErrorCode(HttpStatus.SC_UNAUTHORIZED); } httpRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(httpRequest); if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { log.error("Error occurred while invoking the API after refreshing the token."); - HandlerUtil.handleError(resp, proxyResponse); - return null; + return proxyResponse; } return proxyResponse; + } - return null; + return retryResponse; } /*** * This method is responsible to get the refresh token * * @param req {@link HttpServletRequest} - * @param resp {@link HttpServletResponse} * @return If successfully renew tokens, returns TRUE otherwise return FALSE * @throws IOException If an error occurs while witting error response to client side or invoke token renewal API */ - private static boolean refreshToken(HttpServletRequest req, HttpServletResponse resp, String keymanagerUrl) + private static ProxyResponse refreshToken(HttpServletRequest req, String keymanagerUrl) throws IOException { if (log.isDebugEnabled()) { log.debug("refreshing the token"); } - - HttpPost tokenEndpoint = new HttpPost(keymanagerUrl + HandlerConstants.TOKEN_ENDPOINT); + ProxyResponse tokenResultResponse; HttpSession session = req.getSession(false); if (session == null) { log.error("Couldn't find a session, hence it is required to login and proceed."); - handleError(resp, HttpStatus.SC_UNAUTHORIZED); - return false; + tokenResultResponse = constructProxyResponseByErrorCode(HttpStatus.SC_UNAUTHORIZED); +// handleError(resp, HttpStatus.SC_UNAUTHORIZED); + return tokenResultResponse; } authData = (AuthData) session.getAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY); + tokenResultResponse = getTokenResult(authData, keymanagerUrl); + if (tokenResultResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { + log.error("Error occurred while refreshing access token."); +// HandlerUtil.handleError(resp, tokenResultResponse); + return tokenResultResponse; + } + + JsonParser jsonParser = new JsonParser(); + JsonElement jTokenResult = jsonParser.parse(tokenResultResponse.getData()); + + if (jTokenResult.isJsonObject()) { + setNewAuthData(constructAuthDataFromTokenResult(jTokenResult, authData), session); + return tokenResultResponse; + } + + log.error("Error Occurred in token renewal process."); + tokenResultResponse = constructProxyResponseByErrorCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); +// handleError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR); + return tokenResultResponse; + } + public static ProxyResponse getTokenResult(AuthData authData, String keymanagerUrl) throws IOException { + HttpPost tokenEndpoint = new HttpPost(keymanagerUrl + HandlerConstants.TOKEN_ENDPOINT); StringEntity tokenEndpointPayload = new StringEntity( "grant_type=refresh_token&refresh_token=" + authData.getRefreshToken() + "&scope=PRODUCTION", ContentType.APPLICATION_FORM_URLENCODED); @@ -555,35 +654,75 @@ public class HandlerUtil { tokenEndpoint.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC + encodedClientApp); tokenEndpoint.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.toString()); + return HandlerUtil.execute(tokenEndpoint); + } - ProxyResponse tokenResultResponse = HandlerUtil.execute(tokenEndpoint); - if (tokenResultResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { - log.error("Error occurred while refreshing access token."); - HandlerUtil.handleError(resp, tokenResultResponse); - return false; + public static void setNewAuthData(AuthData newAuthData, HttpSession session) { + authData = newAuthData; + session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, newAuthData); + } + + public static AuthData constructAuthDataFromTokenResult(JsonElement tokenResult, AuthData authData) { + JsonObject jTokenResultAsJsonObject = tokenResult.getAsJsonObject(); + AuthData newAuthData = new AuthData(); + newAuthData.setAccessToken(jTokenResultAsJsonObject.get("access_token").getAsString()); + newAuthData.setRefreshToken(jTokenResultAsJsonObject.get("refresh_token").getAsString()); + newAuthData.setScope(jTokenResultAsJsonObject.get("scope").getAsString()); + newAuthData.setClientId(authData.getClientId()); + newAuthData.setClientSecret(authData.getClientSecret()); + newAuthData.setEncodedClientApp(authData.getEncodedClientApp()); + newAuthData.setUsername(authData.getUsername()); + return newAuthData; + } + + /** + * Copy incoming request headers to the proxy request. + * + * @param req incoming {@link HttpServletRequest} + * @param httpRequest proxy request instance. + * @param preserveContentType true if content type header needs to be preserved. + * This should be set to false when handling multipart requests as Http + * client will generate the Content-Type header automatically. + */ + public static void copyRequestHeaders(HttpServletRequest req, HttpRequestBase httpRequest, boolean preserveContentType) { + Enumeration headerNames = req.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + if (headerName.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH) || + headerName.equalsIgnoreCase(SM.COOKIE) || + (!preserveContentType && headerName.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE))) { + continue; + } + Enumeration headerValues = req.getHeaders(headerName); + while (headerValues.hasMoreElements()) { + httpRequest.setHeader(headerName, headerValues.nextElement()); + } } + } - JsonParser jsonParser = new JsonParser(); - JsonElement jTokenResult = jsonParser.parse(tokenResultResponse.getData()); + public static String getHeaderValue(String headerName, Header[] headers) { + String headerValue = null; + for(Header header : headers) { + if (header.getName().equalsIgnoreCase(headerName)) { + headerValue = header.getValue(); + } + } + return headerValue; + } - if (jTokenResult.isJsonObject()) { - JsonObject jTokenResultAsJsonObject = jTokenResult.getAsJsonObject(); - AuthData newAuthData = new AuthData(); - - newAuthData.setAccessToken(jTokenResultAsJsonObject.get("access_token").getAsString()); - newAuthData.setRefreshToken(jTokenResultAsJsonObject.get("refresh_token").getAsString()); - newAuthData.setScope(jTokenResultAsJsonObject.get("scope").getAsString()); - newAuthData.setClientId(authData.getClientId()); - newAuthData.setClientSecret(authData.getClientSecret()); - newAuthData.setEncodedClientApp(authData.getEncodedClientApp()); - newAuthData.setUsername(authData.getUsername()); - authData = newAuthData; - session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, newAuthData); - return true; + public static String getResponseString(HttpResponse response) throws IOException { + try (BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) { + StringBuilder responseBuilder = new StringBuilder(); + String line; + while ((line = rd.readLine()) != null) { + responseBuilder.append(line); + } + return responseBuilder.toString(); } + } - log.error("Error Occurred in token renewal process."); - handleError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR); - return false; + + public static boolean isPropertyDefined(String property) { + return StringUtils.isEmpty(System.getProperty(property)); } -} +} \ No newline at end of file diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaLiveSecurityFilter.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaLiveSecurityFilter.java new file mode 100644 index 0000000000..091a1e005b --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaLiveSecurityFilter.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.ui.request.interceptor.websocket; + +import io.entgra.ui.request.interceptor.beans.AuthData; +import io.entgra.ui.request.interceptor.util.HandlerConstants; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.io.IOException; + +public class GrafanaLiveSecurityFilter implements Filter { + + private static final Log log = LogFactory.getLog(GrafanaLiveSecurityFilter.class); + + @Override + public void init(FilterConfig filterConfig) { + + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) servletRequest; + HttpSession session = req.getSession(false); + if (session == null) { + log.error("Unauthorized, You are not logged in. Please log in to the portal"); + return; + } + AuthData authData = (AuthData) session.getAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY); + if (authData == null) { + log.error("Unauthorized, Access token not found in the current session"); + return; + } + filterChain.doFilter(servletRequest, servletResponse); + } + + @Override + public void destroy() { + + } +} diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaWebSocketClient.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaWebSocketClient.java new file mode 100644 index 0000000000..f6b157e4f7 --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaWebSocketClient.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.ui.request.interceptor.websocket; + +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.websocket.ClientEndpoint; +import javax.websocket.ContainerProvider; +import javax.websocket.Session; +import javax.websocket.OnOpen; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.CloseReason; +import javax.websocket.WebSocketContainer; +import java.net.URI; + +import javax.websocket.ClientEndpointConfig; +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfig; +import javax.ws.rs.core.HttpHeaders; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +@ClientEndpoint +public class GrafanaWebSocketClient extends Endpoint { + + private static final Log log = LogFactory.getLog(GrafanaWebSocketClient.class); + private Session grafanaServerSession; + private Consumer messageConsumer; + + public GrafanaWebSocketClient(URI endpointURI) { + try { + ClientEndpointConfig clientEndpointConfig = handShakeRequestConfig(); + WebSocketContainer container = ContainerProvider.getWebSocketContainer(); + container.connectToServer(this, clientEndpointConfig, endpointURI); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public ClientEndpointConfig handShakeRequestConfig() { + ClientEndpointConfig.Configurator clientEndpointConfigConfigurator = new ClientEndpointConfig.Configurator() { + @Override + public void beforeRequest(Map> headers) { + try { + headers.put(HttpHeaders.AUTHORIZATION, + Collections.singletonList(GrafanaUtil.getBasicAuthBase64Header())); + } catch (GrafanaManagementException e) { + log.error(e); + } + } + + + }; + return ClientEndpointConfig.Builder.create(). + configurator(clientEndpointConfigConfigurator).build(); + } + + @OnOpen + @Override + public void onOpen(Session grafanaServerSession, EndpointConfig endpointConfig) { + // Due to a bug (https://bz.apache.org/bugzilla/show_bug.cgi?format=multiple&id=57788) + // in the tomcat version used, this has to coded like this + grafanaServerSession.addMessageHandler(String.class, message -> messageConsumer.accept(message)); + this.grafanaServerSession = grafanaServerSession; + } + + @OnClose + @Override + public void onClose(Session session, CloseReason reason) { + log.info("Server session closed: " + reason); + this.grafanaServerSession = null; + } + + @OnError + @Override + public void onError(Session session, Throwable t) { + log.error("Error occurred in grafana server session: " + t.toString()); + t.printStackTrace(); + } + + public void sendMessageToServer(String message) { + if (grafanaServerSession.getAsyncRemote() != null) { + grafanaServerSession.getAsyncRemote().sendText(message); + } + } + + public Session getGrafanaServerSession() { + return grafanaServerSession; + } + + public void addMessageConsumer(Consumer messageConsumer) { + this.messageConsumer = messageConsumer; + } +} diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaWebSocketHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaWebSocketHandler.java new file mode 100644 index 0000000000..04dff01844 --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaWebSocketHandler.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.ui.request.interceptor.websocket; + +import io.entgra.analytics.mgt.grafana.proxy.core.exception.GrafanaEnvVariablesNotDefined; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.websocket.CloseReason; +import javax.websocket.server.ServerEndpoint; +import javax.websocket.Session; +import javax.websocket.OnOpen; +import javax.websocket.OnMessage; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +@ServerEndpoint(value = "/grafana/api/live/ws") +public class GrafanaWebSocketHandler { + private static final Log log = LogFactory.getLog(GrafanaWebSocketHandler.class); + private GrafanaWebSocketClient grafanaClient; + + @OnOpen + public void onOpen(Session browserSession) throws GrafanaEnvVariablesNotDefined { + URI grafanaUri = browserSession.getRequestURI(); + String grafanaWebSocketUrl = getGrafanaWebSocketUrl(grafanaUri); + try { + grafanaClient = new GrafanaWebSocketClient(new URI(grafanaWebSocketUrl)); + grafanaClient.addMessageConsumer(message -> sendMessageToBrowser(browserSession, message)); + } catch (URISyntaxException e) { + log.error("Invalid web socket uri provided", e); + } + } + + @OnClose + public void onClose(CloseReason reason) throws IOException { + log.info("Browser session closed: " + reason); + if (grafanaClient.getGrafanaServerSession() != null) { + grafanaClient.getGrafanaServerSession().close(); + } + } + + @OnMessage + public void onMessage(String message) { + grafanaClient.sendMessageToServer(message); + } + + @OnError + public void onError(Throwable t) { + log.error("Error occurred in grafana browser session: " + t.toString()); + t.printStackTrace(); + } + + public void sendMessageToBrowser(Session browserSession, String message) { + try { + // Avoid grafana client sending messages when browser session is already closed + if(browserSession.isOpen()) { + browserSession.getBasicRemote().sendText(message); + } + } catch (IOException e) { + log.error("Error occurred while sending message to browser", e); + } + } + + private String getGrafanaWebSocketUrl(URI requestUri) throws GrafanaEnvVariablesNotDefined { + return GrafanaUtil.getGrafanaWebSocketBase(requestUri.getScheme()) + GrafanaConstants.WS_LIVE_API; + } +} diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/webapp/WEB-INF/web.xml b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/webapp/WEB-INF/web.xml index f5ba0ae97a..32e9b34f98 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/webapp/WEB-INF/web.xml +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/webapp/WEB-INF/web.xml @@ -88,6 +88,15 @@ Cache-Control: no-store, no-cache, must-revalidate, private + + GrafanaLiveSecurityFilter + io.entgra.ui.request.interceptor.websocket.GrafanaLiveSecurityFilter + + + + GrafanaLiveSecurityFilter + /grafana/api/live/ws + HttpHeaderSecurityFilter diff --git a/components/ui-request-interceptor/pom.xml b/components/ui-request-interceptor/pom.xml index da0217aad5..1bdad315d2 100644 --- a/components/ui-request-interceptor/pom.xml +++ b/components/ui-request-interceptor/pom.xml @@ -21,7 +21,7 @@ carbon-devicemgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml index eee190b03b..de117e328b 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml @@ -21,7 +21,7 @@ org.wso2.carbon.devicemgt webapp-authenticator-framework - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/components/webapp-authenticator-framework/pom.xml b/components/webapp-authenticator-framework/pom.xml index 2d66edebd7..7d56ea512a 100644 --- a/components/webapp-authenticator-framework/pom.xml +++ b/components/webapp-authenticator-framework/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/pom.xml b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/pom.xml new file mode 100644 index 0000000000..65653078ec --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/pom.xml @@ -0,0 +1,120 @@ + + + + + + + org.wso2.carbon.devicemgt + grafana-mgt-feature + 5.0.3-SNAPSHOT + ../pom.xml + + + 4.0.0 + io.entgra.analytics.mgt.grafana.proxy.api.feature + pom + Entgra - Grafana Proxy API Feature + https://entgra.io + This feature contains the APIs required for Grafana + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + grafana-mgt-copy + package + + copy + + + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.api + ${project.version} + war + true + + ${project.build.directory}/maven-shared-archive-resources/webapps + + api#grafana-mgt#v1.0.war + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-resources + generate-resources + + copy-resources + + + src/main/resources + + + resources + + build.properties + p2.inf + + + + + + + + + org.wso2.maven + carbon-p2-plugin + + + p2-feature-generation + package + + p2-feature-gen + + + io.entgra.analytics.mgt.grafana.proxy.api + ../../../etc/feature.properties + + + + org.wso2.carbon.p2.category.type:server + + org.eclipse.equinox.p2.type.group:false + + + + + + + + + + + diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/src/main/resources/build.properties b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/src/main/resources/build.properties new file mode 100644 index 0000000000..9c86577d76 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/src/main/resources/build.properties @@ -0,0 +1 @@ +custom = true diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/src/main/resources/p2.inf b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/src/main/resources/p2.inf new file mode 100644 index 0000000000..ca042b5b3e --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/src/main/resources/p2.inf @@ -0,0 +1,3 @@ +instructions.configure = \ +org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../deployment/server/webapps/);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.analytics.mgt.grafana.proxy.api_${feature.version}/webapps/api#grafana-mgt#v1.0.war,target:${installFolder}/../../deployment/server/webapps/api#grafana-mgt#v1.0.war,overwrite:true);\ diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/pom.xml b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/pom.xml new file mode 100644 index 0000000000..366b373b99 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/pom.xml @@ -0,0 +1,140 @@ + + + + + + + org.wso2.carbon.devicemgt + grafana-mgt-feature + 5.0.3-SNAPSHOT + ../pom.xml + + + 4.0.0 + io.entgra.analytics.mgt.grafana.proxy.server.feature + pom + + Entgra - Grafana Management Server Feature + https://entgra.io + This feature contains the core bundles required for Back-end Grafana Management functionality + + + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.common + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.core + + + com.googlecode.plist + dd-plist + ${googlecode.plist.version} + + + commons-validator + commons-validator + ${commons-validator.version} + + + org.apache.cxf + cxf-rt-frontend-jaxrs + ${cxf.version} + + + com.h2database.wso2 + h2-database-engine + + + + + + + maven-resources-plugin + 2.6 + + + copy-resources + generate-resources + + copy-resources + + + src/main/resources + + + resources + + build.properties + p2.inf + + + + + + + + + org.wso2.maven + carbon-p2-plugin + ${carbon.p2.plugin.version} + + + p2-feature-generation + package + + p2-feature-gen + + + io.entgra.analytics.mgt.grafana.proxy.server + ../../../etc/feature.properties + + + org.wso2.carbon.p2.category.type:server + org.eclipse.equinox.p2.type.group:false + + + + + org.wso2.carbon.devicemgt:io.entgra.analytics.mgt.grafana.proxy.common:${carbon.device.mgt.version} + + + org.wso2.carbon.devicemgt:io.entgra.analytics.mgt.grafana.proxy.core:${carbon.device.mgt.version} + + + com.googlecode.plist:dd-plist:${googlecode.plist.version} + + + commons-validator:commons-validator:${commons-validator.version} + + + + + + + + + + + + 1.6 + + diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/build.properties b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/build.properties new file mode 100644 index 0000000000..9c86577d76 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/build.properties @@ -0,0 +1 @@ +custom = true diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/conf/grafana-config.xml b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/conf/grafana-config.xml new file mode 100644 index 0000000000..09344263f4 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/conf/grafana-config.xml @@ -0,0 +1,35 @@ + + + + + + + + 100 + + + 100 + + + + + admin + admin + + diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/conf_templates.templates.repository.conf/grafana-config.xml.j2 b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/conf_templates.templates.repository.conf/grafana-config.xml.j2 new file mode 100644 index 0000000000..09344263f4 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/conf_templates.templates.repository.conf/grafana-config.xml.j2 @@ -0,0 +1,35 @@ + + + + + + + + 100 + + + 100 + + + + + admin + admin + + diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/p2.inf b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/p2.inf new file mode 100644 index 0000000000..f269b204c4 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/p2.inf @@ -0,0 +1,4 @@ +instructions.configure = \ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.analytics.mgt.grafana.proxy.server_${feature.version}/conf/grafana-config.xml,target:${installFolder}/../../conf/grafana-config.xml,overwrite:true);\ + +instructions.unconfigure = \ \ No newline at end of file diff --git a/features/analytics-mgt/grafana-mgt/pom.xml b/features/analytics-mgt/grafana-mgt/pom.xml new file mode 100644 index 0000000000..6325a7fe3f --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/pom.xml @@ -0,0 +1,40 @@ + + + + + + + org.wso2.carbon.devicemgt + analytics-mgt-feature + 5.0.3-SNAPSHOT + ../pom.xml + + + 4.0.0 + grafana-mgt-feature + pom + Entgra - Grafana Management Feature + http://entgra.io + + + io.entgra.analytics.mgt.grafana.proxy.server.feature + io.entgra.analytics.mgt.grafana.proxy.api.feature + + + diff --git a/features/analytics-mgt/pom.xml b/features/analytics-mgt/pom.xml new file mode 100644 index 0000000000..c447f2013c --- /dev/null +++ b/features/analytics-mgt/pom.xml @@ -0,0 +1,19 @@ + + + + carbon-devicemgt + org.wso2.carbon.devicemgt + 5.0.3-SNAPSHOT + ../../pom.xml + + + 4.0.0 + analytics-mgt-feature + pom + Entgra - Analytics Management Feature + http://entgra.io + + + grafana-mgt + + diff --git a/features/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.feature/pom.xml b/features/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.feature/pom.xml index ff6f03085d..0f4ba11857 100644 --- a/features/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.feature/pom.xml +++ b/features/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.feature/pom.xml @@ -21,7 +21,7 @@ org.wso2.carbon.devicemgt apimgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/pom.xml b/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/pom.xml index 0436f66bd9..8448dbce2a 100644 --- a/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/pom.xml +++ b/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/pom.xml @@ -21,7 +21,7 @@ org.wso2.carbon.devicemgt apimgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/apimgt-extensions/pom.xml b/features/apimgt-extensions/pom.xml index b27d5b62da..f7cac5747f 100644 --- a/features/apimgt-extensions/pom.xml +++ b/features/apimgt-extensions/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/features/application-mgt/io.entgra.application.mgt.api.feature/pom.xml b/features/application-mgt/io.entgra.application.mgt.api.feature/pom.xml index c468368965..679943cec8 100644 --- a/features/application-mgt/io.entgra.application.mgt.api.feature/pom.xml +++ b/features/application-mgt/io.entgra.application.mgt.api.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt application-mgt-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/application-mgt/io.entgra.application.mgt.server.feature/pom.xml b/features/application-mgt/io.entgra.application.mgt.server.feature/pom.xml index efdfec28b5..95ff108355 100644 --- a/features/application-mgt/io.entgra.application.mgt.server.feature/pom.xml +++ b/features/application-mgt/io.entgra.application.mgt.server.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt application-mgt-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/application-mgt/pom.xml b/features/application-mgt/pom.xml index 533845f0fb..58d21c6eb2 100644 --- a/features/application-mgt/pom.xml +++ b/features/application-mgt/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/features/certificate-mgt/org.wso2.carbon.certificate.mgt.api.feature/pom.xml b/features/certificate-mgt/org.wso2.carbon.certificate.mgt.api.feature/pom.xml index aaecb0c3a5..c8617a1e09 100644 --- a/features/certificate-mgt/org.wso2.carbon.certificate.mgt.api.feature/pom.xml +++ b/features/certificate-mgt/org.wso2.carbon.certificate.mgt.api.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt certificate-mgt-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api.feature/pom.xml b/features/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api.feature/pom.xml index 1e4becdceb..599420d441 100644 --- a/features/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api.feature/pom.xml +++ b/features/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt certificate-mgt-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/certificate-mgt/org.wso2.carbon.certificate.mgt.server.feature/pom.xml b/features/certificate-mgt/org.wso2.carbon.certificate.mgt.server.feature/pom.xml index f0a7162ba9..0b6407602c 100644 --- a/features/certificate-mgt/org.wso2.carbon.certificate.mgt.server.feature/pom.xml +++ b/features/certificate-mgt/org.wso2.carbon.certificate.mgt.server.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt certificate-mgt-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/certificate-mgt/pom.xml b/features/certificate-mgt/pom.xml index 57fb502342..748f5a4162 100644 --- a/features/certificate-mgt/pom.xml +++ b/features/certificate-mgt/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer.feature/pom.xml b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer.feature/pom.xml index e86ca55b36..7ee1e6838a 100644 --- a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer.feature/pom.xml +++ b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm.feature/pom.xml b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm.feature/pom.xml index f4691b0b76..f65fd9cc67 100644 --- a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm.feature/pom.xml +++ b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http.feature/pom.xml b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http.feature/pom.xml index 59f61d98fd..afb9dee99f 100644 --- a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http.feature/pom.xml +++ b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt.feature/pom.xml b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt.feature/pom.xml index d00139fadf..050fa8ab1d 100644 --- a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt.feature/pom.xml +++ b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp.feature/pom.xml b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp.feature/pom.xml index ca96e26819..d0367a8b9f 100644 --- a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp.feature/pom.xml +++ b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/device-mgt-extensions/pom.xml b/features/device-mgt-extensions/pom.xml index 0badd06753..bae0c55c10 100644 --- a/features/device-mgt-extensions/pom.xml +++ b/features/device-mgt-extensions/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.api.feature/pom.xml b/features/device-mgt/org.wso2.carbon.device.mgt.api.feature/pom.xml index b89c530906..68f18bb6fb 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.api.feature/pom.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.api.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/pom.xml b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/pom.xml index 03f77097ea..f6b8799ea7 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/pom.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/cdm-config.xml b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/cdm-config.xml index d15901005e..50e3afd824 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/cdm-config.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/cdm-config.xml @@ -178,5 +178,9 @@ + + + + diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/datasources/reporting-mgt-datasources.xml b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/datasources/reporting-mgt-datasources.xml new file mode 100644 index 0000000000..c070808f0a --- /dev/null +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/datasources/reporting-mgt-datasources.xml @@ -0,0 +1,48 @@ + + + + + org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader + + + + + + jdbc/MDM_REPORTS_DS + The datasource used for Report Management + + jdbc/MDM_REPORTS_DS + + + + jdbc:mysql://localhost:3306/DM_DB?autoReconnect=true&relaxAutoCommit=true&useSSL=false + root + root + com.mysql.cj.jdbc.Driver + 50 + 60000 + true + SELECT 1 + 30000 + + + + + + diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/mdm-ui-config.xml b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/mdm-ui-config.xml index 88d9b47d29..22f4ef0175 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/mdm-ui-config.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/mdm-ui-config.xml @@ -26,6 +26,7 @@ 10000 + analytics_management application_management device_management subscription_management @@ -34,6 +35,7 @@ true + perm:grafana:api:view perm:app:review:view perm:app:review:update perm:app:publisher:view diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/reporting-mgt.xml b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/reporting-mgt.xml new file mode 100644 index 0000000000..b768822971 --- /dev/null +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/reporting-mgt.xml @@ -0,0 +1,21 @@ + + + + jdbc/MDM_REPORTS_DS + diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/cdm-config.xml.j2 b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/cdm-config.xml.j2 index 5a25843f94..19feedc4b1 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/cdm-config.xml.j2 +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/cdm-config.xml.j2 @@ -44,7 +44,7 @@ org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp.XMPPBasedPushNotificationProvider {% if device_mgt_conf.push_notification_conf.push_notification_providers is defined %} {%- for push_notification_provider in device_mgt_conf.push_notification_conf.push_notification_providers -%} - {{push_notification_provider.name}} + {{push_notification_provider}} {% endfor %} {% endif %} @@ -311,5 +311,34 @@ + + + + + + + + + + + + {% if device_mgt_conf.operation_timeout_conf is defined %} + {% for timeout_conf in device_mgt_conf.operation_timeout_conf %} + + + {% for device_type in timeout_conf.device_types %} + {{device_type}} + {% endfor %} + + + {{timeout_conf.code}} + {{timeout_conf.initial_status}} + {{timeout_conf.timeout}} + {{timeout_conf.next_status}} + + {% endfor %} + {% endif%} + + diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/datasources/reporting-mgt-datasources.xml.j2 b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/datasources/reporting-mgt-datasources.xml.j2 new file mode 100644 index 0000000000..c070808f0a --- /dev/null +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/datasources/reporting-mgt-datasources.xml.j2 @@ -0,0 +1,48 @@ + + + + + org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader + + + + + + jdbc/MDM_REPORTS_DS + The datasource used for Report Management + + jdbc/MDM_REPORTS_DS + + + + jdbc:mysql://localhost:3306/DM_DB?autoReconnect=true&relaxAutoCommit=true&useSSL=false + root + root + com.mysql.cj.jdbc.Driver + 50 + 60000 + true + SELECT 1 + 30000 + + + + + + diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/mdm-ui-config.xml.j2 b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/mdm-ui-config.xml.j2 index 077e55c9ed..845b8cb5db 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/mdm-ui-config.xml.j2 +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/mdm-ui-config.xml.j2 @@ -215,6 +215,7 @@ perm:ios:app-configurations perm:mac-os:shut-down perm:mac-os:restart + perm:android:info {% if mdm_ui_conf.scopes is defined %} {%- for scope in mdm_ui_conf.scopes -%} {{scope}} diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/reporting-mgt.xml.j2 b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/reporting-mgt.xml.j2 new file mode 100644 index 0000000000..b768822971 --- /dev/null +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/reporting-mgt.xml.j2 @@ -0,0 +1,21 @@ + + + + jdbc/MDM_REPORTS_DS + diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql index ed9ce41c7d..6372b87e16 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql @@ -108,7 +108,19 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT uk_dm_device_enrolment UNIQUE (DEVICE_ID, OWNER, OWNERSHIP, TENANT_ID) ); - +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING ( ID INTEGER AUTO_INCREMENT NOT NULL, ENROLMENT_ID INTEGER NOT NULL, diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql index f0cbcf2458..bd301d6641 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql @@ -120,6 +120,20 @@ CREATE TABLE DM_OPERATION ( PRIMARY KEY (ID) ); +IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[DM_DEVICE_STATUS]') AND TYPE IN (N'U')) +CREATE TABLE DM_DEVICE_STATUS ( + ID INTEGER IDENTITY(1,1) NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME DATETIME2 DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_DEVICE FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT FK_DM_DEVICE_STATUS_ENROLMENT FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[DM_ENROLMENT]') AND TYPE IN (N'U')) CREATE TABLE DM_ENROLMENT ( diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql index e11919b0a0..b191a177b7 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql @@ -122,7 +122,19 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( CONSTRAINT FK_DM_DEVICE_ENROLMENT FOREIGN KEY (DEVICE_ID) REFERENCES DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION )ENGINE = InnoDB; - +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_DEVICE FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT FK_DM_DEVICE_STATUS_ENROLMENT FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +)ENGINE = InnoDB; CREATE INDEX IDX_ENROLMENT_FK_DEVICE_ID ON DM_ENROLMENT(DEVICE_ID); CREATE INDEX IDX_ENROLMENT_DEVICE_ID_TENANT_ID ON DM_ENROLMENT(DEVICE_ID, TENANT_ID); CREATE INDEX IDX_ENROLMENT_DEVICE_ID_TENANT_ID_STATUS ON DM_ENROLMENT(DEVICE_ID, TENANT_ID, STATUS); diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql index 00adba3500..878d609543 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql @@ -197,6 +197,7 @@ WHEN (NEW.ID IS NULL) / + CREATE TABLE DM_ENROLMENT ( ID NUMBER(10) NOT NULL, DEVICE_ID NUMBER(10) NOT NULL, @@ -212,6 +213,18 @@ CREATE TABLE DM_ENROLMENT ( DM_DEVICE (ID) ) / +CREATE TABLE DM_DEVICE_STATUS ( + ID NUMBER(10) NOT NULL, + ENROLMENT_ID NUMBER(10) NOT NULL, + DEVICE_ID NUMBER(10) NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP(0) DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_DEVICE FOREIGN KEY (DEVICE_ID) REFERENCES DM_DEVICE (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_ENROLMENT FOREIGN KEY (ENROLMENT_ID) REFERENCES DM_ENROLMENT (ID) +); +/ -- Generate ID using sequence and trigger CREATE SEQUENCE DM_ENROLMENT_seq START WITH 1 INCREMENT BY 1 NOCACHE / diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql index d2537af3a8..a9f08b45f8 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql @@ -102,6 +102,8 @@ CREATE TABLE IF NOT EXISTS DM_OPERATION ( PRIMARY KEY (ID) ); + + CREATE SEQUENCE DM_ENROLMENT_seq; CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( @@ -118,7 +120,21 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( CONSTRAINT FK_DM_DEVICE_ENROLMENT FOREIGN KEY (DEVICE_ID) REFERENCES DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); +CREATE SEQUENCE DM_DEVICE_STATUS_seq; +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER DEFAULT NEXTVAL ('DM_DEVICE_STATUS_seq') NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP(0) DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_DEVICE FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT FK_DM_DEVICE_STATUS_ENROLMENT FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); CREATE INDEX IDX_ENROLMENT_FK_DEVICE_ID ON DM_ENROLMENT(DEVICE_ID); CREATE INDEX IDX_ENROLMENT_DEVICE_ID_TENANT_ID ON DM_ENROLMENT(DEVICE_ID, TENANT_ID); CREATE INDEX IDX_ENROLMENT_DEVICE_ID_TENANT_ID_STATUS ON DM_ENROLMENT(DEVICE_ID, TENANT_ID, STATUS); diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/p2.inf b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/p2.inf index 5ebad17202..178b0961b0 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/p2.inf +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/p2.inf @@ -1,7 +1,9 @@ instructions.configure = \ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/datasources/cdm-datasources.xml,target:${installFolder}/../../conf/datasources/cdm-datasources.xml,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/datasources/reporting-mgt-datasources.xml,target:${installFolder}/../../conf/datasources/reporting-mgt-datasources.xml,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/cdm-config.xml,target:${installFolder}/../../conf/cdm-config.xml,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/mdm-ui-config.xml,target:${installFolder}/../../conf/mdm-ui-config.xml,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/reporting-mgt.xml,target:${installFolder}/../../conf/reporting-mgt.xml,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/license-config.xml,target:${installFolder}/../../conf/etc/license-config.xml,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/remote-appmanager-config.xml,target:${installFolder}/../../conf/etc/remote-appmanager-config.xml,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/dbscripts/cdm,target:${installFolder}/../../../dbscripts/cdm,overwrite:true);\ diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.extensions.feature/pom.xml b/features/device-mgt/org.wso2.carbon.device.mgt.extensions.feature/pom.xml index 2e116892e6..de0db8befd 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.extensions.feature/pom.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.extensions.feature/pom.xml @@ -4,7 +4,7 @@ org.wso2.carbon.devicemgt device-mgt-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.feature/pom.xml b/features/device-mgt/org.wso2.carbon.device.mgt.feature/pom.xml index e2730296be..ac676b58dc 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.feature/pom.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/pom.xml b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/pom.xml index a059dbe4a9..e253b3077a 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/pom.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/device-mgt/pom.xml b/features/device-mgt/pom.xml index 8232f41213..4be64209a2 100644 --- a/features/device-mgt/pom.xml +++ b/features/device-mgt/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/features/heartbeat-management/io.entgra.server.heart.beat.feature/pom.xml b/features/heartbeat-management/io.entgra.server.heart.beat.feature/pom.xml index cd2992f106..5ba3411991 100644 --- a/features/heartbeat-management/io.entgra.server.heart.beat.feature/pom.xml +++ b/features/heartbeat-management/io.entgra.server.heart.beat.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt heart-beat-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/heartbeat-management/pom.xml b/features/heartbeat-management/pom.xml index 6d4368557f..097b375aae 100644 --- a/features/heartbeat-management/pom.xml +++ b/features/heartbeat-management/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/features/jwt-client/org.wso2.carbon.identity.jwt.client.extension.feature/pom.xml b/features/jwt-client/org.wso2.carbon.identity.jwt.client.extension.feature/pom.xml index a7e0498cfc..1c2bea1512 100644 --- a/features/jwt-client/org.wso2.carbon.identity.jwt.client.extension.feature/pom.xml +++ b/features/jwt-client/org.wso2.carbon.identity.jwt.client.extension.feature/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.devicemgt jwt-client-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/jwt-client/pom.xml b/features/jwt-client/pom.xml index 9cbc01241f..a9c12a6bf0 100644 --- a/features/jwt-client/pom.xml +++ b/features/jwt-client/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/features/policy-mgt/org.wso2.carbon.policy.mgt.server.feature/pom.xml b/features/policy-mgt/org.wso2.carbon.policy.mgt.server.feature/pom.xml index a3b5dc52b0..c884210b8e 100644 --- a/features/policy-mgt/org.wso2.carbon.policy.mgt.server.feature/pom.xml +++ b/features/policy-mgt/org.wso2.carbon.policy.mgt.server.feature/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.devicemgt policy-mgt-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/policy-mgt/pom.xml b/features/policy-mgt/pom.xml index 0a225108ea..120a70506c 100644 --- a/features/policy-mgt/pom.xml +++ b/features/policy-mgt/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/features/transport-mgt/email-sender/org.wso2.carbon.email.sender.feature/pom.xml b/features/transport-mgt/email-sender/org.wso2.carbon.email.sender.feature/pom.xml index 4d14ab0da2..2847488dff 100644 --- a/features/transport-mgt/email-sender/org.wso2.carbon.email.sender.feature/pom.xml +++ b/features/transport-mgt/email-sender/org.wso2.carbon.email.sender.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt email-sender-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/transport-mgt/email-sender/pom.xml b/features/transport-mgt/email-sender/pom.xml index 93f44cad18..0662490003 100644 --- a/features/transport-mgt/email-sender/pom.xml +++ b/features/transport-mgt/email-sender/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt transport-mgt-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/transport-mgt/pom.xml b/features/transport-mgt/pom.xml index 51f6c27a4d..52634d75c6 100644 --- a/features/transport-mgt/pom.xml +++ b/features/transport-mgt/pom.xml @@ -3,7 +3,7 @@ carbon-devicemgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api.feature/pom.xml b/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api.feature/pom.xml index 3a9adddc28..05ce668d47 100644 --- a/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api.feature/pom.xml +++ b/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt sms-handler-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.server.feature/pom.xml b/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.server.feature/pom.xml index 9704347dc9..250da5ae94 100644 --- a/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.server.feature/pom.xml +++ b/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.server.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt sms-handler-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/transport-mgt/sms-handler/pom.xml b/features/transport-mgt/sms-handler/pom.xml index 4e898834ca..aea81035a4 100644 --- a/features/transport-mgt/sms-handler/pom.xml +++ b/features/transport-mgt/sms-handler/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt transport-mgt-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/ui-request-interceptor/io.entgra.ui.request.interceptor.feature/pom.xml b/features/ui-request-interceptor/io.entgra.ui.request.interceptor.feature/pom.xml index 3c80a5009c..808cc0fe01 100644 --- a/features/ui-request-interceptor/io.entgra.ui.request.interceptor.feature/pom.xml +++ b/features/ui-request-interceptor/io.entgra.ui.request.interceptor.feature/pom.xml @@ -21,7 +21,7 @@ ui-request-interceptor-feature io.entgra.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT 4.0.0 diff --git a/features/ui-request-interceptor/pom.xml b/features/ui-request-interceptor/pom.xml index 8d1f9a30d0..ec14a0bf8d 100644 --- a/features/ui-request-interceptor/pom.xml +++ b/features/ui-request-interceptor/pom.xml @@ -21,7 +21,7 @@ carbon-devicemgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/features/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework.server.feature/pom.xml b/features/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework.server.feature/pom.xml index 697b967c04..ca338ae0f3 100644 --- a/features/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework.server.feature/pom.xml +++ b/features/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework.server.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt webapp-authenticator-framework-feature - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../pom.xml diff --git a/features/webapp-authenticator-framework/pom.xml b/features/webapp-authenticator-framework/pom.xml index 4109747f00..3812c48e6e 100644 --- a/features/webapp-authenticator-framework/pom.xml +++ b/features/webapp-authenticator-framework/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT ../../pom.xml diff --git a/pom.xml b/pom.xml index 6ee8d4c5ee..86ffab18b8 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt pom - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT WSO2 Carbon - Device Management - Parent http://wso2.org WSO2 Connected Device Manager Components @@ -44,6 +44,7 @@ components/certificate-mgt components/ui-request-interceptor components/transport-mgt + components/analytics-mgt components/webapp-authenticator-framework features/device-mgt features/apimgt-extensions @@ -55,6 +56,7 @@ features/jwt-client features/device-mgt-extensions features/transport-mgt + features/analytics-mgt features/webapp-authenticator-framework @@ -287,6 +289,18 @@ org.wso2.carbon.apimgt.application.extension.api ${carbon.device.mgt.version} + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.api.feature + zip + ${carbon.device.mgt.version} + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.server.feature + zip + ${carbon.device.mgt.version} + org.wso2.carbon.devicemgt io.entgra.application.mgt.server.feature @@ -314,6 +328,21 @@ io.entgra.transport.mgt.sms.handler.common ${carbon.device.mgt.version} + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.api + ${carbon.device.mgt.version} + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.core + ${carbon.device.mgt.version} + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.common + ${carbon.device.mgt.version} + @@ -1259,6 +1288,11 @@ gson ${google.gson.version} + + com.google.guava + guava + ${google.guava.version} + com.squareup.okhttp3 okhttp @@ -2042,7 +2076,7 @@ 1.2.11.wso2v10 - 5.0.1-SNAPSHOT + 5.0.3-SNAPSHOT 4.7.35 @@ -2085,6 +2119,7 @@ 3.0.0.wso2v1 1.3 2.8.5 + 31.0.1-jre 4.6.0 1.13.0 9.3.1 @@ -2199,7 +2234,7 @@ 7.0.85 - 1.0 + 1.1 1.13.1 5.1.13.RELEASE