diff options
Diffstat (limited to 'policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server')
15 files changed, 321 insertions, 404 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 49dfea06..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,7 +3,8 @@ * ONAP * ================================================================================ * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. - * Modifications Copyright (C) 2020 Nordix Foundation. + * 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. @@ -51,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 @@ -90,6 +79,14 @@ public interface HttpServletServer extends Startable { 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 @@ -123,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 90c0db2e..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,7 +3,7 @@ * policy-endpoints * ================================================================================ * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. - * Modifications Copyright (C) 2020 Nordix Foundation. + * 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. @@ -30,23 +30,24 @@ import java.util.Properties; public interface HttpServletServerFactory { /** - * Builds an http or https rest 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 rest server with support for servlets. + * Builds a http rest server with support for servlets. * * @param name name * @param host binding host @@ -69,19 +70,20 @@ public interface HttpServletServerFactory { List<HttpServletServer> build(Properties properties); /** - * Builds an http or https server to manage static resources. + * 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, String contextPath, - boolean managed); + HttpServletServer buildStaticResourceServer(String name, boolean https, String host, int port, boolean sniHostCheck, + String contextPath, boolean managed); /** * Gets a server based on the 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 445a3515..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,8 +2,9 @@ * ============LICENSE_START======================================================= * ONAP Policy Engine - Common Modules * ================================================================================ - * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. - * Modifications Copyright (C) 2020 Nordix Foundation. + * 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. @@ -21,6 +22,7 @@ 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; @@ -37,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. @@ -51,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); } @@ -69,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 @@ -83,23 +84,21 @@ 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, - String contextPath, boolean managed) { + boolean sniHostCheck, String contextPath, boolean managed) { if (servers.containsKey(port)) { return servers.get(port); } - JettyStaticResourceServer server = new JettyStaticResourceServer(name, https, host, port, contextPath); + var server = new JettyStaticResourceServer(name, https, host, port, sniHostCheck, contextPath); if (managed) { servers.put(port, server); } @@ -111,92 +110,106 @@ class IndexedHttpServletServerFactory implements HttpServletServerFactory { 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 */ + 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); - 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); - - 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 6776a328..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-2020 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,15 +94,15 @@ 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"); @@ -95,12 +111,19 @@ public class RestServer extends ServiceManagerContainer { props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_PASSWORD_SUFFIX, 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,18 +133,8 @@ 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(",")); } private String getValue(final String value) { @@ -131,11 +144,6 @@ public class RestServer extends ServiceManagerContainer { return value; } - @Override - public String toString() { - return "RestServer [servers=" + servers + "]"; - } - /** * Factory used to access objects. */ 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 df6823a0..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-2020 AT&T Intellectual Property. All rights reserved. - * Modifications Copyright (C) 2019-2020 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,12 +22,11 @@ package org.onap.policy.common.endpoints.http.server.internal; -import io.swagger.jersey.config.JerseyJaxrsConfig; -import java.util.HashMap; -import java.util.Map; 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; @@ -38,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 { @@ -67,13 +67,7 @@ public class JettyJerseyServer extends JettyServletServer { * Jersey GSON Classes Init Param Value. */ 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"; + String.join(",", GsonMessageBodyHandler.class.getName(), JsonExceptionMapper.class.getName()); /** * Logger. @@ -81,11 +75,6 @@ public class JettyJerseyServer extends JettyServletServer { protected static Logger logger = LoggerFactory.getLogger(JettyJerseyServer.class); /** - * Container for servlets. - */ - protected final Map<String, ServletHolder> servlets = new HashMap<>(); - - /** * Swagger ID. */ protected String swaggerId = null; @@ -102,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); @@ -121,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)) { @@ -129,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"); @@ -149,15 +142,10 @@ 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 @@ -236,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); } @@ -258,9 +246,9 @@ public class JettyJerseyServer extends JettyServletServer { @Override public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("JettyJerseyServer [Jerseyservlets=").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 699d5d43..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,8 +2,9 @@ * ============LICENSE_START======================================================= * ONAP * ================================================================================ - * Copyright (C) 2017-2020 AT&T Intellectual Property. All rights reserved. - * Modifications Copyright (C) 2019-2020 Nordix Foundation. + * 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. @@ -21,8 +22,15 @@ package org.onap.policy.common.endpoints.http.server.internal; +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,60 +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; /** @@ -117,9 +135,15 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable 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. @@ -127,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()) { @@ -156,6 +182,7 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable this.host = srvHost; this.port = port; + this.sniHostCheck = sniHostCheck; this.contextPath = ctxtPath; @@ -164,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) { @@ -183,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 @@ -198,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)); + } } /** @@ -207,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() { @@ -240,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; @@ -266,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); @@ -312,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); } @@ -454,56 +490,34 @@ 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; - } - - /** - * Get host. - * - * @return the host - */ - public String getHost() { - return host; - } - - /** - * Get user. - * - * @return the user - */ - public String getUser() { - return user; + public void addServletClass(String servletPath, String servletClass) { + throw new UnsupportedOperationException("addServletClass()" + NOT_SUPPORTED); } - /** - * Get password. - * - * @return the password - */ - @GsonJsonIgnore - public String getPassword() { - return password; + @Override + public void addStdServletClass(@NonNull String servletPath, @NonNull String plainServletClass) { + this.getServlet(plainServletClass, servletPath); } @Override - public void setSerializationProvider(String provider) { - throw new UnsupportedOperationException("setSerializationProvider()" + NOT_SUPPORTED); + public void setPrometheus(String metricsPath) { + this.getServlet(MetricsServlet.class, metricsPath); + DefaultExports.initialize(); } @Override - public void addServletClass(String servletPath, String restClass) { - throw new UnsupportedOperationException("addServletClass()" + NOT_SUPPORTED); + public boolean isPrometheus() { + for (ServletHolder servlet : context.getServletHandler().getServlets()) { + if (MetricsServlet.class.getName().equals(servlet.getClassName())) { + return true; + } + } + return false; } @Override @@ -516,15 +530,4 @@ public abstract class JettyServletServer implements HttpServletServer, Runnable throw new UnsupportedOperationException("addServletResource()" + 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(); - } - } 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 index 15d9990b..ee2b0540 100644 --- 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 @@ -1,6 +1,7 @@ /*- * ============LICENSE_START======================================================= - * Copyright (C) 2020 Nordix Foundation. + * 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. @@ -20,8 +21,6 @@ package org.onap.policy.common.endpoints.http.server.internal; -import java.util.HashMap; -import java.util.Map; import lombok.ToString; import org.apache.commons.lang3.StringUtils; import org.eclipse.jetty.servlet.DefaultServlet; @@ -30,7 +29,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Jetty Server that uses DefaultServlets to support web static resources management. + * Jetty Server that uses DefaultServlets to support web static resources' management. */ @ToString public class JettyStaticResourceServer extends JettyServletServer { @@ -56,24 +55,20 @@ public class JettyStaticResourceServer extends JettyServletServer { protected static Logger logger = LoggerFactory.getLogger(JettyStaticResourceServer.class); /** - * Container for default servlets. - */ - protected final Map<String, ServletHolder> servlets = new HashMap<>(); - - /** * 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, String contextPath) { + public JettyStaticResourceServer(String name, boolean https, String host, int port, boolean sniHostCheck, + String contextPath) { - super(name, https, host, port, contextPath); + super(name, https, host, port, sniHostCheck, contextPath); } /** @@ -84,15 +79,14 @@ public class JettyStaticResourceServer extends JettyServletServer { * * @throws IllegalArgumentException if invalid arguments are provided */ - protected synchronized ServletHolder getDefaultServlet(String servPath) { - - return servlets.computeIfAbsent(servPath, key -> context.addServlet(DefaultServlet.class, servPath)); + protected synchronized ServletHolder getDefaultServlet(String servletPath) { + return super.getServlet(DefaultServlet.class, servletPath); } @Override - public synchronized void addServletResource(String servletPath, String resoureBase) { + public synchronized void addServletResource(String servletPath, String resourceBase) { - if (StringUtils.isBlank(resoureBase)) { + if (StringUtils.isBlank(resourceBase)) { throw new IllegalArgumentException("No resourceBase provided"); } @@ -102,7 +96,7 @@ public class JettyStaticResourceServer extends JettyServletServer { ServletHolder defaultServlet = this.getDefaultServlet(servletPath); - defaultServlet.setInitParameter(SERVLET_HOLDER_RESOURCE_BASE, resoureBase); + defaultServlet.setInitParameter(SERVLET_HOLDER_RESOURCE_BASE, resourceBase); defaultServlet.setInitParameter(SERVLET_HOLDER_DIR_ALLOWED, "false"); defaultServlet.setInitParameter(SERVLET_HOLDER_PATH_INFO_ONLY, "true"); |