mirror of
https://github.com/bitwarden/android.git
synced 2025-03-15 18:58:59 +03:00
Add set-password function to auth repo (#1121)
This commit is contained in:
parent
2237d58ed5
commit
53c241b4d7
4 changed files with 268 additions and 0 deletions
|
@ -17,6 +17,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.PrevalidateSsoResult
|
|||
import com.x8bit.bitwarden.data.auth.repository.model.RegisterResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ResendEmailResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ResetPasswordResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.SetPasswordResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
|
||||
|
@ -208,6 +209,16 @@ interface AuthRepository : AuthenticatorProvider, AuthRequestManager {
|
|||
passwordHint: String?,
|
||||
): ResetPasswordResult
|
||||
|
||||
/**
|
||||
* Sets the user's password to [password] for the user within the given [organizationId] with
|
||||
* an optional [passwordHint].
|
||||
*/
|
||||
suspend fun setPassword(
|
||||
organizationId: String,
|
||||
password: String,
|
||||
passwordHint: String?,
|
||||
): SetPasswordResult
|
||||
|
||||
/**
|
||||
* Set the value of [captchaTokenResultFlow].
|
||||
*/
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJso
|
|||
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.ResendEmailRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.ResetPasswordRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.SetPasswordRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.service.AccountsService
|
||||
|
@ -45,6 +46,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.PrevalidateSsoResult
|
|||
import com.x8bit.bitwarden.data.auth.repository.model.RegisterResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ResendEmailResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ResetPasswordResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.SetPasswordResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
|
||||
|
@ -803,6 +805,62 @@ class AuthRepositoryImpl(
|
|||
)
|
||||
}
|
||||
|
||||
override suspend fun setPassword(
|
||||
organizationId: String,
|
||||
password: String,
|
||||
passwordHint: String?,
|
||||
): SetPasswordResult {
|
||||
val activeAccount = authDiskSource
|
||||
.userState
|
||||
?.activeAccount
|
||||
?: return SetPasswordResult.Error
|
||||
|
||||
// Update the saved master password hash.
|
||||
val passwordHash = authSdkSource
|
||||
.hashPassword(
|
||||
email = activeAccount.profile.email,
|
||||
password = password,
|
||||
kdf = activeAccount.profile.toSdkParams(),
|
||||
purpose = HashPurpose.SERVER_AUTHORIZATION,
|
||||
)
|
||||
.getOrElse { return@setPassword SetPasswordResult.Error }
|
||||
|
||||
return authSdkSource
|
||||
.makeRegisterKeys(
|
||||
email = activeAccount.profile.email,
|
||||
password = password,
|
||||
kdf = activeAccount.profile.toSdkParams(),
|
||||
)
|
||||
.flatMap { keyResponse ->
|
||||
accountsService.setPassword(
|
||||
body = SetPasswordRequestJson(
|
||||
passwordHash = passwordHash,
|
||||
passwordHint = passwordHint,
|
||||
organizationIdentifier = organizationId,
|
||||
kdfIterations = activeAccount.profile.kdfIterations,
|
||||
kdfMemory = activeAccount.profile.kdfMemory,
|
||||
kdfParallelism = activeAccount.profile.kdfParallelism,
|
||||
kdfType = activeAccount.profile.kdfType,
|
||||
key = keyResponse.encryptedUserKey,
|
||||
keys = RegisterRequestJson.Keys(
|
||||
publicKey = keyResponse.keys.public,
|
||||
encryptedPrivateKey = keyResponse.keys.private,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
.onSuccess {
|
||||
authDiskSource.storeMasterPasswordHash(
|
||||
userId = activeAccount.profile.userId,
|
||||
passwordHash = passwordHash,
|
||||
)
|
||||
}
|
||||
.fold(
|
||||
onFailure = { SetPasswordResult.Error },
|
||||
onSuccess = { SetPasswordResult.Success },
|
||||
)
|
||||
}
|
||||
|
||||
override fun setCaptchaCallbackTokenResult(tokenResult: CaptchaCallbackTokenResult) {
|
||||
captchaTokenChannel.trySend(tokenResult)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package com.x8bit.bitwarden.data.auth.repository.model
|
||||
|
||||
/**
|
||||
* Models result of setting a user's password.
|
||||
*/
|
||||
sealed class SetPasswordResult {
|
||||
/**
|
||||
* The password was set successfully.
|
||||
*/
|
||||
data object Success : SetPasswordResult()
|
||||
|
||||
/**
|
||||
* There was an error setting the password.
|
||||
*/
|
||||
data object Error : SetPasswordResult()
|
||||
}
|
|
@ -27,6 +27,7 @@ import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJso
|
|||
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.ResendEmailRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.ResetPasswordRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.SetPasswordRequestJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorAuthMethod
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.TwoFactorDataModel
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.service.AccountsService
|
||||
|
@ -54,6 +55,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.PrevalidateSsoResult
|
|||
import com.x8bit.bitwarden.data.auth.repository.model.RegisterResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ResendEmailResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ResetPasswordResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.SetPasswordResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.SwitchAccountResult
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserOrganizations
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
|
||||
|
@ -2456,6 +2458,187 @@ class AuthRepositoryTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `setPassword without active account should return Error`() = runTest {
|
||||
fakeAuthDiskSource.userState = null
|
||||
|
||||
val result = repository.setPassword(
|
||||
organizationId = "organizationId",
|
||||
password = "password",
|
||||
passwordHint = "passwordHint",
|
||||
)
|
||||
|
||||
assertEquals(SetPasswordResult.Error, result)
|
||||
fakeAuthDiskSource.assertMasterPasswordHash(userId = USER_ID_1, passwordHash = null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `setPassword with authSdkSource hashPassword failure should return Error`() = runTest {
|
||||
val password = "password"
|
||||
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
|
||||
coEvery {
|
||||
authSdkSource.hashPassword(
|
||||
email = EMAIL,
|
||||
password = password,
|
||||
kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(),
|
||||
purpose = HashPurpose.SERVER_AUTHORIZATION,
|
||||
)
|
||||
} returns Throwable("Fail").asFailure()
|
||||
|
||||
val result = repository.setPassword(
|
||||
organizationId = "organizationId",
|
||||
password = password,
|
||||
passwordHint = "passwordHint",
|
||||
)
|
||||
|
||||
assertEquals(SetPasswordResult.Error, result)
|
||||
fakeAuthDiskSource.assertMasterPasswordHash(userId = USER_ID_1, passwordHash = null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `setPassword with authSdkSource makeRegisterKeys failure should return Error`() = runTest {
|
||||
val password = "password"
|
||||
val passwordHash = "passwordHash"
|
||||
val kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams()
|
||||
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
|
||||
coEvery {
|
||||
authSdkSource.hashPassword(
|
||||
email = EMAIL,
|
||||
password = password,
|
||||
kdf = kdf,
|
||||
purpose = HashPurpose.SERVER_AUTHORIZATION,
|
||||
)
|
||||
} returns passwordHash.asSuccess()
|
||||
coEvery {
|
||||
authSdkSource.makeRegisterKeys(
|
||||
email = EMAIL,
|
||||
password = password,
|
||||
kdf = kdf,
|
||||
)
|
||||
} returns Throwable("Fail").asFailure()
|
||||
|
||||
val result = repository.setPassword(
|
||||
organizationId = "organizationId",
|
||||
password = password,
|
||||
passwordHint = "passwordHint",
|
||||
)
|
||||
|
||||
assertEquals(SetPasswordResult.Error, result)
|
||||
fakeAuthDiskSource.assertMasterPasswordHash(userId = USER_ID_1, passwordHash = null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `setPassword with accountsService setPassword failure should return Error`() = runTest {
|
||||
val password = "password"
|
||||
val passwordHash = "passwordHash"
|
||||
val passwordHint = "passwordHint"
|
||||
val organizationId = "organizationIdentifier"
|
||||
val encryptedUserKey = "encryptedUserKey"
|
||||
val privateRsaKey = "privateRsaKey"
|
||||
val publicRsaKey = "publicRsaKey"
|
||||
val profile = SINGLE_USER_STATE_1.activeAccount.profile
|
||||
val kdf = profile.toSdkParams()
|
||||
val registerKeyResponse = RegisterKeyResponse(
|
||||
masterPasswordHash = passwordHash,
|
||||
encryptedUserKey = encryptedUserKey,
|
||||
keys = RsaKeyPair(public = publicRsaKey, private = privateRsaKey),
|
||||
)
|
||||
val setPasswordRequestJson = SetPasswordRequestJson(
|
||||
passwordHash = passwordHash,
|
||||
passwordHint = passwordHint,
|
||||
organizationIdentifier = organizationId,
|
||||
kdfIterations = profile.kdfIterations,
|
||||
kdfMemory = profile.kdfMemory,
|
||||
kdfParallelism = profile.kdfParallelism,
|
||||
kdfType = profile.kdfType,
|
||||
key = encryptedUserKey,
|
||||
keys = RegisterRequestJson.Keys(
|
||||
publicKey = publicRsaKey,
|
||||
encryptedPrivateKey = privateRsaKey,
|
||||
),
|
||||
)
|
||||
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
|
||||
coEvery {
|
||||
authSdkSource.hashPassword(
|
||||
email = EMAIL,
|
||||
password = password,
|
||||
kdf = kdf,
|
||||
purpose = HashPurpose.SERVER_AUTHORIZATION,
|
||||
)
|
||||
} returns passwordHash.asSuccess()
|
||||
coEvery {
|
||||
authSdkSource.makeRegisterKeys(email = EMAIL, password = password, kdf = kdf)
|
||||
} returns registerKeyResponse.asSuccess()
|
||||
coEvery {
|
||||
accountsService.setPassword(body = setPasswordRequestJson)
|
||||
} returns Throwable("Fail").asFailure()
|
||||
|
||||
val result = repository.setPassword(
|
||||
organizationId = organizationId,
|
||||
password = password,
|
||||
passwordHint = passwordHint,
|
||||
)
|
||||
|
||||
assertEquals(SetPasswordResult.Error, result)
|
||||
fakeAuthDiskSource.assertMasterPasswordHash(userId = USER_ID_1, passwordHash = null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `setPassword with accountsService setPassword success should return Success`() = runTest {
|
||||
val password = "password"
|
||||
val passwordHash = "passwordHash"
|
||||
val passwordHint = "passwordHint"
|
||||
val organizationId = "organizationIdentifier"
|
||||
val encryptedUserKey = "encryptedUserKey"
|
||||
val privateRsaKey = "privateRsaKey"
|
||||
val publicRsaKey = "publicRsaKey"
|
||||
val profile = SINGLE_USER_STATE_1.activeAccount.profile
|
||||
val kdf = profile.toSdkParams()
|
||||
val registerKeyResponse = RegisterKeyResponse(
|
||||
masterPasswordHash = passwordHash,
|
||||
encryptedUserKey = encryptedUserKey,
|
||||
keys = RsaKeyPair(public = publicRsaKey, private = privateRsaKey),
|
||||
)
|
||||
val setPasswordRequestJson = SetPasswordRequestJson(
|
||||
passwordHash = passwordHash,
|
||||
passwordHint = passwordHint,
|
||||
organizationIdentifier = organizationId,
|
||||
kdfIterations = profile.kdfIterations,
|
||||
kdfMemory = profile.kdfMemory,
|
||||
kdfParallelism = profile.kdfParallelism,
|
||||
kdfType = profile.kdfType,
|
||||
key = encryptedUserKey,
|
||||
keys = RegisterRequestJson.Keys(
|
||||
publicKey = publicRsaKey,
|
||||
encryptedPrivateKey = privateRsaKey,
|
||||
),
|
||||
)
|
||||
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
|
||||
coEvery {
|
||||
authSdkSource.hashPassword(
|
||||
email = EMAIL,
|
||||
password = password,
|
||||
kdf = kdf,
|
||||
purpose = HashPurpose.SERVER_AUTHORIZATION,
|
||||
)
|
||||
} returns passwordHash.asSuccess()
|
||||
coEvery {
|
||||
authSdkSource.makeRegisterKeys(email = EMAIL, password = password, kdf = kdf)
|
||||
} returns registerKeyResponse.asSuccess()
|
||||
coEvery {
|
||||
accountsService.setPassword(body = setPasswordRequestJson)
|
||||
} returns Unit.asSuccess()
|
||||
|
||||
val result = repository.setPassword(
|
||||
organizationId = organizationId,
|
||||
password = password,
|
||||
passwordHint = passwordHint,
|
||||
)
|
||||
|
||||
assertEquals(SetPasswordResult.Success, result)
|
||||
fakeAuthDiskSource.assertMasterPasswordHash(userId = USER_ID_1, passwordHash = passwordHash)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `passwordHintRequest with valid email should return Success`() = runTest {
|
||||
val email = "valid@example.com"
|
||||
|
|
Loading…
Add table
Reference in a new issue