diff options
| author | Keith Wall <kwall@apache.org> | 2012-09-10 15:37:45 +0000 |
|---|---|---|
| committer | Keith Wall <kwall@apache.org> | 2012-09-10 15:37:45 +0000 |
| commit | b0a4911fa51737570a1e9767f7fd37f50f06b3bd (patch) | |
| tree | d3183ccd29662cd13926e529aa3f3d0f6db24ef3 /qpid/java/broker/src | |
| parent | 3957c7f5aab759d2a9b2f10b38c116f0472b32fa (diff) | |
| download | qpid-python-b0a4911fa51737570a1e9767f7fd37f50f06b3bd.tar.gz | |
QPID-4292: add ACL rule to authorise access to the web management UI
* added object name MANAGEMENT to represent both JMX and Web Management layers
* Change both JMX/Web entry points to permission access with an access management check
* Updated examples and docbook
* Made Principals serialised to avoid container warnings when Qpid principals are placed within a HttpSession.
Work of Robbie Gemmell <robbie@apache.org> and myself.
git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1382947 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/broker/src')
7 files changed, 118 insertions, 61 deletions
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 ce0ea2faea..1e377be1d2 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 @@ -29,6 +29,7 @@ import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.plugins.PluginManager; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.security.access.ObjectProperties; +import org.apache.qpid.server.security.access.ObjectType; import org.apache.qpid.server.security.access.Operation; import static org.apache.qpid.server.security.access.ObjectType.EXCHANGE; @@ -359,6 +360,17 @@ public class SecurityManager }); } + public boolean accessManagement() + { + return checkAllPlugins(new AccessCheck() + { + Result allowed(SecurityPlugin plugin) + { + return plugin.access(ObjectType.MANAGEMENT, null); + } + }); + } + public boolean accessVirtualhost(final String vhostname, final SocketAddress remoteAddress) { return checkAllPlugins(new AccessCheck() 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 043d4909d5..8bc4b9d278 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 @@ -41,6 +41,7 @@ public enum ObjectType { ALL(Operation.ALL), VIRTUALHOST(Operation.ALL, ACCESS), + MANAGEMENT(Operation.ALL, ACCESS), QUEUE(Operation.ALL, CREATE, DELETE, PURGE, CONSUME), EXCHANGE(Operation.ALL, ACCESS, CREATE, DELETE, BIND, UNBIND, PUBLISH), LINK, // Not allowed in the Java broker 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 index 96360e83e4..fb31132514 100644 --- 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 @@ -18,6 +18,7 @@ */ package org.apache.qpid.server.security.auth; +import java.io.Serializable; import java.security.Principal; import java.util.Set; @@ -30,7 +31,7 @@ import org.apache.qpid.server.security.auth.UsernamePrincipal; * by calling {@link Subject#getPrincipals(Class)}, passing in {@link AuthenticatedPrincipal}.class, * e.g. when logging. */ -public final class AuthenticatedPrincipal implements Principal +public final class AuthenticatedPrincipal implements Principal, Serializable { private final Principal _wrappedPrincipal; 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 index cc414f801a..5b3c1d59cf 100644 --- 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 @@ -20,10 +20,11 @@ */ package org.apache.qpid.server.security.auth; +import java.io.Serializable; import java.security.Principal; /** A principal that is just a wrapper for a simple username. */ -public class UsernamePrincipal implements Principal +public class UsernamePrincipal implements Principal, Serializable { private final String _name; 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 808447b7ff..9f5d87bf62 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 @@ -22,7 +22,8 @@ package org.apache.qpid.server.security.auth.rmi; import java.net.SocketAddress; -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.SubjectCreator; import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; @@ -37,23 +38,33 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator static final String SHOULD_HAVE_2_ELEMENTS = "User details should have 2 elements, username, password"; static final String SHOULD_BE_NON_NULL = "Supplied username and password should be non-null"; static final String INVALID_CREDENTIALS = "Invalid user details supplied"; + static final String USER_NOT_AUTHORISED_FOR_MANAGEMENT = "User not authorised for management"; static final String CREDENTIALS_REQUIRED = "User details are required. " + - "Please ensure you are using an up to date management console to connect."; + "Please ensure you are using an up to date management console to connect."; - private SubjectCreator _subjectCreator = null; - private SocketAddress _socketAddress; + private final IApplicationRegistry _appRegistry; + private final SocketAddress _socketAddress; - public RMIPasswordAuthenticator(SocketAddress socketAddress) + public RMIPasswordAuthenticator(IApplicationRegistry appRegistry, SocketAddress socketAddress) { + _appRegistry = appRegistry; _socketAddress = socketAddress; } - public void setSubjectCreator(final SubjectCreator subjectCreator) + public Subject authenticate(Object credentials) throws SecurityException { - _subjectCreator = subjectCreator; + validateCredentials(credentials); + + final String[] userCredentials = (String[]) credentials; + final String username = (String) userCredentials[0]; + final String password = (String) userCredentials[1]; + + final Subject authenticatedSubject = doAuthentication(username, password); + doManagementAuthorisation(authenticatedSubject); + return authenticatedSubject; } - public Subject authenticate(Object credentials) throws SecurityException + private void validateCredentials(Object credentials) { // Verify that credential's are of type String[]. if (!(credentials instanceof String[])) @@ -69,41 +80,27 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator } // Verify that required number of credentials. - final String[] userCredentials = (String[]) credentials; - if (userCredentials.length != 2) + if (((String[])credentials).length != 2) { throw new SecurityException(SHOULD_HAVE_2_ELEMENTS); } + } - final String username = (String) userCredentials[0]; - final String password = (String) userCredentials[1]; - + private Subject doAuthentication(final String username, final String password) + { // Verify that all required credentials are actually present. if (username == null || password == null) { throw new SecurityException(SHOULD_BE_NON_NULL); } - // Verify that an SubjectCreator has been set. - if (_subjectCreator == null) + SubjectCreator subjectCreator = _appRegistry.getSubjectCreator(_socketAddress); + if (subjectCreator == null) { - try - { - if(ApplicationRegistry.getInstance().getSubjectCreator(_socketAddress) != null) - { - _subjectCreator = ApplicationRegistry.getInstance().getSubjectCreator(_socketAddress); - } - else - { - throw new SecurityException(UNABLE_TO_LOOKUP); - } - } - catch(IllegalStateException e) - { - throw new SecurityException(UNABLE_TO_LOOKUP); - } + throw new SecurityException("Can't get subject creator for " + _socketAddress); } - final SubjectAuthenticationResult result = _subjectCreator.authenticate(username, password); + + final SubjectAuthenticationResult result = subjectCreator.authenticate(username, password); if (AuthenticationStatus.ERROR.equals(result.getStatus())) { @@ -119,4 +116,21 @@ public class RMIPasswordAuthenticator implements JMXAuthenticator } } + private void doManagementAuthorisation(Subject authenticatedSubject) + { + SecurityManager.setThreadSubject(authenticatedSubject); + try + { + if (!_appRegistry.getSecurityManager().accessManagement()) + { + throw new SecurityException(USER_NOT_AUTHORISED_FOR_MANAGEMENT); + } + } + finally + { + SecurityManager.setThreadSubject(null); + } + } + + }
\ No newline at end of file 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 index ccb446b719..a9590bb964 100644 --- 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 @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.security.group; +import java.io.Serializable; import java.security.Principal; import java.security.acl.Group; import java.util.Enumeration; @@ -30,7 +31,7 @@ import java.util.Enumeration; * methods etc throw {@link UnsupportedOperationException}. * */ -public class GroupPrincipal implements Group +public class GroupPrincipal implements Group, Serializable { /** Name of the group */ private final String _groupName; 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 efdb286866..89580dc392 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 @@ -31,10 +31,12 @@ import javax.security.auth.Subject; import junit.framework.TestCase; +import org.apache.qpid.server.registry.ApplicationRegistry; 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.SubjectAuthenticationResult; +import org.apache.qpid.server.security.SecurityManager; /** * Tests the RMIPasswordAuthenticator and its collaboration with the AuthenticationManager. @@ -42,17 +44,25 @@ import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; */ public class RMIPasswordAuthenticatorTest extends TestCase { - private static final Subject SUBJECT = new Subject(); - private final String USERNAME = "guest"; - private final String PASSWORD = "guest"; + private static final String USERNAME = "guest"; + private static final String PASSWORD = "password"; + + private final ApplicationRegistry _applicationRegistry = mock(ApplicationRegistry.class); + private final SecurityManager _securityManager = mock(SecurityManager.class); + private final InetSocketAddress _jmxSocketAddress = new InetSocketAddress(8999); + private final Subject _loginSubject = new Subject(); + private final String[] _credentials = new String[] {USERNAME, PASSWORD}; + private RMIPasswordAuthenticator _rmipa; - private String[] _credentials; + + private SubjectCreator _usernamePasswordOkaySuvjectCreator = createMockSubjectCreator(true, null); + private SubjectCreator _badPasswordSubjectCreator = createMockSubjectCreator(false, null); protected void setUp() throws Exception { - _rmipa = new RMIPasswordAuthenticator(new InetSocketAddress(5672)); + _rmipa = new RMIPasswordAuthenticator(_applicationRegistry, _jmxSocketAddress); - _credentials = new String[] {USERNAME, PASSWORD}; + when(_applicationRegistry.getSecurityManager()).thenReturn(_securityManager); } /** @@ -60,10 +70,11 @@ public class RMIPasswordAuthenticatorTest extends TestCase */ public void testAuthenticationSuccess() { - _rmipa.setSubjectCreator(createMockSubjectCreator(true, null)); + when(_applicationRegistry.getSubjectCreator(_jmxSocketAddress)).thenReturn(_usernamePasswordOkaySuvjectCreator); + when(_securityManager.accessManagement()).thenReturn(true); Subject newSubject = _rmipa.authenticate(_credentials); - assertSame("Subject must be unchanged", SUBJECT, newSubject); + assertSame("Subject must be unchanged", _loginSubject, newSubject); } /** @@ -71,7 +82,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase */ public void testUsernameOrPasswordInvalid() { - _rmipa.setSubjectCreator(createMockSubjectCreator(false, null)); + when(_applicationRegistry.getSubjectCreator(_jmxSocketAddress)).thenReturn(_badPasswordSubjectCreator); try { @@ -82,17 +93,31 @@ public class RMIPasswordAuthenticatorTest extends TestCase { assertEquals("Unexpected exception message", RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage()); + } + } + + public void testAuthorisationFailure() + { + when(_applicationRegistry.getSubjectCreator(_jmxSocketAddress)).thenReturn(_usernamePasswordOkaySuvjectCreator); + when(_securityManager.accessManagement()).thenReturn(false); + try + { + _rmipa.authenticate(_credentials); + fail("Exception not thrown"); + } + catch (SecurityException se) + { + assertEquals("Unexpected exception message", + RMIPasswordAuthenticator.USER_NOT_AUTHORISED_FOR_MANAGEMENT, se.getMessage()); } } - /** - * Tests case where authentication system itself fails. - */ - public void testAuthenticationFailure() + public void testSubjectCreatorInternalFailure() { final Exception mockAuthException = new Exception("Mock Auth system failure"); - _rmipa.setSubjectCreator(createMockSubjectCreator(false, mockAuthException)); + SubjectCreator subjectCreator = createMockSubjectCreator(false, mockAuthException); + when(_applicationRegistry.getSubjectCreator(_jmxSocketAddress)).thenReturn(subjectCreator); try { @@ -105,13 +130,13 @@ public class RMIPasswordAuthenticatorTest extends TestCase } } - /** * Tests case where authentication manager is not set. */ - public void testNullAuthenticationManager() throws Exception + public void testNullSubjectCreator() throws Exception { - _rmipa.setSubjectCreator(null); + when(_applicationRegistry.getSubjectCreator(_jmxSocketAddress)).thenReturn(null); + try { _rmipa.authenticate(_credentials); @@ -120,7 +145,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase catch (SecurityException se) { assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.UNABLE_TO_LOOKUP, se.getMessage()); + "Can't get subject creator for 0.0.0.0/0.0.0.0:8999", se.getMessage()); } } @@ -148,11 +173,13 @@ public class RMIPasswordAuthenticatorTest extends TestCase */ public void testWithIllegalNumberOfArguments() { + String[] credentials; + // Test handling of incorrect number of credentials try { - _credentials = new String[]{USERNAME, PASSWORD, PASSWORD}; - _rmipa.authenticate(_credentials); + credentials = new String[]{USERNAME, PASSWORD, PASSWORD}; + _rmipa.authenticate(credentials); fail("SecurityException expected due to supplying wrong number of credentials"); } catch (SecurityException se) @@ -165,8 +192,8 @@ public class RMIPasswordAuthenticatorTest extends TestCase try { //send a null array - _credentials = null; - _rmipa.authenticate(_credentials); + credentials = null; + _rmipa.authenticate(credentials); fail("SecurityException expected due to not supplying an array of credentials"); } catch (SecurityException se) @@ -178,8 +205,8 @@ public class RMIPasswordAuthenticatorTest extends TestCase try { //send a null password - _credentials = new String[]{USERNAME, null}; - _rmipa.authenticate(_credentials); + credentials = new String[]{USERNAME, null}; + _rmipa.authenticate(credentials); fail("SecurityException expected due to sending a null password"); } catch (SecurityException se) @@ -191,8 +218,8 @@ public class RMIPasswordAuthenticatorTest extends TestCase try { //send a null username - _credentials = new String[]{null, PASSWORD}; - _rmipa.authenticate(_credentials); + credentials = new String[]{null, PASSWORD}; + _rmipa.authenticate(credentials); fail("SecurityException expected due to sending a null username"); } catch (SecurityException se) @@ -217,7 +244,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase { subjectAuthenticationResult = new SubjectAuthenticationResult( - new AuthenticationResult(mock(Principal.class)), SUBJECT); + new AuthenticationResult(mock(Principal.class)), _loginSubject); } else { |
