blob: 410c913846b2eb2a2bf9e83a49451a2e9e2c1a9d [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.model.adapter;
import static org.apache.qpid.server.model.adapter.FileBasedGroupProviderImpl.GROUP_FILE_PROVIDER_TYPE;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.FileOutputStream;
import java.security.Principal;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.common.collect.Sets;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.model.Broker;
import org.apache.qpid.server.model.BrokerTestHelper;
import org.apache.qpid.server.model.ConfiguredObjectFactory;
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.security.group.FileGroupDatabase;
import org.apache.qpid.test.utils.TestFileUtils;
import org.apache.qpid.test.utils.UnitTestBase;
public class FileBasedGroupProviderImplTest extends UnitTestBase
{
private Broker<?> _broker;
private File _groupFile;
private ConfiguredObjectFactory _objectFactory;
@Before
public void setUp() throws Exception
{
_broker = BrokerTestHelper.createBrokerMock();
_objectFactory = _broker.getObjectFactory();
}
@After
public void tearDown() throws Exception
{
try
{
if (_groupFile.exists())
{
_groupFile.delete();
}
}
finally
{
}
}
@Test
public void testValidationOnCreateWithInvalidPath()
{
Map<String, Object> attributes = new HashMap<>();
_groupFile = TestFileUtils.createTempFile(this, "groups");
String groupsFile = _groupFile.getAbsolutePath() + File.separator + "groups";
assertFalse("File should not exist", new File(groupsFile).exists());
attributes.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
attributes.put(FileBasedGroupProvider.PATH, groupsFile);
attributes.put(FileBasedGroupProvider.NAME, getTestName());
try
{
_objectFactory.create(GroupProvider.class, attributes, _broker);
fail("Exception is expected on validation of groups provider with invalid path");
}
catch (IllegalConfigurationException e)
{
assertEquals("Unexpected exception message:" + e.getMessage(),
String.format("Cannot create groups file at '%s'", groupsFile),
e.getMessage());
}
}
@Test
public void testValidationOnCreateWithInvalidGroups()
{
_groupFile = TestFileUtils.createTempFile(this, "groups", "=blah");
Map<String, Object> attributes = new HashMap<>();
String groupsFile = _groupFile.getAbsolutePath();
attributes.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
attributes.put(FileBasedGroupProvider.PATH, groupsFile);
attributes.put(FileBasedGroupProvider.NAME, getTestName());
try
{
_objectFactory.create(GroupProvider.class, attributes, _broker);
fail("Exception is expected on validation of groups provider with invalid group file");
}
catch (IllegalConfigurationException e)
{
assertEquals("Unexpected exception message:" + e.getMessage(),
String.format("Cannot load groups from '%s'", groupsFile),
e.getMessage());
}
}
@Test
public void testExistingGroupFile() throws Exception
{
Map<String, Set<String>> input = new HashMap<>();
input.put("super", Sets.newHashSet("root"));
_groupFile = createTemporaryGroupFile(input);
Map<String, Object> providerAttrs = new HashMap<>();
String groupsFile = _groupFile.getAbsolutePath();
providerAttrs.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
providerAttrs.put(FileBasedGroupProvider.PATH, groupsFile);
providerAttrs.put(FileBasedGroupProvider.NAME, getTestName());
@SuppressWarnings("unchecked")
GroupProvider<?> provider = _objectFactory.create(GroupProvider.class, providerAttrs, _broker);
Set<Principal> adminGroups = provider.getGroupPrincipalsForUser(() -> "root");
assertThat("root has unexpected group membership",
adminGroups.stream().map(Principal::getName).collect(Collectors.toSet()),
containsInAnyOrder("super"));
Collection<Group> groups = provider.getChildren(Group.class);
assertThat(groups.size(), is(equalTo(1)));
Group<?> superGroup = groups.iterator().next();
assertThat(superGroup.getName(), is(equalTo("super")));
Collection<GroupMember> members = superGroup.getChildren(GroupMember.class);
assertThat(members.size(), is(equalTo(1)));
GroupMember rootMember = members.iterator().next();
assertThat(rootMember.getName(), is(equalTo("root")));
}
@Test
public void testGetGroupPrincipalsForUserCaseAware() throws Exception
{
Map<String, Set<String>> input = new HashMap<>();
input.put("super", Sets.newHashSet("root"));
_groupFile = createTemporaryGroupFile(input);
Map<String, Object> providerAttrs = new HashMap<>();
String groupsFile = _groupFile.getAbsolutePath();
providerAttrs.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
providerAttrs.put(FileBasedGroupProvider.PATH, groupsFile);
providerAttrs.put(FileBasedGroupProvider.NAME, getTestName());
@SuppressWarnings("unchecked")
GroupProvider<?> provider = _objectFactory.create(GroupProvider.class, providerAttrs, _broker);
assertThat(provider, is(instanceOf(FileBasedGroupProvider.class)));
assertThat(((FileBasedGroupProvider) provider).isCaseSensitive(), is(true));
Set<Principal> adminGroups = provider.getGroupPrincipalsForUser(() -> "Root");
assertThat("No group should be found when caseSensitive=true",
adminGroups.stream().map(Principal::getName).collect(Collectors.toSet()),
is(empty()));
provider.setAttributes(Collections.singletonMap("caseSensitive", false));
assertThat(((FileBasedGroupProvider) provider).isCaseSensitive(), is(false));
Set<Principal> adminGroups2 = provider.getGroupPrincipalsForUser(() -> "Root");
assertThat("root has unexpected group membership",
adminGroups2.stream().map(Principal::getName).collect(Collectors.toSet()),
containsInAnyOrder("super"));
}
@Test
public void testAddGroupAndMember() throws Exception
{
_groupFile = createTemporaryGroupFile(Collections.emptyMap());
Map<String, Object> providerAttrs = new HashMap<>();
String groupsFile = _groupFile.getAbsolutePath();
providerAttrs.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
providerAttrs.put(FileBasedGroupProvider.PATH, groupsFile);
providerAttrs.put(FileBasedGroupProvider.NAME, getTestName());
@SuppressWarnings("unchecked")
GroupProvider<?> provider = _objectFactory.create(GroupProvider.class, providerAttrs, _broker);
assertThat(provider.getChildren(Group.class).size(), is(equalTo(0)));
final Map<String, Object> groupAttrs = Collections.singletonMap(Group.NAME, "supers");
Group superGroup = provider.createChild(Group.class, groupAttrs);
assertThat(superGroup.getName(), is(equalTo("supers")));
final Map<String, Object> memberAttrs = Collections.singletonMap(GroupMember.NAME, "root");
GroupMember rootMember = (GroupMember) superGroup.createChild(GroupMember.class, memberAttrs);
assertThat(rootMember.getName(), is(equalTo("root")));
}
@Test
public void testRemoveGroupAndMember() throws Exception
{
Map<String, Set<String>> input = new HashMap<>();
input.put("supers", Sets.newHashSet("root"));
input.put("operators", Sets.newHashSet("operator", "root"));
_groupFile = createTemporaryGroupFile(input);
Map<String, Object> providerAttrs = new HashMap<>();
String groupsFile = _groupFile.getAbsolutePath();
providerAttrs.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
providerAttrs.put(FileBasedGroupProvider.PATH, groupsFile);
providerAttrs.put(FileBasedGroupProvider.NAME, getTestName());
@SuppressWarnings("unchecked")
GroupProvider<?> provider = _objectFactory.create(GroupProvider.class, providerAttrs, _broker);
assertThat(provider.getChildren(Group.class).size(), is(equalTo(2)));
Group operators = provider.getChildByName(Group.class, "operators");
GroupMember rootMember = (GroupMember) operators.getChildByName(GroupMember.class, "root");
rootMember.delete();
assertThat(operators.getChildren(GroupMember.class).size(), is(equalTo(1)));
Group supers = provider.getChildByName(Group.class, "supers");
assertThat(supers.getChildren(GroupMember.class).size(), is(equalTo(1)));
operators.delete();
assertThat(provider.getChildren(Group.class).size(), is(equalTo(1)));
}
@Test
public void testGroupAndMemberDurability() throws Exception
{
_groupFile = createTemporaryGroupFile(Collections.emptyMap());
Map<String, Object> providerAttrs = new HashMap<>();
String groupsFile = _groupFile.getAbsolutePath();
providerAttrs.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
providerAttrs.put(FileBasedGroupProvider.PATH, groupsFile);
providerAttrs.put(FileBasedGroupProvider.NAME, getTestName());
{
@SuppressWarnings("unchecked") final GroupProvider<?> provider =
_objectFactory.create(GroupProvider.class, providerAttrs, _broker);
assertThat(provider.getChildren(Group.class).size(), is(equalTo(0)));
final Map<String, Object> groupAttrs = Collections.singletonMap(Group.NAME, "group");
final Group group = provider.createChild(Group.class, groupAttrs);
final Map<String, Object> memberAttrs = Collections.singletonMap(GroupMember.NAME, "user");
group.createChild(GroupMember.class, memberAttrs);
provider.close();
}
{
@SuppressWarnings("unchecked") final GroupProvider<?> provider =
_objectFactory.create(GroupProvider.class, providerAttrs, _broker);
assertThat(provider.getChildren(Group.class).size(), is(equalTo(1)));
final Group group = provider.getChildByName(Group.class, "group");
assertThat(group.getChildren(GroupMember.class).size(), is(equalTo(1)));
final GroupMember member = (GroupMember) group.getChildByName(GroupMember.class, "user");
member.delete();
provider.close();
}
{
@SuppressWarnings("unchecked") final GroupProvider<?> provider =
_objectFactory.create(GroupProvider.class, providerAttrs, _broker);
final Group group = provider.getChildByName(Group.class, "group");
assertThat(group.getChildren(GroupMember.class).size(), is(equalTo(0)));
group.delete();
provider.close();
}
{
@SuppressWarnings("unchecked") final GroupProvider<?> provider =
_objectFactory.create(GroupProvider.class, providerAttrs, _broker);
assertThat(provider.getChildren(Group.class).size(), is(equalTo(0)));
provider.close();
}
}
@Test
public void testProvideDelete() throws Exception
{
_groupFile = createTemporaryGroupFile(Collections.emptyMap());
Map<String, Object> providerAttrs = new HashMap<>();
String groupsFile = _groupFile.getAbsolutePath();
providerAttrs.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
providerAttrs.put(FileBasedGroupProvider.PATH, groupsFile);
providerAttrs.put(FileBasedGroupProvider.NAME, getTestName());
@SuppressWarnings("unchecked") final GroupProvider<?> provider =
_objectFactory.create(GroupProvider.class, providerAttrs, _broker);
provider.delete();
assertThat(_groupFile.exists(), is(equalTo(false)));
}
@Test
public void testSharingUnderlyingFileDisallowed() throws Exception
{
_groupFile = createTemporaryGroupFile(Collections.emptyMap());
String groupsFile = _groupFile.getAbsolutePath();
Map<String, Object> providerAttrs1 = new HashMap<>();
providerAttrs1.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
providerAttrs1.put(FileBasedGroupProvider.PATH, groupsFile);
providerAttrs1.put(FileBasedGroupProvider.NAME, getTestName() + "1");
@SuppressWarnings("unchecked")
GroupProvider<?> provider = _objectFactory.create(GroupProvider.class, providerAttrs1, _broker);
when(_broker.getChildren(GroupProvider.class)).thenReturn(Collections.singletonList(provider));
try
{
Map<String, Object> providerAttrs2 = new HashMap<>();
providerAttrs2.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
providerAttrs2.put(FileBasedGroupProvider.PATH, groupsFile);
providerAttrs2.put(FileBasedGroupProvider.NAME, getTestName() + "2");
_objectFactory.create(GroupProvider.class, providerAttrs2, _broker);
fail("Exception not thrown");
}
catch (IllegalConfigurationException e)
{
// PASS
}
}
@Test
public void testCreateDuplicateGroup() throws Exception
{
_groupFile = createTemporaryGroupFile(Collections.emptyMap());
Map<String, Object> providerAttrs = new HashMap<>();
String groupsFile = _groupFile.getAbsolutePath();
providerAttrs.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
providerAttrs.put(FileBasedGroupProvider.PATH, groupsFile);
providerAttrs.put(FileBasedGroupProvider.NAME, getTestName());
@SuppressWarnings("unchecked")
GroupProvider<?> provider = _objectFactory.create(GroupProvider.class, providerAttrs, _broker);
assertThat(provider.getChildren(Group.class).size(), is(equalTo(0)));
final Map<String, Object> groupAttrs = Collections.singletonMap(Group.NAME, "supers");
Group superGroup = provider.createChild(Group.class, groupAttrs);
assertThat(superGroup.getName(), is(equalTo("supers")));
assertThrows("Group member with name root1 already exists", IllegalConfigurationException.class, ()->provider.createChild(Group.class, groupAttrs));
}
@Test
public void testCreateDuplicateMember() throws Exception
{
_groupFile = createTemporaryGroupFile(Collections.emptyMap());
Map<String, Object> providerAttrs = new HashMap<>();
String groupsFile = _groupFile.getAbsolutePath();
providerAttrs.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
providerAttrs.put(FileBasedGroupProvider.PATH, groupsFile);
providerAttrs.put(FileBasedGroupProvider.NAME, getTestName());
@SuppressWarnings("unchecked")
GroupProvider<?> provider = _objectFactory.create(GroupProvider.class, providerAttrs, _broker);
assertThat(provider.getChildren(Group.class).size(), is(equalTo(0)));
final Map<String, Object> groupAttrs = Collections.singletonMap(Group.NAME, "supers");
Group superGroup = provider.createChild(Group.class, groupAttrs);
assertThat(superGroup.getName(), is(equalTo("supers")));
final Map<String, Object> memberAttrs1 = Collections.singletonMap(GroupMember.NAME, "root1");
GroupMember rootMember = (GroupMember) superGroup.createChild(GroupMember.class, memberAttrs1);
assertThat(rootMember.getName(), is(equalTo("root1")));
assertThrows("Group member with name root1 already exists", IllegalConfigurationException.class, ()->superGroup.createChild(GroupMember.class, memberAttrs1));
assertThat(superGroup.getChildren(GroupMember.class).size(), is(equalTo(1)));
}
@Test
public void testCreateGroups() throws Exception
{
_groupFile = createTemporaryGroupFile(Collections.emptyMap());
Map<String, Object> providerAttrs = new HashMap<>();
String groupsFile = _groupFile.getAbsolutePath();
providerAttrs.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
providerAttrs.put(FileBasedGroupProvider.PATH, groupsFile);
providerAttrs.put(FileBasedGroupProvider.NAME, getTestName());
@SuppressWarnings("unchecked")
GroupProvider<?> provider = _objectFactory.create(GroupProvider.class, providerAttrs, _broker);
assertThat(provider.getChildren(Group.class).size(), is(equalTo(0)));
final Map<String, Object> groupAttrs = Collections.singletonMap(Group.NAME, "supers");
Group superGroup = provider.createChild(Group.class, groupAttrs);
assertThat(superGroup.getName(), is(equalTo("supers")));
final Map<String, Object> groupAttrs2 = Collections.singletonMap(Group.NAME, "Supers");
Group superGroup2 = provider.createChild(Group.class, groupAttrs2);
assertThat(superGroup2.getName(), is(equalTo("Supers")));
assertThat(provider.getChildren(Group.class).size(), is(equalTo(2)));
}
@Test(expected = IllegalConfigurationException.class)
public void testCreateMembers() throws Exception
{
_groupFile = createTemporaryGroupFile(Collections.emptyMap());
Map<String, Object> providerAttrs = new HashMap<>();
String groupsFile = _groupFile.getAbsolutePath();
providerAttrs.put(FileBasedGroupProvider.TYPE, GROUP_FILE_PROVIDER_TYPE);
providerAttrs.put(FileBasedGroupProvider.PATH, groupsFile);
providerAttrs.put(FileBasedGroupProvider.NAME, getTestName());
@SuppressWarnings("unchecked")
GroupProvider<?> provider = _objectFactory.create(GroupProvider.class, providerAttrs, _broker);
assertThat(provider.getChildren(Group.class).size(), is(equalTo(0)));
final Map<String, Object> groupAttrs = Collections.singletonMap(Group.NAME, "supers");
Group superGroup = provider.createChild(Group.class, groupAttrs);
assertThat(superGroup.getName(), is(equalTo("supers")));
final Map<String, Object> memberAttrs1 = Collections.singletonMap(GroupMember.NAME, "root1");
GroupMember rootMember = (GroupMember) superGroup.createChild(GroupMember.class, memberAttrs1);
assertThat(rootMember.getName(), is(equalTo("root1")));
superGroup.createChild(GroupMember.class, memberAttrs1);
assertThrows("Group member with name root1 already exists",
IllegalConfigurationException.class,
() -> superGroup.createChild(GroupMember.class, memberAttrs1));
assertThat(superGroup.getChildren(GroupMember.class).size(), is(equalTo(1)));
}
private File createTemporaryGroupFile(Map<String, Set<String>> groups) throws Exception
{
File groupFile = File.createTempFile("group", "grp");
groupFile.deleteOnExit();
Properties props = new Properties();
Map<String, String> m = groups.entrySet()
.stream()
.collect(Collectors.toMap(e -> e.getKey() + ".users",
e -> e.getValue()
.stream()
.collect(Collectors.joining(","))));
props.putAll(m);
try (final FileOutputStream out = new FileOutputStream(groupFile))
{
props.store(out, "test group file");
}
return groupFile;
}
}