summaryrefslogtreecommitdiff
path: root/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java')
-rw-r--r--java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java447
1 files changed, 356 insertions, 91 deletions
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
index a76bd98179..689bdb50d8 100644
--- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
+++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
@@ -18,191 +18,456 @@
* under the License.
*
*/
-
package org.apache.qpid.server.management.plugin.servlet.rest;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
-import java.security.Principal;
-import java.util.Collections;
+import java.security.AccessControlException;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
import javax.security.auth.Subject;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.codec.binary.Base64;
+import org.apache.log4j.Logger;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.logging.LogActor;
+import org.apache.qpid.server.logging.RootMessageLogger;
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.actors.HttpManagementActor;
+import org.apache.qpid.server.management.plugin.HttpManagement;
+import org.apache.qpid.server.management.plugin.session.LoginLogoutReporter;
import org.apache.qpid.server.model.Broker;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.security.auth.AuthenticationResult;
+import org.apache.qpid.server.security.SecurityManager;
+import org.apache.qpid.server.security.SubjectCreator;
+import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus;
+import org.apache.qpid.server.security.auth.SubjectAuthenticationResult;
import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManager;
-import org.apache.qpid.server.security.auth.manager.AuthenticationManager;
public abstract class AbstractServlet extends HttpServlet
{
- private final Broker _broker;
+ private static final Logger LOGGER = Logger.getLogger(AbstractServlet.class);
+
+ /**
+ * Servlet context attribute holding a reference to a broker instance
+ */
+ public static final String ATTR_BROKER = "Qpid.broker";
+
+ /**
+ * Servlet context attribute holding a reference to plugin configuration
+ */
+ public static final String ATTR_MANAGEMENT = "Qpid.management";
+
+ private static final String ATTR_LOGIN_LOGOUT_REPORTER = "AbstractServlet.loginLogoutReporter";
+ private static final String ATTR_SUBJECT = "AbstractServlet.subject";
+ private static final String ATTR_LOG_ACTOR = "AbstractServlet.logActor";
+
+ private Broker _broker;
+ private RootMessageLogger _rootLogger;
+ private HttpManagement _httpManagement;
protected AbstractServlet()
{
super();
- _broker = ApplicationRegistry.getInstance().getBroker();
}
- protected AbstractServlet(Broker broker)
+ @Override
+ public void init() throws ServletException
{
- _broker = broker;
+ ServletConfig servletConfig = getServletConfig();
+ ServletContext servletContext = servletConfig.getServletContext();
+ _broker = (Broker)servletContext.getAttribute(ATTR_BROKER);
+ _rootLogger = _broker.getRootMessageLogger();
+ _httpManagement = (HttpManagement)servletContext.getAttribute(ATTR_MANAGEMENT);
+ super.init();
}
@Override
- protected final void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException
+ protected final void doGet(final HttpServletRequest request, final HttpServletResponse resp)
{
- setAuthorizedSubject(request);
- try
- {
- onGet(request, resp);
- }
- finally
- {
- clearAuthorizedSubject();
- }
+ doWithSubjectAndActor(
+ new PrivilegedExceptionAction<Void>()
+ {
+ @Override
+ public Void run() throws Exception
+ {
+ doGetWithSubjectAndActor(request, resp);
+ return null;
+ }
+ },
+ request,
+ resp
+ );
+ }
+
+ /**
+ * Performs the GET action as the logged-in {@link Subject}.
+ * The {@link LogActor} is set before this method is called.
+ * Subclasses commonly override this method
+ */
+ protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException
+ {
+ throw new UnsupportedOperationException("GET not supported by this servlet");
+ }
+
+
+ @Override
+ protected final void doPost(final HttpServletRequest request, final HttpServletResponse resp)
+ {
+ doWithSubjectAndActor(
+ new PrivilegedExceptionAction<Void>()
+ {
+ @Override
+ public Void run() throws Exception
+ {
+ doPostWithSubjectAndActor(request, resp);
+ return null;
+ }
+ },
+ request,
+ resp
+ );
+ }
+
+ /**
+ * Performs the POST action as the logged-in {@link Subject}.
+ * The {@link LogActor} is set before this method is called.
+ * Subclasses commonly override this method
+ */
+ protected void doPostWithSubjectAndActor(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ {
+ throw new UnsupportedOperationException("POST not supported by this servlet");
+ }
+
+ @Override
+ protected final void doPut(final HttpServletRequest request, final HttpServletResponse resp)
+ {
+ doWithSubjectAndActor(
+ new PrivilegedExceptionAction<Void>()
+ {
+ @Override
+ public Void run() throws Exception
+ {
+ doPutWithSubjectAndActor(request, resp);
+ return null;
+ }
+ },
+ request,
+ resp
+ );
}
- protected void onGet(HttpServletRequest request, HttpServletResponse resp) throws IOException, ServletException
+ /**
+ * Performs the PUT action as the logged-in {@link Subject}.
+ * The {@link LogActor} is set before this method is called.
+ * Subclasses commonly override this method
+ */
+ protected void doPutWithSubjectAndActor(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
- super.doGet(request, resp);
+ throw new UnsupportedOperationException("PUT not supported by this servlet");
}
- private void clearAuthorizedSubject()
+ @Override
+ protected final void doDelete(final HttpServletRequest request, final HttpServletResponse resp)
+ throws ServletException, IOException
{
- org.apache.qpid.server.security.SecurityManager.setThreadSubject(null);
+ doWithSubjectAndActor(
+ new PrivilegedExceptionAction<Void>()
+ {
+ @Override
+ public Void run() throws Exception
+ {
+ doDeleteWithSubjectAndActor(request, resp);
+ return null;
+ }
+ },
+ request,
+ resp
+ );
}
+ /**
+ * Performs the PUT action as the logged-in {@link Subject}.
+ * The {@link LogActor} is set before this method is called.
+ * Subclasses commonly override this method
+ */
+ protected void doDeleteWithSubjectAndActor(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ {
+ throw new UnsupportedOperationException("DELETE not supported by this servlet");
+ }
- private void setAuthorizedSubject(HttpServletRequest request)
+ private void doWithSubjectAndActor(
+ PrivilegedExceptionAction<Void> privilegedExceptionAction,
+ final HttpServletRequest request,
+ final HttpServletResponse resp)
{
- HttpSession session = request.getSession(true);
- Subject subject = (Subject) session.getAttribute("subject");
+ Subject subject;
+ try
+ {
+ subject = getAndCacheAuthorizedSubject(request);
+ }
+ catch (AccessControlException e)
+ {
+ sendError(resp, HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
- if(subject == null)
+ SecurityManager.setThreadSubject(subject);
+ try
{
- Principal principal = request.getUserPrincipal();
- if(principal != null)
+ HttpManagementActor logActor = getLogActorAndCacheInSession(request);
+ CurrentActor.set(logActor);
+ try
+ {
+ Subject.doAs(subject, privilegedExceptionAction);
+ }
+ catch(RuntimeException e)
{
- subject = new Subject(false, Collections.singleton(principal),Collections.emptySet(),
- Collections.emptySet());
+ LOGGER.error("Unable to perform action", e);
+ throw e;
}
- else
+ catch (PrivilegedActionException e)
{
- String header = request.getHeader("Authorization");
+ LOGGER.error("Unable to perform action", e);
+ throw new RuntimeException(e.getCause());
+ }
+ finally
+ {
+ CurrentActor.remove();
+ }
+ }
+ finally
+ {
+ try
+ {
+ SecurityManager.setThreadSubject(null);
+ }
+ finally
+ {
+ AMQShortString.clearLocalCache();
+ }
+ }
+ }
+
+ /**
+ * Gets the logged-in {@link Subject} by trying the following:
+ *
+ * <ul>
+ * <li>Get it from the session</li>
+ * <li>Get it from the request</li>
+ * <li>Log in using the username and password in the Authorization HTTP header</li>
+ * <li>Create a Subject representing the anonymous user.</li>
+ * </ul>
+ *
+ * If an authenticated subject is found it is cached in the http session.
+ */
+ private Subject getAndCacheAuthorizedSubject(HttpServletRequest request)
+ {
+ HttpSession session = request.getSession();
+ Subject subject = getAuthorisedSubjectFromSession(session);
- /*
- * TODO - Should configure whether basic authentication is allowed... and in particular whether it
- * should be allowed over non-ssl connections
- * */
+ if(subject != null)
+ {
+ return subject;
+ }
- if (header != null)
+ SubjectCreator subjectCreator = getSubjectCreator(request);
+ subject = authenticate(request, subjectCreator);
+ if (subject != null)
+ {
+ authoriseManagement(request, subject);
+ setAuthorisedSubjectInSession(subject, request, session);
+ }
+ else
+ {
+ subject = subjectCreator.createSubjectWithGroups(AnonymousAuthenticationManager.ANONYMOUS_USERNAME);
+ }
+
+ return subject;
+ }
+
+ protected void authoriseManagement(HttpServletRequest request, Subject subject)
+ {
+ // TODO: We should eliminate SecurityManager.setThreadSubject in favour of Subject.doAs
+ SecurityManager.setThreadSubject(subject); // Required for accessManagement check
+ LogActor actor = createHttpManagementActor(request);
+ CurrentActor.set(actor);
+ try
+ {
+ try
+ {
+ Subject.doAs(subject, new PrivilegedExceptionAction<Void>() // Required for proper logging of Subject
{
- String[] tokens = header.split("\\s");
- if(tokens.length >= 2
- && "BASIC".equalsIgnoreCase(tokens[0]))
+ @Override
+ public Void run() throws Exception
{
- String[] credentials = (new String(Base64.decodeBase64(tokens[1].getBytes()))).split(":",2);
- if(credentials.length == 2)
+ boolean allowed = getSecurityManager().accessManagement();
+ if (!allowed)
{
- SocketAddress address = getSocketAddress(request);
- AuthenticationManager authenticationManager =
- ApplicationRegistry.getInstance().getAuthenticationManager(address);
- AuthenticationResult authResult =
- authenticationManager.authenticate(credentials[0], credentials[1]);
- subject = authResult.getSubject();
-
+ throw new AccessControlException("User is not authorised for management");
}
+ return null;
}
- }
+ });
+ }
+ catch (PrivilegedActionException e)
+ {
+ throw new RuntimeException("Unable to perform access check", e);
}
}
- if (subject == null)
+ finally
{
- subject = AnonymousAuthenticationManager.ANONYMOUS_SUBJECT;
+ try
+ {
+ CurrentActor.remove();
+ }
+ finally
+ {
+ SecurityManager.setThreadSubject(null);
+ }
}
- org.apache.qpid.server.security.SecurityManager.setThreadSubject(subject);
-
}
- protected Subject getSubject(HttpSession session)
+ private Subject authenticate(HttpServletRequest request, SubjectCreator subjectCreator)
{
- return (Subject)session.getAttribute("subject");
+ Subject subject = null;
+
+ String remoteUser = request.getRemoteUser();
+ if(remoteUser != null)
+ {
+ subject = authenticateUserAndGetSubject(subjectCreator, remoteUser, null);
+ }
+ else
+ {
+ String header = request.getHeader("Authorization");
+
+ if (header != null)
+ {
+ String[] tokens = header.split("\\s");
+ if(tokens.length >= 2 && "BASIC".equalsIgnoreCase(tokens[0]))
+ {
+ if(!isBasicAuthSupported(request))
+ {
+ //TODO: write a return response indicating failure?
+ throw new IllegalArgumentException("BASIC Authorization is not enabled.");
+ }
+
+ subject = performBasicAuth(subject, subjectCreator, tokens[1]);
+ }
+ }
+ }
+
+ return subject;
}
- @Override
- protected final void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ private Subject performBasicAuth(Subject subject,SubjectCreator subjectCreator, String base64UsernameAndPassword)
{
- setAuthorizedSubject(req);
- try
+ String[] credentials = (new String(Base64.decodeBase64(base64UsernameAndPassword.getBytes()))).split(":",2);
+ if(credentials.length == 2)
{
- onPost(req, resp);
+ subject = authenticateUserAndGetSubject(subjectCreator, credentials[0], credentials[1]);
}
- finally
+ else
{
- clearAuthorizedSubject();
+ //TODO: write a return response indicating failure?
+ throw new AccessControlException("Invalid number of credentials supplied: "
+ + credentials.length);
}
+ return subject;
+ }
+ private Subject authenticateUserAndGetSubject(SubjectCreator subjectCreator, String username, String password)
+ {
+ SubjectAuthenticationResult authResult = subjectCreator.authenticate(username, password);
+ if( authResult.getStatus() != AuthenticationStatus.SUCCESS)
+ {
+ //TODO: write a return response indicating failure?
+ throw new AccessControlException("Incorrect username or password");
+ }
+ Subject subject = authResult.getSubject();
+ return subject;
}
- protected void onPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ private boolean isBasicAuthSupported(HttpServletRequest req)
{
- super.doPost(req, resp);
+ return req.isSecure() ? _httpManagement.isHttpsBasicAuthenticationEnabled()
+ : _httpManagement.isHttpBasicAuthenticationEnabled();
}
- @Override
- protected final void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ private HttpManagementActor getLogActorAndCacheInSession(HttpServletRequest req)
{
- setAuthorizedSubject(req);
- try
- {
- onPut(req, resp);
+ HttpSession session = req.getSession();
- }
- finally
+ HttpManagementActor actor = (HttpManagementActor) session.getAttribute(ATTR_LOG_ACTOR);
+ if(actor == null)
{
- clearAuthorizedSubject();
+ actor = createHttpManagementActor(req);
+ session.setAttribute(ATTR_LOG_ACTOR, actor);
}
+
+ return actor;
}
- protected void onPut(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException
+ protected Subject getAuthorisedSubjectFromSession(HttpSession session)
{
- super.doPut(req,resp);
+ return (Subject)session.getAttribute(ATTR_SUBJECT);
}
- @Override
- protected final void doDelete(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException
+ protected void setAuthorisedSubjectInSession(Subject subject, HttpServletRequest request, final HttpSession session)
+ {
+ session.setAttribute(ATTR_SUBJECT, subject);
+
+ LogActor logActor = createHttpManagementActor(request);
+ // Cause the user logon to be logged.
+ session.setAttribute(ATTR_LOGIN_LOGOUT_REPORTER, new LoginLogoutReporter(logActor, subject));
+ }
+
+ protected Broker getBroker()
+ {
+ return _broker;
+ }
+
+ protected SocketAddress getSocketAddress(HttpServletRequest request)
+ {
+ return InetSocketAddress.createUnresolved(request.getServerName(), request.getServerPort());
+ }
+
+ protected void sendError(final HttpServletResponse resp, int errorCode)
{
- setAuthorizedSubject(req);
try
{
- onDelete(req, resp);
+ resp.sendError(errorCode);
}
- finally
+ catch (IOException e)
{
- clearAuthorizedSubject();
+ throw new RuntimeException("Failed to send error response code " + errorCode, e);
}
}
- protected void onDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ private HttpManagementActor createHttpManagementActor(HttpServletRequest request)
{
- super.doDelete(req, resp);
+ return new HttpManagementActor(_rootLogger, request.getRemoteAddr(), request.getRemotePort());
}
+ protected HttpManagement getManagement()
+ {
+ return _httpManagement;
+ }
- protected Broker getBroker()
+ protected SecurityManager getSecurityManager()
{
- return _broker;
+ return _broker.getSecurityManager();
}
- protected SocketAddress getSocketAddress(HttpServletRequest request)
+ protected SubjectCreator getSubjectCreator(HttpServletRequest request)
{
- return InetSocketAddress.createUnresolved(request.getServerName(), request.getServerPort());
+ return _broker.getSubjectCreator(getSocketAddress(request));
}
}