mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-06 09:48:58 +00:00
fix(web): avoid false default-password reminders
Only flag seeded accounts that still use the shipped password hash, and keep auth status and password change responses stable during review follow-up.
This commit is contained in:
@@ -377,7 +377,6 @@ web:
|
||||
change_password: 修改密码
|
||||
old_password: 旧密码
|
||||
new_password: 新密码
|
||||
new_password_empty: 新密码不能为空
|
||||
confirm_password: 确认新密码
|
||||
language: 语言
|
||||
theme: 主题
|
||||
|
||||
@@ -377,7 +377,6 @@ web:
|
||||
change_password: Change Password
|
||||
old_password: Old Password
|
||||
new_password: New Password
|
||||
new_password_empty: New password cannot be empty
|
||||
confirm_password: Confirm New Password
|
||||
language: Language
|
||||
theme: Theme
|
||||
|
||||
@@ -164,10 +164,21 @@ export class ApiClient {
|
||||
}
|
||||
|
||||
public async check_login_status(): Promise<CheckLoginStatusResponse> {
|
||||
const response = await this.client.get<any, AuthStatusResponse>('/auth/check_login_status');
|
||||
return {
|
||||
loggedIn: true,
|
||||
mustChangePassword: response.must_change_password,
|
||||
try {
|
||||
const response = await this.client.get<any, AuthStatusResponse>('/auth/check_login_status');
|
||||
return {
|
||||
loggedIn: true,
|
||||
mustChangePassword: response.must_change_password,
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError && error.response?.status === 401) {
|
||||
return {
|
||||
loggedIn: false,
|
||||
mustChangePassword: false,
|
||||
};
|
||||
}
|
||||
|
||||
throw error;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,16 @@ use sea_orm_migration::prelude::*;
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
const DEFAULT_USER_PASSWORD_HASH: &str =
|
||||
"$argon2i$v=19$m=16,t=2,p=1$aGVyRDBrcnRycnlaMDhkbw$449SEcv/qXf+0fnI9+fYVQ";
|
||||
const DEFAULT_ADMIN_PASSWORD_HASH: &str =
|
||||
"$argon2i$v=19$m=16,t=2,p=1$bW5idXl0cmY$61n+JxL4r3dwLPAEDlDdtg";
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum Users {
|
||||
Table,
|
||||
Username,
|
||||
Password,
|
||||
MustChangePassword,
|
||||
}
|
||||
|
||||
@@ -37,7 +43,14 @@ impl MigrationTrait for Migration {
|
||||
Query::update()
|
||||
.table(Users::Table)
|
||||
.value(Users::MustChangePassword, true)
|
||||
.and_where(Expr::col(Users::Username).is_in(["admin", "user"]))
|
||||
.cond_where(any![
|
||||
Expr::col(Users::Username)
|
||||
.eq("admin")
|
||||
.and(Expr::col(Users::Password).eq(DEFAULT_ADMIN_PASSWORD_HASH)),
|
||||
Expr::col(Users::Username)
|
||||
.eq("user")
|
||||
.and(Expr::col(Users::Password).eq(DEFAULT_USER_PASSWORD_HASH)),
|
||||
])
|
||||
.to_owned(),
|
||||
)
|
||||
.await?;
|
||||
@@ -58,3 +71,59 @@ impl MigrationTrait for Migration {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter as _, SqlxSqliteConnector};
|
||||
use sea_orm_migration::prelude::SchemaManager;
|
||||
use sqlx::sqlite::SqlitePoolOptions;
|
||||
|
||||
use super::{Migration, MigrationTrait, DEFAULT_USER_PASSWORD_HASH};
|
||||
use crate::db::entity::users;
|
||||
|
||||
async fn find_user(db: &sea_orm::DatabaseConnection, username: &str) -> users::Model {
|
||||
users::Entity::find()
|
||||
.filter(users::Column::Username.eq(username))
|
||||
.one(db)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn migration_only_marks_seeded_accounts_still_using_default_passwords() {
|
||||
let pool = SqlitePoolOptions::new()
|
||||
.max_connections(1)
|
||||
.connect("sqlite::memory:")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
sqlx::query(
|
||||
"CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL
|
||||
)",
|
||||
)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let changed_admin_password = password_auth::generate_hash("already-changed");
|
||||
|
||||
sqlx::query("INSERT INTO users (username, password) VALUES (?, ?), (?, ?)")
|
||||
.bind("admin")
|
||||
.bind(changed_admin_password)
|
||||
.bind("user")
|
||||
.bind(DEFAULT_USER_PASSWORD_HASH)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let db = SqlxSqliteConnector::from_sqlx_sqlite_pool(pool);
|
||||
Migration.up(&SchemaManager::new(&db)).await.unwrap();
|
||||
|
||||
assert!(!find_user(&db, "admin").await.must_change_password);
|
||||
assert!(find_user(&db, "user").await.must_change_password);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,13 +60,16 @@ mod put {
|
||||
.await
|
||||
{
|
||||
tracing::error!("Failed to change password: {:?}", e);
|
||||
let status = match e {
|
||||
ChangePasswordError::EmptyPassword => StatusCode::BAD_REQUEST,
|
||||
ChangePasswordError::UserNotFound | ChangePasswordError::Db(_) => {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
let (status, message) = match &e {
|
||||
ChangePasswordError::EmptyPassword => {
|
||||
(StatusCode::BAD_REQUEST, "password cannot be empty")
|
||||
}
|
||||
ChangePasswordError::UserNotFound | ChangePasswordError::Db(_) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"failed to change password",
|
||||
),
|
||||
};
|
||||
return Err((status, Json::from(other_error(format!("{:?}", e)))));
|
||||
return Err((status, Json::from(other_error(message.to_string()))));
|
||||
}
|
||||
|
||||
let _ = auth_session.logout().await;
|
||||
|
||||
Reference in New Issue
Block a user