michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.fxa.activities; michael@0: michael@0: import java.util.concurrent.Executor; michael@0: import java.util.concurrent.Executors; michael@0: michael@0: import org.mozilla.gecko.R; michael@0: import org.mozilla.gecko.background.common.log.Logger; michael@0: import org.mozilla.gecko.background.fxa.FxAccountClient; michael@0: import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate; michael@0: import org.mozilla.gecko.background.fxa.FxAccountClient20; michael@0: import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse; michael@0: import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException; michael@0: import org.mozilla.gecko.background.fxa.FxAccountUtils; michael@0: import org.mozilla.gecko.background.fxa.PasswordStretcher; michael@0: import org.mozilla.gecko.fxa.FirefoxAccounts; michael@0: import org.mozilla.gecko.fxa.FxAccountConstants; michael@0: import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.FxAccountSignInTask; michael@0: import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; michael@0: import org.mozilla.gecko.fxa.login.Engaged; michael@0: import org.mozilla.gecko.fxa.login.State; michael@0: import org.mozilla.gecko.fxa.login.State.StateLabel; michael@0: import org.mozilla.gecko.sync.setup.activities.ActivityUtils; michael@0: michael@0: import android.os.Bundle; michael@0: import android.view.View; michael@0: import android.view.View.OnClickListener; michael@0: import android.widget.AutoCompleteTextView; michael@0: import android.widget.Button; michael@0: import android.widget.EditText; michael@0: import android.widget.ProgressBar; michael@0: import android.widget.TextView; michael@0: michael@0: /** michael@0: * Activity which displays a screen for updating the local password. michael@0: */ michael@0: public class FxAccountUpdateCredentialsActivity extends FxAccountAbstractSetupActivity { michael@0: protected static final String LOG_TAG = FxAccountUpdateCredentialsActivity.class.getSimpleName(); michael@0: michael@0: protected AndroidFxAccount fxAccount; michael@0: michael@0: public FxAccountUpdateCredentialsActivity() { michael@0: // We want to share code with the other setup activities, but this activity michael@0: // doesn't create a new Android Account, it modifies an existing one. If you michael@0: // manage to get an account, and somehow be locked out too, we'll let you michael@0: // update it. michael@0: super(CANNOT_RESUME_WHEN_NO_ACCOUNTS_EXIST); michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: @Override michael@0: public void onCreate(Bundle icicle) { michael@0: Logger.debug(LOG_TAG, "onCreate(" + icicle + ")"); michael@0: michael@0: super.onCreate(icicle); michael@0: setContentView(R.layout.fxaccount_update_credentials); michael@0: michael@0: emailEdit = (AutoCompleteTextView) ensureFindViewById(null, R.id.email, "email edit"); michael@0: passwordEdit = (EditText) ensureFindViewById(null, R.id.password, "password edit"); michael@0: showPasswordButton = (Button) ensureFindViewById(null, R.id.show_password, "show password button"); michael@0: remoteErrorTextView = (TextView) ensureFindViewById(null, R.id.remote_error, "remote error text view"); michael@0: button = (Button) ensureFindViewById(null, R.id.button, "update credentials"); michael@0: progressBar = (ProgressBar) ensureFindViewById(null, R.id.progress, "progress bar"); michael@0: michael@0: minimumPasswordLength = 1; // Minimal restriction on passwords entered to sign in. michael@0: createButton(); michael@0: addListeners(); michael@0: updateButtonState(); michael@0: createShowPasswordButton(); michael@0: michael@0: emailEdit.setEnabled(false); michael@0: michael@0: TextView view = (TextView) findViewById(R.id.forgot_password_link); michael@0: ActivityUtils.linkTextView(view, R.string.fxaccount_sign_in_forgot_password, R.string.fxaccount_link_forgot_password); michael@0: } michael@0: michael@0: @Override michael@0: public void onResume() { michael@0: super.onResume(); michael@0: this.fxAccount = getAndroidFxAccount(); michael@0: if (fxAccount == null) { michael@0: Logger.warn(LOG_TAG, "Could not get Firefox Account."); michael@0: setResult(RESULT_CANCELED); michael@0: finish(); michael@0: return; michael@0: } michael@0: State state = fxAccount.getState(); michael@0: if (state.getStateLabel() != StateLabel.Separated) { michael@0: Logger.warn(LOG_TAG, "Cannot update credentials from Firefox Account in state: " + state.getStateLabel()); michael@0: setResult(RESULT_CANCELED); michael@0: finish(); michael@0: return; michael@0: } michael@0: emailEdit.setText(fxAccount.getEmail()); michael@0: } michael@0: michael@0: protected class UpdateCredentialsDelegate implements RequestDelegate { michael@0: public final String email; michael@0: public final String serverURI; michael@0: public final PasswordStretcher passwordStretcher; michael@0: michael@0: public UpdateCredentialsDelegate(String email, PasswordStretcher passwordStretcher, String serverURI) { michael@0: this.email = email; michael@0: this.serverURI = serverURI; michael@0: this.passwordStretcher = passwordStretcher; michael@0: } michael@0: michael@0: @Override michael@0: public void handleError(Exception e) { michael@0: showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error); michael@0: } michael@0: michael@0: @Override michael@0: public void handleFailure(FxAccountClientRemoteException e) { michael@0: if (e.isUpgradeRequired()) { michael@0: Logger.error(LOG_TAG, "Got upgrade required from remote server; transitioning Firefox Account to Doghouse state."); michael@0: final State state = fxAccount.getState(); michael@0: fxAccount.setState(state.makeDoghouseState()); michael@0: // The status activity will say that the user needs to upgrade. michael@0: redirectToActivity(FxAccountStatusActivity.class); michael@0: return; michael@0: } michael@0: showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error); michael@0: } michael@0: michael@0: @Override michael@0: public void handleSuccess(LoginResponse result) { michael@0: Logger.info(LOG_TAG, "Got success signing in."); michael@0: michael@0: if (fxAccount == null) { michael@0: this.handleError(new IllegalStateException("fxAccount must not be null")); michael@0: return; michael@0: } michael@0: michael@0: byte[] unwrapkB; michael@0: try { michael@0: // It is crucial that we use the email address provided by the server michael@0: // (rather than whatever the user entered), because the user's keys are michael@0: // wrapped and salted with the initial email they provided to michael@0: // /create/account. Of course, we want to pass through what the user michael@0: // entered locally as much as possible. michael@0: byte[] quickStretchedPW = passwordStretcher.getQuickStretchedPW(result.remoteEmail.getBytes("UTF-8")); michael@0: unwrapkB = FxAccountUtils.generateUnwrapBKey(quickStretchedPW); michael@0: } catch (Exception e) { michael@0: this.handleError(e); michael@0: return; michael@0: } michael@0: fxAccount.setState(new Engaged(email, result.uid, result.verified, unwrapkB, result.sessionToken, result.keyFetchToken)); michael@0: fxAccount.requestSync(FirefoxAccounts.FORCE); michael@0: michael@0: // For great debugging. michael@0: if (FxAccountConstants.LOG_PERSONAL_INFORMATION) { michael@0: fxAccount.dump(); michael@0: } michael@0: michael@0: redirectToActivity(FxAccountStatusActivity.class); michael@0: } michael@0: } michael@0: michael@0: public void updateCredentials(String email, String password) { michael@0: String serverURI = fxAccount.getAccountServerURI(); michael@0: Executor executor = Executors.newSingleThreadExecutor(); michael@0: FxAccountClient client = new FxAccountClient20(serverURI, executor); michael@0: PasswordStretcher passwordStretcher = makePasswordStretcher(password); michael@0: try { michael@0: hideRemoteError(); michael@0: RequestDelegate delegate = new UpdateCredentialsDelegate(email, passwordStretcher, serverURI); michael@0: new FxAccountSignInTask(this, this, email, passwordStretcher, client, delegate).execute(); michael@0: } catch (Exception e) { michael@0: Logger.warn(LOG_TAG, "Got exception updating credentials for account.", e); michael@0: showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error); michael@0: } michael@0: } michael@0: michael@0: protected void createButton() { michael@0: button.setOnClickListener(new OnClickListener() { michael@0: @Override michael@0: public void onClick(View v) { michael@0: final String email = emailEdit.getText().toString(); michael@0: final String password = passwordEdit.getText().toString(); michael@0: updateCredentials(email, password); michael@0: } michael@0: }); michael@0: } michael@0: }