diff options
Diffstat (limited to 'policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server')
15 files changed, 497 insertions, 384 deletions
diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/AuthorizationFilter.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/AuthorizationFilter.java index d884b869..44204cfd 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/AuthorizationFilter.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/AuthorizationFilter.java @@ -3,6 +3,7 @@ * ONAP * ================================================================================ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2023-2024 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,36 +21,33 @@ package org.onap.policy.common.endpoints.http.server; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class AuthorizationFilter implements Filter { - private static Logger logger = LoggerFactory.getLogger(AuthorizationFilter.class); + private static final Logger logger = LoggerFactory.getLogger(AuthorizationFilter.class); @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - if (!(servletRequest instanceof HttpServletRequest)) { + if (!(servletRequest instanceof HttpServletRequest request)) { throw new ServletException("Not an HttpServletRequest instance"); } - if (!(servletResponse instanceof HttpServletResponse)) { + if (!(servletResponse instanceof HttpServletResponse response)) { throw new ServletException("Not an HttpServletResponse instance"); } - HttpServletRequest request = (HttpServletRequest) servletRequest; - HttpServletResponse response = (HttpServletResponse) servletResponse; - String role = getRole(request); boolean authorized = request.isUserInRole(role); diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java index 0adf782c..a20c125d 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java @@ -3,6 +3,8 @@ * ONAP * ================================================================================ * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2020, 2024 Nordix Foundation. + * Modifications Copyright (C) 2021 Bell Canada. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,18 +52,6 @@ public interface HttpServletServer extends Startable { void setBasicAuthentication(String user, String password, String relativeUriPath); /** - * Enables AAF based authentication. - * - * @param filterPath filter path - */ - void setAafAuthentication(String filterPath); - - /** - * Checks if AAF authentication has been enabled. - */ - boolean isAaf(); - - /** * Sets the serialization provider to be used when classes are added to the service. * * @param provider the provider to use for message serialization and de-serialization @@ -83,22 +73,44 @@ public interface HttpServletServer extends Startable { * @param restClass JAX-RS API Class * * @throws IllegalArgumentException unable to process because of invalid input - * @throws IllegalStateException unable to process because of invalid state + * @throws IllegalStateException unable to process because of invalid state, for example + * different types of servlets map to the same servletPath */ void addServletClass(String servletPath, String restClass); /** + * Adds a Java Servlet. + * + * @param servletPath servlet path + * @param plainServletClass servlet class + */ + void addStdServletClass(String servletPath, String plainServletClass); + + /** * Adds a package containing JAX-RS classes to serve REST requests. * * @param servletPath servlet path * @param restPackage JAX-RS package to scan * * @throws IllegalArgumentException unable to process because of invalid input - * @throws IllegalStateException unable to process because of invalid state + * @throws IllegalStateException unable to process because of invalid state, for example + * different types of servlets map to the same servletPath */ void addServletPackage(String servletPath, String restPackage); /** + * Add a static resource path to manage static resources. + * + * @param servletPath servlet path + * @param resourceBase static resources folder + * + * @throws IllegalArgumentException unable to process because of invalid input + * @throws IllegalStateException unable to process because of invalid state, for example + * different types of servlets map to the same servletPath + */ + void addServletResource(String servletPath, String resourceBase); + + /** * Blocking start of the http server. * * @param maxWaitTime max time to wait for the start to take place @@ -108,4 +120,14 @@ public interface HttpServletServer extends Startable { * @throws InterruptedException if the blocking operation is interrupted */ boolean waitedStart(long maxWaitTime) throws InterruptedException; + + /** + * Are prometheus metrics enabled?. + */ + public boolean isPrometheus(); + + /** + * Enable prometheus metrics. + */ + public void setPrometheus(String metricsPath); } diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactory.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactory.java index 0c30e3e5..7ce0becd 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactory.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactory.java @@ -3,13 +3,14 @@ * policy-endpoints * ================================================================================ * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2020,2023 Nordix Foundation. * ================================================================================ * Licensed 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. @@ -29,23 +30,24 @@ import java.util.Properties; public interface HttpServletServerFactory { /** - * Builds an http or https server with support for servlets. - * + * Builds a http or https rest server with support for servlets. + * * @param name name * @param https use secured http over tls connection * @param host binding host * @param port port + * @param sniHostCheck SNI Host checking flag * @param contextPath server base path * @param swagger enable swagger documentation * @param managed is it managed by infrastructure * @return http server * @throws IllegalArgumentException when invalid parameters are provided */ - HttpServletServer build(String name, boolean https, String host, int port, String contextPath, boolean swagger, - boolean managed); + HttpServletServer build(String name, boolean https, String host, int port, boolean sniHostCheck, String contextPath, + boolean swagger, boolean managed); /** - * Builds an http server with support for servlets. + * Builds a http rest server with support for servlets. * * @param name name * @param host binding host @@ -59,8 +61,8 @@ public interface HttpServletServerFactory { HttpServletServer build(String name, String host, int port, String contextPath, boolean swagger, boolean managed); /** - * Build a list of http servers per properties. - * + * Build a list of http rest servers per properties. + * * @param properties properties based configuration * @return list of http servers * @throws IllegalArgumentException when invalid parameters are provided @@ -68,8 +70,24 @@ public interface HttpServletServerFactory { List<HttpServletServer> build(Properties properties); /** + * Builds a http or https server to manage static resources. + * + * @param name name + * @param https use secured http over tls connection + * @param host binding host + * @param port port + * @param sniHostCheck SNI Host checking flag + * @param contextPath server base path + * @param managed is it managed by infrastructure + * @return http server + * @throws IllegalArgumentException when invalid parameters are provided + */ + HttpServletServer buildStaticResourceServer(String name, boolean https, String host, int port, boolean sniHostCheck, + String contextPath, boolean managed); + + /** * Gets a server based on the port. - * + * * @param port port * @return http server */ @@ -77,14 +95,14 @@ public interface HttpServletServerFactory { /** * Provides an inventory of servers. - * + * * @return inventory of servers */ List<HttpServletServer> inventory(); /** * Destroys server bound to a port. - * + * * @param port the port the server is bound to */ void destroy(int port); diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactoryInstance.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactoryInstance.java index a56be701..58e27cbf 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactoryInstance.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactoryInstance.java @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2019, 2021 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,8 +20,11 @@ package org.onap.policy.common.endpoints.http.server; +import lombok.AccessLevel; import lombok.Getter; +import lombok.NoArgsConstructor; +@NoArgsConstructor(access = AccessLevel.PRIVATE) public class HttpServletServerFactoryInstance { /** @@ -29,10 +32,4 @@ public class HttpServletServerFactoryInstance { */ @Getter private static final HttpServletServerFactory serverFactory = new IndexedHttpServletServerFactory(); - - - private HttpServletServerFactoryInstance() { - // do nothing - } - } diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/IndexedHttpServletServerFactory.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/IndexedHttpServletServerFactory.java index 517ad208..7c9aca4c 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/IndexedHttpServletServerFactory.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/IndexedHttpServletServerFactory.java @@ -2,7 +2,9 @@ * ============LICENSE_START======================================================= * ONAP Policy Engine - Common Modules * ================================================================================ - * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2019, 2021 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2020,2023-2024 Nordix Foundation. + * Modifications Copyright (C) 2021 Bell Canada. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,12 +22,14 @@ package org.onap.policy.common.endpoints.http.server; +import com.google.re2j.Pattern; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Properties; import org.apache.commons.lang3.StringUtils; import org.onap.policy.common.endpoints.http.server.internal.JettyJerseyServer; +import org.onap.policy.common.endpoints.http.server.internal.JettyStaticResourceServer; import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties; import org.onap.policy.common.endpoints.utils.PropertyUtils; import org.slf4j.Logger; @@ -35,8 +39,7 @@ import org.slf4j.LoggerFactory; * Indexed factory implementation. */ class IndexedHttpServletServerFactory implements HttpServletServerFactory { - - private static final String SPACES_COMMA_SPACES = "\\s*,\\s*"; + private static final Pattern COMMA_SPACE_PAT = Pattern.compile("\\s*,\\s*"); /** * logger. @@ -49,14 +52,14 @@ class IndexedHttpServletServerFactory implements HttpServletServerFactory { protected HashMap<Integer, HttpServletServer> servers = new HashMap<>(); @Override - public synchronized HttpServletServer build(String name, boolean https, String host, int port, String contextPath, - boolean swagger, boolean managed) { + public synchronized HttpServletServer build(String name, boolean https, String host, int port, boolean sniHostCheck, + String contextPath, boolean swagger, boolean managed) { if (servers.containsKey(port)) { return servers.get(port); } - JettyJerseyServer server = new JettyJerseyServer(name, https, host, port, contextPath, swagger); + var server = new JettyJerseyServer(name, https, host, port, sniHostCheck, contextPath, swagger); if (managed) { servers.put(port, server); } @@ -67,7 +70,7 @@ class IndexedHttpServletServerFactory implements HttpServletServerFactory { @Override public synchronized HttpServletServer build(String name, String host, int port, String contextPath, boolean swagger, boolean managed) { - return build(name, false, host, port, contextPath, swagger, managed); + return build(name, false, host, port, false, contextPath, swagger, managed); } @Override @@ -81,103 +84,132 @@ class IndexedHttpServletServerFactory implements HttpServletServerFactory { return serviceList; } - for (String serviceName : serviceNames.split(SPACES_COMMA_SPACES)) { + for (String serviceName : COMMA_SPACE_PAT.split(serviceNames)) { addService(serviceList, serviceName, properties); } return serviceList; } + @Override + public HttpServletServer buildStaticResourceServer(String name, boolean https, String host, int port, + boolean sniHostCheck, String contextPath, boolean managed) { + if (servers.containsKey(port)) { + return servers.get(port); + } + + var server = new JettyStaticResourceServer(name, https, host, port, sniHostCheck, contextPath); + if (managed) { + servers.put(port, server); + } + + return server; + } + private void addService(ArrayList<HttpServletServer> serviceList, String serviceName, Properties properties) { String servicePrefix = PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + serviceName; - PropertyUtils props = new PropertyUtils(properties, servicePrefix, + var props = new PropertyUtils(properties, servicePrefix, (name, value, ex) -> logger - .warn("{}: {} {} is in invalid format for http service {} ", this, name, value, serviceName)); + .warn("{}: {} {} is in invalid format for http service {} ", this, name, value, serviceName)); - int servicePort = props.getInteger(PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX, -1); + var servicePort = props.getInteger(PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX, -1); if (servicePort < 0) { logger.warn("No HTTP port for service in {}", serviceName); return; } - final String hostName = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX, null); - final String contextUriPath = - props.getString(PolicyEndPointProperties.PROPERTY_HTTP_CONTEXT_URIPATH_SUFFIX, null); - boolean managed = props.getBoolean(PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, true); - boolean swagger = props.getBoolean(PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, false); - boolean https = props.getBoolean(PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX, false); + final var hostName = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX, null); + final var contextUriPath = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_CONTEXT_URIPATH_SUFFIX, null); + var managed = props.getBoolean(PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, true); + var swagger = props.getBoolean(PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, false); + var https = props.getBoolean(PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX, false); + var sniHostCheck = props.getBoolean(PolicyEndPointProperties.PROPERTY_HTTP_SNI_HOST_CHECK_SUFFIX, false); // create the service - HttpServletServer service = build(serviceName, https, hostName, servicePort, contextUriPath, swagger, managed); + HttpServletServer service = + build(serviceName, https, hostName, servicePort, sniHostCheck, contextUriPath, swagger, managed); // configure the service setSerializationProvider(props, service); - setAuthentication(props, service, contextUriPath); + setAuthentication(props, service); - final String restUriPath = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_REST_URIPATH_SUFFIX, null); + final var restUriPath = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_REST_URIPATH_SUFFIX, null); addFilterClasses(props, service, restUriPath); - addServletClasses(props, service, restUriPath); + addRestServletClasses(props, service, restUriPath); addServletPackages(props, service, restUriPath); + addServletClass(props, service); + setPrometheus(props, service); + serviceList.add(service); } private void setSerializationProvider(PropertyUtils props, HttpServletServer service) { - final String classProv = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER, null); + final var classProv = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER, null); if (!StringUtils.isBlank(classProv)) { service.setSerializationProvider(classProv); } } - private void setAuthentication(PropertyUtils props, HttpServletServer service, final String contextUriPath) { - /* authentication method either AAF or HTTP Basic Auth */ - - boolean aaf = props.getBoolean(PolicyEndPointProperties.PROPERTY_AAF_SUFFIX, false); - final String userName = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_AUTH_USERNAME_SUFFIX, null); - final String password = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_AUTH_PASSWORD_SUFFIX, null); - final String authUriPath = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_AUTH_URIPATH_SUFFIX, null); + private void setAuthentication(PropertyUtils props, HttpServletServer service) { + /* authentication method HTTP Basic Auth */ + final var userName = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_AUTH_USERNAME_SUFFIX, null); + final var password = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_AUTH_PASSWORD_SUFFIX, null); + final var authUriPath = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_AUTH_URIPATH_SUFFIX, null); - if (aaf) { - service.setAafAuthentication(contextUriPath); - } else if (!StringUtils.isBlank(userName) && !StringUtils.isBlank(password)) { + if (!StringUtils.isBlank(userName) && !StringUtils.isBlank(password)) { service.setBasicAuthentication(userName, password, authUriPath); } } + private void setPrometheus(PropertyUtils props, HttpServletServer service) { + if (props.getBoolean(PolicyEndPointProperties.PROPERTY_HTTP_PROMETHEUS_SUFFIX, false)) { + service.setPrometheus("/metrics"); + } + } + private void addFilterClasses(PropertyUtils props, HttpServletServer service, final String restUriPath) { - final String filterClasses = - props.getString(PolicyEndPointProperties.PROPERTY_HTTP_FILTER_CLASSES_SUFFIX, null); + final var filterClasses = + props.getString(PolicyEndPointProperties.PROPERTY_HTTP_FILTER_CLASSES_SUFFIX, null); if (!StringUtils.isBlank(filterClasses)) { - for (String filterClass : filterClasses.split(SPACES_COMMA_SPACES)) { + for (String filterClass : COMMA_SPACE_PAT.split(filterClasses)) { service.addFilterClass(restUriPath, filterClass); } } } - private void addServletClasses(PropertyUtils props, HttpServletServer service, final String restUriPath) { - - final String restClasses = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX, null); + private void addRestServletClasses(PropertyUtils props, HttpServletServer service, final String restUriPath) { + final var restClasses = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX, null); if (!StringUtils.isBlank(restClasses)) { - for (String restClass : restClasses.split(SPACES_COMMA_SPACES)) { + for (String restClass : COMMA_SPACE_PAT.split(restClasses)) { service.addServletClass(restUriPath, restClass); } } } + private void addServletClass(PropertyUtils props, HttpServletServer service) { + var servletClass = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_CLASS_SUFFIX, null); + var servletUriPath = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_URIPATH_SUFFIX, null); + + if (!StringUtils.isBlank(servletClass) && !StringUtils.isBlank(servletUriPath)) { + service.addStdServletClass(servletUriPath, servletClass); + } + } + private void addServletPackages(PropertyUtils props, HttpServletServer service, final String restUriPath) { - final String restPackages = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_REST_PACKAGES_SUFFIX, null); + final var restPackages = props.getString(PolicyEndPointProperties.PROPERTY_HTTP_REST_PACKAGES_SUFFIX, null); if (!StringUtils.isBlank(restPackages)) { - for (String restPackage : restPackages.split(SPACES_COMMA_SPACES)) { + for (String restPackage : COMMA_SPACE_PAT.split(restPackages)) { service.addServletPackage(restUriPath, restPackage); } } diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/JsonExceptionMapper.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/JsonExceptionMapper.java index 55b3a0d5..0030c121 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/JsonExceptionMapper.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/JsonExceptionMapper.java @@ -1,6 +1,7 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2019, 2021 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2023 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,11 +22,12 @@ package org.onap.policy.common.endpoints.http.server; import com.google.gson.JsonSyntaxException; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; +import lombok.AllArgsConstructor; import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,7 +39,7 @@ import org.slf4j.LoggerFactory; @Provider @Produces(MediaType.APPLICATION_JSON) public class JsonExceptionMapper implements ExceptionMapper<JsonSyntaxException> { - private static Logger logger = LoggerFactory.getLogger(JsonExceptionMapper.class); + private static final Logger logger = LoggerFactory.getLogger(JsonExceptionMapper.class); @Override public Response toResponse(JsonSyntaxException exception) { @@ -46,11 +48,8 @@ public class JsonExceptionMapper implements ExceptionMapper<JsonSyntaxException> } @Getter + @AllArgsConstructor private static class SimpleResponse { private String errorDetails; - - public SimpleResponse(String errorDetails) { - this.errorDetails = errorDetails; - } } } diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/RestServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/RestServer.java index 43e39d33..7e6ce866 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/RestServer.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/RestServer.java @@ -1,7 +1,8 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2019 Nordix Foundation. - * Modifications Copyright (C) 2019 AT&T Intellectual Property. + * Copyright (C) 2019, 2023-2024 Nordix Foundation. + * Modifications Copyright (C) 2019-2021 AT&T Intellectual Property. + * Modifications Copyright (C) 2021 Bell Canada. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +22,13 @@ package org.onap.policy.common.endpoints.http.server; +import jakarta.servlet.Filter; +import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.Properties; -import org.onap.policy.common.endpoints.http.server.aaf.AafAuthFilter; +import java.util.stream.Collectors; +import lombok.ToString; import org.onap.policy.common.endpoints.parameters.RestServerParameters; import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties; import org.onap.policy.common.gson.GsonMessageBodyHandler; @@ -34,6 +39,7 @@ import org.onap.policy.common.utils.services.ServiceManagerContainer; * * @author Ram Krishna Verma (ram.krishna.verma@est.tech) */ +@ToString public class RestServer extends ServiceManagerContainer { /** @@ -47,24 +53,34 @@ public class RestServer extends ServiceManagerContainer { * Constructs the object. * * @param restServerParameters the rest server parameters - * @param aafFilter class of object to use to filter AAF requests, or {@code null} * @param jaxrsProviders classes providing the services */ - public RestServer(final RestServerParameters restServerParameters, Class<? extends AafAuthFilter> aafFilter, - Class<?>... jaxrsProviders) { + public RestServer(final RestServerParameters restServerParameters, + Class<?>... jaxrsProviders) { + this(restServerParameters, null, Arrays.asList(jaxrsProviders)); + } + + /** + * Constructs the object. + * + * @param restServerParameters the rest server parameters + * @param filters class of object to use to filter requests, or {@code null} + * @param jaxrsProviders classes providing the services + */ + public RestServer(final RestServerParameters restServerParameters, List<Class<? extends Filter>> filters, + List<Class<?>> jaxrsProviders) { - if (jaxrsProviders.length == 0) { + if (jaxrsProviders == null || jaxrsProviders.isEmpty()) { throw new IllegalArgumentException("no providers specified"); } this.servers = factory.getServerFactory() - .build(getServerProperties(restServerParameters, getProviderClassNames(jaxrsProviders))); + .build(getServerProperties(restServerParameters, getProviderClassNames(jaxrsProviders))); for (HttpServletServer server : this.servers) { - if (aafFilter != null && server.isAaf()) { - server.addFilterClass(null, aafFilter.getName()); + if (filters != null && !filters.isEmpty()) { + filters.forEach(filter -> server.addFilterClass(null, filter.getName())); } - addAction("REST " + server.getName(), server::start, server::stop); } } @@ -78,29 +94,36 @@ public class RestServer extends ServiceManagerContainer { * @return the properties object */ protected Properties getServerProperties(RestServerParameters restServerParameters, String names) { - final Properties props = new Properties(); + final var props = new Properties(); props.setProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES, restServerParameters.getName()); final String svcpfx = - PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + restServerParameters.getName(); + PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + restServerParameters.getName(); props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX, restServerParameters.getHost()); props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX, - Integer.toString(restServerParameters.getPort())); + Integer.toString(restServerParameters.getPort())); props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX, names); props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, "false"); props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "true"); props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_USERNAME_SUFFIX, - restServerParameters.getUserName()); + getValue(restServerParameters.getUserName())); props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_PASSWORD_SUFFIX, - restServerParameters.getPassword()); + getValue(restServerParameters.getPassword())); props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX, - String.valueOf(restServerParameters.isHttps())); - props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_AAF_SUFFIX, - String.valueOf(restServerParameters.isAaf())); + String.valueOf(restServerParameters.isHttps())); + props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SNI_HOST_CHECK_SUFFIX, + String.valueOf(restServerParameters.isSniHostCHeck())); props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERIALIZATION_PROVIDER, - String.join(",", GsonMessageBodyHandler.class.getName(), YamlMessageBodyHandler.class.getName(), - JsonExceptionMapper.class.getName(), YamlExceptionMapper.class.getName())); + String.join(",", GsonMessageBodyHandler.class.getName(), YamlMessageBodyHandler.class.getName(), + JsonExceptionMapper.class.getName(), YamlExceptionMapper.class.getName())); + + props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_URIPATH_SUFFIX, + Optional.ofNullable(restServerParameters.getServletUriPath()).orElse("")); + props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SERVLET_CLASS_SUFFIX, + Optional.ofNullable(restServerParameters.getServletClass()).orElse("")); + props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PROMETHEUS_SUFFIX, + String.valueOf(restServerParameters.isPrometheus())); return props; } @@ -110,23 +133,15 @@ public class RestServer extends ServiceManagerContainer { * @param jaxrsProviders classes providing the services * @return the provider class names */ - private String getProviderClassNames(Class<?>[] jaxrsProviders) { - StringBuilder names = new StringBuilder(); - - for (Class<?> prov : jaxrsProviders) { - if (names.length() > 0) { - names.append(','); - } - - names.append(prov.getName()); - } - - return names.toString(); + private String getProviderClassNames(List<Class<?>> jaxrsProviders) { + return jaxrsProviders.stream().map(Class::getName).collect(Collectors.joining(",")); } - @Override - public String toString() { - return "RestServer [servers=" + servers + "]"; + private String getValue(final String value) { + if (value != null && value.startsWith("${") && value.endsWith("}")) { + return System.getenv(value.substring(2, value.length() - 1)); + } + return value; } /** diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/YamlExceptionMapper.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/YamlExceptionMapper.java index 7eac932a..c30c2072 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/YamlExceptionMapper.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/YamlExceptionMapper.java @@ -1,6 +1,7 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2019, 2021 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2023 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,10 +21,11 @@ package org.onap.policy.common.endpoints.http.server; -import javax.ws.rs.Produces; -import javax.ws.rs.core.Response; -import javax.ws.rs.ext.ExceptionMapper; -import javax.ws.rs.ext.Provider; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; +import lombok.AllArgsConstructor; import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +38,7 @@ import org.yaml.snakeyaml.error.YAMLException; @Provider @Produces(YamlMessageBodyHandler.APPLICATION_YAML) public class YamlExceptionMapper implements ExceptionMapper<YAMLException> { - private static Logger logger = LoggerFactory.getLogger(YamlExceptionMapper.class); + private static final Logger logger = LoggerFactory.getLogger(YamlExceptionMapper.class); @Override public Response toResponse(YAMLException exception) { @@ -45,11 +47,8 @@ public class YamlExceptionMapper implements ExceptionMapper<YAMLException> { } @Getter + @AllArgsConstructor private static class SimpleResponse { private String errorDetails; - - public SimpleResponse(String errorDetails) { - this.errorDetails = errorDetails; - } } } diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/YamlJacksonHandler.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/YamlJacksonHandler.java index f71aa90f..4639e6a2 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/YamlJacksonHandler.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/YamlJacksonHandler.java @@ -3,6 +3,7 @@ * ONAP * ================================================================================ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2023 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,10 +22,10 @@ package org.onap.policy.common.endpoints.http.server; import com.google.gson.GsonBuilder; -import javax.ws.rs.Consumes; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.ext.Provider; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.ext.Provider; import org.onap.policy.common.gson.JacksonHandler; import org.onap.policy.common.utils.coder.YamlJsonTranslator; diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/YamlMessageBodyHandler.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/YamlMessageBodyHandler.java index 8506a283..6de6b754 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/YamlMessageBodyHandler.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/YamlMessageBodyHandler.java @@ -2,7 +2,8 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2019, 2021 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2023 Nordix Foundation. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +23,13 @@ package org.onap.policy.common.endpoints.http.server; import com.google.gson.GsonBuilder; import com.google.gson.JsonSyntaxException; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.ext.MessageBodyReader; +import jakarta.ws.rs.ext.MessageBodyWriter; +import jakarta.ws.rs.ext.Provider; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -30,13 +38,6 @@ import java.io.OutputStreamWriter; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; -import javax.ws.rs.Consumes; -import javax.ws.rs.Produces; -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 org.onap.policy.common.gson.GsonMessageBodyHandler; import org.onap.policy.common.utils.coder.YamlJsonTranslator; import org.slf4j.Logger; @@ -96,7 +97,7 @@ public class YamlMessageBodyHandler implements MessageBodyReader<Object>, Messag public void writeTo(Object object, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException { - try (OutputStreamWriter writer = new OutputStreamWriter(entityStream, StandardCharsets.UTF_8)) { + try (var writer = new OutputStreamWriter(entityStream, StandardCharsets.UTF_8)) { translator.toYaml(writer, object); } } @@ -121,7 +122,7 @@ public class YamlMessageBodyHandler implements MessageBodyReader<Object>, Messag public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException { - try (InputStreamReader streamReader = new InputStreamReader(entityStream, StandardCharsets.UTF_8)) { + try (var streamReader = new InputStreamReader(entityStream, StandardCharsets.UTF_8)) { Class<?> clazz = (Class<?>) genericType; return translator.fromYaml(streamReader, clazz); diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/aaf/AafAuthFilter.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/aaf/AafAuthFilter.java deleted file mode 100644 index df1f6044..00000000 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/aaf/AafAuthFilter.java +++ /dev/null @@ -1,45 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * ONAP - * ================================================================================ - * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed 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. - * ============LICENSE_END========================================================= - */ - -package org.onap.policy.common.endpoints.http.server.aaf; - -import javax.servlet.http.HttpServletRequest; -import org.onap.policy.common.endpoints.http.server.AuthorizationFilter; - -/** - * Generic Authorization AAF Filter Skeleton. This class will return - * a permission in AAF format. Subclasses are responsible to provide - * the AAF permission type and instance. - */ -public abstract class AafAuthFilter extends AuthorizationFilter { - - public static final String DEFAULT_NAMESPACE = "org.onap.policy"; - - @Override - protected String getRole(HttpServletRequest request) { - return - String.format("%s|%s|%s", getPermissionType(request), getPermissionInstance(request), - request.getMethod().toLowerCase()); - } - - protected abstract String getPermissionType(HttpServletRequest request); - - protected abstract String getPermissionInstance(HttpServletRequest request); -} diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/aaf/AafGranularAuthFilter.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/aaf/AafGranularAuthFilter.java deleted file mode 100644 index 27b15a9c..00000000 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/aaf/AafGranularAuthFilter.java +++ /dev/null @@ -1,48 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * ONAP - * ================================================================================ - * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed 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. - * ============LICENSE_END========================================================= - */ - -package org.onap.policy.common.endpoints.http.server.aaf; - -import javax.servlet.http.HttpServletRequest; -import org.onap.policy.common.utils.network.NetworkUtil; - -/** - * This generic class allows the mapping of REST APIs to AAF permissions - * to be evaluated in an AAF context. This class can be used for - * highly granular permissions where each REST resource can be directly - * mapped transparently to an AAF permission type, the instance being the host - * server, and the HTTP method corresponding to the action. - * Subclasses are responsible to provide the root permission prefix, typically - * the namespace. - */ -public abstract class AafGranularAuthFilter extends AafAuthFilter { - - @Override - protected String getPermissionType(HttpServletRequest request) { - return getPermissionTypeRoot() + request.getRequestURI().replace('/', '.'); - } - - @Override - protected String getPermissionInstance(HttpServletRequest request) { - return NetworkUtil.getHostname(); - } - - public abstract String getPermissionTypeRoot(); -} diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java index d809479a..d4c392b9 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java @@ -2,8 +2,9 @@ * ============LICENSE_START======================================================= * policy-endpoints * ================================================================================ - * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. - * Modifications Copyright (C) 2019 Nordix Foundation. + * Copyright (C) 2017-2021 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2019-2020,2023 Nordix Foundation. + * Modifications Copyright (C) 2021 Bell Canada. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,11 +22,13 @@ package org.onap.policy.common.endpoints.http.server.internal; -import io.swagger.jersey.config.JerseyJaxrsConfig; -import java.util.HashMap; import org.apache.commons.lang3.StringUtils; +import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.glassfish.jersey.server.ServerProperties; +import org.glassfish.jersey.servlet.ServletContainer; +import org.onap.policy.common.endpoints.http.server.JsonExceptionMapper; +import org.onap.policy.common.gson.GsonMessageBodyHandler; import org.onap.policy.common.utils.network.NetworkUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,7 +38,7 @@ import org.slf4j.LoggerFactory; * * <p>Note: the serialization provider will always be added to the server's class providers, as will the swagger * providers (assuming swagger has been enabled). This happens whether {@link #addServletClass(String, String)} is used - * or {@link #addServletPackage(String, String)} is used. Thus it's possible to have both the server's class provider + * or {@link #addServletPackage(String, String)} is used. Thus, it's possible to have both the server's class provider * property and the server's package provider property populated. */ public class JettyJerseyServer extends JettyServletServer { @@ -61,27 +64,17 @@ public class JettyJerseyServer extends JettyServletServer { protected static final String SWAGGER_PRETTY_PRINT = "swagger.pretty.print"; /** - * Jersey Jackson Classes Init Param Value. + * Jersey GSON Classes Init Param Value. */ - protected static final String JERSEY_JACKSON_INIT_CLASSNAMES_PARAM_VALUE = - "org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJsonProvider"; + protected static final String JERSEY_GSON_INIT_CLASSNAMES_PARAM_VALUE = + String.join(",", GsonMessageBodyHandler.class.getName(), JsonExceptionMapper.class.getName()); /** - * Jersey Swagger Classes Init Param Value. - */ - protected static final String SWAGGER_INIT_CLASSNAMES_PARAM_VALUE = - "io.swagger.jaxrs.listing.ApiListingResource," + "io.swagger.jaxrs.listing.SwaggerSerializers"; - /** * Logger. */ protected static Logger logger = LoggerFactory.getLogger(JettyJerseyServer.class); /** - * Container for servlets. - */ - protected HashMap<String, ServletHolder> servlets = new HashMap<>(); - - /** * Swagger ID. */ protected String swaggerId = null; @@ -89,7 +82,7 @@ public class JettyJerseyServer extends JettyServletServer { /** * The serialization provider to be used when classes are added to the service. */ - private String classProvider = JERSEY_JACKSON_INIT_CLASSNAMES_PARAM_VALUE; + private String classProvider = JERSEY_GSON_INIT_CLASSNAMES_PARAM_VALUE; /** * Constructor. @@ -98,14 +91,15 @@ public class JettyJerseyServer extends JettyServletServer { * @param https enable https? * @param host host server host * @param port port server port + * @param sniHostCheck SNI Host checking flag * @param swagger support swagger? * @param contextPath context path - * * @throws IllegalArgumentException in invalid arguments are provided */ - public JettyJerseyServer(String name, boolean https, String host, int port, String contextPath, boolean swagger) { + public JettyJerseyServer(String name, boolean https, String host, int port, boolean sniHostCheck, + String contextPath, boolean swagger) { - super(name, https, host, port, contextPath); + super(name, https, host, port, sniHostCheck, contextPath); if (swagger) { this.swaggerId = "swagger-" + this.port; attachSwaggerServlet(https); @@ -117,7 +111,10 @@ public class JettyJerseyServer extends JettyServletServer { */ protected void attachSwaggerServlet(boolean https) { - ServletHolder swaggerServlet = context.addServlet(JerseyJaxrsConfig.class, "/"); + ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS); + handler.setContextPath("/"); + + ServletHolder swaggerServlet = handler.addServlet(ServletContainer.class, "/*"); String hostname = this.connector.getHost(); if (StringUtils.isBlank(hostname) || hostname.equals(NetworkUtil.IPV4_WILDCARD_ADDRESS)) { @@ -125,7 +122,7 @@ public class JettyJerseyServer extends JettyServletServer { } swaggerServlet.setInitParameter(SWAGGER_API_BASEPATH, - ((https) ? "https://" : "http://") + hostname + ":" + this.connector.getPort() + "/"); + ((https) ? "https://" : "http://") + hostname + ":" + this.connector.getPort() + "/"); swaggerServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId); swaggerServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId); swaggerServlet.setInitParameter(SWAGGER_PRETTY_PRINT, "true"); @@ -145,21 +142,16 @@ public class JettyJerseyServer extends JettyServletServer { * @throws IllegalArgumentException if invalid arguments are provided */ protected synchronized ServletHolder getServlet(String servletPath) { - - return servlets.computeIfAbsent(servletPath, key -> { - - ServletHolder jerseyServlet = - context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, servletPath); - jerseyServlet.setInitOrder(0); - - return jerseyServlet; - }); + ServletHolder jerseyServlet = + super.getServlet(org.glassfish.jersey.servlet.ServletContainer.class, servletPath); + jerseyServlet.setInitOrder(0); + return jerseyServlet; } @Override public synchronized void addServletPackage(String servletPath, String restPackage) { String servPath = servletPath; - if (restPackage == null || restPackage.isEmpty()) { + if (StringUtils.isBlank(restPackage)) { throw new IllegalArgumentException("No discoverable REST package provided"); } @@ -189,7 +181,7 @@ public class JettyJerseyServer extends JettyServletServer { @Override public synchronized void addServletClass(String servletPath, String restClass) { - if (restClass == null || restClass.isEmpty()) { + if (StringUtils.isBlank(restClass)) { throw new IllegalArgumentException("No discoverable REST class provided"); } @@ -232,8 +224,8 @@ public class JettyJerseyServer extends JettyServletServer { initClasses = classProvider; if (this.swaggerId != null) { - initClasses += "," + SWAGGER_INIT_CLASSNAMES_PARAM_VALUE; - + jerseyServlet.setInitParameter("jersey.config.server.provider.packages", + "io.swagger.v3.jaxrs2.integration.resources,io.swagger.sample.resource"); jerseyServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId); jerseyServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId); } @@ -254,9 +246,9 @@ public class JettyJerseyServer extends JettyServletServer { @Override public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("JettyJerseyServer [servlets=").append(servlets).append(", swaggerId=").append(swaggerId) - .append(", toString()=").append(super.toString()).append("]"); - return builder.toString(); + return "JettyJerseyServer [Jerseyservlets=" + servlets + + ", swaggerId=" + swaggerId + + ", toString()=" + super.toString() + + "]"; } } diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyServletServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyServletServer.java index 133adade..78858a77 100644 --- a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyServletServer.java +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyServletServer.java @@ -2,7 +2,9 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2017-2020 AT&T Intellectual Property. All rights reserved. + * Copyright (C) 2017-2021 AT&T Intellectual Property. All rights reserved. + * Modifications Copyright (C) 2019-2020, 2023-2024 Nordix Foundation. + * Modifications Copyright (C) 2020-2021 Bell Canada. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,9 +22,15 @@ package org.onap.policy.common.endpoints.http.server.internal; -import com.fasterxml.jackson.annotation.JsonIgnore; +import io.prometheus.client.hotspot.DefaultExports; +import io.prometheus.client.servlet.jakarta.exporter.MetricsServlet; +import jakarta.servlet.Servlet; import java.util.EnumSet; -import javax.servlet.DispatcherType; +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.HashLoginService; @@ -35,58 +43,70 @@ import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.Slf4jRequestLogWriter; -import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.util.security.Credential; import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.onap.aaf.cadi.filter.CadiFilter; import org.onap.policy.common.endpoints.http.server.HttpServletServer; -import org.onap.policy.common.gson.annotation.GsonJsonIgnore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Http Server implementation using Embedded Jetty. */ +@ToString public abstract class JettyServletServer implements HttpServletServer, Runnable { /** * Keystore/Truststore system property names. */ public static final String SYSTEM_KEYSTORE_PROPERTY_NAME = "javax.net.ssl.keyStore"; - public static final String SYSTEM_KEYSTORE_PASSWORD_PROPERTY_NAME = "javax.net.ssl.keyStorePassword"; //NOSONAR + public static final String SYSTEM_KEYSTORE_PASSWORD_PROPERTY_NAME = "javax.net.ssl.keyStorePassword"; // NOSONAR public static final String SYSTEM_TRUSTSTORE_PROPERTY_NAME = "javax.net.ssl.trustStore"; - public static final String SYSTEM_TRUSTSTORE_PASSWORD_PROPERTY_NAME = "javax.net.ssl.trustStorePassword"; //NOSONAR + public static final String SYSTEM_TRUSTSTORE_PASSWORD_PROPERTY_NAME = "javax.net.ssl.trustStorePassword"; // NOSONAR /** * Logger. */ - private static Logger logger = LoggerFactory.getLogger(JettyServletServer.class); + private static final Logger logger = LoggerFactory.getLogger(JettyServletServer.class); + + private static final String NOT_SUPPORTED = " is not supported on this type of jetty server"; /** * Server name. */ + @Getter protected final String name; /** * Server host address. */ + @Getter protected final String host; /** * Server port to bind. */ + @Getter protected final int port; /** - * Server auth user name. + * Should SNI host checking be done. + */ + @Getter + protected boolean sniHostCheck; + + /** + * Server auth username. */ + @Getter protected String user; /** * Server auth password name. */ + @Getter protected String password; /** @@ -112,12 +132,18 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable /** * Jetty thread. */ - protected volatile Thread jettyThread; + protected Thread jettyThread; + + /** + * Container for default servlets. + */ + protected final Map<String, ServletHolder> servlets = new HashMap<>(); /** * Start condition. */ - protected Object startCondition = new Object(); + @ToString.Exclude + protected final Object startCondition = new Object(); /** * Constructor. @@ -125,11 +151,13 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable * @param name server name * @param host server host * @param port server port + * @param sniHostCheck SNI Host checking flag * @param contextPath context path * * @throws IllegalArgumentException if invalid parameters are passed in */ - public JettyServletServer(String name, boolean https, String host, int port, String contextPath) { + protected JettyServletServer(String name, boolean https, String host, int port, boolean sniHostCheck, + String contextPath) { String srvName = name; if (srvName == null || srvName.isEmpty()) { @@ -154,6 +182,7 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable this.host = srvHost; this.port = port; + this.sniHostCheck = sniHostCheck; this.contextPath = ctxtPath; @@ -162,8 +191,7 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable this.jettyServer = new Server(); - CustomRequestLog requestLog = - new CustomRequestLog(new Slf4jRequestLogWriter(), CustomRequestLog.EXTENDED_NCSA_FORMAT); + var requestLog = new CustomRequestLog(new Slf4jRequestLogWriter(), CustomRequestLog.EXTENDED_NCSA_FORMAT); this.jettyServer.setRequestLog(requestLog); if (https) { @@ -181,8 +209,8 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable this.jettyServer.setHandler(context); } - public JettyServletServer(String name, String host, int port, String contextPath) { - this(name, false, host, port, contextPath); + protected JettyServletServer(String name, String host, int port, boolean sniHostCheck, String contextPath) { + this(name, false, host, port, sniHostCheck, contextPath); } @Override @@ -196,7 +224,20 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable tempFilterPath = "/*"; } - context.addFilter(filterClass, tempFilterPath, EnumSet.of(DispatcherType.INCLUDE, DispatcherType.REQUEST)); + context.addFilter(filterClass, tempFilterPath, + EnumSet.of(jakarta.servlet.DispatcherType.INCLUDE, jakarta.servlet.DispatcherType.REQUEST)); + } + + protected ServletHolder getServlet(@NonNull Class<? extends Servlet> servlet, @NonNull String servletPath) { + synchronized (servlets) { + return servlets.computeIfAbsent(servletPath, key -> context.addServlet(servlet, servletPath)); + } + } + + protected ServletHolder getServlet(String servletClass, String servletPath) { + synchronized (servlets) { + return servlets.computeIfAbsent(servletPath, key -> context.addServlet(servletClass, servletPath)); + } } /** @@ -205,32 +246,35 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable * @return the server connector */ public ServerConnector httpsConnector() { - SslContextFactory sslContextFactory = new SslContextFactory.Server(); + SslContextFactory.Server sslContextFactoryServer = new SslContextFactory.Server(); String keyStore = System.getProperty(SYSTEM_KEYSTORE_PROPERTY_NAME); if (keyStore != null) { - sslContextFactory.setKeyStorePath(keyStore); + sslContextFactoryServer.setKeyStorePath(keyStore); String ksPassword = System.getProperty(SYSTEM_KEYSTORE_PASSWORD_PROPERTY_NAME); if (ksPassword != null) { - sslContextFactory.setKeyStorePassword(ksPassword); + sslContextFactoryServer.setKeyStorePassword(ksPassword); } } String trustStore = System.getProperty(SYSTEM_TRUSTSTORE_PROPERTY_NAME); if (trustStore != null) { - sslContextFactory.setTrustStorePath(trustStore); + sslContextFactoryServer.setTrustStorePath(trustStore); String tsPassword = System.getProperty(SYSTEM_TRUSTSTORE_PASSWORD_PROPERTY_NAME); if (tsPassword != null) { - sslContextFactory.setTrustStorePassword(tsPassword); + sslContextFactoryServer.setTrustStorePassword(tsPassword); } } - HttpConfiguration https = new HttpConfiguration(); - https.addCustomizer(new SecureRequestCustomizer()); - return new ServerConnector(jettyServer, sslContextFactory, new HttpConnectionFactory(https)); + var httpsConfiguration = new HttpConfiguration(); + SecureRequestCustomizer src = new SecureRequestCustomizer(); + src.setSniHostCheck(sniHostCheck); + httpsConfiguration.addCustomizer(src); + + return new ServerConnector(jettyServer, sslContextFactoryServer, new HttpConnectionFactory(httpsConfiguration)); } public ServerConnector httpConnector() { @@ -238,21 +282,6 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable } @Override - public void setAafAuthentication(String filterPath) { - this.addFilterClass(filterPath, CadiFilter.class.getName()); - } - - @Override - public boolean isAaf() { - for (FilterHolder filter : context.getServletHandler().getFilters()) { - if (CadiFilter.class.getName().equals(filter.getClassName())) { - return true; - } - } - return false; - } - - @Override public void setBasicAuthentication(String user, String password, String servletPath) { String srvltPath = servletPath; @@ -264,22 +293,26 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable srvltPath = "/*"; } - final HashLoginService hashLoginService = new HashLoginService(); - final UserStore userStore = new UserStore(); - userStore.addUser(user, Credential.getCredential(password), new String[] {"user"}); + final var hashLoginService = new HashLoginService(); + final var userStore = new UserStore(); + userStore.addUser(user, Credential.getCredential(password), new String[] { + "user" + }); hashLoginService.setUserStore(userStore); hashLoginService.setName(this.connector.getName() + "-login-service"); - Constraint constraint = new Constraint(); + var constraint = new Constraint(); constraint.setName(Constraint.__BASIC_AUTH); - constraint.setRoles(new String[] {"user"}); + constraint.setRoles(new String[] { + "user" + }); constraint.setAuthenticate(true); - ConstraintMapping constraintMapping = new ConstraintMapping(); + var constraintMapping = new ConstraintMapping(); constraintMapping.setConstraint(constraint); constraintMapping.setPathSpec(srvltPath); - ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler(); + var securityHandler = new ConstraintSecurityHandler(); securityHandler.setAuthenticator(new BasicAuthenticator()); securityHandler.setRealmName(this.connector.getName() + "-realm"); securityHandler.addConstraintMapping(constraintMapping); @@ -310,6 +343,11 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable } this.jettyServer.join(); + + } catch (InterruptedException e) { + logger.error("{}: error found while bringing up server", this, e); + Thread.currentThread().interrupt(); + } catch (Exception e) { logger.error("{}: error found while bringing up server", this, e); } @@ -415,12 +453,13 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable this.stop(); - if (this.jettyThread == null) { - return; + Thread jettyThreadCopy; + synchronized (this) { + if ((jettyThreadCopy = this.jettyThread) == null) { + return; + } } - Thread jettyThreadCopy = this.jettyThread; - if (jettyThreadCopy.isAlive()) { try { jettyThreadCopy.join(2000L); @@ -451,58 +490,44 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable } @Override - public int getPort() { - return this.port; + public void setSerializationProvider(String provider) { + throw new UnsupportedOperationException("setSerializationProvider()" + NOT_SUPPORTED); } - /** - * Get name. - * - * @return the name - */ @Override - public String getName() { - return name; + public void addServletClass(String servletPath, String servletClass) { + throw new UnsupportedOperationException("addServletClass()" + NOT_SUPPORTED); } - /** - * Get host. - * - * @return the host - */ - public String getHost() { - return host; + @Override + public void addStdServletClass(@NonNull String servletPath, @NonNull String plainServletClass) { + this.getServlet(plainServletClass, servletPath); } - /** - * Get user. - * - * @return the user - */ - public String getUser() { - return user; + @Override + public void setPrometheus(String metricsPath) { + this.getServlet(MetricsServlet.class, metricsPath); + DefaultExports.initialize(); } - /** - * Get password. - * - * @return the password - */ - @JsonIgnore - @GsonJsonIgnore - public String getPassword() { - return password; + @Override + public boolean isPrometheus() { + for (ServletHolder servlet : context.getServletHandler().getServlets()) { + if (MetricsServlet.class.getName().equals(servlet.getClassName())) { + return true; + } + } + return false; + } + + @Override + public void addServletPackage(String servletPath, String restPackage) { + throw new UnsupportedOperationException("addServletPackage()" + NOT_SUPPORTED); } @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("JettyServer [name=").append(name).append(", host=").append(host).append(", port=").append(port) - .append(", user=").append(user).append(", password=").append(password != null).append(", contextPath=") - .append(contextPath).append(", jettyServer=").append(jettyServer).append(", context=") - .append(this.context).append(", connector=").append(connector).append(", jettyThread=") - .append(jettyThread).append("]"); - return builder.toString(); + public void addServletResource(String servletPath, String resourceBase) { + throw new UnsupportedOperationException("addServletResource()" + NOT_SUPPORTED); } } diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyStaticResourceServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyStaticResourceServer.java new file mode 100644 index 00000000..ee2b0540 --- /dev/null +++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyStaticResourceServer.java @@ -0,0 +1,107 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2020, 2023-2024 Nordix Foundation. + * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.endpoints.http.server.internal; + +import lombok.ToString; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jetty.servlet.DefaultServlet; +import org.eclipse.jetty.servlet.ServletHolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Jetty Server that uses DefaultServlets to support web static resources' management. + */ +@ToString +public class JettyStaticResourceServer extends JettyServletServer { + + /** + * Servlet Holder Resource Base Path. + */ + protected static final String SERVLET_HOLDER_RESOURCE_BASE = "resourceBase"; + + /** + * Servlet Holder Directory Allowed. + */ + protected static final String SERVLET_HOLDER_DIR_ALLOWED = "dirAllowed"; + + /** + * Servlet Holder Path Information Only. + */ + protected static final String SERVLET_HOLDER_PATH_INFO_ONLY = "pathInfoOnly"; + + /** + * Logger. + */ + protected static Logger logger = LoggerFactory.getLogger(JettyStaticResourceServer.class); + + /** + * Constructor. + * + * @param name name + * @param https enable https? + * @param host host server host + * @param port port server port + * @param sniHostCheck SNI Host checking flag + * @param contextPath context path + * @throws IllegalArgumentException in invalid arguments are provided + */ + public JettyStaticResourceServer(String name, boolean https, String host, int port, boolean sniHostCheck, + String contextPath) { + + super(name, https, host, port, sniHostCheck, contextPath); + } + + /** + * Retrieves cached default servlet based on servlet path. + * + * @param servletPath servlet path + * @return the jetty servlet holder + * + * @throws IllegalArgumentException if invalid arguments are provided + */ + protected synchronized ServletHolder getDefaultServlet(String servletPath) { + return super.getServlet(DefaultServlet.class, servletPath); + } + + @Override + public synchronized void addServletResource(String servletPath, String resourceBase) { + + if (StringUtils.isBlank(resourceBase)) { + throw new IllegalArgumentException("No resourceBase provided"); + } + + if (servletPath == null || servletPath.isEmpty()) { + servletPath = "/*"; + } + + ServletHolder defaultServlet = this.getDefaultServlet(servletPath); + + defaultServlet.setInitParameter(SERVLET_HOLDER_RESOURCE_BASE, resourceBase); + defaultServlet.setInitParameter(SERVLET_HOLDER_DIR_ALLOWED, "false"); + defaultServlet.setInitParameter(SERVLET_HOLDER_PATH_INFO_ONLY, "true"); + + if (logger.isDebugEnabled()) { + logger.debug("{}: added Default Servlet: {}", this, defaultServlet.dump()); + } + } +} |