1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/passwordmgr/test/auth2/authenticate.sjs Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,220 @@ 1.4 +function handleRequest(request, response) 1.5 +{ 1.6 + try { 1.7 + reallyHandleRequest(request, response); 1.8 + } catch (e) { 1.9 + response.setStatusLine("1.0", 200, "AlmostOK"); 1.10 + response.write("Error handling request: " + e); 1.11 + } 1.12 +} 1.13 + 1.14 + 1.15 +function reallyHandleRequest(request, response) { 1.16 + var match; 1.17 + var requestAuth = true, requestProxyAuth = true; 1.18 + 1.19 + // Allow the caller to drive how authentication is processed via the query. 1.20 + // Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar 1.21 + // The extra ? allows the user/pass/realm checks to succeed if the name is 1.22 + // at the beginning of the query string. 1.23 + var query = "?" + request.queryString; 1.24 + 1.25 + var expected_user = "", expected_pass = "", realm = "mochitest"; 1.26 + var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy"; 1.27 + var huge = false, plugin = false, anonymous = false; 1.28 + var authHeaderCount = 1; 1.29 + // user=xxx 1.30 + match = /[^_]user=([^&]*)/.exec(query); 1.31 + if (match) 1.32 + expected_user = match[1]; 1.33 + 1.34 + // pass=xxx 1.35 + match = /[^_]pass=([^&]*)/.exec(query); 1.36 + if (match) 1.37 + expected_pass = match[1]; 1.38 + 1.39 + // realm=xxx 1.40 + match = /[^_]realm=([^&]*)/.exec(query); 1.41 + if (match) 1.42 + realm = match[1]; 1.43 + 1.44 + // proxy_user=xxx 1.45 + match = /proxy_user=([^&]*)/.exec(query); 1.46 + if (match) 1.47 + proxy_expected_user = match[1]; 1.48 + 1.49 + // proxy_pass=xxx 1.50 + match = /proxy_pass=([^&]*)/.exec(query); 1.51 + if (match) 1.52 + proxy_expected_pass = match[1]; 1.53 + 1.54 + // proxy_realm=xxx 1.55 + match = /proxy_realm=([^&]*)/.exec(query); 1.56 + if (match) 1.57 + proxy_realm = match[1]; 1.58 + 1.59 + // huge=1 1.60 + match = /huge=1/.exec(query); 1.61 + if (match) 1.62 + huge = true; 1.63 + 1.64 + // plugin=1 1.65 + match = /plugin=1/.exec(query); 1.66 + if (match) 1.67 + plugin = true; 1.68 + 1.69 + // multiple=1 1.70 + match = /multiple=([^&]*)/.exec(query); 1.71 + if (match) 1.72 + authHeaderCount = match[1]+0; 1.73 + 1.74 + // anonymous=1 1.75 + match = /anonymous=1/.exec(query); 1.76 + if (match) 1.77 + anonymous = true; 1.78 + 1.79 + // Look for an authentication header, if any, in the request. 1.80 + // 1.81 + // EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== 1.82 + // 1.83 + // This test only supports Basic auth. The value sent by the client is 1.84 + // "username:password", obscured with base64 encoding. 1.85 + 1.86 + var actual_user = "", actual_pass = "", authHeader, authPresent = false; 1.87 + if (request.hasHeader("Authorization")) { 1.88 + authPresent = true; 1.89 + authHeader = request.getHeader("Authorization"); 1.90 + match = /Basic (.+)/.exec(authHeader); 1.91 + if (match.length != 2) 1.92 + throw "Couldn't parse auth header: " + authHeader; 1.93 + 1.94 + var userpass = base64ToString(match[1]); // no atob() :-( 1.95 + match = /(.*):(.*)/.exec(userpass); 1.96 + if (match.length != 3) 1.97 + throw "Couldn't decode auth header: " + userpass; 1.98 + actual_user = match[1]; 1.99 + actual_pass = match[2]; 1.100 + } 1.101 + 1.102 + var proxy_actual_user = "", proxy_actual_pass = ""; 1.103 + if (request.hasHeader("Proxy-Authorization")) { 1.104 + authHeader = request.getHeader("Proxy-Authorization"); 1.105 + match = /Basic (.+)/.exec(authHeader); 1.106 + if (match.length != 2) 1.107 + throw "Couldn't parse auth header: " + authHeader; 1.108 + 1.109 + var userpass = base64ToString(match[1]); // no atob() :-( 1.110 + match = /(.*):(.*)/.exec(userpass); 1.111 + if (match.length != 3) 1.112 + throw "Couldn't decode auth header: " + userpass; 1.113 + proxy_actual_user = match[1]; 1.114 + proxy_actual_pass = match[2]; 1.115 + } 1.116 + 1.117 + // Don't request authentication if the credentials we got were what we 1.118 + // expected. 1.119 + if (expected_user == actual_user && 1.120 + expected_pass == actual_pass) { 1.121 + requestAuth = false; 1.122 + } 1.123 + if (proxy_expected_user == proxy_actual_user && 1.124 + proxy_expected_pass == proxy_actual_pass) { 1.125 + requestProxyAuth = false; 1.126 + } 1.127 + 1.128 + if (anonymous) { 1.129 + if (authPresent) { 1.130 + response.setStatusLine("1.0", 400, "Unexpected authorization header found"); 1.131 + } else { 1.132 + response.setStatusLine("1.0", 200, "Authorization header not found"); 1.133 + } 1.134 + } else { 1.135 + if (requestProxyAuth) { 1.136 + response.setStatusLine("1.0", 407, "Proxy authentication required"); 1.137 + for (i = 0; i < authHeaderCount; ++i) 1.138 + response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true); 1.139 + } else if (requestAuth) { 1.140 + response.setStatusLine("1.0", 401, "Authentication required"); 1.141 + for (i = 0; i < authHeaderCount; ++i) 1.142 + response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true); 1.143 + } else { 1.144 + response.setStatusLine("1.0", 200, "OK"); 1.145 + } 1.146 + } 1.147 + 1.148 + response.setHeader("Content-Type", "application/xhtml+xml", false); 1.149 + response.write("<html xmlns='http://www.w3.org/1999/xhtml'>"); 1.150 + response.write("<p>Login: <span id='ok'>" + (requestAuth ? "FAIL" : "PASS") + "</span></p>\n"); 1.151 + response.write("<p>Proxy: <span id='proxy'>" + (requestProxyAuth ? "FAIL" : "PASS") + "</span></p>\n"); 1.152 + response.write("<p>Auth: <span id='auth'>" + authHeader + "</span></p>\n"); 1.153 + response.write("<p>User: <span id='user'>" + actual_user + "</span></p>\n"); 1.154 + response.write("<p>Pass: <span id='pass'>" + actual_pass + "</span></p>\n"); 1.155 + 1.156 + if (huge) { 1.157 + response.write("<div style='display: none'>"); 1.158 + for (i = 0; i < 100000; i++) { 1.159 + response.write("123456789\n"); 1.160 + } 1.161 + response.write("</div>"); 1.162 + response.write("<span id='footnote'>This is a footnote after the huge content fill</span>"); 1.163 + } 1.164 + 1.165 + if (plugin) { 1.166 + response.write("<embed id='embedtest' style='width: 400px; height: 100px;' " + 1.167 + "type='application/x-test'></embed>\n"); 1.168 + } 1.169 + 1.170 + response.write("</html>"); 1.171 +} 1.172 + 1.173 + 1.174 +// base64 decoder 1.175 +// 1.176 +// Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa() 1.177 +// doesn't seem to exist. :-( 1.178 +/* Convert Base64 data to a string */ 1.179 +const toBinaryTable = [ 1.180 + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 1.181 + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, 1.182 + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, 1.183 + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, 1.184 + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, 1.185 + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, 1.186 + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, 1.187 + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 1.188 +]; 1.189 +const base64Pad = '='; 1.190 + 1.191 +function base64ToString(data) { 1.192 + 1.193 + var result = ''; 1.194 + var leftbits = 0; // number of bits decoded, but yet to be appended 1.195 + var leftdata = 0; // bits decoded, but yet to be appended 1.196 + 1.197 + // Convert one by one. 1.198 + for (var i = 0; i < data.length; i++) { 1.199 + var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; 1.200 + var padding = (data[i] == base64Pad); 1.201 + // Skip illegal characters and whitespace 1.202 + if (c == -1) continue; 1.203 + 1.204 + // Collect data into leftdata, update bitcount 1.205 + leftdata = (leftdata << 6) | c; 1.206 + leftbits += 6; 1.207 + 1.208 + // If we have 8 or more bits, append 8 bits to the result 1.209 + if (leftbits >= 8) { 1.210 + leftbits -= 8; 1.211 + // Append if not padding. 1.212 + if (!padding) 1.213 + result += String.fromCharCode((leftdata >> leftbits) & 0xff); 1.214 + leftdata &= (1 << leftbits) - 1; 1.215 + } 1.216 + } 1.217 + 1.218 + // If there are any bits left, the base64 string was corrupted 1.219 + if (leftbits) 1.220 + throw Components.Exception('Corrupted base64 string'); 1.221 + 1.222 + return result; 1.223 +}