Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ import com.x8bit.bitwarden.data.auth.repository.util.activeUserIdChangesFlow
import com.x8bit.bitwarden.data.auth.repository.util.policyInformation
import com.x8bit.bitwarden.data.auth.repository.util.toAccountCryptographicState
import com.x8bit.bitwarden.data.auth.repository.util.toDeviceInfo
import com.x8bit.bitwarden.data.auth.repository.util.toKdfRequestModel
import com.x8bit.bitwarden.data.auth.repository.util.toOrganizations
import com.x8bit.bitwarden.data.auth.repository.util.toRemovedPasswordUserStateJson
import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams
Expand Down Expand Up @@ -1103,13 +1102,11 @@ class AuthRepositoryImpl(
)
.flatMap { response ->
accountsService.resetPassword(
body = ResetPasswordRequestJson(
body = ResetPasswordRequestJson.V1(
currentPasswordHash = currentPasswordHash,
newPasswordHash = response.passwordHash,
passwordHint = passwordHint,
kdf = profile.toKdfRequestModel(),
salt = profile.email,
masterPasswordAuthenticationHash = response.passwordHash,
masterKeyWrappedUserKey = response.newKey,
key = response.newKey,
),
Comment thread
david-livefront marked this conversation as resolved.
)
}
Expand Down Expand Up @@ -1168,13 +1165,16 @@ class AuthRepositoryImpl(
.flatMap { response ->
accountsService
.setPassword(
body = SetPasswordRequestJson.V2(
body = SetPasswordRequestJson.V1(
passwordHint = passwordHint,
organizationIdentifier = organizationIdentifier,
kdf = profile.toKdfRequestModel(),
salt = profile.email,
masterPasswordAuthenticationHash = response.passwordHash,
masterKeyWrappedUserKey = response.newKey,
kdfIterations = profile.kdfIterations,
kdfMemory = profile.kdfMemory,
kdfParallelism = profile.kdfParallelism,
kdfType = profile.kdfType,
key = response.newKey,
passwordHash = response.passwordHash,
keys = null,
),
)
.map { response }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ import com.x8bit.bitwarden.data.auth.repository.util.CookieCallbackResult
import com.x8bit.bitwarden.data.auth.repository.util.DuoCallbackTokenResult
import com.x8bit.bitwarden.data.auth.repository.util.SsoCallbackResult
import com.x8bit.bitwarden.data.auth.repository.util.WebAuthResult
import com.x8bit.bitwarden.data.auth.repository.util.toKdfRequestModel
import com.x8bit.bitwarden.data.auth.repository.util.toRemovedPasswordUserStateJson
import com.x8bit.bitwarden.data.auth.repository.util.toSdkParams
import com.x8bit.bitwarden.data.auth.repository.util.toUserState
Expand Down Expand Up @@ -5059,7 +5058,6 @@ class AuthRepositoryTest {
val newPassword = "newPassword"
val newPasswordHash = "newPasswordHash"
val newKey = "newKey"
val kdf = ACCOUNT_1.profile.toKdfRequestModel()
val email = ACCOUNT_1.profile.email
fakeAuthDiskSource.userState = SINGLE_USER_STATE_1
coEvery {
Expand All @@ -5082,13 +5080,11 @@ class AuthRepositoryTest {
.asSuccess()
coEvery {
accountsService.resetPassword(
body = ResetPasswordRequestJson(
body = ResetPasswordRequestJson.V1(
currentPasswordHash = currentPasswordHash,
newPasswordHash = newPasswordHash,
passwordHint = null,
kdf = kdf,
salt = email,
masterPasswordAuthenticationHash = newPasswordHash,
masterKeyWrappedUserKey = newKey,
key = newKey,
),
)
} returns Unit.asSuccess()
Expand Down Expand Up @@ -5123,13 +5119,11 @@ class AuthRepositoryTest {
newPassword = newPassword,
)
accountsService.resetPassword(
body = ResetPasswordRequestJson(
body = ResetPasswordRequestJson.V1(
currentPasswordHash = currentPasswordHash,
newPasswordHash = newPasswordHash,
passwordHint = null,
kdf = kdf,
salt = email,
masterPasswordAuthenticationHash = newPasswordHash,
masterKeyWrappedUserKey = newKey,
key = newKey,
),
)
}
Expand Down Expand Up @@ -5752,13 +5746,16 @@ class AuthRepositoryTest {
passwordHash = passwordHash,
newKey = encryptedUserKey,
)
val setPasswordRequestJson = SetPasswordRequestJson.V2(
val setPasswordRequestJson = SetPasswordRequestJson.V1(
passwordHint = passwordHint,
organizationIdentifier = organizationIdentifier,
kdf = profile.toKdfRequestModel(),
salt = EMAIL,
masterPasswordAuthenticationHash = passwordHash,
masterKeyWrappedUserKey = encryptedUserKey,
kdfType = profile.kdfType,
kdfIterations = profile.kdfIterations,
kdfMemory = profile.kdfMemory,
kdfParallelism = profile.kdfParallelism,
passwordHash = passwordHash,
key = encryptedUserKey,
keys = null,
)
fakeAuthDiskSource.userState = userState
coEvery {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,76 @@ import kotlinx.serialization.Serializable

/**
* Request body for resetting the password.
*
* @property currentPasswordHash The hash of the user's current password.
* @property passwordHint The hint for the master password (nullable).
* @property authenticationData The data to authenticate with a master password.
* @property unlockData The data to unlock with a master password.
*/
@Serializable
data class ResetPasswordRequestJson(
@SerialName("masterPasswordHash")
val currentPasswordHash: String?,

@SerialName("masterPasswordHint")
val passwordHint: String?,

@SerialName("authenticationData")
val authenticationData: MasterPasswordAuthenticationDataJson,

@SerialName("unlockData")
val unlockData: MasterPasswordUnlockDataJson,
) {
constructor(
currentPasswordHash: String?,
passwordHint: String?,
kdf: KdfJson,
salt: String,
masterPasswordAuthenticationHash: String,
masterKeyWrappedUserKey: String,
) : this(
currentPasswordHash = currentPasswordHash,
passwordHint = passwordHint,
authenticationData = MasterPasswordAuthenticationDataJson(
kdf = kdf,
salt = salt,
masterPasswordAuthenticationHash = masterPasswordAuthenticationHash,
),
unlockData = MasterPasswordUnlockDataJson(
kdf = kdf,
salt = salt,
masterKeyWrappedUserKey = masterKeyWrappedUserKey,
),
)
sealed class ResetPasswordRequestJson {
abstract val currentPasswordHash: String?

/**
* Request body for resetting the password using the V1 flow.
*
* @param currentPasswordHash The hash of the user's current password.
* @param newPasswordHash The hash of the user's new password.
* @param passwordHint The hint for the master password (nullable).
* @param key The user key for the request (encrypted).
*/
@Serializable
data class V1(
@SerialName("masterPasswordHash")
override val currentPasswordHash: String?,

@SerialName("newMasterPasswordHash")
val newPasswordHash: String,

@SerialName("masterPasswordHint")
val passwordHint: String?,

@SerialName("key")
val key: String,
) : ResetPasswordRequestJson()

/**
* Request body for resetting the password using the V2 flow.
*
* @property currentPasswordHash The hash of the user's current password.
* @property passwordHint The hint for the master password (nullable).
* @property authenticationData The data to authenticate with a master password.
* @property unlockData The data to unlock with a master password.
*/
@Serializable
data class V2(
@SerialName("masterPasswordHash")
override val currentPasswordHash: String?,

@SerialName("masterPasswordHint")
val passwordHint: String?,

@SerialName("authenticationData")
val authenticationData: MasterPasswordAuthenticationDataJson,

@SerialName("unlockData")
val unlockData: MasterPasswordUnlockDataJson,
) : ResetPasswordRequestJson() {
constructor(
currentPasswordHash: String?,
passwordHint: String?,
kdf: KdfJson,
salt: String,
masterPasswordAuthenticationHash: String,
masterKeyWrappedUserKey: String,
) : this(
currentPasswordHash = currentPasswordHash,
passwordHint = passwordHint,
authenticationData = MasterPasswordAuthenticationDataJson(
kdf = kdf,
salt = salt,
masterPasswordAuthenticationHash = masterPasswordAuthenticationHash,
),
unlockData = MasterPasswordUnlockDataJson(
kdf = kdf,
salt = salt,
masterKeyWrappedUserKey = masterKeyWrappedUserKey,
),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -166,34 +166,27 @@ class AccountsServiceTest : BaseServiceTest() {
}

@Test
fun `resetPassword with empty response is success`() = runTest {
fun `resetPassword with v1 request and empty response is success`() = runTest {
val response = MockResponse().setBody("")
server.enqueue(response)
val result = service.resetPassword(
body = ResetPasswordRequestJson(
body = ResetPasswordRequestJson.V1(
currentPasswordHash = "",
newPasswordHash = "",
passwordHint = null,
kdf = KdfJson(
iterations = 7,
memory = 1,
parallelism = 2,
kdfType = KdfTypeJson.ARGON2_ID,
),
salt = "",
masterPasswordAuthenticationHash = "",
masterKeyWrappedUserKey = "",
key = "",
),
)
assertTrue(result.isSuccess)
}

@Test
fun `resetPassword with empty response and null current password is success`() = runTest {
fun `resetPassword with v2 request and empty response is success`() = runTest {
val response = MockResponse().setBody("")
server.enqueue(response)
val result = service.resetPassword(
body = ResetPasswordRequestJson(
currentPasswordHash = null,
body = ResetPasswordRequestJson.V2(
currentPasswordHash = "",
passwordHint = null,
kdf = KdfJson(
iterations = 7,
Expand All @@ -209,6 +202,45 @@ class AccountsServiceTest : BaseServiceTest() {
assertTrue(result.isSuccess)
}

@Test
fun `resetPassword with v1 request and empty response and null current password is success`() =
runTest {
val response = MockResponse().setBody("")
server.enqueue(response)
val result = service.resetPassword(
body = ResetPasswordRequestJson.V1(
currentPasswordHash = null,
newPasswordHash = "",
passwordHint = null,
key = "",
),
)
assertTrue(result.isSuccess)
}
Comment thread
david-livefront marked this conversation as resolved.

@Test
fun `resetPassword with v2 request and empty response and null current password is success`() =
runTest {
val response = MockResponse().setBody("")
server.enqueue(response)
val result = service.resetPassword(
body = ResetPasswordRequestJson.V2(
currentPasswordHash = null,
passwordHint = null,
kdf = KdfJson(
iterations = 7,
memory = 1,
parallelism = 2,
kdfType = KdfTypeJson.ARGON2_ID,
),
salt = "",
masterPasswordAuthenticationHash = "",
masterKeyWrappedUserKey = "",
),
)
assertTrue(result.isSuccess)
}

@Test
fun `setPassword with v1 request and empty response is success`() = runTest {
val response = MockResponse().setBody("")
Expand Down
Loading