Skip to content

Commit

Permalink
Merge pull request #552 from jorgeblacio/recovery_code
Browse files Browse the repository at this point in the history
Added recovery code functionality
  • Loading branch information
danieltigse authored Oct 24, 2019
2 parents 5aae486 + 44ee8e5 commit 3b43635
Show file tree
Hide file tree
Showing 23 changed files with 402 additions and 22 deletions.
2 changes: 0 additions & 2 deletions src/main/kotlin/com/criptext/mail/db/ComposerLocalDB.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ class ComposerLocalDB(val contactDao: ContactDao, val emailDao: EmailDao, val fi
val labels = emailLabelDao.getLabelsFromEmail(id)
val contactsCC = emailContactDao.getContactsFromEmail(id, ContactTypes.CC)
val contactsBCC = emailContactDao.getContactsFromEmail(id, ContactTypes.BCC)
val contactsFROM = emailContactDao.getContactsFromEmail(id, ContactTypes.FROM)
val contactsTO = emailContactDao.getContactsFromEmail(id, ContactTypes.TO)
val files = fileDao.getAttachmentsFromEmail(id)
val fileKey = fileKeyDao.getAttachmentKeyFromEmail(id)
Expand Down Expand Up @@ -65,7 +64,6 @@ class ComposerLocalDB(val contactDao: ContactDao, val emailDao: EmailDao, val fi
labelDao.get(selectedLabel, activeAccount.id).id) else -1
val contactsCC = emailContactDao.getContactsFromEmail(id, ContactTypes.CC)
val contactsBCC = emailContactDao.getContactsFromEmail(id, ContactTypes.BCC)
val contactsFROM = emailContactDao.getContactsFromEmail(id, ContactTypes.FROM)
val contactsTO = emailContactDao.getContactsFromEmail(id, ContactTypes.TO)
val files = fileDao.getAttachmentsFromEmail(id)
val fileKey: FileKey? = fileKeyDao.getAttachmentKeyFromEmail(id)
Expand Down
1 change: 0 additions & 1 deletion src/main/kotlin/com/criptext/mail/db/EventLocalDB.kt
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,6 @@ class EventLocalDB(private val db: AppDatabase, private val filesDir: File, priv
val labels = db.emailLabelDao().getLabelsFromEmail(id)
val contactsCC = db.emailContactDao().getContactsFromEmail(id, ContactTypes.CC)
val contactsBCC = db.emailContactDao().getContactsFromEmail(id, ContactTypes.BCC)
val contactsFROM = db.emailContactDao().getContactsFromEmail(id, ContactTypes.FROM)
val contactsTO = db.emailContactDao().getContactsFromEmail(id, ContactTypes.TO)
val files = db.fileDao().getAttachmentsFromEmail(id)
val fileKey = db.fileKeyDao().getAttachmentKeyFromEmail(id)
Expand Down
1 change: 0 additions & 1 deletion src/main/kotlin/com/criptext/mail/db/MailboxLocalDB.kt
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,6 @@ interface MailboxLocalDB {
db.labelDao().get(selectedLabel, activeAccount.id).id) else -1
val contactsCC = db.emailContactDao().getContactsFromEmail(id, ContactTypes.CC)
val contactsBCC = db.emailContactDao().getContactsFromEmail(id, ContactTypes.BCC)
val contactsFROM = db.emailContactDao().getContactsFromEmail(id, ContactTypes.FROM)
val contactsTO = db.emailContactDao().getContactsFromEmail(id, ContactTypes.TO)
val files = db.fileDao().getAttachmentsFromEmail(id)
val fileKey: FileKey? = db.fileKeyDao().getAttachmentKeyFromEmail(id)
Expand Down
1 change: 0 additions & 1 deletion src/main/kotlin/com/criptext/mail/db/SearchLocalDB.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ interface SearchLocalDB{
val labels = db.emailLabelDao().getLabelsFromEmail(id)
val contactsCC = db.emailContactDao().getContactsFromEmail(id, ContactTypes.CC)
val contactsBCC = db.emailContactDao().getContactsFromEmail(id, ContactTypes.BCC)
val contactsFROM = db.emailContactDao().getContactsFromEmail(id, ContactTypes.FROM)
val contactsTO = db.emailContactDao().getContactsFromEmail(id, ContactTypes.TO)
val files = db.fileDao().getAttachmentsFromEmail(id)
val fileKey = db.fileKeyDao().getAttachmentKeyFromEmail(id)
Expand Down
31 changes: 31 additions & 0 deletions src/main/kotlin/com/criptext/mail/scenes/signin/SignInScene.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.criptext.mail.scenes.signup.holders.KeyGenerationHolder
import com.criptext.mail.utils.UIMessage
import com.criptext.mail.utils.getLocalizedUIMessage
import com.criptext.mail.utils.ui.ForgotPasswordDialog
import com.criptext.mail.utils.ui.GeneralDialogWithInput
import com.criptext.mail.utils.ui.GeneralMessageOkDialog
import com.criptext.mail.utils.ui.RetrySyncAlertDialogNewDevice
import com.criptext.mail.utils.ui.data.DialogData
Expand Down Expand Up @@ -45,6 +46,10 @@ interface SignInScene {
fun showDeviceCountRemaining(remaining: Int)
fun showDeviceRemovalError()
fun showToolbarCount(checked: Int)
fun showRecoveryCode()
fun showRecoveryDialogError(message: UIMessage?)
fun toggleLoadRecoveryCode(load: Boolean)
fun dismissRecoveryCodeDialog()

var signInUIObserver: SignInSceneController.SignInUIObserver?

Expand All @@ -71,6 +76,14 @@ interface SignInScene {
)
)

private val recoveryCodeDialog = GeneralDialogWithInput(view.context,
DialogData.DialogDataForRecoveryCode(
title = UIMessage(R.string.recovery_code_dialog_title),
message = UIMessage(R.string.recovery_code_dialog_message),
type = DialogType.RecoveryCode()
)
)

override var signInUIObserver: SignInSceneController.SignInUIObserver? = null
set(value) {
holder.uiObserver = value
Expand Down Expand Up @@ -254,6 +267,24 @@ interface SignInScene {
currentHolder.setToolbarCount(checked)
}

override fun showRecoveryCode() {
recoveryCodeDialog.showDialog(signInUIObserver)
recoveryCodeDialog.editTextEmail.setHint(R.string.recovery_code_dialog_hint)
recoveryCodeDialog.editTextEmailLayout.hint = view.context.getLocalizedUIMessage(UIMessage(R.string.recovery_code_dialog_hint))
}

override fun showRecoveryDialogError(message: UIMessage?) {
recoveryCodeDialog.setEmailError(message)
}

override fun toggleLoadRecoveryCode(load: Boolean) {
recoveryCodeDialog.toggleLoad(load)
}

override fun dismissRecoveryCodeDialog() {
recoveryCodeDialog.dismiss()
}

override fun showKeyGenerationHolder() {
viewGroup.removeAllViews()
val keyGenerationLayout = View.inflate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import com.criptext.mail.utils.*
import com.criptext.mail.utils.generaldatasource.data.GeneralDataSource
import com.criptext.mail.utils.generaldatasource.data.GeneralRequest
import com.criptext.mail.utils.generaldatasource.data.GeneralResult
import com.criptext.mail.utils.ui.data.DialogResult
import com.criptext.mail.utils.ui.data.DialogType
import com.criptext.mail.utils.uiobserver.UIObserver
import com.criptext.mail.utils.virtuallist.VirtualListView
import com.criptext.mail.validation.AccountDataValidator
import com.criptext.mail.validation.FormData
Expand Down Expand Up @@ -67,6 +70,7 @@ class SignInSceneController(
is SignInResult.LinkStatus -> onLinkStatus(result)
is SignInResult.FindDevices -> onFindDevices(result)
is SignInResult.RemoveDevices -> onRemoveDevices(result)
is SignInResult.RecoveryCode -> onGenerateRecoveryCode(result)
}
}

Expand Down Expand Up @@ -391,6 +395,27 @@ class SignInSceneController(
}
}

private fun onGenerateRecoveryCode(result: SignInResult.RecoveryCode){
when(result){
is SignInResult.RecoveryCode.Success -> {
if(result.isValidate) {
scene.dismissRecoveryCodeDialog()
scene.showKeyGenerationHolder()
} else {
scene.showRecoveryCode()
}
}
is SignInResult.RecoveryCode.Failure -> {
if(result.isValidate){
scene.toggleLoadRecoveryCode(false)
scene.showRecoveryDialogError(result.message)
} else {
scene.showError(result.message)
}
}
}
}

private fun onUserAuthenticated(result: SignInResult.AuthenticateUser) {
when (result) {
is SignInResult.AuthenticateUser.Success -> {
Expand Down Expand Up @@ -622,6 +647,51 @@ class SignInSceneController(
}

private val uiObserver = object : SignInUIObserver {
override fun onRecoveryCodeChangeListener(newPassword: String) {

}

override fun onGeneralOkButtonPressed(result: DialogResult) {
if(result is DialogResult.DialogWithInput && result.type is DialogType.RecoveryCode){
scene.toggleLoadRecoveryCode(true)
val currentState = model.state as SignInLayoutState.LoginValidation
dataSource.submitRequest(SignInRequest.RecoveryCode(currentState.username, currentState.domain, model.ephemeralJwt, model.isMultiple, result.textInput))
}
}

override fun onOkButtonPressed(password: String) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}

override fun onCancelButtonPressed() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}

override fun onLinkAuthConfirmed(untrustedDeviceInfo: DeviceInfo.UntrustedDeviceInfo) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}

override fun onLinkAuthDenied(untrustedDeviceInfo: DeviceInfo.UntrustedDeviceInfo) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}

override fun onSnackbarClicked() {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}

override fun onSyncAuthConfirmed(trustedDeviceInfo: DeviceInfo.TrustedDeviceInfo) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}

override fun onSyncAuthDenied(trustedDeviceInfo: DeviceInfo.TrustedDeviceInfo) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}

override fun onRecoveryCodeClicked() {
val currentState = model.state as SignInLayoutState.LoginValidation
dataSource.submitRequest(SignInRequest.RecoveryCode(currentState.username, currentState.domain, model.ephemeralJwt, model.isMultiple))
}

override fun onTrashPressed(recipient: String, domain: String) {
val checkedIndexes = Pair(mutableListOf<Int>(), mutableListOf<Int>())
model.devices.forEachIndexed { index, deviceItem ->
Expand Down Expand Up @@ -921,14 +991,16 @@ class SignInSceneController(
override fun requestPermissionResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
}

interface SignInUIObserver {
interface SignInUIObserver: UIObserver {
fun onSubmitButtonClicked()
fun toggleUsernameFocusState(isFocused: Boolean)
fun onSignUpLabelClicked()
fun userLoginReady()
fun onCantAccessDeviceClick()
fun onRecoveryCodeClicked()
fun onResendDeviceLinkAuth(username: String, domain: String)
fun onPasswordChangeListener(newPassword: String)
fun onRecoveryCodeChangeListener(newPassword: String)
fun onConfirmPasswordChangeListener(confirmPassword: String)
fun onUsernameTextChanged(newUsername: String)
fun onForgotPasswordClick()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@ class SignInDataSource(override val runner: WorkRunner,
flushResults(result)
}
)
is SignInRequest.RecoveryCode -> RecoveryCodeWorker(
httpClient = httpClient, jwt = params.tempToken,
db = db,
code = params.code,
recipientId = params.recipientId,
domain = params.domain,
signUpDao = signUpDao,
keyGenerator = keyGenerator,
keyValueStorage = keyValueStorage,
accountDao = accountDao,
isMultiple = params.isMultiple,
messagingInstance = MessagingInstance.Default(),
publishFn = { result ->
flushResults(result)
}
)
is SignInRequest.LinkBegin -> LinkBeginWorker(
httpClient = httpClient, username = params.username,
domain = params.domain,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ sealed class SignInRequest{

data class ForgotPassword(val username: String, val domain: String): SignInRequest()

data class RecoveryCode(val recipientId: String, val domain: String, val tempToken: String, val isMultiple: Boolean, val code: String? = null): SignInRequest()

data class LinkBegin(val username: String, val domain: String): SignInRequest()

data class LinkAuth(val username: String, val ephemeralJwt: String, val domain: String, val password: String? = null): SignInRequest()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ sealed class SignInResult {
val exception: Exception): ForgotPassword()
}

sealed class RecoveryCode: SignInResult() {
data class Success(val isValidate: Boolean): RecoveryCode()
data class Failure(val isValidate: Boolean, val message: UIMessage,
val exception: Exception): RecoveryCode()
}

sealed class LinkBegin: SignInResult() {
data class Success(val ephemeralJwt: String, val hasTwoFA: Boolean): LinkBegin()
data class NoDevicesAvailable(val message: UIMessage): LinkBegin()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class LoginValidationHolder(
private var animLoading: AnimatorSet? = null
private val rootLayout: View
private val cantAccessDevice: TextView
private val recoveryCodeText: TextView
private val textViewTitle: TextView
private val textViewBody: TextView
private val textViewRejected: TextView
Expand All @@ -40,6 +41,7 @@ class LoginValidationHolder(
init {
rootLayout = view.findViewById<View>(R.id.viewRoot)
cantAccessDevice = view.findViewById(R.id.cant_access_device)
recoveryCodeText = view.findViewById(R.id.recovery_code)
textViewTitle = view.findViewById(R.id.textViewTitle)
textViewRejected = view.findViewById(R.id.device_rejected)
textViewBody = view.findViewById(R.id.textViewBody)
Expand All @@ -53,6 +55,7 @@ class LoginValidationHolder(
if(initialState.hasTwoFA){
cantAccessDevice.visibility = View.GONE
textViewTitle.text = view.context.getText(R.string.title_two_fa)
recoveryCodeText.visibility = View.VISIBLE
}

setListeners()
Expand Down Expand Up @@ -145,6 +148,10 @@ class LoginValidationHolder(
uiObserver?.onCantAccessDeviceClick()
}

recoveryCodeText.setOnClickListener {
uiObserver?.onRecoveryCodeClicked()
}

buttonResend.setOnClickListener {
uiObserver?.onResendDeviceLinkAuth(initialState.username, initialState.domain)
setEnableButtons(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class CheckUsernameAvailabilityWorker(val httpClient: HttpClient,
ServerCodes.Gone -> SignInResult.CheckUsernameAvailability.Failure(UIMessage(R.string.username_not_available))
ServerCodes.EnterpriseAccountSuspended ->
SignInResult.CheckUsernameAvailability.Failure(UIMessage(R.string.account_suspended_sign_in_error))
ServerCodes.BadRequest ->
SignInResult.CheckUsernameAvailability.Failure(UIMessage(R.string.username_invalid_error))
else -> SignInResult.CheckUsernameAvailability.Failure(UIMessage(R.string.server_bad_status, arrayOf(ex.errorCode)))
}
} else {
Expand Down
Loading

0 comments on commit 3b43635

Please sign in to comment.