PIHTTPServer basic auth works

This commit is contained in:
2024-11-24 23:23:08 +03:00
parent c2c9822169
commit 1acaf24df9
8 changed files with 184 additions and 61 deletions

View File

@@ -1,7 +1,6 @@
#ifndef curl_thread_pool_p_H
#define curl_thread_pool_p_H
#include "piconditionvar.h"
#include "piprotectedvariable.h"
#include "pisemaphore.h"
#include "pithread.h"

View File

@@ -102,8 +102,8 @@ void PIHTTPClient::procHeaderLine(PIString & line) {
size_t PIHTTPClient::writeMemoryFunc(void * contents, size_t size, size_t nmemb, void * ptr) {
size_t bytes = size * nmemb;
piCout << "writeMemoryFunc" << bytes;
auto client = reinterpret_cast<PIHTTPClient *>(ptr);
// piCout << "writeMemoryFunc" << bytes;
auto client = reinterpret_cast<PIHTTPClient *>(ptr);
if (client->is_cancel) return CURL_WRITEFUNC_ERROR;
client->buffer_out.append(contents, bytes);
return bytes;
@@ -112,8 +112,8 @@ size_t PIHTTPClient::writeMemoryFunc(void * contents, size_t size, size_t nmemb,
size_t PIHTTPClient::readMemoryFunc(void * contents, size_t size, size_t nmemb, void * ptr) {
size_t bytes = size * nmemb;
piCout << "readMemoryFunc" << bytes;
auto client = reinterpret_cast<PIHTTPClient *>(ptr);
// piCout << "readMemoryFunc" << bytes;
auto client = reinterpret_cast<PIHTTPClient *>(ptr);
if (client->is_cancel) return CURL_READFUNC_ABORT;
const auto & buffer(client->request.body());
if (buffer.isEmpty()) return 0;

View File

@@ -30,13 +30,23 @@
// clang-format on
struct MicrohttpdServerConnection {
bool ready();
int sendReply(const PIHTTP::MessageMutable & r);
int sendError();
using namespace PIHTTP;
bool done = false;
PIHTTP::Method method = PIHTTP::Method::Unknown;
struct BasicAuthCred {
bool exists = false;
PIString user;
PIString pass;
};
struct MicrohttpdServerConnection {
bool checkBasicAuth();
bool ready();
int sendReply(const MessageMutable & r);
BasicAuthCred getBasicAuthCred();
bool done = false, authorized = false;
Method method = Method::Unknown;
PIString path;
PIByteArray body;
PIMap<PIString, PIString> headers, args, post;
@@ -46,12 +56,31 @@ struct MicrohttpdServerConnection {
};
bool MicrohttpdServerConnection::checkBasicAuth() {
if (headers.contains(Header::Authorization)) {
auto ba_up = getBasicAuthCred();
bool ok = false; // server->callback_auth(ba_up.user, ba_up.pass);
if (server->callback_auth) ok = server->callback_auth(ba_up.user, ba_up.pass);
if (ok) {
authorized = true;
return true;
}
}
// piCout << "miss authorization";
sendReply(MessageMutable::fromCode(Code::Unauthorized)
.addHeader(Header::WWWAuthenticate, "Basic realm=\"%1\", charset=\"UTF-8\""_a.arg(server->realm))
.setBody(PIByteArray::fromAscii("Authorization required")));
// piCout << "answer sent";
return false;
}
bool MicrohttpdServerConnection::ready() {
if (!server) return false;
if (done) return true;
done = true;
PIHTTP::MessageMutable rep;
if (method == PIHTTP::Method::Get) {
MessageMutable rep;
if (method == Method::Get) {
if (path == "/favicon.ico"_a) {
// piCout << "send favicon" << server->favicon.size() << "bytes";
rep.setBody(server->favicon);
@@ -60,13 +89,13 @@ bool MicrohttpdServerConnection::ready() {
}
}
// piCout << "ready" << (int)method << path;
PIHTTP::MessageMutable req;
MessageMutable req;
req.setMethod(method);
req.setPath(path);
req.setBody(body);
req.headers() = headers;
req.arguments() = args;
rep.setCode(PIHTTP::Code::BadRequest);
rep.setCode(Code::BadRequest);
if (server->callback) rep = server->callback(req);
MicrohttpdServer::addFixedHeaders(rep);
sendReply(rep);
@@ -75,7 +104,7 @@ bool MicrohttpdServerConnection::ready() {
}
int MicrohttpdServerConnection::sendReply(const PIHTTP::MessageMutable & r) {
int MicrohttpdServerConnection::sendReply(const MessageMutable & r) {
MHD_Response * response = MHD_create_response_from_buffer(r.body().size(), (void *)r.body().data(), MHD_RESPMEM_MUST_COPY);
if (!response) {
// piCout << "null response" << r.body.size() << (void *)r.body.data();
@@ -91,8 +120,21 @@ int MicrohttpdServerConnection::sendReply(const PIHTTP::MessageMutable & r) {
}
int MicrohttpdServerConnection::sendError() {
return MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_MUST_COPY));
BasicAuthCred MicrohttpdServerConnection::getBasicAuthCred() {
BasicAuthCred ret;
char * p = nullptr;
auto u = MHD_basic_auth_get_username_password(connection, &p);
if (u) {
ret.user = PIString::fromUTF8(u);
ret.exists = true;
MHD_free(u);
}
if (p) {
ret.pass = PIString::fromUTF8(p);
ret.exists = true;
MHD_free(p);
}
return ret;
}
@@ -152,38 +194,38 @@ int args_iterate(void * cls, MHD_ValueKind kind, const char * key, const char *
}
int answer_to_connection(void * cls,
MHD_Connection * connection,
const char * url,
const char * method,
const char * version,
const char * upload_data,
size_t * upload_data_size,
void ** con_cls) {
int answer_callback(void * cls,
MHD_Connection * connection,
const char * url,
const char * method,
const char * version,
const char * upload_data,
size_t * upload_data_size,
void ** con_cls) {
MicrohttpdServer * server = (MicrohttpdServer *)cls;
PIHTTP::Method m = PIHTTP::Method::Unknown;
Method m = Method::Unknown;
if (0 == strcmp(method, "GET"))
m = PIHTTP::Method::Get;
m = Method::Get;
else if (0 == strcmp(method, "POST"))
m = PIHTTP::Method::Post;
m = Method::Post;
else if (0 == strcmp(method, "HEAD"))
m = PIHTTP::Method::Head;
m = Method::Head;
else if (0 == strcmp(method, "PUT"))
m = PIHTTP::Method::Put;
m = Method::Put;
else if (0 == strcmp(method, "DELETE"))
m = PIHTTP::Method::Delete;
m = Method::Delete;
else if (0 == strcmp(method, "CONNECT"))
m = PIHTTP::Method::Connect;
m = Method::Connect;
else if (0 == strcmp(method, "OPTIONS"))
m = PIHTTP::Method::Options;
m = Method::Options;
else if (0 == strcmp(method, "TRACE"))
m = PIHTTP::Method::Trace;
m = Method::Trace;
else if (0 == strcmp(method, "PATCH"))
m = PIHTTP::Method::Patch;
m = Method::Patch;
if (m == PIHTTP::Method::Unknown) {
if (m == Method::Unknown) {
piCout << "[MicrohttpdServer]"
<< "Warning:"
<< "Unknown method!";
@@ -200,11 +242,14 @@ int answer_to_connection(void * cls,
conn->method = m;
MHD_get_connection_values(connection, MHD_HEADER_KIND, (MHD_KeyValueIterator)header_iterate, *con_cls);
MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, (MHD_KeyValueIterator)args_iterate, *con_cls);
return MHD_YES;
}
if (m == PIHTTP::Method::Unknown) {
return conn->sendError();
if (m == Method::Unknown) {
return conn->sendReply(MessageMutable::fromCode(Code::MethodNotAllowed));
}
if (server->isBasicAuthEnabled() && !conn->authorized) {
if (!conn->checkBasicAuth()) return MHD_YES;
}
if (*upload_data_size) {
@@ -216,7 +261,7 @@ int answer_to_connection(void * cls,
*upload_data_size = 0;
} else {
// qDebug() << "answer ok";
if (!conn->ready()) return conn->sendError();
if (!conn->ready()) return conn->sendReply(MessageMutable::fromCode(Code::InternalServerError));
}
return MHD_YES;
}
@@ -231,6 +276,7 @@ MicrohttpdServer::MicrohttpdServer() {
PRIVATE->daemon = nullptr;
opts[Option::ConnectionLimit] = FD_SETSIZE - 4;
opts[Option::ConnectionTimeout] = 0_s;
realm = "Restricted"_a;
}
@@ -285,7 +331,7 @@ bool MicrohttpdServer::listen(PINetworkAddress addr) {
addr.port(),
nullptr,
nullptr,
(MHD_AccessHandlerCallback)answer_to_connection,
(MHD_AccessHandlerCallback)answer_callback,
this,
MHD_OPTION_ARRAY,
options.data(),
@@ -307,14 +353,14 @@ void MicrohttpdServer::stop() {
}
void MicrohttpdServer::addFixedHeaders(PIHTTP::MessageMutable & msg) {
if (!msg.headers().contains(PIHTTP::Header::ContentType)) {
void MicrohttpdServer::addFixedHeaders(MessageMutable & msg) {
if (!msg.headers().contains(Header::ContentType)) {
if (msg.body().isNotEmpty()) {
if (msg.body().startsWith(PIByteArray::fromAscii("<!DOCTYPE html>")))
msg.addHeader(PIHTTP::Header::ContentType, "text/html; charset=utf-8");
msg.addHeader(Header::ContentType, "text/html; charset=utf-8");
else if (msg.body()[0] == '[' || msg.body()[0] == '{')
msg.addHeader(PIHTTP::Header::ContentType, "application/json; charset=utf-8");
msg.addHeader(Header::ContentType, "application/json; charset=utf-8");
}
}
msg.addHeader(PIHTTP::Header::AccessControlAllowOrigin, "*");
msg.addHeader(Header::AccessControlAllowOrigin, "*");
}

View File

@@ -16,3 +16,47 @@ const char * PIHTTP::methodName(Method m) {
};
return "UNKNOWN";
}
PIHTTP::MessageMutable & PIHTTP::MessageMutable::addHeader(const PIString & header, const PIString & value) {
m_headers[header] = value;
return *this;
}
PIHTTP::MessageMutable & PIHTTP::MessageMutable::setMethod(Method m) {
m_method = m;
return *this;
}
PIHTTP::MessageMutable & PIHTTP::MessageMutable::setCode(Code c) {
m_code = c;
return *this;
}
PIHTTP::MessageMutable & PIHTTP::MessageMutable::setPath(PIString p) {
m_path = std::move(p);
return *this;
}
PIHTTP::MessageMutable & PIHTTP::MessageMutable::setBody(PIByteArray b) {
m_body = std::move(b);
return *this;
}
PIHTTP::MessageMutable PIHTTP::MessageMutable::fromCode(Code c) {
PIHTTP::MessageMutable ret;
ret.setCode(c);
return ret;
}
PIHTTP::MessageMutable PIHTTP::MessageMutable::fromMethod(Method m) {
PIHTTP::MessageMutable ret;
ret.setMethod(m);
return ret;
}

View File

@@ -30,16 +30,18 @@ protected:
class PIP_EXPORT MessageMutable: public MessageConst {
public:
void setMethod(PIHTTP::Method m) { m_method = m; }
void setCode(PIHTTP::Code c) { m_code = c; }
void setPath(PIString p) { m_path = std::move(p); }
void setBody(PIByteArray b) { m_body = std::move(b); }
MessageMutable & setMethod(PIHTTP::Method m);
MessageMutable & setCode(PIHTTP::Code c);
MessageMutable & setPath(PIString p);
MessageMutable & setBody(PIByteArray b);
const PIMap<PIString, PIString> & headers() const { return m_headers; }
const PIMap<PIString, PIString> & arguments() const { return m_arguments; }
PIMap<PIString, PIString> & headers() { return m_headers; }
PIMap<PIString, PIString> & arguments() { return m_arguments; }
void addHeader(const PIString & header, const PIString & value) { m_headers[header] = value; }
MessageMutable & addHeader(const PIString & header, const PIString & value);
void removeHeader(const PIString & header) { m_headers.remove(header); }
static MessageMutable fromCode(PIHTTP::Code c);
static MessageMutable fromMethod(PIHTTP::Method m);
};

View File

@@ -32,15 +32,25 @@ public:
bool isListen() const;
void stop();
void enableBasicAuth() { setBasicAuthEnabled(true); }
void disableBasicAuth() { setBasicAuthEnabled(false); }
void setBasicAuthEnabled(bool yes) { use_basic_auth = yes; }
bool isBasicAuthEnabled() const { return use_basic_auth; }
void setBasicAuthRealm(const PIString & r) { realm = r; }
void setRequestCallback(std::function<PIHTTP::MessageMutable(const PIHTTP::MessageConst &)> c) { callback = c; }
void setBasicAuthCallback(std::function<bool(const PIString &, const PIString &)> c) { callback_auth = c; }
private:
static void addFixedHeaders(PIHTTP::MessageMutable & msg);
PRIVATE_DECLARATION(PIP_HTTP_SERVER_EXPORT)
PIByteArray favicon;
PIString realm;
PIMap<Option, PIVariant> opts;
std::function<PIHTTP::MessageMutable(const PIHTTP::MessageConst &)> callback;
std::function<bool(const PIString &, const PIString &)> callback_auth;
std::atomic_bool use_basic_auth = {false};
PIByteArray mem_key, mem_cert, key_pass;
};

View File

@@ -17,6 +17,8 @@ public:
void unregisterPath(const PIString & path, PIHTTP::Method method);
void unregisterPath(const PIString & path);
// void registerBasicAuth() {}
void addReplyHeader(const PIString & name, const PIString & value) { reply_headers[name] = value; }
void removeReplyHeader(const PIString & name) { reply_headers.remove(name); }
void clearReplyHeaders() { reply_headers.clear(); }