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 b6fde857a74b24cbc71631468b13656f01e370c7
parent 3c66deb5cc7a4387e4176d2a5bdd3f321f09a6bd
Author: BlackDex <black.dex@gmail.com>
Date:   Thu, 28 May 2020 20:25:25 +0200

Added version check to diagnostics

- Added a version check based upon the github api information.

Diffstat:
Msrc/api/admin.rs | 44+++++++++++++++++++++++++++++++++++++++++++-
Msrc/static/templates/admin/diagnostics.hbs | 94++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
2 files changed, 112 insertions(+), 26 deletions(-)

diff --git a/src/api/admin.rs b/src/api/admin.rs @@ -319,6 +319,24 @@ pub struct WebVaultVersion { version: String, } +fn get_github_api(url: &str) -> Result<Value, Error> { + use reqwest::{header::USER_AGENT, blocking::Client}; + let github_api = Client::builder().build()?; + + let res = github_api + .get(url) + .header(USER_AGENT, "Bitwarden_RS") + .send()?; + + let res_status = res.status(); + if res_status != 200 { + error!("Could not retrieve '{}', response code: {}", url, res_status); + } + + let value: Value = res.error_for_status()?.json()?; + Ok(value) +} + #[get("/diagnostics")] fn diagnostics(_token: AdminToken, _conn: DbConn) -> ApiResult<Html<String>> { use std::net::ToSocketAddrs; @@ -331,10 +349,31 @@ fn diagnostics(_token: AdminToken, _conn: DbConn) -> ApiResult<Html<String>> { let github_ips = ("github.com", 0).to_socket_addrs().map(|mut i| i.next()); let dns_resolved = match github_ips { - Ok(Some(a)) => a.ip().to_string() , + Ok(Some(a)) => a.ip().to_string(), _ => "Could not resolve domain name.".to_string(), }; + let bitwarden_rs_releases = get_github_api("https://api.github.com/repos/dani-garcia/bitwarden_rs/releases/latest"); + let latest_release = match &bitwarden_rs_releases { + Ok(j) => j["tag_name"].as_str().unwrap(), + _ => "-", + }; + + let bitwarden_rs_commits = get_github_api("https://api.github.com/repos/dani-garcia/bitwarden_rs/commits/master"); + let mut latest_commit = match &bitwarden_rs_commits { + Ok(j) => j["sha"].as_str().unwrap(), + _ => "-", + }; + if latest_commit.len() >= 8 { + latest_commit = &latest_commit[..8]; + } + + let bw_web_builds_releases = get_github_api("https://api.github.com/repos/dani-garcia/bw_web_builds/releases/latest"); + let latest_web_build = match &bw_web_builds_releases { + Ok(j) => j["tag_name"].as_str().unwrap(), + _ => "-", + }; + let dt = Utc::now(); let server_time = dt.format("%Y-%m-%d %H:%M:%S").to_string(); @@ -342,6 +381,9 @@ fn diagnostics(_token: AdminToken, _conn: DbConn) -> ApiResult<Html<String>> { "dns_resolved": dns_resolved, "server_time": server_time, "web_vault_version": web_vault_version.version, + "latest_release": latest_release, + "latest_commit": latest_commit, + "latest_web_build": latest_web_build.replace("v", ""), }); let text = AdminTemplateData::diagnostics(diagnostics_json).render()?; diff --git a/src/static/templates/admin/diagnostics.hbs b/src/static/templates/admin/diagnostics.hbs @@ -6,14 +6,30 @@ <div class="row"> <div class="col-md"> <dl class="row"> - <dt class="col-sm-5">Server Installed</dt> + <dt class="col-sm-5">Server Installed + <span class="badge badge-success d-none" id="server-success" title="Latest version is installed.">Ok</span> + <span class="badge badge-warning d-none" id="server-warning" title="There seems to be an update available.">Update</span> + <span class="badge badge-danger d-none" id="server-failed" title="Unable to determine latest version.">Unknown</span> + </dt> <dd class="col-sm-7"> <span id="server-installed">{{version}}</span> </dd> - <dt class="col-sm-5">Web Installed</dt> + <dt class="col-sm-5">Server Latest</dt> + <dd class="col-sm-7"> + <span id="server-latest">{{diagnostics.latest_release}}<span id="server-latest-commit" class="d-none">-{{diagnostics.latest_commit}}</span></span> + </dd> + <dt class="col-sm-5">Web Installed + <span class="badge badge-success d-none" id="web-success" title="Latest version is installed.">Ok</span> + <span class="badge badge-warning d-none" id="web-warning" title="There seems to be an update available.">Update</span> + <span class="badge badge-danger d-none" id="web-failed" title="Unable to determine latest version.">Unknown</span> + </dt> <dd class="col-sm-7"> <span id="web-installed">{{diagnostics.web_vault_version}}</span> </dd> + <dt class="col-sm-5">Web Latest</dt> + <dd class="col-sm-7"> + <span id="web-latest">{{diagnostics.latest_web_build}}</span> + </dd> </dl> </div> </div> @@ -45,29 +61,57 @@ </main> <script> - const d = new Date(); - const year = d.getUTCFullYear(); - const month = String((d.getUTCMonth()+1)).padStart(2, '0'); - const day = String(d.getUTCDate()).padStart(2, '0'); - const hour = String(d.getUTCHours()).padStart(2, '0'); - const minute = String(d.getUTCMinutes()).padStart(2, '0'); - const seconds = String(d.getUTCSeconds()).padStart(2, '0'); - const browserUTC = year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + seconds; - document.getElementById("time-browser-string").innerText = browserUTC; + (() => { + const d = new Date(); + const year = d.getUTCFullYear(); + const month = String((d.getUTCMonth()+1)).padStart(2, '0'); + const day = String(d.getUTCDate()).padStart(2, '0'); + const hour = String(d.getUTCHours()).padStart(2, '0'); + const minute = String(d.getUTCMinutes()).padStart(2, '0'); + const seconds = String(d.getUTCSeconds()).padStart(2, '0'); + const browserUTC = year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + seconds; + document.getElementById("time-browser-string").innerText = browserUTC; + + const serverUTC = document.getElementById("time-server-string").innerText; + const timeDrift = (Date.parse(serverUTC) - Date.parse(browserUTC)) / 1000; + if (timeDrift > 30 || timeDrift < -30) { + document.getElementById('time-warning').classList.remove('d-none'); + } else { + document.getElementById('time-success').classList.remove('d-none'); + } + + // Check if the output is a valid IP + const isValidIp = value => (/^(?:(?:^|\.)(?:2(?:5[0-5]|[0-4]\d)|1?\d?\d)){4}$/.test(value) ? true : false); + if (isValidIp(document.getElementById('dns-resolved').innerText)) { + document.getElementById('dns-success').classList.remove('d-none'); + } else { + document.getElementById('dns-warning').classList.remove('d-none'); + } + + let serverInstalled = document.getElementById('server-installed').innerText; + let serverLatest = document.getElementById('server-latest').innerText; + if (serverInstalled.indexOf('-') > -1 && serverLatest !== '-') { + document.getElementById('server-latest-commit').classList.remove('d-none'); + serverLatest += document.getElementById('server-latest-commit').innerText; + } + + const webInstalled = document.getElementById('web-installed').innerText; + const webLatest = document.getElementById('web-latest').innerText; + + checkVersions('server', serverInstalled, serverLatest); + checkVersions('web', webInstalled, webLatest); - const serverUTC = document.getElementById("time-server-string").innerText; - const timeDrift = (Date.parse(serverUTC) - Date.parse(browserUTC)) / 1000; - if (timeDrift > 30 || timeDrift < -30) { - document.getElementById('time-warning').classList.remove('d-none'); - } else { - document.getElementById('time-success').classList.remove('d-none'); - } + function checkVersions(platform, installed, latest) { + if (installed === '-' || latest === '-') { + document.getElementById(platform + '-failed').classList.remove('d-none'); + return; + } - // Check if the output is a valid IP - const isValidIp = value => (/^(?:(?:^|\.)(?:2(?:5[0-5]|[0-4]\d)|1?\d?\d)){4}$/.test(value) ? true : false); - if (isValidIp(document.getElementById('dns-resolved').innerText)) { - document.getElementById('dns-success').classList.remove('d-none'); - } else { - document.getElementById('dns-warning').classList.remove('d-none'); - } + if (installed !== latest) { + document.getElementById(platform + '-warning').classList.remove('d-none'); + } else { + document.getElementById(platform + '-success').classList.remove('d-none'); + } + } + })(); </script> \ No newline at end of file