Merge branch 'master' into codex/puzzle-clear-template-runtime-fixes
This commit is contained in:
@@ -57,10 +57,16 @@ pub struct AuthUser {
|
||||
pub display_name: String,
|
||||
#[serde(default)]
|
||||
pub avatar_url: Option<String>,
|
||||
#[serde(default)]
|
||||
pub phone_number: Option<String>,
|
||||
pub phone_number_masked: Option<String>,
|
||||
pub login_method: AuthLoginMethod,
|
||||
pub binding_status: AuthBindingStatus,
|
||||
pub wechat_bound: bool,
|
||||
#[serde(default)]
|
||||
pub wechat_display_name: Option<String>,
|
||||
#[serde(default)]
|
||||
pub wechat_account: Option<String>,
|
||||
pub token_version: u64,
|
||||
#[serde(default)]
|
||||
pub created_at: String,
|
||||
|
||||
@@ -97,6 +97,33 @@ struct StoredWechatIdentity {
|
||||
session_key: Option<String>,
|
||||
}
|
||||
|
||||
fn hydrate_private_auth_fields(
|
||||
state: &InMemoryAuthStoreState,
|
||||
stored_user: &StoredPasswordUser,
|
||||
) -> StoredPasswordUser {
|
||||
let mut hydrated = stored_user.clone();
|
||||
if hydrated.user.phone_number.is_none() {
|
||||
hydrated.user.phone_number = hydrated.phone_number.clone();
|
||||
}
|
||||
let hydrated_wechat_identity = state
|
||||
.wechat_identity_by_provider_uid
|
||||
.values()
|
||||
.find(|identity| identity.user_id == hydrated.user.id);
|
||||
if hydrated.user.wechat_display_name.is_none() {
|
||||
hydrated.user.wechat_display_name = hydrated_wechat_identity
|
||||
.and_then(|identity| identity.display_name.clone())
|
||||
.or_else(|| {
|
||||
(hydrated.user.login_method == AuthLoginMethod::Wechat)
|
||||
.then(|| hydrated.user.display_name.clone())
|
||||
});
|
||||
}
|
||||
if hydrated.user.wechat_account.is_none() {
|
||||
hydrated.user.wechat_account =
|
||||
hydrated_wechat_identity.map(|identity| identity.provider_uid.clone());
|
||||
}
|
||||
hydrated
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PasswordEntryService {
|
||||
store: InMemoryAuthStore,
|
||||
@@ -1067,7 +1094,7 @@ impl InMemoryAuthStore {
|
||||
.users_by_username
|
||||
.values()
|
||||
.find(|stored_user| stored_user.user.id == user_id)
|
||||
.cloned())
|
||||
.map(|stored_user| hydrate_private_auth_fields(&state, stored_user)))
|
||||
}
|
||||
|
||||
fn ensure_orphan_work_owner_user(
|
||||
@@ -1107,10 +1134,13 @@ impl InMemoryAuthStore {
|
||||
username: username.clone(),
|
||||
display_name,
|
||||
avatar_url: None,
|
||||
phone_number: None,
|
||||
phone_number_masked: None,
|
||||
login_method: AuthLoginMethod::Password,
|
||||
binding_status: AuthBindingStatus::Active,
|
||||
wechat_bound: false,
|
||||
wechat_display_name: None,
|
||||
wechat_account: None,
|
||||
token_version: 1,
|
||||
created_at,
|
||||
};
|
||||
@@ -1141,7 +1171,7 @@ impl InMemoryAuthStore {
|
||||
.users_by_username
|
||||
.values()
|
||||
.find(|stored_user| stored_user.user.public_user_code == public_user_code)
|
||||
.cloned())
|
||||
.map(|stored_user| hydrate_private_auth_fields(&state, stored_user)))
|
||||
}
|
||||
|
||||
fn find_by_phone_number(
|
||||
@@ -1152,7 +1182,8 @@ impl InMemoryAuthStore {
|
||||
.inner
|
||||
.lock()
|
||||
.map_err(|_| PhoneAuthError::Store("用户仓储锁已中毒".to_string()))?;
|
||||
Ok(Self::resolve_phone_user_locked(&mut state, phone_number))
|
||||
Ok(Self::resolve_phone_user_locked(&mut state, phone_number)
|
||||
.map(|stored_user| hydrate_private_auth_fields(&state, &stored_user)))
|
||||
}
|
||||
|
||||
fn find_by_phone_number_for_password(
|
||||
@@ -1163,7 +1194,8 @@ impl InMemoryAuthStore {
|
||||
.inner
|
||||
.lock()
|
||||
.map_err(|_| PasswordEntryError::Store("用户仓储锁已中毒".to_string()))?;
|
||||
Ok(Self::resolve_phone_user_locked(&mut state, phone_number))
|
||||
Ok(Self::resolve_phone_user_locked(&mut state, phone_number)
|
||||
.map(|stored_user| hydrate_private_auth_fields(&state, &stored_user)))
|
||||
}
|
||||
|
||||
fn update_user_profile(
|
||||
@@ -1226,10 +1258,13 @@ impl InMemoryAuthStore {
|
||||
username: username.clone(),
|
||||
display_name,
|
||||
avatar_url: None,
|
||||
phone_number: Some(phone_number.e164.clone()),
|
||||
phone_number_masked: Some(phone_number.masked_national_number.clone()),
|
||||
login_method: AuthLoginMethod::Phone,
|
||||
binding_status: AuthBindingStatus::Active,
|
||||
wechat_bound: false,
|
||||
wechat_display_name: None,
|
||||
wechat_account: None,
|
||||
token_version: 1,
|
||||
created_at,
|
||||
};
|
||||
@@ -1278,10 +1313,13 @@ impl InMemoryAuthStore {
|
||||
username: username.clone(),
|
||||
display_name,
|
||||
avatar_url: None,
|
||||
phone_number: Some(phone_number.e164.clone()),
|
||||
phone_number_masked: Some(phone_number.masked_national_number.clone()),
|
||||
login_method: AuthLoginMethod::Password,
|
||||
binding_status: AuthBindingStatus::Active,
|
||||
wechat_bound: false,
|
||||
wechat_display_name: None,
|
||||
wechat_account: None,
|
||||
token_version: 1,
|
||||
created_at,
|
||||
};
|
||||
@@ -1327,17 +1365,23 @@ impl InMemoryAuthStore {
|
||||
.filter(|value| !value.is_empty())
|
||||
.unwrap_or("微信旅人")
|
||||
.to_string();
|
||||
let wechat_display_name = normalize_optional_string(profile.display_name.clone())
|
||||
.or_else(|| Some(display_name.clone()));
|
||||
let username = build_wechat_username(&display_name, &profile.provider_uid);
|
||||
let provider_uid = normalize_required_string(&profile.provider_uid).unwrap_or_default();
|
||||
let user = AuthUser {
|
||||
id: user_id.clone(),
|
||||
public_user_code,
|
||||
username: username.clone(),
|
||||
display_name,
|
||||
avatar_url: avatar_url.clone(),
|
||||
phone_number: None,
|
||||
phone_number_masked: None,
|
||||
login_method: AuthLoginMethod::Wechat,
|
||||
binding_status: AuthBindingStatus::PendingBindPhone,
|
||||
wechat_bound: true,
|
||||
wechat_display_name,
|
||||
wechat_account: Some(provider_uid.clone()),
|
||||
token_version: 1,
|
||||
created_at,
|
||||
};
|
||||
@@ -1352,7 +1396,7 @@ impl InMemoryAuthStore {
|
||||
);
|
||||
let identity = StoredWechatIdentity {
|
||||
user_id: user_id.clone(),
|
||||
provider_uid: normalize_required_string(&profile.provider_uid).unwrap_or_default(),
|
||||
provider_uid,
|
||||
provider_union_id: normalize_optional_string(profile.provider_union_id),
|
||||
display_name: normalize_optional_string(profile.display_name),
|
||||
avatar_url,
|
||||
@@ -1390,7 +1434,7 @@ impl InMemoryAuthStore {
|
||||
.values()
|
||||
.find(|stored_user| stored_user.user.id == *user_id)
|
||||
{
|
||||
return Ok(Some(stored.user.clone()));
|
||||
return Ok(Some(hydrate_private_auth_fields(&state, stored).user));
|
||||
}
|
||||
|
||||
let Some(identity) = state
|
||||
@@ -1403,7 +1447,7 @@ impl InMemoryAuthStore {
|
||||
.users_by_username
|
||||
.values()
|
||||
.find(|stored_user| stored_user.user.id == identity.user_id)
|
||||
.map(|stored| stored.user.clone()))
|
||||
.map(|stored| hydrate_private_auth_fields(&state, stored).user))
|
||||
}
|
||||
|
||||
fn get_wechat_identity_by_user_id(
|
||||
@@ -1492,6 +1536,10 @@ impl InMemoryAuthStore {
|
||||
{
|
||||
stored_user.user.display_name = display_name.to_string();
|
||||
}
|
||||
stored_user.user.wechat_account = Some(next_provider_uid.clone());
|
||||
if let Some(display_name) = next_display_name.clone() {
|
||||
stored_user.user.wechat_display_name = Some(display_name);
|
||||
}
|
||||
stored_user.user.clone()
|
||||
};
|
||||
self.persist_wechat_state(&state)?;
|
||||
@@ -1728,6 +1776,8 @@ impl InMemoryAuthStore {
|
||||
.find(|identity| identity.user_id == pending_user_id)
|
||||
.cloned()
|
||||
.ok_or(PhoneAuthError::UserStateMismatch)?;
|
||||
let pending_wechat_account = pending_wechat_identity.provider_uid.clone();
|
||||
let pending_wechat_display_name = pending_wechat_identity.display_name.clone();
|
||||
|
||||
let pending_username = state
|
||||
.users_by_username
|
||||
@@ -1756,6 +1806,11 @@ impl InMemoryAuthStore {
|
||||
.find(|stored| stored.user.id == target_user_id)
|
||||
.ok_or(PhoneAuthError::UserNotFound)?;
|
||||
target_user.user.wechat_bound = true;
|
||||
target_user.user.wechat_account = Some(pending_wechat_account);
|
||||
target_user.user.wechat_display_name = pending_wechat_display_name;
|
||||
if target_user.user.phone_number.is_none() {
|
||||
target_user.user.phone_number = target_user.phone_number.clone();
|
||||
}
|
||||
let next_user = target_user.user.clone();
|
||||
self.persist_phone_state(&state)?;
|
||||
|
||||
@@ -1765,15 +1820,32 @@ impl InMemoryAuthStore {
|
||||
state
|
||||
.phone_to_user_id
|
||||
.insert(phone_number.e164.clone(), pending_user_id.to_string());
|
||||
let bound_wechat_account = state
|
||||
.wechat_identity_by_provider_uid
|
||||
.values()
|
||||
.find(|identity| identity.user_id == pending_user_id)
|
||||
.map(|identity| identity.provider_uid.clone());
|
||||
let bound_wechat_display_name = state
|
||||
.wechat_identity_by_provider_uid
|
||||
.values()
|
||||
.find(|identity| identity.user_id == pending_user_id)
|
||||
.and_then(|identity| identity.display_name.clone());
|
||||
|
||||
let stored_user = state
|
||||
.users_by_username
|
||||
.values_mut()
|
||||
.find(|stored| stored.user.id == pending_user_id)
|
||||
.ok_or(PhoneAuthError::UserNotFound)?;
|
||||
stored_user.user.phone_number = Some(phone_number.e164.clone());
|
||||
stored_user.user.phone_number_masked = Some(phone_number.masked_national_number.clone());
|
||||
stored_user.user.binding_status = AuthBindingStatus::Active;
|
||||
stored_user.user.wechat_bound = true;
|
||||
if stored_user.user.wechat_account.is_none() {
|
||||
stored_user.user.wechat_account = bound_wechat_account;
|
||||
}
|
||||
if stored_user.user.wechat_display_name.is_none() {
|
||||
stored_user.user.wechat_display_name = bound_wechat_display_name;
|
||||
}
|
||||
stored_user.phone_number = Some(phone_number.e164);
|
||||
let next_user = stored_user.user.clone();
|
||||
self.persist_phone_state(&state)?;
|
||||
@@ -3412,6 +3484,10 @@ mod tests {
|
||||
AuthBindingStatus::PendingBindPhone
|
||||
);
|
||||
assert_eq!(first_wechat.user.username, "微信旅人甲_wx-openid-first");
|
||||
assert_eq!(
|
||||
first_wechat.user.wechat_display_name.as_deref(),
|
||||
Some("微信旅人甲")
|
||||
);
|
||||
assert!(first_wechat.user.id.starts_with("user_"));
|
||||
assert!(!first_wechat.user.id.ends_with("00000001"));
|
||||
|
||||
@@ -3433,6 +3509,10 @@ mod tests {
|
||||
assert_ne!(second_wechat.user.id, phone_user.id);
|
||||
assert_eq!(second_wechat.user.login_method, AuthLoginMethod::Wechat);
|
||||
assert_eq!(second_wechat.user.username, first_wechat.user.username);
|
||||
assert_eq!(
|
||||
second_wechat.user.wechat_display_name.as_deref(),
|
||||
Some("微信旅人乙")
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -3482,6 +3562,10 @@ mod tests {
|
||||
wechat_user.binding_status,
|
||||
AuthBindingStatus::PendingBindPhone
|
||||
);
|
||||
assert_eq!(
|
||||
wechat_user.wechat_display_name.as_deref(),
|
||||
Some("待绑定微信用户")
|
||||
);
|
||||
assert_ne!(wechat_user.id, phone_user.id);
|
||||
|
||||
phone_service
|
||||
@@ -3509,6 +3593,10 @@ mod tests {
|
||||
assert_eq!(merged.user.id, phone_user.id);
|
||||
assert_eq!(merged.user.binding_status, AuthBindingStatus::Active);
|
||||
assert!(merged.user.wechat_bound);
|
||||
assert_eq!(
|
||||
merged.user.wechat_display_name.as_deref(),
|
||||
Some("待绑定微信用户")
|
||||
);
|
||||
|
||||
let reused_wechat_user = wechat_service
|
||||
.resolve_login(ResolveWechatLoginInput {
|
||||
@@ -3526,5 +3614,9 @@ mod tests {
|
||||
assert!(!reused_wechat_user.created);
|
||||
assert_eq!(reused_wechat_user.user.id, phone_user.id);
|
||||
assert!(reused_wechat_user.user.wechat_bound);
|
||||
assert_eq!(
|
||||
reused_wechat_user.user.wechat_display_name.as_deref(),
|
||||
Some("已归并微信用户")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user