feat: Add a configurable headline to the form

This commit is contained in:
Philipp Matthias Schaefer 2021-02-27 20:11:47 +01:00
parent 0916b3fa7c
commit 10d5a1c515
6 changed files with 62 additions and 12 deletions

View File

@ -22,7 +22,7 @@ use rocket_contrib::json::Json;
use rocket::post; use rocket::post;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use crate::config::Config; use crate::context::Context;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct PasswordData { pub struct PasswordData {
@ -43,10 +43,11 @@ pub struct Response {
message: Option<Message>, message: Option<Message>,
} }
fn change_password(data: Json<PasswordData>, config: rocket::State<Config>) -> Result<()> { fn change_password(data: Json<PasswordData>,
context: rocket::State<Context>) -> Result<()> {
let dn = format!("uid={},ou=People,dc=fiveop,dc=de", &data.username); let dn = format!("uid={},ou=People,dc=fiveop,dc=de", &data.username);
let mut ldap = LdapConn::new(&config.ldap_url)?; let mut ldap = LdapConn::new(&context.ldap_url)?;
ldap ldap
.simple_bind(&dn, &data.old_password)? .simple_bind(&dn, &data.old_password)?
.success()?; .success()?;
@ -63,9 +64,10 @@ fn change_password(data: Json<PasswordData>, config: rocket::State<Config>) -> R
} }
#[post("/update", data = "<data>")] #[post("/update", data = "<data>")]
pub fn update(data: Json<PasswordData>, config: rocket::State<Config>) -> Json<Response> { pub fn update(data: Json<PasswordData>,
context: rocket::State<Context>) -> Json<Response> {
Json( Json(
match change_password(data, config) { match change_password(data, context) {
Ok(_) => Response{ Ok(_) => Response{
success: true, success: true,
message: None, message: None,

View File

@ -22,6 +22,10 @@ use handlebars::Handlebars;
use serde_derive::Deserialize; use serde_derive::Deserialize;
use serde_json::json; use serde_json::json;
fn default_headline() -> String {
"Change your password".to_string()
}
fn default_ldap_url() -> String { fn default_ldap_url() -> String {
"ldap://localhost".to_string() "ldap://localhost".to_string()
} }
@ -38,6 +42,9 @@ fn default_port() -> u16 {
pub struct Config { pub struct Config {
pub dn: String, pub dn: String,
#[serde(default = "default_headline")]
pub headline: String,
#[serde(default = "default_ldap_url")] #[serde(default = "default_ldap_url")]
pub ldap_url: String, pub ldap_url: String,
@ -50,15 +57,21 @@ pub struct Config {
pub fn load_config(file_path: &str) -> Result<Config> { pub fn load_config(file_path: &str) -> Result<Config> {
let file = File::open(file_path). let file = File::open(file_path).
with_context(|| format!("Failed to open configuration file '{}'", file_path))?; with_context(|| format!("Failed to open configuration file '{}'",
file_path))?;
let config: Config = serde_json::from_reader(BufReader::new(file)) let config: Config = serde_json::from_reader(BufReader::new(file))
.with_context(|| format!("Failed to parse configuration file '{}'", file_path))?; .with_context(|| format!("Failed to parse configuration file '{}'",
file_path))?;
Handlebars::new() Handlebars::new()
.render_template(&config.dn, &json!({"username" : "foo"})) .render_template(&config.dn, &json!({"username" : "foo"}))
.map(|_| ()) .map(|_| ())
.with_context(|| format!("Failed to parse DN ({}) in configuration file '{}'", config.dn, file_path))?; .with_context(|| format!(
"Failed to render DN ({}) from configuration file '{}'",
config.dn,
file_path
))?;
Ok(config) Ok(config)
} }

25
src/context.rs Normal file
View File

@ -0,0 +1,25 @@
use anyhow::{Context as AnyhowContext, Result};
use handlebars::Handlebars;
use serde_json::json;
use crate::config::Config;
pub struct Context {
pub ldap_url: String,
pub index_html: String,
}
impl Context {
pub fn new(config: &Config) -> Result<Context> {
let index_html = Handlebars::new()
.render_template(include_str!("static/index.html.hbs"),
&json!({"headline" : config.headline}))
.with_context(|| format!(
"Failed to render index.html from template and configured headline '{}'",
config.headline
))?;
Ok(Context{ldap_url: config.ldap_url.clone(),
index_html})
}
}

View File

@ -18,6 +18,7 @@
mod api; mod api;
mod config; mod config;
mod context;
mod r#static; mod r#static;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
@ -46,11 +47,17 @@ fn main() -> Result<()> {
let config = load_config(config_file_path)?; let config = load_config(config_file_path)?;
let context = context::Context::new(&config)?;
let rocket_config = Config::build(Environment::Production) let rocket_config = Config::build(Environment::Production)
.address(&config.host) .address(&config.host)
.port(config.port) .port(config.port)
.finalize() .finalize()
.with_context(|| format!("Bad host address ({}) in configuration file '{}'", config.host, config_file_path))?; .with_context(|| format!(
"Bad host address ({}) in configuration file '{}'",
config.host,
config_file_path
))?;
rocket::custom(rocket_config) rocket::custom(rocket_config)
.mount("/", routes![r#static::index, .mount("/", routes![r#static::index,
@ -62,7 +69,7 @@ fn main() -> Result<()> {
r#static::logo, r#static::logo,
r#static::hourglass, r#static::hourglass,
api::update]) api::update])
.manage(config) .manage(context)
.launch(); .launch();
Ok(()) Ok(())

View File

@ -20,6 +20,8 @@ use rocket::response::{content, Responder, Response};
use rocket::get; use rocket::get;
use crate::context::Context;
pub struct Svg<R>(pub R); pub struct Svg<R>(pub R);
impl<'r, R: Responder<'r>> Responder<'r> for Svg<R> { impl<'r, R: Responder<'r>> Responder<'r> for Svg<R> {
@ -29,8 +31,8 @@ impl<'r, R: Responder<'r>> Responder<'r> for Svg<R> {
} }
#[get("/")] #[get("/")]
pub fn index() -> content::Html<&'static str> { pub fn index(context: rocket::State<Context>) -> content::Html<String> {
content::Html(include_str!("static/index.html")) content::Html(context.index_html.clone())
} }
#[get("/checkmark.svg")] #[get("/checkmark.svg")]

View File

@ -26,6 +26,7 @@ with the WebLDAPPasswd. If not, see <https://www.gnu.org/licenses/>.
<script src="webldappasswd.js"></script> <script src="webldappasswd.js"></script>
</head> </head>
<body> <body>
<h1>{{headline}}</h1>
<form> <form>
<label>Username</label> <label>Username</label>
<input type="text" name="username" autocomplete="username"> <input type="text" name="username" autocomplete="username">