mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-06 17:59:11 +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: 修改密码
|
change_password: 修改密码
|
||||||
old_password: 旧密码
|
old_password: 旧密码
|
||||||
new_password: 新密码
|
new_password: 新密码
|
||||||
new_password_empty: 新密码不能为空
|
|
||||||
confirm_password: 确认新密码
|
confirm_password: 确认新密码
|
||||||
language: 语言
|
language: 语言
|
||||||
theme: 主题
|
theme: 主题
|
||||||
|
|||||||
@@ -377,7 +377,6 @@ web:
|
|||||||
change_password: Change Password
|
change_password: Change Password
|
||||||
old_password: Old Password
|
old_password: Old Password
|
||||||
new_password: New Password
|
new_password: New Password
|
||||||
new_password_empty: New password cannot be empty
|
|
||||||
confirm_password: Confirm New Password
|
confirm_password: Confirm New Password
|
||||||
language: Language
|
language: Language
|
||||||
theme: Theme
|
theme: Theme
|
||||||
|
|||||||
@@ -164,11 +164,22 @@ export class ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async check_login_status(): Promise<CheckLoginStatusResponse> {
|
public async check_login_status(): Promise<CheckLoginStatusResponse> {
|
||||||
|
try {
|
||||||
const response = await this.client.get<any, AuthStatusResponse>('/auth/check_login_status');
|
const response = await this.client.get<any, AuthStatusResponse>('/auth/check_login_status');
|
||||||
return {
|
return {
|
||||||
loggedIn: true,
|
loggedIn: true,
|
||||||
mustChangePassword: response.must_change_password,
|
mustChangePassword: response.must_change_password,
|
||||||
};
|
};
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError && error.response?.status === 401) {
|
||||||
|
return {
|
||||||
|
loggedIn: false,
|
||||||
|
mustChangePassword: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async list_session() {
|
public async list_session() {
|
||||||
|
|||||||
@@ -2,10 +2,16 @@ use sea_orm_migration::prelude::*;
|
|||||||
|
|
||||||
pub struct Migration;
|
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)]
|
#[derive(DeriveIden)]
|
||||||
enum Users {
|
enum Users {
|
||||||
Table,
|
Table,
|
||||||
Username,
|
Username,
|
||||||
|
Password,
|
||||||
MustChangePassword,
|
MustChangePassword,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +43,14 @@ impl MigrationTrait for Migration {
|
|||||||
Query::update()
|
Query::update()
|
||||||
.table(Users::Table)
|
.table(Users::Table)
|
||||||
.value(Users::MustChangePassword, true)
|
.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(),
|
.to_owned(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -58,3 +71,59 @@ impl MigrationTrait for Migration {
|
|||||||
Ok(())
|
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
|
.await
|
||||||
{
|
{
|
||||||
tracing::error!("Failed to change password: {:?}", e);
|
tracing::error!("Failed to change password: {:?}", e);
|
||||||
let status = match e {
|
let (status, message) = match &e {
|
||||||
ChangePasswordError::EmptyPassword => StatusCode::BAD_REQUEST,
|
ChangePasswordError::EmptyPassword => {
|
||||||
ChangePasswordError::UserNotFound | ChangePasswordError::Db(_) => {
|
(StatusCode::BAD_REQUEST, "password cannot be empty")
|
||||||
StatusCode::INTERNAL_SERVER_ERROR
|
|
||||||
}
|
}
|
||||||
|
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;
|
let _ = auth_session.logout().await;
|
||||||
|
|||||||
Reference in New Issue
Block a user