001 package org.hackystat.sensorshell.usermap; 002 003 import java.io.File; 004 import java.util.ArrayList; 005 import java.util.HashMap; 006 import java.util.HashSet; 007 import java.util.List; 008 import java.util.Locale; 009 import java.util.Map; 010 import java.util.Set; 011 012 import javax.xml.bind.JAXBContext; 013 import javax.xml.bind.JAXBException; 014 import javax.xml.bind.Unmarshaller; 015 016 import org.hackystat.sensorbase.client.SensorBaseClient; 017 import org.hackystat.sensorshell.usermap.resource.jaxb.ObjectFactory; 018 import org.hackystat.sensorshell.usermap.resource.jaxb.User; 019 import org.hackystat.sensorshell.usermap.resource.jaxb.Usermap; 020 import org.hackystat.sensorshell.usermap.resource.jaxb.Usermaps; 021 import org.hackystat.utilities.home.HackystatUserHome; 022 023 /** 024 * Gets hackystat information for a specific tool by parsing a predefined xml file located in the 025 * user's .hackystat/usermap directory. The file should be named 'UserMaps.xml'. It is important to 026 * keep in mind that this code is working client-side, not on the Hackystat server. 027 * 028 * <p> 029 * Instantiation of <code>UserMap</code> will parse the assumed xml file and derive a mapping from 030 * specific tool accounts for users to their Hackystat information. 031 * 032 * <p> 033 * Developers should not have to directly use this class at all. They should only have to interface 034 * with the <code>SensorShellMap</code> class which manages an instance of this class. 035 * <p> 036 * If the UserMap.xml file does not exist, an empty UserMap is returned. 037 * <p> 038 * The "tool" string is always compared in a case-insensitive fashion. The User, ToolAccount, 039 * Password, and Sensorbase are case-sensitive. 040 * 041 * @author Julie Ann Sakuda 042 */ 043 class UserMap { 044 045 /** Keys in the user map used to access information. */ 046 enum UserMapKey { 047 /** The key for accessing the user value. */ 048 USER, 049 /** The key for accessing the password value. */ 050 PASSWORD, 051 /** The key for accessing the sensorbase value. */ 052 SENSORBASE 053 } 054 055 /** Three-key map for storing mappings for tool, toolaccount, user, password, and sensorbase. */ 056 private Map<String, Map<String, Map<UserMapKey, String>>> userMappings; 057 058 /** The (potentially non-existent) UserMap.xml file. */ 059 private File userMapFile = null; 060 061 /** 062 * Creates the user map and initializes the user mappings. Returns an empty UserMap if the 063 * UserMap.xml file cannot be found. 064 * 065 * @throws SensorShellMapException Thrown if the UserMap.xml cannot be parsed. 066 */ 067 UserMap() throws SensorShellMapException { 068 this.userMappings = new HashMap<String, Map<String, Map<UserMapKey, String>>>(); 069 070 File sensorPropsDir = new File(HackystatUserHome.getHome(), "/.hackystat/sensorshell/"); 071 String userMapFilePath = sensorPropsDir.getAbsolutePath() + "/usermap/UserMap.xml"; 072 this.userMapFile = new File(userMapFilePath); 073 if (userMapFile.exists()) { 074 this.loadUserMapFile(userMapFile); 075 } 076 } 077 078 /** 079 * A method that will check all of the mappings associated with the given tool and throw an 080 * error if (a) any of the sensorbases could not be contacted, and/or (b) any of the users 081 * did not appear to be registered. 082 * @param tool The tool of interest. 083 * @throws SensorShellMapException The exception thrown if any errors are discovered. 084 */ 085 public void validateHackystatUsers(String tool) throws SensorShellMapException { 086 List<String> invalidSensorBases = new ArrayList<String>(); 087 List<String> invalidUsers = new ArrayList<String>(); 088 Map<String, Map<UserMapKey, String>> toolAccountMap = userMappings.get(tool); 089 // If there are no mappings for this tool, then return. 090 if (toolAccountMap == null) { 091 return; 092 } 093 // Check all mappings associated with this tool. 094 for (Map<UserMapKey, String> userMap : toolAccountMap.values()) { 095 if (userMap == null) { 096 return; 097 } 098 String user = userMap.get(UserMapKey.USER); 099 String sensorbase = userMap.get(UserMapKey.SENSORBASE); 100 String password = userMap.get(UserMapKey.PASSWORD); 101 // Ignore this entire entry if we've already determined the sensorbase to be invalid. 102 if (invalidSensorBases.contains(sensorbase)) { 103 continue; 104 } 105 // Now check to see if it's validated. 106 if (!SensorBaseClient.isHost(sensorbase)) { 107 invalidSensorBases.add(sensorbase); 108 // No use doing anything else. 109 continue; 110 } 111 // If we get here, it's a valid sensorbase. Now check to see if the user is OK. 112 if (!SensorBaseClient.isRegistered(sensorbase, user, password)) { 113 invalidUsers.add(user); 114 } 115 } 116 117 // If all mappings are OK, we can return right now. 118 if (invalidSensorBases.isEmpty() && invalidUsers.isEmpty()) { 119 return; 120 } 121 122 // Otherwise throw an exception indicating the problem(s). Create the message. 123 StringBuffer buff = new StringBuffer(20); 124 buff.append("Errors found in ").append(this.userMapFile.getAbsolutePath()).append(". "); 125 if (!invalidSensorBases.isEmpty()) { 126 buff.append("The following SensorBase hosts were not found or available: "); 127 for (String badBase : invalidSensorBases) { 128 buff.append(badBase).append(' '); 129 } 130 } 131 if (!invalidUsers.isEmpty()) { 132 buff.append("The following users did not appear to be valid: "); 133 for (String badUser : invalidUsers) { 134 buff.append(badUser).append(' '); 135 } 136 } 137 throw new SensorShellMapException(buff.toString()); 138 } 139 140 /** 141 * This constructor initializes the user mappings from a given file. This version of the 142 * constructor is mainly useful for junit test cases that want to verify the content of a dummy 143 * UserMap.xml file. 144 * 145 * @param userMapFile A UserMap.xml file. 146 * @exception SensorShellMapException Occurs if UserMap.xml is invalid. 147 */ 148 UserMap(File userMapFile) throws SensorShellMapException { 149 this.userMappings = new HashMap<String, Map<String, Map<UserMapKey, String>>>(); 150 this.loadUserMapFile(userMapFile); 151 } 152 153 /** 154 * Uses JAXB to read through the UserMap.xml file and add all information to the user map. 155 * 156 * @param userMapFile The UserMap.xml file. 157 * @throws SensorShellMapException Thrown if JAXB encounters an error reading the xml file. 158 */ 159 private void loadUserMapFile(File userMapFile) throws SensorShellMapException { 160 try { 161 JAXBContext context = JAXBContext.newInstance(ObjectFactory.class); 162 Unmarshaller unmarshaller = context.createUnmarshaller(); 163 164 Usermaps usermaps = (Usermaps) unmarshaller.unmarshal(userMapFile); 165 List<Usermap> usermapList = usermaps.getUsermap(); 166 for (Usermap usermap : usermapList) { 167 // user lowercase tool for case-insensitive comparison 168 String tool = usermap.getTool().toLowerCase(); 169 List<User> userList = usermap.getUser(); 170 for (User user : userList) { 171 // Lowercase the toolaccount for case-insensitive comparison. 172 String toolAccount = user.getToolaccount().toLowerCase(); 173 174 String userName = user.getUser(); 175 this.put(tool, toolAccount, UserMapKey.USER, userName); 176 177 String password = user.getPassword(); 178 this.put(tool, toolAccount, UserMapKey.PASSWORD, password); 179 180 String sensorbase = user.getSensorbase(); 181 this.put(tool, toolAccount, UserMapKey.SENSORBASE, sensorbase); 182 } 183 } 184 } 185 catch (JAXBException e) { 186 throw new SensorShellMapException("Error reading UserMap.xml file.", e); 187 } 188 } 189 190 /** 191 * Puts a single user map entry for user, password, or sensorbase into the three-key map. 192 * 193 * @param tool The tool the mapping is for. 194 * @param toolAccount The tool account for the mapping. 195 * @param key Either user, password, or sensorbase key. 196 * @param value The value associated with the key given. 197 */ 198 private void put(String tool, String toolAccount, UserMapKey key, String value) { 199 if (!this.userMappings.containsKey(tool.toLowerCase(Locale.ENGLISH))) { 200 this.userMappings.put(tool.toLowerCase(Locale.ENGLISH), 201 new HashMap<String, Map<UserMapKey, String>>()); 202 } 203 204 Map<String, Map<UserMapKey, String>> toolMapping = this.userMappings.get(tool); 205 if (!toolMapping.containsKey(toolAccount.toLowerCase(Locale.ENGLISH))) { 206 toolMapping.put(toolAccount.toLowerCase(Locale.ENGLISH), 207 new HashMap<UserMapKey, String>()); 208 } 209 210 Map<UserMapKey, String> toolAccountMapping = 211 toolMapping.get(toolAccount.toLowerCase(Locale.ENGLISH)); 212 toolAccountMapping.put(key, value); 213 } 214 215 /** 216 * Gets the value of the given <code>UserMapKey</code> associated with the given tool and 217 * toolAccount, or null if not found. 218 * 219 * @param tool The tool name. This is case-insensitive. 220 * @param toolAccount The tool account name. This is case-insensitive. 221 * @param key The USER, PASSWORD, or SENSORBASE key representing the desired value. 222 * @return Returns the value matching the criteria given or null if none can be found. 223 */ 224 String get(String tool, String toolAccount, UserMapKey key) { 225 if (tool == null) { 226 return null; 227 } 228 String lowercasetool = tool.toLowerCase(Locale.ENGLISH); 229 String lowercaseaccount = toolAccount.toLowerCase(Locale.ENGLISH); 230 if (this.userMappings.containsKey(lowercasetool)) { 231 Map<String, Map<UserMapKey, String>> toolMapping = this.userMappings.get(lowercasetool); 232 if (toolMapping.containsKey(lowercaseaccount)) { 233 Map<UserMapKey, String> toolAccountMapping = toolMapping.get(lowercaseaccount); 234 return toolAccountMapping.get(key); 235 } 236 } 237 return null; 238 } 239 240 /** 241 * Returns true if there is a defined userKey for the given Tool and ToolAccount. 242 * 243 * @param tool A Tool, such as "Jira". 244 * @param toolAccount A Tool account, such as "johnson". 245 * @return True if the toolAccount is defined for the given tool in this userMap. 246 */ 247 boolean hasUser(String tool, String toolAccount) { 248 // Lowercase the tool if possible. 249 if (tool == null) { 250 return false; 251 } 252 String lowercaseTool = tool.toLowerCase(Locale.ENGLISH); 253 String lowercaseAccount = toolAccount.toLowerCase(Locale.ENGLISH); 254 if (this.userMappings.containsKey(lowercaseTool)) { 255 Map<String, Map<UserMapKey, String>> toolMapping = this.userMappings.get(lowercaseTool); 256 return toolMapping.containsKey(lowercaseAccount); 257 } 258 return false; 259 } 260 261 /** 262 * Returns the usermap.xml file path, which may or may not exist. 263 * 264 * @return The usermap.xml file path. 265 */ 266 String getUserMapFile() { 267 return this.userMapFile.getAbsolutePath(); 268 } 269 270 /** 271 * Returns the set of tool account names for the passed tool. 272 * 273 * @param tool The tool of interest. 274 * @return The tool account names. 275 */ 276 Set<String> getToolAccounts(String tool) { 277 String lowerCaseTool = tool.toLowerCase(Locale.ENGLISH); 278 Set<String> toolAccounts = new HashSet<String>(); 279 Map<String, Map<UserMapKey, String>> toolMapping = this.userMappings.get(lowerCaseTool); 280 if (toolMapping == null) { 281 return toolAccounts; 282 } 283 else { 284 toolAccounts.addAll(toolMapping.keySet()); 285 return toolAccounts; 286 } 287 } 288 }