Skip to content

Commit

Permalink
[PM-3437] add validation for master password policy (#452)
Browse files Browse the repository at this point in the history
## Type of change

<!-- (mark with an `X`) -->

```
- [ ] Bug fix
- [x] New feature development
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
- [ ] Build/deploy pipeline (DevOps)
- [ ] Other
```

## Objective

<!--Describe what the purpose of this PR is. For example: what bug
you're fixing or what new feature you're adding-->
Implements the validation for master password policy
## Code changes

<!--Explain the changes you've made to each file or major component.
This should help the reviewer understand your changes-->
<!--Also refer to any related changes or PRs in other repositories-->

- **file.ext:** Description of what was changed and why

## Screenshots

<!--Required for any UI changes. Delete if not applicable-->

## Before you submit

- Please add **unit tests** where it makes sense to do so (encouraged
but not required)
  • Loading branch information
jlf0dev authored Dec 20, 2023
1 parent 0360a9a commit ac3e93e
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 5 deletions.
2 changes: 1 addition & 1 deletion crates/bitwarden-uniffi/src/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl ClientAuth {
.await
}

/// **API Draft:** Evaluate if the provided password satisfies the provided policy
/// Evaluate if the provided password satisfies the provided policy
pub async fn satisfies_policy(
&self,
password: String,
Expand Down
154 changes: 150 additions & 4 deletions crates/bitwarden/src/auth/password.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,36 @@ pub(super) fn password_strength(
2
}

/// Validate the provided password passes the provided Master Password Requirements Policy.
pub(super) fn satisfies_policy(
_password: String,
_strength: u8,
_policy: &MasterPasswordPolicyOptions,
password: String,
strength: u8,
policy: &MasterPasswordPolicyOptions,
) -> bool {
if policy.min_complexity > 0 && policy.min_complexity > strength {
return false;
}

if policy.min_length > 0 && usize::from(policy.min_length) > password.len() {
return false;
}

if policy.require_upper && password.to_lowercase() == password {
return false;
}

if policy.require_lower && password.to_uppercase() == password {
return false;
}

if policy.require_numbers && !password.chars().any(|c| c.is_numeric()) {
return false;
}

if policy.require_special && !password.chars().any(|c| "!@#$%^&*".contains(c)) {
return false;
}

true
}

Expand Down Expand Up @@ -69,8 +94,129 @@ pub struct MasterPasswordPolicyOptions {
}

#[cfg(test)]

mod tests {
mod satisfies_policy {
use crate::auth::password::{satisfies_policy, MasterPasswordPolicyOptions};

#[test]
fn satisfies_policy_gives_success() {
let password = "lkasfo!icbb$2323ALKJCO22".to_string();
let options = MasterPasswordPolicyOptions {
min_complexity: 3,
min_length: 5,
require_upper: true,
require_lower: true,
require_numbers: true,
require_special: true,
enforce_on_login: false,
};

let result = satisfies_policy(password, 4, &options);
assert!(result);
}

#[test]
fn satisfies_policy_evaluates_strength() {
let password = "password123".to_string();
let options = MasterPasswordPolicyOptions {
min_complexity: 3,
min_length: 0,
require_upper: false,
require_lower: false,
require_numbers: false,
require_special: false,
enforce_on_login: false,
};

let result = satisfies_policy(password, 0, &options);
assert!(!result);
}

#[test]
fn satisfies_policy_evaluates_length() {
let password = "password123".to_string();
let options = MasterPasswordPolicyOptions {
min_complexity: 0,
min_length: 20,
require_upper: false,
require_lower: false,
require_numbers: false,
require_special: false,
enforce_on_login: false,
};

let result = satisfies_policy(password, 0, &options);
assert!(!result);
}

#[test]
fn satisfies_policy_evaluates_upper() {
let password = "password123".to_string();
let options = MasterPasswordPolicyOptions {
min_complexity: 0,
min_length: 0,
require_upper: true,
require_lower: false,
require_numbers: false,
require_special: false,
enforce_on_login: false,
};

let result = satisfies_policy(password, 0, &options);
assert!(!result);
}

#[test]
fn satisfies_policy_evaluates_lower() {
let password = "ABCDEFG123".to_string();
let options = MasterPasswordPolicyOptions {
min_complexity: 0,
min_length: 0,
require_upper: false,
require_lower: true,
require_numbers: false,
require_special: false,
enforce_on_login: false,
};

let result = satisfies_policy(password, 0, &options);
assert!(!result);
}

#[test]
fn satisfies_policy_evaluates_numbers() {
let password = "password".to_string();
let options = MasterPasswordPolicyOptions {
min_complexity: 0,
min_length: 0,
require_upper: false,
require_lower: false,
require_numbers: true,
require_special: false,
enforce_on_login: false,
};

let result = satisfies_policy(password, 0, &options);
assert!(!result);
}

#[test]
fn satisfies_policy_evaluates_special() {
let password = "Password123".to_string();
let options = MasterPasswordPolicyOptions {
min_complexity: 0,
min_length: 0,
require_upper: false,
require_lower: false,
require_numbers: false,
require_special: true,
enforce_on_login: false,
};

let result = satisfies_policy(password, 0, &options);
assert!(!result);
}
}

#[cfg(feature = "mobile")]
#[tokio::test]
Expand Down

0 comments on commit ac3e93e

Please sign in to comment.