Skip to content

Commit 83a3529

Browse files
committed
Rip out the email verification codes
This considers all user_emails as confirmed, and removes the verification code. It will be replaced by a new email authentication code flow
1 parent 1707735 commit 83a3529

33 files changed

+86
-1081
lines changed

crates/cli/src/commands/manage.rs

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ enum Subcommand {
6565
/// Add an email address to the specified user
6666
AddEmail { username: String, email: String },
6767

68-
/// Mark email address as verified
68+
/// [DEPRECATED] Mark email address as verified
6969
VerifyEmail { username: String, email: String },
7070

7171
/// Set a user password
@@ -252,10 +252,8 @@ impl Options {
252252
.await?
253253
};
254254

255-
let email = repo.user_email().mark_as_verified(&clock, email).await?;
256-
257255
repo.into_inner().commit().await?;
258-
info!(?email, "Email added and marked as verified");
256+
info!(?email, "Email added");
259257

260258
Ok(ExitCode::SUCCESS)
261259
}
@@ -268,26 +266,7 @@ impl Options {
268266
)
269267
.entered();
270268

271-
let database_config = DatabaseConfig::extract_or_default(figment)?;
272-
let mut conn = database_connection_from_config(&database_config).await?;
273-
let txn = conn.begin().await?;
274-
let mut repo = PgRepository::from_conn(txn);
275-
276-
let user = repo
277-
.user()
278-
.find_by_username(&username)
279-
.await?
280-
.context("User not found")?;
281-
282-
let email = repo
283-
.user_email()
284-
.find(&user, &email)
285-
.await?
286-
.context("Email not found")?;
287-
let email = repo.user_email().mark_as_verified(&clock, email).await?;
288-
289-
repo.into_inner().commit().await?;
290-
info!(?email, "Email marked as verified");
269+
tracing::warn!("The 'verify-email' command is deprecated and will be removed in a future version. Use 'add-email' instead.");
291270

292271
Ok(ExitCode::SUCCESS)
293272
}
@@ -933,13 +912,8 @@ impl UserCreationRequest<'_> {
933912
}
934913

935914
for email in emails {
936-
let user_email = repo
937-
.user_email()
938-
.add(rng, clock, &user, email.to_string())
939-
.await?;
940-
941915
repo.user_email()
942-
.mark_as_verified(clock, user_email)
916+
.add(rng, clock, &user, email.to_string())
943917
.await?;
944918
}
945919

crates/cli/src/sync.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,6 @@ fn map_claims_imports(
5656
action: map_import_action(config.email.action),
5757
template: config.email.template.clone(),
5858
},
59-
verify_email: match config.email.set_email_verification {
60-
mas_config::UpstreamOAuth2SetEmailVerification::Always => {
61-
mas_data_model::UpsreamOAuthProviderSetEmailVerification::Always
62-
}
63-
mas_config::UpstreamOAuth2SetEmailVerification::Never => {
64-
mas_data_model::UpsreamOAuthProviderSetEmailVerification::Never
65-
}
66-
mas_config::UpstreamOAuth2SetEmailVerification::Import => {
67-
mas_data_model::UpsreamOAuthProviderSetEmailVerification::Import
68-
}
69-
},
7059
account_name: mas_data_model::UpstreamOAuthProviderSubjectPreference {
7160
template: config.account_name.template.clone(),
7261
},

crates/config/src/sections/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ pub use self::{
5252
EmailImportPreference as UpstreamOAuth2EmailImportPreference,
5353
ImportAction as UpstreamOAuth2ImportAction, PkceMethod as UpstreamOAuth2PkceMethod,
5454
ResponseMode as UpstreamOAuth2ResponseMode,
55-
SetEmailVerification as UpstreamOAuth2SetEmailVerification,
5655
TokenAuthMethod as UpstreamOAuth2TokenAuthMethod, UpstreamOAuth2Config,
5756
},
5857
};

crates/config/src/sections/upstream_oauth2.rs

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -180,29 +180,6 @@ impl ImportAction {
180180
}
181181
}
182182

183-
/// Should the email address be marked as verified
184-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
185-
#[serde(rename_all = "lowercase")]
186-
pub enum SetEmailVerification {
187-
/// Mark the email address as verified
188-
Always,
189-
190-
/// Don't mark the email address as verified
191-
Never,
192-
193-
/// Mark the email address as verified if the upstream provider says it is
194-
/// through the `email_verified` claim
195-
#[default]
196-
Import,
197-
}
198-
199-
impl SetEmailVerification {
200-
#[allow(clippy::trivially_copy_pass_by_ref)]
201-
const fn is_default(&self) -> bool {
202-
matches!(self, SetEmailVerification::Import)
203-
}
204-
}
205-
206183
/// What should be done for the subject attribute
207184
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, JsonSchema)]
208185
pub struct SubjectImportPreference {
@@ -271,17 +248,11 @@ pub struct EmailImportPreference {
271248
/// If not provided, the default template is `{{ user.email }}`
272249
#[serde(default, skip_serializing_if = "Option::is_none")]
273250
pub template: Option<String>,
274-
275-
/// Should the email address be marked as verified
276-
#[serde(default, skip_serializing_if = "SetEmailVerification::is_default")]
277-
pub set_email_verification: SetEmailVerification,
278251
}
279252

280253
impl EmailImportPreference {
281254
const fn is_default(&self) -> bool {
282-
self.action.is_default()
283-
&& self.template.is_none()
284-
&& self.set_email_verification.is_default()
255+
self.action.is_default() && self.template.is_none()
285256
}
286257
}
287258

crates/data-model/src/lib.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,17 @@ pub use self::{
3737
AccessToken, AccessTokenState, RefreshToken, RefreshTokenState, TokenFormatError, TokenType,
3838
},
3939
upstream_oauth2::{
40-
UpsreamOAuthProviderSetEmailVerification, UpstreamOAuthAuthorizationSession,
41-
UpstreamOAuthAuthorizationSessionState, UpstreamOAuthLink, UpstreamOAuthProvider,
42-
UpstreamOAuthProviderClaimsImports, UpstreamOAuthProviderDiscoveryMode,
43-
UpstreamOAuthProviderImportAction, UpstreamOAuthProviderImportPreference,
44-
UpstreamOAuthProviderPkceMode, UpstreamOAuthProviderResponseMode,
45-
UpstreamOAuthProviderSubjectPreference, UpstreamOAuthProviderTokenAuthMethod,
40+
UpstreamOAuthAuthorizationSession, UpstreamOAuthAuthorizationSessionState,
41+
UpstreamOAuthLink, UpstreamOAuthProvider, UpstreamOAuthProviderClaimsImports,
42+
UpstreamOAuthProviderDiscoveryMode, UpstreamOAuthProviderImportAction,
43+
UpstreamOAuthProviderImportPreference, UpstreamOAuthProviderPkceMode,
44+
UpstreamOAuthProviderResponseMode, UpstreamOAuthProviderSubjectPreference,
45+
UpstreamOAuthProviderTokenAuthMethod,
4646
},
4747
user_agent::{DeviceType, UserAgent},
4848
users::{
4949
Authentication, AuthenticationMethod, BrowserSession, Password, User, UserEmail,
50-
UserEmailAuthentication, UserEmailAuthenticationCode, UserEmailVerification,
51-
UserEmailVerificationState, UserRecoverySession, UserRecoveryTicket,
50+
UserEmailAuthentication, UserEmailAuthenticationCode, UserRecoverySession,
51+
UserRecoveryTicket,
5252
},
5353
};

crates/data-model/src/upstream_oauth2/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ pub use self::{
1717
ImportPreference as UpstreamOAuthProviderImportPreference,
1818
PkceMode as UpstreamOAuthProviderPkceMode,
1919
ResponseMode as UpstreamOAuthProviderResponseMode,
20-
SetEmailVerification as UpsreamOAuthProviderSetEmailVerification,
2120
SubjectPreference as UpstreamOAuthProviderSubjectPreference,
2221
TokenAuthMethod as UpstreamOAuthProviderTokenAuthMethod, UpstreamOAuthProvider,
2322
},

crates/data-model/src/upstream_oauth2/provider.rs

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -263,32 +263,6 @@ impl UpstreamOAuthProvider {
263263
}
264264
}
265265

266-
/// Whether to set the email as verified when importing it from the upstream
267-
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
268-
#[serde(rename_all = "lowercase")]
269-
pub enum SetEmailVerification {
270-
/// Set the email as verified
271-
Always,
272-
273-
/// Never set the email as verified
274-
Never,
275-
276-
/// Set the email as verified if the upstream provider claims it is verified
277-
#[default]
278-
Import,
279-
}
280-
281-
impl SetEmailVerification {
282-
#[must_use]
283-
pub fn should_mark_as_verified(&self, upstream_verified: bool) -> bool {
284-
match self {
285-
Self::Always => true,
286-
Self::Never => false,
287-
Self::Import => upstream_verified,
288-
}
289-
}
290-
}
291-
292266
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
293267
pub struct ClaimsImports {
294268
#[serde(default)]
@@ -305,9 +279,6 @@ pub struct ClaimsImports {
305279

306280
#[serde(default)]
307281
pub account_name: SubjectPreference,
308-
309-
#[serde(default)]
310-
pub verify_email: SetEmailVerification,
311282
}
312283

313284
// XXX: this should have another name

crates/data-model/src/users.rs

Lines changed: 3 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
// SPDX-License-Identifier: AGPL-3.0-only
55
// Please see LICENSE in the repository root for full details.
66

7-
use std::{net::IpAddr, ops::Deref};
7+
use std::net::IpAddr;
88

9-
use chrono::{DateTime, Duration, Utc};
10-
use rand::{Rng, SeedableRng};
9+
use chrono::{DateTime, Utc};
10+
use rand::Rng;
1111
use serde::Serialize;
1212
use ulid::Ulid;
1313

@@ -171,7 +171,6 @@ pub struct UserEmail {
171171
pub user_id: Ulid,
172172
pub email: String,
173173
pub created_at: DateTime<Utc>,
174-
pub confirmed_at: Option<DateTime<Utc>>,
175174
}
176175

177176
impl UserEmail {
@@ -183,79 +182,13 @@ impl UserEmail {
183182
user_id: Ulid::from_datetime_with_source(now.into(), rng),
184183
email: "[email protected]".to_owned(),
185184
created_at: now,
186-
confirmed_at: Some(now),
187185
},
188186
Self {
189187
id: Ulid::from_datetime_with_source(now.into(), rng),
190188
user_id: Ulid::from_datetime_with_source(now.into(), rng),
191189
email: "[email protected]".to_owned(),
192190
created_at: now,
193-
confirmed_at: None,
194191
},
195192
]
196193
}
197194
}
198-
199-
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
200-
pub enum UserEmailVerificationState {
201-
AlreadyUsed { when: DateTime<Utc> },
202-
Expired { when: DateTime<Utc> },
203-
Valid,
204-
}
205-
206-
impl UserEmailVerificationState {
207-
#[must_use]
208-
pub fn is_valid(&self) -> bool {
209-
matches!(self, Self::Valid)
210-
}
211-
}
212-
213-
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
214-
pub struct UserEmailVerification {
215-
pub id: Ulid,
216-
pub user_email_id: Ulid,
217-
pub code: String,
218-
pub created_at: DateTime<Utc>,
219-
pub state: UserEmailVerificationState,
220-
}
221-
222-
impl Deref for UserEmailVerification {
223-
type Target = UserEmailVerificationState;
224-
225-
fn deref(&self) -> &Self::Target {
226-
&self.state
227-
}
228-
}
229-
230-
impl UserEmailVerification {
231-
#[doc(hidden)]
232-
#[must_use]
233-
pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<Self> {
234-
let states = [
235-
UserEmailVerificationState::AlreadyUsed {
236-
when: now - Duration::microseconds(5 * 60 * 1000 * 1000),
237-
},
238-
UserEmailVerificationState::Expired {
239-
when: now - Duration::microseconds(5 * 60 * 1000 * 1000),
240-
},
241-
UserEmailVerificationState::Valid,
242-
];
243-
244-
states
245-
.into_iter()
246-
.flat_map(move |state| {
247-
let mut rng =
248-
rand_chacha::ChaChaRng::from_rng(&mut *rng).expect("could not seed rng");
249-
UserEmail::samples(now, &mut rng)
250-
.into_iter()
251-
.map(move |email| Self {
252-
id: Ulid::from_datetime_with_source(now.into(), &mut rng),
253-
user_email_id: email.id,
254-
code: "123456".to_owned(),
255-
created_at: now - Duration::microseconds(10 * 60 * 1000 * 1000),
256-
state: state.clone(),
257-
})
258-
})
259-
.collect()
260-
}
261-
}

crates/email/src/mailer.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,6 @@ impl Mailer {
111111
email.to = %to,
112112
email.language = %context.language(),
113113
user.id = %context.user().id,
114-
user_email_verification.id = %context.verification().id,
115-
user_email_verification.code = context.verification().code,
116114
),
117115
err,
118116
)]

crates/handlers/src/graphql/model/users.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,11 @@ impl User {
323323
&self,
324324
ctx: &Context<'_>,
325325

326-
#[graphql(name = "state", desc = "List only emails in the given state.")]
326+
#[graphql(
327+
deprecation = "Emails are always confirmed, and have only one state",
328+
name = "state",
329+
desc = "List only emails in the given state."
330+
)]
327331
state_param: Option<UserEmailState>,
328332

329333
#[graphql(desc = "Returns the elements in the list that come after the cursor.")]
@@ -335,6 +339,7 @@ impl User {
335339
) -> Result<Connection<Cursor, UserEmail, PreloadedTotalCount>, async_graphql::Error> {
336340
let state = ctx.state();
337341
let mut repo = state.repository().await?;
342+
let _ = state_param;
338343

339344
query(
340345
after,
@@ -352,12 +357,6 @@ impl User {
352357

353358
let filter = UserEmailFilter::new().for_user(&self.0);
354359

355-
let filter = match state_param {
356-
Some(UserEmailState::Pending) => filter.pending_only(),
357-
Some(UserEmailState::Confirmed) => filter.verified_only(),
358-
None => filter,
359-
};
360-
361360
let page = repo.user_email().list(filter, pagination).await?;
362361

363362
// Preload the total count if requested
@@ -738,8 +737,9 @@ impl UserEmail {
738737

739738
/// When the email address was confirmed. Is `null` if the email was never
740739
/// verified by the user.
740+
#[graphql(deprecation = "Emails are always confirmed now.")]
741741
async fn confirmed_at(&self) -> Option<DateTime<Utc>> {
742-
self.0.confirmed_at
742+
Some(self.0.created_at)
743743
}
744744
}
745745

0 commit comments

Comments
 (0)