Merge pull request #8107 from deepak1556/clear_auth_cache_patch
session: add api to clear http auth cache
This commit is contained in:
commit
dc5b27069a
10 changed files with 204 additions and 2 deletions
|
@ -62,6 +62,15 @@ struct ClearStorageDataOptions {
|
|||
uint32_t quota_types = StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL;
|
||||
};
|
||||
|
||||
struct ClearAuthCacheOptions {
|
||||
std::string type;
|
||||
GURL origin;
|
||||
std::string realm;
|
||||
base::string16 username;
|
||||
base::string16 password;
|
||||
net::HttpAuth::Scheme auth_scheme;
|
||||
};
|
||||
|
||||
uint32_t GetStorageMask(const std::vector<std::string>& storage_types) {
|
||||
uint32_t storage_mask = 0;
|
||||
for (const auto& it : storage_types) {
|
||||
|
@ -100,6 +109,18 @@ uint32_t GetQuotaMask(const std::vector<std::string>& quota_types) {
|
|||
return quota_mask;
|
||||
}
|
||||
|
||||
net::HttpAuth::Scheme GetAuthSchemeFromString(const std::string& scheme) {
|
||||
if (scheme == "basic")
|
||||
return net::HttpAuth::AUTH_SCHEME_BASIC;
|
||||
if (scheme == "digest")
|
||||
return net::HttpAuth::AUTH_SCHEME_DIGEST;
|
||||
if (scheme == "ntlm")
|
||||
return net::HttpAuth::AUTH_SCHEME_NTLM;
|
||||
if (scheme == "negotiate")
|
||||
return net::HttpAuth::AUTH_SCHEME_NEGOTIATE;
|
||||
return net::HttpAuth::AUTH_SCHEME_MAX;
|
||||
}
|
||||
|
||||
void SetUserAgentInIO(scoped_refptr<net::URLRequestContextGetter> getter,
|
||||
const std::string& accept_lang,
|
||||
const std::string& user_agent) {
|
||||
|
@ -131,7 +152,27 @@ struct Converter<ClearStorageDataOptions> {
|
|||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
template <>
|
||||
struct Converter<ClearAuthCacheOptions> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
ClearAuthCacheOptions* out) {
|
||||
mate::Dictionary options;
|
||||
if (!ConvertFromV8(isolate, val, &options))
|
||||
return false;
|
||||
options.Get("type", &out->type);
|
||||
options.Get("origin", &out->origin);
|
||||
options.Get("realm", &out->realm);
|
||||
options.Get("username", &out->username);
|
||||
options.Get("password", &out->password);
|
||||
std::string scheme;
|
||||
if (options.Get("scheme", &scheme))
|
||||
out->auth_scheme = GetAuthSchemeFromString(scheme);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<net::ProxyConfig> {
|
||||
static bool FromV8(v8::Isolate* isolate,
|
||||
v8::Local<v8::Value> val,
|
||||
|
@ -314,6 +355,33 @@ void ClearHostResolverCacheInIO(
|
|||
}
|
||||
}
|
||||
|
||||
void ClearAuthCacheInIO(
|
||||
const scoped_refptr<net::URLRequestContextGetter>& context_getter,
|
||||
const ClearAuthCacheOptions& options,
|
||||
const base::Closure& callback) {
|
||||
auto request_context = context_getter->GetURLRequestContext();
|
||||
auto network_session =
|
||||
request_context->http_transaction_factory()->GetSession();
|
||||
if (network_session) {
|
||||
if (options.type == "password") {
|
||||
auto auth_cache = network_session->http_auth_cache();
|
||||
if (!options.origin.is_empty()) {
|
||||
auth_cache->Remove(
|
||||
options.origin, options.realm, options.auth_scheme,
|
||||
net::AuthCredentials(options.username, options.password));
|
||||
} else {
|
||||
auth_cache->Clear();
|
||||
}
|
||||
} else if (options.type == "clientCertificate") {
|
||||
auto client_auth_cache = network_session->ssl_client_auth_cache();
|
||||
client_auth_cache->Remove(net::HostPortPair::FromURL(options.origin));
|
||||
}
|
||||
network_session->CloseAllConnections();
|
||||
}
|
||||
if (!callback.is_null())
|
||||
RunCallbackInUI(callback);
|
||||
}
|
||||
|
||||
void AllowNTLMCredentialsForDomainsInIO(
|
||||
const scoped_refptr<net::URLRequestContextGetter>& context_getter,
|
||||
const std::string& domains) {
|
||||
|
@ -501,6 +569,22 @@ void Session::ClearHostResolverCache(mate::Arguments* args) {
|
|||
callback));
|
||||
}
|
||||
|
||||
void Session::ClearAuthCache(mate::Arguments* args) {
|
||||
ClearAuthCacheOptions options;
|
||||
if (!args->GetNext(&options)) {
|
||||
args->ThrowError("Must specify options object");
|
||||
return;
|
||||
}
|
||||
base::Closure callback;
|
||||
args->GetNext(&callback);
|
||||
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&ClearAuthCacheInIO,
|
||||
make_scoped_refptr(browser_context_->GetRequestContext()),
|
||||
options, callback));
|
||||
}
|
||||
|
||||
void Session::AllowNTLMCredentialsForDomains(const std::string& domains) {
|
||||
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&AllowNTLMCredentialsForDomainsInIO,
|
||||
|
@ -649,6 +733,7 @@ void Session::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("setPermissionRequestHandler",
|
||||
&Session::SetPermissionRequestHandler)
|
||||
.SetMethod("clearHostResolverCache", &Session::ClearHostResolverCache)
|
||||
.SetMethod("clearAuthCache", &Session::ClearAuthCache)
|
||||
.SetMethod("allowNTLMCredentialsForDomains",
|
||||
&Session::AllowNTLMCredentialsForDomains)
|
||||
.SetMethod("setUserAgent", &Session::SetUserAgent)
|
||||
|
|
|
@ -74,6 +74,7 @@ class Session: public mate::TrackableObject<Session>,
|
|||
void SetPermissionRequestHandler(v8::Local<v8::Value> val,
|
||||
mate::Arguments* args);
|
||||
void ClearHostResolverCache(mate::Arguments* args);
|
||||
void ClearAuthCache(mate::Arguments* args);
|
||||
void AllowNTLMCredentialsForDomains(const std::string& domains);
|
||||
void SetUserAgent(const std::string& user_agent, mate::Arguments* args);
|
||||
std::string GetUserAgent();
|
||||
|
|
|
@ -176,6 +176,7 @@ void URLRequest::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("setExtraHeader", &URLRequest::SetExtraHeader)
|
||||
.SetMethod("removeExtraHeader", &URLRequest::RemoveExtraHeader)
|
||||
.SetMethod("setChunkedUpload", &URLRequest::SetChunkedUpload)
|
||||
.SetMethod("_setLoadFlags", &URLRequest::SetLoadFlags)
|
||||
.SetProperty("notStarted", &URLRequest::NotStarted)
|
||||
.SetProperty("finished", &URLRequest::Finished)
|
||||
// Response APi
|
||||
|
@ -292,6 +293,18 @@ void URLRequest::SetChunkedUpload(bool is_chunked_upload) {
|
|||
}
|
||||
}
|
||||
|
||||
void URLRequest::SetLoadFlags(int flags) {
|
||||
// State must be equal to not started.
|
||||
if (!request_state_.NotStarted()) {
|
||||
// Cannot change load flags after start.
|
||||
return;
|
||||
}
|
||||
DCHECK(atom_request_);
|
||||
if (atom_request_) {
|
||||
atom_request_->SetLoadFlags(flags);
|
||||
}
|
||||
}
|
||||
|
||||
void URLRequest::OnAuthenticationRequired(
|
||||
scoped_refptr<const net::AuthChallengeInfo> auth_info) {
|
||||
if (request_state_.Canceled() || request_state_.Closed()) {
|
||||
|
|
|
@ -173,6 +173,7 @@ class URLRequest : public mate::EventEmitter<URLRequest> {
|
|||
bool SetExtraHeader(const std::string& name, const std::string& value);
|
||||
void RemoveExtraHeader(const std::string& name);
|
||||
void SetChunkedUpload(bool is_chunked_upload);
|
||||
void SetLoadFlags(int flags);
|
||||
|
||||
int StatusCode() const;
|
||||
std::string StatusMessage() const;
|
||||
|
|
|
@ -177,6 +177,13 @@ void AtomURLRequest::PassLoginInformation(
|
|||
}
|
||||
}
|
||||
|
||||
void AtomURLRequest::SetLoadFlags(int flags) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
content::BrowserThread::PostTask(
|
||||
content::BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&AtomURLRequest::DoSetLoadFlags, this, flags));
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoWriteBuffer(
|
||||
scoped_refptr<const net::IOBufferWithSize> buffer,
|
||||
bool is_last) {
|
||||
|
@ -244,6 +251,7 @@ void AtomURLRequest::DoSetExtraHeader(const std::string& name,
|
|||
}
|
||||
request_->SetExtraRequestHeaderByName(name, value, true);
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoRemoveExtraHeader(const std::string& name) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (!request_) {
|
||||
|
@ -278,6 +286,14 @@ void AtomURLRequest::DoCancelWithError(const std::string& error,
|
|||
isRequestError));
|
||||
}
|
||||
|
||||
void AtomURLRequest::DoSetLoadFlags(int flags) const {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
if (!request_) {
|
||||
return;
|
||||
}
|
||||
request_->SetLoadFlags(request_->load_flags() | flags);
|
||||
}
|
||||
|
||||
void AtomURLRequest::OnAuthRequired(net::URLRequest* request,
|
||||
net::AuthChallengeInfo* auth_info) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
|
|
|
@ -40,6 +40,7 @@ class AtomURLRequest : public base::RefCountedThreadSafe<AtomURLRequest>,
|
|||
void RemoveExtraHeader(const std::string& name) const;
|
||||
void PassLoginInformation(const base::string16& username,
|
||||
const base::string16& password) const;
|
||||
void SetLoadFlags(int flags) const;
|
||||
|
||||
protected:
|
||||
// Overrides of net::URLRequest::Delegate
|
||||
|
@ -71,6 +72,7 @@ class AtomURLRequest : public base::RefCountedThreadSafe<AtomURLRequest>,
|
|||
const base::string16& password) const;
|
||||
void DoCancelAuth() const;
|
||||
void DoCancelWithError(const std::string& error, bool isRequestError);
|
||||
void DoSetLoadFlags(int flags) const;
|
||||
|
||||
void ReadResponse();
|
||||
bool CopyAndPostBuffer(int bytes_read);
|
||||
|
|
|
@ -363,6 +363,13 @@ event. The [DownloadItem](download-item.md) will not have any `WebContents` asso
|
|||
the initial state will be `interrupted`. The download will start only when the
|
||||
`resume` API is called on the [DownloadItem](download-item.md).
|
||||
|
||||
#### `ses.clearAuthCache(options[, callback])`
|
||||
|
||||
* `options` ([RemovePassword](structures/remove-password.md) | [RemoveClientCertificate](structures/remove-client-certificate.md))
|
||||
* `callback` Function (optional) - Called when operation is done
|
||||
|
||||
Clears the session’s HTTP authentication cache.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
The following properties are available on instances of `Session`:
|
||||
|
|
5
docs/api/structures/remove-client-certificate.md
Normal file
5
docs/api/structures/remove-client-certificate.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# RemoveClientCertificate Object
|
||||
|
||||
* `type` String - `clientCertificate`.
|
||||
* `origin` String - Origin of the server whose associated client certificate
|
||||
must be removed from the cache.
|
15
docs/api/structures/remove-password.md
Normal file
15
docs/api/structures/remove-password.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# RemovePassword Object
|
||||
|
||||
* `type` String - `password`.
|
||||
* `origin` String (optional) - When provided, the authentication info
|
||||
related to the origin will only be removed otherwise the entire cache
|
||||
will be cleared.
|
||||
* `scheme` String (optional) - Scheme of the authentication.
|
||||
Can be `basic`, `digest`, `ntlm`, `negotiate`. Must be provided if
|
||||
removing by `origin`.
|
||||
* `realm` String (optional) - Realm of the authentication. Must be provided if
|
||||
removing by `origin`.
|
||||
* `username` String (optional) - Credentials of the authentication. Must be
|
||||
provided if removing by `origin`.
|
||||
* `password` String (optional) - Credentials of the authentication. Must be
|
||||
provided if removing by `origin`.
|
|
@ -4,10 +4,11 @@ const https = require('https')
|
|||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const send = require('send')
|
||||
const auth = require('basic-auth')
|
||||
const {closeWindow} = require('./window-helpers')
|
||||
|
||||
const {ipcRenderer, remote} = require('electron')
|
||||
const {ipcMain, session, BrowserWindow} = remote
|
||||
const {ipcMain, session, BrowserWindow, net} = remote
|
||||
|
||||
describe('session module', function () {
|
||||
var fixtures = path.resolve(__dirname, 'fixtures')
|
||||
|
@ -661,4 +662,60 @@ describe('session module', function () {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('ses.clearAuthCache(options[, callback])', function () {
|
||||
it('can clear http auth info from cache', function (done) {
|
||||
const ses = session.fromPartition('auth-cache')
|
||||
const server = http.createServer(function (req, res) {
|
||||
var credentials = auth(req)
|
||||
if (!credentials || credentials.name !== 'test' || credentials.pass !== 'test') {
|
||||
res.statusCode = 401
|
||||
res.setHeader('WWW-Authenticate', 'Basic realm="Restricted"')
|
||||
res.end()
|
||||
} else {
|
||||
res.end('authenticated')
|
||||
}
|
||||
})
|
||||
server.listen(0, '127.0.0.1', function () {
|
||||
const port = server.address().port
|
||||
function issueLoginRequest (attempt = 1) {
|
||||
if (attempt > 2) {
|
||||
server.close()
|
||||
return done()
|
||||
}
|
||||
const request = net.request({
|
||||
url: `http://127.0.0.1:${port}`,
|
||||
session: ses
|
||||
})
|
||||
request.on('login', function (info, callback) {
|
||||
attempt++
|
||||
assert.equal(info.scheme, 'basic')
|
||||
assert.equal(info.realm, 'Restricted')
|
||||
callback('test', 'test')
|
||||
})
|
||||
request.on('response', function (response) {
|
||||
let data = ''
|
||||
response.pause()
|
||||
response.on('data', function (chunk) {
|
||||
data += chunk
|
||||
})
|
||||
response.on('end', function () {
|
||||
assert.equal(data, 'authenticated')
|
||||
ses.clearAuthCache({type: 'password'}, function () {
|
||||
issueLoginRequest(attempt)
|
||||
})
|
||||
})
|
||||
response.on('error', function (error) {
|
||||
done(error)
|
||||
})
|
||||
response.resume()
|
||||
})
|
||||
// Internal api to bypass cache for testing.
|
||||
request.urlRequest._setLoadFlags(1 << 1)
|
||||
request.end()
|
||||
}
|
||||
issueLoginRequest()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue