diff options
author | Robert Godfrey <rgodfrey@apache.org> | 2013-04-23 20:54:19 +0000 |
---|---|---|
committer | Robert Godfrey <rgodfrey@apache.org> | 2013-04-23 20:54:19 +0000 |
commit | 985af562e1c232940251fc75c199ad9a51e356bb (patch) | |
tree | f0d8abd3367deb87ca9095b4e4e84be2f6870b92 | |
parent | 917a1f26f8f26730528dd538ab817a340cdd8715 (diff) | |
download | qpid-python-985af562e1c232940251fc75c199ad9a51e356bb.tar.gz |
Merged up to r1465457
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/QPID-4659@1471141 13f79535-47bb-0310-9956-ffa450edef68
206 files changed, 11565 insertions, 4658 deletions
diff --git a/qpid/java/amqp-1-0-common/src/main/java/org/apache/qpid/amqp_1_0/transport/ConnectionEndpoint.java b/qpid/java/amqp-1-0-common/src/main/java/org/apache/qpid/amqp_1_0/transport/ConnectionEndpoint.java index cdb2007b4a..0ef286e89e 100644 --- a/qpid/java/amqp-1-0-common/src/main/java/org/apache/qpid/amqp_1_0/transport/ConnectionEndpoint.java +++ b/qpid/java/amqp-1-0-common/src/main/java/org/apache/qpid/amqp_1_0/transport/ConnectionEndpoint.java @@ -21,7 +21,8 @@ package org.apache.qpid.amqp_1_0.transport; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import org.apache.qpid.amqp_1_0.codec.DescribedTypeConstructorRegistry; import org.apache.qpid.amqp_1_0.codec.ValueWriter; import org.apache.qpid.amqp_1_0.framing.AMQFrame; @@ -59,12 +60,15 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour { private static final short CONNECTION_CONTROL_CHANNEL = (short) 0; private static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.wrap(new byte[0]); + private static final Symbol SASL_PLAIN = Symbol.valueOf("PLAIN"); + private static final Symbol SASL_ANONYMOUS = Symbol.valueOf("ANONYMOUS"); + private static final Symbol SASL_EXTERNAL = Symbol.valueOf("EXTERNAL"); private final Container _container; private Principal _user; private static final short DEFAULT_CHANNEL_MAX = 255; - private static final int DEFAULT_MAX_FRAME = Integer.getInteger("amqp.max_frame_size",1<<15); + private static final int DEFAULT_MAX_FRAME = Integer.getInteger("amqp.max_frame_size", 1 << 15); private ConnectionState _state = ConnectionState.UNOPENED; @@ -75,20 +79,20 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour private SocketAddress _remoteAddress; // positioned by the *outgoing* channel - private SessionEndpoint[] _sendingSessions = new SessionEndpoint[DEFAULT_CHANNEL_MAX+1]; + private SessionEndpoint[] _sendingSessions = new SessionEndpoint[DEFAULT_CHANNEL_MAX + 1]; // positioned by the *incoming* channel - private SessionEndpoint[] _receivingSessions = new SessionEndpoint[DEFAULT_CHANNEL_MAX+1]; + private SessionEndpoint[] _receivingSessions = new SessionEndpoint[DEFAULT_CHANNEL_MAX + 1]; private boolean _closedForInput; private boolean _closedForOutput; private long _idleTimeout; private AMQPDescribedTypeRegistry _describedTypeRegistry = AMQPDescribedTypeRegistry.newInstance() - .registerTransportLayer() - .registerMessagingLayer() - .registerTransactionLayer() - .registerSecurityLayer(); + .registerTransportLayer() + .registerMessagingLayer() + .registerTransactionLayer() + .registerSecurityLayer(); private FrameOutputHandler<FrameBody> _frameOutputHandler; @@ -135,11 +139,11 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour public synchronized void open() { - if(_requiresSASLClient) + if (_requiresSASLClient) { synchronized (getLock()) { - while(!_saslComplete) + while (!_saslComplete) { try { @@ -151,12 +155,12 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour } } } - if(!_authenticated) + if (!_authenticated) { throw new RuntimeException("Could not connect - authentication error"); } } - if(_state == ConnectionState.UNOPENED) + if (_state == ConnectionState.UNOPENED) { sendOpen(DEFAULT_CHANNEL_MAX, DEFAULT_MAX_FRAME); _state = ConnectionState.AWAITING_OPEN; @@ -172,8 +176,8 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour { // todo assert connection state SessionEndpoint endpoint = new SessionEndpoint(this); - short channel = getFirstFreeChannel(); - if(channel != -1) + short channel = getFirstFreeChannel(); + if (channel != -1) { _sendingSessions[channel] = endpoint; endpoint.setSendingChannel(channel); @@ -244,8 +248,6 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour } - - private void closeSender() { setClosedForOutput(true); @@ -255,9 +257,9 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour short getFirstFreeChannel() { - for(int i = 0; i<_sendingSessions.length;i++) + for (int i = 0; i < _sendingSessions.length; i++) { - if(_sendingSessions[i]==null) + if (_sendingSessions[i] == null) { return (short) i; } @@ -276,22 +278,25 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour { _channelMax = open.getChannelMax() == null ? DEFAULT_CHANNEL_MAX - : open.getChannelMax().shortValue() < DEFAULT_CHANNEL_MAX - ? DEFAULT_CHANNEL_MAX - : open.getChannelMax().shortValue(); + : open.getChannelMax().shortValue() < DEFAULT_CHANNEL_MAX + ? DEFAULT_CHANNEL_MAX + : open.getChannelMax().shortValue(); - UnsignedInteger remoteDesiredMaxFrameSize = open.getMaxFrameSize() == null ? UnsignedInteger.valueOf(DEFAULT_MAX_FRAME) : open.getMaxFrameSize(); + UnsignedInteger remoteDesiredMaxFrameSize = + open.getMaxFrameSize() == null ? UnsignedInteger.valueOf(DEFAULT_MAX_FRAME) : open.getMaxFrameSize(); - _maxFrameSize = (remoteDesiredMaxFrameSize.compareTo(_desiredMaxFrameSize) < 0 ? remoteDesiredMaxFrameSize : _desiredMaxFrameSize).intValue(); + _maxFrameSize = (remoteDesiredMaxFrameSize.compareTo(_desiredMaxFrameSize) < 0 + ? remoteDesiredMaxFrameSize + : _desiredMaxFrameSize).intValue(); _remoteContainerId = open.getContainerId(); - if(open.getIdleTimeOut() != null) + if (open.getIdleTimeOut() != null) { _idleTimeout = open.getIdleTimeOut().longValue(); } - switch(_state) + switch (_state) { case UNOPENED: sendOpen(_channelMax, _maxFrameSize); @@ -313,7 +318,7 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour { setClosedForInput(true); _connectionEventListener.closeReceived(); - switch(_state) + switch (_state) { case UNOPENED: case AWAITING_OPEN: @@ -341,7 +346,7 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour { Close close = new Close(); close.setError(error); - switch(_state) + switch (_state) { case UNOPENED: _state = ConnectionState.CLOSED; @@ -359,17 +364,17 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour } } - public synchronized void inputClosed() + public synchronized void inputClosed() { - if(!_closedForInput) + if (!_closedForInput) { _closedForInput = true; - for(int i = 0; i < _receivingSessions.length; i++) + for (int i = 0; i < _receivingSessions.length; i++) { - if(_receivingSessions[i] != null) + if (_receivingSessions[i] != null) { _receivingSessions[i].end(); - _receivingSessions[i]=null; + _receivingSessions[i] = null; } } @@ -395,8 +400,7 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour short myChannelId; - - if(begin.getRemoteChannel() != null) + if (begin.getRemoteChannel() != null) { myChannelId = begin.getRemoteChannel().shortValue(); SessionEndpoint endpoint; @@ -404,7 +408,7 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour { endpoint = _sendingSessions[myChannelId]; } - catch(IndexOutOfBoundsException e) + catch (IndexOutOfBoundsException e) { final Error error = new Error(); error.setCondition(ConnectionError.FRAMING_ERROR); @@ -414,9 +418,9 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour connectionError(error); return; } - if(endpoint != null) + if (endpoint != null) { - if(_receivingSessions[channel] == null) + if (_receivingSessions[channel] == null) { _receivingSessions[channel] = endpoint; endpoint.setReceivingChannel(channel); @@ -446,16 +450,16 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour { myChannelId = getFirstFreeChannel(); - if(myChannelId == -1) + if (myChannelId == -1) { // close any half open channel myChannelId = getFirstFreeChannel(); } - if(_receivingSessions[channel] == null) + if (_receivingSessions[channel] == null) { - SessionEndpoint endpoint = new SessionEndpoint(this,begin); + SessionEndpoint endpoint = new SessionEndpoint(this, begin); _receivingSessions[channel] = endpoint; _sendingSessions[myChannelId] = endpoint; @@ -483,15 +487,13 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour } - } - public synchronized void receiveEnd(short channel, End end) { SessionEndpoint endpoint = _receivingSessions[channel]; - if(endpoint != null) + if (endpoint != null) { _receivingSessions[channel] = null; @@ -551,18 +553,18 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour public synchronized int send(short channel, FrameBody body, ByteBuffer payload) { - if(!_closedForOutput) + if (!_closedForOutput) { ValueWriter<FrameBody> writer = _describedTypeRegistry.getValueWriter(body); int size = writer.writeToBuffer(EMPTY_BYTE_BUFFER); ByteBuffer payloadDup = payload == null ? null : payload.duplicate(); int payloadSent = getMaxFrameSize() - (size + 9); - if(payloadSent < (payload == null ? 0 : payload.remaining())) + if (payloadSent < (payload == null ? 0 : payload.remaining())) { - if(body instanceof Transfer) + if (body instanceof Transfer) { - ((Transfer)body).setMore(Boolean.TRUE); + ((Transfer) body).setMore(Boolean.TRUE); } writer = _describedTypeRegistry.getValueWriter(body); @@ -571,9 +573,9 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour try { - payloadDup.limit(payloadDup.position()+payloadSent); + payloadDup.limit(payloadDup.position() + payloadSent); } - catch(NullPointerException npe) + catch (NullPointerException npe) { throw npe; } @@ -592,7 +594,6 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour } - public void invalidHeaderReceived() { // TODO @@ -606,7 +607,7 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour public synchronized void protocolHeaderReceived(final byte major, final byte minorVersion, final byte revision) { - if(_requiresSASLServer && _state != ConnectionState.UNOPENED) + if (_requiresSASLServer && _state != ConnectionState.UNOPENED) { // TODO - bad stuff } @@ -618,7 +619,7 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour public synchronized void handleError(final Error error) { - if(!closedForOutput()) + if (!closedForOutput()) { Close close = new Close(); close.setError(error); @@ -631,17 +632,17 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour public synchronized void receive(final short channel, final Object frame) { - if(_logger.isLoggable(Level.FINE)) + if (_logger.isLoggable(Level.FINE)) { - _logger.fine("RECV["+ _remoteAddress + "|"+channel+"] : " + frame); + _logger.fine("RECV[" + _remoteAddress + "|" + channel + "] : " + frame); } - if(frame instanceof FrameBody) + if (frame instanceof FrameBody) { - ((FrameBody)frame).invoke(channel, this); + ((FrameBody) frame).invoke(channel, this); } - else if(frame instanceof SaslFrameBody) + else if (frame instanceof SaslFrameBody) { - ((SaslFrameBody)frame).invoke(this); + ((SaslFrameBody) frame).invoke(this); } } @@ -674,7 +675,7 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour public synchronized void close() { - switch(_state) + switch (_state) { case AWAITING_OPEN: case OPEN: @@ -737,10 +738,11 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour { _saslComplete = true; _authenticated = true; + _user = _saslServerProvider.getAuthenticatedPrincipal(_saslServer); getLock().notifyAll(); } - if(_onSaslCompleteTask != null) + if (_onSaslCompleteTask != null) { _onSaslCompleteTask.run(); } @@ -766,7 +768,7 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour _authenticated = false; getLock().notifyAll(); } - if(_onSaslCompleteTask != null) + if (_onSaslCompleteTask != null) { _onSaslCompleteTask.run(); } @@ -776,19 +778,32 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour public void receiveSaslMechanisms(final SaslMechanisms saslMechanisms) { - if(Arrays.asList(saslMechanisms.getSaslServerMechanisms()).contains(Symbol.valueOf("PLAIN"))) + SaslInit init = new SaslInit(); + init.setHostname(_remoteHostname); + + Set<Symbol> mechanisms = new HashSet<Symbol>(Arrays.asList(saslMechanisms.getSaslServerMechanisms())); + if (mechanisms.contains(SASL_PLAIN) && _password != null) { - SaslInit init = new SaslInit(); - init.setMechanism(Symbol.valueOf("PLAIN")); - init.setHostname(_remoteHostname); + + init.setMechanism(SASL_PLAIN); + byte[] usernameBytes = _user.getName().getBytes(Charset.forName("UTF-8")); byte[] passwordBytes = _password.getBytes(Charset.forName("UTF-8")); - byte[] initResponse = new byte[usernameBytes.length+passwordBytes.length+2]; - System.arraycopy(usernameBytes,0,initResponse,1,usernameBytes.length); - System.arraycopy(passwordBytes,0,initResponse,usernameBytes.length+2,passwordBytes.length); + byte[] initResponse = new byte[usernameBytes.length + passwordBytes.length + 2]; + System.arraycopy(usernameBytes, 0, initResponse, 1, usernameBytes.length); + System.arraycopy(passwordBytes, 0, initResponse, usernameBytes.length + 2, passwordBytes.length); init.setInitialResponse(new Binary(initResponse)); - _saslFrameOutput.send(new SASLFrame(init),null); + + } + else if (mechanisms.contains(SASL_ANONYMOUS)) + { + init.setMechanism(SASL_ANONYMOUS); + } + else if (mechanisms.contains(SASL_EXTERNAL)) + { + init.setMechanism(SASL_EXTERNAL); } + _saslFrameOutput.send(new SASLFrame(init), null); } public void receiveSaslChallenge(final SaslChallenge saslChallenge) @@ -798,65 +813,66 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour public void receiveSaslResponse(final SaslResponse saslResponse) { - final Binary responseBinary = saslResponse.getResponse(); - byte[] response = responseBinary == null ? new byte[0] : responseBinary.getArray(); + final Binary responseBinary = saslResponse.getResponse(); + byte[] response = responseBinary == null ? new byte[0] : responseBinary.getArray(); - try - { - - // Process response from the client - byte[] challenge = _saslServer.evaluateResponse(response != null ? response : new byte[0]); + try + { - if (_saslServer.isComplete()) - { - SaslOutcome outcome = new SaslOutcome(); - - outcome.setCode(SaslCode.OK); - _saslFrameOutput.send(new SASLFrame(outcome),null); - synchronized (getLock()) - { - _saslComplete = true; - _authenticated = true; - getLock().notifyAll(); - } - if(_onSaslCompleteTask != null) - { - _onSaslCompleteTask.run(); - } + // Process response from the client + byte[] challenge = _saslServer.evaluateResponse(response != null ? response : new byte[0]); - } - else - { - SaslChallenge challengeBody = new SaslChallenge(); - challengeBody.setChallenge(new Binary(challenge)); - _saslFrameOutput.send(new SASLFrame(challengeBody), null); + if (_saslServer.isComplete()) + { + SaslOutcome outcome = new SaslOutcome(); - } + outcome.setCode(SaslCode.OK); + _saslFrameOutput.send(new SASLFrame(outcome), null); + synchronized (getLock()) + { + _saslComplete = true; + _authenticated = true; + _user = _saslServerProvider.getAuthenticatedPrincipal(_saslServer); + getLock().notifyAll(); } - catch (SaslException e) + if (_onSaslCompleteTask != null) { - SaslOutcome outcome = new SaslOutcome(); + _onSaslCompleteTask.run(); + } - outcome.setCode(SaslCode.AUTH); - _saslFrameOutput.send(new SASLFrame(outcome),null); - synchronized (getLock()) - { - _saslComplete = true; - _authenticated = false; - getLock().notifyAll(); - } - if(_onSaslCompleteTask != null) - { - _onSaslCompleteTask.run(); - } + } + else + { + SaslChallenge challengeBody = new SaslChallenge(); + challengeBody.setChallenge(new Binary(challenge)); + _saslFrameOutput.send(new SASLFrame(challengeBody), null); - } + } } + catch (SaslException e) + { + SaslOutcome outcome = new SaslOutcome(); + + outcome.setCode(SaslCode.AUTH); + _saslFrameOutput.send(new SASLFrame(outcome), null); + synchronized (getLock()) + { + _saslComplete = true; + _authenticated = false; + getLock().notifyAll(); + } + if (_onSaslCompleteTask != null) + { + _onSaslCompleteTask.run(); + } + + } + } public void receiveSaslOutcome(final SaslOutcome saslOutcome) { - if(saslOutcome.getCode() == SaslCode.OK) + if (saslOutcome.getCode() == SaslCode.OK) { _saslFrameOutput.close(); synchronized (getLock()) @@ -865,7 +881,7 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour _authenticated = true; getLock().notifyAll(); } - if(_onSaslCompleteTask != null) + if (_onSaslCompleteTask != null) { _onSaslCompleteTask.run(); } @@ -904,22 +920,13 @@ public class ConnectionEndpoint implements DescribedTypeConstructorRegistry.Sour return _authenticated; } - public void initiateSASL() + public void initiateSASL(String[] mechanismNames) { SaslMechanisms mechanisms = new SaslMechanisms(); - final Enumeration<SaslServerFactory> saslServerFactories = Sasl.getSaslServerFactories(); - - SaslServerFactory f; ArrayList<Symbol> mechanismsList = new ArrayList<Symbol>(); - while(saslServerFactories.hasMoreElements()) + for (String name : mechanismNames) { - f = saslServerFactories.nextElement(); - final String[] mechanismNames = f.getMechanismNames(null); - for(String name : mechanismNames) - { - mechanismsList.add(Symbol.valueOf(name)); - } - + mechanismsList.add(Symbol.valueOf(name)); } mechanisms.setSaslServerMechanisms(mechanismsList.toArray(new Symbol[mechanismsList.size()])); _saslFrameOutput.send(new SASLFrame(mechanisms), null); diff --git a/qpid/java/amqp-1-0-common/src/main/java/org/apache/qpid/amqp_1_0/transport/SaslServerProvider.java b/qpid/java/amqp-1-0-common/src/main/java/org/apache/qpid/amqp_1_0/transport/SaslServerProvider.java index 1b08488673..abc92e8acf 100644 --- a/qpid/java/amqp-1-0-common/src/main/java/org/apache/qpid/amqp_1_0/transport/SaslServerProvider.java +++ b/qpid/java/amqp-1-0-common/src/main/java/org/apache/qpid/amqp_1_0/transport/SaslServerProvider.java @@ -20,10 +20,12 @@ package org.apache.qpid.amqp_1_0.transport; +import java.security.Principal; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; public interface SaslServerProvider { SaslServer getSaslServer(String mechanism, String fqdn) throws SaslException; + Principal getAuthenticatedPrincipal(SaslServer server); } diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControl.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControl.java index 6f7885da94..451b1f9c40 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControl.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControl.java @@ -29,6 +29,7 @@ import javax.security.auth.Subject; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.lang.ObjectUtils; import org.apache.log4j.Logger; +import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.security.Result; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.AccessControl; @@ -44,6 +45,7 @@ public class DefaultAccessControl implements AccessControl private static final Logger _logger = Logger.getLogger(DefaultAccessControl.class); private RuleSet _ruleSet; + private File _aclFile; public DefaultAccessControl(String fileName) { @@ -51,10 +53,8 @@ public class DefaultAccessControl implements AccessControl { _logger.debug("Creating AccessControl instance using file: " + fileName); } - File aclFile = new File(fileName); - ConfigurationFile configFile = new PlainConfiguration(aclFile); - _ruleSet = configFile.load(); + _aclFile = new File(fileName); } DefaultAccessControl(RuleSet rs) throws ConfigurationException @@ -62,6 +62,45 @@ public class DefaultAccessControl implements AccessControl _ruleSet = rs; } + public void open() + { + if(_aclFile != null) + { + if (!_aclFile.exists()) + { + throw new IllegalConfigurationException("ACL file '" + _aclFile + "' is not found"); + } + + ConfigurationFile configFile = new PlainConfiguration(_aclFile); + _ruleSet = configFile.load(); + } + } + + @Override + public void close() + { + //no-op + } + + @Override + public void onDelete() + { + //no-op + } + + @Override + public void onCreate() + { + //verify file exists + if(_aclFile != null) + { + if (!_aclFile.exists()) + { + throw new IllegalConfigurationException("ACL file '" + _aclFile + "' is not found"); + } + } + } + public Result getDefault() { return _ruleSet.getDefault(); @@ -119,4 +158,5 @@ public class DefaultAccessControl implements AccessControl return Result.DENIED; } } + } diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactory.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactory.java index a3d7823caf..f4e041a8d2 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactory.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactory.java @@ -20,40 +20,60 @@ */ package org.apache.qpid.server.security.access.plugins; -import java.io.File; +import static org.apache.qpid.server.security.access.FileAccessControlProviderConstants.ACL_FILE_PROVIDER_TYPE; +import static org.apache.qpid.server.security.access.FileAccessControlProviderConstants.PATH; +import static org.apache.qpid.server.util.MapValueConverter.getStringAttribute; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.Map; import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.plugin.AccessControlFactory; import org.apache.qpid.server.security.AccessControl; +import org.apache.qpid.server.util.ResourceBundleLoader; public class DefaultAccessControlFactory implements AccessControlFactory { - public static final String ATTRIBUTE_ACL_FILE = "aclFile"; + public static final String RESOURCE_BUNDLE = "org.apache.qpid.server.security.access.plugins.FileAccessControlProviderAttributeDescriptions"; + + public static final Collection<String> ATTRIBUTES = Collections.<String> unmodifiableList(Arrays.asList( + ATTRIBUTE_TYPE, + PATH + )); - public AccessControl createInstance(Map<String, Object> aclConfiguration) + public AccessControl createInstance(Map<String, Object> attributes) { - if (aclConfiguration != null) + if(attributes == null || !ACL_FILE_PROVIDER_TYPE.equals(attributes.get(ATTRIBUTE_TYPE))) + { + return null; + } + + String path = getStringAttribute(PATH, attributes, null); + if (path == null || "".equals(path.trim())) { - Object aclFile = aclConfiguration.get(ATTRIBUTE_ACL_FILE); - if (aclFile != null) - { - if (aclFile instanceof String) - { - String aclPath = (String) aclFile; - if (!new File(aclPath).exists()) - { - throw new IllegalConfigurationException("ACL file '" + aclPath + "' is not found"); - } - return new DefaultAccessControl(aclPath); - } - else - { - throw new IllegalConfigurationException("Expected '" + ATTRIBUTE_ACL_FILE + "' attribute value of type String but was " + aclFile.getClass() - + ": " + aclFile); - } - } + throw new IllegalConfigurationException("Path to ACL was not specified!"); } - return null; + + return new DefaultAccessControl(path); + } + + @Override + public String getType() + { + return ACL_FILE_PROVIDER_TYPE; + } + + @Override + public Collection<String> getAttributeNames() + { + return ATTRIBUTES; + } + + @Override + public Map<String, String> getAttributeDescriptions() + { + return ResourceBundleLoader.getResources(RESOURCE_BUNDLE); } } diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/FileAccessControlProviderAttributeDescriptions.properties b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/FileAccessControlProviderAttributeDescriptions.properties new file mode 100644 index 0000000000..e847e90f57 --- /dev/null +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/FileAccessControlProviderAttributeDescriptions.properties @@ -0,0 +1,19 @@ +# +# 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. + +path=File location*
\ No newline at end of file diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactoryTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactoryTest.java index ca1f19098f..2c55652f04 100644 --- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactoryTest.java +++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/DefaultAccessControlFactoryTest.java @@ -6,7 +6,9 @@ import java.util.Map; import java.util.regex.Pattern; import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.model.GroupProvider; import org.apache.qpid.server.security.AccessControl; +import org.apache.qpid.server.security.access.FileAccessControlProviderConstants; import org.apache.qpid.test.utils.QpidTestCase; import org.apache.qpid.test.utils.TestFileUtils; @@ -25,8 +27,10 @@ public class DefaultAccessControlFactoryTest extends QpidTestCase File aclFile = TestFileUtils.createTempFile(this, ".acl", "ACL ALLOW all all"); DefaultAccessControlFactory factory = new DefaultAccessControlFactory(); Map<String, Object> attributes = new HashMap<String, Object>(); - attributes.put(DefaultAccessControlFactory.ATTRIBUTE_ACL_FILE, aclFile.getAbsolutePath()); + attributes.put(GroupProvider.TYPE, FileAccessControlProviderConstants.ACL_FILE_PROVIDER_TYPE); + attributes.put(FileAccessControlProviderConstants.PATH, aclFile.getAbsolutePath()); AccessControl acl = factory.createInstance(attributes); + acl.open(); assertNotNull("ACL was not created from acl file: " + aclFile.getAbsolutePath(), acl); } @@ -37,33 +41,17 @@ public class DefaultAccessControlFactoryTest extends QpidTestCase assertFalse("ACL file " + aclFile.getAbsolutePath() + " actually exists but should not", aclFile.exists()); DefaultAccessControlFactory factory = new DefaultAccessControlFactory(); Map<String, Object> attributes = new HashMap<String, Object>(); - attributes.put(DefaultAccessControlFactory.ATTRIBUTE_ACL_FILE, aclFile.getAbsolutePath()); + attributes.put(GroupProvider.TYPE, FileAccessControlProviderConstants.ACL_FILE_PROVIDER_TYPE); + attributes.put(FileAccessControlProviderConstants.PATH, aclFile.getAbsolutePath()); try { - factory.createInstance(attributes); - fail("It should not be possible to create ACL from non existing file"); + AccessControl control = factory.createInstance(attributes); + control.open(); + fail("It should not be possible to create and initialise ACL with non existing file"); } catch (IllegalConfigurationException e) { - assertTrue("Unexpected exception message", Pattern.matches("ACL file '.*' is not found", e.getMessage())); - } - } - - public void testCreateInstanceWhenAclFileIsSpecifiedAsNonString() - { - DefaultAccessControlFactory factory = new DefaultAccessControlFactory(); - Map<String, Object> attributes = new HashMap<String, Object>(); - Integer aclFile = new Integer(0); - attributes.put(DefaultAccessControlFactory.ATTRIBUTE_ACL_FILE, aclFile); - try - { - factory.createInstance(attributes); - fail("It should not be possible to create ACL from Integer"); - } - catch (IllegalConfigurationException e) - { - assertEquals("Unexpected exception message", "Expected '" + DefaultAccessControlFactory.ATTRIBUTE_ACL_FILE - + "' attribute value of type String but was " + Integer.class + ": " + aclFile, e.getMessage()); + assertTrue("Unexpected exception message: " + e.getMessage(), Pattern.matches("ACL file '.*' is not found", e.getMessage())); } } } diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java index 5f281504e9..2fc54482a4 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java @@ -22,8 +22,10 @@ package org.apache.qpid.server.management.plugin; import java.io.File; import java.lang.reflect.Type; +import java.net.SocketAddress; import java.util.Collection; import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -33,9 +35,10 @@ 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.filter.ForbiddingAuthorisationFilter; +import org.apache.qpid.server.management.plugin.filter.RedirectingAuthorisationFilter; 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.HelperServlet; import org.apache.qpid.server.management.plugin.servlet.rest.LogRecordsServlet; import org.apache.qpid.server.management.plugin.servlet.rest.LogoutServlet; @@ -44,6 +47,7 @@ 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.AccessControlProvider; import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Binding; import org.apache.qpid.server.model.Broker; @@ -60,21 +64,25 @@ 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.TrustStore; 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.security.SubjectCreator; import org.apache.qpid.server.util.MapValueConverter; import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.DispatcherType; 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.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.ssl.SslContextFactory; -public class HttpManagement extends AbstractPluginAdapter +public class HttpManagement extends AbstractPluginAdapter implements HttpManagementConfiguration { private final Logger _logger = Logger.getLogger(HttpManagement.class); @@ -105,8 +113,6 @@ public class HttpManagement extends AbstractPluginAdapter add(PluginFactory.PLUGIN_TYPE); }}); - public static final String ENTRY_POINT_PATH = "/management"; - private static final String OPERATIONAL_LOGGING_NAME = "Web"; @@ -238,7 +244,7 @@ public class HttpManagement extends AbstractPluginAdapter } else if (protocols.contains(Protocol.HTTPS)) { - KeyStore keyStore = _broker.getDefaultKeyStore(); + KeyStore keyStore = port.getKeyStore(); if (keyStore == null) { throw new IllegalConfigurationException("Key store is not configured. Cannot start management on HTTPS port without keystore"); @@ -266,12 +272,19 @@ public class HttpManagement extends AbstractPluginAdapter 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); + root.getServletContext().setAttribute(HttpManagementUtil.ATTR_BROKER, _broker); + root.getServletContext().setAttribute(HttpManagementUtil.ATTR_MANAGEMENT_CONFIGURATION, this); + + FilterHolder restAuthorizationFilter = new FilterHolder(new ForbiddingAuthorisationFilter()); + restAuthorizationFilter.setInitParameter(ForbiddingAuthorisationFilter.INIT_PARAM_ALLOWED, "/rest/sasl"); + root.addFilter(restAuthorizationFilter, "/rest/*", EnumSet.of(DispatcherType.REQUEST)); + root.addFilter(new FilterHolder(new RedirectingAuthorisationFilter()), HttpManagementUtil.ENTRY_POINT_PATH, EnumSet.of(DispatcherType.REQUEST)); + root.addFilter(new FilterHolder(new RedirectingAuthorisationFilter()), "/index.html", EnumSet.of(DispatcherType.REQUEST)); addRestServlet(root, "broker"); addRestServlet(root, "virtualhost", VirtualHost.class); addRestServlet(root, "authenticationprovider", AuthenticationProvider.class); + addRestServlet(root, "accesscontrolprovider", AccessControlProvider.class); addRestServlet(root, "user", AuthenticationProvider.class, User.class); addRestServlet(root, "groupprovider", GroupProvider.class); addRestServlet(root, "group", GroupProvider.class, Group.class); @@ -282,6 +295,8 @@ public class HttpManagement extends AbstractPluginAdapter 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); + addRestServlet(root, "keystore", KeyStore.class); + addRestServlet(root, "truststore", TrustStore.class); root.addServlet(new ServletHolder(new StructureServlet()), "/rest/structure"); root.addServlet(new ServletHolder(new MessageServlet()), "/rest/message/*"); @@ -291,7 +306,7 @@ public class HttpManagement extends AbstractPluginAdapter root.addServlet(new ServletHolder(new SaslServlet()), "/rest/sasl"); - root.addServlet(new ServletHolder(new DefinedFileServlet("index.html")), ENTRY_POINT_PATH); + root.addServlet(new ServletHolder(new DefinedFileServlet("index.html")), HttpManagementUtil.ENTRY_POINT_PATH); root.addServlet(new ServletHolder(new LogoutServlet()), "/logout"); root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.js"); @@ -396,24 +411,34 @@ public class HttpManagement extends AbstractPluginAdapter return Collections.unmodifiableCollection(AVAILABLE_ATTRIBUTES); } + @Override public boolean isHttpsSaslAuthenticationEnabled() { return (Boolean)getAttribute(HTTPS_SASL_AUTHENTICATION_ENABLED); } + @Override public boolean isHttpSaslAuthenticationEnabled() { return (Boolean)getAttribute(HTTP_SASL_AUTHENTICATION_ENABLED); } + @Override public boolean isHttpsBasicAuthenticationEnabled() { return (Boolean)getAttribute(HTTPS_BASIC_AUTHENTICATION_ENABLED); } + @Override public boolean isHttpBasicAuthenticationEnabled() { return (Boolean)getAttribute(HTTP_BASIC_AUTHENTICATION_ENABLED); } + @Override + public SubjectCreator getSubjectCreator(SocketAddress localAddress) + { + return _broker.getSubjectCreator(localAddress); + } + } diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementConfiguration.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementConfiguration.java new file mode 100644 index 0000000000..56919e2e6b --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementConfiguration.java @@ -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. + * + */ +package org.apache.qpid.server.management.plugin; + +import java.net.SocketAddress; + +import org.apache.qpid.server.security.SubjectCreator; + +public interface HttpManagementConfiguration +{ + boolean isHttpsSaslAuthenticationEnabled(); + + boolean isHttpSaslAuthenticationEnabled(); + + boolean isHttpsBasicAuthenticationEnabled(); + + boolean isHttpBasicAuthenticationEnabled(); + + SubjectCreator getSubjectCreator(SocketAddress localAddress); +} diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java new file mode 100644 index 0000000000..4c6e5bf63e --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java @@ -0,0 +1,225 @@ +/* + * + * 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.net.InetSocketAddress; +import java.net.SocketAddress; +import java.security.AccessControlException; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +import javax.security.auth.Subject; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.apache.commons.codec.binary.Base64; +import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.HttpManagementActor; +import org.apache.qpid.server.management.plugin.session.LoginLogoutReporter; +import org.apache.qpid.server.model.Broker; +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; + +public class HttpManagementUtil +{ + + /** + * 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_CONFIGURATION = "Qpid.managementConfiguration"; + + /** + * Default management entry URL + */ + public static final String ENTRY_POINT_PATH = "/management"; + + private static final String ATTR_LOGIN_LOGOUT_REPORTER = "Qpid.loginLogoutReporter"; + private static final String ATTR_SUBJECT = "Qpid.subject"; + private static final String ATTR_LOG_ACTOR = "Qpid.logActor"; + + public static Broker getBroker(ServletContext servletContext) + { + return (Broker) servletContext.getAttribute(ATTR_BROKER); + } + + public static HttpManagementConfiguration getManagementConfiguration(ServletContext servletContext) + { + return (HttpManagementConfiguration) servletContext.getAttribute(ATTR_MANAGEMENT_CONFIGURATION); + } + + public static SocketAddress getSocketAddress(HttpServletRequest request) + { + return InetSocketAddress.createUnresolved(request.getServerName(), request.getServerPort()); + } + + public static Subject getAuthorisedSubject(HttpSession session) + { + return (Subject) session.getAttribute(ATTR_SUBJECT); + } + + public static void checkRequestAuthenticatedAndAccessAuthorized(HttpServletRequest request, Broker broker, + HttpManagementConfiguration managementConfig) + { + HttpSession session = request.getSession(); + Subject subject = getAuthorisedSubject(session); + if (subject == null) + { + subject = tryToAuthenticate(request, managementConfig); + if (subject == null) + { + throw new SecurityException("Only authenticated users can access the management interface"); + } + LogActor actor = createHttpManagementActor(broker, request); + if (hasAccessToManagement(broker.getSecurityManager(), subject, actor)) + { + saveAuthorisedSubject(session, subject, actor); + } + else + { + throw new AccessControlException("Access to the management interface denied"); + } + } + } + + public static boolean hasAccessToManagement(final SecurityManager securityManager, Subject subject, LogActor actor) + { + // TODO: We should eliminate SecurityManager.setThreadSubject in favour of Subject.doAs + SecurityManager.setThreadSubject(subject); // Required for accessManagement check + CurrentActor.set(actor); + try + { + try + { + return Subject.doAs(subject, new PrivilegedExceptionAction<Boolean>() + { + @Override + public Boolean run() throws Exception + { + return securityManager.accessManagement(); + } + }); + } + catch (PrivilegedActionException e) + { + throw new RuntimeException("Unable to perform access check", e); + } + } + finally + { + try + { + CurrentActor.remove(); + } + finally + { + SecurityManager.setThreadSubject(null); + } + } + } + + public static HttpManagementActor getOrCreateAndCacheLogActor(HttpServletRequest request, Broker broker) + { + HttpSession session = request.getSession(); + HttpManagementActor actor = (HttpManagementActor) session.getAttribute(ATTR_LOG_ACTOR); + if (actor == null) + { + actor = createHttpManagementActor(broker, request); + session.setAttribute(ATTR_LOG_ACTOR, actor); + } + return actor; + } + + public static void saveAuthorisedSubject(HttpSession session, Subject subject, LogActor logActor) + { + session.setAttribute(ATTR_SUBJECT, subject); + + // Cause the user logon to be logged. + session.setAttribute(ATTR_LOGIN_LOGOUT_REPORTER, new LoginLogoutReporter(logActor, subject)); + } + + private static Subject tryToAuthenticate(HttpServletRequest request, HttpManagementConfiguration managementConfig) + { + Subject subject = null; + SocketAddress localAddress = getSocketAddress(request); + SubjectCreator subjectCreator = managementConfig.getSubjectCreator(localAddress); + String remoteUser = request.getRemoteUser(); + + if (remoteUser != null || subjectCreator.isAnonymousAuthenticationAllowed()) + { + subject = authenticateUser(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])) + { + boolean isBasicAuthSupported = false; + if (request.isSecure()) + { + isBasicAuthSupported = managementConfig.isHttpsBasicAuthenticationEnabled(); + } + else + { + isBasicAuthSupported = managementConfig.isHttpBasicAuthenticationEnabled(); + } + if (isBasicAuthSupported) + { + String base64UsernameAndPassword = tokens[1]; + String[] credentials = (new String(Base64.decodeBase64(base64UsernameAndPassword.getBytes()))).split(":", 2); + if (credentials.length == 2) + { + subject = authenticateUser(subjectCreator, credentials[0], credentials[1]); + } + } + } + } + } + return subject; + } + + private static Subject authenticateUser(SubjectCreator subjectCreator, String username, String password) + { + SubjectAuthenticationResult authResult = subjectCreator.authenticate(username, password); + if (authResult.getStatus() == AuthenticationStatus.SUCCESS) + { + return authResult.getSubject(); + } + return null; + } + + private static HttpManagementActor createHttpManagementActor(Broker broker, HttpServletRequest request) + { + return new HttpManagementActor(broker.getRootMessageLogger(), request.getRemoteAddr(), request.getRemotePort()); + } + +} diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/filter/ForbiddingAuthorisationFilter.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/filter/ForbiddingAuthorisationFilter.java new file mode 100644 index 0000000000..b7f4b347c7 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/filter/ForbiddingAuthorisationFilter.java @@ -0,0 +1,73 @@ +package org.apache.qpid.server.management.plugin.filter; + +import java.io.IOException; +import java.security.AccessControlException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.qpid.server.management.plugin.HttpManagementConfiguration; +import org.apache.qpid.server.management.plugin.HttpManagementUtil; +import org.apache.qpid.server.model.Broker; + +public class ForbiddingAuthorisationFilter implements Filter +{ + public static String INIT_PARAM_ALLOWED = "allowed"; + private String _allowed = null; + + private Broker _broker; + private HttpManagementConfiguration _managementConfiguration; + + @Override + public void destroy() + { + } + + @Override + public void init(FilterConfig config) throws ServletException + { + String allowed = config.getInitParameter(INIT_PARAM_ALLOWED); + if (allowed != null) + { + _allowed = allowed; + } + ServletContext servletContext = config.getServletContext(); + _broker = HttpManagementUtil.getBroker(servletContext); + _managementConfiguration = HttpManagementUtil.getManagementConfiguration(servletContext); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, + ServletException + { + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + String servletPath = httpRequest.getServletPath(); + if (_allowed == null || "".equals(_allowed) || servletPath.indexOf(_allowed) == -1) + { + try + { + HttpManagementUtil.checkRequestAuthenticatedAndAccessAuthorized(httpRequest, _broker, _managementConfiguration); + } + catch(AccessControlException e) + { + httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } + catch(SecurityException e) + { + httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + } + chain.doFilter(request, response); + } + +} diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/filter/RedirectingAuthorisationFilter.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/filter/RedirectingAuthorisationFilter.java new file mode 100644 index 0000000000..c5e9890620 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/filter/RedirectingAuthorisationFilter.java @@ -0,0 +1,83 @@ +/* + * + * 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.filter; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.qpid.server.management.plugin.HttpManagementConfiguration; +import org.apache.qpid.server.management.plugin.HttpManagementUtil; +import org.apache.qpid.server.model.Broker; + +public class RedirectingAuthorisationFilter implements Filter +{ + public static String DEFAULT_LOGIN_URL = "login.html"; + public static String INIT_PARAM_LOGIN_URL = "login-url"; + + private String _loginUrl = DEFAULT_LOGIN_URL; + private Broker _broker; + private HttpManagementConfiguration _managementConfiguration; + + @Override + public void destroy() + { + } + + @Override + public void init(FilterConfig config) throws ServletException + { + String loginlUrl = config.getInitParameter(INIT_PARAM_LOGIN_URL); + if (loginlUrl != null) + { + _loginUrl = loginlUrl; + } + ServletContext servletContext = config.getServletContext(); + _broker = HttpManagementUtil.getBroker(servletContext); + _managementConfiguration = HttpManagementUtil.getManagementConfiguration(servletContext); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, + ServletException + { + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + try + { + HttpManagementUtil.checkRequestAuthenticatedAndAccessAuthorized(httpRequest, _broker, _managementConfiguration); + chain.doFilter(request, response); + } + catch(SecurityException e) + { + httpResponse.sendRedirect(_loginUrl); + } + } + +} diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java index 689bdb50d8..9614ded3d8 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java @@ -21,9 +21,6 @@ package org.apache.qpid.server.management.plugin.servlet.rest; import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.security.AccessControlException; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; @@ -34,44 +31,22 @@ 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.management.plugin.HttpManagementConfiguration; +import org.apache.qpid.server.management.plugin.HttpManagementUtil; import org.apache.qpid.server.model.Broker; 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; public abstract class AbstractServlet extends HttpServlet { 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; + private HttpManagementConfiguration _managementConfiguration; protected AbstractServlet() { @@ -83,9 +58,8 @@ public abstract class AbstractServlet extends HttpServlet { ServletConfig servletConfig = getServletConfig(); ServletContext servletContext = servletConfig.getServletContext(); - _broker = (Broker)servletContext.getAttribute(ATTR_BROKER); - _rootLogger = _broker.getRootMessageLogger(); - _httpManagement = (HttpManagement)servletContext.getAttribute(ATTR_MANAGEMENT); + _broker = HttpManagementUtil.getBroker(servletContext); + _managementConfiguration = HttpManagementUtil.getManagementConfiguration(servletContext); super.init(); } @@ -211,18 +185,18 @@ public abstract class AbstractServlet extends HttpServlet Subject subject; try { - subject = getAndCacheAuthorizedSubject(request); + subject = getAuthorisedSubject(request); } - catch (AccessControlException e) + catch (SecurityException e) { - sendError(resp, HttpServletResponse.SC_FORBIDDEN); + sendError(resp, HttpServletResponse.SC_UNAUTHORIZED); return; } SecurityManager.setThreadSubject(subject); try { - HttpManagementActor logActor = getLogActorAndCacheInSession(request); + HttpManagementActor logActor = HttpManagementUtil.getOrCreateAndCacheLogActor(request, _broker); CurrentActor.set(logActor); try { @@ -256,187 +230,24 @@ public abstract class AbstractServlet extends HttpServlet } } - /** - * 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) + protected Subject getAuthorisedSubject(HttpServletRequest request) { - HttpSession session = request.getSession(); - Subject subject = getAuthorisedSubjectFromSession(session); - - if(subject != null) - { - return subject; - } - - SubjectCreator subjectCreator = getSubjectCreator(request); - subject = authenticate(request, subjectCreator); - if (subject != null) + Subject subject = HttpManagementUtil.getAuthorisedSubject(request.getSession()); + if (subject == null) { - authoriseManagement(request, subject); - setAuthorisedSubjectInSession(subject, request, session); + throw new SecurityException("Access to management rest interfaces is denied for un-authorised user"); } - 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 - { - @Override - public Void run() throws Exception - { - boolean allowed = getSecurityManager().accessManagement(); - if (!allowed) - { - throw new AccessControlException("User is not authorised for management"); - } - return null; - } - }); - } - catch (PrivilegedActionException e) - { - throw new RuntimeException("Unable to perform access check", e); - } - } - finally - { - try - { - CurrentActor.remove(); - } - finally - { - SecurityManager.setThreadSubject(null); - } - } - } - - private Subject authenticate(HttpServletRequest request, SubjectCreator subjectCreator) - { - 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; - } - - private Subject performBasicAuth(Subject subject,SubjectCreator subjectCreator, String base64UsernameAndPassword) - { - String[] credentials = (new String(Base64.decodeBase64(base64UsernameAndPassword.getBytes()))).split(":",2); - if(credentials.length == 2) - { - subject = authenticateUserAndGetSubject(subjectCreator, credentials[0], credentials[1]); - } - else - { - //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; - } - - private boolean isBasicAuthSupported(HttpServletRequest req) - { - return req.isSecure() ? _httpManagement.isHttpsBasicAuthenticationEnabled() - : _httpManagement.isHttpBasicAuthenticationEnabled(); - } - - private HttpManagementActor getLogActorAndCacheInSession(HttpServletRequest req) - { - HttpSession session = req.getSession(); - - HttpManagementActor actor = (HttpManagementActor) session.getAttribute(ATTR_LOG_ACTOR); - if(actor == null) - { - actor = createHttpManagementActor(req); - session.setAttribute(ATTR_LOG_ACTOR, actor); - } - - return actor; - } - - protected Subject getAuthorisedSubjectFromSession(HttpSession session) - { - return (Subject)session.getAttribute(ATTR_SUBJECT); - } - - 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) + protected HttpManagementConfiguration getManagementConfiguration() { - return InetSocketAddress.createUnresolved(request.getServerName(), request.getServerPort()); + return _managementConfiguration; } protected void sendError(final HttpServletResponse resp, int errorCode) @@ -450,24 +261,4 @@ public abstract class AbstractServlet extends HttpServlet throw new RuntimeException("Failed to send error response code " + errorCode, e); } } - - private HttpManagementActor createHttpManagementActor(HttpServletRequest request) - { - return new HttpManagementActor(_rootLogger, request.getRemoteAddr(), request.getRemotePort()); - } - - protected HttpManagement getManagement() - { - return _httpManagement; - } - - protected SecurityManager getSecurityManager() - { - return _broker.getSecurityManager(); - } - - protected SubjectCreator getSubjectCreator(HttpServletRequest request) - { - return _broker.getSubjectCreator(getSocketAddress(request)); - } } diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/HelperServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/HelperServlet.java index 3c6fb1c44d..a7066c73d8 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/HelperServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/HelperServlet.java @@ -31,9 +31,10 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.qpid.server.management.plugin.servlet.rest.action.ListAccessControlProviderAttributes; import org.apache.qpid.server.management.plugin.servlet.rest.action.ListAuthenticationProviderAttributes; +import org.apache.qpid.server.management.plugin.servlet.rest.action.ListGroupProviderAttributes; import org.apache.qpid.server.management.plugin.servlet.rest.action.ListMessageStoreTypes; -import org.apache.qpid.server.model.Broker; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; @@ -53,6 +54,10 @@ public class HelperServlet extends AbstractServlet _actions.put(listProviderAttributes.getName(), listProviderAttributes); Action listMessageStoreTypes = new ListMessageStoreTypes(); _actions.put(listMessageStoreTypes.getName(), listMessageStoreTypes); + Action groupProviderAttributes = new ListGroupProviderAttributes(); + _actions.put(groupProviderAttributes.getName(), groupProviderAttributes); + Action aclProviderAttributes = new ListAccessControlProviderAttributes(); + _actions.put(aclProviderAttributes.getName(), aclProviderAttributes); } @Override @@ -96,7 +101,7 @@ public class HelperServlet extends AbstractServlet } } - Object output = action.perform(parameters, (Broker) getServletContext().getAttribute(ATTR_BROKER)); + Object output = action.perform(parameters, getBroker()); if (output == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogoutServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogoutServlet.java index 4188e7d60d..34b115ed13 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogoutServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogoutServlet.java @@ -29,13 +29,13 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.management.plugin.HttpManagementUtil; @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; + private String _returnUrl = HttpManagementUtil.ENTRY_POINT_PATH; @Override public void init(ServletConfig config) throws ServletException diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java index 069132af1e..b67c83dc7a 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java @@ -25,7 +25,10 @@ import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; import org.apache.log4j.Logger; -import org.apache.qpid.server.management.plugin.HttpManagement; +import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.management.plugin.HttpManagementConfiguration; +import org.apache.qpid.server.management.plugin.HttpManagementUtil; +import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; @@ -38,7 +41,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.net.SocketAddress; import java.security.Principal; import java.security.SecureRandom; import java.util.LinkedHashMap; @@ -80,7 +83,7 @@ public class SaslServlet extends AbstractServlet String[] mechanisms = subjectCreator.getMechanisms().split(" "); Map<String, Object> outputObject = new LinkedHashMap<String, Object>(); - final Subject subject = getAuthorisedSubjectFromSession(session); + final Subject subject = getAuthorisedSubject(request); if(subject != null) { Principal principal = AuthenticatedPrincipal.getAuthenticatedPrincipalFromSubject(subject); @@ -195,8 +198,8 @@ public class SaslServlet extends AbstractServlet private void checkSaslAuthEnabled(HttpServletRequest request) { - boolean saslAuthEnabled; - HttpManagement management = getManagement(); + boolean saslAuthEnabled = false; + HttpManagementConfiguration management = getManagementConfiguration(); if (request.isSecure()) { saslAuthEnabled = management.isHttpsSaslAuthenticationEnabled(); @@ -205,7 +208,6 @@ public class SaslServlet extends AbstractServlet { saslAuthEnabled = management.isHttpSaslAuthenticationEnabled(); } - if (!saslAuthEnabled) { throw new RuntimeException("Sasl authentication disabled."); @@ -227,7 +229,7 @@ public class SaslServlet extends AbstractServlet session.removeAttribute(ATTR_ID); session.removeAttribute(ATTR_SASL_SERVER); session.removeAttribute(ATTR_EXPIRY); - response.setStatus(HttpServletResponse.SC_FORBIDDEN); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } @@ -236,17 +238,15 @@ public class SaslServlet extends AbstractServlet { Subject subject = subjectCreator.createSubjectWithGroups(saslServer.getAuthorizationID()); - try - { - authoriseManagement(request, subject); - } - catch (AccessControlException ace) + Broker broker = getBroker(); + LogActor actor = HttpManagementUtil.getOrCreateAndCacheLogActor(request, broker); + if (!HttpManagementUtil.hasAccessToManagement(broker.getSecurityManager(), subject, actor)) { sendError(response, HttpServletResponse.SC_FORBIDDEN); return; } - setAuthorisedSubjectInSession(subject, request, session); + HttpManagementUtil.saveAuthorisedSubject(request.getSession(), subject, actor); session.removeAttribute(ATTR_ID); session.removeAttribute(ATTR_SASL_SERVER); session.removeAttribute(ATTR_EXPIRY); @@ -274,4 +274,16 @@ public class SaslServlet extends AbstractServlet mapper.writeValue(writer, outputObject); } } + + private SubjectCreator getSubjectCreator(HttpServletRequest request) + { + SocketAddress localAddress = HttpManagementUtil.getSocketAddress(request); + return HttpManagementUtil.getManagementConfiguration(getServletContext()).getSubjectCreator(localAddress); + } + + @Override + protected Subject getAuthorisedSubject(HttpServletRequest request) + { + return HttpManagementUtil.getAuthorisedSubject(request.getSession()); + } } diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/action/ListAccessControlProviderAttributes.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/action/ListAccessControlProviderAttributes.java new file mode 100644 index 0000000000..6887217016 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/action/ListAccessControlProviderAttributes.java @@ -0,0 +1,76 @@ +/* + * + * 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.action; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.qpid.server.management.plugin.servlet.rest.Action; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.plugin.AccessControlFactory; +import org.apache.qpid.server.plugin.QpidServiceLoader; + +public class ListAccessControlProviderAttributes implements Action +{ + private static final String ATTRIBUTES = "attributes"; + private static final String DESCRIPTIONS = "descriptions"; + private Map<String, AccessControlFactory> _factories; + + public ListAccessControlProviderAttributes() + { + _factories = new TreeMap<String, AccessControlFactory>(); + Iterable<AccessControlFactory> factories = new QpidServiceLoader<AccessControlFactory>() + .instancesOf(AccessControlFactory.class); + for (AccessControlFactory factory : factories) + { + _factories.put(factory.getType(), factory); + } + } + + @Override + public String getName() + { + return ListAccessControlProviderAttributes.class.getSimpleName(); + } + + @Override + public Object perform(Map<String, Object> request, Broker broker) + { + Map<String, Object> attributes = new TreeMap<String, Object>(); + for (String providerType : _factories.keySet()) + { + AccessControlFactory factory = _factories.get(providerType); + + Map<String, Object> data = new HashMap<String, Object>(); + data.put(ATTRIBUTES, factory.getAttributeNames()); + Map<String, String> resources = factory.getAttributeDescriptions(); + if (resources != null) + { + data.put(DESCRIPTIONS, resources); + } + + attributes.put(factory.getType(), data); + } + return attributes; + } + +} diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/action/ListGroupProviderAttributes.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/action/ListGroupProviderAttributes.java new file mode 100644 index 0000000000..d1414faa71 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/action/ListGroupProviderAttributes.java @@ -0,0 +1,76 @@ +/* + * 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.action; + +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; + +import org.apache.qpid.server.management.plugin.servlet.rest.Action; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.plugin.GroupManagerFactory; +import org.apache.qpid.server.plugin.QpidServiceLoader; + +public class ListGroupProviderAttributes implements Action +{ + private static final String ATTRIBUTES = "attributes"; + private static final String DESCRIPTIONS = "descriptions"; + private Map<String, GroupManagerFactory> _factories; + + public ListGroupProviderAttributes() + { + _factories = new TreeMap<String, GroupManagerFactory>(); + Iterable<GroupManagerFactory> factories = new QpidServiceLoader<GroupManagerFactory>() + .instancesOf(GroupManagerFactory.class); + for (GroupManagerFactory factory : factories) + { + _factories.put(factory.getType(), factory); + } + } + + @Override + public String getName() + { + return ListGroupProviderAttributes.class.getSimpleName(); + } + + @Override + public Object perform(Map<String, Object> request, Broker broker) + { + Map<String, Object> attributes = new TreeMap<String, Object>(); + for (String providerType : _factories.keySet()) + { + GroupManagerFactory factory = _factories.get(providerType); + + Map<String, Object> data = new HashMap<String, Object>(); + data.put(ATTRIBUTES, factory.getAttributeNames()); + Map<String, String> resources = factory.getAttributeDescriptions(); + if (resources != null) + { + data.put(DESCRIPTIONS, resources); + } + + attributes.put(factory.getType(), data); + } + return attributes; + } + +} diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/action/ListMessageStoreTypes.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/action/ListMessageStoreTypes.java index 4166736c01..091068f238 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/action/ListMessageStoreTypes.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/action/ListMessageStoreTypes.java @@ -17,7 +17,7 @@ public class ListMessageStoreTypes implements Action @Override public Object perform(Map<String, Object> request, Broker broker) { - return broker.getAttribute(Broker.SUPPORTED_STORE_TYPES); + return broker.getAttribute(Broker.SUPPORTED_VIRTUALHOST_STORE_TYPES); } } diff --git a/qpid/java/systests/etc/config-systests.xml b/qpid/java/broker-plugins/management-http/src/main/java/resources/accesscontrolprovider/showAclFile.html index 958aafc9da..182e651a51 100644 --- a/qpid/java/systests/etc/config-systests.xml +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/accesscontrolprovider/showAclFile.html @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="ISO-8859-1"?> <!-- - - Licensed to the Apache Software Foundation (ASF) under one @@ -19,10 +18,7 @@ - under the License. - --> -<configuration> - <system/> - <override> - <xml fileName="${QPID_HOME}/${test.config}" optional="true"/> - <xml fileName="${QPID_HOME}/etc/config-systests-settings.xml"/> - </override> -</configuration> +<div class="AclFileProvider"> + <span style="">Path:</span><span class="path" style="position:absolute; left:6em"></span> + <br/> +</div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/addAuthenticationProvider.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/addAuthenticationProvider.html index 90dd1f1090..f4846ac556 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/addAuthenticationProvider.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/addAuthenticationProvider.html @@ -15,15 +15,16 @@ ~ limitations under the License. --> <div class="dijitHidden"> - <div data-dojo-type="dijit.Dialog" style="width:600px;" data-dojo-props="title:'Authentication Provider'" id="addAuthenticationProvider"> + <div data-dojo-type="dijit.Dialog" data-dojo-props="title:'Authentication Provider'" id="addAuthenticationProvider"> <form id="formAddAuthenticationProvider" method="post" dojoType="dijit.form.Form"> + <div style="height:100px; width:420px; overflow: auto"> <table class="tableContainer-table tableContainer-table-horiz" width="100%" cellspacing="1"> <tr> - <td class="tableContainer-labelCell" style="width: 300px;">Type*:</td> + <td class="tableContainer-labelCell" style="width: 200px;">Type*:</td> <td class="tableContainer-valueCell"><div id="addAuthenticationProvider.selectAuthenticationProviderDiv"></div></td> </tr> <tr> - <td class="tableContainer-labelCell" style="width: 300px;">Name*:</td> + <td class="tableContainer-labelCell" style="width: 200px;">Name*:</td> <td class="tableContainer-valueCell"><input type="text" required="true" name="name" id="formAddAuthenticationProvider.name" placeholder="Name" regexp="^[\x20-\x2e\x30-\x7F]{1,255}$" dojoType="dijit.form.ValidationTextBox" missingMessage="A name must be supplied" /></div></td> @@ -31,8 +32,11 @@ </table> <input type="hidden" id="formAddAuthenticationProvider.id" name="id"/> <div id="addAuthenticationProvider.fieldSets"></div> + </div> + <div class="dijitDialogPaneActionBar"> <!-- submit buttons --> <input type="submit" value="Save Authentication Provider" label="Save Authentication Provider" dojoType="dijit.form.Button" /> + </div> </form> </div> </div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/addBinding.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/addBinding.html index 8dbd219c8d..9aebca90d7 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/addBinding.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/addBinding.html @@ -32,11 +32,10 @@ dojoType="dijit.form.ValidationTextBox" missingMessage="A name must be supplied" /></td> </tr> </table> - <br/> - + <div class="dijitDialogPaneActionBar"> <!-- submit buttons --> <input type="submit" value="Create Binding" label="Create Binding" dojoType="dijit.form.Button" /> - + </div> </form> </div> </div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/addExchange.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/addExchange.html index 4a59cd2cbc..8c9968e37a 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/addExchange.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/addExchange.html @@ -44,11 +44,10 @@ </td> </tr> </table> - <br/> - + <div class="dijitDialogPaneActionBar"> <!-- submit buttons --> <input type="submit" value="Create Exchange" label="Create Exchange" dojoType="dijit.form.Button" /> - + </div> </form> </div> </div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/addPort.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/addPort.html index c37b879bd5..a6f3384020 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/addPort.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/addPort.html @@ -19,43 +19,33 @@ - --> <div class="dijitHidden"> - <div data-dojo-type="dijit.Dialog" style="width:600px;" data-dojo-props="title:'Port'" id="addPort"> + <div data-dojo-type="dijit.Dialog" data-dojo-props="title:'Port'" id="addPort"> <form id="formAddPort" method="post" dojoType="dijit.form.Form"> - <div class="dijitDialogPaneContentArea"> + <div style="height:320px; width:420px; overflow: auto"> <div id="formAddPort:fields"> <input type="text" required="true" name="name" id="formAddPort.name" placeholder="Name" data-dojo-props="label: 'Name*:'" dojoType="dijit.form.ValidationTextBox" missingMessage="A name must be supplied" regexp="^[\x20-\x2e\x30-\x7F]{1,255}$"/> - <input data-dojo-type="dijit.form.NumberSpinner" id="formAddPort.port" required="true" data-dojo-props="label: 'Port Number*:'" + <input data-dojo-type="dijit.form.NumberSpinner" id="formAddPort.port" required="true" data-dojo-props="label: 'Port Number*:', placeHolder: 'Enter port number'" name="port" smallDelta="1" constraints="{min:1,max:65535,places:0, pattern: '#####'}" missingMessage="A port number must be supplied" /> - <select id="formAddPort.transports" data-dojo-type="dijit.form.FilteringSelect" - data-dojo-props="name: 'transports',label: 'Transport:',searchAttr: 'name',required:false,placeHolder: 'TCP', value: '' " - style="margin: 0;"> - <option value="TCP">TCP</option> - <option value="SSL">SSL</option> - </select> - <select id="formAddPort.authenticationProvider" data-dojo-type="dijit.form.FilteringSelect" style="margin: 0;" - data-dojo-props="name:'authenticationProvider',label:'Authentication Provider:', searchAttr: 'name', required: false, placeHolder: 'Default', value: '' "> - </select> <select id="formAddPort.type" data-dojo-type="dijit.form.FilteringSelect" - data-dojo-props="name: 'type', value: '',placeHolder: 'Select Port Type', label: 'Port Type:'"> + data-dojo-props="name: 'type', value: '',placeHolder: 'Select Port Type', label: 'Port Type*:'"> <option value="AMQP" selected="selected">AMQP</option> <option value="JMX">JMX</option> <option value="HTTP">HTTP</option> </select> </div> - <div id="formAddPort:fieldsClientAuth"> - <input id="formAddPort.needClientAuth" type="checkbox" name="needClientAuth" - dojoType="dijit.form.CheckBox" data-dojo-props="label: 'Need SSL Client Certificate:'"/> - <input id="formAddPort.wantClientAuth" type="checkbox" name="wantClientAuth" - dojoType="dijit.form.CheckBox" data-dojo-props="label: 'Want SSL Client Certificate:'"/> + <div id="formAddPort:fieldsAuthenticationProvider"> + <select id="formAddPort.authenticationProvider" data-dojo-type="dijit.form.FilteringSelect" + data-dojo-props="name:'authenticationProvider',label:'Authentication Provider*:', searchAttr: 'name', required: true, placeHolder: 'Select Provider'"> + </select> </div> <div id="formAddPort:fieldsAMQP"> <input id="formAddPort.bindingAddress" type="text" name="bindingAddress" placeholder="*" dojoType="dijit.form.TextBox" data-dojo-props="label: 'Binding address:'"/> <input id="formAddPort.protocolsDefault" type="checkbox" checked="checked" - dojoType="dijit.form.CheckBox" data-dojo-props="label: 'Support broker default AMQP versions:'"/> + dojoType="dijit.form.CheckBox" data-dojo-props="label: 'Support default protocols:'"/> <select id="formAddPort.protocolsAMQP" name="protocols" data-dojo-type="dijit.form.MultiSelect" multiple="true" data-dojo-props="name: 'protocols', value: '', placeHolder: 'Select AMQP versions', label: 'AMQP versions:'" missingMessage="AMQP protocol(s) must be supplied"> @@ -80,6 +70,37 @@ <option value="HTTPS">HTTPS</option> </select> </div> + <div id="formAddPort:transport" > + <select id="formAddPort.transports" data-dojo-type="dijit.form.FilteringSelect" + data-dojo-props="name: 'transports',label: 'Transport:',searchAttr: 'name',required:false,placeHolder: 'TCP', value: '' " + style="margin: 0;"> + <option value="TCP">TCP</option> + <option value="SSL">SSL</option> + </select> + </div> + <div id="formAddPort:fieldsTransportSSL"> + <select id="formAddPort.keyStore" data-dojo-type="dijit.form.FilteringSelect" + data-dojo-props="name:'keyStore',label:'Key Store*:', searchAttr: 'name', placeHolder: 'Select keystore', value: '', required: true "> + </select> + </div> + <div id="formAddPort:fieldsClientAuth"> + <div id="formAddPort:fieldsClientAuthCheckboxes"> + <input id="formAddPort.needClientAuth" type="checkbox" name="needClientAuth" + dojoType="dijit.form.CheckBox" data-dojo-props="label: 'Need SSL Client Certificate:'" /> + <input id="formAddPort.wantClientAuth" type="checkbox" name="wantClientAuth" + dojoType="dijit.form.CheckBox" data-dojo-props="label: 'Want SSL Client Certificate:'" /> + </div> + <div><strong>Trust Stores:</strong></div> + <table id="formAddPort.trustStores" data-dojo-type="dojox.grid.EnhancedGrid" + data-dojo-props="label:'Trust Stores:',plugins:{indirectSelection: true},rowSelector:'0px' " style="height: 100px; width:400px"> + <thead> + <tr> + <th field="name">Name</th> + <th field="peersOnly">Peers Only</th> + </tr> + </thead> + </table> + </div> <input type="hidden" id="formAddPort.id" name="id"/> </div> <div class="dijitDialogPaneActionBar"> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/addQueue.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/addQueue.html index 950809d5fc..90a0af7ea9 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/addQueue.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/addQueue.html @@ -19,8 +19,9 @@ - --> <div class="dijitHidden"> - <div data-dojo-type="dijit.Dialog" style="width:600px;" data-dojo-props="title:'Add Queue'" id="addQueue"> + <div data-dojo-type="dijit.Dialog" data-dojo-props="title:'Add Queue'" id="addQueue"> <form id="formAddQueue" method="post" dojoType="dijit.form.Form"> + <div style="height:250px; width:600px; overflow: auto"> <table cellpadding="0" cellspacing="2"> <tr> <td valign="top"><strong>Queue Name*: </strong></td> @@ -173,10 +174,11 @@ </tr> </table> </div> - <br/> + </div> + <div class="dijitDialogPaneActionBar"> <!-- submit buttons --> <input type="submit" value="Create Queue" label="Create Queue" dojoType="dijit.form.Button" /> - + </div> </form> </div> </div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/addVirtualHost.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/addVirtualHost.html index 9b492ef26d..43281f600d 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/addVirtualHost.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/addVirtualHost.html @@ -62,10 +62,11 @@ </div> </div> - <br/> + <div class="dijitDialogPaneActionBar"> <!-- submit buttons --> <input type="submit" value="Save" label="Save" dojoType="dijit.form.Button" /> <input type="hidden" id="formAddVirtualHost.id" name="id"/> + </div> </form> </div> </div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/css/common.css b/qpid/java/broker-plugins/management-http/src/main/java/resources/css/common.css index f5ca3e68dd..4c8b79ab82 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/css/common.css +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/css/common.css @@ -43,7 +43,6 @@ button { -moz-border-radius: 4px 4px 4px 4px; -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); background-color: #E4F2FF; - background-image: url("../dojo/dijit/themes/claro/form/images/button.png"); background-position: center top; background-repeat: repeat-x; border: 1px solid #769DC0; diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/groupprovider/showFileGroupManager.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/groupprovider/showFileGroupManager.html index 734e8b5419..5055cbdc47 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/groupprovider/showFileGroupManager.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/groupprovider/showFileGroupManager.html @@ -19,6 +19,8 @@ - --> <div class="FileGroupManager"> + <span style="">Path:</span><span class="path" style="position:absolute; left:6em"></span> + <br/> <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> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/index.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/index.html index 2fb9137ff8..2117ef361d 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/index.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/index.html @@ -24,6 +24,7 @@ <link rel="stylesheet" href="dojo/dojox/grid/resources/claroGrid.css"> <link rel="stylesheet" href="dojo/dojox/grid/enhanced/resources/claro/EnhancedGrid.css"> <link rel="stylesheet" href="dojo/dojox/grid/enhanced/resources/EnhancedGrid_rtl.css"> + <link rel="stylesheet" href="dojo/dojox/form/resources/CheckedMultiSelect.css"> <link rel="stylesheet" href="css/common.css" media="screen"> <script> function getContextPath() @@ -65,7 +66,7 @@ "qpid/management/treeView", "qpid/management/controller", "qpid/common/footer", - "qpid/authorization/sasl"]); + "qpid/authorization/checkUser"]); </script> </head> @@ -74,9 +75,9 @@ <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" style="float: left; width: 300px"></div> - <div id="login" style="float: right"></div> + <div id="login" style="float: right; display:none"><strong>User: </strong> <span id="authenticatedUser"></span><a href="logout">[logout]</a></div> </div> - <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'leading', splitter: true"> + <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'leading', splitter: true" style="width:20%"> <div qpid-type="treeView" qpid-props="query: 'rest/structure'" ></div> </div> <div id="managedViews" data-dojo-type="dijit.layout.TabContainer" data-dojo-props="region:'center', tabPosition: 'top'"> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/checkUser.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/checkUser.js new file mode 100644 index 0000000000..3fc60854f6 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/checkUser.js @@ -0,0 +1,36 @@ +/* + * + * 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. + * + */ + +require(["dojo/dom", + "qpid/authorization/sasl", + "dojo/domReady!"], function(dom, sasl){ + +var updateUI = function updateUI(data) +{ + if(data.user) + { + dom.byId("authenticatedUser").innerHTML = data.user; + dom.byId("login").style.display = "block"; + } +}; + +sasl.getUser(updateUI); +});
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js index b4f0728685..33e736322f 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/sasl.js @@ -18,10 +18,7 @@ * under the License. * */ -require(["dijit/form/DropDownButton", "dijit/TooltipDialog", "dijit/form/TextBox", - "dojo/_base/xhr", "dojox/encoding/base64", "dojox/encoding/digests/_base", "dojox/encoding/digests/MD5"]); -var button; -var usernameSpan; +define(["dojo/_base/xhr", "dojox/encoding/base64", "dojox/encoding/digests/_base", "dojox/encoding/digests/MD5"], function () { var encodeUTF8 = function encodeUTF8(str) { var byteArray = []; @@ -49,8 +46,23 @@ var decodeUTF8 = function decodeUTF8(byteArray) return decodeURIComponent(str); }; +var errorHandler = function errorHandler(error) +{ + if(error.status == 401) + { + alert("Authentication Failed"); + } + else if(error.status == 403) + { + alert("Authorization Failed"); + } + else + { + alert(error); + } +} -var saslPlain = function saslPlain(user, password) +var saslPlain = function saslPlain(user, password, callbackFunction) { var responseArray = [ 0 ].concat(encodeUTF8( user )).concat( [ 0 ] ).concat( encodeUTF8( password ) ); var plainResponse = dojox.encoding.base64.encode(responseArray); @@ -65,25 +77,10 @@ var saslPlain = function saslPlain(user, password) }, handleAs: "json", failOk: true - }).then(function() - { - updateAuthentication(); - }, - function(error) - { - if(error.status == 403) - { - alert("Authentication Failed"); - } - else - { - alert(error); - } - updateAuthentication(); - }); + }).then(callbackFunction, errorHandler); }; -var saslCramMD5 = function saslCramMD5(user, password) +var saslCramMD5 = function saslCramMD5(user, password, saslMechanism, callbackFunction) { // Using dojo.xhrGet, as very little information is being sent @@ -91,7 +88,7 @@ var saslCramMD5 = function saslCramMD5(user, password) // The URL of the request url: "rest/sasl", content: { - mechanism: "CRAM-MD5" + mechanism: saslMechanism }, handleAs: "json", failOk: true @@ -121,22 +118,7 @@ var saslCramMD5 = function saslCramMD5(user, password) }, handleAs: "json", failOk: true - }).then(function() - { - updateAuthentication(); - }, - function(error) - { - if(error.status == 403) - { - alert("Authentication Failed"); - } - else - { - alert(error); - } - updateAuthentication(); - }); + }).then(callbackFunction, errorHandler); }, function(error) @@ -163,86 +145,45 @@ var containsMechanism = function containsMechanism(mechanisms, mech) return false; }; -var doAuthenticate = function doAuthenticate() +var SaslClient = {}; + +SaslClient.authenticate = function(username, password, callbackFunction) { dojo.xhrGet({ - // The URL of the request url: "rest/sasl", - handleAs: "json" + handleAs: "json", + failOk: true }).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); - } - } - ); - - + var mechMap = data.mechanisms; + if (containsMechanism(mechMap, "CRAM-MD5")) + { + saslCramMD5(username, password, "CRAM-MD5", callbackFunction); + } + else if (containsMechanism(mechMap, "CRAM-MD5-HEX")) + { + var hashedPassword = dojox.encoding.digests.MD5(password, dojox.encoding.digests.outputTypes.Hex); + saslCramMD5(username, hashedPassword, "CRAM-MD5-HEX", callbackFunction); + } + else if (containsMechanism(mechMap, "PLAIN")) + { + saslPlain(username, password, callbackFunction); + } + else + { + alert("No supported SASL mechanism offered: " + mechMap); + } + }, errorHandler); }; - -var updateAuthentication = function updateAuthentication() +SaslClient.getUser = function(callbackFunction) { dojo.xhrGet({ - // The URL of the request url: "rest/sasl", - handleAs: "json" - }).then(function(data) - { - if(data.user) - { - dojo.byId("authenticatedUser").innerHTML = data.user; - dojo.style(button.domNode, {display: 'none'}); - dojo.style(usernameSpan, {display: 'block'}); - } - else - { - dojo.style(button.domNode, {display: 'block'}); - dojo.style(usernameSpan, {display: 'none'}); - } - } - ); + handleAs: "json", + failOk: true + }).then(callbackFunction, errorHandler); }; -require(["dijit/form/DropDownButton", "dijit/TooltipDialog", "dijit/form/TextBox", "dojo/_base/xhr", "dojo/dom", "dojo/dom-construct", "dojo/domReady!"], - function(DropDownButton, TooltipDialog, TextBox, xhr, dom, domConstruct){ - var dialog = new TooltipDialog({ - content: - '<strong><label for="username" style="display:inline-block;width:100px;">Username:</label></strong>' + - '<div data-dojo-type="dijit.form.TextBox" id="username"></div><br/>' + - '<strong><label for="pass" style="display:inline-block;width:100px;">Password:</label></strong>' + - '<div data-dojo-type="dijit.form.TextBox" type="password" id="pass"></div><br/>' + - '<button data-dojo-type="dijit.form.Button" data-dojo-props="onClick:doAuthenticate" type="submit">Login</button>' - }); - - button = new DropDownButton({ - label: "Login", - dropDown: dialog - }); - - 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(usernameSpan); - loginDiv.appendChild(button.domNode); - - - - - updateAuthentication(); -});
\ No newline at end of file +return SaslClient; +}); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js index 5ff208d43f..77ae1ccf47 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js @@ -33,7 +33,7 @@ define(["dojo/_base/xhr", "dijit/form/RadioButton", "dijit/form/CheckBox", "dojox/layout/TableContainer", - "dojox/layout/ScrollPane", + "dijit/layout/ContentPane", "dojox/validate/us", "dojox/validate/web", "dojo/domReady!" @@ -141,7 +141,7 @@ define(["dojo/_base/xhr", return (type === "PlainPasswordFile" || type === "Base64MD5PasswordFile"); }; - util.showSetAttributesDialog = function(attributeWidgetFactories, data, putURL, dialogTitle) + util.showSetAttributesDialog = function(attributeWidgetFactories, data, putURL, dialogTitle, appendNameToUrl) { var layout = new dojox.layout.TableContainer({ cols: 1, @@ -154,7 +154,7 @@ define(["dojo/_base/xhr", var form = new dijit.form.Form(); var dialogContent = dom.create("div"); - var dialogContentArea = dom.create("div", { "class": "dijitDialogPaneContentArea"}); + var dialogContentArea = dom.create("div", {"style": {width: 600}}); var dialogActionBar = dom.create("div", { "class": "dijitDialogPaneActionBar"} ); dialogContent.appendChild(dialogContentArea); dialogContent.appendChild(dialogActionBar); @@ -171,11 +171,17 @@ define(["dojo/_base/xhr", var widget = attributeWidgetFactory.createWidget(data); var name = attributeWidgetFactory.name ? attributeWidgetFactory.name : widget.name; widgets[name] = widget; - widget.initialValue = widget.value; var dotPos = name.indexOf("."); if (dotPos == -1) { - layout.addChild(widget); + if (widget instanceof dijit.layout.ContentPane) + { + dialogContentArea.appendChild(widget.domNode); + } + else + { + layout.addChild(widget); + } } else { @@ -197,7 +203,7 @@ define(["dojo/_base/xhr", groups[groupName] = groupFieldContainer; var groupTitle = attributeWidgetFactory.groupName ? attributeWidgetFactory.groupName : groupName.charAt(0).toUpperCase() + groupName.slice(1); - var panel = new dijit.TitlePane({title: groupTitle, toggleable: false, content: groupFieldContainer.domNode}); + var panel = new dijit.TitlePane({title: groupTitle, content: groupFieldContainer.domNode}); dialogContentArea.appendChild(dom.create("br")); dialogContentArea.appendChild(panel.domNode); } @@ -224,8 +230,7 @@ define(["dojo/_base/xhr", } var setAttributesDialog = new dijit.Dialog({ title: dialogTitle, - content: form, - style: "width: 600px; max-height: 80%" + content: form }); form.on("submit", function(e) { @@ -235,23 +240,29 @@ define(["dojo/_base/xhr", if(form.validate()) { var values = {}; - for(var i in widgets) + var formWidgets = form.getDescendants(); + for(var i in formWidgets) { - var widget = widgets[i]; + var widget = formWidgets[i]; var value = widget.value; var propName = widget.name; - if ((widget instanceof dijit.form.CheckBox || widget instanceof dijit.form.RadioButton)) - { - values[ propName ] = widget.checked; - } - else if (value != widget.initialValue) - { - values[ propName ] = value ? value: null; + if (propName && !widget.disabled){ + if ((widget instanceof dijit.form.CheckBox || widget instanceof dijit.form.RadioButton)) { + if (widget.checked != widget.initialValue) { + values[ propName ] = widget.checked; + } + } else if (value != widget.initialValue) { + values[ propName ] = value ? value: null; + } } } var that = this; - xhr.put({url: putURL, sync: true, handleAs: "json", + var url = putURL; + if (appendNameToUrl){ + url = url + "/" + encodeURIComponent(values["name"]); + } + xhr.put({url: url , sync: true, handleAs: "json", headers: { "Content-Type": "application/json"}, putData: json.toJson(values), load: function(x) {that.success = true; }, @@ -280,15 +291,24 @@ define(["dojo/_base/xhr", }); form.connectChildren(true); setAttributesDialog.startup(); - setAttributesDialog.on("show", function(){ - var data = geometry.position(dialogContentArea); - var maxHeight = win.getBox().h * 0.6; - if (data.h > maxHeight) - { - dialogContentArea.style.height = maxHeight + "px"; - dialogContentArea.style.overflow= "auto"; - } - }) + var formWidgets = form.getDescendants(); + var aproximateHeight = 0; + for(var i in formWidgets){ + var widget = formWidgets[i]; + var propName = widget.name; + if (propName) { + if ((widget instanceof dijit.form.CheckBox || widget instanceof dijit.form.RadioButton)) { + widget.initialValue = widget.checked; + } else { + widget.initialValue = widget.value; + } + aproximateHeight += 30; + } + } + var viewport = win.getBox(); + var maxHeight = Math.max(Math.floor(viewport.h * 0.6), 100); + dialogContentArea.style.overflow= "auto"; + dialogContentArea.style.height = Math.min(aproximateHeight, maxHeight ) + "px"; setAttributesDialog.show(); }; diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AccessControlProvider.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AccessControlProvider.js new file mode 100644 index 0000000000..fd8a3ecb0e --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AccessControlProvider.js @@ -0,0 +1,131 @@ +/* + * + * 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", + "dijit/registry", + "dojo/_base/event", + "dojox/grid/enhanced/plugins/Pagination", + "dojox/grid/enhanced/plugins/IndirectSelection", + "dojo/domReady!"], + function (xhr, parser, query, connect, properties, updater, util, UpdatableStore, EnhancedGrid, registry, event) { + + function AccessControlProvider(name, parent, controller) { + this.name = name; + this.controller = controller; + this.modelObj = { type: "accesscontrolprovider", name: name }; + if(parent) { + this.modelObj.parent = {}; + this.modelObj.parent[ parent.type] = parent; + } + } + + AccessControlProvider.prototype.getTitle = function() { + return "AccessControlProvider: " + this.name ; + }; + + AccessControlProvider.prototype.open = function(contentPane) { + var that = this; + this.contentPane = contentPane; + xhr.get({url: "showAccessControlProvider.html", + sync: true, + load: function(data) { + contentPane.containerNode.innerHTML = data; + parser.parse(contentPane.containerNode); + + that.accessControlProviderUpdater = new AccessControlProviderUpdater(contentPane.containerNode, that.modelObj, that.controller); + + var deleteButton = query(".deleteAccessControlProviderButton", contentPane.containerNode)[0]; + var deleteWidget = registry.byNode(deleteButton); + connect.connect(deleteWidget, "onClick", + function(evt){ + event.stop(evt); + that.deleteAccessControlProvider(); + }); + }}); + }; + + AccessControlProvider.prototype.close = function() { + if (this.accessControlProviderUpdater.details) + { + this.accessControlProviderUpdater.details.close(); + } + }; + + AccessControlProvider.prototype.deleteAccessControlProvider = function() { + if(confirm("Are you sure you want to delete access control provider '" + this.name + "'?")) { + var query = "rest/accesscontrolprovider/" +encodeURIComponent(this.name); + this.success = true + var that = this; + xhr.del({url: query, sync: true, handleAs: "json"}).then( + function(data) { + that.close(); + 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); + } + } + }; + + function AccessControlProviderUpdater(node, groupProviderObj, controller) + { + this.controller = controller; + this.name = query(".name", node)[0]; + this.type = query(".type", node)[0]; + this.query = "rest/accesscontrolprovider/"+encodeURIComponent(groupProviderObj.name); + + var that = this; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) + .then(function(data) + { + that.accessControlProviderData = data[0]; + + util.flattenStatistics( that.accessControlProviderData ); + + that.updateHeader(); + + var ui = that.accessControlProviderData.type; + require(["qpid/management/accesscontrolprovider/"+ ui], + function(SpecificProvider) { + that.details = new SpecificProvider(query(".providerDetails", node)[0], groupProviderObj, controller); + }); + }); + } + + AccessControlProviderUpdater.prototype.updateHeader = function() + { + this.name.innerHTML = this.accessControlProviderData[ "name" ]; + this.type.innerHTML = this.accessControlProviderData[ "type" ]; + }; + + return AccessControlProvider; + }); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js index 04ac80784a..a27e43d1b1 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js @@ -31,6 +31,9 @@ define(["dojo/_base/xhr", "qpid/management/addAuthenticationProvider", "qpid/management/addVirtualHost", "qpid/management/addPort", + "qpid/management/addKeystore", + "qpid/management/addGroupProvider", + "qpid/management/addAccessControlProvider", "dojox/grid/enhanced/plugins/Pagination", "dojox/grid/enhanced/plugins/IndirectSelection", "dijit/layout/AccordionContainer", @@ -41,7 +44,7 @@ define(["dojo/_base/xhr", "dijit/form/CheckBox", "dojo/store/Memory", "dojo/domReady!"], - function (xhr, parser, query, connect, properties, updater, util, UpdatableStore, EnhancedGrid, registry, addAuthenticationProvider, addVirtualHost, addPort) { + function (xhr, parser, query, connect, properties, updater, util, UpdatableStore, EnhancedGrid, registry, addAuthenticationProvider, addVirtualHost, addPort, addKeystore, addGroupProvider, addAccessControlProvider) { function Broker(name, parent, controller) { this.name = name; @@ -57,29 +60,10 @@ define(["dojo/_base/xhr", return new dijit.form.ValidationTextBox({ required: true, value: brokerData.name, - disabled: true, label: "Name*:", name: "name"}) } }, { - name: "defaultAuthenticationProvider", - createWidget: function(brokerData) { - var providers = brokerData.authenticationproviders; - var data = []; - if (providers) { - for (var i=0; i< providers.length; i++) { - data.push({id: providers[i].name, name: providers[i].name}); - } - } - var providersStore = new dojo.store.Memory({ data: data }); - return new dijit.form.FilteringSelect({ - required: true, - store: providersStore, - value: brokerData.defaultAuthenticationProvider, - label: "Default Authentication Provider*:", - name: "defaultAuthenticationProvider"}) - } - }, { name: "defaultVirtualHost", createWidget: function(brokerData) { var hosts = brokerData.virtualhosts; @@ -97,98 +81,6 @@ define(["dojo/_base/xhr", name: "defaultVirtualHost"}) } }, { - name: "aclFile", - createWidget: function(brokerData) { - return new dijit.form.ValidationTextBox({ - required: false, - value: brokerData.aclFile, - label: "ACL file location:", - name: "aclFile"}) - } - }, { - name: "groupFile", - createWidget: function(brokerData) - { - return new dijit.form.ValidationTextBox({ - required: false, - value: brokerData.groupFile, - label: "Group file location:", - name: "groupFile"}); - } - }, { - name: "keyStorePath", - createWidget: function(brokerData) { - return new dijit.form.ValidationTextBox({ - required: false, - value: brokerData.keyStorePath, - label: "Path to keystore:", - name: "keyStorePath"}); - } - }, { - name: "keyStorePassword", - requiredFor: "keyStorePath", - createWidget: function(brokerData) { - return new dijit.form.ValidationTextBox({ - required: false, - label: "Keystore password:", - invalidMessage: "Missed keystore password", - name: "keyStorePassword", - placeholder: brokerData["keyStorePassword"] ? brokerData["keyStorePassword"] : "" - }); - } - }, { - name: "keyStoreCertAlias", - createWidget: function(brokerData) { - return new dijit.form.ValidationTextBox({ - required: false, - value: brokerData.keyStoreCertAlias, - label: "Keystore certificate alias:", - name: "keyStoreCertAlias"}); - } - }, { - name: "trustStorePath", - createWidget: function(brokerData) - { - return new dijit.form.ValidationTextBox({ - required: false, - value: brokerData.trustStorePath, - label: "Path to truststore:", - name: "trustStorePath"}); - } - }, { - name: "trustStorePassword", - requiredFor: "trustStorePath", - createWidget: function(brokerData) { - return new dijit.form.ValidationTextBox({ - required: false, - label: "Truststore password:", - invalidMessage: "Missed trustore password", - name: "trustStorePassword", - placeholder: brokerData["trustStorePassword"] ? brokerData["trustStorePassword"] : "" - }); - } - }, { - name: "peerStorePath", - createWidget: function(brokerData) { - return new dijit.form.ValidationTextBox({ - required: false, - value: brokerData.peerStorePath, - label: "Path to peerstore:", - name: "peerStorePath"}); - } - }, { - name: "peerStorePassword", - requiredFor: "peerStorePath", - createWidget: function(brokerData) { - return new dijit.form.ValidationTextBox({ - required: false, - label: "Peerstore password:", - invalidMessage: "Missed peerstore password", - name: "peerStorePassword", - placeholder: brokerData["peerStorePassword"] ? brokerData["peerStorePassword"] : "" - }); - } - }, { name: "statisticsReportingPeriod", createWidget: function(brokerData) { return new dijit.form.ValidationTextBox({ @@ -305,7 +197,7 @@ define(["dojo/_base/xhr", checked: brokerData["queue.deadLetterQueueEnabled"], value: "true", label: "Dead letter queue enabled:", - name: "queue.deadLetterQueueEnabled", + name: "queue.deadLetterQueueEnabled" }); } }, { @@ -319,7 +211,7 @@ define(["dojo/_base/xhr", value: brokerData["queue.flowControlSizeBytes"], placeholder: "Size in bytes", label: "Flow control threshold (bytes):", - name: "queue.flowControlSizeBytes", + name: "queue.flowControlSizeBytes" }); } }, { @@ -333,7 +225,7 @@ define(["dojo/_base/xhr", value: brokerData["queue.flowResumeSizeBytes"], placeholder: "Size in bytes", label: "Flow resume threshold (bytes):", - name: "queue.flowResumeSizeBytes", + name: "queue.flowResumeSizeBytes" }); } }, { @@ -488,7 +380,10 @@ define(["dojo/_base/xhr", ); var addPortButton = query(".addPort", contentPane.containerNode)[0]; - connect.connect(registry.byNode(addPortButton), "onClick", function(evt){ addPort.show(null, that.brokerUpdater.brokerData.authenticationproviders); }); + connect.connect(registry.byNode(addPortButton), "onClick", function(evt){ + addPort.show(null, that.brokerUpdater.brokerData.authenticationproviders, + that.brokerUpdater.brokerData.keystores, that.brokerUpdater.brokerData.truststores); + }); var deletePort = query(".deletePort", contentPane.containerNode)[0]; connect.connect(registry.byNode(deletePort), "onClick", @@ -512,6 +407,79 @@ define(["dojo/_base/xhr", } ); + var addKeystoreButton = query(".addKeystore", contentPane.containerNode)[0]; + connect.connect(registry.byNode(addKeystoreButton), "onClick", + function(evt){ addKeystore.showKeystoreDialog() }); + + var deleteKeystore = query(".deleteKeystore", contentPane.containerNode)[0]; + connect.connect(registry.byNode(deleteKeystore), "onClick", + function(evt){ + util.deleteGridSelections( + that.brokerUpdater, + that.brokerUpdater.keyStoresGrid.grid, + "rest/keystore", + "Are you sure you want to delete key store"); + } + ); + + var addTruststoreButton = query(".addTruststore", contentPane.containerNode)[0]; + connect.connect(registry.byNode(addTruststoreButton), "onClick", + function(evt){ addKeystore.showTruststoreDialog() }); + + var deleteTruststore = query(".deleteTruststore", contentPane.containerNode)[0]; + connect.connect(registry.byNode(deleteTruststore), "onClick", + function(evt){ + util.deleteGridSelections( + that.brokerUpdater, + that.brokerUpdater.trustStoresGrid.grid, + "rest/truststore", + "Are you sure you want to delete trust store"); + } + ); + + var addGroupProviderButton = query(".addGroupProvider", contentPane.containerNode)[0]; + connect.connect(registry.byNode(addGroupProviderButton), "onClick", + function(evt){addGroupProvider.show();}); + + var deleteGroupProvider = query(".deleteGroupProvider", contentPane.containerNode)[0]; + connect.connect(registry.byNode(deleteGroupProvider), "onClick", + function(evt){ + var warning = ""; + var data = that.brokerUpdater.groupProvidersGrid.grid.selection.getSelected(); + if(data.length && data.length > 0) + { + for(var i = 0; i<data.length; i++) + { + if (data[i].type.indexOf("File") != -1) + { + warning = "NOTE: provider deletion will also remove the group file on disk.\n\n" + break; + } + } + } + + util.deleteGridSelections( + that.brokerUpdater, + that.brokerUpdater.groupProvidersGrid.grid, + "rest/groupprovider", + warning + "Are you sure you want to delete group provider"); + } + ); + + var addAccessControlButton = query(".addAccessControlProvider", contentPane.containerNode)[0]; + connect.connect(registry.byNode(addAccessControlButton), "onClick", + function(evt){addAccessControlProvider.show();}); + + var deleteAccessControlProviderButton = query(".deleteAccessControlProvider", contentPane.containerNode)[0]; + connect.connect(registry.byNode(deleteAccessControlProviderButton), "onClick", + function(evt){ + util.deleteGridSelections( + that.brokerUpdater, + that.brokerUpdater.accessControlProvidersGrid.grid, + "rest/accesscontrolprovider", + "Are you sure you want to delete access control provider"); + } + ); }}); }; @@ -524,6 +492,7 @@ define(["dojo/_base/xhr", this.controller = controller; this.query = "rest/broker"; this.attributes = attributes; + this.accessControlProvidersWarn = query(".broker-access-control-providers-warning", node)[0] var that = this; xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) @@ -570,7 +539,7 @@ define(["dojo/_base/xhr", new UpdatableStore(that.brokerData.ports, query(".broker-ports")[0], [ { name: "Name", field: "name", width: "150px"}, { name: "State", field: "state", width: "60px"}, - { name: "Authentication", field: "authenticationProvider", width: "100px"}, + { name: "Auth Provider", field: "authenticationProvider", width: "100px"}, { name: "Address", field: "bindingAddress", width: "70px"}, { name: "Port", field: "port", width: "50px"}, { name: "Transports", field: "transports", width: "100px"}, @@ -581,7 +550,7 @@ define(["dojo/_base/xhr", var idx = evt.rowIndex, theItem = this.getItem(idx); var name = obj.dataStore.getValue(theItem,"name"); - addPort.show(name, that.brokerData.authenticationproviders); + that.controller.show("port", name, brokerObj); }); }, gridProperties, EnhancedGrid); @@ -615,6 +584,71 @@ define(["dojo/_base/xhr", }); }, gridProperties, EnhancedGrid); + that.keyStoresGrid = + new UpdatableStore(that.brokerData.keystores, query(".broker-key-stores")[0], + [ { name: "Name", field: "name", width: "20%"}, + { name: "Path", field: "path", width: "40%"}, + { name: "Type", field: "type", width: "5%"}, + { name: "Key Manager Algorithm", field: "keyManagerFactoryAlgorithm", width: "20%"}, + { name: "Alias", field: "certificateAlias", width: "15%"} + ], function(obj) { + connect.connect(obj.grid, "onRowDblClick", obj.grid, + function(evt){ + var idx = evt.rowIndex, + theItem = this.getItem(idx); + var name = obj.dataStore.getValue(theItem,"name"); + that.controller.show("keystore", name, brokerObj); + }); + }, gridProperties, EnhancedGrid); + + that.trustStoresGrid = + new UpdatableStore(that.brokerData.truststores, query(".broker-trust-stores")[0], + [ { name: "Name", field: "name", width: "20%"}, + { name: "Path", field: "path", width: "40%"}, + { name: "Type", field: "type", width: "5%"}, + { name: "Trust Manager Algorithm", field: "trustManagerFactoryAlgorithm", width: "25%"}, + { name: "Peers only", field: "peersOnly", width: "10%", + formatter: function(val){ + return "<input type='radio' disabled='disabled' "+(val ? "checked='checked'": "")+" />"; + } + } + ], function(obj) { + connect.connect(obj.grid, "onRowDblClick", obj.grid, + function(evt){ + var idx = evt.rowIndex, + theItem = this.getItem(idx); + var name = obj.dataStore.getValue(theItem,"name"); + that.controller.show("truststore", name, brokerObj); + }); + }, gridProperties, EnhancedGrid); + that.groupProvidersGrid = + new UpdatableStore(that.brokerData.groupproviders, query(".broker-group-providers")[0], + [ { name: "Name", field: "name", width: "50%"}, + { name: "Type", field: "type", width: "50%"} + ], function(obj) { + connect.connect(obj.grid, "onRowDblClick", obj.grid, + function(evt){ + var idx = evt.rowIndex, + theItem = this.getItem(idx); + var name = obj.dataStore.getValue(theItem,"name"); + that.controller.show("groupprovider", name, brokerObj); + }); + }, gridProperties, EnhancedGrid); + var aclData = that.brokerData.accesscontrolproviders ? that.brokerData.accesscontrolproviders :[]; + that.accessControlProvidersGrid = + new UpdatableStore(aclData, query(".broker-access-control-providers")[0], + [ { name: "Name", field: "name", width: "60%"}, + { name: "Type", field: "type", width: "40%"} + ], function(obj) { + connect.connect(obj.grid, "onRowDblClick", obj.grid, + function(evt){ + var idx = evt.rowIndex, + theItem = this.getItem(idx); + var name = obj.dataStore.getValue(theItem,"name"); + that.controller.show("accesscontrolprovider", name, brokerObj); + }); + }, gridProperties, EnhancedGrid); + that.displayACLWarnMessage(aclData); }); xhr.get({url: "rest/logrecords", sync: properties.useSyncGet, handleAs: "json"}) @@ -659,6 +693,7 @@ define(["dojo/_base/xhr", { this.showReadOnlyAttributes(); var brokerData = this.brokerData; + window.document.title = "Qpid: " + brokerData.name + " Management"; for(var i in this.attributes) { var propertyName = this.attributes[i].name; @@ -682,6 +717,43 @@ define(["dojo/_base/xhr", } }; + BrokerUpdater.prototype.displayACLWarnMessage = function(aclProviderData) + { + var message = ""; + if (aclProviderData.length > 1) + { + var aclProviders = {}; + var theSameTypeFound = false; + for(var d=0; d<aclProviderData.length; d++) + { + var acl = aclProviderData[d]; + var aclType = acl.type; + if (aclProviders[aclType]) + { + aclProviders[aclType].push(acl.name); + theSameTypeFound = true; + } + else + { + aclProviders[aclType] = [acl.name]; + } + } + + if (theSameTypeFound) + { + message = "Only one instance of a given type will be used. Please remove an instance of type(s):"; + for(var aclType in aclProviders) + { + if(aclProviders[aclType].length>1) + { + message += " " + aclType; + } + } + } + } + this.accessControlProvidersWarn.innerHTML = message; + } + BrokerUpdater.prototype.update = function() { @@ -699,6 +771,25 @@ define(["dojo/_base/xhr", that.portsGrid.update(that.brokerData.ports); that.authenticationProvidersGrid.update(that.brokerData.authenticationproviders); + + if (that.keyStoresGrid) + { + that.keyStoresGrid.update(that.brokerData.keystores); + } + if (that.trustStoresGrid) + { + that.trustStoresGrid.update(that.brokerData.truststores); + } + if (that.groupProvidersGrid) + { + that.groupProvidersGrid.update(that.brokerData.groupproviders); + } + if (that.accessControlProvidersGrid) + { + var data = that.brokerData.accesscontrolproviders ? that.brokerData.accesscontrolproviders :[]; + that.accessControlProvidersGrid.update(data); + that.displayACLWarnMessage(data); + } }); @@ -718,7 +809,7 @@ define(["dojo/_base/xhr", dojo.byId("brokerAttribute.operatingSystem").innerHTML = brokerData.operatingSystem; dojo.byId("brokerAttribute.platform").innerHTML = brokerData.platform; dojo.byId("brokerAttribute.productVersion").innerHTML = brokerData.productVersion; - dojo.byId("brokerAttribute.modelVersion").innerHTML = brokerData.managementVersion; + dojo.byId("brokerAttribute.modelVersion").innerHTML = brokerData.modelVersion; dojo.byId("brokerAttribute.storeType").innerHTML = brokerData.storeType; dojo.byId("brokerAttribute.storeVersion").innerHTML = brokerData.storeVersion; dojo.byId("brokerAttribute.storePath").innerHTML = brokerData.storePath; diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/GroupProvider.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/GroupProvider.js index 4e05f4b0ea..98e01773ef 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/GroupProvider.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/GroupProvider.js @@ -27,10 +27,12 @@ define(["dojo/_base/xhr", "qpid/common/util", "qpid/common/UpdatableStore", "dojox/grid/EnhancedGrid", + "dijit/registry", + "dojo/_base/event", "dojox/grid/enhanced/plugins/Pagination", "dojox/grid/enhanced/plugins/IndirectSelection", "dojo/domReady!"], - function (xhr, parser, query, connect, properties, updater, util, UpdatableStore, EnhancedGrid) { + function (xhr, parser, query, connect, properties, updater, util, UpdatableStore, EnhancedGrid, registry, event) { function GroupProvider(name, parent, controller) { this.name = name; @@ -43,7 +45,7 @@ define(["dojo/_base/xhr", } GroupProvider.prototype.getTitle = function() { - return "GroupProvider"; + return "GroupProvider: " + this.name ; }; GroupProvider.prototype.open = function(contentPane) { @@ -61,6 +63,13 @@ define(["dojo/_base/xhr", that.groupProviderAdapter.update(); + var deleteButton = query(".deleteGroupProviderButton", contentPane.containerNode)[0]; + var deleteWidget = registry.byNode(deleteButton); + connect.connect(deleteWidget, "onClick", + function(evt){ + event.stop(evt); + that.deleteGroupProvider(); + }); }}); }; @@ -68,12 +77,37 @@ define(["dojo/_base/xhr", updater.remove( this.groupProviderAdapter ); }; + GroupProvider.prototype.deleteGroupProvider = function() { + var warnMessage = ""; + if (this.groupProviderAdapter.groupProviderData && this.groupProviderAdapter.groupProviderData.type.indexOf("File") != -1) + { + warnMessage = "NOTE: provider deletion will also remove the group file on disk.\n\n"; + } + if(confirm(warnMessage + "Are you sure you want to delete group provider '" + this.name + "'?")) { + var query = "rest/groupprovider/" +encodeURIComponent(this.name); + this.success = true + var that = this; + xhr.del({url: query, sync: true, handleAs: "json"}).then( + function(data) { + that.close(); + 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); + } + } + }; + function GroupProviderUpdater(node, groupProviderObj, controller) { this.controller = controller; this.name = query(".name", node)[0]; + this.type = query(".type", node)[0]; this.query = "rest/groupprovider/"+encodeURIComponent(groupProviderObj.name); - + this.typeUI ={"GroupFile": "FileGroupManager"}; var that = this; xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) @@ -85,9 +119,10 @@ define(["dojo/_base/xhr", that.updateHeader(); - require(["qpid/management/groupprovider/"+that.groupProviderData.type], + var ui = that.typeUI[that.groupProviderData.type]; + require(["qpid/management/groupprovider/"+ ui], function(SpecificProvider) { - that.details = new SpecificProvider(node, groupProviderObj, controller); + that.details = new SpecificProvider(query(".providerDetails", node)[0], groupProviderObj, controller); that.details.update(); }); @@ -98,6 +133,7 @@ define(["dojo/_base/xhr", GroupProviderUpdater.prototype.updateHeader = function() { this.name.innerHTML = this.groupProviderData[ "name" ]; + this.type.innerHTML = this.groupProviderData[ "type" ]; }; GroupProviderUpdater.prototype.update = function() diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/KeyStore.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/KeyStore.js new file mode 100644 index 0000000000..9702c6b9f6 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/KeyStore.js @@ -0,0 +1,160 @@ +/* + * + * 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/dom", + "dojo/_base/xhr", + "dojo/parser", + "dojo/query", + "dojo/_base/connect", + "dijit/registry", + "qpid/common/properties", + "qpid/common/updater", + "qpid/common/util", + "qpid/common/formatter", + "qpid/management/addKeystore", + "dojo/domReady!"], + function (dom, xhr, parser, query, connect, registry, properties, updater, util, formatter, addKeystore) { + + function KeyStore(name, parent, controller, objectType) { + this.keyStoreName = name; + this.controller = controller; + this.modelObj = { type: "keystore", name: name }; + if(parent) { + this.modelObj.parent = {}; + this.modelObj.parent[ parent.type] = parent; + } + this.url = "rest/keystore/" + encodeURIComponent(name); + this.dialog = addKeystore.showKeystoreDialog; + } + + KeyStore.prototype.getTitle = function() { + return "KeyStore: " + this.keyStoreName; + }; + + KeyStore.prototype.open = function(contentPane) { + var that = this; + this.contentPane = contentPane; + xhr.get({url: "showKeyStore.html", + sync: true, + load: function(data) { + contentPane.containerNode.innerHTML = data; + parser.parse(contentPane.containerNode); + + that.keyStoreUpdater = new KeyStoreUpdater(contentPane.containerNode, that.modelObj, that.controller, that.url); + + updater.add( that.keyStoreUpdater ); + + that.keyStoreUpdater.update(); + + var deleteKeyStoreButton = query(".deleteKeyStoreButton", contentPane.containerNode)[0]; + var node = registry.byNode(deleteKeyStoreButton); + connect.connect(node, "onClick", + function(evt){ + that.deleteKeyStore(); + }); + + var editKeyStoreButton = query(".editKeyStoreButton", contentPane.containerNode)[0]; + var node = registry.byNode(editKeyStoreButton); + connect.connect(node, "onClick", + function(evt){ + that.dialog(that.keyStoreUpdater.keyStoreData) + }); + }}); + }; + + KeyStore.prototype.close = function() { + updater.remove( this.keyStoreUpdater ); + }; + + function KeyStoreUpdater(containerNode, keyStoreObj, controller, url) + { + var that = this; + + function findNode(name) { + return query("." + name + "Value", containerNode)[0]; + } + + function storeNodes(names) + { + for(var i = 0; i < names.length; i++) { + that[names[i]] = findNode(names[i]); + } + } + + storeNodes(["name", + "path", + "type", + "keyManagerFactoryAlgorithm", + "certificateAlias", + "peersOnly" + ]); + + this.query = url; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data) + { + that.keyStoreData = data[0]; + that.updateHeader(); + }); + + } + + KeyStoreUpdater.prototype.updateHeader = function() + { + this.name.innerHTML = this.keyStoreData[ "name" ]; + this.path.innerHTML = this.keyStoreData[ "path" ]; + this.type.innerHTML = this.keyStoreData[ "type" ]; + this.keyManagerFactoryAlgorithm.innerHTML = this.keyStoreData[ "keyManagerFactoryAlgorithm" ]; + this.certificateAlias.innerHTML = this.keyStoreData[ "certificateAlias" ] ? this.keyStoreData[ "certificateAlias" ] : ""; + }; + + KeyStoreUpdater.prototype.update = function() + { + + var thisObj = this; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data) + { + thisObj.keyStoreData = data[0]; + thisObj.updateHeader(); + }); + }; + + KeyStore.prototype.deleteKeyStore = function() { + if(confirm("Are you sure you want to delete key store '" +this.keyStoreName+"'?")) { + var query = this.url; + 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(); + that.close(); + }, + function(error) {that.success = false; that.failureReason = error;}); + if(!this.success ) { + alert("Error:" + this.failureReason); + } + } + } + + return KeyStore; + }); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Port.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Port.js new file mode 100644 index 0000000000..d1ba4043c2 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Port.js @@ -0,0 +1,205 @@ +/* + * + * 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/dom", + "dojo/_base/xhr", + "dojo/parser", + "dojo/query", + "dojo/_base/connect", + "dijit/registry", + "qpid/common/properties", + "qpid/common/updater", + "qpid/common/util", + "qpid/common/formatter", + "qpid/management/addPort", + "dojo/domReady!"], + function (dom, xhr, parser, query, connect, registry, properties, updater, util, formatter, addPort) { + + function Port(name, parent, controller) { + this.name = name; + this.controller = controller; + this.modelObj = { type: "port", name: name }; + if(parent) { + this.modelObj.parent = {}; + this.modelObj.parent[ parent.type] = parent; + } + } + + Port.prototype.getTitle = function() { + return "Port: " + this.name; + }; + + Port.prototype.open = function(contentPane) { + var that = this; + this.contentPane = contentPane; + xhr.get({url: "showPort.html", + sync: true, + load: function(data) { + contentPane.containerNode.innerHTML = data; + parser.parse(contentPane.containerNode); + + that.portUpdater = new PortUpdater(contentPane.containerNode, that.modelObj, that.controller, "rest/port/" + encodeURIComponent(that.name)); + + updater.add( that.portUpdater ); + + that.portUpdater.update(); + + var deletePortButton = query(".deletePortButton", contentPane.containerNode)[0]; + var node = registry.byNode(deletePortButton); + connect.connect(node, "onClick", + function(evt){ + that.deletePort(); + }); + + var editPortButton = query(".editPortButton", contentPane.containerNode)[0]; + var node = registry.byNode(editPortButton); + connect.connect(node, "onClick", + function(evt){ + that.showEditDialog(); + }); + }}); + }; + + Port.prototype.close = function() { + updater.remove( this.portUpdater ); + }; + + + Port.prototype.deletePort = function() { + if(confirm("Are you sure you want to delete port '" +this.name+"'?")) { + var query = "rest/port/" + 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(); + that.close(); + }, + function(error) {that.success = false; that.failureReason = error;}); + if(!this.success ) { + alert("Error:" + this.failureReason); + } + } + } + + Port.prototype.showEditDialog = function() { + var that = this; + xhr.get({url: "rest/broker", sync: properties.useSyncGet, handleAs: "json"}) + .then(function(data) + { + var brokerData= data[0]; + addPort.show(that.name, brokerData.authenticationproviders, brokerData.keystores, brokerData.truststores); + } + ); + } + + function PortUpdater(containerNode, portObj, controller, url) + { + 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(["nameValue", + "portValue", + "authenticationProviderValue", + "protocolsValue", + "transportsValue", + "bindingAddressValue", + "keyStoreValue", + "needClientAuthValue", + "wantClientAuthValue", + "trustStoresValue", + "bindingAddress", + "keyStore", + "needClientAuth", + "wantClientAuth", + "trustStores" + ]); + + this.query = url; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data) + { + that.keyStoreData = data[0]; + that.updateHeader(); + }); + + } + + PortUpdater.prototype.updateHeader = function() + { + function printArray(fieldName, object) + { + var array = object[fieldName]; + var data = "<div>"; + if (array) { + for(var i = 0; i < array.length; i++) { + data+= "<div>" + array[i] + "</div>"; + } + } + return data + "</div>"; + } + + this.nameValue.innerHTML = this.keyStoreData[ "name" ]; + this.portValue.innerHTML = this.keyStoreData[ "port" ]; + this.authenticationProviderValue.innerHTML = this.keyStoreData[ "authenticationProvider" ] ? this.keyStoreData[ "authenticationProvider" ] : ""; + this.protocolsValue.innerHTML = printArray( "protocols", this.keyStoreData); + this.transportsValue.innerHTML = printArray( "transports", this.keyStoreData); + this.bindingAddressValue.innerHTML = this.keyStoreData[ "bindingAddress" ] ? this.keyStoreData[ "bindingAddress" ] : "" ; + this.keyStoreValue.innerHTML = this.keyStoreData[ "keyStore" ] ? this.keyStoreData[ "keyStore" ] : ""; + this.needClientAuthValue.innerHTML = "<input type='checkbox' disabled='disabled' "+(this.keyStoreData[ "needClientAuth" ] ? "checked='checked'": "")+" />" ; + this.wantClientAuthValue.innerHTML = "<input type='checkbox' disabled='disabled' "+(this.keyStoreData[ "wantClientAuth" ] ? "checked='checked'": "")+" />" ; + this.trustStoresValue.innerHTML = printArray( "trustStores", this.keyStoreData); + var amqpProtocol = this.keyStoreData["protocols"][0] && this.keyStoreData["protocols"][0].indexOf("AMQP") == 0; + this.bindingAddress.style.display= amqpProtocol? "block" : "none"; + var sslTransport = this.keyStoreData["transports"][0] && this.keyStoreData["transports"][0] == "SSL"; + var displayStyle = sslTransport ? "block" : "none"; + this.trustStoresValue.style.display = displayStyle; + this.keyStore.style.display = displayStyle; + this.needClientAuth.style.display = displayStyle; + this.wantClientAuth.style.display = displayStyle; + this.trustStores.style.display = displayStyle; + }; + + PortUpdater.prototype.update = function() + { + + var thisObj = this; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data) + { + thisObj.keyStoreData = data[0]; + thisObj.updateHeader(); + }); + }; + + return Port; + }); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/TrustStore.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/TrustStore.js new file mode 100644 index 0000000000..703ef34ec2 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/TrustStore.js @@ -0,0 +1,160 @@ +/* + * + * 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/dom", + "dojo/_base/xhr", + "dojo/parser", + "dojo/query", + "dojo/_base/connect", + "dijit/registry", + "qpid/common/properties", + "qpid/common/updater", + "qpid/common/util", + "qpid/common/formatter", + "qpid/management/addKeystore", + "dojo/domReady!"], + function (dom, xhr, parser, query, connect, registry, properties, updater, util, formatter, addKeystore) { + + function TrustStore(name, parent, controller) { + this.keyStoreName = name; + this.controller = controller; + this.modelObj = { type: "truststore", name: name }; + if(parent) { + this.modelObj.parent = {}; + this.modelObj.parent[ parent.type] = parent; + } + this.url = "rest/truststore/" + encodeURIComponent(name); + this.dialog = addKeystore.showTruststoreDialog; + } + + TrustStore.prototype.getTitle = function() { + return "TrustStore: " + this.keyStoreName; + }; + + TrustStore.prototype.open = function(contentPane) { + var that = this; + this.contentPane = contentPane; + xhr.get({url: "showTrustStore.html", + sync: true, + load: function(data) { + contentPane.containerNode.innerHTML = data; + parser.parse(contentPane.containerNode); + + that.keyStoreUpdater = new KeyStoreUpdater(contentPane.containerNode, that.modelObj, that.controller, that.url); + + updater.add( that.keyStoreUpdater ); + + that.keyStoreUpdater.update(); + + var deleteTrustStoreButton = query(".deleteTrustStoreButton", contentPane.containerNode)[0]; + var node = registry.byNode(deleteTrustStoreButton); + connect.connect(node, "onClick", + function(evt){ + that.deleteKeyStore(); + }); + + var editTrustStoreButton = query(".editTrustStoreButton", contentPane.containerNode)[0]; + var node = registry.byNode(editTrustStoreButton); + connect.connect(node, "onClick", + function(evt){ + that.dialog(that.keyStoreUpdater.keyStoreData) + }); + }}); + }; + + TrustStore.prototype.close = function() { + updater.remove( this.keyStoreUpdater ); + }; + + function KeyStoreUpdater(containerNode, keyStoreObj, controller, url) + { + var that = this; + + function findNode(name) { + return query("." + name + "Value", containerNode)[0]; + } + + function storeNodes(names) + { + for(var i = 0; i < names.length; i++) { + that[names[i]] = findNode(names[i]); + } + } + + storeNodes(["name", + "path", + "type", + "trustManagerFactoryAlgorithm", + "certificateAlias", + "peersOnly" + ]); + + this.query = url; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data) + { + that.keyStoreData = data[0]; + that.updateHeader(); + }); + + } + + KeyStoreUpdater.prototype.updateHeader = function() + { + this.name.innerHTML = this.keyStoreData[ "name" ]; + this.path.innerHTML = this.keyStoreData[ "path" ]; + this.type.innerHTML = this.keyStoreData[ "type" ]; + this.trustManagerFactoryAlgorithm.innerHTML = this.keyStoreData[ "trustManagerFactoryAlgorithm" ]; + this.peersOnly.innerHTML = "<input type='checkbox' disabled='disabled' "+(this.keyStoreData[ "peersOnly" ] ? "checked='checked'": "")+" />" ; + }; + + KeyStoreUpdater.prototype.update = function() + { + + var thisObj = this; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data) + { + thisObj.keyStoreData = data[0]; + thisObj.updateHeader(); + }); + }; + + TrustStore.prototype.deleteKeyStore = function() { + if(confirm("Are you sure you want to delete trust store '" +this.keyStoreName+"'?")) { + var query = this.url; + 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(); + that.close(); + }, + function(error) {that.success = false; that.failureReason = error;}); + if(!this.success ) { + alert("Error:" + this.failureReason); + } + } + } + + return TrustStore; + }); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/accesscontrolprovider/AclFile.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/accesscontrolprovider/AclFile.js new file mode 100644 index 0000000000..56f92c2025 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/accesscontrolprovider/AclFile.js @@ -0,0 +1,95 @@ +/* + * + * 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 AclFile(containerNode, aclProviderObj, controller) { + var node = construct.create("div", null, containerNode, "last"); + var that = this; + this.name = aclProviderObj.name; + xhr.get({url: "accesscontrolprovider/showAclFile.html", + sync: true, + load: function(data) { + node.innerHTML = data; + parser.parse(node); + + that.groupDatabaseUpdater= new AclFileUpdater(node, aclProviderObj, controller); + + updater.add( that.groupDatabaseUpdater); + + that.groupDatabaseUpdater.update(); + + + }}); + } + + AclFile.prototype.close = function() { + updater.remove( this.groupDatabaseUpdater ); + }; + + function AclFileUpdater(node, aclProviderObj, controller) + { + this.controller = controller; + this.query = "rest/accesscontrolprovider/"+encodeURIComponent(aclProviderObj.name); + this.name = aclProviderObj.name; + this.path = query(".path", node)[0]; + } + + AclFileUpdater.prototype.update = function() + { + var that = this; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) + .then(function(data) { + if (data[0]) + { + that.aclProviderData = data[0]; + that.path.innerHTML = that.aclProviderData.path; + } + }); + + }; + + return AclFile; + }); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addAccessControlProvider.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addAccessControlProvider.js new file mode 100644 index 0000000000..d20897c1d8 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addAccessControlProvider.js @@ -0,0 +1,177 @@ +/* + * + * 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/lang", + "dojo/_base/xhr", + "dojo/dom", + "dojo/dom-construct", + "dijit/registry", + "dojo/parser", + "dojo/_base/array", + "dojo/_base/event", + 'dojo/_base/json', + "qpid/common/util", + "dojo/store/Memory", + "dojox/validate/us", + "dojox/validate/web", + "dijit/Dialog", + "dijit/form/CheckBox", + "dijit/form/Textarea", + "dijit/form/ComboBox", + "dijit/form/TextBox", + "dijit/form/ValidationTextBox", + "dijit/form/Button", + "dijit/form/Form", + "dijit/layout/ContentPane", + "dojox/layout/TableContainer", + "dojo/domReady!"], + function (lang, xhr, dom, construct, registry, parser, array, event, json, util) { + + var addAccessControlProvider = {}; + + addAccessControlProvider.show = function(accessControlProvider) { + var fields = [{ + name: "name", + createWidget: function(accessControlProvider) { + return new dijit.form.ValidationTextBox({ + required: true, + value: accessControlProvider.name, + disabled: accessControlProvider.name ? true : false, + label: "Name*:", + regexp: "^[\x20-\x2e\x30-\x7F]{1,255}$", + name: "name"}); + } + }, { + name: "type", + createWidget: function(accessControlProvider) { + + var typeContainer = construct.create("div"); + + var typeListContainer = new dojox.layout.TableContainer({ + cols: 1, + "labelWidth": "300", + customClass: "formLabel", + showLabels: true, + orientation: "horiz" + }); + + typeContainer.appendChild(typeListContainer.domNode); + + var providers = []; + var fieldSetContainers = {}; + xhr.get({ + url: "rest/helper?action=ListAccessControlProviderAttributes", + handleAs: "json", + sync: true + }).then( + function(data) { + var providerIndex = 0; + + for (var providerType in data) { + if (data.hasOwnProperty(providerType)) { + providers[providerIndex++] = {id: providerType, name: providerType}; + + var attributes = data[providerType].attributes; + var descriptions = data[providerType].descriptions; + + var layout = new dojox.layout.TableContainer( { + cols: 1, + "labelWidth": "300", + customClass: "formLabel", + showLabels: true, + orientation: "horiz" + }); + + for(var i=0; i < attributes.length; i++) { + if ("type" == attributes[i]) + { + continue; + } + var labelValue = attributes[i]; + if (descriptions && descriptions[attributes[i]]) + { + labelValue = descriptions[attributes[i]]; + } + var text = new dijit.form.TextBox({ + label: labelValue + ":", + name: attributes[i] + }); + layout.addChild(text); + } + + typeContainer.appendChild(layout.domNode); + fieldSetContainers[providerType] = layout; + } + } + }); + + var providersStore = new dojo.store.Memory({ data: providers }); + + var typeList = new dijit.form.FilteringSelect({ + required: true, + value: accessControlProvider.type, + store: providersStore, + label: "Type*:", + name: "type"}); + + typeListContainer.addChild(typeList); + + var onChangeHandler = function onChangeHandler(newValue){ + for (var i in fieldSetContainers) { + var container = fieldSetContainers[i]; + var descendants = container.getChildren(); + for(var i in descendants){ + var descendant = descendants[i]; + var propName = descendant.name; + if (propName) { + descendant.set("disabled", true); + } + } + container.domNode.style.display = "none"; + } + var container = fieldSetContainers[newValue]; + if (container) + { + container.domNode.style.display = "block"; + var descendants = container.getChildren(); + for(var i in descendants){ + var descendant = descendants[i]; + var propName = descendant.name; + if (propName) { + descendant.set("disabled", false); + } + } + } + }; + typeList.on("change", onChangeHandler); + onChangeHandler(typeList.value); + return new dijit.layout.ContentPane({content: typeContainer, style:{padding: 0}}); + } + }]; + + util.showSetAttributesDialog( + fields, + accessControlProvider ? accessControlProvider : {}, + "rest/accesscontrolprovider" + (name ? "/" + encodeURIComponent(name.name) : ""), + accessControlProvider ? "Edit access control provider - " + accessControlProvider.name : "Add access control provider", + accessControlProvider ? false : true); + }; + return addAccessControlProvider; + });
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addAuthenticationProvider.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addAuthenticationProvider.js index decc7fa0b3..d2891c7d3b 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addAuthenticationProvider.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addAuthenticationProvider.js @@ -207,7 +207,7 @@ define(["dojo/_base/xhr", var layout = new dojox.layout.TableContainer( { id: providerType + "FieldSet", cols: 1, - "labelWidth": "300", + "labelWidth": "200", showLabels: true, orientation: "horiz" }); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addGroupProvider.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addGroupProvider.js new file mode 100644 index 0000000000..c8cd33fd6a --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addGroupProvider.js @@ -0,0 +1,177 @@ +/* + * + * 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/lang", + "dojo/_base/xhr", + "dojo/dom", + "dojo/dom-construct", + "dijit/registry", + "dojo/parser", + "dojo/_base/array", + "dojo/_base/event", + 'dojo/_base/json', + "qpid/common/util", + "dojo/store/Memory", + "dojox/validate/us", + "dojox/validate/web", + "dijit/Dialog", + "dijit/form/CheckBox", + "dijit/form/Textarea", + "dijit/form/ComboBox", + "dijit/form/TextBox", + "dijit/form/ValidationTextBox", + "dijit/form/Button", + "dijit/form/Form", + "dijit/layout/ContentPane", + "dojox/layout/TableContainer", + "dojo/domReady!"], + function (lang, xhr, dom, construct, registry, parser, array, event, json, util) { + + var addGroupProvider = {}; + + addGroupProvider.show = function(groupProvider) { + var fields = [{ + name: "name", + createWidget: function(groupProvider) { + return new dijit.form.ValidationTextBox({ + required: true, + value: groupProvider.name, + disabled: groupProvider.name ? true : false, + label: "Name*:", + regexp: "^[\x20-\x2e\x30-\x7F]{1,255}$", + name: "name"}); + } + }, { + name: "type", + createWidget: function(groupProvider) { + + var typeContainer = construct.create("div"); + + var typeListContainer = new dojox.layout.TableContainer({ + cols: 1, + "labelWidth": "300", + customClass: "formLabel", + showLabels: true, + orientation: "horiz" + }); + + typeContainer.appendChild(typeListContainer.domNode); + + var providers = []; + var fieldSetContainers = {}; + xhr.get({ + url: "rest/helper?action=ListGroupProviderAttributes", + handleAs: "json", + sync: true + }).then( + function(data) { + var providerIndex = 0; + + for (var providerType in data) { + if (data.hasOwnProperty(providerType)) { + providers[providerIndex++] = {id: providerType, name: providerType}; + + var attributes = data[providerType].attributes; + var descriptions = data[providerType].descriptions; + + var layout = new dojox.layout.TableContainer( { + cols: 1, + "labelWidth": "300", + customClass: "formLabel", + showLabels: true, + orientation: "horiz" + }); + + for(var i=0; i < attributes.length; i++) { + if ("type" == attributes[i]) + { + continue; + } + var labelValue = attributes[i]; + if (descriptions && descriptions[attributes[i]]) + { + labelValue = descriptions[attributes[i]]; + } + var text = new dijit.form.TextBox({ + label: labelValue + ":", + name: attributes[i] + }); + layout.addChild(text); + } + + typeContainer.appendChild(layout.domNode); + fieldSetContainers[providerType] = layout; + } + } + }); + + var providersStore = new dojo.store.Memory({ data: providers }); + + var typeList = new dijit.form.FilteringSelect({ + required: true, + value: groupProvider.type, + store: providersStore, + label: "Type*:", + name: "type"}); + + typeListContainer.addChild(typeList); + + var onChangeHandler = function onChangeHandler(newValue){ + for (var i in fieldSetContainers) { + var container = fieldSetContainers[i]; + var descendants = container.getChildren(); + for(var i in descendants){ + var descendant = descendants[i]; + var propName = descendant.name; + if (propName) { + descendant.set("disabled", true); + } + } + container.domNode.style.display = "none"; + } + var container = fieldSetContainers[newValue]; + if (container) + { + container.domNode.style.display = "block"; + var descendants = container.getChildren(); + for(var i in descendants){ + var descendant = descendants[i]; + var propName = descendant.name; + if (propName) { + descendant.set("disabled", false); + } + } + } + }; + typeList.on("change", onChangeHandler); + onChangeHandler(typeList.value); + return new dijit.layout.ContentPane({content: typeContainer, style:{padding: 0}}); + } + }]; + + util.showSetAttributesDialog( + fields, + groupProvider ? groupProvider : {}, + "rest/groupprovider" + (name ? "/" + encodeURIComponent(name.name) : ""), + groupProvider ? "Edit group provider - " + groupProvider.name : "Add group provider", + groupProvider ? false : true); + }; + return addGroupProvider; + });
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addKeystore.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addKeystore.js new file mode 100644 index 0000000000..4fdcffb7f1 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addKeystore.js @@ -0,0 +1,164 @@ +/* + * + * 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/lang", + "dojo/_base/xhr", + "dojo/dom", + "dojo/dom-construct", + "dijit/registry", + "dojo/parser", + "dojo/_base/array", + "dojo/_base/event", + 'dojo/_base/json', + "qpid/common/util", + "dojo/store/Memory", + "dojox/validate/us", + "dojox/validate/web", + "dijit/Dialog", + "dijit/form/CheckBox", + "dijit/form/Textarea", + "dijit/form/ComboBox", + "dijit/form/TextBox", + "dijit/form/ValidationTextBox", + "dijit/form/Button", + "dijit/form/Form", + "dijit/TitlePane", + "dojox/layout/TableContainer", + "dojo/domReady!"], + function (lang, xhr, dom, construct, registry, parser, array, event, json, util) { + + var addKeystore = { }; + + addKeystore.createWidgetFactories = function(isKeystore) + { + var fields = [{ + name: "name", + createWidget: function(keystore) { + return new dijit.form.ValidationTextBox({ + required: true, + value: keystore.name, + disabled: keystore.name ? true : false, + label: "Name:", + regexp: "^[\x20-\x2e\x30-\x7F]{1,255}$", + name: "name"}); + } + }, { + name: "path", + createWidget: function(keystore) { + return new dijit.form.ValidationTextBox({ + required: true, + value: keystore.path, + label: "Path to keystore:", + name: "path"}); + } + }, { + name: "password", + requiredFor: "path", + createWidget: function(keystore) { + return new dijit.form.ValidationTextBox({ + required: false, + label: "Keystore password:", + invalidMessage: "Missed keystore password", + name: "password", + placeHolder: keystore["password"] ? keystore["password"] : "" + }); + } + }]; + if (!isKeystore) + { + fields.push({ + name: "peersOnly", + createWidget: function(keystore) { + return new dijit.form.CheckBox({ + required: false, + checked: keystore && keystore.peersOnly, + label: "Peers only:", + name: "peersOnly"}); + } + }); + } + fields.push({ + name: "Options", + createWidget: function(keystore) { + var optionalFieldContainer = new dojox.layout.TableContainer({ + cols: 1, + "labelWidth": "290", + showLabels: true, + orientation: "horiz", + customClass: "formLabel" + }); + if (isKeystore) + { + optionalFieldContainer.addChild(new dijit.form.ValidationTextBox({ + required: false, + value: keystore.certificateAlias, + label: "Keystore certificate alias:", + name: "certificateAlias"})); + optionalFieldContainer.addChild( new dijit.form.ValidationTextBox({ + required: false, + value: keystore.keyManagerFactoryAlgorithm, + label: "Key manager factory algorithm:", + placeHolder: "Use default", + name: "keyManagerFactoryAlgorithm"})); + } + else + { + optionalFieldContainer.addChild( new dijit.form.ValidationTextBox({ + required: false, + value: keystore.trustManagerFactoryAlgorithm, + label: "Trust manager factory algorithm:", + placeHolder: "Use default", + name: "trustManagerFactoryAlgorithm"})); + } + optionalFieldContainer.addChild(new dijit.form.ValidationTextBox({ + required: false, + value: keystore.type, + label: "Key store type:", + placeHolder: "Use default", + name: "type"})); + var panel = new dijit.TitlePane({title: "Optional Attributes", content: optionalFieldContainer.domNode, open: false}); + return panel; + } + }); + return fields; + } + + addKeystore.showKeystoreDialog = function(keystore) { + var keystoreAttributeWidgetFactories = addKeystore.createWidgetFactories(true); + + util.showSetAttributesDialog( + keystoreAttributeWidgetFactories, + keystore ? keystore : {}, + "rest/keystore" + (keystore ? "/" + encodeURIComponent(keystore.name) : ""), + keystore ? "Edit keystore - " + keystore.name : "Add keystore", + keystore ? false : true); + }; + + addKeystore.showTruststoreDialog = function(truststore) { + var truststoreAttributeWidgetFactories = addKeystore.createWidgetFactories(false); + util.showSetAttributesDialog( + truststoreAttributeWidgetFactories, + truststore ? truststore : {}, + "rest/truststore" + (truststore ? "/" + encodeURIComponent(truststore.name) : ""), + truststore ? "Edit truststore - " + truststore.name : "Add truststore", + truststore ? false : true); + }; + return addKeystore; + });
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addPort.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addPort.js index 0c1a188cbf..cf50b39c87 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addPort.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addPort.js @@ -28,6 +28,7 @@ define(["dojo/_base/xhr", "dojo/_base/event", 'dojo/_base/json', "dojo/store/Memory", + "dojo/data/ObjectStore", "dijit/form/FilteringSelect", "dojo/dom-style", "dojo/_base/lang", @@ -50,9 +51,10 @@ define(["dojo/_base/xhr", "dijit/form/Select", "dijit/form/NumberSpinner", /* basic dojox classes */ - "dojox/form/BusyButton", + "dojox/grid/EnhancedGrid", + "dojox/grid/enhanced/plugins/IndirectSelection", "dojo/domReady!"], - function (xhr, dom, construct, win, registry, parser, array, event, json, Memory, FilteringSelect, domStyle, lang) { + function (xhr, dom, construct, win, registry, parser, array, event, json, Memory, ObjectStore, FilteringSelect, domStyle, lang) { var addPort = {}; @@ -110,36 +112,79 @@ define(["dojo/_base/xhr", var type = dijit.byId("formAddPort.type").value; if (type == "AMQP") { + var transportWidget = registry.byId("formAddPort.transports"); var needClientAuth = dijit.byId("formAddPort.needClientAuth"); var wantClientAuth = dijit.byId("formAddPort.wantClientAuth"); - newPort.needClientAuth = needClientAuth.disabled ? false : needClientAuth.checked; - newPort.wantClientAuth = wantClientAuth.disabled ? false : wantClientAuth.checked + var trustStoreWidget = dijit.byId("formAddPort.trustStores"); + + var initialTransport = transportWidget.initialValue; + var currentTransport = transportWidget.value; + if (currentTransport == "SSL") + { + newPort.needClientAuth = needClientAuth.checked; + newPort.wantClientAuth = wantClientAuth.checked + + var items = trustStoreWidget.selection.getSelected(); + var trustStores = []; + if(items.length > 0){ + for(var i in items) + { + var item = items[i]; + trustStores.push(trustStoreWidget.store.getValue(item, "name")); + } + newPort.trustStores = trustStores; + } + else if (trustStoreWidget.initialValue && trustStoreWidget.initialValue.length > 0) + { + newPort.trustStores = null; + } + } + else if (initialTransport && currentTransport != initialTransport) + { + newPort.needClientAuth = false; + newPort.wantClientAuth = false; + newPort.trustStores = null; + } } + return newPort; }; - var toggleCertificateWidgets = function toggleCertificateWidgets(protocolType, transportType) + var toggleSslWidgets = function toggleSslWidgets(protocolType, transportType) { - var clientAuthPanel = registry.byId("formAddPort:fieldsClientAuth"); - var display = clientAuthPanel.domNode.style.display; + var clientAuthPanel = dojo.byId("formAddPort:fieldsClientAuth"); + var display = clientAuthPanel.style.display; if (transportType == "SSL" && protocolType == "AMQP") { - clientAuthPanel.domNode.style.display = "block"; + clientAuthPanel.style.display = "block"; registry.byId("formAddPort.needClientAuth").set("disabled", false); registry.byId("formAddPort.wantClientAuth").set("disabled", false); } else { - clientAuthPanel.domNode.style.display = "none"; + clientAuthPanel.style.display = "none"; registry.byId("formAddPort.needClientAuth").set("disabled", true); registry.byId("formAddPort.wantClientAuth").set("disabled", true); } - if (clientAuthPanel.domNode.style.display != display) + + var transportSSLPanel = registry.byId("formAddPort:fieldsTransportSSL"); + var transportSSLPanelDisplay = transportSSLPanel.domNode.style.display; + if (transportType == "SSL") { - clientAuthPanel.resize(); + transportSSLPanel.domNode.style.display = "block"; + registry.byId("formAddPort.keyStore").set("disabled", false); + } + else + { + transportSSLPanel.domNode.style.display = "none"; + registry.byId("formAddPort.keyStore").set("disabled", true); } - }; + if (transportSSLPanel.domNode.style.display != transportSSLPanelDisplay && transportSSLPanel.domNode.style.display=="block") + { + registry.byId("formAddPort.trustStores").resize(); + } + }; xhr.get({url: "addPort.html", sync: true, @@ -155,7 +200,7 @@ define(["dojo/_base/xhr", registry.byId("formAddPort.transports").on("change", function(newValue){ var protocolType = registry.byId("formAddPort.type").value; - toggleCertificateWidgets(protocolType, newValue); + toggleSslWidgets(protocolType, newValue); }); registry.byId("formAddPort.type").on("change", function(newValue) { @@ -166,8 +211,9 @@ define(["dojo/_base/xhr", registry.byId("formAddPort:fields" + option.value).domNode.style.display = "none"; }); - registry.byId("formAddPort.needClientAuth").set("enabled", ("AMQP" == newValue)); - registry.byId("formAddPort.wantClientAuth").set("enabled", ("AMQP" == newValue)); + var isAMQP = ("AMQP" == newValue); + registry.byId("formAddPort.needClientAuth").set("enabled", isAMQP); + registry.byId("formAddPort.wantClientAuth").set("enabled", isAMQP); registry.byId("formAddPort:fields" + newValue).domNode.style.display = "block"; var defaultsAMQPProtocols = registry.byId("formAddPort.protocolsDefault"); @@ -175,19 +221,95 @@ define(["dojo/_base/xhr", var protocolsWidget = registry.byId("formAddPort.protocols" + newValue); if (protocolsWidget) { - protocolsWidget.set("disabled", ("AMQP" == newValue && defaultsAMQPProtocols.checked)); + protocolsWidget.set("disabled", (isAMQP && defaultsAMQPProtocols.checked)); + } + + var transportWidget = registry.byId("formAddPort.transports"); + var disableTransportWidget = false; + var toggleSsl = true; + var isRMI = (newValue == "JMX" && registry.byId("formAddPort.protocolsJMX").value == "RMI"); + if (isRMI) + { + if (transportWidget.value != "TCP") + { + transportWidget.set("value", "TCP"); + + // changing of transport widget value will cause the call to toggleSslWidgets + toggleSsl = false; + } + disableTransportWidget = true; + } + else if (newValue == "HTTP" && registry.byId("formAddPort.protocolsHTTP").value == "HTTPS") + { + if (transportWidget.value != "SSL") + { + transportWidget.set("value", "SSL"); + + // changing of transport widget value will cause the call to toggleSslWidgets + toggleSsl = false; + } + disableTransportWidget = true; + } + if (toggleSsl) + { + toggleSslWidgets(newValue, transportWidget.value); } - var transport = registry.byId("formAddPort.transports").value; - toggleCertificateWidgets(newValue, transport); + transportWidget.set("disabled", disableTransportWidget); + registry.byId("formAddPort.authenticationProvider").set("disabled", isRMI); + registry.byId("formAddPort:fieldsAuthenticationProvider").domNode.style.display = isRMI? "none" : "block"; }); + theForm = registry.byId("formAddPort"); + var containers = ["formAddPort:fields", "formAddPort:fieldsTransportSSL", "formAddPort:fieldsAMQP", + "formAddPort:fieldsJMX", "formAddPort:fieldsHTTP", "formAddPort:transport", + "formAddPort:fieldsClientAuthCheckboxes", "formAddPort:fieldsAuthenticationProvider"]; + var labelWidthValue = "200"; + for(var i = 0; i < containers.length; i++) + { + var containerId = containers[i]; + var fields = new dojox.layout.TableContainer( { + cols: 1, + labelWidth: labelWidthValue, + showLabels: true, + orientation: "horiz", + customClass: "formLabel" + }, dom.byId(containerId)); + fields.startup(); + } + + registry.byId("formAddPort.protocolsJMX").on("change", function(newValue){ + var isRMI = newValue == "RMI"; + var transportWidget = registry.byId("formAddPort.transports"); + if (isRMI && transportWidget.value != "TCP") + { + transportWidget.set("value", "TCP"); + } + transportWidget.set("disabled", isRMI); + registry.byId("formAddPort:fieldsAuthenticationProvider").domNode.style.display = isRMI? "none" : "block"; + registry.byId("formAddPort.authenticationProvider").set("disabled", isRMI); + }); + + registry.byId("formAddPort.protocolsHTTP").on("change", function(newValue){ + var isHTTPS = newValue == "HTTPS"; + var transportWidget = registry.byId("formAddPort.transports"); + if (isHTTPS && transportWidget.value != "SSL") { + transportWidget.set("value", "SSL"); + } + transportWidget.set("disabled", isHTTPS); + }); + theForm.on("submit", function(e) { event.stop(e); if(theForm.validate()){ var newPort = convertToPort(theForm.getValues()); + if ((newPort.needClientAuth || newPort.wantClientAuth) && (!newPort.hasOwnProperty("trustStores") || newPort.trustStores.length==0)) + { + alert("A trustore must be selected when requesting client certificates."); + return false; + } var that = this; xhr.put({url: "rest/port/"+encodeURIComponent(newPort.name), sync: true, handleAs: "json", @@ -216,52 +338,8 @@ define(["dojo/_base/xhr", }); }}); - addPort.show = function(portName, providers) { + addPort.show = function(portName, providers, keystores, truststores) { - if (!addPort.fields) - { - var labelWidthValue = "300"; - addPort.fields = new dojox.layout.TableContainer( { - cols: 1, - labelWidth: labelWidthValue, - showLabels: true, - orientation: "horiz", - customClass: "formLabel" - }, dom.byId("formAddPort:fields")); - addPort.fields.startup(); - addPort.fieldsClientAuth = new dojox.layout.TableContainer( { - cols: 1, - labelWidth: labelWidthValue, - showLabels: true, - orientation: "horiz", - customClass: "formLabel" - }, dom.byId("formAddPort:fieldsClientAuth")); - addPort.fieldsClientAuth.startup(); - addPort.fieldsAMQP = new dojox.layout.TableContainer( { - cols: 1, - labelWidth: labelWidthValue, - showLabels: true, - orientation: "horiz", - customClass: "formLabel" - }, dom.byId("formAddPort:fieldsAMQP")); - addPort.fieldsAMQP.startup(); - addPort.fieldsJMX = new dojox.layout.TableContainer( { - cols: 1, - labelWidth: labelWidthValue, - showLabels: true, - orientation: "horiz", - customClass: "formLabel" - }, dom.byId("formAddPort:fieldsJMX")); - addPort.fieldsJMX.startup(); - addPort.fieldsHTTP = new dojox.layout.TableContainer( { - cols: 1, - labelWidth: labelWidthValue, - showLabels: true, - orientation: "horiz", - customClass: "formLabel" - }, dom.byId("formAddPort:fieldsHTTP")); - addPort.fieldsHTTP.startup(); - } registry.byId("formAddPort").reset(); dojo.byId("formAddPort.id").value = ""; @@ -278,6 +356,36 @@ define(["dojo/_base/xhr", providerWidget.startup(); } + var keystoreWidget = registry.byId("formAddPort.keyStore"); + if (keystores) + { + var data = []; + for (var i=0; i< keystores.length; i++) + { + data.push( {id: keystores[i].name, name: keystores[i].name} ); + } + var keystoresStore = new Memory({ data: data }); + keystoreWidget.set("store", keystoresStore); + keystoreWidget.startup(); + } + + var truststoreWidget = registry.byId("formAddPort.trustStores"); + if (truststores) + { + var layout = [[{name: "Name", field: "name", width: "80%"}, + {name: "Peers only", field: "peersOnly", width: "20%", + formatter: function(val){ + return "<input type='radio' disabled='disabled' "+(val?"checked='checked'": "")+" />" + } + }]]; + + var mem = new Memory({ data: truststores, idProperty: "id"}); + truststoreWidget.set("store", new ObjectStore({objectStore: mem})); + truststoreWidget.set("structure", layout); + truststoreWidget.rowSelectCell.toggleAllSelection(false); + truststoreWidget.startup(); + } + if (portName) { xhr.get({ @@ -291,11 +399,32 @@ define(["dojo/_base/xhr", nameField.set("disabled", true); dom.byId("formAddPort.id").value=port.id; providerWidget.set("value", port.authenticationProvider ? port.authenticationProvider : ""); + keystoreWidget.set("value", port.keyStore ? port.keyStore : ""); + if (port.trustStores) + { + var items = truststoreWidget.store.objectStore.data; + for (var j=0; j< items.length; j++) + { + var selected = false; + for (var i=0; i< port.trustStores.length; i++) + { + var trustStore = port.trustStores[i]; + if (items[j].name == trustStore) + { + selected = true; + break; + } + } + truststoreWidget.selection.setSelected(j,selected); + } + } + var transportWidget = registry.byId("formAddPort.transports"); transportWidget.set("value", port.transports ? port.transports[0] : ""); registry.byId("formAddPort.port").set("value", port.port); var protocols = port.protocols; var typeWidget = registry.byId("formAddPort.type"); + var store = typeWidget.store; store.data.forEach(function(option){ registry.byId("formAddPort.protocols" + option.value).set("disabled", true); @@ -310,7 +439,7 @@ define(["dojo/_base/xhr", var defaultProtocolsWidget = registry.byId("formAddPort.protocolsDefault"); var addressWidget = registry.byId("formAddPort.bindingAddress"); addressWidget.set("value", port.bindingAddress); - amqpProtocolsWidget.set("disabled", false); + if (protocols) { amqpProtocolsWidget.set("value", protocols) @@ -343,16 +472,24 @@ define(["dojo/_base/xhr", registry.byId("formAddPort:fields" + typeWidget.value).domNode.style.display = "block"; typeWidget.set("disabled", true); - toggleCertificateWidgets(typeWidget.value, transportWidget.value); + keystoreWidget.initialValue = port.keyStore; + truststoreWidget.initialValue = port.trustStores; + transportWidget.initialValue = transportWidget.value; + providerWidget.initialValue = providerWidget.value; + registry.byId("addPort").show(); }); } else { var typeWidget = registry.byId("formAddPort.type"); - typeWidget.set("disabled", false); + if (typeWidget.get("disabled")) + { + typeWidget.set("disabled", false); + } typeWidget.set("value", "AMQP"); - registry.byId("formAddPort.name").set("disabled", false); + var name = registry.byId("formAddPort.name"); + name.set("disabled", false); registry.byId("addPort").show(); } }; diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js index 5d3a666760..cd18a0c4b6 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js @@ -29,15 +29,20 @@ define(["dojo/dom", "qpid/management/AuthenticationProvider", "qpid/management/GroupProvider", "qpid/management/group/Group", + "qpid/management/KeyStore", + "qpid/management/TrustStore", + "qpid/management/AccessControlProvider", + "qpid/management/Port", "dojo/ready", "dojo/domReady!"], - function (dom, registry, ContentPane, Broker, VirtualHost, Exchange, Queue, Connection, AuthProvider, GroupProvider, Group, ready) { + function (dom, registry, ContentPane, Broker, VirtualHost, Exchange, Queue, Connection, AuthProvider, GroupProvider, Group, KeyStore, TrustStore, AccessControlProvider, Port, ready) { var controller = {}; var constructors = { broker: Broker, virtualhost: VirtualHost, exchange: Exchange, queue: Queue, connection: Connection, authenticationprovider: AuthProvider, groupprovider: GroupProvider, - group: Group }; + group: Group, keystore: KeyStore, truststore: TrustStore, + accesscontrolprovider: AccessControlProvider, port: Port}; var tabDiv = dom.byId("managedViews"); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/groupprovider/FileGroupManager.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/groupprovider/FileGroupManager.js index 44fc9702e2..02276b8713 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/groupprovider/FileGroupManager.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/groupprovider/FileGroupManager.js @@ -82,11 +82,12 @@ define(["dojo/_base/xhr", xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) .then(function(data) { + that.path = query(".path", node)[0]; that.groupProviderData = data[0]; util.flattenStatistics( that.groupProviderData ); - var groupDiv = query(".groups")[0]; + var groupDiv = query(".groups", node)[0]; var gridProperties = { height: 400, @@ -109,7 +110,15 @@ define(["dojo/_base/xhr", that.groupsGrid = new UpdatableStore(that.groupProviderData.groups, groupDiv, [ { name: "Group Name", field: "name", width: "100%" } - ], null, gridProperties, EnhancedGrid); + ], function(obj) { + connect.connect(obj.grid, "onRowDblClick", obj.grid, + function(evt){ + var idx = evt.rowIndex, + theItem = this.getItem(idx); + var name = obj.dataStore.getValue(theItem,"name"); + that.controller.show("group", name, groupProviderObj); + }); + }, gridProperties, EnhancedGrid); var addGroupButton = query(".addGroupButton", node)[0]; @@ -167,6 +176,7 @@ define(["dojo/_base/xhr", xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) .then(function(data) { that.groupProviderData = data[0]; + that.path.innerHTML = that.groupProviderData.path; util.flattenStatistics( that.groupProviderData ); that.groupsGrid.update(that.groupProviderData.groups); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js index 59356cfce1..7bb52b938f 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js @@ -277,8 +277,13 @@ define(["dojo/_base/xhr", 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:""}}}); + } else if (details.type == 'keystore') { + controller.show("keystore", details.keystore, {broker: {type:"broker", name:""}}); + } else if (details.type == 'truststore') { + controller.show("truststore", details.truststore, {broker: {type:"broker", name:""}}); + } else if (details.type == 'accesscontrolprovider') { + controller.show("accesscontrolprovider", details.accesscontrolprovider, {broker: {type:"broker", name:""}}); } - }; TreeViewModel.prototype.update = function () { diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/login.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/login.html new file mode 100644 index 0000000000..aaa2855bd2 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/login.html @@ -0,0 +1,120 @@ +<!DOCTYPE HTML> +<!-- + ~ 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. + --> +<html lang="en"> +<head> + <meta charset="utf-8"> + <title>Qpid Management Login</title> + <link rel="stylesheet" href="dojo/dojo/resources/dojo.css"> + <link rel="stylesheet" href="dojo/dijit/themes/claro/claro.css"> + <link rel="stylesheet" href="css/common.css" media="screen"> + <script> + function getContextPath() + { + var contextPath = "/"; + var documentURL = document.URL; + var managementPageStart = documentURL.lastIndexOf("/"); + var firstSlashPos = documentURL.indexOf("/", documentURL.indexOf("//") + 2); + if (managementPageStart > firstSlashPos) + { + contextPath = documentURL.substring(firstSlashPos, managementPageStart); + } + return contextPath; + } + + var dojoConfig = { + tlmSiblingOfDojo:false, + parseOnLoad:true, + async:true, + baseUrl: getContextPath(), + packages:[ + { name:"dojo", location:"dojo/dojo" }, + { name:"dijit", location:"dojo/dijit" }, + { name:"dojox", location:"dojo/dojox" }, + { name:"qpid", location:"js/qpid" } + ] + }; + + </script> + <script src="dojo/dojo/dojo.js"> + </script> + + <script> + require(["dojo/_base/xhr", + "dojo/parser", + "dijit/form/Form", + "dijit/form/Button", + "dijit/form/TextBox", + "dijit/form/ValidationTextBox", + "dijit/layout/BorderContainer", + "dijit/layout/ContentPane", + "dijit/TitlePane", + "dojox/layout/TableContainer", + "dojox/validate/us", + "dojox/validate/web", + "qpid/common/footer"]); + </script> + +</head> +<body class="claro"> + +<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" 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:'center'"> + <div style="width:350px; margin-left: auto; margin-right: auto;"> + <div data-dojo-type="dijit.form.Form" method="POST" id="loginForm"> + <script type="dojo/on" data-dojo-event="submit" data-dojo-args="e"> + e.preventDefault() + if(this.validate()){ + require(["qpid/authorization/sasl"], function(sasl){ + var redirectIfAuthenticated = function redirectIfAuthenticated(){ + sasl.getUser(function(data){ + if(data.user){ + window.location = "index.html"; + } + }); + }; + + sasl.authenticate(dijit.byId("username").value, dijit.byId("password").value, redirectIfAuthenticated); + }); + } + return false; + </script> + <div data-dojo-type="dijit.TitlePane" data-dojo-props="title:'Login', toggleable: false" > + <div class="dijitDialogPaneContentArea"> + <div data-dojo-type="dojox.layout.TableContainer" data-dojo-props="cols:1,labelWidth:'100',showLabels:true,orientation:'horiz',customClass:'formLabel'"> + <div data-dojo-type="dijit.form.ValidationTextBox" id="username" name="username" data-dojo-props="label:'User name:',required:true, intermediateChanges:true"></div> + <div data-dojo-type="dijit.form.ValidationTextBox" type="password" id="password" name="password" data-dojo-props="label:'Password:',required:true, intermediateChanges:true"></div> + </div> + </div> + <div class="dijitDialogPaneActionBar"> + <button data-dojo-type="dijit.form.Button" type="submit" id="loginButton">Login</button> + </div> + </div> + </div> + </div> + </div> + <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'bottom'"> + <div qpid-type="footer"></div> + </div> +</div> + +</body> +</html>
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/showAccessControlProvider.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/showAccessControlProvider.html new file mode 100644 index 0000000000..399425a7de --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/showAccessControlProvider.html @@ -0,0 +1,31 @@ +<!-- + - + - 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="accessControlProvider"> + <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> + <br/> + <div class="providerDetails"></div> + <div class="dijitDialogPaneActionBar"> + <input class="deleteAccessControlProviderButton" type="button" value="Delete Access Control provider" label="Delete Access Control Provider" dojoType="dijit.form.Button" /> + </div> +</div> +<br/>
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/showBroker.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/showBroker.html index 887ca4e736..d9991452af 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/showBroker.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/showBroker.html @@ -53,36 +53,10 @@ <div class="formLabel-labelCell" style="float:left; width: 250px;">Broker store location:</div> <div id="brokerAttribute.storePath" style="float:left;"></div> </div> - <div id="brokerAttribute.defaultAuthenticationProvider.container" style="display: none; clear:both"> - <div class="formLabel-labelCell" style="float:left; width: 250px;">Default authentication provider:</div> - <div id="brokerAttribute.defaultAuthenticationProvider" style="float:left;"></div> - </div> <div id="brokerAttribute.defaultVirtualHost.container" style="display: none; clear:both; clear:both;"> <div class="formLabel-labelCell" style="float:left; width: 250px;">Default virtual host:</div> <div id="brokerAttribute.defaultVirtualHost" style="float:left;"></div> </div> - <div id="brokerAttribute.aclFile.container" style="display: none; clear:both"> - <div class="formLabel-labelCell" style="float:left; width: 250px;">ACL file location:</div> - <div id="brokerAttribute.aclFile" style="float:left;"></div> - </div> - <div id="brokerAttribute.groupFile.container" style="display: none; clear:both"> - <div class="formLabel-labelCell" style="float:left; width: 250px;">Group file location:</div> - <div id="brokerAttribute.groupFile" style="float:left;"></div> - </div> - <div id="brokerAttribute.keyStorePath.container" style="display: none; clear:both"> - <div class="formLabel-labelCell" style="float:left; width: 250px;">Path to keystore:</div> - <div id="brokerAttribute.keyStorePath" style="float:left;"></div><br/> - <div class="formLabel-labelCell" style="float:left; width: 250px;">Keystore certificate alias:</div> - <div id="brokerAttribute.keyStoreCertAlias" style="float:left;"></div> - </div> - <div id="brokerAttribute.trustStorePath.container" style="display: none; clear:both"> - <div class="formLabel-labelCell" style="float:left; width: 250px;">Path to truststore:</div> - <div id="brokerAttribute.trustStorePath" style="float:left;"></div> - </div> - <div id="brokerAttribute.peerStorePath.container" style="display: none; clear:both"> - <div class="formLabel-labelCell" style="float:left; width: 250px;">Path to peerstore:</div> - <div id="brokerAttribute.peerStorePath" style="float:left;"></div> - </div> <div id="brokerAttribute.statisticsReportingPeriod.container" style="display: none; clear:both"> <div class="formLabel-labelCell" style="float:left; width: 250px;">Statistics reporting period:</div> <div id="brokerAttribute.statisticsReportingPeriod" style="float:left;"></div> @@ -191,6 +165,31 @@ <button data-dojo-type="dijit.form.Button" class="deleteAuthenticationProvider">Delete Provider</button> </div> <br/> + <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Key stores'"> + <div class="broker-key-stores"></div> + <button data-dojo-type="dijit.form.Button" class="addKeystore">Add Key Store</button> + <button data-dojo-type="dijit.form.Button" class="deleteKeystore">Delete Key Store</button> + </div> + <br/> + <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Trust stores'"> + <div class="broker-trust-stores"></div> + <button data-dojo-type="dijit.form.Button" class="addTruststore">Add Trust Store</button> + <button data-dojo-type="dijit.form.Button" class="deleteTruststore">Delete Trust Store</button> + </div> + <br/> + <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Group Providers'"> + <div class="broker-group-providers"></div> + <button data-dojo-type="dijit.form.Button" class="addGroupProvider">Add Group Provider</button> + <button data-dojo-type="dijit.form.Button" class="deleteGroupProvider">Delete Group Provider</button> + </div> + <br/> + <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Access Control Providers'"> + <div class="broker-access-control-providers-warning" style="color: red"></div> + <div class="broker-access-control-providers"></div> + <button data-dojo-type="dijit.form.Button" class="addAccessControlProvider">Add Access Control Provider</button> + <button data-dojo-type="dijit.form.Button" class="deleteAccessControlProvider">Delete Access Control Provider</button> + </div> + <br/> <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Log File', open: false"> <div class="broker-logfile"></div> </div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/showGroupProvider.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/showGroupProvider.html index 914857db5c..332c7f5eaa 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/showGroupProvider.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/showGroupProvider.html @@ -22,4 +22,10 @@ <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 + <br/> + <div class="providerDetails"></div> + <div class="dijitDialogPaneActionBar"> + <input class="deleteGroupProviderButton" type="button" value="Delete Group provider" label="Delete Group Provider" dojoType="dijit.form.Button" /> + </div> +</div> +<br/>
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/showKeyStore.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/showKeyStore.html new file mode 100644 index 0000000000..5caee836d3 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/showKeyStore.html @@ -0,0 +1,47 @@ +<!-- + - + - 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="keystore"> + <div class="keyStoreContainer"> + + <div class="formLabel-labelCell" style="float:left; width: 250px;">Name:</div> + <div class="nameValue" style="float:left;"></div><br/> + + <div class="formLabel-labelCell" style="float:left; width: 250px;">Type:</div> + <div class="typeValue" style="float:left;"></div><br/> + + <div class="formLabel-labelCell" style="float:left; width: 250px;">Key Manager Factory Algorithm:</div> + <div class="keyManagerFactoryAlgorithmValue" style="float:left;"></div><br/> + + <div class="formLabel-labelCell" style="float:left; width: 250px;">Path:</div> + <div class="pathValue" style="float:left;"></div><br/> + + <div class="certificateAlias"> + <div class="formLabel-labelCell" style="float:left; width: 250px;">Certificate alias:</div> + <div class="certificateAliasValue" style="float:left;"></div><br> + </div> + + </div> + <br/> + <div class="dijitDialogPaneActionBar"> + <button data-dojo-type="dijit.form.Button" class="editKeyStoreButton" type="button">Edit</button> + <button data-dojo-type="dijit.form.Button" class="deleteKeyStoreButton" type="button">Delete</button> + </div> +</div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/showPort.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/showPort.html new file mode 100644 index 0000000000..f297f2d751 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/showPort.html @@ -0,0 +1,77 @@ +<!-- + - + - 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="port"> + <div class="portContainer"> + + <div class="formLabel-labelCell" style="float:left; width: 250px;">Name:</div> + <div class="nameValue" style="float:left;"></div><br/> + + <div class="formLabel-labelCell" style="float:left; width: 250px;">Port Number:</div> + <div class="portValue" style="float:left;"></div><br/> + + <div class="authenticationProvider" style="clear:both"> + <div class="formLabel-labelCell" style="float:left; width: 250px;">Authentication Provider:</div> + <div class="authenticationProviderValue" style="float:left;"></div><br/> + </div> + + <div class="protocols" style="clear:both"> + <div class="formLabel-labelCell" style="float:left; width: 250px;">Protocols:</div> + <div class="protocolsValue" style="float:left;"></div><br/> + </div> + + <div class="transports" style="clear:both"> + <div class="formLabel-labelCell" style="float:left; width: 250px;">Transports:</div> + <div class="transportsValue" style="float:left;"></div><br/> + </div> + + <div class="bindingAddress" style="clear:both"> + <div class="formLabel-labelCell" style="float:left; width: 250px;">Binding address:</div> + <div class="bindingAddressValue" style="float:left;"></div><br> + </div> + + <div class="keyStore" style="clear:both"> + <div class="formLabel-labelCell" style="float:left; width: 250px;">Key Store:</div> + <div class="keyStoreValue" style="float:left;"></div><br> + </div> + + <div class="needClientAuth" style="clear:both"> + <div class="formLabel-labelCell" style="float:left; width: 250px;">Need SSL Client Certificate:</div> + <div class="needClientAuthValue" style="float:left;"></div><br> + </div> + + <div class="wantClientAuth" style="clear:both"> + <div class="formLabel-labelCell" style="float:left; width: 250px;">Want SSL Client Certificate:</div> + <div class="wantClientAuthValue" style="float:left;"></div><br> + </div> + + <div class="trustStores" style="clear:both"> + <div class="formLabel-labelCell" style="float:left; width: 250px;">Trust Stores:</div> + <div class="trustStoresValue" style="float:left;"></div><br> + </div> + + </div> + <br/> + + <div class="dijitDialogPaneActionBar"> + <button data-dojo-type="dijit.form.Button" class="editPortButton" type="button">Edit</button> + <button data-dojo-type="dijit.form.Button" class="deletePortButton" type="button">Delete</button> + </div> +</div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/showTrustStore.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/showTrustStore.html new file mode 100644 index 0000000000..6f9146fdfe --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/showTrustStore.html @@ -0,0 +1,47 @@ +<!-- + - + - 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="truststore"> + <div class="trustStoreContainer"> + + <div class="formLabel-labelCell" style="float:left; width: 250px;">Name:</div> + <div class="nameValue" style="float:left;"></div><br/> + + <div class="formLabel-labelCell" style="float:left; width: 250px;">Type:</div> + <div class="typeValue" style="float:left;"></div><br/> + + <div class="formLabel-labelCell" style="float:left; width: 250px;">Trust Manager Factory Algorithm:</div> + <div class="trustManagerFactoryAlgorithmValue" style="float:left;"></div><br/> + + <div class="formLabel-labelCell" style="float:left; width: 250px;">Path:</div> + <div class="pathValue" style="float:left;"></div><br/> + + <div class="peersOnly"> + <div class="formLabel-labelCell" style="float:left; width: 250px;">Peer store:</div> + <div class="peersOnlyValue" style="float:left;"></div> + </div> + + </div> + <br/> + <div class="dijitDialogPaneActionBar"> + <button data-dojo-type="dijit.form.Button" class="editTrustStoreButton" type="button">Edit</button> + <button data-dojo-type="dijit.form.Button" class="deleteTrustStoreButton" type="button">Delete</button> + </div> +</div> diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementTest.java new file mode 100644 index 0000000000..55606af117 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementTest.java @@ -0,0 +1,105 @@ +/* + * + * 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 static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.test.utils.QpidTestCase; + +public class HttpManagementTest extends QpidTestCase +{ + private UUID _id; + private Broker _broker; + private HttpManagement _management; + + @Override + public void setUp() throws Exception + { + super.setUp(); + _id = UUID.randomUUID(); + _broker = mock(Broker.class); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(HttpManagement.HTTP_BASIC_AUTHENTICATION_ENABLED, false); + attributes.put(HttpManagement.HTTPS_BASIC_AUTHENTICATION_ENABLED, true); + attributes.put(HttpManagement.HTTP_SASL_AUTHENTICATION_ENABLED, false); + attributes.put(HttpManagement.HTTPS_SASL_AUTHENTICATION_ENABLED, true); + attributes.put(HttpManagement.NAME, getTestName()); + attributes.put(HttpManagement.TIME_OUT, 10000l); + _management = new HttpManagement(_id, _broker, attributes); + } + + public void testGetBroker() + { + assertEquals("Unexpected broker", _broker, _management.getBroker()); + } + + public void testGetSessionTimeout() + { + assertEquals("Unexpected session timeout", 10000l, _management.getSessionTimeout()); + } + + public void testGetName() + { + assertEquals("Unexpected name", getTestName(), _management.getName()); + } + + public void testIsHttpsSaslAuthenticationEnabled() + { + assertEquals("Unexpected value for the https sasl enabled attribute", true, + _management.isHttpsSaslAuthenticationEnabled()); + } + + public void testIsHttpSaslAuthenticationEnabled() + { + assertEquals("Unexpected value for the http sasl enabled attribute", false, _management.isHttpSaslAuthenticationEnabled()); + } + + public void testIsHttpsBasicAuthenticationEnabled() + { + assertEquals("Unexpected value for the https basic authentication enabled attribute", true, + _management.isHttpsBasicAuthenticationEnabled()); + } + + public void testIsHttpBasicAuthenticationEnabled() + { + assertEquals("Unexpected value for the http basic authentication enabled attribute", false, + _management.isHttpBasicAuthenticationEnabled()); + } + + public void testGetSubjectCreator() + { + SocketAddress localAddress = InetSocketAddress.createUnresolved("localhost", 8080); + SubjectCreator subjectCreator = mock(SubjectCreator.class); + when(_broker.getSubjectCreator(localAddress)).thenReturn(subjectCreator); + SubjectCreator httpManagementSubjectCreator = _management.getSubjectCreator(localAddress); + assertEquals("Unexpected subject creator", subjectCreator, httpManagementSubjectCreator); + } + +} diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java index 93f7df8c85..d094134e11 100644 --- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java +++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/JMXManagedObjectRegistry.java @@ -22,14 +22,15 @@ package org.apache.qpid.server.jmx; import org.apache.log4j.Logger; import org.apache.qpid.server.configuration.BrokerProperties; -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.model.Broker; +import org.apache.qpid.server.model.KeyStore; import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.Transport; -import org.apache.qpid.server.security.auth.rmi.RMIPasswordAuthenticator; +import org.apache.qpid.server.security.auth.jmx.JMXPasswordAuthenticator; +import org.apache.qpid.ssl.SSLContextFactory; import javax.management.JMException; import javax.management.MBeanServer; @@ -39,10 +40,8 @@ import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXServiceURL; import javax.management.remote.MBeanServerForwarder; import javax.management.remote.rmi.RMIConnectorServer; +import javax.net.ssl.SSLContext; import javax.rmi.ssl.SslRMIClientSocketFactory; -import javax.rmi.ssl.SslRMIServerSocketFactory; -import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.lang.management.ManagementFactory; import java.net.InetAddress; @@ -57,6 +56,7 @@ import java.rmi.registry.Registry; import java.rmi.server.RMIClientSocketFactory; import java.rmi.server.RMIServerSocketFactory; import java.rmi.server.UnicastRemoteObject; +import java.security.GeneralSecurityException; import java.util.HashMap; /** @@ -122,16 +122,32 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry if (connectorSslEnabled) { - String keyStorePath = System.getProperty("javax.net.ssl.keyStore"); - String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword"); + KeyStore keyStore = _connectorPort.getKeyStore(); - validateKeyStoreProperties(keyStorePath, keyStorePassword); + String keyStorePath = (String) keyStore.getAttribute(KeyStore.PATH); + String keyStorePassword = keyStore.getPassword(); + String keyStoreType = (String) keyStore.getAttribute(KeyStore.TYPE); + String keyManagerFactoryAlgorithm = (String) keyStore.getAttribute(KeyStore.KEY_MANAGER_FACTORY_ALGORITHM); + + SSLContext sslContext; + try + { + sslContext = SSLContextFactory.buildServerContext(keyStorePath, keyStorePassword, keyStoreType, keyManagerFactoryAlgorithm); + } + catch (GeneralSecurityException e) + { + throw new RuntimeException("Unable to create SSLContext for key store", e); + } + catch (IOException e) + { + throw new RuntimeException("Unable to create SSLContext for key store", e); + } CurrentActor.get().message(ManagementConsoleMessages.SSL_KEYSTORE(keyStorePath)); //create the SSL RMI socket factories csf = new SslRMIClientSocketFactory(); - ssf = new SslRMIServerSocketFactory(); + ssf = new QpidSslRMIServerSocketFactory(sslContext); } else { @@ -144,7 +160,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry int jmxPortConnectorServer = _connectorPort.getPort(); //add a JMXAuthenticator implementation the env map to authenticate the RMI based JMX connector server - RMIPasswordAuthenticator rmipa = new RMIPasswordAuthenticator(_broker, new InetSocketAddress(jmxPortConnectorServer)); + JMXPasswordAuthenticator rmipa = new JMXPasswordAuthenticator(_broker, new InetSocketAddress(jmxPortConnectorServer)); HashMap<String,Object> connectorEnv = new HashMap<String,Object>(); connectorEnv.put(JMXConnectorServer.AUTHENTICATOR, rmipa); @@ -262,31 +278,6 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry return rmiRegistry; } - private void validateKeyStoreProperties(String keyStorePath, String keyStorePassword) throws FileNotFoundException - { - if (keyStorePath == null) - { - throw new IllegalConfigurationException("JVM system property 'javax.net.ssl.keyStore' is not set, " - + "unable to start requested SSL protected JMX connector"); - } - if (keyStorePassword == null) - { - throw new IllegalConfigurationException( "JVM system property 'javax.net.ssl.keyStorePassword' is not set, " - + "unable to start requested SSL protected JMX connector"); - } - - File ksf = new File(keyStorePath); - if (!ksf.exists()) - { - throw new FileNotFoundException("Cannot find SSL keystore file for JMX management: " + ksf); - } - if (!ksf.canRead()) - { - throw new FileNotFoundException("Cannot read SSL keystore file for JMX management: " - + ksf + ". Check permissions."); - } - } - @Override public void registerObject(ManagedObject managedObject) throws JMException { diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/QpidSslRMIServerSocketFactory.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/QpidSslRMIServerSocketFactory.java new file mode 100644 index 0000000000..ab114f0888 --- /dev/null +++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/QpidSslRMIServerSocketFactory.java @@ -0,0 +1,105 @@ +/* + * + * 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.jmx; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.rmi.ssl.SslRMIServerSocketFactory; + +public class QpidSslRMIServerSocketFactory extends SslRMIServerSocketFactory +{ + private final SSLContext _sslContext; + + /** + * SslRMIServerSocketFactory which creates the ServerSocket using the + * supplied SSLContext rather than the system default context normally + * used by the superclass, allowing us to use a configuration-specified + * key store. + * + * @param sslContext previously created sslContext using the desired key store. + * @throws NullPointerException if the provided {@link SSLContext} is null. + */ + public QpidSslRMIServerSocketFactory(SSLContext sslContext) throws NullPointerException + { + super(); + + if(sslContext == null) + { + throw new NullPointerException("The provided SSLContext must not be null"); + } + + _sslContext = sslContext; + + //TODO: settings + implementation for SSL client auth, updating equals and hashCode appropriately. + } + + @Override + public ServerSocket createServerSocket(int port) throws IOException + { + final SSLSocketFactory factory = _sslContext.getSocketFactory(); + + return new ServerSocket(port) + { + public Socket accept() throws IOException + { + Socket socket = super.accept(); + + SSLSocket sslSocket = + (SSLSocket) factory.createSocket(socket, + socket.getInetAddress().getHostName(), + socket.getPort(), + true); + sslSocket.setUseClientMode(false); + + return sslSocket; + } + }; + } + + /** + * One QpidSslRMIServerSocketFactory is equal to + * another if their (non-null) SSLContext are equal. + */ + @Override + public boolean equals(Object object) + { + if (!(object instanceof QpidSslRMIServerSocketFactory)) + { + return false; + } + + QpidSslRMIServerSocketFactory that = (QpidSslRMIServerSocketFactory) object; + + return _sslContext.equals(that._sslContext); + } + + @Override + public int hashCode() + { + return _sslContext.hashCode(); + } + +} diff --git a/qpid/java/broker/etc/md5passwd b/qpid/java/broker/etc/md5passwd index 59354a21f5..f7185c0e92 100644 --- a/qpid/java/broker/etc/md5passwd +++ b/qpid/java/broker/etc/md5passwd @@ -20,3 +20,4 @@ guest:CE4DQ6BIb/BVMN9scFyLtA== client:CE4DQ6BIb/BVMN9scFyLtA==
server:CE4DQ6BIb/BVMN9scFyLtA==
admin:ISMvKXpXpadDiUoOSoAfww==
+webadmin:rda7WOE5vhAzJNBNgtj1RQ==
diff --git a/qpid/java/broker/etc/virtualhosts.xml b/qpid/java/broker/etc/virtualhosts.xml deleted file mode 100644 index 0f7cc7866f..0000000000 --- a/qpid/java/broker/etc/virtualhosts.xml +++ /dev/null @@ -1,165 +0,0 @@ -<?xml version="1.0" encoding="ISO-8859-1"?> -<!-- - - - - 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. - - - --> -<virtualhosts> - <default>test</default> - <virtualhost> - <name>localhost</name> - <localhost> - <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> - <!--<class>org.apache.qpid.server.store.derby.DerbyMessageStore</class> - <environment-path>${QPID_WORK}/derbystore</environment-path>--> - </store> - - <housekeeping> - <threadCount>2</threadCount> - <checkPeriod>20000</checkPeriod> - </housekeeping> - - <exchanges> - <exchange> - <type>direct</type> - <name>test.direct</name> - <durable>true</durable> - </exchange> - <exchange> - <type>topic</type> - <name>test.topic</name> - </exchange> - </exchanges> - <queues> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> - <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> - <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> - <!-- 10 mins --> - <maximumMessageCount>50</maximumMessageCount> - <!-- 50 messages --> - - <queue> - <name>queue</name> - </queue> - <queue> - <name>ping</name> - </queue> - <queue> - <name>test-queue</name> - <test-queue> - <exchange>test.direct</exchange> - <durable>true</durable> - </test-queue> - </queue> - <queue> - <name>test-ping</name> - <test-ping> - <exchange>test.direct</exchange> - </test-ping> - </queue> - - </queues> - </localhost> - </virtualhost> - - - <virtualhost> - <name>development</name> - <development> - <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> - <!--<class>org.apache.qpid.server.store.derby.DerbyMessageStore</class> - <environment-path>${QPID_WORK}/derbystore</environment-path>--> - </store> - - <queues> - <minimumAlertRepeatGap>30000</minimumAlertRepeatGap> - <maximumMessageCount>50</maximumMessageCount> - <queue> - <name>queue</name> - <queue> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> - <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> - <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> - <!-- 10 mins --> - </queue> - </queue> - <queue> - <name>ping</name> - <ping> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> - <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> - <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> - <!-- 10 mins --> - </ping> - </queue> - </queues> - </development> - </virtualhost> - <virtualhost> - <name>test</name> - <test> - <store> - <class>org.apache.qpid.server.store.MemoryMessageStore</class> - <!--<class>org.apache.qpid.server.store.derby.DerbyMessageStore</class> - <environment-path>${QPID_WORK}/derbystore</environment-path>--> - </store> - - <queues> - <minimumAlertRepeatGap>30000</minimumAlertRepeatGap> - <maximumMessageCount>50</maximumMessageCount> - <queue> - <name>queue</name> - <queue> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> - <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> - <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> - <!-- 10 mins --> - </queue> - </queue> - <queue> - <name>ping</name> - <ping> - <exchange>amq.direct</exchange> - <maximumQueueDepth>4235264</maximumQueueDepth> - <!-- 4Mb --> - <maximumMessageSize>2117632</maximumMessageSize> - <!-- 2Mb --> - <maximumMessageAge>600000</maximumMessageAge> - <!-- 10 mins --> - </ping> - </queue> - </queues> - </test> - </virtualhost> -</virtualhosts> - - diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java index d1be4213ec..dd0fde5f7a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java @@ -124,7 +124,7 @@ public class Broker } BrokerConfigurationStoreCreator storeCreator = new BrokerConfigurationStoreCreator(); - ConfigurationEntryStore store = storeCreator.createStore(storeLocation, storeType, options.getInitialConfigurationLocation()); + ConfigurationEntryStore store = storeCreator.createStore(storeLocation, storeType, options.getInitialConfigurationLocation(), options.isOverwriteConfigurationStore()); if (options.isManagementMode()) { @@ -134,7 +134,7 @@ public class Broker _applicationRegistry = new ApplicationRegistry(store); try { - _applicationRegistry.initialise(); + _applicationRegistry.initialise(options); } catch(Exception e) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java index 289cfebc29..5c2a8fd090 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java @@ -24,15 +24,19 @@ import java.io.File; import org.apache.qpid.server.configuration.BrokerProperties; import org.apache.qpid.server.configuration.ConfigurationEntryStore; -import org.apache.qpid.server.configuration.store.JsonConfigurationEntryStore; +import org.apache.qpid.server.configuration.store.MemoryConfigurationEntryStore; +import org.apache.qpid.server.util.StringUtil; public class BrokerOptions { + public static final String DEFAULT_INITIAL_CONFIG_NAME = "initial-config.json"; public static final String DEFAULT_STORE_TYPE = "json"; public static final String DEFAULT_CONFIG_NAME_PREFIX = "config"; public static final String DEFAULT_LOG_CONFIG_FILE = "etc/log4j.xml"; public static final String DEFAULT_INITIAL_CONFIG_LOCATION = - BrokerOptions.class.getClassLoader().getResource("initial-store.json").toExternalForm(); + BrokerOptions.class.getClassLoader().getResource(DEFAULT_INITIAL_CONFIG_NAME).toExternalForm(); + public static final String MANAGEMENT_MODE_USER_NAME = "mm_admin"; + private static final int MANAGEMENT_MODE_PASSWORD_LENGTH = 10; private String _logConfigFile; private Integer _logWatchFrequency = 0; @@ -43,17 +47,35 @@ public class BrokerOptions private String _initialConfigurationLocation; private boolean _managementMode; + private boolean _managementModeQuiesceVhosts; private int _managementModeRmiPort; private int _managementModeConnectorPort; private int _managementModeHttpPort; + private String _managementModePassword; private String _workingDir; private boolean _skipLoggingConfiguration; + private boolean _overwriteConfigurationStore; public String getLogConfigFile() { return _logConfigFile; } + public String getManagementModePassword() + { + if(_managementModePassword == null) + { + _managementModePassword = new StringUtil().randomAlphaNumericString(MANAGEMENT_MODE_PASSWORD_LENGTH); + } + + return _managementModePassword; + } + + public void setManagementModePassword(String managementModePassword) + { + _managementModePassword = managementModePassword; + } + public void setLogConfigFile(final String logConfigFile) { _logConfigFile = logConfigFile; @@ -83,6 +105,16 @@ public class BrokerOptions _managementMode = managementMode; } + public boolean isManagementModeQuiesceVirtualHosts() + { + return _managementModeQuiesceVhosts; + } + + public void setManagementModeQuiesceVirtualHosts(boolean managementModeQuiesceVhosts) + { + _managementModeQuiesceVhosts = managementModeQuiesceVhosts; + } + public int getManagementModeRmiPort() { return _managementModeRmiPort; @@ -169,6 +201,24 @@ public class BrokerOptions } /** + * Returns whether the existing broker configuration store should be overwritten with the current + * initial configuration file (see {@link BrokerOptions#getInitialConfigurationLocation()}). + */ + public boolean isOverwriteConfigurationStore() + { + return _overwriteConfigurationStore; + } + + /** + * Sets whether the existing broker configuration store should be overwritten with the current + * initial configuration file (see {@link BrokerOptions#getInitialConfigurationLocation()}). + */ + public void setOverwriteConfigurationStore(boolean overwrite) + { + _overwriteConfigurationStore = overwrite; + } + + /** * Get the broker work directory location. * * Defaults to the location set in the "QPID_WORK" system property if it is set, or the 'work' sub-directory @@ -205,7 +255,7 @@ public class BrokerOptions /** * Get the broker initial JSON configuration location. * - * Defaults to an internal configuration file within the broker jar, which is loaded with the {@link JsonConfigurationEntryStore}. + * Defaults to an internal configuration file within the broker jar. * * @return the previously set configuration location, or the default location if none was set. */ @@ -221,7 +271,7 @@ public class BrokerOptions /** * Set the absolute path or URL to use for the initial JSON configuration, which is loaded with the - * {@link JsonConfigurationEntryStore} in order to initialise any new {@link ConfigurationEntryStore} for the broker. + * {@link MemoryConfigurationEntryStore} in order to initialise any new {@link ConfigurationEntryStore} for the broker. * * Passing null clears any previously set value and returns to the default. */ diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java index dcae3a0eaf..46612613dd 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.server; +import java.io.File; + import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; @@ -30,6 +32,7 @@ import org.apache.commons.cli.PosixParser; import org.apache.log4j.Logger; import org.apache.qpid.common.QpidProperties; import org.apache.qpid.framing.ProtocolVersion; +import org.apache.qpid.server.configuration.store.ConfigurationEntryStoreUtil; /** * Main entry point for AMQPD. @@ -37,7 +40,6 @@ import org.apache.qpid.framing.ProtocolVersion; */ public class Main { - private static final Option OPTION_HELP = new Option("h", "help", false, "print this message"); private static final Option OPTION_VERSION = new Option("v", "version", false, "print the version information and exit"); @@ -46,10 +48,17 @@ public class Main .withDescription("use given configuration store location").withLongOpt("store-path").create("sp"); private static final Option OPTION_CONFIGURATION_STORE_TYPE = OptionBuilder.withArgName("type").hasArg() - .withDescription("use given store type").withLongOpt("store-type").create("st"); + .withDescription("use given broker configuration store type").withLongOpt("store-type").create("st"); private static final Option OPTION_INITIAL_CONFIGURATION_PATH = OptionBuilder.withArgName("path").hasArg() - .withDescription("pass the location of initial JSON config to use when creating a new configuration store").withLongOpt("initial-config-path").create("icp"); + .withDescription("set the location of initial JSON config to use when creating/overwriting a broker configuration store").withLongOpt("initial-config-path").create("icp"); + + private static final Option OPTION_OVERWRITE_CONFIGURATION_STORE = OptionBuilder.withDescription("overwrite the broker configuration store with the current initial configuration") + .withLongOpt("overwrite-store").create("os"); + + private static final Option OPTION_CREATE_INITIAL_CONFIG = OptionBuilder.withArgName("path").hasOptionalArg().withDescription("create a copy of the initial config file, either to an" + + " optionally specified file path, or as " + BrokerOptions.DEFAULT_INITIAL_CONFIG_NAME + " in the current directory") + .withLongOpt("create-initial-config").create("cic"); private static final Option OPTION_LOG_CONFIG_FILE = OptionBuilder.withArgName("file").hasArg() @@ -62,14 +71,18 @@ public class Main .withDescription("monitor the log file configuration file for changes. Units are seconds. " + "Zero means do not check for changes.").withLongOpt("logwatch").create("w"); - private static final Option OPTION_MANAGEMENT_MODE = OptionBuilder.withDescription("start broker in a management mode") + private static final Option OPTION_MANAGEMENT_MODE = OptionBuilder.withDescription("start broker in management mode, disabling the AMQP ports") .withLongOpt("management-mode").create("mm"); - private static final Option OPTION_RMI_PORT = OptionBuilder.withArgName("port").hasArg() - .withDescription("override jmx rmi port in management mode").withLongOpt("jmxregistryport").create("rmi"); - private static final Option OPTION_CONNECTOR_PORT = OptionBuilder.withArgName("port").hasArg() - .withDescription("override jmx connector port in management mode").withLongOpt("jmxconnectorport").create("jmxrmi"); - private static final Option OPTION_HTTP_PORT = OptionBuilder.withArgName("port").hasArg() - .withDescription("override web management port in management mode").withLongOpt("httpport").create("http"); + private static final Option OPTION_MM_QUIESCE_VHOST = OptionBuilder.withDescription("make virtualhosts stay in the quiesced state during management mode.") + .withLongOpt("management-mode-quiesce-virtualhosts").create("mmqv"); + private static final Option OPTION_MM_RMI_PORT = OptionBuilder.withArgName("port").hasArg() + .withDescription("override jmx rmi registry port in management mode").withLongOpt("management-mode-rmi-registry-port").create("mmrmi"); + private static final Option OPTION_MM_CONNECTOR_PORT = OptionBuilder.withArgName("port").hasArg() + .withDescription("override jmx connector port in management mode").withLongOpt("management-mode-jmx-connector-port").create("mmjmx"); + private static final Option OPTION_MM_HTTP_PORT = OptionBuilder.withArgName("port").hasArg() + .withDescription("override http management port in management mode").withLongOpt("management-mode-http-port").create("mmhttp"); + private static final Option OPTION_MM_PASSWORD = OptionBuilder.withArgName("password").hasArg() + .withDescription("Set the password for the management mode user " + BrokerOptions.MANAGEMENT_MODE_USER_NAME).withLongOpt("management-mode-password").create("mmpass"); private static final Options OPTIONS = new Options(); @@ -79,13 +92,17 @@ public class Main OPTIONS.addOption(OPTION_VERSION); OPTIONS.addOption(OPTION_CONFIGURATION_STORE_PATH); OPTIONS.addOption(OPTION_CONFIGURATION_STORE_TYPE); + OPTIONS.addOption(OPTION_OVERWRITE_CONFIGURATION_STORE); + OPTIONS.addOption(OPTION_CREATE_INITIAL_CONFIG); OPTIONS.addOption(OPTION_LOG_CONFIG_FILE); OPTIONS.addOption(OPTION_LOG_WATCH); OPTIONS.addOption(OPTION_INITIAL_CONFIGURATION_PATH); OPTIONS.addOption(OPTION_MANAGEMENT_MODE); - OPTIONS.addOption(OPTION_RMI_PORT); - OPTIONS.addOption(OPTION_CONNECTOR_PORT); - OPTIONS.addOption(OPTION_HTTP_PORT); + OPTIONS.addOption(OPTION_MM_QUIESCE_VHOST); + OPTIONS.addOption(OPTION_MM_RMI_PORT); + OPTIONS.addOption(OPTION_MM_CONNECTOR_PORT); + OPTIONS.addOption(OPTION_MM_HTTP_PORT); + OPTIONS.addOption(OPTION_MM_PASSWORD); } protected CommandLine _commandLine; @@ -140,11 +157,38 @@ public class Main protected void execute() throws Exception { + BrokerOptions options = new BrokerOptions(); + String initialConfigLocation = _commandLine.getOptionValue(OPTION_INITIAL_CONFIGURATION_PATH.getOpt()); + if (initialConfigLocation != null) + { + options.setInitialConfigurationLocation(initialConfigLocation); + } + + //process the remaining options if (_commandLine.hasOption(OPTION_HELP.getOpt())) { final HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("Qpid", OPTIONS, true); } + else if (_commandLine.hasOption(OPTION_CREATE_INITIAL_CONFIG.getOpt())) + { + File destinationFile = null; + + String destinationOption = _commandLine.getOptionValue(OPTION_CREATE_INITIAL_CONFIG.getOpt()); + if (destinationOption != null) + { + destinationFile = new File(destinationOption); + } + else + { + destinationFile = new File(System.getProperty("user.dir"), BrokerOptions.DEFAULT_INITIAL_CONFIG_NAME); + } + + ConfigurationEntryStoreUtil util = new ConfigurationEntryStoreUtil(); + util.copyInitialConfigFile(options.getInitialConfigurationLocation(), destinationFile); + + System.out.println("Initial config written to: " + destinationFile.getAbsolutePath()); + } else if (_commandLine.hasOption(OPTION_VERSION.getOpt())) { final StringBuilder protocol = new StringBuilder("AMQP version(s) [major.minor]: "); @@ -166,12 +210,12 @@ public class Main } else { - BrokerOptions options = new BrokerOptions(); String configurationStore = _commandLine.getOptionValue(OPTION_CONFIGURATION_STORE_PATH.getOpt()); if (configurationStore != null) { options.setConfigurationStoreLocation(configurationStore); } + String configurationStoreType = _commandLine.getOptionValue(OPTION_CONFIGURATION_STORE_TYPE.getOpt()); if (configurationStoreType != null) { @@ -190,27 +234,24 @@ public class Main options.setLogConfigFile(logConfig); } - String initialConfigLocation = _commandLine.getOptionValue(OPTION_INITIAL_CONFIGURATION_PATH.getOpt()); - if (initialConfigLocation != null) - { - options.setInitialConfigurationLocation(initialConfigLocation); - } + boolean overwriteConfigurationStore = _commandLine.hasOption(OPTION_OVERWRITE_CONFIGURATION_STORE.getOpt()); + options.setOverwriteConfigurationStore(overwriteConfigurationStore); - boolean managmentMode = _commandLine.hasOption(OPTION_MANAGEMENT_MODE.getOpt()); - if (managmentMode) + boolean managementMode = _commandLine.hasOption(OPTION_MANAGEMENT_MODE.getOpt()); + if (managementMode) { options.setManagementMode(true); - String rmiPort = _commandLine.getOptionValue(OPTION_RMI_PORT.getOpt()); + String rmiPort = _commandLine.getOptionValue(OPTION_MM_RMI_PORT.getOpt()); if (rmiPort != null) { options.setManagementModeRmiPort(Integer.parseInt(rmiPort)); } - String connectorPort = _commandLine.getOptionValue(OPTION_CONNECTOR_PORT.getOpt()); + String connectorPort = _commandLine.getOptionValue(OPTION_MM_CONNECTOR_PORT.getOpt()); if (connectorPort != null) { options.setManagementModeConnectorPort(Integer.parseInt(connectorPort)); } - String httpPort = _commandLine.getOptionValue(OPTION_HTTP_PORT.getOpt()); + String httpPort = _commandLine.getOptionValue(OPTION_MM_HTTP_PORT.getOpt()); if (httpPort != null) { options.setManagementModeHttpPort(Integer.parseInt(httpPort)); @@ -218,6 +259,12 @@ public class Main boolean quiesceVhosts = _commandLine.hasOption(OPTION_MM_QUIESCE_VHOST.getOpt()); options.setManagementModeQuiesceVirtualHosts(quiesceVhosts); + + String password = _commandLine.getOptionValue(OPTION_MM_PASSWORD.getOpt()); + if (password != null) + { + options.setManagementModePassword(password); + } } setExceptionHandler(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/BrokerConfigurationStoreCreator.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/BrokerConfigurationStoreCreator.java index 2bb63a803b..8ce74ff334 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/BrokerConfigurationStoreCreator.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/BrokerConfigurationStoreCreator.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.configuration; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -58,9 +60,10 @@ public class BrokerConfigurationStoreCreator * @param storeLocation store location * @param storeType store type * @param initialConfigLocation initial store location + * @param overwrite whether to overwrite an existing configuration store with the initial configuration * @throws IllegalConfigurationException if store type is unknown */ - public ConfigurationEntryStore createStore(String storeLocation, String storeType, String initialConfigLocation) + public ConfigurationEntryStore createStore(String storeLocation, String storeType, String initialConfigLocation, boolean overwrite) { ConfigurationEntryStore initialStore = new MemoryConfigurationEntryStore(initialConfigLocation, null); ConfigurationStoreFactory factory = _factories.get(storeType.toLowerCase()); @@ -68,7 +71,11 @@ public class BrokerConfigurationStoreCreator { throw new IllegalConfigurationException("Unknown store type: " + storeType); } - return factory.createStore(storeLocation, initialStore); + return factory.createStore(storeLocation, initialStore, overwrite); } + public Collection<String> getStoreTypes() + { + return Collections.unmodifiableCollection(_factories.keySet()); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/AccessControlProviderRecoverer.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/AccessControlProviderRecoverer.java new file mode 100644 index 0000000000..df80b9fe5f --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/AccessControlProviderRecoverer.java @@ -0,0 +1,54 @@ +/* + * + * 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.configuration.startup; + +import java.util.Map; + +import org.apache.qpid.server.configuration.ConfigurationEntry; +import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; +import org.apache.qpid.server.configuration.RecovererProvider; +import org.apache.qpid.server.model.AccessControlProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.adapter.AccessControlProviderFactory; + +public class AccessControlProviderRecoverer implements ConfiguredObjectRecoverer<AccessControlProvider> +{ + private final AccessControlProviderFactory _accessControlProviderFactory; + + public AccessControlProviderRecoverer(AccessControlProviderFactory authenticationProviderFactory) + { + _accessControlProviderFactory = authenticationProviderFactory; + } + + @Override + public AccessControlProvider create(RecovererProvider recovererProvider, ConfigurationEntry configurationEntry, ConfiguredObject... parents) + { + Broker broker = RecovererHelper.verifyOnlyBrokerIsParent(parents); + Map<String, Object> attributes = configurationEntry.getAttributes(); + AccessControlProvider authenticationProvider = _accessControlProviderFactory.recover( + configurationEntry.getId(), + broker, + attributes); + + return authenticationProvider; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/AuthenticationProviderRecoverer.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/AuthenticationProviderRecoverer.java index 3290c827c9..81aff002d1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/AuthenticationProviderRecoverer.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/AuthenticationProviderRecoverer.java @@ -51,5 +51,4 @@ public class AuthenticationProviderRecoverer implements ConfiguredObjectRecovere return authenticationProvider; } - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java index 0d7be75a0b..35c96bc993 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java @@ -1,23 +1,50 @@ +/* + * + * 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.configuration.startup; +import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; +import java.util.List; import java.util.Map; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.ConfigurationEntry; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.configuration.RecovererProvider; +import org.apache.qpid.server.configuration.store.StoreConfigurationChangeListener; +import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.logging.LogRecorder; import org.apache.qpid.server.logging.RootMessageLogger; import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.ConfiguredObject; -import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.KeyStore; +import org.apache.qpid.server.model.TrustStore; +import org.apache.qpid.server.model.adapter.AccessControlProviderFactory; import org.apache.qpid.server.model.adapter.AuthenticationProviderFactory; import org.apache.qpid.server.model.adapter.BrokerAdapter; +import org.apache.qpid.server.model.adapter.GroupProviderFactory; import org.apache.qpid.server.model.adapter.PortFactory; -import org.apache.qpid.server.configuration.store.StoreConfigurationChangeListener; -import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.stats.StatisticsGatherer; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; @@ -28,20 +55,26 @@ public class BrokerRecoverer implements ConfiguredObjectRecoverer<Broker> private final LogRecorder _logRecorder; private final RootMessageLogger _rootMessageLogger; private final AuthenticationProviderFactory _authenticationProviderFactory; + private final AccessControlProviderFactory _accessControlProviderFactory; private final PortFactory _portFactory; private final TaskExecutor _taskExecutor; + private final BrokerOptions _brokerOptions; + private final GroupProviderFactory _groupProviderFactory; - public BrokerRecoverer(AuthenticationProviderFactory authenticationProviderFactory, PortFactory portFactory, - StatisticsGatherer statisticsGatherer, VirtualHostRegistry virtualHostRegistry, LogRecorder logRecorder, - RootMessageLogger rootMessageLogger, TaskExecutor taskExecutor) + public BrokerRecoverer(AuthenticationProviderFactory authenticationProviderFactory, GroupProviderFactory groupProviderFactory, + AccessControlProviderFactory accessControlProviderFactory, PortFactory portFactory, StatisticsGatherer statisticsGatherer, + VirtualHostRegistry virtualHostRegistry, LogRecorder logRecorder, RootMessageLogger rootMessageLogger, TaskExecutor taskExecutor, BrokerOptions brokerOptions) { + _groupProviderFactory = groupProviderFactory; _portFactory = portFactory; _authenticationProviderFactory = authenticationProviderFactory; + _accessControlProviderFactory = accessControlProviderFactory; _statisticsGatherer = statisticsGatherer; _virtualHostRegistry = virtualHostRegistry; _logRecorder = logRecorder; _rootMessageLogger = rootMessageLogger; _taskExecutor = taskExecutor; + _brokerOptions = brokerOptions; } @Override @@ -49,85 +82,62 @@ public class BrokerRecoverer implements ConfiguredObjectRecoverer<Broker> { StoreConfigurationChangeListener storeChangeListener = new StoreConfigurationChangeListener(entry.getStore()); BrokerAdapter broker = new BrokerAdapter(entry.getId(), entry.getAttributes(), _statisticsGatherer, _virtualHostRegistry, - _logRecorder, _rootMessageLogger, _authenticationProviderFactory, _portFactory, _taskExecutor, entry.getStore()); + _logRecorder, _rootMessageLogger, _authenticationProviderFactory, _groupProviderFactory, _accessControlProviderFactory, + _portFactory, _taskExecutor, entry.getStore(), _brokerOptions); broker.addChangeListener(storeChangeListener); - Map<String, Collection<ConfigurationEntry>> childEntries = entry.getChildren(); - for (String type : childEntries.keySet()) + + //Recover the SSL keystores / truststores first, then others that depend on them + Map<String, Collection<ConfigurationEntry>> childEntries = new HashMap<String, Collection<ConfigurationEntry>>(entry.getChildren()); + Map<String, Collection<ConfigurationEntry>> priorityChildEntries = new HashMap<String, Collection<ConfigurationEntry>>(childEntries); + List<String> types = new ArrayList<String>(childEntries.keySet()); + + for(String type : types) { - ConfiguredObjectRecoverer<?> recoverer = recovererProvider.getRecoverer(type); - if (recoverer == null) + if(KeyStore.class.getSimpleName().equals(type) || TrustStore.class.getSimpleName().equals(type) + || AuthenticationProvider.class.getSimpleName().equals(type)) { - throw new IllegalConfigurationException("Cannot recover entry for the type '" + type + "' from broker"); + childEntries.remove(type); } - Collection<ConfigurationEntry> entries = childEntries.get(type); - for (ConfigurationEntry childEntry : entries) + else { - ConfiguredObject object = recoverer.create(recovererProvider, childEntry, broker); - if (object == null) - { - throw new IllegalConfigurationException("Cannot create configured object for the entry " + childEntry); - } - broker.recoverChild(object); - object.addChangeListener(storeChangeListener); + priorityChildEntries.remove(type); } } - wireUpConfiguredObjects(broker, entry.getAttributes()); - return broker; - } - - private void wireUpConfiguredObjects(BrokerAdapter broker, Map<String, Object> brokerAttributes) - { - AuthenticationProvider defaultAuthenticationProvider = null; - Collection<AuthenticationProvider> authenticationProviders = broker.getAuthenticationProviders(); - int numberOfAuthenticationProviders = authenticationProviders.size(); - if (numberOfAuthenticationProviders == 0) + for (String type : priorityChildEntries.keySet()) { - throw new IllegalConfigurationException("No authentication provider was configured"); + recoverType(recovererProvider, storeChangeListener, broker, priorityChildEntries, type); } - else if (numberOfAuthenticationProviders == 1) + for (String type : childEntries.keySet()) { - defaultAuthenticationProvider = authenticationProviders.iterator().next(); + recoverType(recovererProvider, storeChangeListener, broker, childEntries, type); } - else - { - String name = (String) brokerAttributes.get(Broker.DEFAULT_AUTHENTICATION_PROVIDER); - if (name == null) - { - throw new IllegalConfigurationException("Multiple authentication providers defined, but no default was configured."); - } - defaultAuthenticationProvider = getAuthenticationProviderByName(broker, name); - } - broker.setDefaultAuthenticationProvider(defaultAuthenticationProvider); - - Collection<Port> ports = broker.getPorts(); - for (Port port : ports) - { - String authenticationProviderName = (String) port.getAttribute(Port.AUTHENTICATION_PROVIDER); - AuthenticationProvider provider = null; - if (authenticationProviderName != null) - { - provider = getAuthenticationProviderByName(broker, authenticationProviderName); - } - else - { - provider = defaultAuthenticationProvider; - } - port.setAuthenticationProvider(provider); - } + return broker; } - private AuthenticationProvider getAuthenticationProviderByName(BrokerAdapter broker, String authenticationProviderName) + private void recoverType(RecovererProvider recovererProvider, + StoreConfigurationChangeListener storeChangeListener, + BrokerAdapter broker, + Map<String, Collection<ConfigurationEntry>> childEntries, + String type) { - AuthenticationProvider provider = broker.findAuthenticationProviderByName(authenticationProviderName); - if (provider == null) + ConfiguredObjectRecoverer<?> recoverer = recovererProvider.getRecoverer(type); + if (recoverer == null) { - throw new IllegalConfigurationException("Cannot find the authentication provider with name: " - + authenticationProviderName); + throw new IllegalConfigurationException("Cannot recover entry for the type '" + type + "' from broker"); + } + Collection<ConfigurationEntry> entries = childEntries.get(type); + for (ConfigurationEntry childEntry : entries) + { + ConfiguredObject object = recoverer.create(recovererProvider, childEntry, broker); + if (object == null) + { + throw new IllegalConfigurationException("Cannot create configured object for the entry " + childEntry); + } + broker.recoverChild(object); + object.addChangeListener(storeChangeListener); } - return provider; } - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProvider.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProvider.java index 15cb229d8a..2de454c34f 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProvider.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProvider.java @@ -20,10 +20,12 @@ */ package org.apache.qpid.server.configuration.startup; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; import org.apache.qpid.server.configuration.RecovererProvider; import org.apache.qpid.server.logging.LogRecorder; import org.apache.qpid.server.logging.RootMessageLogger; +import org.apache.qpid.server.model.AccessControlProvider; import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.GroupProvider; @@ -32,9 +34,12 @@ import org.apache.qpid.server.model.Plugin; import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.TrustStore; import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.model.adapter.AccessControlProviderFactory; import org.apache.qpid.server.model.adapter.AuthenticationProviderFactory; +import org.apache.qpid.server.model.adapter.GroupProviderFactory; import org.apache.qpid.server.model.adapter.PortFactory; import org.apache.qpid.server.configuration.updater.TaskExecutor; +import org.apache.qpid.server.plugin.AccessControlFactory; import org.apache.qpid.server.plugin.AuthenticationManagerFactory; import org.apache.qpid.server.plugin.GroupManagerFactory; import org.apache.qpid.server.plugin.PluginFactory; @@ -50,23 +55,27 @@ public class DefaultRecovererProvider implements RecovererProvider private final LogRecorder _logRecorder; private final RootMessageLogger _rootMessageLogger; private final AuthenticationProviderFactory _authenticationProviderFactory; + private final AccessControlProviderFactory _accessControlProviderFactory; private final PortFactory _portFactory; - private final QpidServiceLoader<GroupManagerFactory> _groupManagerServiceLoader; + private final GroupProviderFactory _groupProviderFactory; private final QpidServiceLoader<PluginFactory> _pluginFactoryServiceLoader; private final TaskExecutor _taskExecutor; + private final BrokerOptions _brokerOptions; public DefaultRecovererProvider(StatisticsGatherer brokerStatisticsGatherer, VirtualHostRegistry virtualHostRegistry, - LogRecorder logRecorder, RootMessageLogger rootMessageLogger, TaskExecutor taskExecutor) + LogRecorder logRecorder, RootMessageLogger rootMessageLogger, TaskExecutor taskExecutor, BrokerOptions brokerOptions) { _authenticationProviderFactory = new AuthenticationProviderFactory(new QpidServiceLoader<AuthenticationManagerFactory>()); + _accessControlProviderFactory = new AccessControlProviderFactory(new QpidServiceLoader<AccessControlFactory>()); + _groupProviderFactory = new GroupProviderFactory(new QpidServiceLoader<GroupManagerFactory>()); _portFactory = new PortFactory(); _brokerStatisticsGatherer = brokerStatisticsGatherer; _virtualHostRegistry = virtualHostRegistry; _logRecorder = logRecorder; _rootMessageLogger = rootMessageLogger; - _groupManagerServiceLoader = new QpidServiceLoader<GroupManagerFactory>(); _pluginFactoryServiceLoader = new QpidServiceLoader<PluginFactory>(); _taskExecutor = taskExecutor; + _brokerOptions = brokerOptions; } @Override @@ -74,13 +83,17 @@ public class DefaultRecovererProvider implements RecovererProvider { if (Broker.class.getSimpleName().equals(type)) { - return new BrokerRecoverer(_authenticationProviderFactory, _portFactory, _brokerStatisticsGatherer, _virtualHostRegistry, - _logRecorder, _rootMessageLogger, _taskExecutor); + return new BrokerRecoverer(_authenticationProviderFactory, _groupProviderFactory, _accessControlProviderFactory, _portFactory, _brokerStatisticsGatherer, + _virtualHostRegistry, _logRecorder, _rootMessageLogger, _taskExecutor, _brokerOptions); } else if(VirtualHost.class.getSimpleName().equals(type)) { return new VirtualHostRecoverer(_brokerStatisticsGatherer); } + else if(AccessControlProvider.class.getSimpleName().equals(type)) + { + return new AccessControlProviderRecoverer(_accessControlProviderFactory); + } else if(AuthenticationProvider.class.getSimpleName().equals(type)) { return new AuthenticationProviderRecoverer(_authenticationProviderFactory); @@ -91,7 +104,7 @@ public class DefaultRecovererProvider implements RecovererProvider } else if(GroupProvider.class.getSimpleName().equals(type)) { - return new GroupProviderRecoverer(_groupManagerServiceLoader); + return new GroupProviderRecoverer(_groupProviderFactory); } else if(KeyStore.class.getSimpleName().equals(type)) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/GroupProviderRecoverer.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/GroupProviderRecoverer.java index 275a0c736c..00f23b3c1c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/GroupProviderRecoverer.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/GroupProviderRecoverer.java @@ -24,24 +24,20 @@ import java.util.Map; import org.apache.qpid.server.configuration.ConfigurationEntry; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; -import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.configuration.RecovererProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.GroupProvider; -import org.apache.qpid.server.model.adapter.GroupProviderAdapter; -import org.apache.qpid.server.plugin.GroupManagerFactory; -import org.apache.qpid.server.plugin.QpidServiceLoader; -import org.apache.qpid.server.security.group.GroupManager; +import org.apache.qpid.server.model.adapter.GroupProviderFactory; public class GroupProviderRecoverer implements ConfiguredObjectRecoverer<GroupProvider> { - private QpidServiceLoader<GroupManagerFactory> _groupManagerServiceLoader; + private GroupProviderFactory _groupProviderFactory; - public GroupProviderRecoverer(QpidServiceLoader<GroupManagerFactory> groupManagerServiceLoader) + public GroupProviderRecoverer(GroupProviderFactory groupProviderFactory) { super(); - _groupManagerServiceLoader = groupManagerServiceLoader; + _groupProviderFactory = groupProviderFactory; } @Override @@ -49,26 +45,9 @@ public class GroupProviderRecoverer implements ConfiguredObjectRecoverer<GroupPr { Broker broker = RecovererHelper.verifyOnlyBrokerIsParent(parents); Map<String, Object> attributes = configurationEntry.getAttributes(); - GroupManager groupManager = createGroupManager(attributes); - if (groupManager == null) - { - throw new IllegalConfigurationException("Cannot create GroupManager from attributes : " + attributes); - } - GroupProviderAdapter groupProviderAdapter = new GroupProviderAdapter(configurationEntry.getId(), groupManager, broker); - return groupProviderAdapter; - } - private GroupManager createGroupManager(Map<String, Object> attributes) - { - for(GroupManagerFactory factory : _groupManagerServiceLoader.instancesOf(GroupManagerFactory.class)) - { - GroupManager groupManager = factory.createInstance(attributes); - if (groupManager != null) - { - return groupManager; - } - } - return null; - } + GroupProvider groupProvider = _groupProviderFactory.recover(configurationEntry.getId(), broker, attributes); + return groupProvider; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreUtil.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreUtil.java new file mode 100644 index 0000000000..6d895892b3 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreUtil.java @@ -0,0 +1,91 @@ +/* + * + * 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.configuration.store; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.util.FileUtils; + +public class ConfigurationEntryStoreUtil +{ + public void copyInitialConfigFile(String initialConfigLocation, File destinationFile) + { + URL initialStoreURL = toURL(initialConfigLocation); + InputStream in = null; + try + { + in = initialStoreURL.openStream(); + FileUtils.copy(in, destinationFile); + } + catch (IOException e) + { + throw new IllegalConfigurationException("Cannot create file " + destinationFile + " by copying initial config from " + initialConfigLocation , e); + } + finally + { + if (in != null) + { + try + { + in.close(); + } + catch (IOException e) + { + throw new IllegalConfigurationException("Cannot close initial config input stream: " + initialConfigLocation , e); + } + } + } + } + + public URL toURL(String location) + { + URL url = null; + try + { + url = new URL(location); + } + catch (MalformedURLException e) + { + File locationFile = new File(location); + url = fileToURL(locationFile); + } + return url; + } + + protected URL fileToURL(File storeFile) + { + URL storeURL = null; + try + { + storeURL = storeFile.toURI().toURL(); + } + catch (MalformedURLException e) + { + throw new IllegalConfigurationException("Cannot create URL for file " + storeFile, e); + } + return storeURL; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/JsonConfigurationEntryStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/JsonConfigurationEntryStore.java index 7a1db3d46d..5f7e68b62a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/JsonConfigurationEntryStore.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/JsonConfigurationEntryStore.java @@ -1,9 +1,6 @@ package org.apache.qpid.server.configuration.store; import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -11,7 +8,6 @@ import java.util.UUID; import org.apache.qpid.server.configuration.ConfigurationEntry; import org.apache.qpid.server.configuration.ConfigurationEntryStore; import org.apache.qpid.server.configuration.IllegalConfigurationException; -import org.apache.qpid.util.FileUtils; public class JsonConfigurationEntryStore extends MemoryConfigurationEntryStore { @@ -21,13 +17,32 @@ public class JsonConfigurationEntryStore extends MemoryConfigurationEntryStore public JsonConfigurationEntryStore(String storeLocation, ConfigurationEntryStore initialStore) { + this(storeLocation, initialStore, false); + } + + public JsonConfigurationEntryStore(String storeLocation, ConfigurationEntryStore initialStore, boolean overwrite) + { super(); _storeFile = new File(storeLocation); + + if(_storeFile.isDirectory()) + { + throw new IllegalConfigurationException("A directory exists at the location for the broker configuration store file: " + storeLocation); + } + + if(overwrite && _storeFile.exists()) + { + if(!_storeFile.delete()) + { + throw new RuntimeException("Unable to overwrite existing configuration store file as requested: " + storeLocation); + } + } + if ((!_storeFile.exists() || _storeFile.length() == 0)) { initialiseStore(_storeFile, initialStore); } - load(fileToURL(_storeFile)); + load(getConfigurationEntryStoreUtil().fileToURL(_storeFile)); if(isGeneratedObjectIdDuringLoad()) { saveAsTree(_storeFile); @@ -72,7 +87,6 @@ public class JsonConfigurationEntryStore extends MemoryConfigurationEntryStore return "JsonConfigurationEntryStore [_storeFile=" + _storeFile + ", _rootId=" + getRootEntry().getId() + "]"; } - private void initialiseStore(File storeFile, ConfigurationEntryStore initialStore) { createFileIfNotExist(storeFile); @@ -84,7 +98,7 @@ public class JsonConfigurationEntryStore extends MemoryConfigurationEntryStore { if (initialStore instanceof MemoryConfigurationEntryStore && initialStore.getStoreLocation() != null) { - copyInitialStoreFile(initialStore.getStoreLocation(), storeFile); + getConfigurationEntryStoreUtil().copyInitialConfigFile(initialStore.getStoreLocation(), storeFile); } else { @@ -96,33 +110,4 @@ public class JsonConfigurationEntryStore extends MemoryConfigurationEntryStore } } - private void copyInitialStoreFile(String initialStoreLocation, File storeFile) - { - URL initialStoreURL = toURL(initialStoreLocation); - InputStream in = null; - try - { - in = initialStoreURL.openStream(); - FileUtils.copy(in, storeFile); - } - catch (IOException e) - { - throw new IllegalConfigurationException("Cannot create store file " + storeFile + " by copying initial store from " + initialStoreLocation , e); - } - finally - { - if (in != null) - { - try - { - in.close(); - } - catch (IOException e) - { - throw new IllegalConfigurationException("Cannot close initial store input stream: " + initialStoreLocation , e); - } - } - } - } - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandler.java index 385d2f7327..abf1537ef7 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandler.java @@ -12,6 +12,7 @@ import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.ConfigurationEntry; import org.apache.qpid.server.configuration.ConfigurationEntryStore; import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.model.AccessControlProvider; import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.Protocol; import org.apache.qpid.server.model.State; @@ -25,7 +26,9 @@ public class ManagementModeStoreHandler implements ConfigurationEntryStore private static final String MANAGEMENT_MODE_PORT_PREFIX = "MANAGEMENT-MODE-PORT-"; private static final String PORT_TYPE = Port.class.getSimpleName(); private static final String VIRTUAL_HOST_TYPE = VirtualHost.class.getSimpleName(); + private static final String ACCESS_CONTROL_PROVIDER_TYPE = AccessControlProvider.class.getSimpleName(); private static final String ATTRIBUTE_STATE = VirtualHost.STATE; + private static final Object MANAGEMENT_MODE_AUTH_PROVIDER = "mm-auth"; private final ConfigurationEntryStore _store; private final Map<UUID, ConfigurationEntry> _cliEntries; @@ -208,6 +211,10 @@ public class ManagementModeStoreHandler implements ConfigurationEntryStore attributes.put(Port.PORT, port); attributes.put(Port.PROTOCOLS, Collections.singleton(protocol)); attributes.put(Port.NAME, MANAGEMENT_MODE_PORT_PREFIX + protocol.name()); + if (protocol != Protocol.RMI) + { + attributes.put(Port.AUTHENTICATION_PROVIDER, MANAGEMENT_MODE_AUTH_PROVIDER); + } ConfigurationEntry portEntry = new ConfigurationEntry(UUID.randomUUID(), PORT_TYPE, attributes, Collections.<UUID> emptySet(), this); if (LOGGER.isDebugEnabled()) @@ -244,7 +251,11 @@ public class ManagementModeStoreHandler implements ConfigurationEntryStore String entryType = entry.getType(); Map<String, Object> attributes = entry.getAttributes(); boolean quiesce = false; - if (VIRTUAL_HOST_TYPE.equals(entryType)) + if (VIRTUAL_HOST_TYPE.equals(entryType) && options.isManagementModeQuiesceVirtualHosts()) + { + quiesce = true; + } + else if (ACCESS_CONTROL_PROVIDER_TYPE.equals(entryType)) { quiesce = true; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStore.java index 6931e22d63..6ee26c0726 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStore.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/MemoryConfigurationEntryStore.java @@ -26,7 +26,6 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; @@ -71,6 +70,7 @@ public class MemoryConfigurationEntryStore implements ConfigurationEntryStore private final ObjectMapper _objectMapper; private final Map<UUID, ConfigurationEntry> _entries; private final Map<String, Class<? extends ConfiguredObject>> _relationshipClasses; + private final ConfigurationEntryStoreUtil _util = new ConfigurationEntryStoreUtil(); private String _storeLocation; private UUID _rootId; @@ -119,7 +119,7 @@ public class MemoryConfigurationEntryStore implements ConfigurationEntryStore else { _storeLocation = initialStoreLocation; - load(toURL(_storeLocation)); + load(_util.toURL(_storeLocation)); } } @@ -289,21 +289,6 @@ public class MemoryConfigurationEntryStore implements ConfigurationEntryStore } } - protected URL toURL(String location) - { - URL url = null; - try - { - url = new URL(location); - } - catch (MalformedURLException e) - { - File locationFile = new File(location); - url = fileToURL(locationFile); - } - return url; - } - protected void createFileIfNotExist(File file) { File parent = file.getParentFile(); @@ -349,20 +334,6 @@ public class MemoryConfigurationEntryStore implements ConfigurationEntryStore } } - protected URL fileToURL(File storeFile) - { - URL storeURL = null; - try - { - storeURL = storeFile.toURI().toURL(); - } - catch (MalformedURLException e) - { - throw new IllegalConfigurationException("Cannot create URL for file " + storeFile, e); - } - return storeURL; - } - private void loadFromJson(String json) { ByteArrayInputStream bais = null; @@ -586,14 +557,7 @@ public class MemoryConfigurationEntryStore implements ConfigurationEntryStore UUID id = null; if (idAsString == null) { - if (expectedConfiguredObjectClass == Broker.class) - { - id = UUIDGenerator.generateRandomUUID(); - } - else - { - id = UUIDGenerator.generateBrokerChildUUID(type, name); - } + id = UUIDGenerator.generateRandomUUID(); _generatedObjectIdDuringLoad = true; } @@ -691,4 +655,9 @@ public class MemoryConfigurationEntryStore implements ConfigurationEntryStore { return _generatedObjectIdDuringLoad; } + + protected ConfigurationEntryStoreUtil getConfigurationEntryStoreUtil() + { + return _util; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/factory/JsonConfigurationStoreFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/factory/JsonConfigurationStoreFactory.java index 1a0b514b4c..7c7d3e2071 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/factory/JsonConfigurationStoreFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/factory/JsonConfigurationStoreFactory.java @@ -27,9 +27,9 @@ import org.apache.qpid.server.plugin.ConfigurationStoreFactory; public class JsonConfigurationStoreFactory implements ConfigurationStoreFactory { @Override - public ConfigurationEntryStore createStore(String storeLocation, ConfigurationEntryStore initialStore) + public ConfigurationEntryStore createStore(String storeLocation, ConfigurationEntryStore initialStore, boolean overwrite) { - return new JsonConfigurationEntryStore(storeLocation, initialStore); + return new JsonConfigurationEntryStore(storeLocation, initialStore, overwrite); } @Override diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/factory/MemoryConfigurationStoreFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/factory/MemoryConfigurationStoreFactory.java index fcd6c73170..ed952ff475 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/factory/MemoryConfigurationStoreFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/factory/MemoryConfigurationStoreFactory.java @@ -27,7 +27,7 @@ import org.apache.qpid.server.plugin.ConfigurationStoreFactory; public class MemoryConfigurationStoreFactory implements ConfigurationStoreFactory { @Override - public ConfigurationEntryStore createStore(String storeLocation, ConfigurationEntryStore initialStore) + public ConfigurationEntryStore createStore(String storeLocation, ConfigurationEntryStore initialStore, boolean overwrite) { return new MemoryConfigurationEntryStore(null, initialStore); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java index 246e056f0b..7a3367d215 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.exchange; +import java.util.ArrayList; +import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.binding.Binding; @@ -47,6 +49,7 @@ import java.util.concurrent.atomic.AtomicLong; public abstract class AbstractExchange implements Exchange { + private static final Logger _logger = Logger.getLogger(AbstractExchange.class); private AMQShortString _name; private final AtomicBoolean _closed = new AtomicBoolean(); @@ -295,7 +298,29 @@ public abstract class AbstractExchange implements Exchange { _receivedMessageCount.incrementAndGet(); _receivedMessageSize.addAndGet(message.getSize()); - final List<? extends BaseQueue> queues = doRoute(message); + List<? extends BaseQueue> queues = doRoute(message); + List<? extends BaseQueue> allQueues = queues; + + boolean deletedQueues = false; + + for(BaseQueue q : allQueues) + { + if(q.isDeleted()) + { + if(!deletedQueues) + { + deletedQueues = true; + queues = new ArrayList<BaseQueue>(allQueues); + } + if(_logger.isDebugEnabled()) + { + _logger.debug("Exchange: " + getName() + " - attempt to enqueue message onto deleted queue " + String.valueOf(q.getNameShortString())); + } + queues.remove(q); + } + } + + if(!queues.isEmpty()) { _routedMessageCount.incrementAndGet(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java index 47cacdc176..3d0d9a0f31 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java @@ -14,14 +14,17 @@ * "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. + * under the License. + * * - * */ package org.apache.qpid.server.filter; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.lang.builder.ToStringStyle; import org.apache.log4j.Logger; - import org.apache.qpid.filter.BooleanExpression; import org.apache.qpid.filter.FilterableMessage; import org.apache.qpid.filter.SelectorParsingException; @@ -119,6 +122,34 @@ public class JMSSelectorFilter implements MessageFilter @Override public String toString() { - return "JMSSelector("+_selector+")"; + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append("selector", _selector) + .toString(); } + + @Override + public int hashCode() + { + return new HashCodeBuilder().append(_selector).toHashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + if (obj == this) + { + return true; + } + if (obj.getClass() != getClass()) + { + return false; + } + JMSSelectorFilter rhs = (JMSSelectorFilter) obj; + return new EqualsBuilder().append(_selector, rhs._selector).isEquals(); + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties index 1aa7815c39..76c1fa1b5b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties @@ -45,4 +45,6 @@ STATS_MSGS = BRK-1009 : {0,choice,0#delivered|1#received} : {1,number,#.###} msg PLATFORM = BRK-1010 : Platform : JVM : {0} version: {1} OS : {2} version: {3} arch: {4} # 0 Maximum Memory -MAX_MEMORY = BRK-1011 : Maximum Memory : {0,number} bytes
\ No newline at end of file +MAX_MEMORY = BRK-1011 : Maximum Memory : {0,number} bytes + +MANAGEMENT_MODE = BRK-1012 : Management Mode : User Details : {0} / {1}
\ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/AccessControlProvider.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/AccessControlProvider.java new file mode 100644 index 0000000000..d96bef0463 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/AccessControlProvider.java @@ -0,0 +1,56 @@ +/* + * 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.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import org.apache.qpid.server.security.AccessControl; + +public interface AccessControlProvider extends ConfiguredObject +{ + public static final String ID = "id"; + public static final String DESCRIPTION = "description"; + public static final String NAME = "name"; + public static final String STATE = "state"; + public static final String DURABLE = "durable"; + public static final String LIFETIME_POLICY = "lifetimePolicy"; + public static final String TIME_TO_LIVE = "timeToLive"; + public static final String CREATED = "created"; + public static final String UPDATED = "updated"; + public static final String TYPE = "type"; + + public static final Collection<String> AVAILABLE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList(ID, + NAME, + DESCRIPTION, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + TYPE)); + + //retrieve the underlying AccessControl object + AccessControl getAccessControl(); +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java index ad0e68cee6..f666eb29f1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java @@ -21,11 +21,9 @@ package org.apache.qpid.server.model; import java.net.SocketAddress; -import java.security.AccessControlException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Map; import org.apache.qpid.server.logging.LogRecorder; import org.apache.qpid.server.logging.RootMessageLogger; @@ -43,7 +41,8 @@ public interface Broker extends ConfiguredObject String PLATFORM = "platform"; String PROCESS_PID = "processPid"; String PRODUCT_VERSION = "productVersion"; - String SUPPORTED_STORE_TYPES = "supportedStoreTypes"; + String SUPPORTED_BROKER_STORE_TYPES = "supportedBrokerStoreTypes"; + String SUPPORTED_VIRTUALHOST_STORE_TYPES = "supportedVirtualHostStoreTypes"; String SUPPORTED_AUTHENTICATION_PROVIDERS = "supportedAuthenticationProviders"; String CREATED = "created"; String DURABLE = "durable"; @@ -53,7 +52,6 @@ public interface Broker extends ConfiguredObject String STATE = "state"; String TIME_TO_LIVE = "timeToLive"; String UPDATED = "updated"; - String DEFAULT_AUTHENTICATION_PROVIDER = "defaultAuthenticationProvider"; String DEFAULT_VIRTUAL_HOST = "defaultVirtualHost"; String STATISTICS_REPORTING_PERIOD = "statisticsReportingPeriod"; String STATISTICS_REPORTING_RESET_ENABLED = "statisticsReportingResetEnabled"; @@ -81,30 +79,6 @@ public interface Broker extends ConfiguredObject String VIRTUALHOST_STORE_TRANSACTION_OPEN_TIMEOUT_CLOSE = "virtualhost.storeTransactionOpenTimeoutClose"; String VIRTUALHOST_STORE_TRANSACTION_OPEN_TIMEOUT_WARN = "virtualhost.storeTransactionOpenTimeoutWarn"; - /* - * A temporary attribute to pass the path to ACL file. - * TODO: It should be a part of AuthorizationProvider. - */ - String ACL_FILE = "aclFile"; - - /* - * A temporary attributes to set the broker default key/trust stores. - * TODO: Remove them after adding a full support to configure KeyStore/TrustStore via management layers. - */ - String KEY_STORE_PATH = "keyStorePath"; - String KEY_STORE_PASSWORD = "keyStorePassword"; - String KEY_STORE_CERT_ALIAS = "keyStoreCertAlias"; - String TRUST_STORE_PATH = "trustStorePath"; - String TRUST_STORE_PASSWORD = "trustStorePassword"; - String PEER_STORE_PATH = "peerStorePath"; - String PEER_STORE_PASSWORD = "peerStorePassword"; - - /* - * A temporary attributes to set the broker group file. - * TODO: Remove them after adding a full support to configure authorization providers via management layers. - */ - String GROUP_FILE = "groupFile"; - // Attributes Collection<String> AVAILABLE_ATTRIBUTES = Collections.unmodifiableList( @@ -114,7 +88,8 @@ public interface Broker extends ConfiguredObject PLATFORM, PROCESS_PID, PRODUCT_VERSION, - SUPPORTED_STORE_TYPES, + SUPPORTED_BROKER_STORE_TYPES, + SUPPORTED_VIRTUALHOST_STORE_TYPES, SUPPORTED_AUTHENTICATION_PROVIDERS, CREATED, DURABLE, @@ -124,7 +99,6 @@ public interface Broker extends ConfiguredObject STATE, TIME_TO_LIVE, UPDATED, - DEFAULT_AUTHENTICATION_PROVIDER, DEFAULT_VIRTUAL_HOST, QUEUE_ALERT_THRESHOLD_MESSAGE_AGE, QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, @@ -147,17 +121,7 @@ public interface Broker extends ConfiguredObject VIRTUALHOST_STORE_TRANSACTION_IDLE_TIMEOUT_CLOSE, VIRTUALHOST_STORE_TRANSACTION_IDLE_TIMEOUT_WARN, VIRTUALHOST_STORE_TRANSACTION_OPEN_TIMEOUT_CLOSE, - VIRTUALHOST_STORE_TRANSACTION_OPEN_TIMEOUT_WARN, - - ACL_FILE, - KEY_STORE_PATH, - KEY_STORE_PASSWORD, - KEY_STORE_CERT_ALIAS, - TRUST_STORE_PATH, - TRUST_STORE_PASSWORD, - PEER_STORE_PATH, - PEER_STORE_PASSWORD, - GROUP_FILE + VIRTUALHOST_STORE_TRANSACTION_OPEN_TIMEOUT_WARN )); //children @@ -167,11 +131,7 @@ public interface Broker extends ConfiguredObject Collection<AuthenticationProvider> getAuthenticationProviders(); - VirtualHost createVirtualHost(String name, State initialState, boolean durable, - LifetimePolicy lifetime, long ttl, Map<String, Object> attributes) - throws AccessControlException, IllegalArgumentException; - - AuthenticationProvider getDefaultAuthenticationProvider(); + Collection<AccessControlProvider> getAccessControlProviders(); Collection<GroupProvider> getGroupProviders(); @@ -192,8 +152,14 @@ public interface Broker extends ConfiguredObject */ LogRecorder getLogRecorder(); + AuthenticationProvider findAuthenticationProviderByName(String authenticationProviderName); + VirtualHost findVirtualHostByName(String name); + KeyStore findKeyStoreByName(String name); + + TrustStore findTrustStoreByName(String name); + /** * Get the SubjectCreator for the given socket address. * TODO: move the authentication related functionality into host aliases and AuthenticationProviders @@ -211,10 +177,6 @@ public interface Broker extends ConfiguredObject */ VirtualHostRegistry getVirtualHostRegistry(); - KeyStore getDefaultKeyStore(); - - TrustStore getDefaultTrustStore(); - TaskExecutor getTaskExecutor(); boolean isManagementMode(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/KeyStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/KeyStore.java index 959714656b..74a7469ffb 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/KeyStore.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/KeyStore.java @@ -24,10 +24,23 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -public interface KeyStore extends TrustStore +public interface KeyStore extends ConfiguredObject { + String ID = "id"; + String NAME = "name"; + String DURABLE = "durable"; + String LIFETIME_POLICY = "lifetimePolicy"; + String STATE = "state"; + String TIME_TO_LIVE = "timeToLive"; + String CREATED = "created"; + String UPDATED = "updated"; + String DESCRIPTION = "description"; + String PATH = "path"; + String PASSWORD = "password"; + String TYPE = "type"; String CERTIFICATE_ALIAS = "certificateAlias"; + String KEY_MANAGER_FACTORY_ALGORITHM = "keyManagerFactoryAlgorithm"; public static final Collection<String> AVAILABLE_ATTRIBUTES = Collections.unmodifiableList( @@ -44,8 +57,11 @@ public interface KeyStore extends TrustStore PATH, PASSWORD, TYPE, - KEY_MANAGER_FACTORY_ALGORITHM, - CERTIFICATE_ALIAS + CERTIFICATE_ALIAS, + KEY_MANAGER_FACTORY_ALGORITHM )); + public String getPassword(); + + public void setPassword(String password); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Model.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Model.java index bf4c40815a..bccb6b48ee 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Model.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Model.java @@ -52,6 +52,7 @@ public class Model { addRelationship(Broker.class, VirtualHost.class); addRelationship(Broker.class, Port.class); + addRelationship(Broker.class, AccessControlProvider.class); addRelationship(Broker.class, AuthenticationProvider.class); addRelationship(Broker.class, GroupProvider.class); addRelationship(Broker.class, TrustStore.class); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java index 33ba34767d..60d62e3f27 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java @@ -45,6 +45,8 @@ public interface Port extends ConfiguredObject String NEED_CLIENT_AUTH = "needClientAuth"; String WANT_CLIENT_AUTH = "wantClientAuth"; String AUTHENTICATION_PROVIDER = "authenticationProvider"; + String KEY_STORE = "keyStore"; + String TRUST_STORES = "trustStores"; // Attributes public static final Collection<String> AVAILABLE_ATTRIBUTES = @@ -67,7 +69,9 @@ public interface Port extends ConfiguredObject RECEIVE_BUFFER_SIZE, NEED_CLIENT_AUTH, WANT_CLIENT_AUTH, - AUTHENTICATION_PROVIDER + AUTHENTICATION_PROVIDER, + KEY_STORE, + TRUST_STORES )); @@ -75,6 +79,10 @@ public interface Port extends ConfiguredObject int getPort(); + KeyStore getKeyStore(); + + Collection<TrustStore> getTrustStores(); + Collection<Transport> getTransports(); void addTransport(Transport transport) throws IllegalStateException, @@ -85,7 +93,6 @@ public interface Port extends ConfiguredObject AccessControlException, IllegalArgumentException; - Collection<Protocol> getProtocols(); void addProtocol(Protocol protocol) throws IllegalStateException, @@ -96,12 +103,9 @@ public interface Port extends ConfiguredObject AccessControlException, IllegalArgumentException; + AuthenticationProvider getAuthenticationProvider(); //children Collection<VirtualHostAlias> getVirtualHostBindings(); Collection<Connection> getConnections(); - - AuthenticationProvider getAuthenticationProvider(); - - void setAuthenticationProvider(AuthenticationProvider authenticationProvider); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/TrustStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/TrustStore.java index 53498ab431..c686e7bd50 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/TrustStore.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/TrustStore.java @@ -38,9 +38,9 @@ public interface TrustStore extends ConfiguredObject String PATH = "path"; String PASSWORD = "password"; - String PEERS_ONLY = "peersOnly"; String TYPE = "type"; - String KEY_MANAGER_FACTORY_ALGORITHM = "keyManagerFactoryAlgorithm"; + String PEERS_ONLY = "peersOnly"; + String TRUST_MANAGER_FACTORY_ALGORITHM = "trustManagerFactoryAlgorithm"; public static final Collection<String> AVAILABLE_ATTRIBUTES = Collections.unmodifiableList( @@ -56,9 +56,9 @@ public interface TrustStore extends ConfiguredObject DESCRIPTION, PATH, PASSWORD, - PEERS_ONLY, TYPE, - KEY_MANAGER_FACTORY_ALGORITHM + PEERS_ONLY, + TRUST_MANAGER_FACTORY_ALGORITHM )); public String getPassword(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java index 20929a337a..51fd3a5c78 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractAdapter.java @@ -63,6 +63,7 @@ abstract class AbstractAdapter implements ConfiguredObject { if (attributes.containsKey(name)) { + //TODO: dont put nulls _attributes.put(name, attributes.get(name)); } } @@ -254,6 +255,7 @@ abstract class AbstractAdapter implements ConfiguredObject if((currentValue == null && expected == null) || (currentValue != null && currentValue.equals(expected))) { + //TODO: dont put nulls _attributes.put(name, desired); return true; } @@ -397,4 +399,51 @@ abstract class AbstractAdapter implements ConfiguredObject { return _defaultAttributes; } + + /** + * Returns a map of effective attribute values that would result + * if applying the supplied changes. Does not apply the changes. + */ + protected Map<String, Object> generateEffectiveAttributes(Map<String,Object> changedValues) + { + //Build a new set of effective attributes that would be + //the result of applying the attribute changes, so we + //can validate the configuration that would result + + Map<String, Object> defaultValues = getDefaultAttributes(); + Map<String, Object> existingActualValues = getActualAttributes(); + + //create a new merged map, starting with the defaults + Map<String, Object> merged = new HashMap<String, Object>(defaultValues); + + for(String name : getAttributeNames()) + { + if(changedValues.containsKey(name)) + { + Object changedValue = changedValues.get(name); + if(changedValue != null) + { + //use the new non-null value for the merged values + merged.put(name, changedValue); + } + else + { + //we just use the default (if there was one) since the changed + //value is null and effectively clears any existing actual value + } + } + else if(existingActualValues.get(name) != null) + { + //Use existing non-null actual value for the merge + merged.put(name, existingActualValues.get(name)); + } + else + { + //There was neither a change or an existing non-null actual + //value, so just use the default value (if there was one). + } + } + + return merged; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractKeyStoreAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractKeyStoreAdapter.java index 80196c395e..707cf8076d 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractKeyStoreAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AbstractKeyStoreAdapter.java @@ -37,20 +37,21 @@ import org.apache.qpid.server.util.MapValueConverter; public abstract class AbstractKeyStoreAdapter extends AbstractAdapter { + public static final String DUMMY_PASSWORD_MASK = "********"; + public static final String DEFAULT_KEYSTORE_TYPE = java.security.KeyStore.getDefaultType(); + private String _name; private String _password; - protected AbstractKeyStoreAdapter(UUID id, Broker broker, Map<String, Object> attributes) + protected AbstractKeyStoreAdapter(UUID id, Broker broker, Map<String, Object> defaults, + Map<String, Object> attributes) { - super(id, broker.getTaskExecutor()); + super(id, defaults, attributes, broker.getTaskExecutor()); addParent(Broker.class, broker); + _name = MapValueConverter.getStringAttribute(TrustStore.NAME, attributes); _password = MapValueConverter.getStringAttribute(TrustStore.PASSWORD, attributes); - setMandatoryAttribute(TrustStore.PATH, attributes); - setOptionalAttribute(TrustStore.PEERS_ONLY, attributes); - setOptionalAttribute(TrustStore.TYPE, attributes); - setOptionalAttribute(TrustStore.KEY_MANAGER_FACTORY_ALGORITHM, attributes); - setOptionalAttribute(TrustStore.DESCRIPTION, attributes); + MapValueConverter.assertMandatoryAttribute(KeyStore.PATH, attributes); } @Override @@ -163,15 +164,16 @@ public abstract class AbstractKeyStoreAdapter extends AbstractAdapter } else if(KeyStore.PASSWORD.equals(name)) { - return null; // for security reasons we don't expose the password + // For security reasons we don't expose the password + if (getPassword() != null) + { + return DUMMY_PASSWORD_MASK; + } + + return null; } - return super.getAttribute(name); - } - @Override - protected boolean setState(State currentState, State desiredState) - { - return false; + return super.getAttribute(name); } public String getPassword() @@ -183,25 +185,4 @@ public abstract class AbstractKeyStoreAdapter extends AbstractAdapter { _password = password; } - - private void setMandatoryAttribute(String name, Map<String, Object> attributeValues) - { - changeAttribute(name, null, MapValueConverter.getStringAttribute(name, attributeValues)); - } - - private void setOptionalAttribute(String name, Map<String, Object> attributeValues) - { - Object attrValue = attributeValues.get(name); - if (attrValue != null) - { - if (attrValue instanceof Boolean) - { - changeAttribute(name, null, MapValueConverter.getBooleanAttribute(name, attributeValues)); - } - else - { - changeAttribute(name, null, MapValueConverter.getStringAttribute(name, attributeValues)); - } - } - } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AccessControlProviderAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AccessControlProviderAdapter.java new file mode 100644 index 0000000000..75b80eb56c --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AccessControlProviderAdapter.java @@ -0,0 +1,283 @@ +/* + * + * 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.model.adapter; + +import java.security.AccessControlException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.qpid.server.model.AccessControlProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.IllegalStateTransitionException; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.model.Statistics; +import org.apache.qpid.server.plugin.AccessControlFactory; +import org.apache.qpid.server.security.AccessControl; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.util.MapValueConverter; + +public class AccessControlProviderAdapter extends AbstractAdapter implements AccessControlProvider +{ + protected AccessControl _accessControl; + protected final Broker _broker; + + protected Collection<String> _supportedAttributes; + protected Map<String, AccessControlFactory> _factories; + private AtomicReference<State> _state; + + public AccessControlProviderAdapter(UUID id, Broker broker, AccessControl accessControl, Map<String, Object> attributes, Collection<String> attributeNames) + { + super(id, null, null, broker.getTaskExecutor()); + + if (accessControl == null) + { + throw new IllegalArgumentException("AccessControl must not be null"); + } + + _accessControl = accessControl; + _broker = broker; + _supportedAttributes = createSupportedAttributes(attributeNames); + addParent(Broker.class, broker); + + State state = MapValueConverter.getEnumAttribute(State.class, STATE, attributes, State.INITIALISING); + _state = new AtomicReference<State>(state); + + // set attributes now after all attribute names are known + if (attributes != null) + { + for (String name : _supportedAttributes) + { + if (attributes.containsKey(name)) + { + changeAttribute(name, null, attributes.get(name)); + } + } + } + } + + protected Collection<String> createSupportedAttributes(Collection<String> factoryAttributes) + { + List<String> attributesNames = new ArrayList<String>(AVAILABLE_ATTRIBUTES); + if (factoryAttributes != null) + { + attributesNames.addAll(factoryAttributes); + } + + return Collections.unmodifiableCollection(attributesNames); + } + + @Override + public String getName() + { + return (String)getAttribute(AccessControlProvider.NAME); + } + + @Override + public String setName(String currentName, String desiredName) throws IllegalStateException, AccessControlException + { + return null; + } + + @Override + public State getActualState() + { + return _state.get(); + } + + @Override + public boolean isDurable() + { + return true; + } + + @Override + public void setDurable(boolean durable) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + } + + @Override + public LifetimePolicy getLifetimePolicy() + { + return LifetimePolicy.PERMANENT; + } + + @Override + public LifetimePolicy setLifetimePolicy(LifetimePolicy expected, LifetimePolicy desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return null; + } + + @Override + public long getTimeToLive() + { + return 0; + } + + @Override + public long setTimeToLive(long expected, long desired) + throws IllegalStateException, AccessControlException, IllegalArgumentException + { + return 0; + } + + @Override + public Statistics getStatistics() + { + return NoStatistics.getInstance(); + } + + @Override + public Collection<String> getAttributeNames() + { + return _supportedAttributes; + } + + @Override + public Object getAttribute(String name) + { + if(CREATED.equals(name)) + { + // TODO + } + else if(DURABLE.equals(name)) + { + return true; + } + else if(ID.equals(name)) + { + return getId(); + } + else if(LIFETIME_POLICY.equals(name)) + { + return LifetimePolicy.PERMANENT; + } + else if(STATE.equals(name)) + { + return getActualState(); + } + else if(TIME_TO_LIVE.equals(name)) + { + // TODO + } + else if(UPDATED.equals(name)) + { + // TODO + } + return super.getAttribute(name); + } + + @Override + public <C extends ConfiguredObject> Collection<C> getChildren(Class<C> clazz) + { + return Collections.emptySet(); + } + + @Override + public boolean setState(State currentState, State desiredState) + throws IllegalStateTransitionException, AccessControlException + { + State state = _state.get(); + + if(desiredState == State.DELETED) + { + return _state.compareAndSet(state, State.DELETED); + } + else if (desiredState == State.QUIESCED) + { + return _state.compareAndSet(state, State.QUIESCED); + } + else if(desiredState == State.ACTIVE) + { + if ((state == State.INITIALISING || state == State.QUIESCED) && _state.compareAndSet(state, State.ACTIVE)) + { + _accessControl.open(); + return true; + } + else + { + throw new IllegalStateException("Can't activate access control provider in " + state + " state"); + } + } + else if(desiredState == State.STOPPED) + { + if(_state.compareAndSet(state, State.STOPPED)) + { + _accessControl.close(); + return true; + } + + return false; + } + + return false; + } + + + @Override + protected void changeAttributes(Map<String, Object> attributes) + { + throw new UnsupportedOperationException("Changing attributes on AccessControlProvider is not supported"); + } + + @Override + protected void authoriseSetDesiredState(State currentState, State desiredState) throws AccessControlException + { + if(desiredState == State.DELETED) + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), AccessControlProvider.class, Operation.DELETE)) + { + throw new AccessControlException("Deletion of AccessControlProvider is denied"); + } + } + } + + @Override + protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), AccessControlProvider.class, Operation.UPDATE)) + { + throw new AccessControlException("Setting of AccessControlProvider attributes is denied"); + } + } + + @Override + protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), AccessControlProvider.class, Operation.UPDATE)) + { + throw new AccessControlException("Setting of AccessControlProvider attributes is denied"); + } + } + + public AccessControl getAccessControl() + { + return _accessControl; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AccessControlProviderFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AccessControlProviderFactory.java new file mode 100644 index 0000000000..6cdf2f2c1a --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AccessControlProviderFactory.java @@ -0,0 +1,90 @@ +/* + * + * 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.model.adapter; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.apache.qpid.server.model.AccessControlProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.plugin.AccessControlFactory; +import org.apache.qpid.server.plugin.QpidServiceLoader; +import org.apache.qpid.server.security.AccessControl; + +public class AccessControlProviderFactory +{ + private final Iterable<AccessControlFactory> _factories; + private Collection<String> _supportedAcessControlProviders; + + public AccessControlProviderFactory(QpidServiceLoader<AccessControlFactory> accessControlFactoryServiceLoader) + { + _factories = accessControlFactoryServiceLoader.instancesOf(AccessControlFactory.class); + List<String> supportedAccessControlProviders = new ArrayList<String>(); + for (AccessControlFactory factory : _factories) + { + supportedAccessControlProviders.add(factory.getType()); + } + _supportedAcessControlProviders = Collections.unmodifiableCollection(supportedAccessControlProviders); + } + + /** + * Creates {@link AccessControlProvider} for given ID, {@link Broker} and attributes. + * <p> + * The configured {@link AccessControlFactory}'s are used to try to create the {@link AccessControlProvider}. + * The first non-null instance is returned. The factories are used in non-deterministic order. + */ + public AccessControlProvider create(UUID id, Broker broker, Map<String, Object> attributes) + { + AccessControlProvider ac = createAccessControlProvider(id, broker, attributes); + ac.getAccessControl().onCreate(); + + return ac; + } + + public AccessControlProvider recover(UUID id, Broker broker, Map<String, Object> attributes) + { + return createAccessControlProvider(id, broker, attributes); + } + + private AccessControlProvider createAccessControlProvider(UUID id, + Broker broker, Map<String, Object> attributes) + { + for (AccessControlFactory factory : _factories) + { + AccessControl accessControl = factory.createInstance(attributes); + if (accessControl != null) + { + return new AccessControlProviderAdapter(id, broker,accessControl, attributes, factory.getAttributeNames()); + } + } + + throw new IllegalArgumentException("No access control provider factory found for configuration attributes " + attributes); + } + + public Collection<String> getSupportedAuthenticationProviders() + { + return _supportedAcessControlProviders; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java index e7057f89d3..71f5397d2b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AmqpPortAdapter.java @@ -133,15 +133,11 @@ public class AmqpPortAdapter extends PortAdapter private SSLContext createSslContext() { - KeyStore keyStore = _broker.getDefaultKeyStore(); - if (keyStore == null) - { - throw new IllegalConfigurationException("SSL was requested on AMQP port '" - + this.getName() + "' but no key store defined"); - } + KeyStore keyStore = getKeyStore(); - Collection<TrustStore> trustStores = _broker.getTrustStores(); - if (((Boolean)getAttribute(NEED_CLIENT_AUTH) || (Boolean)getAttribute(WANT_CLIENT_AUTH)) && trustStores.isEmpty()) + Collection<TrustStore> trustStores = getTrustStores(); + boolean needClientCert = (Boolean)getAttribute(NEED_CLIENT_AUTH) || (Boolean)getAttribute(WANT_CLIENT_AUTH); + if (needClientCert && trustStores.isEmpty()) { throw new IllegalConfigurationException("Client certificate authentication is enabled on AMQP port '" + this.getName() + "' but no trust store defined"); @@ -165,7 +161,7 @@ public class AmqpPortAdapter extends PortAdapter trustStore.getPassword(), (String)trustStore.getAttribute(TrustStore.TYPE), (Boolean) trustStore.getAttribute(TrustStore.PEERS_ONLY), - (String)trustStore.getAttribute(TrustStore.KEY_MANAGER_FACTORY_ALGORITHM))); + (String)trustStore.getAttribute(TrustStore.TRUST_MANAGER_FACTORY_ALGORITHM))); } sslContext = SSLContextFactory.buildClientContext(trstWrappers, keystorePath, keystorePassword, keystoreType, diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java index f788923b3a..8d601460ad 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java @@ -219,10 +219,6 @@ public abstract class AuthenticationProviderAdapter<T extends AuthenticationMana String providerName = getName(); // verify that provider is not in use - if (providerName.equals(_broker.getAttribute(Broker.DEFAULT_AUTHENTICATION_PROVIDER))) - { - throw new IntegrityViolationException("Authentication provider '" + providerName + "' is set as default and cannot be deleted"); - } Collection<Port> ports = new ArrayList<Port>(_broker.getPorts()); for (Port port : ports) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java index e968d91e79..c715f1989c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java @@ -24,7 +24,6 @@ import java.lang.reflect.Type; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.security.AccessControlException; -import java.security.KeyStoreException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -32,17 +31,19 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; -import javax.net.ssl.KeyManagerFactory; -import java.security.cert.Certificate; - import org.apache.log4j.Logger; import org.apache.qpid.common.QpidProperties; +import org.apache.qpid.server.BrokerOptions; +import org.apache.qpid.server.configuration.BrokerConfigurationStoreCreator; import org.apache.qpid.server.configuration.ConfigurationEntryStore; import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.logging.LogRecorder; import org.apache.qpid.server.logging.RootMessageLogger; import org.apache.qpid.server.logging.actors.BrokerActor; import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.messages.BrokerMessages; +import org.apache.qpid.server.model.AccessControlProvider; import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.ConfigurationChangeListener; @@ -58,22 +59,19 @@ import org.apache.qpid.server.model.Statistics; import org.apache.qpid.server.model.TrustStore; import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.model.VirtualHost; -import org.apache.qpid.server.configuration.store.ManagementModeStoreHandler; -import org.apache.qpid.server.configuration.updater.TaskExecutor; -import org.apache.qpid.server.security.access.Operation; -import org.apache.qpid.server.security.group.FileGroupManager; -import org.apache.qpid.server.security.group.GroupManager; +import org.apache.qpid.server.model.adapter.AuthenticationProviderAdapter.SimpleAuthenticationProviderAdapter; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.manager.SimpleAuthenticationManager; import org.apache.qpid.server.stats.StatisticsGatherer; import org.apache.qpid.server.store.MessageStoreCreator; import org.apache.qpid.server.util.MapValueConverter; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; -import org.apache.qpid.transport.network.security.ssl.SSLUtil; public class BrokerAdapter extends AbstractAdapter implements Broker, ConfigurationChangeListener { - private static final Logger LOGGER = Logger.getLogger(BrokerAdapter.class); @SuppressWarnings("serial") @@ -95,19 +93,9 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat put(CONNECTION_HEART_BEAT_DELAY, Integer.class); put(STATISTICS_REPORTING_PERIOD, Integer.class); - put(ACL_FILE, String.class); put(NAME, String.class); put(DEFAULT_VIRTUAL_HOST, String.class); - put(DEFAULT_AUTHENTICATION_PROVIDER, String.class); - - put(KEY_STORE_PATH, String.class); - put(KEY_STORE_PASSWORD, String.class); - put(KEY_STORE_CERT_ALIAS, String.class); - put(TRUST_STORE_PATH, String.class); - put(TRUST_STORE_PASSWORD, String.class); - put(PEER_STORE_PATH, String.class); - put(PEER_STORE_PASSWORD, String.class); - put(GROUP_FILE, String.class); + put(VIRTUALHOST_STORE_TRANSACTION_IDLE_TIMEOUT_CLOSE, Long.class); put(VIRTUALHOST_STORE_TRANSACTION_IDLE_TIMEOUT_WARN, Long.class); put(VIRTUALHOST_STORE_TRANSACTION_OPEN_TIMEOUT_CLOSE, Long.class); @@ -133,12 +121,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat public static final long DEFAULT_STORE_TRANSACTION_IDLE_TIMEOUT_WARN = 0l; public static final long DEFAULT_STORE_TRANSACTION_OPEN_TIMEOUT_CLOSE = 0l; public static final long DEFAULT_STORE_TRANSACTION_OPEN_TIMEOUT_WARN = 0l; - private static final String DEFAULT_KEY_STORE_NAME = "defaultKeyStore"; - private static final String DEFAULT_TRUST_STORE_NAME = "defaultTrustStore"; - private static final String DEFAULT_GROUP_PROVIDER_NAME = "defaultGroupProvider"; - private static final String DEFAULT_PEER_STORE_NAME = "defaultPeerStore"; - - private static final String DUMMY_PASSWORD_MASK = "********"; @SuppressWarnings("serial") private static final Map<String, Object> DEFAULTS = Collections.unmodifiableMap(new HashMap<String, Object>(){{ @@ -182,25 +164,28 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat private final Map<UUID, AuthenticationProvider> _authenticationProviders = new HashMap<UUID, AuthenticationProvider>(); private final Map<String, GroupProvider> _groupProviders = new HashMap<String, GroupProvider>(); private final Map<UUID, ConfiguredObject> _plugins = new HashMap<UUID, ConfiguredObject>(); - private final Map<UUID, KeyStore> _keyStores = new HashMap<UUID, KeyStore>(); - private final Map<UUID, TrustStore> _trustStores = new HashMap<UUID, TrustStore>(); + private final Map<String, KeyStore> _keyStores = new HashMap<String, KeyStore>(); + private final Map<String, TrustStore> _trustStores = new HashMap<String, TrustStore>(); + private final Map<UUID, AccessControlProvider> _accessControlProviders = new HashMap<UUID, AccessControlProvider>(); + private final GroupProviderFactory _groupProviderFactory; private final AuthenticationProviderFactory _authenticationProviderFactory; - private AuthenticationProvider _defaultAuthenticationProvider; + private final AccessControlProviderFactory _accessControlProviderFactory; private final PortFactory _portFactory; private final SecurityManager _securityManager; - private final UUID _defaultKeyStoreId; - private final UUID _defaultTrustStoreId; - private final Collection<String> _supportedStoreTypes; + private final Collection<String> _supportedVirtualHostStoreTypes; + private Collection<String> _supportedBrokerStoreTypes; private final ConfigurationEntryStore _brokerStore; - private boolean _managementMode; + private AuthenticationProvider _managementAuthenticationProvider; + private BrokerOptions _brokerOptions; public BrokerAdapter(UUID id, Map<String, Object> attributes, StatisticsGatherer statisticsGatherer, VirtualHostRegistry virtualHostRegistry, LogRecorder logRecorder, RootMessageLogger rootMessageLogger, AuthenticationProviderFactory authenticationProviderFactory, - PortFactory portFactory, TaskExecutor taskExecutor, ConfigurationEntryStore brokerStore) + GroupProviderFactory groupProviderFactory, AccessControlProviderFactory accessControlProviderFactory, PortFactory portFactory, TaskExecutor taskExecutor, + ConfigurationEntryStore brokerStore, BrokerOptions brokerOptions) { super(id, DEFAULTS, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES), taskExecutor); _statisticsGatherer = statisticsGatherer; @@ -209,109 +194,20 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat _rootMessageLogger = rootMessageLogger; _statistics = new StatisticsAdapter(statisticsGatherer); _authenticationProviderFactory = authenticationProviderFactory; + _groupProviderFactory = groupProviderFactory; + _accessControlProviderFactory = accessControlProviderFactory; _portFactory = portFactory; - _securityManager = new SecurityManager((String)getAttribute(ACL_FILE)); - addChangeListener(_securityManager); - _defaultKeyStoreId = UUIDGenerator.generateBrokerChildUUID(KeyStore.class.getSimpleName(), DEFAULT_KEY_STORE_NAME); - _defaultTrustStoreId = UUIDGenerator.generateBrokerChildUUID(TrustStore.class.getSimpleName(), DEFAULT_TRUST_STORE_NAME); - createBrokerChildrenFromAttributes(); - _supportedStoreTypes = new MessageStoreCreator().getStoreTypes(); + _brokerOptions = brokerOptions; + _securityManager = new SecurityManager(this, _brokerOptions.isManagementMode()); + _supportedVirtualHostStoreTypes = new MessageStoreCreator().getStoreTypes(); + _supportedBrokerStoreTypes = new BrokerConfigurationStoreCreator().getStoreTypes(); _brokerStore = brokerStore; - _managementMode = brokerStore instanceof ManagementModeStoreHandler; - } - - /* - * A temporary method to create broker children that can be only configured via broker attributes - */ - private void createBrokerChildrenFromAttributes() - { - createGroupProvider(); - createKeyStore(); - createTrustStore(); - createPeerStore(); - } - - private void createGroupProvider() - { - String groupFile = (String) getAttribute(GROUP_FILE); - if (groupFile != null) - { - GroupManager groupManager = new FileGroupManager(groupFile); - UUID groupProviderId = UUIDGenerator.generateBrokerChildUUID(GroupProvider.class.getSimpleName(), - DEFAULT_GROUP_PROVIDER_NAME); - GroupProviderAdapter groupProviderAdapter = new GroupProviderAdapter(groupProviderId, groupManager, this); - _groupProviders.put(DEFAULT_GROUP_PROVIDER_NAME, groupProviderAdapter); - } - else - { - _groupProviders.remove(DEFAULT_GROUP_PROVIDER_NAME); - } - } - - private void createKeyStore() - { - Map<String, Object> actualAttributes = getActualAttributes(); - String keyStorePath = (String) getAttribute(KEY_STORE_PATH); - if (keyStorePath != null) + if (_brokerOptions.isManagementMode()) { - Map<String, Object> keyStoreAttributes = new HashMap<String, Object>(); - keyStoreAttributes.put(KeyStore.NAME, DEFAULT_KEY_STORE_NAME); - keyStoreAttributes.put(KeyStore.PATH, keyStorePath); - keyStoreAttributes.put(KeyStore.PASSWORD, (String) actualAttributes.get(KEY_STORE_PASSWORD)); - keyStoreAttributes.put(KeyStore.TYPE, java.security.KeyStore.getDefaultType()); - keyStoreAttributes.put(KeyStore.CERTIFICATE_ALIAS, getAttribute(KEY_STORE_CERT_ALIAS)); - keyStoreAttributes.put(KeyStore.KEY_MANAGER_FACTORY_ALGORITHM, KeyManagerFactory.getDefaultAlgorithm()); - KeyStoreAdapter keyStoreAdapter = new KeyStoreAdapter(_defaultKeyStoreId, this, keyStoreAttributes); - _keyStores.put(keyStoreAdapter.getId(), keyStoreAdapter); - } - else - { - _keyStores.remove(_defaultKeyStoreId); - } - } - - private void createTrustStore() - { - Map<String, Object> actualAttributes = getActualAttributes(); - String trustStorePath = (String) getAttribute(TRUST_STORE_PATH); - if (trustStorePath != null) - { - Map<String, Object> trsustStoreAttributes = new HashMap<String, Object>(); - trsustStoreAttributes.put(TrustStore.NAME, DEFAULT_TRUST_STORE_NAME); - trsustStoreAttributes.put(TrustStore.PATH, trustStorePath); - trsustStoreAttributes.put(TrustStore.PEERS_ONLY, Boolean.FALSE); - trsustStoreAttributes.put(TrustStore.PASSWORD, (String) actualAttributes.get(TRUST_STORE_PASSWORD)); - trsustStoreAttributes.put(TrustStore.TYPE, java.security.KeyStore.getDefaultType()); - trsustStoreAttributes.put(TrustStore.KEY_MANAGER_FACTORY_ALGORITHM, KeyManagerFactory.getDefaultAlgorithm()); - TrustStoreAdapter trustStore = new TrustStoreAdapter(_defaultTrustStoreId, this, trsustStoreAttributes); - _trustStores.put(trustStore.getId(), trustStore); - } - else - { - _trustStores.remove(_defaultTrustStoreId); - } - } - - private void createPeerStore() - { - Map<String, Object> actualAttributes = getActualAttributes(); - String peerStorePath = (String) getAttribute(PEER_STORE_PATH); - UUID peerStoreId = UUIDGenerator.generateBrokerChildUUID(TrustStore.class.getSimpleName(), DEFAULT_PEER_STORE_NAME); - if (peerStorePath != null) - { - Map<String, Object> peerStoreAttributes = new HashMap<String, Object>(); - peerStoreAttributes.put(TrustStore.NAME, peerStoreId.toString()); - peerStoreAttributes.put(TrustStore.PATH, peerStorePath); - peerStoreAttributes.put(TrustStore.PEERS_ONLY, Boolean.TRUE); - peerStoreAttributes.put(TrustStore.PASSWORD, (String) actualAttributes.get(PEER_STORE_PASSWORD)); - peerStoreAttributes.put(TrustStore.TYPE, java.security.KeyStore.getDefaultType()); - peerStoreAttributes.put(TrustStore.KEY_MANAGER_FACTORY_ALGORITHM, KeyManagerFactory.getDefaultAlgorithm()); - TrustStoreAdapter trustStore = new TrustStoreAdapter(peerStoreId, this, peerStoreAttributes); - _trustStores.put(trustStore.getId(), trustStore); - } - else - { - _trustStores.remove(peerStoreId); + AuthenticationManager authManager = new SimpleAuthenticationManager(BrokerOptions.MANAGEMENT_MODE_USER_NAME, _brokerOptions.getManagementModePassword()); + AuthenticationProvider authenticationProvider = new SimpleAuthenticationProviderAdapter(UUID.randomUUID(), this, + authManager, Collections.<String, Object> emptyMap(), Collections.<String> emptySet()); + _managementAuthenticationProvider = authenticationProvider; } } @@ -321,7 +217,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { return new ArrayList<VirtualHost>(_vhostAdapters.values()); } - } public Collection<Port> getPorts() @@ -343,6 +238,10 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat public AuthenticationProvider findAuthenticationProviderByName(String authenticationProviderName) { + if (isManagementMode()) + { + return _managementAuthenticationProvider; + } Collection<AuthenticationProvider> providers = getAuthenticationProviders(); for (AuthenticationProvider authenticationProvider : providers) { @@ -354,15 +253,20 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat return null; } - @Override - public AuthenticationProvider getDefaultAuthenticationProvider() + public KeyStore findKeyStoreByName(String keyStoreName) { - return _defaultAuthenticationProvider; + synchronized(_keyStores) + { + return _keyStores.get(keyStoreName); + } } - public void setDefaultAuthenticationProvider(AuthenticationProvider provider) + public TrustStore findTrustStoreByName(String trustStoreName) { - _defaultAuthenticationProvider = provider; + synchronized(_trustStores) + { + return _trustStores.get(trustStoreName); + } } @Override @@ -376,17 +280,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat } } - public VirtualHost createVirtualHost(final String name, - final State initialState, - final boolean durable, - final LifetimePolicy lifetime, - final long ttl, - final Map<String, Object> attributes) - throws AccessControlException, IllegalArgumentException - { - return null; //TODO - } - private VirtualHost createVirtualHost(final Map<String, Object> attributes) throws AccessControlException, IllegalArgumentException { @@ -396,14 +289,14 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat // permission has already been granted to create the virtual host // disable further access check on other operations, e.g. create exchange - _securityManager.setAccessChecksDisabled(true); + SecurityManager.setAccessChecksDisabled(true); try { virtualHostAdapter.setDesiredState(State.INITIALISING, State.ACTIVE); } finally { - _securityManager.setAccessChecksDisabled(false); + SecurityManager.setAccessChecksDisabled(false); } return virtualHostAdapter; } @@ -486,6 +379,10 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { return (Collection<C>) getPorts(); } + else if(clazz == AccessControlProvider.class) + { + return (Collection<C>) getAccessControlProviders(); + } else if(clazz == AuthenticationProvider.class) { return (Collection<C>) getAuthenticationProviders(); @@ -522,10 +419,26 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { return (C) createPort(attributes); } + else if(childClass == AccessControlProvider.class) + { + return (C) createAccessControlProvider(attributes); + } else if(childClass == AuthenticationProvider.class) { return (C) createAuthenticationProvider(attributes); } + else if(childClass == KeyStore.class) + { + return (C) createKeyStore(attributes); + } + else if(childClass == TrustStore.class) + { + return (C) createTrustStore(attributes); + } + else if(childClass == GroupProvider.class) + { + return (C) createGroupProvider(attributes); + } else { throw new IllegalArgumentException("Cannot create child of class " + childClass.getSimpleName()); @@ -563,6 +476,64 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat return port; } + private AccessControlProvider createAccessControlProvider(Map<String, Object> attributes) + { + AccessControlProvider accessControlProvider = null; + synchronized (_accessControlProviders) + { + accessControlProvider = _accessControlProviderFactory.create(UUID.randomUUID(), this, attributes); + addAccessControlProvider(accessControlProvider); + } + + boolean quiesce = isManagementMode() ; + accessControlProvider.setDesiredState(State.INITIALISING, quiesce ? State.QUIESCED : State.ACTIVE); + + return accessControlProvider; + } + + /** + * @throws IllegalConfigurationException if an AuthenticationProvider with the same name already exists + */ + private void addAccessControlProvider(AccessControlProvider accessControlProvider) + { + String name = accessControlProvider.getName(); + synchronized (_authenticationProviders) + { + if (_accessControlProviders.containsKey(accessControlProvider.getId())) + { + throw new IllegalConfigurationException("Can't add AccessControlProvider because one with id " + accessControlProvider.getId() + " already exists"); + } + for (AccessControlProvider provider : _accessControlProviders.values()) + { + if (provider.getName().equals(name)) + { + throw new IllegalConfigurationException("Can't add AccessControlProvider because one with name " + name + " already exists"); + } + } + _accessControlProviders.put(accessControlProvider.getId(), accessControlProvider); + } + + accessControlProvider.addChangeListener(this); + accessControlProvider.addChangeListener(_securityManager); + } + + private boolean deleteAccessControlProvider(AccessControlProvider accessControlProvider) + { + AccessControlProvider removedAccessControlProvider = null; + synchronized (_accessControlProviders) + { + removedAccessControlProvider = _accessControlProviders.remove(accessControlProvider.getId()); + } + + if(removedAccessControlProvider != null) + { + removedAccessControlProvider.removeChangeListener(this); + removedAccessControlProvider.removeChangeListener(_securityManager); + } + + return removedAccessControlProvider != null; + } + private AuthenticationProvider createAuthenticationProvider(Map<String, Object> attributes) { AuthenticationProvider authenticationProvider = null; @@ -599,6 +570,14 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat authenticationProvider.addChangeListener(this); } + private GroupProvider createGroupProvider(Map<String, Object> attributes) + { + GroupProvider groupProvider = _groupProviderFactory.create(UUID.randomUUID(), this, attributes); + groupProvider.setDesiredState(State.INITIALISING, State.ACTIVE); + addGroupProvider(groupProvider); + return groupProvider; + } + private void addGroupProvider(GroupProvider groupProvider) { synchronized (_groupProviders) @@ -613,45 +592,92 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat groupProvider.addChangeListener(this); } - private boolean deleteGroupProvider(GroupProvider object) + private boolean deleteGroupProvider(GroupProvider groupProvider) + { + GroupProvider removedGroupProvider = null; + synchronized (_groupProviders) + { + removedGroupProvider = _groupProviders.remove(groupProvider.getName()); + } + + if(removedGroupProvider != null) + { + removedGroupProvider.removeChangeListener(this); + } + + return removedGroupProvider != null; + } + + private KeyStore createKeyStore(Map<String, Object> attributes) + { + KeyStore keyStore = new KeyStoreAdapter(UUIDGenerator.generateRandomUUID(), this, attributes); + addKeyStore(keyStore); + + return keyStore; + } + + private TrustStore createTrustStore(Map<String, Object> attributes) { - throw new UnsupportedOperationException("Not implemented yet!"); + TrustStore trustStore = new TrustStoreAdapter(UUIDGenerator.generateRandomUUID(), this, attributes); + addTrustStore(trustStore); + + return trustStore; } private void addKeyStore(KeyStore keyStore) { synchronized (_keyStores) { - if(_keyStores.containsKey(keyStore.getId())) + if(_keyStores.containsKey(keyStore.getName())) { - throw new IllegalConfigurationException("Cannot add KeyStore because one with id " + keyStore.getId() + " already exists"); + throw new IllegalConfigurationException("Can't add KeyStore because one with name " + keyStore.getName() + " already exists"); } - _keyStores.put(keyStore.getId(), keyStore); + _keyStores.put(keyStore.getName(), keyStore); } keyStore.addChangeListener(this); } private boolean deleteKeyStore(KeyStore object) { - throw new UnsupportedOperationException("Not implemented yet!"); + synchronized(_keyStores) + { + String name = object.getName(); + KeyStore removedKeyStore = _keyStores.remove(name); + if(removedKeyStore != null) + { + removedKeyStore.removeChangeListener(this); + } + + return removedKeyStore != null; + } } private void addTrustStore(TrustStore trustStore) { synchronized (_trustStores) { - if(_trustStores.containsKey(trustStore.getId())) + if(_trustStores.containsKey(trustStore.getName())) { - throw new IllegalConfigurationException("Cannot add TrustStore because one with id " + trustStore.getId() + " already exists"); + throw new IllegalConfigurationException("Can't add TrustStore because one with name " + trustStore.getName() + " already exists"); } - _trustStores.put(trustStore.getId(), trustStore); + _trustStores.put(trustStore.getName(), trustStore); } trustStore.addChangeListener(this); } private boolean deleteTrustStore(TrustStore object) { - throw new UnsupportedOperationException("Not implemented yet!"); + synchronized(_trustStores) + { + String name = object.getName(); + TrustStore removedTrustStore = _trustStores.remove(name); + if(removedTrustStore != null) + { + removedTrustStore.removeChangeListener(this); + } + + return removedTrustStore != null; + } } @Override @@ -718,21 +744,17 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { return QpidProperties.getReleaseVersion(); } - else if(SUPPORTED_STORE_TYPES.equals(name)) + else if(SUPPORTED_BROKER_STORE_TYPES.equals(name)) { - return _supportedStoreTypes; + return _supportedBrokerStoreTypes; } - else if(SUPPORTED_AUTHENTICATION_PROVIDERS.equals(name)) + else if(SUPPORTED_VIRTUALHOST_STORE_TYPES.equals(name)) { - return _authenticationProviderFactory.getSupportedAuthenticationProviders(); + return _supportedVirtualHostStoreTypes; } - else if (KEY_STORE_PASSWORD.equals(name) || TRUST_STORE_PASSWORD.equals(name) || PEER_STORE_PASSWORD.equals(name)) + else if(SUPPORTED_AUTHENTICATION_PROVIDERS.equals(name)) { - if (getActualAttributes().get(name) != null) - { - return DUMMY_PASSWORD_MASK; - } - return null; + return _authenticationProviderFactory.getSupportedAuthenticationProviders(); } else if (MODEL_VERSION.equals(name)) { @@ -760,6 +782,12 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { removedPort = _portAdapters.remove(portAdapter.getPort()); } + + if (removedPort != null) + { + removedPort.removeChangeListener(this); + } + return removedPort != null; } @@ -770,6 +798,12 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { removedAuthenticationProvider = _authenticationProviders.remove(authenticationProvider.getId()); } + + if(removedAuthenticationProvider != null) + { + removedAuthenticationProvider.removeChangeListener(this); + } + return removedAuthenticationProvider != null; } @@ -794,6 +828,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { changeState(_groupProviders, currentState, State.ACTIVE, false); changeState(_authenticationProviders, currentState, State.ACTIVE, false); + changeState(_accessControlProviders, currentState, State.ACTIVE, false); CurrentActor.set(new BrokerActor(getRootMessageLogger())); try @@ -807,6 +842,11 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat changeState(_portAdapters, currentState,State.ACTIVE, false); changeState(_plugins, currentState,State.ACTIVE, false); + + if (isManagementMode()) + { + CurrentActor.get().message(BrokerMessages.MANAGEMENT_MODE(BrokerOptions.MANAGEMENT_MODE_USER_NAME, _brokerOptions.getManagementModePassword())); + } return true; } else if (desiredState == State.STOPPED) @@ -865,6 +905,10 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { childDeleted = deleteAuthenticationProvider((AuthenticationProvider)object); } + else if(object instanceof AccessControlProvider) + { + childDeleted = deleteAccessControlProvider((AccessControlProvider)object); + } else if(object instanceof Port) { childDeleted = deletePort((Port)object); @@ -885,6 +929,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { childDeleted = deleteTrustStore((TrustStore)object); } + if(childDeleted) { childRemoved(object); @@ -938,6 +983,10 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { addAuthenticationProvider((AuthenticationProvider)object); } + else if(object instanceof AccessControlProvider) + { + addAccessControlProvider((AccessControlProvider)object); + } else if(object instanceof Port) { addPort((Port)object); @@ -996,7 +1045,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat public SubjectCreator getSubjectCreator(SocketAddress localAddress) { InetSocketAddress inetSocketAddress = (InetSocketAddress)localAddress; - AuthenticationProvider provider = _defaultAuthenticationProvider; + AuthenticationProvider provider = null; Collection<Port> ports = getPorts(); for (Port p : ports) { @@ -1006,6 +1055,12 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat break; } } + + if(provider == null) + { + throw new IllegalConfigurationException("Unable to determine authentication provider for address: " + localAddress); + } + return provider.getSubjectCreator(); } @@ -1034,18 +1089,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat } @Override - public KeyStore getDefaultKeyStore() - { - return _keyStores.get(_defaultKeyStoreId); - } - - @Override - public TrustStore getDefaultTrustStore() - { - return _trustStores.get(_defaultTrustStoreId); - } - - @Override public TaskExecutor getTaskExecutor() { return super.getTaskExecutor(); @@ -1054,13 +1097,9 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat @Override protected void changeAttributes(Map<String, Object> attributes) { - //TODO: Add management mode check Map<String, Object> convertedAttributes = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES); validateAttributes(convertedAttributes); - boolean keyStoreChanged = false; - boolean trustStoreChanged = false; - boolean peerStoreChanged = false; Collection<String> names = AVAILABLE_ATTRIBUTES; for (String name : names) { @@ -1070,74 +1109,14 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat Object expected = getAttribute(name); if (changeAttribute(name, expected, desired)) { - if (GROUP_FILE.equals(name)) - { - createGroupProvider(); - } - else if (DEFAULT_AUTHENTICATION_PROVIDER.equals(name)) - { - if (!_defaultAuthenticationProvider.getName().equals(desired)) - { - _defaultAuthenticationProvider = findAuthenticationProviderByName((String)desired); - } - } - else if (KEY_STORE_PATH.equals(name) || KEY_STORE_PASSWORD.equals(name) || KEY_STORE_CERT_ALIAS.equals(name)) - { - keyStoreChanged = true; - } - else if (TRUST_STORE_PATH.equals(name) || TRUST_STORE_PASSWORD.equals(name)) - { - trustStoreChanged = true; - } - else if (PEER_STORE_PATH.equals(name) || PEER_STORE_PASSWORD.equals(name)) - { - peerStoreChanged = true; - } attributeSet(name, expected, desired); } } } - if (keyStoreChanged) - { - createKeyStore(); - } - if (trustStoreChanged) - { - createTrustStore(); - } - if (peerStoreChanged) - { - createPeerStore(); - } } private void validateAttributes(Map<String, Object> convertedAttributes) { - String aclFile = (String) convertedAttributes.get(ACL_FILE); - if (aclFile != null) - { - // create a security manager to validate the ACL specified in file - new SecurityManager(aclFile); - } - String groupFile = (String) convertedAttributes.get(GROUP_FILE); - if (groupFile != null) - { - // create a group manager to validate the groups specified in file - new FileGroupManager(groupFile); - } - validateKeyStoreAttributes(convertedAttributes, "key store", KEY_STORE_PATH, KEY_STORE_PASSWORD, KEY_STORE_CERT_ALIAS); - validateKeyStoreAttributes(convertedAttributes, "trust store", TRUST_STORE_PATH, TRUST_STORE_PASSWORD, null); - validateKeyStoreAttributes(convertedAttributes, "peer store", PEER_STORE_PATH, PEER_STORE_PASSWORD, null); - String defaultAuthenticationProvider = (String) convertedAttributes.get(DEFAULT_AUTHENTICATION_PROVIDER); - if (defaultAuthenticationProvider != null) - { - AuthenticationProvider provider = findAuthenticationProviderByName(defaultAuthenticationProvider); - if (provider == null) - { - throw new IllegalConfigurationException("Authentication provider with name " + defaultAuthenticationProvider - + " canot be set as a default as it does not exist"); - } - } String defaultVirtualHost = (String) convertedAttributes.get(DEFAULT_VIRTUAL_HOST); if (defaultVirtualHost != null) { @@ -1176,59 +1155,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat } } - private void validateKeyStoreAttributes(Map<String, Object> convertedAttributes, String type, String pathAttribute, - String passwordAttribute, String aliasAttribute) - { - String keyStoreFile = (String) convertedAttributes.get(pathAttribute); - String password = (String) convertedAttributes.get(passwordAttribute); - String alias = aliasAttribute!= null? (String) convertedAttributes.get(aliasAttribute) : null; - if (keyStoreFile != null || password != null || alias != null) - { - if (keyStoreFile == null) - { - keyStoreFile = (String) getActualAttributes().get(pathAttribute); - } - if (password == null) - { - password = (String) getActualAttributes().get(passwordAttribute); - } - java.security.KeyStore keyStore = null; - try - { - keyStore = SSLUtil.getInitializedKeyStore(keyStoreFile, password, java.security.KeyStore.getDefaultType()); - } - catch (Exception e) - { - throw new IllegalConfigurationException("Cannot instantiate " + type + " at " + keyStoreFile, e); - } - if (aliasAttribute != null) - { - if (alias == null) - { - alias = (String) getActualAttributes().get(aliasAttribute); - } - if (alias != null) - { - Certificate cert = null; - try - { - cert = keyStore.getCertificate(alias); - } - catch (KeyStoreException e) - { - // key store should be initialized above - throw new RuntimeException("Key store has not been initialized", e); - } - if (cert == null) - { - throw new IllegalConfigurationException("Cannot find a certificate with alias " + alias + "in " + type - + " : " + keyStoreFile); - } - } - } - } - } - @Override protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException { @@ -1260,6 +1186,15 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat @Override public boolean isManagementMode() { - return _managementMode; + return _brokerOptions.isManagementMode(); + } + + @Override + public Collection<AccessControlProvider> getAccessControlProviders() + { + synchronized (_accessControlProviders) + { + return new ArrayList<AccessControlProvider>(_accessControlProviders.values()); + } } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderAdapter.java index 9ad58f9670..a0e5bdb0e8 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderAdapter.java @@ -24,6 +24,7 @@ import java.security.Principal; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -48,9 +49,11 @@ public class GroupProviderAdapter extends AbstractAdapter implements { private final GroupManager _groupManager; private final Broker _broker; - public GroupProviderAdapter(UUID id, GroupManager groupManager, Broker broker) + private Collection<String> _supportedAttributes; + + public GroupProviderAdapter(UUID id, Broker broker, GroupManager groupManager, Map<String, Object> attributes, Collection<String> attributeNames) { - super(id, broker.getTaskExecutor()); + super(id, null, null, broker.getTaskExecutor()); if (groupManager == null) { @@ -58,13 +61,37 @@ public class GroupProviderAdapter extends AbstractAdapter implements } _groupManager = groupManager; _broker = broker; + _supportedAttributes = createSupportedAttributes(attributeNames); addParent(Broker.class, broker); + + // set attributes now after all attribute names are known + if (attributes != null) + { + for (String name : _supportedAttributes) + { + if (attributes.containsKey(name)) + { + changeAttribute(name, null, attributes.get(name)); + } + } + } + } + + protected Collection<String> createSupportedAttributes(Collection<String> factoryAttributes) + { + List<String> attributesNames = new ArrayList<String>(AVAILABLE_ATTRIBUTES); + if (factoryAttributes != null) + { + attributesNames.addAll(factoryAttributes); + } + + return Collections.unmodifiableCollection(attributesNames); } @Override public String getName() { - return _groupManager.getClass().getSimpleName(); + return (String)getAttribute(NAME); } @Override @@ -129,17 +156,13 @@ public class GroupProviderAdapter extends AbstractAdapter implements @Override public Collection<String> getAttributeNames() { - return GroupProvider.AVAILABLE_ATTRIBUTES; + return _supportedAttributes; } @Override public Object getAttribute(String name) { - if (TYPE.equals(name)) - { - return getName(); - } - else if (CREATED.equals(name)) + if (CREATED.equals(name)) { // TODO } @@ -155,10 +178,6 @@ public class GroupProviderAdapter extends AbstractAdapter implements { return LifetimePolicy.PERMANENT; } - else if (NAME.equals(name)) - { - return getName(); - } else if (STATE.equals(name)) { return State.ACTIVE; // TODO @@ -220,11 +239,91 @@ public class GroupProviderAdapter extends AbstractAdapter implements } } + public GroupManager getGroupManager() + { + return _groupManager; + } + private SecurityManager getSecurityManager() { return _broker.getSecurityManager(); } + @Override + protected boolean setState(State currentState, State desiredState) + { + if (desiredState == State.ACTIVE) + { + _groupManager.open(); + return true; + } + else if (desiredState == State.STOPPED) + { + _groupManager.close(); + return true; + } + else if (desiredState == State.DELETED) + { + _groupManager.close(); + _groupManager.onDelete(); + return true; + } + return false; + } + + public Set<Principal> getGroupPrincipalsForUser(String username) + { + return _groupManager.getGroupPrincipalsForUser(username); + } + + @Override + protected void childAdded(ConfiguredObject child) + { + // no-op, prevent storing groups in the broker store + } + + @Override + protected void childRemoved(ConfiguredObject child) + { + // no-op, as per above, groups are not in the store + } + + @Override + protected void authoriseSetDesiredState(State currentState, State desiredState) throws AccessControlException + { + if(desiredState == State.DELETED) + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), GroupProvider.class, Operation.DELETE)) + { + throw new AccessControlException("Deletion of groups provider is denied"); + } + } + } + + @Override + protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), GroupProvider.class, Operation.UPDATE)) + { + throw new AccessControlException("Setting of group provider attributes is denied"); + } + } + + @Override + protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), GroupProvider.class, Operation.UPDATE)) + { + throw new AccessControlException("Setting of group provider attributes is denied"); + } + } + + @Override + protected void changeAttributes(Map<String, Object> attributes) + { + throw new UnsupportedOperationException("Changing attributes on group providers is not supported."); + } + private class GroupAdapter extends AbstractAdapter implements Group { private final String _group; @@ -526,23 +625,5 @@ public class GroupProviderAdapter extends AbstractAdapter implements } } - @Override - protected boolean setState(State currentState, State desiredState) - { - if (desiredState == State.ACTIVE) - { - return true; - } - else if (desiredState == State.STOPPED) - { - return true; - } - // TODO: DELETE state is ignored for now - return false; - } - public Set<Principal> getGroupPrincipalsForUser(String username) - { - return _groupManager.getGroupPrincipalsForUser(username); - } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderFactory.java new file mode 100644 index 0000000000..1d3ccd81b3 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderFactory.java @@ -0,0 +1,118 @@ +/* + * + * 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.model.adapter; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.GroupProvider; +import org.apache.qpid.server.plugin.GroupManagerFactory; +import org.apache.qpid.server.plugin.QpidServiceLoader; +import org.apache.qpid.server.security.group.GroupManager; + +public class GroupProviderFactory +{ + private final Map<String, GroupManagerFactory> _factories; + private Collection<String> _supportedGroupProviders; + + public GroupProviderFactory(QpidServiceLoader<GroupManagerFactory> groupManagerFactoryServiceLoader) + { + Iterable<GroupManagerFactory> factories = groupManagerFactoryServiceLoader.instancesOf(GroupManagerFactory.class); + + Map<String, GroupManagerFactory> registeredGroupProviderFactories = new HashMap<String, GroupManagerFactory>(); + for (GroupManagerFactory factory : factories) + { + GroupManagerFactory existingFactory = registeredGroupProviderFactories.put(factory.getType(), factory); + if (existingFactory != null) + { + throw new IllegalConfigurationException("Group provider factory of the same type '" + factory.getType() + + "' is already registered using class '" + existingFactory.getClass().getName() + + "', can not register class '" + factory.getClass().getName() + "'"); + } + } + _factories = registeredGroupProviderFactories; + _supportedGroupProviders = Collections.unmodifiableCollection(registeredGroupProviderFactories.keySet()); + } + + /** + * Creates {@link GroupProvider} for given ID, {@link Broker} and attributes. + * <p> + * The configured {@link GroupManagerFactory}'s are used to try to create the {@link GroupProvider}. The first non-null + * instance is returned. The factories are used in non-deterministic order. + */ + public GroupProvider create(UUID id, Broker broker, Map<String, Object> attributes) + { + GroupProviderAdapter authenticationProvider = createGroupProvider(id, broker, attributes); + authenticationProvider.getGroupManager().onCreate(); + return authenticationProvider; + } + + /** + * Recovers {@link GroupProvider} with given ID, {@link Broker} and attributes. + * <p> + * The configured {@link GroupManagerFactory}'s are used to try to create the {@link GroupProvider}. The first non-null + * instance is returned. The factories are used in non-deterministic order. + */ + public GroupProvider recover(UUID id, Broker broker, Map<String, Object> attributes) + { + return createGroupProvider(id, broker, attributes); + } + + public Collection<String> getSupportedGroupProviders() + { + return _supportedGroupProviders; + } + + private GroupProviderAdapter createGroupProvider(UUID id, Broker broker, Map<String, Object> attributes) + { + for (GroupManagerFactory factory : _factories.values()) + { + GroupManager manager = factory.createInstance(attributes); + if (manager != null) + { + verifyGroupManager(manager, broker); + return new GroupProviderAdapter(id, broker, manager, attributes,factory.getAttributeNames()); + } + } + throw new IllegalConfigurationException("No group provider factory found for configuration attributes " + attributes); + } + + private void verifyGroupManager(GroupManager manager, Broker broker) + { + Collection<GroupProvider> groupProviders = broker.getGroupProviders(); + for (GroupProvider groupProvider : groupProviders) + { + if (groupProvider instanceof GroupProviderAdapter) + { + GroupManager providerManager = ((GroupProviderAdapter) groupProvider).getGroupManager(); + if (manager.equals(providerManager)) + { + throw new IllegalConfigurationException("A group provider with the same settings already exists"); + } + } + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/KeyStoreAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/KeyStoreAdapter.java index 113d895e62..4d4d3bb31d 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/KeyStoreAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/KeyStoreAdapter.java @@ -20,23 +20,63 @@ */ package org.apache.qpid.server.model.adapter; +import java.lang.reflect.Type; +import java.security.AccessControlException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.UUID; +import javax.net.ssl.KeyManagerFactory; + +import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.IntegrityViolationException; import org.apache.qpid.server.model.KeyStore; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.State; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.util.MapValueConverter; +import org.apache.qpid.transport.network.security.ssl.SSLUtil; public class KeyStoreAdapter extends AbstractKeyStoreAdapter implements KeyStore { + @SuppressWarnings("serial") + public static final Map<String, Type> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Type>(){{ + put(NAME, String.class); + put(PATH, String.class); + put(PASSWORD, String.class); + put(TYPE, String.class); + put(CERTIFICATE_ALIAS, String.class); + put(KEY_MANAGER_FACTORY_ALGORITHM, String.class); + }}); + + @SuppressWarnings("serial") + public static final Map<String, Object> DEFAULTS = Collections.unmodifiableMap(new HashMap<String, Object>(){{ + put(KeyStore.TYPE, DEFAULT_KEYSTORE_TYPE); + put(KeyStore.KEY_MANAGER_FACTORY_ALGORITHM, KeyManagerFactory.getDefaultAlgorithm()); + }}); + + private Broker _broker; public KeyStoreAdapter(UUID id, Broker broker, Map<String, Object> attributes) { - super(id, broker, attributes); - if (attributes.get(CERTIFICATE_ALIAS) != null) - { - changeAttribute(CERTIFICATE_ALIAS, null, attributes.get(CERTIFICATE_ALIAS)); - } + super(id, broker, DEFAULTS, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES)); + _broker = broker; + + String keyStorePath = (String)getAttribute(KeyStore.PATH); + String keyStorePassword = getPassword(); + String keyStoreType = (String)getAttribute(KeyStore.TYPE); + String keyManagerFactoryAlgorithm = (String)getAttribute(KeyStore.KEY_MANAGER_FACTORY_ALGORITHM); + String certAlias = (String)getAttribute(KeyStore.CERTIFICATE_ALIAS); + + validateKeyStoreAttributes(keyStoreType, keyStorePath, keyStorePassword, + certAlias, keyManagerFactoryAlgorithm); } @Override @@ -45,4 +85,129 @@ public class KeyStoreAdapter extends AbstractKeyStoreAdapter implements KeyStore return AVAILABLE_ATTRIBUTES; } + @Override + protected boolean setState(State currentState, State desiredState) + { + if(desiredState == State.DELETED) + { + // verify that it is not in use + String storeName = getName(); + + Collection<Port> ports = new ArrayList<Port>(_broker.getPorts()); + for (Port port : ports) + { + if (storeName.equals(port.getAttribute(Port.KEY_STORE))) + { + throw new IntegrityViolationException("Key store '" + storeName + "' can't be deleted as it is in use by a port:" + port.getName()); + } + } + + return true; + } + + return false; + } + + @Override + protected void authoriseSetDesiredState(State currentState, State desiredState) throws AccessControlException + { + if(desiredState == State.DELETED) + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), KeyStore.class, Operation.DELETE)) + { + throw new AccessControlException("Deletion of key store is denied"); + } + } + } + + @Override + protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException + { + authoriseSetAttribute(); + } + + @Override + protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException + { + authoriseSetAttribute(); + } + + private void authoriseSetAttribute() + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), KeyStore.class, Operation.UPDATE)) + { + throw new AccessControlException("Setting key store attributes is denied"); + } + } + + @Override + protected void changeAttributes(Map<String, Object> attributes) + { + Map<String, Object> changedValues = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES); + if(changedValues.containsKey(KeyStore.NAME)) + { + String newName = (String) changedValues.get(KeyStore.NAME); + if(!getName().equals(newName)) + { + throw new IllegalConfigurationException("Changing the key store name is not allowed"); + } + } + + Map<String, Object> merged = generateEffectiveAttributes(changedValues); + + String keyStorePath = (String)merged.get(KeyStore.PATH); + String keyStorePassword = (String) merged.get(KeyStore.PASSWORD); + String keyStoreType = (String)merged.get(KeyStore.TYPE); + String keyManagerFactoryAlgorithm = (String)merged.get(KeyStore.KEY_MANAGER_FACTORY_ALGORITHM); + String certAlias = (String)merged.get(KeyStore.CERTIFICATE_ALIAS); + + validateKeyStoreAttributes(keyStoreType, keyStorePath, keyStorePassword, + certAlias, keyManagerFactoryAlgorithm); + + super.changeAttributes(changedValues); + } + + private void validateKeyStoreAttributes(String type, String keyStorePath, + String keyStorePassword, String alias, + String keyManagerFactoryAlgorithm) + { + java.security.KeyStore keyStore = null; + try + { + keyStore = SSLUtil.getInitializedKeyStore(keyStorePath, keyStorePassword, type); + } + catch (Exception e) + { + throw new IllegalConfigurationException("Cannot instantiate key store at " + keyStorePath, e); + } + + if (alias != null) + { + Certificate cert = null; + try + { + cert = keyStore.getCertificate(alias); + } + catch (KeyStoreException e) + { + // key store should be initialized above + throw new RuntimeException("Key store has not been initialized", e); + } + if (cert == null) + { + throw new IllegalConfigurationException("Cannot find a certificate with alias " + alias + + "in key store : " + keyStorePath); + } + } + + try + { + KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm); + } + catch (NoSuchAlgorithmException e) + { + throw new IllegalConfigurationException("Unknown keyManagerFactoryAlgorithm: " + + keyManagerFactoryAlgorithm); + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java index 4250de17a7..af6b1421a7 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java @@ -37,12 +37,14 @@ import org.apache.qpid.server.model.AuthenticationProvider; 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.KeyStore; import org.apache.qpid.server.model.LifetimePolicy; import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.Protocol; import org.apache.qpid.server.model.State; import org.apache.qpid.server.model.Statistics; import org.apache.qpid.server.model.Transport; +import org.apache.qpid.server.model.TrustStore; import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.model.VirtualHostAlias; import org.apache.qpid.server.security.access.Operation; @@ -58,6 +60,8 @@ public class PortAdapter extends AbstractAdapter implements Port put(NAME, String.class); put(PROTOCOLS, new ParameterizedTypeImpl(Set.class, Protocol.class)); put(TRANSPORTS, new ParameterizedTypeImpl(Set.class, Transport.class)); + put(TRUST_STORES, new ParameterizedTypeImpl(Set.class, String.class)); + put(KEY_STORE, String.class); put(PORT, Integer.class); put(TCP_NO_DELAY, Boolean.class); put(RECEIVE_BUFFER_SIZE, Integer.class); @@ -78,6 +82,24 @@ public class PortAdapter extends AbstractAdapter implements Port super(id, defaults, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES), taskExecutor); _broker = broker; State state = MapValueConverter.getEnumAttribute(State.class, STATE, attributes, State.INITIALISING); + + Collection<Protocol> protocols = getProtocols(); + boolean rmiRegistry = protocols != null && protocols.contains(Protocol.RMI); + if (!rmiRegistry) + { + String authProvider = (String)getAttribute(Port.AUTHENTICATION_PROVIDER); + if (authProvider == null) + { + throw new IllegalConfigurationException("An authentication provider must be specified for port : " + getName()); + } + _authenticationProvider = broker.findAuthenticationProviderByName(authProvider); + + if(_authenticationProvider == null) + { + throw new IllegalConfigurationException("The authentication provider '" + authProvider + "' could not be found for port : " + getName()); + } + } + _state = new AtomicReference<State>(state); addParent(Broker.class, broker); } @@ -346,11 +368,6 @@ public class PortAdapter extends AbstractAdapter implements Port return _authenticationProvider; } - public void setAuthenticationProvider(AuthenticationProvider authenticationProvider) - { - _authenticationProvider = authenticationProvider; - } - @Override protected void changeAttributes(Map<String, Object> attributes) { @@ -373,16 +390,40 @@ public class PortAdapter extends AbstractAdapter implements Port boolean requiresCertificate = (needClientCertificate != null && needClientCertificate.booleanValue()) || (wantClientCertificate != null && wantClientCertificate.booleanValue()); - if (transports != null && transports.contains(Transport.SSL)) + String keyStoreName = (String) merged.get(KEY_STORE); + boolean hasKeyStore = keyStoreName != null; + if(keyStoreName != null) { - if (_broker.getKeyStores().isEmpty()) + if (_broker.findKeyStoreByName(keyStoreName) == null) { - throw new IllegalConfigurationException("Can't create port which requires SSL as the broker has no keystore configured."); + throw new IllegalConfigurationException("Can't find key store with name '" + keyStoreName + "' for port " + getName()); } + } - if (_broker.getTrustStores().isEmpty() && requiresCertificate) + Set<String> trustStoreNames = (Set<String>) merged.get(TRUST_STORES); + boolean hasTrustStore = trustStoreNames != null && !trustStoreNames.isEmpty(); + if(hasTrustStore) + { + for (String trustStoreName : trustStoreNames) { - throw new IllegalConfigurationException("Can't create port which requests SSL client certificates as the broker has no trust/peer stores configured."); + if (_broker.findTrustStoreByName(trustStoreName) == null) + { + throw new IllegalConfigurationException("Cannot find trust store with name '" + trustStoreName + "'"); + } + } + } + + boolean usesSsl = transports != null && transports.contains(Transport.SSL); + if (usesSsl) + { + if (keyStoreName == null) + { + throw new IllegalConfigurationException("Can't create port which requires SSL but has no key store configured."); + } + + if (!hasTrustStore && requiresCertificate) + { + throw new IllegalConfigurationException("Can't create port which requests SSL client certificates but has no trust store configured."); } } else @@ -393,9 +434,14 @@ public class PortAdapter extends AbstractAdapter implements Port } } - if (protocols != null && protocols.contains(Protocol.HTTPS) && _broker.getKeyStores().isEmpty()) + if (protocols != null && protocols.contains(Protocol.HTTPS) && !hasKeyStore) + { + throw new IllegalConfigurationException("Can't create port which requires SSL but has no key store configured."); + } + + if (protocols != null && protocols.contains(Protocol.RMI) && usesSsl) { - throw new IllegalConfigurationException("Can't create port which requires SSL as the broker has no keystore configured."); + throw new IllegalConfigurationException("Can't create RMI Registry port which requires SSL."); } String authenticationProviderName = (String)merged.get(AUTHENTICATION_PROVIDER); @@ -418,6 +464,14 @@ public class PortAdapter extends AbstractAdapter implements Port + authenticationProviderName + "'"); } } + else + { + if (protocols != null && !protocols.contains(Protocol.RMI)) + { + throw new IllegalConfigurationException("An authentication provider must be specified"); + } + } + super.changeAttributes(converted); } @@ -450,4 +504,42 @@ public class PortAdapter extends AbstractAdapter implements Port throw new AccessControlException("Setting of port attributes is denied"); } } + + @Override + public KeyStore getKeyStore() + { + String keyStoreName = (String)getAttribute(Port.KEY_STORE); + KeyStore keyStore = _broker.findKeyStoreByName(keyStoreName); + + if (keyStoreName != null && keyStore == null) + { + throw new IllegalConfigurationException("Can't find key store with name '" + keyStoreName + "' for port " + getName()); + } + + return keyStore; + } + + @Override + public Collection<TrustStore> getTrustStores() + { + Set<String> trustStoreNames = (Set<String>) getAttribute(TRUST_STORES); + boolean hasTrustStoreName = trustStoreNames != null && !trustStoreNames.isEmpty(); + + final Collection<TrustStore> trustStores = new ArrayList<TrustStore>(); + if(hasTrustStoreName) + { + for (String name : trustStoreNames) + { + TrustStore trustStore = _broker.findTrustStoreByName(name); + if (trustStore == null) + { + throw new IllegalConfigurationException("Can't find trust store with name '" + name + "' for port " + getName()); + } + + trustStores.add(trustStore); + } + } + + return trustStores; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortFactory.java index ffbd24997a..2efe189d73 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortFactory.java @@ -106,9 +106,9 @@ public class PortFactory port = new AmqpPortAdapter(id, broker, attributes, defaults, broker.getTaskExecutor()); boolean useClientAuth = (Boolean) port.getAttribute(Port.NEED_CLIENT_AUTH) || (Boolean) port.getAttribute(Port.WANT_CLIENT_AUTH); - if(useClientAuth && broker.getTrustStores().isEmpty()) + if(useClientAuth && port.getTrustStores().isEmpty()) { - throw new IllegalConfigurationException("Can't create port which requests SSL client certificates as the broker has no trust/peer stores configured."); + throw new IllegalConfigurationException("Can't create port which requests SSL client certificates but has no trust stores configured."); } if(useClientAuth && !port.getTransports().contains(Transport.SSL)) @@ -142,13 +142,19 @@ public class PortFactory defaults.put(Port.NAME, portValue + "-" + protocol.name()); port = new PortAdapter(id, broker, attributes, defaults, broker.getTaskExecutor()); + + boolean rmiPort = port.getProtocols().contains(Protocol.RMI); + if (rmiPort && port.getTransports().contains(Transport.SSL)) + { + throw new IllegalConfigurationException("Can't create RMI registry port which requires SSL"); + } } if(port.getTransports().contains(Transport.SSL) || port.getProtocols().contains(Protocol.HTTPS)) { - if(broker.getKeyStores().isEmpty()) + if(port.getKeyStore() == null) { - throw new IllegalConfigurationException("Can't create port which requires SSL as the broker has no keystore configured."); + throw new IllegalConfigurationException("Can't create port which requires SSL but has no key store configured."); } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/TrustStoreAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/TrustStoreAdapter.java index bdffe605ec..06089e43c6 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/TrustStoreAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/TrustStoreAdapter.java @@ -20,18 +20,61 @@ */ package org.apache.qpid.server.model.adapter; +import java.lang.reflect.Type; +import java.security.AccessControlException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.UUID; +import javax.net.ssl.TrustManagerFactory; + +import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.IntegrityViolationException; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.State; import org.apache.qpid.server.model.TrustStore; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.util.MapValueConverter; +import org.apache.qpid.transport.network.security.ssl.SSLUtil; public class TrustStoreAdapter extends AbstractKeyStoreAdapter implements TrustStore { + @SuppressWarnings("serial") + public static final Map<String, Type> ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap<String, Type>(){{ + put(NAME, String.class); + put(PATH, String.class); + put(PASSWORD, String.class); + put(TYPE, String.class); + put(PEERS_ONLY, Boolean.class); + put(TRUST_MANAGER_FACTORY_ALGORITHM, String.class); + }}); + + @SuppressWarnings("serial") + public static final Map<String, Object> DEFAULTS = Collections.unmodifiableMap(new HashMap<String, Object>(){{ + put(TrustStore.TYPE, DEFAULT_KEYSTORE_TYPE); + put(TrustStore.PEERS_ONLY, Boolean.FALSE); + put(TrustStore.TRUST_MANAGER_FACTORY_ALGORITHM, TrustManagerFactory.getDefaultAlgorithm()); + }}); + + private Broker _broker; + public TrustStoreAdapter(UUID id, Broker broker, Map<String, Object> attributes) { - super(id, broker, attributes); + super(id, broker, DEFAULTS, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES)); + _broker = broker; + + String trustStorePath = (String) getAttribute(TrustStore.PATH); + String trustStorePassword = getPassword(); + String trustStoreType = (String) getAttribute(TrustStore.TYPE); + String trustManagerFactoryAlgorithm = (String) getAttribute(TrustStore.TRUST_MANAGER_FACTORY_ALGORITHM); + + validateTrustStoreAttributes(trustStoreType, trustStorePath, + trustStorePassword, trustManagerFactoryAlgorithm); } @Override @@ -40,4 +83,110 @@ public class TrustStoreAdapter extends AbstractKeyStoreAdapter implements TrustS return AVAILABLE_ATTRIBUTES; } + @Override + protected boolean setState(State currentState, State desiredState) + { + if(desiredState == State.DELETED) + { + // verify that it is not in use + String storeName = getName(); + + Collection<Port> ports = new ArrayList<Port>(_broker.getPorts()); + for (Port port : ports) + { + Collection<TrustStore> trustStores = port.getTrustStores(); + for(TrustStore store : trustStores) + { + if (storeName.equals(store.getAttribute(TrustStore.NAME))) + { + throw new IntegrityViolationException("Trust store '" + storeName + "' can't be deleted as it is in use by a port: " + port.getName()); + } + } + } + + return true; + } + + return false; + } + + @Override + protected void authoriseSetDesiredState(State currentState, State desiredState) throws AccessControlException + { + if(desiredState == State.DELETED) + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), TrustStore.class, Operation.DELETE)) + { + throw new AccessControlException("Deletion of key store is denied"); + } + } + } + + @Override + protected void authoriseSetAttribute(String name, Object expected, Object desired) throws AccessControlException + { + authoriseSetAttribute(); + } + + @Override + protected void authoriseSetAttributes(Map<String, Object> attributes) throws AccessControlException + { + authoriseSetAttribute(); + } + + private void authoriseSetAttribute() + { + if (!_broker.getSecurityManager().authoriseConfiguringBroker(getName(), TrustStore.class, Operation.UPDATE)) + { + throw new AccessControlException("Setting key store attributes is denied"); + } + } + + @Override + protected void changeAttributes(Map<String, Object> attributes) + { + Map<String, Object> changedValues = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES); + if(changedValues.containsKey(TrustStore.NAME)) + { + String newName = (String) changedValues.get(TrustStore.NAME); + if(!getName().equals(newName)) + { + throw new IllegalConfigurationException("Changing the trust store name is not allowed"); + } + } + + Map<String, Object> merged = generateEffectiveAttributes(changedValues); + + String trustStorePath = (String)merged.get(TrustStore.PATH); + String trustStorePassword = (String) merged.get(TrustStore.PASSWORD); + String trustStoreType = (String)merged.get(TrustStore.TYPE); + String trustManagerFactoryAlgorithm = (String)merged.get(TrustStore.TRUST_MANAGER_FACTORY_ALGORITHM); + + validateTrustStoreAttributes(trustStoreType, trustStorePath, + trustStorePassword, trustManagerFactoryAlgorithm); + + super.changeAttributes(changedValues); + } + + private void validateTrustStoreAttributes(String type, String trustStorePath, + String password, String trustManagerFactoryAlgorithm) + { + try + { + SSLUtil.getInitializedKeyStore(trustStorePath, password, type); + } + catch (Exception e) + { + throw new IllegalConfigurationException("Cannot instantiate trust store at " + trustStorePath, e); + } + + try + { + TrustManagerFactory.getInstance(trustManagerFactoryAlgorithm); + } + catch (NoSuchAlgorithmException e) + { + throw new IllegalConfigurationException("Unknown trustManagerFactoryAlgorithm: " + trustManagerFactoryAlgorithm); + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugin/AccessControlFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugin/AccessControlFactory.java index 7708b90efc..0694032db2 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugin/AccessControlFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugin/AccessControlFactory.java @@ -18,11 +18,34 @@ */ package org.apache.qpid.server.plugin; +import java.util.Collection; import java.util.Map; +import org.apache.qpid.server.model.AccessControlProvider; import org.apache.qpid.server.security.AccessControl; public interface AccessControlFactory { + public static final String ATTRIBUTE_TYPE = AccessControlProvider.TYPE; + AccessControl createInstance(Map<String, Object> attributes); + + /** + * Returns the access control provider type + * @return authentication provider type + */ + String getType(); + + /** + * Get the names of attributes of the access control which can be passed into + * {@link #createInstance(Map)} to create the group manager + * + * @return the collection of attribute names + */ + Collection<String> getAttributeNames(); + + /** + * @return returns human readable descriptions for the attributes + */ + Map<String, String> getAttributeDescriptions(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugin/ConfigurationStoreFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugin/ConfigurationStoreFactory.java index a625579ece..c451ae2adc 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugin/ConfigurationStoreFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugin/ConfigurationStoreFactory.java @@ -34,11 +34,12 @@ public interface ConfigurationStoreFactory /** * Creates and opens the store from a given location using initial store if provided. * <p> - * If location does not exists than a new store is created either empty or from the initial store if it is provided + * If location does not exist, or the overwrite option is specified, then a new store is created from the initial store if it is provided * * @param storeLocation store location * @param initialStore initial store + * @param overwrite overwrite existing store with initial store * @throws IllegalConfigurationException if store cannot be opened in the given location */ - public ConfigurationEntryStore createStore(String storeLocation, ConfigurationEntryStore initialStore); + public ConfigurationEntryStore createStore(String storeLocation, ConfigurationEntryStore initialStore, boolean overwrite); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugin/GroupManagerFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugin/GroupManagerFactory.java index 5d80ca24fd..2ab9701e2e 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugin/GroupManagerFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugin/GroupManagerFactory.java @@ -18,11 +18,34 @@ */ package org.apache.qpid.server.plugin; +import java.util.Collection; import java.util.Map; +import org.apache.qpid.server.model.GroupProvider; import org.apache.qpid.server.security.group.GroupManager; public interface GroupManagerFactory { + public static final String ATTRIBUTE_TYPE = GroupProvider.TYPE; + GroupManager createInstance(Map<String, Object> attributes); + + /** + * Returns the authentication provider type + * @return authentication provider type + */ + String getType(); + + /** + * Get the names of attributes the group manager which can be passed into {@link #createInstance(Map)} to create the + * group manager + * + * @return the collection of attribute names + */ + Collection<String> getAttributeNames(); + + /** + * @return returns human readable descriptions for the attributes + */ + Map<String, String> getAttributeDescriptions(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ProtocolEngine_1_0_0.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ProtocolEngine_1_0_0.java index 96b1172a57..c2a3256ba6 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ProtocolEngine_1_0_0.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ProtocolEngine_1_0_0.java @@ -22,6 +22,7 @@ package org.apache.qpid.server.protocol.v1_0; import java.net.SocketAddress; import java.nio.ByteBuffer; +import java.security.Principal; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; import java.util.logging.Logger; @@ -41,6 +42,7 @@ import org.apache.qpid.protocol.ServerProtocolEngine; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.protocol.v1_0.Connection_1_0; import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.security.auth.UsernamePrincipal; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.Sender; import org.apache.qpid.transport.network.NetworkConnection; @@ -170,6 +172,12 @@ public class ProtocolEngine_1_0_0 implements ServerProtocolEngine, FrameOutputHa { return subjectCreator.createSaslServer(mechanism, fqdn, null); } + + @Override + public Principal getAuthenticatedPrincipal(SaslServer server) + { + return new UsernamePrincipal(server.getAuthorizationID()); + } }; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ProtocolEngine_1_0_0_SASL.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ProtocolEngine_1_0_0_SASL.java index ec76d7e581..10ebb6e05e 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ProtocolEngine_1_0_0_SASL.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ProtocolEngine_1_0_0_SASL.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.protocol.v1_0; import java.io.PrintWriter; import java.net.SocketAddress; import java.nio.ByteBuffer; +import java.security.Principal; import java.util.logging.Level; import java.util.logging.Logger; import javax.security.sasl.SaslException; @@ -42,6 +43,7 @@ import org.apache.qpid.protocol.ServerProtocolEngine; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.protocol.v1_0.Connection_1_0; import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.security.auth.UsernamePrincipal; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.Sender; import org.apache.qpid.transport.network.NetworkConnection; @@ -162,7 +164,8 @@ public class ProtocolEngine_1_0_0_SASL implements ServerProtocolEngine, FrameOut Container container = new Container(_broker.getId().toString()); VirtualHost virtualHost = _broker.getVirtualHostRegistry().getVirtualHost((String)_broker.getAttribute(Broker.DEFAULT_VIRTUAL_HOST)); - _conn = new ConnectionEndpoint(container, asSaslServerProvider(_broker.getSubjectCreator(getLocalAddress()))); + SubjectCreator subjectCreator = _broker.getSubjectCreator(getLocalAddress()); + _conn = new ConnectionEndpoint(container, asSaslServerProvider(subjectCreator)); _conn.setRemoteAddress(getRemoteAddress()); _conn.setConnectionEventListener(new Connection_1_0(virtualHost, _conn, _connectionId)); _conn.setFrameOutputHandler(this); @@ -189,7 +192,7 @@ public class ProtocolEngine_1_0_0_SASL implements ServerProtocolEngine, FrameOut _sender.send(HEADER.duplicate()); _sender.flush(); - _conn.initiateSASL(); + _conn.initiateSASL(subjectCreator.getMechanisms().split(" ")); } @@ -201,7 +204,13 @@ public class ProtocolEngine_1_0_0_SASL implements ServerProtocolEngine, FrameOut @Override public SaslServer getSaslServer(String mechanism, String fqdn) throws SaslException { - return subjectCreator.createSaslServer(mechanism, fqdn, null); + return subjectCreator.createSaslServer(mechanism, fqdn, _network.getPeerPrincipal()); + } + + @Override + public Principal getAuthenticatedPrincipal(SaslServer server) + { + return new UsernamePrincipal(server.getAuthorizationID()); } }; } @@ -230,7 +239,7 @@ public class ProtocolEngine_1_0_0_SASL implements ServerProtocolEngine, FrameOut Binary bin = new Binary(data); RAW_LOGGER.fine("RECV[" + getRemoteAddress() + "] : " + bin.toString()); } - _readBytes += msg.remaining(); + _readBytes += msg.remaining(); switch(_state) { case A: @@ -392,7 +401,6 @@ public class ProtocolEngine_1_0_0_SASL implements ServerProtocolEngine, FrameOut RAW_LOGGER.fine("SEND[" + getRemoteAddress() + "] : " + bin.toString()); } - _sender.send(dup); _sender.flush(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/BaseQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/BaseQueue.java index 35b7cac1a1..cef7e2d0c8 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/BaseQueue.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/BaseQueue.java @@ -38,6 +38,7 @@ public interface BaseQueue extends TransactionLogResource void enqueue(ServerMessage message, boolean transactional, PostEnqueueAction action) throws AMQException; boolean isDurable(); + boolean isDeleted(); AMQShortString getNameShortString(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index 1379b375cf..fa5024c1fe 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -27,6 +27,7 @@ import java.util.TimerTask; import org.apache.log4j.Logger; import org.apache.qpid.common.Closeable; import org.apache.qpid.common.QpidProperties; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.BrokerProperties; import org.apache.qpid.server.configuration.ConfigurationEntryStore; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; @@ -89,7 +90,7 @@ public class ApplicationRegistry implements IApplicationRegistry initialiseStatistics(); } - public void initialise() throws Exception + public void initialise(BrokerOptions brokerOptions) throws Exception { // Create the RootLogger to be used during broker operation boolean statusUpdatesEnabled = Boolean.parseBoolean(System.getProperty(BrokerProperties.PROPERTY_STATUS_UPDATES, "true")); @@ -112,7 +113,7 @@ public class ApplicationRegistry implements IApplicationRegistry _taskExecutor = new TaskExecutor(); _taskExecutor.start(); - RecovererProvider provider = new DefaultRecovererProvider((StatisticsGatherer)this, _virtualHostRegistry, _logRecorder, _rootMessageLogger, _taskExecutor); + RecovererProvider provider = new DefaultRecovererProvider((StatisticsGatherer)this, _virtualHostRegistry, _logRecorder, _rootMessageLogger, _taskExecutor, brokerOptions); ConfiguredObjectRecoverer<? extends ConfiguredObject> brokerRecoverer = provider.getRecoverer(Broker.class.getSimpleName()); _broker = (Broker) brokerRecoverer.create(provider, _store.getRootEntry()); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java index d12258d194..7341922bd0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java @@ -20,17 +20,14 @@ */ package org.apache.qpid.server.registry; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.stats.StatisticsGatherer; public interface IApplicationRegistry extends StatisticsGatherer { - /** - * Initialise the application registry. All initialisation must be done in this method so that any components - * that need access to the application registry itself for initialisation are able to use it. Attempting to - * initialise in the constructor will lead to failures since the registry reference will not have been set. - */ - void initialise() throws Exception; + + void initialise(BrokerOptions brokerOptions) throws Exception; /** * Shutdown this Registry diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AccessControl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AccessControl.java index b4831f83e5..61e928a38c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AccessControl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AccessControl.java @@ -42,4 +42,25 @@ public interface AccessControl * Authorise an operation on an object defined by a set of properties. */ Result authorise(Operation operation, ObjectType objectType, ObjectProperties properties); + + /** + * Called to open any resources required by the implementation. + */ + void open(); + + /** + * Called to close any resources required by the implementation. + */ + void close(); + + /** + * Called when deleting to allow clearing any resources used by the implementation. + */ + void onDelete(); + + /** + * Called when first creating (but not when recovering after startup) to allow + * creating any resources required by the implementation. + */ + void onCreate(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java index 3fb3f70f2e..375b731d85 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SecurityManager.java @@ -23,6 +23,7 @@ import org.apache.log4j.Logger; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.model.AccessControlProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.ConfigurationChangeListener; import org.apache.qpid.server.model.ConfiguredObject; @@ -30,6 +31,7 @@ import org.apache.qpid.server.model.State; import org.apache.qpid.server.plugin.AccessControlFactory; import org.apache.qpid.server.plugin.QpidServiceLoader; import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.security.access.FileAccessControlProviderConstants; import org.apache.qpid.server.security.access.ObjectProperties; import org.apache.qpid.server.security.access.ObjectType; import org.apache.qpid.server.security.access.Operation; @@ -53,22 +55,13 @@ import static org.apache.qpid.server.security.access.Operation.UNBIND; import javax.security.auth.Subject; import java.net.SocketAddress; -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; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; -/** - * The security manager contains references to all loaded {@link AccessControl}s and delegates security decisions to them based - * on virtual host name. The plugins can be external <em>OSGi</em> .jar files that export the required classes or just internal - * objects for simpler plugins. - * - * @see AccessControl - */ public class SecurityManager implements ConfigurationChangeListener { private static final Logger _logger = Logger.getLogger(SecurityManager.class); @@ -78,9 +71,12 @@ public class SecurityManager implements ConfigurationChangeListener public static final ThreadLocal<Boolean> _accessChecksDisabled = new ClearingThreadLocal(false); - private Map<String, AccessControl> _globalPlugins = new ConcurrentHashMap<String, AccessControl>(); - private Map<String, AccessControl> _hostPlugins = new ConcurrentHashMap<String, AccessControl>(); - private Map<String, List<String>> _aclConfigurationToPluginNamesMapping = new ConcurrentHashMap<String, List<String>>(); + private ConcurrentHashMap<String, AccessControl> _globalPlugins = new ConcurrentHashMap<String, AccessControl>(); + private ConcurrentHashMap<String, AccessControl> _hostPlugins = new ConcurrentHashMap<String, AccessControl>(); + + private boolean _managementMode; + + private Broker _broker; /** * A special ThreadLocal, which calls remove() on itself whenever the value is @@ -128,34 +124,53 @@ public class SecurityManager implements ConfigurationChangeListener } /* - * Used by the VirtualHost to allow deferring to the broker level security plugins if required. + * Used by the Broker. */ - public SecurityManager(SecurityManager parent, String aclFile) + public SecurityManager(Broker broker, boolean managementMode) { - this(aclFile); - - // our global plugins are the parent's host plugins - _globalPlugins = parent._hostPlugins; + _managementMode = managementMode; + _broker = broker; } - public SecurityManager(String aclFile) + /* + * Used by the VirtualHost to allow deferring to the broker level security plugins if required. + */ + public SecurityManager(SecurityManager parent, String aclFile, String vhostName) { - configureACLPlugin(aclFile); + if(!_managementMode) + { + configureVirtualHostAclPlugin(aclFile, vhostName); + + // our global plugins are the parent's host plugins + _globalPlugins = parent._hostPlugins; + } } - private void configureACLPlugin(String aclFile) + private void configureVirtualHostAclPlugin(String aclFile, String vhostName) { - Map<String, Object> attributes = new HashMap<String, Object>(); - attributes.put("aclFile", aclFile); - - for (AccessControlFactory provider : (new QpidServiceLoader<AccessControlFactory>()).instancesOf(AccessControlFactory.class)) + if(aclFile != null) { - AccessControl accessControl = provider.createInstance(attributes); - if(accessControl != null) - { - addHostPlugin(accessControl); + Map<String, Object> attributes = new HashMap<String, Object>(); - mapAclConfigurationToPluginName(aclFile, accessControl.getClass().getName()); + attributes.put(AccessControlProvider.TYPE, FileAccessControlProviderConstants.ACL_FILE_PROVIDER_TYPE); + attributes.put(FileAccessControlProviderConstants.PATH, aclFile); + + for (AccessControlFactory provider : (new QpidServiceLoader<AccessControlFactory>()).instancesOf(AccessControlFactory.class)) + { + AccessControl accessControl = provider.createInstance(attributes); + accessControl.open(); + if(accessControl != null) + { + String pluginTypeName = getPluginTypeName(accessControl); + _hostPlugins.put(pluginTypeName, accessControl); + + if(_logger.isDebugEnabled()) + { + _logger.debug("Added access control to host plugins with name: " + vhostName); + } + + break; + } } } @@ -165,15 +180,9 @@ public class SecurityManager implements ConfigurationChangeListener } } - private void mapAclConfigurationToPluginName(String aclFile, String pluginName) + private String getPluginTypeName(AccessControl accessControl) { - List<String> pluginNames = _aclConfigurationToPluginNamesMapping.get(aclFile); - if (pluginNames == null) - { - pluginNames = new ArrayList<String>(); - _aclConfigurationToPluginNamesMapping.put(aclFile, pluginNames); - } - pluginNames.add(pluginName); + return accessControl.getClass().getName(); } public static Subject getThreadSubject() @@ -191,15 +200,6 @@ public class SecurityManager implements ConfigurationChangeListener return _logger; } - private static class CachedPropertiesMap extends LinkedHashMap<String, PublishAccessCheck> - { - @Override - protected boolean removeEldestEntry(Entry<String, PublishAccessCheck> eldest) - { - return size() >= 200; - } - } - private abstract class AccessCheck { abstract Result allowed(AccessControl plugin); @@ -500,16 +500,72 @@ public class SecurityManager implements ConfigurationChangeListener } } - - public void addHostPlugin(AccessControl plugin) - { - _hostPlugins.put(plugin.getClass().getName(), plugin); - } - @Override public void stateChanged(ConfiguredObject object, State oldState, State newState) { - // no op + if(_managementMode) + { + //AccessControl is disabled in ManagementMode + return; + } + + if(object instanceof AccessControlProvider) + { + if(newState == State.ACTIVE) + { + synchronized (_hostPlugins) + { + AccessControl accessControl = ((AccessControlProvider)object).getAccessControl(); + String pluginTypeName = getPluginTypeName(accessControl); + + _hostPlugins.put(pluginTypeName, accessControl); + } + } + else if(newState == State.DELETED) + { + synchronized (_hostPlugins) + { + AccessControl control = ((AccessControlProvider)object).getAccessControl(); + String pluginTypeName = getPluginTypeName(control); + + // Remove the type->control mapping for this type key only if the + // given control is actually referred to. + if(_hostPlugins.containsValue(control)) + { + // If we are removing this control, check if another of the same + // type already exists on the broker and use it in instead. + AccessControl other = null; + Collection<AccessControlProvider> providers = _broker.getAccessControlProviders(); + for(AccessControlProvider p : providers) + { + if(p == object || p.getActualState() != State.ACTIVE) + { + //we don't count ourself as another + continue; + } + + AccessControl ac = p.getAccessControl(); + if(pluginTypeName.equals(getPluginTypeName(ac))) + { + other = ac; + break; + } + } + + if(other != null) + { + //Another control of this type was found, use it instead + _hostPlugins.replace(pluginTypeName, control, other); + } + else + { + //No other was found, remove the type entirely + _hostPlugins.remove(pluginTypeName); + } + } + } + } + } } @Override @@ -527,29 +583,7 @@ public class SecurityManager implements ConfigurationChangeListener @Override public void attributeSet(ConfiguredObject object, String attributeName, Object oldAttributeValue, Object newAttributeValue) { - if (object instanceof Broker && Broker.ACL_FILE.equals(attributeName)) - { - // the code below is not thread safe, however, it should be fine in a management mode - // as there will be no user connected - - if (oldAttributeValue != null) - { - List<String> pluginNames = _aclConfigurationToPluginNamesMapping.remove(oldAttributeValue); - if (pluginNames != null) - { - for (String name : pluginNames) - { - _hostPlugins.remove(name); - } - } - } - if (newAttributeValue != null) - { - configureACLPlugin((String)newAttributeValue); - } - _immediatePublishPropsCache.clear(); - _publishPropsCache.clear(); - } + // no op } public boolean authoriseConfiguringBroker(String configuredObjectName, Class<? extends ConfiguredObject> configuredObjectType, Operation configuredObjectOperation) diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SubjectCreator.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SubjectCreator.java index 213f19dc5c..244ab0dd94 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SubjectCreator.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SubjectCreator.java @@ -35,6 +35,7 @@ import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; import org.apache.qpid.server.security.auth.AuthenticationResult; 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; /** @@ -153,4 +154,9 @@ public class SubjectCreator return Collections.unmodifiableSet(principals); } + + public boolean isAnonymousAuthenticationAllowed() + { + return _authenticationManager instanceof AnonymousAuthenticationManager; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessControlProviderConstants.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessControlProviderConstants.java new file mode 100644 index 0000000000..3a98a947df --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/FileAccessControlProviderConstants.java @@ -0,0 +1,27 @@ +/* + * + * 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.security.access; + +public class FileAccessControlProviderConstants +{ + public static final String ACL_FILE_PROVIDER_TYPE = "AclFile"; + public static final String PATH = "path"; +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/jmx/JMXPasswordAuthenticator.java index abb8677e90..bf8d489e61 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/jmx/JMXPasswordAuthenticator.java @@ -18,7 +18,7 @@ * under the License. * */ -package org.apache.qpid.server.security.auth.rmi; +package org.apache.qpid.server.security.auth.jmx; import java.net.SocketAddress; @@ -31,7 +31,7 @@ import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; import javax.management.remote.JMXAuthenticator; import javax.security.auth.Subject; -public class RMIPasswordAuthenticator implements JMXAuthenticator +public class JMXPasswordAuthenticator implements JMXAuthenticator { static final String UNABLE_TO_LOOKUP = "The broker was unable to lookup the user details"; static final String SHOULD_BE_STRING_ARRAY = "User details should be String[]"; @@ -45,7 +45,7 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator private final Broker _broker; private final SocketAddress _address; - public RMIPasswordAuthenticator(Broker broker, SocketAddress address) + public JMXPasswordAuthenticator(Broker broker, SocketAddress address) { _broker = broker; _address = address; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManager.java new file mode 100644 index 0000000000..903f54dd8e --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManager.java @@ -0,0 +1,214 @@ +/* + * 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.security.auth.manager; + +import java.io.IOException; +import java.security.Principal; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.UsernamePrincipal; +import org.apache.qpid.server.security.auth.sasl.plain.PlainPasswordCallback; +import org.apache.qpid.server.security.auth.sasl.plain.PlainSaslServer; + +public class SimpleAuthenticationManager implements AuthenticationManager +{ + private static final Logger _logger = Logger.getLogger(SimpleAuthenticationManager.class); + + private static final String PLAIN_MECHANISM = "PLAIN"; + private static final String CRAM_MD5_MECHANISM = "CRAM-MD5"; + + private Map<String, String> _users; + + public SimpleAuthenticationManager(String userName, String userPassword) + { + this(Collections.singletonMap(userName, userPassword)); + } + + public SimpleAuthenticationManager(Map<String, String> users) + { + _users = new HashMap<String, String>(users); + } + + @Override + public void initialise() + { + } + + @Override + public String getMechanisms() + { + return PLAIN_MECHANISM + " " + CRAM_MD5_MECHANISM; + } + + @Override + public SaslServer createSaslServer(String mechanism, String localFQDN, Principal externalPrincipal) throws SaslException + { + if (PLAIN_MECHANISM.equals(mechanism)) + { + return new PlainSaslServer(new SimplePlainCallbackHandler()); + } + else if (CRAM_MD5_MECHANISM.equals(mechanism)) + { + return Sasl.createSaslServer(mechanism, "AMQP", localFQDN, null, new SimpleCramMd5CallbackHandler()); + } + else + { + throw new SaslException("Unknown mechanism: " + mechanism); + } + } + + @Override + public AuthenticationResult authenticate(SaslServer server, byte[] response) + { + try + { + // Process response from the client + byte[] challenge = server.evaluateResponse(response != null ? response : new byte[0]); + + if (server.isComplete()) + { + String authorizationID = server.getAuthorizationID(); + _logger.debug("Authenticated as " + authorizationID); + + return new AuthenticationResult(new UsernamePrincipal(authorizationID)); + } + else + { + return new AuthenticationResult(challenge, AuthenticationResult.AuthenticationStatus.CONTINUE); + } + } + catch (SaslException e) + { + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR, e); + } + } + + @Override + public AuthenticationResult authenticate(String username, String password) + { + if (_users.containsKey(username)) + { + String userPassword = _users.get(username); + if (userPassword.equals(password)) + { + return new AuthenticationResult(new UsernamePrincipal(username)); + } + } + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR); + } + + @Override + public void close() + { + } + + @Override + public void onCreate() + { + // nothing to do, no external resource is required + } + + @Override + public void onDelete() + { + // nothing to do, no external resource is used + } + + private class SimpleCramMd5CallbackHandler implements CallbackHandler + { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException + { + String username = null; + for (Callback callback : callbacks) + { + if (callback instanceof NameCallback) + { + username = ((NameCallback) callback).getDefaultName(); + } + else if (callback instanceof PasswordCallback) + { + if (_users.containsKey(username)) + { + String password = _users.get(username); + ((PasswordCallback) callback).setPassword(password.toCharArray()); + } + else + { + throw new SaslException("Authentication failed"); + } + } + else if (callback instanceof AuthorizeCallback) + { + ((AuthorizeCallback) callback).setAuthorized(true); + } + else + { + throw new UnsupportedCallbackException(callback); + } + } + } + } + + private class SimplePlainCallbackHandler implements CallbackHandler + { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException + { + String username = null; + for (Callback callback : callbacks) + { + if (callback instanceof NameCallback) + { + username = ((NameCallback) callback).getDefaultName(); + } + else if (callback instanceof PlainPasswordCallback) + { + if (_users.containsKey(username)) + { + PlainPasswordCallback plainPasswordCallback = (PlainPasswordCallback) callback; + String password = plainPasswordCallback.getPlainPassword(); + plainPasswordCallback.setAuthenticated(password.equals(_users.get(username))); + } + } + else if (callback instanceof AuthorizeCallback) + { + ((AuthorizeCallback) callback).setAuthorized(true); + } + else + { + throw new UnsupportedCallbackException(callback); + } + } + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/external/ExternalSaslServer.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/external/ExternalSaslServer.java index 509442b14b..475f74180e 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/external/ExternalSaslServer.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/external/ExternalSaslServer.java @@ -61,7 +61,7 @@ public class ExternalSaslServer implements SaslServer public String getAuthorizationID() { - return null; + return getAuthenticatedPrincipal().getName(); } public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupManager.java index 8295f28f9e..e11a4f83db 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupManager.java @@ -19,6 +19,7 @@ */ package org.apache.qpid.server.security.group; +import java.io.File; import java.io.IOException; import java.security.Principal; import java.util.Collections; @@ -34,32 +35,26 @@ import org.apache.qpid.server.security.auth.UsernamePrincipal; * This plugin is configured in the following manner: * </p> * <pre> - * <file-group-manager> - * <attributes> - * <attribute> - * <name>groupFile</name> - * <value>${conf}/groups</value> - * </attribute> - * </attributes> - * </file-group-manager> + * "groupproviders":[ + * ... + * { + * "name" : "...", + * "type" : "GroupFile", + * "path" : "path/to/file/with/groups", + * } + * ... + * ] * </pre> */ public class FileGroupManager implements GroupManager { private final FileGroupDatabase _groupDatabase; - + private final String _groupFile; public FileGroupManager(String groupFile) { + _groupFile = groupFile; _groupDatabase = new FileGroupDatabase(); - try - { - _groupDatabase.setGroupFile(groupFile); - } - catch (IOException e) - { - throw new IllegalConfigurationException("Unable to set group file " + groupFile, e); - } } @Override @@ -144,4 +139,101 @@ public class FileGroupManager implements GroupManager } + @Override + public void onDelete() + { + File file = new File(_groupFile); + if (file.exists()) + { + if (!file.delete()) + { + throw new IllegalConfigurationException("Cannot delete group file"); + } + } + } + + @Override + public void onCreate() + { + File file = new File(_groupFile); + if (!file.exists()) + { + File parent = file.getParentFile(); + if (!parent.exists()) + { + parent.mkdirs(); + } + if (parent.exists()) + { + try + { + file.createNewFile(); + } + catch (IOException e) + { + throw new IllegalConfigurationException("Cannot create group file"); + } + } + else + { + throw new IllegalConfigurationException("Cannot create group file"); + } + } + } + + @Override + public void open() + { + try + { + _groupDatabase.setGroupFile(_groupFile); + } + catch (IOException e) + { + throw new IllegalConfigurationException("Unable to set group file " + _groupFile, e); + } + } + + @Override + public void close() + { + // no-op + } + + @Override + public int hashCode() + { + return ((_groupFile == null) ? 0 : _groupFile.hashCode()); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + FileGroupManager other = (FileGroupManager) obj; + if (_groupFile == null) + { + if (other._groupFile != null) + { + return false; + } + else + { + return true; + } + } + return _groupFile.equals(other._groupFile); + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupManagerFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupManagerFactory.java index 5c4730a9c8..50f08623cd 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupManagerFactory.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupManagerFactory.java @@ -20,32 +20,60 @@ package org.apache.qpid.server.security.group; import static org.apache.qpid.server.util.MapValueConverter.getStringAttribute; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; import java.util.Map; -import org.apache.commons.lang.StringUtils; import org.apache.qpid.server.configuration.IllegalConfigurationException; -import org.apache.qpid.server.model.GroupProvider; import org.apache.qpid.server.plugin.GroupManagerFactory; +import org.apache.qpid.server.util.ResourceBundleLoader; public class FileGroupManagerFactory implements GroupManagerFactory { - static final String FILE_GROUP_MANAGER_TYPE = "file-group-manager"; - static final String FILE = "file"; + public static final String RESOURCE_BUNDLE = "org.apache.qpid.server.security.group.FileGroupProviderAttributeDescriptions"; + + public static final String GROUP_FILE_PROVIDER_TYPE = "GroupFile"; + public static final String PATH = "path"; + + public static final Collection<String> ATTRIBUTES = Collections.<String> unmodifiableList(Arrays.asList( + ATTRIBUTE_TYPE, + PATH + )); @Override public GroupManager createInstance(Map<String, Object> attributes) { - if(!FILE_GROUP_MANAGER_TYPE.equals(getStringAttribute(GroupProvider.TYPE, attributes, null))) + if(attributes == null || !GROUP_FILE_PROVIDER_TYPE.equals(attributes.get(ATTRIBUTE_TYPE))) { return null; } - String groupFile = getStringAttribute(FILE, attributes, null); - if (StringUtils.isBlank(groupFile)) + String groupFile = getStringAttribute(PATH, attributes, null); + if (groupFile == null || "".equals(groupFile.trim())) { throw new IllegalConfigurationException("Path to file containing groups is not specified!"); } + return new FileGroupManager(groupFile); } + @Override + public String getType() + { + return GROUP_FILE_PROVIDER_TYPE; + } + + @Override + public Collection<String> getAttributeNames() + { + return ATTRIBUTES; + } + + @Override + public Map<String, String> getAttributeDescriptions() + { + return ResourceBundleLoader.getResources(RESOURCE_BUNDLE); + } + } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupManager.java index 6d2df86919..3fd6dd2800 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupManager.java @@ -37,4 +37,12 @@ public interface GroupManager void addUserToGroup(String user, String group); void removeUserFromGroup(String user, String group); + + void open(); + + void close(); + + void onDelete(); + + void onCreate(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java new file mode 100644 index 0000000000..2950fdb039 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java @@ -0,0 +1,2041 @@ +/* +* +* 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.store; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.ref.SoftReference; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import org.apache.commons.configuration.Configuration; +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.message.EnqueableMessage; +import org.apache.qpid.server.plugin.MessageMetaDataType; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.BindingRecoveryHandler; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.ExchangeRecoveryHandler; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.QueueRecoveryHandler; + +abstract public class AbstractJDBCMessageStore implements MessageStore +{ + private static final String DB_VERSION_TABLE_NAME = "QPID_DB_VERSION"; + + private static final String QUEUE_ENTRY_TABLE_NAME = "QPID_QUEUE_ENTRIES"; + + private static final String META_DATA_TABLE_NAME = "QPID_MESSAGE_METADATA"; + private static final String MESSAGE_CONTENT_TABLE_NAME = "QPID_MESSAGE_CONTENT"; + + private static final String LINKS_TABLE_NAME = "QPID_LINKS"; + private static final String BRIDGES_TABLE_NAME = "QPID_BRIDGES"; + + private static final String XID_TABLE_NAME = "QPID_XIDS"; + private static final String XID_ACTIONS_TABLE_NAME = "QPID_XID_ACTIONS"; + + private static final String CONFIGURED_OBJECTS_TABLE_NAME = "QPID_CONFIGURED_OBJECTS"; + + private static final int DB_VERSION = 6; + + private final AtomicLong _messageId = new AtomicLong(0); + private AtomicBoolean _closed = new AtomicBoolean(false); + + protected String _connectionURL; + + + private static final String CREATE_DB_VERSION_TABLE = "CREATE TABLE "+ DB_VERSION_TABLE_NAME + " ( version int not null )"; + private static final String INSERT_INTO_DB_VERSION = "INSERT INTO "+ DB_VERSION_TABLE_NAME + " ( version ) VALUES ( ? )"; + + private static final String INSERT_INTO_QUEUE_ENTRY = "INSERT INTO " + QUEUE_ENTRY_TABLE_NAME + " (queue_id, message_id) values (?,?)"; + private static final String DELETE_FROM_QUEUE_ENTRY = "DELETE FROM " + QUEUE_ENTRY_TABLE_NAME + " WHERE queue_id = ? AND message_id =?"; + private static final String SELECT_FROM_QUEUE_ENTRY = "SELECT queue_id, message_id FROM " + QUEUE_ENTRY_TABLE_NAME + " ORDER BY queue_id, message_id"; + private static final String INSERT_INTO_MESSAGE_CONTENT = "INSERT INTO " + MESSAGE_CONTENT_TABLE_NAME + + "( message_id, content ) values (?, ?)"; + private static final String SELECT_FROM_MESSAGE_CONTENT = "SELECT content FROM " + MESSAGE_CONTENT_TABLE_NAME + + " WHERE message_id = ?"; + private static final String DELETE_FROM_MESSAGE_CONTENT = "DELETE FROM " + MESSAGE_CONTENT_TABLE_NAME + + " WHERE message_id = ?"; + + private static final String INSERT_INTO_META_DATA = "INSERT INTO " + META_DATA_TABLE_NAME + "( message_id , meta_data ) values (?, ?)"; + private static final String SELECT_FROM_META_DATA = + "SELECT meta_data FROM " + META_DATA_TABLE_NAME + " WHERE message_id = ?"; + private static final String DELETE_FROM_META_DATA = "DELETE FROM " + META_DATA_TABLE_NAME + " WHERE message_id = ?"; + private static final String SELECT_ALL_FROM_META_DATA = "SELECT message_id, meta_data FROM " + META_DATA_TABLE_NAME; + + private static final String SELECT_FROM_LINKS = + "SELECT create_time, arguments FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and id_msb"; + private static final String DELETE_FROM_LINKS = "DELETE FROM " + LINKS_TABLE_NAME + + " WHERE id_lsb = ? and id_msb = ?"; + private static final String SELECT_ALL_FROM_LINKS = "SELECT id_lsb, id_msb, create_time, " + + "arguments FROM " + LINKS_TABLE_NAME; + private static final String FIND_LINK = "SELECT id_lsb, id_msb FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and" + + " id_msb = ?"; + private static final String INSERT_INTO_LINKS = "INSERT INTO " + LINKS_TABLE_NAME + "( id_lsb, " + + "id_msb, create_time, arguments ) values (?, ?, ?, ?)"; + private static final String SELECT_FROM_BRIDGES = + "SELECT create_time, link_id_lsb, link_id_msb, arguments FROM " + + BRIDGES_TABLE_NAME + " WHERE id_lsb = ? and id_msb = ?"; + private static final String DELETE_FROM_BRIDGES = "DELETE FROM " + BRIDGES_TABLE_NAME + + " WHERE id_lsb = ? and id_msb = ?"; + private static final String SELECT_ALL_FROM_BRIDGES = "SELECT id_lsb, id_msb, " + + " create_time," + + " link_id_lsb, link_id_msb, " + + "arguments FROM " + BRIDGES_TABLE_NAME + + " WHERE link_id_lsb = ? and link_id_msb = ?"; + private static final String FIND_BRIDGE = "SELECT id_lsb, id_msb FROM " + BRIDGES_TABLE_NAME + + " WHERE id_lsb = ? and id_msb = ?"; + private static final String INSERT_INTO_BRIDGES = "INSERT INTO " + BRIDGES_TABLE_NAME + "( id_lsb, id_msb, " + + "create_time, " + + "link_id_lsb, link_id_msb, " + + "arguments )" + + " values (?, ?, ?, ?, ?, ?)"; + + private static final String INSERT_INTO_XIDS = + "INSERT INTO "+ XID_TABLE_NAME +" ( format, global_id, branch_id ) values (?, ?, ?)"; + private static final String DELETE_FROM_XIDS = "DELETE FROM " + XID_TABLE_NAME + + " WHERE format = ? and global_id = ? and branch_id = ?"; + private static final String SELECT_ALL_FROM_XIDS = "SELECT format, global_id, branch_id FROM " + XID_TABLE_NAME; + private static final String INSERT_INTO_XID_ACTIONS = + "INSERT INTO "+ XID_ACTIONS_TABLE_NAME +" ( format, global_id, branch_id, action_type, " + + "queue_id, message_id ) values (?,?,?,?,?,?) "; + private static final String DELETE_FROM_XID_ACTIONS = "DELETE FROM " + XID_ACTIONS_TABLE_NAME + + " WHERE format = ? and global_id = ? and branch_id = ?"; + private static final String SELECT_ALL_FROM_XID_ACTIONS = + "SELECT action_type, queue_id, message_id FROM " + XID_ACTIONS_TABLE_NAME + + " WHERE format = ? and global_id = ? and branch_id = ?"; + private static final String INSERT_INTO_CONFIGURED_OBJECTS = "INSERT INTO " + CONFIGURED_OBJECTS_TABLE_NAME + + " ( id, object_type, attributes) VALUES (?,?,?)"; + private static final String UPDATE_CONFIGURED_OBJECTS = "UPDATE " + CONFIGURED_OBJECTS_TABLE_NAME + + " set object_type =?, attributes = ? where id = ?"; + private static final String DELETE_FROM_CONFIGURED_OBJECTS = "DELETE FROM " + CONFIGURED_OBJECTS_TABLE_NAME + + " where id = ?"; + private static final String FIND_CONFIGURED_OBJECT = "SELECT object_type, attributes FROM " + CONFIGURED_OBJECTS_TABLE_NAME + + " where id = ?"; + private static final String SELECT_FROM_CONFIGURED_OBJECTS = "SELECT id, object_type, attributes FROM " + CONFIGURED_OBJECTS_TABLE_NAME; + + protected static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); + + protected final EventManager _eventManager = new EventManager(); + + protected final StateManager _stateManager; + + private MessageStoreRecoveryHandler _messageRecoveryHandler; + private TransactionLogRecoveryHandler _tlogRecoveryHandler; + private ConfigurationRecoveryHandler _configRecoveryHandler; + + public AbstractJDBCMessageStore() + { + _stateManager = new StateManager(_eventManager); + } + + private ConfiguredObjectHelper _configuredObjectHelper = new ConfiguredObjectHelper(); + + @Override + public void configureConfigStore(String name, + ConfigurationRecoveryHandler configRecoveryHandler, + Configuration storeConfiguration) throws Exception + { + _stateManager.attainState(State.INITIALISING); + _configRecoveryHandler = configRecoveryHandler; + + commonConfiguration(name, storeConfiguration); + + } + + @Override + public void configureMessageStore(String name, + MessageStoreRecoveryHandler recoveryHandler, + TransactionLogRecoveryHandler tlogRecoveryHandler, + Configuration storeConfiguration) throws Exception + { + _tlogRecoveryHandler = tlogRecoveryHandler; + _messageRecoveryHandler = recoveryHandler; + + _stateManager.attainState(State.INITIALISED); + } + + @Override + public void activate() throws Exception + { + _stateManager.attainState(State.ACTIVATING); + + // this recovers durable exchanges, queues, and bindings + recoverConfiguration(_configRecoveryHandler); + recoverMessages(_messageRecoveryHandler); + TransactionLogRecoveryHandler.DtxRecordRecoveryHandler dtxrh = recoverQueueEntries(_tlogRecoveryHandler); + recoverXids(dtxrh); + + _stateManager.attainState(State.ACTIVE); + } + + private void commonConfiguration(String name, Configuration storeConfiguration) + throws ClassNotFoundException, SQLException + { + implementationSpecificConfiguration(name, storeConfiguration); + createOrOpenDatabase(); + + } + + protected abstract void implementationSpecificConfiguration(String name, Configuration storeConfiguration) throws ClassNotFoundException; + + abstract protected Logger getLogger(); + + abstract protected String getSqlBlobType(); + + abstract protected String getSqlVarBinaryType(int size); + + abstract protected String getSqlBigIntType(); + + protected void createOrOpenDatabase() throws SQLException + { + Connection conn = newAutoCommitConnection(); + + createVersionTable(conn); + createConfiguredObjectsTable(conn); + createQueueEntryTable(conn); + createMetaDataTable(conn); + createMessageContentTable(conn); + createLinkTable(conn); + createBridgeTable(conn); + createXidTable(conn); + createXidActionTable(conn); + conn.close(); + } + + private void createVersionTable(final Connection conn) throws SQLException + { + if(!tableExists(DB_VERSION_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute(CREATE_DB_VERSION_TABLE); + } + finally + { + stmt.close(); + } + + PreparedStatement pstmt = conn.prepareStatement(INSERT_INTO_DB_VERSION); + try + { + pstmt.setInt(1, DB_VERSION); + pstmt.execute(); + } + finally + { + pstmt.close(); + } + } + + } + + private void createConfiguredObjectsTable(final Connection conn) throws SQLException + { + if(!tableExists(CONFIGURED_OBJECTS_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute("CREATE TABLE " + CONFIGURED_OBJECTS_TABLE_NAME + + " ( id VARCHAR(36) not null, object_type varchar(255), attributes "+getSqlBlobType()+", PRIMARY KEY (id))"); + } + finally + { + stmt.close(); + } + } + } + + private void createQueueEntryTable(final Connection conn) throws SQLException + { + if(!tableExists(QUEUE_ENTRY_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute("CREATE TABLE "+ QUEUE_ENTRY_TABLE_NAME +" ( queue_id varchar(36) not null, message_id " + + getSqlBigIntType() + " not null, PRIMARY KEY (queue_id, message_id) )"); + } + finally + { + stmt.close(); + } + } + + } + + private void createMetaDataTable(final Connection conn) throws SQLException + { + if(!tableExists(META_DATA_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute("CREATE TABLE " + + META_DATA_TABLE_NAME + + " ( message_id " + + getSqlBigIntType() + + " not null, meta_data " + + getSqlBlobType() + + ", PRIMARY KEY ( message_id ) )"); + } + finally + { + stmt.close(); + } + } + + } + + private void createMessageContentTable(final Connection conn) throws SQLException + { + if(!tableExists(MESSAGE_CONTENT_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute("CREATE TABLE " + + MESSAGE_CONTENT_TABLE_NAME + + " ( message_id " + + getSqlBigIntType() + + " not null, content " + + getSqlBlobType() + + ", PRIMARY KEY (message_id) )"); + } + finally + { + stmt.close(); + } + } + + } + + private void createLinkTable(final Connection conn) throws SQLException + { + if(!tableExists(LINKS_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute("CREATE TABLE "+ LINKS_TABLE_NAME +" ( id_lsb " + getSqlBigIntType() + " not null," + + " id_msb " + getSqlBigIntType() + " not null," + + " create_time " + getSqlBigIntType() + " not null," + + " arguments "+getSqlBlobType()+", PRIMARY KEY ( id_lsb, id_msb ))"); + } + finally + { + stmt.close(); + } + } + } + + private void createBridgeTable(final Connection conn) throws SQLException + { + if(!tableExists(BRIDGES_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute("CREATE TABLE "+ BRIDGES_TABLE_NAME +" ( id_lsb " + getSqlBigIntType() + " not null," + + " id_msb " + getSqlBigIntType() + " not null," + + " create_time " + getSqlBigIntType() + " not null," + + " link_id_lsb " + getSqlBigIntType() + " not null," + + " link_id_msb " + getSqlBigIntType() + " not null," + + " arguments "+getSqlBlobType()+", PRIMARY KEY ( id_lsb, id_msb ))"); + } + finally + { + stmt.close(); + } + } + } + + private void createXidTable(final Connection conn) throws SQLException + { + if(!tableExists(XID_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute("CREATE TABLE " + + XID_TABLE_NAME + + " ( format " + getSqlBigIntType() + " not null," + + " global_id " + + getSqlVarBinaryType(64) + + ", branch_id " + + getSqlVarBinaryType(64) + + " , PRIMARY KEY ( format, " + + + "global_id, branch_id ))"); + } + finally + { + stmt.close(); + } + } + } + + private void createXidActionTable(final Connection conn) throws SQLException + { + if(!tableExists(XID_ACTIONS_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute("CREATE TABLE " + XID_ACTIONS_TABLE_NAME + " ( format " + getSqlBigIntType() + " not null," + + " global_id " + getSqlVarBinaryType(64) + " not null, branch_id " + getSqlVarBinaryType( + 64) + " not null, " + + "action_type char not null, queue_id varchar(36) not null, message_id " + getSqlBigIntType() + " not null" + + ", PRIMARY KEY ( " + + "format, global_id, branch_id, action_type, queue_id, message_id))"); + } + finally + { + stmt.close(); + } + } + } + + protected boolean tableExists(final String tableName, final Connection conn) throws SQLException + { + DatabaseMetaData metaData = conn.getMetaData(); + ResultSet rs = metaData.getTables(null, null, "%", null); + + try + { + + while(rs.next()) + { + final String table = rs.getString(3); + if(tableName.equalsIgnoreCase(table)) + { + return true; + } + } + return false; + } + finally + { + rs.close(); + } + } + + protected void recoverConfiguration(ConfigurationRecoveryHandler recoveryHandler) throws AMQException + { + try + { + List<ConfiguredObjectRecord> configuredObjects = loadConfiguredObjects(); + + ExchangeRecoveryHandler erh = recoveryHandler.begin(this); + _configuredObjectHelper.recoverExchanges(erh, configuredObjects); + + QueueRecoveryHandler qrh = erh.completeExchangeRecovery(); + _configuredObjectHelper.recoverQueues(qrh, configuredObjects); + + BindingRecoveryHandler brh = qrh.completeQueueRecovery(); + _configuredObjectHelper.recoverBindings(brh, configuredObjects); + + brh.completeBindingRecovery(); + } + catch (SQLException e) + { + throw new AMQStoreException("Error recovering persistent state: " + e.getMessage(), e); + } + } + + @Override + public void close() throws Exception + { + _closed.getAndSet(true); + _stateManager.attainState(State.CLOSING); + + doClose(); + + _stateManager.attainState(State.CLOSED); + } + + + protected abstract void doClose() throws Exception; + + @Override + public StoredMessage addMessage(StorableMessageMetaData metaData) + { + if(metaData.isPersistent()) + { + return new StoredJDBCMessage(_messageId.incrementAndGet(), metaData); + } + else + { + return new StoredMemoryMessage(_messageId.incrementAndGet(), metaData); + } + } + + public StoredMessage getMessage(long messageNumber) + { + return null; + } + + public void removeMessage(long messageId) + { + try + { + Connection conn = newConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_META_DATA); + try + { + stmt.setLong(1,messageId); + int results = stmt.executeUpdate(); + stmt.close(); + + if (results == 0) + { + getLogger().warn("Message metadata not found for message id " + messageId); + } + + if (getLogger().isDebugEnabled()) + { + getLogger().debug("Deleted metadata for message " + messageId); + } + + stmt = conn.prepareStatement(DELETE_FROM_MESSAGE_CONTENT); + stmt.setLong(1,messageId); + results = stmt.executeUpdate(); + } + finally + { + stmt.close(); + } + conn.commit(); + } + catch(SQLException e) + { + try + { + conn.rollback(); + } + catch(SQLException t) + { + // ignore - we are re-throwing underlying exception + } + + throw e; + + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new RuntimeException("Error removing message with id " + messageId + " from database: " + e.getMessage(), e); + } + + } + + @Override + public void createExchange(Exchange exchange) throws AMQStoreException + { + if (_stateManager.isInState(State.ACTIVE)) + { + ConfiguredObjectRecord configuredObject = _configuredObjectHelper.createExchangeConfiguredObject(exchange); + insertConfiguredObject(configuredObject); + } + + } + + @Override + public void removeExchange(Exchange exchange) throws AMQStoreException + { + int results = removeConfiguredObject(exchange.getId()); + if (results == 0) + { + throw new AMQStoreException("Exchange " + exchange.getName() + " with id " + exchange.getId() + " not found"); + } + } + + @Override + public void bindQueue(Binding binding) + throws AMQStoreException + { + if (_stateManager.isInState(State.ACTIVE)) + { + ConfiguredObjectRecord configuredObject = _configuredObjectHelper.createBindingConfiguredObject(binding); + insertConfiguredObject(configuredObject); + } + } + + @Override + public void unbindQueue(Binding binding) + throws AMQStoreException + { + int results = removeConfiguredObject(binding.getId()); + if (results == 0) + { + throw new AMQStoreException("Binding " + binding + " not found"); + } + } + + @Override + public void createQueue(AMQQueue queue) throws AMQStoreException + { + createQueue(queue, null); + } + + @Override + public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException + { + getLogger().debug("public void createQueue(AMQQueue queue = " + queue + "): called"); + + if (_stateManager.isInState(State.ACTIVE)) + { + ConfiguredObjectRecord queueConfiguredObject = _configuredObjectHelper.createQueueConfiguredObject(queue, arguments); + insertConfiguredObject(queueConfiguredObject); + } + } + + /** + * Updates the specified queue in the persistent store, IF it is already present. If the queue + * is not present in the store, it will not be added. + * + * NOTE: Currently only updates the exclusivity. + * + * @param queue The queue to update the entry for. + * @throws org.apache.qpid.AMQStoreException If the operation fails for any reason. + */ + @Override + public void updateQueue(final AMQQueue queue) throws AMQStoreException + { + if (_stateManager.isInState(State.ACTIVE)) + { + ConfiguredObjectRecord queueConfiguredObject = loadConfiguredObject(queue.getId()); + if (queueConfiguredObject != null) + { + ConfiguredObjectRecord newQueueRecord = _configuredObjectHelper.updateQueueConfiguredObject(queue, queueConfiguredObject); + updateConfiguredObject(newQueueRecord); + } + } + + } + + + /** + * Convenience method to create a new Connection configured for TRANSACTION_READ_COMMITED + * isolation and with auto-commit transactions enabled. + */ + protected Connection newAutoCommitConnection() throws SQLException + { + final Connection connection = newConnection(); + try + { + connection.setAutoCommit(true); + } + catch (SQLException sqlEx) + { + + try + { + connection.close(); + } + finally + { + throw sqlEx; + } + } + + return connection; + } + + /** + * Convenience method to create a new Connection configured for TRANSACTION_READ_COMMITED + * isolation and with auto-commit transactions disabled. + */ + protected Connection newConnection() throws SQLException + { + final Connection connection = DriverManager.getConnection(_connectionURL); + try + { + connection.setAutoCommit(false); + connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + } + catch (SQLException sqlEx) + { + try + { + connection.close(); + } + finally + { + throw sqlEx; + } + } + return connection; + } + + @Override + public void removeQueue(final AMQQueue queue) throws AMQStoreException + { + AMQShortString name = queue.getNameShortString(); + getLogger().debug("public void removeQueue(AMQShortString name = " + name + "): called"); + int results = removeConfiguredObject(queue.getId()); + if (results == 0) + { + throw new AMQStoreException("Queue " + name + " with id " + queue.getId() + " not found"); + } + } + + private byte[] convertStringMapToBytes(final Map<String, String> arguments) throws AMQStoreException + { + byte[] argumentBytes; + if(arguments == null) + { + argumentBytes = new byte[0]; + } + else + { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(bos); + + + try + { + dos.writeInt(arguments.size()); + for(Map.Entry<String,String> arg : arguments.entrySet()) + { + dos.writeUTF(arg.getKey()); + dos.writeUTF(arg.getValue()); + } + } + catch (IOException e) + { + // This should never happen + throw new AMQStoreException(e.getMessage(), e); + } + argumentBytes = bos.toByteArray(); + } + return argumentBytes; + } + + @Override + public Transaction newTransaction() + { + return new JDBCTransaction(); + } + + public void enqueueMessage(ConnectionWrapper connWrapper, final TransactionLogResource queue, Long messageId) throws AMQStoreException + { + Connection conn = connWrapper.getConnection(); + + + try + { + if (getLogger().isDebugEnabled()) + { + getLogger().debug("Enqueuing message " + + messageId + + " on queue " + + (queue instanceof AMQQueue + ? ((AMQQueue) queue).getName() + : "") + + queue.getId() + + "[Connection" + + conn + + "]"); + } + + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_QUEUE_ENTRY); + try + { + stmt.setString(1, queue.getId().toString()); + stmt.setLong(2,messageId); + stmt.executeUpdate(); + } + finally + { + stmt.close(); + } + + } + catch (SQLException e) + { + getLogger().error("Failed to enqueue: " + e.getMessage(), e); + throw new AMQStoreException("Error writing enqueued message with id " + messageId + " for queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + " with id " + queue.getId() + + " to database", e); + } + + } + + public void dequeueMessage(ConnectionWrapper connWrapper, final TransactionLogResource queue, Long messageId) throws AMQStoreException + { + + Connection conn = connWrapper.getConnection(); + + + try + { + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_QUEUE_ENTRY); + try + { + stmt.setString(1, queue.getId().toString()); + stmt.setLong(2, messageId); + int results = stmt.executeUpdate(); + + + + if(results != 1) + { + throw new AMQStoreException("Unable to find message with id " + messageId + " on queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + + " with id " + queue.getId()); + } + + if (getLogger().isDebugEnabled()) + { + getLogger().debug("Dequeuing message " + messageId + " on queue " + (queue instanceof AMQQueue + ? ((AMQQueue) queue).getName() + : "") + + " with id " + queue.getId()); + } + } + finally + { + stmt.close(); + } + + } + catch (SQLException e) + { + getLogger().error("Failed to dequeue: " + e.getMessage(), e); + throw new AMQStoreException("Error deleting enqueued message with id " + messageId + " for queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + + " with id " + queue.getId() + " from database", e); + } + + } + + private void removeXid(ConnectionWrapper connWrapper, long format, byte[] globalId, byte[] branchId) + throws AMQStoreException + { + Connection conn = connWrapper.getConnection(); + + + try + { + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_XIDS); + try + { + stmt.setLong(1,format); + stmt.setBytes(2,globalId); + stmt.setBytes(3,branchId); + int results = stmt.executeUpdate(); + + + + if(results != 1) + { + throw new AMQStoreException("Unable to find message with xid"); + } + } + finally + { + stmt.close(); + } + + stmt = conn.prepareStatement(DELETE_FROM_XID_ACTIONS); + try + { + stmt.setLong(1,format); + stmt.setBytes(2,globalId); + stmt.setBytes(3,branchId); + int results = stmt.executeUpdate(); + + } + finally + { + stmt.close(); + } + + } + catch (SQLException e) + { + getLogger().error("Failed to dequeue: " + e.getMessage(), e); + throw new AMQStoreException("Error deleting enqueued message with xid", e); + } + + } + + private void recordXid(ConnectionWrapper connWrapper, long format, byte[] globalId, byte[] branchId, + Transaction.Record[] enqueues, Transaction.Record[] dequeues) throws AMQStoreException + { + Connection conn = connWrapper.getConnection(); + + + try + { + + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_XIDS); + try + { + stmt.setLong(1,format); + stmt.setBytes(2, globalId); + stmt.setBytes(3, branchId); + stmt.executeUpdate(); + } + finally + { + stmt.close(); + } + + stmt = conn.prepareStatement(INSERT_INTO_XID_ACTIONS); + + try + { + stmt.setLong(1,format); + stmt.setBytes(2, globalId); + stmt.setBytes(3, branchId); + + if(enqueues != null) + { + stmt.setString(4, "E"); + for(Transaction.Record record : enqueues) + { + stmt.setString(5, record.getQueue().getId().toString()); + stmt.setLong(6, record.getMessage().getMessageNumber()); + stmt.executeUpdate(); + } + } + + if(dequeues != null) + { + stmt.setString(4, "D"); + for(Transaction.Record record : dequeues) + { + stmt.setString(5, record.getQueue().getId().toString()); + stmt.setLong(6, record.getMessage().getMessageNumber()); + stmt.executeUpdate(); + } + } + + } + finally + { + stmt.close(); + } + + } + catch (SQLException e) + { + getLogger().error("Failed to enqueue: " + e.getMessage(), e); + throw new AMQStoreException("Error writing xid ", e); + } + + } + + private static final class ConnectionWrapper + { + private final Connection _connection; + + public ConnectionWrapper(Connection conn) + { + _connection = conn; + } + + public Connection getConnection() + { + return _connection; + } + } + + + public void commitTran(ConnectionWrapper connWrapper) throws AMQStoreException + { + + try + { + Connection conn = connWrapper.getConnection(); + conn.commit(); + + if (getLogger().isDebugEnabled()) + { + getLogger().debug("commit tran completed"); + } + + conn.close(); + } + catch (SQLException e) + { + throw new AMQStoreException("Error commit tx: " + e.getMessage(), e); + } + finally + { + + } + } + + public StoreFuture commitTranAsync(ConnectionWrapper connWrapper) throws AMQStoreException + { + commitTran(connWrapper); + return StoreFuture.IMMEDIATE_FUTURE; + } + + public void abortTran(ConnectionWrapper connWrapper) throws AMQStoreException + { + if (connWrapper == null) + { + throw new AMQStoreException("Fatal internal error: transactional context is empty at abortTran"); + } + + if (getLogger().isDebugEnabled()) + { + getLogger().debug("abort tran called: " + connWrapper.getConnection()); + } + + try + { + Connection conn = connWrapper.getConnection(); + conn.rollback(); + conn.close(); + } + catch (SQLException e) + { + throw new AMQStoreException("Error aborting transaction: " + e.getMessage(), e); + } + + } + + public Long getNewMessageId() + { + return _messageId.incrementAndGet(); + } + + private void storeMetaData(Connection conn, long messageId, StorableMessageMetaData metaData) + throws SQLException + { + if(getLogger().isDebugEnabled()) + { + getLogger().debug("Adding metadata for message " + messageId); + } + + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_META_DATA); + try + { + stmt.setLong(1,messageId); + + final int bodySize = 1 + metaData.getStorableSize(); + byte[] underlying = new byte[bodySize]; + underlying[0] = (byte) metaData.getType().ordinal(); + ByteBuffer buf = ByteBuffer.wrap(underlying); + buf.position(1); + buf = buf.slice(); + + metaData.writeToBuffer(0, buf); + ByteArrayInputStream bis = new ByteArrayInputStream(underlying); + try + { + stmt.setBinaryStream(2,bis,underlying.length); + int result = stmt.executeUpdate(); + + if(result == 0) + { + throw new RuntimeException("Unable to add meta data for message " +messageId); + } + } + finally + { + try + { + bis.close(); + } + catch (IOException e) + { + + throw new SQLException(e); + } + } + + } + finally + { + stmt.close(); + } + + } + + protected void recoverMessages(MessageStoreRecoveryHandler recoveryHandler) throws SQLException + { + Connection conn = newAutoCommitConnection(); + try + { + MessageStoreRecoveryHandler.StoredMessageRecoveryHandler messageHandler = recoveryHandler.begin(); + + Statement stmt = conn.createStatement(); + try + { + ResultSet rs = stmt.executeQuery(SELECT_ALL_FROM_META_DATA); + try + { + + long maxId = 0; + + while(rs.next()) + { + + long messageId = rs.getLong(1); + if(messageId > maxId) + { + maxId = messageId; + } + + byte[] dataAsBytes = getBlobAsBytes(rs, 2); + + ByteBuffer buf = ByteBuffer.wrap(dataAsBytes); + buf.position(1); + buf = buf.slice(); + MessageMetaDataType type = MessageMetaDataTypeRegistry.fromOrdinal(dataAsBytes[0]); + StorableMessageMetaData metaData = type.createMetaData(buf); + StoredJDBCMessage message = new StoredJDBCMessage(messageId, metaData, true); + messageHandler.message(message); + } + + _messageId.set(maxId); + + messageHandler.completeMessageRecovery(); + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + + + protected TransactionLogRecoveryHandler.DtxRecordRecoveryHandler recoverQueueEntries(TransactionLogRecoveryHandler recoveryHandler) throws SQLException + { + Connection conn = newAutoCommitConnection(); + try + { + TransactionLogRecoveryHandler.QueueEntryRecoveryHandler queueEntryHandler = recoveryHandler.begin(this); + + Statement stmt = conn.createStatement(); + try + { + ResultSet rs = stmt.executeQuery(SELECT_FROM_QUEUE_ENTRY); + try + { + while(rs.next()) + { + + String id = rs.getString(1); + long messageId = rs.getLong(2); + queueEntryHandler.queueEntry(UUID.fromString(id), messageId); + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + + return queueEntryHandler.completeQueueEntryRecovery(); + } + finally + { + conn.close(); + } + } + + private static final class Xid + { + + private final long _format; + private final byte[] _globalId; + private final byte[] _branchId; + + public Xid(long format, byte[] globalId, byte[] branchId) + { + _format = format; + _globalId = globalId; + _branchId = branchId; + } + + public long getFormat() + { + return _format; + } + + public byte[] getGlobalId() + { + return _globalId; + } + + public byte[] getBranchId() + { + return _branchId; + } + } + + private static class RecordImpl implements Transaction.Record, TransactionLogResource, EnqueableMessage + { + + private long _messageNumber; + private UUID _queueId; + + public RecordImpl(UUID queueId, long messageNumber) + { + _messageNumber = messageNumber; + _queueId = queueId; + } + + @Override + public TransactionLogResource getQueue() + { + return this; + } + + @Override + public EnqueableMessage getMessage() + { + return this; + } + + @Override + public long getMessageNumber() + { + return _messageNumber; + } + + @Override + public boolean isPersistent() + { + return true; + } + + @Override + public StoredMessage getStoredMessage() + { + throw new UnsupportedOperationException(); + } + + @Override + public UUID getId() + { + return _queueId; + } + } + + protected void recoverXids(TransactionLogRecoveryHandler.DtxRecordRecoveryHandler dtxrh) throws SQLException + { + Connection conn = newAutoCommitConnection(); + try + { + List<Xid> xids = new ArrayList<Xid>(); + + Statement stmt = conn.createStatement(); + try + { + ResultSet rs = stmt.executeQuery(SELECT_ALL_FROM_XIDS); + try + { + while(rs.next()) + { + + long format = rs.getLong(1); + byte[] globalId = rs.getBytes(2); + byte[] branchId = rs.getBytes(3); + xids.add(new Xid(format, globalId, branchId)); + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + + + + for(Xid xid : xids) + { + List<RecordImpl> enqueues = new ArrayList<RecordImpl>(); + List<RecordImpl> dequeues = new ArrayList<RecordImpl>(); + + PreparedStatement pstmt = conn.prepareStatement(SELECT_ALL_FROM_XID_ACTIONS); + + try + { + pstmt.setLong(1, xid.getFormat()); + pstmt.setBytes(2, xid.getGlobalId()); + pstmt.setBytes(3, xid.getBranchId()); + + ResultSet rs = pstmt.executeQuery(); + try + { + while(rs.next()) + { + + String actionType = rs.getString(1); + UUID queueId = UUID.fromString(rs.getString(2)); + long messageId = rs.getLong(3); + + RecordImpl record = new RecordImpl(queueId, messageId); + List<RecordImpl> records = "E".equals(actionType) ? enqueues : dequeues; + records.add(record); + } + } + finally + { + rs.close(); + } + } + finally + { + pstmt.close(); + } + + dtxrh.dtxRecord(xid.getFormat(), xid.getGlobalId(), xid.getBranchId(), + enqueues.toArray(new RecordImpl[enqueues.size()]), + dequeues.toArray(new RecordImpl[dequeues.size()])); + } + + + dtxrh.completeDtxRecordRecovery(); + } + finally + { + conn.close(); + } + + } + + StorableMessageMetaData getMetaData(long messageId) throws SQLException + { + + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_META_DATA); + try + { + stmt.setLong(1,messageId); + ResultSet rs = stmt.executeQuery(); + try + { + + if(rs.next()) + { + byte[] dataAsBytes = getBlobAsBytes(rs, 1); + ByteBuffer buf = ByteBuffer.wrap(dataAsBytes); + buf.position(1); + buf = buf.slice(); + MessageMetaDataType type = MessageMetaDataTypeRegistry.fromOrdinal(dataAsBytes[0]); + StorableMessageMetaData metaData = type.createMetaData(buf); + + return metaData; + } + else + { + throw new RuntimeException("Meta data not found for message with id " + messageId); + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + + protected abstract byte[] getBlobAsBytes(ResultSet rs, int col) throws SQLException; + + private void addContent(Connection conn, long messageId, ByteBuffer src) + { + if(getLogger().isDebugEnabled()) + { + getLogger().debug("Adding content for message " + messageId); + } + PreparedStatement stmt = null; + + try + { + src = src.slice(); + + byte[] chunkData = new byte[src.limit()]; + src.duplicate().get(chunkData); + + stmt = conn.prepareStatement(INSERT_INTO_MESSAGE_CONTENT); + stmt.setLong(1,messageId); + + ByteArrayInputStream bis = new ByteArrayInputStream(chunkData); + stmt.setBinaryStream(2, bis, chunkData.length); + stmt.executeUpdate(); + } + catch (SQLException e) + { + closeConnection(conn); + throw new RuntimeException("Error adding content for message " + messageId + ": " + e.getMessage(), e); + } + finally + { + closePreparedStatement(stmt); + } + + } + + public int getContent(long messageId, int offset, ByteBuffer dst) + { + Connection conn = null; + PreparedStatement stmt = null; + + try + { + conn = newAutoCommitConnection(); + + stmt = conn.prepareStatement(SELECT_FROM_MESSAGE_CONTENT); + stmt.setLong(1,messageId); + ResultSet rs = stmt.executeQuery(); + + int written = 0; + + if (rs.next()) + { + + byte[] dataAsBytes = getBlobAsBytes(rs, 1); + int size = dataAsBytes.length; + + if (offset > size) + { + throw new RuntimeException("Offset " + offset + " is greater than message size " + size + + " for message id " + messageId + "!"); + + } + + written = size - offset; + if(written > dst.remaining()) + { + written = dst.remaining(); + } + + dst.put(dataAsBytes, offset, written); + } + + return written; + + } + catch (SQLException e) + { + throw new RuntimeException("Error retrieving content from offset " + offset + " for message " + messageId + ": " + e.getMessage(), e); + } + finally + { + closePreparedStatement(stmt); + closeConnection(conn); + } + + + } + + @Override + public boolean isPersistent() + { + return true; + } + + + protected class JDBCTransaction implements Transaction + { + private final ConnectionWrapper _connWrapper; + private int _storeSizeIncrease; + + + protected JDBCTransaction() + { + try + { + _connWrapper = new ConnectionWrapper(newConnection()); + } + catch (SQLException e) + { + throw new RuntimeException(e); + } + } + + @Override + public void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException + { + final StoredMessage storedMessage = message.getStoredMessage(); + if(storedMessage instanceof StoredJDBCMessage) + { + try + { + ((StoredJDBCMessage) storedMessage).store(_connWrapper.getConnection()); + } + catch (SQLException e) + { + throw new AMQStoreException("Exception on enqueuing message " + _messageId, e); + } + } + _storeSizeIncrease += storedMessage.getMetaData().getContentSize(); + AbstractJDBCMessageStore.this.enqueueMessage(_connWrapper, queue, message.getMessageNumber()); + } + + @Override + public void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException + { + AbstractJDBCMessageStore.this.dequeueMessage(_connWrapper, queue, message.getMessageNumber()); + + } + + @Override + public void commitTran() throws AMQStoreException + { + AbstractJDBCMessageStore.this.commitTran(_connWrapper); + storedSizeChange(_storeSizeIncrease); + } + + @Override + public StoreFuture commitTranAsync() throws AMQStoreException + { + final StoreFuture storeFuture = AbstractJDBCMessageStore.this.commitTranAsync(_connWrapper); + storedSizeChange(_storeSizeIncrease); + return storeFuture; + } + + @Override + public void abortTran() throws AMQStoreException + { + AbstractJDBCMessageStore.this.abortTran(_connWrapper); + } + + @Override + public void removeXid(long format, byte[] globalId, byte[] branchId) throws AMQStoreException + { + AbstractJDBCMessageStore.this.removeXid(_connWrapper, format, globalId, branchId); + } + + @Override + public void recordXid(long format, byte[] globalId, byte[] branchId, Record[] enqueues, Record[] dequeues) + throws AMQStoreException + { + AbstractJDBCMessageStore.this.recordXid(_connWrapper, format, globalId, branchId, enqueues, dequeues); + } + } + + private class StoredJDBCMessage implements StoredMessage + { + + private final long _messageId; + private final boolean _isRecovered; + + private StorableMessageMetaData _metaData; + private volatile SoftReference<StorableMessageMetaData> _metaDataRef; + private byte[] _data; + private volatile SoftReference<byte[]> _dataRef; + + + StoredJDBCMessage(long messageId, StorableMessageMetaData metaData) + { + this(messageId, metaData, false); + } + + + StoredJDBCMessage(long messageId, + StorableMessageMetaData metaData, boolean isRecovered) + { + _messageId = messageId; + _isRecovered = isRecovered; + + if(!_isRecovered) + { + _metaData = metaData; + } + _metaDataRef = new SoftReference<StorableMessageMetaData>(metaData); + } + + @Override + public StorableMessageMetaData getMetaData() + { + StorableMessageMetaData metaData = _metaData == null ? _metaDataRef.get() : _metaData; + if(metaData == null) + { + try + { + metaData = AbstractJDBCMessageStore.this.getMetaData(_messageId); + } + catch (SQLException e) + { + throw new RuntimeException(e); + } + _metaDataRef = new SoftReference<StorableMessageMetaData>(metaData); + } + + return metaData; + } + + @Override + public long getMessageNumber() + { + return _messageId; + } + + @Override + public void addContent(int offsetInMessage, ByteBuffer src) + { + src = src.slice(); + + if(_data == null) + { + _data = new byte[src.remaining()]; + _dataRef = new SoftReference<byte[]>(_data); + src.duplicate().get(_data); + } + else + { + byte[] oldData = _data; + _data = new byte[oldData.length + src.remaining()]; + _dataRef = new SoftReference<byte[]>(_data); + + System.arraycopy(oldData,0,_data,0,oldData.length); + src.duplicate().get(_data, oldData.length, src.remaining()); + } + + } + + @Override + public int getContent(int offsetInMessage, ByteBuffer dst) + { + byte[] data = _dataRef == null ? null : _dataRef.get(); + if(data != null) + { + int length = Math.min(dst.remaining(), data.length - offsetInMessage); + dst.put(data, offsetInMessage, length); + return length; + } + else + { + return AbstractJDBCMessageStore.this.getContent(_messageId, offsetInMessage, dst); + } + } + + + @Override + public ByteBuffer getContent(int offsetInMessage, int size) + { + ByteBuffer buf = ByteBuffer.allocate(size); + int length = getContent(offsetInMessage, buf); + buf.position(0); + buf.limit(length); + return buf; + } + + @Override + public synchronized StoreFuture flushToStore() + { + Connection conn = null; + try + { + if(!stored()) + { + conn = newConnection(); + + store(conn); + + conn.commit(); + storedSizeChange(getMetaData().getContentSize()); + } + } + catch (SQLException e) + { + if(getLogger().isDebugEnabled()) + { + getLogger().debug("Error when trying to flush message " + _messageId + " to store: " + e); + } + throw new RuntimeException(e); + } + finally + { + closeConnection(conn); + } + return StoreFuture.IMMEDIATE_FUTURE; + } + + @Override + public void remove() + { + int delta = getMetaData().getContentSize(); + AbstractJDBCMessageStore.this.removeMessage(_messageId); + storedSizeChange(-delta); + } + + private synchronized void store(final Connection conn) throws SQLException + { + if (!stored()) + { + try + { + storeMetaData(conn, _messageId, _metaData); + AbstractJDBCMessageStore.this.addContent(conn, _messageId, + _data == null ? ByteBuffer.allocate(0) : ByteBuffer.wrap(_data)); + } + finally + { + _metaData = null; + _data = null; + } + + if(getLogger().isDebugEnabled()) + { + getLogger().debug("Storing message " + _messageId + " to store"); + } + } + } + + private boolean stored() + { + return _metaData == null || _isRecovered; + } + } + + protected void closeConnection(final Connection conn) + { + if(conn != null) + { + try + { + conn.close(); + } + catch (SQLException e) + { + getLogger().error("Problem closing connection", e); + } + } + } + + protected void closePreparedStatement(final PreparedStatement stmt) + { + if (stmt != null) + { + try + { + stmt.close(); + } + catch(SQLException e) + { + getLogger().error("Problem closing prepared statement", e); + } + } + } + + @Override + public void addEventListener(EventListener eventListener, Event... events) + { + _eventManager.addEventListener(eventListener, events); + } + + private void insertConfiguredObject(ConfiguredObjectRecord configuredObject) throws AMQStoreException + { + if (_stateManager.isInState(State.ACTIVE)) + { + try + { + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); + try + { + stmt.setString(1, configuredObject.getId().toString()); + ResultSet rs = stmt.executeQuery(); + try + { + // If we don't have any data in the result set then we can add this configured object + if (!rs.next()) + { + PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_CONFIGURED_OBJECTS); + try + { + insertStmt.setString(1, configuredObject.getId().toString()); + insertStmt.setString(2, configuredObject.getType()); + if(configuredObject.getAttributes() == null) + { + insertStmt.setNull(3, Types.BLOB); + } + else + { + byte[] attributesAsBytes = configuredObject.getAttributes().getBytes(UTF8_CHARSET); + ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); + insertStmt.setBinaryStream(3, bis, attributesAsBytes.length); + } + insertStmt.execute(); + } + finally + { + insertStmt.close(); + } + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error inserting of configured object " + configuredObject + " into database: " + e.getMessage(), e); + } + } + } + + private int removeConfiguredObject(UUID id) throws AMQStoreException + { + int results = 0; + try + { + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_CONFIGURED_OBJECTS); + try + { + stmt.setString(1, id.toString()); + results = stmt.executeUpdate(); + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error deleting of configured object with id " + id + " from database: " + e.getMessage(), e); + } + return results; + } + + private void updateConfiguredObject(final ConfiguredObjectRecord configuredObject) throws AMQStoreException + { + if (_stateManager.isInState(State.ACTIVE)) + { + try + { + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); + try + { + stmt.setString(1, configuredObject.getId().toString()); + ResultSet rs = stmt.executeQuery(); + try + { + if (rs.next()) + { + PreparedStatement stmt2 = conn.prepareStatement(UPDATE_CONFIGURED_OBJECTS); + try + { + stmt2.setString(1, configuredObject.getType()); + if (configuredObject.getAttributes() != null) + { + byte[] attributesAsBytes = configuredObject.getAttributes().getBytes(UTF8_CHARSET); + ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); + stmt2.setBinaryStream(2, bis, attributesAsBytes.length); + } + else + { + stmt2.setNull(2, Types.BLOB); + } + stmt2.setString(3, configuredObject.getId().toString()); + stmt2.execute(); + } + finally + { + stmt2.close(); + } + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error updating configured object " + configuredObject + " in database: " + e.getMessage(), e); + } + } + } + + private ConfiguredObjectRecord loadConfiguredObject(final UUID id) throws AMQStoreException + { + ConfiguredObjectRecord result = null; + try + { + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); + try + { + stmt.setString(1, id.toString()); + ResultSet rs = stmt.executeQuery(); + try + { + if (rs.next()) + { + String type = rs.getString(1); + String attributes = getBlobAsString(rs, 2); + result = new ConfiguredObjectRecord(id, type, attributes); + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error loading of configured object with id " + id + " from database: " + + e.getMessage(), e); + } + return result; + } + + private List<ConfiguredObjectRecord> loadConfiguredObjects() throws SQLException + { + ArrayList<ConfiguredObjectRecord> results = new ArrayList<ConfiguredObjectRecord>(); + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_CONFIGURED_OBJECTS); + try + { + ResultSet rs = stmt.executeQuery(); + try + { + while (rs.next()) + { + String id = rs.getString(1); + String objectType = rs.getString(2); + String attributes = getBlobAsString(rs, 3); + results.add(new ConfiguredObjectRecord(UUID.fromString(id), objectType, attributes)); + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + return results; + } + + protected abstract String getBlobAsString(ResultSet rs, int col) throws SQLException; + + protected abstract void storedSizeChange(int storeSizeIncrease); + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java index e89bdd57fb..d0e48b5e8a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java @@ -21,14 +21,7 @@ package org.apache.qpid.server.store.derby; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; import java.io.File; -import java.io.IOException; -import java.lang.ref.SoftReference; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; import java.sql.Blob; import java.sql.CallableStatement; import java.sql.Connection; @@ -37,1853 +30,209 @@ import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; -import java.sql.Types; import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.configuration.Configuration; import org.apache.log4j.Logger; -import org.apache.qpid.AMQException; -import org.apache.qpid.AMQStoreException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.binding.Binding; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.message.EnqueableMessage; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.store.*; -import org.apache.qpid.server.plugin.MessageMetaDataType; -import org.apache.qpid.server.store.ConfigurationRecoveryHandler.BindingRecoveryHandler; -import org.apache.qpid.server.store.ConfigurationRecoveryHandler.ExchangeRecoveryHandler; -import org.apache.qpid.server.store.ConfigurationRecoveryHandler.QueueRecoveryHandler; +import org.apache.qpid.server.store.AbstractJDBCMessageStore; +import org.apache.qpid.server.store.Event; +import org.apache.qpid.server.store.EventListener; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MessageStoreConstants; /** * An implementation of a {@link MessageStore} that uses Apache Derby as the persistence * mechanism. * - * TODO extract the SQL statements into a generic JDBC store */ -public class DerbyMessageStore implements MessageStore +public class DerbyMessageStore extends AbstractJDBCMessageStore implements MessageStore { private static final Logger _logger = Logger.getLogger(DerbyMessageStore.class); private static final String SQL_DRIVER_NAME = "org.apache.derby.jdbc.EmbeddedDriver"; - private static final String DB_VERSION_TABLE_NAME = "QPID_DB_VERSION"; - - private static final String QUEUE_ENTRY_TABLE_NAME = "QPID_QUEUE_ENTRIES"; - - private static final String META_DATA_TABLE_NAME = "QPID_MESSAGE_METADATA"; - private static final String MESSAGE_CONTENT_TABLE_NAME = "QPID_MESSAGE_CONTENT"; - - private static final String LINKS_TABLE_NAME = "QPID_LINKS"; - private static final String BRIDGES_TABLE_NAME = "QPID_BRIDGES"; - - private static final String XID_TABLE_NAME = "QPID_XIDS"; - private static final String XID_ACTIONS_TABLE_NAME = "QPID_XID_ACTIONS"; - - private static final String CONFIGURED_OBJECTS_TABLE_NAME = "QPID_CONFIGURED_OBJECTS"; - - private static final int DB_VERSION = 6; - - - - private static Class<Driver> DRIVER_CLASS; public static final String MEMORY_STORE_LOCATION = ":memory:"; - private final AtomicLong _messageId = new AtomicLong(0); - private AtomicBoolean _closed = new AtomicBoolean(false); - - private String _connectionURL; - private static final String TABLE_EXISTANCE_QUERY = "SELECT 1 FROM SYS.SYSTABLES WHERE TABLENAME = ?"; - private static final String CREATE_DB_VERSION_TABLE = "CREATE TABLE "+DB_VERSION_TABLE_NAME+" ( version int not null )"; - private static final String INSERT_INTO_DB_VERSION = "INSERT INTO "+DB_VERSION_TABLE_NAME+" ( version ) VALUES ( ? )"; - - private static final String CREATE_QUEUE_ENTRY_TABLE = "CREATE TABLE "+QUEUE_ENTRY_TABLE_NAME+" ( queue_id varchar(36) not null, message_id bigint not null, PRIMARY KEY (queue_id, message_id) )"; - private static final String INSERT_INTO_QUEUE_ENTRY = "INSERT INTO " + QUEUE_ENTRY_TABLE_NAME + " (queue_id, message_id) values (?,?)"; - private static final String DELETE_FROM_QUEUE_ENTRY = "DELETE FROM " + QUEUE_ENTRY_TABLE_NAME + " WHERE queue_id = ? AND message_id =?"; - private static final String SELECT_FROM_QUEUE_ENTRY = "SELECT queue_id, message_id FROM " + QUEUE_ENTRY_TABLE_NAME + " ORDER BY queue_id, message_id"; - - - private static final String CREATE_META_DATA_TABLE = "CREATE TABLE " + META_DATA_TABLE_NAME - + " ( message_id bigint not null, meta_data blob, PRIMARY KEY ( message_id ) )"; - private static final String CREATE_MESSAGE_CONTENT_TABLE = "CREATE TABLE " + MESSAGE_CONTENT_TABLE_NAME - + " ( message_id bigint not null, content blob , PRIMARY KEY (message_id) )"; - - private static final String INSERT_INTO_MESSAGE_CONTENT = "INSERT INTO " + MESSAGE_CONTENT_TABLE_NAME - + "( message_id, content ) values (?, ?)"; - private static final String SELECT_FROM_MESSAGE_CONTENT = "SELECT content FROM " + MESSAGE_CONTENT_TABLE_NAME - + " WHERE message_id = ?"; - private static final String DELETE_FROM_MESSAGE_CONTENT = "DELETE FROM " + MESSAGE_CONTENT_TABLE_NAME - + " WHERE message_id = ?"; - - private static final String INSERT_INTO_META_DATA = "INSERT INTO " + META_DATA_TABLE_NAME + "( message_id , meta_data ) values (?, ?)";; - private static final String SELECT_FROM_META_DATA = - "SELECT meta_data FROM " + META_DATA_TABLE_NAME + " WHERE message_id = ?"; - private static final String DELETE_FROM_META_DATA = "DELETE FROM " + META_DATA_TABLE_NAME + " WHERE message_id = ?"; - private static final String SELECT_ALL_FROM_META_DATA = "SELECT message_id, meta_data FROM " + META_DATA_TABLE_NAME; - - private static final String CREATE_LINKS_TABLE = - "CREATE TABLE "+LINKS_TABLE_NAME+" ( id_lsb bigint not null," - + " id_msb bigint not null," - + " create_time bigint not null," - + " arguments blob, PRIMARY KEY ( id_lsb, id_msb ))"; - private static final String SELECT_FROM_LINKS = - "SELECT create_time, arguments FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and id_msb"; - private static final String DELETE_FROM_LINKS = "DELETE FROM " + LINKS_TABLE_NAME - + " WHERE id_lsb = ? and id_msb = ?"; - private static final String SELECT_ALL_FROM_LINKS = "SELECT id_lsb, id_msb, create_time, " - + "arguments FROM " + LINKS_TABLE_NAME; - private static final String FIND_LINK = "SELECT id_lsb, id_msb FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and" - + " id_msb = ?"; - private static final String INSERT_INTO_LINKS = "INSERT INTO " + LINKS_TABLE_NAME + "( id_lsb, " - + "id_msb, create_time, arguments ) values (?, ?, ?, ?)"; - - - private static final String CREATE_BRIDGES_TABLE = - "CREATE TABLE "+BRIDGES_TABLE_NAME+" ( id_lsb bigint not null," - + " id_msb bigint not null," - + " create_time bigint not null," - + " link_id_lsb bigint not null," - + " link_id_msb bigint not null," - + " arguments blob, PRIMARY KEY ( id_lsb, id_msb ))"; - private static final String SELECT_FROM_BRIDGES = - "SELECT create_time, link_id_lsb, link_id_msb, arguments FROM " - + BRIDGES_TABLE_NAME + " WHERE id_lsb = ? and id_msb = ?"; - private static final String DELETE_FROM_BRIDGES = "DELETE FROM " + BRIDGES_TABLE_NAME - + " WHERE id_lsb = ? and id_msb = ?"; - private static final String SELECT_ALL_FROM_BRIDGES = "SELECT id_lsb, id_msb, " - + " create_time," - + " link_id_lsb, link_id_msb, " - + "arguments FROM " + BRIDGES_TABLE_NAME - + " WHERE link_id_lsb = ? and link_id_msb = ?"; - private static final String FIND_BRIDGE = "SELECT id_lsb, id_msb FROM " + BRIDGES_TABLE_NAME + - " WHERE id_lsb = ? and id_msb = ?"; - private static final String INSERT_INTO_BRIDGES = "INSERT INTO " + BRIDGES_TABLE_NAME + "( id_lsb, id_msb, " - + "create_time, " - + "link_id_lsb, link_id_msb, " - + "arguments )" - + " values (?, ?, ?, ?, ?, ?)"; - - private static final String CREATE_XIDS_TABLE = - "CREATE TABLE "+XID_TABLE_NAME+" ( format bigint not null," - + " global_id varchar(64) for bit data, branch_id varchar(64) for bit data, PRIMARY KEY ( format, " + - "global_id, branch_id ))"; - private static final String INSERT_INTO_XIDS = - "INSERT INTO "+XID_TABLE_NAME+" ( format, global_id, branch_id ) values (?, ?, ?)"; - private static final String DELETE_FROM_XIDS = "DELETE FROM " + XID_TABLE_NAME - + " WHERE format = ? and global_id = ? and branch_id = ?"; - private static final String SELECT_ALL_FROM_XIDS = "SELECT format, global_id, branch_id FROM " + XID_TABLE_NAME; - - - private static final String CREATE_XID_ACTIONS_TABLE = - "CREATE TABLE "+XID_ACTIONS_TABLE_NAME+" ( format bigint not null," - + " global_id varchar(64) for bit data not null, branch_id varchar(64) for bit data not null, " + - "action_type char not null, queue_id varchar(36) not null, message_id bigint not null" + - ", PRIMARY KEY ( " + - "format, global_id, branch_id, action_type, queue_id, message_id))"; - private static final String INSERT_INTO_XID_ACTIONS = - "INSERT INTO "+XID_ACTIONS_TABLE_NAME+" ( format, global_id, branch_id, action_type, " + - "queue_id, message_id ) values (?,?,?,?,?,?) "; - private static final String DELETE_FROM_XID_ACTIONS = "DELETE FROM " + XID_ACTIONS_TABLE_NAME - + " WHERE format = ? and global_id = ? and branch_id = ?"; - private static final String SELECT_ALL_FROM_XID_ACTIONS = - "SELECT action_type, queue_id, message_id FROM " + XID_ACTIONS_TABLE_NAME + - " WHERE format = ? and global_id = ? and branch_id = ?"; - - private static final String CREATE_CONFIGURED_OBJECTS_TABLE = "CREATE TABLE " + CONFIGURED_OBJECTS_TABLE_NAME - + " ( id VARCHAR(36) not null, object_type varchar(255), attributes blob, PRIMARY KEY (id))"; - private static final String INSERT_INTO_CONFIGURED_OBJECTS = "INSERT INTO " + CONFIGURED_OBJECTS_TABLE_NAME - + " ( id, object_type, attributes) VALUES (?,?,?)"; - private static final String UPDATE_CONFIGURED_OBJECTS = "UPDATE " + CONFIGURED_OBJECTS_TABLE_NAME - + " set object_type =?, attributes = ? where id = ?"; - private static final String DELETE_FROM_CONFIGURED_OBJECTS = "DELETE FROM " + CONFIGURED_OBJECTS_TABLE_NAME - + " where id = ?"; - private static final String FIND_CONFIGURED_OBJECT = "SELECT object_type, attributes FROM " + CONFIGURED_OBJECTS_TABLE_NAME - + " where id = ?"; - private static final String SELECT_FROM_CONFIGURED_OBJECTS = "SELECT id, object_type, attributes FROM " + CONFIGURED_OBJECTS_TABLE_NAME; - - private final Charset UTF8_CHARSET = Charset.forName("UTF-8"); - private static final String DERBY_SINGLE_DB_SHUTDOWN_CODE = "08006"; public static final String TYPE = "DERBY"; - private final StateManager _stateManager; - - private final EventManager _eventManager = new EventManager(); - private long _totalStoreSize; private boolean _limitBusted; private long _persistentSizeLowThreshold; private long _persistentSizeHighThreshold; - private MessageStoreRecoveryHandler _messageRecoveryHandler; - - private TransactionLogRecoveryHandler _tlogRecoveryHandler; - - private ConfigurationRecoveryHandler _configRecoveryHandler; private String _storeLocation; + private Class<Driver> _driverClass; public DerbyMessageStore() { - _stateManager = new StateManager(_eventManager); } - private ConfiguredObjectHelper _configuredObjectHelper = new ConfiguredObjectHelper(); - - @Override - public void configureConfigStore(String name, - ConfigurationRecoveryHandler configRecoveryHandler, - Configuration storeConfiguration) throws Exception + protected Logger getLogger() { - _stateManager.attainState(State.INITIALISING); - _configRecoveryHandler = configRecoveryHandler; - - commonConfiguration(name, storeConfiguration); - + return _logger; } @Override - public void configureMessageStore(String name, - MessageStoreRecoveryHandler recoveryHandler, - TransactionLogRecoveryHandler tlogRecoveryHandler, - Configuration storeConfiguration) throws Exception + protected String getSqlBlobType() { - _tlogRecoveryHandler = tlogRecoveryHandler; - _messageRecoveryHandler = recoveryHandler; - - _stateManager.attainState(State.INITIALISED); + return "blob"; } @Override - public void activate() throws Exception - { - _stateManager.attainState(State.ACTIVATING); - - // this recovers durable exchanges, queues, and bindings - recoverConfiguration(_configRecoveryHandler); - recoverMessages(_messageRecoveryHandler); - TransactionLogRecoveryHandler.DtxRecordRecoveryHandler dtxrh = recoverQueueEntries(_tlogRecoveryHandler); - recoverXids(dtxrh); - - _stateManager.attainState(State.ACTIVE); - } - - private void commonConfiguration(String name, Configuration storeConfiguration) - throws ClassNotFoundException, SQLException - { - initialiseDriver(); - - //Update to pick up QPID_WORK and use that as the default location not just derbyDB - - final String databasePath = storeConfiguration.getString(MessageStoreConstants.ENVIRONMENT_PATH_PROPERTY, System.getProperty("QPID_WORK") - + File.separator + "derbyDB"); - - if(!MEMORY_STORE_LOCATION.equals(databasePath)) - { - File environmentPath = new File(databasePath); - if (!environmentPath.exists()) - { - if (!environmentPath.mkdirs()) - { - throw new IllegalArgumentException("Environment path " + environmentPath + " could not be read or created. " - + "Ensure the path is correct and that the permissions are correct."); - } - } - } - - _storeLocation = databasePath; - - _persistentSizeHighThreshold = storeConfiguration.getLong(MessageStoreConstants.OVERFULL_SIZE_PROPERTY, -1l); - _persistentSizeLowThreshold = storeConfiguration.getLong(MessageStoreConstants.UNDERFULL_SIZE_PROPERTY, _persistentSizeHighThreshold); - if(_persistentSizeLowThreshold > _persistentSizeHighThreshold || _persistentSizeLowThreshold < 0l) - { - _persistentSizeLowThreshold = _persistentSizeHighThreshold; - } - - createOrOpenDatabase(name, databasePath); - - Connection conn = newAutoCommitConnection();; - try - { - _totalStoreSize = getSizeOnDisk(conn); - } - finally - { - conn.close(); - } - } - - private static synchronized void initialiseDriver() throws ClassNotFoundException - { - if(DRIVER_CLASS == null) - { - DRIVER_CLASS = (Class<Driver>) Class.forName(SQL_DRIVER_NAME); - } - } - - private void createOrOpenDatabase(String name, final String environmentPath) throws SQLException - { - //FIXME this the _vhost name should not be added here, but derby wont use an empty directory as was possibly just created. - _connectionURL = "jdbc:derby" + (environmentPath.equals(MEMORY_STORE_LOCATION) ? environmentPath : ":" + environmentPath + "/") + name + ";create=true"; - - Connection conn = newAutoCommitConnection(); - - createVersionTable(conn); - createConfiguredObjectsTable(conn); - createQueueEntryTable(conn); - createMetaDataTable(conn); - createMessageContentTable(conn); - createLinkTable(conn); - createBridgeTable(conn); - createXidTable(conn); - createXidActionTable(conn); - conn.close(); - } - - - - private void createVersionTable(final Connection conn) throws SQLException - { - if(!tableExists(DB_VERSION_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_DB_VERSION_TABLE); - } - finally - { - stmt.close(); - } - - PreparedStatement pstmt = conn.prepareStatement(INSERT_INTO_DB_VERSION); - try - { - pstmt.setInt(1, DB_VERSION); - pstmt.execute(); - } - finally - { - pstmt.close(); - } - } - - } - - private void createConfiguredObjectsTable(final Connection conn) throws SQLException - { - if(!tableExists(CONFIGURED_OBJECTS_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_CONFIGURED_OBJECTS_TABLE); - } - finally - { - stmt.close(); - } - } - } - - private void createQueueEntryTable(final Connection conn) throws SQLException - { - if(!tableExists(QUEUE_ENTRY_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_QUEUE_ENTRY_TABLE); - } - finally - { - stmt.close(); - } - } - - } - - private void createMetaDataTable(final Connection conn) throws SQLException - { - if(!tableExists(META_DATA_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_META_DATA_TABLE); - } - finally - { - stmt.close(); - } - } - - } - - - private void createMessageContentTable(final Connection conn) throws SQLException - { - if(!tableExists(MESSAGE_CONTENT_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_MESSAGE_CONTENT_TABLE); - } - finally - { - stmt.close(); - } - } - - } - - private void createLinkTable(final Connection conn) throws SQLException - { - if(!tableExists(LINKS_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_LINKS_TABLE); - } - finally - { - stmt.close(); - } - } - } - - - private void createBridgeTable(final Connection conn) throws SQLException + protected String getSqlVarBinaryType(int size) { - if(!tableExists(BRIDGES_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_BRIDGES_TABLE); - } - finally - { - stmt.close(); - } - } + return "varchar("+size+") for bit data"; } - private void createXidTable(final Connection conn) throws SQLException - { - if(!tableExists(XID_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_XIDS_TABLE); - } - finally - { - stmt.close(); - } - } - } - - - private void createXidActionTable(final Connection conn) throws SQLException - { - if(!tableExists(XID_ACTIONS_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_XID_ACTIONS_TABLE); - } - finally - { - stmt.close(); - } - } - } - - private boolean tableExists(final String tableName, final Connection conn) throws SQLException - { - PreparedStatement stmt = conn.prepareStatement(TABLE_EXISTANCE_QUERY); - try - { - stmt.setString(1, tableName); - ResultSet rs = stmt.executeQuery(); - try - { - return rs.next(); - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - - private void recoverConfiguration(ConfigurationRecoveryHandler recoveryHandler) throws AMQException + @Override + protected String getSqlBigIntType() { - try - { - List<ConfiguredObjectRecord> configuredObjects = loadConfiguredObjects(); - - ExchangeRecoveryHandler erh = recoveryHandler.begin(this); - _configuredObjectHelper.recoverExchanges(erh, configuredObjects); - - QueueRecoveryHandler qrh = erh.completeExchangeRecovery(); - _configuredObjectHelper.recoverQueues(qrh, configuredObjects); - - BindingRecoveryHandler brh = qrh.completeQueueRecovery(); - _configuredObjectHelper.recoverBindings(brh, configuredObjects); - - brh.completeBindingRecovery(); - } - catch (SQLException e) - { - throw new AMQStoreException("Error recovering persistent state: " + e.getMessage(), e); - } + return "bigint"; } - @Override - public void close() throws Exception + protected void doClose() throws SQLException { - _closed.getAndSet(true); - _stateManager.attainState(State.CLOSING); - try { Connection conn = DriverManager.getConnection(_connectionURL + ";shutdown=true"); // Shouldn't reach this point - shutdown=true should throw SQLException conn.close(); - _logger.error("Unable to shut down the store"); + getLogger().error("Unable to shut down the store"); } catch (SQLException e) { - if (e.getSQLState().equalsIgnoreCase(DERBY_SINGLE_DB_SHUTDOWN_CODE)) + if (e.getSQLState().equalsIgnoreCase(DerbyMessageStore.DERBY_SINGLE_DB_SHUTDOWN_CODE)) { //expected and represents a clean shutdown of this database only, do nothing. } else { - _logger.error("Exception whilst shutting down the store: " + e); - } - } - - _stateManager.attainState(State.CLOSED); - } - - @Override - public StoredMessage addMessage(StorableMessageMetaData metaData) - { - if(metaData.isPersistent()) - { - return new StoredDerbyMessage(_messageId.incrementAndGet(), metaData); - } - else - { - return new StoredMemoryMessage(_messageId.incrementAndGet(), metaData); - } - } - - public StoredMessage getMessage(long messageNumber) - { - return null; - } - - public void removeMessage(long messageId) - { - try - { - Connection conn = newConnection(); - try - { - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_META_DATA); - try - { - stmt.setLong(1,messageId); - int results = stmt.executeUpdate(); - stmt.close(); - - if (results == 0) - { - _logger.warn("Message metadata not found for message id " + messageId); - } - - if (_logger.isDebugEnabled()) - { - _logger.debug("Deleted metadata for message " + messageId); - } - - stmt = conn.prepareStatement(DELETE_FROM_MESSAGE_CONTENT); - stmt.setLong(1,messageId); - results = stmt.executeUpdate(); - } - finally - { - stmt.close(); - } - conn.commit(); - } - catch(SQLException e) - { - try - { - conn.rollback(); - } - catch(SQLException t) - { - // ignore - we are re-throwing underlying exception - } - + getLogger().error("Exception whilst shutting down the store: " + e); throw e; - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new RuntimeException("Error removing message with id " + messageId + " from database: " + e.getMessage(), e); - } - - } - - @Override - public void createExchange(Exchange exchange) throws AMQStoreException - { - if (_stateManager.isInState(State.ACTIVE)) - { - ConfiguredObjectRecord configuredObject = _configuredObjectHelper.createExchangeConfiguredObject(exchange); - insertConfiguredObject(configuredObject); - } - - } - - @Override - public void removeExchange(Exchange exchange) throws AMQStoreException - { - int results = removeConfiguredObject(exchange.getId()); - if (results == 0) - { - throw new AMQStoreException("Exchange " + exchange.getName() + " with id " + exchange.getId() + " not found"); - } - } - - @Override - public void bindQueue(Binding binding) - throws AMQStoreException - { - if (_stateManager.isInState(State.ACTIVE)) - { - ConfiguredObjectRecord configuredObject = _configuredObjectHelper.createBindingConfiguredObject(binding); - insertConfiguredObject(configuredObject); } } @Override - public void unbindQueue(Binding binding) - throws AMQStoreException + protected void implementationSpecificConfiguration(String name, Configuration storeConfiguration) + throws ClassNotFoundException { - int results = removeConfiguredObject(binding.getId()); - if (results == 0) - { - throw new AMQStoreException("Binding " + binding + " not found"); - } - } - - @Override - public void createQueue(AMQQueue queue) throws AMQStoreException - { - createQueue(queue, null); - } - - @Override - public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException - { - _logger.debug("public void createQueue(AMQQueue queue = " + queue + "): called"); - - if (_stateManager.isInState(State.ACTIVE)) - { - ConfiguredObjectRecord queueConfiguredObject = _configuredObjectHelper.createQueueConfiguredObject(queue, arguments); - insertConfiguredObject(queueConfiguredObject); - } - } - - /** - * Updates the specified queue in the persistent store, IF it is already present. If the queue - * is not present in the store, it will not be added. - * - * NOTE: Currently only updates the exclusivity. - * - * @param queue The queue to update the entry for. - * @throws AMQStoreException If the operation fails for any reason. - */ - @Override - public void updateQueue(final AMQQueue queue) throws AMQStoreException - { - if (_stateManager.isInState(State.ACTIVE)) - { - ConfiguredObjectRecord queueConfiguredObject = loadConfiguredObject(queue.getId()); - if (queueConfiguredObject != null) - { - ConfiguredObjectRecord newQueueRecord = _configuredObjectHelper.updateQueueConfiguredObject(queue, queueConfiguredObject); - updateConfiguredObject(newQueueRecord); - } - } - - } - - /** - * Convenience method to create a new Connection configured for TRANSACTION_READ_COMMITED - * isolation and with auto-commit transactions enabled. - */ - private Connection newAutoCommitConnection() throws SQLException - { - final Connection connection = newConnection(); - try - { - connection.setAutoCommit(true); - } - catch (SQLException sqlEx) - { - - try - { - connection.close(); - } - finally - { - throw sqlEx; - } - } + //Update to pick up QPID_WORK and use that as the default location not just derbyDB - return connection; - } + _driverClass = (Class<Driver>) Class.forName(SQL_DRIVER_NAME); - /** - * Convenience method to create a new Connection configured for TRANSACTION_READ_COMMITED - * isolation and with auto-commit transactions disabled. - */ - private Connection newConnection() throws SQLException - { - final Connection connection = DriverManager.getConnection(_connectionURL); - try - { - connection.setAutoCommit(false); - connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); - } - catch (SQLException sqlEx) - { - try - { - connection.close(); - } - finally - { - throw sqlEx; - } - } - return connection; - } - - @Override - public void removeQueue(final AMQQueue queue) throws AMQStoreException - { - AMQShortString name = queue.getNameShortString(); - _logger.debug("public void removeQueue(AMQShortString name = " + name + "): called"); - int results = removeConfiguredObject(queue.getId()); - if (results == 0) - { - throw new AMQStoreException("Queue " + name + " with id " + queue.getId() + " not found"); - } - } + final String databasePath = storeConfiguration.getString(MessageStoreConstants.ENVIRONMENT_PATH_PROPERTY, System.getProperty("QPID_WORK") + + File.separator + "derbyDB"); - private byte[] convertStringMapToBytes(final Map<String, String> arguments) throws AMQStoreException - { - byte[] argumentBytes; - if(arguments == null) - { - argumentBytes = new byte[0]; - } - else + if(!MEMORY_STORE_LOCATION.equals(databasePath)) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(bos); - - - try + File environmentPath = new File(databasePath); + if (!environmentPath.exists()) { - dos.writeInt(arguments.size()); - for(Map.Entry<String,String> arg : arguments.entrySet()) + if (!environmentPath.mkdirs()) { - dos.writeUTF(arg.getKey()); - dos.writeUTF(arg.getValue()); + throw new IllegalArgumentException("Environment path " + environmentPath + " could not be read or created. " + + "Ensure the path is correct and that the permissions are correct."); } } - catch (IOException e) - { - // This should never happen - throw new AMQStoreException(e.getMessage(), e); - } - argumentBytes = bos.toByteArray(); } - return argumentBytes; - } - - - - @Override - public Transaction newTransaction() - { - return new DerbyTransaction(); - } - - public void enqueueMessage(ConnectionWrapper connWrapper, final TransactionLogResource queue, Long messageId) throws AMQStoreException - { - Connection conn = connWrapper.getConnection(); + _storeLocation = databasePath; - try - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Enqueuing message " + messageId + " on queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + queue.getId()+ "[Connection" + conn + "]"); - } - - PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_QUEUE_ENTRY); - try - { - stmt.setString(1, queue.getId().toString()); - stmt.setLong(2,messageId); - stmt.executeUpdate(); - } - finally - { - stmt.close(); - } - } - catch (SQLException e) + _persistentSizeHighThreshold = storeConfiguration.getLong(MessageStoreConstants.OVERFULL_SIZE_PROPERTY, -1l); + _persistentSizeLowThreshold = storeConfiguration.getLong(MessageStoreConstants.UNDERFULL_SIZE_PROPERTY, _persistentSizeHighThreshold); + if(_persistentSizeLowThreshold > _persistentSizeHighThreshold || _persistentSizeLowThreshold < 0l) { - _logger.error("Failed to enqueue: " + e.getMessage(), e); - throw new AMQStoreException("Error writing enqueued message with id " + messageId + " for queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + " with id " + queue.getId() - + " to database", e); + _persistentSizeLowThreshold = _persistentSizeHighThreshold; } - } - - public void dequeueMessage(ConnectionWrapper connWrapper, final TransactionLogResource queue, Long messageId) throws AMQStoreException - { - - Connection conn = connWrapper.getConnection(); - - - try - { - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_QUEUE_ENTRY); - try - { - stmt.setString(1, queue.getId().toString()); - stmt.setLong(2,messageId); - int results = stmt.executeUpdate(); - + //FIXME this the _vhost name should not be added here, but derby wont use an empty directory as was possibly just created. + _connectionURL = "jdbc:derby" + (databasePath.equals(MEMORY_STORE_LOCATION) ? databasePath: ":" + databasePath+ "/") + name + ";create=true"; - if(results != 1) - { - throw new AMQStoreException("Unable to find message with id " + messageId + " on queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) - + " with id " + queue.getId()); - } - if (_logger.isDebugEnabled()) - { - _logger.debug("Dequeuing message " + messageId + " on queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) - + " with id " + queue.getId()); - } - } - finally - { - stmt.close(); - } - } - catch (SQLException e) - { - _logger.error("Failed to dequeue: " + e.getMessage(), e); - throw new AMQStoreException("Error deleting enqueued message with id " + messageId + " for queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) - + " with id " + queue.getId() + " from database", e); - } + _eventManager.addEventListener(new EventListener() + { + @Override + public void event(Event event) + { + setInitialSize(); + } + }, Event.BEFORE_ACTIVATE); } - - private void removeXid(ConnectionWrapper connWrapper, long format, byte[] globalId, byte[] branchId) - throws AMQStoreException + private void setInitialSize() { - Connection conn = connWrapper.getConnection(); - - + Connection conn = null; try { - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_XIDS); - try - { - stmt.setLong(1,format); - stmt.setBytes(2,globalId); - stmt.setBytes(3,branchId); - int results = stmt.executeUpdate(); - - if(results != 1) - { - throw new AMQStoreException("Unable to find message with xid"); - } - } - finally - { - stmt.close(); - } - - stmt = conn.prepareStatement(DELETE_FROM_XID_ACTIONS); try { - stmt.setLong(1,format); - stmt.setBytes(2,globalId); - stmt.setBytes(3,branchId); - int results = stmt.executeUpdate(); - + conn = newAutoCommitConnection(); + _totalStoreSize = getSizeOnDisk(conn); } finally { - stmt.close(); - } - - } - catch (SQLException e) - { - _logger.error("Failed to dequeue: " + e.getMessage(), e); - throw new AMQStoreException("Error deleting enqueued message with xid", e); - } - - } - - - private void recordXid(ConnectionWrapper connWrapper, long format, byte[] globalId, byte[] branchId, - Transaction.Record[] enqueues, Transaction.Record[] dequeues) throws AMQStoreException - { - Connection conn = connWrapper.getConnection(); - - - try - { - - PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_XIDS); - try - { - stmt.setLong(1,format); - stmt.setBytes(2, globalId); - stmt.setBytes(3, branchId); - stmt.executeUpdate(); - } - finally - { - stmt.close(); - } - - stmt = conn.prepareStatement(INSERT_INTO_XID_ACTIONS); - - try - { - stmt.setLong(1,format); - stmt.setBytes(2, globalId); - stmt.setBytes(3, branchId); - - if(enqueues != null) + if(conn != null) { - stmt.setString(4, "E"); - for(Transaction.Record record : enqueues) - { - stmt.setString(5, record.getQueue().getId().toString()); - stmt.setLong(6, record.getMessage().getMessageNumber()); - stmt.executeUpdate(); - } - } + conn.close(); - if(dequeues != null) - { - stmt.setString(4, "D"); - for(Transaction.Record record : dequeues) - { - stmt.setString(5, record.getQueue().getId().toString()); - stmt.setLong(6, record.getMessage().getMessageNumber()); - stmt.executeUpdate(); - } - } + } } - finally - { - stmt.close(); - } - - } - catch (SQLException e) - { - _logger.error("Failed to enqueue: " + e.getMessage(), e); - throw new AMQStoreException("Error writing xid ", e); - } - - } - - private static final class ConnectionWrapper - { - private final Connection _connection; - - public ConnectionWrapper(Connection conn) - { - _connection = conn; - } - - public Connection getConnection() - { - return _connection; - } - } - - - public void commitTran(ConnectionWrapper connWrapper) throws AMQStoreException - { - - try - { - Connection conn = connWrapper.getConnection(); - conn.commit(); - - if (_logger.isDebugEnabled()) - { - _logger.debug("commit tran completed"); - } - - conn.close(); } catch (SQLException e) { - throw new AMQStoreException("Error commit tx: " + e.getMessage(), e); - } - finally - { - + getLogger().error("Unable to set initial store size", e); } } - public StoreFuture commitTranAsync(ConnectionWrapper connWrapper) throws AMQStoreException - { - commitTran(connWrapper); - return StoreFuture.IMMEDIATE_FUTURE; - } - - public void abortTran(ConnectionWrapper connWrapper) throws AMQStoreException + protected String getBlobAsString(ResultSet rs, int col) throws SQLException { - if (connWrapper == null) + Blob blob = rs.getBlob(col); + if(blob == null) { - throw new AMQStoreException("Fatal internal error: transactional context is empty at abortTran"); - } - - if (_logger.isDebugEnabled()) - { - _logger.debug("abort tran called: " + connWrapper.getConnection()); - } - - try - { - Connection conn = connWrapper.getConnection(); - conn.rollback(); - conn.close(); + return null; } - catch (SQLException e) - { - throw new AMQStoreException("Error aborting transaction: " + e.getMessage(), e); - } - + byte[] bytes = blob.getBytes(1, (int)blob.length()); + return new String(bytes, UTF8_CHARSET); } - public Long getNewMessageId() + protected byte[] getBlobAsBytes(ResultSet rs, int col) throws SQLException { - return _messageId.incrementAndGet(); + Blob dataAsBlob = rs.getBlob(col); + return dataAsBlob.getBytes(1,(int) dataAsBlob.length()); } - private void storeMetaData(Connection conn, long messageId, StorableMessageMetaData metaData) - throws SQLException + protected boolean tableExists(final String tableName, final Connection conn) throws SQLException { - if(_logger.isDebugEnabled()) - { - _logger.debug("Adding metadata for message " +messageId); - } - - PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_META_DATA); + PreparedStatement stmt = conn.prepareStatement(TABLE_EXISTANCE_QUERY); try { - stmt.setLong(1,messageId); - - final int bodySize = 1 + metaData.getStorableSize(); - byte[] underlying = new byte[bodySize]; - underlying[0] = (byte) metaData.getType().ordinal(); - java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(underlying); - buf.position(1); - buf = buf.slice(); - - metaData.writeToBuffer(0, buf); - ByteArrayInputStream bis = new ByteArrayInputStream(underlying); + stmt.setString(1, tableName); + ResultSet rs = stmt.executeQuery(); try { - stmt.setBinaryStream(2,bis,underlying.length); - int result = stmt.executeUpdate(); - - if(result == 0) - { - throw new RuntimeException("Unable to add meta data for message " +messageId); - } + return rs.next(); } finally { - try - { - bis.close(); - } - catch (IOException e) - { - - throw new SQLException(e); - } + rs.close(); } - } finally { stmt.close(); } - - } - - - - - private void recoverMessages(MessageStoreRecoveryHandler recoveryHandler) throws SQLException - { - Connection conn = newAutoCommitConnection(); - try - { - MessageStoreRecoveryHandler.StoredMessageRecoveryHandler messageHandler = recoveryHandler.begin(); - - Statement stmt = conn.createStatement(); - try - { - ResultSet rs = stmt.executeQuery(SELECT_ALL_FROM_META_DATA); - try - { - - long maxId = 0; - - while(rs.next()) - { - - long messageId = rs.getLong(1); - Blob dataAsBlob = rs.getBlob(2); - - if(messageId > maxId) - { - maxId = messageId; - } - - byte[] dataAsBytes = dataAsBlob.getBytes(1,(int) dataAsBlob.length()); - java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(dataAsBytes); - buf.position(1); - buf = buf.slice(); - MessageMetaDataType type = MessageMetaDataTypeRegistry.fromOrdinal(dataAsBytes[0]); - StorableMessageMetaData metaData = type.createMetaData(buf); - StoredDerbyMessage message = new StoredDerbyMessage(messageId, metaData, true); - messageHandler.message(message); - } - - _messageId.set(maxId); - - messageHandler.completeMessageRecovery(); - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - - - - private TransactionLogRecoveryHandler.DtxRecordRecoveryHandler recoverQueueEntries(TransactionLogRecoveryHandler recoveryHandler) throws SQLException - { - Connection conn = newAutoCommitConnection(); - try - { - TransactionLogRecoveryHandler.QueueEntryRecoveryHandler queueEntryHandler = recoveryHandler.begin(this); - - Statement stmt = conn.createStatement(); - try - { - ResultSet rs = stmt.executeQuery(SELECT_FROM_QUEUE_ENTRY); - try - { - while(rs.next()) - { - - String id = rs.getString(1); - long messageId = rs.getLong(2); - queueEntryHandler.queueEntry(UUID.fromString(id), messageId); - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - - return queueEntryHandler.completeQueueEntryRecovery(); - } - finally - { - conn.close(); - } - } - - private static final class Xid - { - - private final long _format; - private final byte[] _globalId; - private final byte[] _branchId; - - public Xid(long format, byte[] globalId, byte[] branchId) - { - _format = format; - _globalId = globalId; - _branchId = branchId; - } - - public long getFormat() - { - return _format; - } - - public byte[] getGlobalId() - { - return _globalId; - } - - public byte[] getBranchId() - { - return _branchId; - } - } - - private static class RecordImpl implements Transaction.Record, TransactionLogResource, EnqueableMessage - { - - private long _messageNumber; - private UUID _queueId; - - public RecordImpl(UUID queueId, long messageNumber) - { - _messageNumber = messageNumber; - _queueId = queueId; - } - - @Override - public TransactionLogResource getQueue() - { - return this; - } - - @Override - public EnqueableMessage getMessage() - { - return this; - } - - @Override - public long getMessageNumber() - { - return _messageNumber; - } - - @Override - public boolean isPersistent() - { - return true; - } - - @Override - public StoredMessage getStoredMessage() - { - throw new UnsupportedOperationException(); - } - - @Override - public UUID getId() - { - return _queueId; - } - } - - private void recoverXids(TransactionLogRecoveryHandler.DtxRecordRecoveryHandler dtxrh) throws SQLException - { - Connection conn = newAutoCommitConnection(); - try - { - List<Xid> xids = new ArrayList<Xid>(); - - Statement stmt = conn.createStatement(); - try - { - ResultSet rs = stmt.executeQuery(SELECT_ALL_FROM_XIDS); - try - { - while(rs.next()) - { - - long format = rs.getLong(1); - byte[] globalId = rs.getBytes(2); - byte[] branchId = rs.getBytes(3); - xids.add(new Xid(format, globalId, branchId)); - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - - - - for(Xid xid : xids) - { - List<RecordImpl> enqueues = new ArrayList<RecordImpl>(); - List<RecordImpl> dequeues = new ArrayList<RecordImpl>(); - - PreparedStatement pstmt = conn.prepareStatement(SELECT_ALL_FROM_XID_ACTIONS); - - try - { - pstmt.setLong(1, xid.getFormat()); - pstmt.setBytes(2, xid.getGlobalId()); - pstmt.setBytes(3, xid.getBranchId()); - - ResultSet rs = pstmt.executeQuery(); - try - { - while(rs.next()) - { - - String actionType = rs.getString(1); - UUID queueId = UUID.fromString(rs.getString(2)); - long messageId = rs.getLong(3); - - RecordImpl record = new RecordImpl(queueId, messageId); - List<RecordImpl> records = "E".equals(actionType) ? enqueues : dequeues; - records.add(record); - } - } - finally - { - rs.close(); - } - } - finally - { - pstmt.close(); - } - - dtxrh.dtxRecord(xid.getFormat(), xid.getGlobalId(), xid.getBranchId(), - enqueues.toArray(new RecordImpl[enqueues.size()]), - dequeues.toArray(new RecordImpl[dequeues.size()])); - } - - - dtxrh.completeDtxRecordRecovery(); - } - finally - { - conn.close(); - } - - } - - StorableMessageMetaData getMetaData(long messageId) throws SQLException - { - - Connection conn = newAutoCommitConnection(); - try - { - PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_META_DATA); - try - { - stmt.setLong(1,messageId); - ResultSet rs = stmt.executeQuery(); - try - { - - if(rs.next()) - { - Blob dataAsBlob = rs.getBlob(1); - - byte[] dataAsBytes = dataAsBlob.getBytes(1,(int) dataAsBlob.length()); - java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(dataAsBytes); - buf.position(1); - buf = buf.slice(); - MessageMetaDataType type = MessageMetaDataTypeRegistry.fromOrdinal(dataAsBytes[0]); - StorableMessageMetaData metaData = type.createMetaData(buf); - - return metaData; - } - else - { - throw new RuntimeException("Meta data not found for message with id " + messageId); - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - - - private void addContent(Connection conn, long messageId, ByteBuffer src) - { - if(_logger.isDebugEnabled()) - { - _logger.debug("Adding content for message " +messageId); - } - PreparedStatement stmt = null; - - try - { - src = src.slice(); - - byte[] chunkData = new byte[src.limit()]; - src.duplicate().get(chunkData); - - stmt = conn.prepareStatement(INSERT_INTO_MESSAGE_CONTENT); - stmt.setLong(1,messageId); - - ByteArrayInputStream bis = new ByteArrayInputStream(chunkData); - stmt.setBinaryStream(2, bis, chunkData.length); - stmt.executeUpdate(); - } - catch (SQLException e) - { - closeConnection(conn); - throw new RuntimeException("Error adding content for message " + messageId + ": " + e.getMessage(), e); - } - finally - { - closePreparedStatement(stmt); - } - - } - - - public int getContent(long messageId, int offset, ByteBuffer dst) - { - Connection conn = null; - PreparedStatement stmt = null; - - try - { - conn = newAutoCommitConnection(); - - stmt = conn.prepareStatement(SELECT_FROM_MESSAGE_CONTENT); - stmt.setLong(1,messageId); - ResultSet rs = stmt.executeQuery(); - - int written = 0; - - if (rs.next()) - { - - Blob dataAsBlob = rs.getBlob(1); - - final int size = (int) dataAsBlob.length(); - byte[] dataAsBytes = dataAsBlob.getBytes(1, size); - - if (offset > size) - { - throw new RuntimeException("Offset " + offset + " is greater than message size " + size - + " for message id " + messageId + "!"); - - } - - written = size - offset; - if(written > dst.remaining()) - { - written = dst.remaining(); - } - - dst.put(dataAsBytes, offset, written); - } - - return written; - - } - catch (SQLException e) - { - throw new RuntimeException("Error retrieving content from offset " + offset + " for message " + messageId + ": " + e.getMessage(), e); - } - finally - { - closePreparedStatement(stmt); - closeConnection(conn); - } - - - } - - @Override - public boolean isPersistent() - { - return true; - } - - - private class DerbyTransaction implements Transaction - { - private final ConnectionWrapper _connWrapper; - private int _storeSizeIncrease; - - - private DerbyTransaction() - { - try - { - _connWrapper = new ConnectionWrapper(newConnection()); - } - catch (SQLException e) - { - throw new RuntimeException(e); - } - } - - @Override - public void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException - { - final StoredMessage storedMessage = message.getStoredMessage(); - if(storedMessage instanceof StoredDerbyMessage) - { - try - { - ((StoredDerbyMessage) storedMessage).store(_connWrapper.getConnection()); - } - catch (SQLException e) - { - throw new AMQStoreException("Exception on enqueuing message " + _messageId, e); - } - } - _storeSizeIncrease += storedMessage.getMetaData().getContentSize(); - DerbyMessageStore.this.enqueueMessage(_connWrapper, queue, message.getMessageNumber()); - } - - @Override - public void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException - { - DerbyMessageStore.this.dequeueMessage(_connWrapper, queue, message.getMessageNumber()); - - } - - @Override - public void commitTran() throws AMQStoreException - { - DerbyMessageStore.this.commitTran(_connWrapper); - storedSizeChange(_storeSizeIncrease); - } - - @Override - public StoreFuture commitTranAsync() throws AMQStoreException - { - final StoreFuture storeFuture = DerbyMessageStore.this.commitTranAsync(_connWrapper); - storedSizeChange(_storeSizeIncrease); - return storeFuture; - } - - @Override - public void abortTran() throws AMQStoreException - { - DerbyMessageStore.this.abortTran(_connWrapper); - } - - @Override - public void removeXid(long format, byte[] globalId, byte[] branchId) throws AMQStoreException - { - DerbyMessageStore.this.removeXid(_connWrapper, format, globalId, branchId); - } - - @Override - public void recordXid(long format, byte[] globalId, byte[] branchId, Record[] enqueues, Record[] dequeues) - throws AMQStoreException - { - DerbyMessageStore.this.recordXid(_connWrapper, format, globalId, branchId, enqueues, dequeues); - } - } - - - - private class StoredDerbyMessage implements StoredMessage - { - - private final long _messageId; - private final boolean _isRecovered; - - private StorableMessageMetaData _metaData; - private volatile SoftReference<StorableMessageMetaData> _metaDataRef; - private byte[] _data; - private volatile SoftReference<byte[]> _dataRef; - - - StoredDerbyMessage(long messageId, StorableMessageMetaData metaData) - { - this(messageId, metaData, false); - } - - - StoredDerbyMessage(long messageId, - StorableMessageMetaData metaData, boolean isRecovered) - { - _messageId = messageId; - _isRecovered = isRecovered; - - if(!_isRecovered) - { - _metaData = metaData; - } - _metaDataRef = new SoftReference<StorableMessageMetaData>(metaData); - } - - @Override - public StorableMessageMetaData getMetaData() - { - StorableMessageMetaData metaData = _metaData == null ? _metaDataRef.get() : _metaData; - if(metaData == null) - { - try - { - metaData = DerbyMessageStore.this.getMetaData(_messageId); - } - catch (SQLException e) - { - throw new RuntimeException(e); - } - _metaDataRef = new SoftReference<StorableMessageMetaData>(metaData); - } - - return metaData; - } - - @Override - public long getMessageNumber() - { - return _messageId; - } - - @Override - public void addContent(int offsetInMessage, java.nio.ByteBuffer src) - { - src = src.slice(); - - if(_data == null) - { - _data = new byte[src.remaining()]; - _dataRef = new SoftReference<byte[]>(_data); - src.duplicate().get(_data); - } - else - { - byte[] oldData = _data; - _data = new byte[oldData.length + src.remaining()]; - _dataRef = new SoftReference<byte[]>(_data); - - System.arraycopy(oldData,0,_data,0,oldData.length); - src.duplicate().get(_data, oldData.length, src.remaining()); - } - - } - - @Override - public int getContent(int offsetInMessage, java.nio.ByteBuffer dst) - { - byte[] data = _dataRef == null ? null : _dataRef.get(); - if(data != null) - { - int length = Math.min(dst.remaining(), data.length - offsetInMessage); - dst.put(data, offsetInMessage, length); - return length; - } - else - { - return DerbyMessageStore.this.getContent(_messageId, offsetInMessage, dst); - } - } - - - @Override - public ByteBuffer getContent(int offsetInMessage, int size) - { - ByteBuffer buf = ByteBuffer.allocate(size); - int length = getContent(offsetInMessage, buf); - buf.position(0); - buf.limit(length); - return buf; - } - - @Override - public synchronized StoreFuture flushToStore() - { - Connection conn = null; - try - { - if(!stored()) - { - conn = newConnection(); - - store(conn); - - conn.commit(); - storedSizeChange(getMetaData().getContentSize()); - } - } - catch (SQLException e) - { - if(_logger.isDebugEnabled()) - { - _logger.debug("Error when trying to flush message " + _messageId + " to store: " + e); - } - throw new RuntimeException(e); - } - finally - { - closeConnection(conn); - } - return StoreFuture.IMMEDIATE_FUTURE; - } - - @Override - public void remove() - { - int delta = getMetaData().getContentSize(); - DerbyMessageStore.this.removeMessage(_messageId); - storedSizeChange(-delta); - } - - private synchronized void store(final Connection conn) throws SQLException - { - if (!stored()) - { - try - { - storeMetaData(conn, _messageId, _metaData); - DerbyMessageStore.this.addContent(conn, _messageId, - _data == null ? ByteBuffer.allocate(0) : ByteBuffer.wrap(_data)); - } - finally - { - _metaData = null; - _data = null; - } - - if(_logger.isDebugEnabled()) - { - _logger.debug("Storing message " + _messageId + " to store"); - } - } - } - - private boolean stored() - { - return _metaData == null || _isRecovered; - } - } - - private void closeConnection(final Connection conn) - { - if(conn != null) - { - try - { - conn.close(); - } - catch (SQLException e) - { - _logger.error("Problem closing connection", e); - } - } - } - - private void closePreparedStatement(final PreparedStatement stmt) - { - if (stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - _logger.error("Problem closing prepared statement", e); - } - } - } - - @Override - public void addEventListener(EventListener eventListener, Event... events) - { - _eventManager.addEventListener(eventListener, events); } @Override @@ -1892,258 +241,7 @@ public class DerbyMessageStore implements MessageStore return _storeLocation; } - private void insertConfiguredObject(ConfiguredObjectRecord configuredObject) throws AMQStoreException - { - if (_stateManager.isInState(State.ACTIVE)) - { - try - { - Connection conn = newAutoCommitConnection(); - try - { - PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); - try - { - stmt.setString(1, configuredObject.getId().toString()); - ResultSet rs = stmt.executeQuery(); - try - { - // If we don't have any data in the result set then we can add this configured object - if (!rs.next()) - { - PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_CONFIGURED_OBJECTS); - try - { - insertStmt.setString(1, configuredObject.getId().toString()); - insertStmt.setString(2, configuredObject.getType()); - if(configuredObject.getAttributes() == null) - { - insertStmt.setNull(3, Types.BLOB); - } - else - { - byte[] attributesAsBytes = configuredObject.getAttributes().getBytes(UTF8_CHARSET); - ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); - insertStmt.setBinaryStream(3, bis, attributesAsBytes.length); - } - insertStmt.execute(); - } - finally - { - insertStmt.close(); - } - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error inserting of configured object " + configuredObject + " into database: " + e.getMessage(), e); - } - } - } - - private int removeConfiguredObject(UUID id) throws AMQStoreException - { - int results = 0; - try - { - Connection conn = newAutoCommitConnection(); - try - { - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_CONFIGURED_OBJECTS); - try - { - stmt.setString(1, id.toString()); - results = stmt.executeUpdate(); - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error deleting of configured object with id " + id + " from database: " + e.getMessage(), e); - } - return results; - } - - private void updateConfiguredObject(final ConfiguredObjectRecord configuredObject) throws AMQStoreException - { - if (_stateManager.isInState(State.ACTIVE)) - { - try - { - Connection conn = newAutoCommitConnection(); - try - { - PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); - try - { - stmt.setString(1, configuredObject.getId().toString()); - ResultSet rs = stmt.executeQuery(); - try - { - if (rs.next()) - { - PreparedStatement stmt2 = conn.prepareStatement(UPDATE_CONFIGURED_OBJECTS); - try - { - stmt2.setString(1, configuredObject.getType()); - if (configuredObject.getAttributes() != null) - { - byte[] attributesAsBytes = configuredObject.getAttributes().getBytes(UTF8_CHARSET); - ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); - stmt2.setBinaryStream(2, bis, attributesAsBytes.length); - } - else - { - stmt2.setNull(2, Types.BLOB); - } - stmt2.setString(3, configuredObject.getId().toString()); - stmt2.execute(); - } - finally - { - stmt2.close(); - } - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error updating configured object " + configuredObject + " in database: " + e.getMessage(), e); - } - } - } - - private ConfiguredObjectRecord loadConfiguredObject(final UUID id) throws AMQStoreException - { - ConfiguredObjectRecord result = null; - try - { - Connection conn = newAutoCommitConnection(); - try - { - PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); - try - { - stmt.setString(1, id.toString()); - ResultSet rs = stmt.executeQuery(); - try - { - if (rs.next()) - { - String type = rs.getString(1); - Blob blob = rs.getBlob(2); - String attributes = null; - if (blob != null) - { - attributes = blobToString(blob); - } - result = new ConfiguredObjectRecord(id, type, attributes); - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error loading of configured object with id " + id + " from database: " - + e.getMessage(), e); - } - return result; - } - - private String blobToString(Blob blob) throws SQLException - { - byte[] bytes = blob.getBytes(1, (int)blob.length()); - return new String(bytes, UTF8_CHARSET); - } - - private List<ConfiguredObjectRecord> loadConfiguredObjects() throws SQLException - { - ArrayList<ConfiguredObjectRecord> results = new ArrayList<ConfiguredObjectRecord>(); - Connection conn = newAutoCommitConnection(); - try - { - PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_CONFIGURED_OBJECTS); - try - { - ResultSet rs = stmt.executeQuery(); - try - { - while (rs.next()) - { - String id = rs.getString(1); - String objectType = rs.getString(2); - String attributes = blobToString(rs.getBlob(3)); - results.add(new ConfiguredObjectRecord(UUID.fromString(id), objectType, attributes)); - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - return results; - } - - private synchronized void storedSizeChange(final int delta) + protected synchronized void storedSizeChange(final int delta) { if(getPersistentSizeHighThreshold() > 0) { @@ -2193,7 +291,7 @@ public class DerbyMessageStore implements MessageStore catch (SQLException e) { closeConnection(conn); - throw new RuntimeException("Exception will processing store size change", e); + throw new RuntimeException("Exception while processing store size change", e); } } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/jdbc/JDBCMessageStore.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/jdbc/JDBCMessageStore.java new file mode 100644 index 0000000000..2c4b0e8119 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/jdbc/JDBCMessageStore.java @@ -0,0 +1,396 @@ +/* +* +* 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.store.jdbc; + + +import java.sql.Blob; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import org.apache.commons.configuration.Configuration; +import org.apache.log4j.Logger; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.server.store.AbstractJDBCMessageStore; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MessageStoreConstants; +import org.apache.qpid.server.store.StoreFuture; +import org.apache.qpid.server.store.Transaction; + +/** + * An implementation of a {@link org.apache.qpid.server.store.MessageStore} that uses a JDBC database as the persistence + * mechanism. + * + */ +public class JDBCMessageStore extends AbstractJDBCMessageStore implements MessageStore +{ + + private static final Logger _logger = Logger.getLogger(JDBCMessageStore.class); + + + public static final String TYPE = "JDBC"; + + + private static class JDBCDetails + { + private final String _vendor; + private String _blobType; + private String _varBinaryType; + private String _bigintType; + private boolean _useBytesMethodsForBlob; + + private JDBCDetails(String vendor, + String blobType, + String varBinaryType, + String bigIntType, + boolean useBytesMethodsForBlob) + { + _vendor = vendor; + setBlobType(blobType); + setVarBinaryType(varBinaryType); + setBigintType(bigIntType); + setUseBytesMethodsForBlob(useBytesMethodsForBlob); + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + + JDBCDetails that = (JDBCDetails) o; + + if (!getVendor().equals(that.getVendor())) + { + return false; + } + + return true; + } + + @Override + public int hashCode() + { + return getVendor().hashCode(); + } + + @Override + public String toString() + { + return "JDBCDetails{" + + "vendor='" + getVendor() + '\'' + + ", blobType='" + getBlobType() + '\'' + + ", varBinaryType='" + getVarBinaryType() + '\'' + + ", bigIntType='" + getBigintType() + '\'' + + ", useBytesMethodsForBlob=" + isUseBytesMethodsForBlob() + + '}'; + } + + public String getVendor() + { + return _vendor; + } + + public String getBlobType() + { + return _blobType; + } + + public void setBlobType(String blobType) + { + _blobType = blobType; + } + + public String getVarBinaryType() + { + return _varBinaryType; + } + + public void setVarBinaryType(String varBinaryType) + { + _varBinaryType = varBinaryType; + } + + public boolean isUseBytesMethodsForBlob() + { + return _useBytesMethodsForBlob; + } + + public void setUseBytesMethodsForBlob(boolean useBytesMethodsForBlob) + { + _useBytesMethodsForBlob = useBytesMethodsForBlob; + } + + public String getBigintType() + { + return _bigintType; + } + + public void setBigintType(String bigintType) + { + _bigintType = bigintType; + } + } + + private static JDBCDetails DERBY_DETAILS = + new JDBCDetails("derby", + "blob", + "varchar(%d) for bit data", + "bigint", + false); + + private static JDBCDetails POSTGRESQL_DETAILS = + new JDBCDetails("postgresql", + "bytea", + "bytea", + "bigint", + true); + + private static JDBCDetails MYSQL_DETAILS = + new JDBCDetails("mysql", + "blob", + "varbinary(%d)", + "bigint", + false); + + + private static JDBCDetails SYBASE_DETAILS = + new JDBCDetails("sybase", + "image", + "varbinary(%d)", + "bigint", + false); + + + private static JDBCDetails ORACLE_DETAILS = + new JDBCDetails("oracle", + "blob", + "raw(%d)", + "number", + false); + + + private static Map<String, JDBCDetails> VENDOR_DETAILS = new HashMap<String,JDBCDetails>(); + + static + { + + addDetails(DERBY_DETAILS); + addDetails(POSTGRESQL_DETAILS); + addDetails(MYSQL_DETAILS); + addDetails(SYBASE_DETAILS); + addDetails(ORACLE_DETAILS); + } + + private static void addDetails(JDBCDetails details) + { + VENDOR_DETAILS.put(details.getVendor(), details); + } + + private String _blobType; + private String _varBinaryType; + private String _bigIntType; + private boolean _useBytesMethodsForBlob; + + private List<RecordedJDBCTransaction> _transactions = new CopyOnWriteArrayList<RecordedJDBCTransaction>(); + + + public JDBCMessageStore() + { + } + + protected Logger getLogger() + { + return _logger; + } + + protected String getSqlBlobType() + { + return _blobType; + } + + protected String getSqlVarBinaryType(int size) + { + return String.format(_varBinaryType, size); + } + + public String getSqlBigIntType() + { + return _bigIntType; + } + + @Override + protected void doClose() throws AMQStoreException + { + while(!_transactions.isEmpty()) + { + RecordedJDBCTransaction txn = _transactions.get(0); + txn.abortTran(); + } + } + + protected void implementationSpecificConfiguration(String name, Configuration storeConfiguration) + throws ClassNotFoundException + { + + _connectionURL = storeConfiguration.getString("connectionUrl", + storeConfiguration.getString(MessageStoreConstants.ENVIRONMENT_PATH_PROPERTY)); + + JDBCDetails details = null; + + String[] components = _connectionURL.split(":",3); + if(components.length >= 2) + { + String vendor = components[1]; + details = VENDOR_DETAILS.get(vendor); + } + + if(details == null) + { + getLogger().info("Do not recognize vendor from connection URL: " + _connectionURL); + + // TODO - is there a better default than derby + details = DERBY_DETAILS; + } + + _blobType = storeConfiguration.getString("sqlBlobType",details.getBlobType()); + _varBinaryType = storeConfiguration.getString("sqlVarbinaryType",details.getVarBinaryType()); + _useBytesMethodsForBlob = storeConfiguration.getBoolean("useBytesForBlob",details.isUseBytesMethodsForBlob()); + _bigIntType = storeConfiguration.getString("sqlBigIntType", details.getBigintType()); + } + + protected void storedSizeChange(int contentSize) + { + } + + @Override + public String getStoreLocation() + { + return ""; + } + + @Override + public String getStoreType() + { + return TYPE; + } + + @Override + protected byte[] getBlobAsBytes(ResultSet rs, int col) throws SQLException + { + if(_useBytesMethodsForBlob) + { + return rs.getBytes(col); + } + else + { + Blob dataAsBlob = rs.getBlob(col); + return dataAsBlob.getBytes(1,(int) dataAsBlob.length()); + + } + } + + @Override + protected String getBlobAsString(ResultSet rs, int col) throws SQLException + { + byte[] bytes; + if(_useBytesMethodsForBlob) + { + bytes = rs.getBytes(col); + return new String(bytes,UTF8_CHARSET); + } + else + { + Blob blob = rs.getBlob(col); + if(blob == null) + { + return null; + } + bytes = blob.getBytes(1, (int)blob.length()); + } + return new String(bytes, UTF8_CHARSET); + + } + + @Override + public Transaction newTransaction() + { + return new RecordedJDBCTransaction(); + } + + private class RecordedJDBCTransaction extends JDBCTransaction + { + private RecordedJDBCTransaction() + { + super(); + JDBCMessageStore.this._transactions.add(this); + } + + @Override + public void commitTran() throws AMQStoreException + { + try + { + super.commitTran(); + } + finally + { + JDBCMessageStore.this._transactions.remove(this); + } + } + + @Override + public StoreFuture commitTranAsync() throws AMQStoreException + { + try + { + return super.commitTranAsync(); + } + finally + { + JDBCMessageStore.this._transactions.remove(this); + } + } + + @Override + public void abortTran() throws AMQStoreException + { + try + { + super.abortTran(); + } + finally + { + JDBCMessageStore.this._transactions.remove(this); + } + } + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/store/jdbc/JDBCMessageStoreFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/jdbc/JDBCMessageStoreFactory.java new file mode 100644 index 0000000000..1446ad34e9 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/store/jdbc/JDBCMessageStoreFactory.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.store.jdbc; + +import org.apache.qpid.server.plugin.MessageStoreFactory; +import org.apache.qpid.server.store.MessageStore; + +public class JDBCMessageStoreFactory implements MessageStoreFactory +{ + + @Override + public String getType() + { + return JDBCMessageStore.TYPE; + } + + @Override + public MessageStore createMessageStore() + { + return new JDBCMessageStore(); + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/util/MapValueConverter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/MapValueConverter.java index 8c57d04348..16e717a9c7 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/util/MapValueConverter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/MapValueConverter.java @@ -62,7 +62,7 @@ public class MapValueConverter return getStringAttribute(name, attributes, null); } - private static void assertMandatoryAttribute(String name, Map<String, Object> attributes) + public static void assertMandatoryAttribute(String name, Map<String, Object> attributes) { if (!attributes.containsKey(name)) { @@ -326,6 +326,10 @@ public class MapValueConverter public static <T> Set<T> toSet(Object rawValue, Class<T> setItemClass, String attributeName) { + if (rawValue == null) + { + return null; + } HashSet<T> set = new HashSet<T>(); if (rawValue instanceof Iterable) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/util/StringUtil.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/StringUtil.java new file mode 100644 index 0000000000..aa17a9493b --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/StringUtil.java @@ -0,0 +1,44 @@ +/* + * + * 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.util; + +import java.util.Random; + +public class StringUtil +{ + private static final String NUMBERS = "0123456789"; + private static final String LETTERS = "abcdefghijklmnopqrstuvwxwy"; + private static final String OTHERS = "_-"; + private static final char[] CHARACTERS = (NUMBERS + LETTERS + LETTERS.toUpperCase() + OTHERS).toCharArray(); + + private Random _random = new Random(); + + public String randomAlphaNumericString(int maxLength) + { + char[] result = new char[maxLength]; + for (int i = 0; i < maxLength; i++) + { + result[i] = (char) CHARACTERS[_random.nextInt(CHARACTERS.length)]; + } + return new String(result); + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java index 657cf4b762..af5b8b1270 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java @@ -131,7 +131,7 @@ public class VirtualHostImpl implements VirtualHost, IConnectionRegistry.Registr CurrentActor.get().message(VirtualHostMessages.CREATED(_name)); - _securityManager = new SecurityManager(parentSecurityManager, _vhostConfig.getConfig().getString("security.acl")); + _securityManager = new SecurityManager(parentSecurityManager, _vhostConfig.getConfig().getString("security.acl"), _name); _connectionRegistry = new ConnectionRegistry(); _connectionRegistry.addRegistryChangeListener(this); diff --git a/qpid/java/broker/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.MessageStoreFactory b/qpid/java/broker/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.MessageStoreFactory index 1357f816b7..0edd44f5a5 100644 --- a/qpid/java/broker/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.MessageStoreFactory +++ b/qpid/java/broker/src/main/resources/META-INF/services/org.apache.qpid.server.plugin.MessageStoreFactory @@ -17,4 +17,5 @@ # under the License. # org.apache.qpid.server.store.derby.DerbyMessageStoreFactory -org.apache.qpid.server.store.MemoryMessageStoreFactory
\ No newline at end of file +org.apache.qpid.server.store.MemoryMessageStoreFactory +org.apache.qpid.server.store.jdbc.JDBCMessageStoreFactory diff --git a/qpid/java/broker/src/main/resources/initial-store.json b/qpid/java/broker/src/main/resources/initial-config.json index 7e73772d6d..f8b724d351 100644 --- a/qpid/java/broker/src/main/resources/initial-store.json +++ b/qpid/java/broker/src/main/resources/initial-config.json @@ -19,9 +19,8 @@ * */ { - "name": "QpidBroker", + "name": "Broker", "storeVersion": 1, - "defaultAuthenticationProvider" : "passwordFile", "defaultVirtualHost" : "default", "authenticationproviders" : [ { "name" : "passwordFile", @@ -29,25 +28,28 @@ "path" : "${QPID_HOME}/etc/passwd" } ], "ports" : [ { - "name" : "5672-AMQP", - "port" : 5672 + "name" : "AMQP", + "port" : 5672, + "authenticationProvider" : "passwordFile" }, { - "name" : "8080-HTTP", + "name" : "HTTP", "port" : 8080, + "authenticationProvider" : "passwordFile", "protocols" : [ "HTTP" ] }, { - "name" : "8999-RMI", + "name" : "RMI_REGISTRY", "port" : 8999, "protocols" : [ "RMI" ] }, { - "name" : "9099-JMX_RMI", + "name" : "JMX_CONNECTOR", "port" : 9099, + "authenticationProvider" : "passwordFile", "protocols" : [ "JMX_RMI" ] }], "virtualhosts" : [ { "name" : "default", "storeType" : "DERBY", - "storePath" : "${QPID_WORK}/store" + "storePath" : "${QPID_WORK}/derbystore/default" } ], "plugins" : [ { "pluginType" : "MANAGEMENT-HTTP", diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/BrokerOptionsTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/BrokerOptionsTest.java index 51fe2d88d3..08031c36a4 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/BrokerOptionsTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/BrokerOptionsTest.java @@ -28,8 +28,9 @@ public class BrokerOptionsTest extends QpidTestCase { private BrokerOptions _options; - protected void setUp() + protected void setUp() throws Exception { + super.setUp(); _options = new BrokerOptions(); } @@ -129,6 +130,17 @@ public class BrokerOptionsTest extends QpidTestCase assertEquals(true, _options.isManagementMode()); } + public void testDefaultManagementModeQuiesceVirtualHosts() + { + assertEquals(false, _options.isManagementModeQuiesceVirtualHosts()); + } + + public void testOverriddenDefaultManagementModeQuiesceVirtualHosts() + { + _options.setManagementModeQuiesceVirtualHosts(true); + assertEquals(true, _options.isManagementModeQuiesceVirtualHosts()); + } + public void testDefaultManagementModeRmiPort() { assertEquals(0, _options.getManagementModeRmiPort()); @@ -197,4 +209,21 @@ public class BrokerOptionsTest extends QpidTestCase _options.setSkipLoggingConfiguration(true); assertTrue(_options.isSkipLoggingConfiguration()); } + + public void testDefaultOverwriteConfigurationStore() + { + assertFalse(_options.isOverwriteConfigurationStore()); + } + + public void testOverriddenOverwriteConfigurationStore() + { + _options.setOverwriteConfigurationStore(true); + assertTrue(_options.isOverwriteConfigurationStore()); + } + + public void testManagementModePassword() + { + _options.setManagementModePassword("test"); + assertEquals("Unexpected management mode password", "test", _options.getManagementModePassword()); + } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/MainTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/MainTest.java index 46c63c9e34..d7579e2b2a 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/MainTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/MainTest.java @@ -47,7 +47,7 @@ public class MainTest extends QpidTestCase assertEquals(null, options.getLogConfigFile()); assertEquals(0, options.getLogWatchFrequency()); assertEquals(BrokerOptions.DEFAULT_INITIAL_CONFIG_LOCATION, options.getInitialConfigurationLocation()); - + assertFalse(options.isOverwriteConfigurationStore()); assertFalse(options.isManagementMode()); assertEquals(0, options.getManagementModeConnectorPort()); assertEquals(0, options.getManagementModeRmiPort()); @@ -72,6 +72,15 @@ public class MainTest extends QpidTestCase assertEquals("bdb", options.getConfigurationStoreType()); } + public void testOverwriteConfigurationStore() + { + BrokerOptions options = startDummyMain("-os"); + assertTrue(options.isOverwriteConfigurationStore()); + + options = startDummyMain("-overwrite-store"); + assertTrue(options.isOverwriteConfigurationStore()); + } + public void testLogConfig() { BrokerOptions options = startDummyMain("-l wxyz/log4j.xml"); @@ -122,46 +131,68 @@ public class MainTest extends QpidTestCase public void testManagementModeRmiPort() { - BrokerOptions options = startDummyMain("-mm -rmi 7777"); + BrokerOptions options = startDummyMain("-mm -mmrmi 7777"); assertTrue(options.isManagementMode()); assertEquals(7777, options.getManagementModeRmiPort()); - options = startDummyMain("-mm --jmxregistryport 7777"); + options = startDummyMain("-mm --management-mode-rmi-registry-port 7777"); assertTrue(options.isManagementMode()); assertEquals(7777, options.getManagementModeRmiPort()); - options = startDummyMain("-rmi 7777"); + options = startDummyMain("-mmrmi 7777"); assertEquals(0, options.getManagementModeRmiPort()); } public void testManagementModeConnectorPort() { - BrokerOptions options = startDummyMain("-mm -jmxrmi 8888"); + BrokerOptions options = startDummyMain("-mm -mmjmx 8888"); assertTrue(options.isManagementMode()); assertEquals(8888, options.getManagementModeConnectorPort()); - options = startDummyMain("-mm --jmxconnectorport 8888"); + options = startDummyMain("-mm --management-mode-jmx-connector-port 8888"); assertTrue(options.isManagementMode()); assertEquals(8888, options.getManagementModeConnectorPort()); - options = startDummyMain("-jmxrmi 8888"); + options = startDummyMain("-mmjmx 8888"); assertEquals(0, options.getManagementModeConnectorPort()); } public void testManagementModeHttpPort() { - BrokerOptions options = startDummyMain("-mm -http 9999"); + BrokerOptions options = startDummyMain("-mm -mmhttp 9999"); assertTrue(options.isManagementMode()); assertEquals(9999, options.getManagementModeHttpPort()); - options = startDummyMain("-mm --httpport 9999"); + options = startDummyMain("-mm --management-mode-http-port 9999"); assertTrue(options.isManagementMode()); assertEquals(9999, options.getManagementModeHttpPort()); - options = startDummyMain("-http 9999"); + options = startDummyMain("-mmhttp 9999"); assertEquals(0, options.getManagementModeHttpPort()); } + public void testManagementModePassword() + { + String password = getTestName(); + BrokerOptions options = startDummyMain("-mm -mmpass " + password); + assertTrue(options.isManagementMode()); + assertEquals(password, options.getManagementModePassword()); + + options = startDummyMain("-mm --management-mode-password " + password); + assertTrue(options.isManagementMode()); + assertEquals(password, options.getManagementModePassword()); + + options = startDummyMain("-mmpass " + password); + assertNotNull(options.getManagementModePassword()); + } + + public void testDefaultManagementModePassword() + { + BrokerOptions options = startDummyMain("-mm"); + assertTrue(options.isManagementMode()); + assertNotNull(options.getManagementModePassword()); + } + private BrokerOptions startDummyMain(String commandLine) { return (new TestMain(commandLine.split("\\s"))).getOptions(); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/BrokerConfigurationStoreCreatorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/BrokerConfigurationStoreCreatorTest.java index d61117868f..7555a5632c 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/BrokerConfigurationStoreCreatorTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/BrokerConfigurationStoreCreatorTest.java @@ -73,7 +73,7 @@ public class BrokerConfigurationStoreCreatorTest extends QpidTestCase public void testCreateJsonStore() { - ConfigurationEntryStore store = _storeCreator.createStore(_userStoreLocation.getAbsolutePath(), "json", BrokerOptions.DEFAULT_INITIAL_CONFIG_LOCATION); + ConfigurationEntryStore store = _storeCreator.createStore(_userStoreLocation.getAbsolutePath(), "json", BrokerOptions.DEFAULT_INITIAL_CONFIG_LOCATION, false); assertNotNull("Store was not created", store); assertTrue("File should exists", _userStoreLocation.exists()); assertTrue("File size should be greater than 0", _userStoreLocation.length() > 0); @@ -84,41 +84,73 @@ public class BrokerConfigurationStoreCreatorTest extends QpidTestCase public void testCreateJsonStoreFromInitialStore() throws Exception { + createJsonStoreFromInitialStoreTestImpl(false); + } + + public void testOverwriteExistingJsonStoreWithInitialConfig() throws Exception + { + createJsonStoreFromInitialStoreTestImpl(true); + } + + public void createJsonStoreFromInitialStoreTestImpl(boolean overwrite) throws Exception + { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true); + String defaultBrokerName = "Broker"; + String testBrokerName = getTestName(); + Map<String, Object> brokerObjectMap = new HashMap<String, Object>(); - UUID brokerId = UUID.randomUUID(); - brokerObjectMap.put(Broker.ID, brokerId); - brokerObjectMap.put("name", "Test"); + UUID testBrokerId = UUID.randomUUID(); + brokerObjectMap.put(Broker.ID, testBrokerId); + brokerObjectMap.put("name", testBrokerName); StringWriter sw = new StringWriter(); objectMapper.writeValue(sw, brokerObjectMap); String brokerJson = sw.toString(); - File _storeFile = TestFileUtils.createTempFile(this, ".json", brokerJson); + File _initialStoreFile = TestFileUtils.createTempFile(this, ".json", brokerJson); - ConfigurationEntryStore store = _storeCreator.createStore(_userStoreLocation.getAbsolutePath(), "json", _storeFile.getAbsolutePath()); + ConfigurationEntryStore store = _storeCreator.createStore(_userStoreLocation.getAbsolutePath(), "json", _initialStoreFile.getAbsolutePath(), false); assertNotNull("Store was not created", store); assertTrue("File should exists", _userStoreLocation.exists()); assertTrue("File size should be greater than 0", _userStoreLocation.length() > 0); JsonConfigurationEntryStore jsonStore = new JsonConfigurationEntryStore(_userStoreLocation.getAbsolutePath(), null); ConfigurationEntry entry = jsonStore.getRootEntry(); - assertEquals("Unexpected root id", brokerId, entry.getId()); + assertEquals("Unexpected root id", testBrokerId, entry.getId()); Map<String, Object> attributes = entry.getAttributes(); assertNotNull("Unexpected attributes: " + attributes, attributes); assertEquals("Unexpected attributes size: " + attributes.size(), 1, attributes.size()); - assertEquals("Unexpected attribute name: " + attributes.get("name"), "Test", attributes.get("name")); + assertEquals("Unexpected attribute name: " + attributes.get("name"), testBrokerName, attributes.get(Broker.NAME)); Set<UUID> childrenIds = entry.getChildrenIds(); assertTrue("Unexpected children: " + childrenIds, childrenIds.isEmpty()); + + if(overwrite) + { + ConfigurationEntryStore overwrittenStore = _storeCreator.createStore(_userStoreLocation.getAbsolutePath(), "json", BrokerOptions.DEFAULT_INITIAL_CONFIG_LOCATION, true); + assertNotNull("Store was not created", overwrittenStore); + assertTrue("File should exists", _userStoreLocation.exists()); + assertTrue("File size should be greater than 0", _userStoreLocation.length() > 0); + + //check the contents reflect the test store content having been overwritten with the default store + JsonConfigurationEntryStore reopenedOverwrittenStore = new JsonConfigurationEntryStore(_userStoreLocation.getAbsolutePath(), null, false); + entry = reopenedOverwrittenStore.getRootEntry(); + assertFalse("Root id did not change, store content was not overwritten", testBrokerId.equals(entry.getId())); + attributes = entry.getAttributes(); + assertNotNull("No attributes found", attributes); + assertFalse("Test name should not equal default broker name", testBrokerName.equals(defaultBrokerName)); + assertEquals("Unexpected broker name value" , defaultBrokerName, attributes.get(Broker.NAME)); + childrenIds = entry.getChildrenIds(); + assertFalse("Expected children were not found" + childrenIds, childrenIds.isEmpty()); + } } public void testCreateStoreWithUnknownType() { try { - _storeCreator.createStore(_userStoreLocation.getAbsolutePath(), "derby", null); + _storeCreator.createStore(_userStoreLocation.getAbsolutePath(), "derby", null, false); fail("Store is not yet supported"); } catch(IllegalConfigurationException e) diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/BrokerRecovererTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/BrokerRecovererTest.java index 3a41b61961..64b432f471 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/BrokerRecovererTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/BrokerRecovererTest.java @@ -20,10 +20,8 @@ */ package org.apache.qpid.server.configuration.startup; -import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.verify; import java.util.Arrays; import java.util.Collection; @@ -35,9 +33,9 @@ import java.util.UUID; import junit.framework.TestCase; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.ConfigurationEntry; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; -import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.configuration.RecovererProvider; import org.apache.qpid.server.logging.LogRecorder; import org.apache.qpid.server.logging.RootMessageLogger; @@ -50,7 +48,9 @@ import org.apache.qpid.server.model.Plugin; import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.TrustStore; import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.model.adapter.AccessControlProviderFactory; import org.apache.qpid.server.model.adapter.AuthenticationProviderFactory; +import org.apache.qpid.server.model.adapter.GroupProviderFactory; import org.apache.qpid.server.model.adapter.PortFactory; import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.stats.StatisticsGatherer; @@ -72,8 +72,8 @@ public class BrokerRecovererTest extends TestCase { super.setUp(); - _brokerRecoverer = new BrokerRecoverer(mock(AuthenticationProviderFactory.class), mock(PortFactory.class), mock(StatisticsGatherer.class), - mock(VirtualHostRegistry.class), mock(LogRecorder.class), mock(RootMessageLogger.class), mock(TaskExecutor.class)); + _brokerRecoverer = new BrokerRecoverer(mock(AuthenticationProviderFactory.class), mock(GroupProviderFactory.class), mock(AccessControlProviderFactory.class), mock(PortFactory.class), + mock(StatisticsGatherer.class), mock(VirtualHostRegistry.class), mock(LogRecorder.class), mock(RootMessageLogger.class), mock(TaskExecutor.class), mock(BrokerOptions.class)); when(_brokerEntry.getId()).thenReturn(_brokerId); when(_brokerEntry.getChildren()).thenReturn(_brokerEntryChildren); @@ -89,7 +89,6 @@ public class BrokerRecovererTest extends TestCase { Map<String, Object> attributes = new HashMap<String, Object>(); attributes.put(Broker.DEFAULT_VIRTUAL_HOST, "test"); - attributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, "authenticationProvider1"); attributes.put(Broker.QUEUE_ALERT_THRESHOLD_MESSAGE_AGE, 9l); attributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 8l); attributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, 7l); @@ -100,7 +99,6 @@ public class BrokerRecovererTest extends TestCase attributes.put(Broker.QUEUE_MAXIMUM_DELIVERY_ATTEMPTS, 2); attributes.put(Broker.QUEUE_DEAD_LETTER_QUEUE_ENABLED, true); attributes.put(Broker.VIRTUALHOST_HOUSEKEEPING_CHECK_PERIOD, 1l); - attributes.put(Broker.ACL_FILE, "/path/to/acl"); attributes.put(Broker.CONNECTION_SESSION_COUNT_LIMIT, 1000); attributes.put(Broker.CONNECTION_HEART_BEAT_DELAY, 2000); attributes.put(Broker.STATISTICS_REPORTING_PERIOD, 4000); @@ -172,24 +170,6 @@ public class BrokerRecovererTest extends TestCase assertEquals(Collections.singletonList(port), broker.getPorts()); } - public void testCreateBrokerWithoutAuthenticationProviderThrowsException() - { - assertNotNull("expected to remove the base entry", _brokerEntryChildren.remove(AuthenticationProvider.class.getSimpleName())); - assertTrue("should be empty", _brokerEntryChildren.isEmpty()); - - RecovererProvider recovererProvider = createRecoveryProvider(new ConfigurationEntry[0], new ConfiguredObject[0]); - - try - { - _brokerRecoverer.create(recovererProvider, _brokerEntry); - fail("should have thrown an exception due to missing authentication provider configuration"); - } - catch(IllegalConfigurationException e) - { - //expected - } - } - public void testCreateBrokerWithOneAuthenticationProvider() { RecovererProvider recovererProvider = createRecoveryProvider(new ConfigurationEntry[]{_authenticationProviderEntry1}, @@ -202,29 +182,6 @@ public class BrokerRecovererTest extends TestCase assertEquals(Collections.singletonList(_authenticationProvider1), broker.getAuthenticationProviders()); } - public void testCreateBrokerWithMultipleAuthenticationProvidersAndNoDefaultThrowsException() - { - AuthenticationProvider authenticationProvider2 = mock(AuthenticationProvider.class); - when(authenticationProvider2.getName()).thenReturn("authenticationProvider2"); - ConfigurationEntry authenticationProviderEntry2 = mock(ConfigurationEntry.class); - _brokerEntryChildren.put(AuthenticationProvider.class.getSimpleName(), Arrays.asList(_authenticationProviderEntry1, authenticationProviderEntry2)); - - Map<String,Object> emptyBrokerAttributes = new HashMap<String,Object>(); - when(_brokerEntry.getAttributes()).thenReturn(emptyBrokerAttributes); - - RecovererProvider recovererProvider = createRecoveryProvider(new ConfigurationEntry[]{authenticationProviderEntry2, _authenticationProviderEntry1}, - new ConfiguredObject[]{authenticationProvider2, _authenticationProvider1}); - try - { - _brokerRecoverer.create(recovererProvider, _brokerEntry); - fail("should have thrown an exception due to missing authentication provider default"); - } - catch(IllegalConfigurationException e) - { - //expected - } - } - public void testCreateBrokerWithMultipleAuthenticationProvidersAndPorts() { //Create a second authentication provider @@ -233,13 +190,10 @@ public class BrokerRecovererTest extends TestCase ConfigurationEntry authenticationProviderEntry2 = mock(ConfigurationEntry.class); _brokerEntryChildren.put(AuthenticationProvider.class.getSimpleName(), Arrays.asList(_authenticationProviderEntry1, authenticationProviderEntry2)); - //Set the default authentication provider Map<String,Object> brokerAtttributes = new HashMap<String,Object>(); when(_brokerEntry.getAttributes()).thenReturn(brokerAtttributes); - brokerAtttributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, "authenticationProvider2"); - //Add a couple ports, one with a defined authentication provider and - //one without (which should then use the default) + //Add a couple ports ConfigurationEntry portEntry1 = mock(ConfigurationEntry.class); Port port1 = mock(Port.class); when(port1.getName()).thenReturn("port1"); @@ -249,6 +203,7 @@ public class BrokerRecovererTest extends TestCase Port port2 = mock(Port.class); when(port2.getName()).thenReturn("port2"); when(port2.getPort()).thenReturn(5672); + when(port2.getAttribute(Port.AUTHENTICATION_PROVIDER)).thenReturn("authenticationProvider2"); _brokerEntryChildren.put(Port.class.getSimpleName(), Arrays.asList(portEntry1, portEntry2)); RecovererProvider recovererProvider = createRecoveryProvider( @@ -258,47 +213,12 @@ public class BrokerRecovererTest extends TestCase Broker broker = _brokerRecoverer.create(recovererProvider, _brokerEntry); assertNotNull(broker); - assertEquals("Unexpected number of authentication providers", 2,broker.getAuthenticationProviders().size()); + assertEquals("Unexpected number of authentication providers", 2, broker.getAuthenticationProviders().size()); Collection<Port> ports = broker.getPorts(); assertEquals("Unexpected number of ports", 2, ports.size()); assertTrue(ports.contains(port1)); assertTrue(ports.contains(port2)); - - verify(port1).setAuthenticationProvider(any(AuthenticationProvider.class)); - verify(port1).setAuthenticationProvider(_authenticationProvider1); - - verify(port2).setAuthenticationProvider(any(AuthenticationProvider.class)); - verify(port2).setAuthenticationProvider(authenticationProvider2); - } - - public void testCreateBrokerAssignsGroupAccessorToAuthenticationProviders() - { - //Create a second authentication provider - AuthenticationProvider authenticationProvider2 = mock(AuthenticationProvider.class); - when(authenticationProvider2.getName()).thenReturn("authenticationProvider2"); - ConfigurationEntry authenticationProviderEntry2 = mock(ConfigurationEntry.class); - _brokerEntryChildren.put(AuthenticationProvider.class.getSimpleName(), Arrays.asList(_authenticationProviderEntry1, authenticationProviderEntry2)); - - //Set the default authentication provider - Map<String,Object> brokerAtttributes = new HashMap<String,Object>(); - when(_brokerEntry.getAttributes()).thenReturn(brokerAtttributes); - brokerAtttributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, "authenticationProvider2"); - - //Create a group provider - ConfigurationEntry groupProviderEntry = mock(ConfigurationEntry.class); - GroupProvider groupProvider = mock(GroupProvider.class); - _brokerEntryChildren.put(GroupProvider.class.getSimpleName(), Arrays.asList(groupProviderEntry)); - - RecovererProvider recovererProvider = createRecoveryProvider( - new ConfigurationEntry[]{groupProviderEntry, authenticationProviderEntry2, _authenticationProviderEntry1}, - new ConfiguredObject[]{groupProvider, authenticationProvider2, _authenticationProvider1}); - - Broker broker = _brokerRecoverer.create(recovererProvider, _brokerEntry); - - assertNotNull(broker); - assertEquals("Unexpected number of authentication providers", 2, broker.getAuthenticationProviders().size()); - } public void testCreateBrokerWithGroupProvider() diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProviderTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProviderTest.java index c95f67beb9..b958ba1f56 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProviderTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProviderTest.java @@ -23,14 +23,17 @@ package org.apache.qpid.server.configuration.startup; import static org.mockito.Mockito.mock; import junit.framework.TestCase; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; import org.apache.qpid.server.logging.LogRecorder; import org.apache.qpid.server.logging.RootMessageLogger; import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; 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.TrustStore; import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.stats.StatisticsGatherer; @@ -42,7 +45,8 @@ public class DefaultRecovererProviderTest extends TestCase { String[] supportedTypes = {Broker.class.getSimpleName(), VirtualHost.class.getSimpleName(), AuthenticationProvider.class.getSimpleName(), - GroupProvider.class.getSimpleName(), Plugin.class.getSimpleName(), Port.class.getSimpleName()}; + GroupProvider.class.getSimpleName(), Plugin.class.getSimpleName(), Port.class.getSimpleName(), + KeyStore.class.getSimpleName(), TrustStore.class.getSimpleName()}; // mocking the required object StatisticsGatherer statisticsGatherer = mock(StatisticsGatherer.class); @@ -51,7 +55,7 @@ public class DefaultRecovererProviderTest extends TestCase RootMessageLogger rootMessageLogger = mock(RootMessageLogger.class); TaskExecutor taskExecutor = mock(TaskExecutor.class); - DefaultRecovererProvider provider = new DefaultRecovererProvider(statisticsGatherer, virtualHostRegistry, logRecorder, rootMessageLogger, taskExecutor); + DefaultRecovererProvider provider = new DefaultRecovererProvider(statisticsGatherer, virtualHostRegistry, logRecorder, rootMessageLogger, taskExecutor, mock(BrokerOptions.class)); for (String configuredObjectType : supportedTypes) { ConfiguredObjectRecoverer<?> recovever = provider.getRecoverer(configuredObjectType); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/GroupProviderRecovererTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/GroupProviderRecovererTest.java index 6713574e4b..d6f03a9758 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/GroupProviderRecovererTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/GroupProviderRecovererTest.java @@ -30,6 +30,7 @@ import org.apache.qpid.server.configuration.ConfigurationEntry; import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.GroupProvider; +import org.apache.qpid.server.model.adapter.GroupProviderFactory; import org.apache.qpid.server.plugin.GroupManagerFactory; import org.apache.qpid.server.plugin.QpidServiceLoader; import org.apache.qpid.server.security.group.GroupManager; @@ -46,6 +47,7 @@ public class GroupProviderRecovererTest extends TestCase private QpidServiceLoader<GroupManagerFactory> _groupManagerServiceLoader; private Broker _broker; private ConfigurationEntry _configurationEntry; + private GroupProviderFactory _groupProviderFactory; @SuppressWarnings("unchecked") protected void setUp() throws Exception @@ -58,6 +60,7 @@ public class GroupProviderRecovererTest extends TestCase _groupManagerServiceLoader = mock(QpidServiceLoader.class); when(_groupManagerServiceLoader.instancesOf(GroupManagerFactory.class)).thenReturn(Collections.singletonList(_factory )); + _groupProviderFactory = new GroupProviderFactory(_groupManagerServiceLoader); _broker = mock(Broker.class); @@ -70,8 +73,9 @@ public class GroupProviderRecovererTest extends TestCase { GroupManager groupManager = mock(GroupManager.class); String name = groupManager.getClass().getSimpleName(); + _attributes.put(GroupProvider.NAME, name); when(_factory.createInstance(_attributes)).thenReturn(groupManager); - GroupProviderRecoverer groupProviderRecoverer = new GroupProviderRecoverer(_groupManagerServiceLoader); + GroupProviderRecoverer groupProviderRecoverer = new GroupProviderRecoverer(_groupProviderFactory); GroupProvider groupProvider = groupProviderRecoverer.create(null, _configurationEntry, _broker); assertNotNull("Null group provider", groupProvider); assertEquals("Unexpected name", name, groupProvider.getName()); @@ -82,7 +86,7 @@ public class GroupProviderRecovererTest extends TestCase { when(_factory.createInstance(_attributes)).thenReturn(null); - GroupProviderRecoverer groupProviderRecoverer = new GroupProviderRecoverer(_groupManagerServiceLoader); + GroupProviderRecoverer groupProviderRecoverer = new GroupProviderRecoverer(_groupProviderFactory); try { groupProviderRecoverer.create(null, _configurationEntry, _broker); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/KeyStoreRecovererTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/KeyStoreRecovererTest.java index 0d7dc1bb06..e0a736df89 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/KeyStoreRecovererTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/KeyStoreRecovererTest.java @@ -27,12 +27,15 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; +import javax.net.ssl.KeyManagerFactory; + +import junit.framework.TestCase; + import org.apache.qpid.server.configuration.ConfigurationEntry; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.KeyStore; -import org.apache.qpid.server.model.TrustStore; - -import junit.framework.TestCase; +import org.apache.qpid.server.model.adapter.AbstractKeyStoreAdapter; +import org.apache.qpid.test.utils.TestSSLConstants; public class KeyStoreRecovererTest extends TestCase { @@ -40,6 +43,7 @@ public class KeyStoreRecovererTest extends TestCase public void testCreateWithAllAttributesProvided() { Map<String, Object> attributes = getKeyStoreAttributes(); + Map<String, Object> attributesCopy = new HashMap<String, Object>(attributes); UUID id = UUID.randomUUID(); Broker broker = mock(Broker.class); @@ -49,36 +53,27 @@ public class KeyStoreRecovererTest extends TestCase KeyStoreRecoverer recovever = new KeyStoreRecoverer(); - KeyStore KeyStore = recovever.create(null, entry, broker); - assertNotNull("Key store configured object is not created", KeyStore); - assertEquals(id, KeyStore.getId()); - assertEquals("my-secret-password", KeyStore.getPassword()); + KeyStore keyStore = recovever.create(null, entry, broker); + assertNotNull("Key store configured object is not created", keyStore); + assertEquals(id, keyStore.getId()); - assertNull("Password was unexpectedly returned from configured object", KeyStore.getAttribute(TrustStore.PASSWORD)); + //verify we can retrieve the actual password using the method + assertEquals(TestSSLConstants.BROKER_TRUSTSTORE_PASSWORD, keyStore.getPassword()); + assertNotNull(keyStore.getPassword()); - // password attribute should not be exposed by a key store configured object - // so, we should set password value to null in the map being used to create the key store configured object - attributes.put(KeyStore.PASSWORD, null); - for (Map.Entry<String, Object> attribute : attributes.entrySet()) + //verify that we havent configured the key store with the actual dummy password value + assertFalse(AbstractKeyStoreAdapter.DUMMY_PASSWORD_MASK.equals(keyStore.getPassword())); + + // Verify the remaining attributes, including that the password value returned + // via getAttribute is actually the dummy value and not the real password + attributesCopy.put(KeyStore.PASSWORD, AbstractKeyStoreAdapter.DUMMY_PASSWORD_MASK); + for (Map.Entry<String, Object> attribute : attributesCopy.entrySet()) { - Object attributeValue = KeyStore.getAttribute(attribute.getKey()); + Object attributeValue = keyStore.getAttribute(attribute.getKey()); assertEquals("Unexpected value of attribute '" + attribute.getKey() + "'", attribute.getValue(), attributeValue); } } - private Map<String, Object> getKeyStoreAttributes() - { - Map<String, Object> attributes = new HashMap<String, Object>(); - attributes.put(KeyStore.NAME, getName()); - attributes.put(KeyStore.PATH, "/path/to/KeyStore"); - attributes.put(KeyStore.PASSWORD, "my-secret-password"); - attributes.put(KeyStore.TYPE, "NON-JKS"); - attributes.put(KeyStore.KEY_MANAGER_FACTORY_ALGORITHM, "NON-STANDARD"); - attributes.put(KeyStore.CERTIFICATE_ALIAS, "my-cert-alias"); - attributes.put(KeyStore.DESCRIPTION, "description"); - return attributes; - } - public void testCreateWithMissedRequiredAttributes() { Map<String, Object> attributes = getKeyStoreAttributes(); @@ -108,4 +103,16 @@ public class KeyStoreRecovererTest extends TestCase } } + private Map<String, Object> getKeyStoreAttributes() + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(KeyStore.NAME, getName()); + attributes.put(KeyStore.PATH, TestSSLConstants.BROKER_KEYSTORE); + attributes.put(KeyStore.PASSWORD, TestSSLConstants.BROKER_KEYSTORE_PASSWORD); + attributes.put(KeyStore.TYPE, "jks"); + attributes.put(KeyStore.KEY_MANAGER_FACTORY_ALGORITHM, KeyManagerFactory.getDefaultAlgorithm()); + attributes.put(KeyStore.CERTIFICATE_ALIAS, "java-broker"); + return attributes; + } + } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/TrustStoreRecovererTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/TrustStoreRecovererTest.java index 5e7784bc06..4d92f99306 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/TrustStoreRecovererTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/TrustStoreRecovererTest.java @@ -27,16 +27,21 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; +import javax.net.ssl.TrustManagerFactory; + import org.apache.qpid.server.configuration.ConfigurationEntry; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.TrustStore; +import org.apache.qpid.server.model.adapter.AbstractKeyStoreAdapter; import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.test.utils.TestSSLConstants; public class TrustStoreRecovererTest extends QpidTestCase { public void testCreateWithAllAttributesProvided() { Map<String, Object> attributes = getTrustStoreAttributes(); + Map<String, Object> attributesCopy = new HashMap<String, Object>(attributes); UUID id = UUID.randomUUID(); Broker broker = mock(Broker.class); @@ -44,38 +49,29 @@ public class TrustStoreRecovererTest extends QpidTestCase when(entry.getAttributes()).thenReturn(attributes); when(entry.getId()).thenReturn(id); - TrustStoreRecoverer recovever = new TrustStoreRecoverer(); + TrustStoreRecoverer recoverer = new TrustStoreRecoverer(); - TrustStore trustStore = recovever.create(null, entry, broker); + TrustStore trustStore = recoverer.create(null, entry, broker); assertNotNull("Trust store configured object is not created", trustStore); assertEquals(id, trustStore.getId()); - assertEquals("my-secret-password", trustStore.getPassword()); - assertNull("Password was unexpectedly returned from configured object", trustStore.getAttribute(TrustStore.PASSWORD)); + //verify we can retrieve the actual password using the method + assertEquals(TestSSLConstants.BROKER_TRUSTSTORE_PASSWORD, trustStore.getPassword()); + assertNotNull(trustStore.getPassword()); + + //verify that we havent configured the trust store with the actual dummy password value + assertFalse(AbstractKeyStoreAdapter.DUMMY_PASSWORD_MASK.equals(trustStore.getPassword())); - // password attribute should not be exposed by a trust store configured object - // so, we should set password value to null in the map being used to create the trust store configured object - attributes.put(TrustStore.PASSWORD, null); - for (Map.Entry<String, Object> attribute : attributes.entrySet()) + // Verify the remaining attributes, including that the password value returned + // via getAttribute is actually the dummy value and not the real password + attributesCopy.put(TrustStore.PASSWORD, AbstractKeyStoreAdapter.DUMMY_PASSWORD_MASK); + for (Map.Entry<String, Object> attribute : attributesCopy.entrySet()) { Object attributeValue = trustStore.getAttribute(attribute.getKey()); assertEquals("Unexpected value of attribute '" + attribute.getKey() + "'", attribute.getValue(), attributeValue); } } - private Map<String, Object> getTrustStoreAttributes() - { - Map<String, Object> attributes = new HashMap<String, Object>(); - attributes.put(TrustStore.NAME, getName()); - attributes.put(TrustStore.PATH, "/path/to/truststore"); - attributes.put(TrustStore.PASSWORD, "my-secret-password"); - attributes.put(TrustStore.TYPE, "NON-JKS"); - attributes.put(TrustStore.KEY_MANAGER_FACTORY_ALGORITHM, "NON-STANDARD"); - attributes.put(TrustStore.PEERS_ONLY, Boolean.TRUE); - attributes.put(TrustStore.DESCRIPTION, "Description"); - return attributes; - } - public void testCreateWithMissedRequiredAttributes() { Map<String, Object> attributes = getTrustStoreAttributes(); @@ -106,4 +102,16 @@ public class TrustStoreRecovererTest extends QpidTestCase } } + private Map<String, Object> getTrustStoreAttributes() + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(TrustStore.NAME, getName()); + attributes.put(TrustStore.PATH, TestSSLConstants.BROKER_TRUSTSTORE); + attributes.put(TrustStore.PASSWORD, TestSSLConstants.BROKER_TRUSTSTORE_PASSWORD); + attributes.put(TrustStore.TYPE, "jks"); + attributes.put(TrustStore.TRUST_MANAGER_FACTORY_ALGORITHM, TrustManagerFactory.getDefaultAlgorithm()); + attributes.put(TrustStore.PEERS_ONLY, Boolean.TRUE); + return attributes; + } + } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreTestCase.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreTestCase.java index adb4472694..d56481340b 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreTestCase.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreTestCase.java @@ -60,7 +60,6 @@ public abstract class ConfigurationEntryStoreTestCase extends QpidTestCase _brokerId = UUID.randomUUID(); _brokerAttributes = new HashMap<String, Object>(); _brokerAttributes.put(Broker.DEFAULT_VIRTUAL_HOST, "test"); - _brokerAttributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, "authenticationProvider1"); _brokerAttributes.put(Broker.QUEUE_ALERT_THRESHOLD_MESSAGE_AGE, 9); _brokerAttributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 8); _brokerAttributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, 7); @@ -71,7 +70,6 @@ public abstract class ConfigurationEntryStoreTestCase extends QpidTestCase _brokerAttributes.put(Broker.QUEUE_MAXIMUM_DELIVERY_ATTEMPTS, 2); _brokerAttributes.put(Broker.QUEUE_DEAD_LETTER_QUEUE_ENABLED, true); _brokerAttributes.put(Broker.VIRTUALHOST_HOUSEKEEPING_CHECK_PERIOD, 1); - _brokerAttributes.put(Broker.ACL_FILE, "/path/to/acl"); _brokerAttributes.put(Broker.CONNECTION_SESSION_COUNT_LIMIT, 1000); _brokerAttributes.put(Broker.CONNECTION_HEART_BEAT_DELAY, 2000); _brokerAttributes.put(Broker.STATISTICS_REPORTING_PERIOD, 4000); @@ -171,7 +169,6 @@ public abstract class ConfigurationEntryStoreTestCase extends QpidTestCase ConfigurationEntry brokerConfigEntry = _store.getRootEntry(); Map<String, Object> attributes = new HashMap<String, Object>(); attributes.put(Broker.DEFAULT_VIRTUAL_HOST, "test"); - attributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, "authenticationProvider1"); attributes.put(Broker.QUEUE_ALERT_THRESHOLD_MESSAGE_AGE, 19); attributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 18); attributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, 17); @@ -182,7 +179,6 @@ public abstract class ConfigurationEntryStoreTestCase extends QpidTestCase attributes.put(Broker.QUEUE_MAXIMUM_DELIVERY_ATTEMPTS, 12); attributes.put(Broker.QUEUE_DEAD_LETTER_QUEUE_ENABLED, false); attributes.put(Broker.VIRTUALHOST_HOUSEKEEPING_CHECK_PERIOD, 11); - attributes.put(Broker.ACL_FILE, "/path/to/acl1"); attributes.put(Broker.CONNECTION_SESSION_COUNT_LIMIT, 11000); attributes.put(Broker.CONNECTION_HEART_BEAT_DELAY, 12000); attributes.put(Broker.STATISTICS_REPORTING_PERIOD, 14000); @@ -284,7 +280,7 @@ public abstract class ConfigurationEntryStoreTestCase extends QpidTestCase attributes.put(TrustStore.PATH, "/path/to/truststore"); attributes.put(TrustStore.PASSWORD, "my-secret-password"); attributes.put(TrustStore.TYPE, "NON-JKS"); - attributes.put(TrustStore.KEY_MANAGER_FACTORY_ALGORITHM, "NON-STANDARD"); + attributes.put(TrustStore.TRUST_MANAGER_FACTORY_ALGORITHM, "NON-STANDARD"); attributes.put(TrustStore.DESCRIPTION, "Description"); ConfigurationEntry trustStoreEntry = new ConfigurationEntry(trustStoreId, TrustStore.class.getSimpleName(), attributes, diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/JsonConfigurationEntryStoreTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/JsonConfigurationEntryStoreTest.java index 1cb760f611..b30508ee0b 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/JsonConfigurationEntryStoreTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/JsonConfigurationEntryStoreTest.java @@ -72,20 +72,20 @@ public class JsonConfigurationEntryStoreTest extends ConfigurationEntryStoreTest public void testAttributeIsResolvedFromSystemProperties() { - String aclLocation = "path/to/acl/" + getTestName(); - setTestSystemProperty("my.test.property", aclLocation); + String defaultVhost = getTestName(); + setTestSystemProperty("my.test.property", defaultVhost); ConfigurationEntryStore store = getStore(); ConfigurationEntry brokerConfigEntry = store.getRootEntry(); Map<String, Object> attributes = new HashMap<String, Object>(brokerConfigEntry.getAttributes()); - attributes.put(Broker.ACL_FILE, "${my.test.property}"); + attributes.put(Broker.DEFAULT_VIRTUAL_HOST, "${my.test.property}"); ConfigurationEntry updatedBrokerEntry = new ConfigurationEntry(brokerConfigEntry.getId(), Broker.class.getSimpleName(), attributes, brokerConfigEntry.getChildrenIds(), store); store.save(updatedBrokerEntry); JsonConfigurationEntryStore store2 = new JsonConfigurationEntryStore(_storeFile.getAbsolutePath(), null); - assertEquals("Unresolved ACL value", aclLocation, store2.getRootEntry().getAttributes().get(Broker.ACL_FILE)); + assertEquals("Unresolved default virtualhost value", defaultVhost, store2.getRootEntry().getAttributes().get(Broker.DEFAULT_VIRTUAL_HOST)); } public void testCreateEmptyStore() diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandlerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandlerTest.java index 52e021240e..d9d3c9fe8f 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandlerTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandlerTest.java @@ -176,7 +176,17 @@ public class ManagementModeStoreHandlerTest extends QpidTestCase assertEquals("Unexpected state", State.QUIESCED, portEntry.getAttributes().get(Port.STATE)); } - public void testVirtualHostEntryIsQuiesced() + public void testVirtualHostEntryIsNotQuiescedByDefault() + { + virtualHostEntryQuiescedStatusTestImpl(false); + } + + public void testVirtualHostEntryIsQuiescedWhenRequested() + { + virtualHostEntryQuiescedStatusTestImpl(true); + } + + private void virtualHostEntryQuiescedStatusTestImpl(boolean mmQuiesceVhosts) { UUID virtualHostId = UUID.randomUUID(); ConfigurationEntry virtualHost = mock(ConfigurationEntry.class); @@ -188,11 +198,17 @@ public class ManagementModeStoreHandlerTest extends QpidTestCase when(_store.getEntry(virtualHostId)).thenReturn(virtualHost); when(_root.getChildrenIds()).thenReturn(new HashSet<UUID>(Arrays.asList(_portEntryId, virtualHostId))); + State expectedState = mmQuiesceVhosts ? State.QUIESCED : null; + if(mmQuiesceVhosts) + { + _options.setManagementModeQuiesceVirtualHosts(mmQuiesceVhosts); + } + _handler = new ManagementModeStoreHandler(_store, _options); ConfigurationEntry hostEntry = _handler.getEntry(virtualHostId); Map<String, Object> hostAttributes = hostEntry.getAttributes(); - assertEquals("Unexpected state", State.QUIESCED, hostAttributes.get(VirtualHost.STATE)); + assertEquals("Unexpected state", expectedState, hostAttributes.get(VirtualHost.STATE)); hostAttributes.remove(VirtualHost.STATE); assertEquals("Unexpected attributes", attributes, hostAttributes); } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/filter/JMSSelectorFilterTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/filter/JMSSelectorFilterTest.java new file mode 100644 index 0000000000..91002edfc6 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/filter/JMSSelectorFilterTest.java @@ -0,0 +1,56 @@ +/* + * 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.filter; + +import junit.framework.TestCase; + +public class JMSSelectorFilterTest extends TestCase +{ + public void testEqualsAndHashCodeUsingSelectorString() throws Exception + { + final String selectorString = "1 = 1"; + + JMSSelectorFilter filter1 = new JMSSelectorFilter(new String(selectorString)); + JMSSelectorFilter filter2 = new JMSSelectorFilter(new String(selectorString)); + + assertEquals(filter1 + " should equal itself", filter1, filter1); + assertFalse(filter1 + " should not equal null", filter1.equals(null)); + assertEqualsAndHashcodeMatch(filter1, filter2); + + JMSSelectorFilter differentFilter = new JMSSelectorFilter("2 = 2"); + assertNotEqual(filter1, differentFilter); + } + + private void assertEqualsAndHashcodeMatch(JMSSelectorFilter filter1, JMSSelectorFilter filter2) + { + String message = filter1 + " and " + filter2 + " should be equal"; + + assertEquals(message, filter1, filter2); + assertEquals(message, filter2, filter1); + + assertEquals("Hashcodes of " + filter1 + " and " + filter2 + " should be equal", + filter1.hashCode(), filter2.hashCode()); + } + + private void assertNotEqual(JMSSelectorFilter filter, JMSSelectorFilter differentFilter) + { + assertFalse(filter.equals(differentFilter)); + assertFalse(differentFilter.equals(filter)); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/model/BrokerShutdownTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/model/BrokerShutdownTest.java index 7c1db6348b..eb5c672eb8 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/model/BrokerShutdownTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/model/BrokerShutdownTest.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.model; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.ConfigurationEntry; import org.apache.qpid.server.configuration.ConfigurationEntryStore; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; @@ -121,7 +122,7 @@ public class BrokerShutdownTest extends QpidTestCase RootMessageLogger rootMessageLogger = mock(RootMessageLogger.class); // recover the broker from the store - RecovererProvider provider = new DefaultRecovererProvider(statisticsGatherer, virtualHostRegistry, logRecorder, rootMessageLogger, _taskExecutor); + RecovererProvider provider = new DefaultRecovererProvider(statisticsGatherer, virtualHostRegistry, logRecorder, rootMessageLogger, _taskExecutor, mock(BrokerOptions.class)); ConfiguredObjectRecoverer<? extends ConfiguredObject> brokerRecoverer = provider.getRecoverer(Broker.class.getSimpleName()); Broker broker = (Broker) brokerRecoverer.create(provider, store.getRootEntry()); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/model/adapter/PortFactoryTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/model/adapter/PortFactoryTest.java index a9303c264e..54826b8c88 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/model/adapter/PortFactoryTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/model/adapter/PortFactoryTest.java @@ -22,8 +22,8 @@ package org.apache.qpid.server.model.adapter; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.any; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -36,6 +36,7 @@ import java.util.UUID; import org.apache.qpid.server.configuration.BrokerProperties; import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.KeyStore; import org.apache.qpid.server.model.Port; @@ -47,8 +48,6 @@ import org.apache.qpid.test.utils.QpidTestCase; public class PortFactoryTest extends QpidTestCase { private UUID _portId = UUID.randomUUID(); - private UUID _keyStoreId = UUID.randomUUID(); - private UUID _trustStoreId = UUID.randomUUID(); private int _portNumber = 123; private Set<String> _tcpStringSet = Collections.singleton(Transport.TCP.name()); private Set<Transport> _tcpTransportSet = Collections.singleton(Transport.TCP); @@ -60,19 +59,22 @@ public class PortFactoryTest extends QpidTestCase private Broker _broker = mock(Broker.class); private KeyStore _keyStore = mock(KeyStore.class); private TrustStore _trustStore = mock(TrustStore.class); - + private String _authProviderName = "authProvider"; + private AuthenticationProvider _authProvider = mock(AuthenticationProvider.class); private PortFactory _portFactory; @Override protected void setUp() throws Exception { + when(_broker.findAuthenticationProviderByName(_authProviderName)).thenReturn(_authProvider); + setTestSystemProperty(BrokerProperties.PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_EXCLUDES, null); setTestSystemProperty(BrokerProperties.PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_INCLUDES, null); _portFactory = new PortFactory(); _attributes.put(Port.PORT, _portNumber); _attributes.put(Port.TRANSPORTS, _tcpStringSet); - + _attributes.put(Port.AUTHENTICATION_PROVIDER, _authProviderName); _attributes.put(Port.TCP_NO_DELAY, "true"); _attributes.put(Port.RECEIVE_BUFFER_SIZE, "1"); _attributes.put(Port.SEND_BUFFER_SIZE, "2"); @@ -113,6 +115,7 @@ public class PortFactoryTest extends QpidTestCase { Map<String, Object> attributes = new HashMap<String, Object>(); attributes.put(Port.PORT, 1); + attributes.put(Port.AUTHENTICATION_PROVIDER, _authProviderName); Port port = _portFactory.createPort(_portId, _broker, attributes); assertNotNull(port); @@ -134,15 +137,14 @@ public class PortFactoryTest extends QpidTestCase public void testCreateAmqpPort() { - createAmqpPortTestImpl(false,false,false); + createAmqpPortTestImpl(false, false, false, null, null); } public void testCreateAmqpPortUsingSslFailsWithoutKeyStore() { - when(_broker.getKeyStores()).thenReturn(new ArrayList<KeyStore>()); try { - createAmqpPortTestImpl(true,false,false); + createAmqpPortTestImpl(true, false, false, null, null); fail("expected exception due to lack of SSL keystore"); } catch(IllegalConfigurationException e) @@ -153,18 +155,22 @@ public class PortFactoryTest extends QpidTestCase public void testCreateAmqpPortUsingSslSucceedsWithKeyStore() { - when(_broker.getKeyStores()).thenReturn(Collections.singleton(_keyStore)); + String keyStoreName = "myKeyStore"; + when(_broker.findKeyStoreByName(keyStoreName)).thenReturn(_keyStore); - createAmqpPortTestImpl(true,false,false); + createAmqpPortTestImpl(true, false, false, keyStoreName, null); } public void testCreateAmqpPortNeedingClientAuthFailsWithoutTrustStore() { - when(_broker.getKeyStores()).thenReturn(Collections.singleton(_keyStore)); - when(_broker.getTrustStores()).thenReturn(new ArrayList<TrustStore>()); + String keyStoreName = "myKeyStore"; + when(_broker.findKeyStoreByName(keyStoreName)).thenReturn(_keyStore); + + when(_broker.findTrustStoreByName(any(String.class))).thenReturn(null); + try { - createAmqpPortTestImpl(true,true,false); + createAmqpPortTestImpl(true, true, false, keyStoreName, null); fail("expected exception due to lack of SSL truststore"); } catch(IllegalConfigurationException e) @@ -175,19 +181,23 @@ public class PortFactoryTest extends QpidTestCase public void testCreateAmqpPortNeedingClientAuthSucceedsWithTrustStore() { - when(_broker.getKeyStores()).thenReturn(Collections.singleton(_keyStore)); - when(_broker.getTrustStores()).thenReturn(Collections.singleton(_trustStore)); + String keyStoreName = "myKeyStore"; + when(_broker.findKeyStoreByName(keyStoreName)).thenReturn(_keyStore); + + String trustStoreName = "myTrustStore"; + when(_broker.findTrustStoreByName(trustStoreName)).thenReturn(_trustStore); - createAmqpPortTestImpl(true,true,false); + createAmqpPortTestImpl(true, true, false, keyStoreName, new String[]{trustStoreName}); } public void testCreateAmqpPortWantingClientAuthFailsWithoutTrustStore() { - when(_broker.getKeyStores()).thenReturn(Collections.singleton(_keyStore)); - when(_broker.getTrustStores()).thenReturn(new ArrayList<TrustStore>()); + String keyStoreName = "myKeyStore"; + when(_broker.findKeyStoreByName(keyStoreName)).thenReturn(_keyStore); + try { - createAmqpPortTestImpl(true,false,true); + createAmqpPortTestImpl(true, false, true, keyStoreName, null); fail("expected exception due to lack of SSL truststore"); } catch(IllegalConfigurationException e) @@ -198,13 +208,17 @@ public class PortFactoryTest extends QpidTestCase public void testCreateAmqpPortWantingClientAuthSucceedsWithTrustStore() { - when(_broker.getKeyStores()).thenReturn(Collections.singleton(_keyStore)); - when(_broker.getTrustStores()).thenReturn(Collections.singleton(_trustStore)); + String keyStoreName = "myKeyStore"; + when(_broker.findKeyStoreByName(keyStoreName)).thenReturn(_keyStore); + + String trustStoreName = "myTrustStore"; + when(_broker.findTrustStoreByName(trustStoreName)).thenReturn(_trustStore); - createAmqpPortTestImpl(true,false,true); + createAmqpPortTestImpl(true, false, true, keyStoreName, new String[]{trustStoreName}); } - public void createAmqpPortTestImpl(boolean useSslTransport, boolean needClientAuth, boolean wantClientAuth) + public void createAmqpPortTestImpl(boolean useSslTransport, boolean needClientAuth, boolean wantClientAuth, + String keystoreName, String[] trustStoreNames) { Set<Protocol> amqp010ProtocolSet = Collections.singleton(Protocol.AMQP_0_10); Set<String> amqp010StringSet = Collections.singleton(Protocol.AMQP_0_10.name()); @@ -225,6 +239,16 @@ public class PortFactoryTest extends QpidTestCase _attributes.put(Port.WANT_CLIENT_AUTH, "true"); } + if(keystoreName != null) + { + _attributes.put(Port.KEY_STORE, keystoreName); + } + + if(trustStoreNames != null) + { + _attributes.put(Port.TRUST_STORES, Arrays.asList(trustStoreNames)); + } + Port port = _portFactory.createPort(_portId, _broker, _attributes); assertNotNull(port); @@ -254,6 +278,7 @@ public class PortFactoryTest extends QpidTestCase Set<String> nonAmqpStringSet = Collections.singleton(Protocol.JMX_RMI.name()); _attributes = new HashMap<String, Object>(); _attributes.put(Port.PROTOCOLS, nonAmqpStringSet); + _attributes.put(Port.AUTHENTICATION_PROVIDER, _authProviderName); _attributes.put(Port.PORT, _portNumber); _attributes.put(Port.TRANSPORTS, _tcpStringSet); @@ -279,6 +304,7 @@ public class PortFactoryTest extends QpidTestCase Set<String> nonAmqpStringSet = Collections.singleton(Protocol.JMX_RMI.name()); _attributes = new HashMap<String, Object>(); _attributes.put(Port.PROTOCOLS, nonAmqpStringSet); + _attributes.put(Port.AUTHENTICATION_PROVIDER, _authProviderName); _attributes.put(Port.PORT, _portNumber); Port port = _portFactory.createPort(_portId, _broker, _attributes); @@ -335,4 +361,29 @@ public class PortFactoryTest extends QpidTestCase // pass } } + + public void testCreateRMIPortRequestingSslFails() + { + String keyStoreName = "myKeyStore"; + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(Port.PORT, 1); + attributes.put(Port.NAME, getTestName()); + attributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + attributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.RMI)); + _attributes.put(Port.KEY_STORE, keyStoreName); + + when(_broker.findKeyStoreByName(keyStoreName)).thenReturn(_keyStore); + + try + { + _portFactory.createPort(_portId, _broker, attributes); + fail("RMI port creation should fail due to requesting SSL"); + } + catch(IllegalConfigurationException e) + { + e.printStackTrace(); + // pass + } + } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java deleted file mode 100644 index c41b9bf081..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java +++ /dev/null @@ -1,160 +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.security.auth.database; - -import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; -import org.apache.qpid.server.security.auth.UsernamePrincipal; -import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5Initialiser; -import org.apache.qpid.server.security.auth.sasl.plain.PlainInitialiser; - -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.login.AccountNotFoundException; - -import java.io.File; -import java.io.IOException; -import java.security.Principal; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -public class PropertiesPrincipalDatabase implements PrincipalDatabase -{ - private Properties _users; - - private Map<String, AuthenticationProviderInitialiser> _saslServers; - - public PropertiesPrincipalDatabase(Properties users) - { - _users = users; - - _saslServers = new HashMap<String, AuthenticationProviderInitialiser>(); - - /** - * Create Authenticators for Properties Principal Database. - */ - - // Accept MD5 incomming and use plain comparison with the file - PlainInitialiser cram = new PlainInitialiser(); - cram.initialise(this); - // Accept Plain incomming and hash it for comparison to the file. - CRAMMD5Initialiser plain = new CRAMMD5Initialiser(); - plain.initialise(this, CRAMMD5Initialiser.HashDirection.INCOMMING); - - _saslServers.put(plain.getMechanismName(), cram); - _saslServers.put(cram.getMechanismName(), plain); - } - - public void setPassword(Principal principal, PasswordCallback callback) throws IOException, AccountNotFoundException - { - if (principal == null) - { - throw new IllegalArgumentException("principal must not be null"); - } - - - - final String pwd = _users.getProperty(principal.getName()); - - if (pwd != null) - { - callback.setPassword(pwd.toCharArray()); - } - else - { - throw new AccountNotFoundException("No account found for principal " + principal); - } - } - - public boolean verifyPassword(String principal, char[] password) throws AccountNotFoundException - { - //fixme this is not correct as toCharArray is not safe based on the type of string. - char[] pwd = _users.getProperty(principal).toCharArray(); - - return compareCharArray(pwd, password); - } - - public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException - { - return false; // updates denied - } - - public boolean createPrincipal(Principal principal, char[] password) - { - return false; // updates denied - } - - public boolean deletePrincipal(Principal principal) throws AccountNotFoundException - { - return false; // updates denied - } - - private boolean compareCharArray(char[] a, char[] b) - { - boolean equal = false; - if (a.length == b.length) - { - equal = true; - int index = 0; - while (equal && index < a.length) - { - equal = a[index] == b[index]; - index++; - } - } - return equal; - } - - - public Map<String, AuthenticationProviderInitialiser> getMechanisms() - { - return _saslServers; - } - - public List<Principal> getUsers() - { - return new LinkedList<Principal>(); //todo - } - - public Principal getUser(String username) - { - if (_users.getProperty(username) != null) - { - return new UsernamePrincipal(username); - } - else - { - return null; - } - } - - public void reload() throws IOException - { - //No file to update from, so do nothing. - } - - @Override - public void open(File passwordFile) - { - throw new UnsupportedOperationException(); - } -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/jmx/JMXPasswordAuthenticatorTest.java index 52b525dd80..a4dd97e6a1 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/jmx/JMXPasswordAuthenticatorTest.java @@ -18,7 +18,7 @@ * under the License. * */ -package org.apache.qpid.server.security.auth.rmi; +package org.apache.qpid.server.security.auth.jmx; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.any; @@ -38,14 +38,15 @@ import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.server.security.auth.AuthenticationResult; import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; +import org.apache.qpid.server.security.auth.jmx.JMXPasswordAuthenticator; import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; import org.apache.qpid.server.security.SecurityManager; /** - * Tests the RMIPasswordAuthenticator and its collaboration with the AuthenticationManager. + * Tests the JMXPasswordAuthenticator and its collaboration with the AuthenticationManager. * */ -public class RMIPasswordAuthenticatorTest extends TestCase +public class JMXPasswordAuthenticatorTest extends TestCase { private static final String USERNAME = "guest"; private static final String PASSWORD = "password"; @@ -55,7 +56,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase private final Subject _loginSubject = new Subject(); private final String[] _credentials = new String[] {USERNAME, PASSWORD}; - private RMIPasswordAuthenticator _rmipa; + private JMXPasswordAuthenticator _rmipa; private SubjectCreator _usernamePasswordOkaySuvjectCreator = createMockSubjectCreator(true, null); private SubjectCreator _badPasswordSubjectCreator = createMockSubjectCreator(false, null); @@ -63,7 +64,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase protected void setUp() throws Exception { when(_broker.getSecurityManager()).thenReturn(_securityManager); - _rmipa = new RMIPasswordAuthenticator(_broker, new InetSocketAddress(8999)); + _rmipa = new JMXPasswordAuthenticator(_broker, new InetSocketAddress(8999)); } /** @@ -93,7 +94,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase catch (SecurityException se) { assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage()); + JMXPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage()); } } @@ -110,7 +111,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase catch (SecurityException se) { assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.USER_NOT_AUTHORISED_FOR_MANAGEMENT, se.getMessage()); + JMXPasswordAuthenticator.USER_NOT_AUTHORISED_FOR_MANAGEMENT, se.getMessage()); } } @@ -164,7 +165,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase catch (SecurityException se) { assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.SHOULD_BE_STRING_ARRAY, se.getMessage()); + JMXPasswordAuthenticator.SHOULD_BE_STRING_ARRAY, se.getMessage()); } } @@ -185,7 +186,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase catch (SecurityException se) { assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.SHOULD_HAVE_2_ELEMENTS, se.getMessage()); + JMXPasswordAuthenticator.SHOULD_HAVE_2_ELEMENTS, se.getMessage()); } // Test handling of null credentials @@ -199,7 +200,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase catch (SecurityException se) { assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.CREDENTIALS_REQUIRED, se.getMessage()); + JMXPasswordAuthenticator.CREDENTIALS_REQUIRED, se.getMessage()); } try @@ -212,7 +213,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase catch (SecurityException se) { assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.SHOULD_BE_NON_NULL, se.getMessage()); + JMXPasswordAuthenticator.SHOULD_BE_NON_NULL, se.getMessage()); } try @@ -225,7 +226,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase catch (SecurityException se) { assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.SHOULD_BE_NON_NULL, se.getMessage()); + JMXPasswordAuthenticator.SHOULD_BE_NON_NULL, se.getMessage()); } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManagerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManagerTest.java new file mode 100644 index 0000000000..5868f44b8f --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManagerTest.java @@ -0,0 +1,140 @@ +package org.apache.qpid.server.security.auth.manager; + +import java.security.Principal; +import java.util.Set; + +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; +import org.apache.qpid.server.security.auth.sasl.SaslUtil; +import org.apache.qpid.server.security.auth.sasl.plain.PlainSaslServer; +import org.apache.qpid.test.utils.QpidTestCase; + +public class SimpleAuthenticationManagerTest extends QpidTestCase +{ + private static final String TEST_USER = "testUser"; + private static final String TEST_PASSWORD = "testPassword"; + private AuthenticationManager _authenticationManager; + + public void setUp() throws Exception + { + super.setUp(); + _authenticationManager = new SimpleAuthenticationManager(TEST_USER, TEST_PASSWORD); + } + + public void testGetMechanisms() + { + assertEquals("Unexpected mechanisms", "PLAIN CRAM-MD5", _authenticationManager.getMechanisms()); + } + + public void testCreateSaslServerForUnsupportedMechanisms() throws Exception + { + String[] unsupported = new String[] { "EXTERNAL", "CRAM-MD5-HEX", "CRAM-MD5-HASHED", "ANONYMOUS", "GSSAPI"}; + for (int i = 0; i < unsupported.length; i++) + { + String mechanism = unsupported[i]; + try + { + _authenticationManager.createSaslServer(mechanism, "test", null); + fail("Mechanism " + mechanism + " should not be supported by SimpleAuthenticationManager"); + } + catch (SaslException e) + { + // pass + } + } + } + + public void testAuthenticateWithPlainSaslServer() throws Exception + { + AuthenticationResult result = authenticatePlain(TEST_USER, TEST_PASSWORD); + assertAuthenticated(result); + } + + public void testAuthenticateWithPlainSaslServerInvalidPassword() throws Exception + { + AuthenticationResult result = authenticatePlain(TEST_USER, "wrong-password"); + assertUnauthenticated(result); + } + + public void testAuthenticateWithPlainSaslServerInvalidUsername() throws Exception + { + AuthenticationResult result = authenticatePlain("wrong-user", TEST_PASSWORD); + assertUnauthenticated(result); + } + + public void testAuthenticateWithCramMd5SaslServer() throws Exception + { + AuthenticationResult result = authenticateCramMd5(TEST_USER, TEST_PASSWORD); + assertAuthenticated(result); + } + + public void testAuthenticateWithCramMd5SaslServerInvalidPassword() throws Exception + { + AuthenticationResult result = authenticateCramMd5(TEST_USER, "wrong-password"); + assertUnauthenticated(result); + } + + public void testAuthenticateWithCramMd5SaslServerInvalidUsername() throws Exception + { + AuthenticationResult result = authenticateCramMd5("wrong-user", TEST_PASSWORD); + assertUnauthenticated(result); + } + + public void testAuthenticateValidCredentials() + { + AuthenticationResult result = _authenticationManager.authenticate(TEST_USER, TEST_PASSWORD); + assertEquals("Unexpected authentication result", AuthenticationStatus.SUCCESS, result.getStatus()); + assertAuthenticated(result); + } + + public void testAuthenticateInvalidPassword() + { + AuthenticationResult result = _authenticationManager.authenticate(TEST_USER, "invalid"); + assertUnauthenticated(result); + } + + public void testAuthenticateInvalidUserName() + { + AuthenticationResult result = _authenticationManager.authenticate("invalid", TEST_PASSWORD); + assertUnauthenticated(result); + } + + private void assertAuthenticated(AuthenticationResult result) + { + assertEquals("Unexpected authentication result", AuthenticationStatus.SUCCESS, result.getStatus()); + Principal principal = result.getMainPrincipal(); + assertEquals("Unexpected principal name", TEST_USER, principal.getName()); + Set<Principal> principals = result.getPrincipals(); + assertEquals("Unexpected principals size", 1, principals.size()); + assertEquals("Unexpected principal name", TEST_USER, principals.iterator().next().getName()); + } + + private void assertUnauthenticated(AuthenticationResult result) + { + assertEquals("Unexpected authentication result", AuthenticationStatus.ERROR, result.getStatus()); + assertNull("Unexpected principal", result.getMainPrincipal()); + Set<Principal> principals = result.getPrincipals(); + assertEquals("Unexpected principals size", 0, principals.size()); + } + + private AuthenticationResult authenticatePlain(String userName, String userPassword) throws SaslException, Exception + { + PlainSaslServer ss = (PlainSaslServer) _authenticationManager.createSaslServer("PLAIN", "test", null); + byte[] response = SaslUtil.generatePlainClientResponse(userName, userPassword); + + return _authenticationManager.authenticate(ss, response); + } + + private AuthenticationResult authenticateCramMd5(String userName, String userPassword) throws SaslException, Exception + { + SaslServer ss = _authenticationManager.createSaslServer("CRAM-MD5", "test", null); + byte[] challenge = ss.evaluateResponse(new byte[0]); + byte[] response = SaslUtil.generateCramMD5ClientResponse(userName, userPassword, challenge); + + AuthenticationResult result = _authenticationManager.authenticate(ss, response); + return result; + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexInitialiserTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexInitialiserTest.java index e408fd73d5..3079222b1c 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexInitialiserTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexInitialiserTest.java @@ -20,88 +20,94 @@ */ package org.apache.qpid.server.security.auth.sasl; -import junit.framework.TestCase; - -import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabase; -import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HexInitialiser; +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; -import java.io.IOException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Properties; + +import junit.framework.TestCase; + +import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase; +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; +import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HexInitialiser; +import org.apache.qpid.test.utils.TestFileUtils; +import org.apache.qpid.tools.security.Passwd; /** * These tests ensure that the Hex wrapping that the initialiser performs does actually operate when the handle method is called. */ public class CRAMMD5HexInitialiserTest extends TestCase { - public void testHex() - { - //Create User details for testing - String user = "testUser"; - String password = "testPassword"; + private static final String TEST_PASSWORD = "testPassword"; + private static final String TEST_USER = "testUser"; + private File _file; - perform(user, password); - } - - public void testHashedHex() + public void testHashedHex() throws Exception { - //Create User details for testing - String user = "testUser"; - String password = "testPassword"; - - //Create a hashed password that we then attempt to put through the call back mechanism. - try - { - password = new String(MessageDigest.getInstance("MD5").digest(password.getBytes())); - } - catch (NoSuchAlgorithmException e) - { - fail(e.getMessage()); - } - - perform(user, password); + perform(TEST_USER, getHash(TEST_PASSWORD)); } - public void perform(String user, String password) + public void perform(String user, char[] password) throws Exception { CRAMMD5HexInitialiser initialiser = new CRAMMD5HexInitialiser(); - //Use properties to create a PrincipalDatabase - Properties users = new Properties(); - users.put(user, password); - - PropertiesPrincipalDatabase db = new PropertiesPrincipalDatabase(users); - + PrincipalDatabase db = new Base64MD5PasswordFilePrincipalDatabase(); + db.open(_file); initialiser.initialise(db); - //setup the callbacks PasswordCallback passwordCallback = new PasswordCallback("password:", false); NameCallback usernameCallback = new NameCallback("user:", user); Callback[] callbacks = new Callback[]{usernameCallback, passwordCallback}; - //Check the - try + assertNull("The password was not null before the handle call.", passwordCallback.getPassword()); + initialiser.getCallbackHandler().handle(callbacks); + + assertArrayEquals(toHex(password), passwordCallback.getPassword()); + } + + public void setUp() throws Exception + { + super.setUp(); + _file = TestFileUtils.createTempFile(this, "password-file", new Passwd().getOutput(TEST_USER , TEST_PASSWORD)); + } + + public void tearDown() throws Exception + { + if (_file != null) { - assertNull("The password was not null before the handle call.", passwordCallback.getPassword()); - initialiser.getCallbackHandler().handle(callbacks); + _file.delete(); } - catch (IOException e) + super.tearDown(); + } + + private char[] getHash(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException + { + + byte[] data = text.getBytes("utf-8"); + + MessageDigest md = MessageDigest.getInstance("MD5"); + + for (byte b : data) { - fail(e.getMessage()); + md.update(b); } - catch (UnsupportedCallbackException e) + + byte[] digest = md.digest(); + + char[] hash = new char[digest.length]; + + int index = 0; + for (byte b : digest) { - fail(e.getMessage()); + hash[index++] = (char) b; } - //Hex the password we initialised with and compare it with the passwordCallback - assertArrayEquals(toHex(password.toCharArray()), passwordCallback.getPassword()); + return hash; } private void assertArrayEquals(char[] expected, char[] actual) @@ -135,4 +141,5 @@ public class CRAMMD5HexInitialiserTest extends TestCase return hex; } + } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslUtil.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslUtil.java new file mode 100644 index 0000000000..251ebc4c81 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslUtil.java @@ -0,0 +1,85 @@ +/* + * + * 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.security.auth.sasl; + +import java.security.MessageDigest; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +public class SaslUtil +{ + + private static byte SEPARATOR = 0; + + public static byte[] generatePlainClientResponse(String userName, String userPassword) throws Exception + { + byte[] password = userPassword.getBytes("UTF8"); + byte user[] = userName.getBytes("UTF8"); + byte response[] = new byte[password.length + user.length + 2]; + int size = 0; + response[size++] = SEPARATOR; + System.arraycopy(user, 0, response, size, user.length); + size += user.length; + response[size++] = SEPARATOR; + System.arraycopy(password, 0, response, size, password.length); + return response; + } + + public static byte[] generateCramMD5HexClientResponse(String userName, String userPassword, byte[] challengeBytes) + throws Exception + { + String macAlgorithm = "HmacMD5"; + byte[] digestedPasswordBytes = MessageDigest.getInstance("MD5").digest(userPassword.getBytes("UTF-8")); + byte[] hexEncodedDigestedPasswordBytes = toHex(digestedPasswordBytes).getBytes("UTF-8"); + Mac mac = Mac.getInstance(macAlgorithm); + mac.init(new SecretKeySpec(hexEncodedDigestedPasswordBytes, macAlgorithm)); + final byte[] messageAuthenticationCode = mac.doFinal(challengeBytes); + String responseAsString = userName + " " + toHex(messageAuthenticationCode); + return responseAsString.getBytes(); + } + + public static byte[] generateCramMD5ClientResponse(String userName, String userPassword, byte[] challengeBytes) + throws Exception + { + String macAlgorithm = "HmacMD5"; + Mac mac = Mac.getInstance(macAlgorithm); + mac.init(new SecretKeySpec(userPassword.getBytes("UTF-8"), macAlgorithm)); + final byte[] messageAuthenticationCode = mac.doFinal(challengeBytes); + String responseAsString = userName + " " + toHex(messageAuthenticationCode); + return responseAsString.getBytes(); + } + + public static String toHex(byte[] data) + { + StringBuffer hash = new StringBuffer(); + for (int i = 0; i < data.length; i++) + { + String hex = Integer.toHexString(0xFF & data[i]); + if (hex.length() == 1) + { + hash.append('0'); + } + hash.append(hex); + } + return hash.toString(); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupManagerFactoryTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupManagerFactoryTest.java index 934c0082ea..90308d316b 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupManagerFactoryTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupManagerFactoryTest.java @@ -35,8 +35,8 @@ public class FileGroupManagerFactoryTest extends TestCase public void testInstanceCreated() throws Exception { - _configuration.put(GroupProvider.TYPE, FileGroupManagerFactory.FILE_GROUP_MANAGER_TYPE); - _configuration.put(FileGroupManagerFactory.FILE, _emptyButValidGroupFile); + _configuration.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + _configuration.put(FileGroupManagerFactory.PATH, _emptyButValidGroupFile); GroupManager manager = _factory.createInstance(_configuration); assertNotNull(manager); @@ -60,8 +60,8 @@ public class FileGroupManagerFactoryTest extends TestCase public void testRejectsConfigThatIsMissingAttributeValue() throws Exception { - _configuration.put(GroupProvider.TYPE, FileGroupManagerFactory.FILE_GROUP_MANAGER_TYPE); - _configuration.put(FileGroupManagerFactory.FILE, null); + _configuration.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + _configuration.put(FileGroupManagerFactory.PATH, null); try { diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupManagerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupManagerTest.java index b83d25b206..152703d548 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupManagerTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupManagerTest.java @@ -20,13 +20,11 @@ package org.apache.qpid.server.security.group; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.security.Principal; import java.util.Properties; import java.util.Set; -import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.security.auth.UsernamePrincipal; import org.apache.qpid.test.utils.QpidTestCase; @@ -62,17 +60,26 @@ public class FileGroupManagerTest extends QpidTestCase public void testNonExistentGroupFile() throws Exception { - final String filePath = "/does.not.exist/"; - + final String filePath = TMP_FOLDER + File.separator + "non.existing"; + File file = new File(filePath); + if (file.exists()) + { + file.delete(); + } + assertFalse("File should not exist", file.exists()); try { _manager = new FileGroupManager(filePath); - fail("expected exception was not thrown"); + assertFalse("File should be created", file.exists()); + _manager.onCreate(); + assertTrue("File should be created", file.exists()); + _manager.open(); + Set<Principal> groups = _manager.getGroupPrincipals(); + assertTrue("No group should exist", groups.isEmpty()); } - catch(IllegalConfigurationException ce) + finally { - assertNotNull(ce.getCause()); - assertTrue(ce.getCause() instanceof FileNotFoundException); + file.delete(); } } @@ -80,7 +87,7 @@ public class FileGroupManagerTest extends QpidTestCase { final String groupFileName = writeGroupFile(); _manager = new FileGroupManager(groupFileName); - + _manager.open(); Set<Principal> principals = _manager.getGroupPrincipalsForUser("user1"); assertEquals(1, principals.size()); assertTrue(principals.contains(new GroupPrincipal("myGroup"))); @@ -90,7 +97,7 @@ public class FileGroupManagerTest extends QpidTestCase { final String groupFileName = writeGroupFile(); _manager = new FileGroupManager(groupFileName); - + _manager.open(); Set<Principal> principals = _manager.getUserPrincipalsForGroup("myGroup"); assertEquals(1, principals.size()); assertTrue(principals.contains(new UsernamePrincipal("user1"))); @@ -100,7 +107,7 @@ public class FileGroupManagerTest extends QpidTestCase { final String groupFileName = writeGroupFile(MY_GROUP, MYGROUP_USERS, MY_GROUP2, MYGROUP_USERS); _manager = new FileGroupManager(groupFileName); - + _manager.open(); Set<Principal> principals = _manager.getGroupPrincipals(); assertEquals(2, principals.size()); assertTrue(principals.contains(new GroupPrincipal("myGroup"))); @@ -111,7 +118,7 @@ public class FileGroupManagerTest extends QpidTestCase { final String groupFileName = writeGroupFile(); _manager = new FileGroupManager(groupFileName); - + _manager.open(); Set<Principal> principals = _manager.getGroupPrincipals(); assertEquals(1, principals.size()); @@ -126,7 +133,7 @@ public class FileGroupManagerTest extends QpidTestCase { final String groupFileName = writeGroupFile(MY_GROUP, MYGROUP_USERS); _manager = new FileGroupManager(groupFileName); - + _manager.open(); Set<Principal> principals = _manager.getGroupPrincipals(); assertEquals(1, principals.size()); @@ -140,7 +147,7 @@ public class FileGroupManagerTest extends QpidTestCase { final String groupFileName = writeGroupFile(MY_GROUP, MYGROUP_USERS); _manager = new FileGroupManager(groupFileName); - + _manager.open(); Set<Principal> principals = _manager.getUserPrincipalsForGroup("myGroup"); assertEquals(1, principals.size()); assertFalse(principals.contains(new UsernamePrincipal("user2"))); @@ -156,7 +163,7 @@ public class FileGroupManagerTest extends QpidTestCase { final String groupFileName = writeGroupFile(MY_GROUP, MYGROUP_USERS); _manager = new FileGroupManager(groupFileName); - + _manager.open(); Set<Principal> principals = _manager.getUserPrincipalsForGroup("myGroup"); assertEquals(1, principals.size()); assertTrue(principals.contains(new UsernamePrincipal("user1"))); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupProviderAttributeDescriptions.properties b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupProviderAttributeDescriptions.properties new file mode 100644 index 0000000000..2c2d2ab9e3 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupProviderAttributeDescriptions.properties @@ -0,0 +1,19 @@ +# +# 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. + +path= File location* diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/BrokerTestHelper.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/BrokerTestHelper.java index 860928e380..ec3c006364 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/BrokerTestHelper.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/BrokerTestHelper.java @@ -77,7 +77,7 @@ public class BrokerTestHelper RootMessageLogger rootMessageLogger = CurrentActor.get().getRootMessageLogger(); when(broker.getRootMessageLogger()).thenReturn(rootMessageLogger); when(broker.getVirtualHostRegistry()).thenReturn(new VirtualHostRegistry()); - when(broker.getSecurityManager()).thenReturn(new SecurityManager(null)); + when(broker.getSecurityManager()).thenReturn(new SecurityManager(mock(Broker.class), false)); GenericActor.setDefaultMessageLogger(rootMessageLogger); return broker; } @@ -96,14 +96,14 @@ public class BrokerTestHelper throws Exception { StatisticsGatherer statisticsGatherer = mock(StatisticsGatherer.class); - VirtualHost host = new VirtualHostImpl(virtualHostRegistry, statisticsGatherer, new SecurityManager(null), virtualHostConfiguration); + VirtualHost host = new VirtualHostImpl(virtualHostRegistry, statisticsGatherer, new SecurityManager(mock(Broker.class), false), virtualHostConfiguration); virtualHostRegistry.registerVirtualHost(host); return host; } public static VirtualHost createVirtualHost(VirtualHostConfiguration virtualHostConfiguration) throws Exception { - return new VirtualHostImpl(null, mock(StatisticsGatherer.class), new SecurityManager(null), virtualHostConfiguration); + return new VirtualHostImpl(null, mock(StatisticsGatherer.class), new SecurityManager(mock(Broker.class), false), virtualHostConfiguration); } public static VirtualHost createVirtualHost(String name, VirtualHostRegistry virtualHostRegistry) throws Exception @@ -156,7 +156,7 @@ public class BrokerTestHelper public static Exchange createExchange(String hostName) throws Exception { - SecurityManager securityManager = new SecurityManager(null); + SecurityManager securityManager = new SecurityManager(mock(Broker.class), false); VirtualHost virtualHost = mock(VirtualHost.class); when(virtualHost.getName()).thenReturn(hostName); when(virtualHost.getSecurityManager()).thenReturn(securityManager); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/StringUtilTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/StringUtilTest.java new file mode 100644 index 0000000000..93b4172792 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/StringUtilTest.java @@ -0,0 +1,38 @@ +package org.apache.qpid.server.util; + +import org.apache.qpid.server.util.StringUtil; +import org.apache.qpid.test.utils.QpidTestCase; + +public class StringUtilTest extends QpidTestCase +{ + private StringUtil _util; + + @Override + public void setUp() throws Exception + { + super.setUp(); + _util = new StringUtil(); + } + + public void testRandomAlphaNumericStringInt() + { + String password = _util.randomAlphaNumericString(10); + assertEquals("Unexpected password string length", 10, password.length()); + assertCharacters(password); + } + + private void assertCharacters(String password) + { + String numbers = "0123456789"; + String letters = "abcdefghijklmnopqrstuvwxwy"; + String others = "_-"; + String expectedCharacters = (numbers + letters + letters.toUpperCase() + others); + char[] chars = password.toCharArray(); + for (int i = 0; i < chars.length; i++) + { + char ch = chars[i]; + assertTrue("Unexpected character " + ch, expectedCharacters.indexOf(ch) != -1); + } + } + +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java index 559a7f8aaf..4e58eae1d2 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java @@ -211,7 +211,7 @@ public class VirtualHostImplTest extends QpidTestCase _virtualHostRegistry = broker.getVirtualHostRegistry(); VirtualHostConfiguration configuration = new VirtualHostConfiguration(vhostName, config, broker); - VirtualHost host = new VirtualHostImpl(_virtualHostRegistry, mock(StatisticsGatherer.class), new SecurityManager(null), configuration); + VirtualHost host = new VirtualHostImpl(_virtualHostRegistry, mock(StatisticsGatherer.class), new SecurityManager(mock(Broker.class), false), configuration); _virtualHostRegistry.registerVirtualHost(host); return host; @@ -295,7 +295,7 @@ public class VirtualHostImplTest extends QpidTestCase Configuration config = new PropertiesConfiguration(); config.setProperty("store.type", MemoryMessageStore.TYPE); VirtualHostConfiguration configuration = new VirtualHostConfiguration(virtualHostName, config, broker); - VirtualHost host = new VirtualHostImpl(_virtualHostRegistry, mock(StatisticsGatherer.class), new SecurityManager(null), configuration); + VirtualHost host = new VirtualHostImpl(_virtualHostRegistry, mock(StatisticsGatherer.class), new SecurityManager(mock(Broker.class), false), configuration); _virtualHostRegistry.registerVirtualHost(host); return host; } diff --git a/qpid/java/build.deps b/qpid/java/build.deps index b53ac55b6d..c8a850bce9 100644 --- a/qpid/java/build.deps +++ b/qpid/java/build.deps @@ -47,17 +47,17 @@ slf4j-log4j=lib/required/slf4j-log4j12-1.6.4.jar xalan=lib/required/xalan-2.7.0.jar -jetty=lib/required/jetty-server-7.6.3.v20120416.jar -jetty-continuation=lib/required/jetty-continuation-7.6.3.v20120416.jar -jetty-security=lib/required/jetty-security-7.6.3.v20120416.jar -jetty-util=lib/required/jetty-util-7.6.3.v20120416.jar -jetty-io=lib/required/jetty-io-7.6.3.v20120416.jar -jetty-http=lib/required/jetty-http-7.6.3.v20120416.jar -jetty-servlet=lib/required/jetty-servlet-7.6.3.v20120416.jar -jetty-websocket=lib/required/jetty-websocket-7.6.3.v20120416.jar +jetty=lib/required/jetty-server-7.6.10.v20130312.jar +jetty-continuation=lib/required/jetty-continuation-7.6.10.v20130312.jar +jetty-security=lib/required/jetty-security-7.6.10.v20130312.jar +jetty-util=lib/required/jetty-util-7.6.10.v20130312.jar +jetty-io=lib/required/jetty-io-7.6.10.v20130312.jar +jetty-http=lib/required/jetty-http-7.6.10.v20130312.jar +jetty-servlet=lib/required/jetty-servlet-7.6.10.v20130312.jar +jetty-websocket=lib/required/jetty-websocket-7.6.10.v20130312.jar servlet-api=${geronimo-servlet} -dojo=lib/required/dojo-war-1.7.2.war +dojo=lib/required/dojo-war-1.8.3.war jackson-core=lib/required/jackson-core-asl-1.9.0.jar jackson-mapper=lib/required/jackson-mapper-asl-1.9.0.jar diff --git a/qpid/java/client/example/src/main/java/org/apache/qpid/example/hello.properties b/qpid/java/client/example/src/main/java/org/apache/qpid/example/hello.properties index 27ea66b318..fdfbd418f8 100644 --- a/qpid/java/client/example/src/main/java/org/apache/qpid/example/hello.properties +++ b/qpid/java/client/example/src/main/java/org/apache/qpid/example/hello.properties @@ -20,7 +20,7 @@ java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextF # register some connection factories # connectionfactory.[jndiname] = [ConnectionURL] -connectionfactory.qpidConnectionfactory = amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672' +connectionfactory.qpidConnectionfactory = amqp://guest:guest@clientid/?brokerlist='tcp://localhost:5672' # Register an AMQP destination in JNDI # destination.[jniName] = [Address Format] diff --git a/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java b/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java index 01381ad23f..158006f072 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/ssl/SSLContextFactory.java @@ -31,7 +31,6 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; -import java.security.cert.X509Certificate; import java.io.IOException; import java.security.GeneralSecurityException; @@ -39,7 +38,6 @@ import java.security.KeyStore; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.List; /** * Factory used to create SSLContexts. SSL needs to be configured diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/TestSSLConstants.java b/qpid/java/common/src/test/java/org/apache/qpid/test/utils/TestSSLConstants.java index 5664e94bd9..c48f164d98 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/TestSSLConstants.java +++ b/qpid/java/common/src/test/java/org/apache/qpid/test/utils/TestSSLConstants.java @@ -28,4 +28,10 @@ public interface TestSSLConstants String BROKER_KEYSTORE = "test-profiles/test_resources/ssl/java_broker_keystore.jks"; String BROKER_KEYSTORE_PASSWORD = "password"; + + String BROKER_PEERSTORE = "test-profiles/test_resources/ssl/java_broker_peerstore.jks"; + String BROKER_PEERSTORE_PASSWORD = "password"; + + String BROKER_TRUSTSTORE = "test-profiles/test_resources/ssl/java_broker_truststore.jks"; + String BROKER_TRUSTSTORE_PASSWORD = "password"; } diff --git a/qpid/java/ivy.retrieve.xml b/qpid/java/ivy.retrieve.xml index 4c8e935aca..78769da005 100644 --- a/qpid/java/ivy.retrieve.xml +++ b/qpid/java/ivy.retrieve.xml @@ -59,15 +59,15 @@ <dependency org="org.mockito" name="mockito-all" rev="1.9.0" transitive="false"/> <dependency org="org.slf4j" name="slf4j-api" rev="1.6.4" transitive="false"/> <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.6.4" transitive="false"/> - <dependency org="org.eclipse.jetty" name="jetty-server" rev="7.6.3.v20120416" transitive="false"/> - <dependency org="org.eclipse.jetty" name="jetty-websocket" rev="7.6.3.v20120416" transitive="false"/> - <dependency org="org.eclipse.jetty" name="jetty-continuation" rev="7.6.3.v20120416" transitive="false"/> - <dependency org="org.eclipse.jetty" name="jetty-io" rev="7.6.3.v20120416" transitive="false"/> - <dependency org="org.eclipse.jetty" name="jetty-http" rev="7.6.3.v20120416" transitive="false"/> - <dependency org="org.eclipse.jetty" name="jetty-security" rev="7.6.3.v20120416" transitive="false"/> - <dependency org="org.eclipse.jetty" name="jetty-servlet" rev="7.6.3.v20120416" transitive="false"/> - <dependency org="org.eclipse.jetty" name="jetty-util" rev="7.6.3.v20120416" transitive="false"/> - <dependency org="org.dojotoolkit" name="dojo-war" rev="1.7.2" transitive="false"/> + <dependency org="org.eclipse.jetty" name="jetty-server" rev="7.6.10.v20130312" transitive="false"/> + <dependency org="org.eclipse.jetty" name="jetty-websocket" rev="7.6.10.v20130312" transitive="false"/> + <dependency org="org.eclipse.jetty" name="jetty-continuation" rev="7.6.10.v20130312" transitive="false"/> + <dependency org="org.eclipse.jetty" name="jetty-io" rev="7.6.10.v20130312" transitive="false"/> + <dependency org="org.eclipse.jetty" name="jetty-http" rev="7.6.10.v20130312" transitive="false"/> + <dependency org="org.eclipse.jetty" name="jetty-security" rev="7.6.10.v20130312" transitive="false"/> + <dependency org="org.eclipse.jetty" name="jetty-servlet" rev="7.6.10.v20130312" transitive="false"/> + <dependency org="org.eclipse.jetty" name="jetty-util" rev="7.6.10.v20130312" transitive="false"/> + <dependency org="org.dojotoolkit" name="dojo-war" rev="1.8.3" transitive="false"/> <dependency org="xalan" name="xalan" rev="2.7.0" transitive="false"/> <dependency org="velocity" name="velocity" rev="1.4" transitive="false"/> <dependency org="velocity" name="velocity-dep" rev="1.4" transitive="false"/> diff --git a/qpid/java/lib/poms/jetty-continuation-7.6.3.v20120416.xml b/qpid/java/lib/poms/jetty-continuation-7.6.10.v20130312.xml index 6ace17c18a..5beba95d17 100644 --- a/qpid/java/lib/poms/jetty-continuation-7.6.3.v20120416.xml +++ b/qpid/java/lib/poms/jetty-continuation-7.6.10.v20130312.xml @@ -18,5 +18,5 @@ <dep> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-continuation</artifactId> - <version>7.6.3.v20120416</version> + <version>7.6.10.v20130312</version> </dep> diff --git a/qpid/java/lib/poms/jetty-http-7.6.3.v20120416.xml b/qpid/java/lib/poms/jetty-http-7.6.10.v20130312.xml index 02af020cfe..5c840bedd6 100644 --- a/qpid/java/lib/poms/jetty-http-7.6.3.v20120416.xml +++ b/qpid/java/lib/poms/jetty-http-7.6.10.v20130312.xml @@ -18,7 +18,7 @@ <dep> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-http</artifactId> - <version>7.6.3.v20120416</version> + <version>7.6.10.v20130312</version> <exclusions> <exclusion> <groupId>org.eclipse.jetty</groupId> diff --git a/qpid/java/lib/poms/jetty-io-7.6.3.v20120416.xml b/qpid/java/lib/poms/jetty-io-7.6.10.v20130312.xml index b6c865f0fb..9cec3998ea 100644 --- a/qpid/java/lib/poms/jetty-io-7.6.3.v20120416.xml +++ b/qpid/java/lib/poms/jetty-io-7.6.10.v20130312.xml @@ -18,7 +18,7 @@ <dep> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-io</artifactId> - <version>7.6.3.v20120416</version> + <version>7.6.10.v20130312</version> <exclusions> <exclusion> <groupId>org.eclipse.jetty</groupId> diff --git a/qpid/java/lib/poms/jetty-security-7.6.3.v20120416.xml b/qpid/java/lib/poms/jetty-security-7.6.10.v20130312.xml index ba643ccb97..9501750ba0 100644 --- a/qpid/java/lib/poms/jetty-security-7.6.3.v20120416.xml +++ b/qpid/java/lib/poms/jetty-security-7.6.10.v20130312.xml @@ -18,7 +18,7 @@ <dep> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-security</artifactId> - <version>7.6.3.v20120416</version> + <version>7.6.10.v20130312</version> <exclusions> <exclusion> <groupId>org.eclipse.jetty</groupId> diff --git a/qpid/java/lib/poms/jetty-server-7.6.3.v20120416.xml b/qpid/java/lib/poms/jetty-server-7.6.10.v20130312.xml index 1e7ce42623..587860b50f 100644 --- a/qpid/java/lib/poms/jetty-server-7.6.3.v20120416.xml +++ b/qpid/java/lib/poms/jetty-server-7.6.10.v20130312.xml @@ -18,7 +18,7 @@ <dep> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> - <version>7.6.3.v20120416</version> + <version>7.6.10.v20130312</version> <exclusions> <exclusion> <groupId>org.eclipse.jetty.orbit</groupId> diff --git a/qpid/java/lib/poms/jetty-servlet-7.6.3.v20120416.xml b/qpid/java/lib/poms/jetty-servlet-7.6.10.v20130312.xml index 001bcce824..4c0ff0a41b 100644 --- a/qpid/java/lib/poms/jetty-servlet-7.6.3.v20120416.xml +++ b/qpid/java/lib/poms/jetty-servlet-7.6.10.v20130312.xml @@ -18,7 +18,7 @@ <dep> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlet</artifactId> - <version>7.6.3.v20120416</version> + <version>7.6.10.v20130312</version> <exclusions> <exclusion> <groupId>org.eclipse.jetty</groupId> diff --git a/qpid/java/lib/poms/jetty-util-7.6.3.v20120416.xml b/qpid/java/lib/poms/jetty-util-7.6.10.v20130312.xml index c40d7e5f60..f5c990248f 100644 --- a/qpid/java/lib/poms/jetty-util-7.6.3.v20120416.xml +++ b/qpid/java/lib/poms/jetty-util-7.6.10.v20130312.xml @@ -18,5 +18,5 @@ <dep> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-util</artifactId> - <version>7.6.3.v20120416</version> + <version>7.6.10.v20130312</version> </dep> diff --git a/qpid/java/lib/poms/jetty-websocket-7.6.3.v20120416.xml b/qpid/java/lib/poms/jetty-websocket-7.6.10.v20130312.xml index bba80d9236..4d3ebd1666 100644 --- a/qpid/java/lib/poms/jetty-websocket-7.6.3.v20120416.xml +++ b/qpid/java/lib/poms/jetty-websocket-7.6.10.v20130312.xml @@ -18,7 +18,7 @@ <dep> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-websocket</artifactId> - <version>7.6.3.v20120416</version> + <version>7.6.10.v20130312</version> <exclusions> <exclusion> <groupId>org.eclipse.jetty</groupId> diff --git a/qpid/java/systests/etc/config-systests.json b/qpid/java/systests/etc/config-systests.json index b06b469891..c47744c47c 100644 --- a/qpid/java/systests/etc/config-systests.json +++ b/qpid/java/systests/etc/config-systests.json @@ -19,23 +19,30 @@ * */ { - "name": "QpidBroker", - "defaultAuthenticationProvider" : "plain", + "name": "Broker", "defaultVirtualHost" : "test", - "keyStorePath": "${QPID_HOME}/../test-profiles/test_resources/ssl/java_broker_keystore.jks", - "keyStorePassword": "password", - "trustStorePath": "${QPID_HOME}/../test-profiles/test_resources/ssl/java_broker_truststore.jks", - "trustStorePassword": "password", "authenticationproviders" : [ { "name" : "plain", "type" : "PlainPasswordFile", "path" : "${QPID_HOME}/etc/passwd" } ], + "keystores" : [ { + "name" : "systestsKeyStore", + "path" : "${QPID_HOME}/../test-profiles/test_resources/ssl/java_broker_keystore.jks", + "password" : "password" + } ], + "truststores" : [ { + "name" : "systestsTrustStore", + "path" : "${QPID_HOME}/../test-profiles/test_resources/ssl/java_broker_truststore.jks", + "password" : "password" + } ], "ports" : [ { "name" : "amqp", + "authenticationProvider" : "plain", "port" : "${test.port}" }, { "name" : "http", + "authenticationProvider" : "plain", "port" : "${test.hport}", "protocols" : [ "HTTP" ] }, { @@ -44,6 +51,7 @@ "protocols" : [ "RMI" ] }, { "name" : "jmx", + "authenticationProvider" : "plain", "port" : "${test.cport}", "protocols" : [ "JMX_RMI" ] }], @@ -61,4 +69,4 @@ "name" : "jmxManagement" } ] */ -}
\ No newline at end of file +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java index 884e89fb65..71b763685e 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/client/ssl/SSLTest.java @@ -362,9 +362,12 @@ public class SSLTest extends QpidBrokerTestCase Map<String, Object> sslPortAttributes = new HashMap<String, Object>(); sslPortAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); sslPortAttributes.put(Port.PORT, DEFAULT_SSL_PORT); + sslPortAttributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); sslPortAttributes.put(Port.NEED_CLIENT_AUTH, needClientAuth); sslPortAttributes.put(Port.WANT_CLIENT_AUTH, wantClientAuth); sslPortAttributes.put(Port.NAME, TestBrokerConfiguration.ENTRY_NAME_SSL_PORT); + sslPortAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + sslPortAttributes.put(Port.TRUST_STORES, Collections.singleton(TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE)); getBrokerConfiguration().addPortConfiguration(sslPortAttributes); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java index c5f5e06ae1..95a378f1f6 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java @@ -95,9 +95,7 @@ public class BrokerLoggingTest extends AbstractTestLogging { String TESTID="BRK-1006"; - // This logging startup code only occurs when you run a Java broker, - // that broker must be started via Main so not an InVM broker. - if (isJavaBroker() && isExternalBroker()) + if (isJavaBroker()) { startBroker(); @@ -165,8 +163,6 @@ public class BrokerLoggingTest extends AbstractTestLogging */ public void testBrokerStartupDefaultLog4j() throws Exception { - // This logging startup code only occurs when you run a Java broker, - // that broker must be started via Main so not an InVM broker. if (isJavaBroker() && isExternalBroker() && !isInternalBroker()) { String TESTID = "BRK-1007"; @@ -256,7 +252,7 @@ public class BrokerLoggingTest extends AbstractTestLogging public void testBrokerStartupCustomLog4j() throws Exception { // This logging startup code only occurs when you run a Java broker - if (isJavaBroker() && isExternalBroker()) + if (isJavaBroker()) { String customLog4j = getBrokerCommandLog4JFile().getAbsolutePath(); @@ -344,7 +340,7 @@ public class BrokerLoggingTest extends AbstractTestLogging { // This logging startup code only occurs when you run a Java broker, // that broker must be started via Main so not an InVM broker. - if (isJavaBroker() && isExternalBroker()) + if (isJavaBroker()) { String TESTID = "BRK-1001"; @@ -426,9 +422,7 @@ public class BrokerLoggingTest extends AbstractTestLogging */ public void testBrokerStartupListeningTCPDefault() throws Exception { - // This logging startup code only occurs when you run a Java broker, - // that broker must be started via Main so not an InVM broker. - if (isJavaBroker() && isExternalBroker()) + if (isJavaBroker()) { String TESTID = "BRK-1002"; @@ -484,7 +478,7 @@ public class BrokerLoggingTest extends AbstractTestLogging //3 String message = getMessageString(log); assertTrue("Expected Listen log not correct" + message, - message.endsWith("Listening on [TCP] port " + getPort())); + message.endsWith("Listening on TCP port " + getPort())); validation = true; } @@ -534,9 +528,7 @@ public class BrokerLoggingTest extends AbstractTestLogging */ public void testBrokerStartupListeningTCPSSL() throws Exception { - // This logging startup code only occurs when you run a Java broker, - // that broker must be started via Main so not an InVM broker. - if (isJavaBroker() && isExternalBroker()) + if (isJavaBroker()) { String TESTID = "BRK-1002"; @@ -545,6 +537,8 @@ public class BrokerLoggingTest extends AbstractTestLogging sslPortAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); sslPortAttributes.put(Port.PORT, DEFAULT_SSL_PORT); sslPortAttributes.put(Port.NAME, TestBrokerConfiguration.ENTRY_NAME_SSL_PORT); + sslPortAttributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + sslPortAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); getBrokerConfiguration().addPortConfiguration(sslPortAttributes); startBroker(); @@ -595,16 +589,23 @@ public class BrokerLoggingTest extends AbstractTestLogging assertEquals("Four listen messages should be found.", 4, listenMessages .size()); - //3 - //Check the first - String message = getMessageString(getLog(listenMessages .get(0))); - assertTrue("Expected Listen log not correct" + message, - message.endsWith("Listening on [TCP] port " + getPort())); + int tcpStarted = 0; + int sslStarted = 0; - // Check the third, ssl listen. - message = getMessageString(getLog(listenMessages .get(2))); - assertTrue("Expected Listen log not correct" + message, - message.endsWith("Listening on [SSL] port " + DEFAULT_SSL_PORT)); + for (String message : listenMessages) + { + if (message.endsWith("Listening on TCP port " + getPort())) + { + tcpStarted++; + } + if (message.endsWith("Listening on SSL port " + DEFAULT_SSL_PORT)) + { + sslStarted++; + } + } + + assertEquals("Unexpected number of logs 'Listening on TCP port'", 2, tcpStarted); + assertEquals("Unexpected number of logs 'Listening on SSL port'", 2, sslStarted); //4 Test ports open testSocketOpen(getPort()); @@ -643,9 +644,7 @@ public class BrokerLoggingTest extends AbstractTestLogging */ public void testBrokerStartupReady() throws Exception { - // This logging startup code only occurs when you run a Java broker, - // that broker must be started via Main so not an InVM broker. - if (isJavaBroker() && isExternalBroker()) + if (isJavaBroker()) { String TESTID = "BRK-1004"; @@ -731,9 +730,7 @@ public class BrokerLoggingTest extends AbstractTestLogging */ public void testBrokerShutdownListeningTCPDefault() throws Exception { - // This logging startup code only occurs when you run a Java broker, - // that broker must be started via Main so not an InVM broker. - if (isJavaBroker() && isExternalBroker()) + if (isJavaBroker() && isInternalBroker()) { String TESTID = "BRK-1003"; @@ -825,9 +822,7 @@ public class BrokerLoggingTest extends AbstractTestLogging */ public void testBrokerShutdownListeningTCPSSL() throws Exception { - // This logging startup code only occurs when you run a Java broker, - // that broker must be started via Main so not an InVM broker. - if (isJavaBroker() && isExternalBroker()) + if (isJavaBroker() && isInternalBroker()) { String TESTID = "BRK-1003"; @@ -836,6 +831,8 @@ public class BrokerLoggingTest extends AbstractTestLogging sslPortAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); sslPortAttributes.put(Port.PORT, DEFAULT_SSL_PORT); sslPortAttributes.put(Port.NAME, TestBrokerConfiguration.ENTRY_NAME_SSL_PORT); + sslPortAttributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + sslPortAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); getBrokerConfiguration().addPortConfiguration(sslPortAttributes); startBroker(); @@ -869,15 +866,23 @@ public class BrokerLoggingTest extends AbstractTestLogging assertEquals("Two shutdown messages should be found.", 2, listenMessages.size()); - //3 - String message = getMessageString(getLog(listenMessages.get(0))); - assertTrue("Expected shutdown log not correct" + message, - message.endsWith("TCP port " + getPort())); + int tcpShuttingDown = 0; + int sslShuttingDown = 0; - // Check second, ssl, listen. - message = getMessageString(getLog(listenMessages.get(1))); - assertTrue("Expected shutdown log not correct" + message, - message.endsWith("TCP/SSL port " + DEFAULT_SSL_PORT)); + for (String m : listenMessages) + { + if (m.endsWith("Shutting down : TCP port " + getPort())) + { + tcpShuttingDown++; + } + if (m.endsWith("Shutting down : SSL port " + DEFAULT_SSL_PORT)) + { + sslShuttingDown++; + } + } + + assertEquals("Unexpected number of logs 'Shutting down : TCP port'", 1, tcpShuttingDown); + assertEquals("Unexpected number of logs 'Shutting down : SSL port'", 1, sslShuttingDown); //4 //Test Port closed @@ -913,9 +918,7 @@ public class BrokerLoggingTest extends AbstractTestLogging */ public void testBrokerShutdownStopped() throws Exception { - // This logging startup code only occurs when you run a Java broker, - // that broker must be started via Main so not an InVM broker. - if (isJavaBroker() && isExternalBroker()) + if (isJavaBroker() && isInternalBroker()) { String TESTID = "BRK-1005"; diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java index 1ea105ae1a..25dd5fd2f8 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java @@ -105,22 +105,25 @@ public class VirtualHostLoggingTest extends AbstractTestLogging */ public void testVirtualhostClosure() throws Exception { - stopBroker(); + if (isJavaBroker() && isInternalBroker()) + { + stopBroker(); - // Wait for the correct VHT message to arrive. - waitForMessage(VHT_PREFIX + "1002"); + // Wait for the correct VHT message to arrive. + waitForMessage(VHT_PREFIX + "1002"); - // Validate each vhost logs a closure - List<String> results = findMatches(VHT_PREFIX + "1002"); + // Validate each vhost logs a closure + List<String> results = findMatches(VHT_PREFIX + "1002"); - try - { - assertEquals("Each vhost did not close their store.", 1, results.size()); - } - catch (AssertionFailedError afe) - { - dumpLogs(results, _monitor); - throw afe; + try + { + assertEquals("Each vhost did not close their store.", 1, results.size()); + } + catch (AssertionFailedError afe) + { + dumpLogs(results, _monitor); + throw afe; + } } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java index 782709b24f..41d93b5ca2 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ModelTest.java @@ -65,9 +65,10 @@ public class ModelTest extends QpidBrokerTestCase @Override public void setUp() throws Exception { + getBrokerConfiguration().addJmxManagementConfiguration(); + // Create a JMX Helper _jmxUtils = new JMXTestUtils(this); - _jmxUtils.setUp(); super.setUp(); // Open the JMX Connection diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java index 13053d02df..8bebcc703d 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/queue/ProducerFlowControlTest.java @@ -64,8 +64,9 @@ public class ProducerFlowControlTest extends AbstractTestLogging public void setUp() throws Exception { + getBrokerConfiguration().addJmxManagementConfiguration(); + _jmxUtils = new JMXTestUtils(this); - _jmxUtils.setUp(); _jmxUtilConnected=false; super.setUp(); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java index 814936f342..7a3edd316f 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/AbstractACLTestCase.java @@ -63,7 +63,7 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements @Override public void setUp() throws Exception { - getBrokerConfiguration().setBrokerAttribute(Broker.GROUP_FILE, System.getProperty(QPID_HOME) + "/etc/groups-systests"); + getBrokerConfiguration().addGroupFileConfiguration(System.getProperty(QPID_HOME) + "/etc/groups-systests"); // run test specific setup String testSetup = StringUtils.replace(getName(), "test", "setUp"); @@ -110,7 +110,7 @@ public abstract class AbstractACLTestCase extends QpidBrokerTestCase implements if (vhost == null) { - testcase.getBrokerConfiguration().setBrokerAttribute(Broker.ACL_FILE, aclFile.getAbsolutePath()); + testcase.getBrokerConfiguration().addAclFileConfiguration(aclFile.getAbsolutePath()); } else { diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java index 1830040007..e200741161 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLJMXTest.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.security.acl; import org.apache.qpid.management.common.mbeans.ServerInformation; import org.apache.qpid.server.security.access.ObjectType; import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.TestBrokerConfiguration; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; @@ -43,11 +44,15 @@ public class ExternalACLJMXTest extends AbstractACLTestCase @Override public void setUp() throws Exception { + //remove the normal 'test' vhost, we will configure the vhosts below + getBrokerConfiguration(0).removeObjectConfiguration(TestBrokerConfiguration.ENTRY_NAME_VIRTUAL_HOST); + createTestVirtualHost(0, TEST_VHOST); createTestVirtualHost(0, TEST2_VHOST); + getBrokerConfiguration().addJmxManagementConfiguration(); + _jmx = new JMXTestUtils(this); - _jmx.setUp(); super.setUp(); _jmx.open(); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationTest.java index 2e051d93dd..6cc4ec17c7 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationTest.java @@ -20,12 +20,16 @@ */ package org.apache.qpid.server.security.auth.manager; +import static org.apache.qpid.test.utils.TestSSLConstants.BROKER_PEERSTORE; +import static org.apache.qpid.test.utils.TestSSLConstants.BROKER_PEERSTORE_PASSWORD; import static org.apache.qpid.test.utils.TestSSLConstants.KEYSTORE; import static org.apache.qpid.test.utils.TestSSLConstants.KEYSTORE_PASSWORD; import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE; import static org.apache.qpid.test.utils.TestSSLConstants.TRUSTSTORE_PASSWORD; import static org.apache.qpid.test.utils.TestSSLConstants.UNTRUSTED_KEYSTORE; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -38,9 +42,9 @@ import org.apache.commons.configuration.ConfigurationException; import org.apache.qpid.client.AMQConnectionURL; import org.apache.qpid.management.common.mbeans.ManagedConnection; import org.apache.qpid.server.model.AuthenticationProvider; -import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.Transport; +import org.apache.qpid.server.model.TrustStore; import org.apache.qpid.server.plugin.AuthenticationManagerFactory; import org.apache.qpid.test.utils.JMXTestUtils; import org.apache.qpid.test.utils.QpidBrokerTestCase; @@ -52,6 +56,7 @@ public class ExternalAuthenticationTest extends QpidBrokerTestCase protected void setUp() throws Exception { // not calling super.setUp() to avoid broker start-up + setSystemProperty("javax.net.debug", "ssl"); } /** @@ -61,7 +66,6 @@ public class ExternalAuthenticationTest extends QpidBrokerTestCase public void testExternalAuthenticationManagerOnSSLPort() throws Exception { setCommonBrokerSSLProperties(true); - getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_SSL_PORT, Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_EXTERNAL_PROVIDER); super.setUp(); setClientKeystoreProperties(); @@ -88,13 +92,13 @@ public class ExternalAuthenticationTest extends QpidBrokerTestCase } /** - * Tests that when EXTERNAL authentication manager is set as the default, clients presenting certificates are able to connect. - * Also, checks a client with valid username and password but not using ssl is unable to connect to the non SSL port. + * Tests that when EXTERNAL authentication manager is set on the non-SSL port, clients with valid username and password + * but not using ssl are unable to connect to the non-SSL port. */ - public void testExternalAuthenticationManagerAsDefault() throws Exception + public void testExternalAuthenticationManagerOnNonSslPort() throws Exception { setCommonBrokerSSLProperties(true); - getBrokerConfiguration().setBrokerAttribute(Broker.DEFAULT_AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_EXTERNAL_PROVIDER); + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT, Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_EXTERNAL_PROVIDER); super.setUp(); setClientKeystoreProperties(); @@ -109,25 +113,15 @@ public class ExternalAuthenticationTest extends QpidBrokerTestCase { // pass } - - try - { - getExternalSSLConnection(false); - } - catch (JMSException e) - { - fail("Should be able to create a connection to the SSL port. " + e.getMessage()); - } } /** - * Tests that when EXTERNAL authentication manager is set as the default, clients without certificates are unable to connect to the SSL port + * Tests that when EXTERNAL authentication manager is used, clients without certificates are unable to connect to the SSL port * even with valid username and password. */ public void testExternalAuthenticationManagerWithoutClientKeyStore() throws Exception { setCommonBrokerSSLProperties(false); - getBrokerConfiguration().setBrokerAttribute(Broker.DEFAULT_AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_EXTERNAL_PROVIDER); super.setUp(); setClientTrustoreProperties(); @@ -150,7 +144,6 @@ public class ExternalAuthenticationTest extends QpidBrokerTestCase public void testExternalAuthenticationDeniesUntrustedClientCert() throws Exception { setCommonBrokerSSLProperties(true); - getBrokerConfiguration().setBrokerAttribute(Broker.DEFAULT_AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_EXTERNAL_PROVIDER); super.setUp(); setUntrustedClientKeystoreProperties(); @@ -168,31 +161,85 @@ public class ExternalAuthenticationTest extends QpidBrokerTestCase } /** - * Tests that when using the EXTERNAL auth provide and the broker 'peerstore' is configured to contain a certificate that is - * otherwise untrusted by the broker [truststore], clients using that certificate will then be able to connect. + * Tests that when using the EXTERNAL auth provider and a 'peersOnly' truststore, clients using certs directly in + * in the store will be able to connect and clients using certs signed by the same CA but not in the store will not. */ - public void testExternalAuthenticationWithPeerStoreAllowsOtherwiseUntrustedClientCert() throws Exception + public void testExternalAuthenticationWithPeersOnlyTrustStore() throws Exception { - setCommonBrokerSSLProperties(true); - getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_SSL_PORT, Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_EXTERNAL_PROVIDER); + externalAuthenticationWithPeersOnlyTrustStoreTestImpl(false); + } + + /** + * Tests that when using the EXTERNAL auth provider, with both the regular trust store and a 'peersOnly' truststore, clients + * using certs signed by the CA in the trust store are allowed even if they are not present in the 'peersOnly' store. + */ + public void testExternalAuthenticationWithRegularAndPeersOnlyTrustStores() throws Exception + { + externalAuthenticationWithPeersOnlyTrustStoreTestImpl(true); + } + + private void externalAuthenticationWithPeersOnlyTrustStoreTestImpl(boolean useTrustAndPeerStore) throws Exception + { + String peerStoreName = "myPeerStore"; + + List<String> storeNames = null; + if(useTrustAndPeerStore) + { + //Use the regular trust store AND the 'peersOnly' store. The regular trust store trusts the CA that + //signed both the app1 and app2 certs. The peersOnly store contains only app1 and so does not trust app2 + storeNames = Arrays.asList(TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE, peerStoreName); + } + else + { + //use only the 'peersOnly' store, which contains only app1 and so does not trust app2 + storeNames = Arrays.asList(peerStoreName); + } - //Use the untrusted client keystore as the brokers peerstore to make the broker trust the cert. - getBrokerConfiguration().setBrokerAttribute(Broker.PEER_STORE_PATH, UNTRUSTED_KEYSTORE); - getBrokerConfiguration().setBrokerAttribute(Broker.PEER_STORE_PASSWORD, KEYSTORE_PASSWORD); + //set the brokers SSL config, inc which SSL stores to use + setCommonBrokerSSLProperties(true, storeNames); + + //add the peersOnly store to the config + Map<String, Object> sslTrustStoreAttributes = new HashMap<String, Object>(); + sslTrustStoreAttributes.put(TrustStore.NAME, peerStoreName); + sslTrustStoreAttributes.put(TrustStore.PATH, BROKER_PEERSTORE); + sslTrustStoreAttributes.put(TrustStore.PASSWORD, BROKER_PEERSTORE_PASSWORD); + sslTrustStoreAttributes.put(TrustStore.PEERS_ONLY, true); + getBrokerConfiguration().addTrustStoreConfiguration(sslTrustStoreAttributes); super.setUp(); - setUntrustedClientKeystoreProperties(); + setClientKeystoreProperties(); setClientTrustoreProperties(); try { - getExternalSSLConnection(false); - fail("Untrusted client's validation against the broker's multi store manager unexpectedly passed."); + //use the app1 cert, which IS in the peerstore (and has CA in the trustStore) + getExternalSSLConnection(false, "&ssl_cert_alias='app1'"); + } + catch (JMSException e) + { + fail("Client's validation against the broker's multi store manager unexpectedly failed, when configured store was expected to allow."); + } + + try + { + //use the app2 cert, which is NOT in the peerstore (but is signed by the same CA as app1) + getExternalSSLConnection(false, "&ssl_cert_alias='app2'"); + if(!useTrustAndPeerStore) + { + fail("Client's validation against the broker's multi store manager unexpectedly passed, when configured store was expected to deny."); + } } catch (JMSException e) { - // expected + if(useTrustAndPeerStore) + { + fail("Client's validation against the broker's multi store manager unexpectedly failed, when configured store was expected to allow."); + } + else + { + //expected, the CA in trust store should allow both app1 and app2 + } } } @@ -203,10 +250,9 @@ public class ExternalAuthenticationTest extends QpidBrokerTestCase public void testExternalAuthenticationManagerUsernameAsCN() throws Exception { JMXTestUtils jmxUtils = new JMXTestUtils(this); - jmxUtils.setUp(); setCommonBrokerSSLProperties(true); - getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_SSL_PORT, Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_EXTERNAL_PROVIDER); + getBrokerConfiguration().addJmxManagementConfiguration(); super.setUp(); @@ -215,7 +261,7 @@ public class ExternalAuthenticationTest extends QpidBrokerTestCase try { - getExternalSSLConnection(false); + getExternalSSLConnection(false, "&ssl_cert_alias='app2'"); } catch (JMSException e) { @@ -237,11 +283,10 @@ public class ExternalAuthenticationTest extends QpidBrokerTestCase public void testExternalAuthenticationManagerUsernameAsDN() throws Exception { JMXTestUtils jmxUtils = new JMXTestUtils(this); - jmxUtils.setUp(); setCommonBrokerSSLProperties(true); - getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_SSL_PORT, Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_EXTERNAL_PROVIDER); getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_EXTERNAL_PROVIDER, ExternalAuthenticationManagerFactory.ATTRIBUTE_USE_FULL_DN, "true"); + getBrokerConfiguration().addJmxManagementConfiguration(); super.setUp(); @@ -250,7 +295,7 @@ public class ExternalAuthenticationTest extends QpidBrokerTestCase try { - getExternalSSLConnection(false); + getExternalSSLConnection(false, "&ssl_cert_alias='app2'"); } catch (JMSException e) { @@ -267,32 +312,47 @@ public class ExternalAuthenticationTest extends QpidBrokerTestCase private Connection getExternalSSLConnection(boolean includeUserNameAndPassword) throws Exception { - String url = "amqp://%s@test/?brokerlist='tcp://localhost:%s?ssl='true'&sasl_mechs='EXTERNAL'&ssl_cert_alias='app2''"; + return getExternalSSLConnection(includeUserNameAndPassword, ""); + } + + private Connection getExternalSSLConnection(boolean includeUserNameAndPassword, String optionString) throws Exception + { + String url = "amqp://%s@test/?brokerlist='tcp://localhost:%s?ssl='true'&sasl_mechs='EXTERNAL'%s'"; if (includeUserNameAndPassword) { - url = String.format(url, "guest:guest", String.valueOf(QpidBrokerTestCase.DEFAULT_SSL_PORT)); + url = String.format(url, "guest:guest", String.valueOf(QpidBrokerTestCase.DEFAULT_SSL_PORT), optionString); } else { - url = String.format(url, ":", String.valueOf(QpidBrokerTestCase.DEFAULT_SSL_PORT)); + url = String.format(url, ":", String.valueOf(QpidBrokerTestCase.DEFAULT_SSL_PORT), optionString); } return getConnection(new AMQConnectionURL(url)); } private void setCommonBrokerSSLProperties(boolean needClientAuth) throws ConfigurationException { + setCommonBrokerSSLProperties(needClientAuth, Collections.singleton(TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE)); + } + + private void setCommonBrokerSSLProperties(boolean needClientAuth, Collection<String> trustStoreNames) throws ConfigurationException + { TestBrokerConfiguration config = getBrokerConfiguration(); + Map<String, Object> sslPortAttributes = new HashMap<String, Object>(); sslPortAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); sslPortAttributes.put(Port.PORT, DEFAULT_SSL_PORT); sslPortAttributes.put(Port.NEED_CLIENT_AUTH, String.valueOf(needClientAuth)); sslPortAttributes.put(Port.NAME, TestBrokerConfiguration.ENTRY_NAME_SSL_PORT); + sslPortAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + sslPortAttributes.put(Port.TRUST_STORES, trustStoreNames); config.addPortConfiguration(sslPortAttributes); Map<String, Object> externalAuthProviderAttributes = new HashMap<String, Object>(); - externalAuthProviderAttributes.put(AuthenticationManagerFactory.ATTRIBUTE_TYPE, ExternalAuthenticationManagerFactory.PROVIDER_TYPE); externalAuthProviderAttributes.put(AuthenticationProvider.NAME, TestBrokerConfiguration.ENTRY_NAME_EXTERNAL_PROVIDER); + externalAuthProviderAttributes.put(AuthenticationManagerFactory.ATTRIBUTE_TYPE, ExternalAuthenticationManagerFactory.PROVIDER_TYPE); config.addAuthenticationProviderConfiguration(externalAuthProviderAttributes); + + config.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_SSL_PORT, Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_EXTERNAL_PROVIDER); } private void setUntrustedClientKeystoreProperties() @@ -311,6 +371,5 @@ public class ExternalAuthenticationTest extends QpidBrokerTestCase { setSystemProperty("javax.net.ssl.trustStore", TRUSTSTORE); setSystemProperty("javax.net.ssl.trustStorePassword", TRUSTSTORE_PASSWORD); - setSystemProperty("javax.net.debug", "ssl"); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/auth/manager/MultipleAuthenticationManagersTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/auth/manager/MultipleAuthenticationManagersTest.java index 40346d7424..44057025ba 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/auth/manager/MultipleAuthenticationManagersTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/auth/manager/MultipleAuthenticationManagersTest.java @@ -56,6 +56,8 @@ public class MultipleAuthenticationManagersTest extends QpidBrokerTestCase sslPortAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); sslPortAttributes.put(Port.PORT, DEFAULT_SSL_PORT); sslPortAttributes.put(Port.NAME, TestBrokerConfiguration.ENTRY_NAME_SSL_PORT); + sslPortAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + sslPortAttributes.put(Port.TRUST_STORES, Collections.singleton(TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE)); sslPortAttributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_ANONYMOUS_PROVIDER); config.addPortConfiguration(sslPortAttributes); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java index 954208e78e..3f979bea27 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/BrokerManagementTest.java @@ -40,8 +40,10 @@ public class BrokerManagementTest extends QpidBrokerTestCase public void setUp() throws Exception { + getBrokerConfiguration().addJmxManagementConfiguration(); + _jmxUtils = new JMXTestUtils(this); - _jmxUtils.setUp(); + super.setUp(); _jmxUtils.open(); _managedBroker = _jmxUtils.getManagedBroker(VIRTUAL_HOST); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java index 28d7bf4aed..34b13dfaca 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ConnectionManagementTest.java @@ -51,8 +51,10 @@ public class ConnectionManagementTest extends QpidBrokerTestCase public void setUp() throws Exception { + getBrokerConfiguration().addJmxManagementConfiguration(); + _jmxUtils = new JMXTestUtils(this); - _jmxUtils.setUp(); // modifies broker config therefore must be done before super.setUp() + super.setUp(); _jmxUtils.open(); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java index 3c3bbdca41..3717c1594d 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/LoggingManagementTest.java @@ -47,8 +47,9 @@ public class LoggingManagementTest extends QpidBrokerTestCase public void setUp() throws Exception { + getBrokerConfiguration().addJmxManagementConfiguration(); + _jmxUtils = new JMXTestUtils(this); - _jmxUtils.setUp(); // System test normally run with log for4j test config from beneath test-profiles. We need to // copy it as some of our tests write to this file. diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java index 47b38381c5..69e81cf85d 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ManagementActorLoggingTest.java @@ -49,8 +49,10 @@ public class ManagementActorLoggingTest extends AbstractTestLogging @Override public void setUp() throws Exception { + getBrokerConfiguration().addJmxManagementConfiguration(); + _jmxUtils = new JMXTestUtils(this); - _jmxUtils.setUp(); + super.setUp(); _jmxUtils.open(); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java index 9279287117..7a66fe6a7c 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/ManagementLoggingTest.java @@ -277,7 +277,6 @@ public class ManagementLoggingTest extends AbstractTestLogging List<String> closeResults = null; try { - jmxUtils.setUp(); jmxUtils.open(); openResults = waitAndFindMatches("MNG-1007"); } @@ -314,11 +313,9 @@ public class ManagementLoggingTest extends AbstractTestLogging if(useManagementSSL) { - // This test requires we have an ssl connection + // This test requires we have ssl, change the transport and add they keystore to the port config config.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_JMX_PORT, Port.TRANSPORTS, Collections.singleton(Transport.SSL)); - - setSystemProperty("javax.net.ssl.keyStore", "test-profiles/test_resources/ssl/java_broker_keystore.jks"); - setSystemProperty("javax.net.ssl.keyStorePassword", "password"); + config.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_JMX_PORT, Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); } startBroker(); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java index 0d3289d1bd..448886d056 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/QueueManagementTest.java @@ -87,8 +87,9 @@ public class QueueManagementTest extends QpidBrokerTestCase public void setUp() throws Exception { + getBrokerConfiguration().addJmxManagementConfiguration(); + _jmxUtils = new JMXTestUtils(this); - _jmxUtils.setUp(); super.setUp(); _sourceQueueName = getTestQueueName() + "_src"; diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java index 72fbd65acc..6e4ea13cfc 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/StatisticsTest.java @@ -56,8 +56,9 @@ public class StatisticsTest extends QpidBrokerTestCase createTestVirtualHost(0, TEST_VIRTUALHOST1); createTestVirtualHost(0, TEST_VIRTUALHOST2); + getBrokerConfiguration().addJmxManagementConfiguration(); + _jmxUtils = new JMXTestUtils(this, TEST_USER, TEST_PASSWORD); - _jmxUtils.setUp(); super.setUp(); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java index 7eff1c89ee..9fc92c98a3 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/management/jmx/UserManagementTest.java @@ -65,9 +65,9 @@ public class UserManagementTest extends QpidBrokerTestCase newAttributes.put(AuthenticationManagerFactory.ATTRIBUTE_TYPE, getAuthenticationManagerType()); newAttributes.put(AbstractPrincipalDatabaseAuthManagerFactory.ATTRIBUTE_PATH, _passwordFile.getAbsolutePath()); getBrokerConfiguration().setObjectAttributes(TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, newAttributes); + getBrokerConfiguration().addJmxManagementConfiguration(); _jmxUtils = new JMXTestUtils(this); - _jmxUtils.setUp(); super.setUp(); _jmxUtils.open(); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AccessControlProviderRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AccessControlProviderRestTest.java new file mode 100644 index 0000000000..ae7c648197 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AccessControlProviderRestTest.java @@ -0,0 +1,256 @@ +/* + * + * 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.systest.rest; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.model.AccessControlProvider; +import org.apache.qpid.server.security.access.FileAccessControlProviderConstants; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestFileUtils; + +public class AccessControlProviderRestTest extends QpidRestTestCase +{ + private static final String ALLOWED_USER = "allowed"; + private static final String DENIED_USER = "denied"; + private static final String OTHER_USER = "other"; + + private String _aclFileContent1 = + "ACL ALLOW-LOG " + ALLOWED_USER + " ACCESS MANAGEMENT\n" + + "ACL ALLOW-LOG " + ALLOWED_USER + " CONFIGURE BROKER\n" + + "ACL DENY-LOG ALL ALL"; + + private String _aclFileContent2 = + "ACL ALLOW-LOG " + ALLOWED_USER + " ACCESS MANAGEMENT\n" + + "ACL ALLOW-LOG " + OTHER_USER + " ACCESS MANAGEMENT\n" + + "ACL ALLOW-LOG " + ALLOWED_USER + " CONFIGURE BROKER\n" + + "ACL DENY-LOG ALL ALL"; + + @Override + protected void customizeConfiguration() throws ConfigurationException, IOException + { + super.customizeConfiguration(); + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER, OTHER_USER); + + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, + "httpBasicAuthenticationEnabled", true); + } + + public void testCreateAccessControlProvider() throws Exception + { + String accessControlProviderName = getTestName(); + + //verify that the access control provider doesn't exist, and + //in doing so implicitly verify that the 'denied' user can + //actually currently connect because no ACL is in effect yet + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertAccessControlProviderExistence(accessControlProviderName, false); + + //create the access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + int responseCode = createAccessControlProvider(accessControlProviderName, _aclFileContent1); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + //verify it exists with the 'allowed' user + assertAccessControlProviderExistence(accessControlProviderName, true); + + //verify the 'denied' user can no longer access the management interface + //due to the just-created ACL file now preventing it + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName, false); + } + + public void testRemoveAccessControlProvider() throws Exception + { + String accessControlProviderName = getTestName(); + + //verify that the access control provider doesn't exist, and + //in doing so implicitly verify that the 'denied' user can + //actually currently connect because no ACL is in effect yet + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertAccessControlProviderExistence(accessControlProviderName, false); + + //create the access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + int responseCode = createAccessControlProvider(accessControlProviderName, _aclFileContent1); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + //verify it exists with the 'allowed' user + assertAccessControlProviderExistence(accessControlProviderName, true); + + //verify the 'denied' user can no longer access the management interface + //due to the just-created ACL file now preventing it + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName, false); + + //remove the access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + responseCode = getRestTestHelper().submitRequest("/rest/accesscontrolprovider/" + accessControlProviderName, "DELETE", null); + assertEquals("Access control provider deletion should be allowed", 200, responseCode); + assertAccessControlProviderExistence(accessControlProviderName, false); + + //verify it is gone again, using the 'denied' user to implicitly confirm it is + //now able to connect to the management interface again because the ACL was removed. + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertAccessControlProviderExistence(accessControlProviderName, false); + } + + public void testReplaceAccessControlProvider() throws Exception + { + String accessControlProviderName1 = getTestName() + "1"; + + //verify that the access control provider doesn't exist, and + //in doing so implicitly verify that the 'denied' user can + //actually currently connect because no ACL is in effect yet + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertAccessControlProviderExistence(accessControlProviderName1, false); + + //create the access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + int responseCode = createAccessControlProvider(accessControlProviderName1, _aclFileContent1); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + //verify it exists with the 'allowed' user + assertAccessControlProviderExistence(accessControlProviderName1, true); + + //verify the 'denied' and 'other' user can no longer access the management + //interface due to the just-created ACL file now preventing them + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName1, false); + getRestTestHelper().setUsernameAndPassword(OTHER_USER, OTHER_USER); + assertCanAccessManagementInterface(accessControlProviderName1, false); + + //create the replacement access control provider using the 'allowed' user. + String accessControlProviderName2 = getTestName() + "2"; + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + responseCode = createAccessControlProvider(accessControlProviderName2, _aclFileContent2); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + //Verify that it took effect immediately, replacing the first access control provider + + //verify the 'denied' user still can't access the management interface, but the 'other' user now CAN. + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName2, false); + getRestTestHelper().setUsernameAndPassword(OTHER_USER, OTHER_USER); + assertCanAccessManagementInterface(accessControlProviderName2, true); + + //remove the original access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + responseCode = getRestTestHelper().submitRequest("/rest/accesscontrolprovider/" + accessControlProviderName1, "DELETE", null); + assertEquals("Access control provider deletion should be allowed", 200, responseCode); + assertAccessControlProviderExistence(accessControlProviderName1, false); + + //verify the 'denied' user still can't access the management interface, the 'other' user still can, thus + //confirming that the second access control provider is still in effect + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName2, false); + getRestTestHelper().setUsernameAndPassword(OTHER_USER, OTHER_USER); + assertCanAccessManagementInterface(accessControlProviderName2, true); + } + + + public void testAddAndRemoveSecondAccessControlProviderReinstatesOriginal() throws Exception + { + String accessControlProviderName1 = getTestName() + "1"; + + //verify that the access control provider doesn't exist, and + //in doing so implicitly verify that the 'denied' user can + //actually currently connect because no ACL is in effect yet + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertAccessControlProviderExistence(accessControlProviderName1, false); + + //create the access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + int responseCode = createAccessControlProvider(accessControlProviderName1, _aclFileContent1); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + //verify it exists with the 'allowed' user + assertAccessControlProviderExistence(accessControlProviderName1, true); + + //verify the 'denied' and 'other' user can no longer access the management + //interface due to the just-created ACL file now preventing them + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName1, false); + getRestTestHelper().setUsernameAndPassword(OTHER_USER, OTHER_USER); + assertCanAccessManagementInterface(accessControlProviderName1, false); + + //create the replacement access control provider using the 'allowed' user. + String accessControlProviderName2 = getTestName() + "2"; + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + responseCode = createAccessControlProvider(accessControlProviderName2, _aclFileContent2); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + //Verify that it took effect immediately, replacing the first access control provider + + //verify the 'denied' user still can't access the management interface, but the 'other' user now CAN. + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName2, false); + getRestTestHelper().setUsernameAndPassword(OTHER_USER, OTHER_USER); + assertCanAccessManagementInterface(accessControlProviderName2, true); + + //remove the second access control provider using the 'allowed' user + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + responseCode = getRestTestHelper().submitRequest("/rest/accesscontrolprovider/" + accessControlProviderName2, "DELETE", null); + assertEquals("Access control provider deletion should be allowed", 200, responseCode); + assertAccessControlProviderExistence(accessControlProviderName2, false); + + //verify the 'denied' user still can't access the management interface, the + //'other' now CANT again, the 'allowed' still can, thus confirming that the + //first access control provider is now in effect once again + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + assertCanAccessManagementInterface(accessControlProviderName2, false); + getRestTestHelper().setUsernameAndPassword(OTHER_USER, OTHER_USER); + assertCanAccessManagementInterface(accessControlProviderName2, false); + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + assertCanAccessManagementInterface(accessControlProviderName2, true); + } + + private void assertCanAccessManagementInterface(String accessControlProviderName, boolean canAccess) throws Exception + { + int expected = canAccess ? 200 : 403; + int responseCode = getRestTestHelper().submitRequest("/rest/accesscontrolprovider/" + accessControlProviderName, "GET", null); + assertEquals("Unexpected response code", expected, responseCode); + } + + private void assertAccessControlProviderExistence(String accessControlProviderName, boolean exists) throws Exception + { + String path = "/rest/accesscontrolprovider/" + accessControlProviderName; + List<Map<String, Object>> providers = getRestTestHelper().getJsonAsList(path); + assertEquals("Unexpected result", exists, !providers.isEmpty()); + } + + private int createAccessControlProvider(String accessControlProviderName, String content) throws Exception + { + File file = TestFileUtils.createTempFile(this, ".acl", content); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AccessControlProvider.NAME, accessControlProviderName); + attributes.put(AccessControlProvider.TYPE, FileAccessControlProviderConstants.ACL_FILE_PROVIDER_TYPE); + attributes.put(FileAccessControlProviderConstants.PATH, file.getAbsoluteFile()); + + return getRestTestHelper().submitRequest("/rest/accesscontrolprovider/" + accessControlProviderName, "PUT", attributes); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AnonymousAccessRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AnonymousAccessRestTest.java new file mode 100644 index 0000000000..907b476bc4 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/AnonymousAccessRestTest.java @@ -0,0 +1,95 @@ +package org.apache.qpid.systest.rest; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.plugin.AuthenticationManagerFactory; +import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManagerFactory; +import org.apache.qpid.test.utils.TestBrokerConfiguration; + +public class AnonymousAccessRestTest extends QpidRestTestCase +{ + @Override + public void startBroker() + { + // prevent broker from starting in setUp + } + + public void startBrokerNow() throws Exception + { + super.startBroker(); + } + + @Override + protected void customizeConfiguration() throws ConfigurationException, IOException + { + super.customizeConfiguration(); + TestBrokerConfiguration config = getBrokerConfiguration(); + + Map<String, Object> anonymousAuthProviderAttributes = new HashMap<String, Object>(); + anonymousAuthProviderAttributes.put(AuthenticationManagerFactory.ATTRIBUTE_TYPE, AnonymousAuthenticationManagerFactory.PROVIDER_TYPE); + anonymousAuthProviderAttributes.put(AuthenticationProvider.NAME, TestBrokerConfiguration.ENTRY_NAME_ANONYMOUS_PROVIDER); + config.addAuthenticationProviderConfiguration(anonymousAuthProviderAttributes); + + // set anonymous authentication provider on http port for the tests + config.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.AUTHENTICATION_PROVIDER, + TestBrokerConfiguration.ENTRY_NAME_ANONYMOUS_PROVIDER); + config.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, "httpBasicAuthenticationEnabled", false); + + // reset credentials + getRestTestHelper().setUsernameAndPassword(null, null); + } + + public void testGetWithAnonymousProvider() throws Exception + { + startBrokerNow(); + + Map<String, Object> brokerDetails = getRestTestHelper().getJsonAsSingletonList("/rest/broker"); + assertNotNull("Unexpected broker attributes", brokerDetails); + assertNotNull("Unexpected value of attribute " + Broker.ID, brokerDetails.get(Broker.ID)); + } + + public void testPutAnonymousProvider() throws Exception + { + startBrokerNow(); + + Map<String, Object> brokerAttributes = new HashMap<String, Object>(); + brokerAttributes.put(Broker.DEFAULT_VIRTUAL_HOST, TEST3_VIRTUALHOST); + + int response = getRestTestHelper().submitRequest("/rest/broker", "PUT", brokerAttributes); + assertEquals("Unexpected update response", 200, response); + + Map<String, Object> brokerDetails = getRestTestHelper().getJsonAsSingletonList("/rest/broker"); + assertNotNull("Unexpected broker attributes", brokerDetails); + assertNotNull("Unexpected value of attribute " + Broker.ID, brokerDetails.get(Broker.ID)); + assertEquals("Unexpected default virtual host", TEST3_VIRTUALHOST, brokerDetails.get(Broker.DEFAULT_VIRTUAL_HOST)); + } + + public void testGetWithPasswordAuthProvider() throws Exception + { + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.AUTHENTICATION_PROVIDER, + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + startBrokerNow(); + + int response = getRestTestHelper().submitRequest("/rest/broker", "GET", null); + assertEquals("Anonymous access should be denied", 401, response); + } + + public void testPutWithPasswordAuthProvider() throws Exception + { + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.AUTHENTICATION_PROVIDER, + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + startBrokerNow(); + + Map<String, Object> brokerAttributes = new HashMap<String, Object>(); + brokerAttributes.put(Broker.DEFAULT_VIRTUAL_HOST, TEST3_VIRTUALHOST); + + int response = getRestTestHelper().submitRequest("/rest/broker", "PUT", brokerAttributes); + assertEquals("Anonymous access should be denied", 401, response); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/Asserts.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/Asserts.java index e20db6a6ac..6f795cc61d 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/Asserts.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/Asserts.java @@ -207,14 +207,14 @@ public class Asserts } if (isAMQPPort) { - assertAttributesPresent(port, Port.AVAILABLE_ATTRIBUTES, Port.CREATED, Port.UPDATED, Port.AUTHENTICATION_PROVIDER); + assertAttributesPresent(port, Port.AVAILABLE_ATTRIBUTES, Port.CREATED, Port.UPDATED, Port.AUTHENTICATION_PROVIDER, Port.KEY_STORE, Port.TRUST_STORES); assertNotNull("Unexpected value of attribute " + Port.BINDING_ADDRESS, port.get(Port.BINDING_ADDRESS)); } else { assertAttributesPresent(port, Port.AVAILABLE_ATTRIBUTES, Port.CREATED, Port.UPDATED, Port.AUTHENTICATION_PROVIDER, Port.BINDING_ADDRESS, Port.TCP_NO_DELAY, Port.SEND_BUFFER_SIZE, Port.RECEIVE_BUFFER_SIZE, - Port.NEED_CLIENT_AUTH, Port.WANT_CLIENT_AUTH); + Port.NEED_CLIENT_AUTH, Port.WANT_CLIENT_AUTH, Port.KEY_STORE, Port.TRUST_STORES); } @SuppressWarnings("unchecked") diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/BasicAuthRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/BasicAuthRestTest.java index 0574b6cc24..ea63cc7f4e 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/BasicAuthRestTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/BasicAuthRestTest.java @@ -58,6 +58,8 @@ public class BasicAuthRestTest extends QpidRestTestCase if (useSsl) { getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.PROTOCOLS, Collections.singleton(Protocol.HTTPS)); + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + } super.customizeConfiguration(); } @@ -68,7 +70,7 @@ public class BasicAuthRestTest extends QpidRestTestCase assertEquals(responseCode, conn.getResponseCode()); } - public void testDefaultEnabledWithHttps() throws Exception + public void testBasicAuthWhenEnabledWithHttps() throws Exception { configure(true); super.setUp(); @@ -81,15 +83,16 @@ public class BasicAuthRestTest extends QpidRestTestCase verifyGetBrokerAttempt(HttpServletResponse.SC_OK); } - public void testDefaultDisabledWithHttp() throws Exception + public void testBasicAuthWhenDisabledWithHttp() throws Exception { configure(false); + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, "httpBasicAuthenticationEnabled", false); super.setUp(); // Try the attempt with authentication, it should fail because // BASIC auth is disabled by default on non-secure connections. getRestTestHelper().setUsernameAndPassword(USERNAME, USERNAME); - verifyGetBrokerAttempt(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + verifyGetBrokerAttempt(HttpServletResponse.SC_UNAUTHORIZED); } public void testEnablingForHttp() throws Exception @@ -116,6 +119,6 @@ public class BasicAuthRestTest extends QpidRestTestCase // Try the attempt with authentication, it should fail because // BASIC auth is now disabled on secure connections. getRestTestHelper().setUsernameAndPassword(USERNAME, USERNAME); - verifyGetBrokerAttempt(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + verifyGetBrokerAttempt(HttpServletResponse.SC_UNAUTHORIZED); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestHttpsTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestHttpsTest.java index 06927946ba..da38b9be33 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestHttpsTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestHttpsTest.java @@ -54,6 +54,7 @@ public class BrokerRestHttpsTest extends QpidRestTestCase Map<String, Object> newAttributes = new HashMap<String, Object>(); newAttributes.put(Port.PROTOCOLS, Collections.singleton(Protocol.HTTPS)); newAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + newAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); getBrokerConfiguration().setObjectAttributes(TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT,newAttributes); } @@ -62,8 +63,6 @@ public class BrokerRestHttpsTest extends QpidRestTestCase Map<String, Object> brokerDetails = getRestTestHelper().getJsonAsSingletonList("/rest/broker"); Asserts.assertAttributesPresent(brokerDetails, Broker.AVAILABLE_ATTRIBUTES, Broker.BYTES_RETAINED, - Broker.PROCESS_PID, Broker.SUPPORTED_STORE_TYPES, Broker.CREATED, Broker.TIME_TO_LIVE, Broker.UPDATED, - Broker.ACL_FILE, Broker.KEY_STORE_CERT_ALIAS, Broker.TRUST_STORE_PATH, Broker.TRUST_STORE_PASSWORD, - Broker.GROUP_FILE, Broker.PEER_STORE_PATH, Broker.PEER_STORE_PASSWORD); + Broker.PROCESS_PID, Broker.SUPPORTED_VIRTUALHOST_STORE_TYPES, Broker.CREATED, Broker.TIME_TO_LIVE, Broker.UPDATED); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestTest.java index a795063750..0d40eca745 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/BrokerRestTest.java @@ -20,7 +20,6 @@ */ package org.apache.qpid.systest.rest; -import java.io.File; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; @@ -29,18 +28,18 @@ import java.util.List; import java.util.Map; import org.apache.qpid.common.QpidProperties; +import org.apache.qpid.server.configuration.BrokerConfigurationStoreCreator; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.LifetimePolicy; import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.State; import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.store.MessageStoreCreator; import org.apache.qpid.test.utils.QpidTestCase; import org.apache.qpid.test.utils.TestBrokerConfiguration; -import org.apache.qpid.test.utils.TestSSLConstants; public class BrokerRestTest extends QpidRestTestCase { - private static final String BROKER_AUTHENTICATIONPROVIDERS_ATTRIBUTE = "authenticationproviders"; private static final String BROKER_PORTS_ATTRIBUTE = "ports"; private static final String BROKER_VIRTUALHOSTS_ATTRIBUTE = "virtualhosts"; @@ -127,7 +126,6 @@ public class BrokerRestTest extends QpidRestTestCase public void testPutToUpdateWithInvalidAttributeValues() throws Exception { Map<String, Object> invalidAttributes = new HashMap<String, Object>(); - invalidAttributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, "non-existing-provider"); invalidAttributes.put(Broker.DEFAULT_VIRTUAL_HOST, "non-existing-host"); invalidAttributes.put(Broker.QUEUE_ALERT_THRESHOLD_MESSAGE_AGE, -1000); invalidAttributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, -2000); @@ -141,15 +139,6 @@ public class BrokerRestTest extends QpidRestTestCase invalidAttributes.put(Broker.CONNECTION_SESSION_COUNT_LIMIT, -10); invalidAttributes.put(Broker.CONNECTION_HEART_BEAT_DELAY, -11000); invalidAttributes.put(Broker.STATISTICS_REPORTING_PERIOD, -12000); - invalidAttributes.put(Broker.ACL_FILE, QpidTestCase.QPID_HOME + File.separator + "etc" + File.separator + "non-existing-acl.acl"); - invalidAttributes.put(Broker.KEY_STORE_PATH, QpidTestCase.QPID_HOME + File.separator + "etc" + File.separator + "non-existing-keystore.jks"); - invalidAttributes.put(Broker.KEY_STORE_PASSWORD, "password1"); - invalidAttributes.put(Broker.KEY_STORE_CERT_ALIAS, "java-broker1"); - invalidAttributes.put(Broker.TRUST_STORE_PATH, QpidTestCase.QPID_HOME + File.separator + "etc" + File.separator + "non-existing-truststore.jks"); - invalidAttributes.put(Broker.TRUST_STORE_PASSWORD, "password2"); - invalidAttributes.put(Broker.PEER_STORE_PATH, QpidTestCase.QPID_HOME + File.separator + "etc" + File.separator + "non-existing-peerstore.jks"); - invalidAttributes.put(Broker.PEER_STORE_PASSWORD, "password3"); - invalidAttributes.put(Broker.GROUP_FILE, QpidTestCase.QPID_HOME + File.separator + "etc" + File.separator + "groups-non-existing"); invalidAttributes.put(Broker.VIRTUALHOST_STORE_TRANSACTION_IDLE_TIMEOUT_CLOSE, -13000); invalidAttributes.put(Broker.VIRTUALHOST_STORE_TRANSACTION_IDLE_TIMEOUT_WARN, -14000); invalidAttributes.put(Broker.VIRTUALHOST_STORE_TRANSACTION_OPEN_TIMEOUT_CLOSE, -15000); @@ -174,7 +163,6 @@ public class BrokerRestTest extends QpidRestTestCase private Map<String, Object> getValidBrokerAttributes() { Map<String, Object> brokerAttributes = new HashMap<String, Object>(); - brokerAttributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, ANONYMOUS_AUTHENTICATION_PROVIDER); brokerAttributes.put(Broker.DEFAULT_VIRTUAL_HOST, TEST3_VIRTUALHOST); brokerAttributes.put(Broker.QUEUE_ALERT_THRESHOLD_MESSAGE_AGE, 1000); brokerAttributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 2000); @@ -190,15 +178,6 @@ public class BrokerRestTest extends QpidRestTestCase brokerAttributes.put(Broker.CONNECTION_HEART_BEAT_DELAY, 11000); brokerAttributes.put(Broker.STATISTICS_REPORTING_PERIOD, 12000); brokerAttributes.put(Broker.STATISTICS_REPORTING_RESET_ENABLED, true); - brokerAttributes.put(Broker.ACL_FILE, QpidTestCase.QPID_HOME + File.separator + "etc" + File.separator + "broker_example.acl"); - brokerAttributes.put(Broker.KEY_STORE_PATH, TestSSLConstants.BROKER_KEYSTORE); - brokerAttributes.put(Broker.KEY_STORE_PASSWORD, TestSSLConstants.BROKER_KEYSTORE_PASSWORD); - brokerAttributes.put(Broker.KEY_STORE_CERT_ALIAS, "java-broker"); - brokerAttributes.put(Broker.TRUST_STORE_PATH, TestSSLConstants.TRUSTSTORE); - brokerAttributes.put(Broker.TRUST_STORE_PASSWORD, TestSSLConstants.TRUSTSTORE_PASSWORD); - brokerAttributes.put(Broker.PEER_STORE_PATH, TestSSLConstants.TRUSTSTORE); - brokerAttributes.put(Broker.PEER_STORE_PASSWORD, TestSSLConstants.TRUSTSTORE_PASSWORD); - brokerAttributes.put(Broker.GROUP_FILE, QpidTestCase.QPID_HOME + File.separator + "etc" + File.separator + "groups"); brokerAttributes.put(Broker.VIRTUALHOST_STORE_TRANSACTION_IDLE_TIMEOUT_CLOSE, 13000); brokerAttributes.put(Broker.VIRTUALHOST_STORE_TRANSACTION_IDLE_TIMEOUT_WARN, 14000); brokerAttributes.put(Broker.VIRTUALHOST_STORE_TRANSACTION_OPEN_TIMEOUT_CLOSE, 15000); @@ -212,10 +191,7 @@ public class BrokerRestTest extends QpidRestTestCase { String attributeName = entry.getKey(); Object attributeValue = entry.getValue(); - if (attributeName.equals(Broker.KEY_STORE_PASSWORD) || attributeName.equals(Broker.TRUST_STORE_PASSWORD) || attributeName.equals(Broker.PEER_STORE_PASSWORD)) - { - attributeValue = "********"; - } + Object currentValue = actualAttributes.get(attributeName); assertEquals("Unexpected attribute " + attributeName + " value:", attributeValue, currentValue); } @@ -224,11 +200,8 @@ public class BrokerRestTest extends QpidRestTestCase protected void assertBrokerAttributes(Map<String, Object> brokerDetails) { Asserts.assertAttributesPresent(brokerDetails, Broker.AVAILABLE_ATTRIBUTES, - Broker.BYTES_RETAINED, Broker.PROCESS_PID, Broker.SUPPORTED_STORE_TYPES, - Broker.CREATED, Broker.TIME_TO_LIVE, Broker.UPDATED, Broker.ACL_FILE, - Broker.KEY_STORE_PATH, Broker.KEY_STORE_PASSWORD, Broker.KEY_STORE_CERT_ALIAS, - Broker.TRUST_STORE_PATH, Broker.TRUST_STORE_PASSWORD, Broker.GROUP_FILE, - Broker.PEER_STORE_PATH, Broker.PEER_STORE_PASSWORD); + Broker.BYTES_RETAINED, Broker.PROCESS_PID, + Broker.CREATED, Broker.TIME_TO_LIVE, Broker.UPDATED); assertEquals("Unexpected value of attribute " + Broker.BUILD_VERSION, QpidProperties.getBuildVersion(), brokerDetails.get(Broker.BUILD_VERSION)); @@ -243,7 +216,7 @@ public class BrokerRestTest extends QpidRestTestCase assertEquals("Unexpected value of attribute " + Broker.DURABLE, Boolean.TRUE, brokerDetails.get(Broker.DURABLE)); assertEquals("Unexpected value of attribute " + Broker.LIFETIME_POLICY, LifetimePolicy.PERMANENT.name(), brokerDetails.get(Broker.LIFETIME_POLICY)); - assertEquals("Unexpected value of attribute " + Broker.NAME, "QpidBroker", brokerDetails.get(Broker.NAME)); + assertEquals("Unexpected value of attribute " + Broker.NAME, "Broker", brokerDetails.get(Broker.NAME)); assertEquals("Unexpected value of attribute " + Broker.STATE, State.ACTIVE.name(), brokerDetails.get(Broker.STATE)); assertNotNull("Unexpected value of attribute " + Broker.ID, brokerDetails.get(Broker.ID)); @@ -251,6 +224,16 @@ public class BrokerRestTest extends QpidRestTestCase assertNotNull("Unexpected value of attribute virtualhosts", brokerDetails.get(BROKER_VIRTUALHOSTS_ATTRIBUTE)); assertNotNull("Unexpected value of attribute ports", brokerDetails.get(BROKER_PORTS_ATTRIBUTE)); assertNotNull("Unexpected value of attribute authenticationproviders", brokerDetails.get(BROKER_AUTHENTICATIONPROVIDERS_ATTRIBUTE)); + + @SuppressWarnings("unchecked") + Collection<String> supportedBrokerStoreTypes = (Collection<String>)brokerDetails.get(Broker.SUPPORTED_BROKER_STORE_TYPES); + Collection<String> expectedSupportedBrokerStoreTypes = new BrokerConfigurationStoreCreator().getStoreTypes(); + assertEquals("Unexpected supported broker store types", new HashSet<String>(expectedSupportedBrokerStoreTypes), new HashSet<String>(supportedBrokerStoreTypes)); + + @SuppressWarnings("unchecked") + Collection<String> supportedVirtualHostStoreTypes = (Collection<String>)brokerDetails.get(Broker.SUPPORTED_VIRTUALHOST_STORE_TYPES); + Collection<String> expectedSupportedVirtualHostStoreTypes = new MessageStoreCreator().getStoreTypes(); + assertEquals("Unexpected supported virtual host store types", new HashSet<String>(expectedSupportedVirtualHostStoreTypes), new HashSet<String>(supportedVirtualHostStoreTypes)); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupProviderRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupProviderRestTest.java index 861bf8cb71..ef8e12a929 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupProviderRestTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupProviderRestTest.java @@ -22,20 +22,23 @@ package org.apache.qpid.systest.rest; import java.io.File; import java.io.FileOutputStream; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; -import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.Group; import org.apache.qpid.server.model.GroupProvider; import org.apache.qpid.server.model.LifetimePolicy; import org.apache.qpid.server.model.State; import org.apache.qpid.server.model.UUIDGenerator; +import org.apache.qpid.server.security.group.FileGroupManagerFactory; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestFileUtils; public class GroupProviderRestTest extends QpidRestTestCase { - private static final String FILE_GROUP_MANAGER = "FileGroupManager"; + private static final String FILE_GROUP_MANAGER = TestBrokerConfiguration.ENTRY_NAME_GROUP_FILE; private File _groupFile; @Override @@ -43,7 +46,7 @@ public class GroupProviderRestTest extends QpidRestTestCase { _groupFile = createTemporaryGroupFile(); - getBrokerConfiguration().setBrokerAttribute(Broker.GROUP_FILE, _groupFile.getAbsolutePath()); + getBrokerConfiguration().addGroupFileConfiguration(_groupFile.getAbsolutePath()); super.setUp(); } @@ -69,11 +72,11 @@ public class GroupProviderRestTest extends QpidRestTestCase assertEquals("Unexpected number of providers", 1, providerDetails.size()); for (Map<String, Object> provider : providerDetails) { - assertProvider(FILE_GROUP_MANAGER, provider); + assertProvider(FILE_GROUP_MANAGER, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE, provider); Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + provider.get(GroupProvider.NAME)); assertNotNull("Cannot load data for " + provider.get(GroupProvider.NAME), data); - assertProvider(FILE_GROUP_MANAGER, data); + assertProvider(FILE_GROUP_MANAGER, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE, data); } } @@ -111,8 +114,178 @@ public class GroupProviderRestTest extends QpidRestTestCase getRestTestHelper().assertNumberOfGroups(data, 0); } + public void testCreateNewFileGroupProviderFromExistingGroupFile() throws Exception + { + String[] groupMemberNames = {"test1","test2"}; + File groupFile = TestFileUtils.createTempFile(this, ".groups", "testusers.users=" + groupMemberNames[0] + "," + groupMemberNames[1]); + try + { + String providerName = getTestName(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, providerName); + attributes.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileGroupManagerFactory.PATH, groupFile.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("/rest/groupprovider/" + providerName, "PUT", attributes); + assertEquals("Group provider was not created", 201, responseCode); + + Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + providerName + "?depth=2"); + assertProvider(providerName, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE, data); + assertEquals("Unexpected name", providerName, data.get(GroupProvider.NAME)); + assertEquals("Unexpected path", groupFile.getAbsolutePath(), data.get(FileGroupManagerFactory.PATH)); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> groups = (List<Map<String, Object>>) data.get("groups"); + assertEquals("Unexpected group size", 1, groups.size()); + Map<String, Object> group = groups.get(0); + assertEquals("Unexpected group name", "testusers",group.get("name")); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> groupMemberList = (List<Map<String, Object>>) group.get("groupmembers"); + assertEquals("Unexpected group members size", 2, groupMemberList.size()); + + for (String memberName : groupMemberNames) + { + boolean found = false; + for (Map<String, Object> memberData : groupMemberList) + { + Object name = memberData.get("name"); + if (memberName.equals(name)) + { + found = true; + break; + } + } + assertTrue("Cannot find group member " + memberName + " in " + groupMemberList , found); + } + } + finally + { + groupFile.delete(); + } + } + + public void testCreationOfNewFileGroupProviderFailsWhenPathIsMissed() throws Exception + { + String providerName = getTestName(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, providerName); + attributes.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + + int responseCode = getRestTestHelper().submitRequest("/rest/groupprovider/" + providerName, "PUT", attributes); + assertEquals("Group provider was created", 409, responseCode); + } + + public void testCreateNewFileGroupProviderFromNonExistingGroupFile() throws Exception + { + File groupFile = new File(TMP_FOLDER + File.separator + getTestName() + File.separator + "groups"); + assertFalse("Group file should not exist", groupFile.exists()); + try + { + String providerName = getTestName(); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, providerName); + attributes.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileGroupManagerFactory.PATH, groupFile.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("/rest/groupprovider/" + providerName, "PUT", attributes); + assertEquals("Group provider was not created", 201, responseCode); + + Map<String, Object> data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + providerName); + assertEquals("Unexpected name", providerName, data.get(GroupProvider.NAME)); + assertEquals("Unexpected path", groupFile.getAbsolutePath(), data.get(FileGroupManagerFactory.PATH)); + + @SuppressWarnings("unchecked") + List<Map<String, Object>> groups = (List<Map<String, Object>>) data.get("groups"); + assertNull("Unexpected groups", groups); + + assertTrue("Group file has not been created", groupFile.exists()); + } + finally + { + groupFile.delete(); + groupFile.getParentFile().delete(); + } + } + + public void testCreateNewFileGroupProviderForTheSameGroupFileFails() throws Exception + { + File groupFile = TestFileUtils.createTempFile(this, ".groups", "testusers.users=test1,test2"); + String providerName = getTestName(); + try + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, providerName); + attributes.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileGroupManagerFactory.PATH, groupFile.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("/rest/groupprovider/" + providerName, "PUT", attributes); + assertEquals("Group provider was not created", 201, responseCode); + + attributes.put(GroupProvider.NAME, providerName + 2); + responseCode = getRestTestHelper().submitRequest("/rest/groupprovider/" + providerName + 2, "PUT", attributes); + assertEquals("Group provider for the same group file was created", 409, responseCode); + } + finally + { + groupFile.delete(); + } + } + + public void testDeleteGroupProvider() throws Exception + { + File groupFile = TestFileUtils.createTempFile(this, ".groups", "testusers.users=test1,test2"); + String providerName = getTestName(); + try + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, providerName); + attributes.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileGroupManagerFactory.PATH, groupFile.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("/rest/groupprovider/" + providerName, "PUT", attributes); + assertEquals("Expected to fail because we can have only one password provider", 201, responseCode); + + responseCode = getRestTestHelper().submitRequest("/rest/groupprovider/" + providerName , "DELETE", null); + assertEquals("Group provider was not deleted", 200, responseCode); + + List<Map<String, Object>> providerDetails = getRestTestHelper().getJsonAsList("/rest/groupprovider/" + providerName); + assertEquals("Provider was not deleted", 0, providerDetails.size()); + assertFalse("Groups file should be deleted", groupFile.exists()); + } + finally + { + groupFile.delete(); + } + } + + public void testUpdateGroupProviderAttributesFails() throws Exception + { + File groupFile = TestFileUtils.createTempFile(this, ".groups", "testusers.users=test1,test2"); + String providerName = getTestName(); + try + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, providerName); + attributes.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileGroupManagerFactory.PATH, groupFile.getAbsolutePath()); + + int responseCode = getRestTestHelper().submitRequest("/rest/groupprovider/" + providerName, "PUT", attributes); + assertEquals("Expected to fail because we can have only one password provider", 201, responseCode); + + File newGroupFile = new File(TMP_FOLDER + File.separator + getTestName() + File.separator + "groups"); + attributes.put(FileGroupManagerFactory.PATH, newGroupFile.getAbsolutePath()); + + responseCode = getRestTestHelper().submitRequest("/rest/groupprovider/" + providerName, "PUT", attributes); + assertEquals("Expected to fail because we can have only one password provider", 409, responseCode); + } + finally + { + groupFile.delete(); + } + } - private void assertProvider(String type, Map<String, Object> provider) + private void assertProvider(String name, String type, Map<String, Object> provider) { Asserts.assertAttributesPresent(provider, GroupProvider.AVAILABLE_ATTRIBUTES, GroupProvider.CREATED, GroupProvider.UPDATED, GroupProvider.DESCRIPTION, @@ -126,9 +299,8 @@ public class GroupProviderRestTest extends QpidRestTestCase assertEquals("Unexpected value of provider attribute " + GroupProvider.TYPE, type, provider.get(GroupProvider.TYPE)); - final String name = (String) provider.get(GroupProvider.NAME); - assertEquals("Unexpected value of provider attribute " + GroupProvider.NAME, type, - name); + assertEquals("Unexpected value of provider attribute " + GroupProvider.NAME, name, + (String) provider.get(GroupProvider.NAME)); @SuppressWarnings("unchecked") List<Map<String, Object>> groups = (List<Map<String, Object>>) provider.get("groups"); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupRestTest.java index d3f93cc0fe..67e50b1bd1 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupRestTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/GroupRestTest.java @@ -26,13 +26,13 @@ import java.util.List; import java.util.Map; import java.util.Properties; -import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.GroupMember; +import org.apache.qpid.test.utils.TestBrokerConfiguration; public class GroupRestTest extends QpidRestTestCase { private static final String GROUP_NAME = "myGroup"; - private static final String FILE_GROUP_MANAGER = "FileGroupManager"; + private static final String FILE_GROUP_MANAGER = TestBrokerConfiguration.ENTRY_NAME_GROUP_FILE; private static final String EXISTING_MEMBER = "user1"; private static final String NEW_MEMBER = "user2"; @@ -43,7 +43,7 @@ public class GroupRestTest extends QpidRestTestCase { _groupFile = createTemporaryGroupFile(); - getBrokerConfiguration().setBrokerAttribute(Broker.GROUP_FILE, _groupFile.getAbsolutePath()); + getBrokerConfiguration().addGroupFileConfiguration(_groupFile.getAbsolutePath()); super.setUp(); } @@ -64,7 +64,7 @@ public class GroupRestTest extends QpidRestTestCase public void testGet() throws Exception { - Map<String, Object> group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + Map<String, Object> group = getRestTestHelper().getJsonAsSingletonList("/rest/group/" + FILE_GROUP_MANAGER + "/myGroup"); List<Map<String, Object>> groupmembers = (List<Map<String, Object>>) group.get("groupmembers"); assertEquals(1, groupmembers.size()); @@ -74,23 +74,23 @@ public class GroupRestTest extends QpidRestTestCase public void testCreateNewMemberOfGroup() throws Exception { - Map<String, Object> group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + Map<String, Object> group = getRestTestHelper().getJsonAsSingletonList("/rest/group/" + FILE_GROUP_MANAGER + "/myGroup"); getRestTestHelper().assertNumberOfGroupMembers(group, 1); getRestTestHelper().createNewGroupMember(FILE_GROUP_MANAGER, GROUP_NAME, NEW_MEMBER); - group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + group = getRestTestHelper().getJsonAsSingletonList("/rest/group/" + FILE_GROUP_MANAGER + "/myGroup"); getRestTestHelper().assertNumberOfGroupMembers(group, 2); } public void testRemoveMemberFromGroup() throws Exception { - Map<String, Object> group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + Map<String, Object> group = getRestTestHelper().getJsonAsSingletonList("/rest/group/" + FILE_GROUP_MANAGER + "/myGroup"); getRestTestHelper().assertNumberOfGroupMembers(group, 1); getRestTestHelper().removeMemberFromGroup(FILE_GROUP_MANAGER, GROUP_NAME, EXISTING_MEMBER); - group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + group = getRestTestHelper().getJsonAsSingletonList("/rest/group/" + FILE_GROUP_MANAGER + "/myGroup"); getRestTestHelper().assertNumberOfGroupMembers(group, 0); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/KeyStoreRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/KeyStoreRestTest.java new file mode 100644 index 0000000000..c38d9bb396 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/KeyStoreRestTest.java @@ -0,0 +1,270 @@ +/* + * + * 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.systest.rest; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.model.KeyStore; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Transport; +import org.apache.qpid.server.model.adapter.AbstractKeyStoreAdapter; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestSSLConstants; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; + +public class KeyStoreRestTest extends QpidRestTestCase +{ + @Override + public void setUp() throws Exception + { + // not calling super.setUp() to avoid broker start-up until + // after any necessary configuration + } + + public void testGet() throws Exception + { + super.setUp(); + + //verify existence of the default keystore used by the systests + List<Map<String, Object>> keyStores = assertNumberOfKeyStores(1); + + Map<String, Object> keystore = keyStores.get(0); + assertKeyStoreAttributes(keystore, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE, + System.getProperty(QPID_HOME) + "/../" + TestSSLConstants.BROKER_KEYSTORE, null); + } + + public void testCreate() throws Exception + { + super.setUp(); + + String name = getTestName(); + String certAlias = "app2"; + + assertNumberOfKeyStores(1); + createKeyStore(name, certAlias); + assertNumberOfKeyStores(2); + + List<Map<String, Object>> keyStores = getRestTestHelper().getJsonAsList("/rest/keystore/" + name); + assertNotNull("details cannot be null", keyStores); + + assertKeyStoreAttributes(keyStores.get(0), name, TestSSLConstants.KEYSTORE, certAlias); + } + + public void testDelete() throws Exception + { + super.setUp(); + + String name = getTestName(); + String certAlias = "app2"; + + assertNumberOfKeyStores(1); + createKeyStore(name, certAlias); + assertNumberOfKeyStores(2); + + int responseCode = getRestTestHelper().submitRequest("/rest/keystore/" + name , "DELETE", null); + assertEquals("Unexpected response code for provider deletion", 200, responseCode); + + List<Map<String, Object>> keyStore = getRestTestHelper().getJsonAsList("/rest/keystore/" + name); + assertNotNull("details should not be null", keyStore); + assertTrue("details should be empty as the keystore no longer exists", keyStore.isEmpty()); + + //check only the default systests key store remains + List<Map<String, Object>> keyStores = assertNumberOfKeyStores(1); + Map<String, Object> keystore = keyStores.get(0); + assertKeyStoreAttributes(keystore, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE, + System.getProperty(QPID_HOME) + "/../" + TestSSLConstants.BROKER_KEYSTORE, null); + } + + public void testDeleteFailsWhenKeyStoreInUse() throws Exception + { + String name = "testDeleteFailsWhenKeyStoreInUse"; + + //add a new key store config to use + Map<String, Object> sslKeyStoreAttributes = new HashMap<String, Object>(); + sslKeyStoreAttributes.put(KeyStore.NAME, name); + sslKeyStoreAttributes.put(KeyStore.PATH, TestSSLConstants.BROKER_KEYSTORE); + sslKeyStoreAttributes.put(KeyStore.PASSWORD, TestSSLConstants.BROKER_KEYSTORE_PASSWORD); + getBrokerConfiguration().addKeyStoreConfiguration(sslKeyStoreAttributes); + + //add the SSL port using it + Map<String, Object> sslPortAttributes = new HashMap<String, Object>(); + sslPortAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + sslPortAttributes.put(Port.PORT, DEFAULT_SSL_PORT); + sslPortAttributes.put(Port.NAME, TestBrokerConfiguration.ENTRY_NAME_SSL_PORT); + sslPortAttributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + sslPortAttributes.put(Port.KEY_STORE, name); + getBrokerConfiguration().addPortConfiguration(sslPortAttributes); + + super.setUp(); + + //verify the keystore is there + assertNumberOfKeyStores(2); + + List<Map<String, Object>> keyStore = getRestTestHelper().getJsonAsList("/rest/keystore/" + name); + assertNotNull("details should not be null", keyStore); + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.BROKER_KEYSTORE, null); + + //try to delete it, which should fail as it is in use + int responseCode = getRestTestHelper().submitRequest("/rest/keystore/" + name , "DELETE", null); + assertEquals("Unexpected response code for provider deletion", 409, responseCode); + + //check its still there + assertNumberOfKeyStores(2); + keyStore = getRestTestHelper().getJsonAsList("/rest/keystore/" + name); + assertNotNull("details should not be null", keyStore); + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.BROKER_KEYSTORE, null); + } + + public void testUpdateWithGoodPathSucceeds() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfKeyStores(1); + createKeyStore(name, null); + assertNumberOfKeyStores(2); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(KeyStore.NAME, name); + attributes.put(KeyStore.PATH, TestSSLConstants.UNTRUSTED_KEYSTORE); + + int responseCode = getRestTestHelper().submitRequest("/rest/keystore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for keystore update", 200, responseCode); + + List<Map<String, Object>> keyStore = getRestTestHelper().getJsonAsList("/rest/keystore/" + name); + assertNotNull("details should not be null", keyStore); + + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.UNTRUSTED_KEYSTORE, null); + } + + public void testUpdateWithNonExistentPathFails() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfKeyStores(1); + createKeyStore(name, null); + assertNumberOfKeyStores(2); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(KeyStore.NAME, name); + attributes.put(KeyStore.PATH, "does.not.exist"); + + int responseCode = getRestTestHelper().submitRequest("/rest/keystore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for keystore update", 409, responseCode); + + List<Map<String, Object>> keyStore = getRestTestHelper().getJsonAsList("/rest/keystore/" + name); + assertNotNull("details should not be null", keyStore); + + //verify the details remain unchanged + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.KEYSTORE, null); + } + + public void testUpdateCertificateAlias() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfKeyStores(1); + createKeyStore(name, "app1"); + assertNumberOfKeyStores(2); + + List<Map<String, Object>> keyStore = getRestTestHelper().getJsonAsList("/rest/keystore/" + name); + assertNotNull("details should not be null", keyStore); + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.KEYSTORE, "app1"); + + //Update the certAlias from app1 to app2 + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(KeyStore.NAME, name); + attributes.put(KeyStore.CERTIFICATE_ALIAS, "app2"); + + int responseCode = getRestTestHelper().submitRequest("/rest/keystore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for keystore update", 200, responseCode); + + keyStore = getRestTestHelper().getJsonAsList("/rest/keystore/" + name); + assertNotNull("details should not be null", keyStore); + + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.KEYSTORE, "app2"); + + //Update the certAlias to clear it (i.e go from from app1 to null) + attributes = new HashMap<String, Object>(); + attributes.put(KeyStore.NAME, name); + attributes.put(KeyStore.CERTIFICATE_ALIAS, null); + + responseCode = getRestTestHelper().submitRequest("/rest/keystore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for keystore update", 200, responseCode); + + keyStore = getRestTestHelper().getJsonAsList("/rest/keystore/" + name); + assertNotNull("details should not be null", keyStore); + + assertKeyStoreAttributes(keyStore.get(0), name, TestSSLConstants.KEYSTORE, null); + } + + private List<Map<String, Object>> assertNumberOfKeyStores(int numberOfKeystores) throws IOException, + JsonParseException, JsonMappingException + { + List<Map<String, Object>> keyStores = getRestTestHelper().getJsonAsList("/rest/keystore"); + assertNotNull("keystores should not be null", keyStores); + assertEquals("Unexpected number of keystores", numberOfKeystores, keyStores.size()); + + return keyStores; + } + + private void createKeyStore(String name, String certAlias) throws IOException, JsonGenerationException, JsonMappingException + { + Map<String, Object> keyStoreAttributes = new HashMap<String, Object>(); + keyStoreAttributes.put(KeyStore.NAME, name); + keyStoreAttributes.put(KeyStore.PATH, TestSSLConstants.KEYSTORE); + keyStoreAttributes.put(KeyStore.PASSWORD, TestSSLConstants.KEYSTORE_PASSWORD); + keyStoreAttributes.put(KeyStore.CERTIFICATE_ALIAS, certAlias); + + int responseCode = getRestTestHelper().submitRequest("/rest/keystore/" + name, "PUT", keyStoreAttributes); + assertEquals("Unexpected response code", 201, responseCode); + } + + private void assertKeyStoreAttributes(Map<String, Object> keystore, String name, String path, String certAlias) + { + assertEquals("default systests key store is missing", + name, keystore.get(KeyStore.NAME)); + assertEquals("unexpected path to key store", + path, keystore.get(KeyStore.PATH)); + assertEquals("unexpected (dummy) password of default systests key store", + AbstractKeyStoreAdapter.DUMMY_PASSWORD_MASK, keystore.get(KeyStore.PASSWORD)); + assertEquals("unexpected type of default systests key store", + java.security.KeyStore.getDefaultType(), keystore.get(KeyStore.TYPE)); + assertEquals("unexpected certificateAlias value", + certAlias, keystore.get(KeyStore.CERTIFICATE_ALIAS)); + if(certAlias == null) + { + assertFalse("should not be a certificateAlias attribute", + keystore.containsKey(KeyStore.CERTIFICATE_ALIAS)); + } + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/PortRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/PortRestTest.java index 1497d740dc..be4dea6e81 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/PortRestTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/PortRestTest.java @@ -30,7 +30,6 @@ import java.util.List; import java.util.Map; import org.apache.qpid.server.model.AuthenticationProvider; -import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.Protocol; import org.apache.qpid.server.model.State; @@ -79,6 +78,7 @@ public class PortRestTest extends QpidRestTestCase Map<String, Object> attributes = new HashMap<String, Object>(); attributes.put(Port.NAME, portName); attributes.put(Port.PORT, findFreePort()); + attributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); int responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes); assertEquals("Unexpected response code", 201, responseCode); @@ -139,6 +139,7 @@ public class PortRestTest extends QpidRestTestCase Map<String, Object> attributes = new HashMap<String, Object>(); attributes.put(Port.NAME, portName); attributes.put(Port.PORT, findFreePort()); + attributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); int responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes); assertEquals("Unexpected response code for port creation", 201, responseCode); @@ -162,25 +163,6 @@ public class PortRestTest extends QpidRestTestCase responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes); assertEquals("Port cannot be updated in non management mode", 409, responseCode); - - restartBrokerInManagementMode(); - - responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes); - assertEquals("Port should be allwed to update in a management mode", 200, responseCode); - - portDetails = getRestTestHelper().getJsonAsList("/rest/port/" + portName); - assertNotNull("Port details cannot be null", portDetails); - assertEquals("Unexpected number of ports with name " + portName, 1, portDetails.size()); - port = portDetails.get(0); - - assertEquals("Unexpected authentication provider", TestBrokerConfiguration.ENTRY_NAME_ANONYMOUS_PROVIDER, port.get(Port.AUTHENTICATION_PROVIDER)); - Object protocols = port.get(Port.PROTOCOLS); - assertNotNull("Protocols attribute is not found", protocols); - assertTrue("Protocol attribute value is not collection:" + protocols, protocols instanceof Collection); - @SuppressWarnings("unchecked") - Collection<String> protocolsCollection = ((Collection<String>)protocols); - assertEquals("Unexpected protocols size", 1, protocolsCollection.size()); - assertEquals("Unexpected protocols", Protocol.AMQP_0_9_1.name(), protocolsCollection.iterator().next()); } public void testPutUpdateOpenedAmqpPortFails() throws Exception @@ -200,16 +182,19 @@ public class PortRestTest extends QpidRestTestCase public void testUpdatePortTransportFromTCPToSSLWhenKeystoreIsConfigured() throws Exception { restartBrokerInManagementMode(); + getRestTestHelper().setManagementModeCredentials(); String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; Map<String, Object> attributes = new HashMap<String, Object>(); attributes.put(Port.NAME, portName); attributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + attributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); int responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes); assertEquals("Transport has not been changed to SSL " , 200, responseCode); restartBroker(); + getRestTestHelper().setUsernameAndPassword("webadmin", "webadmin"); Map<String, Object> port = getRestTestHelper().getJsonAsSingletonList("/rest/port/" + portName); @@ -217,13 +202,15 @@ public class PortRestTest extends QpidRestTestCase Collection<String> transports = (Collection<String>) port.get(Port.TRANSPORTS); assertEquals("Unexpected auth provider", new HashSet<String>(Arrays.asList(Transport.SSL.name())), new HashSet<String>(transports)); + + String keyStore = (String) port.get(Port.KEY_STORE); + assertEquals("Unexpected auth provider", TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE, keyStore); } public void testUpdateTransportFromTCPToSSLWithoutKeystoreConfiguredFails() throws Exception { - getBrokerConfiguration().setBrokerAttribute(Broker.KEY_STORE_PATH, null); - getBrokerConfiguration().setSaved(false); restartBrokerInManagementMode(); + getRestTestHelper().setManagementModeCredentials(); String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; Map<String, Object> attributes = new HashMap<String, Object>(); @@ -240,12 +227,16 @@ public class PortRestTest extends QpidRestTestCase Map<String, Object> attributes = new HashMap<String, Object>(); attributes.put(Port.NAME, portName); attributes.put(Port.PORT, DEFAULT_SSL_PORT); + attributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); attributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + attributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + attributes.put(Port.TRUST_STORES, Collections.singleton(TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE)); int responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes); assertEquals("SSL port was not added", 201, responseCode); restartBrokerInManagementMode(); + getRestTestHelper().setManagementModeCredentials(); attributes.put(Port.NEED_CLIENT_AUTH, true); attributes.put(Port.WANT_CLIENT_AUTH, true); @@ -254,18 +245,25 @@ public class PortRestTest extends QpidRestTestCase assertEquals("Attributes for need/want client auth are not set", 200, responseCode); restartBroker(); + getRestTestHelper().setUsernameAndPassword("webadmin", "webadmin"); Map<String, Object> port = getRestTestHelper().getJsonAsSingletonList("/rest/port/" + portName); assertEquals("Unexpected " + Port.NEED_CLIENT_AUTH, true, port.get(Port.NEED_CLIENT_AUTH)); assertEquals("Unexpected " + Port.WANT_CLIENT_AUTH, true, port.get(Port.WANT_CLIENT_AUTH)); + assertEquals("Unexpected " + Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE, port.get(Port.KEY_STORE)); + @SuppressWarnings("unchecked") + Collection<String> trustStores = (Collection<String>) port.get(Port.TRUST_STORES); + assertEquals("Unexpected auth provider", new HashSet<String>(Arrays.asList(TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE)), + new HashSet<String>(trustStores)); restartBrokerInManagementMode(); + getRestTestHelper().setManagementModeCredentials(); attributes = new HashMap<String, Object>(); attributes.put(Port.NAME, portName); attributes.put(Port.TRANSPORTS, Collections.singleton(Transport.TCP)); responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "PUT", attributes); - assertEquals("Should not be able to change transport to SSL without reseting of attributes for need/want client auth", 409, responseCode); + assertEquals("Should not be able to change transport to TCP without reseting of attributes for need/want client auth", 409, responseCode); attributes = new HashMap<String, Object>(); attributes.put(Port.NAME, portName); @@ -277,6 +275,7 @@ public class PortRestTest extends QpidRestTestCase assertEquals("Should be able to change transport to TCP ", 200, responseCode); restartBroker(); + getRestTestHelper().setUsernameAndPassword("webadmin", "webadmin"); port = getRestTestHelper().getJsonAsSingletonList("/rest/port/" + portName); assertEquals("Unexpected " + Port.NEED_CLIENT_AUTH, false, port.get(Port.NEED_CLIENT_AUTH)); assertEquals("Unexpected " + Port.WANT_CLIENT_AUTH, false, port.get(Port.WANT_CLIENT_AUTH)); @@ -290,6 +289,7 @@ public class PortRestTest extends QpidRestTestCase public void testUpdateSettingWantNeedCertificateFailsForNonSSLPort() throws Exception { restartBrokerInManagementMode(); + getRestTestHelper().setManagementModeCredentials(); String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; Map<String, Object> attributes = new HashMap<String, Object>(); @@ -308,6 +308,7 @@ public class PortRestTest extends QpidRestTestCase public void testUpdatePortAuthenticationProvider() throws Exception { restartBrokerInManagementMode(); + getRestTestHelper().setManagementModeCredentials(); String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; Map<String, Object> attributes = new HashMap<String, Object>(); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/QpidRestTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/QpidRestTestCase.java index f83eb391e7..e5aacbba4b 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/QpidRestTestCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/QpidRestTestCase.java @@ -27,9 +27,7 @@ import java.util.Map; import org.apache.commons.configuration.ConfigurationException; import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Port; -import org.apache.qpid.server.plugin.AuthenticationManagerFactory; import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManagerFactory; -import org.apache.qpid.server.security.auth.manager.ExternalAuthenticationManagerFactory; import org.apache.qpid.test.utils.TestBrokerConfiguration; import org.apache.qpid.test.utils.QpidBrokerTestCase; @@ -49,10 +47,17 @@ public class QpidRestTestCase extends QpidBrokerTestCase @Override public void setUp() throws Exception { + // use webadmin account to perform tests + getRestTestHelper().setUsernameAndPassword("webadmin", "webadmin"); + + //remove the normal 'test' vhost, we will configure the vhosts below + getBrokerConfiguration(0).removeObjectConfiguration(TestBrokerConfiguration.ENTRY_NAME_VIRTUAL_HOST); + // Set up virtualhost config with queues and bindings to the amq.direct for (String virtualhost : EXPECTED_VIRTUALHOSTS) { createTestVirtualHost(0, virtualhost); + for (String queue : EXPECTED_QUEUES) { setVirtualHostConfigurationProperty("virtualhosts.virtualhost." + virtualhost + ".queues.exchange", "amq.direct"); @@ -89,6 +94,11 @@ public class QpidRestTestCase extends QpidBrokerTestCase anonymousProviderAttributes.put(AuthenticationProvider.TYPE, AnonymousAuthenticationManagerFactory.PROVIDER_TYPE); anonymousProviderAttributes.put(AuthenticationProvider.NAME, ANONYMOUS_AUTHENTICATION_PROVIDER); config.addAuthenticationProviderConfiguration(anonymousProviderAttributes); + + // set password authentication provider on http port for the tests + config.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_PORT, Port.AUTHENTICATION_PROVIDER, + TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + config.setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, "httpBasicAuthenticationEnabled", true); } public RestTestHelper getRestTestHelper() diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java index 9628423a00..c15e5d7285 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/RestTestHelper.java @@ -51,6 +51,7 @@ import junit.framework.Assert; import org.apache.commons.codec.binary.Base64; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.security.auth.manager.AbstractPrincipalDatabaseAuthManagerFactory; import org.apache.qpid.ssl.SSLContextFactory; import org.apache.qpid.test.utils.QpidBrokerTestCase; @@ -400,6 +401,11 @@ public class RestTestHelper _password = password; } + public void setManagementModeCredentials() + { + setUsernameAndPassword(BrokerOptions.MANAGEMENT_MODE_USER_NAME, QpidBrokerTestCase.MANAGEMENT_MODE_PASSWORD); + } + /** * Create password file that follows the convention username=password, which is deleted by {@link #tearDown()} */ diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java index 856fda9419..1c05f17e25 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/SaslRestTest.java @@ -20,20 +20,20 @@ */ package org.apache.qpid.systest.rest; +import static org.apache.qpid.server.security.auth.sasl.SaslUtil.generateCramMD5ClientResponse; +import static org.apache.qpid.server.security.auth.sasl.SaslUtil.generateCramMD5HexClientResponse; +import static org.apache.qpid.server.security.auth.sasl.SaslUtil.generatePlainClientResponse; + import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; -import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; - import org.apache.commons.codec.binary.Base64; import org.apache.commons.configuration.ConfigurationException; import org.apache.qpid.server.plugin.AuthenticationManagerFactory; @@ -131,7 +131,7 @@ public class SaslRestTest extends QpidRestTestCase os.flush(); int code = connection.getResponseCode(); - assertEquals("Unexpected response code", 403, code); + assertEquals("Unexpected response code", 401, code); List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); @@ -156,7 +156,7 @@ public class SaslRestTest extends QpidRestTestCase os.flush(); int code = connection.getResponseCode(); - assertEquals("Unexpected response code", 403, code); + assertEquals("Unexpected response code", 401, code); List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); @@ -196,7 +196,7 @@ public class SaslRestTest extends QpidRestTestCase // authenticate user with correct credentials int code = authenticateUser(connection, "admin", "incorrect", "CRAM-MD5"); - assertEquals("Unexpected response code", 403, code); + assertEquals("Unexpected response code", 401, code); // request authenticated user details connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET"); @@ -215,7 +215,7 @@ public class SaslRestTest extends QpidRestTestCase // authenticate user with correct credentials int code = authenticateUser(connection, "nonexisting", "admin", "CRAM-MD5"); - assertEquals("Unexpected response code", 403, code); + assertEquals("Unexpected response code", 401, code); // request authenticated user details connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET"); @@ -254,7 +254,7 @@ public class SaslRestTest extends QpidRestTestCase // try to authenticate user with incorrect passowrd int code = authenticateUser(connection, "admin", "incorrect", "CRAM-MD5-HEX"); - assertEquals("Unexpected response code", 403, code); + assertEquals("Unexpected response code", 401, code); // request authenticated user details connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET"); @@ -273,7 +273,7 @@ public class SaslRestTest extends QpidRestTestCase // try to authenticate non-existing user int code = authenticateUser(connection, "nonexisting", "admin", "CRAM-MD5-HEX"); - assertEquals("Unexpected response code", 403, code); + assertEquals("Unexpected response code", 401, code); // request authenticated user details connection = getRestTestHelper().openManagementConnection("/rest/sasl", "GET"); @@ -345,59 +345,6 @@ public class SaslRestTest extends QpidRestTestCase } } - private static byte SEPARATOR = 0; - - private byte[] generatePlainClientResponse(String userName, String userPassword) throws Exception - { - byte[] password = userPassword.getBytes("UTF8"); - byte user[] = userName.getBytes("UTF8"); - byte response[] = new byte[password.length + user.length + 2 ]; - int size = 0; - response[size++] = SEPARATOR; - System.arraycopy(user, 0, response, size, user.length); - size += user.length; - response[size++] = SEPARATOR; - System.arraycopy(password, 0, response, size, password.length); - return response; - } - - private byte[] generateCramMD5HexClientResponse(String userName, String userPassword, byte[] challengeBytes) throws Exception - { - String macAlgorithm = "HmacMD5"; - byte[] digestedPasswordBytes = MessageDigest.getInstance("MD5").digest(userPassword.getBytes("UTF-8")); - byte[] hexEncodedDigestedPasswordBytes = toHex(digestedPasswordBytes).getBytes("UTF-8"); - Mac mac = Mac.getInstance(macAlgorithm); - mac.init(new SecretKeySpec(hexEncodedDigestedPasswordBytes, macAlgorithm)); - final byte[] messageAuthenticationCode = mac.doFinal(challengeBytes); - String responseAsString = userName + " " + toHex(messageAuthenticationCode); - return responseAsString.getBytes(); - } - - private byte[] generateCramMD5ClientResponse(String userName, String userPassword, byte[] challengeBytes) throws Exception - { - String macAlgorithm = "HmacMD5"; - Mac mac = Mac.getInstance(macAlgorithm); - mac.init(new SecretKeySpec(userPassword.getBytes("UTF-8"), macAlgorithm)); - final byte[] messageAuthenticationCode = mac.doFinal(challengeBytes); - String responseAsString = userName + " " + toHex(messageAuthenticationCode); - return responseAsString.getBytes(); - } - - private String toHex(byte[] data) - { - StringBuffer hash = new StringBuffer(); - for (int i = 0; i < data.length; i++) - { - String hex = Integer.toHexString(0xFF & data[i]); - if (hex.length() == 1) - { - hash.append('0'); - } - hash.append(hex); - } - return hash.toString(); - } - private void configureBase64MD5FilePrincipalDatabase() throws IOException, ConfigurationException { // generate user password entry diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/StructureRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/StructureRestTest.java index 5593ad0b42..cb4fd1ad5b 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/StructureRestTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/StructureRestTest.java @@ -33,7 +33,7 @@ public class StructureRestTest extends QpidRestTestCase { Map<String, Object> structure = getRestTestHelper().getJsonAsMap("/rest/structure"); assertNotNull("Structure data cannot be null", structure); - assertNode(structure, "QpidBroker"); + assertNode(structure, "Broker"); @SuppressWarnings("unchecked") List<Map<String, Object>> virtualhosts = (List<Map<String, Object>>) structure.get("virtualhosts"); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/TrustStoreRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/TrustStoreRestTest.java new file mode 100644 index 0000000000..8b788780d6 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/TrustStoreRestTest.java @@ -0,0 +1,262 @@ +/* + * + * 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.systest.rest; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.model.KeyStore; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Transport; +import org.apache.qpid.server.model.TrustStore; +import org.apache.qpid.server.model.adapter.AbstractKeyStoreAdapter; +import org.apache.qpid.test.utils.TestBrokerConfiguration; +import org.apache.qpid.test.utils.TestSSLConstants; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; + +public class TrustStoreRestTest extends QpidRestTestCase +{ + @Override + public void setUp() throws Exception + { + // not calling super.setUp() to avoid broker start-up until + // after any necessary configuration + } + + public void testGet() throws Exception + { + super.setUp(); + + //verify existence of the default trust store used by the systests + List<Map<String, Object>> trustStores = assertNumberOfTrustStores(1); + + Map<String, Object> truststore = trustStores.get(0); + assertTrustStoreAttributes(truststore, TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE, + System.getProperty(QPID_HOME) + "/../" + TestSSLConstants.BROKER_TRUSTSTORE, false); + } + + public void testCreate() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfTrustStores(1); + createTrustStore(name, true); + assertNumberOfTrustStores(2); + + List<Map<String, Object>> trustStores = getRestTestHelper().getJsonAsList("/rest/truststore/" + name); + assertNotNull("details cannot be null", trustStores); + + assertTrustStoreAttributes(trustStores.get(0), name, TestSSLConstants.TRUSTSTORE, true); + } + + public void testDelete() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfTrustStores(1); + createTrustStore(name, false); + assertNumberOfTrustStores(2); + + int responseCode = getRestTestHelper().submitRequest("/rest/truststore/" + name , "DELETE", null); + assertEquals("Unexpected response code for provider deletion", 200, responseCode); + + List<Map<String, Object>> trustStore = getRestTestHelper().getJsonAsList("/rest/truststore/" + name); + assertNotNull("details should not be null", trustStore); + assertTrue("details should be empty as the truststore no longer exists", trustStore.isEmpty()); + + //check only the default systests trust store remains + List<Map<String, Object>> trustStores = assertNumberOfTrustStores(1); + Map<String, Object> truststore = trustStores.get(0); + assertTrustStoreAttributes(truststore, TestBrokerConfiguration.ENTRY_NAME_SSL_TRUSTSTORE, + System.getProperty(QPID_HOME) + "/../" + TestSSLConstants.BROKER_TRUSTSTORE, false); + } + + public void testDeleteFailsWhenTrustStoreInUse() throws Exception + { + String name = "testDeleteFailsWhenTrustStoreInUse"; + + //add a new trust store config to use + Map<String, Object> sslTrustStoreAttributes = new HashMap<String, Object>(); + sslTrustStoreAttributes.put(TrustStore.NAME, name); + sslTrustStoreAttributes.put(TrustStore.PATH, TestSSLConstants.TRUSTSTORE); + sslTrustStoreAttributes.put(TrustStore.PASSWORD, TestSSLConstants.TRUSTSTORE_PASSWORD); + getBrokerConfiguration().addTrustStoreConfiguration(sslTrustStoreAttributes); + + //add the SSL port using it + Map<String, Object> sslPortAttributes = new HashMap<String, Object>(); + sslPortAttributes.put(Port.TRANSPORTS, Collections.singleton(Transport.SSL)); + sslPortAttributes.put(Port.PORT, DEFAULT_SSL_PORT); + sslPortAttributes.put(Port.NAME, TestBrokerConfiguration.ENTRY_NAME_SSL_PORT); + sslPortAttributes.put(Port.AUTHENTICATION_PROVIDER, TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER); + sslPortAttributes.put(Port.KEY_STORE, TestBrokerConfiguration.ENTRY_NAME_SSL_KEYSTORE); + sslPortAttributes.put(Port.TRUST_STORES, Collections.singleton(name)); + getBrokerConfiguration().addPortConfiguration(sslPortAttributes); + + super.setUp(); + + //verify the truststore is there + assertNumberOfTrustStores(2); + + List<Map<String, Object>> trustStore = getRestTestHelper().getJsonAsList("/rest/truststore/" + name); + assertNotNull("details should not be null", trustStore); + assertTrustStoreAttributes(trustStore.get(0), name, TestSSLConstants.TRUSTSTORE, false); + + //try to delete it, which should fail as it is in use + int responseCode = getRestTestHelper().submitRequest("/rest/truststore/" + name , "DELETE", null); + assertEquals("Unexpected response code for provider deletion", 409, responseCode); + + //check its still there + assertNumberOfTrustStores(2); + trustStore = getRestTestHelper().getJsonAsList("/rest/truststore/" + name); + assertNotNull("details should not be null", trustStore); + assertTrustStoreAttributes(trustStore.get(0), name, TestSSLConstants.TRUSTSTORE, false); + } + + public void testUpdateWithGoodPathSucceeds() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfTrustStores(1); + createTrustStore(name, false); + assertNumberOfTrustStores(2); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(TrustStore.NAME, name); + attributes.put(TrustStore.PATH, TestSSLConstants.TRUSTSTORE); + + int responseCode = getRestTestHelper().submitRequest("/rest/truststore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for truststore update", 200, responseCode); + + List<Map<String, Object>> trustStore = getRestTestHelper().getJsonAsList("/rest/truststore/" + name); + assertNotNull("details should not be null", trustStore); + + assertTrustStoreAttributes(trustStore.get(0), name, TestSSLConstants.TRUSTSTORE, false); + } + + public void testUpdateWithNonExistentPathFails() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfTrustStores(1); + createTrustStore(name, false); + assertNumberOfTrustStores(2); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(TrustStore.NAME, name); + attributes.put(TrustStore.PATH, "does.not.exist"); + + int responseCode = getRestTestHelper().submitRequest("/rest/truststore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for trust store update", 409, responseCode); + + List<Map<String, Object>> trustStore = getRestTestHelper().getJsonAsList("/rest/truststore/" + name); + assertNotNull("details should not be null", trustStore); + + //verify the details remain unchanged + assertTrustStoreAttributes(trustStore.get(0), name, TestSSLConstants.TRUSTSTORE, false); + } + + public void testUpdatePeersOnly() throws Exception + { + super.setUp(); + + String name = getTestName(); + + assertNumberOfTrustStores(1); + createTrustStore(name, false); + assertNumberOfTrustStores(2); + + //update the peersOnly attribute from false to true + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(TrustStore.NAME, name); + attributes.put(TrustStore.PEERS_ONLY, true); + + int responseCode = getRestTestHelper().submitRequest("/rest/truststore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for trust store update", 200, responseCode); + + List<Map<String, Object>> trustStore = getRestTestHelper().getJsonAsList("/rest/truststore/" + name); + assertNotNull("details should not be null", trustStore); + + assertTrustStoreAttributes(trustStore.get(0), name, TestSSLConstants.TRUSTSTORE, true); + + //Update peersOnly to clear it (i.e go from from true to null, which will default to false) + attributes = new HashMap<String, Object>(); + attributes.put(TrustStore.NAME, name); + attributes.put(TrustStore.PEERS_ONLY, null); + + responseCode = getRestTestHelper().submitRequest("/rest/truststore/" + name , "PUT", attributes); + assertEquals("Unexpected response code for trust store update", 200, responseCode); + + trustStore = getRestTestHelper().getJsonAsList("/rest/truststore/" + name); + assertNotNull("details should not be null", trustStore); + + assertTrustStoreAttributes(trustStore.get(0), name, TestSSLConstants.TRUSTSTORE, false); + } + + private List<Map<String, Object>> assertNumberOfTrustStores(int numberOfTrustStores) throws IOException, + JsonParseException, JsonMappingException + { + List<Map<String, Object>> trustStores = getRestTestHelper().getJsonAsList("/rest/truststore"); + assertNotNull("trust stores should not be null", trustStores); + assertEquals("Unexpected number of trust stores", numberOfTrustStores, trustStores.size()); + + return trustStores; + } + + private void createTrustStore(String name, boolean peersOnly) throws IOException, JsonGenerationException, JsonMappingException + { + Map<String, Object> trustStoreAttributes = new HashMap<String, Object>(); + trustStoreAttributes.put(TrustStore.NAME, name); + //deliberately using the client trust store to differentiate from the one we are already for broker + trustStoreAttributes.put(TrustStore.PATH, TestSSLConstants.TRUSTSTORE); + trustStoreAttributes.put(TrustStore.PASSWORD, TestSSLConstants.TRUSTSTORE_PASSWORD); + trustStoreAttributes.put(TrustStore.PEERS_ONLY, peersOnly); + + int responseCode = getRestTestHelper().submitRequest("/rest/truststore/" + name, "PUT", trustStoreAttributes); + assertEquals("Unexpected response code", 201, responseCode); + } + + private void assertTrustStoreAttributes(Map<String, Object> truststore, String name, String path, boolean peersOnly) + { + assertEquals("default systests trust store is missing", + name, truststore.get(TrustStore.NAME)); + assertEquals("unexpected path to trust store", + path, truststore.get(TrustStore.PATH)); + assertEquals("unexpected (dummy) password of default systests trust store", + AbstractKeyStoreAdapter.DUMMY_PASSWORD_MASK, truststore.get(TrustStore.PASSWORD)); + assertEquals("unexpected type of default systests trust store", + java.security.KeyStore.getDefaultType(), truststore.get(TrustStore.TYPE)); + assertEquals("unexpected peersOnly value", + peersOnly, truststore.get(TrustStore.PEERS_ONLY)); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/UserRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/UserRestTest.java index 017467a8be..e2a6762731 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/UserRestTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/UserRestTest.java @@ -34,6 +34,7 @@ public class UserRestTest extends QpidRestTestCase getRestTestHelper().configureTemporaryPasswordFile(this, "user1", "user2"); super.setUp(); // do this last because it starts the broker, using the modified config + getRestTestHelper().setUsernameAndPassword("user1", "user1"); } public void testGet() throws Exception diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java index 5d23219336..220d2bc574 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/BrokerACLTest.java @@ -28,22 +28,32 @@ import java.util.List; import java.util.Map; import org.apache.commons.configuration.ConfigurationException; +import org.apache.qpid.server.model.AccessControlProvider; import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.GroupProvider; +import org.apache.qpid.server.model.KeyStore; import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.Protocol; +import org.apache.qpid.server.model.TrustStore; import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.security.access.FileAccessControlProviderConstants; import org.apache.qpid.server.security.acl.AbstractACLTestCase; import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManagerFactory; import org.apache.qpid.server.security.auth.manager.PlainPasswordFileAuthenticationManagerFactory; +import org.apache.qpid.server.security.group.FileGroupManagerFactory; import org.apache.qpid.systest.rest.QpidRestTestCase; import org.apache.qpid.test.utils.TestBrokerConfiguration; import org.apache.qpid.test.utils.TestFileUtils; +import org.apache.qpid.test.utils.TestSSLConstants; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.map.JsonMappingException; public class BrokerACLTest extends QpidRestTestCase { private static final String ALLOWED_USER = "user1"; private static final String DENIED_USER = "user2"; + private String _secondaryAclFileContent = ""; @Override protected void customizeConfiguration() throws ConfigurationException, IOException @@ -57,10 +67,18 @@ public class BrokerACLTest extends QpidRestTestCase "ACL DENY-LOG " + DENIED_USER + " CONFIGURE BROKER", "ACL DENY-LOG ALL ALL"); + _secondaryAclFileContent = + "ACL ALLOW-LOG ALL ACCESS MANAGEMENT\n" + + "ACL ALLOW-LOG " + ALLOWED_USER + " CONFIGURE BROKER\n" + + "ACL DENY-LOG " + DENIED_USER + " CONFIGURE BROKER\n" + + "ACL DENY-LOG ALL ALL"; + getBrokerConfiguration().setObjectAttribute(TestBrokerConfiguration.ENTRY_NAME_HTTP_MANAGEMENT, "httpBasicAuthenticationEnabled", true); } + /* === AuthenticationProvider === */ + public void testCreateAuthenticationProviderAllowed() throws Exception { getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); @@ -168,6 +186,8 @@ public class BrokerACLTest extends QpidRestTestCase provider.get(PlainPasswordFileAuthenticationManagerFactory.ATTRIBUTE_PATH)); } + /* === VirtualHost === */ + public void testCreateVirtualHostAllowed() throws Exception { getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); @@ -218,6 +238,8 @@ public class BrokerACLTest extends QpidRestTestCase assertVirtualHostExists(TEST2_VIRTUALHOST); } + /* === Port === */ + public void testCreatePortAllowed() throws Exception { getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); @@ -249,8 +271,6 @@ public class BrokerACLTest extends QpidRestTestCase String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; assertPortExists(portName); - restartBrokerInManagementMode(); - getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); int responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "DELETE", null); @@ -259,15 +279,14 @@ public class BrokerACLTest extends QpidRestTestCase assertPortExists(portName); } - public void testDeletePortAllowed() throws Exception + // TODO: test disabled until allowing the deletion of active ports outside management mode + public void DISABLED_testDeletePortAllowed() throws Exception { getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); String portName = TestBrokerConfiguration.ENTRY_NAME_AMQP_PORT; assertPortExists(portName); - restartBrokerInManagementMode(); - getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); int responseCode = getRestTestHelper().submitRequest("/rest/port/" + portName, "DELETE", null); @@ -276,7 +295,8 @@ public class BrokerACLTest extends QpidRestTestCase assertPortDoesNotExist(portName); } - public void testSetPortAttributesAllowed() throws Exception + // TODO: test disabled until allowing the updating of active ports outside management mode + public void DISABLED_testSetPortAttributesAllowed() throws Exception { getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); @@ -287,7 +307,6 @@ public class BrokerACLTest extends QpidRestTestCase assertPortExists(portName); - restartBrokerInManagementMode(); Map<String, Object> attributes = new HashMap<String, Object>(); attributes.put(Port.NAME, portName); @@ -311,8 +330,6 @@ public class BrokerACLTest extends QpidRestTestCase assertPortExists(portName); - restartBrokerInManagementMode(); - getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); Map<String, Object> attributes = new HashMap<String, Object>(); @@ -327,48 +344,541 @@ public class BrokerACLTest extends QpidRestTestCase TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER, port.get(Port.AUTHENTICATION_PROVIDER)); } + /* === KeyStore === */ + + public void testCreateKeyStoreAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String keyStoreName = getTestName(); + + assertKeyStoreExistence(keyStoreName, false); + + int responseCode = createKeyStore(keyStoreName, "app1"); + assertEquals("keyStore creation should be allowed", 201, responseCode); + + assertKeyStoreExistence(keyStoreName, true); + } + + public void testCreateKeyStoreDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String keyStoreName = getTestName(); + + assertKeyStoreExistence(keyStoreName, false); + + int responseCode = createKeyStore(keyStoreName, "app1"); + assertEquals("keyStore creation should be allowed", 403, responseCode); + + assertKeyStoreExistence(keyStoreName, false); + } + + public void testDeleteKeyStoreDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String keyStoreName = getTestName(); + + assertKeyStoreExistence(keyStoreName, false); + + int responseCode = createKeyStore(keyStoreName, "app1"); + assertEquals("keyStore creation should be allowed", 201, responseCode); + + assertKeyStoreExistence(keyStoreName, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + responseCode = getRestTestHelper().submitRequest("/rest/keystore/" + keyStoreName, "DELETE", null); + assertEquals("keystore deletion should be denied", 403, responseCode); + + assertKeyStoreExistence(keyStoreName, true); + } + + public void testDeleteKeyStoreAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String keyStoreName = getTestName(); + + assertKeyStoreExistence(keyStoreName, false); + + int responseCode = createKeyStore(keyStoreName, "app1"); + assertEquals("keyStore creation should be allowed", 201, responseCode); + + assertKeyStoreExistence(keyStoreName, true); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + responseCode = getRestTestHelper().submitRequest("/rest/keystore/" + keyStoreName, "DELETE", null); + assertEquals("keystore deletion should be allowed", 200, responseCode); + + assertKeyStoreExistence(keyStoreName, false); + } + + public void testSetKeyStoreAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String keyStoreName = getTestName(); + String initialCertAlias = "app1"; + String updatedCertAlias = "app2"; + + assertKeyStoreExistence(keyStoreName, false); + + int responseCode = createKeyStore(keyStoreName, initialCertAlias); + assertEquals("keyStore creation should be allowed", 201, responseCode); + + assertKeyStoreExistence(keyStoreName, true); + Map<String, Object> keyStore = getRestTestHelper().getJsonAsSingletonList("/rest/keystore/" + keyStoreName); + assertEquals("Unexpected certificateAlias attribute value", initialCertAlias, keyStore.get(KeyStore.CERTIFICATE_ALIAS)); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(KeyStore.NAME, keyStoreName); + attributes.put(KeyStore.CERTIFICATE_ALIAS, updatedCertAlias); + responseCode = getRestTestHelper().submitRequest("/rest/keystore/" + keyStoreName, "PUT", attributes); + assertEquals("Setting of keystore attributes should be allowed", 200, responseCode); + + keyStore = getRestTestHelper().getJsonAsSingletonList("/rest/keystore/" + keyStoreName); + assertEquals("Unexpected certificateAlias attribute value", updatedCertAlias, keyStore.get(KeyStore.CERTIFICATE_ALIAS)); + } + + public void testSetKeyStoreAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String keyStoreName = getTestName(); + String initialCertAlias = "app1"; + String updatedCertAlias = "app2"; + + assertKeyStoreExistence(keyStoreName, false); + + int responseCode = createKeyStore(keyStoreName, initialCertAlias); + assertEquals("keyStore creation should be allowed", 201, responseCode); + + assertKeyStoreExistence(keyStoreName, true); + Map<String, Object> keyStore = getRestTestHelper().getJsonAsSingletonList("/rest/keystore/" + keyStoreName); + assertEquals("Unexpected certificateAlias attribute value", initialCertAlias, keyStore.get(KeyStore.CERTIFICATE_ALIAS)); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(KeyStore.NAME, keyStoreName); + attributes.put(KeyStore.CERTIFICATE_ALIAS, updatedCertAlias); + responseCode = getRestTestHelper().submitRequest("/rest/keystore/" + keyStoreName, "PUT", attributes); + assertEquals("Setting of keystore attributes should be denied", 403, responseCode); + + keyStore = getRestTestHelper().getJsonAsSingletonList("/rest/keystore/" + keyStoreName); + assertEquals("Unexpected certificateAlias attribute value", initialCertAlias, keyStore.get(KeyStore.CERTIFICATE_ALIAS)); + } + + /* === TrustStore === */ + + public void testCreateTrustStoreAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String trustStoreName = getTestName(); + + assertTrustStoreExistence(trustStoreName, false); + + int responseCode = createTrustStore(trustStoreName, false); + assertEquals("trustStore creation should be allowed", 201, responseCode); + + assertTrustStoreExistence(trustStoreName, true); + } + + public void testCreateTrustStoreDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String trustStoreName = getTestName(); + + assertTrustStoreExistence(trustStoreName, false); + + int responseCode = createTrustStore(trustStoreName, false); + assertEquals("trustStore creation should be allowed", 403, responseCode); + + assertTrustStoreExistence(trustStoreName, false); + } + + public void testDeleteTrustStoreDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String trustStoreName = getTestName(); + + assertTrustStoreExistence(trustStoreName, false); + + int responseCode = createTrustStore(trustStoreName, false); + assertEquals("trustStore creation should be allowed", 201, responseCode); + + assertTrustStoreExistence(trustStoreName, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + responseCode = getRestTestHelper().submitRequest("/rest/truststore/" + trustStoreName, "DELETE", null); + assertEquals("truststore deletion should be denied", 403, responseCode); + + assertTrustStoreExistence(trustStoreName, true); + } + + public void testDeleteTrustStoreAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String trustStoreName = getTestName(); + + assertTrustStoreExistence(trustStoreName, false); + + int responseCode = createTrustStore(trustStoreName, false); + assertEquals("trustStore creation should be allowed", 201, responseCode); + + assertTrustStoreExistence(trustStoreName, true); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + responseCode = getRestTestHelper().submitRequest("/rest/truststore/" + trustStoreName, "DELETE", null); + assertEquals("truststore deletion should be allowed", 200, responseCode); + + assertTrustStoreExistence(trustStoreName, false); + } + + public void testSetTrustStoreAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String trustStoreName = getTestName(); + boolean initialPeersOnly = false; + boolean updatedPeersOnly = true; + + assertTrustStoreExistence(trustStoreName, false); + + int responseCode = createTrustStore(trustStoreName, initialPeersOnly); + assertEquals("trustStore creation should be allowed", 201, responseCode); + + assertTrustStoreExistence(trustStoreName, true); + Map<String, Object> trustStore = getRestTestHelper().getJsonAsSingletonList("/rest/truststore/" + trustStoreName); + assertEquals("Unexpected peersOnly attribute value", initialPeersOnly, trustStore.get(TrustStore.PEERS_ONLY)); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(TrustStore.NAME, trustStoreName); + attributes.put(TrustStore.PEERS_ONLY, updatedPeersOnly); + responseCode = getRestTestHelper().submitRequest("/rest/truststore/" + trustStoreName, "PUT", attributes); + assertEquals("Setting of truststore attributes should be allowed", 200, responseCode); + + trustStore = getRestTestHelper().getJsonAsSingletonList("/rest/truststore/" + trustStoreName); + assertEquals("Unexpected peersOnly attribute value", updatedPeersOnly, trustStore.get(TrustStore.PEERS_ONLY)); + } + + public void testSetTrustStoreAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String trustStoreName = getTestName(); + boolean initialPeersOnly = false; + boolean updatedPeersOnly = true; + + assertTrustStoreExistence(trustStoreName, false); + + int responseCode = createTrustStore(trustStoreName, initialPeersOnly); + assertEquals("trustStore creation should be allowed", 201, responseCode); + + assertTrustStoreExistence(trustStoreName, true); + Map<String, Object> trustStore = getRestTestHelper().getJsonAsSingletonList("/rest/truststore/" + trustStoreName); + assertEquals("Unexpected peersOnly attribute value", initialPeersOnly, trustStore.get(TrustStore.PEERS_ONLY)); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(TrustStore.NAME, trustStoreName); + attributes.put(TrustStore.PEERS_ONLY, updatedPeersOnly); + responseCode = getRestTestHelper().submitRequest("/rest/truststore/" + trustStoreName, "PUT", attributes); + assertEquals("Setting of truststore attributes should be denied", 403, responseCode); + + trustStore = getRestTestHelper().getJsonAsSingletonList("/rest/truststore/" + trustStoreName); + assertEquals("Unexpected peersOnly attribute value", initialPeersOnly, trustStore.get(TrustStore.PEERS_ONLY)); + } + + /* === Broker === */ + public void testSetBrokerAttributesAllowed() throws Exception { getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); - String defaultAuthenticationProvider = TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER; + int initialAlertRepeatGap = 30000; + int updatedAlertRepeatGap = 29999; + Map<String, Object> brokerAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/broker"); - assertEquals("Unexpected authentication provider", defaultAuthenticationProvider, - brokerAttributes.get(Broker.DEFAULT_AUTHENTICATION_PROVIDER)); - restartBrokerInManagementMode(); + assertEquals("Unexpected alert repeat gap", initialAlertRepeatGap, + brokerAttributes.get(Broker.QUEUE_ALERT_REPEAT_GAP)); Map<String, Object> newAttributes = new HashMap<String, Object>(); - newAttributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, ANONYMOUS_AUTHENTICATION_PROVIDER); + newAttributes.put(Broker.QUEUE_ALERT_REPEAT_GAP, updatedAlertRepeatGap); + int responseCode = getRestTestHelper().submitRequest("/rest/broker", "PUT", newAttributes); assertEquals("Setting of port attribites should be allowed", 200, responseCode); brokerAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/broker"); - assertEquals("Unexpected default authentication provider attribute value", ANONYMOUS_AUTHENTICATION_PROVIDER, - brokerAttributes.get(Broker.DEFAULT_AUTHENTICATION_PROVIDER)); + assertEquals("Unexpected default alert repeat gap", updatedAlertRepeatGap, + brokerAttributes.get(Broker.QUEUE_ALERT_REPEAT_GAP)); } public void testSetBrokerAttributesDenied() throws Exception { getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); - String defaultAuthenticationProvider = TestBrokerConfiguration.ENTRY_NAME_AUTHENTICATION_PROVIDER; + int initialAlertRepeatGap = 30000; + int updatedAlertRepeatGap = 29999; Map<String, Object> brokerAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/broker"); - assertEquals("Unexpected authentication provider", defaultAuthenticationProvider, - brokerAttributes.get(Broker.DEFAULT_AUTHENTICATION_PROVIDER)); - restartBrokerInManagementMode(); + assertEquals("Unexpected alert repeat gap", initialAlertRepeatGap, + brokerAttributes.get(Broker.QUEUE_ALERT_REPEAT_GAP)); getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); Map<String, Object> newAttributes = new HashMap<String, Object>(); - newAttributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, ANONYMOUS_AUTHENTICATION_PROVIDER); + newAttributes.put(Broker.QUEUE_ALERT_REPEAT_GAP, updatedAlertRepeatGap); + int responseCode = getRestTestHelper().submitRequest("/rest/broker", "PUT", newAttributes); assertEquals("Setting of port attribites should be allowed", 403, responseCode); brokerAttributes = getRestTestHelper().getJsonAsSingletonList("/rest/broker"); - assertEquals("Unexpected default authentication provider attribute value", defaultAuthenticationProvider, - brokerAttributes.get(Broker.DEFAULT_AUTHENTICATION_PROVIDER)); + assertEquals("Unexpected default alert repeat gap", initialAlertRepeatGap, + brokerAttributes.get(Broker.QUEUE_ALERT_REPEAT_GAP)); + } + + /* === GroupProvider === */ + + public void testCreateGroupProviderAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String groupProviderName = getTestName(); + + assertGroupProviderExistence(groupProviderName, false); + + int responseCode = createGroupProvider(groupProviderName); + assertEquals("Group provider creation should be allowed", 201, responseCode); + + assertGroupProviderExistence(groupProviderName, true); } + public void testCreateGroupProviderDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String groupProviderName = getTestName(); + + assertGroupProviderExistence(groupProviderName, false); + + int responseCode = createGroupProvider(groupProviderName); + assertEquals("Group provider creation should be denied", 403, responseCode); + + assertGroupProviderExistence(groupProviderName, false); + } + + public void testDeleteGroupProviderDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String groupProviderName = getTestName(); + + assertGroupProviderExistence(groupProviderName, false); + + int responseCode = createGroupProvider(groupProviderName); + assertEquals("Group provider creation should be allowed", 201, responseCode); + + assertGroupProviderExistence(groupProviderName, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + responseCode = getRestTestHelper().submitRequest("/rest/groupprovider/" + groupProviderName, "DELETE", null); + assertEquals("Group provider deletion should be denied", 403, responseCode); + + assertGroupProviderExistence(groupProviderName, true); + } + + public void testDeleteGroupProviderAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String groupProviderName = getTestName(); + + assertGroupProviderExistence(groupProviderName, false); + + int responseCode = createGroupProvider(groupProviderName); + assertEquals("Group provider creation should be allowed", 201, responseCode); + + assertGroupProviderExistence(groupProviderName, true); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + responseCode = getRestTestHelper().submitRequest("/rest/groupprovider/" + groupProviderName, "DELETE", null); + assertEquals("Group provider deletion should be allowed", 200, responseCode); + + assertGroupProviderExistence(groupProviderName, false); + } + + public void testSetGroupProviderAttributesAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String groupProviderName = getTestName(); + + assertGroupProviderExistence(groupProviderName, false); + + int responseCode = createGroupProvider(groupProviderName); + assertEquals("Group provider creation should be allowed", 201, responseCode); + + assertGroupProviderExistence(groupProviderName, true); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, groupProviderName); + attributes.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileGroupManagerFactory.PATH, "/path/to/file"); + responseCode = getRestTestHelper().submitRequest("/rest/groupprovider/" + groupProviderName, "PUT", attributes); + assertEquals("Setting of group provider attributes should be allowed but not supported", 409, responseCode); + } + + public void testSetGroupProviderAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String groupProviderName = getTestName(); + + assertGroupProviderExistence(groupProviderName, false); + + int responseCode = createGroupProvider(groupProviderName); + assertEquals("Group provider creation should be allowed", 201, responseCode); + + assertGroupProviderExistence(groupProviderName, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, groupProviderName); + attributes.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileGroupManagerFactory.PATH, "/path/to/file"); + responseCode = getRestTestHelper().submitRequest("/rest/groupprovider/" + groupProviderName, "PUT", attributes); + assertEquals("Setting of group provider attributes should be denied", 403, responseCode); + } + + /* === AccessControlProvider === */ + + public void testCreateAccessControlProviderAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String accessControlProviderName = getTestName(); + + assertAccessControlProviderExistence(accessControlProviderName, false); + + int responseCode = createAccessControlProvider(accessControlProviderName); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, true); + } + + public void testCreateAccessControlProviderDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + String accessControlProviderName = getTestName(); + + assertAccessControlProviderExistence(accessControlProviderName, false); + + int responseCode = createAccessControlProvider(accessControlProviderName); + assertEquals("Access control provider creation should be denied", 403, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, false); + } + + public void testDeleteAccessControlProviderDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String accessControlProviderName = getTestName(); + + assertAccessControlProviderExistence(accessControlProviderName, false); + + int responseCode = createAccessControlProvider(accessControlProviderName); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + responseCode = getRestTestHelper().submitRequest("/rest/accesscontrolprovider/" + accessControlProviderName, "DELETE", null); + assertEquals("Access control provider deletion should be denied", 403, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, true); + } + + public void testDeleteAccessControlProviderAllowed() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String accessControlProviderName = getTestName(); + + assertAccessControlProviderExistence(accessControlProviderName, false); + + int responseCode = createAccessControlProvider(accessControlProviderName); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, true); + + responseCode = getRestTestHelper().submitRequest("/rest/accesscontrolprovider/" + accessControlProviderName, "DELETE", null); + assertEquals("Access control provider deletion should be allowed", 200, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, false); + } + + public void testSetAccessControlProviderAttributesAllowedButUnsupported() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String accessControlProviderName = getTestName(); + + assertAccessControlProviderExistence(accessControlProviderName, false); + + int responseCode = createAccessControlProvider(accessControlProviderName); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, true); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, accessControlProviderName); + attributes.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileGroupManagerFactory.PATH, "/path/to/file"); + responseCode = getRestTestHelper().submitRequest("/rest/accesscontrolprovider/" + accessControlProviderName, "PUT", attributes); + assertEquals("Setting of access control provider attributes should be allowed but not supported", 409, responseCode); + } + + public void testSetAccessControlProviderAttributesDenied() throws Exception + { + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + String accessControlProviderName = getTestName(); + + assertAccessControlProviderExistence(accessControlProviderName, false); + + int responseCode = createAccessControlProvider(accessControlProviderName); + assertEquals("Access control provider creation should be allowed", 201, responseCode); + + assertAccessControlProviderExistence(accessControlProviderName, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, accessControlProviderName); + attributes.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileGroupManagerFactory.PATH, "/path/to/file"); + responseCode = getRestTestHelper().submitRequest("/rest/accesscontrolprovider/" + accessControlProviderName, "PUT", attributes); + assertEquals("Setting of access control provider attributes should be denied", 403, responseCode); + } + + /* === Utility Methods === */ + private int createPort(String portName) throws Exception { Map<String, Object> attributes = new HashMap<String, Object>(); @@ -395,6 +905,18 @@ public class BrokerACLTest extends QpidRestTestCase assertEquals("Unexpected result", exists, !hosts.isEmpty()); } + private void assertKeyStoreExistence(String keyStoreName, boolean exists) throws Exception + { + List<Map<String, Object>> keyStores = getRestTestHelper().getJsonAsList("/rest/keystore/" + keyStoreName); + assertEquals("Unexpected result", exists, !keyStores.isEmpty()); + } + + private void assertTrustStoreExistence(String trustStoreName, boolean exists) throws Exception + { + List<Map<String, Object>> trustStores = getRestTestHelper().getJsonAsList("/rest/truststore/" + trustStoreName); + assertEquals("Unexpected result", exists, !trustStores.isEmpty()); + } + private int createHost(String hostName) throws Exception { Map<String, Object> hostData = new HashMap<String, Object>(); @@ -452,4 +974,61 @@ public class BrokerACLTest extends QpidRestTestCase assertEquals("Unexpected result", exists, !providers.isEmpty()); } + private int createKeyStore(String name, String certAlias) throws IOException, JsonGenerationException, JsonMappingException + { + Map<String, Object> keyStoreAttributes = new HashMap<String, Object>(); + keyStoreAttributes.put(KeyStore.NAME, name); + keyStoreAttributes.put(KeyStore.PATH, TestSSLConstants.KEYSTORE); + keyStoreAttributes.put(KeyStore.PASSWORD, TestSSLConstants.KEYSTORE_PASSWORD); + keyStoreAttributes.put(KeyStore.CERTIFICATE_ALIAS, certAlias); + + return getRestTestHelper().submitRequest("/rest/keystore/" + name, "PUT", keyStoreAttributes); + } + + private int createTrustStore(String name, boolean peersOnly) throws IOException, JsonGenerationException, JsonMappingException + { + Map<String, Object> trustStoreAttributes = new HashMap<String, Object>(); + trustStoreAttributes.put(TrustStore.NAME, name); + trustStoreAttributes.put(TrustStore.PATH, TestSSLConstants.KEYSTORE); + trustStoreAttributes.put(TrustStore.PASSWORD, TestSSLConstants.KEYSTORE_PASSWORD); + trustStoreAttributes.put(TrustStore.PEERS_ONLY, peersOnly); + + return getRestTestHelper().submitRequest("/rest/truststore/" + name, "PUT", trustStoreAttributes); + } + + private void assertGroupProviderExistence(String groupProviderName, boolean exists) throws Exception + { + String path = "/rest/groupprovider/" + groupProviderName; + List<Map<String, Object>> providers = getRestTestHelper().getJsonAsList(path); + assertEquals("Unexpected result", exists, !providers.isEmpty()); + } + + private int createGroupProvider(String groupProviderName) throws Exception + { + File file = TestFileUtils.createTempFile(this, ".groups"); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, groupProviderName); + attributes.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileGroupManagerFactory.PATH, file.getAbsoluteFile()); + + return getRestTestHelper().submitRequest("/rest/groupprovider/" + groupProviderName, "PUT", attributes); + } + + private void assertAccessControlProviderExistence(String accessControlProviderName, boolean exists) throws Exception + { + String path = "/rest/accesscontrolprovider/" + accessControlProviderName; + List<Map<String, Object>> providers = getRestTestHelper().getJsonAsList(path); + assertEquals("Unexpected result", exists, !providers.isEmpty()); + } + + private int createAccessControlProvider(String accessControlProviderName) throws Exception + { + File file = TestFileUtils.createTempFile(this, ".acl", _secondaryAclFileContent); + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AccessControlProvider.NAME, accessControlProviderName); + attributes.put(AccessControlProvider.TYPE, FileAccessControlProviderConstants.ACL_FILE_PROVIDER_TYPE); + attributes.put(FileAccessControlProviderConstants.PATH, file.getAbsoluteFile()); + + return getRestTestHelper().submitRequest("/rest/accesscontrolprovider/" + accessControlProviderName, "PUT", attributes); + } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java index 40ea723b1e..3fceb27a4b 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java @@ -29,14 +29,13 @@ import java.util.Properties; import javax.servlet.http.HttpServletResponse; import org.apache.commons.configuration.ConfigurationException; -import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.security.acl.AbstractACLTestCase; import org.apache.qpid.systest.rest.QpidRestTestCase; import org.apache.qpid.test.utils.TestBrokerConfiguration; public class GroupRestACLTest extends QpidRestTestCase { - private static final String FILE_GROUP_MANAGER = "FileGroupManager"; + private static final String FILE_GROUP_MANAGER = TestBrokerConfiguration.ENTRY_NAME_GROUP_FILE; private static final String ALLOWED_GROUP = "allowedGroup"; private static final String DENIED_GROUP = "deniedGroup"; @@ -52,7 +51,7 @@ public class GroupRestACLTest extends QpidRestTestCase public void setUp() throws Exception { _groupFile = createTemporaryGroupFile(); - getBrokerConfiguration().setBrokerAttribute(Broker.GROUP_FILE, _groupFile.getAbsolutePath()); + getBrokerConfiguration().addGroupFileConfiguration(_groupFile.getAbsolutePath()); //DONT call super.setUp(), the tests will start the broker after configuring it } @@ -191,7 +190,7 @@ public class GroupRestACLTest extends QpidRestTestCase private void assertNumberOfGroupMembers(String groupName, int expectedNumberOfMembers) throws IOException { - Map<String, Object> group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/" + groupName); + Map<String, Object> group = getRestTestHelper().getJsonAsSingletonList("/rest/group/" + FILE_GROUP_MANAGER + "/" + groupName); getRestTestHelper().assertNumberOfGroupMembers(group, expectedNumberOfMembers); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java index 12973113d8..3b81df6fd1 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java @@ -29,7 +29,6 @@ import java.util.Properties; import javax.servlet.http.HttpServletResponse; import org.apache.commons.configuration.ConfigurationException; -import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.security.acl.AbstractACLTestCase; import org.apache.qpid.systest.rest.QpidRestTestCase; import org.apache.qpid.test.utils.TestBrokerConfiguration; @@ -52,7 +51,7 @@ public class UserRestACLTest extends QpidRestTestCase public void setUp() throws Exception { _groupFile = createTemporaryGroupFile(); - getBrokerConfiguration().setBrokerAttribute(Broker.GROUP_FILE, _groupFile.getAbsolutePath()); + getBrokerConfiguration().addGroupFileConfiguration(_groupFile.getAbsolutePath()); getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER, OTHER_USER); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/JMSDestinationTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/JMSDestinationTest.java index 3cc15d5e9d..1ee5b997f2 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/JMSDestinationTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/message/JMSDestinationTest.java @@ -142,7 +142,7 @@ public class JMSDestinationTest extends QpidBrokerTestCase { // Setup JMXUtils JMXTestUtils jmxUtils = new JMXTestUtils(this); - jmxUtils.setUp(); + // Open the JMX Connection jmxUtils.open(); try diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java index 4e9477f4b6..734e3f2268 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java @@ -42,8 +42,9 @@ public class DynamicQueueExchangeCreateTest extends QpidBrokerTestCase @Override public void setUp() throws Exception { + getBrokerConfiguration().addJmxManagementConfiguration(); + _jmxUtils = new JMXTestUtils(this); - _jmxUtils.setUp(); super.setUp(); _jmxUtils.open(); diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java index a9ac028af6..4b61b6269c 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java @@ -24,6 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQNoRouteException; import org.apache.qpid.client.AMQQueue; import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.AMQTopic; @@ -31,6 +32,7 @@ import org.apache.qpid.management.common.JMXConnnectionFactory; import org.apache.qpid.test.utils.QpidBrokerTestCase; import javax.jms.Connection; +import javax.jms.ExceptionListener; import javax.jms.InvalidDestinationException; import javax.jms.InvalidSelectorException; import javax.jms.JMSException; @@ -46,6 +48,8 @@ import javax.management.ObjectName; import javax.management.remote.JMXConnector; import java.io.IOException; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * @todo Code to check that a consumer gets only one particular method could be factored into a re-usable method (as @@ -58,7 +62,11 @@ import java.util.Set; public class DurableSubscriptionTest extends QpidBrokerTestCase { private static final Logger _logger = LoggerFactory.getLogger(DurableSubscriptionTest.class); - + + private static final String MY_TOPIC = "MyTopic"; + + private static final String MY_SUBSCRIPTION = "MySubscription"; + /** Timeout for receive() if we are expecting a message */ private static final long POSITIVE_RECEIVE_TIMEOUT = 2000; @@ -80,24 +88,29 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase public void tearDown() throws Exception { - if(_jmxConnected) + try { - try + if(_jmxConnected) { - _jmxc.close(); - } - catch (IOException e) - { - e.printStackTrace(); + try + { + _jmxc.close(); + } + catch (IOException e) + { + _logger.error("Error closing JMX connection", e); + } } } - - super.tearDown(); + finally + { + super.tearDown(); + } } - + public void testUnsubscribe() throws Exception { - AMQConnection con = (AMQConnection) getConnection("guest", "guest"); + AMQConnection con = (AMQConnection) getConnection(); AMQTopic topic = new AMQTopic(con, "MyDurableSubscriptionTestTopic"); _logger.info("Create Session 1"); Session session1 = con.createSession(false, AMQSession.NO_ACKNOWLEDGE); @@ -109,7 +122,7 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase _logger.info("Create Session 2"); Session session2 = con.createSession(false, AMQSession.NO_ACKNOWLEDGE); _logger.info("Create Durable Subscriber on Session 2"); - TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, "MySubscription"); + TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, MY_SUBSCRIPTION); _logger.info("Starting connection"); con.start(); @@ -118,7 +131,7 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase producer.send(session1.createTextMessage("A")); //check the dur sub's underlying queue now has msg count 1 - AMQQueue subQueue = new AMQQueue("amq.topic", "clientid" + ":" + "MySubscription"); + AMQQueue subQueue = new AMQQueue("amq.topic", "clientid" + ":" + MY_SUBSCRIPTION); assertEquals("Msg count should be 1", 1, ((AMQSession<?, ?>) session1).getQueueDepth(subQueue, true)); Message msg; @@ -143,7 +156,7 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase consumer2.close(); _logger.info("Unsubscribe session2/consumer2"); - session2.unsubscribe("MySubscription"); + session2.unsubscribe(MY_SUBSCRIPTION); ((AMQSession<?, ?>) session2).sync(); @@ -157,7 +170,7 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase _mbsc = _jmxc.getMBeanServerConnection(); //must replace the occurrence of ':' in queue name with '-' - String queueObjectNameText = "clientid" + "-" + "MySubscription"; + String queueObjectNameText = "clientid" + "-" + MY_SUBSCRIPTION; ObjectName objName = new ObjectName("org.apache.qpid:type=VirtualHost.Queue,name=" + queueObjectNameText + ",*"); @@ -189,7 +202,74 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase _logger.info("Close connection"); con.close(); } - + + + /** + * Specifically uses a subscriber with a selector because QPID-4731 found that selectors + * can prevent queue removal. + */ + public void testUnsubscribeWhenUsingSelectorMakesTopicUnreachable() throws Exception + { + setTestClientSystemProperty("qpid.default_mandatory_topic","true"); + + // set up subscription + AMQConnection connection = (AMQConnection) getConnection(); + connection.start(); + + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Topic topic = new AMQTopic(connection, MY_TOPIC); + MessageProducer producer = session.createProducer(topic); + + TopicSubscriber subscriber = session.createDurableSubscriber(topic, MY_SUBSCRIPTION, "1 = 1", false); + StoringExceptionListener exceptionListener = new StoringExceptionListener(); + connection.setExceptionListener(exceptionListener); + + // send message and verify it was consumed + producer.send(session.createTextMessage("message1")); + assertNotNull("Message should have been successfully received", subscriber.receive(POSITIVE_RECEIVE_TIMEOUT)); + assertEquals(null, exceptionListener.getException()); + session.unsubscribe(MY_SUBSCRIPTION); + + // send another message and verify that the connection exception listener was fired. + StoringExceptionListener exceptionListener2 = new StoringExceptionListener(); + connection.setExceptionListener(exceptionListener2); + + producer.send(session.createTextMessage("message that should be unroutable")); + ((AMQSession<?, ?>) session).sync(); + + JMSException exception = exceptionListener2.awaitException(); + assertNotNull("Expected exception as message should no longer be routable", exception); + + Throwable linkedException = exception.getLinkedException(); + assertNotNull("The linked exception of " + exception + " should be the 'no route' exception", linkedException); + assertEquals(AMQNoRouteException.class, linkedException.getClass()); + } + + private final class StoringExceptionListener implements ExceptionListener + { + private volatile JMSException _exception; + private CountDownLatch _latch = new CountDownLatch(1); + + @Override + public void onException(JMSException exception) + { + _exception = exception; + _logger.info("Exception listener received: " + exception); + _latch.countDown(); + } + + public JMSException awaitException() throws InterruptedException + { + _latch.await(POSITIVE_RECEIVE_TIMEOUT, TimeUnit.MILLISECONDS); + return _exception; + } + + public JMSException getException() + { + return _exception; + } + } + public void testDurabilityNOACK() throws Exception { durabilityImpl(AMQSession.NO_ACKNOWLEDGE, false); @@ -223,8 +303,8 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase private void durabilityImpl(int ackMode, boolean restartBroker) throws Exception { - AMQConnection con = (AMQConnection) getConnection("guest", "guest"); - AMQTopic topic = new AMQTopic(con, "MyTopic"); + AMQConnection con = (AMQConnection) getConnection(); + AMQTopic topic = new AMQTopic(con, MY_TOPIC); Session session1 = con.createSession(false, ackMode); MessageConsumer consumer1 = session1.createConsumer(topic); @@ -232,7 +312,7 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase MessageProducer producer = sessionProd.createProducer(topic); Session session2 = con.createSession(false, ackMode); - TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, "MySubscription"); + TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, MY_SUBSCRIPTION); con.start(); @@ -267,13 +347,13 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase consumer2.close(); session2.close(); - + //Send message C, then connect consumer 3 to durable subscription and get //message B if not using NO_ACK, then receive C with consumer 1 and 3 producer.send(session1.createTextMessage("C")); Session session3 = con.createSession(false, ackMode); - MessageConsumer consumer3 = session3.createDurableSubscriber(topic, "MySubscription"); + MessageConsumer consumer3 = session3.createDurableSubscriber(topic, MY_SUBSCRIPTION); if(ackMode == AMQSession.NO_ACKNOWLEDGE) { @@ -307,7 +387,7 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase consumer1.close(); consumer3.close(); - session3.unsubscribe("MySubscription"); + session3.unsubscribe(MY_SUBSCRIPTION); con.close(); @@ -328,28 +408,28 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase { Message msg; // Create producer. - AMQConnection con0 = (AMQConnection) getConnection("guest", "guest"); + AMQConnection con0 = (AMQConnection) getConnection(); con0.start(); Session session0 = con0.createSession(false, ackMode); - AMQTopic topic = new AMQTopic(con0, "MyTopic"); + AMQTopic topic = new AMQTopic(con0, MY_TOPIC); Session sessionProd = con0.createSession(false, ackMode); MessageProducer producer = sessionProd.createProducer(topic); // Create consumer 1. - AMQConnection con1 = (AMQConnection) getConnection("guest", "guest"); + AMQConnection con1 = (AMQConnection) getConnection(); con1.start(); Session session1 = con1.createSession(false, ackMode); MessageConsumer consumer1 = session1.createConsumer(topic); // Create consumer 2. - AMQConnection con2 = (AMQConnection) getConnection("guest", "guest"); + AMQConnection con2 = (AMQConnection) getConnection(); con2.start(); Session session2 = con2.createSession(false, ackMode); - TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, "MySubscription"); + TopicSubscriber consumer2 = session2.createDurableSubscriber(topic, MY_SUBSCRIPTION); // Send message and check that both consumers get it and only it. producer.send(session0.createTextMessage("A")); @@ -393,11 +473,11 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase // Re-attach a new consumer to the durable subscription, and check that it gets message B it left (if not NO_ACK) // and also gets message C sent after it was disconnected. - AMQConnection con3 = (AMQConnection) getConnection("guest", "guest"); + AMQConnection con3 = (AMQConnection) getConnection(); con3.start(); Session session3 = con3.createSession(false, ackMode); - TopicSubscriber consumer3 = session3.createDurableSubscriber(topic, "MySubscription"); + TopicSubscriber consumer3 = session3.createDurableSubscriber(topic, MY_SUBSCRIPTION); if(ackMode == AMQSession.NO_ACKNOWLEDGE) { @@ -423,7 +503,7 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase consumer1.close(); consumer3.close(); - session3.unsubscribe("MySubscription"); + session3.unsubscribe(MY_SUBSCRIPTION); con0.close(); con1.close(); @@ -540,7 +620,7 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase TopicSubscriber subB = session.createDurableSubscriber(topic, "testResubscribeWithChangedSelector","Match = False", false); - //verify no messages are now recieved. + //verify no messages are now received. rMsg = subB.receive(NEGATIVE_RECEIVE_TIMEOUT); assertNull("Should not have received message as the selector was changed", rMsg); @@ -746,7 +826,7 @@ public class DurableSubscriptionTest extends QpidBrokerTestCase * <li>create another durable subscriber with a selector and same name * <li>check first subscriber is now closed * <li>create a publisher and send messages - * <li>check messages are recieved correctly + * <li>check messages are received correctly * </ul> * <p> * QPID-2418 diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java index 6e6e3271f0..b8ca4b3f7f 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java @@ -21,7 +21,6 @@ package org.apache.qpid.test.utils; import junit.framework.TestCase; -import org.apache.commons.configuration.ConfigurationException; import org.apache.qpid.management.common.JMXConnnectionFactory; import org.apache.qpid.management.common.mbeans.LoggingManagement; @@ -31,8 +30,6 @@ import org.apache.qpid.management.common.mbeans.ManagedExchange; import org.apache.qpid.management.common.mbeans.ManagedQueue; import org.apache.qpid.management.common.mbeans.ServerInformation; import org.apache.qpid.management.common.mbeans.UserManagement; -import org.apache.qpid.server.model.Plugin; -import org.apache.qpid.server.plugin.PluginFactory; import javax.management.InstanceNotFoundException; import javax.management.JMException; @@ -47,9 +44,7 @@ import javax.management.ObjectName; import javax.management.remote.JMXConnector; import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Set; /** @@ -79,11 +74,6 @@ public class JMXTestUtils this(test, DEFAULT_USERID, DEFAULT_PASSWORD); } - public void setUp() throws IOException, ConfigurationException, Exception - { - _test.getBrokerConfiguration().addJmxManagementConfiguration(); - } - public void open() throws Exception { open(0); // Zero signifies default broker to QBTC. diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java index ba7fac3312..c14c724419 100755 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidBrokerTestCase.java @@ -184,6 +184,7 @@ public class QpidBrokerTestCase extends QpidTestCase protected List<Connection> _connections = new ArrayList<Connection>(); public static final String QUEUE = "queue"; public static final String TOPIC = "topic"; + public static final String MANAGEMENT_MODE_PASSWORD = "mm_password"; /** Map to hold test defined environment properties */ private Map<String, String> _env; @@ -467,6 +468,10 @@ public class QpidBrokerTestCase extends QpidTestCase options.setConfigurationStoreType(_brokerStoreType); options.setConfigurationStoreLocation(testConfig); options.setManagementMode(managementMode); + if (managementMode) + { + options.setManagementModePassword(MANAGEMENT_MODE_PASSWORD); + } //Set the log config file, relying on the log4j.configuration system property //set on the JVM by the JUnit runner task in module.xml. @@ -486,9 +491,11 @@ public class QpidBrokerTestCase extends QpidTestCase String[] cmd = _brokerCommandHelper.getBrokerCommand(port, testConfig, _brokerStoreType, _logConfigFile); if (managementMode) { - String[] newCmd = new String[cmd.length + 1]; + String[] newCmd = new String[cmd.length + 3]; System.arraycopy(cmd, 0, newCmd, 0, cmd.length); newCmd[cmd.length] = "-mm"; + newCmd[cmd.length + 1] = "-mmpass"; + newCmd[cmd.length + 2] = MANAGEMENT_MODE_PASSWORD; cmd = newCmd; } _logger.info("Starting spawn broker using command: " + StringUtils.join(cmd, ' ')); @@ -849,7 +856,7 @@ public class QpidBrokerTestCase extends QpidTestCase attributes.put(VirtualHost.NAME, virtualHostName); attributes.put(VirtualHost.CONFIG_PATH, System.getProperty("broker.virtualhosts-config")); int port = getPort(brokerPort); - getBrokerConfiguration(port).addHostConfiguration(attributes); + getBrokerConfiguration(port).addVirtualHostConfiguration(attributes); } /** diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/TestBrokerConfiguration.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/TestBrokerConfiguration.java index db10bfb7e7..a91c9bb752 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/TestBrokerConfiguration.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/TestBrokerConfiguration.java @@ -21,6 +21,7 @@ package org.apache.qpid.test.utils; import java.io.File; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -29,13 +30,20 @@ import java.util.Set; import java.util.UUID; import org.apache.qpid.server.configuration.ConfigurationEntry; +import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.configuration.store.MemoryConfigurationEntryStore; +import org.apache.qpid.server.model.AccessControlProvider; import org.apache.qpid.server.model.AuthenticationProvider; +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.TrustStore; import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.plugin.PluginFactory; +import org.apache.qpid.server.security.access.FileAccessControlProviderConstants; +import org.apache.qpid.server.security.group.FileGroupManagerFactory; public class TestBrokerConfiguration { @@ -52,13 +60,16 @@ public class TestBrokerConfiguration public static final String ENTRY_NAME_JMX_MANAGEMENT = "MANAGEMENT-JMX"; public static final String MANAGEMENT_JMX_PLUGIN_TYPE = "MANAGEMENT-JMX"; public static final String ENTRY_NAME_ANONYMOUS_PROVIDER = "anonymous"; + public static final String ENTRY_NAME_SSL_KEYSTORE = "systestsKeyStore"; + public static final String ENTRY_NAME_SSL_TRUSTSTORE = "systestsTrustStore"; + public static final String ENTRY_NAME_GROUP_FILE = "groupFile"; + public static final String ENTRY_NAME_ACL_FILE = "aclFile"; private MemoryConfigurationEntryStore _store; private boolean _saved; public TestBrokerConfiguration(String storeType, String intialStoreLocation) { - // TODO: add support for DERBY store _store = new MemoryConfigurationEntryStore(intialStoreLocation, null); } @@ -105,7 +116,7 @@ public class TestBrokerConfiguration public UUID addObjectConfiguration(String name, String type, Map<String, Object> attributes) { - UUID id = UUIDGenerator.generateBrokerChildUUID(type, name); + UUID id = UUIDGenerator.generateRandomUUID(); addObjectConfiguration(id, type, attributes); return id; } @@ -126,13 +137,33 @@ public class TestBrokerConfiguration return addObjectConfiguration(ENTRY_NAME_HTTP_MANAGEMENT, Plugin.class.getSimpleName(), attributes); } + public UUID addGroupFileConfiguration(String groupFilePath) + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(GroupProvider.NAME, ENTRY_NAME_GROUP_FILE); + attributes.put(GroupProvider.TYPE, FileGroupManagerFactory.GROUP_FILE_PROVIDER_TYPE); + attributes.put(FileGroupManagerFactory.PATH, groupFilePath); + + return addGroupProviderConfiguration(attributes); + } + + public UUID addAclFileConfiguration(String aclFilePath) + { + Map<String, Object> attributes = new HashMap<String, Object>(); + attributes.put(AccessControlProvider.NAME, ENTRY_NAME_ACL_FILE); + attributes.put(AccessControlProvider.TYPE, FileAccessControlProviderConstants.ACL_FILE_PROVIDER_TYPE); + attributes.put(FileAccessControlProviderConstants.PATH, aclFilePath); + + return addAccessControlConfiguration(attributes); + } + public UUID addPortConfiguration(Map<String, Object> attributes) { String name = (String) attributes.get(Port.NAME); return addObjectConfiguration(name, Port.class.getSimpleName(), attributes); } - public UUID addHostConfiguration(Map<String, Object> attributes) + public UUID addVirtualHostConfiguration(Map<String, Object> attributes) { String name = (String) attributes.get(VirtualHost.NAME); return addObjectConfiguration(name, VirtualHost.class.getSimpleName(), attributes); @@ -144,6 +175,30 @@ public class TestBrokerConfiguration return addObjectConfiguration(name, AuthenticationProvider.class.getSimpleName(), attributes); } + public UUID addGroupProviderConfiguration(Map<String, Object> attributes) + { + String name = (String) attributes.get(GroupProvider.NAME); + return addObjectConfiguration(name, GroupProvider.class.getSimpleName(), attributes); + } + + public UUID addAccessControlConfiguration(Map<String, Object> attributes) + { + String name = (String) attributes.get(AccessControlProvider.NAME); + return addObjectConfiguration(name, AccessControlProvider.class.getSimpleName(), attributes); + } + + public UUID addTrustStoreConfiguration(Map<String, Object> attributes) + { + String name = (String) attributes.get(TrustStore.NAME); + return addObjectConfiguration(name, TrustStore.class.getSimpleName(), attributes); + } + + public UUID addKeyStoreConfiguration(Map<String, Object> attributes) + { + String name = (String) attributes.get(KeyStore.NAME); + return addObjectConfiguration(name, KeyStore.class.getSimpleName(), attributes); + } + private boolean setObjectAttributes(ConfigurationEntry entry, Map<String, Object> attributes) { Map<String, Object> newAttributes = new HashMap<String, Object>(entry.getAttributes()); @@ -188,6 +243,11 @@ public class TestBrokerConfiguration { ConfigurationEntry entry = new ConfigurationEntry(id, type, attributes, Collections.<UUID> emptySet(), _store); ConfigurationEntry root = _store.getRootEntry(); + + Map<String, Collection<ConfigurationEntry>> children = root.getChildren(); + + verifyChildWithNameDoesNotExist(id, type, attributes, children); + Set<UUID> childrenIds = new HashSet<UUID>(root.getChildrenIds()); childrenIds.add(id); ConfigurationEntry newRoot = new ConfigurationEntry(root.getId(), root.getType(), root.getAttributes(), childrenIds, @@ -195,6 +255,26 @@ public class TestBrokerConfiguration _store.save(newRoot, entry); } + private void verifyChildWithNameDoesNotExist(UUID id, String type, + Map<String, Object> attributes, + Map<String, Collection<ConfigurationEntry>> children) + { + Collection<ConfigurationEntry> childrenOfType = children.get(type); + + if(childrenOfType != null) + { + String name = (String) attributes.get("name"); + for(ConfigurationEntry ce : childrenOfType) + { + Object ceName = ce.getAttributes().get("name"); + if(name.equals(ceName) && !id.equals(ce.getId())) + { + throw new IllegalConfigurationException("A " + type + " with name " + name + " already exists with a different ID"); + } + } + } + } + private boolean setObjectAttribute(ConfigurationEntry entry, String attributeName, Object value) { Map<String, Object> attributes = new HashMap<String, Object>(entry.getAttributes()); diff --git a/qpid/java/test-profiles/CPPExcludes b/qpid/java/test-profiles/CPPExcludes index 8b74a19b8e..b429305572 100755 --- a/qpid/java/test-profiles/CPPExcludes +++ b/qpid/java/test-profiles/CPPExcludes @@ -60,6 +60,7 @@ org.apache.qpid.test.client.timeouts.SyncWaitTimeoutDelayTest#* // c++ broker doesn't support message bouncing org.apache.qpid.server.exchange.ReturnUnroutableMandatoryMessageTest#* +org.apache.qpid.test.unit.topic.DurableSubscriptionTest#testUnsubscribeWhenUsingSelectorMakesTopicUnreachable // c++ broker expires messages on delivery or when the queue cleaner thread runs. org.apache.qpid.server.queue.TimeToLiveTest#testActiveTTL diff --git a/qpid/java/test-profiles/Excludes b/qpid/java/test-profiles/Excludes index 9c07fea574..7a6089004e 100644 --- a/qpid/java/test-profiles/Excludes +++ b/qpid/java/test-profiles/Excludes @@ -17,16 +17,4 @@ // under the License. // -// -// QPID-2031 : Broker is not cleanly shutdown by QpidTestCase -// -org.apache.qpid.server.logging.BrokerLoggingTest#testBrokerShutdownListeningTCPDefault -org.apache.qpid.server.logging.BrokerLoggingTest#testBrokerShutdownListeningTCPSSL -org.apache.qpid.server.logging.BrokerLoggingTest#testBrokerShutdownStopped -org.apache.qpid.server.logging.VirtualHostLoggingTest#testVirtualhostClosure -org.apache.qpid.server.logging.MemoryMessageStoreLoggingTest#testMessageStoreClose - -// QPID-3424 : Test fails to start external broker due to Derby Exception. -org.apache.qpid.server.logging.DerbyMessageStoreLoggingTest#* - org.apache.qpid.client.ssl.SSLTest#testVerifyLocalHostLocalDomain diff --git a/qpid/java/test-profiles/Java010Excludes b/qpid/java/test-profiles/Java010Excludes index c4b3ac8d66..6ae5a40f89 100755 --- a/qpid/java/test-profiles/Java010Excludes +++ b/qpid/java/test-profiles/Java010Excludes @@ -25,6 +25,7 @@ org.apache.qpid.test.client.FlowControlTest#* // 0-10 protocol doesn't support message bouncing org.apache.qpid.server.exchange.ReturnUnroutableMandatoryMessageTest#* +org.apache.qpid.test.unit.topic.DurableSubscriptionTest#testUnsubscribeWhenUsingSelectorMakesTopicUnreachable // 0-10 and 0-9 connections dont generate the exact same logging due to protocol differences org.apache.qpid.server.logging.ChannelLoggingTest#testChannelStartsFlowStopped |