Changeset - a13c26fb9289
[Not reviewed]
0 2 0
Jan Kaluza - 10 years ago 2015-12-30 15:40:28
jkaluza@redhat.com
spectrum2_manager: use get_http_var to get post/get variables
2 files changed with 27 insertions and 34 deletions:
0 comments (0 inline, 0 general)
spectrum_manager/src/server.cpp
Show inline comments
 
#include "server.h"
 
#include "methods.h"
 

	
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <assert.h>
 
#include <string.h>
 
#include <time.h>
 
#include <stdarg.h>
 
#include <pthread.h>
 
#include <fstream>
 
#include <string>
 
#include <cerrno>
 

	
 
#define SESSION_TTL 120
 

	
 
static struct mg_serve_http_opts s_http_server_opts;
 

	
 

	
 
static void get_qsvar(const struct http_message *hm,
 
                      const char *name, char *dst, size_t dst_len) {
 
	mg_get_http_var(&hm->body, name, dst, dst_len);
 
static std::string get_http_var(const struct http_message *hm, const char *name) {
 
	char data[4096];
 
	data[0] = '\0';
 

	
 
	mg_get_http_var(&hm->body, name, data, sizeof(data));
 
	if (data[0] != '\0') {
 
		return data;
 
	}
 

	
 
	mg_get_http_var(&hm->query_string, name, data, sizeof(data));
 
	if (data[0] != '\0') {
 
		return data;
 
	}
 

	
 
	return "";
 
}
 

	
 
static void my_strlcpy(char *dst, const char *src, size_t len) {
 
  strncpy(dst, src, len);
 
  dst[len - 1] = '\0';
 
}
 

	
 
// Generate session ID. buf must be 33 bytes in size.
 
// Note that it is easy to steal session cookies by sniffing traffic.
 
// This is why all communication must be SSL-ed.
 
static void generate_session_id(char *buf, const char *random,
 
                                const char *user) {
 
  cs_md5(buf, random, strlen(random), user, strlen(user), NULL);
 
}
 

	
 
static void _event_handler(struct mg_connection *nc, int ev, void *p) {
 
	static_cast<Server *>(nc->mgr->user_data)->event_handler(nc, ev, p);
 
}
 

	
 
Server::Server(ManagerConfig *config) {
 
	srand((unsigned) time(0));
 
	m_config = config;
 
	m_user = CONFIG_STRING(m_config, "service.admin_username");
 
	m_password = CONFIG_STRING(m_config, "service.admin_password");
 
@@ -61,106 +73,92 @@ Server::Server(ManagerConfig *config) {
 
	}
 

	
 
	std::ifstream footer(std::string(CONFIG_STRING(m_config, "service.data_dir") + "/footer.html").c_str(), std::ios::in);
 
	if (footer) {
 
		footer.seekg(0, std::ios::end);
 
		m_footer.resize(footer.tellg());
 
		footer.seekg(0, std::ios::beg);
 
		footer.read(&m_footer[0], m_footer.size());
 
		footer.close();
 
	}
 
}
 

	
 
Server::~Server() {
 
	mg_mgr_free(&m_mgr);
 
}
 

	
 
bool Server::start() {
 
	for (;;) {
 
		mg_mgr_poll(&m_mgr, 1000);
 
	}
 

	
 
	return true;
 
}
 

	
 
bool Server::check_password(const char *user, const char *password) {
 
bool Server::check_password(const std::string &user, const std::string &password) {
 
	return (m_user == user && m_password == password);
 
}
 

	
 
// Allocate new session object
 
Server::session *Server::new_session(const char *user) {
 
Server::session *Server::new_session(const std::string &user) {
 
	Server::session *session = new Server::session;
 

	
 
	my_strlcpy(session->user, user, sizeof(session->user));
 
	my_strlcpy(session->user, user.c_str(), sizeof(session->user));
 
	snprintf(session->random, sizeof(session->random), "%d", rand());
 
	generate_session_id(session->session_id, session->random, session->user);
 
	session->expire = time(0) + SESSION_TTL;
 
	session->admin = std::string(user) == m_user;
 

	
 
	sessions[session->session_id] = session;
 
	return session;
 
}
 

	
 
// Get session object for the connection. Caller must hold the lock.
 
Server::session *Server::get_session(struct http_message *hm) {
 
	time_t now = time(NULL);
 
	char session_id[255];
 
	struct mg_str *hdr = mg_get_http_header(hm, "Cookie");
 
	int len = mg_http_parse_header(hdr, "session", session_id, sizeof(session_id));
 
	session_id[len] = 0;
 

	
 
	if (sessions.find(session_id) == sessions.end()) {
 
		return NULL;
 
	}
 

	
 
	if (sessions[session_id]->expire != 0 && sessions[session_id]->expire > now) {
 
		return sessions[session_id];
 
	}
 

	
 
	return NULL;
 
}
 

	
 
void Server::authorize(struct mg_connection *conn, struct http_message *hm) {
 
	char user[255], password[255];
 
	Server::session *session;
 

	
 
	// Fetch user name and password.
 
	get_qsvar(hm, "user", user, sizeof(user));
 
	get_qsvar(hm, "password", password, sizeof(password));
 
	std::string user = get_http_var(hm, "user");
 
	std::string password = get_http_var(hm, "password");
 

	
 
	if (check_password(user, password) && (session = new_session(user)) != NULL) {
 
		std::cout << "User authorized\n";
 
		// Authentication success:
 
		//   1. create new session
 
		//   2. set session ID token in the cookie
 
		//   3. remove original_url from the cookie - not needed anymore
 
		//   4. redirect client back to the original URL
 
		//
 
		// The most secure way is to stay HTTPS all the time. However, just to
 
		// show the technique, we redirect to HTTP after the successful
 
		// authentication. The danger of doing this is that session cookie can
 
		// be stolen and an attacker may impersonate the user.
 
		// Secure application must use HTTPS all the time.
 
		mg_printf(conn, "HTTP/1.1 302 Found\r\n"
 
			"Set-Cookie: session=%s; max-age=3600; http-only\r\n"  // Session ID
 
			"Set-Cookie: user=%s\r\n"  // Set user, needed by Javascript code
 
			"Set-Cookie: original_url=/; max-age=0\r\n"  // Delete original_url
 
			"Location: /\r\n\r\n",
 
			session->session_id, session->user);
 
	} else {
 
		// Authentication failure, redirect to login.
 
		redirect_to(conn, hm, "/login");
 
	}
 
}
 

	
 
bool Server::is_authorized(const struct mg_connection *conn, struct http_message *hm) {
 
	Server::session *session;
 
	char valid_id[33];
 
	bool authorized = false;
 

	
 
	// Always authorize accesses to login page and to authorize URI
 
	if (!mg_vcmp(&hm->uri, "/login") ||
 
		!mg_vcmp(&hm->uri, "/login/") ||
 
		!mg_vcmp(&hm->uri, "/form.css") ||
 
		!mg_vcmp(&hm->uri, "/style.css") ||
 
		!mg_vcmp(&hm->uri, "/logo.png") ||
 
		!mg_vcmp(&hm->uri, "/authorize")) {
 
@@ -175,117 +173,112 @@ bool Server::is_authorized(const struct mg_connection *conn, struct http_message
 
		}
 
	}
 

	
 
	return authorized;
 
}
 

	
 
void Server::redirect_to(struct mg_connection *conn, struct http_message *hm, const char *where) {
 
	mg_printf(conn, "HTTP/1.1 302 Found\r\n"
 
		"Set-Cookie: original_url=/\r\n"
 
		"Location: %s\r\n\r\n", where);
 
}
 

	
 
void Server::print_html(struct mg_connection *conn, struct http_message *hm, const std::string &html) {
 
	mg_printf(conn,
 
			"HTTP/1.1 200 OK\r\n"
 
			"Content-Type: text/html\r\n"
 
			"Content-Length: %d\r\n"        // Always set Content-Length
 
			"\r\n"
 
			"%s%s%s",
 
			(int) html.size() + m_header.size() + m_footer.size(), m_header.c_str(), html.c_str(), m_footer.c_str());
 
}
 

	
 
void Server::serve_onlineusers(struct mg_connection *conn, struct http_message *hm) {
 
	std::string html;
 
	char jid[255];
 
	get_qsvar(hm, "jid", jid, sizeof(jid));
 
	std::string jid = get_http_var(hm, "jid");
 

	
 
	html += std::string("<h2>") + jid + " online users</h2><table><tr><th>JID<th>Command</th></tr>";
 

	
 
	Swift::SimpleEventLoop eventLoop;
 
	Swift::BoostNetworkFactories networkFactories(&eventLoop);
 

	
 
	ask_local_server(m_config, networkFactories, jid, "online_users");
 
	eventLoop.runUntilEvents();
 
	while(get_response().empty()) {
 
		eventLoop.runUntilEvents();
 
	}
 

	
 
	std::string response = get_response();
 
	std::vector<std::string> users;
 
	boost::split(users, response, boost::is_any_of("\n"));
 

	
 
	BOOST_FOREACH(std::string &user, users) {
 
		html += "<tr><td>" + user + "</td><td></td></tr>";
 
	}
 

	
 
	html += "</table><a href=\"/\">Back to main page</a>";
 
	html += "</body></html>";
 
	print_html(conn, hm, html);
 
}
 

	
 
void Server::serve_cmd(struct mg_connection *conn, struct http_message *hm) {
 
	std::string html;
 
	char jid[255];
 
	get_qsvar(hm, "jid", jid, sizeof(jid));
 
	char cmd[4096];
 
	get_qsvar(hm, "cmd", cmd, sizeof(cmd));
 
	std::string jid = get_http_var(hm, "jid");
 
	std::string cmd = get_http_var(hm, "cmd");
 

	
 
	html += std::string("<h2>") + jid + " command result</h2>";
 

	
 
	Swift::SimpleEventLoop eventLoop;
 
	Swift::BoostNetworkFactories networkFactories(&eventLoop);
 

	
 
	ask_local_server(m_config, networkFactories, jid, cmd);
 
	while(get_response().empty()) {
 
		eventLoop.runUntilEvents();
 
	}
 

	
 
	std::string response = get_response();
 
	
 
	html += "<pre>" + response + "</pre>";
 

	
 
	html += "<a href=\"/\">Back to main page</a>";
 
	html += "</body></html>";
 
	print_html(conn, hm, html);
 
}
 

	
 

	
 
void Server::serve_start(struct mg_connection *conn, struct http_message *hm) {
 
	std::string html;
 
	char jid[255];
 
	get_qsvar(hm, "jid", jid, sizeof(jid));
 
	std::string jid = get_http_var(hm, "jid");
 

	
 
	start_instances(m_config, jid);
 
	html += "<b>" + get_response() + "</b><br/><a href=\"/\">Back to main page</a>";
 
	html += "</body></html>";
 
	print_html(conn, hm, html);
 
}
 

	
 
void Server::serve_stop(struct mg_connection *conn, struct http_message *hm) {
 
	std::string html;
 
	char jid[255];
 
	get_qsvar(hm, "jid", jid, sizeof(jid));
 
	std::string jid = get_http_var(hm, "jid");
 

	
 
	stop_instances(m_config, jid);
 
	html += "<b>" + get_response() + "</b><br/><a href=\"/\">Back to main page</a>";
 
	html += "</body></html>";
 
	print_html(conn, hm, html);
 
}
 
void Server::serve_root(struct mg_connection *conn, struct http_message *hm) {
 
	std::vector<std::string> list = show_list(m_config, false);
 
	std::string html = "<h2>List of instances</h2>";
 

	
 
	if (list.empty()) {
 
		html += "<p>There are no Spectrum 2 instances yet. You can create new instance by adding configuration files into <pre>/etc/spectrum2/transports</pre> directory. You can then maintain the Spectrum 2 instance here.</p>";
 
	}
 
	else {
 
		html += "<table><tr><th>JID<th>Status</th><th>Command</th><th>Run command</th></tr>";
 
		BOOST_FOREACH(std::string &instance, list) {
 
			html += "<tr>";
 
			html += "<td><a href=\"/onlineusers?jid=" + instance + "\">" + instance + "</a></td>";
 
			Swift::SimpleEventLoop eventLoop;
 
			Swift::BoostNetworkFactories networkFactories(&eventLoop);
 

	
 
			ask_local_server(m_config, networkFactories, instance, "status");
 
			eventLoop.runUntilEvents();
 
			while(get_response().empty()) {
spectrum_manager/src/server.h
Show inline comments
 
@@ -39,45 +39,45 @@ class Server {
 
			char user[255];  // Authenticated user
 
			time_t expire;            // Expiration timestamp, UTC
 
			bool admin;
 
		};
 

	
 
		/// Constructor.
 
		Server(ManagerConfig *config);
 

	
 
		/// Destructor
 
		virtual ~Server();
 

	
 
		bool start();
 

	
 
		void event_handler(struct mg_connection *nc, int ev, void *p);
 

	
 
	private:
 
		void serve_root(struct mg_connection *conn, struct http_message *hm);
 
		void serve_start(struct mg_connection *conn, struct http_message *hm);
 
		void serve_stop(struct mg_connection *conn, struct http_message *hm);
 
		void serve_onlineusers(struct mg_connection *conn, struct http_message *hm);
 
		void serve_cmd(struct mg_connection *conn, struct http_message *hm);
 
		void print_html(struct mg_connection *conn, struct http_message *hm, const std::string &html);
 

	
 
	private:
 
		bool check_password(const char *user, const char *password);
 
		session *new_session(const char *user);
 
		bool check_password(const std::string &user, const std::string &password);
 
		session *new_session(const std::string &user);
 
		session *get_session(struct http_message *hm);
 

	
 
		void authorize(struct mg_connection *conn, struct http_message *hm);
 

	
 
		bool is_authorized(const struct mg_connection *conn, struct http_message *hm);
 

	
 
		void redirect_to(struct mg_connection *conn, struct http_message *hm, const char *where);
 

	
 
	private:
 
		struct mg_mgr m_mgr;
 
		struct mg_connection *m_nc;
 

	
 
		std::map<std::string, session *> sessions;
 
		std::string m_user;
 
		std::string m_password;
 
		ManagerConfig *m_config;
 
		std::string m_header;
 
		std::string m_footer;
 
};
0 comments (0 inline, 0 general)