Commit ed8c675b authored by Francesco Scoccia's avatar Francesco Scoccia

RestAPI Plugin - Provide the possibility to use Custom Auth Filter

Right now, RestAPI plugin provides two option for accessing its
endpoints:
# Basic authentication: uses username and password of an existing
Openfire admin accoount
# Secret key: uses for authentication a key defined in the Openfire
Admin console.

It may be really useful to provide the option to define another Filter
as the authentication mechanism to use for accessing the plugin' Rest
service given its fully qualified class name.
parent da205c4d
......@@ -77,7 +77,7 @@ public class AuthFilter implements ContainerRequestFilter {
}
// HTTP Basic Auth or Shared Secret key
if (plugin.isHttpBasicAuth()) {
if ("basic".equals(plugin.getHttpAuth())) {
String[] usernameAndPassword = BasicAuth.decode(auth);
// If username or password fail
......
......@@ -47,6 +47,8 @@ public class RESTServicePlugin implements Plugin, PropertyEventListener {
/** The Constant INSTANCE. */
public static final RESTServicePlugin INSTANCE = new RESTServicePlugin();
private static final String CUSTOM_AUTH_FILTER_PROPERTY_NAME = "plugin.restapi.customAuthFilter";
/** The secret. */
private String secret;
......@@ -56,8 +58,12 @@ public class RESTServicePlugin implements Plugin, PropertyEventListener {
/** The enabled. */
private boolean enabled;
/** The http basic auth. */
private boolean httpBasicAuth;
/** The http auth. */
private String httpAuth;
/** The custom authentication filter */
private String customAuthFilterClassName;
/**
* Gets the single instance of RESTServicePlugin.
......@@ -78,12 +84,15 @@ public class RESTServicePlugin implements Plugin, PropertyEventListener {
secret = StringUtils.randomString(16);
setSecret(secret);
}
// See if Custom authentication filter has been defined
customAuthFilterClassName = JiveGlobals.getProperty("plugin.restapi.customAuthFilter", "");
// See if the service is enabled or not.
enabled = JiveGlobals.getBooleanProperty("plugin.restapi.enabled", false);
// See if the HTTP Basic Auth is enabled or not.
httpBasicAuth = JiveGlobals.getBooleanProperty("plugin.restapi.httpAuth.enabled", false);
httpAuth = JiveGlobals.getProperty("plugin.restapi.httpAuth", "basic");
// Get the list of IP addresses that can use this service. An empty list
// means that this filter is disabled.
......@@ -201,6 +210,26 @@ public class RESTServicePlugin implements Plugin, PropertyEventListener {
this.secret = secret;
}
/**
* Returns the custom authentication filter class name used in place of the basic ones to grant permission to use the Rest services.
*
* @return custom authentication filter class name .
*/
public String getCustomAuthFilterClassName() {
return customAuthFilterClassName;
}
/**
* Sets the customAuthFIlterClassName used to grant permission to use the Rest services.
*
* @param customAuthFilterClassName
* custom authentication filter class name.
*/
public void setCustomAuthFiIterClassName(String customAuthFilterClassName) {
JiveGlobals.setProperty(CUSTOM_AUTH_FILTER_PROPERTY_NAME, customAuthFilterClassName);
this.customAuthFilterClassName = customAuthFilterClassName;
}
/**
* Gets the allowed i ps.
*
......@@ -243,22 +272,22 @@ public class RESTServicePlugin implements Plugin, PropertyEventListener {
}
/**
* Checks if is http basic auth.
* Gets the http authentication mechanism.
*
* @return true, if is http basic auth
* @return the http authentication mechanism
*/
public boolean isHttpBasicAuth() {
return httpBasicAuth;
public String getHttpAuth() {
return httpAuth;
}
/**
* Sets the http basic auth.
* Sets the http auth.
*
* @param httpBasicAuth the new http basic auth
* @param httpAuth the new http auth
*/
public void setHttpBasicAuth(boolean httpBasicAuth) {
this.httpBasicAuth = httpBasicAuth;
JiveGlobals.setProperty("plugin.restapi.httpAuth.enabled", httpBasicAuth ? "true" : "false");
public void setHttpAuth(String httpAuth) {
this.httpAuth = httpAuth;
JiveGlobals.setProperty("plugin.restapi.httpAuth", httpAuth);
}
/* (non-Javadoc)
......@@ -271,8 +300,10 @@ public class RESTServicePlugin implements Plugin, PropertyEventListener {
this.enabled = Boolean.parseBoolean((String) params.get("value"));
} else if (property.equals("plugin.restapi.allowedIPs")) {
this.allowedIPs = StringUtils.stringToCollection((String) params.get("value"));
} else if (property.equals("plugin.restapi.httpAuth.enabled")) {
this.httpBasicAuth = Boolean.parseBoolean((String) params.get("value"));
} else if (property.equals("plugin.restapi.httpAuth")) {
this.httpAuth = (String) params.get("value");
} else if(property.equals(CUSTOM_AUTH_FILTER_PROPERTY_NAME)) {
this.customAuthFilterClassName = (String) params.get("value");
}
}
......@@ -286,8 +317,10 @@ public class RESTServicePlugin implements Plugin, PropertyEventListener {
this.enabled = false;
} else if (property.equals("plugin.restapi.allowedIPs")) {
this.allowedIPs = Collections.emptyList();
} else if (property.equals("plugin.restapi.httpAuth.enabled")) {
this.httpBasicAuth = false;
} else if (property.equals("plugin.restapi.httpAuth")) {
this.httpAuth = "basic";
} else if(property.equals(CUSTOM_AUTH_FILTER_PROPERTY_NAME)) {
this.customAuthFilterClassName = null;
}
}
......
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.jivesoftware.openfire.plugin.rest.exceptions.RESTExceptionMapper;
import com.sun.jersey.api.core.PackagesResourceConfig;
import com.sun.jersey.spi.container.servlet.ServletContainer;
/**
* The Class JerseyWrapper.
*/
public class JerseyWrapper extends ServletContainer {
/** 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 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<String, Object> config;
/** The prc. */
private static PackagesResourceConfig prc;
/** The Constant JERSEY_LOGGER. */
private final static Logger JERSEY_LOGGER = Logger.getLogger("com.sun.jersey");
static {
JERSEY_LOGGER.setLevel(Level.SEVERE);
config = new HashMap<String, Object>();
config.put(RESOURCE_CONFIG_CLASS_KEY, RESOURCE_CONFIG_CLASS);
prc = new PackagesResourceConfig(SCAN_PACKAGE_DEFAULT);
prc.setPropertiesAndFeatures(config);
prc.getProperties().put(CONTAINER_REQUEST_FILTERS, AUTHFILTER);
prc.getProperties().put(CONTAINER_RESPONSE_FILTERS, CORSFILTER);
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(RESTExceptionMapper.class);
}
/**
* 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 {
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);
}
}
package org.jivesoftware.openfire.plugin.rest.service;
import java.util.HashMap;
import java.util.Map;
import java.lang.ClassNotFoundException;
import java.lang.Class;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import org.jivesoftware.admin.AuthCheckFilter;
import org.jivesoftware.openfire.plugin.rest.exceptions.RESTExceptionMapper;
import org.jivesoftware.util.JiveGlobals;
import com.sun.jersey.api.core.PackagesResourceConfig;
import com.sun.jersey.spi.container.servlet.ServletContainer;
import org.apache.log4j.Logger;
/**
* The Class JerseyWrapper.
*/
public class JerseyWrapper extends ServletContainer {
/** The Constant CUSTOM_AUTH_PROPERTY_NAME */
private static final String CUSTOM_AUTH_PROPERTY_NAME = "plugin.restapi.customAuthFilter";
/** 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 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<String, Object> config;
/** The prc. */
private static PackagesResourceConfig prc;
private static Logger LOGGER = Logger.getLogger(JerseyWrapper.class);
static {
// Check if custom AuthFilter is available
String customAuthFilterClassName = JiveGlobals.getProperty(CUSTOM_AUTH_PROPERTY_NAME);
String pickedAuthFilter = AUTHFILTER;
try {
if(customAuthFilterClassName != null) {
LOGGER.info("Trying to set a custom authentication filter for restAPI plugin with classname: " + customAuthFilterClassName);
Class.forName(customAuthFilterClassName, false, JerseyWrapper.class.getClassLoader());
pickedAuthFilter = customAuthFilterClassName;
}
} catch (ClassNotFoundException e) {
LOGGER.info("No custom auth filter found for restAPI plugin! Still using the default one");
}
config = new HashMap<String, Object>();
config.put(RESOURCE_CONFIG_CLASS_KEY, RESOURCE_CONFIG_CLASS);
prc = new PackagesResourceConfig(SCAN_PACKAGE_DEFAULT);
prc.setPropertiesAndFeatures(config);
prc.getProperties().put(CONTAINER_REQUEST_FILTERS, pickedAuthFilter);
prc.getProperties().put(CONTAINER_RESPONSE_FILTERS, CORSFILTER);
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(RESTExceptionMapper.class);
}
/**
* 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 {
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);
}
}
......@@ -20,9 +20,10 @@
boolean success = request.getParameter("success") != null;
String secret = ParamUtils.getParameter(request, "secret");
boolean enabled = ParamUtils.getBooleanParameter(request, "enabled");
boolean httpBasicAuth = ParamUtils.getBooleanParameter(request, "authtype");
String httpAuth = ParamUtils.getParameter(request, "authtype");
String allowedIPs = ParamUtils.getParameter(request, "allowedIPs");
String customAuthFilterClassName = ParamUtils.getParameter(request, "customAuthFilterClassName");
RESTServicePlugin plugin = (RESTServicePlugin) XMPPServer.getInstance().getPluginManager()
.getPlugin("restapi");
......@@ -32,8 +33,9 @@
if (errors.size() == 0) {
plugin.setEnabled(enabled);
plugin.setSecret(secret);
plugin.setHttpBasicAuth(httpBasicAuth);
plugin.setHttpAuth(httpAuth);
plugin.setAllowedIPs(StringUtils.stringToCollection(allowedIPs));
plugin.setCustomAuthFiIterClassName(customAuthFilterClassName);
response.sendRedirect("rest-api.jsp?success=true");
return;
}
......@@ -41,8 +43,9 @@
secret = plugin.getSecret();
enabled = plugin.isEnabled();
httpBasicAuth = plugin.isHttpBasicAuth();
httpAuth = plugin.getHttpAuth();
allowedIPs = StringUtils.collectionToString(plugin.getAllowedIPs());
customAuthFilterClassName = plugin.getCustomAuthFilterClassName();
%>
<html>
......@@ -101,13 +104,13 @@
<br>
<br>
<input type="radio" name="authtype" value="true"
id="http_basic_auth" <%=((httpBasicAuth) ? "checked" : "")%>>
<input type="radio" name="authtype" value="basic"
id="http_basic_auth" <%=("basic".equals(httpAuth) ? "checked" : "")%>>
<label for="http_basic_auth">HTTP basic auth - REST API
authentication with Openfire admin account.</label>
<br>
<input type="radio" name="authtype" value="false"
id="secretKeyAuth" <%=((!httpBasicAuth) ? "checked" : "")%>>
<input type="radio" name="authtype" value="secret"
id="secretKeyAuth" <%=("secret".equals(httpAuth) ? "checked" : "")%>>
<label for="secretKeyAuth">Secret key auth - REST API
authentication over specified secret key.</label>
<br>
......@@ -116,6 +119,16 @@
<input type="text" name="secret" value="<%=secret%>"
id="text_secret">
<br>
<input type="radio" name="authtype" value="custom"
id="customFilterAuth" <%=("custom".equals(httpAuth) ? "checked" : "")%>>
<label for="secretKeyAuth">Custom authentication filter classname - REST API
authentication delegates to a custom filter implemented in some other plugin.</label>
<br>
<label style="padding-left: 25px" for="text_secret">Filter
classname:</label>
<input type="text" name="customAuthFilterClassName" value="<%=customAuthFilterClassName%>"
id="custom_auth_filter_class_name">
<br>
<br>
<label for="allowedIPs">Allowed IP Addresses:</label>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment