Commit 6218d039 authored by tyoshino@chromium.org's avatar tyoshino@chromium.org

ResourceLoaderOptions also must be updated by updateRequestForAccessControl()

updateRequestForAccessControl() updates ResourceRequest for access
control, but ResourceLoaderOptions passed to FetchRequest also contains
configuration items that must be updated for access control.

This CL removes cookie handling code and iframe from the following
layout tests since they're testing user:pass but not cookie.
- access-control-preflight-credential-async.html
- access-control-preflight-credential-sync.html

A new test is added to check that no cookie is set in a preflight
request. This new test uses third-party-cookie-relaxing-iframe.html
which was loaded by the two tests but was not actually used.

Missing testRunner.setAlwaysAcceptCookies() calls are added to
third-party-cookie-relaxing-iframe.html.

BUG=377541
R=japhet

Review URL: https://codereview.chromium.org/312653002

git-svn-id: svn://svn.chromium.org/blink/trunk@175527 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 8a812887
......@@ -30,6 +30,11 @@ function showCookies()
function sendXHR(queryCommand)
{
if (window.testRunner) {
// setAlwaysAcceptCookies() takes effect asynchronously (IPC is involved).
testRunner.setAlwaysAcceptCookies(true);
}
var baseurl = "http://localhost:8000/cookies/resources/cookie-utility.php";
var url = queryCommand ? baseurl + "?queryfunction=" + queryCommand : baseurl;
alert(url);
......@@ -41,6 +46,10 @@ function sendXHR(queryCommand)
alert("XHR response - " + req.responseText);
else
alert("xhr error");
if (window.testRunner) {
testRunner.setAlwaysAcceptCookies(false);
}
parent.window.postMessage("done", "*");
}
......
ALERT: http://localhost:8000/cookies/resources/cookie-utility.php?queryfunction=setFooCookie
ALERT: XHR response - Set the foo cookie
Test case for bug 37781: [XHR] Cross-Origin asynchronous request with credential raises NETWORK_ERR
PASSED
......
......@@ -10,32 +10,9 @@ function log(message)
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
testRunner.setAlwaysAcceptCookies(true);
}
var cookieSet = false;
window.onmessage = function(evt)
{
if (evt.data != "done") {
alert("Unexpected message: " + evt.data);
return;
}
if (!cookieSet) {
cookieSet = true;
runTest();
}
}
function startTest() {
// Set a cookie for localhost:8000.
window.frames[0].postMessage("sendXHR setFooCookie", "*");
}
function stopTest() {
// Clean up all cookies for localhost:8000.
window.frames[0].postMessage("resetCookiesAndNotifyDone", "*");
if (window.testRunner)
testRunner.notifyDone();
}
......@@ -63,11 +40,8 @@ function runTest() {
</script>
</head>
<body onload="startTest();">
<body onload="runTest();">
<p>Test case for bug <a href="https://bugs.webkit.org/show_bug.cgi?id=37781">37781</a>: [XHR] Cross-Origin asynchronous request with credential raises NETWORK_ERR</p>
<pre id='console'></pre>
<iframe id='testFrame' src="http://localhost:8000/cookies/resources/third-party-cookie-relaxing-iframe.html"></iframe>
</body>
</html>
ALERT: http://localhost:8000/cookies/resources/cookie-utility.php?queryfunction=setFooCookie
ALERT: XHR response - Set the foo cookie
Test case for bug 37781: [XHR] Cross-Origin synchronous request with credential raises NETWORK_ERR
PASSED
......
......@@ -10,32 +10,9 @@ function log(message)
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
testRunner.setAlwaysAcceptCookies(true);
}
var cookieSet = false;
window.onmessage = function(evt)
{
if (evt.data != "done") {
alert("Unexpected message: " + evt.data);
return;
}
if (!cookieSet) {
cookieSet = true;
runTest();
}
}
function startTest() {
// Set a cookie for localhost:8000.
window.frames[0].postMessage("sendXHR setFooCookie", "*");
}
function stopTest() {
// Clean up all cookies for localhost:8000.
window.frames[0].postMessage("resetCookiesAndNotifyDone", "*");
if (window.testRunner)
testRunner.notifyDone();
}
......@@ -63,11 +40,8 @@ function runTest() {
</script>
</head>
<body onload="startTest();">
<body onload="runTest();">
<p>Test case for bug <a href="https://bugs.webkit.org/show_bug.cgi?id=37781">37781</a>: [XHR] Cross-Origin synchronous request with credential raises NETWORK_ERR</p>
<pre id='console'></pre>
<iframe id='testFrame' src="http://localhost:8000/cookies/resources/third-party-cookie-relaxing-iframe.html"></iframe>
</body>
</html>
ALERT: http://localhost:8000/cookies/resources/cookie-utility.php?queryfunction=setFooCookie
ALERT: XHR response - Set the foo cookie
This is a testharness.js-based test.
FAIL Preflight request must not contain any cookie header assert_equals: expected "awesomevalue" but got ""
Harness: the test ran to completion.
<html>
<head>
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<script>
var t = async_test("Preflight request must not contain any cookie header");
var runTest = t.step_func(function()
{
var resolveSetCookiePromise = null;
var setCookiePromise = new Promise(function(resolve, reject)
{
resolveSetCookiePromise = resolve;
});
var resolveClearCookiesPromise = null;
var clearCookiesPromise = new Promise(function(resolve, reject)
{
resolveClearCookiesPromise = resolve;
});
var cookieSet = false;
window.onmessage = t.step_func(function(evt)
{
assert_equals(evt.data, "done");
if (!cookieSet) {
resolveSetCookiePromise();
cookieSet = true;
} else {
resolveClearCookiesPromise();
}
});
// Set a cookie for localhost:8000.
window.frames[0].postMessage("sendXHR setFooCookie", "*");
setCookiePromise.then(t.step_func(function()
{
var xhr = new XMLHttpRequest;
xhr.open("GET", "http://localhost:8000/xmlhttprequest/resources/access-control-preflight-request-must-not-contain-cookie.php");
xhr.setRequestHeader("X-Proprietary-Header", "foo")
xhr.withCredentials = true;
xhr.onerror = t.step_func(function (e) {
assert_unreached(e);
});
var doneXHRPromise = new Promise(function(resolve, reject)
{
xhr.onreadystatechange = t.step_func(function () {
if (xhr.readyState != xhr.DONE)
return;
assert_equals(xhr.status, 200);
assert_equals(xhr.responseText, "awesomevalue");
resolve();
});
});
xhr.send();
return doneXHRPromise;
})).then(t.step_func(function()
{
// Clean up all cookies for localhost:8000.
window.frames[0].postMessage("resetCookiesAndNotifyDone", "*");
return resolveClearCookiesPromise;
})).then(t.step_func(function()
{
t.done();
})).catch(t.step_func(function(e)
{
assert_unreached(e);
}));
});
</script>
</head>
<body onload="runTest()">
<iframe src="http://localhost:8000/cookies/resources/third-party-cookie-relaxing-iframe.html"></iframe>
</body>
</html>
<?php
if (isset($_SERVER["HTTP_ACCESS_CONTROL_REQUEST_METHOD"]) &&
isset($_COOKIE["foo"])) {
header("HTTP/1.1 400 Bad Request");
} else {
header("Cache-Control: no-store");
header("Access-Control-Allow-Origin: http://127.0.0.1:8000");
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Allow-Headers: X-Proprietary-Header");
header("Connection: close");
echo $_COOKIE["foo"];
}
?>
......@@ -73,6 +73,7 @@ void FetchRequest::setCrossOriginAccessControl(SecurityOrigin* origin, StoredCre
{
ASSERT(requested == ClientDidNotRequestCredentials || allowCredentials == AllowStoredCredentials);
updateRequestForAccessControl(m_resourceRequest, origin, allowCredentials);
m_options.allowCredentials = allowCredentials;
m_options.corsEnabled = IsCORSEnabled;
m_options.securityOrigin = origin;
m_options.credentialsRequested = requested;
......
......@@ -73,7 +73,7 @@ DocumentThreadableLoader::DocumentThreadableLoader(Document& document, Threadabl
, m_document(document)
, m_options(options)
, m_resourceLoaderOptions(resourceLoaderOptions)
, m_allowCredentials(m_resourceLoaderOptions.allowCredentials)
, m_forceDoNotAllowStoredCredentials(false)
, m_securityOrigin(m_resourceLoaderOptions.securityOrigin)
, m_sameOriginRequest(securityOrigin()->canRequest(request.url()))
, m_simpleRequest(true)
......@@ -94,7 +94,7 @@ DocumentThreadableLoader::DocumentThreadableLoader(Document& document, Threadabl
}
if (m_sameOriginRequest || m_options.crossOriginRequestPolicy == AllowCrossOriginRequests) {
loadRequest(request);
loadRequest(request, m_resourceLoaderOptions);
return;
}
......@@ -118,21 +118,27 @@ void DocumentThreadableLoader::makeCrossOriginAccessRequest(const ResourceReques
}
ResourceRequest crossOriginRequest(request);
updateRequestForAccessControl(crossOriginRequest, securityOrigin(), m_allowCredentials);
loadRequest(crossOriginRequest);
ResourceLoaderOptions crossOriginOptions(m_resourceLoaderOptions);
updateRequestForAccessControl(crossOriginRequest, securityOrigin(), effectiveAllowCredentials());
loadRequest(crossOriginRequest, crossOriginOptions);
} else {
m_simpleRequest = false;
OwnPtr<ResourceRequest> crossOriginRequest = adoptPtr(new ResourceRequest(request));
OwnPtr<ResourceLoaderOptions> crossOriginOptions = adoptPtr(new ResourceLoaderOptions(m_resourceLoaderOptions));
// Do not set the Origin header for preflight requests.
updateRequestForAccessControl(*crossOriginRequest, 0, m_allowCredentials);
updateRequestForAccessControl(*crossOriginRequest, 0, effectiveAllowCredentials());
m_actualRequest = crossOriginRequest.release();
m_actualOptions = crossOriginOptions.release();
if (CrossOriginPreflightResultCache::shared().canSkipPreflight(securityOrigin()->toString(), m_actualRequest->url(), m_allowCredentials, m_actualRequest->httpMethod(), m_actualRequest->httpHeaderFields())) {
if (CrossOriginPreflightResultCache::shared().canSkipPreflight(securityOrigin()->toString(), m_actualRequest->url(), effectiveAllowCredentials(), m_actualRequest->httpMethod(), m_actualRequest->httpHeaderFields())) {
loadActualRequest();
} else {
ResourceRequest preflightRequest = createAccessControlPreflightRequest(*m_actualRequest, securityOrigin());
loadRequest(preflightRequest);
// Create a ResourceLoaderOptions for preflight.
ResourceLoaderOptions preflightOptions = *m_actualOptions;
preflightOptions.allowCredentials = DoNotAllowStoredCredentials;
loadRequest(preflightRequest, preflightOptions);
}
}
}
......@@ -201,7 +207,7 @@ void DocumentThreadableLoader::redirectReceived(Resource* resource, ResourceRequ
if (m_simpleRequest) {
allowRedirect = CrossOriginAccessControl::isLegalRedirectLocation(request.url(), accessControlErrorDescription)
&& (m_sameOriginRequest || passesAccessControlCheck(redirectResponse, m_allowCredentials, securityOrigin(), accessControlErrorDescription));
&& (m_sameOriginRequest || passesAccessControlCheck(redirectResponse, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription));
} else {
accessControlErrorDescription = "The request was redirected to '"+ request.url().string() + "', which is disallowed for cross-origin requests that require preflight.";
}
......@@ -224,7 +230,7 @@ void DocumentThreadableLoader::redirectReceived(Resource* resource, ResourceRequ
// Since the request is no longer same-origin, if the user didn't request credentials in
// the first place, update our state so we neither request them nor expect they must be allowed.
if (m_resourceLoaderOptions.credentialsRequested == ClientDidNotRequestCredentials)
m_allowCredentials = DoNotAllowStoredCredentials;
m_forceDoNotAllowStoredCredentials = true;
// Remove any headers that may have been added by the network layer that cause access control to fail.
request.clearHTTPReferrer();
......@@ -282,7 +288,7 @@ void DocumentThreadableLoader::handlePreflightResponse(unsigned long identifier,
String accessControlErrorDescription;
if (!passesAccessControlCheck(response, m_allowCredentials, securityOrigin(), accessControlErrorDescription)) {
if (!passesAccessControlCheck(response, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription)) {
handlePreflightFailure(response.url().string(), accessControlErrorDescription);
return;
}
......@@ -292,7 +298,7 @@ void DocumentThreadableLoader::handlePreflightResponse(unsigned long identifier,
return;
}
OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult = adoptPtr(new CrossOriginPreflightResultCacheItem(m_allowCredentials));
OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult = adoptPtr(new CrossOriginPreflightResultCacheItem(effectiveAllowCredentials()));
if (!preflightResult->parse(response, accessControlErrorDescription)
|| !preflightResult->allowsCrossOriginMethod(m_actualRequest->httpMethod(), accessControlErrorDescription)
|| !preflightResult->allowsCrossOriginHeaders(m_actualRequest->httpHeaderFields(), accessControlErrorDescription)) {
......@@ -314,7 +320,7 @@ void DocumentThreadableLoader::handleResponse(unsigned long identifier, const Re
if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == UseAccessControl) {
String accessControlErrorDescription;
if (!passesAccessControlCheck(response, m_allowCredentials, securityOrigin(), accessControlErrorDescription)) {
if (!passesAccessControlCheck(response, effectiveAllowCredentials(), securityOrigin(), accessControlErrorDescription)) {
m_client->didFailAccessControlCheck(ResourceError(errorDomainBlinkInternal, 0, response.url().string(), accessControlErrorDescription));
return;
}
......@@ -376,12 +382,14 @@ void DocumentThreadableLoader::loadActualRequest()
{
OwnPtr<ResourceRequest> actualRequest;
actualRequest.swap(m_actualRequest);
OwnPtr<ResourceLoaderOptions> actualOptions;
actualOptions.swap(m_actualOptions);
actualRequest->setHTTPOrigin(securityOrigin()->toAtomicString());
clearResource();
loadRequest(*actualRequest);
loadRequest(*actualRequest, *actualOptions);
}
void DocumentThreadableLoader::handlePreflightFailure(const String& url, const String& errorDescription)
......@@ -394,16 +402,16 @@ void DocumentThreadableLoader::handlePreflightFailure(const String& url, const S
m_client->didFailAccessControlCheck(error);
}
void DocumentThreadableLoader::loadRequest(const ResourceRequest& request)
void DocumentThreadableLoader::loadRequest(const ResourceRequest& request, ResourceLoaderOptions resourceLoaderOptions)
{
// Any credential should have been removed from the cross-site requests.
const KURL& requestURL = request.url();
ASSERT(m_sameOriginRequest || requestURL.user().isEmpty());
ASSERT(m_sameOriginRequest || requestURL.pass().isEmpty());
ResourceLoaderOptions resourceLoaderOptions = m_resourceLoaderOptions;
// Update resourceLoaderOptions with enforced values.
resourceLoaderOptions.allowCredentials = m_allowCredentials;
if (m_forceDoNotAllowStoredCredentials)
resourceLoaderOptions.allowCredentials = DoNotAllowStoredCredentials;
resourceLoaderOptions.securityOrigin = m_securityOrigin;
if (m_async) {
if (m_actualRequest) {
......@@ -479,6 +487,13 @@ bool DocumentThreadableLoader::isAllowedByPolicy(const KURL& url) const
return m_document.contentSecurityPolicy()->allowConnectToSource(url);
}
StoredCredentials DocumentThreadableLoader::effectiveAllowCredentials() const
{
if (m_forceDoNotAllowStoredCredentials)
return DoNotAllowStoredCredentials;
return m_resourceLoaderOptions.allowCredentials;
}
SecurityOrigin* DocumentThreadableLoader::securityOrigin() const
{
return m_securityOrigin ? m_securityOrigin.get() : m_document.securityOrigin();
......
......@@ -96,9 +96,13 @@ class DocumentThreadableLoader FINAL : public ThreadableLoader, private Resource
// the actual request will be made later in handleSuccessfulFinish().
void handlePreflightResponse(unsigned long identifier, const ResourceResponse&);
void loadRequest(const ResourceRequest&);
void loadRequest(const ResourceRequest&, ResourceLoaderOptions);
bool isAllowedRedirect(const KURL&) const;
bool isAllowedByPolicy(const KURL&) const;
// Returns DoNotAllowStoredCredentials
// if m_forceDoNotAllowStoredCredentials is set. Otherwise, just
// returns allowCredentials value of m_resourceLoaderOptions.
StoredCredentials effectiveAllowCredentials() const;
SecurityOrigin* securityOrigin() const;
......@@ -106,15 +110,23 @@ class DocumentThreadableLoader FINAL : public ThreadableLoader, private Resource
Document& m_document;
const ThreadableLoaderOptions m_options;
// Some items may be overridden by m_forceDoNotAllowStoredCredentials
// and m_securityOrigin. In such a case, build a ResourceLoaderOptions
// with up-to-date values from them and this variable, and use it.
const ResourceLoaderOptions m_resourceLoaderOptions;
StoredCredentials m_allowCredentials;
bool m_forceDoNotAllowStoredCredentials;
RefPtr<SecurityOrigin> m_securityOrigin;
bool m_sameOriginRequest;
bool m_simpleRequest;
bool m_async;
OwnPtr<ResourceRequest> m_actualRequest; // non-null during Access Control preflight checks
// Holds the original request and options for it during preflight
// request handling phase.
OwnPtr<ResourceRequest> m_actualRequest;
OwnPtr<ResourceLoaderOptions> m_actualOptions;
HTTPHeaderMap m_simpleRequestHeaders; // stores simple request headers in case of a cross-origin redirect.
Timer<DocumentThreadableLoader> m_timeoutTimer;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment