mirror of
https://github.com/bitwarden/android.git
synced 2025-02-17 20:40:00 +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.
|
* 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
|
@Serializable
|
||||||
data class Success(
|
data class Success(
|
||||||
@SerialName("access_token")
|
@SerialName("access_token")
|
||||||
val accessToken: String,
|
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()
|
) : 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.api.IdentityApi
|
||||||
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson
|
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.base.BaseServiceTest
|
||||||
import com.x8bit.bitwarden.data.platform.util.DeviceModelProvider
|
import com.x8bit.bitwarden.data.platform.util.DeviceModelProvider
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
|
@ -87,10 +92,79 @@ private val CAPTCHA_BODY = GetTokenResponseJson.CaptchaRequired("123")
|
||||||
|
|
||||||
private const val LOGIN_SUCCESS_JSON = """
|
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 = """
|
private const val INVALID_LOGIN_JSON = """
|
||||||
{
|
{
|
||||||
|
|
|
@ -141,6 +141,9 @@ class AuthRepositoryTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `login get token succeeds should return Success and update AuthState`() = runTest {
|
fun `login get token succeeds should return Success and update AuthState`() = runTest {
|
||||||
|
val successResponse = mockk<GetTokenResponseJson.Success> {
|
||||||
|
every { accessToken } returns ACCESS_TOKEN
|
||||||
|
}
|
||||||
coEvery {
|
coEvery {
|
||||||
accountsService.preLogin(email = EMAIL)
|
accountsService.preLogin(email = EMAIL)
|
||||||
} returns Result.success(PRE_LOGIN_SUCCESS)
|
} returns Result.success(PRE_LOGIN_SUCCESS)
|
||||||
|
@ -151,7 +154,7 @@ class AuthRepositoryTest {
|
||||||
captchaToken = null,
|
captchaToken = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.returns(Result.success(GetTokenResponseJson.Success(accessToken = ACCESS_TOKEN)))
|
.returns(Result.success(successResponse))
|
||||||
every { authInterceptor.authToken = ACCESS_TOKEN } returns Unit
|
every { authInterceptor.authToken = ACCESS_TOKEN } returns Unit
|
||||||
val result = repository.login(email = EMAIL, password = PASSWORD, captchaToken = null)
|
val result = repository.login(email = EMAIL, password = PASSWORD, captchaToken = null)
|
||||||
assertEquals(LoginResult.Success, result)
|
assertEquals(LoginResult.Success, result)
|
||||||
|
@ -205,6 +208,9 @@ class AuthRepositoryTest {
|
||||||
@Test
|
@Test
|
||||||
fun `logout should change AuthState to be Unauthenticated`() = runTest {
|
fun `logout should change AuthState to be Unauthenticated`() = runTest {
|
||||||
// First login:
|
// First login:
|
||||||
|
val successResponse = mockk<GetTokenResponseJson.Success> {
|
||||||
|
every { accessToken } returns ACCESS_TOKEN
|
||||||
|
}
|
||||||
coEvery {
|
coEvery {
|
||||||
accountsService.preLogin(email = EMAIL)
|
accountsService.preLogin(email = EMAIL)
|
||||||
} returns Result.success(PRE_LOGIN_SUCCESS)
|
} returns Result.success(PRE_LOGIN_SUCCESS)
|
||||||
|
@ -215,7 +221,8 @@ class AuthRepositoryTest {
|
||||||
captchaToken = null,
|
captchaToken = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.returns(Result.success(GetTokenResponseJson.Success(accessToken = ACCESS_TOKEN)))
|
.returns(Result.success(successResponse))
|
||||||
|
|
||||||
every { authInterceptor.authToken = ACCESS_TOKEN } returns Unit
|
every { authInterceptor.authToken = ACCESS_TOKEN } returns Unit
|
||||||
repository.login(email = EMAIL, password = PASSWORD, captchaToken = null)
|
repository.login(email = EMAIL, password = PASSWORD, captchaToken = null)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue