From 6e0bc01721c4af314e928d9a33b6e7a1a5fbc090 Mon Sep 17 00:00:00 2001 From: Guus der Kinderen Date: Sat, 18 Dec 2021 12:09:41 +0100 Subject: [PATCH] fixes #67: Migrate to Jersey 2 This updates the Jersey dependency from 1.x to 2.x. This commit requires OF-2352 / https://github.com/igniterealtime/Openfire/pull/1945 which means that this PR will _not_ work with 4.7.0 beta (but hopefully will with 4.7.0 non-beta). --- changelog.html | 3 +- pom.xml | 60 +++---- .../openfire/plugin/rest/AuthFilter.java | 28 ++-- .../openfire/plugin/rest/CORSFilter.java | 27 ++-- .../plugin/rest/RESTServicePlugin.java | 6 + .../plugin/rest/entity/GroupEntities.java | 3 +- .../plugin/rest/entity/MUCRoomEntities.java | 4 +- .../plugin/rest/entity/OccupantEntities.java | 5 +- .../rest/entity/ParticipantEntities.java | 4 +- .../plugin/rest/entity/SecurityAuditLogs.java | 4 +- .../plugin/rest/entity/SessionEntities.java | 4 +- .../plugin/rest/entity/UserEntities.java | 4 +- .../plugin/rest/service/JerseyWrapper.java | 150 +++++------------- src/web/WEB-INF/web.xml | 6 +- 14 files changed, 113 insertions(+), 195 deletions(-) diff --git a/changelog.html b/changelog.html index d6ed1a5350..6dedcea8f2 100644 --- a/changelog.html +++ b/changelog.html @@ -46,9 +46,10 @@

1.7.0 (tbd)

diff --git a/pom.xml b/pom.xml index 85c598ea30..5e8cb41f00 100644 --- a/pom.xml +++ b/pom.xml @@ -13,8 +13,7 @@ Allows administration over a RESTful API. - 2.12.1 - 1.19.4 + 2.35 @@ -38,45 +37,30 @@ - com.fasterxml.jackson.module - jackson-module-jaxb-annotations - ${jackson.version} + org.glassfish.jersey.containers + jersey-container-jetty-http + ${jersey.version} - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider - ${jackson.version} + org.glassfish.jersey.containers + jersey-container-jetty-servlet + ${jersey.version} + + + org.glassfish.jersey.inject + jersey-hk2 + ${jersey.version} + + + org.glassfish.jersey.media + jersey-media-jaxb + ${jersey.version} + + + org.glassfish.jersey.media + jersey-media-json-jackson + ${jersey.version} - - com.sun.jersey - jersey-client - ${jersey.version} - - - com.sun.jersey - jersey-core - ${jersey.version} - - - com.sun.jersey - jersey-server - ${jersey.version} - - - com.sun.jersey - jersey-servlet - ${jersey.version} - - - com.sun.jersey - jersey-json - ${jersey.version} - - - javax.ws.rs - jsr311-api - 1.1.1 - diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java b/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java index 37cdd874db..7030f6fffb 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/AuthFilter.java @@ -1,7 +1,12 @@ package org.jivesoftware.openfire.plugin.rest; +import javax.annotation.Priority; import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Priorities; import javax.ws.rs.WebApplicationException; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.PreMatching; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response.Status; @@ -14,12 +19,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.sun.jersey.spi.container.ContainerRequest; -import com.sun.jersey.spi.container.ContainerRequestFilter; +import java.io.IOException; /** * The Class AuthFilter. */ +@PreMatching +@Priority(Priorities.AUTHORIZATION) public class AuthFilter implements ContainerRequestFilter { /** The log. */ @@ -33,15 +39,8 @@ public class AuthFilter implements ContainerRequestFilter { private RESTServicePlugin plugin = (RESTServicePlugin) XMPPServer.getInstance().getPluginManager() .getPlugin("restapi"); - /* - * (non-Javadoc) - * - * @see - * com.sun.jersey.spi.container.ContainerRequestFilter#filter(com.sun.jersey - * .spi.container.ContainerRequest) - */ @Override - public ContainerRequest filter(ContainerRequest containerRequest) throws WebApplicationException { + public void filter(ContainerRequestContext containerRequest) throws IOException { if (!plugin.isEnabled()) { LOG.debug("REST API Plugin is not enabled"); throw new WebApplicationException(Status.FORBIDDEN); @@ -50,13 +49,13 @@ public ContainerRequest filter(ContainerRequest containerRequest) throws WebAppl // Let the preflight request through the authentication if ("OPTIONS".equals(containerRequest.getMethod())) { LOG.debug("Authentication was bypassed because of OPTIONS request"); - return containerRequest; + return; } // To be backwards compatible to userservice 1.* - if ("restapi/v1/userservice".equals(containerRequest.getPath())) { + if (containerRequest.getUriInfo().getRequestUri().getPath().contains("restapi/v1/userservice")) { LOG.info("Deprecated 'userservice' endpoint was used. Please switch to the new endpoints"); - return containerRequest; + return; } if (!plugin.getAllowedIPs().isEmpty()) { @@ -78,7 +77,7 @@ public ContainerRequest filter(ContainerRequest containerRequest) throws WebAppl } // Get the authentication passed in HTTP headers parameters - String auth = containerRequest.getHeaderValue("authorization"); + String auth = containerRequest.getHeaderString("authorization"); if (auth == null) { throw new WebApplicationException(Status.UNAUTHORIZED); @@ -119,6 +118,5 @@ public ContainerRequest filter(ContainerRequest containerRequest) throws WebAppl throw new WebApplicationException(Status.UNAUTHORIZED); } } - return containerRequest; } } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/CORSFilter.java b/src/java/org/jivesoftware/openfire/plugin/rest/CORSFilter.java index 9c7a70c5fd..2a9e7075cd 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/CORSFilter.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/CORSFilter.java @@ -1,26 +1,23 @@ package org.jivesoftware.openfire.plugin.rest; -import com.sun.jersey.spi.container.ContainerRequest; -import com.sun.jersey.spi.container.ContainerResponse; -import com.sun.jersey.spi.container.ContainerResponseFilter; +import javax.annotation.Priority; +import javax.ws.rs.Priorities; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import java.io.IOException; /** * The Class CORSFilter. */ +@Priority(Priorities.HEADER_DECORATOR) public class CORSFilter implements ContainerResponseFilter { - /* (non-Javadoc) - * @see com.sun.jersey.spi.container.ContainerResponseFilter#filter(com.sun.jersey.spi.container.ContainerRequest, com.sun.jersey.spi.container.ContainerResponse) - */ @Override - public ContainerResponse filter(ContainerRequest request, ContainerResponse response) { - response.getHttpHeaders().add("Access-Control-Allow-Origin", "*"); - response.getHttpHeaders().add("Access-Control-Allow-Headers", - "origin, content-type, accept, authorization"); - response.getHttpHeaders().add("Access-Control-Allow-Credentials", "true"); - response.getHttpHeaders().add("Access-Control-Allow-Methods", - "GET, POST, PUT, DELETE, OPTIONS, HEAD"); - - return response; + public void filter(ContainerRequestContext requestContext, ContainerResponseContext response) throws IOException { + response.getHeaders().add("Access-Control-Allow-Origin", "*"); + response.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization"); + response.getHeaders().add("Access-Control-Allow-Credentials", "true"); + response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); } } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/RESTServicePlugin.java b/src/java/org/jivesoftware/openfire/plugin/rest/RESTServicePlugin.java index d53a6fd061..b74668bf3b 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/RESTServicePlugin.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/RESTServicePlugin.java @@ -25,6 +25,7 @@ import javax.ws.rs.core.Response; +import org.jivesoftware.admin.AuthCheckFilter; import org.jivesoftware.openfire.container.Plugin; import org.jivesoftware.openfire.container.PluginManager; import org.jivesoftware.openfire.plugin.rest.entity.SystemProperties; @@ -112,12 +113,17 @@ public void initializePlugin(PluginManager manager, File pluginDirectory) { setServiceLoggingEnabled(JiveGlobals.getBooleanProperty(SERVICE_LOGGING_ENABLED, false)); // Listen to system property events PropertyEventDispatcher.addListener(this); + + // Exclude this servlet from requering the user to login + AuthCheckFilter.addExclude(JerseyWrapper.SERVLET_URL); } /* (non-Javadoc) * @see org.jivesoftware.openfire.container.Plugin#destroyPlugin() */ public void destroyPlugin() { + // Release the excluded URL + AuthCheckFilter.removeExclude(JerseyWrapper.SERVLET_URL); // Stop listening to system property events PropertyEventDispatcher.removeListener(this); } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntities.java index 3cae5f514d..2b6d46b8f8 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntities.java @@ -1,11 +1,12 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.codehaus.jackson.annotate.JsonProperty; /** * The Class GroupEntities. diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntities.java index f107ef01c3..a2c9d7f77a 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntities.java @@ -1,12 +1,12 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.codehaus.jackson.annotate.JsonProperty; - @XmlRootElement(name = "chatRooms") public class MUCRoomEntities { List mucRooms; diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/OccupantEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/OccupantEntities.java index 56cc1b6ac6..eb36a38ad2 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/OccupantEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/OccupantEntities.java @@ -1,11 +1,10 @@ package org.jivesoftware.openfire.plugin.rest.entity; -import java.util.List; +import com.fasterxml.jackson.annotation.JsonProperty; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; - -import org.codehaus.jackson.annotate.JsonProperty; +import java.util.List; @XmlRootElement(name = "occupants") public class OccupantEntities { diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/ParticipantEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/ParticipantEntities.java index 2024ba96e9..890a1c0546 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/ParticipantEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/ParticipantEntities.java @@ -1,12 +1,12 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.codehaus.jackson.annotate.JsonProperty; - @XmlRootElement(name = "participants") public class ParticipantEntities { List participants; diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SecurityAuditLogs.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SecurityAuditLogs.java index 8dfd81e507..cb9318354b 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SecurityAuditLogs.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SecurityAuditLogs.java @@ -1,12 +1,12 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.codehaus.jackson.annotate.JsonProperty; - @XmlRootElement(name = "logs") public class SecurityAuditLogs { List securityAuditLog; diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionEntities.java index 656daee874..521f44c169 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionEntities.java @@ -1,12 +1,12 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.codehaus.jackson.annotate.JsonProperty; - @XmlRootElement(name = "sessions") public class SessionEntities { List sessions; diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserEntities.java index 0812019f65..4a66386593 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserEntities.java @@ -1,12 +1,12 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import org.codehaus.jackson.annotate.JsonProperty; - /** * The Class UserEntities. */ diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/JerseyWrapper.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/JerseyWrapper.java index 7b2733a13f..7d5e4c1af8 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/JerseyWrapper.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/JerseyWrapper.java @@ -1,106 +1,37 @@ package org.jivesoftware.openfire.plugin.rest.service; -import java.util.HashMap; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; - -import org.jivesoftware.admin.AuthCheckFilter; +import org.glassfish.jersey.server.ResourceConfig; +import org.jivesoftware.openfire.plugin.rest.AuthFilter; +import org.jivesoftware.openfire.plugin.rest.CORSFilter; import org.jivesoftware.openfire.plugin.rest.exceptions.RESTExceptionMapper; +import org.jivesoftware.openfire.plugin.rest.entity.UserEntities; import org.jivesoftware.util.JiveGlobals; +import org.slf4j.LoggerFactory; -import com.sun.jersey.api.core.PackagesResourceConfig; -import com.sun.jersey.spi.container.servlet.ServletContainer; +import java.util.logging.Level; +import java.util.logging.Logger; /** * The Class JerseyWrapper. */ -public class JerseyWrapper extends ServletContainer { - +public class JerseyWrapper extends ResourceConfig { + /** The Constant CUSTOM_AUTH_PROPERTY_NAME */ private static final String CUSTOM_AUTH_PROPERTY_NAME = "plugin.restapi.customAuthFilter"; /** The Constant REST_AUTH_TYPE */ private static final String REST_AUTH_TYPE = "plugin.restapi.httpAuth"; - /** The Constant AUTHFILTER. */ - private static final String AUTHFILTER = "org.jivesoftware.openfire.plugin.rest.AuthFilter"; - - /** The Constant CORSFILTER. */ - private static final String CORSFILTER = "org.jivesoftware.openfire.plugin.rest.CORSFilter"; - - /** The Constant CONTAINER_REQUEST_FILTERS. */ - private static final String CONTAINER_REQUEST_FILTERS = "com.sun.jersey.spi.container.ContainerRequestFilters"; - - /** The Constant CONTAINER_RESPONSE_FILTERS. */ - private static final String CONTAINER_RESPONSE_FILTERS = "com.sun.jersey.spi.container.ContainerResponseFilters"; - - /** The Constant GZIP_FILTER. */ - private static final String GZIP_FILTER = "com.sun.jersey.api.container.filter.GZIPContentEncodingFilter"; - - /** The Constant RESOURCE_CONFIG_CLASS_KEY. */ - private static final String RESOURCE_CONFIG_CLASS_KEY = "com.sun.jersey.config.property.resourceConfigClass"; - - /** The Constant RESOURCE_CONFIG_CLASS. */ - private static final String RESOURCE_CONFIG_CLASS = "com.sun.jersey.api.core.PackagesResourceConfig"; - - /** The Constant SCAN_PACKAGE_DEFAULT. */ - private static final String SCAN_PACKAGE_DEFAULT = JerseyWrapper.class.getPackage().getName(); - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - /** The Constant SERVLET_URL. */ - private static final String SERVLET_URL = "restapi/*"; - - /** The config. */ - private static Map config; - - /** The prc. */ - private static PackagesResourceConfig prc; + public static final String SERVLET_URL = "restapi/*"; /** The Constant JERSEY_LOGGER. */ - private final static Logger JERSEY_LOGGER = Logger.getLogger("com.sun.jersey"); + private final static Logger JERSEY_LOGGER = Logger.getLogger("org.glassfish.jersey"); private static String loadingStatusMessage = null; static { JERSEY_LOGGER.setLevel(Level.SEVERE); - config = new HashMap(); - config.put(RESOURCE_CONFIG_CLASS_KEY, RESOURCE_CONFIG_CLASS); - config.put("com.sun.jersey.api.json.POJOMappingFeature", true); - - prc = new PackagesResourceConfig(SCAN_PACKAGE_DEFAULT); - prc.setPropertiesAndFeatures(config); - prc.getProperties().put(CONTAINER_RESPONSE_FILTERS, CORSFILTER); - // prc.getProperties().put(CONTAINER_RESPONSE_FILTERS, GZIP_FILTER); - loadAuthenticationFilter(); - - prc.getClasses().add(RestAPIService.class); - - prc.getClasses().add(MUCRoomService.class); - prc.getClasses().add(MUCRoomOwnersService.class); - prc.getClasses().add(MUCRoomAdminsService.class); - prc.getClasses().add(MUCRoomMembersService.class); - prc.getClasses().add(MUCRoomOutcastsService.class); - - prc.getClasses().add(UserServiceLegacy.class); - prc.getClasses().add(UserService.class); - prc.getClasses().add(UserRosterService.class); - prc.getClasses().add(UserGroupService.class); - prc.getClasses().add(UserLockoutService.class); - - prc.getClasses().add(GroupService.class); - prc.getClasses().add(SessionService.class); - prc.getClasses().add(MsgArchiveService.class); - prc.getClasses().add(StatisticsService.class); - prc.getClasses().add(MessageService.class); - prc.getClasses().add(SecurityAuditLogService.class); - - prc.getClasses().add(RESTExceptionMapper.class); } public static String tryLoadingAuthenticationFilter(String customAuthFilterClassName) { @@ -119,25 +50,23 @@ public static String tryLoadingAuthenticationFilter(String customAuthFilterClass return loadingStatusMessage; } - public static String loadAuthenticationFilter() { + public String loadAuthenticationFilter() { // Check if custom AuthFilter is available String customAuthFilterClassName = JiveGlobals.getProperty(CUSTOM_AUTH_PROPERTY_NAME); String restAuthType = JiveGlobals.getProperty(REST_AUTH_TYPE); - String pickedAuthFilter = AUTHFILTER; + Class pickedAuthFilter = AuthFilter.class; try { if(customAuthFilterClassName != null && "custom".equals(restAuthType)) { - Class.forName(customAuthFilterClassName, false, JerseyWrapper.class.getClassLoader()); - pickedAuthFilter = customAuthFilterClassName; + pickedAuthFilter = Class.forName(customAuthFilterClassName, false, JerseyWrapper.class.getClassLoader()); loadingStatusMessage = null; } } catch (ClassNotFoundException e) { loadingStatusMessage = "No custom auth filter found for restAPI plugin! " + customAuthFilterClassName + " " + restAuthType; } - // prc.getProperties().put(CONTAINER_REQUEST_FILTERS, GZIP_FILTER); - prc.getProperties().put(CONTAINER_REQUEST_FILTERS, pickedAuthFilter); + register(pickedAuthFilter); return loadingStatusMessage; } @@ -145,32 +74,31 @@ public static String loadAuthenticationFilter() { * Instantiates a new jersey wrapper. */ public JerseyWrapper() { - super(prc); - } - - /* - * (non-Javadoc) - * - * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig) - */ - @Override - public void init(ServletConfig servletConfig) throws ServletException { loadAuthenticationFilter(); - super.init(servletConfig); - // Exclude this servlet from requering the user to login - AuthCheckFilter.addExclude(SERVLET_URL); - } - - /* - * (non-Javadoc) - * - * @see com.sun.jersey.spi.container.servlet.ServletContainer#destroy() - */ - @Override - public void destroy() { - super.destroy(); - // Release the excluded URL - AuthCheckFilter.removeExclude(SERVLET_URL); + register(CORSFilter.class); + + registerClasses( + GroupService.class, + MessageService.class, + MsgArchiveService.class, + MUCRoomAdminsService.class, + MUCRoomMembersService.class, + MUCRoomOutcastsService.class, + MUCRoomOwnersService.class, + MUCRoomService.class, + RestAPIService.class, + SecurityAuditLogService.class, + SessionService.class, + StatisticsService.class, + UserGroupService.class, + UserLockoutService.class, + UserRosterService.class, + UserService.class, + UserServiceLegacy.class + ); + + // Exception mapper + register(RESTExceptionMapper.class); } /* diff --git a/src/web/WEB-INF/web.xml b/src/web/WEB-INF/web.xml index 74f2136209..986d936bec 100644 --- a/src/web/WEB-INF/web.xml +++ b/src/web/WEB-INF/web.xml @@ -6,7 +6,11 @@ JerseyWrapper - org.jivesoftware.openfire.plugin.rest.service.JerseyWrapper + org.glassfish.jersey.servlet.ServletContainer + + javax.ws.rs.Application + org.jivesoftware.openfire.plugin.rest.service.JerseyWrapper +