/* * Copyright (C) 2015 hops.io. * * Licensed 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 io.hops.security; import com.google.common.collect.Lists; import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import com.googlecode.concurrentlinkedhashmap.EvictionListener; import io.hops.metadata.hdfs.entity.Group; import io.hops.metadata.hdfs.entity.User; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; public class UsersGroupsCache { private ConcurrentLinkedHashMap<String, List<String>> usersToGroups; private ConcurrentLinkedHashMap<String, List<String>> groupsToUsers; private ConcurrentLinkedHashMap<Integer, String> idsToUsers; private ConcurrentLinkedHashMap<String, Integer> usersToIds; private ConcurrentLinkedHashMap<Integer, String> idsToGroups; private ConcurrentLinkedHashMap<String, Integer> groupsToIds; private EvictionListener usersToGroupsEvictionListener = new EvictionListener() { @Override public void onEviction(Object key, Object value) { String user = (String) key; List<String> groups = (List<String>) value; removeUser(user); cleanCacheOnUserRemoval(user, groups); } }; private EvictionListener groupsToUsersEvictionListener = new EvictionListener() { @Override public void onEviction(Object key, Object value) { String group = (String) key; List<String> users = (List<String>) value; removeGroup(group); for(String user: users){ removeUser(user); } } }; private EvictionListener idsToUsersEvictionListener = new EvictionListener() { @Override public void onEviction(Object key, Object value) { removeUser(new User((Integer)key, (String)value)); } }; private EvictionListener usersToIdsEvictionListener = new EvictionListener() { @Override public void onEviction(Object key, Object value) { removeUser(new User((Integer)value, (String)key)); } }; private EvictionListener idsToGroupsEvictionListener = new EvictionListener () { @Override public void onEviction(Object key, Object value) { removeGroup(new Group((Integer)key, (String) value), true); } }; private EvictionListener groupsToIdsEvictionListener = new EvictionListener () { @Override public void onEviction(Object key, Object value) { removeGroup(new Group((Integer)value, (String) key), true); } }; public UsersGroupsCache(int lrumax){ usersToGroups = new ConcurrentLinkedHashMap.Builder().maximumWeightedCapacity (lrumax).listener(usersToGroupsEvictionListener).build(); groupsToUsers = new ConcurrentLinkedHashMap.Builder().maximumWeightedCapacity (lrumax).listener(groupsToUsersEvictionListener).build(); idsToUsers = new ConcurrentLinkedHashMap.Builder().maximumWeightedCapacity (lrumax).listener(idsToUsersEvictionListener).build(); usersToIds = new ConcurrentLinkedHashMap.Builder().maximumWeightedCapacity (lrumax).listener(usersToIdsEvictionListener).build(); idsToGroups = new ConcurrentLinkedHashMap.Builder().maximumWeightedCapacity (lrumax).listener(idsToGroupsEvictionListener).build(); groupsToIds = new ConcurrentLinkedHashMap.Builder().maximumWeightedCapacity (lrumax).listener(groupsToIdsEvictionListener).build(); } List<String> getGroups(String user){ if(user == null) return null; return usersToGroups.get(user); } Set<String> knownUsers(){ return usersToGroups.keySet(); } void removeUser(User user){ removeUser(user.getId()); removeUser(user.getName()); } void removeUser(String userName){ if(userName == null) return; Integer userId = usersToIds.remove(userName); if(userId != null){ idsToUsers.remove(userId); } cleanCacheOnUserRemoval(userName); } void removeUser(Integer userId){ if(userId == null) return; String userName = idsToUsers.remove(userId); if(userName != null) { usersToIds.remove(userName); cleanCacheOnUserRemoval(userName); } } void removeGroup(Group group){ removeGroup(group, false); } void removeGroup(String groupName){ removeGroup(groupName, false); } void removeGroup(Integer groupId){ removeGroup(groupId, false); } private void removeGroup(Group group, boolean removeUsers){ removeGroup(group.getId(), removeUsers); removeGroup(group.getName(), removeUsers); } private void removeGroup(String groupName, boolean removeUsers){ if(groupName == null) return; Integer groupId = groupsToIds.remove(groupName); if(groupId != null){ idsToGroups.remove(groupId); } cleanCacheOnGroupRemoval(groupName, removeUsers); } private void removeGroup(Integer groupId, boolean removeUsers){ if(groupId == null) return; String groupName = idsToGroups.remove(groupId); if(groupName != null){ groupsToIds.remove(groupName); cleanCacheOnGroupRemoval(groupName, removeUsers); } } private void cleanCacheOnUserRemoval(String userName){ cleanCacheOnUserRemoval(userName, usersToGroups.remove(userName)); } private void cleanCacheOnUserRemoval(String userName, List<String> groups){ if(groups == null) return; for(String group : groups){ List<String> groupUsers = groupsToUsers.get(group); if(groupUsers != null){ groupUsers.remove(userName); if(groupUsers.isEmpty()){ removeGroup(group); } } } } private void cleanCacheOnGroupRemoval(String groupName, boolean removeUsers){ List<String> users = groupsToUsers.remove(groupName); if(users == null) return; for(String user : users){ if(removeUsers){ removeUser(user); }else { List<String> userGroups = usersToGroups.get(user); if(userGroups != null){ userGroups.remove(groupName); if(userGroups.isEmpty()){ removeUser(user); } } } } } List<String> addUserGroups(User user, List<Group> groups){ List<String> groupNames = Lists.newArrayListWithExpectedSize(groups.size()); for(Group group : groups){ groupNames.add(group.getName()); addGroup(group); groupsToUsers.get(group.getName()).add(user.getName()); } addUser(user); usersToGroups.put(user.getName(), groupNames); return groupNames; } void appendUserGroups(String user, Collection<String> groups){ List<String> currentGroups = usersToGroups.get(user); if(currentGroups == null){ currentGroups = new ArrayList<String>(); } Set<String> newGroups = new HashSet<String>(); newGroups.addAll(groups); newGroups.removeAll(currentGroups); for(String group : newGroups){ groupsToUsers.putIfAbsent(group, new ArrayList<String>()); groupsToUsers.get(group).add(user); } currentGroups.addAll(newGroups); usersToGroups.put(user, currentGroups); } void addUser(User user){ if(user == null) return; idsToUsers.putIfAbsent(user.getId(), user.getName()); usersToIds.putIfAbsent(user.getName(), user.getId()); } void addGroup(Group group){ if (group == null) return; idsToGroups.putIfAbsent(group.getId(), group.getName()); groupsToIds.putIfAbsent(group.getName(), group.getId()); groupsToUsers.putIfAbsent(group.getName(), new ArrayList<String>()); } Integer getUserId(String userName){ if(userName == null) return null; return usersToIds.get(userName); } Integer getGroupId(String groupName){ if(groupName == null) return null; return groupsToIds.get(groupName); } String getUserName(Integer userId) { if(userId == null) return null; return idsToUsers.get(userId); } String getGroupName(Integer groupId) { if(groupId == null) return null; return idsToGroups.get(groupId); } void clear(){ usersToGroups.clear(); groupsToUsers.clear(); idsToUsers.clear(); usersToIds.clear(); idsToGroups.clear(); groupsToIds.clear(); } }