diff options
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.java | 287 |
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; + } + +} |