summaryrefslogtreecommitdiff
path: root/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupDatabase.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupDatabase.java')
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupDatabase.java287
1 files changed, 287 insertions, 0 deletions
diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupDatabase.java b/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupDatabase.java
new file mode 100644
index 0000000000..c66e7fd4e4
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/security/group/FileGroupDatabase.java
@@ -0,0 +1,287 @@
+/*
+ * 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<String, Set<String>> _groupToUserMap = new ConcurrentHashMap<String, Set<String>>();
+ private Map<String, Set<String>> _userToGroupMap = new ConcurrentHashMap<String, Set<String>>();
+ private String _groupFile;
+
+ @Override
+ public Set<String> 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<String> getUsersInGroup(String group)
+ {
+ if (group == null)
+ {
+ LOGGER.warn("Requested user set for null group. Returning empty set.");
+ return Collections.emptySet();
+ }
+
+ Set<String> 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<String> 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<String> groups = _userToGroupMap.get(user);
+ if (groups == null)
+ {
+ groups = new ConcurrentSkipListSet<String>();
+ _userToGroupMap.put(user, groups);
+ }
+ groups.add(group);
+
+ update();
+ }
+
+ @Override
+ public synchronized void removeUserFromGroup(String user, String group)
+ {
+ Set<String> 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<String> groups = _userToGroupMap.get(user);
+ if (groups != null)
+ {
+ groups.remove(group);
+ }
+
+ update();
+ }
+
+ @Override
+ public Set<String> getGroupsForUser(String user)
+ {
+ if(user == null)
+ {
+ LOGGER.warn("Requested group set for null user. Returning empty set.");
+ return Collections.emptySet();
+ }
+
+ Set<String> groups = _userToGroupMap.get(user);
+ if (groups == null)
+ {
+ return Collections.emptySet();
+ }
+ else
+ {
+ return Collections.unmodifiableSet(groups);
+ }
+ }
+
+ @Override
+ public synchronized void createGroup(String group)
+ {
+ Set<String> users = new ConcurrentSkipListSet<String>();
+ _groupToUserMap.put(group, users);
+
+ update();
+ }
+
+ @Override
+ public synchronized void removeGroup(String group)
+ {
+ _groupToUserMap.remove(group);
+ for (Set<String> 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();
+ FileInputStream fileInputStream = new FileInputStream(groupFile);
+ try
+ {
+ propertiesFile.load(fileInputStream);
+ }
+ finally
+ {
+ if(fileInputStream != null)
+ {
+ fileInputStream.close();
+ }
+ }
+
+ for (String propertyName : propertiesFile.stringPropertyNames())
+ {
+ validatePropertyNameIsGroupName(propertyName);
+
+ String groupName = propertyName.replaceAll("\\.users$", "");
+ String userString = propertiesFile.getProperty(propertyName);
+
+ final Set<String> userSet = buildUserSetFromCommaSeparateValue(userString);
+
+ _groupToUserMap.put(groupName, userSet);
+
+ for (String userName : userSet)
+ {
+ Set<String> groupsForThisUser = _userToGroupMap.get(userName);
+
+ if (groupsForThisUser == null)
+ {
+ groupsForThisUser = new ConcurrentSkipListSet<String>();
+ _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<String> users = _groupToUserMap.get(group);
+ String userList = StringUtils.join(users, ",");
+
+ propertiesFile.setProperty(group + ".users", userList);
+ }
+
+ String comment = "Written " + new Date();
+ FileOutputStream fileOutputStream = new FileOutputStream(groupFile);
+ try
+ {
+ propertiesFile.store(fileOutputStream, comment);
+ }
+ finally
+ {
+ if(fileOutputStream != null)
+ {
+ fileOutputStream.close();
+ }
+ }
+ }
+
+ 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<String> buildUserSetFromCommaSeparateValue(String userString)
+ {
+ String[] users = userString.split(",");
+ final ConcurrentSkipListSet<String> userSet = new ConcurrentSkipListSet<String>();
+ for (String user : users)
+ {
+ final String trimmed = user.trim();
+ if (!trimmed.isEmpty())
+ {
+ userSet.add(trimmed);
+ }
+ }
+ return userSet;
+ }
+
+}