1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/fxa/activities/FxAccountUpdateCredentialsActivity.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,190 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +package org.mozilla.gecko.fxa.activities; 1.9 + 1.10 +import java.util.concurrent.Executor; 1.11 +import java.util.concurrent.Executors; 1.12 + 1.13 +import org.mozilla.gecko.R; 1.14 +import org.mozilla.gecko.background.common.log.Logger; 1.15 +import org.mozilla.gecko.background.fxa.FxAccountClient; 1.16 +import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate; 1.17 +import org.mozilla.gecko.background.fxa.FxAccountClient20; 1.18 +import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse; 1.19 +import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException; 1.20 +import org.mozilla.gecko.background.fxa.FxAccountUtils; 1.21 +import org.mozilla.gecko.background.fxa.PasswordStretcher; 1.22 +import org.mozilla.gecko.fxa.FirefoxAccounts; 1.23 +import org.mozilla.gecko.fxa.FxAccountConstants; 1.24 +import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.FxAccountSignInTask; 1.25 +import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; 1.26 +import org.mozilla.gecko.fxa.login.Engaged; 1.27 +import org.mozilla.gecko.fxa.login.State; 1.28 +import org.mozilla.gecko.fxa.login.State.StateLabel; 1.29 +import org.mozilla.gecko.sync.setup.activities.ActivityUtils; 1.30 + 1.31 +import android.os.Bundle; 1.32 +import android.view.View; 1.33 +import android.view.View.OnClickListener; 1.34 +import android.widget.AutoCompleteTextView; 1.35 +import android.widget.Button; 1.36 +import android.widget.EditText; 1.37 +import android.widget.ProgressBar; 1.38 +import android.widget.TextView; 1.39 + 1.40 +/** 1.41 + * Activity which displays a screen for updating the local password. 1.42 + */ 1.43 +public class FxAccountUpdateCredentialsActivity extends FxAccountAbstractSetupActivity { 1.44 + protected static final String LOG_TAG = FxAccountUpdateCredentialsActivity.class.getSimpleName(); 1.45 + 1.46 + protected AndroidFxAccount fxAccount; 1.47 + 1.48 + public FxAccountUpdateCredentialsActivity() { 1.49 + // We want to share code with the other setup activities, but this activity 1.50 + // doesn't create a new Android Account, it modifies an existing one. If you 1.51 + // manage to get an account, and somehow be locked out too, we'll let you 1.52 + // update it. 1.53 + super(CANNOT_RESUME_WHEN_NO_ACCOUNTS_EXIST); 1.54 + } 1.55 + 1.56 + /** 1.57 + * {@inheritDoc} 1.58 + */ 1.59 + @Override 1.60 + public void onCreate(Bundle icicle) { 1.61 + Logger.debug(LOG_TAG, "onCreate(" + icicle + ")"); 1.62 + 1.63 + super.onCreate(icicle); 1.64 + setContentView(R.layout.fxaccount_update_credentials); 1.65 + 1.66 + emailEdit = (AutoCompleteTextView) ensureFindViewById(null, R.id.email, "email edit"); 1.67 + passwordEdit = (EditText) ensureFindViewById(null, R.id.password, "password edit"); 1.68 + showPasswordButton = (Button) ensureFindViewById(null, R.id.show_password, "show password button"); 1.69 + remoteErrorTextView = (TextView) ensureFindViewById(null, R.id.remote_error, "remote error text view"); 1.70 + button = (Button) ensureFindViewById(null, R.id.button, "update credentials"); 1.71 + progressBar = (ProgressBar) ensureFindViewById(null, R.id.progress, "progress bar"); 1.72 + 1.73 + minimumPasswordLength = 1; // Minimal restriction on passwords entered to sign in. 1.74 + createButton(); 1.75 + addListeners(); 1.76 + updateButtonState(); 1.77 + createShowPasswordButton(); 1.78 + 1.79 + emailEdit.setEnabled(false); 1.80 + 1.81 + TextView view = (TextView) findViewById(R.id.forgot_password_link); 1.82 + ActivityUtils.linkTextView(view, R.string.fxaccount_sign_in_forgot_password, R.string.fxaccount_link_forgot_password); 1.83 + } 1.84 + 1.85 + @Override 1.86 + public void onResume() { 1.87 + super.onResume(); 1.88 + this.fxAccount = getAndroidFxAccount(); 1.89 + if (fxAccount == null) { 1.90 + Logger.warn(LOG_TAG, "Could not get Firefox Account."); 1.91 + setResult(RESULT_CANCELED); 1.92 + finish(); 1.93 + return; 1.94 + } 1.95 + State state = fxAccount.getState(); 1.96 + if (state.getStateLabel() != StateLabel.Separated) { 1.97 + Logger.warn(LOG_TAG, "Cannot update credentials from Firefox Account in state: " + state.getStateLabel()); 1.98 + setResult(RESULT_CANCELED); 1.99 + finish(); 1.100 + return; 1.101 + } 1.102 + emailEdit.setText(fxAccount.getEmail()); 1.103 + } 1.104 + 1.105 + protected class UpdateCredentialsDelegate implements RequestDelegate<LoginResponse> { 1.106 + public final String email; 1.107 + public final String serverURI; 1.108 + public final PasswordStretcher passwordStretcher; 1.109 + 1.110 + public UpdateCredentialsDelegate(String email, PasswordStretcher passwordStretcher, String serverURI) { 1.111 + this.email = email; 1.112 + this.serverURI = serverURI; 1.113 + this.passwordStretcher = passwordStretcher; 1.114 + } 1.115 + 1.116 + @Override 1.117 + public void handleError(Exception e) { 1.118 + showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error); 1.119 + } 1.120 + 1.121 + @Override 1.122 + public void handleFailure(FxAccountClientRemoteException e) { 1.123 + if (e.isUpgradeRequired()) { 1.124 + Logger.error(LOG_TAG, "Got upgrade required from remote server; transitioning Firefox Account to Doghouse state."); 1.125 + final State state = fxAccount.getState(); 1.126 + fxAccount.setState(state.makeDoghouseState()); 1.127 + // The status activity will say that the user needs to upgrade. 1.128 + redirectToActivity(FxAccountStatusActivity.class); 1.129 + return; 1.130 + } 1.131 + showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error); 1.132 + } 1.133 + 1.134 + @Override 1.135 + public void handleSuccess(LoginResponse result) { 1.136 + Logger.info(LOG_TAG, "Got success signing in."); 1.137 + 1.138 + if (fxAccount == null) { 1.139 + this.handleError(new IllegalStateException("fxAccount must not be null")); 1.140 + return; 1.141 + } 1.142 + 1.143 + byte[] unwrapkB; 1.144 + try { 1.145 + // It is crucial that we use the email address provided by the server 1.146 + // (rather than whatever the user entered), because the user's keys are 1.147 + // wrapped and salted with the initial email they provided to 1.148 + // /create/account. Of course, we want to pass through what the user 1.149 + // entered locally as much as possible. 1.150 + byte[] quickStretchedPW = passwordStretcher.getQuickStretchedPW(result.remoteEmail.getBytes("UTF-8")); 1.151 + unwrapkB = FxAccountUtils.generateUnwrapBKey(quickStretchedPW); 1.152 + } catch (Exception e) { 1.153 + this.handleError(e); 1.154 + return; 1.155 + } 1.156 + fxAccount.setState(new Engaged(email, result.uid, result.verified, unwrapkB, result.sessionToken, result.keyFetchToken)); 1.157 + fxAccount.requestSync(FirefoxAccounts.FORCE); 1.158 + 1.159 + // For great debugging. 1.160 + if (FxAccountConstants.LOG_PERSONAL_INFORMATION) { 1.161 + fxAccount.dump(); 1.162 + } 1.163 + 1.164 + redirectToActivity(FxAccountStatusActivity.class); 1.165 + } 1.166 + } 1.167 + 1.168 + public void updateCredentials(String email, String password) { 1.169 + String serverURI = fxAccount.getAccountServerURI(); 1.170 + Executor executor = Executors.newSingleThreadExecutor(); 1.171 + FxAccountClient client = new FxAccountClient20(serverURI, executor); 1.172 + PasswordStretcher passwordStretcher = makePasswordStretcher(password); 1.173 + try { 1.174 + hideRemoteError(); 1.175 + RequestDelegate<LoginResponse> delegate = new UpdateCredentialsDelegate(email, passwordStretcher, serverURI); 1.176 + new FxAccountSignInTask(this, this, email, passwordStretcher, client, delegate).execute(); 1.177 + } catch (Exception e) { 1.178 + Logger.warn(LOG_TAG, "Got exception updating credentials for account.", e); 1.179 + showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error); 1.180 + } 1.181 + } 1.182 + 1.183 + protected void createButton() { 1.184 + button.setOnClickListener(new OnClickListener() { 1.185 + @Override 1.186 + public void onClick(View v) { 1.187 + final String email = emailEdit.getText().toString(); 1.188 + final String password = passwordEdit.getText().toString(); 1.189 + updateCredentials(email, password); 1.190 + } 1.191 + }); 1.192 + } 1.193 +}