mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
BIT-765: Add additional properties to GetTokenResponseJson.Success (#136)
This commit is contained in:
parent
1d2f23d426
commit
ab2a500607
7 changed files with 250 additions and 5 deletions
|
@ -10,12 +10,65 @@ sealed class GetTokenResponseJson {
|
|||
/**
|
||||
* Models json response of the get token request.
|
||||
*
|
||||
* @param accessToken the access token.
|
||||
* @property accessToken The user's access token.
|
||||
* @property refreshToken The user's refresh token.
|
||||
* @property tokenType The type of token (ex: "Bearer").
|
||||
* @property expiresInSeconds The amount of time (in seconds) before the [accessToken] expires.
|
||||
* @property key The user's key.
|
||||
* @property privateKey The user's private key.
|
||||
* @property kdfType The KDF type.
|
||||
* @property kdfIterations The number of iterations when calculating a user's password.
|
||||
* @property kdfMemory The amount of memory to use when calculating a password hash (MB).
|
||||
* @property kdfParallelism The number of threads to use when calculating a password hash.
|
||||
* @property shouldForcePasswordReset Whether or not the app must force a password reset.
|
||||
* @property shouldResetMasterPassword Whether or not the user is required to reset their
|
||||
* master password.
|
||||
* @property masterPasswordPolicyOptions The options available for a user's master password.
|
||||
* @property userDecryptionOptions The options available to a user for decryption.
|
||||
*/
|
||||
@Serializable
|
||||
data class Success(
|
||||
@SerialName("access_token")
|
||||
val accessToken: String,
|
||||
|
||||
@SerialName("refresh_token")
|
||||
val refreshToken: String,
|
||||
|
||||
@SerialName("token_type")
|
||||
val tokenType: String,
|
||||
|
||||
@SerialName("expires_in")
|
||||
val expiresInSeconds: Int,
|
||||
|
||||
@SerialName("Key")
|
||||
val key: String,
|
||||
|
||||
@SerialName("PrivateKey")
|
||||
val privateKey: String,
|
||||
|
||||
@SerialName("Kdf")
|
||||
val kdfType: KdfTypeJson,
|
||||
|
||||
@SerialName("KdfIterations")
|
||||
val kdfIterations: Int?,
|
||||
|
||||
@SerialName("KdfMemory")
|
||||
val kdfMemory: Int?,
|
||||
|
||||
@SerialName("KdfParallelism")
|
||||
val kdfParallelism: Int?,
|
||||
|
||||
@SerialName("ForcePasswordReset")
|
||||
val shouldForcePasswordReset: Boolean,
|
||||
|
||||
@SerialName("ResetMasterPassword")
|
||||
val shouldResetMasterPassword: Boolean,
|
||||
|
||||
@SerialName("MasterPasswordPolicy")
|
||||
val masterPasswordPolicyOptions: MasterPasswordPolicyOptionsJson?,
|
||||
|
||||
@SerialName("UserDecryptionOptions")
|
||||
val userDecryptionOptions: UserDecryptionOptionsJson?,
|
||||
) : GetTokenResponseJson()
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package com.x8bit.bitwarden.data.auth.datasource.network.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Decryption options related to a user's key connector.
|
||||
*
|
||||
* @property keyConnectorUrl URL to the user's key connector.
|
||||
*/
|
||||
@Serializable
|
||||
data class KeyConnectorUserDecryptionOptionsJson(
|
||||
@SerialName("KeyConnectorUrl")
|
||||
val keyConnectorUrl: String,
|
||||
)
|
|
@ -0,0 +1,39 @@
|
|||
package com.x8bit.bitwarden.data.auth.datasource.network.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Policies that may be applied to a master password.
|
||||
*
|
||||
* @property minimumComplexity The minimum required password complexity (if applicable).
|
||||
* @property minimumLength The minimum required password length (if applicable).
|
||||
* @property shouldRequireUppercase Whether or not uppercase characters should be required.
|
||||
* @property shouldRequireLowercase Whether or not lowercase characters should be required.
|
||||
* @property shouldRequireNumbers Whether or not numbers should be required.
|
||||
* @property shouldRequireSpecialCharacters Whether or not special characters should be required.
|
||||
* @property shouldEnforceOnLogin Whether or not the restrictions should be enforced on login.
|
||||
*/
|
||||
@Serializable
|
||||
data class MasterPasswordPolicyOptionsJson(
|
||||
@SerialName("MinComplexity")
|
||||
val minimumComplexity: Int?,
|
||||
|
||||
@SerialName("MinLength")
|
||||
val minimumLength: Int?,
|
||||
|
||||
@SerialName("RequireUpper")
|
||||
val shouldRequireUppercase: Boolean?,
|
||||
|
||||
@SerialName("RequireLower")
|
||||
val shouldRequireLowercase: Boolean?,
|
||||
|
||||
@SerialName("RequireNumbers")
|
||||
val shouldRequireNumbers: Boolean?,
|
||||
|
||||
@SerialName("RequireSpecial")
|
||||
val shouldRequireSpecialCharacters: Boolean?,
|
||||
|
||||
@SerialName("EnforceOnLogin")
|
||||
val shouldEnforceOnLogin: Boolean?,
|
||||
)
|
|
@ -0,0 +1,32 @@
|
|||
package com.x8bit.bitwarden.data.auth.datasource.network.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Decryption options related to a user's trusted device.
|
||||
*
|
||||
* @property encryptedPrivateKey The user's encrypted private key.
|
||||
* @property encryptedUserKey The user's encrypted key.
|
||||
* @property hasAdminApproval Whether or not the user has admin approval.
|
||||
* @property hasLoginApprovingDevice Whether or not the user has a login approving device.
|
||||
* @property hasManageResetPasswordPermission Whether or not the user has manage reset password
|
||||
* permission.
|
||||
*/
|
||||
@Serializable
|
||||
data class TrustedDeviceUserDecryptionOptionsJson(
|
||||
@SerialName("EncryptedPrivateKey")
|
||||
val encryptedPrivateKey: String?,
|
||||
|
||||
@SerialName("EncryptedUserKey")
|
||||
val encryptedUserKey: String?,
|
||||
|
||||
@SerialName("HasAdminApproval")
|
||||
val hasAdminApproval: Boolean,
|
||||
|
||||
@SerialName("HasLoginApprovingDevice")
|
||||
val hasLoginApprovingDevice: Boolean,
|
||||
|
||||
@SerialName("HasManageResetPasswordPermission")
|
||||
val hasManageResetPasswordPermission: Boolean,
|
||||
)
|
|
@ -0,0 +1,25 @@
|
|||
package com.x8bit.bitwarden.data.auth.datasource.network.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* The options available to a user for decryption.
|
||||
*
|
||||
* @property hasMasterPassword Whether the current user has a master password that can be used to
|
||||
* decrypt their vault.
|
||||
* @property trustedDeviceUserDecryptionOptions Decryption options related to a user's trusted
|
||||
* device.
|
||||
* @property keyConnectorUserDecryptionOptions Decryption options related to a user's key connector.
|
||||
*/
|
||||
@Serializable
|
||||
data class UserDecryptionOptionsJson(
|
||||
@SerialName("HasMasterPassword")
|
||||
val hasMasterPassword: Boolean,
|
||||
|
||||
@SerialName("TrustedDeviceOption")
|
||||
val trustedDeviceUserDecryptionOptions: TrustedDeviceUserDecryptionOptionsJson?,
|
||||
|
||||
@SerialName("KeyConnectorOption")
|
||||
val keyConnectorUserDecryptionOptions: KeyConnectorUserDecryptionOptionsJson?,
|
||||
)
|
|
@ -2,6 +2,11 @@ package com.x8bit.bitwarden.data.auth.datasource.network.service
|
|||
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.api.IdentityApi
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.KdfTypeJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.KeyConnectorUserDecryptionOptionsJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.MasterPasswordPolicyOptionsJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.TrustedDeviceUserDecryptionOptionsJson
|
||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson
|
||||
import com.x8bit.bitwarden.data.platform.base.BaseServiceTest
|
||||
import com.x8bit.bitwarden.data.platform.util.DeviceModelProvider
|
||||
import io.mockk.every
|
||||
|
@ -87,10 +92,79 @@ private val CAPTCHA_BODY = GetTokenResponseJson.CaptchaRequired("123")
|
|||
|
||||
private const val LOGIN_SUCCESS_JSON = """
|
||||
{
|
||||
"access_token": "123"
|
||||
"access_token": "accessToken",
|
||||
"expires_in": 3600,
|
||||
"token_type": "Bearer",
|
||||
"refresh_token": "refreshToken",
|
||||
"PrivateKey": "privateKey",
|
||||
"Key": "key",
|
||||
"MasterPasswordPolicy": {
|
||||
"MinComplexity": 10,
|
||||
"MinLength": 100,
|
||||
"RequireUpper": true,
|
||||
"RequireLower": true,
|
||||
"RequireNumbers": true,
|
||||
"RequireSpecial": true,
|
||||
"EnforceOnLogin": true
|
||||
},
|
||||
"ForcePasswordReset": true,
|
||||
"ResetMasterPassword": true,
|
||||
"Kdf": 1,
|
||||
"KdfIterations": 600000,
|
||||
"KdfMemory": 16,
|
||||
"KdfParallelism": 4,
|
||||
"UserDecryptionOptions": {
|
||||
"HasMasterPassword": true,
|
||||
"TrustedDeviceOption": {
|
||||
"EncryptedPrivateKey": "encryptedPrivateKey",
|
||||
"EncryptedUserKey": "encryptedUserKey",
|
||||
"HasAdminApproval": true,
|
||||
"HasLoginApprovingDevice": true,
|
||||
"HasManageResetPasswordPermission": true
|
||||
},
|
||||
"KeyConnectorOption": {
|
||||
"KeyConnectorUrl": "keyConnectorUrl"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
private val LOGIN_SUCCESS = GetTokenResponseJson.Success("123")
|
||||
|
||||
private val LOGIN_SUCCESS = GetTokenResponseJson.Success(
|
||||
accessToken = "accessToken",
|
||||
refreshToken = "refreshToken",
|
||||
tokenType = "Bearer",
|
||||
expiresInSeconds = 3600,
|
||||
key = "key",
|
||||
kdfType = KdfTypeJson.ARGON2_ID,
|
||||
kdfIterations = 600000,
|
||||
kdfMemory = 16,
|
||||
kdfParallelism = 4,
|
||||
privateKey = "privateKey",
|
||||
shouldForcePasswordReset = true,
|
||||
shouldResetMasterPassword = true,
|
||||
masterPasswordPolicyOptions = MasterPasswordPolicyOptionsJson(
|
||||
minimumComplexity = 10,
|
||||
minimumLength = 100,
|
||||
shouldRequireUppercase = true,
|
||||
shouldRequireLowercase = true,
|
||||
shouldRequireNumbers = true,
|
||||
shouldRequireSpecialCharacters = true,
|
||||
shouldEnforceOnLogin = true,
|
||||
),
|
||||
userDecryptionOptions = UserDecryptionOptionsJson(
|
||||
hasMasterPassword = true,
|
||||
trustedDeviceUserDecryptionOptions = TrustedDeviceUserDecryptionOptionsJson(
|
||||
encryptedPrivateKey = "encryptedPrivateKey",
|
||||
encryptedUserKey = "encryptedUserKey",
|
||||
hasAdminApproval = true,
|
||||
hasLoginApprovingDevice = true,
|
||||
hasManageResetPasswordPermission = true,
|
||||
),
|
||||
keyConnectorUserDecryptionOptions = KeyConnectorUserDecryptionOptionsJson(
|
||||
keyConnectorUrl = "keyConnectorUrl",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
private const val INVALID_LOGIN_JSON = """
|
||||
{
|
||||
|
|
|
@ -141,6 +141,9 @@ class AuthRepositoryTest {
|
|||
|
||||
@Test
|
||||
fun `login get token succeeds should return Success and update AuthState`() = runTest {
|
||||
val successResponse = mockk<GetTokenResponseJson.Success> {
|
||||
every { accessToken } returns ACCESS_TOKEN
|
||||
}
|
||||
coEvery {
|
||||
accountsService.preLogin(email = EMAIL)
|
||||
} returns Result.success(PRE_LOGIN_SUCCESS)
|
||||
|
@ -151,7 +154,7 @@ class AuthRepositoryTest {
|
|||
captchaToken = null,
|
||||
)
|
||||
}
|
||||
.returns(Result.success(GetTokenResponseJson.Success(accessToken = ACCESS_TOKEN)))
|
||||
.returns(Result.success(successResponse))
|
||||
every { authInterceptor.authToken = ACCESS_TOKEN } returns Unit
|
||||
val result = repository.login(email = EMAIL, password = PASSWORD, captchaToken = null)
|
||||
assertEquals(LoginResult.Success, result)
|
||||
|
@ -205,6 +208,9 @@ class AuthRepositoryTest {
|
|||
@Test
|
||||
fun `logout should change AuthState to be Unauthenticated`() = runTest {
|
||||
// First login:
|
||||
val successResponse = mockk<GetTokenResponseJson.Success> {
|
||||
every { accessToken } returns ACCESS_TOKEN
|
||||
}
|
||||
coEvery {
|
||||
accountsService.preLogin(email = EMAIL)
|
||||
} returns Result.success(PRE_LOGIN_SUCCESS)
|
||||
|
@ -215,7 +221,8 @@ class AuthRepositoryTest {
|
|||
captchaToken = null,
|
||||
)
|
||||
}
|
||||
.returns(Result.success(GetTokenResponseJson.Success(accessToken = ACCESS_TOKEN)))
|
||||
.returns(Result.success(successResponse))
|
||||
|
||||
every { authInterceptor.authToken = ACCESS_TOKEN } returns Unit
|
||||
repository.login(email = EMAIL, password = PASSWORD, captchaToken = null)
|
||||
|
||||
|
|
Loading…
Reference in a new issue