Browse Source

Merge branch 'jensenn-authorization_handler'

bel 9 năm trước cách đây
mục cha
commit
04255d34ad
4 tập tin đã thay đổi với 279 bổ sung72 xóa
  1. 64 0
      include/CivetServer.h
  2. 23 0
      include/civetweb.h
  3. 38 1
      src/CivetServer.cpp
  4. 154 71
      src/civetweb.c

+ 64 - 0
include/CivetServer.h

@@ -98,6 +98,31 @@ class CIVETWEB_API CivetHandler
 };
 
 /**
+ * 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.
  */
@@ -264,6 +289,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()
 	 *
 	 * Returns a list of ports that are listening
@@ -491,6 +544,17 @@ class CIVETWEB_API CivetServer
 	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 *)
 	 *
 	 * Handles closing a request (internal handler)

+ 23 - 0
include/civetweb.h

@@ -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.

+ 38 - 1
src/CivetServer.cpp

@@ -143,6 +143,31 @@ CivetServer::requestHandler(struct mg_connection *conn, void *cbdata)
 }
 
 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);
@@ -318,6 +343,12 @@ CivetServer::addWebSocketHandler(const std::string &uri,
 }
 
 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)
 {
 	mg_set_request_handler(context, uri.c_str(), NULL, NULL);
@@ -331,6 +362,12 @@ CivetServer::removeWebSocketHandler(const std::string &uri)
 }
 
 void
+CivetServer::removeAuthHandler(const std::string &uri)
+{
+	mg_set_auth_handler(context, uri.c_str(), NULL, NULL);
+}
+
+void
 CivetServer::close()
 {
 	if (context) {

+ 154 - 71
src/civetweb.c

@@ -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,
-                            const char *uri,
-                            int is_websocket_handler,
-                            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,
-                            void *cbdata)
-{
-	struct mg_request_handler_info *tmp_rh, **lastref;
+mg_set_handler_type(struct mg_context *ctx,
+                    const char *uri,
+                    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_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,
-	                            uri,
-	                            1,
-	                            is_delete_request,
-	                            NULL,
-	                            connect_handler,
-	                            ready_handler,
-	                            data_handler,
-	                            close_handler,
-	                            cbdata);
+	mg_set_handler_type(ctx,
+	                    uri,
+	                    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
-		    && !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.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.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;