diff options
Diffstat (limited to 'java/broker-plugins/management-http/src/main/java')
35 files changed, 1977 insertions, 956 deletions
diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java new file mode 100644 index 0000000000..c2ac675e20 --- /dev/null +++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java @@ -0,0 +1,416 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ +package org.apache.qpid.server.management.plugin; + +import java.io.File; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.UUID; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.messages.ManagementConsoleMessages; +import org.apache.qpid.server.management.plugin.servlet.DefinedFileServlet; +import org.apache.qpid.server.management.plugin.servlet.FileServlet; +import org.apache.qpid.server.management.plugin.servlet.rest.AbstractServlet; +import org.apache.qpid.server.management.plugin.servlet.rest.LogRecordsServlet; +import org.apache.qpid.server.management.plugin.servlet.rest.LogoutServlet; +import org.apache.qpid.server.management.plugin.servlet.rest.MessageContentServlet; +import org.apache.qpid.server.management.plugin.servlet.rest.MessageServlet; +import org.apache.qpid.server.management.plugin.servlet.rest.RestServlet; +import org.apache.qpid.server.management.plugin.servlet.rest.SaslServlet; +import org.apache.qpid.server.management.plugin.servlet.rest.StructureServlet; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Binding; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.Connection; +import org.apache.qpid.server.model.Exchange; +import org.apache.qpid.server.model.Group; +import org.apache.qpid.server.model.GroupMember; +import org.apache.qpid.server.model.GroupProvider; +import org.apache.qpid.server.model.KeyStore; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Protocol; +import org.apache.qpid.server.model.Queue; +import org.apache.qpid.server.model.Session; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.User; +import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.model.adapter.AbstractPluginAdapter; +import org.apache.qpid.server.plugin.PluginFactory; +import org.apache.qpid.server.util.MapValueConverter; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.SessionManager; +import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.server.ssl.SslSocketConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +public class HttpManagement extends AbstractPluginAdapter +{ + private final Logger _logger = Logger.getLogger(HttpManagement.class); + + // 10 minutes by default + public static final int DEFAULT_TIMEOUT_IN_SECONDS = 60 * 10; + public static final boolean DEFAULT_HTTP_BASIC_AUTHENTICATION_ENABLED = false; + public static final boolean DEFAULT_HTTPS_BASIC_AUTHENTICATION_ENABLED = true; + public static final boolean DEFAULT_HTTP_SASL_AUTHENTICATION_ENABLED = true; + public static final boolean DEFAULT_HTTPS_SASL_AUTHENTICATION_ENABLED = true; + public static final String DEFAULT_NAME = "httpManagement"; + + public static final String TIME_OUT = "sessionTimeout"; + public static final String HTTP_BASIC_AUTHENTICATION_ENABLED = "httpBasicAuthenticationEnabled"; + public static final String HTTPS_BASIC_AUTHENTICATION_ENABLED = "httpsBasicAuthenticationEnabled"; + public static final String HTTP_SASL_AUTHENTICATION_ENABLED = "httpSaslAuthenticationEnabled"; + public static final String HTTPS_SASL_AUTHENTICATION_ENABLED = "httpsSaslAuthenticationEnabled"; + + public static final String PLUGIN_TYPE = "MANAGEMENT-HTTP"; + + @SuppressWarnings("serial") + private static final Collection<String> AVAILABLE_ATTRIBUTES = Collections.unmodifiableSet(new HashSet<String>(Plugin.AVAILABLE_ATTRIBUTES) + {{ + add(HTTP_BASIC_AUTHENTICATION_ENABLED); + add(HTTPS_BASIC_AUTHENTICATION_ENABLED); + add(HTTP_SASL_AUTHENTICATION_ENABLED); + add(HTTPS_SASL_AUTHENTICATION_ENABLED); + add(TIME_OUT); + add(PluginFactory.PLUGIN_TYPE); + }}); + + public static final String ENTRY_POINT_PATH = "/management"; + + private static final String OPERATIONAL_LOGGING_NAME = "Web"; + + + @SuppressWarnings("serial") + public static final Map<String, Object> DEFAULTS = Collections.unmodifiableMap(new HashMap<String, Object>() + {{ + put(HTTP_BASIC_AUTHENTICATION_ENABLED, DEFAULT_HTTP_BASIC_AUTHENTICATION_ENABLED); + put(HTTPS_BASIC_AUTHENTICATION_ENABLED, DEFAULT_HTTPS_BASIC_AUTHENTICATION_ENABLED); + put(HTTP_SASL_AUTHENTICATION_ENABLED, DEFAULT_HTTP_SASL_AUTHENTICATION_ENABLED); + put(HTTPS_SASL_AUTHENTICATION_ENABLED, DEFAULT_HTTPS_SASL_AUTHENTICATION_ENABLED); + put(TIME_OUT, DEFAULT_TIMEOUT_IN_SECONDS); + put(NAME, DEFAULT_NAME); + }}); + + @SuppressWarnings("serial") + private static final Map<String, Class<?>> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Class<?>>(){{ + put(HTTP_BASIC_AUTHENTICATION_ENABLED, Boolean.class); + put(HTTPS_BASIC_AUTHENTICATION_ENABLED, Boolean.class); + put(HTTP_SASL_AUTHENTICATION_ENABLED, Boolean.class); + put(HTTPS_SASL_AUTHENTICATION_ENABLED, Boolean.class); + put(NAME, String.class); + put(TIME_OUT, Integer.class); + put(PluginFactory.PLUGIN_TYPE, String.class); + }}); + + private final Broker _broker; + + private Server _server; + + public HttpManagement(UUID id, Broker broker, Map<String, Object> attributes) + { + super(id, DEFAULTS, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES), broker.getTaskExecutor()); + _broker = broker; + addParent(Broker.class, broker); + } + + @Override + protected boolean setState(State currentState, State desiredState) + { + if(desiredState == State.ACTIVE) + { + start(); + return true; + } + else if(desiredState == State.STOPPED) + { + stop(); + return true; + } + return false; + } + + private void start() + { + CurrentActor.get().message(ManagementConsoleMessages.STARTUP(OPERATIONAL_LOGGING_NAME)); + + Collection<Port> httpPorts = getHttpPorts(_broker.getPorts()); + _server = createServer(httpPorts); + try + { + _server.start(); + logOperationalListenMessages(_server); + } + catch (Exception e) + { + throw new RuntimeException("Failed to start http management on ports " + httpPorts); + } + + CurrentActor.get().message(ManagementConsoleMessages.READY(OPERATIONAL_LOGGING_NAME)); + } + + private void stop() + { + if (_server != null) + { + try + { + _server.stop(); + logOperationalShutdownMessage(_server); + } + catch (Exception e) + { + throw new RuntimeException("Failed to stop http management on port " + getHttpPorts(_broker.getPorts())); + } + } + + CurrentActor.get().message(ManagementConsoleMessages.STOPPED(OPERATIONAL_LOGGING_NAME)); + } + + /** Added for testing purposes */ + Broker getBroker() + { + return _broker; + } + + /** Added for testing purposes */ + int getSessionTimeout() + { + return (Integer)getAttribute(TIME_OUT); + } + + private boolean isManagementHttp(Port port) + { + return port.getProtocols().contains(Protocol.HTTP) || port.getProtocols().contains(Protocol.HTTPS); + } + + @SuppressWarnings("unchecked") + private Server createServer(Collection<Port> ports) + { + if (_logger.isInfoEnabled()) + { + _logger.info("Starting up web server on " + ports); + } + + Server server = new Server(); + for (Port port : ports) + { + if (State.QUIESCED.equals(port.getActualState())) + { + continue; + } + final Collection<Protocol> protocols = port.getProtocols(); + Connector connector = null; + + //TODO: what to do if protocol HTTP and transport SSL? + if (protocols.contains(Protocol.HTTP)) + { + connector = new SelectChannelConnector(); + } + else if (protocols.contains(Protocol.HTTPS)) + { + KeyStore keyStore = _broker.getDefaultKeyStore(); + if (keyStore == null) + { + throw new IllegalConfigurationException("Key store is not configured. Cannot start management on HTTPS port without keystore"); + } + String keyStorePath = (String)keyStore.getAttribute(KeyStore.PATH); + String keyStorePassword = keyStore.getPassword(); + validateKeystoreParameters(keyStorePath, keyStorePassword); + + SslContextFactory factory = new SslContextFactory(); + factory.setKeyStorePath(keyStorePath); + factory.setKeyStorePassword(keyStorePassword); + + connector = new SslSocketConnector(factory); + } + else + { + throw new IllegalArgumentException("Unexpected protocol " + protocols); + } + connector.setPort(port.getPort()); + server.addConnector(connector); + } + + ServletContextHandler root = new ServletContextHandler(ServletContextHandler.SESSIONS); + root.setContextPath("/"); + server.setHandler(root); + + // set servlet context attributes for broker and configuration + root.getServletContext().setAttribute(AbstractServlet.ATTR_BROKER, _broker); + root.getServletContext().setAttribute(AbstractServlet.ATTR_MANAGEMENT, this); + + addRestServlet(root, "broker"); + addRestServlet(root, "virtualhost", VirtualHost.class); + addRestServlet(root, "authenticationprovider", AuthenticationProvider.class); + addRestServlet(root, "user", AuthenticationProvider.class, User.class); + addRestServlet(root, "groupprovider", GroupProvider.class); + addRestServlet(root, "group", GroupProvider.class, Group.class); + addRestServlet(root, "groupmember", GroupProvider.class, Group.class, GroupMember.class); + addRestServlet(root, "exchange", VirtualHost.class, Exchange.class); + addRestServlet(root, "queue", VirtualHost.class, Queue.class); + addRestServlet(root, "connection", VirtualHost.class, Connection.class); + addRestServlet(root, "binding", VirtualHost.class, Exchange.class, Queue.class, Binding.class); + addRestServlet(root, "port", Port.class); + addRestServlet(root, "session", VirtualHost.class, Connection.class, Session.class); + + root.addServlet(new ServletHolder(new StructureServlet()), "/rest/structure"); + root.addServlet(new ServletHolder(new MessageServlet()), "/rest/message/*"); + root.addServlet(new ServletHolder(new MessageContentServlet()), "/rest/message-content/*"); + + root.addServlet(new ServletHolder(new LogRecordsServlet()), "/rest/logrecords"); + + root.addServlet(new ServletHolder(new SaslServlet()), "/rest/sasl"); + + root.addServlet(new ServletHolder(new DefinedFileServlet("index.html")), ENTRY_POINT_PATH); + root.addServlet(new ServletHolder(new LogoutServlet()), "/logout"); + + root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.js"); + root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.css"); + root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.html"); + root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.png"); + root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.gif"); + root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.jpg"); + root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.jpeg"); + root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.json"); + root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.txt"); + root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.xsl"); + + final SessionManager sessionManager = root.getSessionHandler().getSessionManager(); + + sessionManager.setMaxInactiveInterval((Integer)getAttribute(TIME_OUT)); + + return server; + } + + private void addRestServlet(ServletContextHandler root, String name, Class<? extends ConfiguredObject>... hierarchy) + { + root.addServlet(new ServletHolder(new RestServlet(hierarchy)), "/rest/" + name + "/*"); + } + + private void validateKeystoreParameters(String keyStorePath, String password) + { + if (keyStorePath == null) + { + throw new RuntimeException("Management SSL keystore path not defined, unable to start SSL protected HTTP connector"); + } + if (password == null) + { + throw new RuntimeException("Management SSL keystore password, unable to start SSL protected HTTP connector"); + } + File ksf = new File(keyStorePath); + if (!ksf.exists()) + { + throw new RuntimeException("Cannot find management SSL keystore file: " + ksf); + } + if (!ksf.canRead()) + { + throw new RuntimeException("Cannot read management SSL keystore file: " + ksf + ". Check permissions."); + } + } + + private void logOperationalListenMessages(Server server) + { + Connector[] connectors = server.getConnectors(); + for (Connector connector : connectors) + { + CurrentActor.get().message(ManagementConsoleMessages.LISTENING(stringifyConnectorScheme(connector), connector.getPort())); + if (connector instanceof SslSocketConnector) + { + SslContextFactory sslContextFactory = ((SslSocketConnector)connector).getSslContextFactory(); + if (sslContextFactory != null && sslContextFactory.getKeyStorePath() != null) + { + CurrentActor.get().message(ManagementConsoleMessages.SSL_KEYSTORE(sslContextFactory.getKeyStorePath())); + } + } + } + } + + private void logOperationalShutdownMessage(Server server) + { + Connector[] connectors = server.getConnectors(); + for (Connector connector : connectors) + { + CurrentActor.get().message(ManagementConsoleMessages.SHUTTING_DOWN(stringifyConnectorScheme(connector), connector.getPort())); + } + } + + private String stringifyConnectorScheme(Connector connector) + { + return connector instanceof SslSocketConnector ? "HTTPS" : "HTTP"; + } + + private Collection<Port> getHttpPorts(Collection<Port> ports) + { + Collection<Port> httpPorts = new HashSet<Port>(); + for (Port port : ports) + { + if (isManagementHttp(port)) + { + httpPorts.add(port); + } + } + return httpPorts; + } + + + @Override + public String getName() + { + return (String)getAttribute(NAME); + } + + @Override + public Collection<String> getAttributeNames() + { + return Collections.unmodifiableCollection(AVAILABLE_ATTRIBUTES); + } + + public boolean isHttpsSaslAuthenticationEnabled() + { + return (Boolean)getAttribute(HTTPS_SASL_AUTHENTICATION_ENABLED); + } + + public boolean isHttpSaslAuthenticationEnabled() + { + return (Boolean)getAttribute(HTTP_SASL_AUTHENTICATION_ENABLED); + } + + public boolean isHttpsBasicAuthenticationEnabled() + { + return (Boolean)getAttribute(HTTPS_BASIC_AUTHENTICATION_ENABLED); + } + + public boolean isHttpBasicAuthenticationEnabled() + { + return (Boolean)getAttribute(HTTP_BASIC_AUTHENTICATION_ENABLED); + } + +} diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementFactory.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementFactory.java new file mode 100644 index 0000000000..ccf5373234 --- /dev/null +++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementFactory.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +package org.apache.qpid.server.management.plugin; + +import java.util.Map; +import java.util.UUID; + +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.Plugin; +import org.apache.qpid.server.plugin.PluginFactory; + +public class HttpManagementFactory implements PluginFactory +{ + + @Override + public Plugin createInstance(UUID id, Map<String, Object> attributes, Broker broker) + { + if (!HttpManagement.PLUGIN_TYPE.equals(attributes.get(PLUGIN_TYPE))) + { + return null; + } + + return new HttpManagement(id, broker, attributes); + } +} diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/Management.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/Management.java deleted file mode 100644 index c2f9b73b54..0000000000 --- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/Management.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - * - */ -package org.apache.qpid.server.management.plugin; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; - -import org.apache.commons.configuration.ConfigurationException; -import org.apache.log4j.Logger; -import org.apache.qpid.server.management.plugin.servlet.DefinedFileServlet; -import org.apache.qpid.server.management.plugin.servlet.FileServlet; -import org.apache.qpid.server.management.plugin.servlet.api.ExchangesServlet; -import org.apache.qpid.server.management.plugin.servlet.api.VhostsServlet; -import org.apache.qpid.server.management.plugin.servlet.rest.LogRecordsServlet; -import org.apache.qpid.server.management.plugin.servlet.rest.MessageContentServlet; -import org.apache.qpid.server.management.plugin.servlet.rest.MessageServlet; -import org.apache.qpid.server.management.plugin.servlet.rest.RestServlet; -import org.apache.qpid.server.management.plugin.servlet.rest.SaslServlet; -import org.apache.qpid.server.management.plugin.servlet.rest.StructureServlet; -import org.apache.qpid.server.model.AuthenticationProvider; -import org.apache.qpid.server.model.Binding; -import org.apache.qpid.server.model.Broker; -import org.apache.qpid.server.model.ConfiguredObject; -import org.apache.qpid.server.model.Connection; -import org.apache.qpid.server.model.Exchange; -import org.apache.qpid.server.model.Port; -import org.apache.qpid.server.model.Protocol; -import org.apache.qpid.server.model.Queue; -import org.apache.qpid.server.model.Session; -import org.apache.qpid.server.model.Transport; -import org.apache.qpid.server.model.User; -import org.apache.qpid.server.model.VirtualHost; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.IApplicationRegistry; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.SessionManager; -import org.eclipse.jetty.server.nio.SelectChannelConnector; -import org.eclipse.jetty.server.ssl.SslSocketConnector; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.util.ssl.SslContextFactory; - -public class Management -{ - - private final Logger _logger = Logger.getLogger(Management.class); - - private Broker _broker; - - private Collection<Server> _servers = new ArrayList<Server>(); - - public Management() throws ConfigurationException, IOException - { - _broker = ApplicationRegistry.getInstance().getBroker(); - - Collection<Port> ports = _broker.getPorts(); - int httpPort = -1, httpsPort = -1; - for (Port port : ports) - { - if (port.getProtocols().contains(Protocol.HTTP)) - { - if (port.getTransports().contains(Transport.TCP)) - { - httpPort = port.getPort(); - } - } - if (port.getProtocols().contains(Protocol.HTTPS)) - { - if (port.getTransports().contains(Transport.SSL)) - { - httpsPort = port.getPort(); - } - } - } - - if (httpPort != -1 || httpsPort != -1) - { - _servers.add(createServer(httpPort, httpsPort)); - if (_logger.isDebugEnabled()) - { - _logger.debug(_servers.size() + " server(s) defined"); - } - } - else - { - if (_logger.isInfoEnabled()) - { - _logger.info("Cannot create web server as neither HTTP nor HTTPS port specified"); - } - } - } - - @SuppressWarnings("unchecked") - private Server createServer(int port, int sslPort) throws IOException, ConfigurationException - { - if (_logger.isInfoEnabled()) - { - _logger.info("Starting up web server on" + (port == -1 ? "" : " HTTP port " + port) - + (sslPort == -1 ? "" : " HTTPS port " + sslPort)); - } - - Server server = new Server(); - - if (port != -1) - { - SelectChannelConnector connector = new SelectChannelConnector(); - connector.setPort(port); - if (sslPort != -1) - { - connector.setConfidentialPort(sslPort); - } - server.addConnector(connector); - } - - if (sslPort != -1) - { - IApplicationRegistry appRegistry = ApplicationRegistry.getInstance(); - String keyStorePath = getKeyStorePath(appRegistry); - - SslContextFactory factory = new SslContextFactory(); - factory.setKeyStorePath(keyStorePath); - factory.setKeyStorePassword(appRegistry.getConfiguration().getManagementKeyStorePassword()); - - SslSocketConnector connector = new SslSocketConnector(factory); - connector.setPort(sslPort); - server.addConnector(connector); - } - - ServletContextHandler root = new ServletContextHandler(ServletContextHandler.SESSIONS); - root.setContextPath("/"); - server.setHandler(root); - - root.addServlet(new ServletHolder(new VhostsServlet(_broker)), "/api/vhosts/*"); - root.addServlet(new ServletHolder(new ExchangesServlet(_broker)), "/api/exchanges/*"); - - addRestServlet(root, "broker"); - addRestServlet(root, "virtualhost", VirtualHost.class); - addRestServlet(root, "authenticationprovider", AuthenticationProvider.class); - addRestServlet(root, "user", AuthenticationProvider.class, User.class); - addRestServlet(root, "exchange", VirtualHost.class, Exchange.class); - addRestServlet(root, "queue", VirtualHost.class, Queue.class); - addRestServlet(root, "connection", VirtualHost.class, Connection.class); - addRestServlet(root, "binding", VirtualHost.class, Exchange.class, Queue.class, Binding.class); - addRestServlet(root, "port", Port.class); - addRestServlet(root, "session", VirtualHost.class, Connection.class, Session.class); - - root.addServlet(new ServletHolder(new StructureServlet(_broker)), "/rest/structure"); - root.addServlet(new ServletHolder(new MessageServlet(_broker)), "/rest/message/*"); - root.addServlet(new ServletHolder(new MessageContentServlet(_broker)), "/rest/message-content/*"); - - root.addServlet(new ServletHolder(new LogRecordsServlet(_broker)), "/rest/logrecords"); - - root.addServlet(new ServletHolder(new SaslServlet(_broker)), "/rest/sasl"); - - root.addServlet(new ServletHolder(new DefinedFileServlet("management.html")), "/management"); - - root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.js"); - root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.css"); - root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.html"); - root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.png"); - root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.gif"); - root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.jpg"); - root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.jpeg"); - root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.json"); - root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.txt"); - root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.xsl"); - - final SessionManager sessionManager = root.getSessionHandler().getSessionManager(); - - sessionManager.setMaxInactiveInterval(60 * 15); - - return server; - } - - private void addRestServlet(ServletContextHandler root, String name, Class<? extends ConfiguredObject>... hierarchy) - { - root.addServlet(new ServletHolder(new RestServlet(_broker, hierarchy)), "/rest/" + name + "/*"); - } - - public void start() throws Exception - { - for (Server server : _servers) - { - server.start(); - } - } - - public void stop() throws Exception - { - for (Server server : _servers) - { - server.stop(); - } - } - - private String getKeyStorePath(IApplicationRegistry appRegistry) throws ConfigurationException, FileNotFoundException - { - String keyStorePath = null; - if (System.getProperty("javax.net.ssl.keyStore") != null) - { - keyStorePath = System.getProperty("javax.net.ssl.keyStore"); - } - else - { - keyStorePath = appRegistry.getConfiguration().getManagementKeyStorePath(); - } - - if (keyStorePath == null) - { - throw new ConfigurationException("Management SSL keystore path not defined, unable to start SSL protected HTTP connector"); - } - else - { - File ksf = new File(keyStorePath); - if (!ksf.exists()) - { - throw new FileNotFoundException("Cannot find management SSL keystore file: " + ksf); - } - if (!ksf.canRead()) - { - throw new FileNotFoundException("Cannot read management SSL keystore file: " + ksf + ". Check permissions."); - } - } - return keyStorePath; - } - -} diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementActivator.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementActivator.java deleted file mode 100644 index 09b7e08bfb..0000000000 --- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementActivator.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - * - */ -package org.apache.qpid.server.management.plugin; - -import org.apache.log4j.Logger; -import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; - -public class ManagementActivator implements BundleActivator -{ - private static final Logger _logger = Logger.getLogger(ManagementActivator.class); - - - private BundleContext _ctx; - private String _bundleName; - private Management _managementService; - - - public void start(final BundleContext ctx) throws Exception - { - _ctx = ctx; - if (!ApplicationRegistry.getInstance().getConfiguration().getHTTPManagementEnabled() - && !ApplicationRegistry.getInstance().getConfiguration().getHTTPSManagementEnabled()) - { - _logger.info("Management plugin is disabled!"); - ctx.getBundle().uninstall(); - return; - } - _managementService = new Management(); - _managementService.start(); - _bundleName = ctx.getBundle().getSymbolicName(); - - // register the service - _logger.info("Registering management plugin: " + _bundleName); - _ctx.registerService(Management.class.getName(), _managementService, null); - _ctx.registerService(ConfigurationPluginFactory.class.getName(), ManagementConfiguration.FACTORY, null); - } - - public void stop(final BundleContext bundleContext) throws Exception - { - if (_managementService != null) - { - _logger.info("Stopping management plugin: " + _bundleName); - - _managementService.stop(); - - // null object references - _managementService = null; - } - _ctx = null; - } - -} diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementConfiguration.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementConfiguration.java deleted file mode 100644 index 3866da8f89..0000000000 --- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/ManagementConfiguration.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - * - */ -package org.apache.qpid.server.management.plugin; - -import org.apache.commons.configuration.CompositeConfiguration; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.commons.configuration.XMLConfiguration; - -import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; -import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; - -import java.util.Arrays; -import java.util.List; - -public class ManagementConfiguration extends ConfigurationPlugin -{ - CompositeConfiguration _finalConfig; - - public static final ConfigurationPluginFactory FACTORY = new ConfigurationPluginFactory() - { - public ConfigurationPlugin newInstance(String path, Configuration config) throws ConfigurationException - { - ConfigurationPlugin instance = new ManagementConfiguration(); - instance.setConfiguration(path, config); - return instance; - } - - public List<String> getParentPaths() - { - return Arrays.asList("management"); - } - }; - - public String[] getElementsProcessed() - { - return new String[] { "" }; - } - - public Configuration getConfiguration() - { - return _finalConfig; - } - - - @Override - public void validateConfiguration() throws ConfigurationException - { - // Valid Configuration either has xml links to new files - _finalConfig = new CompositeConfiguration(getConfig()); - List subFiles = getConfig().getList("xml[@fileName]"); - for (Object subFile : subFiles) - { - _finalConfig.addConfiguration(new XMLConfiguration((String) subFile)); - } - - } - -} diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java index d8a8395550..e6ae47dcff 100644 --- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java +++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/DefinedFileServlet.java @@ -73,7 +73,7 @@ public class DefinedFileServlet extends HttpServlet } else { - response.sendError(404, "unknown file: "+ _filename); + response.sendError(HttpServletResponse.SC_NOT_FOUND, "unknown file: "+ _filename); } } } diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java index f8ca082d79..24e5e7c049 100644 --- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java +++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/FileServlet.java @@ -20,11 +20,8 @@ */ package org.apache.qpid.server.management.plugin.servlet; -import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; import java.util.HashMap; @@ -101,7 +98,7 @@ public class FileServlet extends HttpServlet } else { - response.sendError(404, "unknown file: "+ filename); + response.sendError(HttpServletResponse.SC_NOT_FOUND, "unknown file: "+ filename); } } diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/ExchangesServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/ExchangesServlet.java deleted file mode 100644 index a3c5ec68a2..0000000000 --- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/ExchangesServlet.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - * - */ -package org.apache.qpid.server.management.plugin.servlet.api; - -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.map.ObjectReader; - -import org.apache.qpid.server.model.Broker; -import org.apache.qpid.server.model.Exchange; -import org.apache.qpid.server.model.LifetimePolicy; -import org.apache.qpid.server.model.State; -import org.apache.qpid.server.model.VirtualHost; -import org.apache.qpid.server.registry.ApplicationRegistry; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -public class ExchangesServlet extends HttpServlet -{ - - - private Broker _broker; - - public ExchangesServlet() - { - super(); - _broker = ApplicationRegistry.getInstance().getBroker(); - } - - public ExchangesServlet(Broker broker) - { - _broker = broker; - } - - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException - { - response.setContentType("application/json"); - response.setStatus(HttpServletResponse.SC_OK); - - Collection<VirtualHost> vhosts = _broker.getVirtualHosts(); - Collection<Exchange> exchanges = new ArrayList<Exchange>(); - Collection<Map<String,Object>> outputObject = new ArrayList<Map<String,Object>>(); - - final PrintWriter writer = response.getWriter(); - - ObjectMapper mapper = new ObjectMapper(); - String vhostName = null; - String exchangeName = null; - - if(request.getPathInfo() != null && request.getPathInfo().length()>0) - { - String path = request.getPathInfo().substring(1); - String[] parts = path.split("/"); - vhostName = parts.length == 0 ? "" : parts[0]; - if(parts.length > 1) - { - exchangeName = parts[1]; - } - } - - for(VirtualHost vhost : vhosts) - { - if(vhostName == null || vhostName.equals(vhost.getName())) - { - for(Exchange exchange : vhost.getExchanges()) - { - if(exchangeName == null || exchangeName.equals(exchange.getName())) - { - outputObject.add(convertToObject(exchange)); - if(exchangeName != null) - { - break; - } - } - } - if(vhostName != null) - { - break; - } - } - } - - mapper.writeValue(writer, outputObject); - - } - - private Map<String,Object> convertToObject(final Exchange exchange) - { - Map<String, Object> object = new LinkedHashMap<String, Object>(); - object.put("name",exchange.getName()); - object.put("type", exchange.getExchangeType()); - object.put("durable", exchange.isDurable()); - object.put("auto-delete", exchange.getLifetimePolicy() == LifetimePolicy.AUTO_DELETE); - - Map<String,Object> arguments = new HashMap<String, Object>(); - for(String key : exchange.getAttributeNames()) - { - if(!key.equals(Exchange.TYPE)) - { - arguments.put(key, exchange.getAttribute(key)); - } - } - object.put("arguments", arguments); - return object; - } - - protected void doPut(final HttpServletRequest request, final HttpServletResponse response) - throws ServletException, IOException - { - - response.setContentType("application/json"); - - - String vhostName = null; - String exchangeName = null; - if(request.getPathInfo() != null && request.getPathInfo().length()>0) - { - String path = request.getPathInfo().substring(1); - String[] parts = path.split("/"); - vhostName = parts.length == 0 ? "" : parts[0]; - if(parts.length > 1) - { - exchangeName = parts[1]; - } - } - if(vhostName == null) - { - response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED); - } - else if (exchangeName == null) - { - response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED); - } - else - { - VirtualHost vhost = null; - for(VirtualHost host : _broker.getVirtualHosts()) - { - if(host.getName().equals(vhostName)) - { - vhost = host; - } - } - if(vhost == null) - { - response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED); - } - else - { - response.setStatus(HttpServletResponse.SC_NO_CONTENT); - ObjectMapper mapper = new ObjectMapper(); - Map<String,Object> exchangeObject = mapper.readValue(request.getInputStream(), LinkedHashMap.class); - - final boolean isDurable = exchangeObject.get("durable") instanceof Boolean - && ((Boolean)exchangeObject.get("durable")); - final boolean isAutoDelete = exchangeObject.get("auto_delete") instanceof Boolean - && ((Boolean)exchangeObject.get("auto_delete")); - - final String type = (String) exchangeObject.get("type"); - final Map<String, Object> attributes = new HashMap<String, Object>(exchangeObject); - attributes.remove("durable"); - attributes.remove("auto_delete"); - attributes.remove("type"); - - vhost.createExchange(exchangeName, State.ACTIVE, isDurable, - isAutoDelete ? LifetimePolicy.AUTO_DELETE : LifetimePolicy.PERMANENT, - 0l, - type, - attributes); - } - - - - } - - - - } -} diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/VhostsServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/VhostsServlet.java deleted file mode 100644 index b2c0fcfe52..0000000000 --- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/api/VhostsServlet.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - * - */ -package org.apache.qpid.server.management.plugin.servlet.api; - -import org.codehaus.jackson.map.ObjectMapper; - -import org.apache.qpid.common.QpidProperties; -import org.apache.qpid.server.model.Broker; -import org.apache.qpid.server.model.LifetimePolicy; -import org.apache.qpid.server.model.State; -import org.apache.qpid.server.model.VirtualHost; -import org.apache.qpid.server.protocol.AMQConnectionModel; -import org.apache.qpid.server.registry.ApplicationRegistry; - - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.*; - -public class VhostsServlet extends HttpServlet -{ - - - private Broker _broker; - - public VhostsServlet() - { - super(); - _broker = ApplicationRegistry.getInstance().getBroker(); - } - - public VhostsServlet(Broker broker) - { - _broker = broker; - } - - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException - { -System.out.println("Get /api/vhosts"); - response.setContentType("application/json"); - response.setStatus(HttpServletResponse.SC_OK); - - Collection<VirtualHost> vhosts = _broker.getVirtualHosts(); - - - - final PrintWriter writer = response.getWriter(); - - ObjectMapper mapper = new ObjectMapper(); - - if(request.getPathInfo() == null || request.getPathInfo().length()==0) - { - - LinkedHashMap<String, Object> vhostObject = new LinkedHashMap<String, Object>(); - List<Map> vhostList = new ArrayList<Map>(); - - for(VirtualHost vhost : vhosts) - { - vhostList.add(Collections.singletonMap("name", vhost.getName())); - } - mapper.writeValue(writer, vhostList); - } - else - { - LinkedHashMap<String, Object> vhostObject = new LinkedHashMap<String, Object>(); - String vhostName = request.getPathInfo().substring(1); - - for(VirtualHost vhost : vhosts) - { - if(vhostName.equals(vhost.getName())) - { - vhostObject.put("name", vhost.getName()); - break; - } - } - mapper.writeValue(writer, vhostObject); - } - } - - - protected void doPut(final HttpServletRequest request, final HttpServletResponse response) - throws ServletException, IOException - { - - response.setContentType("application/json"); - response.setStatus(HttpServletResponse.SC_NO_CONTENT); - - if(request.getPathInfo() != null && request.getPathInfo().length()>0) - { - String vhostName = request.getPathInfo().substring(1); - _broker.createVirtualHost(vhostName, State.ACTIVE, true, LifetimePolicy.PERMANENT, 0L, Collections.EMPTY_MAP); - } - - - } -} 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)); } } diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java index 404793b592..f2cf5d7734 100644 --- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java +++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java @@ -26,8 +26,6 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.qpid.server.logging.LogRecorder; -import org.apache.qpid.server.model.Broker; -import org.apache.qpid.server.registry.ApplicationRegistry; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; @@ -35,16 +33,11 @@ public class LogRecordsServlet extends AbstractServlet { public LogRecordsServlet() { - super(ApplicationRegistry.getInstance().getBroker()); - } - - public LogRecordsServlet(Broker broker) - { - super(broker); + super(); } @Override - protected void onGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); @@ -53,10 +46,10 @@ public class LogRecordsServlet extends AbstractServlet response.setHeader("Pragma","no-cache"); response.setDateHeader ("Expires", 0); - ApplicationRegistry applicationRegistry = (ApplicationRegistry) ApplicationRegistry.getInstance(); List<Map<String,Object>> logRecords = new ArrayList<Map<String, Object>>(); - for(LogRecorder.Record record : applicationRegistry.getLogRecorder()) + LogRecorder logRecorder = getBroker().getLogRecorder(); + for(LogRecorder.Record record : logRecorder) { logRecords.add(logRecordToObject(record)); } diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogoutServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogoutServlet.java new file mode 100644 index 0000000000..4188e7d60d --- /dev/null +++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogoutServlet.java @@ -0,0 +1,65 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ +package org.apache.qpid.server.management.plugin.servlet.rest; + +import java.io.IOException; + +import javax.servlet.ServletConfig; +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.qpid.server.management.plugin.HttpManagement; + +@SuppressWarnings("serial") +public class LogoutServlet extends HttpServlet +{ + public static final String RETURN_URL_INIT_PARAM = "qpid.webui_logout_redirect"; + private String _returnUrl = HttpManagement.ENTRY_POINT_PATH; + + @Override + public void init(ServletConfig config) throws ServletException + { + super.init(config); + + String initValue = config.getServletContext().getInitParameter(RETURN_URL_INIT_PARAM); + if(initValue != null) + { + _returnUrl = initValue; + } + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException + { + HttpSession session = request.getSession(false); + if(session != null) + { + // Invalidating the session will cause LoginLogoutReporter to log the user logoff. + session.invalidate(); + } + + resp.sendRedirect(_returnUrl); + } + +} diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java index bc87f0bcc5..d61c48bb2c 100644 --- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java +++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java @@ -29,7 +29,6 @@ import javax.servlet.http.HttpServletResponse; import org.apache.qpid.server.message.MessageReference; import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.queue.QueueEntry; @@ -42,13 +41,8 @@ public class MessageContentServlet extends AbstractServlet super(); } - public MessageContentServlet(Broker broker) - { - super(broker); - } - @Override - protected void onGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if(request.getPathInfo() != null && request.getPathInfo().length()>0 && request.getPathInfo().substring(1).split("/").length > 2) diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java index 6e7bc1d935..49e0c2b1bf 100644 --- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java +++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java @@ -34,13 +34,10 @@ import org.apache.log4j.Logger; import org.apache.qpid.server.message.AMQMessageHeader; import org.apache.qpid.server.message.MessageReference; import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.queue.QueueEntryVisitor; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.subscription.Subscription; @@ -56,13 +53,8 @@ public class MessageServlet extends AbstractServlet super(); } - public MessageServlet(Broker broker) - { - super(broker); - } - @Override - protected void onGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if(request.getPathInfo() != null && request.getPathInfo().length()>0 && request.getPathInfo().substring(1).split("/").length > 2) @@ -400,7 +392,7 @@ public class MessageServlet extends AbstractServlet * POST moves or copies messages to the given queue from a queue specified in the posted JSON data */ @Override - protected void onPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + protected void doPostWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try @@ -422,7 +414,7 @@ public class MessageServlet extends AbstractServlet // FIXME: added temporary authorization check until we introduce management layer // and review current ACL rules to have common rules for all management interfaces String methodName = isMoveTransaction? "moveMessages":"copyMessages"; - if (isQueueUpdateMethodAuthorized(methodName, vhost.getName())) + if (isQueueUpdateMethodAuthorized(methodName, vhost)) { final Queue destinationQueue = getQueueFromVirtualHost(destQueueName, vhost); final List messageIds = new ArrayList((List) providedObject.get("messages")); @@ -435,7 +427,7 @@ public class MessageServlet extends AbstractServlet } else { - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setStatus(HttpServletResponse.SC_FORBIDDEN); } } catch(RuntimeException e) @@ -450,7 +442,7 @@ public class MessageServlet extends AbstractServlet * DELETE removes messages from the queue */ @Override - protected void onDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + protected void doDeleteWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) { final Queue sourceQueue = getQueueFromRequest(request); @@ -466,37 +458,22 @@ public class MessageServlet extends AbstractServlet // FIXME: added temporary authorization check until we introduce management layer // and review current ACL rules to have common rules for all management interfaces - if (isQueueUpdateMethodAuthorized("deleteMessages", vhost.getName())) + if (isQueueUpdateMethodAuthorized("deleteMessages", vhost)) { vhost.executeTransaction(new DeleteTransaction(sourceQueue, messageIds)); response.setStatus(HttpServletResponse.SC_OK); } else { - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setStatus(HttpServletResponse.SC_FORBIDDEN); } } - private boolean isQueueUpdateMethodAuthorized(String methodName, String virtualHost) + private boolean isQueueUpdateMethodAuthorized(String methodName, VirtualHost host) { - SecurityManager securityManager = getSecurityManager(virtualHost); + SecurityManager securityManager = host.getSecurityManager(); return securityManager.authoriseMethod(Operation.UPDATE, "VirtualHost.Queue", methodName); } - private SecurityManager getSecurityManager(String virtualHost) - { - IApplicationRegistry appRegistry = ApplicationRegistry.getInstance(); - SecurityManager security; - if (virtualHost == null) - { - security = appRegistry.getSecurityManager(); - } - else - { - security = appRegistry.getVirtualHostRegistry().getVirtualHost(virtualHost).getSecurityManager(); - } - return security; - } - } diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java index 6a79916d07..3fab26cde5 100644 --- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java +++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java @@ -19,6 +19,7 @@ package org.apache.qpid.server.management.plugin.servlet.rest; import java.io.BufferedWriter; import java.io.IOException; import java.io.Writer; +import java.security.AccessControlException; import java.util.*; import javax.servlet.ServletConfig; import javax.servlet.ServletException; @@ -31,7 +32,6 @@ import org.apache.qpid.server.model.*; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; - public class RestServlet extends AbstractServlet { private static final Logger LOGGER = Logger.getLogger(RestServlet.class); @@ -47,29 +47,29 @@ public class RestServlet extends AbstractServlet private Class<? extends ConfiguredObject>[] _hierarchy; - private volatile boolean initializationRequired = false; - private final ConfiguredObjectToMapConverter _objectConverter = new ConfiguredObjectToMapConverter(); + private final boolean _hierarchyInitializationRequired; public RestServlet() { super(); - initializationRequired = true; + _hierarchyInitializationRequired = true; } - public RestServlet(Broker broker, Class<? extends ConfiguredObject>... hierarchy) + public RestServlet(Class<? extends ConfiguredObject>... hierarchy) { - super(broker); + super(); _hierarchy = hierarchy; + _hierarchyInitializationRequired = false; } @Override public void init() throws ServletException { - if (initializationRequired) + super.init(); + if (_hierarchyInitializationRequired) { doInitialization(); - initializationRequired = false; } } @@ -285,7 +285,7 @@ public class RestServlet extends AbstractServlet } @Override - protected void onGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); @@ -319,7 +319,7 @@ public class RestServlet extends AbstractServlet } @Override - protected void onPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + protected void doPutWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json"); @@ -336,7 +336,8 @@ public class RestServlet extends AbstractServlet if(names.size() != _hierarchy.length) { - throw new IllegalArgumentException("Path to object to create must be fully specified"); + throw new IllegalArgumentException("Path to object to create must be fully specified. " + + "Found " + names.size() + " expecting " + _hierarchy.length); } } @@ -428,8 +429,11 @@ public class RestServlet extends AbstractServlet || (obj.getName().equals(providedObject.get("name")) && equalParents(obj, otherParents))) { doUpdate(obj, providedObject); + response.setStatus(HttpServletResponse.SC_OK); + return; } } + theParent.createChild(objClass, providedObject, otherParents); } catch (RuntimeException e) @@ -462,13 +466,17 @@ public class RestServlet extends AbstractServlet private void setResponseStatus(HttpServletResponse response, RuntimeException e) throws IOException { - if (e.getCause() instanceof AMQSecurityException) + if (e instanceof AccessControlException || e.getCause() instanceof AMQSecurityException) { - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Caught security exception, sending " + HttpServletResponse.SC_FORBIDDEN, e); + } + response.setStatus(HttpServletResponse.SC_FORBIDDEN); } else { - LOGGER.warn("Unexpected exception is caught", e); + LOGGER.warn("Caught exception", e); // TODO response.setStatus(HttpServletResponse.SC_CONFLICT); @@ -476,7 +484,7 @@ public class RestServlet extends AbstractServlet } @Override - protected void onDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + protected void doDeleteWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java index 1b78611a50..069132af1e 100644 --- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java +++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java @@ -25,10 +25,9 @@ import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; import org.apache.log4j.Logger; -import org.apache.qpid.server.model.Broker; -import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; import javax.security.auth.Subject; import javax.security.sasl.SaslException; @@ -39,6 +38,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.io.PrintWriter; +import java.security.AccessControlException; import java.security.Principal; import java.security.SecureRandom; import java.util.LinkedHashMap; @@ -47,6 +47,7 @@ import java.util.Random; public class SaslServlet extends AbstractServlet { + private static final Logger LOGGER = Logger.getLogger(SaslServlet.class); private static final SecureRandom SECURE_RANDOM = new SecureRandom(); @@ -56,18 +57,12 @@ public class SaslServlet extends AbstractServlet private static final String ATTR_EXPIRY = "SaslServlet.Expiry"; private static final long SASL_EXCHANGE_EXPIRY = 1000L; - public SaslServlet() { super(); } - public SaslServlet(Broker broker) - { - super(broker); - } - - protected void onGet(HttpServletRequest request, HttpServletResponse response) throws + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { @@ -79,15 +74,16 @@ public class SaslServlet extends AbstractServlet response.setDateHeader ("Expires", 0); HttpSession session = request.getSession(); - Random rand = getRandom(session); + getRandom(session); - AuthenticationManager authManager = ApplicationRegistry.getInstance().getAuthenticationManager(getSocketAddress(request)); - String[] mechanisms = authManager.getMechanisms().split(" "); + SubjectCreator subjectCreator = getSubjectCreator(request); + String[] mechanisms = subjectCreator.getMechanisms().split(" "); Map<String, Object> outputObject = new LinkedHashMap<String, Object>(); - final Subject subject = (Subject) session.getAttribute("subject"); + + final Subject subject = getAuthorisedSubjectFromSession(session); if(subject != null) { - final Principal principal = subject.getPrincipals().iterator().next(); + Principal principal = AuthenticatedPrincipal.getAuthenticatedPrincipalFromSubject(subject); outputObject.put("user", principal.getName()); } else if (request.getRemoteUser() != null) @@ -121,9 +117,10 @@ public class SaslServlet extends AbstractServlet @Override - protected void onPost(final HttpServletRequest request, final HttpServletResponse response) - throws ServletException, IOException + protected void doPostWithSubjectAndActor(final HttpServletRequest request, final HttpServletResponse response) throws IOException { + checkSaslAuthEnabled(request); + try { response.setContentType("application/json"); @@ -137,14 +134,18 @@ public class SaslServlet extends AbstractServlet String id = request.getParameter("id"); String saslResponse = request.getParameter("response"); - AuthenticationManager authManager = ApplicationRegistry.getInstance().getAuthenticationManager(getSocketAddress(request)); + SubjectCreator subjectCreator = getSubjectCreator(request); if(mechanism != null) { if(id == null) { - SaslServer saslServer = authManager.createSaslServer(mechanism, request.getServerName(), null/*TODO*/); - evaluateSaslResponse(response, session, saslResponse, saslServer); + if(LOGGER.isDebugEnabled()) + { + LOGGER.debug("Creating SaslServer for mechanism: " + mechanism); + } + SaslServer saslServer = subjectCreator.createSaslServer(mechanism, request.getServerName(), null/*TODO*/); + evaluateSaslResponse(request, response, session, saslResponse, saslServer, subjectCreator); } else { @@ -152,9 +153,7 @@ public class SaslServlet extends AbstractServlet session.removeAttribute(ATTR_ID); session.removeAttribute(ATTR_SASL_SERVER); session.removeAttribute(ATTR_EXPIRY); - } - } else { @@ -163,8 +162,7 @@ public class SaslServlet extends AbstractServlet if(id.equals(session.getAttribute(ATTR_ID)) && System.currentTimeMillis() < (Long) session.getAttribute(ATTR_EXPIRY)) { SaslServer saslServer = (SaslServer) session.getAttribute(ATTR_SASL_SERVER); - evaluateSaslResponse(response, session, saslResponse, saslServer); - + evaluateSaslResponse(request, response, session, saslResponse, saslServer, subjectCreator); } else { @@ -180,7 +178,6 @@ public class SaslServlet extends AbstractServlet session.removeAttribute(ATTR_ID); session.removeAttribute(ATTR_SASL_SERVER); session.removeAttribute(ATTR_EXPIRY); - } } } @@ -194,12 +191,30 @@ public class SaslServlet extends AbstractServlet LOGGER.error("Error processing SASL request", e); throw e; } + } + private void checkSaslAuthEnabled(HttpServletRequest request) + { + boolean saslAuthEnabled; + HttpManagement management = getManagement(); + if (request.isSecure()) + { + saslAuthEnabled = management.isHttpsSaslAuthenticationEnabled(); + } + else + { + saslAuthEnabled = management.isHttpSaslAuthenticationEnabled(); + } + + if (!saslAuthEnabled) + { + throw new RuntimeException("Sasl authentication disabled."); + } } - private void evaluateSaslResponse(final HttpServletResponse response, - final HttpSession session, - final String saslResponse, final SaslServer saslServer) throws IOException + private void evaluateSaslResponse(final HttpServletRequest request, + final HttpServletResponse response, + final HttpSession session, final String saslResponse, final SaslServer saslServer, SubjectCreator subjectCreator) throws IOException { final String id; byte[] challenge; @@ -209,27 +224,34 @@ public class SaslServlet extends AbstractServlet } catch(SaslException e) { - session.removeAttribute(ATTR_ID); session.removeAttribute(ATTR_SASL_SERVER); session.removeAttribute(ATTR_EXPIRY); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setStatus(HttpServletResponse.SC_FORBIDDEN); return; } if(saslServer.isComplete()) { - final Subject subject = new Subject(); - subject.getPrincipals().add(new UsernamePrincipal(saslServer.getAuthorizationID())); - session.setAttribute("subject", subject); + Subject subject = subjectCreator.createSubjectWithGroups(saslServer.getAuthorizationID()); + + try + { + authoriseManagement(request, subject); + } + catch (AccessControlException ace) + { + sendError(response, HttpServletResponse.SC_FORBIDDEN); + return; + } + + setAuthorisedSubjectInSession(subject, request, session); session.removeAttribute(ATTR_ID); session.removeAttribute(ATTR_SASL_SERVER); session.removeAttribute(ATTR_EXPIRY); response.setStatus(HttpServletResponse.SC_OK); - - } else { @@ -250,7 +272,6 @@ public class SaslServlet extends AbstractServlet ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true); mapper.writeValue(writer, outputObject); - } } } diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java index 60f977ca66..40d3c02768 100644 --- a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java +++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java @@ -41,13 +41,8 @@ public class StructureServlet extends AbstractServlet super(); } - public StructureServlet(Broker broker) - { - super(broker); - } - @Override - protected void onGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); @@ -56,6 +51,8 @@ public class StructureServlet extends AbstractServlet response.setHeader("Pragma","no-cache"); response.setDateHeader ("Expires", 0); + // TODO filtering??? request.getParameter("filter"); // filter=1,2,3 /groups/*/* + Map<String,Object> structure = generateStructure(getBroker(), Broker.class); final PrintWriter writer = response.getWriter(); diff --git a/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/session/LoginLogoutReporter.java b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/session/LoginLogoutReporter.java new file mode 100644 index 0000000000..238f1b4719 --- /dev/null +++ b/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/session/LoginLogoutReporter.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ +package org.apache.qpid.server.management.plugin.session; + +import java.security.Principal; +import java.security.PrivilegedAction; + +import javax.security.auth.Subject; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionBindingEvent; +import javax.servlet.http.HttpSessionBindingListener; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.logging.messages.ManagementConsoleMessages; +import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; + +/** + * Logs {@link ManagementConsoleMessages#OPEN(String)} and {@link ManagementConsoleMessages#CLOSE(String)} + * messages. A single instance of this class must be placed in the {@link HttpSession} immediately after + * the user has successfully logged-in, and removed (or the whole session invalidated) as the user logs out. + */ +public class LoginLogoutReporter implements HttpSessionBindingListener +{ + private static final Logger LOGGER = Logger.getLogger(LoginLogoutReporter.class); + private final LogActor _logActor; + private final Subject _subject; + private final Principal _principal; + + public LoginLogoutReporter(LogActor logActor, Subject subject) + { + super(); + _logActor = logActor; + _subject = subject; + _principal = AuthenticatedPrincipal.getAuthenticatedPrincipalFromSubject(_subject); + } + + @Override + public void valueBound(HttpSessionBindingEvent arg0) + { + reportLogin(); + } + + @Override + public void valueUnbound(HttpSessionBindingEvent arg0) + { + reportLogout(); + } + + private void reportLogin() + { + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("User logging in : " + _principal); + } + + Subject.doAs(_subject, new PrivilegedAction<Void>() + { + @Override + public Void run() + { + _logActor.message(ManagementConsoleMessages.OPEN(_principal.getName())); + return null; + } + }); + } + + private void reportLogout() + { + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("User logging out : " + _principal); + } + + Subject.doAs(_subject, new PrivilegedAction<Void>() + { + @Override + public Void run() + { + _logActor.message(ManagementConsoleMessages.CLOSE(_principal.getName())); + return null; + } + }); + } + +} diff --git a/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html b/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html index baadc8c35f..e6c067fddf 100644 --- a/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html +++ b/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html @@ -23,7 +23,5 @@ <div class="users"></div> <button data-dojo-type="dijit.form.Button" class="addUserButton">Add User</button> <button data-dojo-type="dijit.form.Button" class="deleteUserButton">Delete Users</button> - </div> - </div> diff --git a/java/broker-plugins/management-http/src/main/java/resources/group/addGroupMember.html b/java/broker-plugins/management-http/src/main/java/resources/group/addGroupMember.html new file mode 100644 index 0000000000..0372468f91 --- /dev/null +++ b/java/broker-plugins/management-http/src/main/java/resources/group/addGroupMember.html @@ -0,0 +1,37 @@ +<!-- + - + - Licensed to the Apache Software Foundation (ASF) under one + - or more contributor license agreements. See the NOTICE file + - distributed with this work for additional information + - regarding copyright ownership. The ASF licenses this file + - to you 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. + - + --> +<div class="dijitHidden"> + <div data-dojo-type="dijit.Dialog" style="width:600px;" data-dojo-props="title:'Add Group Member'" id="addGroupMember"> + <form id="formAddGroupMember" method="post" dojoType="dijit.form.Form"> + <table cellpadding="0" cellspacing="2"> + <tr> + <td valign="top"><strong>Name*: </strong></td> + <td><input type="text" required="true" name="name" id="formAddGroupMember.name" placeholder="Name" + dojoType="dijit.form.ValidationTextBox" missingMessage="A name must be supplied" /></td> + </tr> + </table> + <br/> + + <!-- submit buttons --> + <input type="submit" value="Add Group Member" label="Add Group Member" dojoType="dijit.form.Button" /> + </form> + </div> +</div> diff --git a/java/broker-plugins/management-http/src/main/java/resources/group/showGroup.html b/java/broker-plugins/management-http/src/main/java/resources/group/showGroup.html new file mode 100644 index 0000000000..4fddf727d0 --- /dev/null +++ b/java/broker-plugins/management-http/src/main/java/resources/group/showGroup.html @@ -0,0 +1,30 @@ +<!-- + - + - Licensed to the Apache Software Foundation (ASF) under one + - or more contributor license agreements. See the NOTICE file + - distributed with this work for additional information + - regarding copyright ownership. The ASF licenses this file + - to you 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. + - + --> +<div class="group"> + <span style="">Name:</span><span class="name" style="position:absolute; left:6em"></span> + <br/> + <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Group Members'"> + <div class="groupMembers"></div> + <button data-dojo-type="dijit.form.Button" class="addGroupMemberButton" type="button">Add Group Member</button> + <button data-dojo-type="dijit.form.Button" class="removeGroupMemberButton" type="button">Remove Group Members</button> + </div> +</div> + diff --git a/java/broker-plugins/management-http/src/main/java/resources/groupprovider/addGroup.html b/java/broker-plugins/management-http/src/main/java/resources/groupprovider/addGroup.html new file mode 100644 index 0000000000..8d3431808a --- /dev/null +++ b/java/broker-plugins/management-http/src/main/java/resources/groupprovider/addGroup.html @@ -0,0 +1,38 @@ +<!-- + - + - Licensed to the Apache Software Foundation (ASF) under one + - or more contributor license agreements. See the NOTICE file + - distributed with this work for additional information + - regarding copyright ownership. The ASF licenses this file + - to you 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. + - + --> +<div class="dijitHidden"> + <div data-dojo-type="dijit.Dialog" style="width:600px;" data-dojo-props="title:'Add Group'" id="addGroup"> + <form id="formAddGroup" method="post" dojoType="dijit.form.Form"> + <table cellpadding="0" cellspacing="2"> + <tr> + <td valign="top"><strong>Group Name*: </strong></td> + <td><input type="text" required="true" name="name" id="formAddGroup.name" placeholder="Group Name" + dojoType="dijit.form.ValidationTextBox" missingMessage="A name must be supplied" /></td> + </tr> + </table> + <br/> + + <!-- submit buttons --> + <input type="submit" value="Create Group" label="Create Group" dojoType="dijit.form.Button" /> + + </form> + </div> +</div> diff --git a/java/broker-plugins/management-http/src/main/java/resources/groupprovider/showFileGroupManager.html b/java/broker-plugins/management-http/src/main/java/resources/groupprovider/showFileGroupManager.html new file mode 100644 index 0000000000..734e8b5419 --- /dev/null +++ b/java/broker-plugins/management-http/src/main/java/resources/groupprovider/showFileGroupManager.html @@ -0,0 +1,28 @@ +<!-- + - + - Licensed to the Apache Software Foundation (ASF) under one + - or more contributor license agreements. See the NOTICE file + - distributed with this work for additional information + - regarding copyright ownership. The ASF licenses this file + - to you 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. + - + --> +<div class="FileGroupManager"> + <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Groups'"> + <div class="groups"></div> + <button data-dojo-type="dijit.form.Button" class="addGroupButton">Add Group</button> + <button data-dojo-type="dijit.form.Button" class="deleteGroupButton">Delete Groups</button> + </div> + +</div> diff --git a/java/broker-plugins/management-http/src/main/java/resources/management.html b/java/broker-plugins/management-http/src/main/java/resources/index.html index a8345a8503..2fb9137ff8 100644 --- a/java/broker-plugins/management-http/src/main/java/resources/management.html +++ b/java/broker-plugins/management-http/src/main/java/resources/index.html @@ -73,10 +73,8 @@ <div id="pageLayout" data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design: 'headline', gutters: false"> <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top'"> - <div id="header" class="header"></div> - </div> - <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top'"> - <div id="login"></div> + <div id="header" class="header" style="float: left; width: 300px"></div> + <div id="login" style="float: right"></div> </div> <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'leading', splitter: true"> <div qpid-type="treeView" qpid-props="query: 'rest/structure'" ></div> diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js index 152504da86..b4f0728685 100644 --- a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js +++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js @@ -71,7 +71,7 @@ var saslPlain = function saslPlain(user, password) }, function(error) { - if(error.status == 401) + if(error.status == 403) { alert("Authentication Failed"); } @@ -127,7 +127,7 @@ var saslCramMD5 = function saslCramMD5(user, password) }, function(error) { - if(error.status == 401) + if(error.status == 403) { alert("Authentication Failed"); } @@ -141,7 +141,7 @@ var saslCramMD5 = function saslCramMD5(user, password) }, function(error) { - if(error.status == 401) + if(error.status == 403) { alert("Authentication Failed"); } @@ -152,10 +152,45 @@ var saslCramMD5 = function saslCramMD5(user, password) }); }; +var containsMechanism = function containsMechanism(mechanisms, mech) +{ + for (var i = 0; i < mechanisms.length; i++) { + if (mechanisms[i] == mech) { + return true; + } + } + + return false; +}; + var doAuthenticate = function doAuthenticate() { - saslCramMD5(dojo.byId("username").value, dojo.byId("pass").value); - updateAuthentication(); + dojo.xhrGet({ + // The URL of the request + url: "rest/sasl", + handleAs: "json" + }).then(function(data) + { + var mechMap = data.mechanisms; + + if (containsMechanism(mechMap, "CRAM-MD5")) + { + saslCramMD5(dojo.byId("username").value, dojo.byId("pass").value); + updateAuthentication(); + } + else if (containsMechanism(mechMap, "PLAIN")) + { + saslPlain(dojo.byId("username").value, dojo.byId("pass").value); + updateAuthentication(); + } + else + { + alert("No supported SASL mechanism offered: " + mechMap); + } + } + ); + + }; @@ -170,13 +205,13 @@ var updateAuthentication = function updateAuthentication() if(data.user) { dojo.byId("authenticatedUser").innerHTML = data.user; - dojo.style(button.domNode, {visibility: 'hidden'}); - dojo.style(usernameSpan, {visibility: 'visible'}); + dojo.style(button.domNode, {display: 'none'}); + dojo.style(usernameSpan, {display: 'block'}); } else { - dojo.style(button.domNode, {visibility: 'visible'}); - dojo.style(usernameSpan, {visibility: 'hidden'}); + dojo.style(button.domNode, {display: 'block'}); + dojo.style(usernameSpan, {display: 'none'}); } } ); @@ -198,13 +233,13 @@ require(["dijit/form/DropDownButton", "dijit/TooltipDialog", "dijit/form/TextBox dropDown: dialog }); - usernameSpan = domConstruct.create("span", { innerHTML: '<strong>User: </strong><span id="authenticatedUser"></span>', - style: { visibility: "hidden" }}); + usernameSpan = domConstruct.create("span", { innerHTML: '<strong>User: </strong> <span id="authenticatedUser"></span><a href="logout">[logout]</a>', + style: { display: "none" }}); var loginDiv = dom.byId("login"); - loginDiv.appendChild(button.domNode); loginDiv.appendChild(usernameSpan); + loginDiv.appendChild(button.domNode); diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js index 08fdf5c99b..5557c37a2c 100644 --- a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js +++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js @@ -58,10 +58,10 @@ define(["dojo/_base/xhr"], return exchangeName == null || exchangeName == "" || "<<default>>" == exchangeName || exchangeName.indexOf("amq.") == 0 || exchangeName.indexOf("qpid.") == 0; }; - util.deleteGridSelections = function(updater, gridName, url, confirmationMessageStart) + util.deleteGridSelections = function(updater, grid, url, confirmationMessageStart) { - var grid = updater[gridName].grid; var data = grid.selection.getSelected(); + if(data.length) { var confirmationMessage = null; @@ -103,7 +103,8 @@ define(["dojo/_base/xhr"], xhr.del({url: query, sync: true, handleAs: "json"}).then( function(data) { - grid.setQuery({id: "*"}); + // TODO why query *?? + //grid.setQuery({id: "*"}); grid.selection.deselectAll(); updater.update(); }, diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Exchange.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Exchange.js index 37bae1ef8e..5a5a6515ef 100644 --- a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Exchange.js +++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Exchange.js @@ -115,7 +115,7 @@ define(["dojo/_base/xhr", { util.deleteGridSelections( this.exchangeUpdater, - "bindingsGrid", + that.exchangeUpdater.bindingsGrid.grid, "rest/binding/"+ encodeURIComponent(this.getVirtualHostName()) + "/" + encodeURIComponent(this.name), "Are you sure you want to delete binding for queue"); } diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/GroupProvider.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/GroupProvider.js new file mode 100644 index 0000000000..4e05f4b0ea --- /dev/null +++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/GroupProvider.js @@ -0,0 +1,109 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ +define(["dojo/_base/xhr", + "dojo/parser", + "dojo/query", + "dojo/_base/connect", + "qpid/common/properties", + "qpid/common/updater", + "qpid/common/util", + "qpid/common/UpdatableStore", + "dojox/grid/EnhancedGrid", + "dojox/grid/enhanced/plugins/Pagination", + "dojox/grid/enhanced/plugins/IndirectSelection", + "dojo/domReady!"], + function (xhr, parser, query, connect, properties, updater, util, UpdatableStore, EnhancedGrid) { + + function GroupProvider(name, parent, controller) { + this.name = name; + this.controller = controller; + this.modelObj = { type: "groupprovider", name: name }; + if(parent) { + this.modelObj.parent = {}; + this.modelObj.parent[ parent.type] = parent; + } + } + + GroupProvider.prototype.getTitle = function() { + return "GroupProvider"; + }; + + GroupProvider.prototype.open = function(contentPane) { + var that = this; + this.contentPane = contentPane; + xhr.get({url: "showGroupProvider.html", + sync: true, + load: function(data) { + contentPane.containerNode.innerHTML = data; + parser.parse(contentPane.containerNode); + + that.groupProviderAdapter = new GroupProviderUpdater(contentPane.containerNode, that.modelObj, that.controller); + + updater.add( that.groupProviderAdapter ); + + that.groupProviderAdapter.update(); + + }}); + }; + + GroupProvider.prototype.close = function() { + updater.remove( this.groupProviderAdapter ); + }; + + function GroupProviderUpdater(node, groupProviderObj, controller) + { + this.controller = controller; + this.name = query(".name", node)[0]; + this.query = "rest/groupprovider/"+encodeURIComponent(groupProviderObj.name); + + var that = this; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) + .then(function(data) + { + that.groupProviderData = data[0]; + + util.flattenStatistics( that.groupProviderData ); + + that.updateHeader(); + + require(["qpid/management/groupprovider/"+that.groupProviderData.type], + function(SpecificProvider) { + that.details = new SpecificProvider(node, groupProviderObj, controller); + that.details.update(); + }); + + }); + + } + + GroupProviderUpdater.prototype.updateHeader = function() + { + this.name.innerHTML = this.groupProviderData[ "name" ]; + }; + + GroupProviderUpdater.prototype.update = function() + { + var that = this; + }; + + return GroupProvider; + }); diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js index 957f2381cf..2efc46476d 100644 --- a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js +++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js @@ -72,7 +72,7 @@ define(["dojo/_base/xhr", function(evt){ util.deleteGridSelections( that.vhostUpdater, - "queuesGrid", + that.vhostUpdater.queuesGrid.grid, "rest/queue/"+ encodeURIComponent(that.name), "Are you sure you want to delete queue"); } @@ -87,7 +87,7 @@ define(["dojo/_base/xhr", { util.deleteGridSelections( that.vhostUpdater, - "exchangesGrid", + that.vhostUpdater.exchangesGrid.grid, "rest/exchange/"+ encodeURIComponent(that.name), "Are you sure you want to delete exchange"); } diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js index 1aa05a5a3c..5d3a666760 100644 --- a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js +++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js @@ -27,13 +27,17 @@ define(["dojo/dom", "qpid/management/Queue", "qpid/management/Connection", "qpid/management/AuthenticationProvider", + "qpid/management/GroupProvider", + "qpid/management/group/Group", "dojo/ready", "dojo/domReady!"], - function (dom, registry, ContentPane, Broker, VirtualHost, Exchange, Queue, Connection, AuthProvider, ready) { + function (dom, registry, ContentPane, Broker, VirtualHost, Exchange, Queue, Connection, AuthProvider, GroupProvider, Group, ready) { var controller = {}; var constructors = { broker: Broker, virtualhost: VirtualHost, exchange: Exchange, - queue: Queue, connection: Connection, authenticationprovider: AuthProvider }; + queue: Queue, connection: Connection, + authenticationprovider: AuthProvider, groupprovider: GroupProvider, + group: Group }; var tabDiv = dom.byId("managedViews"); diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/Group.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/Group.js new file mode 100644 index 0000000000..ea918644e9 --- /dev/null +++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/Group.js @@ -0,0 +1,204 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ +define(["dojo/_base/xhr", + "dojo/parser", + "dojo/query", + "dijit/registry", + "dojo/_base/connect", + "dojo/_base/event", + "dojo/json", + "qpid/common/properties", + "qpid/common/updater", + "qpid/common/util", + "qpid/common/formatter", + "qpid/common/UpdatableStore", + "dojo/store/JsonRest", + "dojox/grid/EnhancedGrid", + "dojo/data/ObjectStore", + "qpid/management/group/addGroupMember", + "dojox/grid/enhanced/plugins/Pagination", + "dojox/grid/enhanced/plugins/IndirectSelection", + "dojo/domReady!"], + function (xhr, parser, query, registry, connect, event, json, properties, updater, util, formatter, + UpdatableStore, JsonRest, EnhancedGrid, ObjectStore, addGroupMember) { + + function Group(name, parent, controller) { + this.name = name; + this.controller = controller; + this.modelObj = { type: "group", name: name }; + + if(parent) { + this.modelObj.parent = {}; + this.modelObj.parent[ parent.type] = parent; + } + } + + Group.prototype.getGroupName = function() + { + return this.name; + }; + + + Group.prototype.getGroupProviderName = function() + { + return this.modelObj.parent.groupprovider.name; + }; + + Group.prototype.getTitle = function() + { + return "Group: " + this.name; + }; + + Group.prototype.open = function(contentPane) { + var that = this; + this.contentPane = contentPane; + + xhr.get({url: "group/showGroup.html", + sync: true, + load: function(data) { + contentPane.containerNode.innerHTML = data; + parser.parse(contentPane.containerNode); + + that.groupUpdater = new GroupUpdater(contentPane.containerNode, that, that.controller); + + updater.add( that.groupUpdater ); + + that.groupUpdater.update(); + + var addGroupMemberButton = query(".addGroupMemberButton", contentPane.containerNode)[0]; + connect.connect(registry.byNode(addGroupMemberButton), "onClick", + function(evt){ + addGroupMember.show(that.getGroupProviderName(), that.getGroupName()) + } + ); + + var removeGroupMemberButton = query(".removeGroupMemberButton", contentPane.containerNode)[0]; + connect.connect(registry.byNode(removeGroupMemberButton), "onClick", + function(evt){ + util.deleteGridSelections( + that.groupUpdater, + that.groupUpdater.groupMembersUpdatableStore.grid, + "rest/groupmember/"+ encodeURIComponent(that.getGroupProviderName()) + + "/" + encodeURIComponent(that.getGroupName()), + "Are you sure you want to remove group member"); + } + ); + }}); + }; + + Group.prototype.close = function() { + updater.remove( this.groupUpdater ); + }; + + function GroupUpdater(containerNode, groupObj, controller) + { + var that = this; + + function findNode(name) { + return query("." + name, containerNode)[0]; + } + + function storeNodes(names) + { + for(var i = 0; i < names.length; i++) { + that[names[i]] = findNode(names[i]); + } + } + + storeNodes(["name", + "state", + "durable", + "lifetimePolicy", + "type"]); + + this.query = "rest/groupmember/"+ encodeURIComponent(groupObj.getGroupProviderName()) + "/" + encodeURIComponent(groupObj.getGroupName()); + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data) + { + that.groupMemberData = data; + + util.flattenStatistics( that.groupMemberData ); + + var gridProperties = { + keepSelection: true, + plugins: { + pagination: { + pageSizes: ["10", "25", "50", "100"], + description: true, + sizeSwitch: true, + pageStepper: true, + gotoButton: true, + maxPageStep: 4, + position: "bottom" + }, + indirectSelection: true + + }}; + + that.groupMembersUpdatableStore = new UpdatableStore(that.groupMemberData, findNode("groupMembers"), + [ { name: "Group Member Name", field: "name", width: "100%" }], + function(obj) + { + connect.connect(obj.grid, "onRowDblClick", obj.grid, + function(evt){ + + }); + } , gridProperties, EnhancedGrid); + + }); + + } + + GroupUpdater.prototype.update = function() + { + + var that = this; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) + .then(function(data) { + that.groupMemberData = data; + + util.flattenStatistics( that.groupMemberData ); + + that.groupMembersUpdatableStore.update(that.groupMemberData); + }); + }; + + Group.prototype.deleteGroupMember = function() { + if(confirm("Are you sure you want to delete group member'" +this.name+"'?")) { + var query = "rest/groupmember/"+ encodeURIComponent(this.getGroupProviderName()) + "/" + encodeURIComponent(this.name); + this.success = true + var that = this; + xhr.del({url: query, sync: true, handleAs: "json"}).then( + function(data) { + that.contentPane.onClose() + that.controller.tabContainer.removeChild(that.contentPane); + that.contentPane.destroyRecursive(); + }, + function(error) {that.success = false; that.failureReason = error;}); + if(!this.success ) { + alert("Error:" + this.failureReason); + } + } + } + + return Group; + }); diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/addGroupMember.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/addGroupMember.js new file mode 100644 index 0000000000..1861cc6ffe --- /dev/null +++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/addGroupMember.js @@ -0,0 +1,108 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ +define(["dojo/_base/xhr", + "dojo/dom", + "dojo/dom-construct", + "dojo/_base/window", + "dijit/registry", + "dojo/parser", + "dojo/_base/array", + "dojo/_base/event", + 'dojo/_base/json', + "dijit/form/NumberSpinner", // required by the form + /* dojox/ validate resources */ + "dojox/validate/us", "dojox/validate/web", + /* basic dijit classes */ + "dijit/Dialog", + "dijit/form/CheckBox", "dijit/form/Textarea", + "dijit/form/FilteringSelect", "dijit/form/TextBox", + "dijit/form/ValidationTextBox", "dijit/form/DateTextBox", + "dijit/form/TimeTextBox", "dijit/form/Button", + "dijit/form/RadioButton", "dijit/form/Form", + "dijit/form/DateTextBox", + /* basic dojox classes */ + "dojox/form/BusyButton", "dojox/form/CheckedMultiSelect", + "dojo/domReady!"], + function (xhr, dom, construct, win, registry, parser, array, event, json) { + + var addGroupMember = {}; + + var node = construct.create("div", null, win.body(), "last"); + + var convertToGroupMember = function convertToGroupMember(formValues) + { + var newGroupMember = {}; + newGroupMember.name = formValues.name; + return newGroupMember; + }; + + xhr.get({url: "group/addGroupMember.html", + sync: true, + load: function(data) { + var theForm; + node.innerHTML = data; + addGroupMember.dialogNode = dom.byId("addGroupMember"); + parser.instantiate([addGroupMember.dialogNode]); + + theForm = registry.byId("formAddGroupMember"); + theForm.on("submit", function(e) { + + event.stop(e); + if(theForm.validate()){ + + var newGroupMember = convertToGroupMember(theForm.getValues()); + var that = this; + xhr.put({url: "rest/groupmember/"+encodeURIComponent(addGroupMember.groupProvider) + + "/" + encodeURIComponent(addGroupMember.group) + "/" + encodeURIComponent(newGroupMember.name), sync: true, handleAs: "json", + headers: { "Content-Type": "application/json"}, + putData: json.toJson(newGroupMember), + load: function(x) {that.success = true; }, + error: function(error) {that.success = false; that.failureReason = error;}}); + + if(this.success === true) + { + registry.byId("addGroupMember").hide(); + } + else + { + alert("Error:" + this.failureReason); + } + + return false; + + + }else{ + alert('Form contains invalid data. Please correct first'); + return false; + } + + }); + }}); + + addGroupMember.show = function(groupProvider, group) { + addGroupMember.groupProvider = groupProvider; + addGroupMember.group = group; + registry.byId("formAddGroupMember").reset(); + registry.byId("addGroupMember").show(); + }; + + return addGroupMember; + });
\ No newline at end of file diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/groupprovider/FileGroupManager.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/groupprovider/FileGroupManager.js new file mode 100644 index 0000000000..44fc9702e2 --- /dev/null +++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/groupprovider/FileGroupManager.js @@ -0,0 +1,251 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + */ +define(["dojo/_base/xhr", + "dojo/dom", + "dojo/parser", + "dojo/query", + "dojo/dom-construct", + "dojo/_base/connect", + "dojo/_base/window", + "dojo/_base/event", + "dojo/_base/json", + "dijit/registry", + "qpid/common/util", + "qpid/common/properties", + "qpid/common/updater", + "qpid/common/UpdatableStore", + "dojox/grid/EnhancedGrid", + "dojox/grid/enhanced/plugins/Pagination", + "dojox/grid/enhanced/plugins/IndirectSelection", + "dojox/validate/us", "dojox/validate/web", + "dijit/Dialog", + "dijit/form/TextBox", + "dijit/form/ValidationTextBox", + "dijit/form/TimeTextBox", "dijit/form/Button", + "dijit/form/Form", + "dijit/form/DateTextBox", + "dojo/domReady!"], + function (xhr, dom, parser, query, construct, connect, win, event, json, registry, util, properties, updater, UpdatableStore, EnhancedGrid) { + function DatabaseGroupManager(containerNode, groupProviderObj, controller) { + var node = construct.create("div", null, containerNode, "last"); + var that = this; + this.name = groupProviderObj.name; + xhr.get({url: "groupprovider/showFileGroupManager.html", + sync: true, + load: function(data) { + node.innerHTML = data; + parser.parse(node); + + + that.groupDatabaseUpdater= new GroupProviderUpdater(node, groupProviderObj, controller); + + updater.add( that.groupDatabaseUpdater); + + that.groupDatabaseUpdater.update(); + + + }}); + } + + DatabaseGroupManager.prototype.update = function() { + this.groupDatabaseUpdater.update(); + }; + + DatabaseGroupManager.prototype.close = function() { + updater.remove( this.groupDatabaseUpdater ); + }; + + function GroupProviderUpdater(node, groupProviderObj, controller) + { + this.controller = controller; + this.query = "rest/groupprovider/"+encodeURIComponent(groupProviderObj.name); + this.name = groupProviderObj.name; + var that = this; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) + .then(function(data) { + that.groupProviderData = data[0]; + + util.flattenStatistics( that.groupProviderData ); + + var groupDiv = query(".groups")[0]; + + var gridProperties = { + height: 400, + keepSelection: true, + plugins: { + pagination: { + pageSizes: ["10", "25", "50", "100"], + description: true, + sizeSwitch: true, + pageStepper: true, + gotoButton: true, + maxPageStep: 4, + position: "bottom" + }, + indirectSelection: true + + }}; + + + that.groupsGrid = + new UpdatableStore(that.groupProviderData.groups, groupDiv, + [ { name: "Group Name", field: "name", width: "100%" } + ], null, gridProperties, EnhancedGrid); + + + var addGroupButton = query(".addGroupButton", node)[0]; + connect.connect(registry.byNode(addGroupButton), "onClick", function(evt){ addGroup.show(groupProviderObj.name) }); + + var deleteMessagesButton = query(".deleteGroupButton", node)[0]; + var deleteWidget = registry.byNode(deleteMessagesButton); + connect.connect(deleteWidget, "onClick", + function(evt){ + event.stop(evt); + that.deleteGroups(); + }); + }); + } + + GroupProviderUpdater.prototype.deleteGroups = function() + { + var grid = this.groupsGrid.grid; + var data = grid.selection.getSelected(); + if(data.length) { + var that = this; + if(confirm("Delete " + data.length + " groups?")) { + var i, queryParam; + for(i = 0; i<data.length; i++) { + if(queryParam) { + queryParam += "&"; + } else { + queryParam = "?"; + } + + queryParam += "id=" + data[i].id; + } + var query = "rest/group/"+ encodeURIComponent(that.name) + + queryParam; + that.success = true + xhr.del({url: query, sync: true, handleAs: "json"}).then( + function(data) { + grid.setQuery({id: "*"}); + grid.selection.deselectAll(); + that.update(); + }, + function(error) {that.success = false; that.failureReason = error;}); + if(!that.success ) { + alert("Error:" + this.failureReason); + } + } +} + }; + + GroupProviderUpdater.prototype.update = function() + { + + var that = this; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) + .then(function(data) { + that.groupProviderData = data[0]; + util.flattenStatistics( that.groupProviderData ); + + that.groupsGrid.update(that.groupProviderData.groups); + + }); + + + }; + + var addGroup = {}; + + var node = construct.create("div", null, win.body(), "last"); + + var convertToGroup = function convertToGroup(formValues) { + var newGroup = {}; + newGroup.name = formValues.name; + for(var propName in formValues) + { + if(formValues.hasOwnProperty(propName)) { + if(formValues[ propName ] !== "") { + newGroup[ propName ] = formValues[propName]; + } + } + } + + return newGroup; + }; + + + xhr.get({url: "groupprovider/addGroup.html", + sync: true, + load: function(data) { + var theForm; + node.innerHTML = data; + addGroup.dialogNode = dom.byId("addGroup"); + parser.instantiate([addGroup.dialogNode]); + + var that = this; + + theForm = registry.byId("formAddGroup"); + theForm.on("submit", function(e) { + + event.stop(e); + if(theForm.validate()){ + + var newGroup = convertToGroup(theForm.getValues()); + + + var url = "rest/group/"+encodeURIComponent(addGroup.groupProvider) + + "/"+encodeURIComponent(newGroup.name); + + xhr.put({url: url, sync: true, handleAs: "json", + headers: { "Content-Type": "application/json"}, + putData: json.toJson(newGroup), + load: function(x) {that.success = true; }, + error: function(error) {that.success = false; that.failureReason = error;}}); + + if(that.success === true) { + registry.byId("addGroup").hide(); + } else { + alert("Error:" + that.failureReason); + } + + return false; + + + }else{ + alert('Form contains invalid data. Please correct first'); + return false; + } + + }); + }}); + + addGroup.show = function(groupProvider) { + addGroup.groupProvider = groupProvider; + registry.byId("formAddGroup").reset(); + registry.byId("addGroup").show(); + }; + + return DatabaseGroupManager; + }); diff --git a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js index b1d4abf8c1..59356cfce1 100644 --- a/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js +++ b/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js @@ -273,10 +273,12 @@ define(["dojo/_base/xhr", controller.show("port", details.port, { type: "virtualhost", name: details.virtualhost, parent: {broker: {type:"broker", name:""}}}); } else if (details.type == 'authenticationprovider') { controller.show("authenticationprovider", details.authenticationprovider, {broker: {type:"broker", name:""}}); + } else if (details.type == 'groupprovider') { + controller.show("groupprovider", details.groupprovider, {broker: {type:"broker", name:""}}); + } else if (details.type == 'group') { + controller.show("group", details.group, { type: "groupprovider", name: details.groupprovider, parent: {broker: {type:"broker", name:""}}}); } - - }; TreeViewModel.prototype.update = function () { diff --git a/java/broker-plugins/management-http/src/main/java/resources/showGroupProvider.html b/java/broker-plugins/management-http/src/main/java/resources/showGroupProvider.html new file mode 100644 index 0000000000..914857db5c --- /dev/null +++ b/java/broker-plugins/management-http/src/main/java/resources/showGroupProvider.html @@ -0,0 +1,25 @@ +<!-- + - + - Licensed to the Apache Software Foundation (ASF) under one + - or more contributor license agreements. See the NOTICE file + - distributed with this work for additional information + - regarding copyright ownership. The ASF licenses this file + - to you 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. + - + --> +<div class="groupProvider"> + <span style="">Name:</span><span class="name" style="position:absolute; left:6em"></span> + <br/> + <span style="">Type:</span><span class="type" style="position:absolute; left:6em"></span> +</div>
\ No newline at end of file |