summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKjetil Orbekk <kjetil.orbekk@gmail.com>2017-06-17 20:08:31 -0400
committerKjetil Orbekk <kjetil.orbekk@gmail.com>2017-06-17 20:08:31 -0400
commit41025a52fe3d2e4988296bfdc1ef549b60b8b667 (patch)
tree8c0110b59bfc98a290a8806cb6a7759765b77129 /src
parent651bfc36a8aa5a83c9143e62859623ef35f31c7f (diff)
Add login / logout functionality.
Adds the ability to log in, but doesn't yet authenticate against the database.
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs2
-rw-r--r--src/render/mod.rs183
-rw-r--r--src/server.rs106
3 files changed, 189 insertions, 102 deletions
diff --git a/src/lib.rs b/src/lib.rs
index e165a1e..3fd5b67 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -13,6 +13,8 @@ extern crate env_logger;
extern crate iron_sessionstorage;
extern crate staticfile;
extern crate regex;
+extern crate params;
+extern crate persistent;
pub mod systemd;
pub mod render;
diff --git a/src/render/mod.rs b/src/render/mod.rs
index a824bd6..8575e9b 100644
--- a/src/render/mod.rs
+++ b/src/render/mod.rs
@@ -3,103 +3,128 @@ use systemd::unit;
use horrorshow::prelude::*;
use horrorshow::Raw;
-fn render_in_page<'a>(content: Box<RenderBox + 'a>) -> String {
- (html!{
- : Raw("<!DOCTYPE html>");
- html {
- head {
- title: "Systemhttpd";
- link(rel="stylesheet", type="text/css", href="/static/main.css");
+#[derive(Debug)]
+pub struct Renderer {
+ pub user: Option<String>
+}
+
+impl Renderer {
+ fn render_in_page<'a>(&self, content: Box<RenderBox + 'a>) -> String {
+ info!("Rendering page with context: {:?}", self);
+ let login_box: Box<RenderBox> = match self.user {
+ Some(ref user) => box_html!{
+ : user;
+ : " (";
+ a(href="logout") { // TODO get base url from context
+ : "log out"
+ }
+ : ")";
+ },
+ None => box_html! {
+ a(href="login") { // TODO Get base url from context
+ : "Login"
+ }
}
- body {
- nav {
- ul {
- li {
- p { : "SystemHttpd" }
+ };
+
+ (html!{
+ : Raw("<!DOCTYPE html>");
+ html {
+ head {
+ title: "Systemhttpd";
+ link(rel="stylesheet", type="text/css", href="/static/main.css");
+ }
+ body {
+ nav {
+ ul {
+ li {
+ p { : "SystemHttpd" }
+ }
}
- }
- ul(class="right") {
- li {
- p { : "Login" }
+ ul(class="right") {
+ li {
+ p {
+ : login_box
+ }
+ }
}
}
- }
- main {
- : content
+ main {
+ : content
+ }
}
}
- }
- }).into_string().unwrap()
-}
+ }).into_string().unwrap()
+ }
-pub fn login_page() -> String {
- render_in_page(box_html! {
- h1 { : "Log in" }
- form(method="post") {
- p { : "Username" }
- input(type="text", name="username") {}
- p { : "Password" }
- input(type="text", name="password") {}
- p {}
- input(type="submit", value="Log in") {}
- }
- })
-}
+ pub fn login_page(&self) -> String {
+ self.render_in_page(box_html! {
+ h1 { : "Log in" }
+ form(method="post") {
+ p { : "Username" }
+ input(type="text", name="username") {}
+ p { : "Password" }
+ input(type="text", name="password") {}
+ p {}
+ input(type="submit", value="Log in") {}
+ }
+ })
+ }
-fn unit_table<'a>(units: &'a [&unit::Unit]) -> Box<RenderBox + 'a> {
- fn render_unit<'a>(unit: &'a unit::Unit) -> Box<RenderBox + 'a> {
- box_html! {
- tr {
- td {
- a(href=format_args!("/status/{}", &unit.name)) {
- : &unit.name
+ fn unit_table<'a>(&self, units: &'a [&unit::Unit]) -> Box<RenderBox + 'a> {
+ fn render_unit<'a>(unit: &'a unit::Unit) -> Box<RenderBox + 'a> {
+ box_html! {
+ tr {
+ td {
+ a(href=format_args!("/status/{}", &unit.name)) {
+ : &unit.name
+ }
+ }
+ td {
+ : format_args!("{} ({})",
+ &unit.active_state,
+ &unit.sub_state)
}
- }
- td {
- : format_args!("{} ({})",
- &unit.active_state,
- &unit.sub_state)
}
}
}
- }
- box_html! {
- table {
- tr {
- th {
- : "Unit"
+ box_html! {
+ table {
+ tr {
+ th {
+ : "Unit"
+ }
+ th {
+ : "Active"
+ }
}
- th {
- : "Active"
+ @ for unit in units {
+ : render_unit(unit)
}
}
- @ for unit in units {
- : render_unit(unit)
- }
}
}
-}
-pub fn system_status(sections: &[(String, Vec<&unit::Unit>)]) -> String {
- let b =
- box_html! {
- @ for &(ref type_, ref units) in sections {
- h1 {
- : type_
- }
- : unit_table(&units)
- }
- };
- render_in_page(b)
-}
+ pub fn system_status(&self, sections: &[(String, Vec<&unit::Unit>)])
+ -> String {
+ self.render_in_page(box_html! {
+ @ for &(ref type_, ref units) in sections {
+ h1 {
+ : type_
+ }
+ : self.unit_table(&units)
+ }
+ })
+ }
-pub fn unit_status(unit: &unit::Unit, log: &str) -> String {
- render_in_page(box_html! {
- h1 { :&unit.name }
- p { : format_args!("{} ({})", &unit.active_state, &unit.sub_state) }
- pre {
- : log
- }
- })
+ pub fn unit_status(&self, unit: &unit::Unit, log: &str) -> String {
+ self.render_in_page(box_html! {
+ h1 { :&unit.name }
+ p { : format_args!("{} ({})", &unit.active_state, &unit.sub_state) }
+ pre {
+ : log
+ }
+ })
+ }
}
diff --git a/src/server.rs b/src/server.rs
index f34489f..2ab4f75 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -1,19 +1,31 @@
-extern crate iron_sessionstorage;
-use iron_sessionstorage::traits::*;
+use iron;
+use iron::error::{HttpResult};
+use iron::headers::ContentType;
+use iron::modifiers::{Header, Redirect};
+use iron::status;
+use iron::{Iron, IronError, Request, IronResult, Response, Chain, Listening, Plugin, Url};
use iron_sessionstorage::SessionStorage;
use iron_sessionstorage::backends::SignedCookieBackend;
-use iron::status;
-use iron::modifiers::Header;
-use iron::headers::ContentType;
-use iron::{Iron, IronError, Request, IronResult, Response, Chain, Listening};
-use iron::error::{HttpResult};
-use router::Router;
-use systemd::unit;
-use systemd::journal;
+use iron_sessionstorage::traits::*;
+use iron_sessionstorage;
+use params::{Params, Value};
+use regex::Regex;
use render;
+use router::Router;
use staticfile::Static;
-use regex::Regex;
+use systemd::journal;
+use systemd::unit;
+use persistent::Read;
+#[derive(Clone, Default, Debug)]
+struct Context {
+ base_url: String
+}
+impl iron::typemap::Key for Context {
+ type Value = Context;
+}
+
+#[derive(Default, Debug, Clone)]
struct Login {
user: String,
}
@@ -52,9 +64,10 @@ fn overview(r: &mut Request) -> IronResult<Response> {
// info!("Updating session value. Current value: {}", session_value.0);
// session_value.0.push('a');
// try!(r.session().set(session_value));
+ let renderer = make_renderer(r)?;
Ok(Response::with((status::Ok,
Header(ContentType::html()),
- render::system_status(&units_by_section))))
+ renderer.system_status(&units_by_section))))
}
fn journal(r: &mut Request) -> IronResult<Response> {
@@ -77,47 +90,94 @@ fn unit_status(r: &mut Request) -> IronResult<Response> {
let unit_name = iexpect!(r.extensions
.get::<Router>()
.unwrap()
- .find("unit"), status::BadRequest);
+ .find("unit"), status::BadRequest).to_string();
let re = Regex::new(r"[-_\w\d]*").unwrap();
- if !re.is_match(unit_name) {
+ if !re.is_match(&unit_name) {
return Ok(Response::with(
(status::BadRequest,
format!("Unit ({}) does not match {}",
unit_name, re))));
}
- let ref unit = itry!(unit::get_units(unit_name))[0];
- let log = itry!(journal::get_log(unit_name, 15));
+ let ref unit = itry!(unit::get_units(&unit_name))[0];
+ let log = itry!(journal::get_log(&unit_name, 15));
+ let renderer = make_renderer(r)?;
Ok(Response::with((status::Ok,
Header(ContentType::html()),
- render::unit_status(&unit, &log))))
+ renderer.unit_status(&unit, &log))))
+}
+
+fn get_logged_in_user(r: &mut Request) -> IronResult<Option<Login>> {
+ let login = r.session().get::<Login>()?;
+ // Session storage doesn't have a way to delete its cookie,
+ // so we set the username to empty on logout.
+ if let &Some(Login { ref user }) = &login {
+ if user.is_empty() {
+ return Ok(None)
+ }
+ }
+ Ok(login)
}
fn login(r: &mut Request) -> IronResult<Response> {
+ let renderer = make_renderer(r)?;
Ok(Response::with((status::Ok,
Header(ContentType::html()),
- render::login_page())))
+ renderer.login_page())))
}
fn login_submit(r: &mut Request) -> IronResult<Response> {
- Ok(Response::with((status::Ok,
- Header(ContentType::plaintext()),
- "login")))
- // TODO: Need this to get params:
- // https://github.com/iron/params
+ let login = {
+ let map = r.get_ref::<Params>().unwrap();
+ let user = match map.get("username") {
+ Some(&Value::String(ref v)) => v,
+ _ => panic!("no username in params: {:?}", map)
+ };
+ let password = match map.get("password") {
+ Some(&Value::String(ref v)) => v,
+ _ => panic!("no password in params: {:?}", map)
+ };
+ Login { user: user.clone() }
+ };
+
+ info!("User logged in: {:?}", login);
+ r.session().set(login)?;
+ let url = Url::parse("http://localhost:8080/").unwrap();
+ Ok(Response::with((status::Found,
+ Redirect(url))))
+}
+
+fn logout(r: &mut Request) -> IronResult<Response> {
+ r.session().set::<Login>(Default::default());
+ let url = Url::parse("http://localhost:8080/").unwrap();
+ Ok(Response::with((status::Found,
+ Redirect(url))))
+}
+
+fn make_renderer(r: &mut Request) -> IronResult<render::Renderer> {
+ let user = get_logged_in_user(r)?.map(|u| u.user);
+ Ok(render::Renderer {
+ user: user
+ })
}
pub fn serve(port: u16) -> HttpResult<Listening> {
+ // TODO: Use a real secret.
let secret = b"secret2".to_vec();
let router = router!(
root: get "/" => overview,
login: get "/login" => login,
login_submit: post "/login" => login_submit,
+ logout: get "/logout" => logout,
details: get "/status/:unit" => unit_status,
journal: get "/journal/:unit" => journal,
css: get "/static/main.css" => Static::new(""),
);
let mut chain = Chain::new(router);
+ let context = Context {
+ base_url: String::from("http://localhost:8080/"),
+ };
chain.link_around(SessionStorage::new(SignedCookieBackend::new(secret)));
+ chain.link(Read::<Context>::both(context));
let bind_address = format!("{}:{}", "::", port);
Iron::new(chain).http(bind_address.as_str())
}