server: Improve the error message in case of duplicate emails

This commit is contained in:
Valentin Tolmer
2023-04-14 11:40:35 +02:00
committed by nitnelave
parent 96b7dbb1c5
commit e5ce98c874
3 changed files with 136 additions and 16 deletions

View File

@@ -3,9 +3,12 @@ use crate::domain::{
types::{GroupId, UserId, Uuid},
};
use anyhow::Context;
use itertools::Itertools;
use sea_orm::{
sea_query::{self, ColumnDef, Expr, ForeignKey, ForeignKeyAction, Index, Query, Table, Value},
ConnectionTrait, FromQueryResult, Iden, Statement, TransactionTrait,
sea_query::{
self, all, ColumnDef, Expr, ForeignKey, ForeignKeyAction, Func, Index, Query, Table, Value,
},
ConnectionTrait, FromQueryResult, Iden, Order, Statement, TransactionTrait,
};
use serde::{Deserialize, Serialize};
use tracing::{info, instrument, warn};
@@ -462,20 +465,73 @@ async fn migrate_to_v3(pool: &DbConnection) -> anyhow::Result<()> {
async fn migrate_to_v4(pool: &DbConnection) -> anyhow::Result<()> {
let builder = pool.get_database_backend();
// Make emails and UUIDs unique.
if let Err(e) = pool
.execute(
builder.build(
Index::create()
.if_not_exists()
.name("unique-user-email")
.table(Users::Table)
.col(Users::Email)
.unique(),
),
)
.await
.context(
r#"while enforcing unicity on emails (2 users have the same email).
See https://github.com/lldap/lldap/blob/main/docs/migration_guides/v0.5.md for details.
"#,
)
{
warn!("Found several users with the same email:");
for (email, users) in &pool
.query_all(
builder.build(
Query::select()
.from(Users::Table)
.columns([Users::Email, Users::UserId])
.order_by_columns([(Users::Email, Order::Asc), (Users::UserId, Order::Asc)])
.and_where(
Expr::col(Users::Email).in_subquery(
Query::select()
.from(Users::Table)
.column(Users::Email)
.group_by_col(Users::Email)
.cond_having(all![Expr::gt(
Expr::expr(Func::count(Expr::col(Users::Email))),
1
)])
.take(),
),
),
),
)
.await
.expect("Could not check duplicate users")
.into_iter()
.map(|row| {
(
row.try_get::<UserId>("", &Users::UserId.to_string())
.unwrap(),
row.try_get::<String>("", &Users::Email.to_string())
.unwrap(),
)
})
.group_by(|(_user, email)| email.to_owned())
{
warn!("Email: {email}");
for (user, _email) in users {
warn!(" User: {}", user.as_str());
}
}
return Err(e);
}
pool.execute(
builder.build(
Index::create()
.name("unique-user-email")
.table(Users::Table)
.col(Users::Email)
.unique(),
),
)
.await
.context("while enforcing unicity on emails (2 users have the same email)")?;
pool.execute(
builder.build(
Index::create()
.if_not_exists()
.name("unique-user-uuid")
.table(Users::Table)
.col(Users::Uuid)
@@ -487,6 +543,7 @@ async fn migrate_to_v4(pool: &DbConnection) -> anyhow::Result<()> {
pool.execute(
builder.build(
Index::create()
.if_not_exists()
.name("unique-group-uuid")
.table(Groups::Table)
.col(Groups::Uuid)

View File

@@ -46,6 +46,7 @@ mod tests {
use super::*;
use chrono::prelude::*;
use sea_orm::{ConnectionTrait, Database, DbBackend, FromQueryResult};
use tracing::error;
async fn get_in_memory_db() -> DbConnection {
let mut sql_opt = sea_orm::ConnectOptions::new("sqlite::memory:".to_owned());
@@ -208,6 +209,7 @@ mod tests {
#[tokio::test]
async fn test_migration_to_v4() {
crate::infra::logging::init_for_tests();
let sql_pool = get_in_memory_db().await;
upgrade_to_v1(&sql_pool).await.unwrap();
migrate_from_version(&sql_pool, SchemaVersion(1), SchemaVersion(3))
@@ -227,9 +229,12 @@ mod tests {
))
.await
.unwrap();
migrate_from_version(&sql_pool, SchemaVersion(3), SchemaVersion(4))
.await
.expect_err("migration should fail");
error!(
"{}",
migrate_from_version(&sql_pool, SchemaVersion(3), SchemaVersion(4))
.await
.expect_err("migration should fail")
);
assert_eq!(
sql_migrations::JustSchemaVersion::find_by_statement(raw_statement(
r#"SELECT version FROM metadata"#