/* * Copyright © 2013 – 2016 Ricki Hirner (bitfire web engineering). * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ package com.etesync.syncadapter.ui import android.accounts.Account import android.app.ProgressDialog import android.content.Context import android.content.Intent import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog import com.etebase.client.Client import com.etesync.syncadapter.AccountSettings import com.etesync.syncadapter.HttpClient import com.etesync.syncadapter.R import com.etesync.journalmanager.Crypto import com.etesync.journalmanager.JournalManager import com.etesync.journalmanager.UserInfoManager import com.etesync.syncadapter.log.Logger import com.etesync.syncadapter.syncadapter.requestSync import com.google.android.material.textfield.TextInputLayout import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.jetbrains.anko.doAsync import org.jetbrains.anko.uiThread import java.util.* open class ChangeEncryptionPasswordActivity : BaseActivity() { protected lateinit var account: Account lateinit var progress: ProgressDialog override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) account = intent.extras!!.getParcelable(EXTRA_ACCOUNT)!! supportActionBar!!.setDisplayHomeAsUpEnabled(true) setContentView(R.layout.change_encryption_password) } fun onCancelClicked(v: View) { finish() } fun changePasswordError(e: Exception) { progress.dismiss() AlertDialog.Builder(this) .setTitle(R.string.wrong_encryption_password) .setIcon(R.drawable.ic_error_dark) .setMessage(e.localizedMessage) .setPositiveButton(android.R.string.ok) { _, _ -> // dismiss }.show() } fun changePasswordDo(old_password: String, new_password: String) { val settings = AccountSettings(this, account) if (settings.isLegacy) { legacyChangePasswordDo(settings, old_password, new_password) return } doAsync { val httpClient = HttpClient.Builder(this@ChangeEncryptionPasswordActivity).setForeground(true).build().okHttpClient try { Logger.log.info("Logging in with old password") val client = Client.create(httpClient, settings.uri?.toString()) val etebase = com.etebase.client.Account.login(client, account.name, old_password) Logger.log.info("Login successful") etebase.changePassword(new_password) settings.etebaseSession = etebase.save(null) uiThread { progress.dismiss() AlertDialog.Builder(this@ChangeEncryptionPasswordActivity) .setTitle(R.string.change_encryption_password_success_title) .setMessage(R.string.change_encryption_password_success_body) .setPositiveButton(android.R.string.ok) { _, _ -> this@ChangeEncryptionPasswordActivity.finish() }.show() requestSync(applicationContext, account) } } catch (e: Exception) { uiThread { changePasswordError(e) } return@doAsync } } } fun legacyChangePasswordDo(settings: AccountSettings, old_password: String, new_password: String) { doAsync { val httpClient = HttpClient.Builder(this@ChangeEncryptionPasswordActivity, settings).setForeground(false).build().okHttpClient Logger.log.info("Started deriving old key") val old_key = Crypto.deriveKey(account.name, old_password) Logger.log.info("Finished deriving old key") var cryptoManager: Crypto.CryptoManager val principal = settings.uri?.toHttpUrlOrNull()!! try { val userInfoManager = UserInfoManager(httpClient, principal) val userInfo = userInfoManager.fetch(account.name)!! Logger.log.info("Fetched userInfo for " + account.name) cryptoManager = Crypto.CryptoManager(userInfo.version!!.toInt(), old_key, "userInfo") userInfo.verify(cryptoManager) Logger.log.info("Started deriving new key") val new_key = Crypto.deriveKey(account.name, new_password) Logger.log.info("Finished deriving new key") val userInfoContent = userInfo.getContent(cryptoManager)!! cryptoManager = Crypto.CryptoManager(userInfo.version!!.toInt(), new_key, "userInfo") userInfo.setContent(cryptoManager, userInfoContent) Logger.log.info("Fetching journal list") val membersToAdd = LinkedList>() val journalManager = JournalManager(httpClient, principal) val journals = journalManager.list() for (journal in journals) { if (journal.owner != account.name) { continue } if (journal.key != null) { // We don't need to handle those cases, as they are already encrypted using pubkey continue } else { cryptoManager = Crypto.CryptoManager(journal.version, old_key, journal.uid!!) } Logger.log.info("Converting journal ${journal.uid}") journal.verify(cryptoManager) membersToAdd.add(Pair(journal, cryptoManager.getEncryptedKey(settings.keyPair!!, userInfo.pubkey!!))) } Logger.log.info("Finished converting account. Uploading changes") userInfoManager.update(userInfo) for ((journal, encryptedKey) in membersToAdd) { if (journal.owner != account.name) { continue } Logger.log.info("Uploading journal ${journal.uid}") val member = JournalManager.Member(account.name, encryptedKey!!) journalManager.addMember(journal, member) } settings.password(new_key) Logger.log.info("Finished uploading changes. Encryption password changed successfully.") uiThread { progress.dismiss() AlertDialog.Builder(this@ChangeEncryptionPasswordActivity) .setTitle(R.string.change_encryption_password_success_title) .setMessage(R.string.change_encryption_password_success_body) .setPositiveButton(android.R.string.ok) { _, _ -> this@ChangeEncryptionPasswordActivity.finish() }.show() requestSync(applicationContext, account) } } catch (e: Exception) { uiThread { changePasswordError(e) } return@doAsync } } } fun changePasswordClicked(v: View) { val old_password_view = findViewById(R.id.encryption_password) val new_password_view = findViewById(R.id.new_encryption_password) var valid = true val old_password = old_password_view.editText?.text.toString() if (old_password.isEmpty()) { old_password_view.error = getString(R.string.login_password_required) valid = false } else { old_password_view.error = null } val new_password = new_password_view.editText?.text.toString() if (new_password.isEmpty()) { new_password_view.error = getString(R.string.login_password_required) valid = false } else { new_password_view.error = null } if (!valid) { return } AlertDialog.Builder(this) .setTitle(R.string.delete_collection_confirm_title) .setMessage(R.string.change_encryption_password_are_you_sure) .setPositiveButton(android.R.string.yes) { _, _ -> changePasswordDo(old_password, new_password) progress = ProgressDialog(this) progress.setTitle(R.string.setting_up_encryption) progress.setMessage(getString(R.string.setting_up_encryption_content)) progress.isIndeterminate = true progress.setCanceledOnTouchOutside(false) progress.setCancelable(false) progress.show() } .setNegativeButton(android.R.string.no) { _, _ -> } .create().show() } companion object { internal val EXTRA_ACCOUNT = "account" fun newIntent(context: Context, account: Account): Intent { val intent = Intent(context, ChangeEncryptionPasswordActivity::class.java) intent.putExtra(CreateCollectionActivity.EXTRA_ACCOUNT, account) return intent } } }