Implement WinHTTP authentication

This commit is contained in:
Tobias Taschner
2018-10-25 23:33:05 +02:00
parent 1a34f3dab9
commit 4fd6091513
5 changed files with 145 additions and 20 deletions

View File

@@ -52,6 +52,23 @@ private:
wxDECLARE_NO_COPY_CLASS(wxWebResponseWinHTTP); 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 class WXDLLIMPEXP_NET wxWebRequestWinHTTP : public wxWebRequest
{ {
public: public:
@@ -65,6 +82,8 @@ public:
wxWebResponse* GetResponse() wxOVERRIDE; wxWebResponse* GetResponse() wxOVERRIDE;
wxWebAuthChallenge* GetAuthChallenge() const wxOVERRIDE { return m_authChallenge.get(); }
void HandleCallback(DWORD dwInternetStatus, LPVOID lpvStatusInformation, void HandleCallback(DWORD dwInternetStatus, LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength); DWORD dwStatusInformationLength);
@@ -76,15 +95,20 @@ private:
HINTERNET m_connect; HINTERNET m_connect;
HINTERNET m_request; HINTERNET m_request;
wxScopedPtr<wxWebResponseWinHTTP> m_response; wxScopedPtr<wxWebResponseWinHTTP> m_response;
wxScopedPtr<wxWebAuthChallengeWinHTTP> m_authChallenge;
wxMemoryBuffer m_dataWriteBuffer; wxMemoryBuffer m_dataWriteBuffer;
wxFileOffset m_dataWritten; wxFileOffset m_dataWritten;
void SendRequest();
void WriteData(); void WriteData();
void CreateResponse(); void CreateResponse();
void SetFailedWithLastError(); void SetFailedWithLastError();
friend class wxWebAuthChallengeWinHTTP;
wxDECLARE_NO_COPY_CLASS(wxWebRequestWinHTTP); wxDECLARE_NO_COPY_CLASS(wxWebRequestWinHTTP);
}; };

View File

@@ -23,6 +23,7 @@
class wxWebResponse; class wxWebResponse;
class wxWebSession; class wxWebSession;
class wxWebAuthChallenge;
WX_DECLARE_STRING_HASH_MAP(wxString, wxWebRequestHeaderMap); WX_DECLARE_STRING_HASH_MAP(wxString, wxWebRequestHeaderMap);
@@ -33,19 +34,12 @@ public:
{ {
State_Idle, State_Idle,
State_Unauthorized, State_Unauthorized,
State_UnauthorizedProxy,
State_Active, State_Active,
State_Completed, State_Completed,
State_Failed, State_Failed,
State_Cancelled State_Cancelled
}; };
enum CredentialTarget
{
CredentialTarget_Server,
CredentialTarget_Proxy
};
virtual ~wxWebRequest() { } virtual ~wxWebRequest() { }
virtual void SetHeader(const wxString& name, const wxString& value) virtual void SetHeader(const wxString& name, const wxString& value)
@@ -57,9 +51,6 @@ public:
void SetData(wxSharedPtr<wxInputStream> dataStream, const wxString& contentType, wxFileOffset dataSize = wxInvalidOffset); void SetData(wxSharedPtr<wxInputStream> dataStream, const wxString& contentType, wxFileOffset dataSize = wxInvalidOffset);
void SetCredentials(const wxString& user, const wxString& password,
CredentialTarget target);
void SetIgnoreServerErrorStatus(bool ignore) { m_ignoreServerErrorStatus = ignore; } void SetIgnoreServerErrorStatus(bool ignore) { m_ignoreServerErrorStatus = ignore; }
virtual void Start() = 0; virtual void Start() = 0;
@@ -68,6 +59,8 @@ public:
virtual wxWebResponse* GetResponse() = 0; virtual wxWebResponse* GetResponse() = 0;
virtual wxWebAuthChallenge* GetAuthChallenge() const = 0;
int GetId() const { return m_id; } int GetId() const { return m_id; }
State GetState() const { return m_state; } State GetState() const { return m_state; }
@@ -125,6 +118,30 @@ private:
wxDECLARE_NO_COPY_CLASS(wxWebResponse); 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 class WXDLLIMPEXP_NET wxWebSessionFactory
{ {
public: public:

View File

@@ -93,15 +93,10 @@ void wxWebRequest::SetData(wxSharedPtr<wxInputStream> dataStream, const wxString
SetHeader("Content-Type", contentType); 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) void wxWebRequest::SetState(State state, const wxString & failMsg)
{ {
// Add a reference while the request is active // 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(); IncRef();
// Trigger the event in the main thread // Trigger the event in the main thread

View File

@@ -219,10 +219,20 @@ void wxWebRequestWinHTTP::CreateResponse()
if (::WinHttpReceiveResponse(m_request, NULL)) if (::WinHttpReceiveResponse(m_request, NULL))
{ {
m_response.reset(new wxWebResponseWinHTTP(*this)); 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 // Start reading the response
if (!m_response->ReadData()) if ( !m_response->ReadData() )
SetFailedWithLastError(); SetFailedWithLastError();
} }
} }
@@ -297,16 +307,21 @@ void wxWebRequestWinHTTP::Start()
return; return;
} }
SendRequest();
}
void wxWebRequestWinHTTP::SendRequest()
{
// Combine all headers to a string // Combine all headers to a string
wxString allHeaders; 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)); allHeaders.append(wxString::Format("%s: %s\n", header->first, header->second));
if ( m_dataSize ) if ( m_dataSize )
m_dataWritten = 0; m_dataWritten = 0;
// Send request // Send request
if ( WinHttpSendRequest(m_request, if ( ::WinHttpSendRequest(m_request,
allHeaders.wc_str(), allHeaders.length(), allHeaders.wc_str(), allHeaders.length(),
NULL, 0, m_dataSize, NULL, 0, m_dataSize,
(DWORD_PTR)this) ) (DWORD_PTR)this) )
@@ -408,6 +423,57 @@ void wxWebResponseWinHTTP::ReportDataComplete()
m_stream.reset(new wxMemoryInputStream(m_readBuffer.GetData(), m_readBuffer.GetDataLen())); 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 // wxWebSessionWinHTTP
// //

View File

@@ -57,6 +57,7 @@ public:
{ {
switch (evt.GetState()) switch (evt.GetState())
{ {
case wxWebRequest::State_Unauthorized:
case wxWebRequest::State_Completed: case wxWebRequest::State_Completed:
case wxWebRequest::State_Failed: case wxWebRequest::State_Failed:
case wxWebRequest::State_Cancelled: case wxWebRequest::State_Cancelled:
@@ -124,6 +125,28 @@ TEST_CASE_METHOD(RequestFixture, "WebRequest", "[net][.]")
request->SetMethod("PUT"); request->SetMethod("PUT");
Run(); 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 #endif // wxUSE_WEBREQUEST