blob: 3896a1aaf3e6b9e77e4d1a1d2d3024382f92adb8 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.qpid.server.security.auth.manager;
import static org.apache.qpid.server.security.auth.AuthenticatedPrincipalTestHelper.assertOnlyContainsWrapped;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.security.Principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.model.BrokerTestHelper;
import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.model.PasswordCredentialManagingAuthenticationProvider;
import org.apache.qpid.server.security.auth.AuthenticationResult;
import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus;
import org.apache.qpid.server.security.auth.UsernamePrincipal;
import org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase;
import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
import org.apache.qpid.server.security.auth.sasl.SaslNegotiator;
import org.apache.qpid.server.security.auth.sasl.SaslSettings;
import org.apache.qpid.test.utils.UnitTestBase;
/**
* Tests the public methods of PrincipalDatabaseAuthenticationManager.
*/
public class PrincipalDatabaseAuthenticationManagerTest extends UnitTestBase
{
private static final String MOCK_MECH_NAME = "MOCK-MECH-NAME";
private PrincipalDatabaseAuthenticationManager _manager = null; // Class under test
private PrincipalDatabase _principalDatabase;
private String _passwordFileLocation;
private SaslNegotiator _saslNegotiator = mock(SaslNegotiator.class);
@Before
public void setUp() throws Exception
{
_passwordFileLocation = TMP_FOLDER + File.separator + PrincipalDatabaseAuthenticationManagerTest.class.getSimpleName() + "-" + getTestName();
deletePasswordFileIfExists();
}
@After
public void tearDown() throws Exception
{
try
{
if (_manager != null)
{
_manager.close();
}
}
finally
{
deletePasswordFileIfExists();
}
}
private void setupMocks() throws Exception
{
setUpPrincipalDatabase();
setupManager(false);
_manager.initialise();
}
private void setUpPrincipalDatabase()
{
_principalDatabase = mock(PrincipalDatabase.class);
when(_principalDatabase.getMechanisms()).thenReturn(Collections.singletonList(MOCK_MECH_NAME));
when(_principalDatabase.createSaslNegotiator(eq(MOCK_MECH_NAME), any(SaslSettings.class))).thenReturn(
_saslNegotiator);
}
private void setupManager(final boolean recovering)
{
Map<String,Object> attrs = new HashMap<String, Object>();
attrs.put(ConfiguredObject.ID, UUID.randomUUID());
attrs.put(ConfiguredObject.NAME, getTestName());
attrs.put("path", _passwordFileLocation);
_manager = getPrincipalDatabaseAuthenticationManager(attrs);
if(recovering)
{
_manager.open();
}
else
{
_manager.create();
}
}
@Test
public void testInitialiseWhenPasswordFileNotFound() throws Exception
{
PasswordCredentialManagingAuthenticationProvider mockAuthProvider = mock(PasswordCredentialManagingAuthenticationProvider.class);
when(mockAuthProvider.getContextValue(Integer.class, AbstractScramAuthenticationManager.QPID_AUTHMANAGER_SCRAM_ITERATION_COUNT)).thenReturn(4096);
_principalDatabase = new PlainPasswordFilePrincipalDatabase(mockAuthProvider);
setupManager(true);
try
{
_manager.initialise();
fail("Initialisiation should fail when users file does not exist");
}
catch (IllegalConfigurationException e)
{
final boolean condition = e.getCause() instanceof FileNotFoundException;
assertTrue(condition);
}
}
@Test
public void testInitialiseWhenPasswordFileExists() throws Exception
{
PasswordCredentialManagingAuthenticationProvider mockAuthProvider = mock(PasswordCredentialManagingAuthenticationProvider.class);
when(mockAuthProvider.getContextValue(Integer.class, AbstractScramAuthenticationManager.QPID_AUTHMANAGER_SCRAM_ITERATION_COUNT)).thenReturn(4096);
_principalDatabase = new PlainPasswordFilePrincipalDatabase(mockAuthProvider);
setupManager(true);
File f = new File(_passwordFileLocation);
f.createNewFile();
FileOutputStream fos = null;
try
{
fos = new FileOutputStream(f);
fos.write("admin:admin".getBytes());
}
finally
{
if (fos != null)
{
fos.close();
}
}
_manager.initialise();
List<Principal> users = _principalDatabase.getUsers();
assertEquals("Unexpected uses size", (long) 1, (long) users.size());
Principal p = _principalDatabase.getUser("admin");
assertEquals("Unexpected principal name", "admin", p.getName());
}
@Test
public void testSaslMechanismCreation() throws Exception
{
setupMocks();
SaslSettings saslSettings = mock(SaslSettings.class);
SaslNegotiator saslNegotiator = _manager.createSaslNegotiator(MOCK_MECH_NAME, saslSettings, null);
assertNotNull(saslNegotiator);
}
/**
* Tests that the authenticate method correctly interprets an
* authentication success.
*
*/
@Test
public void testSaslAuthenticationSuccess() throws Exception
{
setupMocks();
UsernamePrincipal expectedPrincipal = new UsernamePrincipal("guest", _manager);
when(_saslNegotiator.handleResponse(any(byte[].class))).thenReturn(new AuthenticationResult(expectedPrincipal));
AuthenticationResult result = _saslNegotiator.handleResponse("12345".getBytes());
assertOnlyContainsWrapped(expectedPrincipal, result.getPrincipals());
assertEquals(AuthenticationStatus.SUCCESS, result.getStatus());
}
/**
*
* Tests that the authenticate method correctly interprets an
* authentication not complete.
*
*/
@Test
public void testSaslAuthenticationNotCompleted() throws Exception
{
setupMocks();
when(_saslNegotiator.handleResponse(any(byte[].class))).thenReturn(new AuthenticationResult(AuthenticationStatus.CONTINUE));
AuthenticationResult result = _saslNegotiator.handleResponse("12345".getBytes());
assertEquals("Principals was not expected size", (long) 0, (long) result.getPrincipals().size());
assertEquals(AuthenticationStatus.CONTINUE, result.getStatus());
}
/**
*
* Tests that the authenticate method correctly interprets an
* authentication error.
*
*/
@Test
public void testSaslAuthenticationError() throws Exception
{
setupMocks();
when(_saslNegotiator.handleResponse(any(byte[].class))).thenReturn(new AuthenticationResult(AuthenticationStatus.ERROR));
AuthenticationResult result = _saslNegotiator.handleResponse("12345".getBytes());
assertEquals("Principals was not expected size", (long) 0, (long) result.getPrincipals().size());
assertEquals(AuthenticationStatus.ERROR, result.getStatus());
}
@Test
public void testNonSaslAuthenticationSuccess() throws Exception
{
setupMocks();
when(_principalDatabase.verifyPassword("guest", "guest".toCharArray())).thenReturn(true);
AuthenticationResult result = _manager.authenticate("guest", "guest");
UsernamePrincipal expectedPrincipal = new UsernamePrincipal("guest", _manager);
assertOnlyContainsWrapped(expectedPrincipal, result.getPrincipals());
assertEquals(AuthenticationStatus.SUCCESS, result.getStatus());
}
@Test
public void testNonSaslAuthenticationErrored() throws Exception
{
setupMocks();
when(_principalDatabase.verifyPassword("guest", "wrongpassword".toCharArray())).thenReturn(false);
AuthenticationResult result = _manager.authenticate("guest", "wrongpassword");
assertEquals("Principals was not expected size", (long) 0, (long) result.getPrincipals().size());
assertEquals(AuthenticationStatus.ERROR, result.getStatus());
}
@Test
public void testOnCreate() throws Exception
{
setupMocks();
assertTrue("Password file was not created", new File(_passwordFileLocation).exists());
}
@Test
public void testOnDelete() throws Exception
{
setupMocks();
assertTrue("Password file was not created", new File(_passwordFileLocation).exists());
_manager.delete();
assertFalse("Password file was not deleted", new File(_passwordFileLocation).exists());
}
@Test
public void testCreateForInvalidPath() throws Exception
{
setUpPrincipalDatabase();
Map<String,Object> attrs = new HashMap<>();
attrs.put(ConfiguredObject.ID, UUID.randomUUID());
attrs.put(ConfiguredObject.NAME, getTestName());
String path = TMP_FOLDER + File.separator + getTestName() + System.nanoTime() + File.separator + "users";
attrs.put("path", path);
_manager = getPrincipalDatabaseAuthenticationManager(attrs);
try
{
_manager.create();
fail("Creation with invalid path should have failed");
}
catch(IllegalConfigurationException e)
{
assertEquals("Unexpected exception message:" + e.getMessage(),
String.format("Cannot create password file at '%s'", path),
e.getMessage());
}
}
PrincipalDatabaseAuthenticationManager getPrincipalDatabaseAuthenticationManager(final Map<String, Object> attrs)
{
return new PrincipalDatabaseAuthenticationManager(attrs, BrokerTestHelper.createBrokerMock())
{
@Override
protected PrincipalDatabase createDatabase()
{
return _principalDatabase;
}
};
}
private void deletePasswordFileIfExists()
{
File passwordFile = new File(_passwordFileLocation);
if (passwordFile.exists())
{
passwordFile.delete();
}
}
}