release 11.2.0

This commit is contained in:
Bevan Hunt 2021-04-06 20:21:05 -07:00
parent 597b05b6f7
commit 559cc792e8
5 changed files with 227 additions and 13 deletions

View File

@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [11.2.0] - 2021-04-06
### Added
- Added zxcvbn password strength checker
### Fixed
- Fixed JWT issued not being in JSON format
## [11.1.0] - 2021-04-06
### Added

148
Cargo.lock generated
View File

@ -396,6 +396,21 @@ dependencies = [
"which",
]
[[package]]
name = "bit-set"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.2.1"
@ -465,7 +480,7 @@ dependencies = [
[[package]]
name = "broker"
version = "11.1.0"
version = "11.2.0"
dependencies = [
"anyhow",
"async-std",
@ -489,6 +504,7 @@ dependencies = [
"tide-acme",
"tide-rustls",
"uuid",
"zxcvbn",
]
[[package]]
@ -555,11 +571,13 @@ version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"js-sys",
"libc",
"num-integer",
"num-traits",
"serde",
"time 0.1.44",
"wasm-bindgen",
"winapi",
]
@ -592,7 +610,7 @@ dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"strsim 0.8.0",
"textwrap",
"unicode-width",
"vec_map",
@ -734,6 +752,41 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
[[package]]
name = "darling"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim 0.9.3",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "dashmap"
version = "4.0.2"
@ -775,6 +828,31 @@ dependencies = [
"rusticata-macros",
]
[[package]]
name = "derive_builder"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0"
dependencies = [
"darling",
"derive_builder_core",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "derive_builder_core"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "digest"
version = "0.9.0"
@ -802,6 +880,12 @@ dependencies = [
"tide",
]
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "env_logger"
version = "0.7.1"
@ -821,6 +905,16 @@ version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
[[package]]
name = "fancy-regex"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36996e5f56f32ca51a937f325094fa450b32df871af1a89be331b7145b931bfc"
dependencies = [
"bit-set",
"regex",
]
[[package]]
name = "fast_chemail"
version = "0.9.6"
@ -855,6 +949,12 @@ dependencies = [
"web-sys",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.0.1"
@ -1134,9 +1234,15 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
"quick-error",
"quick-error 1.2.3",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "0.2.2"
@ -1163,6 +1269,15 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.7"
@ -1543,6 +1658,12 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-error"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda"
[[package]]
name = "quote"
version = "1.0.9"
@ -2056,6 +2177,12 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "strsim"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]]
name = "subtle"
version = "2.4.0"
@ -2535,3 +2662,18 @@ checksum = "0de7bff972b4f2a06c85f6d8454b09df153af7e3a4ec2aac81db1b105b684ddb"
dependencies = [
"chrono",
]
[[package]]
name = "zxcvbn"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5f9db3a05b2ee81dcda4602487314ab654eca316f517be2e2e64175658f0dd0"
dependencies = [
"chrono",
"derive_builder",
"fancy-regex",
"itertools",
"lazy_static",
"quick-error 2.0.0",
"regex",
]

View File

@ -1,6 +1,6 @@
[package]
name = "broker"
version = "11.1.0"
version = "11.2.0"
authors = ["Bevan Hunt <bevan@bevanhunt.com>"]
edition = "2018"
license = "MIT"
@ -33,3 +33,4 @@ futures = "0.3"
tide-acme = "0.1.0"
base64 = "0.13"
mailchecker = "4"
zxcvbn = "2"

View File

@ -30,6 +30,7 @@ Broker follows an insert-only/publish/subscribe paradigm rather than a REST CRUD
* Verify endpoint for external services using Broker user system like [portal](https://crates.io/crates/portal)
* User Management API endpoints (revoke, unrevoke, list, get, update)
* User Email Address Validation (regex and blacklist check against throwaway emails)
* Password Strength Checker using [zxcvbn](https://crates.io/crates/zxcvbn)
### How it works
@ -283,7 +284,8 @@ will return: `200`
- the `db` flag is the path where the embedded database will be saved - default `db`
- the `domain` flag is the domain name (e.g. api.broker.com) of the domain you want to register with LetsEncrypt - must be fully resolvable
- the `admin_token` flag is the password for the admin to add users - default `letmein`
- production example: `./broker --secure="true" --admin_token"23ce4234@123$" --jwt_secret="xTJEX234$##$" --domain="api.broker.com"`
- the `password_checker` flag enables zxcvbn password checking - default `false`
- production example: `./broker --secure="true" --admin_token"23ce4234@123$" --jwt_secret="xTJEX234$##$" --domain="api.broker.com" --password_checker="true"`
### Service

View File

@ -15,6 +15,7 @@ use std::time::Duration;
use futures::StreamExt;
use tide_acme::{AcmeConfig, TideRustlsExt};
use mailchecker::is_valid;
use zxcvbn::zxcvbn;
lazy_static! {
static ref DB : Arc<rocksdb::DB> = {
@ -45,6 +46,7 @@ pub struct EnvVarConfig {
pub auto_cert: bool,
pub key_path: String,
pub cert_path: String,
pub password_checker: bool,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
@ -146,7 +148,7 @@ fn activate_user(username: String) -> Result<()> {
}
}
fn modify_user(update_user_form: UpdateUserForm) -> Result<()> {
fn modify_user(update_user_form: UpdateUserForm) -> Result<Option<String>> {
match get_user_by_username(update_user_form.clone().username)? {
Some(mut user) => {
@ -170,6 +172,33 @@ fn modify_user(update_user_form: UpdateUserForm) -> Result<()> {
match update_user_form.password {
Some(password) => {
let configure = env_var_config();
if configure.password_checker {
let estimate = zxcvbn(&password, &[&user.username]).unwrap();
if estimate.score() < 3 {
let err: String;
match estimate.feedback() {
Some(feedback) => {
match feedback.warning() {
Some(warning) => {
err = format!("password is too weak because {}", warning);
},
None => {
err = format!("password is too weak");
}
}
},
None => {
err = format!("password is too weak");
}
}
let j = json!({"error": err}).to_string();
return Ok(Some(j));
}
}
let config = Argon2Config::default();
let uuid_string = Uuid::new_v4().to_string();
let salt = uuid_string.as_bytes();
@ -180,9 +209,9 @@ fn modify_user(update_user_form: UpdateUserForm) -> Result<()> {
None => {}
}
puts_user(user)?;
Ok(())
Ok(None)
},
None => { Ok(()) }
None => { Ok(None) }
}
}
@ -265,6 +294,30 @@ fn user_create(user_form: UserForm) -> Result<Option<String>> {
let j = json!({"error": "username already taken"}).to_string();
return Ok(Some(j));
} else {
if configure.password_checker {
let estimate = zxcvbn(&user_form.password, &[&user_form.username]).unwrap();
if estimate.score() < 3 {
let err: String;
match estimate.feedback() {
Some(feedback) => {
match feedback.warning() {
Some(warning) => {
err = format!("password is too weak because {}", warning);
},
None => {
err = format!("password is too weak");
}
}
},
None => {
err = format!("password is too weak");
}
}
let j = json!({"error": err}).to_string();
return Ok(Some(j));
}
}
let uuid = Uuid::new_v4();
let config = Argon2Config::default();
let uuid_string = Uuid::new_v4().to_string();
@ -329,6 +382,7 @@ fn env_var_config() -> EnvVarConfig {
let mut admin_token = "letmein".to_string();
let mut key_path = "certs/private_key.pem".to_string();
let mut cert_path = "certs/chain.pem".to_string();
let mut password_checker = false;
let _ : Vec<String> = go_flag::parse(|flags| {
flags.add_flag("port", &mut port);
flags.add_flag("origin", &mut origin);
@ -342,9 +396,10 @@ fn env_var_config() -> EnvVarConfig {
flags.add_flag("auto_cert", &mut auto_cert);
flags.add_flag("key_path", &mut key_path);
flags.add_flag("cert_path", &mut cert_path);
flags.add_flag("password_checker", &mut password_checker);
});
EnvVarConfig{port, origin, jwt_expiry, jwt_secret, secure, domain, certs, db, admin_token, auto_cert, key_path, cert_path}
EnvVarConfig{port, origin, jwt_expiry, jwt_secret, secure, domain, certs, db, admin_token, auto_cert, key_path, cert_path, password_checker}
}
async fn jwt_verify(token: String) -> Result<Option<TokenData<Claims>>> {
@ -420,7 +475,6 @@ async fn create_user(mut req: Request<()>) -> tide::Result {
let user_form : UserForm = serde_json::from_str(&r)?;
match user_create(user_form)? {
Some(err) => {
let err = format!("error: {}", err);
Ok(tide::Response::builder(400).body(err).header("content-type", "application/json").build())
},
None => {
@ -434,7 +488,7 @@ async fn login_user(mut req: Request<()>) -> tide::Result {
let login_form : LoginForm = serde_json::from_str(&r)?;
match create_jwt(login_form).await? {
Some(jwt) => {
let msg = format!("jwt: {}", jwt);
let msg = json!({"jwt": jwt}).to_string();
Ok(tide::Response::builder(200).body(msg).header("content-type", "application/json").build())
},
None => {
@ -555,8 +609,15 @@ async fn update_user(mut req: Request<()>) -> tide::Result {
let update_user_form : UpdateUserForm = serde_json::from_str(&r)?;
let configure = env_var_config();
if configure.admin_token == update_user_form.admin_token {
modify_user(update_user_form)?;
Ok(tide::Response::builder(200).header("content-type", "application/json").build())
match modify_user(update_user_form)? {
Some(err) => {
Ok(tide::Response::builder(400).body(err).header("content-type", "application/json").build())
},
None => {
Ok(tide::Response::builder(200).header("content-type", "application/json").build())
}
}
} else {
Ok(tide::Response::builder(401).header("content-type", "application/json").build())
}