|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 package org.mozilla.gecko.fxa.activities; |
|
6 |
|
7 import java.util.concurrent.Executor; |
|
8 import java.util.concurrent.Executors; |
|
9 |
|
10 import org.mozilla.gecko.R; |
|
11 import org.mozilla.gecko.background.common.log.Logger; |
|
12 import org.mozilla.gecko.background.fxa.FxAccountClient; |
|
13 import org.mozilla.gecko.background.fxa.FxAccountClient10.RequestDelegate; |
|
14 import org.mozilla.gecko.background.fxa.FxAccountClient20; |
|
15 import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse; |
|
16 import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException; |
|
17 import org.mozilla.gecko.background.fxa.FxAccountUtils; |
|
18 import org.mozilla.gecko.background.fxa.PasswordStretcher; |
|
19 import org.mozilla.gecko.fxa.FirefoxAccounts; |
|
20 import org.mozilla.gecko.fxa.FxAccountConstants; |
|
21 import org.mozilla.gecko.fxa.activities.FxAccountSetupTask.FxAccountSignInTask; |
|
22 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount; |
|
23 import org.mozilla.gecko.fxa.login.Engaged; |
|
24 import org.mozilla.gecko.fxa.login.State; |
|
25 import org.mozilla.gecko.fxa.login.State.StateLabel; |
|
26 import org.mozilla.gecko.sync.setup.activities.ActivityUtils; |
|
27 |
|
28 import android.os.Bundle; |
|
29 import android.view.View; |
|
30 import android.view.View.OnClickListener; |
|
31 import android.widget.AutoCompleteTextView; |
|
32 import android.widget.Button; |
|
33 import android.widget.EditText; |
|
34 import android.widget.ProgressBar; |
|
35 import android.widget.TextView; |
|
36 |
|
37 /** |
|
38 * Activity which displays a screen for updating the local password. |
|
39 */ |
|
40 public class FxAccountUpdateCredentialsActivity extends FxAccountAbstractSetupActivity { |
|
41 protected static final String LOG_TAG = FxAccountUpdateCredentialsActivity.class.getSimpleName(); |
|
42 |
|
43 protected AndroidFxAccount fxAccount; |
|
44 |
|
45 public FxAccountUpdateCredentialsActivity() { |
|
46 // We want to share code with the other setup activities, but this activity |
|
47 // doesn't create a new Android Account, it modifies an existing one. If you |
|
48 // manage to get an account, and somehow be locked out too, we'll let you |
|
49 // update it. |
|
50 super(CANNOT_RESUME_WHEN_NO_ACCOUNTS_EXIST); |
|
51 } |
|
52 |
|
53 /** |
|
54 * {@inheritDoc} |
|
55 */ |
|
56 @Override |
|
57 public void onCreate(Bundle icicle) { |
|
58 Logger.debug(LOG_TAG, "onCreate(" + icicle + ")"); |
|
59 |
|
60 super.onCreate(icicle); |
|
61 setContentView(R.layout.fxaccount_update_credentials); |
|
62 |
|
63 emailEdit = (AutoCompleteTextView) ensureFindViewById(null, R.id.email, "email edit"); |
|
64 passwordEdit = (EditText) ensureFindViewById(null, R.id.password, "password edit"); |
|
65 showPasswordButton = (Button) ensureFindViewById(null, R.id.show_password, "show password button"); |
|
66 remoteErrorTextView = (TextView) ensureFindViewById(null, R.id.remote_error, "remote error text view"); |
|
67 button = (Button) ensureFindViewById(null, R.id.button, "update credentials"); |
|
68 progressBar = (ProgressBar) ensureFindViewById(null, R.id.progress, "progress bar"); |
|
69 |
|
70 minimumPasswordLength = 1; // Minimal restriction on passwords entered to sign in. |
|
71 createButton(); |
|
72 addListeners(); |
|
73 updateButtonState(); |
|
74 createShowPasswordButton(); |
|
75 |
|
76 emailEdit.setEnabled(false); |
|
77 |
|
78 TextView view = (TextView) findViewById(R.id.forgot_password_link); |
|
79 ActivityUtils.linkTextView(view, R.string.fxaccount_sign_in_forgot_password, R.string.fxaccount_link_forgot_password); |
|
80 } |
|
81 |
|
82 @Override |
|
83 public void onResume() { |
|
84 super.onResume(); |
|
85 this.fxAccount = getAndroidFxAccount(); |
|
86 if (fxAccount == null) { |
|
87 Logger.warn(LOG_TAG, "Could not get Firefox Account."); |
|
88 setResult(RESULT_CANCELED); |
|
89 finish(); |
|
90 return; |
|
91 } |
|
92 State state = fxAccount.getState(); |
|
93 if (state.getStateLabel() != StateLabel.Separated) { |
|
94 Logger.warn(LOG_TAG, "Cannot update credentials from Firefox Account in state: " + state.getStateLabel()); |
|
95 setResult(RESULT_CANCELED); |
|
96 finish(); |
|
97 return; |
|
98 } |
|
99 emailEdit.setText(fxAccount.getEmail()); |
|
100 } |
|
101 |
|
102 protected class UpdateCredentialsDelegate implements RequestDelegate<LoginResponse> { |
|
103 public final String email; |
|
104 public final String serverURI; |
|
105 public final PasswordStretcher passwordStretcher; |
|
106 |
|
107 public UpdateCredentialsDelegate(String email, PasswordStretcher passwordStretcher, String serverURI) { |
|
108 this.email = email; |
|
109 this.serverURI = serverURI; |
|
110 this.passwordStretcher = passwordStretcher; |
|
111 } |
|
112 |
|
113 @Override |
|
114 public void handleError(Exception e) { |
|
115 showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error); |
|
116 } |
|
117 |
|
118 @Override |
|
119 public void handleFailure(FxAccountClientRemoteException e) { |
|
120 if (e.isUpgradeRequired()) { |
|
121 Logger.error(LOG_TAG, "Got upgrade required from remote server; transitioning Firefox Account to Doghouse state."); |
|
122 final State state = fxAccount.getState(); |
|
123 fxAccount.setState(state.makeDoghouseState()); |
|
124 // The status activity will say that the user needs to upgrade. |
|
125 redirectToActivity(FxAccountStatusActivity.class); |
|
126 return; |
|
127 } |
|
128 showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error); |
|
129 } |
|
130 |
|
131 @Override |
|
132 public void handleSuccess(LoginResponse result) { |
|
133 Logger.info(LOG_TAG, "Got success signing in."); |
|
134 |
|
135 if (fxAccount == null) { |
|
136 this.handleError(new IllegalStateException("fxAccount must not be null")); |
|
137 return; |
|
138 } |
|
139 |
|
140 byte[] unwrapkB; |
|
141 try { |
|
142 // It is crucial that we use the email address provided by the server |
|
143 // (rather than whatever the user entered), because the user's keys are |
|
144 // wrapped and salted with the initial email they provided to |
|
145 // /create/account. Of course, we want to pass through what the user |
|
146 // entered locally as much as possible. |
|
147 byte[] quickStretchedPW = passwordStretcher.getQuickStretchedPW(result.remoteEmail.getBytes("UTF-8")); |
|
148 unwrapkB = FxAccountUtils.generateUnwrapBKey(quickStretchedPW); |
|
149 } catch (Exception e) { |
|
150 this.handleError(e); |
|
151 return; |
|
152 } |
|
153 fxAccount.setState(new Engaged(email, result.uid, result.verified, unwrapkB, result.sessionToken, result.keyFetchToken)); |
|
154 fxAccount.requestSync(FirefoxAccounts.FORCE); |
|
155 |
|
156 // For great debugging. |
|
157 if (FxAccountConstants.LOG_PERSONAL_INFORMATION) { |
|
158 fxAccount.dump(); |
|
159 } |
|
160 |
|
161 redirectToActivity(FxAccountStatusActivity.class); |
|
162 } |
|
163 } |
|
164 |
|
165 public void updateCredentials(String email, String password) { |
|
166 String serverURI = fxAccount.getAccountServerURI(); |
|
167 Executor executor = Executors.newSingleThreadExecutor(); |
|
168 FxAccountClient client = new FxAccountClient20(serverURI, executor); |
|
169 PasswordStretcher passwordStretcher = makePasswordStretcher(password); |
|
170 try { |
|
171 hideRemoteError(); |
|
172 RequestDelegate<LoginResponse> delegate = new UpdateCredentialsDelegate(email, passwordStretcher, serverURI); |
|
173 new FxAccountSignInTask(this, this, email, passwordStretcher, client, delegate).execute(); |
|
174 } catch (Exception e) { |
|
175 Logger.warn(LOG_TAG, "Got exception updating credentials for account.", e); |
|
176 showRemoteError(e, R.string.fxaccount_update_credentials_unknown_error); |
|
177 } |
|
178 } |
|
179 |
|
180 protected void createButton() { |
|
181 button.setOnClickListener(new OnClickListener() { |
|
182 @Override |
|
183 public void onClick(View v) { |
|
184 final String email = emailEdit.getText().toString(); |
|
185 final String password = passwordEdit.getText().toString(); |
|
186 updateCredentials(email, password); |
|
187 } |
|
188 }); |
|
189 } |
|
190 } |