From 881f4a51e4b9e44f888366fc06f6070f47921b90 Mon Sep 17 00:00:00 2001 From: Robert Gemmell Date: Fri, 24 Aug 2012 15:33:00 +0000 Subject: QPID-4236, QPID-4237, QPID-4245: Added group management functionality, and exposed it via the web management interface. Removed group definition functionality from the ACL plugin. The ACL plugin uses groups but no longer defines them. Introduced SubjectCreator as a facade to AuthenticationManager and group management classes. Applied access control to user and group management. Fixed bug in choosing between update and creation in RestServlet. Moved lots of RestTest helper methods to RestTestHelper. Fixed authentication, authorisation, and status logging when using the web ui. Applied patch from Philip Harvey , Keith Wall and myself. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1376968 13f79535-47bb-0310-9956-ffa450edef68 --- .../security/access/config/PlainConfiguration.java | 70 ++- .../qpid/server/security/access/config/Rule.java | 20 +- .../server/security/access/config/RuleSet.java | 61 +-- .../security/access/plugins/AccessControl.java | 8 +- .../access/plugins/AccessControlConfiguration.java | 4 +- .../security/access/plugins/AccessControlTest.java | 51 +- .../access/plugins/PlainConfigurationTest.java | 86 +++- .../security/access/plugins/RuleSetTest.java | 92 ++-- .../broker-plugins/management-http/MANIFEST.MF | 1 + .../qpid/server/management/plugin/Management.java | 6 + .../plugin/servlet/rest/AbstractServlet.java | 319 ++++++++---- .../plugin/servlet/rest/LogRecordsServlet.java | 2 +- .../plugin/servlet/rest/MessageContentServlet.java | 2 +- .../plugin/servlet/rest/MessageServlet.java | 6 +- .../plugin/servlet/rest/RestServlet.java | 16 +- .../plugin/servlet/rest/SaslServlet.java | 44 +- .../plugin/servlet/rest/StructureServlet.java | 4 +- ...showPrincipalDatabaseAuthenticationManager.html | 2 - .../main/java/resources/group/addGroupMember.html | 37 ++ .../src/main/java/resources/group/showGroup.html | 30 ++ .../java/resources/groupprovider/addGroup.html | 38 ++ .../groupprovider/showFileGroupManager.html | 28 ++ .../src/main/java/resources/js/qpid/common/util.js | 7 +- .../java/resources/js/qpid/management/Exchange.js | 2 +- .../resources/js/qpid/management/GroupProvider.js | 109 ++++ .../resources/js/qpid/management/VirtualHost.js | 4 +- .../resources/js/qpid/management/controller.js | 8 +- .../resources/js/qpid/management/group/Group.js | 215 ++++++++ .../js/qpid/management/group/addGroupMember.js | 108 ++++ .../management/groupprovider/FileGroupManager.js | 251 ++++++++++ .../java/resources/js/qpid/management/treeView.js | 6 +- .../src/main/java/resources/showGroupProvider.html | 25 + .../rest/AuthenticationProviderRestTest.java | 4 +- .../plugin/servlet/rest/BindingRestTest.java | 32 +- .../plugin/servlet/rest/BrokerRestHttpsTest.java | 32 +- .../plugin/servlet/rest/BrokerRestTest.java | 12 +- .../plugin/servlet/rest/ConnectionRestTest.java | 16 +- .../plugin/servlet/rest/ExchangeRestTest.java | 10 +- .../plugin/servlet/rest/GroupProviderRestTest.java | 160 ++++++ .../plugin/servlet/rest/GroupRestTest.java | 109 ++++ .../plugin/servlet/rest/LogRecordsRestTest.java | 4 +- .../plugin/servlet/rest/MessagesRestTest.java | 48 +- .../plugin/servlet/rest/PortRestTest.java | 10 +- .../plugin/servlet/rest/QpidRestTestCase.java | 204 +------- .../plugin/servlet/rest/QueueRestTest.java | 22 +- .../plugin/servlet/rest/RestTestHelper.java | 417 ++++++++++++++++ .../plugin/servlet/rest/SaslRestTest.java | 2 +- .../plugin/servlet/rest/StructureRestTest.java | 14 +- .../plugin/servlet/rest/UserRestTest.java | 51 +- .../plugin/servlet/rest/VirtualHostRestTest.java | 116 ++--- .../qpid/systest/rest/acl/GroupRestACLTest.java | 187 +++++++ .../qpid/systest/rest/acl/UserRestACLTest.java | 188 +++++++ .../qpid/server/jmx/JMXManagedObjectRegistry.java | 13 +- .../server/jmx/MBeanInvocationHandlerImpl.java | 16 +- qpid/java/broker/etc/broker_example.acl | 24 +- qpid/java/broker/etc/config.xml | 19 +- qpid/java/broker/etc/groups | 29 ++ .../handler/ConnectionSecureOkMethodHandler.java | 9 +- .../handler/ConnectionStartOkMethodHandler.java | 11 +- .../logging/actors/AbstractManagementActor.java | 48 ++ .../server/logging/actors/HttpManagementActor.java | 62 +++ .../server/logging/actors/ManagementActor.java | 62 +-- .../server/logging/subjects/LogSubjectFormat.java | 8 + .../java/org/apache/qpid/server/model/Group.java | 52 ++ .../org/apache/qpid/server/model/GroupMember.java | 52 ++ .../apache/qpid/server/model/GroupProvider.java | 51 ++ .../java/org/apache/qpid/server/model/Model.java | 5 + .../apache/qpid/server/model/UUIDGenerator.java | 10 + .../java/org/apache/qpid/server/model/User.java | 2 - .../adapter/AuthenticationProviderAdapter.java | 62 +-- .../qpid/server/model/adapter/BrokerAdapter.java | 75 ++- .../server/model/adapter/GroupProviderAdapter.java | 548 +++++++++++++++++++++ .../apache/qpid/server/plugins/PluginManager.java | 23 +- .../qpid/server/protocol/AMQProtocolEngine.java | 5 +- .../protocol/MultiVersionProtocolEngine.java | 2 +- .../qpid/server/protocol/ProtocolEngine_1_0_0.java | 7 +- .../server/protocol/ProtocolEngine_1_0_0_SASL.java | 10 +- .../qpid/server/registry/ApplicationRegistry.java | 55 ++- .../qpid/server/registry/IApplicationRegistry.java | 22 +- .../qpid/server/security/AuthorizationHolder.java | 12 +- .../qpid/server/security/SecurityManager.java | 23 + .../qpid/server/security/SubjectCreator.java | 137 ++++++ .../qpid/server/security/access/ObjectType.java | 6 +- .../security/auth/AuthenticatedPrincipal.java | 126 +++++ .../server/security/auth/AuthenticationResult.java | 63 ++- .../security/auth/SubjectAuthenticationResult.java | 76 +++ .../server/security/auth/UsernamePrincipal.java | 76 +++ .../AbstractPasswordFilePrincipalDatabase.java | 2 +- .../auth/database/PropertiesPrincipalDatabase.java | 2 +- .../manager/AnonymousAuthenticationManager.java | 12 +- .../auth/manager/AuthenticationManager.java | 12 +- .../manager/AuthenticationManagerRegistry.java | 39 +- .../manager/ExternalAuthenticationManager.java | 7 +- .../manager/IAuthenticationManagerRegistry.java | 18 +- .../manager/KerberosAuthenticationManager.java | 8 +- .../PrincipalDatabaseAuthenticationManager.java | 32 +- .../manager/SimpleLDAPAuthenticationManager.java | 16 +- .../auth/rmi/RMIPasswordAuthenticator.java | 26 +- .../server/security/auth/sasl/GroupPrincipal.java | 99 ---- .../auth/sasl/UsernamePasswordInitialiser.java | 1 + .../security/auth/sasl/UsernamePrincipal.java | 99 ---- .../auth/sasl/anonymous/AnonymousSaslServer.java | 4 +- .../server/security/group/FileGroupDatabase.java | 265 ++++++++++ .../server/security/group/FileGroupManager.java | 251 ++++++++++ .../qpid/server/security/group/GroupDatabase.java | 34 ++ .../qpid/server/security/group/GroupManager.java | 42 ++ .../security/group/GroupManagerPluginFactory.java | 27 + .../qpid/server/security/group/GroupPrincipal.java | 99 ++++ .../security/group/GroupPrincipalAccessor.java | 51 ++ .../apache/qpid/server/state/AMQStateManager.java | 11 +- .../server/transport/ServerConnectionDelegate.java | 20 +- .../actors/AbstractManagementActorTest.java | 86 ++++ .../logging/actors/HttpManagementActorTest.java | 94 ++++ .../server/logging/actors/ManagementActorTest.java | 12 +- .../qpid/server/model/UUIDGeneratorTest.java | 6 +- .../protocol/InternalTestProtocolSession.java | 22 +- .../qpid/server/security/SubjectCreatorTest.java | 138 ++++++ .../security/auth/AuthenticatedPrincipalTest.java | 147 ++++++ .../auth/AuthenticatedPrincipalTestHelper.java | 54 ++ .../security/auth/AuthenticationResultTest.java | 112 +++++ .../server/security/auth/TestPrincipalUtils.java | 49 ++ .../security/auth/UsernamePrincipalTest.java | 70 +++ ...Base64MD5PasswordFilePrincipalDatabaseTest.java | 2 +- .../PlainPasswordFilePrincipalDatabaseTest.java | 2 +- .../AnonymousAuthenticationManagerTest.java | 5 +- .../manager/AuthenticationManagerRegistryTest.java | 38 +- .../manager/ExternalAuthenticationManagerTest.java | 9 +- ...PrincipalDatabaseAuthenticationManagerTest.java | 56 ++- .../auth/rmi/RMIPasswordAuthenticatorTest.java | 102 ++-- .../security/auth/sasl/GroupPrincipalTest.java | 86 ---- .../security/auth/sasl/TestPrincipalUtils.java | 48 -- .../security/auth/sasl/UsernamePrincipalTest.java | 123 ----- .../security/group/FileGroupDatabaseTest.java | 456 +++++++++++++++++ .../security/group/FileGroupManagerTest.java | 236 +++++++++ .../security/group/GroupPrincipalAccessorTest.java | 81 +++ .../server/security/group/GroupPrincipalTest.java | 88 ++++ .../qpid/server/util/TestApplicationRegistry.java | 9 +- .../java/systests/etc/config-systests-settings.xml | 14 +- qpid/java/systests/etc/groups-systests | 29 ++ .../server/logging/AccessControlLoggingTest.java | 42 +- .../server/security/acl/AbstractACLTestCase.java | 2 +- .../qpid/server/security/acl/ExternalACLTest.java | 15 +- 142 files changed, 6838 insertions(+), 1673 deletions(-) create mode 100644 qpid/java/broker-plugins/management-http/src/main/java/resources/group/addGroupMember.html create mode 100644 qpid/java/broker-plugins/management-http/src/main/java/resources/group/showGroup.html create mode 100644 qpid/java/broker-plugins/management-http/src/main/java/resources/groupprovider/addGroup.html create mode 100644 qpid/java/broker-plugins/management-http/src/main/java/resources/groupprovider/showFileGroupManager.html create mode 100644 qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/GroupProvider.js create mode 100644 qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/Group.js create mode 100644 qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/addGroupMember.js create mode 100644 qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/groupprovider/FileGroupManager.js create mode 100644 qpid/java/broker-plugins/management-http/src/main/java/resources/showGroupProvider.html create mode 100644 qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/GroupProviderRestTest.java create mode 100644 qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/GroupRestTest.java create mode 100644 qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/RestTestHelper.java create mode 100644 qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java create mode 100644 qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java create mode 100644 qpid/java/broker/etc/groups create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/AbstractManagementActor.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/HttpManagementActor.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/Group.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/GroupMember.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/GroupProvider.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderAdapter.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/SubjectCreator.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticatedPrincipal.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/SubjectAuthenticationResult.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/UsernamePrincipal.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipal.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupDatabase.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupManager.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupDatabase.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupManager.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupManagerPluginFactory.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupPrincipal.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupPrincipalAccessor.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/AbstractManagementActorTest.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/HttpManagementActorTest.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/SubjectCreatorTest.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/AuthenticatedPrincipalTest.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/AuthenticatedPrincipalTestHelper.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/AuthenticationResultTest.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/TestPrincipalUtils.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/UsernamePrincipalTest.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipalTest.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalUtils.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipalTest.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupDatabaseTest.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupManagerTest.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/GroupPrincipalAccessorTest.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/GroupPrincipalTest.java create mode 100644 qpid/java/systests/etc/groups-systests (limited to 'qpid/java') diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java index 9a08eb6499..afaece6138 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/PlainConfiguration.java @@ -1,5 +1,5 @@ /* - * + * * 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 @@ -7,16 +7,16 @@ * 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.config; @@ -44,43 +44,41 @@ public class PlainConfiguration extends AbstractConfiguration public static final Character COMMENT = '#'; public static final Character CONTINUATION = '\\'; - public static final String GROUP = "group"; public static final String ACL = "acl"; public static final String CONFIG = "config"; public static final String UNRECOGNISED_INITIAL_MSG = "Unrecognised initial token '%s' at line %d"; public static final String NOT_ENOUGH_TOKENS_MSG = "Not enough tokens at line %d"; - public static final String NUMBER_NOT_ALLOWED_MSG = "Number not allowed before '%s' at line %d"; + public static final String NUMBER_NOT_ALLOWED_MSG = "Number not allowed before '%s' at line %d"; public static final String CANNOT_LOAD_MSG = "Cannot load config file %s"; public static final String PREMATURE_CONTINUATION_MSG = "Premature continuation character at line %d"; public static final String PREMATURE_EOF_MSG = "Premature end of file reached at line %d"; public static final String PARSE_TOKEN_FAILED_MSG = "Failed to parse token at line %d"; public static final String CONFIG_NOT_FOUND_MSG = "Cannot find config file %s"; - public static final String NOT_ENOUGH_GROUP_MSG = "Not enough data for a group at line %d"; public static final String NOT_ENOUGH_ACL_MSG = "Not enough data for an acl at line %d"; public static final String NOT_ENOUGH_CONFIG_MSG = "Not enough data for config at line %d"; public static final String BAD_ACL_RULE_NUMBER_MSG = "Invalid rule number at line %d"; public static final String PROPERTY_KEY_ONLY_MSG = "Incomplete property (key only) at line %d"; public static final String PROPERTY_NO_EQUALS_MSG = "Incomplete property (no equals) at line %d"; public static final String PROPERTY_NO_VALUE_MSG = "Incomplete property (no value) at line %d"; - + private StreamTokenizer _st; public PlainConfiguration(File file) { super(file); } - + @Override public RuleSet load() throws ConfigurationException { RuleSet ruleSet = super.load(); - + try { _st = new StreamTokenizer(new BufferedReader(new FileReader(getFile()))); _st.resetSyntax(); // setup the tokenizer - + _st.commentChar(COMMENT); // single line comments _st.eolIsSignificant(true); // return EOL as a token _st.ordinaryChar('='); // equals is a token @@ -97,7 +95,7 @@ public class PlainConfiguration extends AbstractConfiguration _st.wordChars('*', '*'); // star _st.wordChars('@', '@'); // at _st.wordChars(':', ':'); // colon - + // parse the acl file lines Stack stack = new Stack(); int current; @@ -111,7 +109,7 @@ public class PlainConfiguration extends AbstractConfiguration { break; // blank line } - + // pull out the first token from the bottom of the stack and check arguments exist String first = stack.firstElement(); stack.removeElementAt(0); @@ -119,13 +117,13 @@ public class PlainConfiguration extends AbstractConfiguration { throw new ConfigurationException(String.format(NOT_ENOUGH_TOKENS_MSG, getLine())); } - + // check for and parse optional initial number for ACL lines Integer number = null; if (StringUtils.isNumeric(first)) { // set the acl number and get the next element - number = Integer.valueOf(first); + number = Integer.valueOf(first); first = stack.firstElement(); stack.removeElementAt(0); } @@ -136,9 +134,9 @@ public class PlainConfiguration extends AbstractConfiguration } else if (number == null) { - if (StringUtils.equalsIgnoreCase(GROUP, first)) + if(StringUtils.equalsIgnoreCase("GROUP", first)) { - parseGroup(stack); + throw new ConfigurationException(String.format("GROUP keyword not supported. Groups should defined via a Group Provider, not in the ACL file.", getLine())); } else if (StringUtils.equalsIgnoreCase(CONFIG, first)) { @@ -153,7 +151,7 @@ public class PlainConfiguration extends AbstractConfiguration { throw new ConfigurationException(String.format(NUMBER_NOT_ALLOWED_MSG, first, getLine())); } - + // reset stack, start next line stack.clear(); break; @@ -171,7 +169,7 @@ public class PlainConfiguration extends AbstractConfiguration { break; // continue reading next line } - + // invalid location for continuation character (add one to line beacuse we ate the EOL) throw new ConfigurationException(String.format(PREMATURE_CONTINUATION_MSG, getLine() + 1)); } @@ -185,7 +183,7 @@ public class PlainConfiguration extends AbstractConfiguration } } } while (current != StreamTokenizer.TT_EOF); - + if (!stack.isEmpty()) { throw new ConfigurationException(String.format(PREMATURE_EOF_MSG, getLine())); @@ -203,20 +201,10 @@ public class PlainConfiguration extends AbstractConfiguration { throw new ConfigurationException(String.format(CANNOT_LOAD_MSG, getFile().getName()), ioe); } - + return ruleSet; } - - private void parseGroup(List args) throws ConfigurationException - { - if (args.size() < 2) - { - throw new ConfigurationException(String.format(NOT_ENOUGH_GROUP_MSG, getLine())); - } - - getConfiguration().addGroup(args.get(0), args.subList(1, args.size())); - } - + private void parseAcl(Integer number, List args) throws ConfigurationException { if (args.size() < 3) @@ -227,12 +215,12 @@ public class PlainConfiguration extends AbstractConfiguration Permission permission = Permission.parse(args.get(0)); String identity = args.get(1); Operation operation = Operation.parse(args.get(2)); - + if (number != null && !getConfiguration().isValidNumber(number)) { throw new ConfigurationException(String.format(BAD_ACL_RULE_NUMBER_MSG, getLine())); } - + if (args.size() == 3) { getConfiguration().grant(number, identity, permission, operation); @@ -245,7 +233,7 @@ public class PlainConfiguration extends AbstractConfiguration getConfiguration().grant(number, identity, permission, operation, object, properties); } } - + private void parseConfig(List args) throws ConfigurationException { if (args.size() < 3) @@ -254,10 +242,10 @@ public class PlainConfiguration extends AbstractConfiguration } Map properties = toPluginProperties(args); - + getConfiguration().configure(properties); } - + /** Converts a {@link List} of "name", "=", "value" tokens into a {@link Map}. */ protected ObjectProperties toObjectProperties(List args) throws ConfigurationException { @@ -279,14 +267,14 @@ public class PlainConfiguration extends AbstractConfiguration throw new ConfigurationException(String.format(PROPERTY_NO_VALUE_MSG, getLine())); } String value = i.next(); - + // parse property key ObjectProperties.Property property = ObjectProperties.Property.parse(key); properties.put(property, value); } return properties; } - + /** Converts a {@link List} of "name", "=", "value" tokens into a {@link Map}. */ protected Map toPluginProperties(List args) throws ConfigurationException { @@ -307,14 +295,14 @@ public class PlainConfiguration extends AbstractConfiguration { throw new ConfigurationException(String.format(PROPERTY_NO_VALUE_MSG, getLine())); } - + // parse property value and save Boolean value = Boolean.valueOf(i.next()); properties.put(key, value); } return properties; } - + protected int getLine() { return _st.lineno() - 1; diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java index 15d6b67192..5e98e0bd1b 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/Rule.java @@ -29,7 +29,7 @@ import org.apache.qpid.server.security.access.Permission; /** * An access control v2 rule. - * + * * A rule consists of {@link Permission} for a particular identity to perform an {@link Action}. The identity * may be either a user or a group. */ @@ -37,13 +37,13 @@ public class Rule implements Comparable { /** String indicating all identitied. */ public static final String ALL = "all"; - + private Integer _number; private Boolean _enabled = Boolean.TRUE; private String _identity; private Action _action; private Permission _permission; - + public Rule(Integer number, String identity, Action action, Permission permission) { setNumber(number); @@ -51,27 +51,27 @@ public class Rule implements Comparable setAction(action); setPermission(permission); } - + public Rule(String identity, Action action, Permission permission) { this(null, identity, action, permission); } - + public boolean isEnabled() { return _enabled; } - + public void setEnabled(boolean enabled) { _enabled = enabled; } - + public void enable() { _enabled = Boolean.TRUE; } - + public void disable() { _enabled = Boolean.FALSE; @@ -96,7 +96,7 @@ public class Rule implements Comparable { _identity = identity; } - + public Action getAction() { return _action; @@ -136,7 +136,7 @@ public class Rule implements Comparable return false; } Rule r = (Rule) o; - + return new EqualsBuilder() .append(getIdentity(), r.getIdentity()) .append(getAction(), r.getAction()) diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java index 815df99f80..2477455de4 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/config/RuleSet.java @@ -19,7 +19,6 @@ package org.apache.qpid.server.security.access.config; import java.security.Principal; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; @@ -66,7 +65,6 @@ public class RuleSet private static final Integer _increment = 10; - private final Map> _aclGroups = new HashMap>(); private final SortedMap _rules = new TreeMap(); private final Map>>> _cache = new WeakHashMap>>>(); @@ -79,14 +77,13 @@ public class RuleSet } /** - * Clear the contents, including acl groups, rules and configuration. + * Clear the contents, including acl rules and configuration. */ public void clear() { _rules.clear(); _cache.clear(); _config.clear(); - _aclGroups.clear(); } public int getRuleCount() @@ -222,53 +219,6 @@ public class RuleSet _rules.get(Integer.valueOf(ruleNumber)).disable(); } - public boolean addGroup(String group, List constituents) - { - _cache.clear(); - - if (_aclGroups.containsKey(group)) - { - // cannot redefine - return false; - } - else - { - _aclGroups.put(group, new ArrayList()); - } - - for (String name : constituents) - { - if (name.equalsIgnoreCase(group)) - { - // recursive definition - return false; - } - - if (!checkName(name)) - { - // invalid name - return false; - } - - if (_aclGroups.containsKey(name)) - { - // is a group - _aclGroups.get(group).addAll(_aclGroups.get(name)); - } - else - { - // is a user - if (!isvalidUserName(name)) - { - // invalid username - return false; - } - _aclGroups.get(group).add(name); - } - } - return true; - } - /** Return true if the name is well-formed (contains legal characters). */ protected boolean checkName(String name) { @@ -312,12 +262,6 @@ public class RuleSet return true; } - // CPP broker authorise function prototype - // virtual bool authorise(const std::string& id, const Action& action, const ObjectType& objType, - // const std::string& name, std::map* params=0) - - // Possibly add a String name paramater? - /** * Check the authorisation granted to a particular identity for an operation on an object type with * specific properties. @@ -446,8 +390,7 @@ public class RuleSet { final Principal principal = iterator.next(); - if (rule.getIdentity().equalsIgnoreCase(principal.getName()) - || (_aclGroups.containsKey(rule.getIdentity()) && _aclGroups.get(rule.getIdentity()).contains(principal.getName()))) + if (rule.getIdentity().equalsIgnoreCase(principal.getName())) { return true; } diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java index d8a5bd4085..d36ae810c6 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControl.java @@ -40,9 +40,9 @@ import org.apache.qpid.server.security.access.config.RuleSet; public class AccessControl extends AbstractPlugin { public static final Logger _logger = Logger.getLogger(AccessControl.class); - + private RuleSet _ruleSet; - + public static final SecurityPluginFactory FACTORY = new SecurityPluginFactory() { public Class getPluginClass() @@ -86,7 +86,7 @@ public class AccessControl extends AbstractPlugin { return authorise(Operation.ACCESS, objectType, ObjectProperties.EMPTY); } - + /** * Check if an operation is authorised by asking the configuration object about the access * control rules granted to the current thread's {@link Subject}. If there is no current @@ -100,7 +100,7 @@ public class AccessControl extends AbstractPlugin { return Result.ABSTAIN; } - + _logger.debug("Checking " + operation + " " + objectType); return _ruleSet.check(subject, operation, objectType, properties); } diff --git a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControlConfiguration.java b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControlConfiguration.java index c4db6db820..63f7e254ae 100644 --- a/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControlConfiguration.java +++ b/qpid/java/broker-plugins/access-control/src/main/java/org/apache/qpid/server/security/access/plugins/AccessControlConfiguration.java @@ -34,7 +34,7 @@ import org.apache.qpid.server.security.access.config.RuleSet; public class AccessControlConfiguration extends ConfigurationPlugin { - public static final ConfigurationPluginFactory FACTORY = new ConfigurationPluginFactory() + public static final ConfigurationPluginFactory FACTORY = new ConfigurationPluginFactory() { public ConfigurationPlugin newInstance(String path, Configuration config) throws ConfigurationException { @@ -70,7 +70,7 @@ public class AccessControlConfiguration extends ConfigurationPlugin } File aclFile = new File(filename); - + ConfigurationFile configFile = new PlainConfiguration(aclFile); _ruleSet = configFile.load(); } diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AccessControlTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AccessControlTest.java index 5db02d10ce..2385bcc3dd 100644 --- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AccessControlTest.java +++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/AccessControlTest.java @@ -20,8 +20,6 @@ */ package org.apache.qpid.server.security.access.plugins; -import java.util.Arrays; - import junit.framework.TestCase; import org.apache.commons.configuration.ConfigurationException; @@ -37,19 +35,22 @@ import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.security.access.Permission; import org.apache.qpid.server.security.access.config.Rule; import org.apache.qpid.server.security.access.config.RuleSet; -import org.apache.qpid.server.security.auth.sasl.TestPrincipalUtils; +import org.apache.qpid.server.security.auth.TestPrincipalUtils; /** - * Unit test for ACL V2 plugin. - * + * Unit test for ACL V2 plugin. + * * This unit test tests the AccessControl class and it collaboration with {@link RuleSet}, * {@link SecurityManager} and {@link CurrentActor}. The ruleset is configured programmatically, * rather than from an external file. - * + * * @see RuleSetTest */ public class AccessControlTest extends TestCase { + private static final String ALLOWED_GROUP = "allowed_group"; + private static final String DENIED_GROUP = "denied_group"; + private AccessControl _plugin = null; // Class under test private final UnitTestMessageLogger messageLogger = new UnitTestMessageLogger(); @@ -68,14 +69,12 @@ public class AccessControlTest extends TestCase private RuleSet createGroupRuleSet() { final RuleSet rs = new RuleSet(); - rs.addGroup("aclGroup1", Arrays.asList(new String[] {"member1", "Member2"})); // Rule expressed with username rs.grant(0, "user1", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - // Rule expressed with a acl group - rs.grant(1, "aclGroup1", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - // Rule expressed with an external group - rs.grant(2, "extGroup1", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + // Rules expressed with groups + rs.grant(1, ALLOWED_GROUP, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + rs.grant(2, DENIED_GROUP, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); // Catch all rule rs.grant(3, Rule.ALL, Permission.DENY_LOG, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); @@ -117,31 +116,23 @@ public class AccessControlTest extends TestCase * Tests that an allow rule expressed with an ACL groupname allows an operation performed by a thread running * by a user who belongs to the same group.. */ - public void testAclGroupMembershipAllowsOperation() throws ConfigurationException + public void testGroupMembershipAllowsOperation() throws ConfigurationException { setUpGroupAccessControl(); - SecurityManager.setThreadSubject(TestPrincipalUtils.createTestSubject("member1")); - Result result = _plugin.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - assertEquals(Result.ALLOWED, result); - - SecurityManager.setThreadSubject(TestPrincipalUtils.createTestSubject("Member2")); - - result = _plugin.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - assertEquals(Result.ALLOWED, result); + authoriseAndAssertResult(Result.ALLOWED, "member of allowed group", ALLOWED_GROUP); + authoriseAndAssertResult(Result.DENIED, "member of denied group", DENIED_GROUP); + authoriseAndAssertResult(Result.ALLOWED, "another member of allowed group", ALLOWED_GROUP); } /** - * Tests that a deny rule expressed with an External groupname denies an operation performed by a thread running + * Tests that a deny rule expressed with a groupname denies an operation performed by a thread running * by a user who belongs to the same group. */ - public void testExternalGroupMembershipDeniesOperation() throws ConfigurationException + public void testGroupMembershipDeniesOperation() throws ConfigurationException { setUpGroupAccessControl(); - SecurityManager.setThreadSubject(TestPrincipalUtils.createTestSubject("user3", "extGroup1")); - - final Result result = _plugin.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - assertEquals(Result.DENIED, result); + authoriseAndAssertResult(Result.DENIED, "user3", DENIED_GROUP); } /** @@ -325,6 +316,14 @@ public class AccessControlTest extends TestCase assertEquals(Result.DEFER, result); } + private void authoriseAndAssertResult(Result expectedResult, String userName, String... groups) + { + SecurityManager.setThreadSubject(TestPrincipalUtils.createTestSubject(userName, groups)); + + Result result = _plugin.authorise(Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + assertEquals(expectedResult, result); + } + /** * Creates a configuration plugin for the {@link AccessControl} plugin. */ diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java index c2282694fb..be4962615c 100644 --- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java +++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/PlainConfigurationTest.java @@ -108,19 +108,6 @@ public class PlainConfigurationTest extends TestCase } } - public void testACLFileSyntaxNotEnoughGroup() throws Exception - { - try - { - writeACLConfig("GROUP blah"); - fail("fail"); - } - catch (ConfigurationException ce) - { - assertEquals(String.format(PlainConfiguration.NOT_ENOUGH_GROUP_MSG, 1), ce.getMessage()); - } - } - public void testACLFileSyntaxNotEnoughACL() throws Exception { try @@ -391,4 +378,77 @@ public class PlainConfigurationTest extends TestCase assertEquals("Rule has unexpected object properties", ObjectProperties.EMPTY, rule.getAction().getProperties()); } + public void testUserRuleParsing() throws Exception + { + validateRule(writeACLConfig("ACL ALLOW user1 CREATE USER"), + "user1", Operation.CREATE, ObjectType.USER, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 CREATE USER name=\"otherUser\""), + "user1", Operation.CREATE, ObjectType.USER, new ObjectProperties("otherUser")); + + validateRule(writeACLConfig("ACL ALLOW user1 DELETE USER"), + "user1", Operation.DELETE, ObjectType.USER, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 DELETE USER name=\"otherUser\""), + "user1", Operation.DELETE, ObjectType.USER, new ObjectProperties("otherUser")); + + validateRule(writeACLConfig("ACL ALLOW user1 UPDATE USER"), + "user1", Operation.UPDATE, ObjectType.USER, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 UPDATE USER name=\"otherUser\""), + "user1", Operation.UPDATE, ObjectType.USER, new ObjectProperties("otherUser")); + + validateRule(writeACLConfig("ACL ALLOW user1 ALL USER"), + "user1", Operation.ALL, ObjectType.USER, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 ALL USER name=\"otherUser\""), + "user1", Operation.ALL, ObjectType.USER, new ObjectProperties("otherUser")); + } + + public void testGroupRuleParsing() throws Exception + { + validateRule(writeACLConfig("ACL ALLOW user1 CREATE GROUP"), + "user1", Operation.CREATE, ObjectType.GROUP, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 CREATE GROUP name=\"groupName\""), + "user1", Operation.CREATE, ObjectType.GROUP, new ObjectProperties("groupName")); + + validateRule(writeACLConfig("ACL ALLOW user1 DELETE GROUP"), + "user1", Operation.DELETE, ObjectType.GROUP, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 DELETE GROUP name=\"groupName\""), + "user1", Operation.DELETE, ObjectType.GROUP, new ObjectProperties("groupName")); + + validateRule(writeACLConfig("ACL ALLOW user1 UPDATE GROUP"), + "user1", Operation.UPDATE, ObjectType.GROUP, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 UPDATE GROUP name=\"groupName\""), + "user1", Operation.UPDATE, ObjectType.GROUP, new ObjectProperties("groupName")); + + validateRule(writeACLConfig("ACL ALLOW user1 ALL GROUP"), + "user1", Operation.ALL, ObjectType.GROUP, ObjectProperties.EMPTY); + validateRule(writeACLConfig("ACL ALLOW user1 ALL GROUP name=\"groupName\""), + "user1", Operation.ALL, ObjectType.GROUP, new ObjectProperties("groupName")); + } + + /** explicitly test for exception indicating that this functionality has been moved to Group Providers */ + public void testGroupDefinitionThrowsException() throws Exception + { + try + { + writeACLConfig("GROUP group1 bob alice"); + fail("Expected exception not thrown"); + } + catch(ConfigurationException e) + { + assertTrue(e.getMessage().contains("GROUP keyword not supported")); + } + } + + private void validateRule(final PlainConfiguration config, String username, Operation operation, ObjectType objectType, ObjectProperties objectProperties) + { + final RuleSet rs = config.getConfiguration(); + assertEquals(1, rs.getRuleCount()); + + final Map rules = rs.getAllRules(); + assertEquals(1, rules.size()); + final Rule rule = rules.get(0); + assertEquals("Rule has unexpected identity", username, rule.getIdentity()); + assertEquals("Rule has unexpected operation", operation, rule.getAction().getOperation()); + assertEquals("Rule has unexpected operation", objectType, rule.getAction().getObjectType()); + assertEquals("Rule has unexpected object properties", objectProperties, rule.getAction().getProperties()); + } } diff --git a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java index f7cc60543d..181d693614 100644 --- a/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java +++ b/qpid/java/broker-plugins/access-control/src/test/java/org/apache/qpid/server/security/access/plugins/RuleSetTest.java @@ -22,7 +22,6 @@ package org.apache.qpid.server.security.access.plugins; import java.security.Principal; -import java.util.Arrays; import javax.security.auth.Subject; @@ -34,8 +33,7 @@ import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.security.access.Permission; import org.apache.qpid.server.security.access.config.Rule; import org.apache.qpid.server.security.access.config.RuleSet; -import org.apache.qpid.server.security.auth.sasl.TestPrincipalUtils; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.TestPrincipalUtils; import org.apache.qpid.test.utils.QpidTestCase; /** @@ -46,10 +44,7 @@ import org.apache.qpid.test.utils.QpidTestCase; * access control mechanism is validated by checking whether operations would be authorised by calling the * {@link RuleSet#check(Principal, Operation, ObjectType, ObjectProperties)} method. * - * It ensure that permissions can be granted correctly on users directly, ACL groups (that is those - * groups declared directly in the ACL itself), and External groups (that is a group from an External - * Authentication Provider, such as an LDAP). - + * It ensure that permissions can be granted correctly on users directly and on groups. */ public class RuleSetTest extends QpidTestCase { @@ -316,63 +311,36 @@ public class RuleSetTest extends QpidTestCase assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); } - /** - * Tests support for ACL groups (i.e. inline groups declared in the ACL file itself). - */ - public void testAclGroupsSupported() + public void testGroupsSupported() { - assertTrue(_ruleSet.addGroup("aclgroup", Arrays.asList(new String[] {"usera", "userb"}))); - - _ruleSet.grant(1, "aclgroup", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - assertEquals(1, _ruleSet.getRuleCount()); - - assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); - assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); - assertEquals(Result.DEFER, _ruleSet.check(TestPrincipalUtils.createTestSubject("userc"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); - } - - /** - * Tests support for nested ACL groups. - */ - public void testNestedAclGroupsSupported() - { - assertTrue(_ruleSet.addGroup("aclgroup1", Arrays.asList(new String[] {"userb"}))); - assertTrue(_ruleSet.addGroup("aclgroup2", Arrays.asList(new String[] {"usera", "aclgroup1"}))); - - _ruleSet.grant(1, "aclgroup2", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - assertEquals(1, _ruleSet.getRuleCount()); + String allowGroup = "allowGroup"; + String deniedGroup = "deniedGroup"; - assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); - assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); - } + _ruleSet.grant(1, allowGroup, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + _ruleSet.grant(2, deniedGroup, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - /** - * Tests support for nested External groups (i.e. those groups coming from an external source such as an LDAP). - */ - public void testExternalGroupsSupported() - { - _ruleSet.grant(1, "extgroup1", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - _ruleSet.grant(2, "extgroup2", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); assertEquals(2, _ruleSet.getRuleCount()); - assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera", "extgroup1"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); - assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb", "extgroup2"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera", allowGroup),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject("userb", deniedGroup),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + assertEquals(Result.DEFER, _ruleSet.check(TestPrincipalUtils.createTestSubject("user", "group not mentioned in acl"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); } /** * Rule order in the ACL determines the outcome of the check. This test ensures that a user who is - * granted explicit permission on an object, is granted that access even although late a group + * granted explicit permission on an object, is granted that access even though a group * to which the user belongs is later denied the permission. */ public void testAllowDeterminedByRuleOrder() { - assertTrue(_ruleSet.addGroup("aclgroup", Arrays.asList(new String[] {"usera"}))); + String group = "group"; + String user = "user"; - _ruleSet.grant(1, "usera", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - _ruleSet.grant(2, "aclgroup", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + _ruleSet.grant(1, user, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + _ruleSet.grant(2, group, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); assertEquals(2, _ruleSet.getRuleCount()); - assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + assertEquals(Result.ALLOWED, _ruleSet.check(TestPrincipalUtils.createTestSubject(user, group),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); } /** @@ -381,13 +349,33 @@ public class RuleSetTest extends QpidTestCase */ public void testDenyDeterminedByRuleOrder() { - assertTrue(_ruleSet.addGroup("aclgroup", Arrays.asList(new String[] {"usera"}))); + String group = "aclgroup"; + String user = "usera"; - _ruleSet.grant(1, "aclgroup", Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); - _ruleSet.grant(2, "usera", Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + _ruleSet.grant(1, group, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + _ruleSet.grant(2, user, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); assertEquals(2, _ruleSet.getRuleCount()); - assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject("usera"),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + assertEquals(Result.DENIED, _ruleSet.check(TestPrincipalUtils.createTestSubject(user, group),Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + } + + public void testUserInMultipleGroups() + { + String allowedGroup = "group1"; + String deniedGroup = "group2"; + + _ruleSet.grant(1, allowedGroup, Permission.ALLOW, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + _ruleSet.grant(2, deniedGroup, Permission.DENY, Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY); + + Subject subjectInBothGroups = TestPrincipalUtils.createTestSubject("user", allowedGroup, deniedGroup); + Subject subjectInDeniedGroupAndOneOther = TestPrincipalUtils.createTestSubject("user", deniedGroup, "some other group"); + Subject subjectInAllowedGroupAndOneOther = TestPrincipalUtils.createTestSubject("user", allowedGroup, "some other group"); + + assertEquals(Result.ALLOWED, _ruleSet.check(subjectInBothGroups,Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + + assertEquals(Result.DENIED, _ruleSet.check(subjectInDeniedGroupAndOneOther,Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); + + assertEquals(Result.ALLOWED, _ruleSet.check(subjectInAllowedGroupAndOneOther,Operation.ACCESS, ObjectType.VIRTUALHOST, ObjectProperties.EMPTY)); } } diff --git a/qpid/java/broker-plugins/management-http/MANIFEST.MF b/qpid/java/broker-plugins/management-http/MANIFEST.MF index cca10d3f89..525801f65f 100644 --- a/qpid/java/broker-plugins/management-http/MANIFEST.MF +++ b/qpid/java/broker-plugins/management-http/MANIFEST.MF @@ -20,6 +20,7 @@ Import-Package: org.apache.qpid, org.apache.qpid.server.binding, org.apache.qpid.server.exchange, org.apache.qpid.server.logging, + org.apache.qpid.server.logging.actors, org.apache.qpid.server.message, org.apache.qpid.server.model, org.apache.qpid.server.model.adapter, diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/Management.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/Management.java index c2f9b73b54..38a8722852 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/Management.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/Management.java @@ -44,6 +44,9 @@ import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.Connection; import org.apache.qpid.server.model.Exchange; +import org.apache.qpid.server.model.Group; +import org.apache.qpid.server.model.GroupMember; +import org.apache.qpid.server.model.GroupProvider; import org.apache.qpid.server.model.Port; import org.apache.qpid.server.model.Protocol; import org.apache.qpid.server.model.Queue; @@ -158,6 +161,9 @@ public class Management addRestServlet(root, "virtualhost", VirtualHost.class); addRestServlet(root, "authenticationprovider", AuthenticationProvider.class); addRestServlet(root, "user", AuthenticationProvider.class, User.class); + addRestServlet(root, "groupprovider", GroupProvider.class); + addRestServlet(root, "group", GroupProvider.class, Group.class); + addRestServlet(root, "groupmember", GroupProvider.class, Group.class, GroupMember.class); addRestServlet(root, "exchange", VirtualHost.class, Exchange.class); addRestServlet(root, "queue", VirtualHost.class, Queue.class); addRestServlet(root, "connection", VirtualHost.class, Connection.class); 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 a76bd98179..1469808565 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 @@ -24,8 +24,10 @@ package org.apache.qpid.server.management.plugin.servlet.rest; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.security.Principal; -import java.util.Collections; +import java.security.AccessControlException; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + import javax.security.auth.Subject; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -33,168 +35,293 @@ 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.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.model.Broker; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; +import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManager; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; public abstract class AbstractServlet extends HttpServlet { + private static final Logger LOGGER = Logger.getLogger(AbstractServlet.class); + + protected static final String ATTR_SUBJECT = "subject"; + private static final String ATTR_LOG_ACTOR = "AbstractServlet.logActor"; + private final Broker _broker; + private RootMessageLogger _rootLogger; + protected AbstractServlet() { super(); _broker = ApplicationRegistry.getInstance().getBroker(); + _rootLogger = ApplicationRegistry.getInstance().getRootMessageLogger(); } protected AbstractServlet(Broker broker) { _broker = broker; + _rootLogger = ApplicationRegistry.getInstance().getRootMessageLogger(); } @Override - protected final void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException + protected final void doGet(final HttpServletRequest request, final HttpServletResponse resp) { - setAuthorizedSubject(request); - try - { - onGet(request, resp); - } - finally - { - clearAuthorizedSubject(); - } + doWithSubjectAndActor( + new PrivilegedExceptionAction() + { + @Override + public Void run() throws Exception + { + doGetWithSubjectAndActor(request, resp); + return null; + } + }, + request, + resp + ); } - protected void onGet(HttpServletRequest request, HttpServletResponse resp) throws IOException, ServletException + /** + * Performs the GET action as the logged-in {@link Subject}. + * The {@link LogActor} is set before this method is called. + * Subclasses commonly override this method + */ + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException { - super.doGet(request, resp); + throw new UnsupportedOperationException("GET not supported by this servlet"); } - private void clearAuthorizedSubject() + + @Override + protected final void doPost(final HttpServletRequest request, final HttpServletResponse resp) { - org.apache.qpid.server.security.SecurityManager.setThreadSubject(null); + doWithSubjectAndActor( + new PrivilegedExceptionAction() + { + @Override + public Void run() throws Exception + { + doPostWithSubjectAndActor(request, resp); + return null; + } + }, + request, + resp + ); } - - private void setAuthorizedSubject(HttpServletRequest request) + /** + * Performs the POST action as the logged-in {@link Subject}. + * The {@link LogActor} is set before this method is called. + * Subclasses commonly override this method + */ + protected void doPostWithSubjectAndActor(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - HttpSession session = request.getSession(true); - Subject subject = (Subject) session.getAttribute("subject"); + throw new UnsupportedOperationException("POST not supported by this servlet"); + } - if(subject == null) - { - Principal principal = request.getUserPrincipal(); - if(principal != null) - { - subject = new Subject(false, Collections.singleton(principal),Collections.emptySet(), - Collections.emptySet()); - } - else + @Override + protected final void doPut(final HttpServletRequest request, final HttpServletResponse resp) + { + doWithSubjectAndActor( + new PrivilegedExceptionAction() { - String header = request.getHeader("Authorization"); + @Override + public Void run() throws Exception + { + doPutWithSubjectAndActor(request, resp); + return null; + } + }, + request, + resp + ); + } - /* - * TODO - Should configure whether basic authentication is allowed... and in particular whether it - * should be allowed over non-ssl connections - * */ + /** + * Performs the PUT action as the logged-in {@link Subject}. + * The {@link LogActor} is set before this method is called. + * Subclasses commonly override this method + */ + protected void doPutWithSubjectAndActor(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + throw new UnsupportedOperationException("PUT not supported by this servlet"); + } - if (header != null) + @Override + protected final void doDelete(final HttpServletRequest request, final HttpServletResponse resp) + throws ServletException, IOException + { + doWithSubjectAndActor( + new PrivilegedExceptionAction() + { + @Override + public Void run() throws Exception { - String[] tokens = header.split("\\s"); - if(tokens.length >= 2 - && "BASIC".equalsIgnoreCase(tokens[0])) - { - String[] credentials = (new String(Base64.decodeBase64(tokens[1].getBytes()))).split(":",2); - if(credentials.length == 2) - { - SocketAddress address = getSocketAddress(request); - AuthenticationManager authenticationManager = - ApplicationRegistry.getInstance().getAuthenticationManager(address); - AuthenticationResult authResult = - authenticationManager.authenticate(credentials[0], credentials[1]); - subject = authResult.getSubject(); - - } - } + doDeleteWithSubjectAndActor(request, resp); + return null; } - } - } - if (subject == null) - { - subject = AnonymousAuthenticationManager.ANONYMOUS_SUBJECT; - } - org.apache.qpid.server.security.SecurityManager.setThreadSubject(subject); - + }, + request, + resp + ); } - protected Subject getSubject(HttpSession session) + /** + * Performs the PUT action as the logged-in {@link Subject}. + * The {@link LogActor} is set before this method is called. + * Subclasses commonly override this method + */ + protected void doDeleteWithSubjectAndActor(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - return (Subject)session.getAttribute("subject"); + throw new UnsupportedOperationException("DELETE not supported by this servlet"); } - @Override - protected final void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + private void doWithSubjectAndActor( + PrivilegedExceptionAction privilegedExceptionAction, + final HttpServletRequest request, + final HttpServletResponse resp) { - setAuthorizedSubject(req); + Subject subject = getAndCacheAuthorizedSubject(request); + org.apache.qpid.server.security.SecurityManager.setThreadSubject(subject); + try { - onPost(req, resp); + HttpManagementActor logActor = getLogActorAndCacheInSession(request); + CurrentActor.set(logActor); + try + { + Subject.doAs(subject, privilegedExceptionAction); + } + catch(RuntimeException e) + { + LOGGER.error("Unable to perform action", e); + throw e; + } + catch (PrivilegedActionException e) + { + LOGGER.error("Unable to perform action", e); + throw new RuntimeException(e.getCause()); + } + finally + { + CurrentActor.remove(); + } } finally { - clearAuthorizedSubject(); + org.apache.qpid.server.security.SecurityManager.setThreadSubject(null); } - } - protected void onPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + /** + * Gets the logged-in {@link Subject} by trying the following: + * + *
    + *
  • Get it from the session
  • + *
  • Get it from the request
  • + *
  • Log in using the username and password in the Authorization HTTP header
  • + *
  • Create a Subject representing the anonymous user.
  • + *
+ * + * If an authenticated subject is found it is cached in the http session. + */ + private Subject getAndCacheAuthorizedSubject(HttpServletRequest request) { - super.doPost(req, resp); - } + HttpSession session = request.getSession(); + Subject subject = getSubjectFromSession(session); - @Override - protected final void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException - { - setAuthorizedSubject(req); - try + if(subject != null) { - onPut(req, resp); + return subject; + } + SubjectCreator subjectCreator = ApplicationRegistry.getInstance().getSubjectCreator(getSocketAddress(request)); + + String remoteUser = request.getRemoteUser(); + if(remoteUser != null) + { + subject = subjectCreator.createSubjectWithGroups(remoteUser); } - finally + else { - clearAuthorizedSubject(); + String header = request.getHeader("Authorization"); + + /* + * TODO - Should configure whether basic authentication is allowed... and in particular whether it + * should be allowed over non-ssl connections + * */ + + if (header != null) + { + String[] tokens = header.split("\\s"); + if(tokens.length >= 2 + && "BASIC".equalsIgnoreCase(tokens[0])) + { + String[] credentials = (new String(Base64.decodeBase64(tokens[1].getBytes()))).split(":",2); + if(credentials.length == 2) + { + SubjectAuthenticationResult authResult = subjectCreator.authenticate(credentials[0], credentials[1]); + if( authResult.getStatus() != AuthenticationStatus.SUCCESS) + { + //TODO: write a return response indicating failure? + throw new AccessControlException("Incorrect username or password"); + } + subject = authResult.getSubject(); + } + else + { + //TODO: write a return response indicating failure? + throw new AccessControlException("Invalid number of credentials supplied: " + + credentials.length); + } + } + } } - } - protected void onPut(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException - { - super.doPut(req,resp); + if (subject != null) + { + setSubjectInSession(subject, session); + } + else + { + subject = subjectCreator.createSubjectWithGroups(AnonymousAuthenticationManager.ANONYMOUS_USERNAME); + } + + return subject; } - @Override - protected final void doDelete(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException + private HttpManagementActor getLogActorAndCacheInSession(HttpServletRequest req) { - setAuthorizedSubject(req); - try - { - onDelete(req, resp); - } - finally + HttpSession session = req.getSession(); + + HttpManagementActor actor = (HttpManagementActor) session.getAttribute(ATTR_LOG_ACTOR); + if(actor == null) { - clearAuthorizedSubject(); + actor = new HttpManagementActor(_rootLogger, req.getRemoteAddr(), req.getRemotePort()); + session.setAttribute(ATTR_LOG_ACTOR, actor); } + + return actor; } - protected void onDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + protected Subject getSubjectFromSession(HttpSession session) { - super.doDelete(req, resp); + return (Subject)session.getAttribute(ATTR_SUBJECT); } + protected void setSubjectInSession(Subject subject, final HttpSession session) + { + session.setAttribute(ATTR_SUBJECT, subject); + } protected Broker getBroker() { diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java index 404793b592..04eda2a787 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java @@ -44,7 +44,7 @@ public class LogRecordsServlet extends AbstractServlet } @Override - protected void onGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java index bc87f0bcc5..ae794472bf 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageContentServlet.java @@ -48,7 +48,7 @@ public class MessageContentServlet extends AbstractServlet } @Override - protected void onGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if(request.getPathInfo() != null && request.getPathInfo().length()>0 && request.getPathInfo().substring(1).split("/").length > 2) diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java index 6e7bc1d935..3920443b07 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java @@ -62,7 +62,7 @@ public class MessageServlet extends AbstractServlet } @Override - protected void onGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if(request.getPathInfo() != null && request.getPathInfo().length()>0 && request.getPathInfo().substring(1).split("/").length > 2) @@ -400,7 +400,7 @@ public class MessageServlet extends AbstractServlet * POST moves or copies messages to the given queue from a queue specified in the posted JSON data */ @Override - protected void onPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + protected void doPostWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try @@ -450,7 +450,7 @@ public class MessageServlet extends AbstractServlet * DELETE removes messages from the queue */ @Override - protected void onDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + protected void doDeleteWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) { final Queue sourceQueue = getQueueFromRequest(request); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java index 6a79916d07..f2ca25d664 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java @@ -31,7 +31,6 @@ import org.apache.qpid.server.model.*; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; - public class RestServlet extends AbstractServlet { private static final Logger LOGGER = Logger.getLogger(RestServlet.class); @@ -285,7 +284,7 @@ public class RestServlet extends AbstractServlet } @Override - protected void onGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); @@ -319,7 +318,7 @@ public class RestServlet extends AbstractServlet } @Override - protected void onPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + protected void doPutWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json"); @@ -336,7 +335,8 @@ public class RestServlet extends AbstractServlet if(names.size() != _hierarchy.length) { - throw new IllegalArgumentException("Path to object to create must be fully specified"); + throw new IllegalArgumentException("Path to object to create must be fully specified. " + + "Found " + names.size() + " expecting " + _hierarchy.length); } } @@ -428,8 +428,11 @@ public class RestServlet extends AbstractServlet || (obj.getName().equals(providedObject.get("name")) && equalParents(obj, otherParents))) { doUpdate(obj, providedObject); + response.setStatus(HttpServletResponse.SC_OK); + return; } } + theParent.createChild(objClass, providedObject, otherParents); } catch (RuntimeException e) @@ -464,11 +467,12 @@ public class RestServlet extends AbstractServlet { if (e.getCause() instanceof AMQSecurityException) { + LOGGER.debug("Caught AMQSecurityException", e); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); } else { - LOGGER.warn("Unexpected exception is caught", e); + LOGGER.warn("Caught exception", e); // TODO response.setStatus(HttpServletResponse.SC_CONFLICT); @@ -476,7 +480,7 @@ public class RestServlet extends AbstractServlet } @Override - protected void onDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + protected void doDeleteWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); diff --git a/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 1b78611a50..b5929875ff 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 @@ -27,8 +27,8 @@ import org.codehaus.jackson.map.SerializationConfig; import org.apache.log4j.Logger; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; import javax.security.auth.Subject; import javax.security.sasl.SaslException; @@ -67,7 +67,7 @@ public class SaslServlet extends AbstractServlet super(broker); } - protected void onGet(HttpServletRequest request, HttpServletResponse response) throws + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { @@ -79,15 +79,16 @@ public class SaslServlet extends AbstractServlet response.setDateHeader ("Expires", 0); HttpSession session = request.getSession(); - Random rand = getRandom(session); + getRandom(session); - AuthenticationManager authManager = ApplicationRegistry.getInstance().getAuthenticationManager(getSocketAddress(request)); - String[] mechanisms = authManager.getMechanisms().split(" "); + SubjectCreator subjectCreator = ApplicationRegistry.getInstance().getSubjectCreator(getSocketAddress(request)); + String[] mechanisms = subjectCreator.getMechanisms().split(" "); Map outputObject = new LinkedHashMap(); - final Subject subject = (Subject) session.getAttribute("subject"); + + final Subject subject = getSubjectFromSession(session); if(subject != null) { - final Principal principal = subject.getPrincipals().iterator().next(); + Principal principal = AuthenticatedPrincipal.getAuthenticatedPrincipalFromSubject(subject); outputObject.put("user", principal.getName()); } else if (request.getRemoteUser() != null) @@ -121,8 +122,7 @@ public class SaslServlet extends AbstractServlet @Override - protected void onPost(final HttpServletRequest request, final HttpServletResponse response) - throws ServletException, IOException + protected void doPostWithSubjectAndActor(final HttpServletRequest request, final HttpServletResponse response) throws IOException { try { @@ -137,14 +137,14 @@ public class SaslServlet extends AbstractServlet String id = request.getParameter("id"); String saslResponse = request.getParameter("response"); - AuthenticationManager authManager = ApplicationRegistry.getInstance().getAuthenticationManager(getSocketAddress(request)); + SubjectCreator subjectCreator = ApplicationRegistry.getInstance().getSubjectCreator(getSocketAddress(request)); if(mechanism != null) { if(id == null) { - SaslServer saslServer = authManager.createSaslServer(mechanism, request.getServerName(), null/*TODO*/); - evaluateSaslResponse(response, session, saslResponse, saslServer); + SaslServer saslServer = subjectCreator.createSaslServer(mechanism, request.getServerName(), null/*TODO*/); + evaluateSaslResponse(response, session, saslResponse, saslServer, subjectCreator); } else { @@ -152,9 +152,7 @@ public class SaslServlet extends AbstractServlet session.removeAttribute(ATTR_ID); session.removeAttribute(ATTR_SASL_SERVER); session.removeAttribute(ATTR_EXPIRY); - } - } else { @@ -163,8 +161,7 @@ public class SaslServlet extends AbstractServlet if(id.equals(session.getAttribute(ATTR_ID)) && System.currentTimeMillis() < (Long) session.getAttribute(ATTR_EXPIRY)) { SaslServer saslServer = (SaslServer) session.getAttribute(ATTR_SASL_SERVER); - evaluateSaslResponse(response, session, saslResponse, saslServer); - + evaluateSaslResponse(response, session, saslResponse, saslServer, subjectCreator); } else { @@ -180,7 +177,6 @@ public class SaslServlet extends AbstractServlet session.removeAttribute(ATTR_ID); session.removeAttribute(ATTR_SASL_SERVER); session.removeAttribute(ATTR_EXPIRY); - } } } @@ -199,7 +195,7 @@ public class SaslServlet extends AbstractServlet private void evaluateSaslResponse(final HttpServletResponse response, final HttpSession session, - final String saslResponse, final SaslServer saslServer) throws IOException + final String saslResponse, final SaslServer saslServer, SubjectCreator subjectCreator) throws IOException { final String id; byte[] challenge; @@ -209,7 +205,6 @@ public class SaslServlet extends AbstractServlet } catch(SaslException e) { - session.removeAttribute(ATTR_ID); session.removeAttribute(ATTR_SASL_SERVER); session.removeAttribute(ATTR_EXPIRY); @@ -220,16 +215,14 @@ public class SaslServlet extends AbstractServlet if(saslServer.isComplete()) { - final Subject subject = new Subject(); - subject.getPrincipals().add(new UsernamePrincipal(saslServer.getAuthorizationID())); - session.setAttribute("subject", subject); + Subject subject = subjectCreator.createSubjectWithGroups(saslServer.getAuthorizationID()); + + setSubjectInSession(subject, session); session.removeAttribute(ATTR_ID); session.removeAttribute(ATTR_SASL_SERVER); session.removeAttribute(ATTR_EXPIRY); response.setStatus(HttpServletResponse.SC_OK); - - } else { @@ -250,7 +243,6 @@ public class SaslServlet extends AbstractServlet ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true); mapper.writeValue(writer, outputObject); - } } } diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java index 60f977ca66..5f553beb26 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureServlet.java @@ -47,7 +47,7 @@ public class StructureServlet extends AbstractServlet } @Override - protected void onGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); @@ -56,6 +56,8 @@ public class StructureServlet extends AbstractServlet response.setHeader("Pragma","no-cache"); response.setDateHeader ("Expires", 0); + // TODO filtering??? request.getParameter("filter"); // filter=1,2,3 /groups/*/* + Map structure = generateStructure(getBroker(), Broker.class); final PrintWriter writer = response.getWriter(); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html index baadc8c35f..e6c067fddf 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/showPrincipalDatabaseAuthenticationManager.html @@ -23,7 +23,5 @@
- - diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/group/addGroupMember.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/group/addGroupMember.html new file mode 100644 index 0000000000..0372468f91 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/group/addGroupMember.html @@ -0,0 +1,37 @@ + +
+
+
+ + + + + +
Name*:
+
+ + + +
+
+
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/group/showGroup.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/group/showGroup.html new file mode 100644 index 0000000000..4fddf727d0 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/group/showGroup.html @@ -0,0 +1,30 @@ + +
+ Name: +
+
+
+ + +
+
+ diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/groupprovider/addGroup.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/groupprovider/addGroup.html new file mode 100644 index 0000000000..8d3431808a --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/groupprovider/addGroup.html @@ -0,0 +1,38 @@ + +
+
+
+ + + + + +
Group Name*:
+
+ + + + +
+
+
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 new file mode 100644 index 0000000000..734e8b5419 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/groupprovider/showFileGroupManager.html @@ -0,0 +1,28 @@ + +
+
+
+ + +
+ +
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 08fdf5c99b..5557c37a2c 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 @@ -58,10 +58,10 @@ define(["dojo/_base/xhr"], return exchangeName == null || exchangeName == "" || "<>" == exchangeName || exchangeName.indexOf("amq.") == 0 || exchangeName.indexOf("qpid.") == 0; }; - util.deleteGridSelections = function(updater, gridName, url, confirmationMessageStart) + util.deleteGridSelections = function(updater, grid, url, confirmationMessageStart) { - var grid = updater[gridName].grid; var data = grid.selection.getSelected(); + if(data.length) { var confirmationMessage = null; @@ -103,7 +103,8 @@ define(["dojo/_base/xhr"], xhr.del({url: query, sync: true, handleAs: "json"}).then( function(data) { - grid.setQuery({id: "*"}); + // TODO why query *?? + //grid.setQuery({id: "*"}); grid.selection.deselectAll(); updater.update(); }, diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Exchange.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Exchange.js index 37bae1ef8e..5a5a6515ef 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Exchange.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Exchange.js @@ -115,7 +115,7 @@ define(["dojo/_base/xhr", { util.deleteGridSelections( this.exchangeUpdater, - "bindingsGrid", + that.exchangeUpdater.bindingsGrid.grid, "rest/binding/"+ encodeURIComponent(this.getVirtualHostName()) + "/" + encodeURIComponent(this.name), "Are you sure you want to delete binding for queue"); } diff --git a/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 new file mode 100644 index 0000000000..4e05f4b0ea --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/GroupProvider.js @@ -0,0 +1,109 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +define(["dojo/_base/xhr", + "dojo/parser", + "dojo/query", + "dojo/_base/connect", + "qpid/common/properties", + "qpid/common/updater", + "qpid/common/util", + "qpid/common/UpdatableStore", + "dojox/grid/EnhancedGrid", + "dojox/grid/enhanced/plugins/Pagination", + "dojox/grid/enhanced/plugins/IndirectSelection", + "dojo/domReady!"], + function (xhr, parser, query, connect, properties, updater, util, UpdatableStore, EnhancedGrid) { + + function GroupProvider(name, parent, controller) { + this.name = name; + this.controller = controller; + this.modelObj = { type: "groupprovider", name: name }; + if(parent) { + this.modelObj.parent = {}; + this.modelObj.parent[ parent.type] = parent; + } + } + + GroupProvider.prototype.getTitle = function() { + return "GroupProvider"; + }; + + GroupProvider.prototype.open = function(contentPane) { + var that = this; + this.contentPane = contentPane; + xhr.get({url: "showGroupProvider.html", + sync: true, + load: function(data) { + contentPane.containerNode.innerHTML = data; + parser.parse(contentPane.containerNode); + + that.groupProviderAdapter = new GroupProviderUpdater(contentPane.containerNode, that.modelObj, that.controller); + + updater.add( that.groupProviderAdapter ); + + that.groupProviderAdapter.update(); + + }}); + }; + + GroupProvider.prototype.close = function() { + updater.remove( this.groupProviderAdapter ); + }; + + function GroupProviderUpdater(node, groupProviderObj, controller) + { + this.controller = controller; + this.name = query(".name", node)[0]; + this.query = "rest/groupprovider/"+encodeURIComponent(groupProviderObj.name); + + var that = this; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) + .then(function(data) + { + that.groupProviderData = data[0]; + + util.flattenStatistics( that.groupProviderData ); + + that.updateHeader(); + + require(["qpid/management/groupprovider/"+that.groupProviderData.type], + function(SpecificProvider) { + that.details = new SpecificProvider(node, groupProviderObj, controller); + that.details.update(); + }); + + }); + + } + + GroupProviderUpdater.prototype.updateHeader = function() + { + this.name.innerHTML = this.groupProviderData[ "name" ]; + }; + + GroupProviderUpdater.prototype.update = function() + { + var that = this; + }; + + return GroupProvider; + }); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js index 957f2381cf..2efc46476d 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/VirtualHost.js @@ -72,7 +72,7 @@ define(["dojo/_base/xhr", function(evt){ util.deleteGridSelections( that.vhostUpdater, - "queuesGrid", + that.vhostUpdater.queuesGrid.grid, "rest/queue/"+ encodeURIComponent(that.name), "Are you sure you want to delete queue"); } @@ -87,7 +87,7 @@ define(["dojo/_base/xhr", { util.deleteGridSelections( that.vhostUpdater, - "exchangesGrid", + that.vhostUpdater.exchangesGrid.grid, "rest/exchange/"+ encodeURIComponent(that.name), "Are you sure you want to delete exchange"); } diff --git a/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 1aa05a5a3c..5d3a666760 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 @@ -27,13 +27,17 @@ define(["dojo/dom", "qpid/management/Queue", "qpid/management/Connection", "qpid/management/AuthenticationProvider", + "qpid/management/GroupProvider", + "qpid/management/group/Group", "dojo/ready", "dojo/domReady!"], - function (dom, registry, ContentPane, Broker, VirtualHost, Exchange, Queue, Connection, AuthProvider, ready) { + function (dom, registry, ContentPane, Broker, VirtualHost, Exchange, Queue, Connection, AuthProvider, GroupProvider, Group, ready) { var controller = {}; var constructors = { broker: Broker, virtualhost: VirtualHost, exchange: Exchange, - queue: Queue, connection: Connection, authenticationprovider: AuthProvider }; + queue: Queue, connection: Connection, + authenticationprovider: AuthProvider, groupprovider: GroupProvider, + group: Group }; var tabDiv = dom.byId("managedViews"); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/Group.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/Group.js new file mode 100644 index 0000000000..801f35c7aa --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/Group.js @@ -0,0 +1,215 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +define(["dojo/_base/xhr", + "dojo/parser", + "dojo/query", + "dijit/registry", + "dojo/_base/connect", + "dojo/_base/event", + "dojo/json", + "qpid/common/properties", + "qpid/common/updater", + "qpid/common/util", + "qpid/common/formatter", + "qpid/common/UpdatableStore", + "dojo/store/JsonRest", + "dojox/grid/EnhancedGrid", + "dojo/data/ObjectStore", + "qpid/management/group/addGroupMember", + "dojox/grid/enhanced/plugins/Pagination", + "dojox/grid/enhanced/plugins/IndirectSelection", + "dojo/domReady!"], + function (xhr, parser, query, registry, connect, event, json, properties, updater, util, formatter, + UpdatableStore, JsonRest, EnhancedGrid, ObjectStore, addGroupMember) { + + function Group(name, parent, controller) { + this.name = name; + this.controller = controller; + this.modelObj = { type: "group", name: name }; + + if(parent) { + this.modelObj.parent = {}; + this.modelObj.parent[ parent.type] = parent; + } + } + + Group.prototype.getGroupName = function() + { + return this.name; + }; + + + Group.prototype.getGroupProviderName = function() + { + return this.modelObj.parent.groupprovider.name; + }; + + Group.prototype.getTitle = function() + { + return "Group: " + this.name; + }; + + Group.prototype.open = function(contentPane) { + var that = this; + this.contentPane = contentPane; + + xhr.get({url: "group/showGroup.html", + sync: true, + load: function(data) { + contentPane.containerNode.innerHTML = data; + parser.parse(contentPane.containerNode); + + that.groupUpdater = new GroupUpdater(contentPane.containerNode, that, that.controller); + + updater.add( that.groupUpdater ); + + that.groupUpdater.update(); + + var addGroupMemberButton = query(".addGroupMemberButton", contentPane.containerNode)[0]; + connect.connect(registry.byNode(addGroupMemberButton), "onClick", + function(evt){ + addGroupMember.show(that.getGroupProviderName(), that.getGroupName()) + } + ); + + var removeGroupMemberButton = query(".removeGroupMemberButton", contentPane.containerNode)[0]; + connect.connect(registry.byNode(removeGroupMemberButton), "onClick", + function(evt){ + util.deleteGridSelections( + that.groupUpdater, + that.groupUpdater.groupMembersUpdatableStore.grid, + "rest/groupmember/"+ encodeURIComponent(that.getGroupProviderName()) + + "/" + encodeURIComponent(that.getGroupName()), + "Are you sure you want to remove group member"); + } + ); + }}); + }; + + Group.prototype.close = function() { + updater.remove( this.groupUpdater ); + }; + + function GroupUpdater(containerNode, groupObj, controller) + { + var that = this; + + function findNode(name) { + return query("." + name, containerNode)[0]; + } + + function storeNodes(names) + { + for(var i = 0; i < names.length; i++) { + that[names[i]] = findNode(names[i]); + } + } + + storeNodes(["name", + "state", + "durable", + "lifetimePolicy", + "type"]); + + this.query = "rest/groupmember/"+ encodeURIComponent(groupObj.getGroupProviderName()) + "/" + encodeURIComponent(groupObj.getGroupName()); + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}).then(function(data) + { + that.groupMemberData = data; + + util.flattenStatistics( that.groupMemberData ); + + var gridProperties = { + keepSelection: true, + plugins: { + pagination: { + pageSizes: ["10", "25", "50", "100"], + description: true, + sizeSwitch: true, + pageStepper: true, + gotoButton: true, + maxPageStep: 4, + position: "bottom" + }, + indirectSelection: true + + }}; + + that.groupMembersUpdatableStore = new UpdatableStore(that.groupMemberData, findNode("groupMembers"), + [ { name: "Group Member Name", field: "name", width: "100%" }], + function(obj) + { + connect.connect(obj.grid, "onRowDblClick", obj.grid, + function(evt){ + + }); + } , gridProperties, EnhancedGrid); + + }); + + } + + GroupUpdater.prototype.update = function() + { + + var that = this; + + console.log("updater called "); + console.dir(that); + + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) + .then(function(data) { + that.groupMemberData = data; + + console.log("updater data "); + console.dir(that.groupMemberData); + + + util.flattenStatistics( that.groupMemberData ); + + that.groupMembersUpdatableStore.update(that.groupMemberData); + + console.log("updated grid"); + console.dir(that.groupMembersUpdatableStore.grid); + }); + }; + + Group.prototype.deleteGroupMember = function() { + if(confirm("Are you sure you want to delete group member'" +this.name+"'?")) { + var query = "rest/groupmember/"+ encodeURIComponent(this.getGroupProviderName()) + "/" + encodeURIComponent(this.name); + this.success = true + var that = this; + xhr.del({url: query, sync: true, handleAs: "json"}).then( + function(data) { + that.contentPane.onClose() + that.controller.tabContainer.removeChild(that.contentPane); + that.contentPane.destroyRecursive(); + }, + function(error) {that.success = false; that.failureReason = error;}); + if(!this.success ) { + alert("Error:" + this.failureReason); + } + } + } + + return Group; + }); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/addGroupMember.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/addGroupMember.js new file mode 100644 index 0000000000..1861cc6ffe --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/group/addGroupMember.js @@ -0,0 +1,108 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +define(["dojo/_base/xhr", + "dojo/dom", + "dojo/dom-construct", + "dojo/_base/window", + "dijit/registry", + "dojo/parser", + "dojo/_base/array", + "dojo/_base/event", + 'dojo/_base/json', + "dijit/form/NumberSpinner", // required by the form + /* dojox/ validate resources */ + "dojox/validate/us", "dojox/validate/web", + /* basic dijit classes */ + "dijit/Dialog", + "dijit/form/CheckBox", "dijit/form/Textarea", + "dijit/form/FilteringSelect", "dijit/form/TextBox", + "dijit/form/ValidationTextBox", "dijit/form/DateTextBox", + "dijit/form/TimeTextBox", "dijit/form/Button", + "dijit/form/RadioButton", "dijit/form/Form", + "dijit/form/DateTextBox", + /* basic dojox classes */ + "dojox/form/BusyButton", "dojox/form/CheckedMultiSelect", + "dojo/domReady!"], + function (xhr, dom, construct, win, registry, parser, array, event, json) { + + var addGroupMember = {}; + + var node = construct.create("div", null, win.body(), "last"); + + var convertToGroupMember = function convertToGroupMember(formValues) + { + var newGroupMember = {}; + newGroupMember.name = formValues.name; + return newGroupMember; + }; + + xhr.get({url: "group/addGroupMember.html", + sync: true, + load: function(data) { + var theForm; + node.innerHTML = data; + addGroupMember.dialogNode = dom.byId("addGroupMember"); + parser.instantiate([addGroupMember.dialogNode]); + + theForm = registry.byId("formAddGroupMember"); + theForm.on("submit", function(e) { + + event.stop(e); + if(theForm.validate()){ + + var newGroupMember = convertToGroupMember(theForm.getValues()); + var that = this; + xhr.put({url: "rest/groupmember/"+encodeURIComponent(addGroupMember.groupProvider) + + "/" + encodeURIComponent(addGroupMember.group) + "/" + encodeURIComponent(newGroupMember.name), sync: true, handleAs: "json", + headers: { "Content-Type": "application/json"}, + putData: json.toJson(newGroupMember), + load: function(x) {that.success = true; }, + error: function(error) {that.success = false; that.failureReason = error;}}); + + if(this.success === true) + { + registry.byId("addGroupMember").hide(); + } + else + { + alert("Error:" + this.failureReason); + } + + return false; + + + }else{ + alert('Form contains invalid data. Please correct first'); + return false; + } + + }); + }}); + + addGroupMember.show = function(groupProvider, group) { + addGroupMember.groupProvider = groupProvider; + addGroupMember.group = group; + registry.byId("formAddGroupMember").reset(); + registry.byId("addGroupMember").show(); + }; + + return addGroupMember; + }); \ No newline at end of file diff --git a/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 new file mode 100644 index 0000000000..44fc9702e2 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/groupprovider/FileGroupManager.js @@ -0,0 +1,251 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +define(["dojo/_base/xhr", + "dojo/dom", + "dojo/parser", + "dojo/query", + "dojo/dom-construct", + "dojo/_base/connect", + "dojo/_base/window", + "dojo/_base/event", + "dojo/_base/json", + "dijit/registry", + "qpid/common/util", + "qpid/common/properties", + "qpid/common/updater", + "qpid/common/UpdatableStore", + "dojox/grid/EnhancedGrid", + "dojox/grid/enhanced/plugins/Pagination", + "dojox/grid/enhanced/plugins/IndirectSelection", + "dojox/validate/us", "dojox/validate/web", + "dijit/Dialog", + "dijit/form/TextBox", + "dijit/form/ValidationTextBox", + "dijit/form/TimeTextBox", "dijit/form/Button", + "dijit/form/Form", + "dijit/form/DateTextBox", + "dojo/domReady!"], + function (xhr, dom, parser, query, construct, connect, win, event, json, registry, util, properties, updater, UpdatableStore, EnhancedGrid) { + function DatabaseGroupManager(containerNode, groupProviderObj, controller) { + var node = construct.create("div", null, containerNode, "last"); + var that = this; + this.name = groupProviderObj.name; + xhr.get({url: "groupprovider/showFileGroupManager.html", + sync: true, + load: function(data) { + node.innerHTML = data; + parser.parse(node); + + + that.groupDatabaseUpdater= new GroupProviderUpdater(node, groupProviderObj, controller); + + updater.add( that.groupDatabaseUpdater); + + that.groupDatabaseUpdater.update(); + + + }}); + } + + DatabaseGroupManager.prototype.update = function() { + this.groupDatabaseUpdater.update(); + }; + + DatabaseGroupManager.prototype.close = function() { + updater.remove( this.groupDatabaseUpdater ); + }; + + function GroupProviderUpdater(node, groupProviderObj, controller) + { + this.controller = controller; + this.query = "rest/groupprovider/"+encodeURIComponent(groupProviderObj.name); + this.name = groupProviderObj.name; + var that = this; + + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) + .then(function(data) { + that.groupProviderData = data[0]; + + util.flattenStatistics( that.groupProviderData ); + + var groupDiv = query(".groups")[0]; + + var gridProperties = { + height: 400, + keepSelection: true, + plugins: { + pagination: { + pageSizes: ["10", "25", "50", "100"], + description: true, + sizeSwitch: true, + pageStepper: true, + gotoButton: true, + maxPageStep: 4, + position: "bottom" + }, + indirectSelection: true + + }}; + + + that.groupsGrid = + new UpdatableStore(that.groupProviderData.groups, groupDiv, + [ { name: "Group Name", field: "name", width: "100%" } + ], null, gridProperties, EnhancedGrid); + + + var addGroupButton = query(".addGroupButton", node)[0]; + connect.connect(registry.byNode(addGroupButton), "onClick", function(evt){ addGroup.show(groupProviderObj.name) }); + + var deleteMessagesButton = query(".deleteGroupButton", node)[0]; + var deleteWidget = registry.byNode(deleteMessagesButton); + connect.connect(deleteWidget, "onClick", + function(evt){ + event.stop(evt); + that.deleteGroups(); + }); + }); + } + + GroupProviderUpdater.prototype.deleteGroups = function() + { + var grid = this.groupsGrid.grid; + var data = grid.selection.getSelected(); + if(data.length) { + var that = this; + if(confirm("Delete " + data.length + " groups?")) { + var i, queryParam; + for(i = 0; i +
+ Name: +
+ Type: +
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/AuthenticationProviderRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/AuthenticationProviderRestTest.java index 5e6d9a998a..86533b76b4 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/AuthenticationProviderRestTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/AuthenticationProviderRestTest.java @@ -33,13 +33,13 @@ public class AuthenticationProviderRestTest extends QpidRestTestCase public void testGet() throws Exception { - List> providerDetails = getJsonAsList("/rest/authenticationprovider"); + List> providerDetails = getRestTestHelper().getJsonAsList("/rest/authenticationprovider"); assertNotNull("Providers details cannot be null", providerDetails); assertEquals("Unexpected number of providers", 1, providerDetails.size()); for (Map provider : providerDetails) { assertProvider("PrincipalDatabaseAuthenticationManager", provider); - Map data = getJsonAsSingletonList("/rest/authenticationprovider/" + Map data = getRestTestHelper().getJsonAsSingletonList("/rest/authenticationprovider/" + provider.get(AuthenticationProvider.NAME)); assertNotNull("Cannot load data for " + provider.get(AuthenticationProvider.NAME), data); assertProvider("PrincipalDatabaseAuthenticationManager", data); diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BindingRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BindingRestTest.java index 1ed0d97185..c2f11ed237 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BindingRestTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BindingRestTest.java @@ -32,7 +32,7 @@ public class BindingRestTest extends QpidRestTestCase public void testGetAllBindings() throws Exception { - List> bindings = getJsonAsList("/rest/binding"); + List> bindings = getRestTestHelper().getJsonAsList("/rest/binding"); assertNotNull("Bindings cannot be null", bindings); assertTrue("Unexpected number of bindings", bindings.size() >= EXPECTED_HOSTS.length * EXPECTED_QUEUES.length); for (Map binding : bindings) @@ -43,7 +43,7 @@ public class BindingRestTest extends QpidRestTestCase public void testGetVirtualHostBindings() throws Exception { - List> bindings = getJsonAsList("/rest/binding/test"); + List> bindings = getRestTestHelper().getJsonAsList("/rest/binding/test"); assertNotNull("Bindings cannot be null", bindings); assertEquals("Unexpected number of bindings", EXPECTED_QUEUES.length * 2, bindings.size()); for (String queueName : EXPECTED_QUEUES) @@ -52,31 +52,31 @@ public class BindingRestTest extends QpidRestTestCase searchAttributes.put(Binding.NAME, queueName); searchAttributes.put(Binding.EXCHANGE, "amq.direct"); - Map binding = find(searchAttributes, bindings); + Map binding = getRestTestHelper().find(searchAttributes, bindings); Asserts.assertBinding(queueName, "amq.direct", binding); searchAttributes.put(Binding.EXCHANGE, "<>"); - binding = find(searchAttributes, bindings); + binding = getRestTestHelper().find(searchAttributes, bindings); Asserts.assertBinding(queueName, "<>", binding); } } public void testGetVirtualHostExchangeBindings() throws Exception { - List> bindings = getJsonAsList("/rest/binding/test/amq.direct"); + List> bindings = getRestTestHelper().getJsonAsList("/rest/binding/test/amq.direct"); assertNotNull("Bindings cannot be null", bindings); assertEquals("Unexpected number of bindings", EXPECTED_QUEUES.length, bindings.size()); for (String queueName : EXPECTED_QUEUES) { - Map binding = find(Binding.NAME, queueName, bindings); + Map binding = getRestTestHelper().find(Binding.NAME, queueName, bindings); Asserts.assertBinding(queueName, "amq.direct", binding); } } public void testGetVirtualHostExchangeQueueBindings() throws Exception { - List> bindings = getJsonAsList("/rest/binding/test/amq.direct/queue"); + List> bindings = getRestTestHelper().getJsonAsList("/rest/binding/test/amq.direct/queue"); assertNotNull("Bindings cannot be null", bindings); assertEquals("Unexpected number of bindings", 1, bindings.size()); Asserts.assertBinding("queue", "amq.direct", bindings.get(0)); @@ -85,25 +85,25 @@ public class BindingRestTest extends QpidRestTestCase public void testDeleteBinding() throws Exception { - List> bindings = getJsonAsList("/rest/binding/test/amq.direct/queue/queue"); + List> bindings = getRestTestHelper().getJsonAsList("/rest/binding/test/amq.direct/queue/queue"); assertEquals("Unexpected number of bindings", 1, bindings.size()); Asserts.assertBinding("queue", "amq.direct", bindings.get(0)); - HttpURLConnection connection = openManagementConection("/rest/binding/test/amq.direct/queue/queue", "DELETE"); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/binding/test/amq.direct/queue/queue", "DELETE"); connection.connect(); assertEquals("Unexpected response code", 200, connection.getResponseCode()); - bindings = getJsonAsList("/rest/binding/test/amq.direct/queue/queue"); + bindings = getRestTestHelper().getJsonAsList("/rest/binding/test/amq.direct/queue/queue"); assertEquals("Binding should be deleted", 0, bindings.size()); } public void testDeleteBindingById() throws Exception { - Map binding = getJsonAsSingletonList("/rest/binding/test/amq.direct/queue"); - HttpURLConnection connection = openManagementConection("/rest/binding/test/amq.direct?id=" + binding.get(Binding.ID), "DELETE"); + Map binding = getRestTestHelper().getJsonAsSingletonList("/rest/binding/test/amq.direct/queue"); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/binding/test/amq.direct?id=" + binding.get(Binding.ID), "DELETE"); connection.connect(); assertEquals("Unexpected response code", 200, connection.getResponseCode()); - List> bindings = getJsonAsList("/rest/binding/test/amq.direct/queue"); + List> bindings = getRestTestHelper().getJsonAsList("/rest/binding/test/amq.direct/queue"); assertEquals("Binding should be deleted", 0, bindings.size()); } @@ -115,13 +115,13 @@ public class BindingRestTest extends QpidRestTestCase bindingData.put(Binding.QUEUE, "queue"); bindingData.put(Binding.EXCHANGE, "amq.direct"); - HttpURLConnection connection = openManagementConection("/rest/binding/test/amq.direct/queue/" + bindingName, "PUT"); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/binding/test/amq.direct/queue/" + bindingName, "PUT"); connection.connect(); - writeJsonRequest(connection, bindingData); + getRestTestHelper().writeJsonRequest(connection, bindingData); int responseCode = connection.getResponseCode(); connection.disconnect(); assertEquals("Unexpected response code", 201, responseCode); - Map binding = getJsonAsSingletonList("/rest/binding/test/amq.direct/queue/" + bindingName); + Map binding = getRestTestHelper().getJsonAsSingletonList("/rest/binding/test/amq.direct/queue/" + bindingName); Asserts.assertBinding(bindingName, "queue", "amq.direct", binding); } diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestHttpsTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestHttpsTest.java index 4bbe9155cd..08b5863004 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestHttpsTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestHttpsTest.java @@ -21,13 +21,8 @@ package org.apache.qpid.server.management.plugin.servlet.rest; import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URL; import java.util.Map; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLSocketFactory; - import org.apache.commons.configuration.ConfigurationException; import org.apache.qpid.server.model.Broker; @@ -48,37 +43,16 @@ public class BrokerRestHttpsTest extends QpidRestTestCase @Override protected void customizeConfiguration() throws ConfigurationException, IOException { + getRestTestHelper().setUseSsl(true); setConfigurationProperty("management.enabled", "true"); setConfigurationProperty("management.http.enabled", "false"); setConfigurationProperty("management.https.enabled", "true"); - setConfigurationProperty("management.https.port", Integer.toString(getHttpPort())); - } - - @Override - protected String getHostName() - { - return "localhost"; - } - - @Override - protected String getProtocol() - { - return "https"; - } - - @Override - protected HttpURLConnection openManagementConection(String path) throws IOException - { - URL url = getManagementURL(path); - HttpURLConnection httpCon = (HttpURLConnection) url.openConnection(); - ((HttpsURLConnection) httpCon).setSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault()); - httpCon.setDoOutput(true); - return httpCon; + setConfigurationProperty("management.https.port", Integer.toString(getRestTestHelper().getHttpPort())); } public void testGetWithHttps() throws Exception { - Map brokerDetails = getJsonAsSingletonList("/rest/broker"); + Map 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); diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestTest.java index f2970e2ba9..c4cd00416b 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/BrokerRestTest.java @@ -43,7 +43,7 @@ public class BrokerRestTest extends QpidRestTestCase public void testGet() throws Exception { - Map brokerDetails = getJsonAsSingletonList("/rest/broker"); + Map brokerDetails = getRestTestHelper().getJsonAsSingletonList("/rest/broker"); assertBrokerAttributes(brokerDetails); @@ -55,9 +55,9 @@ public class BrokerRestTest extends QpidRestTestCase List> virtualhosts = (List>) brokerDetails.get(BROKER_VIRTUALHOSTS_ATTRIBUTE); assertEquals("Unexpected number of virtual hosts", 3, virtualhosts.size()); - Asserts.assertVirtualHost("development", find(VirtualHost.NAME, "development", virtualhosts)); - Asserts.assertVirtualHost("localhost", find(VirtualHost.NAME, "localhost", virtualhosts)); - Asserts.assertVirtualHost("test", find(VirtualHost.NAME, "test", virtualhosts)); + Asserts.assertVirtualHost("development", getRestTestHelper().find(VirtualHost.NAME, "development", virtualhosts)); + Asserts.assertVirtualHost("localhost", getRestTestHelper().find(VirtualHost.NAME, "localhost", virtualhosts)); + Asserts.assertVirtualHost("test", getRestTestHelper().find(VirtualHost.NAME, "test", virtualhosts)); @SuppressWarnings("unchecked") List> ports = (List>) brokerDetails.get(BROKER_PORTS_ATTRIBUTE); @@ -70,8 +70,8 @@ public class BrokerRestTest extends QpidRestTestCase String bindingAddress = (String)ports.get(0).get(Port.BINDING_ADDRESS); - Map amqpPort = find(Port.NAME, bindingAddress + ":" + getPort(), ports); - Map httpPort = find(Port.NAME, bindingAddress + ":" + getHttpPort(), ports); + Map amqpPort = getRestTestHelper().find(Port.NAME, bindingAddress + ":" + getPort(), ports); + Map httpPort = getRestTestHelper().find(Port.NAME, bindingAddress + ":" + getRestTestHelper().getHttpPort(), ports); assertNotNull("Cannot find AMQP port", amqpPort); assertNotNull("Cannot find HTTP port", httpPort); diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ConnectionRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ConnectionRestTest.java index 3661b94a7c..500a5770fb 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ConnectionRestTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ConnectionRestTest.java @@ -90,14 +90,14 @@ public class ConnectionRestTest extends QpidRestTestCase public void testGetAllConnections() throws Exception { - List> connections = getJsonAsList("/rest/connection"); + List> connections = getRestTestHelper().getJsonAsList("/rest/connection"); assertEquals("Unexpected number of connections", 1, connections.size()); Asserts.assertConnection(connections.get(0), (AMQConnection) _connection); } public void testGetVirtualHostConnections() throws Exception { - List> connections = getJsonAsList("/rest/connection/test"); + List> connections = getRestTestHelper().getJsonAsList("/rest/connection/test"); assertEquals("Unexpected number of connections", 1, connections.size()); Asserts.assertConnection(connections.get(0), (AMQConnection) _connection); } @@ -107,21 +107,21 @@ public class ConnectionRestTest extends QpidRestTestCase // get connection name String connectionName = getConnectionName(); - Map connectionDetails = getJsonAsSingletonList("/rest/connection/test/" + Map connectionDetails = getRestTestHelper().getJsonAsSingletonList("/rest/connection/test/" + URLDecoder.decode(connectionName, "UTF-8")); assertConnection(connectionDetails); } public void testGetAllSessions() throws Exception { - List> sessions = getJsonAsList("/rest/session"); + List> sessions = getRestTestHelper().getJsonAsList("/rest/session"); assertEquals("Unexpected number of sessions", 1, sessions.size()); assertSession(sessions.get(0), (AMQSession) _session); } public void testGetVirtualHostSessions() throws Exception { - List> sessions = getJsonAsList("/rest/session/test"); + List> sessions = getRestTestHelper().getJsonAsList("/rest/session/test"); assertEquals("Unexpected number of sessions", 1, sessions.size()); assertSession(sessions.get(0), (AMQSession) _session); } @@ -131,7 +131,7 @@ public class ConnectionRestTest extends QpidRestTestCase // get connection name String connectionName = getConnectionName(); - List> sessions = getJsonAsList("/rest/session/test/" + List> sessions = getRestTestHelper().getJsonAsList("/rest/session/test/" + URLDecoder.decode(connectionName, "UTF-8")); assertEquals("Unexpected number of sessions", 1, sessions.size()); assertSession(sessions.get(0), (AMQSession) _session); @@ -142,7 +142,7 @@ public class ConnectionRestTest extends QpidRestTestCase // get connection name String connectionName = getConnectionName(); - List> sessions = getJsonAsList("/rest/session/test/" + List> sessions = getRestTestHelper().getJsonAsList("/rest/session/test/" + URLDecoder.decode(connectionName, "UTF-8") + "/" + ((AMQSession) _session).getChannelId()); assertEquals("Unexpected number of sessions", 1, sessions.size()); assertSession(sessions.get(0), (AMQSession) _session); @@ -201,7 +201,7 @@ public class ConnectionRestTest extends QpidRestTestCase private String getConnectionName() throws IOException { - Map hostDetails = getJsonAsSingletonList("/rest/virtualhost/test"); + Map hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); @SuppressWarnings("unchecked") List> connections = (List>) hostDetails .get(VirtualHostRestTest.VIRTUALHOST_CONNECTIONS_ATTRIBUTE); diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ExchangeRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ExchangeRestTest.java index 4904d2adf3..363652d37c 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ExchangeRestTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/ExchangeRestTest.java @@ -31,7 +31,7 @@ public class ExchangeRestTest extends QpidRestTestCase { public void testGet() throws Exception { - List> exchanges = getJsonAsList("/rest/exchange"); + List> exchanges = getRestTestHelper().getJsonAsList("/rest/exchange"); assertNotNull("Exchanges cannot be null", exchanges); assertTrue("Unexpected number of exchanges", exchanges.size() >= EXPECTED_HOSTS.length * EXPECTED_EXCHANGES.length); for (Map exchange : exchanges) @@ -42,12 +42,12 @@ public class ExchangeRestTest extends QpidRestTestCase public void testGetHostExchanges() throws Exception { - List> exchanges = getJsonAsList("/rest/exchange/test"); + List> exchanges = getRestTestHelper().getJsonAsList("/rest/exchange/test"); assertNotNull("Users cannot be null", exchanges); assertEquals("Unexpected number of exchanges", 6, EXPECTED_EXCHANGES.length); for (String exchangeName : EXPECTED_EXCHANGES) { - Map exchange = find(Exchange.NAME, exchangeName, exchanges); + Map exchange = getRestTestHelper().find(Exchange.NAME, exchangeName, exchanges); assertExchange(exchangeName, exchange); } } @@ -56,7 +56,7 @@ public class ExchangeRestTest extends QpidRestTestCase { for (String exchangeName : EXPECTED_EXCHANGES) { - Map exchange = getJsonAsSingletonList("/rest/exchange/test/" + Map exchange = getRestTestHelper().getJsonAsSingletonList("/rest/exchange/test/" + URLDecoder.decode(exchangeName, "UTF-8")); assertExchange(exchangeName, exchange); } @@ -79,7 +79,7 @@ public class ExchangeRestTest extends QpidRestTestCase List> bindings = (List>) exchange.get("bindings"); for (String queueName : EXPECTED_QUEUES) { - Map binding = find(Binding.NAME, queueName, bindings); + Map binding = getRestTestHelper().find(Binding.NAME, queueName, bindings); Asserts.assertBinding(queueName, (String) exchange.get(Exchange.NAME), binding); } } diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/GroupProviderRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/GroupProviderRestTest.java new file mode 100644 index 0000000000..c2eefec395 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/GroupProviderRestTest.java @@ -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. + * + */ +package org.apache.qpid.server.management.plugin.servlet.rest; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +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; + +public class GroupProviderRestTest extends QpidRestTestCase +{ + private static final String FILE_GROUP_MANAGER = "FileGroupManager"; + private File _groupFile; + + @Override + public void setUp() throws Exception + { + _groupFile = createTemporaryGroupFile(); + + setConfigurationProperty("security.file-group-manager.attributes.attribute.name", "groupFile"); + setConfigurationProperty("security.file-group-manager.attributes.attribute.value", _groupFile.getAbsolutePath()); + + super.setUp(); + } + + @Override + public void tearDown() throws Exception + { + super.tearDown(); + + if (_groupFile != null) + { + if (_groupFile.exists()) + { + _groupFile.delete(); + } + } + } + + public void testGet() throws Exception + { + List> providerDetails = getRestTestHelper().getJsonAsList("/rest/groupprovider"); + assertNotNull("Providers details cannot be null", providerDetails); + assertEquals("Unexpected number of providers", 1, providerDetails.size()); + for (Map provider : providerDetails) + { + assertProvider(FILE_GROUP_MANAGER, provider); + Map data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + + provider.get(GroupProvider.NAME)); + assertNotNull("Cannot load data for " + provider.get(GroupProvider.NAME), data); + assertProvider(FILE_GROUP_MANAGER, data); + } + } + + public void testCreateNewGroup() throws Exception + { + String groupName = "newGroup"; + + Map data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + assertNotNull("Cannot load data for provider", data); + + getRestTestHelper().assertNumberOfGroups(data, 1); + + getRestTestHelper().createGroup(groupName, FILE_GROUP_MANAGER); + + data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + assertNotNull("Cannot load data for provider", data); + + getRestTestHelper().assertNumberOfGroups(data, 2); + } + + public void testRemoveGroup() throws Exception + { + String groupName = "myGroup"; + + Map data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + assertNotNull("Cannot load data for provider", data); + + getRestTestHelper().assertNumberOfGroups(data, 1); + + getRestTestHelper().removeGroup(groupName, FILE_GROUP_MANAGER); + + data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + assertNotNull("Cannot load data for provider", data); + + getRestTestHelper().assertNumberOfGroups(data, 0); + } + + + private void assertProvider(String type, Map provider) + { + Asserts.assertAttributesPresent(provider, GroupProvider.AVAILABLE_ATTRIBUTES, + GroupProvider.CREATED, GroupProvider.UPDATED, GroupProvider.DESCRIPTION, + GroupProvider.TIME_TO_LIVE); + assertEquals("Unexpected value of provider attribute " + GroupProvider.STATE, State.ACTIVE.name(), + provider.get(GroupProvider.STATE)); + assertEquals("Unexpected value of provider attribute " + GroupProvider.LIFETIME_POLICY, + LifetimePolicy.PERMANENT.name(), provider.get(GroupProvider.LIFETIME_POLICY)); + assertEquals("Unexpected value of provider attribute " + GroupProvider.DURABLE, Boolean.TRUE, + provider.get(GroupProvider.DURABLE)); + 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); + + @SuppressWarnings("unchecked") + List> groups = (List>) provider.get("groups"); + assertNotNull("Groups were not found", groups); + assertEquals("Unexpected number of groups", 1, groups.size()); + for (Map group : groups) + { + + final String groupName = (String) group.get(Group.NAME); + assertNotNull("Attribute " + Group.NAME, groupName); + + assertNotNull("Attribute " + Group.ID, group.get(Group.ID)); + assertEquals("Attribute " + Group.ID, UUIDGenerator.generateGroupUUID(name, groupName).toString(), group.get(Group.ID)); + } + } + + private File createTemporaryGroupFile() throws Exception + { + File groupFile = File.createTempFile("group", "grp"); + groupFile.deleteOnExit(); + + Properties props = new Properties(); + props.put("myGroup.users", "guest"); + + props.store(new FileOutputStream(groupFile), "test group file"); + + return groupFile; + } +} diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/GroupRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/GroupRestTest.java new file mode 100644 index 0000000000..5430cce6dc --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/GroupRestTest.java @@ -0,0 +1,109 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.management.plugin.servlet.rest; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.qpid.server.model.GroupMember; + +public class GroupRestTest extends QpidRestTestCase +{ + private static final String GROUP_NAME = "myGroup"; + private static final String FILE_GROUP_MANAGER = "FileGroupManager"; + private static final String EXISTING_MEMBER = "user1"; + private static final String NEW_MEMBER = "user2"; + + private File _groupFile; + + @Override + public void setUp() throws Exception + { + _groupFile = createTemporaryGroupFile(); + + setConfigurationProperty("security.file-group-manager.attributes.attribute.name", "groupFile"); + setConfigurationProperty("security.file-group-manager.attributes.attribute.value", _groupFile.getAbsolutePath()); + + super.setUp(); + } + + @Override + public void tearDown() throws Exception + { + super.tearDown(); + + if (_groupFile != null) + { + if (_groupFile.exists()) + { + _groupFile.delete(); + } + } + } + + public void testGet() throws Exception + { + Map group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + List> groupmembers = (List>) group.get("groupmembers"); + assertEquals(1, groupmembers.size()); + + Map member1 = groupmembers.get(0); + assertEquals(EXISTING_MEMBER, (String)member1.get(GroupMember.NAME)); + } + + public void testCreateNewMemberOfGroup() throws Exception + { + Map group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + getRestTestHelper().assertNumberOfGroupMembers(group, 1); + + getRestTestHelper().createNewGroupMember(FILE_GROUP_MANAGER, GROUP_NAME, NEW_MEMBER); + + group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + getRestTestHelper().assertNumberOfGroupMembers(group, 2); + } + + public void testRemoveMemberFromGroup() throws Exception + { + Map group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + getRestTestHelper().assertNumberOfGroupMembers(group, 1); + + getRestTestHelper().removeMemberFromGroup(FILE_GROUP_MANAGER, GROUP_NAME, EXISTING_MEMBER); + + group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/myGroup"); + getRestTestHelper().assertNumberOfGroupMembers(group, 0); + } + + private File createTemporaryGroupFile() throws Exception + { + File groupFile = File.createTempFile("group", "grp"); + groupFile.deleteOnExit(); + + Properties props = new Properties(); + props.put(GROUP_NAME + ".users", EXISTING_MEMBER); + + props.store(new FileOutputStream(groupFile), "test group file"); + + return groupFile; + } +} diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsRestTest.java index c64fd6e1da..34b25e2e40 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsRestTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsRestTest.java @@ -27,10 +27,10 @@ public class LogRecordsRestTest extends QpidRestTestCase { public void testGet() throws Exception { - List> logs = getJsonAsList("/rest/logrecords"); + List> logs = getRestTestHelper().getJsonAsList("/rest/logrecords"); assertNotNull("Logs data cannot be null", logs); assertTrue("Logs are not found", logs.size() > 0); - Map record = find("message", "[Broker] BRK-1004 : Qpid Broker Ready", logs); + Map record = getRestTestHelper().find("message", "[Broker] BRK-1004 : Qpid Broker Ready", logs); assertNotNull("BRK-1004 message is not found", record); assertNotNull("Message id cannot be null", record.get("id")); diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/MessagesRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/MessagesRestTest.java index 492df43957..a4efcc9456 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/MessagesRestTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/MessagesRestTest.java @@ -85,7 +85,7 @@ public class MessagesRestTest extends QpidRestTestCase public void testGet() throws Exception { String queueName = getTestQueueName(); - List> messages = getJsonAsList("/rest/message/test/" + queueName); + List> messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName); assertNotNull("Messages are not found", messages); assertEquals("Unexpected number of messages", MESSAGE_NUMBER, messages.size()); int position = 0; @@ -111,7 +111,7 @@ public class MessagesRestTest extends QpidRestTestCase // get message IDs List ids = getMesssageIds(queueName); - Map message = getJsonAsMap("/rest/message/test/" + queueName + "/" + ids.get(0)); + Map message = getRestTestHelper().getJsonAsMap("/rest/message/test/" + queueName + "/" + ids.get(0)); assertMessageAttributes(message); assertMessageAttributeValues(message, true); @@ -121,7 +121,7 @@ public class MessagesRestTest extends QpidRestTestCase assertEquals("Unexpected message header", 0, headers.get("index")); Long lastMessageId = ids.get(ids.size() - 1); - message = getJsonAsMap("/rest/message/test/" + queueName + "/" + lastMessageId); + message = getRestTestHelper().getJsonAsMap("/rest/message/test/" + queueName + "/" + lastMessageId); assertMessageAttributes(message); assertEquals("Unexpected message attribute mimeType", "application/octet-stream", message.get("mimeType")); assertEquals("Unexpected message attribute size", 4, message.get("size")); @@ -132,10 +132,10 @@ public class MessagesRestTest extends QpidRestTestCase assertEquals("Unexpected message header", "value", bytesMessageHeader.get("test")); // get content - HttpURLConnection connection = openManagementConection("/rest/message-content/test/" + queueName + "/" + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/message-content/test/" + queueName + "/" + lastMessageId, "GET"); connection.connect(); - byte[] data = readConnectionInputStream(connection); + byte[] data = getRestTestHelper().readConnectionInputStream(connection); assertTrue("Unexpected message", Arrays.equals(messageBytes, data)); } @@ -159,38 +159,38 @@ public class MessagesRestTest extends QpidRestTestCase } // move messages - HttpURLConnection connection = openManagementConection("/rest/message/test/" + queueName, "POST"); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/message/test/" + queueName, "POST"); Map messagesData = new HashMap(); messagesData.put("messages", movedMessageIds); messagesData.put("destinationQueue", queueName2); messagesData.put("move", Boolean.TRUE); - writeJsonRequest(connection, messagesData); + getRestTestHelper().writeJsonRequest(connection, messagesData); assertEquals("Unexpected response code", 200, connection.getResponseCode()); // check messages on target queue - List> messages = getJsonAsList("/rest/message/test/" + queueName2); + List> messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName2); assertNotNull("Messages are not found", messages); assertEquals("Unexpected number of messages", movedMessageIds.size(), messages.size()); for (Long id : movedMessageIds) { - Map message = find("id", id.intValue(), messages); + Map message = getRestTestHelper().find("id", id.intValue(), messages); assertMessageAttributes(message); } // check messages on original queue - messages = getJsonAsList("/rest/message/test/" + queueName); + messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName); assertNotNull("Messages are not found", messages); assertEquals("Unexpected number of messages", ids.size(), messages.size()); for (Long id : ids) { - Map message = find("id", id.intValue(), messages); + Map message = getRestTestHelper().find("id", id.intValue(), messages); assertMessageAttributes(message); } for (Long id : movedMessageIds) { - Map message = find("id", id.intValue(), messages); + Map message = getRestTestHelper().find("id", id.intValue(), messages); assertNull("Moved message " + id + " is found on original queue", message); } } @@ -214,37 +214,37 @@ public class MessagesRestTest extends QpidRestTestCase } // copy messages - HttpURLConnection connection = openManagementConection("/rest/message/test/" + queueName, "POST"); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/message/test/" + queueName, "POST"); Map messagesData = new HashMap(); messagesData.put("messages", copyMessageIds); messagesData.put("destinationQueue", queueName2); - writeJsonRequest(connection, messagesData); + getRestTestHelper().writeJsonRequest(connection, messagesData); assertEquals("Unexpected response code", 200, connection.getResponseCode()); // check messages on target queue - List> messages = getJsonAsList("/rest/message/test/" + queueName2); + List> messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName2); assertNotNull("Messages are not found", messages); assertEquals("Unexpected number of messages", copyMessageIds.size(), messages.size()); for (Long id : copyMessageIds) { - Map message = find("id", id.intValue(), messages); + Map message = getRestTestHelper().find("id", id.intValue(), messages); assertMessageAttributes(message); } // check messages on original queue - messages = getJsonAsList("/rest/message/test/" + queueName); + messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName); assertNotNull("Messages are not found", messages); assertEquals("Unexpected number of messages", MESSAGE_NUMBER, messages.size()); for (Long id : ids) { - Map message = find("id", id.intValue(), messages); + Map message = getRestTestHelper().find("id", id.intValue(), messages); assertMessageAttributes(message); } for (Long id : copyMessageIds) { - Map message = find("id", id.intValue(), messages); + Map message = getRestTestHelper().find("id", id.intValue(), messages); assertMessageAttributes(message); } } @@ -272,30 +272,30 @@ public class MessagesRestTest extends QpidRestTestCase } // delete messages - HttpURLConnection connection = openManagementConection( + HttpURLConnection connection = getRestTestHelper().openManagementConnection( "/rest/message/test/" + queueName + "?" + queryString.toString(), "DELETE"); connection.connect(); assertEquals("Unexpected response code", 200, connection.getResponseCode()); // check messages on queue - List> messages = getJsonAsList("/rest/message/test/" + queueName); + List> messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName); assertNotNull("Messages are not found", messages); assertEquals("Unexpected number of messages", ids.size(), messages.size()); for (Long id : ids) { - Map message = find("id", id.intValue(), messages); + Map message = getRestTestHelper().find("id", id.intValue(), messages); assertMessageAttributes(message); } for (Long id : deleteMessageIds) { - Map message = find("id", id.intValue(), messages); + Map message = getRestTestHelper().find("id", id.intValue(), messages); assertNull("Message with id " + id + " was not deleted", message); } } private List getMesssageIds(String queueName) throws IOException, JsonParseException, JsonMappingException { - List> messages = getJsonAsList("/rest/message/test/" + queueName); + List> messages = getRestTestHelper().getJsonAsList("/rest/message/test/" + queueName); List ids = new ArrayList(); for (Map message : messages) { diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/PortRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/PortRestTest.java index 739ef5c737..ea897881a9 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/PortRestTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/PortRestTest.java @@ -30,14 +30,14 @@ public class PortRestTest extends QpidRestTestCase { public void testGet() throws Exception { - List> ports = getJsonAsList("/rest/port/"); + List> ports = getRestTestHelper().getJsonAsList("/rest/port/"); assertNotNull("Port data cannot be null", ports); assertEquals("Unexpected number of ports", 2, ports.size()); - int[] expectedPorts = { getPort(), getHttpPort() }; + int[] expectedPorts = { getPort(), getRestTestHelper().getHttpPort() }; for (int port : expectedPorts) { String portName = "0.0.0.0:" + port; - Map portData = find(Port.NAME, portName, ports); + Map portData = getRestTestHelper().find(Port.NAME, portName, ports); assertNotNull("Port " + portName + " is not found", portData); Asserts.assertPortAttributes(portData); } @@ -45,14 +45,14 @@ public class PortRestTest extends QpidRestTestCase public void testGetPort() throws Exception { - List> ports = getJsonAsList("/rest/port/"); + List> ports = getRestTestHelper().getJsonAsList("/rest/port/"); assertNotNull("Ports data cannot be null", ports); assertEquals("Unexpected number of ports", 2, ports.size()); for (Map portMap : ports) { String portName = (String) portMap.get(Port.NAME); assertNotNull("Port name attribute is not found", portName); - Map portData = getJsonAsSingletonList("/rest/port/" + URLDecoder.decode(portName, "UTF-8")); + Map portData = getRestTestHelper().getJsonAsSingletonList("/rest/port/" + URLDecoder.decode(portName, "UTF-8")); assertNotNull("Port " + portName + " is not found", portData); Asserts.assertPortAttributes(portData); } diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QpidRestTestCase.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QpidRestTestCase.java index e83341de80..958e68ab70 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QpidRestTestCase.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QpidRestTestCase.java @@ -20,226 +20,50 @@ */ package org.apache.qpid.server.management.plugin.servlet.rest; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.InetAddress; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; import org.apache.commons.configuration.ConfigurationException; -import org.apache.log4j.Logger; import org.apache.qpid.test.utils.QpidBrokerTestCase; -import org.codehaus.jackson.JsonGenerationException; -import org.codehaus.jackson.JsonParseException; -import org.codehaus.jackson.map.JsonMappingException; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.type.TypeReference; public class QpidRestTestCase extends QpidBrokerTestCase { - private static final Logger LOGGER = Logger.getLogger(QpidRestTestCase.class); - public static final String[] EXPECTED_HOSTS = { "development", "test", "localhost" }; public static final String[] EXPECTED_QUEUES = { "queue", "ping" }; public static final String[] EXPECTED_EXCHANGES = { "amq.fanout", "amq.match", "amq.direct", "amq.topic", "qpid.management", "<>" }; - private int _httpPort; - private String _hostName; - private List _httpConnections; + private RestTestHelper _restTestHelper = new RestTestHelper(findFreePort()); @Override public void setUp() throws Exception { - _httpConnections = new ArrayList(); - _hostName = InetAddress.getLocalHost().getHostName(); - _httpPort = findFreePort(); customizeConfiguration(); super.setUp(); - - } - - protected void customizeConfiguration() throws ConfigurationException, IOException - { - setConfigurationProperty("management.enabled", "false"); - setConfigurationProperty("management.http.enabled", "true"); - setConfigurationProperty("management.https.enabled", "false"); - setConfigurationProperty("management.http.port", Integer.toString(_httpPort)); - } - - public void teearDown() throws Exception - { - for (HttpURLConnection connection : _httpConnections) - { - try - { - connection.disconnect(); - } - catch (Exception e) - { - // ignore - } - } - super.tearDown(); - } - - protected int getHttpPort() - { - return _httpPort; - } - - protected String getHostName() - { - return _hostName; - } - - protected String getProtocol() - { - return "http"; - } - - protected String getManagementURL() - { - return getProtocol() + "://" + getHostName() + ":" + getHttpPort(); - } - - protected URL getManagementURL(String path) throws MalformedURLException - { - return new URL(getManagementURL() + path); - } - - protected HttpURLConnection openManagementConection(String path) throws IOException - { - URL url = getManagementURL(path); - HttpURLConnection httpCon = (HttpURLConnection) url.openConnection(); - httpCon.setDoOutput(true); - return httpCon; - } - - protected HttpURLConnection openManagementConection(String path, String method) throws IOException - { - HttpURLConnection httpCon = openManagementConection(path); - httpCon.setRequestMethod(method); - return httpCon; } - protected List> readJsonResponseAsList(HttpURLConnection connection) throws IOException, - JsonParseException, JsonMappingException - { - byte[] data = readConnectionInputStream(connection); - - ObjectMapper mapper = new ObjectMapper(); - - TypeReference>> typeReference = new TypeReference>>() - { - }; - List> providedObject = mapper.readValue(new ByteArrayInputStream(data), typeReference); - return providedObject; - } - - protected Map readJsonResponseAsMap(HttpURLConnection connection) throws IOException, - JsonParseException, JsonMappingException - { - byte[] data = readConnectionInputStream(connection); - - ObjectMapper mapper = new ObjectMapper(); - - TypeReference> typeReference = new TypeReference>() - { - }; - Map providedObject = mapper.readValue(new ByteArrayInputStream(data), typeReference); - return providedObject; - } - - protected byte[] readConnectionInputStream(HttpURLConnection connection) throws IOException - { - InputStream is = connection.getInputStream(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int len = -1; - while ((len = is.read(buffer)) != -1) - { - baos.write(buffer, 0, len); - } - if (LOGGER.isTraceEnabled()) - { - LOGGER.trace("RESPONSE:" + new String(baos.toByteArray())); - } - return baos.toByteArray(); - } - - protected void writeJsonRequest(HttpURLConnection connection, Map data) throws JsonGenerationException, - JsonMappingException, IOException - { - ObjectMapper mapper = new ObjectMapper(); - mapper.writeValue(connection.getOutputStream(), data); - } - - protected Map find(String name, Object value, List> data) + @Override + protected void tearDown() throws Exception { - for (Map map : data) + try { - Object mapValue = map.get(name); - if (value.equals(mapValue)) - { - return map; - } + super.tearDown(); } - return null; - } - - protected Map find(Map searchAttributes, List> data) - { - for (Map map : data) + finally { - boolean equals = true; - for (Map.Entry entry : searchAttributes.entrySet()) - { - Object mapValue = map.get(entry.getKey()); - if (!entry.getValue().equals(mapValue)) - { - equals = false; - break; - } - } - if (equals) - { - return map; - } + getRestTestHelper().tearDown(); } - return null; } - protected Map getJsonAsSingletonList(String path) throws IOException - { - List> response = getJsonAsList(path); - - assertNotNull("Response cannot be null", response); - assertEquals("Unexpected response", 1, response.size()); - return response.get(0); - } - - protected List> getJsonAsList(String path) throws IOException, JsonParseException, - JsonMappingException + protected void customizeConfiguration() throws ConfigurationException, IOException { - HttpURLConnection connection = openManagementConection(path, "GET"); - connection.connect(); - List> response = readJsonResponseAsList(connection); - return response; + setConfigurationProperty("management.enabled", "false"); + setConfigurationProperty("management.http.enabled", "true"); + setConfigurationProperty("management.https.enabled", "false"); + setConfigurationProperty("management.http.port", Integer.toString(_restTestHelper.getHttpPort())); } - protected Map getJsonAsMap(String path) throws IOException + public RestTestHelper getRestTestHelper() { - HttpURLConnection connection = openManagementConection(path, "GET"); - connection.connect(); - Map response = readJsonResponseAsMap(connection); - return response; + return _restTestHelper; } } diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QueueRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QueueRestTest.java index 5f11b3fb1d..d7732af688 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QueueRestTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/QueueRestTest.java @@ -80,7 +80,7 @@ public class QueueRestTest extends QpidRestTestCase public void testGetVirtualHostQueues() throws Exception { String queueName = getTestQueueName(); - List> queues = getJsonAsList("/rest/queue/test"); + List> queues = getRestTestHelper().getJsonAsList("/rest/queue/test"); assertEquals("Unexpected number of queues", EXPECTED_QUEUES.length + 1, queues.size()); String[] expectedQueues = new String[EXPECTED_QUEUES.length + 1]; System.arraycopy(EXPECTED_QUEUES, 0, expectedQueues, 0, EXPECTED_QUEUES.length); @@ -88,7 +88,7 @@ public class QueueRestTest extends QpidRestTestCase for (String name : expectedQueues) { - Map queueDetails = find(Queue.NAME, name, queues); + Map queueDetails = getRestTestHelper().find(Queue.NAME, name, queues); Asserts.assertQueue(name, "standard", queueDetails); @SuppressWarnings("unchecked") @@ -96,8 +96,8 @@ public class QueueRestTest extends QpidRestTestCase assertNotNull("Queue bindings are not found", bindings); assertEquals("Unexpected number of bindings", 2, bindings.size()); - Map defaultExchangeBinding = find(Binding.EXCHANGE, "<>", bindings); - Map directExchangeBinding = find(Binding.EXCHANGE, "amq.direct", bindings); + Map defaultExchangeBinding = getRestTestHelper().find(Binding.EXCHANGE, "<>", bindings); + Map directExchangeBinding = getRestTestHelper().find(Binding.EXCHANGE, "amq.direct", bindings); Asserts.assertBinding(name, "<>", defaultExchangeBinding); Asserts.assertBinding(name, "amq.direct", directExchangeBinding); } @@ -106,7 +106,7 @@ public class QueueRestTest extends QpidRestTestCase public void testGetByName() throws Exception { String queueName = getTestQueueName(); - Map queueDetails = getJsonAsSingletonList("/rest/queue/test/" + queueName); + Map queueDetails = getRestTestHelper().getJsonAsSingletonList("/rest/queue/test/" + queueName); Asserts.assertQueue(queueName, "standard", queueDetails); assertStatistics(queueDetails); @@ -115,8 +115,8 @@ public class QueueRestTest extends QpidRestTestCase assertNotNull("Queue bindings are not found", bindings); assertEquals("Unexpected number of bindings", 2, bindings.size()); - Map defaultExchangeBinding = find(Binding.EXCHANGE, "<>", bindings); - Map directExchangeBinding = find(Binding.EXCHANGE, "amq.direct", bindings); + Map defaultExchangeBinding = getRestTestHelper().find(Binding.EXCHANGE, "<>", bindings); + Map directExchangeBinding = getRestTestHelper().find(Binding.EXCHANGE, "amq.direct", bindings); Asserts.assertBinding(queueName, "<>", defaultExchangeBinding); Asserts.assertBinding(queueName, "amq.direct", directExchangeBinding); @@ -138,7 +138,7 @@ public class QueueRestTest extends QpidRestTestCase createBinding(bindingName, exchanges[i], queueName); } - Map queueDetails = getJsonAsSingletonList("/rest/queue/test/" + queueName); + Map queueDetails = getRestTestHelper().getJsonAsSingletonList("/rest/queue/test/" + queueName); Asserts.assertQueue(queueName, "standard", queueDetails); @SuppressWarnings("unchecked") @@ -152,14 +152,14 @@ public class QueueRestTest extends QpidRestTestCase for (int i = 0; i < exchanges.length; i++) { searchAttributes.put(Binding.EXCHANGE, exchanges[i]); - Map binding = find(searchAttributes, bindings); + Map binding = getRestTestHelper().find(searchAttributes, bindings); Asserts.assertBinding(bindingName, queueName, exchanges[i], binding); } } private void createBinding(String bindingName, String exchangeName, String queueName) throws IOException { - HttpURLConnection connection = openManagementConection( + HttpURLConnection connection = getRestTestHelper().openManagementConnection( "/rest/binding/test/" + URLDecoder.decode(exchangeName, "UTF-8") + "/" + queueName + "/" + bindingName, "PUT"); @@ -168,7 +168,7 @@ public class QueueRestTest extends QpidRestTestCase bindingData.put(Binding.EXCHANGE, exchangeName); bindingData.put(Binding.QUEUE, queueName); - writeJsonRequest(connection, bindingData); + getRestTestHelper().writeJsonRequest(connection, bindingData); assertEquals("Unexpected response code", 201, connection.getResponseCode()); connection.disconnect(); diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/RestTestHelper.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/RestTestHelper.java new file mode 100644 index 0000000000..8a323c0639 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/RestTestHelper.java @@ -0,0 +1,417 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.server.management.plugin.servlet.rest; + +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSocketFactory; +import javax.servlet.http.HttpServletResponse; + +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.test.utils.QpidBrokerTestCase; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.type.TypeReference; + +public class RestTestHelper +{ + private static final Logger LOGGER = Logger.getLogger(RestTestHelper.class); + + private int _httpPort; + + private boolean _useSsl; + + private String _username; + + private String _password; + + private File _passwdFile; + + public RestTestHelper(int httpPort) + { + _httpPort = httpPort; + } + + public int getHttpPort() + { + return _httpPort; + } + + private String getHostName() + { + return "localhost"; + } + + private String getProtocol() + { + return _useSsl ? "https" : "http"; + } + + public String getManagementURL() + { + return getProtocol() + "://" + getHostName() + ":" + getHttpPort(); + } + + public URL getManagementURL(String path) throws MalformedURLException + { + return new URL(getManagementURL() + path); + } + + public HttpURLConnection openManagementConnection(String path, String method) throws IOException + { + URL url = getManagementURL(path); + HttpURLConnection httpCon = (HttpURLConnection) url.openConnection(); + if(_useSsl) + { + ((HttpsURLConnection) httpCon).setSSLSocketFactory((SSLSocketFactory) SSLSocketFactory.getDefault()); + } + if(_username != null) + { + String encoded = new String(new Base64().encode((_username + ":" + _password).getBytes())); + httpCon.setRequestProperty("Authorization", "Basic " + encoded); + } + httpCon.setDoOutput(true); + httpCon.setRequestMethod(method); + return httpCon; + } + + public List> readJsonResponseAsList(HttpURLConnection connection) throws IOException, + JsonParseException, JsonMappingException + { + byte[] data = readConnectionInputStream(connection); + + ObjectMapper mapper = new ObjectMapper(); + + TypeReference>> typeReference = new TypeReference>>() + { + }; + List> providedObject = mapper.readValue(new ByteArrayInputStream(data), typeReference); + return providedObject; + } + + public Map readJsonResponseAsMap(HttpURLConnection connection) throws IOException, + JsonParseException, JsonMappingException + { + byte[] data = readConnectionInputStream(connection); + + ObjectMapper mapper = new ObjectMapper(); + + TypeReference> typeReference = new TypeReference>() + { + }; + Map providedObject = mapper.readValue(new ByteArrayInputStream(data), typeReference); + return providedObject; + } + + public byte[] readConnectionInputStream(HttpURLConnection connection) throws IOException + { + InputStream is = connection.getInputStream(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len = -1; + while ((len = is.read(buffer)) != -1) + { + baos.write(buffer, 0, len); + } + if (LOGGER.isTraceEnabled()) + { + LOGGER.trace("RESPONSE:" + new String(baos.toByteArray())); + } + return baos.toByteArray(); + } + + public void writeJsonRequest(HttpURLConnection connection, Map data) throws JsonGenerationException, + JsonMappingException, IOException + { + ObjectMapper mapper = new ObjectMapper(); + mapper.writeValue(connection.getOutputStream(), data); + } + + public Map find(String name, Object value, List> data) + { + for (Map map : data) + { + Object mapValue = map.get(name); + if (value.equals(mapValue)) + { + return map; + } + } + return null; + } + + public Map find(Map searchAttributes, List> data) + { + for (Map map : data) + { + boolean equals = true; + for (Map.Entry entry : searchAttributes.entrySet()) + { + Object mapValue = map.get(entry.getKey()); + if (!entry.getValue().equals(mapValue)) + { + equals = false; + break; + } + } + if (equals) + { + return map; + } + } + return null; + } + + public Map getJsonAsSingletonList(String path) throws IOException + { + List> response = getJsonAsList(path); + + Assert.assertNotNull("Response cannot be null", response); + Assert.assertEquals("Unexpected response", 1, response.size()); + return response.get(0); + } + + public List> getJsonAsList(String path) throws IOException, JsonParseException, + JsonMappingException + { + HttpURLConnection connection = openManagementConnection(path, "GET"); + connection.connect(); + List> response = readJsonResponseAsList(connection); + return response; + } + + public Map getJsonAsMap(String path) throws IOException + { + HttpURLConnection connection = openManagementConnection(path, "GET"); + connection.connect(); + Map response = readJsonResponseAsMap(connection); + return response; + } + + public void createNewGroupMember(String groupProviderName, String groupName, String memberName, int responseCode) throws IOException + { + HttpURLConnection connection = openManagementConnection( + "/rest/groupmember/" + URLDecoder.decode(groupProviderName, "UTF-8") + "/"+ URLDecoder.decode(groupName, "UTF-8") + "/" + URLDecoder.decode(memberName, "UTF-8"), + "PUT"); + + Map groupMemberData = new HashMap(); + // TODO add type + writeJsonRequest(connection, groupMemberData); + + Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); + + connection.disconnect(); + } + + public void createNewGroupMember(String groupProviderName, String groupName, String memberName) throws IOException + { + createNewGroupMember(groupProviderName, groupName, memberName, HttpServletResponse.SC_CREATED); + } + + public void removeMemberFromGroup(String groupProviderName, String groupName, String memberName, int responseCode) throws IOException + { + HttpURLConnection connection = openManagementConnection( + "/rest/groupmember/" + URLDecoder.decode(groupProviderName, "UTF-8") + "/"+ URLDecoder.decode(groupName, "UTF-8") + "/" + URLDecoder.decode(memberName, "UTF-8"), + "DELETE"); + + Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); + + connection.disconnect(); + } + + public void removeMemberFromGroup(String groupProviderName, String groupName, String memberName) throws IOException + { + removeMemberFromGroup(groupProviderName, groupName, memberName, HttpServletResponse.SC_OK); + } + + public void assertNumberOfGroupMembers(Map data, int expectedNumberOfGroupMembers) + { + List> groups = (List>) data.get("groupmembers"); + if (groups == null) + { + groups = Collections.emptyList(); + } + + Assert.assertEquals("Unexpected number of group members", expectedNumberOfGroupMembers, groups.size()); + } + + public void createGroup(String groupName, String groupProviderName) throws IOException + { + createGroup(groupName, groupProviderName, HttpServletResponse.SC_CREATED); + } + + public void createGroup(String groupName, String groupProviderName, int responseCode) throws IOException + { + HttpURLConnection connection = openManagementConnection( + "/rest/group/" + URLDecoder.decode(groupProviderName, "UTF-8") + "/"+ URLDecoder.decode(groupName, "UTF-8"), + "PUT"); + + Map groupData = new HashMap(); + writeJsonRequest(connection, groupData); + + Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); + + connection.disconnect(); + } + + public void createOrUpdateUser(String username, String password) throws IOException + { + createOrUpdateUser(username, password, HttpServletResponse.SC_CREATED); + } + + public void createOrUpdateUser(String username, String password, int responseCode) throws IOException + { + HttpURLConnection connection = openManagementConnection("/rest/user/PrincipalDatabaseAuthenticationManager/" + username, "PUT"); + + Map data = new HashMap(); + data.put("password", password); + writeJsonRequest(connection, data); + + Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); + + connection.disconnect(); + } + + public void removeGroup(String groupName, String groupProviderName, int responseCode) throws IOException + { + HttpURLConnection connection = openManagementConnection( + "/rest/group/" + URLDecoder.decode(groupProviderName, "UTF-8") + "/"+ URLDecoder.decode(groupName, "UTF-8"), + "DELETE"); + + Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); + connection.disconnect(); + } + + public void removeGroup(String groupName, String groupProviderName) throws IOException + { + removeGroup(groupName, groupProviderName, HttpServletResponse.SC_OK); + } + + public void removeUserById(String id) throws IOException + { + HttpURLConnection connection = openManagementConnection("/rest/user/PrincipalDatabaseAuthenticationManager?id=" + id, "DELETE"); + Assert.assertEquals("Unexpected response code", HttpServletResponse.SC_OK, connection.getResponseCode()); + connection.disconnect(); + } + + public void removeUser(String username, int responseCode) throws IOException + { + HttpURLConnection connection = openManagementConnection("/rest/user/PrincipalDatabaseAuthenticationManager/" + username, "DELETE"); + Assert.assertEquals("Unexpected response code", responseCode, connection.getResponseCode()); + connection.disconnect(); + } + + public void removeUser(String username) throws IOException + { + removeUser(username, HttpServletResponse.SC_OK); + } + + public void assertNumberOfGroups(Map data, int expectedNumberOfGroups) + { + List> groups = (List>) data.get("groups"); + if (groups == null) + { + groups = Collections.emptyList(); + } + Assert.assertEquals("Unexpected number of groups", expectedNumberOfGroups, groups.size()); + } + + public void setUseSsl(boolean useSsl) + { + _useSsl = useSsl; + } + + public void setUsernameAndPassword(String username, String password) + { + _username = username; + _password = password; + } + + /** + * Create password file that follows the convention username=password, which is deleted by {@link #tearDown()} + */ + public void configureTemporaryPasswordFile(QpidBrokerTestCase testCase, String... users) throws ConfigurationException, IOException + { + _passwdFile = createTemporaryPasswdFile(users); + + testCase.setConfigurationProperty("security.pd-auth-manager.principal-database.attributes.attribute.name", "passwordFile"); + testCase.setConfigurationProperty("security.pd-auth-manager.principal-database.attributes.attribute.value", _passwdFile.getAbsolutePath()); + } + + public void tearDown() + { + if (_passwdFile != null) + { + if (_passwdFile.exists()) + { + _passwdFile.delete(); + } + } + } + + private File createTemporaryPasswdFile(String[] users) throws IOException + { + BufferedWriter writer = null; + try + { + File testFile = File.createTempFile(this.getClass().getName(),"tmp"); + testFile.deleteOnExit(); + + writer = new BufferedWriter(new FileWriter(testFile)); + for (int i = 0; i < users.length; i++) + { + String username = users[i]; + writer.write(username + ":" + username); + writer.newLine(); + } + + return testFile; + + } + finally + { + if (writer != null) + { + writer.close(); + } + } + } +} diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslRestTest.java index b504c9fa60..d65b06e6a6 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslRestTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslRestTest.java @@ -27,7 +27,7 @@ public class SaslRestTest extends QpidRestTestCase { public void testGet() throws Exception { - Map saslData = getJsonAsMap("/rest/sasl"); + Map saslData = getRestTestHelper().getJsonAsMap("/rest/sasl"); assertNotNull("mechanisms attribute is not found", saslData.get("mechanisms")); @SuppressWarnings("unchecked") diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureRestTest.java index b01e1d44b8..c72dcfc8ab 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureRestTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/StructureRestTest.java @@ -28,7 +28,7 @@ public class StructureRestTest extends QpidRestTestCase public void testGet() throws Exception { - Map structure = getJsonAsMap("/rest/structure"); + Map structure = getRestTestHelper().getJsonAsMap("/rest/structure"); assertNotNull("Structure data cannot be null", structure); assertNode(structure, "Broker"); @@ -46,7 +46,7 @@ public class StructureRestTest extends QpidRestTestCase for (String hostName : EXPECTED_HOSTS) { - Map host = find("name", hostName, virtualhosts); + Map host = getRestTestHelper().find("name", hostName, virtualhosts); assertNotNull("Host " + hostName + " is not found ", host); assertNode(host, hostName); @@ -55,7 +55,7 @@ public class StructureRestTest extends QpidRestTestCase assertNotNull("Host " + hostName + " queues are not found ", queues); for (String queueName : EXPECTED_QUEUES) { - Map queue = find("name", queueName, queues); + Map queue = getRestTestHelper().find("name", queueName, queues); assertNotNull(hostName + " queue " + queueName + " is not found ", queue); assertNode(queue, queueName); @@ -73,7 +73,7 @@ public class StructureRestTest extends QpidRestTestCase assertNotNull("Host " + hostName + " exchanges are not found ", exchanges); for (String exchangeName : EXPECTED_EXCHANGES) { - Map exchange = find("name", exchangeName, exchanges); + Map exchange = getRestTestHelper().find("name", exchangeName, exchanges); assertNotNull("Exchange " + exchangeName + " is not found ", exchange); assertNode(exchange, exchangeName); if ("amq.direct".equalsIgnoreCase(exchangeName) || "<>".equalsIgnoreCase(exchangeName)) @@ -83,7 +83,7 @@ public class StructureRestTest extends QpidRestTestCase assertNotNull(hostName + " exchange " + exchangeName + " bindings are not found ", bindings); for (String queueName : EXPECTED_QUEUES) { - Map binding = find("name", queueName, bindings); + Map binding = getRestTestHelper().find("name", queueName, bindings); assertNotNull(hostName + " exchange " + exchangeName + " binding " + queueName + " is not found", binding); assertNode(binding, queueName); } @@ -97,11 +97,11 @@ public class StructureRestTest extends QpidRestTestCase assertNode(aliases.get(0), hostName); } - int[] expectedPorts = { getPort(), getHttpPort() }; + int[] expectedPorts = { getPort(), getRestTestHelper().getHttpPort() }; for (int port : expectedPorts) { String portName = "0.0.0.0:" + port; - Map portData = find("name", portName, ports); + Map portData = getRestTestHelper().find("name", portName, ports); assertNotNull("Port " + portName + " is not found ", portData); assertNode(portData, portName); } diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/UserRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/UserRestTest.java index e56fa27e21..037d26a7bd 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/UserRestTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/UserRestTest.java @@ -20,8 +20,6 @@ */ package org.apache.qpid.server.management.plugin.servlet.rest; -import java.net.HttpURLConnection; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,9 +27,17 @@ import org.apache.qpid.server.model.User; public class UserRestTest extends QpidRestTestCase { + @Override + public void setUp() throws Exception + { + getRestTestHelper().configureTemporaryPasswordFile(this, "user1", "user2"); + + super.setUp(); // do this last because it starts the broker, using the modified config + } + public void testGet() throws Exception { - List> users = getJsonAsList("/rest/user"); + List> users = getRestTestHelper().getJsonAsList("/rest/user"); assertNotNull("Users cannot be null", users); assertTrue("Unexpected number of users", users.size() > 1); for (Map user : users) @@ -42,7 +48,7 @@ public class UserRestTest extends QpidRestTestCase public void testGetUserByName() throws Exception { - List> users = getJsonAsList("/rest/user"); + List> users = getRestTestHelper().getJsonAsList("/rest/user"); assertNotNull("Users cannot be null", users); assertTrue("Unexpected number of users", users.size() > 1); for (Map user : users) @@ -50,7 +56,7 @@ public class UserRestTest extends QpidRestTestCase assertNotNull("Attribute " + User.ID, user.get(User.ID)); String userName = (String) user.get(User.NAME); assertNotNull("Attribute " + User.NAME, userName); - Map userDetails = getJsonAsSingletonList("/rest/user/PrincipalDatabaseAuthenticationManager/" + Map userDetails = getRestTestHelper().getJsonAsSingletonList("/rest/user/PrincipalDatabaseAuthenticationManager/" + userName); assertUser(userDetails); assertEquals("Unexpected user name", userName, userDetails.get(User.NAME)); @@ -60,19 +66,9 @@ public class UserRestTest extends QpidRestTestCase public void testPut() throws Exception { String userName = getTestName(); - HttpURLConnection connection = openManagementConection("/rest/user/PrincipalDatabaseAuthenticationManager/" - + userName, "PUT"); - - Map userData = new HashMap(); - userData.put(User.NAME, userName); - userData.put(User.PASSWORD, userName); + getRestTestHelper().createOrUpdateUser(userName, "newPassword"); - writeJsonRequest(connection, userData); - assertEquals("Unexpected response code", 201, connection.getResponseCode()); - - connection.disconnect(); - - Map userDetails = getJsonAsSingletonList("/rest/user/PrincipalDatabaseAuthenticationManager/" + Map userDetails = getRestTestHelper().getJsonAsSingletonList("/rest/user/PrincipalDatabaseAuthenticationManager/" + userName); assertUser(userDetails); assertEquals("Unexpected user name", userName, userDetails.get(User.NAME)); @@ -80,27 +76,16 @@ public class UserRestTest extends QpidRestTestCase public void testDelete() throws Exception { - // add user String userName = getTestName(); - HttpURLConnection connection = openManagementConection("/rest/user/PrincipalDatabaseAuthenticationManager/" - + userName, "PUT"); - - Map userData = new HashMap(); - userData.put(User.NAME, userName); - userData.put(User.PASSWORD, userName); + getRestTestHelper().createOrUpdateUser(userName, "newPassword"); - writeJsonRequest(connection, userData); - assertEquals("Unexpected response code", 201, connection.getResponseCode()); - connection.disconnect(); - - Map userDetails = getJsonAsSingletonList("/rest/user/PrincipalDatabaseAuthenticationManager/" + Map userDetails = getRestTestHelper().getJsonAsSingletonList("/rest/user/PrincipalDatabaseAuthenticationManager/" + userName); String id = (String) userDetails.get(User.ID); - connection = openManagementConection("/rest/user/PrincipalDatabaseAuthenticationManager?id=" + id, "DELETE"); - connection.connect(); - assertEquals("Unexpected response code", 200, connection.getResponseCode()); - List> users = getJsonAsList("/rest/user/PrincipalDatabaseAuthenticationManager/" + userName); + getRestTestHelper().removeUserById(id); + + List> users = getRestTestHelper().getJsonAsList("/rest/user/PrincipalDatabaseAuthenticationManager/" + userName); assertEquals("User should be deleted", 0, users.size()); } diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/VirtualHostRestTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/VirtualHostRestTest.java index 17f1aaaf7b..4bd936a948 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/VirtualHostRestTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/servlet/rest/VirtualHostRestTest.java @@ -46,12 +46,12 @@ public class VirtualHostRestTest extends QpidRestTestCase public void testGet() throws Exception { - List> hosts = getJsonAsList("/rest/virtualhost/"); + List> hosts = getRestTestHelper().getJsonAsList("/rest/virtualhost/"); assertNotNull("Hosts data cannot be null", hosts); assertEquals("Unexpected number of hosts", 3, hosts.size()); for (String hostName : EXPECTED_HOSTS) { - Map host = find("name", hostName, hosts); + Map host = getRestTestHelper().find("name", hostName, hosts); Asserts.assertVirtualHost(hostName, host); } } @@ -62,7 +62,7 @@ public class VirtualHostRestTest extends QpidRestTestCase _connection = (AMQConnection) getConnection(); _connection.createSession(true, Session.SESSION_TRANSACTED); - Map hostDetails = getJsonAsSingletonList("/rest/virtualhost/test"); + Map hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); Asserts.assertVirtualHost("test", hostDetails); @SuppressWarnings("unchecked") @@ -74,18 +74,18 @@ public class VirtualHostRestTest extends QpidRestTestCase @SuppressWarnings("unchecked") List> exchanges = (List>) hostDetails.get(VIRTUALHOST_EXCHANGES_ATTRIBUTE); assertEquals("Unexpected number of exchanges", 6, exchanges.size()); - Asserts.assertDurableExchange("amq.fanout", "fanout", find(Exchange.NAME, "amq.fanout", exchanges)); - Asserts.assertDurableExchange("qpid.management", "management", find(Exchange.NAME, "qpid.management", exchanges)); - Asserts.assertDurableExchange("amq.topic", "topic", find(Exchange.NAME, "amq.topic", exchanges)); - Asserts.assertDurableExchange("amq.direct", "direct", find(Exchange.NAME, "amq.direct", exchanges)); - Asserts.assertDurableExchange("amq.match", "headers", find(Exchange.NAME, "amq.match", exchanges)); - Asserts.assertDurableExchange("<>", "direct", find(Exchange.NAME, "<>", exchanges)); + Asserts.assertDurableExchange("amq.fanout", "fanout", getRestTestHelper().find(Exchange.NAME, "amq.fanout", exchanges)); + Asserts.assertDurableExchange("qpid.management", "management", getRestTestHelper().find(Exchange.NAME, "qpid.management", exchanges)); + Asserts.assertDurableExchange("amq.topic", "topic", getRestTestHelper().find(Exchange.NAME, "amq.topic", exchanges)); + Asserts.assertDurableExchange("amq.direct", "direct", getRestTestHelper().find(Exchange.NAME, "amq.direct", exchanges)); + Asserts.assertDurableExchange("amq.match", "headers", getRestTestHelper().find(Exchange.NAME, "amq.match", exchanges)); + Asserts.assertDurableExchange("<>", "direct", getRestTestHelper().find(Exchange.NAME, "<>", exchanges)); @SuppressWarnings("unchecked") List> queues = (List>) hostDetails.get(VIRTUALHOST_QUEUES_ATTRIBUTE); assertEquals("Unexpected number of queues", 2, queues.size()); - Map queue = find(Queue.NAME, "queue", queues); - Map ping = find(Queue.NAME, "ping", queues); + Map queue = getRestTestHelper().find(Queue.NAME, "queue", queues); + Map ping = getRestTestHelper().find(Queue.NAME, "ping", queues); Asserts.assertQueue("queue", "standard", queue); Asserts.assertQueue("ping", "standard", ping); assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.FALSE, queue.get(Queue.DURABLE)); @@ -116,14 +116,14 @@ public class VirtualHostRestTest extends QpidRestTestCase lvqQueueAttributes.put(Queue.LVQ_KEY, "LVQ"); createQueue(queueName + "-lvq", "lvq", lvqQueueAttributes); - Map hostDetails = getJsonAsSingletonList("/rest/virtualhost/test"); + Map hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); @SuppressWarnings("unchecked") List> queues = (List>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); - Map standardQueue = find(Queue.NAME, queueName + "-standard" , queues); - Map sortedQueue = find(Queue.NAME, queueName + "-sorted" , queues); - Map priorityQueue = find(Queue.NAME, queueName + "-priority" , queues); - Map lvqQueue = find(Queue.NAME, queueName + "-lvq" , queues); + Map standardQueue = getRestTestHelper().find(Queue.NAME, queueName + "-standard" , queues); + Map sortedQueue = getRestTestHelper().find(Queue.NAME, queueName + "-sorted" , queues); + Map priorityQueue = getRestTestHelper().find(Queue.NAME, queueName + "-priority" , queues); + Map lvqQueue = getRestTestHelper().find(Queue.NAME, queueName + "-lvq" , queues); Asserts.assertQueue(queueName + "-standard", "standard", standardQueue); Asserts.assertQueue(queueName + "-sorted", "sorted", sortedQueue); @@ -149,14 +149,14 @@ public class VirtualHostRestTest extends QpidRestTestCase createExchange(exchangeName + "-headers", "headers"); createExchange(exchangeName + "-fanout", "fanout"); - Map hostDetails = getJsonAsSingletonList("/rest/virtualhost/test"); + Map hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); @SuppressWarnings("unchecked") List> exchanges = (List>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_EXCHANGES_ATTRIBUTE); - Map directExchange = find(Queue.NAME, exchangeName + "-direct" , exchanges); - Map topicExchange = find(Queue.NAME, exchangeName + "-topic" , exchanges); - Map headersExchange = find(Queue.NAME, exchangeName + "-headers" , exchanges); - Map fanoutExchange = find(Queue.NAME, exchangeName + "-fanout" , exchanges); + Map directExchange = getRestTestHelper().find(Queue.NAME, exchangeName + "-direct" , exchanges); + Map topicExchange = getRestTestHelper().find(Queue.NAME, exchangeName + "-topic" , exchanges); + Map headersExchange = getRestTestHelper().find(Queue.NAME, exchangeName + "-headers" , exchanges); + Map fanoutExchange = getRestTestHelper().find(Queue.NAME, exchangeName + "-fanout" , exchanges); Asserts.assertDurableExchange(exchangeName + "-direct", "direct", directExchange); Asserts.assertDurableExchange(exchangeName + "-topic", "topic", topicExchange); @@ -175,11 +175,11 @@ public class VirtualHostRestTest extends QpidRestTestCase String queueName = getTestQueueName()+ "-lvq"; createQueue(queueName, "lvq", null); - Map hostDetails = getJsonAsSingletonList("/rest/virtualhost/test"); + Map hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); @SuppressWarnings("unchecked") List> queues = (List>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); - Map lvqQueue = find(Queue.NAME, queueName , queues); + Map lvqQueue = getRestTestHelper().find(Queue.NAME, queueName , queues); Asserts.assertQueue(queueName , "lvq", lvqQueue); assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, lvqQueue.get(Queue.DURABLE)); @@ -192,11 +192,11 @@ public class VirtualHostRestTest extends QpidRestTestCase int responseCode = tryCreateQueue(queueName, "sorted", null); assertEquals("Unexpected response code", 409, responseCode); - Map hostDetails = getJsonAsSingletonList("/rest/virtualhost/test"); + Map hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); @SuppressWarnings("unchecked") List> queues = (List>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); - Map testQueue = find(Queue.NAME, queueName , queues); + Map testQueue = getRestTestHelper().find(Queue.NAME, queueName , queues); assertNull("Sorted queue without a key was created ", testQueue); } @@ -206,11 +206,11 @@ public class VirtualHostRestTest extends QpidRestTestCase String queueName = getTestQueueName()+ "-priority"; createQueue(queueName, "priority", null); - Map hostDetails = getJsonAsSingletonList("/rest/virtualhost/test"); + Map hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); @SuppressWarnings("unchecked") List> queues = (List>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); - Map priorityQueue = find(Queue.NAME, queueName , queues); + Map priorityQueue = getRestTestHelper().find(Queue.NAME, queueName , queues); Asserts.assertQueue(queueName , "priority", priorityQueue); assertEquals("Unexpected value of queue attribute " + Queue.DURABLE, Boolean.TRUE, priorityQueue.get(Queue.DURABLE)); @@ -222,11 +222,11 @@ public class VirtualHostRestTest extends QpidRestTestCase String queueName = getTestQueueName(); createQueue(queueName, null, null); - Map hostDetails = getJsonAsSingletonList("/rest/virtualhost/test"); + Map hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); @SuppressWarnings("unchecked") List> queues = (List>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); - Map queue = find(Queue.NAME, queueName , queues); + Map queue = getRestTestHelper().find(Queue.NAME, queueName , queues); Asserts.assertQueue(queueName , "standard", queue); } @@ -237,11 +237,11 @@ public class VirtualHostRestTest extends QpidRestTestCase int responseCode = tryCreateQueue(queueName, "unsupported", null); assertEquals("Unexpected response code", 409, responseCode); - Map hostDetails = getJsonAsSingletonList("/rest/virtualhost/test"); + Map hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); @SuppressWarnings("unchecked") List> queues = (List>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); - Map queue = find(Queue.NAME, queueName , queues); + Map queue = getRestTestHelper().find(Queue.NAME, queueName , queues); assertNull("Queue of unsupported type was created", queue); } @@ -251,10 +251,10 @@ public class VirtualHostRestTest extends QpidRestTestCase String queueName = getTestQueueName(); createQueue(queueName, null, null); - HttpURLConnection connection = openManagementConection("/rest/queue/test/" + queueName, "DELETE"); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/queue/test/" + queueName, "DELETE"); connection.connect(); assertEquals("Unexpected response code", 200, connection.getResponseCode()); - List> queues = getJsonAsList("/rest/queue/test/" + queueName); + List> queues = getRestTestHelper().getJsonAsList("/rest/queue/test/" + queueName); assertEquals("Queue should be deleted", 0, queues.size()); } @@ -262,12 +262,12 @@ public class VirtualHostRestTest extends QpidRestTestCase { String queueName = getTestQueueName(); createQueue(queueName, null, null); - Map queueDetails = getJsonAsSingletonList("/rest/queue/test/" + queueName); + Map queueDetails = getRestTestHelper().getJsonAsSingletonList("/rest/queue/test/" + queueName); - HttpURLConnection connection = openManagementConection("/rest/queue/test?id=" + queueDetails.get(Queue.ID), "DELETE"); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/queue/test?id=" + queueDetails.get(Queue.ID), "DELETE"); connection.connect(); assertEquals("Unexpected response code", 200, connection.getResponseCode()); - List> queues = getJsonAsList("/rest/queue/test/" + queueName); + List> queues = getRestTestHelper().getJsonAsList("/rest/queue/test/" + queueName); assertEquals("Queue should be deleted", 0, queues.size()); } @@ -276,10 +276,10 @@ public class VirtualHostRestTest extends QpidRestTestCase String exchangeName = getTestName(); createExchange(exchangeName, "direct"); - HttpURLConnection connection = openManagementConection("/rest/exchange/test/" + exchangeName, "DELETE"); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/exchange/test/" + exchangeName, "DELETE"); connection.connect(); assertEquals("Unexpected response code", 200, connection.getResponseCode()); - List> queues = getJsonAsList("/rest/exchange/test/" + exchangeName); + List> queues = getRestTestHelper().getJsonAsList("/rest/exchange/test/" + exchangeName); assertEquals("Exchange should be deleted", 0, queues.size()); } @@ -287,12 +287,12 @@ public class VirtualHostRestTest extends QpidRestTestCase { String exchangeName = getTestName(); createExchange(exchangeName, "direct"); - Map echangeDetails = getJsonAsSingletonList("/rest/exchange/test/" + exchangeName); + Map echangeDetails = getRestTestHelper().getJsonAsSingletonList("/rest/exchange/test/" + exchangeName); - HttpURLConnection connection = openManagementConection("/rest/exchange/test?id=" + echangeDetails.get(Exchange.ID), "DELETE"); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/exchange/test?id=" + echangeDetails.get(Exchange.ID), "DELETE"); connection.connect(); assertEquals("Unexpected response code", 200, connection.getResponseCode()); - List> queues = getJsonAsList("/rest/exchange/test/" + exchangeName); + List> queues = getRestTestHelper().getJsonAsList("/rest/exchange/test/" + exchangeName); assertEquals("Exchange should be deleted", 0, queues.size()); } @@ -326,14 +326,14 @@ public class VirtualHostRestTest extends QpidRestTestCase lvqQueueAttributes.put(Queue.LVQ_KEY, "LVQ"); createQueue(queueName + "-lvq", "lvq", lvqQueueAttributes); - Map hostDetails = getJsonAsSingletonList("/rest/virtualhost/test"); + Map hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); @SuppressWarnings("unchecked") List> queues = (List>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); - Map standardQueue = find(Queue.NAME, queueName + "-standard" , queues); - Map sortedQueue = find(Queue.NAME, queueName + "-sorted" , queues); - Map priorityQueue = find(Queue.NAME, queueName + "-priority" , queues); - Map lvqQueue = find(Queue.NAME, queueName + "-lvq" , queues); + Map standardQueue = getRestTestHelper().find(Queue.NAME, queueName + "-standard" , queues); + Map sortedQueue = getRestTestHelper().find(Queue.NAME, queueName + "-sorted" , queues); + Map priorityQueue = getRestTestHelper().find(Queue.NAME, queueName + "-priority" , queues); + Map lvqQueue = getRestTestHelper().find(Queue.NAME, queueName + "-lvq" , queues); attributes.put(Queue.DURABLE, Boolean.TRUE); Asserts.assertQueue(queueName + "-standard", "standard", standardQueue, attributes); @@ -355,25 +355,25 @@ public class VirtualHostRestTest extends QpidRestTestCase attributes.put(AMQQueueFactory.X_QPID_DLQ_ENABLED, true); //verify the starting state - Map hostDetails = getJsonAsSingletonList("/rest/virtualhost/test"); + Map hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); List> queues = (List>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); List> exchanges = (List>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_EXCHANGES_ATTRIBUTE); - assertNull("queue should not have already been present", find(Queue.NAME, queueName , queues)); - assertNull("queue should not have already been present", find(Queue.NAME, queueName + "_DLQ" , queues)); - assertNull("exchange should not have already been present", find(Exchange.NAME, queueName + "_DLE" , exchanges)); + assertNull("queue should not have already been present", getRestTestHelper().find(Queue.NAME, queueName , queues)); + assertNull("queue should not have already been present", getRestTestHelper().find(Queue.NAME, queueName + "_DLQ" , queues)); + assertNull("exchange should not have already been present", getRestTestHelper().find(Exchange.NAME, queueName + "_DLE" , exchanges)); //create the queue createQueue(queueName, "standard", attributes); //verify the new queue, as well as the DLQueue and DLExchange have been created - hostDetails = getJsonAsSingletonList("/rest/virtualhost/test"); + hostDetails = getRestTestHelper().getJsonAsSingletonList("/rest/virtualhost/test"); queues = (List>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_QUEUES_ATTRIBUTE); exchanges = (List>) hostDetails.get(VirtualHostRestTest.VIRTUALHOST_EXCHANGES_ATTRIBUTE); - Map queue = find(Queue.NAME, queueName , queues); - Map dlqQueue = find(Queue.NAME, queueName + "_DLQ" , queues); - Map dlExchange = find(Exchange.NAME, queueName + "_DLE" , exchanges); + Map queue = getRestTestHelper().find(Queue.NAME, queueName , queues); + Map dlqQueue = getRestTestHelper().find(Queue.NAME, queueName + "_DLQ" , queues); + Map dlExchange = getRestTestHelper().find(Exchange.NAME, queueName + "_DLE" , exchanges); assertNotNull("queue should not have been present", queue); assertNotNull("queue should not have been present", dlqQueue); assertNotNull("exchange should not have been present", dlExchange); @@ -388,14 +388,14 @@ public class VirtualHostRestTest extends QpidRestTestCase private void createExchange(String exchangeName, String exchangeType) throws IOException { - HttpURLConnection connection = openManagementConection("/rest/exchange/test/" + exchangeName, "PUT"); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/exchange/test/" + exchangeName, "PUT"); Map queueData = new HashMap(); queueData.put(Exchange.NAME, exchangeName); queueData.put(Exchange.DURABLE, Boolean.TRUE); queueData.put(Exchange.TYPE, exchangeType); - writeJsonRequest(connection, queueData); + getRestTestHelper().writeJsonRequest(connection, queueData); assertEquals("Unexpected response code", 201, connection.getResponseCode()); connection.disconnect(); @@ -411,7 +411,7 @@ public class VirtualHostRestTest extends QpidRestTestCase private int tryCreateQueue(String queueName, String queueType, Map attributes) throws IOException, JsonGenerationException, JsonMappingException { - HttpURLConnection connection = openManagementConection("/rest/queue/test/" + queueName, "PUT"); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/queue/test/" + queueName, "PUT"); Map queueData = new HashMap(); queueData.put(Queue.NAME, queueName); @@ -425,7 +425,7 @@ public class VirtualHostRestTest extends QpidRestTestCase queueData.putAll(attributes); } - writeJsonRequest(connection, queueData); + getRestTestHelper().writeJsonRequest(connection, queueData); int responseCode = connection.getResponseCode(); connection.disconnect(); return responseCode; diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java new file mode 100644 index 0000000000..f9f9fbfef0 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/systest/rest/acl/GroupRestACLTest.java @@ -0,0 +1,187 @@ +/* + * + * 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.acl; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Map; +import java.util.Properties; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.qpid.server.management.plugin.servlet.rest.QpidRestTestCase; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; + +public class GroupRestACLTest extends QpidRestTestCase +{ + private static final String FILE_GROUP_MANAGER = "FileGroupManager"; + + private static final String ALLOWED_GROUP = "allowedGroup"; + private static final String DENIED_GROUP = "deniedGroup"; + private static final String OTHER_GROUP = "otherGroup"; + + private static final String ALLOWED_USER = "webadmin"; + private static final String DENIED_USER = "admin"; + private static final String OTHER_USER = "admin"; + + private File _groupFile; + + @Override + public void setUp() throws Exception + { + _groupFile = createTemporaryGroupFile(); + + setConfigurationProperty("security.file-group-manager.attributes.attribute.name", "groupFile"); + setConfigurationProperty("security.file-group-manager.attributes.attribute.value", _groupFile.getAbsolutePath()); + + //DONT call super.setUp(), the tests will start the broker after configuring it + } + + @Override + public void tearDown() throws Exception + { + super.tearDown(); + + if (_groupFile != null) + { + if (_groupFile.exists()) + { + _groupFile.delete(); + } + } + } + + private File createTemporaryGroupFile() throws Exception + { + File groupFile = File.createTempFile("group", "grp"); + groupFile.deleteOnExit(); + + Properties props = new Properties(); + props.put(ALLOWED_GROUP + ".users", ALLOWED_USER); + props.put(DENIED_GROUP + ".users", DENIED_USER); + props.put(OTHER_GROUP + ".users", OTHER_USER); + + props.store(new FileOutputStream(groupFile), "test group file"); + + return groupFile; + } + + public void testCreateGroup() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG " + ALLOWED_GROUP + " CREATE GROUP", + "ACL DENY-LOG " + DENIED_GROUP + " CREATE GROUP"); + + //Start the broker with the custom config + super.setUp(); + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + Map data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 3); + + getRestTestHelper().createGroup("newGroup", FILE_GROUP_MANAGER); + + data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 4); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + //TODO: the expected response code needs changed when we overhaul the brokers error handling + getRestTestHelper().createGroup("anotherNewGroup", FILE_GROUP_MANAGER, HttpServletResponse.SC_CONFLICT); + + data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 4); + } + + public void testDeleteGroup() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG " + ALLOWED_GROUP + " DELETE GROUP", + "ACL DENY-LOG " + DENIED_GROUP + " DELETE GROUP"); + + //Start the broker with the custom config + super.setUp(); + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + Map data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 3); + + //TODO: the expected response code needs changed when we overhaul the brokers error handling + getRestTestHelper().removeGroup(OTHER_GROUP, FILE_GROUP_MANAGER, HttpServletResponse.SC_CONFLICT); + + data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 3); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + + getRestTestHelper().removeGroup(OTHER_GROUP, FILE_GROUP_MANAGER); + + data = getRestTestHelper().getJsonAsSingletonList("/rest/groupprovider/" + FILE_GROUP_MANAGER); + getRestTestHelper().assertNumberOfGroups(data, 2); + } + + public void testUpdateGroupAddMember() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG " + ALLOWED_GROUP + " UPDATE GROUP", + "ACL DENY-LOG " + DENIED_GROUP + " UPDATE GROUP"); + + //Start the broker with the custom config + super.setUp(); + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + assertNumberOfGroupMembers(OTHER_GROUP, 1); + + getRestTestHelper().createNewGroupMember(FILE_GROUP_MANAGER, OTHER_GROUP, "newGroupMember", HttpServletResponse.SC_CONFLICT); + assertNumberOfGroupMembers(OTHER_GROUP, 1); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + getRestTestHelper().createNewGroupMember(FILE_GROUP_MANAGER, OTHER_GROUP, "newGroupMember"); + assertNumberOfGroupMembers(OTHER_GROUP, 2); + } + + public void testUpdateGroupDeleteMember() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG " + ALLOWED_GROUP + " UPDATE GROUP", + "ACL DENY-LOG " + DENIED_GROUP + " UPDATE GROUP"); + + //Start the broker with the custom config + super.setUp(); + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + assertNumberOfGroupMembers(OTHER_GROUP, 1); + + getRestTestHelper().removeMemberFromGroup(FILE_GROUP_MANAGER, OTHER_GROUP, OTHER_USER, HttpServletResponse.SC_CONFLICT); + assertNumberOfGroupMembers(OTHER_GROUP, 1); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + getRestTestHelper().removeMemberFromGroup(FILE_GROUP_MANAGER, OTHER_GROUP, OTHER_USER); + assertNumberOfGroupMembers(OTHER_GROUP, 0); + } + + private void assertNumberOfGroupMembers(String groupName, int expectedNumberOfMembers) throws IOException + { + Map group = getRestTestHelper().getJsonAsSingletonList("/rest/group/FileGroupManager/" + groupName); + getRestTestHelper().assertNumberOfGroupMembers(group, expectedNumberOfMembers); + } +} diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java new file mode 100644 index 0000000000..514d1bd781 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/systest/rest/acl/UserRestACLTest.java @@ -0,0 +1,188 @@ +/* + * 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.acl; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.servlet.http.HttpServletResponse; + +import org.apache.qpid.server.management.plugin.servlet.rest.QpidRestTestCase; +import org.apache.qpid.server.security.acl.AbstractACLTestCase; +import org.codehaus.jackson.JsonParseException; +import org.codehaus.jackson.map.JsonMappingException; + +public class UserRestACLTest extends QpidRestTestCase +{ + private static final String ALLOWED_GROUP = "allowedGroup"; + private static final String DENIED_GROUP = "deniedGroup"; + private static final String OTHER_GROUP = "otherGroup"; + + private static final String ALLOWED_USER = "webadmin"; + private static final String DENIED_USER = "admin"; + private static final String OTHER_USER = "other"; + + private File _groupFile; + + @Override + public void setUp() throws Exception + { + _groupFile = createTemporaryGroupFile(); + + setConfigurationProperty("security.file-group-manager.attributes.attribute.name", "groupFile"); + setConfigurationProperty("security.file-group-manager.attributes.attribute.value", _groupFile.getAbsolutePath()); + + getRestTestHelper().configureTemporaryPasswordFile(this, ALLOWED_USER, DENIED_USER, OTHER_USER); + + //DONT call super.setUp(), the tests will start the broker after configuring it + } + + @Override + public void tearDown() throws Exception + { + super.tearDown(); + + if (_groupFile != null) + { + if (_groupFile.exists()) + { + _groupFile.delete(); + } + } + } + + private File createTemporaryGroupFile() throws Exception + { + File groupFile = File.createTempFile("group", "grp"); + groupFile.deleteOnExit(); + + Properties props = new Properties(); + props.put(ALLOWED_GROUP + ".users", ALLOWED_USER); + props.put(DENIED_GROUP + ".users", DENIED_USER); + props.put(OTHER_GROUP + ".users", OTHER_USER); + + props.store(new FileOutputStream(groupFile), "test group file"); + + return groupFile; + } + + public void testAddUser() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG " + ALLOWED_GROUP + " CREATE USER", + "ACL DENY-LOG " + DENIED_GROUP + " CREATE USER"); + + //Start the broker with the custom config + super.setUp(); + + String newUser = "newUser"; + String password = "password"; + + assertUserDoesNotExist(newUser); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + + getRestTestHelper().createOrUpdateUser(newUser, password, HttpServletResponse.SC_CONFLICT); + assertUserDoesNotExist(newUser); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + getRestTestHelper().createOrUpdateUser(newUser, password); + assertUserExists(newUser); + } + + public void testDeleteUser() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG " + ALLOWED_GROUP + " DELETE USER", + "ACL DENY-LOG " + DENIED_GROUP + " DELETE USER"); + + //Start the broker with the custom config + super.setUp(); + + assertUserExists(OTHER_USER); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + getRestTestHelper().removeUser(OTHER_USER, HttpServletResponse.SC_CONFLICT); + assertUserExists(OTHER_USER); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + getRestTestHelper().removeUser(OTHER_USER); + assertUserDoesNotExist(OTHER_USER); + } + + public void testUpdateUser() throws Exception + { + AbstractACLTestCase.writeACLFileUtil(this, null, + "ACL ALLOW-LOG " + ALLOWED_GROUP + " UPDATE USER", + "ACL DENY-LOG " + DENIED_GROUP + " UPDATE USER"); + + //Start the broker with the custom config + super.setUp(); + + String newPassword = "newPassword"; + + checkPassword(OTHER_USER, OTHER_USER, true); + + getRestTestHelper().setUsernameAndPassword(DENIED_USER, DENIED_USER); + getRestTestHelper().createOrUpdateUser(OTHER_USER, newPassword, HttpServletResponse.SC_CONFLICT); + + checkPassword(OTHER_USER, newPassword, false); + + getRestTestHelper().setUsernameAndPassword(ALLOWED_USER, ALLOWED_USER); + getRestTestHelper().createOrUpdateUser(OTHER_USER, newPassword, HttpServletResponse.SC_OK); // expect SC_OK rather than the default SC_CREATED + + checkPassword(OTHER_USER, newPassword, true); + checkPassword(OTHER_USER, OTHER_USER, false); + } + + private void checkPassword(String username, String password, boolean passwordExpectedToBeCorrect) throws IOException + { + getRestTestHelper().setUsernameAndPassword(username, password); + HttpURLConnection connection = getRestTestHelper().openManagementConnection("/rest/user/PrincipalDatabaseAuthenticationManager/", "GET"); + + boolean passwordIsCorrect = connection.getResponseCode() == HttpServletResponse.SC_OK; + + connection.disconnect(); + + assertEquals(passwordExpectedToBeCorrect, passwordIsCorrect); + } + + private void assertUserDoesNotExist(String newUser) throws JsonParseException, JsonMappingException, IOException + { + String path = "/rest/user/PrincipalDatabaseAuthenticationManager/" + newUser; + List> userDetailsList = getRestTestHelper().getJsonAsList(path); + assertTrue(userDetailsList.isEmpty()); + } + + private void assertUserExists(String username) throws IOException + { + String path = "/rest/user/PrincipalDatabaseAuthenticationManager/" + username; + Map userDetails = getRestTestHelper().getJsonAsSingletonList(path); + + assertEquals( + "User returned by " + path + " should have name=" + username + ". The returned JSON was: " + userDetails, + username, + userDetails.get("name")); + } +} 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 0648235077..e68c0d650b 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 @@ -29,9 +29,8 @@ import org.apache.qpid.server.logging.messages.ManagementConsoleMessages; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; import org.apache.qpid.server.security.auth.rmi.RMIPasswordAuthenticator; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; - import javax.management.JMException; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; @@ -73,7 +72,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** - * This class starts up an MBeanserver. If out of the box agent has been enabled then there are no + * This class starts up an MBeanserver. If out of the box agent has been enabled then there are no * security features implemented like user authentication and authorisation. */ public class JMXManagedObjectRegistry implements ManagedObjectRegistry @@ -244,8 +243,8 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry protected RMIConnection makeClient(String connectionId, Subject subject) throws IOException { final RMIConnection makeClient = super.makeClient(connectionId, subject); - final UsernamePrincipal usernamePrincipalFromSubject = UsernamePrincipal.getUsernamePrincipalFromSubject(subject); - connectionIdUsernameMap.put(connectionId, usernamePrincipalFromSubject.getName()); + final AuthenticatedPrincipal authenticatedPrincipalFromSubject = AuthenticatedPrincipal.getAuthenticatedPrincipalFromSubject(subject); + connectionIdUsernameMap.put(connectionId, authenticatedPrincipalFromSubject.getName()); return makeClient; } }; @@ -451,7 +450,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry _log.error("Exception while closing the JMX ConnectorServer: ", e); } } - + if (_rmiRegistry != null) { // Stopping the RMI registry @@ -469,7 +468,7 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry _log.error("Exception while closing the RMI Registry: ", e); } } - + //ObjectName query to gather all Qpid related MBeans ObjectName mbeanNameQuery = null; try diff --git a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java index a2a0d2d093..694b0b2913 100644 --- a/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java +++ b/qpid/java/broker-plugins/management-jmx/src/main/java/org/apache/qpid/server/jmx/MBeanInvocationHandlerImpl.java @@ -30,6 +30,7 @@ import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; import javax.management.Attribute; import javax.management.JMException; @@ -41,7 +42,6 @@ import javax.management.NotificationListener; import javax.management.ObjectName; import javax.management.RuntimeErrorException; import javax.management.remote.JMXConnectionNotification; -import javax.management.remote.JMXPrincipal; import javax.management.remote.MBeanServerForwarder; import javax.security.auth.Subject; import java.lang.reflect.InvocationHandler; @@ -52,7 +52,6 @@ import java.security.AccessControlContext; import java.security.AccessController; import java.util.Arrays; import java.util.Map; -import java.util.Set; /** * This class can be used by the JMXConnectorServer as an InvocationHandler for the mbean operations. It delegates @@ -101,7 +100,7 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati { ObjectName mbean = (ObjectName) args[0]; - if(!DefaultManagedObject.DOMAIN.equalsIgnoreCase(mbean.getDomain())) + if(!ManagedObject.DOMAIN.equalsIgnoreCase(mbean.getDomain())) { return true; } @@ -151,11 +150,13 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati return method.invoke(_mbs, args); } - // Retrieve JMXPrincipal from Subject - Set principals = subject.getPrincipals(JMXPrincipal.class); - if (principals == null || principals.isEmpty()) + try + { + AuthenticatedPrincipal.getAuthenticatedPrincipalFromSubject(subject); + } + catch(Exception e) { - throw new SecurityException("Access denied: no JMX principal"); + throw new SecurityException("Access denied: no authenticated principal", e); } // Save the subject @@ -381,6 +382,7 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati String user = null; if (handback instanceof Map) { + @SuppressWarnings("unchecked") final Map connectionIdUsernameMap = (Map) handback; user = connectionIdUsernameMap.get(connectionId); } diff --git a/qpid/java/broker/etc/broker_example.acl b/qpid/java/broker/etc/broker_example.acl index 45a48bda09..a5e01fb895 100644 --- a/qpid/java/broker/etc/broker_example.acl +++ b/qpid/java/broker/etc/broker_example.acl @@ -19,14 +19,7 @@ ### EXAMPLE ACL V2 FILE ### NOTE: Rules are considered from top to bottom, and the first matching rule governs the decision. - -### DEFINE GROUPS ### - -#Define a 'messaging-users' group with users 'client' and 'server' in it -GROUP messaging-users client server - -#Define a group for management web console users -GROUP webadmins webadmin +### Rules may refer to users or groups. Groups are currently defined in the etc/groups file. ### JMX MANAGEMENT #### @@ -34,9 +27,9 @@ GROUP webadmins webadmin # This is used for items such as querying the management API and broker release versions. ACL ALLOW ALL ACCESS METHOD component="ServerInformation" -# Allow 'admin' all management operations. To reduce log file noise, only non-read-only operations are logged. -ACL ALLOW admin ACCESS METHOD -ACL ALLOW-LOG admin ALL METHOD +# Allow 'administrators' all management operations. To reduce log file noise, only non-read-only operations are logged. +ACL ALLOW administrators ACCESS METHOD +ACL ALLOW-LOG administrators ALL METHOD # Allow 'guest' to view logger levels, and use getter methods on LoggingManagement ACL ALLOW guest ACCESS METHOD component="LoggingManagement" name="viewEffectiveRuntimeLoggerLevels" @@ -87,6 +80,13 @@ ACL ALLOW-LOG webadmins CREATE EXCHANGE ACL ALLOW-LOG webadmins DELETE EXCHANGE ACL ALLOW-LOG webadmins BIND EXCHANGE ACL ALLOW-LOG webadmins UNBIND EXCHANGE +ACL ALLOW-LOG webadmins CREATE GROUP +ACL ALLOW-LOG webadmins DELETE GROUP +ACL ALLOW-LOG webadmins UPDATE GROUP +ACL ALLOW-LOG webadmins CREATE USER +ACL ALLOW-LOG webadmins DELETE USER +ACL ALLOW-LOG webadmins UPDATE USER + ACL ALLOW-LOG webadmins UPDATE METHOD # at the moment only the following UPDATE METHOD rules are supported by web management console @@ -96,5 +96,5 @@ ACL ALLOW-LOG webadmins UPDATE METHOD ### DEFAULT ### -#Deny all users from performing all operations +# Deny all users from performing all operations ACL DENY-LOG all all diff --git a/qpid/java/broker/etc/config.xml b/qpid/java/broker/etc/config.xml index 08c7c23d13..7bd4ceb128 100644 --- a/qpid/java/broker/etc/config.xml +++ b/qpid/java/broker/etc/config.xml @@ -79,13 +79,24 @@ - + + + - + false diff --git a/qpid/java/broker/etc/groups b/qpid/java/broker/etc/groups new file mode 100644 index 0000000000..e3912ece99 --- /dev/null +++ b/qpid/java/broker/etc/groups @@ -0,0 +1,29 @@ +# +# 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. +# + +# +# To define a group, use the format: +# +# .users=,,..., +# + +messaging-users.users=guest,client,server +administrators.users=admin +webadmins.users=webadmin + diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java index b8c8411c5d..e65fe10eb5 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java @@ -32,9 +32,8 @@ import org.apache.qpid.framing.MethodRegistry; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.security.auth.AuthenticationResult; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; import org.apache.qpid.server.state.AMQState; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.state.StateAwareMethodListener; @@ -61,7 +60,7 @@ public class ConnectionSecureOkMethodHandler implements StateAwareMethodListener { AMQProtocolSession session = stateManager.getProtocolSession(); - AuthenticationManager authMgr = stateManager.getAuthenticationManager(); + SubjectCreator subjectCreator = stateManager.getSubjectCreator(); SaslServer ss = session.getSaslServer(); if (ss == null) @@ -69,7 +68,7 @@ public class ConnectionSecureOkMethodHandler implements StateAwareMethodListener throw new AMQException("No SASL context set up in session"); } MethodRegistry methodRegistry = session.getMethodRegistry(); - AuthenticationResult authResult = authMgr.authenticate(ss, body.getResponse()); + SubjectAuthenticationResult authResult = subjectCreator.authenticate(ss, body.getResponse()); switch (authResult.getStatus()) { case ERROR: diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java index a522b9f60f..9f596a4637 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java @@ -32,9 +32,8 @@ import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.security.auth.AuthenticationResult; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; import org.apache.qpid.server.state.AMQState; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.state.StateAwareMethodListener; @@ -65,11 +64,11 @@ public class ConnectionStartOkMethodHandler implements StateAwareMethodListener< _logger.info("SASL Mechanism selected: " + body.getMechanism()); _logger.info("Locale selected: " + body.getLocale()); - AuthenticationManager authMgr = stateManager.getAuthenticationManager(); + SubjectCreator subjectCreator = stateManager.getSubjectCreator(); SaslServer ss = null; try { - ss = authMgr.createSaslServer(String.valueOf(body.getMechanism()), session.getLocalFQDN(), session.getPeerPrincipal()); + ss = subjectCreator.createSaslServer(String.valueOf(body.getMechanism()), session.getLocalFQDN(), session.getPeerPrincipal()); if (ss == null) { @@ -78,7 +77,7 @@ public class ConnectionStartOkMethodHandler implements StateAwareMethodListener< session.setSaslServer(ss); - final AuthenticationResult authResult = authMgr.authenticate(ss, body.getResponse()); + final SubjectAuthenticationResult authResult = subjectCreator.authenticate(ss, body.getResponse()); //save clientProperties session.setClientProperties(body.getClientProperties()); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/AbstractManagementActor.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/AbstractManagementActor.java new file mode 100644 index 0000000000..f67c7a1c6a --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/AbstractManagementActor.java @@ -0,0 +1,48 @@ +package org.apache.qpid.server.logging.actors; + +import java.security.AccessController; + +import javax.security.auth.Subject; + +import org.apache.qpid.server.logging.RootMessageLogger; +import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; + +public abstract class AbstractManagementActor extends AbstractActor +{ + /** + * Holds the principal name to display when principal subject is not available. + *

+ * This is useful for cases when users invoke JMX operation over JConsole + * attached to the local JVM. + */ + protected static final String UNKNOWN_PRINCIPAL = "N/A"; + + /** used when the principal name cannot be discovered from the Subject */ + private final String _fallbackPrincipalName; + + public AbstractManagementActor(RootMessageLogger rootLogger, String fallbackPrincipalName) + { + super(rootLogger); + _fallbackPrincipalName = fallbackPrincipalName; + } + + /** + * Returns current {@link AuthenticatedPrincipal} name or {@link #_fallbackPrincipalName} + * if it can't be found. + */ + protected String getPrincipalName() + { + String identity = _fallbackPrincipalName; + + final Subject subject = Subject.getSubject(AccessController.getContext()); + if (subject != null) + { + AuthenticatedPrincipal authenticatedPrincipal = AuthenticatedPrincipal.getOptionalAuthenticatedPrincipalFromSubject(subject); + if(authenticatedPrincipal != null) + { + identity = authenticatedPrincipal.getName(); + } + } + return identity; + } +} \ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/HttpManagementActor.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/HttpManagementActor.java new file mode 100644 index 0000000000..9b445c2bd9 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/HttpManagementActor.java @@ -0,0 +1,62 @@ +/* + * + * 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.logging.actors; + +import java.text.MessageFormat; + +import org.apache.qpid.server.logging.RootMessageLogger; +import org.apache.qpid.server.logging.subjects.LogSubjectFormat; + +/** + * HttpManagement actor to use in {@link AbstractServlet} to log all http management operational logging. + * + * An instance is required per http Session. + */ +public class HttpManagementActor extends AbstractManagementActor +{ + private String _cachedLogString; + private String _lastPrincipalName; + private String _address; + + public HttpManagementActor(RootMessageLogger rootLogger, String ip, int port) + { + super(rootLogger, UNKNOWN_PRINCIPAL); + _address = ip + ":" + port; + } + + private synchronized String getAndCacheLogString() + { + String principalName = getPrincipalName(); + + if(!principalName.equals(_lastPrincipalName)) + { + _lastPrincipalName = principalName; + _cachedLogString = "[" + MessageFormat.format(LogSubjectFormat.MANAGEMENT_FORMAT, principalName, _address) + "] "; + } + + return _cachedLogString; + } + + public String getLogMessage() + { + return getAndCacheLogString(); + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/ManagementActor.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/ManagementActor.java index a2f3506502..ba5ea47fc1 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/ManagementActor.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/ManagementActor.java @@ -21,58 +21,31 @@ package org.apache.qpid.server.logging.actors; import org.apache.qpid.server.logging.RootMessageLogger; +import org.apache.qpid.server.logging.subjects.LogSubjectFormat; -import javax.management.remote.JMXPrincipal; -import javax.security.auth.Subject; -import java.security.AccessController; -import java.security.Principal; import java.text.MessageFormat; -import java.util.Set; /** * Management actor to use in {@link MBeanInvocationHandlerImpl} to log all management operational logging. */ -public class ManagementActor extends AbstractActor +public class ManagementActor extends AbstractManagementActor { - /** - * Holds the principal name to display when principal subject is not available. - *

- * This is useful for cases when users invoke JMX operation over JConsole - * attached to the local JVM. - */ - private static final String UNKNOWN_PRINCIPAL = "N/A"; - private String _lastThreadName = null; - /** - * LOG FORMAT for the ManagementActor, - * Uses a MessageFormat call to insert the required values according to - * these indices: - * - * 0 - User ID - * 1 - IP - */ - public static final String MANAGEMENT_FORMAT = "mng:{0}({1})"; - /** * The logString to be used for logging */ private String _logStringContainingPrincipal; - /** used when the principal name cannot be discovered from the Subject */ - private final String _fallbackPrincipalName; - /** @param rootLogger The RootLogger to use for this Actor */ public ManagementActor(RootMessageLogger rootLogger) { - super(rootLogger); - _fallbackPrincipalName = UNKNOWN_PRINCIPAL; + super(rootLogger, UNKNOWN_PRINCIPAL); } public ManagementActor(RootMessageLogger rootLogger, String principalName) { - super(rootLogger); - _fallbackPrincipalName = principalName; + super(rootLogger, principalName); } private synchronized String getAndCacheLogString() @@ -96,7 +69,7 @@ public class ManagementActor extends AbstractActor if (split.length == 2) { String ip = currentName.split("-")[1]; - actor = MessageFormat.format(MANAGEMENT_FORMAT, principalName, ip); + actor = MessageFormat.format(LogSubjectFormat.MANAGEMENT_FORMAT, principalName, ip); } else { @@ -119,33 +92,8 @@ public class ManagementActor extends AbstractActor return logString; } - /** - * Returns current JMX principal name. - * - * @return principal name or null if principal can not be found - */ - private String getPrincipalName() - { - String identity = _fallbackPrincipalName; - - // retrieve Subject from current AccessControlContext - final Subject subject = Subject.getSubject(AccessController.getContext()); - if (subject != null) - { - // retrieve JMXPrincipal from Subject - final Set principals = subject.getPrincipals(JMXPrincipal.class); - if (principals != null && !principals.isEmpty()) - { - final Principal principal = principals.iterator().next(); - identity = principal.getName(); - } - } - return identity; - } - public String getLogMessage() { return getAndCacheLogString(); } - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/LogSubjectFormat.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/LogSubjectFormat.java index 28c4f0d52a..7611ee1a88 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/LogSubjectFormat.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/LogSubjectFormat.java @@ -32,10 +32,18 @@ package org.apache.qpid.server.logging.subjects; public class LogSubjectFormat { + private LogSubjectFormat() { } + /** + * LOG FORMAT for the ManagementActors, + * 0 - User ID + * 1 - IP[:Port] + */ + public static final String MANAGEMENT_FORMAT = "mng:{0}({1})"; + /** * LOG FORMAT for the Subscription Log Subject * 0 - Subscription ID diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Group.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Group.java new file mode 100644 index 0000000000..aacd515107 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Group.java @@ -0,0 +1,52 @@ +/* + * 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; + +public interface Group extends ConfiguredObject +{ + String CREATED = "created"; + String DURABLE = "durable"; + String ID = "id"; + String LIFETIME_POLICY = "lifetimePolicy"; + String NAME = "name"; + String STATE = "state"; + String TIME_TO_LIVE = "timeToLive"; + String UPDATED = "updated"; + + // Attributes + public static final Collection AVAILABLE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList( + ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED + )); + + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/GroupMember.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/GroupMember.java new file mode 100644 index 0000000000..6832cc6fa6 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/GroupMember.java @@ -0,0 +1,52 @@ +/* + * 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; + +public interface GroupMember extends ConfiguredObject +{ + String CREATED = "created"; + String DURABLE = "durable"; + String ID = "id"; + String LIFETIME_POLICY = "lifetimePolicy"; + String NAME = "name"; + String STATE = "state"; + String TIME_TO_LIVE = "timeToLive"; + String UPDATED = "updated"; + + // Attributes + public static final Collection AVAILABLE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList( + ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED + )); + + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/GroupProvider.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/GroupProvider.java new file mode 100644 index 0000000000..76fa379c1b --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/GroupProvider.java @@ -0,0 +1,51 @@ +/* + * 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; + +public interface GroupProvider 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 AVAILABLE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList(ID, + NAME, + DESCRIPTION, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + TYPE)); +} 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 36179fc105..329574f017 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 @@ -47,6 +47,7 @@ public class Model addRelationship(Broker.class, VirtualHost.class); addRelationship(Broker.class, Port.class); addRelationship(Broker.class, AuthenticationProvider.class); + addRelationship(Broker.class, GroupProvider.class); addRelationship(VirtualHost.class, Exchange.class); addRelationship(VirtualHost.class, Queue.class); @@ -54,6 +55,10 @@ public class Model addRelationship(VirtualHost.class, VirtualHostAlias.class); addRelationship(AuthenticationProvider.class, User.class); + addRelationship(User.class, GroupMember.class); + + addRelationship(GroupProvider.class, Group.class); + addRelationship(Group.class, GroupMember.class); addRelationship(Connection.class, Session.class); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java index 36b6a454dc..6606944dc5 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java @@ -63,6 +63,11 @@ public class UUIDGenerator return createUUID(User.class.getName(), authenticationProviderName, userName); } + public static UUID generateGroupUUID(String groupProviderName, String groupName) + { + return createUUID(Group.class.getName(), groupProviderName, groupName); + } + public static UUID generateVhostUUID(String virtualHostName) { return createUUID(VirtualHost.class.getName(), virtualHostName); @@ -77,4 +82,9 @@ public class UUIDGenerator { return createUUID(Consumer.class.getName(), virtualHostName, queueName, connectionRemoteAddress, channelNumber, consumerName); } + + public static UUID generateGroupMemberUUID(String groupProviderName, String groupName, String groupMemberName) + { + return createUUID(GroupMember.class.getName(), groupProviderName, groupName, groupMemberName); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/User.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/User.java index d97bf46d31..675dc8f0d3 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/User.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/User.java @@ -52,8 +52,6 @@ public interface User extends ConfiguredObject PASSWORD )); - public String getPassword(); - public void setPassword(String password); } 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 8c2bc98ba7..adad2a355c 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 @@ -38,7 +38,7 @@ import org.apache.qpid.server.security.access.Operation; import org.apache.qpid.server.security.auth.database.PrincipalDatabase; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.UsernamePrincipal; public abstract class AuthenticationProviderAdapter extends AbstractAdapter implements AuthenticationProvider { @@ -147,7 +147,7 @@ public abstract class AuthenticationProviderAdapter attributes, ConfiguredObject... otherParents) { - return null; + throw new IllegalArgumentException("This authentication provider does not support" + + " creating children of type: " + childClass); } private static class SimpleAuthenticationProviderAdapter extends AuthenticationProviderAdapter @@ -220,15 +221,20 @@ public abstract class AuthenticationProviderAdapter attributes) { - return getPrincipalDatabase().createPrincipal(new UsernamePrincipal(username), password.toCharArray()); + if(getSecurityManager().authoriseUserOperation(Operation.CREATE, username)) + { + return getPrincipalDatabase().createPrincipal(new UsernamePrincipal(username), password.toCharArray()); + } + else + { + throw new AccessControlException("Do not have permission to create new user"); + } } @Override public void deleteUser(String username) throws AccountNotFoundException { - if(getSecurityManager().authoriseMethod(Operation.DELETE, - "UserManagement", - "deleteUser")) + if(getSecurityManager().authoriseUserOperation(Operation.DELETE, username)) { getPrincipalDatabase().deletePrincipal(new UsernamePrincipal(username)); @@ -252,18 +258,13 @@ public abstract class AuthenticationProviderAdapter C createChild(Class childClass, Map attributes, @@ -286,19 +292,19 @@ public abstract class AuthenticationProviderAdapter _authManagerAdapters = new HashMap(); + private final Map _groupManagerAdapters = + new HashMap(); public BrokerAdapter(final IApplicationRegistry instance) @@ -75,8 +80,10 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, VirtualHos populateVhosts(); instance.addPortBindingListener(this); populatePorts(); - instance.addRegistryChangeListener(this); + instance.addAuthenticationManagerRegistryChangeListener(this); populateAuthenticationManagers(); + instance.addGroupManagerChangeListener(this); + populateGroupManagers(); } private void populateVhosts() @@ -171,6 +178,25 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, VirtualHos } } + private void populateGroupManagers() + { + synchronized (_groupManagerAdapters) + { + List groupManagers = _applicationRegistry.getGroupManagers(); + if(groupManagers != null) + { + for (GroupManager groupManager : groupManagers) + { + if(!_groupManagerAdapters.containsKey(groupManager)) + { + _groupManagerAdapters.put(groupManager, + GroupProviderAdapter.createGroupProviderAdapter(this, groupManager)); + } + } + } + } + } + public Collection getAuthenticationProviders() { synchronized (_authManagerAdapters) @@ -179,7 +205,16 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, VirtualHos new ArrayList(_authManagerAdapters.values()); return authManagers; } + } + public Collection getGroupProviders() + { + synchronized (_groupManagerAdapters) + { + final ArrayList groupManagers = + new ArrayList(_groupManagerAdapters.values()); + return groupManagers; + } } public VirtualHost createVirtualHost(final String name, @@ -277,6 +312,10 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, VirtualHos { return (Collection) getAuthenticationProviders(); } + else if(clazz == GroupProvider.class) + { + return (Collection) getGroupProviders(); + } return Collections.emptySet(); } @@ -491,4 +530,36 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, VirtualHos { return super.setAttribute(name, expected, desired); //TODO - Implement. } + + @Override + public void groupManagerRegistered(GroupManager groupManager) + { + GroupProviderAdapter adapter = null; + synchronized (_groupManagerAdapters) + { + if(!_groupManagerAdapters.containsKey(groupManager)) + { + adapter = GroupProviderAdapter.createGroupProviderAdapter(this, groupManager); + _groupManagerAdapters.put(groupManager, adapter); + } + } + if(adapter != null) + { + childAdded(adapter); + } + } + + @Override + public void groupManagerUnregistered(GroupManager groupManager) + { + GroupProviderAdapter adapter; + synchronized (_groupManagerAdapters) + { + adapter = _groupManagerAdapters.remove(groupManager); + } + if(adapter != null) + { + childRemoved(adapter); + } + } } 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 new file mode 100644 index 0000000000..adc98919d5 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/GroupProviderAdapter.java @@ -0,0 +1,548 @@ +/* + * 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.security.Principal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.Group; +import org.apache.qpid.server.model.GroupMember; +import org.apache.qpid.server.model.GroupProvider; +import org.apache.qpid.server.model.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.model.UUIDGenerator; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.group.GroupManager; + +public class GroupProviderAdapter extends AbstractAdapter implements + GroupProvider +{ + private final GroupManager _groupManager; + + protected GroupProviderAdapter(GroupManager groupManager) + { + super(UUIDGenerator.generateRandomUUID()); + + if (groupManager == null) + { + throw new IllegalArgumentException("GroupManager must not be null"); + } + _groupManager = groupManager; + } + + public static GroupProviderAdapter createGroupProviderAdapter( + BrokerAdapter brokerAdapter, GroupManager groupManager) + { + final GroupProviderAdapter groupProviderAdapter = new GroupProviderAdapter( + groupManager); + groupProviderAdapter.addParent(Broker.class, brokerAdapter); + return groupProviderAdapter; + } + + @Override + public String getName() + { + return _groupManager.getClass().getSimpleName(); + } + + @Override + public String setName(String currentName, String desiredName) + throws IllegalStateException, AccessControlException + { + return null; + } + + @Override + public State getActualState() + { + return null; + } + + @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 getAttributeNames() + { + return GroupProvider.AVAILABLE_ATTRIBUTES; + } + + @Override + public Object getAttribute(String name) + { + if (TYPE.equals(name)) + { + return getName(); + } + else 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 (NAME.equals(name)) + { + return getName(); + } + else if (STATE.equals(name)) + { + return State.ACTIVE; // TODO + } + else if (TIME_TO_LIVE.equals(name)) + { + // TODO + } + else if (UPDATED.equals(name)) + { + // TODO + } + return super.getAttribute(name); + } + + @Override + public C createChild(Class childClass, + Map attributes, ConfiguredObject... otherParents) + { + if (childClass == Group.class) + { + String groupName = (String) attributes.get(Group.NAME); + + if (getSecurityManager().authoriseGroupOperation(Operation.CREATE, groupName)) + { + _groupManager.createGroup(groupName); + return (C) new GroupAdapter(groupName); + } + else + { + throw new AccessControlException("Do not have permission" + + " to create new group"); + } + } + + throw new IllegalArgumentException( + "This group provider does not support creating children of type: " + + childClass); + } + + @SuppressWarnings("unchecked") + @Override + public Collection getChildren(Class clazz) + { + if (clazz == Group.class) + { + Set groups = _groupManager.getGroupPrincipals(); + Collection principals = new ArrayList(groups.size()); + for (Principal group : groups) + { + principals.add(new GroupAdapter(group.getName())); + } + return (Collection) Collections + .unmodifiableCollection(principals); + } + else + { + return null; + } + } + + private org.apache.qpid.server.security.SecurityManager getSecurityManager() + { + return ApplicationRegistry.getInstance().getSecurityManager(); + } + + private class GroupAdapter extends AbstractAdapter implements Group + { + private final String _group; + + public GroupAdapter(String group) + { + super(UUIDGenerator.generateGroupUUID( + GroupProviderAdapter.this.getName(), group)); + _group = group; + + } + + @Override + public String getName() + { + return _group; + } + + @Override + public String setName(String currentName, String desiredName) + throws IllegalStateException, AccessControlException + { + throw new IllegalStateException("Names cannot be updated"); + } + + @Override + public State getActualState() + { + return State.ACTIVE; + } + + @Override + public boolean isDurable() + { + return true; + } + + @Override + public void setDurable(boolean durable) throws IllegalStateException, + AccessControlException, IllegalArgumentException + { + throw new IllegalStateException("Durability cannot be updated"); + } + + @Override + public LifetimePolicy getLifetimePolicy() + { + return LifetimePolicy.PERMANENT; + } + + @Override + public LifetimePolicy setLifetimePolicy(LifetimePolicy expected, + LifetimePolicy desired) throws IllegalStateException, + AccessControlException, IllegalArgumentException + { + throw new IllegalStateException("LifetimePolicy cannot be updated"); + } + + @Override + public long getTimeToLive() + { + return 0; + } + + @Override + public long setTimeToLive(long expected, long desired) + throws IllegalStateException, AccessControlException, + IllegalArgumentException + { + throw new IllegalStateException("ttl cannot be updated"); + } + + @Override + public Statistics getStatistics() + { + return NoStatistics.getInstance(); + } + + @Override + public Collection getChildren( + Class clazz) + { + if (clazz == GroupMember.class) + { + Set usersInGroup = _groupManager + .getUserPrincipalsForGroup(_group); + Collection members = new ArrayList(); + for (Principal principal : usersInGroup) + { + members.add(new GroupMemberAdapter(principal.getName())); + } + return (Collection) Collections + .unmodifiableCollection(members); + } + else + { + return null; + } + + } + + @Override + public C createChild(Class childClass, + Map attributes, + ConfiguredObject... otherParents) + { + if (childClass == GroupMember.class) + { + String memberName = (String) attributes.get(GroupMember.NAME); + + if (getSecurityManager().authoriseGroupOperation(Operation.UPDATE, _group)) + { + _groupManager.addUserToGroup(memberName, _group); + return (C) new GroupMemberAdapter(memberName); + } + else + { + throw new AccessControlException("Do not have permission" + + " to add new group member"); + } + } + + throw new IllegalArgumentException( + "This group provider does not support creating children of type: " + + childClass); + } + + @Override + public Collection getAttributeNames() + { + return Group.AVAILABLE_ATTRIBUTES; + } + + @Override + public Object getAttribute(String name) + { + if (ID.equals(name)) + { + return getId(); + } + else if (NAME.equals(name)) + { + return getName(); + } + return super.getAttribute(name); + } + + @Override + public Object setAttribute(String name, Object expected, Object desired) + throws IllegalStateException, AccessControlException, + IllegalArgumentException + { + return super.setAttribute(name, expected, desired); + } + + @Override + public State setDesiredState(State currentState, State desiredState) + throws IllegalStateTransitionException, AccessControlException + { + if (desiredState == State.DELETED) + { + if (getSecurityManager().authoriseGroupOperation(Operation.DELETE, _group)) + { + _groupManager.removeGroup(_group); + return State.DELETED; + } + else + { + throw new AccessControlException("Do not have permission" + + " to delete group"); + } + } + + return super.setDesiredState(currentState, desiredState); + } + + private class GroupMemberAdapter extends AbstractAdapter implements + GroupMember + { + private String _memberName; + + public GroupMemberAdapter(String memberName) + { + super(UUIDGenerator + .generateGroupMemberUUID( + GroupProviderAdapter.this.getName(), _group, + memberName)); + _memberName = memberName; + } + + @Override + public Collection getAttributeNames() + { + return GroupMember.AVAILABLE_ATTRIBUTES; + } + + @Override + public Object getAttribute(String name) + { + if (ID.equals(name)) + { + return getId(); + } + else if (NAME.equals(name)) + { + return getName(); + } + return super.getAttribute(name); + } + + @Override + public String getName() + { + return _memberName; + } + + @Override + public String setName(String currentName, String desiredName) + throws IllegalStateException, AccessControlException + { + return null; + } + + @Override + public State getActualState() + { + return null; + } + + @Override + public boolean isDurable() + { + return false; + } + + @Override + public void setDurable(boolean durable) + throws IllegalStateException, AccessControlException, + IllegalArgumentException + { + } + + @Override + public LifetimePolicy getLifetimePolicy() + { + return null; + } + + @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 getChildren( + Class clazz) + { + return null; + } + + @Override + public C createChild( + Class childClass, Map attributes, + ConfiguredObject... otherParents) + { + return null; + } + + @Override + public State setDesiredState(State currentState, State desiredState) + throws IllegalStateTransitionException, + AccessControlException + { + if (desiredState == State.DELETED) + { + if (getSecurityManager().authoriseGroupOperation(Operation.UPDATE, _group)) + { + _groupManager.removeUserFromGroup(_memberName, _group); + return State.DELETED; + } + else + { + throw new AccessControlException("Do not have permission" + + " to remove group member"); + } + } + + return super.setDesiredState(currentState, desiredState); + } + + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java index 74abbccd2b..ff38d5ec58 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java @@ -49,6 +49,8 @@ import org.apache.qpid.server.security.auth.manager.ExternalAuthenticationManage import org.apache.qpid.server.security.auth.manager.KerberosAuthenticationManager; import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; import org.apache.qpid.server.security.auth.manager.SimpleLDAPAuthenticationManager; +import org.apache.qpid.server.security.group.FileGroupManager; +import org.apache.qpid.server.security.group.GroupManagerPluginFactory; import org.apache.qpid.server.virtualhost.plugins.SlowConsumerDetection; import org.apache.qpid.server.virtualhost.plugins.VirtualHostPluginFactory; import org.apache.qpid.server.virtualhost.plugins.policies.TopicDeletePolicy; @@ -90,6 +92,7 @@ public class PluginManager implements Closeable private ServiceTracker _virtualHostTracker = null; private ServiceTracker _policyTracker = null; private ServiceTracker _authenticationManagerTracker = null; + private ServiceTracker _groupManagerTracker = null; private Activator _activator; @@ -99,6 +102,7 @@ public class PluginManager implements Closeable private Map _vhostPlugins = new HashMap(); private Map _policyPlugins = new HashMap(); private Map> _authenticationManagerPlugins = new HashMap>(); + private Map> _groupManagerPlugins = new HashMap>(); /** The default name of the OSGI system package list. */ private static final String DEFAULT_RESOURCE_NAME = "org/apache/qpid/server/plugins/OsgiSystemPackages.properties"; @@ -162,7 +166,8 @@ public class PluginManager implements Closeable AnonymousAuthenticationManager.AnonymousAuthenticationManagerConfiguration.FACTORY, KerberosAuthenticationManager.KerberosAuthenticationManagerConfiguration.FACTORY, SimpleLDAPAuthenticationManager.SimpleLDAPAuthenticationManagerConfiguration.FACTORY, - ExternalAuthenticationManager.ExternalAuthenticationManagerConfiguration.FACTORY + ExternalAuthenticationManager.ExternalAuthenticationManagerConfiguration.FACTORY, + FileGroupManager.FileGroupManagerConfiguration.FACTORY )) { _configPlugins.put(configFactory.getParentPaths(), configFactory); @@ -186,6 +191,12 @@ public class PluginManager implements Closeable _authenticationManagerPlugins.put(pluginFactory.getPluginName(), pluginFactory); } + for (GroupManagerPluginFactory pluginFactory : Arrays.asList( + FileGroupManager.FACTORY)) + { + _groupManagerPlugins.put(pluginFactory.getPluginName(), pluginFactory); + } + if(bundleContext == null) { // Check the plugin directory path is set and exist @@ -283,6 +294,11 @@ public class PluginManager implements Closeable _authenticationManagerTracker.open(); _trackers.add(_authenticationManagerTracker); + _groupManagerTracker = new ServiceTracker(bundleContext, GroupManagerPluginFactory.class.getName(), null); + _groupManagerTracker.open(); + _trackers.add(_groupManagerTracker); + + _logger.info("Opened service trackers"); } @@ -358,6 +374,11 @@ public class PluginManager implements Closeable return getServices(_authenticationManagerTracker, _authenticationManagerPlugins); } + public Map> getGroupManagerPlugins() + { + return getServices(_groupManagerTracker, _groupManagerPlugins); + } + public void close() { try diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index 1e649c3cb7..aac7562be2 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -68,6 +68,7 @@ import org.apache.qpid.server.output.ProtocolOutputConverter; import org.apache.qpid.server.output.ProtocolOutputConverterRegistry; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; import org.apache.qpid.server.state.AMQState; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.stats.StatisticsCounter; @@ -367,7 +368,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, AMQProtocolSessi // This sets the protocol version (and hence framing classes) for this session. setProtocolVersion(pv); - String mechanisms = ApplicationRegistry.getInstance().getAuthenticationManager(getLocalAddress()).getMechanisms(); + String mechanisms = ApplicationRegistry.getInstance().getSubjectCreator(getLocalAddress()).getMechanisms(); String locales = "en_US"; @@ -1017,7 +1018,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, AMQProtocolSessi public Principal getAuthorizedPrincipal() { - return _authorizedSubject == null ? null : _authorizedSubject.getPrincipals().iterator().next(); + return _authorizedSubject == null ? null : _authorizedSubject.getPrincipals(AuthenticatedPrincipal.class).iterator().next(); } public SocketAddress getRemoteAddress() diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java index 5c92aa95b6..152f591e66 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java @@ -302,7 +302,7 @@ public class MultiVersionProtocolEngine implements ServerProtocolEngine public ServerProtocolEngine getProtocolEngine() { final ConnectionDelegate connDelegate = - new org.apache.qpid.server.transport.ServerConnectionDelegate(_appRegistry, _fqdn, _appRegistry.getAuthenticationManager(getLocalAddress())); + new org.apache.qpid.server.transport.ServerConnectionDelegate(_appRegistry, _fqdn, _appRegistry.getSubjectCreator(getLocalAddress())); ServerConnection conn = new ServerConnection(_id); conn.setConnectionDelegate(connDelegate); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0.java index e6282315c6..ba5348c0ed 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0.java @@ -43,6 +43,7 @@ import org.apache.qpid.server.configuration.ConfigStore; import org.apache.qpid.server.configuration.ConnectionConfigType; import org.apache.qpid.server.protocol.v1_0.Connection_1_0; import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.transport.Sender; import org.apache.qpid.transport.network.NetworkConnection; @@ -144,7 +145,7 @@ public class ProtocolEngine_1_0_0 implements ServerProtocolEngine, FrameOutputHa Container container = new Container(_appRegistry.getBrokerId().toString()); - _conn = new ConnectionEndpoint(container, asSaslServerProvider(_appRegistry.getAuthenticationManager( + _conn = new ConnectionEndpoint(container, asSaslServerProvider(_appRegistry.getSubjectCreator( getLocalAddress()))); _conn.setConnectionEventListener(new Connection_1_0(_appRegistry, _conn, _connectionId)); _conn.setFrameOutputHandler(this); @@ -157,14 +158,14 @@ public class ProtocolEngine_1_0_0 implements ServerProtocolEngine, FrameOutputHa _sender.flush(); } - private SaslServerProvider asSaslServerProvider(final AuthenticationManager authenticationManager) + private SaslServerProvider asSaslServerProvider(final SubjectCreator subjectCreator) { return new SaslServerProvider() { @Override public SaslServer getSaslServer(String mechanism, String fqdn) throws SaslException { - return authenticationManager.createSaslServer(mechanism, fqdn, null); + return subjectCreator.createSaslServer(mechanism, fqdn, null); } }; } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0_SASL.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0_SASL.java index a48441bf30..b80080b991 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0_SASL.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0_SASL.java @@ -45,7 +45,7 @@ import org.apache.qpid.server.configuration.ConnectionConfigType; import org.apache.qpid.server.protocol.v1_0.Connection_1_0; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.transport.Sender; import org.apache.qpid.transport.network.NetworkConnection; @@ -165,7 +165,7 @@ public class ProtocolEngine_1_0_0_SASL implements ServerProtocolEngine, FrameOut Container container = new Container(_appRegistry.getBrokerId().toString()); _conn = new ConnectionEndpoint(container, asSaslServerProvider(ApplicationRegistry.getInstance() - .getAuthenticationManager(getLocalAddress()))); + .getSubjectCreator(getLocalAddress()))); _conn.setConnectionEventListener(new Connection_1_0(_appRegistry, _conn, _connectionId)); _conn.setRemoteAddress(getRemoteAddress()); @@ -175,8 +175,6 @@ public class ProtocolEngine_1_0_0_SASL implements ServerProtocolEngine, FrameOut _conn.setOnSaslComplete(new Runnable() { - - public void run() { if(_conn.isAuthenticated()) @@ -201,14 +199,14 @@ public class ProtocolEngine_1_0_0_SASL implements ServerProtocolEngine, FrameOut } - private SaslServerProvider asSaslServerProvider(final AuthenticationManager authenticationManager) + private SaslServerProvider asSaslServerProvider(final SubjectCreator subjectCreator) { return new SaslServerProvider() { @Override public SaslServer getSaslServer(String mechanism, String fqdn) throws SaslException { - return authenticationManager.createSaslServer(mechanism, fqdn, null); + return subjectCreator.createSaslServer(mechanism, fqdn, null); } }; } 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 e0e317f75d..f31275fa36 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 @@ -42,11 +42,17 @@ import org.apache.qpid.server.logging.messages.BrokerMessages; import org.apache.qpid.server.logging.messages.VirtualHostMessages; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.adapter.BrokerAdapter; +import org.apache.qpid.server.plugins.Plugin; import org.apache.qpid.server.plugins.PluginManager; import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.security.SecurityManager.SecurityConfiguration; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManagerRegistry; import org.apache.qpid.server.security.auth.manager.IAuthenticationManagerRegistry; +import org.apache.qpid.server.security.group.GroupManager; +import org.apache.qpid.server.security.group.GroupManagerPluginFactory; +import org.apache.qpid.server.security.group.GroupPrincipalAccessor; import org.apache.qpid.server.stats.StatisticsCounter; import org.apache.qpid.server.transport.QpidAcceptor; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -87,7 +93,7 @@ public abstract class ApplicationRegistry implements IApplicationRegistry private ConfigurationManager _configurationManager; - private RootMessageLogger _rootMessageLogger; + private volatile RootMessageLogger _rootMessageLogger; private CompositeStartupMessageLogger _startupMessageLogger; @@ -115,6 +121,11 @@ public abstract class ApplicationRegistry implements IApplicationRegistry private List _authManagerChangeListeners = new ArrayList(); + private List _groupManagerChangeListeners = + new ArrayList(); + + private List _groupManagerList = new ArrayList(); + public Map getAcceptors() { synchronized (_acceptors) @@ -314,7 +325,25 @@ public abstract class ApplicationRegistry implements IApplicationRegistry _securityManager = new SecurityManager(_configuration, _pluginManager); - _authenticationManagerRegistry = createAuthenticationManagerRegistry(_configuration, _pluginManager); + final Collection> factories = _pluginManager.getGroupManagerPlugins().values(); + final SecurityConfiguration securityConfiguration = _configuration.getConfiguration(SecurityConfiguration.class.getName()); + + for(GroupManagerPluginFactory factory : factories) + { + final GroupManager groupManager = factory.newInstance(securityConfiguration); + if(groupManager != null) + { + _groupManagerList.add(groupManager); + + for(GroupManagerChangeListener listener : _groupManagerChangeListeners) + { + listener.groupManagerRegistered(groupManager); + } + } + } + _logger.debug("Created " + _groupManagerList.size() + " group manager(s)"); + + _authenticationManagerRegistry = createAuthenticationManagerRegistry(_configuration, _pluginManager, new GroupPrincipalAccessor(_groupManagerList)); if(!_authManagerChangeListeners.isEmpty()) { @@ -348,10 +377,10 @@ public abstract class ApplicationRegistry implements IApplicationRegistry } } - protected IAuthenticationManagerRegistry createAuthenticationManagerRegistry(ServerConfiguration _configuration, PluginManager _pluginManager) + protected IAuthenticationManagerRegistry createAuthenticationManagerRegistry(ServerConfiguration configuration, PluginManager pluginManager, GroupPrincipalAccessor groupManagerList) throws ConfigurationException { - return new AuthenticationManagerRegistry(_configuration, _pluginManager); + return new AuthenticationManagerRegistry(configuration, pluginManager, groupManagerList); } protected void initialiseVirtualHosts() throws Exception @@ -588,9 +617,9 @@ public abstract class ApplicationRegistry implements IApplicationRegistry } @Override - public AuthenticationManager getAuthenticationManager(SocketAddress address) + public SubjectCreator getSubjectCreator(SocketAddress localAddress) { - return _authenticationManagerRegistry.getAuthenticationManager(address); + return _authenticationManagerRegistry.getSubjectCreator(localAddress); } @Override @@ -599,6 +628,12 @@ public abstract class ApplicationRegistry implements IApplicationRegistry return _authenticationManagerRegistry; } + @Override + public List getGroupManagers() + { + return _groupManagerList; + } + public PluginManager getPluginManager() { return _pluginManager; @@ -758,7 +793,7 @@ public abstract class ApplicationRegistry implements IApplicationRegistry } @Override - public void addRegistryChangeListener(IAuthenticationManagerRegistry.RegistryChangeListener registryChangeListener) + public void addAuthenticationManagerRegistryChangeListener(IAuthenticationManagerRegistry.RegistryChangeListener registryChangeListener) { if(_authenticationManagerRegistry == null) { @@ -769,4 +804,10 @@ public abstract class ApplicationRegistry implements IApplicationRegistry _authenticationManagerRegistry.addRegistryChangeListener(registryChangeListener); } } + + @Override + public void addGroupManagerChangeListener(GroupManagerChangeListener groupManagerChangeListener) + { + _groupManagerChangeListeners.add(groupManagerChangeListener); + } } 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 88c3c93156..5959b6fbe2 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 @@ -30,8 +30,10 @@ import org.apache.qpid.server.logging.RootMessageLogger; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.plugins.PluginManager; import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.security.auth.manager.IAuthenticationManagerRegistry; +import org.apache.qpid.server.security.group.GroupManager; import org.apache.qpid.server.stats.StatisticsGatherer; import org.apache.qpid.server.transport.QpidAcceptor; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -39,6 +41,7 @@ import org.apache.qpid.server.virtualhost.VirtualHostRegistry; import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -64,18 +67,16 @@ public interface IApplicationRegistry extends StatisticsGatherer ServerConfiguration getConfiguration(); /** - * Get the AuthenticationManager for the given socket address - * - * If no AuthenticationManager has been specifically set for the given address, then use the default - * AuthenticationManager + * Get the SubjectCreator for the given socket address. * * @param address The (listening) socket address for which the AuthenticationManager is required - * @return the AuthenticationManager */ - AuthenticationManager getAuthenticationManager(SocketAddress address); + SubjectCreator getSubjectCreator(SocketAddress localAddress); IAuthenticationManagerRegistry getAuthenticationManagerRegistry(); + List getGroupManagers(); + VirtualHostRegistry getVirtualHostRegistry(); SecurityManager getSecurityManager(); @@ -123,7 +124,7 @@ public interface IApplicationRegistry extends StatisticsGatherer int getHTTPSManagementPort(); - void addRegistryChangeListener(IAuthenticationManagerRegistry.RegistryChangeListener registryChangeListener); + void addAuthenticationManagerRegistryChangeListener(IAuthenticationManagerRegistry.RegistryChangeListener registryChangeListener); public interface PortBindingListener { @@ -132,4 +133,11 @@ public interface IApplicationRegistry extends StatisticsGatherer } + void addGroupManagerChangeListener(GroupManagerChangeListener groupManagerChangeListener); + + public static interface GroupManagerChangeListener + { + void groupManagerRegistered(GroupManager groupManager); + void groupManagerUnregistered(GroupManager groupManager); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AuthorizationHolder.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AuthorizationHolder.java index 8f3bdf7738..8243fc3f75 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AuthorizationHolder.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/AuthorizationHolder.java @@ -24,14 +24,14 @@ import javax.security.auth.Subject; import java.security.Principal; /** - * Represents the authorization of the logged on user. - * + * Represents the authorization of the logged on user. + * */ public interface AuthorizationHolder { - /** + /** * Returns the {@link Subject} of the authorized user. This is guaranteed to - * contain at least one {@link org.apache.qpid.server.security.auth.sasl.UsernamePrincipal}, representing the the identity + * contain at least one {@link org.apache.qpid.server.security.auth.UsernamePrincipal}, representing the the identity * used when the user logged on to the application, and zero or more {@link org.apache.qpid.server.security.auth.sasl.GroupPrincipal} * representing the group(s) to which the user belongs. * @@ -39,10 +39,10 @@ public interface AuthorizationHolder */ Subject getAuthorizedSubject(); - /** + /** * Returns the {@link Principal} representing the the identity * used when the user logged on to the application. - * + * * @return a Principal */ Principal getAuthorizedPrincipal(); 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 436660cfaf..088d120821 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 @@ -32,8 +32,10 @@ import org.apache.qpid.server.security.access.ObjectProperties; import org.apache.qpid.server.security.access.Operation; import static org.apache.qpid.server.security.access.ObjectType.EXCHANGE; +import static org.apache.qpid.server.security.access.ObjectType.GROUP; import static org.apache.qpid.server.security.access.ObjectType.METHOD; import static org.apache.qpid.server.security.access.ObjectType.QUEUE; +import static org.apache.qpid.server.security.access.ObjectType.USER; import static org.apache.qpid.server.security.access.ObjectType.VIRTUALHOST; import static org.apache.qpid.server.security.access.Operation.BIND; import static org.apache.qpid.server.security.access.Operation.CONSUME; @@ -387,6 +389,27 @@ public class SecurityManager }); } + public boolean authoriseGroupOperation(final Operation operation, final String groupName) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.authorise(operation, GROUP, new ObjectProperties(groupName)); + } + }); + } + + public boolean authoriseUserOperation(final Operation operation, final String userName) + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.authorise(operation, USER, new ObjectProperties(userName)); + } + }); + } private ConcurrentHashMap> _immediatePublishPropsCache = new ConcurrentHashMap>(); 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 new file mode 100644 index 0000000000..8138745486 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/SubjectCreator.java @@ -0,0 +1,137 @@ +/* + * + * 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; + +import java.security.Principal; + +import javax.security.auth.Subject; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +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.AuthenticationManager; +import org.apache.qpid.server.security.group.GroupPrincipalAccessor; + +/** + * Creates a {@link Subject} formed by the {@link Principal}'s returned from: + *

    + *
  1. Authenticating using an {@link AuthenticationManager}
  2. + *
  3. A {@link GroupPrincipalAccessor}
  4. + *
+ * + *

+ * SubjectCreator is a facade to the {@link AuthenticationManager}, and is intended to be + * the single place that {@link Subject}'s are created in the broker. + *

+ */ +public class SubjectCreator +{ + private AuthenticationManager _authenticationManager; + private GroupPrincipalAccessor _groupAccessor; + + public SubjectCreator(AuthenticationManager authenticationManager, GroupPrincipalAccessor groupAccessor) + { + _authenticationManager = authenticationManager; + _groupAccessor = groupAccessor; + } + + /** + * Gets the known SASL mechanisms + * + * @return SASL mechanism names, space separated. + */ + public String getMechanisms() + { + return _authenticationManager.getMechanisms(); + } + + /** + * @see AuthenticationManager#createSaslServer(String, String, Principal) + */ + public SaslServer createSaslServer(String mechanism, String localFQDN, Principal externalPrincipal) throws SaslException + { + return _authenticationManager.createSaslServer(mechanism, localFQDN, externalPrincipal); + } + + /** + * Authenticates a user using SASL negotiation. + * + * @param server SASL server + * @param response SASL response to process + */ + public SubjectAuthenticationResult authenticate(SaslServer server, byte[] response) + { + AuthenticationResult authenticationResult = _authenticationManager.authenticate(server, response); + if(server.isComplete()) + { + String username = server.getAuthorizationID(); + + return createResultWithGroups(username, authenticationResult); + } + else + { + return new SubjectAuthenticationResult(authenticationResult); + } + } + + /** + * Authenticates a user using their username and password. + */ + public SubjectAuthenticationResult authenticate(String username, String password) + { + final AuthenticationResult authenticationResult = _authenticationManager.authenticate(username, password); + + return createResultWithGroups(username, authenticationResult); + } + + private SubjectAuthenticationResult createResultWithGroups(String username, final AuthenticationResult authenticationResult) + { + if(authenticationResult.getStatus() == AuthenticationStatus.SUCCESS) + { + final Subject authenticationSubject = new Subject(); + + authenticationSubject.getPrincipals().addAll(authenticationResult.getPrincipals()); + authenticationSubject.getPrincipals().addAll(_groupAccessor.getGroupPrincipals(username)); + + authenticationSubject.setReadOnly(); + + return new SubjectAuthenticationResult(authenticationResult, authenticationSubject); + } + else + { + return new SubjectAuthenticationResult(authenticationResult); + } + } + + public Subject createSubjectWithGroups(String username) + { + Subject authenticationSubject = new Subject(); + + authenticationSubject.getPrincipals().add(new AuthenticatedPrincipal(username)); + authenticationSubject.getPrincipals().addAll(_groupAccessor.getGroupPrincipals(username)); + authenticationSubject.setReadOnly(); + + return authenticationSubject; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java index 90ecd1dd17..043d4909d5 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/access/ObjectType.java @@ -45,8 +45,10 @@ public enum ObjectType EXCHANGE(Operation.ALL, ACCESS, CREATE, DELETE, BIND, UNBIND, PUBLISH), LINK, // Not allowed in the Java broker ROUTE, // Not allowed in the Java broker - METHOD(Operation.ALL, ACCESS, UPDATE); - + METHOD(Operation.ALL, ACCESS, UPDATE), + USER(Operation.ALL, CREATE, DELETE, UPDATE), + GROUP(Operation.ALL, CREATE, DELETE, UPDATE); + private EnumSet _actions; private ObjectType() diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticatedPrincipal.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticatedPrincipal.java new file mode 100644 index 0000000000..96360e83e4 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticatedPrincipal.java @@ -0,0 +1,126 @@ +/* + * 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; + +import java.security.Principal; +import java.util.Set; + +import javax.security.auth.Subject; + +import org.apache.qpid.server.security.auth.UsernamePrincipal; + +/** + * A simple Principal wrapper. Exists to allow us to identify the "primary" principal + * by calling {@link Subject#getPrincipals(Class)}, passing in {@link AuthenticatedPrincipal}.class, + * e.g. when logging. + */ +public final class AuthenticatedPrincipal implements Principal +{ + private final Principal _wrappedPrincipal; + + /** convenience constructor for the common case where we're wrapping a {@link UsernamePrincipal} */ + public AuthenticatedPrincipal(String userPrincipalName) + { + this(new UsernamePrincipal(userPrincipalName)); + } + + public AuthenticatedPrincipal(Principal wrappedPrincipal) + { + if(wrappedPrincipal == null) + { + throw new IllegalArgumentException("Wrapped principal is null"); + } + + _wrappedPrincipal = wrappedPrincipal; + } + + @Override + public String getName() + { + return _wrappedPrincipal.getName(); + } + + @Override + public int hashCode() + { + return _wrappedPrincipal.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (!(obj instanceof AuthenticatedPrincipal)) + { + return false; + } + + AuthenticatedPrincipal other = (AuthenticatedPrincipal) obj; + + return _wrappedPrincipal.equals(other._wrappedPrincipal); + } + + public static AuthenticatedPrincipal getOptionalAuthenticatedPrincipalFromSubject(final Subject authSubject) + { + return getAuthenticatedPrincipalFromSubject(authSubject, true); + } + + public static AuthenticatedPrincipal getAuthenticatedPrincipalFromSubject(final Subject authSubject) + { + return getAuthenticatedPrincipalFromSubject(authSubject, false); + } + + private static AuthenticatedPrincipal getAuthenticatedPrincipalFromSubject(final Subject authSubject, boolean isPrincipalOptional) + { + if (authSubject == null) + { + throw new IllegalArgumentException("No authenticated subject."); + } + + final Set principals = authSubject.getPrincipals(AuthenticatedPrincipal.class); + int numberOfAuthenticatedPrincipals = principals.size(); + + if(numberOfAuthenticatedPrincipals == 0 && isPrincipalOptional) + { + return null; + } + else + { + if (numberOfAuthenticatedPrincipals != 1) + { + throw new IllegalArgumentException( + "Can't find single AuthenticatedPrincipal in authenticated subject. There were " + + numberOfAuthenticatedPrincipals + + " authenticated principals out of a total number of principals of: " + authSubject.getPrincipals()); + } + return principals.iterator().next(); + } + } + + @Override + public String toString() + { + return getName(); + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java index 949c0f2b89..a49dd6793f 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/AuthenticationResult.java @@ -7,9 +7,9 @@ * 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 @@ -20,15 +20,20 @@ */ package org.apache.qpid.server.security.auth; -import javax.security.auth.Subject; +import java.security.Principal; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; /** - * Encapsulates the result of an attempt to authenticate. + * Encapsulates the result of an attempt to authenticate using an {@link AuthenticationManager}. *

* The authentication status describes the overall outcome. *

*

    - *
  1. If authentication status is SUCCESS, the subject will be populated. + *
  2. If authentication status is SUCCESS, at least one {@link Principal} will be populated. *
  3. *
  4. If authentication status is CONTINUE, the authentication has failed because the user * supplied incorrect credentials (etc). If the authentication requires it, the next challenge @@ -40,6 +45,8 @@ import javax.security.auth.Subject; *
  5. *
* + * The main principal provided to the constructor is wrapped in an {@link AuthenticatedPrincipal} + * to make it easier for the rest of the application to identify it among the set of other principals. */ public class AuthenticationResult { @@ -56,37 +63,54 @@ public class AuthenticationResult private final AuthenticationStatus _status; private final byte[] _challenge; private final Exception _cause; - private final Subject _subject; + private final Set _principals = new HashSet(); public AuthenticationResult(final AuthenticationStatus status) { this(null, status, null); } + public AuthenticationResult(Principal mainPrincipal) + { + this(mainPrincipal, Collections.emptySet()); + } + + public AuthenticationResult(Principal mainPrincipal, Set otherPrincipals) + { + AuthenticatedPrincipal specialQpidAuthenticatedPrincipal = new AuthenticatedPrincipal(mainPrincipal); + _principals.addAll(otherPrincipals); + _principals.remove(mainPrincipal); + _principals.add(specialQpidAuthenticatedPrincipal); + + _status = AuthenticationStatus.SUCCESS; + _challenge = null; + _cause = null; + } + public AuthenticationResult(final byte[] challenge, final AuthenticationStatus status) { - this(challenge, status, null); + _challenge = challenge; + _status = status; + _cause = null; } public AuthenticationResult(final AuthenticationStatus error, final Exception cause) { - this(null, error, cause); + _status = error; + _challenge = null; + _cause = cause; } public AuthenticationResult(final byte[] challenge, final AuthenticationStatus status, final Exception cause) { + if(status == AuthenticationStatus.SUCCESS) + { + throw new IllegalArgumentException("Successful authentication requires at least one principal"); + } + this._status = status; this._challenge = challenge; this._cause = cause; - this._subject = null; - } - - public AuthenticationResult(final Subject subject) - { - this._status = AuthenticationStatus.SUCCESS; - this._challenge = null; - this._cause = null; - this._subject = subject; } public Exception getCause() @@ -104,9 +128,8 @@ public class AuthenticationResult return _challenge; } - public Subject getSubject() + public Set getPrincipals() { - return _subject; + return _principals; } - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/SubjectAuthenticationResult.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/SubjectAuthenticationResult.java new file mode 100644 index 0000000000..3be96b87eb --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/SubjectAuthenticationResult.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.security.auth; + +import java.security.Principal; + +import javax.security.auth.Subject; + +import org.apache.qpid.server.security.SubjectCreator; + +/** + * Encapsulates the result of an attempt to authenticate using a {@link SubjectCreator}. + * + *

+ * iff authentication was successful, {@link #getSubject()} will return a non-null value and + * {@link #getStatus()} will return {@link AuthenticationResult.AuthenticationStatus#SUCCESS}. + * + * In this case, the {@link Subject} will contain the user {@link Principal} and zero or more other principals + * representing groups. + *

+ * @see SubjectCreator + */ +public class SubjectAuthenticationResult +{ + private final AuthenticationResult _authenticationResult; + private final Subject _subject; + + public SubjectAuthenticationResult(AuthenticationResult authenticationResult, Subject subject) + { + _authenticationResult = authenticationResult; + _subject = subject; + } + + public SubjectAuthenticationResult(AuthenticationResult unsuccessfulAuthenticationResult) + { + this(unsuccessfulAuthenticationResult, null); + } + + public Exception getCause() + { + return _authenticationResult.getCause(); + } + + public AuthenticationResult.AuthenticationStatus getStatus() + { + return _authenticationResult.getStatus(); + } + + public byte[] getChallenge() + { + return _authenticationResult.getChallenge(); + } + + public Subject getSubject() + { + return _subject; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/UsernamePrincipal.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/UsernamePrincipal.java new file mode 100644 index 0000000000..cc414f801a --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/UsernamePrincipal.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.security.auth; + +import java.security.Principal; + +/** A principal that is just a wrapper for a simple username. */ +public class UsernamePrincipal implements Principal +{ + private final String _name; + + public UsernamePrincipal(String name) + { + if (name == null) + { + throw new IllegalArgumentException("name cannot be null"); + } + _name = name; + } + + public String getName() + { + return _name; + } + + public String toString() + { + return _name; + } + + @Override + public int hashCode() + { + final int prime = 31; + return prime * _name.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else + { + if (obj instanceof UsernamePrincipal) + { + UsernamePrincipal other = (UsernamePrincipal) obj; + return _name.equals(other._name); + } + else + { + return false; + } + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java index cac60a5283..578bb96efa 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/AbstractPasswordFilePrincipalDatabase.java @@ -21,9 +21,9 @@ package org.apache.qpid.server.security.auth.database; import org.apache.log4j.Logger; +import org.apache.qpid.server.security.auth.UsernamePrincipal; import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; import org.apache.qpid.server.security.auth.sasl.UsernamePasswordInitialiser; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.login.AccountNotFoundException; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java index 4203cb0e07..ecf08de72e 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java @@ -21,7 +21,7 @@ package org.apache.qpid.server.security.auth.database; import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +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; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManager.java index 5676c43754..9ac7a8b11a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManager.java @@ -24,7 +24,6 @@ import java.security.Principal; import java.util.Arrays; import java.util.List; import javax.security.auth.Subject; -import javax.security.auth.callback.CallbackHandler; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import org.apache.commons.configuration.Configuration; @@ -33,7 +32,7 @@ import org.apache.log4j.Logger; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; import org.apache.qpid.server.security.auth.AuthenticationResult; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.UsernamePrincipal; import org.apache.qpid.server.security.auth.sasl.anonymous.AnonymousInitialiser; import org.apache.qpid.server.security.auth.sasl.anonymous.AnonymousSaslServer; @@ -45,7 +44,9 @@ public class AnonymousAuthenticationManager implements AuthenticationManager private static final String ANONYMOUS = SASL_INITIALISER.getMechanismName(); - private static final Principal ANONYMOUS_PRINCIPAL = new UsernamePrincipal("ANONYMOUS"); + public static final String ANONYMOUS_USERNAME = "ANONYMOUS"; + + public static final Principal ANONYMOUS_PRINCIPAL = new UsernamePrincipal(ANONYMOUS_USERNAME); public static final Subject ANONYMOUS_SUBJECT = new Subject(); static @@ -53,10 +54,7 @@ public class AnonymousAuthenticationManager implements AuthenticationManager ANONYMOUS_SUBJECT.getPrincipals().add(ANONYMOUS_PRINCIPAL); } - private static final AuthenticationResult ANONYMOUS_AUTHENTICATION = new AuthenticationResult(ANONYMOUS_SUBJECT); - - - private static CallbackHandler _callbackHandler = SASL_INITIALISER.getCallbackHandler(); + private static final AuthenticationResult ANONYMOUS_AUTHENTICATION = new AuthenticationResult(ANONYMOUS_PRINCIPAL); static final AnonymousAuthenticationManager INSTANCE = new AnonymousAuthenticationManager(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java index ccddcb7669..ba635cd023 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java @@ -30,14 +30,15 @@ import org.apache.qpid.server.security.auth.AuthenticationResult; /** * Implementations of the AuthenticationManager are responsible for determining * the authenticity of a user's credentials. - * - * If the authentication is successful, the manager is responsible for producing a populated - * {@link javax.security.auth.Subject} containing the user's identity and zero or more principals representing - * groups to which the user belongs. + *

+ * If the authentication is successful, the manager is responsible for producing an + * {@link AuthenticationResult} containing the user's main {@link Principal} and zero or + * more other implementation-specific principals. + *

*

* The {@link #initialise()} method is responsible for registering SASL mechanisms required by * the manager. The {@link #close()} method must reverse this registration. - * + *

*/ public interface AuthenticationManager extends Closeable, Plugin { @@ -88,5 +89,4 @@ public interface AuthenticationManager extends Closeable, Plugin * @return authentication result */ AuthenticationResult authenticate(String username, String password); - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerRegistry.java index 89a4d8ae66..323ee15dd9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerRegistry.java @@ -25,7 +25,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; @@ -35,6 +34,8 @@ import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.plugins.Plugin; import org.apache.qpid.server.plugins.PluginManager; import org.apache.qpid.server.security.SecurityManager.SecurityConfiguration; +import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.security.group.GroupPrincipalAccessor; /** * A concrete implementation of {@link IAuthenticationManagerRegistry} that registers all {@link AuthenticationManager} @@ -50,12 +51,12 @@ import org.apache.qpid.server.security.SecurityManager.SecurityConfiguration; public class AuthenticationManagerRegistry implements Closeable, IAuthenticationManagerRegistry { private final Map _classToAuthManagerMap = new HashMap(); - private final AuthenticationManager _defaultAuthenticationManager; - private final Map _portToAuthenticationManagerMap; + private final SubjectCreator _defaultSubjectCreator; + private final Map _portToSubjectCreatorMap; private final List _listeners = Collections.synchronizedList(new ArrayList()); - public AuthenticationManagerRegistry(ServerConfiguration serverConfiguration, PluginManager _pluginManager) + public AuthenticationManagerRegistry(ServerConfiguration serverConfiguration, PluginManager _pluginManager, GroupPrincipalAccessor groupPrincipalAccessor) throws ConfigurationException { final Collection> factories = _pluginManager.getAuthenticationManagerPlugins().values(); @@ -78,9 +79,9 @@ public class AuthenticationManagerRegistry implements Closeable, IAuthentication throw new ConfigurationException("No authentication managers configured within the configuration file."); } - _defaultAuthenticationManager = getDefaultAuthenticationManager(serverConfiguration); + _defaultSubjectCreator = createDefaultSubectCreator(serverConfiguration, groupPrincipalAccessor); - _portToAuthenticationManagerMap = getPortToAuthenticationManagerMap(serverConfiguration); + _portToSubjectCreatorMap = createPortToSubjectCreatorMap(serverConfiguration, groupPrincipalAccessor); willClose = false; } finally @@ -95,14 +96,14 @@ public class AuthenticationManagerRegistry implements Closeable, IAuthentication } @Override - public AuthenticationManager getAuthenticationManager(SocketAddress address) + public SubjectCreator getSubjectCreator(SocketAddress address) { - AuthenticationManager authManager = + SubjectCreator subjectCreator = address instanceof InetSocketAddress - ? _portToAuthenticationManagerMap.get(((InetSocketAddress)address).getPort()) + ? _portToSubjectCreatorMap.get(((InetSocketAddress)address).getPort()) : null; - return authManager == null ? _defaultAuthenticationManager : authManager; + return subjectCreator == null ? _defaultSubjectCreator : subjectCreator; } @Override @@ -140,8 +141,8 @@ public class AuthenticationManagerRegistry implements Closeable, IAuthentication } } - private AuthenticationManager getDefaultAuthenticationManager( - ServerConfiguration serverConfiguration) + private SubjectCreator createDefaultSubectCreator( + ServerConfiguration serverConfiguration, GroupPrincipalAccessor groupAccessor) throws ConfigurationException { final AuthenticationManager defaultAuthenticationManager; @@ -164,14 +165,14 @@ public class AuthenticationManagerRegistry implements Closeable, IAuthentication { throw new ConfigurationException("If more than one authentication manager is configured a default MUST be specified."); } - return defaultAuthenticationManager; + return new SubjectCreator(defaultAuthenticationManager, groupAccessor); } - private Map getPortToAuthenticationManagerMap( - ServerConfiguration serverConfiguration) + private Map createPortToSubjectCreatorMap( + ServerConfiguration serverConfiguration, GroupPrincipalAccessor groupPrincipalAccessor) throws ConfigurationException { - Map portToAuthenticationManagerMap = new HashMap(); + Map portToSubjectCreatorMap = new HashMap(); for(Map.Entry portMapping : serverConfiguration.getPortAuthenticationMappings().entrySet()) { @@ -182,10 +183,12 @@ public class AuthenticationManagerRegistry implements Closeable, IAuthentication throw new ConfigurationException("Unknown authentication manager class " + portMapping.getValue() + " configured for port " + portMapping.getKey()); } - portToAuthenticationManagerMap.put(portMapping.getKey(), authenticationManager); + + SubjectCreator subjectCreator = new SubjectCreator(authenticationManager, groupPrincipalAccessor); + portToSubjectCreatorMap.put(portMapping.getKey(), subjectCreator); } - return portToAuthenticationManagerMap; + return portToSubjectCreatorMap; } @Override diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManager.java index 2d6866b657..a1328ca0de 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManager.java @@ -21,7 +21,6 @@ package org.apache.qpid.server.security.auth.manager; import java.security.Principal; import java.util.Arrays; import java.util.List; -import javax.security.auth.Subject; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import org.apache.commons.configuration.Configuration; @@ -137,15 +136,13 @@ public class ExternalAuthenticationManager implements AuthenticationManager // Process response from the client try { - byte[] challenge = server.evaluateResponse(response != null ? response : new byte[0]); + server.evaluateResponse(response != null ? response : new byte[0]); Principal principal = ((ExternalSaslServer)server).getAuthenticatedPrincipal(); if(principal != null) { - final Subject subject = new Subject(); - subject.getPrincipals().add(principal); - return new AuthenticationResult(subject); + return new AuthenticationResult(principal); } else { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/IAuthenticationManagerRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/IAuthenticationManagerRegistry.java index 485ca2e1e9..6ed8f95512 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/IAuthenticationManagerRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/IAuthenticationManagerRegistry.java @@ -23,13 +23,11 @@ import java.net.SocketAddress; import java.util.Map; import org.apache.qpid.common.Closeable; -import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.server.security.SubjectCreator; /** - * Registry for {@link AuthenticationManager} instances. - * - *

A lookup method {@link #getAuthenticationManager(SocketAddress)} allows a caller to determine - * the AuthenticationManager associated with a particular port number.

+ * Registry for {@link AuthenticationManager} instances, also exposing them wrapped in {@link SubjectCreator}'s + * as a convenience. * *

It is important to {@link #close()} the registry after use and this allows the AuthenticationManagers * to reverse any security registrations they have performed.

@@ -37,14 +35,11 @@ import org.apache.qpid.server.virtualhost.VirtualHost; public interface IAuthenticationManagerRegistry extends Closeable { /** - * Returns the {@link AuthenticationManager} associated with a particular {@link SocketAddress}. - * If no authentication manager is associated with this address, a default authentication manager will be + * Returns the {@link SubjectCreator} associated with a particular {@link SocketAddress}. + * If no subject creator is associated with this address, a default will be * returned. Null is never returned. - * - * @param address - * @return authentication manager. */ - public AuthenticationManager getAuthenticationManager(SocketAddress address); + public SubjectCreator getSubjectCreator(SocketAddress address); Map getAvailableAuthenticationManagers(); @@ -55,5 +50,4 @@ public interface IAuthenticationManagerRegistry extends Closeable } public void addRegistryChangeListener(RegistryChangeListener listener); - } \ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/KerberosAuthenticationManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/KerberosAuthenticationManager.java index d735ecb1d4..ed15d244eb 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/KerberosAuthenticationManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/KerberosAuthenticationManager.java @@ -23,7 +23,6 @@ import java.security.Principal; import java.util.Arrays; import java.util.HashMap; import java.util.List; -import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; @@ -37,7 +36,7 @@ import org.apache.log4j.Logger; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; import org.apache.qpid.server.security.auth.AuthenticationResult; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.UsernamePrincipal; public class KerberosAuthenticationManager implements AuthenticationManager { @@ -158,10 +157,7 @@ public class KerberosAuthenticationManager implements AuthenticationManager if (server.isComplete()) { - final Subject subject = new Subject(); - _logger.debug("Authenticated as " + server.getAuthorizationID()); - subject.getPrincipals().add(new UsernamePrincipal(server.getAuthorizationID())); - return new AuthenticationResult(subject); + return new AuthenticationResult(new UsernamePrincipal(server.getAuthorizationID())); } else { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java index e6498919a1..529f2bf6a8 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java @@ -34,9 +34,8 @@ import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationS import org.apache.qpid.server.security.auth.database.PrincipalDatabase; import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; import org.apache.qpid.server.security.auth.sasl.JCAProvider; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.UsernamePrincipal; -import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.login.AccountNotFoundException; import javax.security.sasl.Sasl; @@ -164,6 +163,7 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan return getConfig().getString("principal-database.class"); } + @SuppressWarnings({ "unchecked", "rawtypes" }) public Map getPdClassAttributeMap() throws ConfigurationException { final List argumentNames = (List) getConfig().getList("principal-database.attributes.attribute.name"); @@ -284,9 +284,8 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan if (server.isComplete()) { - final Subject subject = new Subject(); - subject.getPrincipals().add(new UsernamePrincipal(server.getAuthorizationID())); - return new AuthenticationResult(subject); + final String userId = server.getAuthorizationID(); + return new AuthenticationResult(new UsernamePrincipal(userId)); } else { @@ -308,9 +307,7 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan { if (_principalDatabase.verifyPassword(username, password.toCharArray())) { - final Subject subject = new Subject(); - subject.getPrincipals().add(new UsernamePrincipal(username)); - return new AuthenticationResult(subject); + return new AuthenticationResult(new UsernamePrincipal(username)); } else { @@ -353,6 +350,16 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan } } + public PrincipalDatabase getPrincipalDatabase() + { + return _principalDatabase; + } + + protected void setPrincipalDatabase(final PrincipalDatabase principalDatabase) + { + _principalDatabase = principalDatabase; + } + private void configPrincipalDatabase(final PrincipalDatabase principalDatabase, final PrincipalDatabaseAuthenticationManagerConfiguration config) throws ConfigurationException { @@ -400,11 +407,6 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan } } - public PrincipalDatabase getPrincipalDatabase() - { - return _principalDatabase; - } - private String generateSetterName(String argName) throws ConfigurationException { if ((argName == null) || (argName.length() == 0)) @@ -421,8 +423,4 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan return methodName; } - protected void setPrincipalDatabase(final PrincipalDatabase principalDatabase) - { - _principalDatabase = principalDatabase; - } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManager.java index 64b24e28bc..8490a1c373 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleLDAPAuthenticationManager.java @@ -32,7 +32,6 @@ import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; -import javax.security.auth.Subject; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; @@ -47,7 +46,7 @@ import org.apache.log4j.Logger; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; import org.apache.qpid.server.security.auth.AuthenticationResult; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.UsernamePrincipal; import org.apache.qpid.server.security.auth.sasl.plain.PlainPasswordCallback; public class SimpleLDAPAuthenticationManager implements AuthenticationManager @@ -205,10 +204,10 @@ public class SimpleLDAPAuthenticationManager implements AuthenticationManager if (server.isComplete()) { - final Subject subject = new Subject(); - _logger.debug("Authenticated as " + server.getAuthorizationID()); - subject.getPrincipals().add(new UsernamePrincipal(server.getAuthorizationID())); - return new AuthenticationResult(subject); + String authorizationID = server.getAuthorizationID(); + _logger.debug("Authenticated as " + authorizationID); + + return new AuthenticationResult(new UsernamePrincipal(authorizationID)); } else { @@ -249,9 +248,8 @@ public class SimpleLDAPAuthenticationManager implements AuthenticationManager env.put(Context.SECURITY_CREDENTIALS, password); DirContext ctx = new InitialDirContext(env); ctx.close(); - final Subject subject = new Subject(); - subject.getPrincipals().add(new UsernamePrincipal(username)); - return new AuthenticationResult(subject); + + return new AuthenticationResult(new UsernamePrincipal(username)); } @Override 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/rmi/RMIPasswordAuthenticator.java index 2e21cfbb07..808447b7ff 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/rmi/RMIPasswordAuthenticator.java @@ -23,12 +23,11 @@ package org.apache.qpid.server.security.auth.rmi; import java.net.SocketAddress; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; import javax.management.remote.JMXAuthenticator; -import javax.management.remote.JMXPrincipal; import javax.security.auth.Subject; public class RMIPasswordAuthenticator implements JMXAuthenticator @@ -41,7 +40,7 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator static final String CREDENTIALS_REQUIRED = "User details are required. " + "Please ensure you are using an up to date management console to connect."; - private AuthenticationManager _authenticationManager = null; + private SubjectCreator _subjectCreator = null; private SocketAddress _socketAddress; public RMIPasswordAuthenticator(SocketAddress socketAddress) @@ -49,9 +48,9 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator _socketAddress = socketAddress; } - public void setAuthenticationManager(final AuthenticationManager authenticationManager) + public void setSubjectCreator(final SubjectCreator subjectCreator) { - _authenticationManager = authenticationManager; + _subjectCreator = subjectCreator; } public Subject authenticate(Object credentials) throws SecurityException @@ -85,14 +84,14 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator throw new SecurityException(SHOULD_BE_NON_NULL); } - // Verify that an AuthenticationManager has been set. - if (_authenticationManager == null) + // Verify that an SubjectCreator has been set. + if (_subjectCreator == null) { try { - if(ApplicationRegistry.getInstance().getAuthenticationManager(_socketAddress) != null) + if(ApplicationRegistry.getInstance().getSubjectCreator(_socketAddress) != null) { - _authenticationManager = ApplicationRegistry.getInstance().getAuthenticationManager(_socketAddress); + _subjectCreator = ApplicationRegistry.getInstance().getSubjectCreator(_socketAddress); } else { @@ -104,7 +103,7 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator throw new SecurityException(UNABLE_TO_LOOKUP); } } - final AuthenticationResult result = _authenticationManager.authenticate(username, password); + final SubjectAuthenticationResult result = _subjectCreator.authenticate(username, password); if (AuthenticationStatus.ERROR.equals(result.getStatus())) { @@ -112,10 +111,7 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator } else if (AuthenticationStatus.SUCCESS.equals(result.getStatus())) { - final Subject subject = result.getSubject(); - subject.getPrincipals().add(new JMXPrincipal(username)); - subject.setReadOnly(); - return subject; + return result.getSubject(); } else { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipal.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipal.java deleted file mode 100644 index 30a503c769..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipal.java +++ /dev/null @@ -1,99 +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.sasl; - -import java.security.Principal; -import java.security.acl.Group; -import java.util.Enumeration; - -/** - * Immutable representation of a user group. In Qpid, groups do not know - * about their membership, and therefore the {@link #addMember(Principal)} - * methods etc throw {@link UnsupportedOperationException}. - * - */ -public class GroupPrincipal implements Group -{ - /** Name of the group */ - private final String _groupName; - - public GroupPrincipal(final String groupName) - { - _groupName = groupName; - } - - public String getName() - { - return _groupName; - } - - public boolean addMember(Principal user) - { - throw new UnsupportedOperationException("Not supported"); - } - - public boolean removeMember(Principal user) - { - throw new UnsupportedOperationException("Not supported"); - } - - public boolean isMember(Principal member) - { - throw new UnsupportedOperationException("Not supported"); - } - - public Enumeration members() - { - throw new UnsupportedOperationException("Not supported"); - } - - /** - * @see java.lang.Object#hashCode() - */ - public int hashCode() - { - final int prime = 37; - return prime * _groupName.hashCode(); - } - - /** - * @see java.lang.Object#equals(java.lang.Object) - */ - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else - { - if (obj instanceof GroupPrincipal) - { - GroupPrincipal other = (GroupPrincipal) obj; - return _groupName.equals(other._groupName); - } - else - { - return false; - } - } - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java index f4e8f800c6..b70a987107 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePasswordInitialiser.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.security.auth.sasl; import org.apache.commons.configuration.Configuration; import org.apache.log4j.Logger; +import org.apache.qpid.server.security.auth.UsernamePrincipal; import org.apache.qpid.server.security.auth.database.PrincipalDatabase; import javax.security.auth.callback.Callback; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java deleted file mode 100644 index 9e7db94216..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipal.java +++ /dev/null @@ -1,99 +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.sasl; - -import javax.security.auth.Subject; -import java.security.Principal; -import java.util.Set; - -/** A principal that is just a wrapper for a simple username. */ -public class UsernamePrincipal implements Principal -{ - private final String _name; - - public UsernamePrincipal(String name) - { - if (name == null) - { - throw new IllegalArgumentException("name cannot be null"); - } - _name = name; - } - - public String getName() - { - return _name; - } - - public String toString() - { - return _name; - } - - /** - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() - { - final int prime = 31; - return prime * _name.hashCode(); - } - - /** - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else - { - if (obj instanceof UsernamePrincipal) - { - UsernamePrincipal other = (UsernamePrincipal) obj; - return _name.equals(other._name); - } - else - { - return false; - } - } - } - - public static UsernamePrincipal getUsernamePrincipalFromSubject(final Subject authSubject) - { - if (authSubject == null) - { - throw new IllegalArgumentException("No authenticated subject."); - } - - final Set principals = authSubject.getPrincipals(UsernamePrincipal.class); - if (principals.size() != 1) - { - throw new IllegalArgumentException("Can't find single UsernamePrincipal in authenticated subject"); - } - return principals.iterator().next(); - } -} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServer.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServer.java index 52d36023c2..d10193e743 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServer.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServer.java @@ -23,6 +23,8 @@ package org.apache.qpid.server.security.auth.sasl.anonymous; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; +import org.apache.qpid.server.security.auth.manager.AnonymousAuthenticationManager; + public class AnonymousSaslServer implements SaslServer { @@ -52,7 +54,7 @@ public class AnonymousSaslServer implements SaslServer public String getAuthorizationID() { - return null; + return AnonymousAuthenticationManager.ANONYMOUS_PRINCIPAL.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/FileGroupDatabase.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupDatabase.java new file mode 100644 index 0000000000..2e4fc9e3a3 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupDatabase.java @@ -0,0 +1,265 @@ +/* + * 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.group; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.Date; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListSet; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; + +/** + * A group database that reads/writes the following file format: + * + * group1.users=user1,user2 + * group2.users=user2,user3 + */ +public class FileGroupDatabase implements GroupDatabase +{ + private static final Logger LOGGER = Logger.getLogger(FileGroupDatabase.class); + + private Map> _groupToUserMap = new ConcurrentHashMap>(); + private Map> _userToGroupMap = new ConcurrentHashMap>(); + private String _groupFile; + + @Override + public Set getAllGroups() + { + return Collections.unmodifiableSet(_groupToUserMap.keySet()); + } + + public synchronized void setGroupFile(String groupFile) throws IOException + { + File file = new File(groupFile); + + if (!file.canRead()) + { + throw new FileNotFoundException(groupFile + + " cannot be found or is not readable"); + } + + readGroupFile(groupFile); + } + + @Override + public Set getUsersInGroup(String group) + { + if (group == null) + { + LOGGER.warn("Requested user set for null group. Returning empty set."); + return Collections.emptySet(); + } + + Set set = _groupToUserMap.get(group); + if (set == null) + { + return Collections.emptySet(); + } + else + { + return Collections.unmodifiableSet(set); + } + } + + @Override + public synchronized void addUserToGroup(String user, String group) + { + Set users = _groupToUserMap.get(group); + if (users == null) + { + throw new IllegalArgumentException("Group " + group + " does not exist so could not add " + user + " to it"); + } + + users.add(user); + + Set groups = _userToGroupMap.get(user); + if (groups == null) + { + groups = new ConcurrentSkipListSet(); + _userToGroupMap.put(user, groups); + } + groups.add(group); + + update(); + } + + @Override + public synchronized void removeUserFromGroup(String user, String group) + { + Set users = _groupToUserMap.get(group); + if (users == null) + { + throw new IllegalArgumentException("Group " + group + " does not exist so could not remove " + user + " from it"); + } + + users.remove(user); + + Set groups = _userToGroupMap.get(user); + if (groups != null) + { + groups.remove(group); + } + + update(); + } + + @Override + public Set getGroupsForUser(String user) + { + if(user == null) + { + LOGGER.warn("Requested group set for null user. Returning empty set."); + return Collections.emptySet(); + } + + Set groups = _userToGroupMap.get(user); + if (groups == null) + { + return Collections.emptySet(); + } + else + { + return Collections.unmodifiableSet(groups); + } + } + + @Override + public synchronized void createGroup(String group) + { + Set users = new ConcurrentSkipListSet(); + _groupToUserMap.put(group, users); + + update(); + } + + @Override + public synchronized void removeGroup(String group) + { + _groupToUserMap.remove(group); + for (Set groupsForUser : _userToGroupMap.values()) + { + groupsForUser.remove(group); + } + + update(); + } + + private synchronized void update() + { + if (_groupFile != null) + { + try + { + writeGroupFile(_groupFile); + } + catch (IOException e) + { + throw new RuntimeException("Unable to persist change to file " + _groupFile); + } + } + } + + private synchronized void readGroupFile(String groupFile) throws IOException + { + _groupFile = groupFile; + _groupToUserMap.clear(); + _userToGroupMap.clear(); + Properties propertiesFile = new Properties(); + propertiesFile.load(new FileInputStream(groupFile)); + + for (String propertyName : propertiesFile.stringPropertyNames()) + { + validatePropertyNameIsGroupName(propertyName); + + String groupName = propertyName.replaceAll("\\.users$", ""); + String userString = propertiesFile.getProperty(propertyName); + + final Set userSet = buildUserSetFromCommaSeparateValue(userString); + + _groupToUserMap.put(groupName, userSet); + + for (String userName : userSet) + { + Set groupsForThisUser = _userToGroupMap.get(userName); + + if (groupsForThisUser == null) + { + groupsForThisUser = new ConcurrentSkipListSet(); + _userToGroupMap.put(userName, groupsForThisUser); + } + + groupsForThisUser.add(groupName); + } + } + } + + private synchronized void writeGroupFile(String groupFile) throws IOException + { + Properties propertiesFile = new Properties(); + + for (String group : _groupToUserMap.keySet()) + { + Set users = _groupToUserMap.get(group); + String userList = StringUtils.join(users, ","); + + propertiesFile.setProperty(group + ".users", userList); + } + + String comment = "Written " + new Date(); + propertiesFile.store(new FileOutputStream(groupFile), comment); + } + + private void validatePropertyNameIsGroupName(String propertyName) + { + if (!propertyName.endsWith(".users")) + { + throw new IllegalArgumentException( + "Invalid definition with name '" + + propertyName + + "'. Group definitions must end with suffix '.users'"); + } + } + + private ConcurrentSkipListSet buildUserSetFromCommaSeparateValue(String userString) + { + String[] users = userString.split(","); + final ConcurrentSkipListSet userSet = new ConcurrentSkipListSet(); + for (String user : users) + { + final String trimmed = user.trim(); + if (!trimmed.isEmpty()) + { + userSet.add(trimmed); + } + } + return userSet; + } + +} 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 new file mode 100644 index 0000000000..1b393c3ecf --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupManager.java @@ -0,0 +1,251 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.security.group; + +import java.io.IOException; +import java.security.Principal; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.log4j.Logger; +import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory; +import org.apache.qpid.server.security.auth.UsernamePrincipal; + +/** + * Implementation of a group manager whose implementation is backed by a flat group file. + *

+ * This plugin is configured in the following manner: + *

+ *
+ * <file-group-manager>
+ *    <attributes>
+ *       <attribute>
+ *            <name>groupFile</name>
+ *            <value>${conf}/groups</value>
+ *        </attribute>
+ *    </attributes>
+ * </file-group-manager>
+ * 
+ */ +public class FileGroupManager implements GroupManager +{ + private static final Logger LOGGER = Logger.getLogger(FileGroupManager.class); + + public static final GroupManagerPluginFactory FACTORY = new GroupManagerPluginFactory() + { + public FileGroupManager newInstance(final ConfigurationPlugin config) throws ConfigurationException + { + final FileGroupManagerConfiguration configuration = + config == null + ? null + : (FileGroupManagerConfiguration) config.getConfiguration(FileGroupManagerConfiguration.class.getName()); + + // If there is no configuration for this plugin then don't load it. + if (configuration == null) + { + LOGGER.info("No file-group-manager configuration found for FileGroupManager"); + return null; + } + + final FileGroupManager fgm = new FileGroupManager(); + fgm.configure(configuration); + return fgm; + } + + public Class getPluginClass() + { + return FileGroupManager.class; + } + + public String getPluginName() + { + return FileGroupManager.class.getName(); + } + }; + + private FileGroupDatabase _groupDatabase; + + public static class FileGroupManagerConfiguration extends ConfigurationPlugin { + + public static final ConfigurationPluginFactory FACTORY = new ConfigurationPluginFactory() + { + public List getParentPaths() + { + return Arrays.asList("security.file-group-manager"); + } + + public ConfigurationPlugin newInstance(final String path, final Configuration config) throws ConfigurationException + { + final ConfigurationPlugin instance = new FileGroupManagerConfiguration(); + + instance.setConfiguration(path, config); + return instance; + } + }; + + public String[] getElementsProcessed() + { + return new String[] {"attributes.attribute.name", + "attributes.attribute.value"}; + } + + public void validateConfiguration() throws ConfigurationException + { + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Map getAttributeMap() throws ConfigurationException + { + final List argumentNames = (List) getConfig().getList("attributes.attribute.name"); + final List argumentValues = (List) getConfig().getList("attributes.attribute.value"); + final Map attributes = new HashMap(argumentNames.size()); + + for (int i = 0; i < argumentNames.size(); i++) + { + final String argName = argumentNames.get(i); + final String argValue = argumentValues.get(i); + + attributes.put(argName, argValue); + } + + return Collections.unmodifiableMap(attributes); + } + } + + @Override + public void configure(ConfigurationPlugin config) + throws ConfigurationException + { + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("configuring file group plugin"); + } + + FileGroupManagerConfiguration fileGroupMangerConfig = (FileGroupManagerConfiguration) config; + Map attribMap = fileGroupMangerConfig.getAttributeMap(); + String groupFile = attribMap.get("groupFile"); + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Group file : " + groupFile); + } + + _groupDatabase = new FileGroupDatabase(); + try + { + _groupDatabase.setGroupFile(groupFile); + } + catch (IOException e) + { + throw new ConfigurationException("Unable to set group file " + groupFile, e); + } + } + + @Override + public Set getGroupPrincipalsForUser(String userId) + { + Set groups = _groupDatabase.getGroupsForUser(userId); + if (groups.isEmpty()) + { + return Collections.emptySet(); + } + else + { + Set principals = new HashSet(); + for (String groupName : groups) + { + principals.add(new GroupPrincipal(groupName)); + } + return principals; + } + } + + @Override + public Set getUserPrincipalsForGroup(String group) + { + Set users = _groupDatabase.getUsersInGroup(group); + if (users.isEmpty()) + { + return Collections.emptySet(); + } + else + { + Set principals = new HashSet(); + for (String user : users) + { + principals.add(new UsernamePrincipal(user)); + } + return principals; + } + } + + @Override + public Set getGroupPrincipals() + { + Set groups = _groupDatabase.getAllGroups(); + if (groups.isEmpty()) + { + return Collections.emptySet(); + } + else + { + Set principals = new HashSet(); + for (String groupName : groups) + { + principals.add(new GroupPrincipal(groupName)); + } + return principals; + } + } + + @Override + public void createGroup(String group) + { + _groupDatabase.createGroup(group); + } + + @Override + public void removeGroup(String group) + { + _groupDatabase.removeGroup(group); + } + + @Override + public void addUserToGroup(String user, String group) + { + _groupDatabase.addUserToGroup(user, group); + } + + @Override + public void removeUserFromGroup(String user, String group) + { + _groupDatabase.removeUserFromGroup(user, group); + + } + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupDatabase.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupDatabase.java new file mode 100644 index 0000000000..98c12782d8 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupDatabase.java @@ -0,0 +1,34 @@ +/* + * 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.group; + +import java.util.Set; + +public interface GroupDatabase +{ + Set getAllGroups(); + Set getUsersInGroup(String group); + + void addUserToGroup(String user, String group); + void removeUserFromGroup(String user, String group); + Set getGroupsForUser(String user); + void createGroup(String group); + void removeGroup(String group); +} 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 new file mode 100644 index 0000000000..30510eaad5 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupManager.java @@ -0,0 +1,42 @@ +/* + * 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.group; + +import java.security.Principal; +import java.util.Set; + +import org.apache.qpid.server.plugins.Plugin; + +public interface GroupManager extends Plugin +{ + Set getGroupPrincipalsForUser(String user); + + Set getGroupPrincipals(); + + Set getUserPrincipalsForGroup(String group); + + void createGroup(String group); + + void removeGroup(String group); + + void addUserToGroup(String user, String group); + + void removeUserFromGroup(String user, String group); +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupManagerPluginFactory.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupManagerPluginFactory.java new file mode 100644 index 0000000000..dc5ab33668 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupManagerPluginFactory.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.group; + +import org.apache.qpid.server.plugins.PluginFactory; + +public interface GroupManagerPluginFactory extends PluginFactory +{ + +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupPrincipal.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupPrincipal.java new file mode 100644 index 0000000000..ccb446b719 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupPrincipal.java @@ -0,0 +1,99 @@ +/* + * + * 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.group; + +import java.security.Principal; +import java.security.acl.Group; +import java.util.Enumeration; + +/** + * Immutable representation of a user group. In Qpid, groups do not know + * about their membership, and therefore the {@link #addMember(Principal)} + * methods etc throw {@link UnsupportedOperationException}. + * + */ +public class GroupPrincipal implements Group +{ + /** Name of the group */ + private final String _groupName; + + public GroupPrincipal(final String groupName) + { + _groupName = groupName; + } + + public String getName() + { + return _groupName; + } + + public boolean addMember(Principal user) + { + throw new UnsupportedOperationException("Not supported"); + } + + public boolean removeMember(Principal user) + { + throw new UnsupportedOperationException("Not supported"); + } + + public boolean isMember(Principal member) + { + throw new UnsupportedOperationException("Not supported"); + } + + public Enumeration members() + { + throw new UnsupportedOperationException("Not supported"); + } + + /** + * @see java.lang.Object#hashCode() + */ + public int hashCode() + { + final int prime = 37; + return prime * _groupName.hashCode(); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else + { + if (obj instanceof GroupPrincipal) + { + GroupPrincipal other = (GroupPrincipal) obj; + return _groupName.equals(other._groupName); + } + else + { + return false; + } + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupPrincipalAccessor.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupPrincipalAccessor.java new file mode 100644 index 0000000000..9ab8ee586c --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/group/GroupPrincipalAccessor.java @@ -0,0 +1,51 @@ +/* + * 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.group; + +import java.security.Principal; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + + +public class GroupPrincipalAccessor +{ + private final List _groupManagerList; + + public GroupPrincipalAccessor(List groupManagerList) + { + _groupManagerList = groupManagerList; + } + + public Set getGroupPrincipals(String username) + { + Set principals = new HashSet(); + for (GroupManager groupManager : _groupManagerList) + { + Set groups = groupManager.getGroupPrincipalsForUser(username); + if (groups != null) + { + principals.addAll(groups); + } + } + + return Collections.unmodifiableSet(principals); + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java index f352bbdd2c..f8b8d14abf 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/state/AMQStateManager.java @@ -34,6 +34,7 @@ import org.apache.qpid.protocol.AMQMethodListener; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; @@ -157,13 +158,9 @@ public class AMQStateManager implements AMQMethodListener return _protocolSession; } - /** - * Get the AuthenticationManager associated with the ProtocolSession of the AMQStateManager - * - * @return the AuthenticationManager - */ - public AuthenticationManager getAuthenticationManager() + + public SubjectCreator getSubjectCreator() { - return getApplicationRegistry().getAuthenticationManager(getProtocolSession().getLocalAddress()); + return getApplicationRegistry().getSubjectCreator(getProtocolSession().getLocalAddress()); } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java index c13f63b44d..615fce2909 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java @@ -37,9 +37,9 @@ import org.apache.qpid.server.configuration.BrokerConfig; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; -import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; import org.apache.qpid.server.subscription.Subscription_0_10; import org.apache.qpid.server.virtualhost.State; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -57,25 +57,25 @@ public class ServerConnectionDelegate extends ServerDelegate private final IApplicationRegistry _appRegistry; private int _maxNoOfChannels; private Map _clientProperties; - private final AuthenticationManager _authManager; + private final SubjectCreator _subjectCreator; - public ServerConnectionDelegate(IApplicationRegistry appRegistry, String localFQDN, AuthenticationManager authManager) + public ServerConnectionDelegate(IApplicationRegistry appRegistry, String localFQDN, SubjectCreator subjectCreator) { - this(createConnectionProperties(appRegistry.getBrokerConfig()), Collections.singletonList((Object)"en_US"), appRegistry, localFQDN, authManager); + this(createConnectionProperties(appRegistry.getBrokerConfig()), Collections.singletonList((Object)"en_US"), appRegistry, localFQDN, subjectCreator); } private ServerConnectionDelegate(Map properties, List locales, IApplicationRegistry appRegistry, String localFQDN, - AuthenticationManager authManager) + SubjectCreator subjectCreator) { - super(properties, parseToList(authManager.getMechanisms()), locales); + super(properties, parseToList(subjectCreator.getMechanisms()), locales); _appRegistry = appRegistry; _localFQDN = localFQDN; _maxNoOfChannels = appRegistry.getConfiguration().getMaxChannelCount(); - _authManager = authManager; + _subjectCreator = subjectCreator; } private static Map createConnectionProperties(final BrokerConfig brokerConfig) @@ -112,14 +112,14 @@ public class ServerConnectionDelegate extends ServerDelegate protected SaslServer createSaslServer(Connection conn, String mechanism) throws SaslException { - return _authManager.createSaslServer(mechanism, _localFQDN, ((ServerConnection) conn).getPeerPrincipal()); + return _subjectCreator.createSaslServer(mechanism, _localFQDN, ((ServerConnection) conn).getPeerPrincipal()); } protected void secure(final SaslServer ss, final Connection conn, final byte[] response) { final ServerConnection sconn = (ServerConnection) conn; - final AuthenticationResult authResult = _authManager.authenticate(ss, response); + final SubjectAuthenticationResult authResult = _subjectCreator.authenticate(ss, response); if (AuthenticationStatus.SUCCESS.equals(authResult.getStatus())) { diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/AbstractManagementActorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/AbstractManagementActorTest.java new file mode 100644 index 0000000000..bf38bb64bf --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/AbstractManagementActorTest.java @@ -0,0 +1,86 @@ +/* + * + * 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.logging.actors; + +import java.security.Principal; +import java.security.PrivilegedAction; +import java.util.Collections; + +import javax.security.auth.Subject; + +import org.apache.qpid.server.logging.NullRootMessageLogger; +import org.apache.qpid.server.security.auth.TestPrincipalUtils; +import org.apache.qpid.test.utils.QpidTestCase; + +public class AbstractManagementActorTest extends QpidTestCase +{ + private AbstractManagementActor _logActor; + + @Override + public void setUp() + { + _logActor = new AbstractManagementActor(new NullRootMessageLogger(), AbstractManagementActor.UNKNOWN_PRINCIPAL) + { + @Override + public String getLogMessage() + { + return null; + } + }; + } + + public void testGetPrincipalName() + { + Subject subject = TestPrincipalUtils.createTestSubject("guest"); + + final String principalName = Subject.doAs(subject, + new PrivilegedAction() + { + public String run() + { + return _logActor.getPrincipalName(); + } + }); + + assertEquals("guest", principalName); + } + + public void testGetPrincipalNameUsingSubjectWithoutAuthenticatedPrincipal() + { + Subject subject = new Subject(true, Collections.emptySet(), Collections.emptySet(), Collections.emptySet()); + + final String principalName = Subject.doAs(subject, + new PrivilegedAction() + { + public String run() + { + return _logActor.getPrincipalName(); + } + }); + + assertEquals(AbstractManagementActor.UNKNOWN_PRINCIPAL, principalName); + } + + public void testGetPrincipalWithoutSubject() + { + assertEquals(AbstractManagementActor.UNKNOWN_PRINCIPAL, _logActor.getPrincipalName()); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/HttpManagementActorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/HttpManagementActorTest.java new file mode 100644 index 0000000000..b40405e1f4 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/HttpManagementActorTest.java @@ -0,0 +1,94 @@ +/* + * + * 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.logging.actors; + +import javax.security.auth.Subject; + +import org.apache.qpid.server.security.auth.TestPrincipalUtils; + +import java.security.PrivilegedAction; +import java.util.List; + +public class HttpManagementActorTest extends BaseActorTestCase +{ + private static final String IP = "127.0.0.1"; + private static final int PORT = 1; + private static final String SUFFIX = "(" + IP + ":" + PORT + ")] "; + + @Override + public void createBroker() throws Exception + { + super.createBroker(); + _amqpActor = new HttpManagementActor(_rootLogger, IP, PORT); + } + + public void testSubjectPrincipalNameAppearance() + { + Subject subject = TestPrincipalUtils.createTestSubject("guest"); + + final String message = Subject.doAs(subject, new PrivilegedAction() + { + public String run() + { + return sendTestLogMessage(_amqpActor); + } + }); + + assertNotNull("Test log message is not created!", message); + + List logs = _rawLogger.getLogMessages(); + assertEquals("Message log size not as expected.", 1, logs.size()); + + String logMessage = logs.get(0).toString(); + assertTrue("Message was not found in log message", logMessage.contains(message)); + assertTrue("Message does not contain expected value: " + logMessage, logMessage.contains("[mng:guest" + SUFFIX)); + } + + /** It's necessary to test successive calls because HttpManagementActor caches + * its log message based on principal name */ + public void testGetLogMessageCaching() + { + assertLogMessageWithoutPrincipal(); + assertLogMessageWithPrincipal("my_principal"); + assertLogMessageWithPrincipal("my_principal2"); + assertLogMessageWithoutPrincipal(); + } + + private void assertLogMessageWithoutPrincipal() + { + String message = _amqpActor.getLogMessage(); + assertEquals("Unexpected log message", "[mng:" + AbstractManagementActor.UNKNOWN_PRINCIPAL + SUFFIX, message); + } + + private void assertLogMessageWithPrincipal(String principalName) + { + Subject subject = TestPrincipalUtils.createTestSubject(principalName); + final String message = Subject.doAs(subject, new PrivilegedAction() + { + public String run() + { + return _amqpActor.getLogMessage(); + } + }); + + assertEquals("Unexpected log message", "[mng:" + principalName + SUFFIX, message); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java index cb866245f0..20cc321aab 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java @@ -20,10 +20,11 @@ */ package org.apache.qpid.server.logging.actors; -import javax.management.remote.JMXPrincipal; import javax.security.auth.Subject; + +import org.apache.qpid.server.security.auth.TestPrincipalUtils; + import java.security.PrivilegedAction; -import java.util.Collections; import java.util.List; public class ManagementActorTest extends BaseActorTestCase @@ -94,8 +95,7 @@ public class ManagementActorTest extends BaseActorTestCase */ public void testSubjectPrincipalNameAppearance() { - Subject subject = new Subject(true, Collections.singleton(new JMXPrincipal("guest")), Collections.EMPTY_SET, - Collections.EMPTY_SET); + Subject subject = TestPrincipalUtils.createTestSubject("guest"); final String message = Subject.doAs(subject, new PrivilegedAction() { @@ -172,9 +172,7 @@ public class ManagementActorTest extends BaseActorTestCase private void assertLogMessageInRMIThreadWithPrincipal(String threadName, String principalName) { Thread.currentThread().setName(threadName); - Subject subject = new Subject(true, Collections.singleton(new JMXPrincipal(principalName)), Collections.EMPTY_SET, - Collections.EMPTY_SET); - + Subject subject = TestPrincipalUtils.createTestSubject(principalName); final String message = Subject.doAs(subject, new PrivilegedAction() { public String run() diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/model/UUIDGeneratorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/model/UUIDGeneratorTest.java index 643132d371..c686a24e99 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/model/UUIDGeneratorTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/model/UUIDGeneratorTest.java @@ -70,8 +70,12 @@ public class UUIDGeneratorTest extends QpidTestCase idSet.add(id6); UUID id7 = UUIDGenerator.generateVhostAliasUUID(value, value); idSet.add(id7); + UUID id8 = UUIDGenerator.generateGroupUUID(value, value); + idSet.add(id8); + UUID id9 = UUIDGenerator.generateGroupMemberUUID(value, value, value); + idSet.add(id9); - assertEquals("The produced UUIDs were not all unique", 7, idSet.size()); + assertEquals("The produced UUIDs were not all unique", 9, idSet.size()); } public void testQueueIdGeneration() throws Exception diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java index 96c67941f9..0016e31236 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java @@ -31,7 +31,8 @@ import org.apache.qpid.server.message.MessageContentSource; import org.apache.qpid.server.output.ProtocolOutputConverter; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; +import org.apache.qpid.server.security.auth.UsernamePrincipal; import org.apache.qpid.server.subscription.ClientDeliveryMethod; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.subscription.SubscriptionImpl; @@ -39,6 +40,8 @@ import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.TestNetworkConnection; import javax.security.auth.Subject; + +import java.security.Principal; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -61,13 +64,22 @@ public class InternalTestProtocolSession extends AMQProtocolEngine implements Pr _channelDelivers = new HashMap>>(); - // Need to authenticate session for it to be representative testing. - setAuthorizedSubject(new Subject(true, Collections.singleton(new UsernamePrincipal("InternalTestProtocolSession")), - Collections.EMPTY_SET, Collections.EMPTY_SET)); - + setTestAuthorizedSubject(); setVirtualHost(virtualHost); } + private void setTestAuthorizedSubject() + { + Principal principal = new AuthenticatedPrincipal(new UsernamePrincipal("InternalTestProtocolSession")); + Subject authorizedSubject = new Subject( + true, + Collections.singleton(principal), + Collections.emptySet(), + Collections.emptySet()); + + setAuthorizedSubject(authorizedSubject); + } + public ProtocolOutputConverter getProtocolOutputConverter() { return this; diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/SubjectCreatorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/SubjectCreatorTest.java new file mode 100644 index 0000000000..b1bc9bea68 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/SubjectCreatorTest.java @@ -0,0 +1,138 @@ +/* + * 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; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.security.Principal; +import java.util.Arrays; +import java.util.HashSet; + +import javax.security.auth.Subject; +import javax.security.sasl.SaslServer; + +import junit.framework.TestCase; + +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.AuthenticationManager; +import org.apache.qpid.server.security.group.GroupPrincipalAccessor; + +public class SubjectCreatorTest extends TestCase +{ + private static final String USERNAME = "username"; + private static final String PASSWORD = "password"; + + private AuthenticationManager _authenticationManager = mock(AuthenticationManager.class); + private GroupPrincipalAccessor _groupPrincipalAccessor = mock(GroupPrincipalAccessor.class); + private SubjectCreator _subjectCreator = new SubjectCreator(_authenticationManager, _groupPrincipalAccessor); + + private Principal _userPrincipal = mock(Principal.class); + private Principal _group1 = mock(Principal.class); + private Principal _group2 = mock(Principal.class); + + private AuthenticationResult _authenticationResult; + private SaslServer _testSaslServer = mock(SaslServer.class); + private byte[] _saslResponseBytes = PASSWORD.getBytes(); + + @Override + public void setUp() + { + _authenticationResult = new AuthenticationResult(_userPrincipal); + when(_authenticationManager.authenticate(USERNAME, PASSWORD)).thenReturn(_authenticationResult); + + when(_groupPrincipalAccessor.getGroupPrincipals(USERNAME)) + .thenReturn(new HashSet(Arrays.asList(_group1, _group2))); + } + + public void testAuthenticateUsernameAndPasswordReturnsSubjectWithUserAndGroupPrincipals() + { + final SubjectAuthenticationResult actualResult = _subjectCreator.authenticate(USERNAME, PASSWORD); + + assertEquals(AuthenticationStatus.SUCCESS, actualResult.getStatus()); + + final Subject actualSubject = actualResult.getSubject(); + + assertEquals("Should contain one user principal and two groups ", 3, actualSubject.getPrincipals().size()); + + assertTrue(actualSubject.getPrincipals().contains(new AuthenticatedPrincipal(_userPrincipal))); + assertTrue(actualSubject.getPrincipals().contains(_group1)); + assertTrue(actualSubject.getPrincipals().contains(_group2)); + + assertTrue(actualSubject.isReadOnly()); + } + + public void testSaslAuthenticationSuccessReturnsSubjectWithUserAndGroupPrincipals() throws Exception + { + when(_authenticationManager.authenticate(_testSaslServer, _saslResponseBytes)).thenReturn(_authenticationResult); + when(_testSaslServer.isComplete()).thenReturn(true); + when(_testSaslServer.getAuthorizationID()).thenReturn(USERNAME); + + SubjectAuthenticationResult result = _subjectCreator.authenticate(_testSaslServer, _saslResponseBytes); + + final Subject actualSubject = result.getSubject(); + assertEquals("Should contain one user principal and two groups ", 3, actualSubject.getPrincipals().size()); + + assertTrue(actualSubject.getPrincipals().contains(new AuthenticatedPrincipal(_userPrincipal))); + assertTrue(actualSubject.getPrincipals().contains(_group1)); + assertTrue(actualSubject.getPrincipals().contains(_group2)); + + assertTrue(actualSubject.isReadOnly()); + } + + public void testAuthenticateUnsuccessfulWithUsernameReturnsNullSubjectAndCorrectStatus() + { + testUnsuccessfulAuthentication(AuthenticationResult.AuthenticationStatus.CONTINUE); + testUnsuccessfulAuthentication(AuthenticationResult.AuthenticationStatus.ERROR); + } + + private void testUnsuccessfulAuthentication(AuthenticationStatus expectedStatus) + { + AuthenticationResult failedAuthenticationResult = new AuthenticationResult(expectedStatus); + + when(_authenticationManager.authenticate(USERNAME, PASSWORD)).thenReturn(failedAuthenticationResult); + + SubjectAuthenticationResult subjectAuthenticationResult = _subjectCreator.authenticate(USERNAME, PASSWORD); + + assertSame(expectedStatus, subjectAuthenticationResult.getStatus()); + assertNull(subjectAuthenticationResult.getSubject()); + } + + public void testAuthenticateUnsuccessfulWithSaslServerReturnsNullSubjectAndCorrectStatus() + { + testUnsuccessfulAuthenticationWithSaslServer(AuthenticationResult.AuthenticationStatus.CONTINUE); + testUnsuccessfulAuthenticationWithSaslServer(AuthenticationResult.AuthenticationStatus.ERROR); + } + + private void testUnsuccessfulAuthenticationWithSaslServer(AuthenticationStatus expectedStatus) + { + AuthenticationResult failedAuthenticationResult = new AuthenticationResult(expectedStatus); + + when(_authenticationManager.authenticate(_testSaslServer, _saslResponseBytes)).thenReturn(failedAuthenticationResult); + when(_testSaslServer.isComplete()).thenReturn(false); + + SubjectAuthenticationResult subjectAuthenticationResult = _subjectCreator.authenticate(_testSaslServer, _saslResponseBytes); + + assertSame(expectedStatus, subjectAuthenticationResult.getStatus()); + assertNull(subjectAuthenticationResult.getSubject()); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/AuthenticatedPrincipalTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/AuthenticatedPrincipalTest.java new file mode 100644 index 0000000000..cd5791952f --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/AuthenticatedPrincipalTest.java @@ -0,0 +1,147 @@ +/* + * 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; + +import java.security.Principal; + +import javax.security.auth.Subject; + +import org.apache.qpid.server.security.auth.UsernamePrincipal; + +import junit.framework.TestCase; + +public class AuthenticatedPrincipalTest extends TestCase +{ + + private AuthenticatedPrincipal _authenticatedPrincipal = new AuthenticatedPrincipal(new UsernamePrincipal("name")); + + public void testGetAuthenticatedPrincipalFromSubject() + { + final Subject subject = createSubjectContainingAuthenticatedPrincipal(); + final AuthenticatedPrincipal actual = AuthenticatedPrincipal.getAuthenticatedPrincipalFromSubject(subject); + assertSame(_authenticatedPrincipal, actual); + } + + public void testAuthenticatedPrincipalNotInSubject() + { + try + { + AuthenticatedPrincipal.getAuthenticatedPrincipalFromSubject(new Subject()); + fail("Exception not thrown"); + } + catch (IllegalArgumentException iae) + { + // PASS + } + } + + public void testGetOptionalAuthenticatedPrincipalFromSubject() + { + final Subject subject = createSubjectContainingAuthenticatedPrincipal(); + final AuthenticatedPrincipal actual = AuthenticatedPrincipal.getOptionalAuthenticatedPrincipalFromSubject(subject); + assertSame(_authenticatedPrincipal, actual); + } + + public void testGetOptionalAuthenticatedPrincipalFromSubjectReturnsNullIfMissing() + { + Subject subjectWithNoPrincipals = new Subject(); + assertNull(AuthenticatedPrincipal.getOptionalAuthenticatedPrincipalFromSubject(subjectWithNoPrincipals)); + + Subject subjectWithoutAuthenticatedPrincipal = new Subject(); + subjectWithoutAuthenticatedPrincipal.getPrincipals().add(new UsernamePrincipal("name1")); + assertNull("Should return null for a subject containing a principal that isn't an AuthenticatedPrincipal", + AuthenticatedPrincipal.getOptionalAuthenticatedPrincipalFromSubject(subjectWithoutAuthenticatedPrincipal)); + } + + public void testTooManyAuthenticatedPrincipalsInSubject() + { + final Subject subject = new Subject(); + subject.getPrincipals().add(new AuthenticatedPrincipal(new UsernamePrincipal("name1"))); + subject.getPrincipals().add(new AuthenticatedPrincipal(new UsernamePrincipal("name2"))); + + try + { + AuthenticatedPrincipal.getAuthenticatedPrincipalFromSubject(subject); + fail("Exception not thrown"); + } + catch (IllegalArgumentException iae) + { + // PASS + } + } + + private Subject createSubjectContainingAuthenticatedPrincipal() + { + final Principal other = new Principal() + { + public String getName() + { + return "otherprincipal"; + } + }; + + final Subject subject = new Subject(); + subject.getPrincipals().add(_authenticatedPrincipal); + subject.getPrincipals().add(other); + return subject; + } + + public void testEqualsAndHashcode() + { + AuthenticatedPrincipal user1principal1 = new AuthenticatedPrincipal(new UsernamePrincipal("user1")); + AuthenticatedPrincipal user1principal2 = new AuthenticatedPrincipal(new UsernamePrincipal("user1")); + + assertTrue(user1principal1.equals(user1principal1)); + assertTrue(user1principal1.equals(user1principal2)); + assertTrue(user1principal2.equals(user1principal1)); + + assertEquals(user1principal1.hashCode(), user1principal2.hashCode()); + } + + public void testEqualsAndHashcodeWithSameWrappedObject() + { + UsernamePrincipal wrappedPrincipal = new UsernamePrincipal("user1"); + AuthenticatedPrincipal user1principal1 = new AuthenticatedPrincipal(wrappedPrincipal); + AuthenticatedPrincipal user1principal2 = new AuthenticatedPrincipal(wrappedPrincipal); + + assertTrue(user1principal1.equals(user1principal1)); + assertTrue(user1principal1.equals(user1principal2)); + assertTrue(user1principal2.equals(user1principal1)); + + assertEquals(user1principal1.hashCode(), user1principal2.hashCode()); + } + + public void testEqualsWithDifferentUsernames() + { + AuthenticatedPrincipal user1principal1 = new AuthenticatedPrincipal(new UsernamePrincipal("user1")); + AuthenticatedPrincipal user1principal2 = new AuthenticatedPrincipal(new UsernamePrincipal("user2")); + + assertFalse(user1principal1.equals(user1principal2)); + assertFalse(user1principal2.equals(user1principal1)); + } + + public void testEqualsWithDisimilarObjects() + { + UsernamePrincipal wrappedPrincipal = new UsernamePrincipal("user1"); + AuthenticatedPrincipal authenticatedPrincipal = new AuthenticatedPrincipal(wrappedPrincipal); + + assertFalse(authenticatedPrincipal.equals(wrappedPrincipal)); + assertFalse(wrappedPrincipal.equals(authenticatedPrincipal)); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/AuthenticatedPrincipalTestHelper.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/AuthenticatedPrincipalTestHelper.java new file mode 100644 index 0000000000..e9d8d16fce --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/AuthenticatedPrincipalTestHelper.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.security.auth; + +import java.security.Principal; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import junit.framework.Assert; + +/** + * Helper class for testing that sets of principals contain {@link AuthenticatedPrincipal}'s that wrap + * expected {@link Principal}'s. + */ +public class AuthenticatedPrincipalTestHelper +{ + public static void assertOnlyContainsWrapped(Principal wrappedPrincipal, Set principals) + { + assertOnlyContainsWrappedAndSecondaryPrincipals(wrappedPrincipal, Collections.emptySet(), principals); + } + + + public static void assertOnlyContainsWrappedAndSecondaryPrincipals( + Principal expectedWrappedPrincipal, + Set expectedSecondaryPrincipals, + Set actualPrincipals) + { + Assert.assertEquals("Principal set should contain one principal " + "but the principal set is: " + actualPrincipals, + 1 + expectedSecondaryPrincipals.size(), + actualPrincipals.size()); + + Set expectedSet = new HashSet(expectedSecondaryPrincipals); + expectedSet.add(new AuthenticatedPrincipal(expectedWrappedPrincipal)); + + Assert.assertEquals(expectedSet, actualPrincipals); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/AuthenticationResultTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/AuthenticationResultTest.java new file mode 100644 index 0000000000..a023cbdbb2 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/AuthenticationResultTest.java @@ -0,0 +1,112 @@ +/* + * 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; + +import static org.apache.qpid.server.security.auth.AuthenticatedPrincipalTestHelper.assertOnlyContainsWrapped; +import static org.apache.qpid.server.security.auth.AuthenticatedPrincipalTestHelper.assertOnlyContainsWrappedAndSecondaryPrincipals; +import static org.mockito.Mockito.mock; + +import java.security.Principal; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import junit.framework.TestCase; + +public class AuthenticationResultTest extends TestCase +{ + public void testConstructWithAuthenticationStatusContinue() + { + AuthenticationResult authenticationResult = new AuthenticationResult(AuthenticationResult.AuthenticationStatus.CONTINUE); + assertSame(AuthenticationResult.AuthenticationStatus.CONTINUE, authenticationResult.getStatus()); + assertTrue(authenticationResult.getPrincipals().isEmpty()); + } + + public void testConstructWithAuthenticationStatusError() + { + AuthenticationResult authenticationResult = new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR); + assertSame(AuthenticationResult.AuthenticationStatus.ERROR, authenticationResult.getStatus()); + assertTrue(authenticationResult.getPrincipals().isEmpty()); + } + + public void testConstructWithAuthenticationStatusSuccessThrowsException() + { + try + { + new AuthenticationResult(AuthenticationResult.AuthenticationStatus.SUCCESS); + fail("Exception not thrown"); + } + catch(IllegalArgumentException e) + { + // PASS + } + } + + public void testConstructWithPrincipal() + { + Principal mainPrincipal = mock(Principal.class); + AuthenticationResult authenticationResult = new AuthenticationResult(mainPrincipal); + + assertOnlyContainsWrapped(mainPrincipal, authenticationResult.getPrincipals()); + assertSame(AuthenticationResult.AuthenticationStatus.SUCCESS, authenticationResult.getStatus()); + } + + public void testConstructWithNullPrincipalThrowsException() + { + try + { + new AuthenticationResult((Principal)null); + fail("Exception not thrown"); + } + catch(IllegalArgumentException e) + { + // pass + } + } + + public void testConstructWithSetOfPrincipals() + { + Principal mainPrincipal = mock(Principal.class); + Principal secondaryPrincipal = mock(Principal.class); + Set secondaryPrincipals = Collections.singleton(secondaryPrincipal); + + AuthenticationResult authenticationResult = new AuthenticationResult(mainPrincipal, secondaryPrincipals); + + assertOnlyContainsWrappedAndSecondaryPrincipals(mainPrincipal, secondaryPrincipals, authenticationResult.getPrincipals()); + assertSame(AuthenticationResult.AuthenticationStatus.SUCCESS, authenticationResult.getStatus()); + } + + public void testConstructWithSetOfPrincipalsDeDuplicatesMainPrincipal() + { + Principal mainPrincipal = mock(Principal.class); + Principal secondaryPrincipal = mock(Principal.class); + + Set secondaryPrincipalsContainingDuplicateOfMainPrincipal = new HashSet( + Arrays.asList(secondaryPrincipal, mainPrincipal)); + Set deDuplicatedSecondaryPrincipals = Collections.singleton(secondaryPrincipal); + + AuthenticationResult authenticationResult = new AuthenticationResult( + mainPrincipal, secondaryPrincipalsContainingDuplicateOfMainPrincipal); + + assertOnlyContainsWrappedAndSecondaryPrincipals(mainPrincipal, deDuplicatedSecondaryPrincipals, authenticationResult.getPrincipals()); + + assertSame(AuthenticationResult.AuthenticationStatus.SUCCESS, authenticationResult.getStatus()); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/TestPrincipalUtils.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/TestPrincipalUtils.java new file mode 100644 index 0000000000..ea6b40e3de --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/TestPrincipalUtils.java @@ -0,0 +1,49 @@ +/* + * + * 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; + +import javax.security.auth.Subject; + +import org.apache.qpid.server.security.group.GroupPrincipal; + +import java.security.Principal; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class TestPrincipalUtils +{ + /** + * Creates a test subject, with exactly one {@link AuthenticatedPrincipal} and zero or more GroupPrincipals. + */ + public static Subject createTestSubject(final String username, final String... groups) + { + final Set principals = new HashSet(1 + groups.length); + principals.add(new AuthenticatedPrincipal(username)); + for (String group : groups) + { + principals.add(new GroupPrincipal(group)); + } + + return new Subject(true, principals, Collections.EMPTY_SET, Collections.EMPTY_SET); + } + +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/UsernamePrincipalTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/UsernamePrincipalTest.java new file mode 100644 index 0000000000..5e025d3ca8 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/UsernamePrincipalTest.java @@ -0,0 +1,70 @@ +/* + * + * 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; + +import junit.framework.TestCase; + +/** + * Tests the UsernamePrincipal. + * + */ +public class UsernamePrincipalTest extends TestCase +{ + public void testEqualitySameObject() + { + final UsernamePrincipal principal = new UsernamePrincipal("string"); + assertTrue(principal.equals(principal)); + } + + public void testEqualitySameName() + { + final String string = "string"; + final UsernamePrincipal principal1 = new UsernamePrincipal(string); + final UsernamePrincipal principal2 = new UsernamePrincipal(string); + assertTrue(principal1.equals(principal2)); + } + + public void testEqualityEqualName() + { + final UsernamePrincipal principal1 = new UsernamePrincipal(new String("string")); + final UsernamePrincipal principal2 = new UsernamePrincipal(new String("string")); + assertTrue(principal1.equals(principal2)); + } + + public void testInequalityDifferentUserPrincipals() + { + UsernamePrincipal principal1 = new UsernamePrincipal("string1"); + UsernamePrincipal principal2 = new UsernamePrincipal("string2"); + assertFalse(principal1.equals(principal2)); + } + + public void testInequalityNonUserPrincipal() + { + UsernamePrincipal principal = new UsernamePrincipal("string"); + assertFalse(principal.equals(new String("string"))); + } + + public void testInequalityNull() + { + UsernamePrincipal principal = new UsernamePrincipal("string"); + assertFalse(principal.equals(null)); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java index 33740af1e7..7b244e219e 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/Base64MD5PasswordFilePrincipalDatabaseTest.java @@ -23,7 +23,7 @@ package org.apache.qpid.server.security.auth.database; import junit.framework.TestCase; import org.apache.commons.codec.binary.Base64; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.UsernamePrincipal; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.login.AccountNotFoundException; diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabaseTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabaseTest.java index b8601f0e5c..8e62324f7d 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabaseTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PlainPasswordFilePrincipalDatabaseTest.java @@ -22,7 +22,7 @@ package org.apache.qpid.server.security.auth.database; import junit.framework.TestCase; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.UsernamePrincipal; import javax.security.auth.login.AccountNotFoundException; import java.io.BufferedReader; diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManagerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManagerTest.java index 9dcd22c088..a36e97199f 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManagerTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/AnonymousAuthenticationManagerTest.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.security.auth.manager; +import static org.apache.qpid.server.security.auth.AuthenticatedPrincipalTestHelper.assertOnlyContainsWrapped; + import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import org.apache.commons.configuration.CompositeConfiguration; @@ -102,7 +104,8 @@ public class AnonymousAuthenticationManagerTest extends InternalBrokerBaseCase assertEquals("Expected authentication to be successful", AuthenticationResult.AuthenticationStatus.SUCCESS, result.getStatus()); - assertNotNull("Subject should not be null", result.getSubject()); + + assertOnlyContainsWrapped(AnonymousAuthenticationManager.ANONYMOUS_PRINCIPAL, result.getPrincipals()); } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerRegistryTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerRegistryTest.java index efb8df3a38..9b7131c71a 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerRegistryTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/AuthenticationManagerRegistryTest.java @@ -35,6 +35,8 @@ import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.plugins.Plugin; import org.apache.qpid.server.plugins.PluginManager; import org.apache.qpid.server.security.SecurityManager.SecurityConfiguration; +import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.security.group.GroupPrincipalAccessor; import org.mockito.Mockito; import junit.framework.TestCase; @@ -49,6 +51,8 @@ public class AuthenticationManagerRegistryTest extends TestCase private List _allCreatedAuthManagers = new ArrayList(); + private GroupPrincipalAccessor _groupPrincipalAccessor = mock(GroupPrincipalAccessor.class);; + @Override protected void setUp() throws Exception { @@ -76,7 +80,7 @@ public class AuthenticationManagerRegistryTest extends TestCase when(_pluginManager.getAuthenticationManagerPlugins()).thenReturn(EMPTY_PLUGINMAP); try { - new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager); + new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager, _groupPrincipalAccessor); fail("Exception not thrown"); } catch (ConfigurationException ce) @@ -97,7 +101,7 @@ public class AuthenticationManagerRegistryTest extends TestCase try { - new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager); + new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager, _groupPrincipalAccessor); fail("Exception not thrown"); } catch (ConfigurationException ce) @@ -120,7 +124,7 @@ public class AuthenticationManagerRegistryTest extends TestCase try { - new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager); + new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager, _groupPrincipalAccessor); fail("Exception not thrown"); } catch (ConfigurationException ce) @@ -145,7 +149,7 @@ public class AuthenticationManagerRegistryTest extends TestCase try { - new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager); + new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager, _groupPrincipalAccessor); fail("Exception not thrown"); } catch (ConfigurationException ce) @@ -170,7 +174,7 @@ public class AuthenticationManagerRegistryTest extends TestCase try { - new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager); + new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager, _groupPrincipalAccessor); fail("Exception not thrown"); } catch (ConfigurationException ce) @@ -187,10 +191,10 @@ public class AuthenticationManagerRegistryTest extends TestCase when(_pluginManager.getAuthenticationManagerPlugins()).thenReturn(pluginMap); - AuthenticationManagerRegistry registry = new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager); + AuthenticationManagerRegistry registry = new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager, _groupPrincipalAccessor); - AuthenticationManager authenticationManager = registry.getAuthenticationManager(new InetSocketAddress(1234)); - assertEquals("TestAuthenticationManager1", authenticationManager.getMechanisms()); + SubjectCreator subjectCreator = registry.getSubjectCreator(new InetSocketAddress(1234)); + assertEquals("TestAuthenticationManager1", subjectCreator.getMechanisms()); registry.close(); } @@ -202,10 +206,10 @@ public class AuthenticationManagerRegistryTest extends TestCase when(_pluginManager.getAuthenticationManagerPlugins()).thenReturn(pluginMap); - AuthenticationManagerRegistry registry = new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager); + AuthenticationManagerRegistry registry = new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager, _groupPrincipalAccessor); - AuthenticationManager authenticationManager = registry.getAuthenticationManager(mock(SocketAddress.class)); - assertEquals("TestAuthenticationManager1", authenticationManager.getMechanisms()); + SubjectCreator subjectCreator = registry.getSubjectCreator(mock(SocketAddress.class)); + assertEquals("TestAuthenticationManager1", subjectCreator.getMechanisms()); registry.close(); } @@ -225,13 +229,13 @@ public class AuthenticationManagerRegistryTest extends TestCase when(_serverConfiguration.getDefaultAuthenticationManager()).thenReturn(defaultAuthManger); when(_serverConfiguration.getPortAuthenticationMappings()).thenReturn(Collections.singletonMap(mappedPortNumber, mappedAuthManager)); - AuthenticationManagerRegistry registry = new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager); + AuthenticationManagerRegistry registry = new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager, _groupPrincipalAccessor); - AuthenticationManager authenticationManager1 = registry.getAuthenticationManager(new InetSocketAddress(unmappedPortNumber)); - assertEquals("TestAuthenticationManager1", authenticationManager1.getMechanisms()); + SubjectCreator subjectCreator = registry.getSubjectCreator(new InetSocketAddress(unmappedPortNumber)); + assertEquals("TestAuthenticationManager1", subjectCreator.getMechanisms()); - AuthenticationManager authenticationManager2 = registry.getAuthenticationManager(new InetSocketAddress(mappedPortNumber)); - assertEquals("TestAuthenticationManager2", authenticationManager2.getMechanisms()); + SubjectCreator subjectCreator2 = registry.getSubjectCreator(new InetSocketAddress(mappedPortNumber)); + assertEquals("TestAuthenticationManager2", subjectCreator2.getMechanisms()); registry.close(); } @@ -246,7 +250,7 @@ public class AuthenticationManagerRegistryTest extends TestCase when(_pluginManager.getAuthenticationManagerPlugins()).thenReturn(pluginMap); when(_serverConfiguration.getDefaultAuthenticationManager()).thenReturn(defaultAuthManger); - AuthenticationManagerRegistry registry = new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager); + AuthenticationManagerRegistry registry = new AuthenticationManagerRegistry(_serverConfiguration, _pluginManager, _groupPrincipalAccessor); registry.close(); } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerTest.java index c1a55ef2ad..4e0643e229 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/ExternalAuthenticationManagerTest.java @@ -18,6 +18,8 @@ */ package org.apache.qpid.server.security.auth.manager; +import static org.apache.qpid.server.security.auth.AuthenticatedPrincipalTestHelper.assertOnlyContainsWrapped; + import javax.security.auth.x500.X500Principal; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; @@ -25,6 +27,7 @@ import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; + import org.apache.qpid.server.security.auth.AuthenticationResult; import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase; import org.apache.qpid.server.util.InternalBrokerBaseCase; @@ -103,12 +106,12 @@ public class ExternalAuthenticationManagerTest extends InternalBrokerBaseCase assertEquals("Expected authentication to be successful", AuthenticationResult.AuthenticationStatus.SUCCESS, result.getStatus()); - assertEquals("Expected principal to be unchanged", - principal, - result.getSubject().getPrincipals().iterator().next()); + + assertOnlyContainsWrapped(principal, result.getPrincipals()); saslServer = _manager.createSaslServer("EXTERNAL", "example.example.com", null); result = _manager.authenticate(saslServer, new byte[0]); + assertNotNull(result); assertEquals("Expected authentication to be unsuccessful", AuthenticationResult.AuthenticationStatus.ERROR, diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManagerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManagerTest.java index 47c189e4fa..391eb4e665 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManagerTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManagerTest.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.security.auth.manager; +import static org.apache.qpid.server.security.auth.AuthenticatedPrincipalTestHelper.assertOnlyContainsWrapped; + import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; @@ -28,10 +30,9 @@ import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; import org.apache.qpid.server.security.auth.AuthenticationResult; import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase; -import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; +import org.apache.qpid.server.security.auth.UsernamePrincipal; import org.apache.qpid.server.util.InternalBrokerBaseCase; -import javax.security.auth.Subject; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import java.io.BufferedWriter; @@ -48,6 +49,7 @@ import java.security.Security; */ public class PrincipalDatabaseAuthenticationManagerTest extends InternalBrokerBaseCase { + private static final UsernamePrincipal PRINCIPAL = new UsernamePrincipal("guest"); private AuthenticationManager _manager = null; // Class under test private String TEST_USERNAME = "guest"; private String TEST_PASSWORD = "guest"; @@ -72,7 +74,7 @@ public class PrincipalDatabaseAuthenticationManagerTest extends InternalBrokerBa public void setUp() throws Exception { super.setUp(); - + final String passwdFilename = createPasswordFile().getCanonicalPath(); final ConfigurationPlugin config = getConfig(PlainPasswordFilePrincipalDatabase.class.getName(), "passwordFile", passwdFilename); @@ -121,7 +123,7 @@ public class PrincipalDatabaseAuthenticationManagerTest extends InternalBrokerBa { try { - _manager = PrincipalDatabaseAuthenticationManager.FACTORY.newInstance(getConfig(PlainPasswordFilePrincipalDatabase.class.getName(), "noMethod", "test")); + _manager = PrincipalDatabaseAuthenticationManager.FACTORY.newInstance(getConfig(PlainPasswordFilePrincipalDatabase.class.getName(), "noMethod", "test")); fail("Exception not thrown"); } catch (ConfigurationException ce) @@ -137,7 +139,7 @@ public class PrincipalDatabaseAuthenticationManagerTest extends InternalBrokerBa { try { - _manager = PrincipalDatabaseAuthenticationManager.FACTORY.newInstance(getConfig(PlainPasswordFilePrincipalDatabase.class.getName(), "passwordFile", "/not/found")); + _manager = PrincipalDatabaseAuthenticationManager.FACTORY.newInstance(getConfig(PlainPasswordFilePrincipalDatabase.class.getName(), "passwordFile", "/not/found")); fail("Exception not thrown"); } catch (ConfigurationException ce) @@ -157,7 +159,7 @@ public class PrincipalDatabaseAuthenticationManagerTest extends InternalBrokerBa // relies on those mechanisms attached to PropertiesPrincipalDatabaseManager assertEquals("AMQPLAIN PLAIN CRAM-MD5", _manager.getMechanisms()); - Provider qpidProvider = Security.getProvider(PrincipalDatabaseAuthenticationManager.PROVIDER_NAME); + Provider qpidProvider = Security.getProvider(AuthenticationManager.PROVIDER_NAME); assertNotNull(qpidProvider); } @@ -172,49 +174,51 @@ public class PrincipalDatabaseAuthenticationManagerTest extends InternalBrokerBa // Merely tests the creation of the mechanism. Mechanisms themselves are tested // by their own tests. } - + /** * Tests that the authenticate method correctly interprets an * authentication success. - * + * */ public void testSaslAuthenticationSuccess() throws Exception { + SaslServer testServer = createTestSaslServer(true, false); - + AuthenticationResult result = _manager.authenticate(testServer, "12345".getBytes()); - final Subject subject = result.getSubject(); - assertTrue(subject.getPrincipals().contains(new UsernamePrincipal("guest"))); + + assertOnlyContainsWrapped(PRINCIPAL, result.getPrincipals()); assertEquals(AuthenticationStatus.SUCCESS, result.getStatus()); } /** - * + * * Tests that the authenticate method correctly interprets an * authentication not complete. - * + * */ public void testSaslAuthenticationNotCompleted() throws Exception { SaslServer testServer = createTestSaslServer(false, false); - + AuthenticationResult result = _manager.authenticate(testServer, "12345".getBytes()); - assertNull(result.getSubject()); + assertEquals("Principals was not expected size", 0, result.getPrincipals().size()); + assertEquals(AuthenticationStatus.CONTINUE, result.getStatus()); } /** - * + * * Tests that the authenticate method correctly interprets an * authentication error. - * + * */ public void testSaslAuthenticationError() throws Exception { SaslServer testServer = createTestSaslServer(false, true); - + AuthenticationResult result = _manager.authenticate(testServer, "12345".getBytes()); - assertNull(result.getSubject()); + assertEquals("Principals was not expected size", 0, result.getPrincipals().size()); assertEquals(AuthenticationStatus.ERROR, result.getStatus()); } @@ -226,9 +230,7 @@ public class PrincipalDatabaseAuthenticationManagerTest extends InternalBrokerBa public void testNonSaslAuthenticationSuccess() throws Exception { AuthenticationResult result = _manager.authenticate("guest", "guest"); - final Subject subject = result.getSubject(); - assertFalse("Subject should not be set read-only", subject.isReadOnly()); - assertTrue(subject.getPrincipals().contains(new UsernamePrincipal("guest"))); + assertOnlyContainsWrapped(PRINCIPAL, result.getPrincipals()); assertEquals(AuthenticationStatus.SUCCESS, result.getStatus()); } @@ -240,23 +242,23 @@ public class PrincipalDatabaseAuthenticationManagerTest extends InternalBrokerBa public void testNonSaslAuthenticationNotCompleted() throws Exception { AuthenticationResult result = _manager.authenticate("guest", "wrongpassword"); - assertNull(result.getSubject()); + assertEquals("Principals was not expected size", 0, result.getPrincipals().size()); assertEquals(AuthenticationStatus.CONTINUE, result.getStatus()); } - + /** * Tests the ability to de-register the provider. */ public void testClose() throws Exception { assertEquals("AMQPLAIN PLAIN CRAM-MD5", _manager.getMechanisms()); - assertNotNull(Security.getProvider(PrincipalDatabaseAuthenticationManager.PROVIDER_NAME)); + assertNotNull(Security.getProvider(AuthenticationManager.PROVIDER_NAME)); _manager.close(); // Check provider has been removed. assertNull(_manager.getMechanisms()); - assertNull(Security.getProvider(PrincipalDatabaseAuthenticationManager.PROVIDER_NAME)); + assertNull(Security.getProvider(AuthenticationManager.PROVIDER_NAME)); _manager = null; } @@ -343,7 +345,7 @@ public class PrincipalDatabaseAuthenticationManagerTest extends InternalBrokerBa writer = new BufferedWriter(new FileWriter(testFile)); writer.write(TEST_USERNAME + ":" + TEST_PASSWORD); writer.newLine(); - + return testFile; } 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/rmi/RMIPasswordAuthenticatorTest.java index c0c55de92a..efdb286866 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/rmi/RMIPasswordAuthenticatorTest.java @@ -20,20 +20,21 @@ */ package org.apache.qpid.server.security.auth.rmi; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.net.InetSocketAddress; import java.security.Principal; + +import javax.security.auth.Subject; + import junit.framework.TestCase; -import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +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.manager.AuthenticationManager; - -import javax.management.remote.JMXPrincipal; -import javax.security.auth.Subject; -import javax.security.sasl.SaslException; -import javax.security.sasl.SaslServer; -import java.net.InetSocketAddress; -import java.util.Collections; +import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; /** * Tests the RMIPasswordAuthenticator and its collaboration with the AuthenticationManager. @@ -41,6 +42,7 @@ import java.util.Collections; */ public class RMIPasswordAuthenticatorTest extends TestCase { + private static final Subject SUBJECT = new Subject(); private final String USERNAME = "guest"; private final String PASSWORD = "guest"; private RMIPasswordAuthenticator _rmipa; @@ -54,23 +56,14 @@ public class RMIPasswordAuthenticatorTest extends TestCase } /** - * Tests a successful authentication. Ensures that a populated read-only subject it returned. + * Tests a successful authentication. Ensures that the expected subject is returned. */ public void testAuthenticationSuccess() { - final Subject expectedSubject = new Subject(true, - Collections.singleton(new JMXPrincipal(USERNAME)), - Collections.EMPTY_SET, - Collections.EMPTY_SET); - - _rmipa.setAuthenticationManager(createTestAuthenticationManager(true, null)); - + _rmipa.setSubjectCreator(createMockSubjectCreator(true, null)); Subject newSubject = _rmipa.authenticate(_credentials); - assertTrue("Subject must be readonly", newSubject.isReadOnly()); - assertTrue("Returned subject does not equal expected value", - newSubject.equals(expectedSubject)); - + assertSame("Subject must be unchanged", SUBJECT, newSubject); } /** @@ -78,7 +71,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase */ public void testUsernameOrPasswordInvalid() { - _rmipa.setAuthenticationManager(createTestAuthenticationManager(false, null)); + _rmipa.setSubjectCreator(createMockSubjectCreator(false, null)); try { @@ -99,7 +92,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase public void testAuthenticationFailure() { final Exception mockAuthException = new Exception("Mock Auth system failure"); - _rmipa.setAuthenticationManager(createTestAuthenticationManager(false, mockAuthException)); + _rmipa.setSubjectCreator(createMockSubjectCreator(false, mockAuthException)); try { @@ -118,7 +111,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase */ public void testNullAuthenticationManager() throws Exception { - _rmipa.setAuthenticationManager(null); + _rmipa.setSubjectCreator(null); try { _rmipa.authenticate(_credentials); @@ -209,55 +202,30 @@ public class RMIPasswordAuthenticatorTest extends TestCase } } - private AuthenticationManager createTestAuthenticationManager(final boolean successfulAuth, final Exception exception) + private SubjectCreator createMockSubjectCreator(final boolean successfulAuth, final Exception exception) { - return new AuthenticationManager() - { - public void configure(ConfigurationPlugin config) - { - throw new UnsupportedOperationException(); - } + SubjectCreator subjectCreator = mock(SubjectCreator.class); - public void initialise() - { - throw new UnsupportedOperationException(); - } + SubjectAuthenticationResult subjectAuthenticationResult; - public void close() - { - throw new UnsupportedOperationException(); - } + if (exception != null) { - public String getMechanisms() - { - throw new UnsupportedOperationException(); - } - - public SaslServer createSaslServer(String mechanism, String localFQDN, Principal externalPrincipal) throws SaslException - { - throw new UnsupportedOperationException(); - } + subjectAuthenticationResult = new SubjectAuthenticationResult( + new AuthenticationResult(AuthenticationStatus.ERROR, exception)); + } + else if (successfulAuth) + { - public AuthenticationResult authenticate(SaslServer server, byte[] response) - { - throw new UnsupportedOperationException(); - } + subjectAuthenticationResult = new SubjectAuthenticationResult( + new AuthenticationResult(mock(Principal.class)), SUBJECT); + } + else + { + subjectAuthenticationResult = new SubjectAuthenticationResult(new AuthenticationResult(AuthenticationStatus.CONTINUE)); + } - public AuthenticationResult authenticate(String username, String password) - { - if (exception != null) { - return new AuthenticationResult(AuthenticationStatus.ERROR, exception); - } - else if (successfulAuth) - { - return new AuthenticationResult(new Subject()); - } - else - { - return new AuthenticationResult(AuthenticationStatus.CONTINUE); - } - } + when(subjectCreator.authenticate(anyString(), anyString())).thenReturn(subjectAuthenticationResult); - }; + return subjectCreator; } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipalTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipalTest.java deleted file mode 100644 index 076b7c9248..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/GroupPrincipalTest.java +++ /dev/null @@ -1,86 +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.sasl; - -import junit.framework.TestCase; - -public class GroupPrincipalTest extends TestCase -{ - public void testGetName() - { - final GroupPrincipal principal = new GroupPrincipal("group"); - assertEquals("group", principal.getName()); - } - - public void testAddRejected() - { - final GroupPrincipal principal = new GroupPrincipal("group"); - final UsernamePrincipal user = new UsernamePrincipal("name"); - - try - { - principal.addMember(user); - fail("Exception not thrown"); - } - catch (UnsupportedOperationException uso) - { - // PASS - } - } - - public void testEqualitySameName() - { - final String string = "string"; - final GroupPrincipal principal1 = new GroupPrincipal(string); - final GroupPrincipal principal2 = new GroupPrincipal(string); - assertTrue(principal1.equals(principal2)); - } - - public void testEqualityEqualName() - { - final GroupPrincipal principal1 = new GroupPrincipal(new String("string")); - final GroupPrincipal principal2 = new GroupPrincipal(new String("string")); - assertTrue(principal1.equals(principal2)); - } - - public void testInequalityDifferentGroupPrincipals() - { - GroupPrincipal principal1 = new GroupPrincipal("string1"); - GroupPrincipal principal2 = new GroupPrincipal("string2"); - assertFalse(principal1.equals(principal2)); - } - - public void testInequalityNonGroupPrincipal() - { - GroupPrincipal principal = new GroupPrincipal("string"); - assertFalse(principal.equals(new UsernamePrincipal("string"))); - } - - public void testInequalityNull() - { - GroupPrincipal principal = new GroupPrincipal("string"); - assertFalse(principal.equals(null)); - } - - - - -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalUtils.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalUtils.java deleted file mode 100644 index 7ce03eaa79..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/TestPrincipalUtils.java +++ /dev/null @@ -1,48 +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.sasl; - -import javax.security.auth.Subject; -import java.security.Principal; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -public class TestPrincipalUtils -{ - - /** - * Creates a test subject, with exactly one UsernamePrincipal and zero or more GroupPrincipals. - */ - public static Subject createTestSubject(final String username, final String... groups) - { - final Set principals = new HashSet(1 + groups.length); - principals.add(new UsernamePrincipal(username)); - for (String group : groups) - { - principals.add(new GroupPrincipal(group)); - } - - final Subject subject = new Subject(true, principals, Collections.EMPTY_SET, Collections.EMPTY_SET); - return subject; - } - -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipalTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipalTest.java deleted file mode 100644 index 75bc76c688..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/UsernamePrincipalTest.java +++ /dev/null @@ -1,123 +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.sasl; - -import junit.framework.TestCase; - -import javax.security.auth.Subject; -import java.security.Principal; - -/** - * Tests the UsernamePrincipal. - * - */ -public class UsernamePrincipalTest extends TestCase -{ - public void testEqualitySameObject() - { - final UsernamePrincipal principal = new UsernamePrincipal("string"); - assertTrue(principal.equals(principal)); - } - - public void testEqualitySameName() - { - final String string = "string"; - final UsernamePrincipal principal1 = new UsernamePrincipal(string); - final UsernamePrincipal principal2 = new UsernamePrincipal(string); - assertTrue(principal1.equals(principal2)); - } - - public void testEqualityEqualName() - { - final UsernamePrincipal principal1 = new UsernamePrincipal(new String("string")); - final UsernamePrincipal principal2 = new UsernamePrincipal(new String("string")); - assertTrue(principal1.equals(principal2)); - } - - public void testInequalityDifferentUserPrincipals() - { - UsernamePrincipal principal1 = new UsernamePrincipal("string1"); - UsernamePrincipal principal2 = new UsernamePrincipal("string2"); - assertFalse(principal1.equals(principal2)); - } - - public void testInequalityNonUserPrincipal() - { - UsernamePrincipal principal = new UsernamePrincipal("string"); - assertFalse(principal.equals(new String("string"))); - } - - public void testInequalityNull() - { - UsernamePrincipal principal = new UsernamePrincipal("string"); - assertFalse(principal.equals(null)); - } - - public void testGetUsernamePrincipalFromSubject() - { - final UsernamePrincipal expected = new UsernamePrincipal("name"); - final Principal other = new Principal() - { - public String getName() - { - return "otherprincipal"; - } - }; - - final Subject subject = new Subject(); - subject.getPrincipals().add(expected); - subject.getPrincipals().add(other); - - final UsernamePrincipal actual = UsernamePrincipal.getUsernamePrincipalFromSubject(subject); - assertSame(expected, actual); - } - - public void testUsernamePrincipalNotInSubject() - { - try - { - UsernamePrincipal.getUsernamePrincipalFromSubject(new Subject()); - fail("Exception not thrown"); - } - catch (IllegalArgumentException iae) - { - // PASS - } - } - - public void testTooManyUsernamePrincipalInSubject() - { - final Subject subject = new Subject(); - subject.getPrincipals().add(new UsernamePrincipal("name1")); - subject.getPrincipals().add(new UsernamePrincipal("name2")); - try - { - - UsernamePrincipal.getUsernamePrincipalFromSubject(subject); - fail("Exception not thrown"); - } - catch (IllegalArgumentException iae) - { - // PASS - } - } - -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupDatabaseTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupDatabaseTest.java new file mode 100644 index 0000000000..b020c1655a --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupDatabaseTest.java @@ -0,0 +1,456 @@ +/* + * 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.group; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Properties; +import java.util.Set; + +import org.apache.qpid.server.security.group.FileGroupDatabase; + +import junit.framework.TestCase; + +public class FileGroupDatabaseTest extends TestCase +{ + private static final String USER1 = "user1"; + private static final String USER2 = "user2"; + private static final String USER3 = "user3"; + + private static final String MY_GROUP = "myGroup"; + private static final String MY_GROUP2 = "myGroup2"; + private static final String MY_GROUP1 = "myGroup1"; + + private FileGroupDatabase _groupDatabase = new FileGroupDatabase(); + private String _groupFile; + + public void testGetAllGroups() throws Exception + { + writeAndSetGroupFile("myGroup.users", USER1); + + Set groups = _groupDatabase.getAllGroups(); + assertEquals(1, groups.size()); + assertTrue(groups.contains(MY_GROUP)); + } + + public void testGetAllGroupsWhenGroupFileEmpty() throws Exception + { + _groupDatabase.setGroupFile(_groupFile); + + Set groups = _groupDatabase.getAllGroups(); + assertEquals(0, groups.size()); + } + + public void testMissingGroupFile() throws Exception + { + try + { + _groupDatabase.setGroupFile("/not/a/file"); + fail("Exception not thrown"); + } + catch (FileNotFoundException fnfe) + { + // PASS + } + } + + public void testInvalidFormat() throws Exception + { + writeGroupFile("name.notvalid", USER1); + + try + { + _groupDatabase.setGroupFile(_groupFile); + fail("Exception not thrown"); + } + catch (IllegalArgumentException gde) + { + // PASS + } + } + + public void testGetUsersInGroup() throws Exception + { + writeGroupFile("myGroup.users", "user1,user2,user3"); + + _groupDatabase.setGroupFile(_groupFile); + + Set users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertNotNull(users); + assertEquals(3, users.size()); + } + + public void testDuplicateUsersInGroupAreConflated() throws Exception + { + writeAndSetGroupFile("myGroup.users", "user1,user1,user3,user1"); + + Set users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertNotNull(users); + assertEquals(2, users.size()); + } + + public void testGetUsersWithEmptyGroup() throws Exception + { + writeAndSetGroupFile("myGroup.users", ""); + + Set users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertNotNull(users); + assertTrue(users.isEmpty()); + } + + public void testGetUsersInNonExistentGroup() throws Exception + { + writeAndSetGroupFile("myGroup.users", "user1,user2,user3"); + + Set users = _groupDatabase.getUsersInGroup("groupDoesntExist"); + assertNotNull(users); + assertTrue(users.isEmpty()); + } + + public void testGetUsersInNullGroup() throws Exception + { + writeAndSetGroupFile(); + assertTrue(_groupDatabase.getUsersInGroup(null).isEmpty()); + } + + public void testGetGroupPrincipalsForUserWhenUserBelongsToOneGroup() throws Exception + { + writeAndSetGroupFile("myGroup.users", "user1,user2"); + Set groups = _groupDatabase.getGroupsForUser(USER1); + assertEquals(1, groups.size()); + assertTrue(groups.contains(MY_GROUP)); + } + + public void testGetGroupPrincipalsForUserWhenUserBelongsToTwoGroup() throws Exception + { + writeAndSetGroupFile("myGroup1.users", "user1,user2", + "myGroup2.users", "user1,user3"); + Set groups = _groupDatabase.getGroupsForUser(USER1); + assertEquals(2, groups.size()); + assertTrue(groups.contains(MY_GROUP1)); + assertTrue(groups.contains(MY_GROUP2)); + } + + public void testGetGroupPrincipalsForUserWhenUserAddedToGroup() throws Exception + { + writeAndSetGroupFile("myGroup1.users", "user1,user2", + "myGroup2.users", USER2); + Set groups = _groupDatabase.getGroupsForUser(USER1); + assertEquals(1, groups.size()); + assertTrue(groups.contains(MY_GROUP1)); + + _groupDatabase.addUserToGroup(USER1, MY_GROUP2); + + groups = _groupDatabase.getGroupsForUser(USER1); + assertEquals(2, groups.size()); + assertTrue(groups.contains(MY_GROUP1)); + assertTrue(groups.contains(MY_GROUP2)); + + Set users = _groupDatabase.getUsersInGroup(MY_GROUP2); + assertEquals(2, users.size()); + assertTrue(users.contains(USER1)); + assertTrue(users.contains(USER2)); + } + + public void testGetGroupPrincipalsForUserWhenUserRemovedFromGroup() throws Exception + { + writeAndSetGroupFile("myGroup1.users", "user1,user2", + "myGroup2.users", "user1,user2"); + Set groups = _groupDatabase.getGroupsForUser(USER1); + assertEquals(2, groups.size()); + assertTrue(groups.contains(MY_GROUP1)); + assertTrue(groups.contains(MY_GROUP2)); + + _groupDatabase.removeUserFromGroup(USER1, MY_GROUP2); + + groups = _groupDatabase.getGroupsForUser(USER1); + assertEquals(1, groups.size()); + assertTrue(groups.contains(MY_GROUP1)); + } + + public void testGetGroupPrincipalsForUserWhenUserAdddedToGroupTheyAreAlreadyIn() throws Exception + { + writeAndSetGroupFile("myGroup.users", USER1); + _groupDatabase.addUserToGroup(USER1, MY_GROUP); + + Set groups = _groupDatabase.getGroupsForUser(USER1); + + assertEquals(1, groups.size()); + assertTrue(groups.contains(MY_GROUP)); + + Set users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertEquals(1, users.size()); + assertTrue(users.contains(USER1)); + } + + public void testGetGroupPrincipalsForUserWhenUserNotKnown() throws Exception + { + writeAndSetGroupFile("myGroup.users", "user1,user2"); + Set groups = _groupDatabase.getGroupsForUser(USER3); + assertEquals(0, groups.size()); + } + + public void testGetGroupPrincipalsForNullUser() throws Exception + { + writeAndSetGroupFile(); + assertTrue(_groupDatabase.getGroupsForUser(null).isEmpty()); + } + + public void testAddUserToExistingGroup() throws Exception + { + writeAndSetGroupFile("myGroup.users", "user1,user2"); + + Set users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertNotNull(users); + assertEquals(2, users.size()); + + _groupDatabase.addUserToGroup(USER3, MY_GROUP); + + users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertNotNull(users); + assertEquals(3, users.size()); + } + + public void testAddUserToEmptyGroup() throws Exception + { + writeAndSetGroupFile("myGroup.users", ""); + + Set users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertNotNull(users); + assertEquals(0, users.size()); + + _groupDatabase.addUserToGroup(USER3, MY_GROUP); + + users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertNotNull(users); + assertEquals(1, users.size()); + } + + public void testAddUserToNonExistentGroup() throws Exception + { + writeAndSetGroupFile(); + + Set users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertNotNull(users); + assertEquals(0, users.size()); + + try + { + _groupDatabase.addUserToGroup(USER3, MY_GROUP); + fail("Expected exception not thrown"); + } + catch(IllegalArgumentException e) + { + // pass + } + + users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertNotNull(users); + assertEquals(0, users.size()); + } + + public void testRemoveUserFromExistingGroup() throws Exception + { + writeAndSetGroupFile("myGroup.users", "user1,user2"); + + Set users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertNotNull(users); + assertEquals(2, users.size()); + + _groupDatabase.removeUserFromGroup(USER2, MY_GROUP); + + users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertNotNull(users); + assertEquals(1, users.size()); + } + + public void testRemoveUserFromNonexistentGroup() throws Exception + { + writeAndSetGroupFile(); + + try + { + _groupDatabase.removeUserFromGroup(USER1, MY_GROUP); + fail("Expected exception not thrown"); + } + catch(IllegalArgumentException e) + { + // pass + } + + assertTrue(_groupDatabase.getUsersInGroup(MY_GROUP).isEmpty()); + } + + public void testRemoveUserFromGroupTwice() throws Exception + { + writeAndSetGroupFile("myGroup.users", USER1); + assertTrue(_groupDatabase.getUsersInGroup(MY_GROUP).contains(USER1)); + + _groupDatabase.removeUserFromGroup(USER1, MY_GROUP); + assertTrue(_groupDatabase.getUsersInGroup(MY_GROUP).isEmpty()); + + _groupDatabase.removeUserFromGroup(USER1, MY_GROUP); + assertTrue(_groupDatabase.getUsersInGroup(MY_GROUP).isEmpty()); + } + + public void testAddUserPersistedToFile() throws Exception + { + writeAndSetGroupFile("myGroup.users", "user1,user2"); + + Set users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertEquals(2, users.size()); + + _groupDatabase.addUserToGroup(USER3, MY_GROUP); + assertEquals(3, users.size()); + + FileGroupDatabase newGroupDatabase = new FileGroupDatabase(); + newGroupDatabase.setGroupFile(_groupFile); + + Set newUsers = newGroupDatabase.getUsersInGroup(MY_GROUP); + assertEquals(users.size(), newUsers.size()); + } + + public void testRemoveUserPersistedToFile() throws Exception + { + writeAndSetGroupFile("myGroup.users", "user1,user2"); + + Set users = _groupDatabase.getUsersInGroup(MY_GROUP); + assertEquals(2, users.size()); + + _groupDatabase.removeUserFromGroup(USER2, MY_GROUP); + assertEquals(1, users.size()); + + FileGroupDatabase newGroupDatabase = new FileGroupDatabase(); + newGroupDatabase.setGroupFile(_groupFile); + + Set newUsers = newGroupDatabase.getUsersInGroup(MY_GROUP); + assertEquals(users.size(), newUsers.size()); + } + + public void testCreateGroupPersistedToFile() throws Exception + { + writeAndSetGroupFile(); + + Set groups = _groupDatabase.getAllGroups(); + assertEquals(0, groups.size()); + + _groupDatabase.createGroup(MY_GROUP); + + groups = _groupDatabase.getAllGroups(); + assertEquals(1, groups.size()); + assertTrue(groups.contains(MY_GROUP)); + + FileGroupDatabase newGroupDatabase = new FileGroupDatabase(); + newGroupDatabase.setGroupFile(_groupFile); + + Set newGroups = newGroupDatabase.getAllGroups(); + assertEquals(1, newGroups.size()); + assertTrue(newGroups.contains(MY_GROUP)); + } + + public void testRemoveGroupPersistedToFile() throws Exception + { + writeAndSetGroupFile("myGroup1.users", "user1,user2", + "myGroup2.users", "user1,user2"); + + Set groups = _groupDatabase.getAllGroups(); + assertEquals(2, groups.size()); + + Set groupsForUser1 = _groupDatabase.getGroupsForUser(USER1); + assertEquals(2, groupsForUser1.size()); + + _groupDatabase.removeGroup(MY_GROUP1); + + groups = _groupDatabase.getAllGroups(); + assertEquals(1, groups.size()); + assertTrue(groups.contains(MY_GROUP2)); + + groupsForUser1 = _groupDatabase.getGroupsForUser(USER1); + assertEquals(1, groupsForUser1.size()); + + FileGroupDatabase newGroupDatabase = new FileGroupDatabase(); + newGroupDatabase.setGroupFile(_groupFile); + + Set newGroups = newGroupDatabase.getAllGroups(); + assertEquals(1, newGroups.size()); + assertTrue(newGroups.contains(MY_GROUP2)); + + Set newGroupsForUser1 = newGroupDatabase.getGroupsForUser(USER1); + assertEquals(1, newGroupsForUser1.size()); + assertTrue(newGroupsForUser1.contains(MY_GROUP2)); +} + + @Override + protected void setUp() throws Exception + { + super.setUp(); + _groupFile = createEmptyTestGroupFile(); + } + + private void writeAndSetGroupFile(String... groupAndUsers) throws Exception + { + writeGroupFile(groupAndUsers); + _groupDatabase.setGroupFile(_groupFile); + } + + private void writeGroupFile(String... groupAndUsers) throws Exception + { + if (groupAndUsers.length % 2 != 0) + { + throw new IllegalArgumentException("Number of groupAndUsers must be even"); + } + + Properties props = new Properties(); + for (int i = 0 ; i < groupAndUsers.length; i=i+2) + { + String group = groupAndUsers[i]; + String users = groupAndUsers[i+1]; + props.put(group, users); + } + + props.store(new FileOutputStream(_groupFile), "test group file"); + } + + private String createEmptyTestGroupFile() throws IOException + { + File tmpGroupFile = File.createTempFile("groups", "grp"); + tmpGroupFile.deleteOnExit(); + + return tmpGroupFile.getAbsolutePath(); + } + + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + + if (_groupFile != null) + { + File groupFile = new File(_groupFile); + if (groupFile.exists()) + { + groupFile.delete(); + } + } + } + +} 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 new file mode 100644 index 0000000000..165ecb098f --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/FileGroupManagerTest.java @@ -0,0 +1,236 @@ +/* + * 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.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.commons.configuration.CompositeConfiguration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.security.auth.UsernamePrincipal; +import org.apache.qpid.server.util.InternalBrokerBaseCase; + +public class FileGroupManagerTest extends InternalBrokerBaseCase +{ + private static final String MYGROUP_USERS = "user1"; + private static final String MY_GROUP = "myGroup.users"; + private static final String MY_GROUP2 = "myGroup2.users"; + private File _tmpGroupFile; + private FileGroupManager _manager; + + @Override + public void tearDown() throws Exception + { + super.tearDown(); +//TODO: implement closable +// if (_manager != null) +// { +// _manager.close(); +// } + + if (_tmpGroupFile != null) + { + if (_tmpGroupFile.exists()) + { + _tmpGroupFile.delete(); + } + } + } + + public void testValidGroupFile() throws Exception + { + final String groupFileName = writeGroupFile(); + final ConfigurationPlugin config = getConfig("groupFile", groupFileName); + + _manager = FileGroupManager.FACTORY.newInstance(config); + assertNotNull(_manager); + } + + public void testNonExistentGroupFile() throws Exception + { + final String filePath = "/does.not.exist/"; + final File fileFile = new File(filePath); + + assertFalse("File already exists", fileFile.exists()); + final ConfigurationPlugin config = getConfig("groupFile", filePath); + + try + { + _manager = FileGroupManager.FACTORY.newInstance(config); + fail("expected exception was not thrown"); + } + catch(ConfigurationException ce) + { + assertNotNull(ce.getCause()); + assertTrue(ce.getCause() instanceof FileNotFoundException); + } + } + + public void testGetGroupPrincipalsForUser() throws Exception + { + final String groupFileName = writeGroupFile(); + final ConfigurationPlugin config = getConfig("groupFile", groupFileName); + _manager = FileGroupManager.FACTORY.newInstance(config); + + Set principals = _manager.getGroupPrincipalsForUser("user1"); + assertEquals(1, principals.size()); + assertTrue(principals.contains(new GroupPrincipal("myGroup"))); + } + + public void testGetUserPrincipalsForGroup() throws Exception + { + final String groupFileName = writeGroupFile(); + final ConfigurationPlugin config = getConfig("groupFile", groupFileName); + _manager = FileGroupManager.FACTORY.newInstance(config); + + Set principals = _manager.getUserPrincipalsForGroup("myGroup"); + assertEquals(1, principals.size()); + assertTrue(principals.contains(new UsernamePrincipal("user1"))); + } + + public void testGetGroupPrincipals() throws Exception + { + final String groupFileName = writeGroupFile(MY_GROUP, MYGROUP_USERS, MY_GROUP2, MYGROUP_USERS); + final ConfigurationPlugin config = getConfig("groupFile", groupFileName); + _manager = FileGroupManager.FACTORY.newInstance(config); + + Set principals = _manager.getGroupPrincipals(); + assertEquals(2, principals.size()); + assertTrue(principals.contains(new GroupPrincipal("myGroup"))); + assertTrue(principals.contains(new GroupPrincipal("myGroup2"))); + } + + public void testCreateGroup() throws Exception + { + final String groupFileName = writeGroupFile(); + final ConfigurationPlugin config = getConfig("groupFile", groupFileName); + _manager = FileGroupManager.FACTORY.newInstance(config); + + Set principals = _manager.getGroupPrincipals(); + assertEquals(1, principals.size()); + + _manager.createGroup("myGroup2"); + + principals = _manager.getGroupPrincipals(); + assertEquals(2, principals.size()); + assertTrue(principals.contains(new GroupPrincipal("myGroup2"))); + } + + public void testRemoveGroup() throws Exception + { + final String groupFileName = writeGroupFile(MY_GROUP, MYGROUP_USERS); + final ConfigurationPlugin config = getConfig("groupFile", groupFileName); + _manager = FileGroupManager.FACTORY.newInstance(config); + + Set principals = _manager.getGroupPrincipals(); + assertEquals(1, principals.size()); + + _manager.removeGroup("myGroup"); + + principals = _manager.getGroupPrincipals(); + assertEquals(0, principals.size()); + } + + public void testAddUserToGroup() throws Exception + { + final String groupFileName = writeGroupFile(MY_GROUP, MYGROUP_USERS); + final ConfigurationPlugin config = getConfig("groupFile", groupFileName); + _manager = FileGroupManager.FACTORY.newInstance(config); + + Set principals = _manager.getUserPrincipalsForGroup("myGroup"); + assertEquals(1, principals.size()); + assertFalse(principals.contains(new UsernamePrincipal("user2"))); + + _manager.addUserToGroup("user2", "myGroup"); + + principals = _manager.getUserPrincipalsForGroup("myGroup"); + assertEquals(2, principals.size()); + assertTrue(principals.contains(new UsernamePrincipal("user2"))); + } + + public void testRemoveUserInGroup() throws Exception + { + final String groupFileName = writeGroupFile(MY_GROUP, MYGROUP_USERS); + final ConfigurationPlugin config = getConfig("groupFile", groupFileName); + _manager = FileGroupManager.FACTORY.newInstance(config); + + Set principals = _manager.getUserPrincipalsForGroup("myGroup"); + assertEquals(1, principals.size()); + assertTrue(principals.contains(new UsernamePrincipal("user1"))); + + _manager.removeUserFromGroup("user1", "myGroup"); + + principals = _manager.getUserPrincipalsForGroup("myGroup"); + assertEquals(0, principals.size()); + } + + private ConfigurationPlugin getConfig(final String argName, final String argValue) throws Exception + { + final ConfigurationPlugin config = new FileGroupManager.FileGroupManagerConfiguration(); + + XMLConfiguration xmlconfig = new XMLConfiguration(); + + if (argName != null) + { + xmlconfig.addProperty("file-group-manager.attributes.attribute.name", argName); + xmlconfig.addProperty("file-group-manager.attributes.attribute.value", argValue); + } + + // Create a CompositeConfiguration as this is what the broker uses + CompositeConfiguration composite = new CompositeConfiguration(); + composite.addConfiguration(xmlconfig); + config.setConfiguration("security", xmlconfig); + return config; + } + + private String writeGroupFile() throws Exception + { + return writeGroupFile(MY_GROUP, MYGROUP_USERS); + } + + private String writeGroupFile(String... groupAndUsers) throws Exception + { + if (groupAndUsers.length % 2 != 0) + { + throw new IllegalArgumentException("Number of groupAndUsers must be even"); + } + + _tmpGroupFile = File.createTempFile("groups", "grp"); + _tmpGroupFile.deleteOnExit(); + + Properties props = new Properties(); + for (int i = 0 ; i < groupAndUsers.length; i=i+2) + { + String group = groupAndUsers[i]; + String users = groupAndUsers[i+1]; + props.put(group, users); + } + + props.store(new FileOutputStream(_tmpGroupFile), "test group file"); + + return _tmpGroupFile.getCanonicalPath(); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/GroupPrincipalAccessorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/GroupPrincipalAccessorTest.java new file mode 100644 index 0000000000..6f9b73845d --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/GroupPrincipalAccessorTest.java @@ -0,0 +1,81 @@ +/* + * 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.group; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.security.Principal; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import junit.framework.TestCase; + +import org.apache.qpid.server.security.group.GroupManager; +import org.apache.qpid.server.security.group.GroupPrincipalAccessor; + +public class GroupPrincipalAccessorTest extends TestCase +{ + private static final String USERNAME = "username"; + + private GroupManager _groupManager1 = mock(GroupManager.class); + private GroupManager _groupManager2 = mock(GroupManager.class); + + private Principal _group1 = mock(Principal.class); + private Principal _group2 = mock(Principal.class); + + @Override + public void setUp() + { + when(_groupManager1.getGroupPrincipalsForUser(USERNAME)).thenReturn(Collections.singleton(_group1)); + when(_groupManager2.getGroupPrincipalsForUser(USERNAME)).thenReturn(Collections.singleton(_group2)); + } + + public void testGetGroupPrincipals() + { + getAndAssertGroupPrincipals(_group1, _group2); + } + + public void testGetGroupPrincipalsWhenAGroupManagerReturnsNull() + { + when(_groupManager1.getGroupPrincipalsForUser(USERNAME)).thenReturn(null); + + getAndAssertGroupPrincipals(_group2); + } + + public void testGetGroupPrincipalsWhenAGroupManagerReturnsEmptySet() + { + when(_groupManager2.getGroupPrincipalsForUser(USERNAME)).thenReturn(new HashSet()); + + getAndAssertGroupPrincipals(_group1); + } + + private void getAndAssertGroupPrincipals(Principal... expectedGroups) + { + GroupPrincipalAccessor groupPrincipalAccessor = new GroupPrincipalAccessor(Arrays.asList(_groupManager1, _groupManager2)); + + Set actualGroupPrincipals = groupPrincipalAccessor.getGroupPrincipals(USERNAME); + + Set expectedGroupPrincipals = new HashSet(Arrays.asList(expectedGroups)); + + assertEquals(expectedGroupPrincipals, actualGroupPrincipals); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/GroupPrincipalTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/GroupPrincipalTest.java new file mode 100644 index 0000000000..d285a0797a --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/group/GroupPrincipalTest.java @@ -0,0 +1,88 @@ +/* + * + * 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.group; + +import org.apache.qpid.server.security.auth.UsernamePrincipal; + +import junit.framework.TestCase; + +public class GroupPrincipalTest extends TestCase +{ + public void testGetName() + { + final GroupPrincipal principal = new GroupPrincipal("group"); + assertEquals("group", principal.getName()); + } + + public void testAddRejected() + { + final GroupPrincipal principal = new GroupPrincipal("group"); + final UsernamePrincipal user = new UsernamePrincipal("name"); + + try + { + principal.addMember(user); + fail("Exception not thrown"); + } + catch (UnsupportedOperationException uso) + { + // PASS + } + } + + public void testEqualitySameName() + { + final String string = "string"; + final GroupPrincipal principal1 = new GroupPrincipal(string); + final GroupPrincipal principal2 = new GroupPrincipal(string); + assertTrue(principal1.equals(principal2)); + } + + public void testEqualityEqualName() + { + final GroupPrincipal principal1 = new GroupPrincipal(new String("string")); + final GroupPrincipal principal2 = new GroupPrincipal(new String("string")); + assertTrue(principal1.equals(principal2)); + } + + public void testInequalityDifferentGroupPrincipals() + { + GroupPrincipal principal1 = new GroupPrincipal("string1"); + GroupPrincipal principal2 = new GroupPrincipal("string2"); + assertFalse(principal1.equals(principal2)); + } + + public void testInequalityNonGroupPrincipal() + { + GroupPrincipal principal = new GroupPrincipal("string"); + assertFalse(principal.equals(new UsernamePrincipal("string"))); + } + + public void testInequalityNull() + { + GroupPrincipal principal = new GroupPrincipal("string"); + assertFalse(principal.equals(null)); + } + + + + +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java index a64ab620ab..411ed81d2a 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/TestApplicationRegistry.java @@ -33,10 +33,12 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.GenericActor; import org.apache.qpid.server.plugins.PluginManager; import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabase; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.security.auth.manager.IAuthenticationManagerRegistry; import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; +import org.apache.qpid.server.security.group.GroupPrincipalAccessor; import java.util.Properties; @@ -58,7 +60,7 @@ public class TestApplicationRegistry extends ApplicationRegistry @Override protected IAuthenticationManagerRegistry createAuthenticationManagerRegistry( - ServerConfiguration _configuration, PluginManager _pluginManager) + ServerConfiguration configuration, PluginManager pluginManager, final GroupPrincipalAccessor groupPrincipalAccessor) throws ConfigurationException { final Properties users = new Properties(); @@ -98,10 +100,9 @@ public class TestApplicationRegistry extends ApplicationRegistry } @Override - public AuthenticationManager getAuthenticationManager( - SocketAddress address) + public SubjectCreator getSubjectCreator(SocketAddress address) { - return pdam; + return new SubjectCreator(pdam, groupPrincipalAccessor); } @Override diff --git a/qpid/java/systests/etc/config-systests-settings.xml b/qpid/java/systests/etc/config-systests-settings.xml index 0b65ad83c3..4dfa0a01ee 100644 --- a/qpid/java/systests/etc/config-systests-settings.xml +++ b/qpid/java/systests/etc/config-systests-settings.xml @@ -78,12 +78,14 @@ - - - + + + + groupFile + ${conf}/groups-systests + + + false diff --git a/qpid/java/systests/etc/groups-systests b/qpid/java/systests/etc/groups-systests new file mode 100644 index 0000000000..e3912ece99 --- /dev/null +++ b/qpid/java/systests/etc/groups-systests @@ -0,0 +1,29 @@ +# +# 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. +# + +# +# To define a group, use the format: +# +# .users=,,..., +# + +messaging-users.users=guest,client,server +administrators.users=admin +webadmins.users=webadmin + diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AccessControlLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AccessControlLoggingTest.java index 4b7b3f0cf0..37f960a65a 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AccessControlLoggingTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/AccessControlLoggingTest.java @@ -31,10 +31,10 @@ import java.util.List; /** * ACL version 2/3 file testing to verify that ACL actor logging works correctly. - * + * * This suite of tests validate that the AccessControl messages occur correctly * and according to the following format: - * + * *
  * ACL-1001 : Allowed Operation Object {PROPERTIES}
  * ACL-1002 : Denied Operation Object {PROPERTIES}
@@ -83,12 +83,12 @@ public class AccessControlLoggingTest extends AbstractTestLogging
         Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
         conn.start();
         ((AMQSession) sess).createQueue(new AMQShortString("allow"), false, false, false);
-        
+
         List matches = findMatches(ACL_LOG_PREFIX);
-        
+
         assertTrue("Should be no ACL log messages", matches.isEmpty());
     }
-    
+
     /**
      * Test that {@code allow-log} ACL entries log correctly.
      */
@@ -98,25 +98,25 @@ public class AccessControlLoggingTest extends AbstractTestLogging
         Session sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
         conn.start();
         ((AMQSession) sess).createQueue(new AMQShortString("allow-log"), false, false, false);
-        
+
         List matches = findMatches(ACL_LOG_PREFIX);
-        
+
         assertEquals("Should only be one ACL log message", 1, matches.size());
-        
+
         String log = getLogMessage(matches, 0);
         String actor = fromActor(log);
         String subject = fromSubject(log);
         String message = getMessageString(fromMessage(log));
-        
+
         validateMessageID(ACL_LOG_PREFIX + 1001, log);
-        
-        assertTrue("Actor should contain the user identity", actor.contains(USER));
+
+        assertTrue("Actor " + actor + " should contain the user identity: " + USER, actor.contains(USER));
         assertTrue("Subject should be empty", subject.length() == 0);
         assertTrue("Message should start with 'Allowed'", message.startsWith("Allowed"));
         assertTrue("Message should contain 'Create Queue'", message.contains("Create Queue"));
         assertTrue("Message should have contained the queue name", message.contains("allow-log"));
     }
-    
+
     /**
      * Test that {@code deny-log} ACL entries log correctly.
      */
@@ -134,25 +134,25 @@ public class AccessControlLoggingTest extends AbstractTestLogging
             // Denied, so exception thrown
             assertEquals("Expected ACCESS_REFUSED error code", AMQConstant.ACCESS_REFUSED, amqe.getErrorCode());
         }
-        
+
         List matches = findMatches(ACL_LOG_PREFIX);
-        
+
         assertEquals("Should only be one ACL log message", 1, matches.size());
-        
+
         String log = getLogMessage(matches, 0);
         String actor = fromActor(log);
         String subject = fromSubject(log);
         String message = getMessageString(fromMessage(log));
-        
+
         validateMessageID(ACL_LOG_PREFIX + 1002, log);
-        
-        assertTrue("Actor should contain the user identity", actor.contains(USER));
+
+        assertTrue("Actor " + actor + " should contain the user identity: " + USER, actor.contains(USER));
         assertTrue("Subject should be empty", subject.length() == 0);
         assertTrue("Message should start with 'Denied'", message.startsWith("Denied"));
         assertTrue("Message should contain 'Create Queue'", message.contains("Create Queue"));
         assertTrue("Message should have contained the queue name", message.contains("deny-log"));
     }
-    
+
     /**
      * Test that {@code deny} ACL entries do not log anything.
      */
@@ -170,9 +170,9 @@ public class AccessControlLoggingTest extends AbstractTestLogging
             // Denied, so exception thrown
             assertEquals("Expected ACCESS_REFUSED error code", AMQConstant.ACCESS_REFUSED, amqe.getErrorCode());
         }
-        
+
         List matches = findMatches(ACL_LOG_PREFIX);
-        
+
         assertTrue("Should be no ACL log messages", matches.isEmpty());
     }
 }
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 8ccf74a22b..7b50749f5f 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
@@ -45,7 +45,7 @@ import java.util.concurrent.TimeUnit;
 /**
  * Abstract test case for ACLs.
  * 
- * This base class contains convenience methods to mange ACL files and implements a mechanism that allows each
+ * This base class contains convenience methods to manage ACL files and implements a mechanism that allows each
  * test method to run its own setup code before the broker starts.
  * 
  * TODO move the pre broker-startup setup method invocation code to {@link QpidBrokerTestCase}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java
index 0e45ca9493..400464b4eb 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/security/acl/ExternalACLTest.java
@@ -319,8 +319,12 @@ public class ExternalACLTest extends AbstractACLTestCase
 
     public void setUpRequestResponseSuccess() throws Exception
     {
-        writeACLFile("test", "GROUP messaging-users client server",
-                             "ACL ALLOW-LOG messaging-users ACCESS VIRTUALHOST",
+        // The group "messaging-users", referenced in the ACL below, is currently defined
+        // in broker/etc/groups-systests.
+        // We tolerate a dependency from this test to that file because its
+        // contents are expected to change rarely.
+
+        writeACLFile("test", "ACL ALLOW-LOG messaging-users ACCESS VIRTUALHOST",
                              "# Server side",
                              "ACL ALLOW-LOG server CREATE QUEUE name=\"example.RequestQueue\"" ,
                              "ACL ALLOW-LOG server BIND EXCHANGE",
@@ -389,11 +393,12 @@ public class ExternalACLTest extends AbstractACLTestCase
         conn.start();
 
         // create kipper
-        Topic kipper = sess.createTopic("kipper");
-        TopicSubscriber subscriber = sess.createDurableSubscriber(kipper, "kipper");
+        String topicName = "kipper";
+        Topic topic = sess.createTopic(topicName);
+        TopicSubscriber subscriber = sess.createDurableSubscriber(topic, topicName);
 
         subscriber.close();
-        sess.unsubscribe("kipper");
+        sess.unsubscribe(topicName);
 
         //Do something to show connection is active.
         sess.rollback();
-- 
cgit v1.2.1