mirror of
https://github.com/zotero/zotero.git
synced 2026-10-01 02:01:24 +00:00
Fix WebDAV download auth for usernames with special characters
HTTP.download() was rewritten in 0fe31b0f04 to use fetch() and build the
Basic auth header itself via btoa(username + ':' + password), but the
username and password come from nsIURI.username/password, which are
percent-encoded. As a result, a username like "user@example.com" was
sent as "user%40example.com", causing a 401 on every WebDAV download for
any user with @, :, space, etc. in their username. Other request types
were unaffected because they go through xmlhttp.open(method, url, true,
username, password), which decodes internally.
Decode username and password in _parseURI() so the values returned can
be used directly for Basic auth (and as a side effect, fix the other
display/use sites that were getting the percent-encoded form).
https://forums.zotero.org/discussion/131174/zotero-10-betas-1-2-3-cant-download-from-webdav-http-401
This commit is contained in:
parent
e13e85b2e5
commit
fd812070b6
2 changed files with 34 additions and 6 deletions
|
|
@ -15,8 +15,8 @@ Zotero.HTTP = new function () {
|
|||
* Parse a URI (nsIURI or string), extract any embedded credentials, and
|
||||
* return the URL as a credential-free string
|
||||
*
|
||||
* Mozilla percent-encodes periods in the username component of nsIURIs (%2E), which is
|
||||
* technically valid but breaks Basic auth against most servers, so we undo that here.
|
||||
* nsIURI returns the username and password percent-encoded, so we decode them so
|
||||
* callers can use them directly (e.g., when building a Basic auth header).
|
||||
*
|
||||
* @param {nsIURI|String} uri
|
||||
* @return {{ url: String, username: String|null, password: String|null }}
|
||||
|
|
@ -30,11 +30,9 @@ Zotero.HTTP = new function () {
|
|||
return { url: uri, username: null, password: null };
|
||||
}
|
||||
}
|
||||
let username = uri.username || null;
|
||||
let password = null;
|
||||
let username = uri.username ? decodeURIComponent(uri.username) : null;
|
||||
let password = uri.password ? decodeURIComponent(uri.password) : null;
|
||||
if (username) {
|
||||
username = username.replace(/%2E/, '.');
|
||||
password = uri.password || null;
|
||||
uri = uri.mutate().setUserPass('').finalize();
|
||||
}
|
||||
return { url: uri.spec, username, password };
|
||||
|
|
|
|||
|
|
@ -421,6 +421,23 @@ describe("Zotero.HTTP", function () {
|
|||
}
|
||||
}
|
||||
);
|
||||
httpd.registerPathHandler(
|
||||
'/download/auth',
|
||||
{
|
||||
handle: function (request, response) {
|
||||
let val;
|
||||
try {
|
||||
val = request.getHeader("Authorization");
|
||||
}
|
||||
catch (e) {
|
||||
val = "";
|
||||
}
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
response.setHeader("X-Echo-Auth", val, false);
|
||||
response.write("ok");
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(async function () {
|
||||
|
|
@ -551,6 +568,19 @@ describe("Zotero.HTTP", function () {
|
|||
let stat = await IOUtils.stat(dest);
|
||||
assert.equal(stat.size, 3 * 1024);
|
||||
});
|
||||
|
||||
it("should decode percent-encoded credentials from URL for Basic auth", async function () {
|
||||
let username = "user@example.com";
|
||||
let password = "secret";
|
||||
let expected = "Basic " + btoa(username + ":" + password);
|
||||
let url = `http://${encodeURIComponent(username)}:${password}`
|
||||
+ `@127.0.0.1:${port}/download/auth`;
|
||||
let dest = PathUtils.join(tmpDir, "auth.bin");
|
||||
let nsUri = Services.io.newURI(url);
|
||||
let req = await Zotero.HTTP.download(nsUri, dest);
|
||||
assert.equal(req.status, 200);
|
||||
assert.equal(req.headers.get("X-Echo-Auth"), expected);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue