Skip to content

Commit 7124d58

Browse files
committed
feat!: [torrust#591] extract new type for password constrains in configuration
From: ```toml [auth] email_on_signup = "Optional" max_password_length = 64 min_password_length = 6 secret_key = "MaxVerstappenWC2021" ``` To: ```toml [auth] email_on_signup = "Optional" secret_key = "MaxVerstappenWC2021" [auth.password_constraints] max_password_length = 64 min_password_length = 6 ```
1 parent 50ebb9a commit 7124d58

File tree

8 files changed

+96
-44
lines changed

8 files changed

+96
-44
lines changed

src/config/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub type Tracker = v1::tracker::Tracker;
3030
pub type Logging = v1::logging::Logging;
3131
pub type Website = v1::website::Website;
3232
pub type EmailOnSignup = v1::auth::EmailOnSignup;
33+
pub type PasswordConstraints = v1::auth::PasswordConstraints;
3334

3435
/// Prefix for env vars that overwrite configuration options.
3536
const CONFIG_OVERRIDE_PREFIX: &str = "TORRUST_INDEX_CONFIG_OVERRIDE_";
@@ -344,10 +345,12 @@ mod tests {
344345
345346
[auth]
346347
email_on_signup = "optional"
347-
min_password_length = 6
348-
max_password_length = 64
349348
secret_key = "MaxVerstappenWC2021"
350349
350+
[auth.password_constraints]
351+
min_password_length = 6
352+
max_password_length = 64
353+
351354
[database]
352355
connect_url = "sqlite://data.db?mode=rwc"
353356

src/config/v1/auth.rs

+40-15
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,21 @@ use serde::{Deserialize, Serialize};
77
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
88
pub struct Auth {
99
/// Whether or not to require an email on signup.
10-
#[serde(default = "EmailOnSignup::default")]
10+
#[serde(default = "Auth::default_email_on_signup")]
1111
pub email_on_signup: EmailOnSignup,
12-
/// The minimum password length.
13-
#[serde(default = "Auth::default_min_password_length")]
14-
pub min_password_length: usize,
15-
/// The maximum password length.
16-
#[serde(default = "Auth::default_max_password_length")]
17-
pub max_password_length: usize,
1812
/// The secret key used to sign JWT tokens.
1913
#[serde(default = "Auth::default_secret_key")]
2014
pub secret_key: SecretKey,
15+
/// The password constraints
16+
#[serde(default = "Auth::default_password_constraints")]
17+
pub password_constraints: PasswordConstraints,
2118
}
2219

2320
impl Default for Auth {
2421
fn default() -> Self {
2522
Self {
2623
email_on_signup: EmailOnSignup::default(),
27-
min_password_length: Self::default_min_password_length(),
28-
max_password_length: Self::default_max_password_length(),
24+
password_constraints: Self::default_password_constraints(),
2925
secret_key: Self::default_secret_key(),
3026
}
3127
}
@@ -36,17 +32,17 @@ impl Auth {
3632
self.secret_key = SecretKey::new(secret_key);
3733
}
3834

39-
fn default_min_password_length() -> usize {
40-
6
41-
}
42-
43-
fn default_max_password_length() -> usize {
44-
64
35+
fn default_email_on_signup() -> EmailOnSignup {
36+
EmailOnSignup::default()
4537
}
4638

4739
fn default_secret_key() -> SecretKey {
4840
SecretKey::new("MaxVerstappenWC2021")
4941
}
42+
43+
fn default_password_constraints() -> PasswordConstraints {
44+
PasswordConstraints::default()
45+
}
5046
}
5147

5248
/// Whether the email is required on signup or not.
@@ -119,6 +115,35 @@ impl fmt::Display for SecretKey {
119115
}
120116
}
121117

118+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
119+
pub struct PasswordConstraints {
120+
/// The minimum password length.
121+
#[serde(default = "PasswordConstraints::default_min_password_length")]
122+
pub min_password_length: usize,
123+
/// The maximum password length.
124+
#[serde(default = "PasswordConstraints::default_max_password_length")]
125+
pub max_password_length: usize,
126+
}
127+
128+
impl Default for PasswordConstraints {
129+
fn default() -> Self {
130+
Self {
131+
min_password_length: Self::default_min_password_length(),
132+
max_password_length: Self::default_max_password_length(),
133+
}
134+
}
135+
}
136+
137+
impl PasswordConstraints {
138+
fn default_min_password_length() -> usize {
139+
6
140+
}
141+
142+
fn default_max_password_length() -> usize {
143+
64
144+
}
145+
}
146+
122147
#[cfg(test)]
123148
mod tests {
124149
use super::SecretKey;

src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,11 @@
181181
//!
182182
//! [auth]
183183
//! email_on_signup = "optional"
184+
//! secret_key = "MaxVerstappenWC2021"
185+
//!
186+
//! [auth.password_constraints]
184187
//! min_password_length = 6
185188
//! max_password_length = 64
186-
//! secret_key = "MaxVerstappenWC2021"
187189
//!
188190
//! [database]
189191
//! connect_url = "sqlite://data.db?mode=rwc"

src/services/user.rs

+8-13
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use pbkdf2::password_hash::rand_core::OsRng;
1111
use tracing::{debug, info};
1212

1313
use super::authentication::DbUserAuthenticationRepository;
14-
use crate::config::{Configuration, EmailOnSignup};
14+
use crate::config::{Configuration, EmailOnSignup, PasswordConstraints};
1515
use crate::databases::database::{Database, Error};
1616
use crate::errors::ServiceError;
1717
use crate::mailer;
@@ -97,11 +97,11 @@ impl RegistrationService {
9797
}
9898

9999
let password_constraints = PasswordConstraints {
100-
min_password_length: settings.auth.min_password_length,
101-
max_password_length: settings.auth.max_password_length,
100+
min_password_length: settings.auth.password_constraints.min_password_length,
101+
max_password_length: settings.auth.password_constraints.max_password_length,
102102
};
103103

104-
validate_password_constrains(
104+
validate_password_constraints(
105105
&registration_form.password,
106106
&registration_form.confirm_password,
107107
&password_constraints,
@@ -216,11 +216,11 @@ impl ProfileService {
216216
verify_password(change_password_form.current_password.as_bytes(), &user_authentication)?;
217217

218218
let password_constraints = PasswordConstraints {
219-
min_password_length: settings.auth.min_password_length,
220-
max_password_length: settings.auth.max_password_length,
219+
min_password_length: settings.auth.password_constraints.min_password_length,
220+
max_password_length: settings.auth.password_constraints.max_password_length,
221221
};
222222

223-
validate_password_constrains(
223+
validate_password_constraints(
224224
&change_password_form.password,
225225
&change_password_form.confirm_password,
226226
&password_constraints,
@@ -415,12 +415,7 @@ impl DbBannedUserList {
415415
}
416416
}
417417

418-
struct PasswordConstraints {
419-
pub min_password_length: usize,
420-
pub max_password_length: usize,
421-
}
422-
423-
fn validate_password_constrains(
418+
fn validate_password_constraints(
424419
password: &str,
425420
confirm_password: &str,
426421
password_rules: &PasswordConstraints,

src/web/api/client/v1/contexts/settings/mod.rs

+18-5
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use url::Url;
66
use crate::config::v1::tracker::ApiToken;
77
use crate::config::{
88
Api as DomainApi, Auth as DomainAuth, Database as DomainDatabase, ImageCache as DomainImageCache, Mail as DomainMail,
9-
Network as DomainNetwork, Settings as DomainSettings, Tracker as DomainTracker,
10-
TrackerStatisticsImporter as DomainTrackerStatisticsImporter, Website as DomainWebsite,
9+
Network as DomainNetwork, PasswordConstraints as DomainPasswordConstraints, Settings as DomainSettings,
10+
Tracker as DomainTracker, TrackerStatisticsImporter as DomainTrackerStatisticsImporter, Website as DomainWebsite,
1111
};
1212

1313
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
@@ -46,9 +46,14 @@ pub struct Network {
4646
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
4747
pub struct Auth {
4848
pub email_on_signup: String,
49+
pub secret_key: String,
50+
pub password_constraints: PasswordConstraints,
51+
}
52+
53+
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
54+
pub struct PasswordConstraints {
4955
pub min_password_length: usize,
5056
pub max_password_length: usize,
51-
pub secret_key: String,
5257
}
5358

5459
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
@@ -135,9 +140,17 @@ impl From<DomainAuth> for Auth {
135140
fn from(auth: DomainAuth) -> Self {
136141
Self {
137142
email_on_signup: format!("{:?}", auth.email_on_signup),
138-
min_password_length: auth.min_password_length,
139-
max_password_length: auth.max_password_length,
140143
secret_key: auth.secret_key.to_string(),
144+
password_constraints: auth.password_constraints.into(),
145+
}
146+
}
147+
}
148+
149+
impl From<DomainPasswordConstraints> for PasswordConstraints {
150+
fn from(password_constraints: DomainPasswordConstraints) -> Self {
151+
Self {
152+
min_password_length: password_constraints.min_password_length,
153+
max_password_length: password_constraints.max_password_length,
141154
}
142155
}
143156
}

src/web/api/server/v1/contexts/user/mod.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,8 @@
4545
//!
4646
//! ```toml
4747
//! [auth]
48-
//! email_on_signup = "optional"
49-
//! min_password_length = 6
50-
//! max_password_length = 64
48+
//! email_on_signup = "Optional"
49+
//! secret_key = "MaxVerstappenWC2021"
5150
//! ```
5251
//!
5352
//! Refer to the [`RegistrationForm`](crate::web::api::server::v1::contexts::user::forms::RegistrationForm)

tests/common/contexts/settings/mod.rs

+18-5
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
44
use torrust_index::config::v1::tracker::ApiToken;
55
use torrust_index::config::{
66
Api as DomainApi, Auth as DomainAuth, Database as DomainDatabase, ImageCache as DomainImageCache, Logging as DomainLogging,
7-
Mail as DomainMail, Network as DomainNetwork, Settings as DomainSettings, Tracker as DomainTracker,
8-
TrackerStatisticsImporter as DomainTrackerStatisticsImporter, Website as DomainWebsite,
7+
Mail as DomainMail, Network as DomainNetwork, PasswordConstraints as DomainPasswordConstraints, Settings as DomainSettings,
8+
Tracker as DomainTracker, TrackerStatisticsImporter as DomainTrackerStatisticsImporter, Website as DomainWebsite,
99
};
1010
use url::Url;
1111

@@ -51,9 +51,14 @@ pub struct Network {
5151
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
5252
pub struct Auth {
5353
pub email_on_signup: String,
54+
pub secret_key: String,
55+
pub password_constraints: PasswordConstraints,
56+
}
57+
58+
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
59+
pub struct PasswordConstraints {
5460
pub min_password_length: usize,
5561
pub max_password_length: usize,
56-
pub secret_key: String,
5762
}
5863

5964
#[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
@@ -149,9 +154,17 @@ impl From<DomainAuth> for Auth {
149154
fn from(auth: DomainAuth) -> Self {
150155
Self {
151156
email_on_signup: auth.email_on_signup.to_string(),
152-
min_password_length: auth.min_password_length,
153-
max_password_length: auth.max_password_length,
154157
secret_key: auth.secret_key.to_string(),
158+
password_constraints: auth.password_constraints.into(),
159+
}
160+
}
161+
}
162+
163+
impl From<DomainPasswordConstraints> for PasswordConstraints {
164+
fn from(password_constraints: DomainPasswordConstraints) -> Self {
165+
Self {
166+
min_password_length: password_constraints.min_password_length,
167+
max_password_length: password_constraints.max_password_length,
155168
}
156169
}
157170
}

tests/e2e/web/api/v1/contexts/settings/contract.rs

+2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ async fn it_should_allow_admins_to_get_all_the_settings() {
5959

6060
let response = client.get_settings().await;
6161

62+
println!("{}", response.body);
63+
6264
let res: AllSettingsResponse = serde_json::from_str(&response.body).unwrap();
6365

6466
assert_eq!(res.data, env.server_settings_masking_secrets().unwrap());

0 commit comments

Comments
 (0)