feat: add dev password auto registration
Some checks failed
CI / verify (pull_request) Has been cancelled
Some checks failed
CI / verify (pull_request) Has been cancelled
This commit is contained in:
@@ -486,6 +486,38 @@ impl PasswordEntryService {
|
||||
verify_stored_password_user(existing_user, &input.password).await
|
||||
}
|
||||
|
||||
pub async fn execute_with_dev_registration(
|
||||
&self,
|
||||
input: PasswordEntryInput,
|
||||
) -> Result<PasswordEntryResult, PasswordEntryError> {
|
||||
validate_password(&input.password)?;
|
||||
let normalized_phone = normalize_mainland_china_phone_number(&input.phone_number)
|
||||
.map_err(|_| PasswordEntryError::InvalidPhoneNumber)?;
|
||||
if let Some(existing_user) = self
|
||||
.store
|
||||
.find_by_phone_number_for_password(&normalized_phone.e164)?
|
||||
{
|
||||
return verify_stored_password_user(existing_user, &input.password).await;
|
||||
}
|
||||
|
||||
let password_hash = hash_password(&input.password)
|
||||
.await
|
||||
.map_err(|error| PasswordEntryError::PasswordHash(error.to_string()))?;
|
||||
let user = self.store.create_dev_password_phone_user(
|
||||
normalized_phone.clone(),
|
||||
normalized_phone.masked_national_number,
|
||||
password_hash,
|
||||
)?;
|
||||
|
||||
Ok(PasswordEntryResult {
|
||||
user: AuthUser {
|
||||
login_method: AuthLoginMethod::Password,
|
||||
..user
|
||||
},
|
||||
created: true,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_user_by_id(
|
||||
&self,
|
||||
user_id: &str,
|
||||
@@ -1336,6 +1368,53 @@ impl InMemoryAuthStore {
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
fn create_dev_password_phone_user(
|
||||
&self,
|
||||
phone_number: PhoneNumberSnapshot,
|
||||
display_name: String,
|
||||
password_hash: String,
|
||||
) -> Result<AuthUser, PasswordEntryError> {
|
||||
let mut state = self
|
||||
.inner
|
||||
.lock()
|
||||
.map_err(|_| PasswordEntryError::Store("用户仓储锁已中毒".to_string()))?;
|
||||
if state.phone_to_user_id.contains_key(&phone_number.e164) {
|
||||
return Err(PasswordEntryError::InvalidCredentials);
|
||||
}
|
||||
|
||||
let sequence = state.next_user_id;
|
||||
let user_id = format!("user_{sequence:08}");
|
||||
let public_user_code = build_public_user_code(sequence);
|
||||
state.next_user_id += 1;
|
||||
let username = build_system_username("phone", state.next_user_id);
|
||||
let user = AuthUser {
|
||||
id: user_id.clone(),
|
||||
public_user_code,
|
||||
username: username.clone(),
|
||||
display_name,
|
||||
phone_number_masked: Some(phone_number.masked_national_number.clone()),
|
||||
login_method: AuthLoginMethod::Password,
|
||||
binding_status: AuthBindingStatus::Active,
|
||||
wechat_bound: false,
|
||||
token_version: 1,
|
||||
};
|
||||
state
|
||||
.phone_to_user_id
|
||||
.insert(phone_number.e164.clone(), user_id);
|
||||
state.users_by_username.insert(
|
||||
username,
|
||||
StoredPasswordUser {
|
||||
user: user.clone(),
|
||||
password_hash,
|
||||
password_login_enabled: true,
|
||||
phone_number: Some(phone_number.e164),
|
||||
},
|
||||
);
|
||||
self.persist_password_state(&state)?;
|
||||
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
fn create_pending_wechat_user(
|
||||
&self,
|
||||
profile: WechatIdentityProfile,
|
||||
@@ -2474,6 +2553,39 @@ mod tests {
|
||||
assert_eq!(error, PasswordEntryError::InvalidCredentials);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn password_entry_dev_registration_creates_unknown_phone_user() {
|
||||
let service = build_password_service(build_store());
|
||||
|
||||
let created = service
|
||||
.execute_with_dev_registration(PasswordEntryInput {
|
||||
phone_number: "13800138009".to_string(),
|
||||
password: "secret123".to_string(),
|
||||
})
|
||||
.await
|
||||
.expect("dev registration should create user");
|
||||
let reused = service
|
||||
.execute_with_dev_registration(PasswordEntryInput {
|
||||
phone_number: "13800138009".to_string(),
|
||||
password: "secret123".to_string(),
|
||||
})
|
||||
.await
|
||||
.expect("same password should reuse created user");
|
||||
let wrong_password = service
|
||||
.execute_with_dev_registration(PasswordEntryInput {
|
||||
phone_number: "13800138009".to_string(),
|
||||
password: "secret999".to_string(),
|
||||
})
|
||||
.await
|
||||
.expect_err("existing user still requires the right password");
|
||||
|
||||
assert!(created.created);
|
||||
assert_eq!(created.user.login_method, AuthLoginMethod::Password);
|
||||
assert!(!reused.created);
|
||||
assert_eq!(created.user.id, reused.user.id);
|
||||
assert_eq!(wrong_password, PasswordEntryError::InvalidCredentials);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn phone_user_can_set_password_then_login() {
|
||||
let store = build_store();
|
||||
|
||||
Reference in New Issue
Block a user