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" #include "wx/private/webrequest.h"
DECLARE_WXCOCOA_OBJC_CLASS(NSURLCredential);
DECLARE_WXCOCOA_OBJC_CLASS(NSURLSession); DECLARE_WXCOCOA_OBJC_CLASS(NSURLSession);
DECLARE_WXCOCOA_OBJC_CLASS(NSURLSessionTask); DECLARE_WXCOCOA_OBJC_CLASS(NSURLSessionTask);
DECLARE_WXCOCOA_OBJC_CLASS(wxWebSessionDelegate); DECLARE_WXCOCOA_OBJC_CLASS(wxWebSessionDelegate);
@@ -22,6 +23,29 @@ class wxWebSessionURLSession;
class wxWebRequestURLSession; class wxWebRequestURLSession;
class wxWebResponseURLSession; 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 class wxWebResponseURLSession : public wxWebResponseImpl
{ {
public: public:
@@ -67,7 +91,8 @@ public:
wxWebResponseImplPtr GetResponse() const wxOVERRIDE wxWebResponseImplPtr GetResponse() const wxOVERRIDE
{ return m_response; } { return m_response; }
wxWebAuthChallengeImplPtr GetAuthChallenge() const wxOVERRIDE; wxWebAuthChallengeImplPtr GetAuthChallenge() const wxOVERRIDE
{ return m_authChallenge; }
wxFileOffset GetBytesSent() const wxOVERRIDE; wxFileOffset GetBytesSent() const wxOVERRIDE;
@@ -79,14 +104,22 @@ public:
void HandleCompletion(); void HandleCompletion();
void HandleChallenge(wxWebAuthChallengeURLSession* challenge);
void OnSetCredentials(const wxWebCredentials& cred);
wxWebResponseURLSession* GetResponseImplPtr() const wxWebResponseURLSession* GetResponseImplPtr() const
{ return m_response.get(); } { return m_response.get(); }
wxWebAuthChallengeURLSession* GetAuthChallengeImplPtr() const
{ return m_authChallenge.get(); }
private: private:
wxWebSessionURLSession& m_sessionImpl; wxWebSessionURLSession& m_sessionImpl;
wxString m_url; wxString m_url;
WX_NSURLSessionTask m_task; WX_NSURLSessionTask m_task;
wxObjectDataPtr<wxWebResponseURLSession> m_response; wxObjectDataPtr<wxWebResponseURLSession> m_response;
wxObjectDataPtr<wxWebAuthChallengeURLSession> m_authChallenge;
wxDECLARE_NO_COPY_CLASS(wxWebRequestURLSession); wxDECLARE_NO_COPY_CLASS(wxWebRequestURLSession);
}; };

View File

@@ -97,6 +97,60 @@
[m_requests removeObjectForKey:task]; [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 @end
// //
@@ -170,14 +224,22 @@ void wxWebRequestURLSession::Cancel()
void wxWebRequestURLSession::HandleCompletion() void wxWebRequestURLSession::HandleCompletion()
{ {
if ( CheckServerStatus() ) switch ( m_response->GetStatus() )
SetState(wxWebRequest::State_Completed); {
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"); m_authChallenge.reset(challenge);
return wxWebAuthChallengeImplPtr();
} }
wxFileOffset wxWebRequestURLSession::GetBytesSent() const wxFileOffset wxWebRequestURLSession::GetBytesSent() const
@@ -200,6 +262,33 @@ wxFileOffset wxWebRequestURLSession::GetBytesExpectedToReceive() const
return m_task.countOfBytesExpectedToReceive; 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 // wxWebResponseURLSession
// //

View File

@@ -42,12 +42,6 @@ public:
dataSize = 0; dataSize = 0;
} }
static bool UsingNSURLSession()
{
return wxWebSession::GetDefault().GetLibraryVersionInfo().GetName()
== "URLSession";
}
// All tests should call this function first and skip the test entirely if // 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. // it returns false, as this indicates that web requests tests are disabled.
bool InitBaseURL() bool InitBaseURL()
@@ -240,12 +234,6 @@ TEST_CASE_METHOD(RequestFixture,
if ( !InitBaseURL() ) if ( !InitBaseURL() )
return; return;
if ( UsingNSURLSession() )
{
WARN("NSURLSession backend doesn't support authentication, skipping.");
return;
}
Create("/basic-auth/wxtest/wxwidgets"); Create("/basic-auth/wxtest/wxwidgets");
Run(wxWebRequest::State_Unauthorized, 401); Run(wxWebRequest::State_Unauthorized, 401);
REQUIRE( request.GetAuthChallenge().IsOk() ); REQUIRE( request.GetAuthChallenge().IsOk() );
@@ -273,12 +261,6 @@ TEST_CASE_METHOD(RequestFixture,
if ( !InitBaseURL() ) if ( !InitBaseURL() )
return; return;
if ( UsingNSURLSession() )
{
WARN("NSURLSession backend doesn't support authentication, skipping.");
return;
}
Create("/digest-auth/auth/wxtest/wxwidgets"); Create("/digest-auth/auth/wxtest/wxwidgets");
Run(wxWebRequest::State_Unauthorized, 401); Run(wxWebRequest::State_Unauthorized, 401);
REQUIRE( request.GetAuthChallenge().IsOk() ); REQUIRE( request.GetAuthChallenge().IsOk() );