Implement authentication support for wxWebRequest under Mac

Add wxWebAuthChallengeURLSession and use the appropriate delegate
callback to create it.
This commit is contained in:
Vadim Zeitlin
2021-01-14 23:07:27 +01:00
parent e80b65b132
commit 70e7861a7d
3 changed files with 128 additions and 24 deletions

View File

@@ -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);
};

View File

@@ -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
//

View File

@@ -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() );