vw_small

Hardened fork of Vaultwarden (https://github.com/dani-garcia/vaultwarden) with fewer features.
git clone https://git.philomathiclife.com/repos/vw_small
Log | Files | Refs | README

commit bfd93e5b1389b7b885bf0654b29c57d7a23f7a66
parent a79745956025de9d3c859f6eec9eb329f2717c24
Author: Daniel GarcĂ­a <dani-garcia@users.noreply.github.com>
Date:   Sun, 20 Jan 2019 17:43:56 +0100

Show organizations in admin panel, implement reload templates option

Diffstat:
M.env.template | 2++
Msrc/api/admin.rs | 3++-
Msrc/mail.rs | 2+-
Msrc/main.rs | 25+++++++++++++++++++++++--
Msrc/static/templates/admin/page.hbs | 33++++++++++++++++++++++++++++-----
5 files changed, 56 insertions(+), 9 deletions(-)

diff --git a/.env.template b/.env.template @@ -13,6 +13,8 @@ ## Templates data folder, by default uses embedded templates ## Check source code to see the format # TEMPLATES_FOLDER=/path/to/templates +## Automatically reload the templates for every request, slow, use only for development +# RELOAD_TEMPLATES=false ## Cache time-to-live for successfully obtained icons, in seconds (0 is "forever") # ICON_CACHE_TTL=2592000 diff --git a/src/api/admin.rs b/src/api/admin.rs @@ -49,7 +49,7 @@ impl AdminTemplateData { } fn render(self) -> Result<String, Error> { - CONFIG.templates.render("admin/base", &self).map_err(Into::into) + CONFIG.render_template("admin/base", &self) } } @@ -72,6 +72,7 @@ struct LoginForm { fn post_admin_login(data: Form<LoginForm>, mut cookies: Cookies, ip: ClientIp) -> Result<Redirect, Flash<Redirect>> { let data = data.into_inner(); + // If the token is invalid, redirect to login page if !_validate_token(&data.token) { error!("Invalid admin token. IP: {}", ip.ip); Err(Flash::error( diff --git a/src/mail.rs b/src/mail.rs @@ -36,7 +36,7 @@ fn mailer(config: &MailConfig) -> SmtpTransport { } fn get_text(template_name: &'static str, data: serde_json::Value) -> Result<(String, String), Error> { - let text = CONFIG.templates.render(template_name, &data)?; + let text = CONFIG.render_template(template_name, &data)?; let mut text_split = text.split("<!---------------->"); let subject = match text_split.next() { diff --git a/src/main.rs b/src/main.rs @@ -331,9 +331,11 @@ pub struct Config { mail: Option<MailConfig>, templates: Handlebars, + templates_folder: String, + reload_templates: bool, } -fn load_templates(path: String) -> Handlebars { +fn load_templates(path: &str) -> Handlebars { let mut hb = Handlebars::new(); // Error on missing params hb.set_strict_mode(true); @@ -365,6 +367,21 @@ fn load_templates(path: String) -> Handlebars { } impl Config { + pub fn render_template<T: serde::ser::Serialize>(&self, name: &str, data: &T) -> Result<String, error::Error> { + // We add this to signal the compiler not to drop the result of 'load_templates' + let hb_owned; + + let hb = if CONFIG.reload_templates { + warn!("RELOADING TEMPLATES"); + hb_owned = load_templates(&self.templates_folder); + &hb_owned + } else { + &self.templates + }; + + hb.render(name, data).map_err(Into::into) + } + fn load() -> Self { use crate::util::{get_env, get_env_or}; dotenv::dotenv().ok(); @@ -377,11 +394,15 @@ impl Config { let yubico_client_id = get_env("YUBICO_CLIENT_ID"); let yubico_secret_key = get_env("YUBICO_SECRET_KEY"); + let templates_folder = get_env_or("TEMPLATES_FOLDER", format!("{}/{}", &df, "templates")); + Config { database_url: get_env_or("DATABASE_URL", format!("{}/{}", &df, "db.sqlite3")), icon_cache_folder: get_env_or("ICON_CACHE_FOLDER", format!("{}/{}", &df, "icon_cache")), attachments_folder: get_env_or("ATTACHMENTS_FOLDER", format!("{}/{}", &df, "attachments")), - templates: load_templates(get_env_or("TEMPLATES_FOLDER", format!("{}/{}", &df, "templates"))), + templates: load_templates(&templates_folder), + templates_folder, + reload_templates: get_env_or("RELOAD_TEMPLATES", false), // icon_cache_ttl defaults to 30 days (30 * 24 * 60 * 60 seconds) icon_cache_ttl: get_env_or("ICON_CACHE_TTL", 2_592_000), diff --git a/src/static/templates/admin/page.hbs b/src/static/templates/admin/page.hbs @@ -7,11 +7,23 @@ <div class="media pt-3"> <img class="mr-2 rounded identicon" data-src="{{Email}}"> <div class="media-body pb-3 mb-0 small border-bottom"> - <div class="d-flex justify-content-between"> - <strong>{{Name}}</strong> - <a class="tmp-del mr-3" href="" onclick='deleteUser("{{Id}}", "{{Email}}");'>Delete User</a> + <div class="row justify-content-between"> + <div class="col"> + <strong>{{Name}}</strong> + <span class="d-block">{{Email}}</span> + </div> + <div class="col"> + <strong> Organizations:</strong> + <span class="d-block"> + {{#each Organizations}} + <span class="badge badge-primary" data-orgtype="{{Type}}">{{Name}}</span> + {{/each}} + </span> + </div> + <div style="flex: 0 0 100px;"> + <a class="mr-3" href="" onclick='deleteUser("{{Id}}", "{{Email}}");'>Delete User</a> + </div> </div> - <span class="d-block">{{Email}}</span> </div> </div> {{/each}} @@ -73,11 +85,22 @@ "Error inviting user", data); } + let OrgTypes = { + "0": { "name": "Owner", "color": "orange" }, + "1": { "name": "Admin", "color": "blueviolet" }, + "2": { "name": "User", "color": "blue" }, + "3": { "name": "Manager", "color": "green" }, + }; + $(window).on('load', function () { - //$("#reload-btn").click(reload); $("#invite-form").submit(inviteUser); $("img.identicon").each(function (i, e) { e.src = identicon(e.dataset.src); }); + $('[data-orgtype]').each(function (i, e) { + let orgtype = OrgTypes[e.dataset.orgtype]; + e.style.backgroundColor = orgtype.color; + e.title = orgtype.name; + }); }); </script> \ No newline at end of file