Changeset - af6c160261c2
[Not reviewed]
3 5 3
Jan Kaluza - 10 years ago 2016-01-20 15:02:41
jkaluza@redhat.com
Web interface: support service.base_location=/something/ to override default '/' root location used by the web interface
8 files changed with 43 insertions and 14 deletions:
0 comments (0 inline, 0 general)
spectrum_manager/src/html/footer.shtml
Show inline comments
 
file renamed from spectrum_manager/src/html/footer.html to spectrum_manager/src/html/footer.shtml
 
      </section>
 
    </div>
 

	
 
    <!-- FOOTER  -->
 
    <div id="footer_wrap" class="outer">
 
      <footer class="inner">
 
        <p class="copyright">Spectrum 2 developed by <a href="https://github.com/hanzz">Jan Kaluza - HanzZ</a></p>
 
      </footer>
 
    </div>
 
</body></html>
 
<!--#call replace_base_url -->
spectrum_manager/src/html/header.shtml
Show inline comments
 
file renamed from spectrum_manager/src/html/header.html to spectrum_manager/src/html/header.shtml
spectrum_manager/src/html/instances/index.shtml
Show inline comments
 
<!--#include virtual="/header.html" -->
 
<!--#include virtual="/header.shtml" -->
 

	
 
<script type="text/javascript">
 
$(function() {
 
	show_instances();
 
});
 
</script>
 

	
 
<!--#include virtual="/footer.html" -->
 
<!--#include virtual="/footer.shtml" -->
spectrum_manager/src/html/instances/register.shtml
Show inline comments
 
<!--#include virtual="/header.html" -->
 
<!--#include virtual="/header.shtml" -->
 

	
 
<h2>Register Spectrum 2 instance</h2>
 
<form action="/api/v1/instances/register" class="basic-grey" method="POST"> 
 
<h1>Register Spectrum 2 instance 
 
	<span id="description"></span> 
 
</h1> 
 
<label> 
 
	<span id="jid_desc"></span> 
 
	<input type="text" id="jid" name="jid" placeholder=""></textarea> 
 
</label> 
 
<label> 
 
	<span id="uin_desc"></span> 
 
	<input type="text" id="uin" name="uin" placeholder=""></textarea> 
 
</label> 
 
<label><span id="password_desc"></span> 
 
	<input type="password" id="password" name="password" placeholder=""></textarea> 
 
</label> 
 
<label> 
 
	<span>&nbsp;</span> 
 
	<input type="submit" class="button" value="Register" />
 
</label> 
 
<input type="hidden" name="instance" id="instance" value=""></input> 
 
</form><br/>
 

	
 
<script type="text/javascript">
 
$(function() {
 
	fill_instances_register_form();
 
});
 
</script>
 

	
 
<!--#include virtual="/footer.html" -->
 
<!--#include virtual="/footer.shtml" -->
spectrum_manager/src/html/js/app.js
Show inline comments
 
function show_instances() {
 
	$.get("/api/v1/instances", function(data) {
 
	$.get($.cookie("base_location") + "api/v1/instances", function(data) {
 
		$("#main_content").html("<h2>List of Spectrum 2 instances</h2><table id='main_result'><tr><th>Name<th>Status</th><th>Actions</th></tr>");
 

	
 
		var admin = $.cookie("admin") == "1";
 
		$.each(data.instances, function(i, instance) {
 
			if (instance.running) {
 
				if (admin) {
 
					var command = instance.running ? "stop" : "start";
 
				}
 
				else {
 
					var command = instance.registered ? "unregister" : "register";
 
					if (instance.registered) {
 
						instance.status += "<br/>Registered as " + instance.username;
 
					}
 
				}
 
			}
 
			else if (admin) {
 
				var command = "start";
 
			}
 
			else {
 
				var command = "";
 
			}
 
			var row = '<tr>'
 
			row += '<td>' + instance.name + '</td>'
 
			row += '<td>' + instance.status + '</td>'
 

	
 
			if (command == 'register') {
 
				row += '<td><a class="button_command" href="/instances/register.shtml?id=' + instance.id + '">' + command + '</a>' + '</td></tr>';
 
				row += '<td><a class="button_command" href="' + $.cookie("base_location") + 'instances/register.shtml?id=' + instance.id + '">' + command + '</a>' + '</td></tr>';
 
				$("#main_result  > tbody:last-child").append(row);
 
			}
 
			else if (command == "") {
 
				row += '<td></td></tr>';
 
				$("#main_result  > tbody:last-child").append(row);
 
			}
 
			else {
 
				row += '<td><a class="button_command" href="/api/v1/instances/' + command + '/' + instance.id + '">' + command + '</a>' + '</td></tr>';
 
				row += '<td><a class="button_command" href="' + $.cookie("base_location") +  'api/v1/instances/' + command + '/' + instance.id + '">' + command + '</a>' + '</td></tr>';
 
				$("#main_result  > tbody:last-child").append(row);
 
				$(".button_command").click(function(e) {
 
					e.preventDefault();
 
					$(this).parent().empty().progressbar( {value: false} ).css('height', '1em');
 

	
 
					var url = $(this).attr('href');
 
					$.get(url, function(data) {
 
						show_instances();
 
					});
 
				})
 
			}
 
		});
 
	});
 
}
 

	
 
function getQueryParams(qs) {
 
	qs = qs.split('+').join(' ');
 

	
 
	var params = {},
 
		tokens,
 
		re = /[?&]?([^=]+)=([^&]*)/g;
 

	
 
	while (tokens = re.exec(qs)) {
 
		params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
 
	}
 

	
 
	return params;
 
}
 

	
 
function fill_instances_register_form() {
 
	var query = getQueryParams(document.location.search);
 
	$("#instance").attr("value", query.id);
 
	
 
	$.get("/api/v1/instances/register_form/" + query.id, function(data) {
 
	$.get($.cookie("base_location") + "api/v1/instances/register_form/" + query.id, function(data) {
 
		$("#jid_desc").html(data.username_label + ":");
 
		$("#uin_desc").html(data.legacy_username_label + ":");
 
		$("#password_desc").html(data.password_label + ":");
 

	
 
		$("#jid").attr("placeholder", data.username_label);
 
		$("#uin").attr("placeholder", data.legacy_username_label);
 
		$("#password").attr("placeholder", data.password_label);
 
	});
 
}
 

	
spectrum_manager/src/html/login/index.shtml
Show inline comments
 
file renamed from spectrum_manager/src/html/login/index.html to spectrum_manager/src/html/login/index.shtml
 
@@ -48,25 +48,26 @@
 
        <span>Password:</span>
 
        <input type="password" id="password" name="password" placeholder="Password"></textarea>
 
    </label>
 
     <label>
 
        <span>&nbsp;</span>
 
        <input type="submit" class="button" value="Login" />
 
    </label>    
 
</form>
 

	
 
      </section>
 
    </div>
 

	
 
    <!-- FOOTER  -->
 
    <div id="footer_wrap" class="outer">
 
      <footer class="inner">
 
        <p class="copyright">Spectrum 2 developed by <a href="https://github.com/hanzz">Jan Kaluza - HanzZ</a></p>
 
      </footer>
 
    </div>
 

	
 
    
 

	
 
  
 

	
 

	
 
</body></html>
 
\ No newline at end of file
 
</body></html>
 
<!--#call replace_base_url -->
 
\ No newline at end of file
spectrum_manager/src/managerconfig.cpp
Show inline comments
 
@@ -14,48 +14,49 @@
 
 * GNU General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU General Public License
 
 * along with this program; if not, write to the Free Software
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#include "managerconfig.h"
 
#include <fstream>
 

	
 
using namespace boost::program_options;
 

	
 
bool ManagerConfig::load(const std::string &configfile, boost::program_options::options_description &opts) {
 
	std::ifstream ifs(configfile.c_str());
 
	if (!ifs.is_open())
 
		return false;
 

	
 
	opts.add_options()
 
		("service.admin_username", value<std::string>()->default_value(""), "Administrator username.")
 
		("service.admin_password", value<std::string>()->default_value(""), "Administrator password.")
 
		("service.port", value<int>()->default_value(8081), "Web interface port.")
 
		("service.cert", value<std::string>()->default_value(""), "Web interface certificate in PEM format when TLS should be used.")
 
		("service.config_directory", value<std::string>()->default_value("/etc/spectrum2/transports/"), "Directory with spectrum2 configuration files. One .cfg file per one instance")
 
		("service.data_dir", value<std::string>()->default_value("/var/lib/spectrum2_manager/html"), "Directory to store Spectrum 2 manager data")
 
		("service.base_location", value<std::string>()->default_value("/"), "Base location of the web interface")
 
		("servers.server", value<std::vector<std::string> >()->multitoken(), "Server.")
 
		("database.type", value<std::string>()->default_value("none"), "Database type.")
 
		("database.database", value<std::string>()->default_value("/var/lib/spectrum2/$jid/database.sql"), "Database used to store data")
 
		("database.server", value<std::string>()->default_value("localhost"), "Database server.")
 
		("database.user", value<std::string>()->default_value(""), "Database user.")
 
		("database.password", value<std::string>()->default_value(""), "Database Password.")
 
		("database.port", value<int>()->default_value(0), "Database port.")
 
		("database.prefix", value<std::string>()->default_value(""), "Prefix of tables in database")
 
	;
 

	
 
	store(parse_config_file(ifs, opts), m_variables);
 
	notify(m_variables);
 

	
 
	m_file = configfile;
 

	
 
	onManagerConfigReloaded();
 

	
 
	return true;
 
}
 

	
 
bool ManagerConfig::load(const std::string &configfile) {
 
	options_description opts("Transport options");
 
	return load(configfile, opts);
 
}
spectrum_manager/src/server.cpp
Show inline comments
 
@@ -187,101 +187,104 @@ Server::session *Server::get_session(struct http_message *hm) {
 

	
 
void Server::authorize(struct mg_connection *conn, struct http_message *hm) {
 
	Server::session *session;
 
	std::string user = get_http_var(hm, "user");
 
	std::string password = get_http_var(hm, "password");
 

	
 
	std::string host;
 
	mg_str *host_hdr = mg_get_http_header(hm, "Host");
 
	if (host_hdr) {
 
		if (!CONFIG_STRING(m_config, "service.cert").empty()) {
 
			host += "https://";
 
		}
 
		else {
 
			host += "http://";
 
		}
 
		host += std::string(host_hdr->p, host_hdr->len);
 
	}
 

	
 
	if (check_password(user, password) && (session = new_session(user)) != NULL) {
 
		std::cout << "User authorized\n";
 
		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: admin=%s\r\n"  // Set user, needed by Javascript code
 
			"Set-Cookie: base_location=%s\r\n"  // Set user, needed by Javascript code
 
			"Set-Cookie: original_url=/; max-age=0\r\n"  // Delete original_url
 
			"Location: %s/instances\r\n\r\n",
 
			session->session_id, session->user, session->admin ? "1" : "0", host.c_str());
 
			"Location: %s%sinstances\r\n\r\n",
 
			session->session_id, session->user, session->admin ? "1" : "0", CONFIG_STRING(m_config, "service.base_location").c_str(), host.c_str(), CONFIG_STRING(m_config, "service.base_location").c_str());
 
	} 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, "/users") ||
 
		!mg_vcmp(&hm->uri, "/users/add") ||
 
		!mg_vcmp(&hm->uri, "/authorize")) {
 
		return true;
 
	}
 

	
 
	if ((session = get_session(hm)) != NULL) {
 
		generate_session_id(valid_id, session->random, session->user);
 
		if (strcmp(valid_id, session->session_id) == 0) {
 
			session->expire = time(0) + SESSION_TTL;
 
			authorized = true;
 
		}
 
	}
 

	
 
	return authorized;
 
}
 

	
 
void Server::redirect_to(struct mg_connection *conn, struct http_message *hm, const char *where) {
 
	std::string host;
 
	mg_str *host_hdr = mg_get_http_header(hm, "Host");
 
	if (host_hdr) {
 
		if (!CONFIG_STRING(m_config, "service.cert").empty()) {
 
			host += "https://";
 
		}
 
		else {
 
			host += "http://";
 
		}
 
		host += std::string(host_hdr->p, host_hdr->len);
 
	}
 

	
 
	where = where + 1;
 

	
 
	mg_printf(conn, "HTTP/1.1 302 Found\r\n"
 
		"Set-Cookie: original_url=/\r\n"
 
		"Location: %s%s\r\n\r\n", host.c_str(), where);
 
		"Location: %s%s%s\r\n\r\n", host.c_str(), CONFIG_STRING(m_config, "service.base_location").c_str(), 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());
 
}
 

	
 
std::string Server::send_command(const std::string &jid, const std::string &cmd) {
 
	Swift::SimpleEventLoop eventLoop;
 
	Swift::BoostNetworkFactories networkFactories(&eventLoop);
 

	
 
	ask_local_server(m_config, networkFactories, jid, cmd);
 
	struct timeval td_start,td_end;
 
	float elapsed = 0; 
 
	gettimeofday(&td_start, NULL);
 
	gettimeofday(&td_end, NULL);
 

	
 
	time_t started = time(NULL);
 
	while(get_response().empty() && td_end.tv_sec - td_start.tv_sec < 1) {
 
@@ -352,50 +355,50 @@ void Server::serve_cmd(struct mg_connection *conn, struct http_message *hm) {
 
	html += "<pre>" + response + "</pre>";
 

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

	
 
void Server::serve_logout(struct mg_connection *conn, struct http_message *hm) {
 
	std::string host;
 
	mg_str *host_hdr = mg_get_http_header(hm, "Host");
 
	if (host_hdr) {
 
		if (!CONFIG_STRING(m_config, "service.cert").empty()) {
 
			host += "https://";
 
		}
 
		else {
 
			host += "http://";
 
		}
 
		host += std::string(host_hdr->p, host_hdr->len);
 
	}
 

	
 
	Server:session *session = get_session(hm);
 
	mg_printf(conn, "HTTP/1.1 302 Found\r\n"
 
		"Set-Cookie: session=%s; max-age=0\r\n"
 
		"Set-Cookie: admin=%s; max-age=0\r\n"
 
		"Location: %s/\r\n\r\n",
 
		session->session_id, session->admin ? "1" : "0", host.c_str());
 
		"Location: %s%s\r\n\r\n",
 
		session->session_id, session->admin ? "1" : "0", host.c_str(), CONFIG_STRING(m_config, "service.base_location").c_str());
 

	
 
	sessions.erase(session->session_id);
 
	delete session;
 
}
 

	
 
void Server::serve_users_add(struct mg_connection *conn, struct http_message *hm) {
 
	std::string user = get_http_var(hm, "user");
 
	std::string password = get_http_var(hm, "password");
 

	
 
	if (!user.empty() && !password.empty()) {
 
		if (m_storage) {
 
			UserInfo dummy;
 
			bool registered = m_storage->getUser(user, dummy);
 
			if (!registered) {
 
				UserInfo info;
 
				info.jid = user;
 
				info.password = password;
 
				m_storage->setUser(info);
 
			}
 
			else {
 
				redirect_to(conn, hm, "/users?error=This+username+is+already+registered");
 
				return;
 
			}
 
		}
 
@@ -706,70 +709,93 @@ void Server::serve_instances(struct mg_connection *conn, struct http_message *hm
 
						html += "<td><a href=\"/instances/register?instance=" + instance + "\">Register</a></td>";
 
					}
 
				}
 
				else {
 
					html += "<td>" + response + "</td>";
 
					html += "<td>No available action</td>";
 
				}
 

	
 
				html += "</tr>";
 
			}
 

	
 
			html += "</table>";
 
		}
 
	}
 
	print_html(conn, hm, html);
 
}
 

	
 
void Server::serve_oauth2(struct mg_connection *conn, struct http_message *hm) {
 
	std::cout << "OAUTH2 handler\n";
 
}
 

	
 
void Server::event_handler(struct mg_connection *conn, int ev, void *p) {
 
	struct http_message *hm = (struct http_message *) p;
 

	
 
	if (ev == MG_EV_SSI_CALL) {
 
		mbuf_resize(&conn->send_mbuf, conn->send_mbuf.size * 2);
 
		std::string resp(conn->send_mbuf.buf, conn->send_mbuf.len);
 
		boost::replace_all(resp, "href=\"/", std::string("href=\"") + CONFIG_STRING(m_config, "service.base_location"));
 
		boost::replace_all(resp, "src=\"/", std::string("src=\"") + CONFIG_STRING(m_config, "service.base_location"));
 
		boost::replace_all(resp, "action=\"/", std::string("action=\"") + CONFIG_STRING(m_config, "service.base_location"));
 
		strcpy(conn->send_mbuf.buf, resp.c_str());
 
		mbuf_trim(&conn->send_mbuf);
 
		return;
 
	}
 

	
 
	if (ev != MG_EV_HTTP_REQUEST) {
 
		return;
 
	}
 

	
 
	hm->uri.p += CONFIG_STRING(m_config, "service.base_location").size() - 1;
 
	hm->uri.len -= CONFIG_STRING(m_config, "service.base_location").size() - 1;
 

	
 
	if (!is_authorized(conn, hm)) {
 
		redirect_to(conn, hm, "/login");
 
	} else if (mg_vcmp(&hm->uri, "/authorize") == 0) {
 
		authorize(conn, hm);
 
// 	} else if (mg_vcmp(&hm->uri, "/") == 0) {
 
// 		serve_instances(conn, hm);
 
	} else if (mg_vcmp(&hm->uri, "/logout") == 0) {
 
		serve_logout(conn, hm);
 
// 	} else if (mg_vcmp(&hm->uri, "/instances") == 0) {
 
// 		serve_instances(conn, hm);
 
// 	} else if (mg_vcmp(&hm->uri, "/onlineusers") == 0) {
 
// 		serve_onlineusers(conn, hm);
 
// 	} else if (mg_vcmp(&hm->uri, "/cmd") == 0) {
 
// 		serve_cmd(conn, hm);
 
// 	} else if (mg_vcmp(&hm->uri, "/instances/start") == 0) {
 
// 		serve_instances_start(conn, hm);
 
// 	} else if (mg_vcmp(&hm->uri, "/instances/stop") == 0) {
 
// 		serve_instances_stop(conn, hm);
 
// 	} else if (mg_vcmp(&hm->uri, "/instances/register") == 0) {
 
// 		serve_instances_register(conn, hm);
 
// 	} else if (mg_vcmp(&hm->uri, "/instances/unregister") == 0) {
 
// 		serve_instances_unregister(conn, hm);
 
// 	} else if (mg_vcmp(&hm->uri, "/users") == 0) {
 
// 		serve_users(conn, hm);
 
// 	} else if (mg_vcmp(&hm->uri, "/users/add") == 0) {
 
// 		serve_users_add(conn, hm);
 
// 	} else if (mg_vcmp(&hm->uri, "/users/remove") == 0) {
 
// 		serve_users_remove(conn, hm);
 
	} else if (has_prefix(&hm->uri, "/oauth2")) {
 
		serve_oauth2(conn, hm);
 
	} else if (has_prefix(&hm->uri, "/api/v1/")) {
 
		m_apiServer->handleRequest(this, get_session(hm), conn, hm);
 
	} else {
 
		if (hm->uri.p[hm->uri.len - 1] != '/') {
 
			std::string url(hm->uri.p, hm->uri.len);
 
			if (url.find(".") == std::string::npos) {
 
				url += "/";
 
				redirect_to(conn, hm, url.c_str());
 
				conn->flags |= MG_F_SEND_AND_CLOSE;
 
				return;
 
			}
 
		}
 
		mg_serve_http(conn, hm, s_http_server_opts);
 
	}
 

	
 
	conn->flags |= MG_F_SEND_AND_CLOSE;
 
}
 

	
 

	
 

	
 

	
0 comments (0 inline, 0 general)