From 4fd60915138d95a7b26b7fc8d8e5f247f27fecd7 Mon Sep 17 00:00:00 2001 From: Tobias Taschner Date: Thu, 25 Oct 2018 23:33:05 +0200 Subject: [PATCH] Implement WinHTTP authentication --- include/wx/msw/webrequest_winhttp.h | 24 ++++++++++ include/wx/webrequest.h | 37 +++++++++++---- src/common/webrequest.cpp | 7 +-- src/msw/webrequest_winhttp.cpp | 74 +++++++++++++++++++++++++++-- tests/net/webrequest.cpp | 23 +++++++++ 5 files changed, 145 insertions(+), 20 deletions(-) diff --git a/include/wx/msw/webrequest_winhttp.h b/include/wx/msw/webrequest_winhttp.h index 09b51fb3f8..5da61c7772 100644 --- a/include/wx/msw/webrequest_winhttp.h +++ b/include/wx/msw/webrequest_winhttp.h @@ -52,6 +52,23 @@ private: wxDECLARE_NO_COPY_CLASS(wxWebResponseWinHTTP); }; +class WXDLLIMPEXP_NET wxWebAuthChallengeWinHTTP : public wxWebAuthChallenge +{ +public: + explicit wxWebAuthChallengeWinHTTP(Source source, wxWebRequestWinHTTP& request); + + bool Init(); + + void SetCredentials(const wxString& user, const wxString& password) wxOVERRIDE; + +private: + wxWebRequestWinHTTP& m_request; + DWORD m_target; + DWORD m_selectedScheme; + + wxDECLARE_NO_COPY_CLASS(wxWebAuthChallengeWinHTTP); +}; + class WXDLLIMPEXP_NET wxWebRequestWinHTTP : public wxWebRequest { public: @@ -65,6 +82,8 @@ public: wxWebResponse* GetResponse() wxOVERRIDE; + wxWebAuthChallenge* GetAuthChallenge() const wxOVERRIDE { return m_authChallenge.get(); } + void HandleCallback(DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength); @@ -76,15 +95,20 @@ private: HINTERNET m_connect; HINTERNET m_request; wxScopedPtr m_response; + wxScopedPtr m_authChallenge; wxMemoryBuffer m_dataWriteBuffer; wxFileOffset m_dataWritten; + void SendRequest(); + void WriteData(); void CreateResponse(); void SetFailedWithLastError(); + friend class wxWebAuthChallengeWinHTTP; + wxDECLARE_NO_COPY_CLASS(wxWebRequestWinHTTP); }; diff --git a/include/wx/webrequest.h b/include/wx/webrequest.h index 00cc2cecae..31987d0706 100644 --- a/include/wx/webrequest.h +++ b/include/wx/webrequest.h @@ -23,6 +23,7 @@ class wxWebResponse; class wxWebSession; +class wxWebAuthChallenge; WX_DECLARE_STRING_HASH_MAP(wxString, wxWebRequestHeaderMap); @@ -33,19 +34,12 @@ public: { State_Idle, State_Unauthorized, - State_UnauthorizedProxy, State_Active, State_Completed, State_Failed, State_Cancelled }; - enum CredentialTarget - { - CredentialTarget_Server, - CredentialTarget_Proxy - }; - virtual ~wxWebRequest() { } virtual void SetHeader(const wxString& name, const wxString& value) @@ -57,9 +51,6 @@ public: void SetData(wxSharedPtr dataStream, const wxString& contentType, wxFileOffset dataSize = wxInvalidOffset); - void SetCredentials(const wxString& user, const wxString& password, - CredentialTarget target); - void SetIgnoreServerErrorStatus(bool ignore) { m_ignoreServerErrorStatus = ignore; } virtual void Start() = 0; @@ -68,6 +59,8 @@ public: virtual wxWebResponse* GetResponse() = 0; + virtual wxWebAuthChallenge* GetAuthChallenge() const = 0; + int GetId() const { return m_id; } State GetState() const { return m_state; } @@ -125,6 +118,30 @@ private: wxDECLARE_NO_COPY_CLASS(wxWebResponse); }; +class WXDLLIMPEXP_NET wxWebAuthChallenge +{ +public: + enum Source + { + Source_Server, + Source_Proxy + }; + + virtual ~wxWebAuthChallenge() { } + + Source GetSource() const { return m_source; } + + virtual void SetCredentials(const wxString& user, const wxString& password) = 0; + +protected: + wxWebAuthChallenge(Source source): m_source(source) { } + +private: + Source m_source; + + wxDECLARE_NO_COPY_CLASS(wxWebAuthChallenge); +}; + class WXDLLIMPEXP_NET wxWebSessionFactory { public: diff --git a/src/common/webrequest.cpp b/src/common/webrequest.cpp index 0433d9e5f9..5f985fa3a1 100644 --- a/src/common/webrequest.cpp +++ b/src/common/webrequest.cpp @@ -93,15 +93,10 @@ void wxWebRequest::SetData(wxSharedPtr dataStream, const wxString SetHeader("Content-Type", contentType); } -void wxWebRequest::SetCredentials(const wxString & user, const wxString & password, CredentialTarget target) -{ - wxFAIL_MSG("not implemented"); -} - void wxWebRequest::SetState(State state, const wxString & failMsg) { // Add a reference while the request is active - if (state == State_Active && m_state != State_Active) + if (state == State_Active && m_state != State_Active && m_state != State_Unauthorized) IncRef(); // Trigger the event in the main thread diff --git a/src/msw/webrequest_winhttp.cpp b/src/msw/webrequest_winhttp.cpp index e2e9663e03..26aa705ef6 100644 --- a/src/msw/webrequest_winhttp.cpp +++ b/src/msw/webrequest_winhttp.cpp @@ -219,10 +219,20 @@ void wxWebRequestWinHTTP::CreateResponse() if (::WinHttpReceiveResponse(m_request, NULL)) { m_response.reset(new wxWebResponseWinHTTP(*this)); - if (CheckServerStatus()) + int status = m_response->GetStatus(); + if ( status == 401 || status == 407) + { + m_authChallenge.reset(new wxWebAuthChallengeWinHTTP( + (status == 407) ? wxWebAuthChallenge::Source_Proxy : wxWebAuthChallenge::Source_Server, *this)); + if ( m_authChallenge->Init() ) + SetState(State_Unauthorized, m_response->GetStatusText()); + else + SetFailedWithLastError(); + } + else if ( CheckServerStatus() ) { // Start reading the response - if (!m_response->ReadData()) + if ( !m_response->ReadData() ) SetFailedWithLastError(); } } @@ -297,16 +307,21 @@ void wxWebRequestWinHTTP::Start() return; } + SendRequest(); +} + +void wxWebRequestWinHTTP::SendRequest() +{ // Combine all headers to a string wxString allHeaders; - for (wxWebRequestHeaderMap::const_iterator header = m_headers.begin(); header != m_headers.end(); ++header) + for ( wxWebRequestHeaderMap::const_iterator header = m_headers.begin(); header != m_headers.end(); ++header ) allHeaders.append(wxString::Format("%s: %s\n", header->first, header->second)); if ( m_dataSize ) m_dataWritten = 0; // Send request - if ( WinHttpSendRequest(m_request, + if ( ::WinHttpSendRequest(m_request, allHeaders.wc_str(), allHeaders.length(), NULL, 0, m_dataSize, (DWORD_PTR)this) ) @@ -408,6 +423,57 @@ void wxWebResponseWinHTTP::ReportDataComplete() m_stream.reset(new wxMemoryInputStream(m_readBuffer.GetData(), m_readBuffer.GetDataLen())); } +// +// wxWebAuthChallengeWinHTTP +// +wxWebAuthChallengeWinHTTP::wxWebAuthChallengeWinHTTP(Source source, wxWebRequestWinHTTP & request): + wxWebAuthChallenge(source), + m_request(request), + m_target(0), + m_selectedScheme(0) +{ + +} + +bool wxWebAuthChallengeWinHTTP::Init() +{ + DWORD supportedSchemes; + DWORD firstScheme; + + if ( ::WinHttpQueryAuthSchemes(m_request.GetHandle(), + &supportedSchemes, &firstScheme, &m_target) ) + { + if ( supportedSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE ) + m_selectedScheme = WINHTTP_AUTH_SCHEME_NEGOTIATE; + else if ( supportedSchemes & WINHTTP_AUTH_SCHEME_NTLM ) + m_selectedScheme = WINHTTP_AUTH_SCHEME_NTLM; + else if ( supportedSchemes & WINHTTP_AUTH_SCHEME_PASSPORT ) + m_selectedScheme = WINHTTP_AUTH_SCHEME_PASSPORT; + else if ( supportedSchemes & WINHTTP_AUTH_SCHEME_DIGEST ) + m_selectedScheme = WINHTTP_AUTH_SCHEME_DIGEST; + else if ( supportedSchemes & WINHTTP_AUTH_SCHEME_BASIC ) + m_selectedScheme = WINHTTP_AUTH_SCHEME_BASIC; + else + m_selectedScheme = 0; + + if ( m_selectedScheme ) + return true; + } + + return false; +} + +void wxWebAuthChallengeWinHTTP::SetCredentials(const wxString& user, + const wxString& password) +{ + if ( ::WinHttpSetCredentials(m_request.GetHandle(), m_target, m_selectedScheme, + user.wc_str(), password.wc_str(), NULL) ) + m_request.SendRequest(); + else + m_request.SetFailedWithLastError(); +} + + // // wxWebSessionWinHTTP // diff --git a/tests/net/webrequest.cpp b/tests/net/webrequest.cpp index 147c3f9e5c..9d40350e01 100644 --- a/tests/net/webrequest.cpp +++ b/tests/net/webrequest.cpp @@ -57,6 +57,7 @@ public: { switch (evt.GetState()) { + case wxWebRequest::State_Unauthorized: case wxWebRequest::State_Completed: case wxWebRequest::State_Failed: case wxWebRequest::State_Cancelled: @@ -124,6 +125,28 @@ TEST_CASE_METHOD(RequestFixture, "WebRequest", "[net][.]") request->SetMethod("PUT"); Run(); } + + SECTION("Server auth BASIC") + { + Create("/digest-auth/auth/wxtest/wxwidgets"); + Run(wxWebRequest::State_Unauthorized, 401); + REQUIRE( request->GetAuthChallenge() != NULL ); + request->GetAuthChallenge()->SetCredentials("wxtest", "wxwidgets"); + loop.Run(); + REQUIRE( request->GetResponse()->GetStatus() == 200 ); + REQUIRE( request->GetState() == wxWebRequest::State_Completed ); + } + + SECTION("Server auth DIGEST") + { + Create("/digest-auth/auth/wxtest/wxwidgets"); + Run(wxWebRequest::State_Unauthorized, 401); + REQUIRE( request->GetAuthChallenge() != NULL ); + request->GetAuthChallenge()->SetCredentials("wxtest", "wxwidgets"); + loop.Run(); + REQUIRE( request->GetResponse()->GetStatus() == 200 ); + REQUIRE( request->GetState() == wxWebRequest::State_Completed ); + } } #endif // wxUSE_WEBREQUEST