1
0

Added Basic auth support to cHTTPRequest.

This commit is contained in:
madmaxoft 2013-10-04 20:28:30 +02:00
parent 1012fd82fd
commit db3d83b38d
7 changed files with 136 additions and 1 deletions

View File

@ -26,6 +26,18 @@ void cHTTPConnection::SendStatusAndReason(int a_StatusCode, const AString & a_Re
{
AppendPrintf(m_OutgoingData, "%d %s\r\n\r\n", a_StatusCode, a_Response.c_str());
m_HTTPServer.NotifyConnectionWrite(*this);
m_State = wcsRecvHeaders;
}
void cHTTPConnection::SendNeedAuth(const AString & a_Realm)
{
AppendPrintf(m_OutgoingData, "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"%s\"\r\n\r\n", a_Realm.c_str());
m_HTTPServer.NotifyConnectionWrite(*this);
m_State = wcsRecvHeaders;
}
@ -73,6 +85,12 @@ void cHTTPConnection::AwaitNextRequest(void)
{
switch (m_State)
{
case wcsRecvHeaders:
{
// Nothing has been received yet, or a special response was given (SendStatusAndReason() or SendNeedAuth() )
break;
}
case wcsRecvIdle:
{
// The client is waiting for a response, send an "Internal server error":

View File

@ -43,6 +43,9 @@ public:
/// Sends HTTP status code together with a_Reason (used for HTTP errors)
void SendStatusAndReason(int a_StatusCode, const AString & a_Reason);
/// Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm
void SendNeedAuth(const AString & a_Realm);
/// Sends the headers contained in a_Response
void Send(const cHTTPResponse & a_Response);

View File

@ -71,7 +71,8 @@ cHTTPRequest::cHTTPRequest(void) :
super(mkRequest),
m_EnvelopeParser(*this),
m_IsValid(true),
m_UserData(NULL)
m_UserData(NULL),
m_HasAuth(false)
{
}
@ -204,6 +205,20 @@ int cHTTPRequest::ParseRequestLine(const char * a_Data, int a_Size)
void cHTTPRequest::OnHeaderLine(const AString & a_Key, const AString & a_Value)
{
if (
(NoCaseCompare(a_Key, "Authorization") == 0) &&
(strncmp(a_Value.c_str(), "Basic ", 6) == 0)
)
{
AString UserPass = Base64Decode(a_Value.substr(6));
size_t idxCol = UserPass.find(':');
if (idxCol != AString::npos)
{
m_AuthUsername = UserPass.substr(0, idxCol);
m_AuthPassword = UserPass.substr(idxCol + 1);
m_HasAuth = true;
}
}
AddHeader(a_Key, a_Value);
}

View File

@ -91,6 +91,15 @@ public:
/// Returns true if more data is expected for the request headers
bool IsInHeaders(void) const { return m_EnvelopeParser.IsInHeaders(); }
/// Returns true if the request did present auth data that was understood by the parser
bool HasAuth(void) const { return m_HasAuth; }
/// Returns the username that the request presented. Only valid if HasAuth() is true
const AString & GetAuthUsername(void) const { return m_AuthUsername; }
/// Returns the password that the request presented. Only valid if HasAuth() is true
const AString & GetAuthPassword(void) const { return m_AuthPassword; }
protected:
/// Parser for the envelope data
cEnvelopeParser m_EnvelopeParser;
@ -110,6 +119,15 @@ protected:
/// Data that the HTTPServer callbacks are allowed to store.
void * m_UserData;
/// Set to true if the request contains auth data that was understood by the parser
bool m_HasAuth;
/// The username used for auth
AString m_AuthUsername;
/// The password used for auth
AString m_AuthPassword;
/** Parses the incoming data for the first line (RequestLine)
Returns the number of bytes consumed, or -1 for an error

View File

@ -73,6 +73,16 @@ class cDebugCallbacks :
return;
}
// Test the auth failure and success:
if (a_Request.GetURL() == "/auth")
{
if (!a_Request.HasAuth() || (a_Request.GetAuthUsername() != "a") || (a_Request.GetAuthPassword() != "b"))
{
a_Connection.SendNeedAuth("MCServer WebAdmin");
return;
}
}
cHTTPResponse Resp;
Resp.SetContentType("text/plain");
a_Connection.Send(Resp);

View File

@ -745,3 +745,71 @@ AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_
/// Converts one Hex character in a Base64 encoding into the data value
static inline int UnBase64(char c)
{
if (c >='A' && c <= 'Z')
{
return c - 'A';
}
if (c >='a' && c <= 'z')
{
return c - 'a' + 26;
}
if (c >= '0' && c <= '9')
{
return c - '0' + 52;
}
if (c == '+')
{
return 62;
}
if (c == '/')
{
return 63;
}
if (c == '=')
{
return -1;
}
return -2;
}
AString Base64Decode(const AString & a_Base64String)
{
AString res;
size_t i, len = a_Base64String.size();
int o, c;
res.resize((len * 4) / 3 + 5, 0); // Approximate the upper bound on the result length
for (o = 0, i = 0; i < len; i++)
{
c = UnBase64(a_Base64String[i]);
if (c >= 0)
{
switch (o & 7)
{
case 0: res[o >> 3] |= (c << 2); break;
case 6: res[o >> 3] |= (c >> 4); res[(o >> 3) + 1] |= (c << 4); break;
case 4: res[o >> 3] |= (c >> 2); res[(o >> 3) + 1] |= (c << 6); break;
case 2: res[o >> 3] |= c; break;
}
o += 6;
}
if (c == -1)
{
// Error while decoding, invalid input. Return as much as we've decoded:
res.resize(o >> 3);
return ERROR_SUCCESS;
}
}
res.resize(o >> 3);
return res;}

View File

@ -81,6 +81,9 @@ extern AString URLDecode(const AString & a_String); // Cannot export to Lua aut
/// Replaces all occurrences of char a_From inside a_String with char a_To.
extern AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_To); // Needn't export to Lua, since Lua doesn't have chars anyway
/// Decodes a Base64-encoded string into the raw data
extern AString Base64Decode(const AString & a_Base64String);
// If you have any other string helper functions, declare them here