1
0
mirror of https://github.com/lammertb/libhttp.git synced 2025-12-22 04:02:04 +03:00

Merge branch 'jensenn-authorization_handler'

This commit is contained in:
bel
2016-01-12 20:36:32 +01:00
4 changed files with 278 additions and 71 deletions

View File

@@ -97,6 +97,31 @@ class CIVETWEB_API CivetHandler
virtual bool handlePatch(CivetServer *server, struct mg_connection *conn);
};
/**
* Basic interface for a URI authorization handler. Handler implementations
* must be reentrant.
*/
class CIVETWEB_API CivetAuthHandler
{
public:
/**
* Destructor
*/
virtual ~CivetAuthHandler()
{
}
/**
* Callback method for authorization requests. It is up the this handler
* to generate 401 responses if authorization fails.
*
* @param server - the calling server
* @param conn - the connection information
* @returns true if authorization succeeded, false otherwise
*/
virtual bool authorize(CivetServer *server, struct mg_connection *conn) = 0;
};
/**
* Basic interface for a websocket handler. Handlers implementations
* must be reentrant.
@@ -263,6 +288,34 @@ class CIVETWEB_API CivetServer
*/
void removeWebSocketHandler(const std::string &uri);
/**
* addAuthHandler(const std::string &, CivetAuthHandler *)
*
* Adds a URI authorization handler. If there is existing URI authorization
* handler, it will be replaced with this one.
*
* URI's are ordered and prefix (REST) URI's are supported.
*
* @param uri - URI to match.
* @param handler - authorization handler instance to use.
*/
void addAuthHandler(const std::string &uri, CivetAuthHandler *handler);
void
addAuthHandler(const std::string &uri, CivetAuthHandler &handler)
{
addAuthHandler(uri, &handler);
}
/**
* removeAuthHandler(const std::string &)
*
* Removes an authorization handler.
*
* @param uri - the exact URL used in addAuthHandler().
*/
void removeAuthHandler(const std::string &uri);
/**
* getListeningPorts()
*
@@ -490,6 +543,17 @@ class CIVETWEB_API CivetServer
void *cbdata);
static void webSocketCloseHandler(const struct mg_connection *conn,
void *cbdata);
/**
* authHandler(struct mg_connection *, void *cbdata)
*
* Handles the authorization requests.
*
* @param conn - the connection information
* @param cbdata - pointer to the CivetAuthHandler instance.
* @returns 1 if authorized, 0 otherwise
*/
static int authHandler(struct mg_connection *conn, void *cbdata);
/**
* closeHandler(struct mg_connection *)
*

View File

@@ -334,6 +334,29 @@ mg_set_websocket_handler(struct mg_context *ctx,
mg_websocket_close_handler close_handler,
void *cbdata);
/* mg_authorization_handler
Some description here
Parameters:
conn: current connection information.
cbdata: the callback data configured with mg_set_request_handler().
Returns:
0: access denied
1: access granted
*/
typedef int (*mg_authorization_handler)(struct mg_connection *conn,
void *cbdata);
/* mg_set_auth_handler
Sets or removes a URI mapping for an authorization handler.
This function works similar to mg_set_request_handler - see there. */
CIVETWEB_API void mg_set_auth_handler(struct mg_context *ctx,
const char *uri,
mg_authorization_handler handler,
void *cbdata);
/* Get the value of particular configuration parameter.
The value returned is read-only. Civetweb does not allow changing
configuration at run time.

View File

@@ -142,6 +142,31 @@ CivetServer::requestHandler(struct mg_connection *conn, void *cbdata)
return 0; // No handler found
}
int
CivetServer::authHandler(struct mg_connection *conn, void *cbdata)
{
const struct mg_request_info *request_info = mg_get_request_info(conn);
assert(request_info != NULL);
CivetServer *me = (CivetServer *)(request_info->user_data);
assert(me != NULL);
// Happens when a request hits the server before the context is saved
if (me->context == NULL)
return 0;
mg_lock_context(me->context);
me->connections[conn] = CivetConnection();
mg_unlock_context(me->context);
CivetAuthHandler *handler = (CivetAuthHandler *)cbdata;
if (handler) {
return handler->authorize(me, conn) ? 1 : 0;
}
return 0; // No handler found
}
int
CivetServer::webSocketConnectionHandler(const struct mg_connection *conn,
void *cbdata)
@@ -263,7 +288,7 @@ CivetServer::CivetServer(std::vector<std::string> options,
callbacks.connection_close = closeHandler;
std::vector<const char *> pointers(options.size());
for (int i = 0; i < options.size(); i++) {
for (size_t i = 0; i < options.size(); i++) {
pointers.push_back(options[i].c_str());
}
pointers.push_back(0);
@@ -317,6 +342,12 @@ CivetServer::addWebSocketHandler(const std::string &uri,
handler);
}
void
CivetServer::addAuthHandler(const std::string &uri, CivetAuthHandler *handler)
{
mg_set_auth_handler(context, uri.c_str(), authHandler, handler);
}
void
CivetServer::removeHandler(const std::string &uri)
{
@@ -330,6 +361,12 @@ CivetServer::removeWebSocketHandler(const std::string &uri)
context, uri.c_str(), NULL, NULL, NULL, NULL, NULL);
}
void
CivetServer::removeAuthHandler(const std::string &uri)
{
mg_set_auth_handler(context, uri.c_str(), NULL, NULL);
}
void
CivetServer::close()
{

View File

@@ -1159,16 +1159,17 @@ mg_static_assert((sizeof(config_options) / sizeof(config_options[0]))
== (NUM_OPTIONS + 1),
"config_options and enum not sync");
enum { REQUEST_HANDLER, WEBSOCKET_HANDLER, AUTH_HANDLER };
struct mg_request_handler_info {
struct mg_handler_info {
/* Name/Pattern of the URI. */
char *uri;
size_t uri_len;
/* URI type: ws/wss (websocket) or http/https (web page). */
int is_websocket_handler;
/* handler type */
int handler_type;
/* Handler for http/https requests. */
/* Handler for http/https or authorization requests. */
mg_request_handler handler;
/* Handler for ws/wss (websocket) requests. */
@@ -1177,11 +1178,14 @@ struct mg_request_handler_info {
mg_websocket_data_handler data_handler;
mg_websocket_close_handler close_handler;
/* Handler for authorization requests */
mg_authorization_handler auth_handler;
/* User supplied argument for the handler function. */
void *cbdata;
/* next request handler in a linked list */
struct mg_request_handler_info *next;
/* next handler in a linked list */
struct mg_handler_info *next;
};
struct mg_context {
@@ -1219,7 +1223,7 @@ struct mg_context {
char *systemName; /* What operating system is running */
/* linked list of uri handlers */
struct mg_request_handler_info *request_handlers;
struct mg_handler_info *handlers;
#if defined(USE_LUA) && defined(USE_WEBSOCKET)
/* linked list of shared lua websockets */
@@ -9099,26 +9103,28 @@ redirect_to_https_port(struct mg_connection *conn, int ssl_index)
static void
mg_set_request_handler_type(struct mg_context *ctx,
mg_set_handler_type(struct mg_context *ctx,
const char *uri,
int is_websocket_handler,
int handler_type,
int is_delete_request,
mg_request_handler handler,
mg_websocket_connect_handler connect_handler,
mg_websocket_ready_handler ready_handler,
mg_websocket_data_handler data_handler,
mg_websocket_close_handler close_handler,
mg_authorization_handler auth_handler,
void *cbdata)
{
struct mg_request_handler_info *tmp_rh, **lastref;
struct mg_handler_info *tmp_rh, **lastref;
size_t urilen = strlen(uri);
if (is_websocket_handler) {
if (handler_type == WEBSOCKET_HANDLER) {
/* assert(handler == NULL); */
/* assert(is_delete_request || connect_handler!=NULL ||
* ready_handler!=NULL || data_handler!=NULL ||
* close_handler!=NULL);
*/
/* assert(auth_handler == NULL); */
if (handler != NULL) {
return;
}
@@ -9128,11 +9134,15 @@ mg_set_request_handler_type(struct mg_context *ctx,
&& close_handler == NULL) {
return;
}
} else {
if (auth_handler != NULL) {
return;
}
} else if (handler_type == REQUEST_HANDLER) {
/* assert(connect_handler==NULL && ready_handler==NULL &&
* data_handler==NULL && close_handler==NULL); */
/* assert(is_delete_request || (handler!=NULL));
*/
/* assert(auth_handler == NULL); */
if (connect_handler != NULL || ready_handler != NULL
|| data_handler != NULL
|| close_handler != NULL) {
@@ -9141,6 +9151,25 @@ mg_set_request_handler_type(struct mg_context *ctx,
if (!is_delete_request && (handler == NULL)) {
return;
}
if (auth_handler != NULL) {
return;
}
} else { /* AUTH_HANDLER */
/* assert(handler == NULL); */
/* assert(connect_handler==NULL && ready_handler==NULL &&
* data_handler==NULL && close_handler==NULL); */
/* assert(auth_handler != NULL); */
if (handler != NULL) {
return;
}
if (connect_handler != NULL || ready_handler != NULL
|| data_handler != NULL
|| close_handler != NULL) {
return;
}
if (!is_delete_request && (auth_handler == NULL)) {
return;
}
}
if (!ctx) {
@@ -9150,20 +9179,21 @@ mg_set_request_handler_type(struct mg_context *ctx,
mg_lock_context(ctx);
/* first try to find an existing handler */
lastref = &(ctx->request_handlers);
for (tmp_rh = ctx->request_handlers; tmp_rh != NULL;
tmp_rh = tmp_rh->next) {
if (tmp_rh->is_websocket_handler == is_websocket_handler) {
lastref = &(ctx->handlers);
for (tmp_rh = ctx->handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) {
if (tmp_rh->handler_type == handler_type) {
if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri, uri)) {
if (!is_delete_request) {
/* update existing handler */
if (!is_websocket_handler) {
if (handler_type == REQUEST_HANDLER) {
tmp_rh->handler = handler;
} else {
} else if (handler_type == WEBSOCKET_HANDLER) {
tmp_rh->connect_handler = connect_handler;
tmp_rh->ready_handler = ready_handler;
tmp_rh->data_handler = data_handler;
tmp_rh->close_handler = close_handler;
} else { /* AUTH_HANDLER */
tmp_rh->auth_handler = auth_handler;
}
tmp_rh->cbdata = cbdata;
} else {
@@ -9186,8 +9216,8 @@ mg_set_request_handler_type(struct mg_context *ctx,
return;
}
tmp_rh = (struct mg_request_handler_info *)
mg_calloc(sizeof(struct mg_request_handler_info), 1);
tmp_rh =
(struct mg_handler_info *)mg_calloc(sizeof(struct mg_handler_info), 1);
if (tmp_rh == NULL) {
mg_unlock_context(ctx);
mg_cry(fc(ctx), "%s", "Cannot create new request handler struct, OOM");
@@ -9201,16 +9231,18 @@ mg_set_request_handler_type(struct mg_context *ctx,
return;
}
tmp_rh->uri_len = urilen;
if (!is_websocket_handler) {
if (handler_type == REQUEST_HANDLER) {
tmp_rh->handler = handler;
} else {
} else if (handler_type == WEBSOCKET_HANDLER) {
tmp_rh->connect_handler = connect_handler;
tmp_rh->ready_handler = ready_handler;
tmp_rh->data_handler = data_handler;
tmp_rh->close_handler = close_handler;
} else { /* AUTH_HANDLER */
tmp_rh->auth_handler = auth_handler;
}
tmp_rh->cbdata = cbdata;
tmp_rh->is_websocket_handler = is_websocket_handler;
tmp_rh->handler_type = handler_type;
tmp_rh->next = NULL;
*lastref = tmp_rh;
@@ -9224,8 +9256,17 @@ mg_set_request_handler(struct mg_context *ctx,
mg_request_handler handler,
void *cbdata)
{
mg_set_request_handler_type(
ctx, uri, 0, handler == NULL, handler, NULL, NULL, NULL, NULL, cbdata);
mg_set_handler_type(ctx,
uri,
REQUEST_HANDLER,
handler == NULL,
handler,
NULL,
NULL,
NULL,
NULL,
NULL,
cbdata);
}
@@ -9241,34 +9282,54 @@ mg_set_websocket_handler(struct mg_context *ctx,
int is_delete_request = (connect_handler == NULL) && (ready_handler == NULL)
&& (data_handler == NULL)
&& (close_handler == NULL);
mg_set_request_handler_type(ctx,
mg_set_handler_type(ctx,
uri,
1,
WEBSOCKET_HANDLER,
is_delete_request,
NULL,
connect_handler,
ready_handler,
data_handler,
close_handler,
NULL,
cbdata);
}
void
mg_set_auth_handler(struct mg_context *ctx,
const char *uri,
mg_request_handler handler,
void *cbdata)
{
mg_set_handler_type(ctx,
uri,
AUTH_HANDLER,
handler == NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
handler,
cbdata);
}
static int
get_request_handler(struct mg_connection *conn,
int is_websocket_request,
int handler_type,
mg_request_handler *handler,
mg_websocket_connect_handler *connect_handler,
mg_websocket_ready_handler *ready_handler,
mg_websocket_data_handler *data_handler,
mg_websocket_close_handler *close_handler,
mg_authorization_handler *auth_handler,
void **cbdata)
{
const struct mg_request_info *request_info = mg_get_request_info(conn);
if (request_info) {
const char *uri = request_info->local_uri;
size_t urilen = strlen(uri);
struct mg_request_handler_info *tmp_rh;
struct mg_handler_info *tmp_rh;
if (!conn || !conn->ctx) {
return 0;
@@ -9277,17 +9338,19 @@ get_request_handler(struct mg_connection *conn,
mg_lock_context(conn->ctx);
/* first try for an exact match */
for (tmp_rh = conn->ctx->request_handlers; tmp_rh != NULL;
for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL;
tmp_rh = tmp_rh->next) {
if (tmp_rh->is_websocket_handler == is_websocket_request) {
if (tmp_rh->handler_type == handler_type) {
if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri, uri)) {
if (is_websocket_request) {
if (handler_type == WEBSOCKET_HANDLER) {
*connect_handler = tmp_rh->connect_handler;
*ready_handler = tmp_rh->ready_handler;
*data_handler = tmp_rh->data_handler;
*close_handler = tmp_rh->close_handler;
} else {
} else if (handler_type == REQUEST_HANDLER) {
*handler = tmp_rh->handler;
} else { /* AUTH_HANDLER */
*auth_handler = tmp_rh->auth_handler;
}
*cbdata = tmp_rh->cbdata;
mg_unlock_context(conn->ctx);
@@ -9297,18 +9360,20 @@ get_request_handler(struct mg_connection *conn,
}
/* next try for a partial match, we will accept uri/something */
for (tmp_rh = conn->ctx->request_handlers; tmp_rh != NULL;
for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL;
tmp_rh = tmp_rh->next) {
if (tmp_rh->is_websocket_handler == is_websocket_request) {
if (tmp_rh->handler_type == handler_type) {
if (tmp_rh->uri_len < urilen && uri[tmp_rh->uri_len] == '/'
&& memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0) {
if (is_websocket_request) {
if (handler_type == WEBSOCKET_HANDLER) {
*connect_handler = tmp_rh->connect_handler;
*ready_handler = tmp_rh->ready_handler;
*data_handler = tmp_rh->data_handler;
*close_handler = tmp_rh->close_handler;
} else {
} else if (handler_type == REQUEST_HANDLER) {
*handler = tmp_rh->handler;
} else { /* AUTH_HANDLER */
*auth_handler = tmp_rh->auth_handler;
}
*cbdata = tmp_rh->cbdata;
mg_unlock_context(conn->ctx);
@@ -9318,17 +9383,19 @@ get_request_handler(struct mg_connection *conn,
}
/* finally try for pattern match */
for (tmp_rh = conn->ctx->request_handlers; tmp_rh != NULL;
for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL;
tmp_rh = tmp_rh->next) {
if (tmp_rh->is_websocket_handler == is_websocket_request) {
if (tmp_rh->handler_type == handler_type) {
if (match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0) {
if (is_websocket_request) {
if (handler_type == WEBSOCKET_HANDLER) {
*connect_handler = tmp_rh->connect_handler;
*ready_handler = tmp_rh->ready_handler;
*data_handler = tmp_rh->data_handler;
*close_handler = tmp_rh->close_handler;
} else {
} else if (handler_type == REQUEST_HANDLER) {
*handler = tmp_rh->handler;
} else { /* AUTH_HANDLER */
*auth_handler = tmp_rh->auth_handler;
}
*cbdata = tmp_rh->cbdata;
mg_unlock_context(conn->ctx);
@@ -9405,6 +9472,8 @@ handle_request(struct mg_connection *conn)
mg_websocket_data_handler ws_data_handler = NULL;
mg_websocket_close_handler ws_close_handler = NULL;
void *callback_data = NULL;
mg_authorization_handler auth_handler = NULL;
void *auth_callback_data = NULL;
#if !defined(NO_FILES)
time_t curtime = time(NULL);
char date[64];
@@ -9491,12 +9560,14 @@ handle_request(struct mg_connection *conn)
/* 5.2. check if the request will be handled by a callback */
if (get_request_handler(conn,
is_websocket_request,
is_websocket_request ? WEBSOCKET_HANDLER
: REQUEST_HANDLER,
&callback_handler,
&ws_connect_handler,
&ws_ready_handler,
&ws_data_handler,
&ws_close_handler,
NULL,
&callback_data)) {
/* 5.2.1. A callback will handle this request. All requests
* handled
@@ -9522,10 +9593,23 @@ handle_request(struct mg_connection *conn)
}
/* 6. authorization check */
if (is_put_or_delete_request && !is_script_resource
/* 6.1. a custom authorization handler is installed */
if (get_request_handler(conn,
AUTH_HANDLER,
NULL,
NULL,
NULL,
NULL,
NULL,
&auth_handler,
&auth_callback_data)) {
if (!auth_handler(conn, auth_callback_data)) {
return;
}
} else if (is_put_or_delete_request && !is_script_resource
&& !is_callback_resource) {
/* 6.1. this request is a PUT/DELETE to a real file */
/* 6.1.1. thus, the server must have real files */
/* 6.2. this request is a PUT/DELETE to a real file */
/* 6.2.1. thus, the server must have real files */
#if defined(NO_FILES)
if (1) {
#else
@@ -9541,8 +9625,7 @@ handle_request(struct mg_connection *conn)
}
#if !defined(NO_FILES)
/* 6.1.2. Check if put authorization for static files is
* available.
/* 6.2.2. Check if put authorization for static files is available.
*/
if (!is_authorized_for_put(conn)) {
send_authorization_request(conn);
@@ -9551,7 +9634,7 @@ handle_request(struct mg_connection *conn)
#endif
} else {
/* 6.2. This is either a OPTIONS, GET, HEAD or POST request,
/* 6.3. This is either a OPTIONS, GET, HEAD or POST request,
* or it is a PUT or DELETE request to a resource that does not
* correspond to a file. Check authorization. */
if (!check_authorization(conn, path)) {
@@ -12126,7 +12209,7 @@ static void
free_context(struct mg_context *ctx)
{
int i;
struct mg_request_handler_info *tmp_rh;
struct mg_handler_info *tmp_rh;
if (ctx == NULL) {
return;
@@ -12162,9 +12245,9 @@ free_context(struct mg_context *ctx)
}
/* Deallocate request handlers */
while (ctx->request_handlers) {
tmp_rh = ctx->request_handlers;
ctx->request_handlers = tmp_rh->next;
while (ctx->handlers) {
tmp_rh = ctx->handlers;
ctx->handlers = tmp_rh->next;
mg_free(tmp_rh->uri);
mg_free(tmp_rh);
}
@@ -12359,7 +12442,7 @@ mg_start(const struct mg_callbacks *callbacks,
ctx->callbacks.exit_context = 0;
}
ctx->user_data = user_data;
ctx->request_handlers = NULL;
ctx->handlers = NULL;
#if defined(USE_LUA) && defined(USE_WEBSOCKET)
ctx->shared_lua_websockets = 0;