Implement authentication support for wxWebRequest under Mac
Add wxWebAuthChallengeURLSession and use the appropriate delegate callback to create it.
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "wx/private/webrequest.h"
|
||||
|
||||
DECLARE_WXCOCOA_OBJC_CLASS(NSURLCredential);
|
||||
DECLARE_WXCOCOA_OBJC_CLASS(NSURLSession);
|
||||
DECLARE_WXCOCOA_OBJC_CLASS(NSURLSessionTask);
|
||||
DECLARE_WXCOCOA_OBJC_CLASS(wxWebSessionDelegate);
|
||||
@@ -22,6 +23,29 @@ class wxWebSessionURLSession;
|
||||
class wxWebRequestURLSession;
|
||||
class wxWebResponseURLSession;
|
||||
|
||||
class wxWebAuthChallengeURLSession : public wxWebAuthChallengeImpl
|
||||
{
|
||||
public:
|
||||
wxWebAuthChallengeURLSession(wxWebAuthChallenge::Source source,
|
||||
wxWebRequestURLSession& request)
|
||||
: wxWebAuthChallengeImpl(source),
|
||||
m_request(request)
|
||||
{
|
||||
}
|
||||
|
||||
~wxWebAuthChallengeURLSession();
|
||||
|
||||
void SetCredentials(const wxWebCredentials& cred) wxOVERRIDE;
|
||||
|
||||
WX_NSURLCredential GetURLCredential() const { return m_cred; }
|
||||
|
||||
private:
|
||||
wxWebRequestURLSession& m_request;
|
||||
WX_NSURLCredential m_cred = NULL;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxWebAuthChallengeURLSession);
|
||||
};
|
||||
|
||||
class wxWebResponseURLSession : public wxWebResponseImpl
|
||||
{
|
||||
public:
|
||||
@@ -67,7 +91,8 @@ public:
|
||||
wxWebResponseImplPtr GetResponse() const wxOVERRIDE
|
||||
{ return m_response; }
|
||||
|
||||
wxWebAuthChallengeImplPtr GetAuthChallenge() const wxOVERRIDE;
|
||||
wxWebAuthChallengeImplPtr GetAuthChallenge() const wxOVERRIDE
|
||||
{ return m_authChallenge; }
|
||||
|
||||
wxFileOffset GetBytesSent() const wxOVERRIDE;
|
||||
|
||||
@@ -79,14 +104,22 @@ public:
|
||||
|
||||
void HandleCompletion();
|
||||
|
||||
void HandleChallenge(wxWebAuthChallengeURLSession* challenge);
|
||||
|
||||
void OnSetCredentials(const wxWebCredentials& cred);
|
||||
|
||||
wxWebResponseURLSession* GetResponseImplPtr() const
|
||||
{ return m_response.get(); }
|
||||
|
||||
wxWebAuthChallengeURLSession* GetAuthChallengeImplPtr() const
|
||||
{ return m_authChallenge.get(); }
|
||||
|
||||
private:
|
||||
wxWebSessionURLSession& m_sessionImpl;
|
||||
wxString m_url;
|
||||
WX_NSURLSessionTask m_task;
|
||||
wxObjectDataPtr<wxWebResponseURLSession> m_response;
|
||||
wxObjectDataPtr<wxWebAuthChallengeURLSession> m_authChallenge;
|
||||
|
||||
wxDECLARE_NO_COPY_CLASS(wxWebRequestURLSession);
|
||||
};
|
||||
|
@@ -97,6 +97,60 @@
|
||||
[m_requests removeObjectForKey:task];
|
||||
}
|
||||
|
||||
- (void)URLSession:(NSURLSession *)session
|
||||
task:(NSURLSessionTask *)task
|
||||
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
|
||||
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
|
||||
{
|
||||
wxUnusedVar(session);
|
||||
|
||||
wxWebRequestURLSession* request = [self requestForTask:task];
|
||||
wxCHECK_RET( request, "received authentication challenge for an unknown task" );
|
||||
|
||||
NSURLProtectionSpace* const space = [challenge protectionSpace];
|
||||
|
||||
wxLogTrace(wxTRACE_WEBREQUEST, "Request %p: didReceiveChallenge for %s",
|
||||
request,
|
||||
wxCFStringRefFromGet([space description]).AsString());
|
||||
|
||||
// We need to distinguish between session-wide and task-specific
|
||||
// authentication challenges, we're only really interested in the latter
|
||||
// ones here (but apparently there is no way to get just them, even though
|
||||
// the documentation seems to imply that session-wide challenges shouldn't
|
||||
// be sent to this task-specific delegate -- but they're, at least under
|
||||
// 10.14).
|
||||
const auto authMethod = space.authenticationMethod;
|
||||
if ( authMethod == NSURLAuthenticationMethodHTTPBasic ||
|
||||
authMethod == NSURLAuthenticationMethodHTTPDigest )
|
||||
{
|
||||
if ( auto* const authChallenge = request->GetAuthChallengeImplPtr() )
|
||||
{
|
||||
// We're going to get called until we don't provide the correct
|
||||
// credentials, so don't use them again (and again, and again...)
|
||||
// if we had already used them unsuccessfully.
|
||||
if ( !challenge.previousFailureCount )
|
||||
{
|
||||
wxLogTrace(wxTRACE_WEBREQUEST, "Request %p: using credentials", request);
|
||||
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential,
|
||||
authChallenge->GetURLCredential());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
wxLogTrace(wxTRACE_WEBREQUEST, "Request %p: not using failing credentials again", request);
|
||||
}
|
||||
|
||||
request->HandleChallenge(new wxWebAuthChallengeURLSession(
|
||||
[space isProxy] ? wxWebAuthChallenge::Source_Proxy
|
||||
: wxWebAuthChallenge::Source_Server,
|
||||
*request
|
||||
));
|
||||
}
|
||||
|
||||
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//
|
||||
@@ -170,14 +224,22 @@ void wxWebRequestURLSession::Cancel()
|
||||
|
||||
void wxWebRequestURLSession::HandleCompletion()
|
||||
{
|
||||
switch ( m_response->GetStatus() )
|
||||
{
|
||||
case 401:
|
||||
case 407:
|
||||
SetState(wxWebRequest::State_Unauthorized, m_response->GetStatusText());
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( CheckServerStatus() )
|
||||
SetState(wxWebRequest::State_Completed);
|
||||
}
|
||||
}
|
||||
|
||||
wxWebAuthChallengeImplPtr wxWebRequestURLSession::GetAuthChallenge() const
|
||||
void wxWebRequestURLSession::HandleChallenge(wxWebAuthChallengeURLSession* challenge)
|
||||
{
|
||||
wxFAIL_MSG("not implemented");
|
||||
return wxWebAuthChallengeImplPtr();
|
||||
m_authChallenge.reset(challenge);
|
||||
}
|
||||
|
||||
wxFileOffset wxWebRequestURLSession::GetBytesSent() const
|
||||
@@ -200,6 +262,33 @@ wxFileOffset wxWebRequestURLSession::GetBytesExpectedToReceive() const
|
||||
return m_task.countOfBytesExpectedToReceive;
|
||||
}
|
||||
|
||||
//
|
||||
// wxWebAuthChallengeURLSession
|
||||
//
|
||||
|
||||
wxWebAuthChallengeURLSession::~wxWebAuthChallengeURLSession()
|
||||
{
|
||||
[m_cred release];
|
||||
}
|
||||
|
||||
void wxWebAuthChallengeURLSession::SetCredentials(const wxWebCredentials& cred)
|
||||
{
|
||||
wxLogTrace(wxTRACE_WEBREQUEST, "Request %p: setting credentials", &m_request);
|
||||
|
||||
[m_cred release];
|
||||
|
||||
m_cred = [NSURLCredential
|
||||
credentialWithUser:wxCFStringRef(cred.GetUser()).AsNSString()
|
||||
password:wxCFStringRef(wxSecretString(cred.GetPassword())).AsNSString()
|
||||
persistence:NSURLCredentialPersistenceNone
|
||||
];
|
||||
|
||||
[m_cred retain];
|
||||
|
||||
m_request.Start();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// wxWebResponseURLSession
|
||||
//
|
||||
|
@@ -42,12 +42,6 @@ public:
|
||||
dataSize = 0;
|
||||
}
|
||||
|
||||
static bool UsingNSURLSession()
|
||||
{
|
||||
return wxWebSession::GetDefault().GetLibraryVersionInfo().GetName()
|
||||
== "URLSession";
|
||||
}
|
||||
|
||||
// All tests should call this function first and skip the test entirely if
|
||||
// it returns false, as this indicates that web requests tests are disabled.
|
||||
bool InitBaseURL()
|
||||
@@ -240,12 +234,6 @@ TEST_CASE_METHOD(RequestFixture,
|
||||
if ( !InitBaseURL() )
|
||||
return;
|
||||
|
||||
if ( UsingNSURLSession() )
|
||||
{
|
||||
WARN("NSURLSession backend doesn't support authentication, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
Create("/basic-auth/wxtest/wxwidgets");
|
||||
Run(wxWebRequest::State_Unauthorized, 401);
|
||||
REQUIRE( request.GetAuthChallenge().IsOk() );
|
||||
@@ -273,12 +261,6 @@ TEST_CASE_METHOD(RequestFixture,
|
||||
if ( !InitBaseURL() )
|
||||
return;
|
||||
|
||||
if ( UsingNSURLSession() )
|
||||
{
|
||||
WARN("NSURLSession backend doesn't support authentication, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
Create("/digest-auth/auth/wxtest/wxwidgets");
|
||||
Run(wxWebRequest::State_Unauthorized, 401);
|
||||
REQUIRE( request.GetAuthChallenge().IsOk() );
|
||||
|
Reference in New Issue
Block a user