1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package org.apache.jmeter.protocol.http.control; 20 21 import java.io.BufferedReader; 22 import java.io.File; 23 import java.io.FileReader; 24 import java.io.FileWriter; 25 import java.io.IOException; 26 import java.io.PrintWriter; 27 import java.io.Serializable; 28 import java.net.MalformedURLException; 29 import java.net.URL; 30 import java.util.ArrayList; 31 import java.util.Collection; 32 import java.util.NoSuchElementException; 33 import java.util.StringTokenizer; 34 35 import org.apache.commons.io.IOUtils; 36 import org.apache.jmeter.config.ConfigElement; 37 import org.apache.jmeter.config.ConfigTestElement; 38 import org.apache.jmeter.protocol.http.util.HTTPConstants; 39 import org.apache.jmeter.testelement.property.CollectionProperty; 40 import org.apache.jmeter.testelement.property.PropertyIterator; 41 import org.apache.jmeter.testelement.property.TestElementProperty; 42 import org.apache.jmeter.util.JMeterUtils; 43 import org.apache.jorphan.logging.LoggingManager; 44 import org.apache.log.Logger; 45 46 // For Unit tests, @see TestAuthManager 47 48 /** 49 * This class provides a way to provide Authorization in jmeter requests. The 50 * format of the authorization file is: URL user pass where URL is an HTTP URL, 51 * user a username to use and pass the appropriate password. 52 * 53 */ 54 public class AuthManager extends ConfigTestElement implements ConfigElement, Serializable { 55 private static final long serialVersionUID = 233L; 56 57 private static final Logger log = LoggingManager.getLoggerForClass(); 58 59 private final static String AUTH_LIST = "AuthManager.auth_list"; //$NON-NLS-1$ 60 61 private final static String[] COLUMN_RESOURCE_NAMES = { 62 "auth_base_url", //$NON-NLS-1$ 63 "username", //$NON-NLS-1$ 64 "password", //$NON-NLS-1$ 65 "domain", //$NON-NLS-1$ 66 "realm", //$NON-NLS-1$ 67 }; 68 69 // Column numbers - must agree with order above 70 public final static int COL_URL = 0; 71 public final static int COL_USERNAME = 1; 72 public final static int COL_PASSWORD = 2; 73 public final static int COL_DOMAIN = 3; 74 public final static int COL_REALM = 4; 75 76 private final static int COLUMN_COUNT = COLUMN_RESOURCE_NAMES.length; 77 78 /** 79 * Default Constructor. 80 */ 81 public AuthManager() { 82 setProperty(new CollectionProperty(AUTH_LIST, new ArrayList())); 83 } 84 85 public void clear() { 86 super.clear(); 87 setProperty(new CollectionProperty(AUTH_LIST, new ArrayList())); 88 } 89 90 /** 91 * Update an authentication record. 92 */ 93 public void set(int index, String url, String user, String pass, String domain, String realm) { 94 Authorization auth = new Authorization(url, user, pass, domain, realm); 95 if (index >= 0) { 96 getAuthObjects().set(index, new TestElementProperty(auth.getName(), auth)); 97 } else { 98 getAuthObjects().addItem(auth); 99 } 100 } 101 102 public CollectionProperty getAuthObjects() { 103 return (CollectionProperty) getProperty(AUTH_LIST); 104 } 105 106 public int getColumnCount() { 107 return COLUMN_COUNT; 108 } 109 110 public String getColumnName(int column) { 111 return COLUMN_RESOURCE_NAMES[column]; 112 } 113 114 public Class getColumnClass(int column) { 115 return COLUMN_RESOURCE_NAMES[column].getClass(); 116 } 117 118 public Authorization getAuthObjectAt(int row) { 119 return (Authorization) getAuthObjects().get(row).getObjectValue(); 120 } 121 122 public boolean isEditable() { 123 return true; 124 } 125 126 public Class getGuiClass() { 127 return org.apache.jmeter.protocol.http.gui.AuthPanel.class; 128 } 129 130 public Collection getAddList() { 131 return null; 132 } 133 134 /** 135 * Return the record at index i 136 */ 137 public Authorization get(int i) { 138 return (Authorization) getAuthObjects().get(i).getObjectValue(); 139 } 140 141 public String getAuthHeaderForURL(URL url) { 142 Authorization auth = getAuthForURL(url); 143 if (auth == null) { 144 return null; 145 } 146 return auth.toBasicHeader(); 147 } 148 149 public Authorization getAuthForURL(URL url) { 150 if (!isSupportedProtocol(url)) { 151 return null; 152 } 153 154 // TODO: replace all this url2 mess with a proper method 155 // "areEquivalent(url1, url2)" that 156 // would also ignore case in protocol and host names, etc. -- use that 157 // method in the CookieManager too. 158 159 URL url2 = null; 160 161 try { 162 if (url.getPort() == -1) { 163 // Obtain another URL with an explicit port: 164 int port = url.getProtocol().equalsIgnoreCase("http") ? 80 : 443; 165 // only http and https are supported 166 url2 = new URL(url.getProtocol(), url.getHost(), port, url.getPath()); 167 } else if ((url.getPort() == 80 && url.getProtocol().equalsIgnoreCase("http")) 168 || (url.getPort() == 443 && url.getProtocol().equalsIgnoreCase("https"))) { 169 url2 = new URL(url.getProtocol(), url.getHost(), url.getPath()); 170 } 171 } catch (MalformedURLException e) { 172 log.error("Internal error!", e); // this should never happen 173 // anyway, we'll continue with url2 set to null. 174 } 175 176 String s1 = url.toString(); 177 String s2 = null; 178 if (url2 != null) { 179 s2 = url2.toString(); 180 } 181 182 log.debug("Target URL strings to match against: "+s1+" and "+s2); 183 // TODO should really return most specific (i.e. longest) match. 184 for (PropertyIterator iter = getAuthObjects().iterator(); iter.hasNext();) { 185 Authorization auth = (Authorization) iter.next().getObjectValue(); 186 187 String uRL = auth.getURL(); 188 log.debug("Checking match against auth'n entry: "+uRL); 189 if (s1.startsWith(uRL) || s2 != null && s2.startsWith(uRL)) { 190 log.debug("Matched"); 191 return auth; 192 } 193 log.debug("Did not match"); 194 } 195 return null; 196 } 197 198 public void addConfigElement(ConfigElement config) { 199 } 200 201 public void addAuth(Authorization auth) { 202 getAuthObjects().addItem(auth); 203 } 204 205 public void addAuth() { 206 getAuthObjects().addItem(new Authorization()); 207 } 208 209 public boolean expectsModification() { 210 return false; 211 } 212 213 public void uncompile() {// TODO is this used? 214 } 215 216 /** 217 * Save the authentication data to a file. 218 */ 219 public void save(String authFile) throws IOException { 220 File file = new File(authFile); 221 if (!file.isAbsolute()) { 222 file = new File(System.getProperty("user.dir"),authFile); 223 } 224 PrintWriter writer = new PrintWriter(new FileWriter(file)); 225 writer.println("# JMeter generated Authorization file"); 226 for (int i = 0; i < getAuthObjects().size(); i++) { 227 Authorization auth = (Authorization) getAuthObjects().get(i).getObjectValue(); 228 writer.println(auth.toString()); 229 } 230 writer.flush(); 231 writer.close(); 232 } 233 234 /** 235 * Add authentication data from a file. 236 */ 237 public void addFile(String authFile) throws IOException { 238 File file = new File(authFile); 239 if (!file.isAbsolute()) { 240 file = new File(System.getProperty("user.dir") + File.separator + authFile); 241 } 242 if (!file.canRead()) { 243 throw new IOException("The file you specified cannot be read."); 244 } 245 246 BufferedReader reader = null; 247 boolean ok = true; 248 try { 249 reader = new BufferedReader(new FileReader(file)); 250 String line; 251 while ((line = reader.readLine()) != null) { 252 try { 253 if (line.startsWith("#") || line.trim().length() == 0) { //$NON-NLS-1$ 254 continue; 255 } 256 StringTokenizer st = new StringTokenizer(line, "\t"); //$NON-NLS-1$ 257 String url = st.nextToken(); 258 String user = st.nextToken(); 259 String pass = st.nextToken(); 260 String domain = ""; 261 String realm = ""; 262 if (st.hasMoreTokens()){// Allow for old format file without the extra columnns 263 domain = st.nextToken(); 264 realm = st.nextToken(); 265 } 266 Authorization auth = new Authorization(url, user, pass,domain,realm); 267 getAuthObjects().addItem(auth); 268 } catch (NoSuchElementException e) { 269 log.error("Error parsing auth line: '" + line + "'"); 270 ok = false; 271 } 272 } 273 } finally { 274 IOUtils.closeQuietly(reader); 275 } 276 if (!ok){ 277 JMeterUtils.reportErrorToUser("One or more errors found when reading the Auth file - see the log file"); 278 } 279 } 280 281 /** 282 * Remove an authentication record. 283 */ 284 public void remove(int index) { 285 getAuthObjects().remove(index); 286 } 287 288 /** 289 * Return the number of records. 290 */ 291 public int getAuthCount() { 292 return getAuthObjects().size(); 293 } 294 295 // Needs to be package protected for Unit test 296 static boolean isSupportedProtocol(URL url) { 297 String protocol = url.getProtocol().toLowerCase(java.util.Locale.ENGLISH); 298 return protocol.equals(HTTPConstants.PROTOCOL_HTTP) || protocol.equals(HTTPConstants.PROTOCOL_HTTPS); 299 } 300 }