mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 08:15:31 +00:00
Bug 705767 - Validate credentials before completing setup. r=rnewman
This commit is contained in:
parent
b921ba52c1
commit
0f04823a37
File diff suppressed because one or more lines are too long
@ -78,4 +78,7 @@
|
|||||||
<!ENTITY sync.notification.oneaccount.label 'Only one &syncBrand.fullName.label; account is supported.'>
|
<!ENTITY sync.notification.oneaccount.label 'Only one &syncBrand.fullName.label; account is supported.'>
|
||||||
|
|
||||||
<!-- Incorrect settings and changing credentials. -->
|
<!-- Incorrect settings and changing credentials. -->
|
||||||
|
<!ENTITY sync.invalidcreds.label 'Incorrect account name or password.'>
|
||||||
|
<!ENTITY sync.invalidserver.label 'Please enter a valid server URL'>
|
||||||
|
<!ENTITY sync.verifying.label 'Verifying…'>
|
||||||
<!ENTITY sync.new.recoverykey.status.incorrect 'Recovery Key incorrect. Please try again.'>
|
<!ENTITY sync.new.recoverykey.status.incorrect 'Recovery Key incorrect. Please try again.'>
|
||||||
|
@ -43,6 +43,10 @@
|
|||||||
<EditText android:id="@+id/keyInput"
|
<EditText android:id="@+id/keyInput"
|
||||||
style="@style/SyncEditItem"
|
style="@style/SyncEditItem"
|
||||||
android:hint="@string/sync_input_key" />
|
android:hint="@string/sync_input_key" />
|
||||||
|
|
||||||
|
<TextView android:id="@+id/cred_error"
|
||||||
|
style="@style/SyncTextError"
|
||||||
|
android:text="@string/sync_invalidcreds_label" />
|
||||||
|
|
||||||
<CheckBox android:id="@+id/checkbox_server"
|
<CheckBox android:id="@+id/checkbox_server"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
@ -56,6 +60,10 @@
|
|||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:hint="@string/sync_input_server" />
|
android:hint="@string/sync_input_server" />
|
||||||
|
|
||||||
|
<TextView android:id="@+id/server_error"
|
||||||
|
style="@style/SyncTextError"
|
||||||
|
android:text="@string/sync_invalidserver_label" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
@ -65,6 +73,7 @@
|
|||||||
android:orientation="horizontal" >
|
android:orientation="horizontal" >
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
android:id="@+id/accountCancelButton"
|
||||||
style="@style/SyncButton"
|
style="@style/SyncButton"
|
||||||
android:onClick="cancelClickHandler"
|
android:onClick="cancelClickHandler"
|
||||||
android:text="@string/sync_button_cancel" />
|
android:text="@string/sync_button_cancel" />
|
||||||
|
@ -99,8 +99,7 @@
|
|||||||
style="@style/SyncBottom"
|
style="@style/SyncBottom"
|
||||||
android:orientation="horizontal" >
|
android:orientation="horizontal" >
|
||||||
<Button
|
<Button
|
||||||
android:layout_width="wrap_content"
|
style="@style/SyncButton"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:onClick="cancelClickHandler"
|
android:onClick="cancelClickHandler"
|
||||||
android:text="@string/sync_button_cancel" />
|
android:text="@string/sync_button_cancel" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -27,22 +27,17 @@
|
|||||||
android:orientation="horizontal" >
|
android:orientation="horizontal" >
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:layout_width="fill_parent"
|
style="@style/SyncButton"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:onClick="tryAgainClickHandler"
|
android:onClick="tryAgainClickHandler"
|
||||||
android:text="@string/sync_button_tryagain" />
|
android:text="@string/sync_button_tryagain" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:layout_width="fill_parent"
|
style="@style/SyncButton"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:onClick="manualClickHandler"
|
android:onClick="manualClickHandler"
|
||||||
android:text="@string/sync_button_manual" />
|
android:text="@string/sync_button_manual" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:layout_width="fill_parent"
|
style="@style/SyncButton"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:onClick="cancelClickHandler"
|
android:onClick="cancelClickHandler"
|
||||||
android:text="@string/sync_button_cancel" />
|
android:text="@string/sync_button_cancel" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
android:visibility="invisible" >
|
android:visibility="invisible" >
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
style="@style/SyncTextItem"
|
style="@style/SyncTextError"
|
||||||
android:layout_margin="10dp"
|
android:layout_margin="10dp"
|
||||||
android:text="@string/sync_pair_tryagain"
|
android:text="@string/sync_pair_tryagain"
|
||||||
android:textSize="10dp" />
|
android:textSize="10dp" />
|
||||||
|
@ -28,7 +28,12 @@
|
|||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:textSize">15dp</item>
|
<item name="android:textSize">15dp</item>
|
||||||
</style>
|
</style>
|
||||||
<style name="SyncLinkItem" parent="SyncTextItem">
|
<style name="SyncTextError" parent="@style/SyncTextItem">
|
||||||
|
<item name="android:layout_margin">10dp</item>
|
||||||
|
<item name="android:textSize">15dp</item>
|
||||||
|
<item name="android:visibility">gone</item>
|
||||||
|
</style>
|
||||||
|
<style name="SyncLinkItem" parent="@style/SyncTextItem">
|
||||||
<item name="android:clickable">true</item>
|
<item name="android:clickable">true</item>
|
||||||
<item name="android:textColor">#ACC4D5</item>
|
<item name="android:textColor">#ACC4D5</item>
|
||||||
</style>
|
</style>
|
||||||
@ -67,6 +72,7 @@
|
|||||||
<style name="SyncBottom">
|
<style name="SyncBottom">
|
||||||
<item name="android:layout_width">fill_parent</item>
|
<item name="android:layout_width">fill_parent</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:layout_gravity">center</item>
|
||||||
<item name="android:gravity">center</item>
|
<item name="android:gravity">center</item>
|
||||||
<item name="android:layout_alignParentBottom">true</item>
|
<item name="android:layout_alignParentBottom">true</item>
|
||||||
<item name="android:background">@android:drawable/bottom_bar</item>
|
<item name="android:background">@android:drawable/bottom_bar</item>
|
||||||
|
@ -12,6 +12,7 @@ import org.mozilla.gecko.sync.Logger;
|
|||||||
import org.mozilla.gecko.sync.jpake.JPakeClient;
|
import org.mozilla.gecko.sync.jpake.JPakeClient;
|
||||||
import org.mozilla.gecko.sync.net.BaseResource;
|
import org.mozilla.gecko.sync.net.BaseResource;
|
||||||
import org.mozilla.gecko.sync.net.SyncResourceDelegate;
|
import org.mozilla.gecko.sync.net.SyncResourceDelegate;
|
||||||
|
import org.mozilla.gecko.sync.setup.auth.AccountAuthenticator;
|
||||||
|
|
||||||
import ch.boye.httpclientandroidlib.HttpResponse;
|
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||||
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
|
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
|
||||||
@ -85,7 +86,7 @@ public class DeleteChannel {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
JPakeClient.runOnThread(new Runnable() {
|
AccountAuthenticator.runOnThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
httpResource.delete();
|
httpResource.delete();
|
||||||
|
@ -26,6 +26,17 @@ public class Constants {
|
|||||||
Intent.FLAG_ACTIVITY_REORDER_TO_FRONT |
|
Intent.FLAG_ACTIVITY_REORDER_TO_FRONT |
|
||||||
Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
||||||
|
|
||||||
|
// Constants for Account Authentication.
|
||||||
|
public static final String AUTH_NODE_DEFAULT = "https://auth.services.mozilla.com/";
|
||||||
|
public static final String AUTH_NODE_PATHNAME = "user/";
|
||||||
|
public static final String AUTH_NODE_VERSION = "1.0/";
|
||||||
|
public static final String AUTH_NODE_SUFFIX = "node/weave";
|
||||||
|
public static final String AUTH_SERVER_VERSION = "1.1/";
|
||||||
|
public static final String AUTH_SERVER_SUFFIX = "info/collections/";
|
||||||
|
|
||||||
|
// Account Authentication Errors.
|
||||||
|
public static final String AUTH_ERROR_NOUSER = "auth.error.badcredentials";
|
||||||
|
|
||||||
// Links for J-PAKE setup help pages.
|
// Links for J-PAKE setup help pages.
|
||||||
public static final String LINK_FIND_CODE = "https://support.mozilla.org/kb/find-code-to-add-device-to-firefox-sync";
|
public static final String LINK_FIND_CODE = "https://support.mozilla.org/kb/find-code-to-add-device-to-firefox-sync";
|
||||||
public static final String LINK_FIND_ADD_DEVICE = "https://support.mozilla.org/kb/add-a-device-to-firefox-sync";
|
public static final String LINK_FIND_ADD_DEVICE = "https://support.mozilla.org/kb/add-a-device-to-firefox-sync";
|
||||||
|
@ -12,19 +12,22 @@ import org.mozilla.gecko.sync.setup.Constants;
|
|||||||
import org.mozilla.gecko.sync.setup.InvalidSyncKeyException;
|
import org.mozilla.gecko.sync.setup.InvalidSyncKeyException;
|
||||||
import org.mozilla.gecko.sync.setup.SyncAccounts;
|
import org.mozilla.gecko.sync.setup.SyncAccounts;
|
||||||
import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters;
|
import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters;
|
||||||
|
import org.mozilla.gecko.sync.setup.auth.AccountAuthenticator;
|
||||||
|
import org.mozilla.gecko.sync.setup.auth.AuthenticationResult;
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.accounts.AccountAuthenticatorActivity;
|
import android.accounts.AccountAuthenticatorActivity;
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.CompoundButton;
|
import android.widget.CompoundButton;
|
||||||
@ -33,14 +36,14 @@ import android.widget.EditText;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
public class AccountActivity extends AccountAuthenticatorActivity {
|
public class AccountActivity extends AccountAuthenticatorActivity {
|
||||||
private final static String LOG_TAG = "AccountActivity";
|
private final static String LOG_TAG = "AccountActivity";
|
||||||
|
|
||||||
private AccountManager mAccountManager;
|
private AccountManager mAccountManager;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private String username;
|
private String username;
|
||||||
private String password;
|
private String password;
|
||||||
private String key;
|
private String key;
|
||||||
private String server;
|
private String server = Constants.AUTH_NODE_DEFAULT;
|
||||||
|
|
||||||
// UI elements.
|
// UI elements.
|
||||||
private EditText serverInput;
|
private EditText serverInput;
|
||||||
@ -49,6 +52,10 @@ public class AccountActivity extends AccountAuthenticatorActivity {
|
|||||||
private EditText synckeyInput;
|
private EditText synckeyInput;
|
||||||
private CheckBox serverCheckbox;
|
private CheckBox serverCheckbox;
|
||||||
private Button connectButton;
|
private Button connectButton;
|
||||||
|
private Button cancelButton;
|
||||||
|
private ProgressDialog progressDialog;
|
||||||
|
|
||||||
|
private AccountAuthenticator accountAuthenticator;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
@ -56,7 +63,7 @@ public class AccountActivity extends AccountAuthenticatorActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.sync_account);
|
setContentView(R.layout.sync_account);
|
||||||
mContext = getApplicationContext();
|
mContext = getApplicationContext();
|
||||||
Log.d(LOG_TAG, "AccountManager.get(" + mContext + ")");
|
Logger.debug(LOG_TAG, "AccountManager.get(" + mContext + ")");
|
||||||
mAccountManager = AccountManager.get(mContext);
|
mAccountManager = AccountManager.get(mContext);
|
||||||
|
|
||||||
// Set "screen on" flag.
|
// Set "screen on" flag.
|
||||||
@ -78,18 +85,20 @@ public class AccountActivity extends AccountAuthenticatorActivity {
|
|||||||
serverInput.addTextChangedListener(inputValidator);
|
serverInput.addTextChangedListener(inputValidator);
|
||||||
|
|
||||||
connectButton = (Button) findViewById(R.id.accountConnectButton);
|
connectButton = (Button) findViewById(R.id.accountConnectButton);
|
||||||
|
cancelButton = (Button) findViewById(R.id.accountCancelButton);
|
||||||
serverCheckbox = (CheckBox) findViewById(R.id.checkbox_server);
|
serverCheckbox = (CheckBox) findViewById(R.id.checkbox_server);
|
||||||
|
|
||||||
serverCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
|
serverCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||||
Log.i(LOG_TAG, "Toggling checkbox: " + isChecked);
|
Logger.info(LOG_TAG, "Toggling checkbox: " + isChecked);
|
||||||
// Hack for pre-3.0 Android: can enter text into disabled EditText.
|
|
||||||
if (!isChecked) { // Clear server input.
|
if (!isChecked) { // Clear server input.
|
||||||
serverInput.setVisibility(View.GONE);
|
serverInput.setVisibility(View.GONE);
|
||||||
|
findViewById(R.id.server_error).setVisibility(View.GONE);
|
||||||
serverInput.setText("");
|
serverInput.setText("");
|
||||||
} else {
|
} else {
|
||||||
serverInput.setVisibility(View.VISIBLE);
|
serverInput.setVisibility(View.VISIBLE);
|
||||||
|
serverInput.setEnabled(true);
|
||||||
}
|
}
|
||||||
// Activate connectButton if necessary.
|
// Activate connectButton if necessary.
|
||||||
activateView(connectButton, validateInputs());
|
activateView(connectButton, validateInputs());
|
||||||
@ -101,12 +110,32 @@ public class AccountActivity extends AccountAuthenticatorActivity {
|
|||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
clearCredentials();
|
clearCredentials();
|
||||||
|
usernameInput.requestFocus();
|
||||||
|
cancelButton.setOnClickListener(new OnClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
cancelClickHandler(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancelClickHandler(View target) {
|
public void cancelClickHandler(View target) {
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void cancelConnectHandler(View target) {
|
||||||
|
if (accountAuthenticator != null) {
|
||||||
|
accountAuthenticator.isCanceled = true;
|
||||||
|
accountAuthenticator = null;
|
||||||
|
}
|
||||||
|
displayVerifying(false);
|
||||||
|
activateView(connectButton, true);
|
||||||
|
clearCredentials();
|
||||||
|
usernameInput.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
private void clearCredentials() {
|
private void clearCredentials() {
|
||||||
// Only clear password. Re-typing the sync key or email is annoying.
|
// Only clear password. Re-typing the sync key or email is annoying.
|
||||||
passwordInput.setText("");
|
passwordInput.setText("");
|
||||||
@ -116,13 +145,11 @@ public class AccountActivity extends AccountAuthenticatorActivity {
|
|||||||
* accessed by Fennec and Sync Service.
|
* accessed by Fennec and Sync Service.
|
||||||
*/
|
*/
|
||||||
public void connectClickHandler(View target) {
|
public void connectClickHandler(View target) {
|
||||||
Log.d(LOG_TAG, "connectClickHandler for view " + target);
|
Logger.debug(LOG_TAG, "connectClickHandler for view " + target);
|
||||||
enableCredEntry(false);
|
|
||||||
// Validate sync key format.
|
// Validate sync key format.
|
||||||
try {
|
try {
|
||||||
key = ActivityUtils.validateSyncKey(synckeyInput.getText().toString());
|
key = ActivityUtils.validateSyncKey(synckeyInput.getText().toString());
|
||||||
} catch (InvalidSyncKeyException e) {
|
} catch (InvalidSyncKeyException e) {
|
||||||
enableCredEntry(true);
|
|
||||||
// Toast: invalid sync key format.
|
// Toast: invalid sync key format.
|
||||||
Toast toast = Toast.makeText(mContext, R.string.sync_new_recoverykey_status_incorrect, Toast.LENGTH_LONG);
|
Toast toast = Toast.makeText(mContext, R.string.sync_new_recoverykey_status_incorrect, Toast.LENGTH_LONG);
|
||||||
toast.show();
|
toast.show();
|
||||||
@ -130,26 +157,42 @@ public class AccountActivity extends AccountAuthenticatorActivity {
|
|||||||
}
|
}
|
||||||
username = usernameInput.getText().toString().toLowerCase(Locale.US);
|
username = usernameInput.getText().toString().toLowerCase(Locale.US);
|
||||||
password = passwordInput.getText().toString();
|
password = passwordInput.getText().toString();
|
||||||
|
key = synckeyInput.getText().toString();
|
||||||
|
server = Constants.AUTH_NODE_DEFAULT;
|
||||||
|
|
||||||
if (serverCheckbox.isChecked()) {
|
if (serverCheckbox.isChecked()) {
|
||||||
server = serverInput.getText().toString();
|
String userServer = serverInput.getText().toString();
|
||||||
|
if (userServer != null) {
|
||||||
|
userServer = userServer.trim();
|
||||||
|
if (userServer.length() != 0) {
|
||||||
|
if (!userServer.startsWith("https://") &&
|
||||||
|
!userServer.startsWith("http://")) {
|
||||||
|
// Assume HTTPS if not specified.
|
||||||
|
userServer = "https://" + userServer;
|
||||||
|
serverInput.setText(userServer);
|
||||||
|
}
|
||||||
|
server = userServer;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO : Authenticate with Sync Service, once implemented, with
|
clearErrors();
|
||||||
// onAuthSuccess as callback
|
displayVerifying(true);
|
||||||
|
cancelButton.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
cancelConnectHandler(v);
|
||||||
|
// Set cancel click handler to leave account setup.
|
||||||
|
cancelButton.setOnClickListener(new OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
cancelClickHandler(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
authCallback();
|
accountAuthenticator = new AccountAuthenticator(this);
|
||||||
}
|
accountAuthenticator.authenticate(server, username, password);
|
||||||
|
|
||||||
/* Helper UI functions */
|
|
||||||
private void enableCredEntry(boolean toEnable) {
|
|
||||||
usernameInput.setEnabled(toEnable);
|
|
||||||
passwordInput.setEnabled(toEnable);
|
|
||||||
synckeyInput.setEnabled(toEnable);
|
|
||||||
if (!toEnable) {
|
|
||||||
serverInput.setEnabled(toEnable);
|
|
||||||
} else {
|
|
||||||
serverInput.setEnabled(serverCheckbox.isChecked());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TextWatcher makeInputValidator() {
|
private TextWatcher makeInputValidator() {
|
||||||
@ -185,13 +228,18 @@ public class AccountActivity extends AccountAuthenticatorActivity {
|
|||||||
/*
|
/*
|
||||||
* Callback that handles auth based on success/failure
|
* Callback that handles auth based on success/failure
|
||||||
*/
|
*/
|
||||||
private void authCallback() {
|
public void authCallback(final AuthenticationResult result) {
|
||||||
// Create and add account to AccountManager
|
displayVerifying(false);
|
||||||
// TODO: only allow one account to be added?
|
if (result != AuthenticationResult.SUCCESS) {
|
||||||
final SyncAccountParameters syncAccount = new SyncAccountParameters(mContext, mAccountManager,
|
Logger.debug(LOG_TAG, "displayFailure()");
|
||||||
username, key, password, server);
|
displayFailure(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Successful authentication. Create and add account to AccountManager.
|
||||||
|
final SyncAccountParameters syncAccount = new SyncAccountParameters(
|
||||||
|
mContext, mAccountManager, username, key, password, server);
|
||||||
final Account account = SyncAccounts.createSyncAccount(syncAccount);
|
final Account account = SyncAccounts.createSyncAccount(syncAccount);
|
||||||
final boolean result = (account != null);
|
final boolean accountResult = (account != null);
|
||||||
|
|
||||||
final Intent intent = new Intent(); // The intent to return.
|
final Intent intent = new Intent(); // The intent to return.
|
||||||
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, syncAccount.username);
|
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, syncAccount.username);
|
||||||
@ -199,39 +247,77 @@ public class AccountActivity extends AccountAuthenticatorActivity {
|
|||||||
intent.putExtra(AccountManager.KEY_AUTHTOKEN, Constants.ACCOUNTTYPE_SYNC);
|
intent.putExtra(AccountManager.KEY_AUTHTOKEN, Constants.ACCOUNTTYPE_SYNC);
|
||||||
setAccountAuthenticatorResult(intent.getExtras());
|
setAccountAuthenticatorResult(intent.getExtras());
|
||||||
|
|
||||||
if (!result) {
|
if (!accountResult) {
|
||||||
// Failed to add account!
|
// Failed to add account!
|
||||||
setResult(RESULT_CANCELED, intent);
|
setResult(RESULT_CANCELED, intent);
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
authFailure();
|
// Use default error.
|
||||||
|
// TODO: Display more accurate error (Account failed to be created).
|
||||||
|
Logger.debug(LOG_TAG, "displayFailure()");
|
||||||
|
displayFailure(result);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Currently, we do not actually authenticate username/pass against
|
clearErrors();
|
||||||
// Moz sync server.
|
if (intent != null) {
|
||||||
|
setAccountAuthenticatorResult(intent.getExtras());
|
||||||
|
setResult(RESULT_OK, intent);
|
||||||
|
|
||||||
// Successfully added account.
|
runOnUiThread(new Runnable() {
|
||||||
setResult(RESULT_OK, intent);
|
@Override
|
||||||
|
public void run() {
|
||||||
|
authSuccess();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayVerifying(final boolean isVerifying) {
|
||||||
|
if (isVerifying) {
|
||||||
|
progressDialog = ProgressDialog.show(AccountActivity.this, "", getString(R.string.sync_verifying_label), true);
|
||||||
|
} else {
|
||||||
|
progressDialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayFailure(final AuthenticationResult result) {
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
authSuccess();
|
switch (result) {
|
||||||
|
case FAILURE_USERNAME:
|
||||||
|
// No such username. Don't leak whether the username exists.
|
||||||
|
case FAILURE_PASSWORD:
|
||||||
|
findViewById(R.id.cred_error).setVisibility(View.VISIBLE);
|
||||||
|
usernameInput.requestFocus();
|
||||||
|
break;
|
||||||
|
case FAILURE_SERVER:
|
||||||
|
findViewById(R.id.server_error).setVisibility(View.VISIBLE);
|
||||||
|
serverInput.requestFocus();
|
||||||
|
break;
|
||||||
|
case FAILURE_OTHER:
|
||||||
|
default:
|
||||||
|
// Display default error screen.
|
||||||
|
Logger.debug(LOG_TAG, "displaying default failure.");
|
||||||
|
Intent intent = new Intent(mContext, SetupFailureActivity.class);
|
||||||
|
intent.setFlags(Constants.FLAG_ACTIVITY_REORDER_TO_FRONT_NO_ANIMATION);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void authFailure() {
|
/**
|
||||||
enableCredEntry(true);
|
* Feedback to user of account setup success.
|
||||||
Intent intent = new Intent(mContext, SetupFailureActivity.class);
|
*/
|
||||||
intent.setFlags(Constants.FLAG_ACTIVITY_REORDER_TO_FRONT_NO_ANIMATION);
|
public void authSuccess() {
|
||||||
startActivity(intent);
|
// Display feedback of successful account setup.
|
||||||
}
|
|
||||||
|
|
||||||
private void authSuccess() {
|
|
||||||
Intent intent = new Intent(mContext, SetupSuccessActivity.class);
|
Intent intent = new Intent(mContext, SetupSuccessActivity.class);
|
||||||
intent.setFlags(Constants.FLAG_ACTIVITY_REORDER_TO_FRONT_NO_ANIMATION);
|
intent.setFlags(Constants.FLAG_ACTIVITY_REORDER_TO_FRONT_NO_ANIMATION);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
@ -242,4 +328,14 @@ public class AccountActivity extends AccountAuthenticatorActivity {
|
|||||||
view.setEnabled(toActivate);
|
view.setEnabled(toActivate);
|
||||||
view.setClickable(toActivate);
|
view.setClickable(toActivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void clearErrors() {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
findViewById(R.id.cred_error).setVisibility(View.GONE);
|
||||||
|
findViewById(R.id.server_error).setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ public class SetupFailureActivity extends Activity {
|
|||||||
|
|
||||||
public void cancelClickHandler(View target) {
|
public void cancelClickHandler(View target) {
|
||||||
setResult(RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
|
moveTaskToBack(true);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
108
mobile/android/base/sync/setup/auth/AccountAuthenticator.java
Normal file
108
mobile/android/base/sync/setup/auth/AccountAuthenticator.java
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.sync.setup.auth;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.sync.Logger;
|
||||||
|
import org.mozilla.gecko.sync.ThreadPool;
|
||||||
|
import org.mozilla.gecko.sync.crypto.KeyBundle;
|
||||||
|
import org.mozilla.gecko.sync.setup.activities.AccountActivity;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class AccountAuthenticator {
|
||||||
|
private final String LOG_TAG = "AccountAuthenticator";
|
||||||
|
|
||||||
|
private AccountActivity activityCallback;
|
||||||
|
private Queue<AuthenticatorStage> stages;
|
||||||
|
|
||||||
|
// Values for authentication.
|
||||||
|
public String password;
|
||||||
|
public String username;
|
||||||
|
|
||||||
|
public String authServer;
|
||||||
|
public String nodeServer;
|
||||||
|
|
||||||
|
public boolean isSuccess = false;
|
||||||
|
public boolean isCanceled = false;
|
||||||
|
|
||||||
|
public AccountAuthenticator(AccountActivity activity) {
|
||||||
|
activityCallback = activity;
|
||||||
|
prepareStages();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareStages() {
|
||||||
|
stages = new LinkedList<AuthenticatorStage>();
|
||||||
|
stages.add(new EnsureUserExistenceStage());
|
||||||
|
stages.add(new FetchUserNodeStage());
|
||||||
|
stages.add(new AuthenticateAccountStage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void authenticate(String server, String account, String password) {
|
||||||
|
// Set authentication values.
|
||||||
|
if (!server.endsWith("/")) {
|
||||||
|
server += "/";
|
||||||
|
}
|
||||||
|
nodeServer = server;
|
||||||
|
this.password = password;
|
||||||
|
|
||||||
|
// Calculate and save username hash.
|
||||||
|
try {
|
||||||
|
username = KeyBundle.usernameFromAccount(account);
|
||||||
|
} catch (Exception e) {
|
||||||
|
abort(AuthenticationResult.FAILURE_OTHER, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Logger.debug(LOG_TAG, "username:" + username);
|
||||||
|
|
||||||
|
Log.d(LOG_TAG, "running first stage.");
|
||||||
|
// Start first stage of authentication.
|
||||||
|
runNextStage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run next stage of authentication.
|
||||||
|
*/
|
||||||
|
public void runNextStage() {
|
||||||
|
if (isCanceled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stages.size() == 0) {
|
||||||
|
Logger.debug(LOG_TAG, "Authentication completed.");
|
||||||
|
activityCallback.authCallback(isSuccess ? AuthenticationResult.SUCCESS : AuthenticationResult.FAILURE_PASSWORD);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AuthenticatorStage nextStage = stages.remove();
|
||||||
|
try {
|
||||||
|
nextStage.execute(this);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.warn(LOG_TAG, "Unhandled exception in stage " + nextStage);
|
||||||
|
abort(AuthenticationResult.FAILURE_OTHER, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abort authentication.
|
||||||
|
*
|
||||||
|
* @param e
|
||||||
|
* Exception causing abort.
|
||||||
|
* @param reason
|
||||||
|
* Reason for abort.
|
||||||
|
*/
|
||||||
|
public void abort(AuthenticationResult result, Exception e) {
|
||||||
|
if (isCanceled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Logger.warn(LOG_TAG, "Authentication failed.", e);
|
||||||
|
activityCallback.authCallback(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper functions */
|
||||||
|
public static void runOnThread(Runnable run) {
|
||||||
|
ThreadPool.run(run);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,175 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.sync.setup.auth;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
import org.mozilla.apache.commons.codec.binary.Base64;
|
||||||
|
import org.mozilla.gecko.sync.GlobalConstants;
|
||||||
|
import org.mozilla.gecko.sync.Logger;
|
||||||
|
import org.mozilla.gecko.sync.net.BaseResource;
|
||||||
|
import org.mozilla.gecko.sync.net.SyncResourceDelegate;
|
||||||
|
import org.mozilla.gecko.sync.setup.Constants;
|
||||||
|
|
||||||
|
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||||
|
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
|
||||||
|
import ch.boye.httpclientandroidlib.client.methods.HttpRequestBase;
|
||||||
|
import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient;
|
||||||
|
import ch.boye.httpclientandroidlib.message.BasicHeader;
|
||||||
|
|
||||||
|
public class AuthenticateAccountStage implements AuthenticatorStage {
|
||||||
|
private final String LOG_TAG = "AuthAccountStage";
|
||||||
|
private HttpRequestBase httpRequest = null;
|
||||||
|
|
||||||
|
public interface AuthenticateAccountStageDelegate {
|
||||||
|
public void handleSuccess(boolean isSuccess);
|
||||||
|
public void handleFailure(HttpResponse response);
|
||||||
|
public void handleError(Exception e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(final AccountAuthenticator aa) throws URISyntaxException, UnsupportedEncodingException {
|
||||||
|
AuthenticateAccountStageDelegate callbackDelegate = new AuthenticateAccountStageDelegate() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSuccess(boolean isSuccess) {
|
||||||
|
aa.isSuccess = isSuccess;
|
||||||
|
aa.runNextStage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleFailure(HttpResponse response) {
|
||||||
|
Logger.debug(LOG_TAG, "handleFailure");
|
||||||
|
aa.abort(AuthenticationResult.FAILURE_OTHER, new Exception(response.getStatusLine().getStatusCode() + " error."));
|
||||||
|
if (response.getEntity() == null) {
|
||||||
|
// No cleanup necessary.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
|
||||||
|
Logger.warn(LOG_TAG, "content: " + reader.readLine());
|
||||||
|
BaseResource.consumeReader(reader);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
Logger.debug(LOG_TAG, "Error reading content.", e);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
Logger.debug(LOG_TAG, "Unexpected exception.", e);
|
||||||
|
if (httpRequest != null) {
|
||||||
|
httpRequest.abort();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Logger.debug(LOG_TAG, "Error reading content.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleError(Exception e) {
|
||||||
|
Logger.debug(LOG_TAG, "handleError", e);
|
||||||
|
aa.abort(AuthenticationResult.FAILURE_OTHER, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate BasicAuth hash of username/password.
|
||||||
|
String authHeader = makeAuthHeader(aa.username, aa.password);
|
||||||
|
String authRequestUrl = makeAuthRequestUrl(aa.authServer, aa.username);
|
||||||
|
Logger.trace(LOG_TAG, "Making auth request to: " + authRequestUrl);
|
||||||
|
authenticateAccount(callbackDelegate, authRequestUrl, authHeader);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes an authentication request to the server and passes appropriate response back to callback.
|
||||||
|
* @param callbackDelegate
|
||||||
|
* Delegate to deal with HTTP response.
|
||||||
|
* @param authRequestUrl
|
||||||
|
* @param authHeader
|
||||||
|
* @throws URISyntaxException
|
||||||
|
*/
|
||||||
|
// Made public for testing.
|
||||||
|
public void authenticateAccount(final AuthenticateAccountStageDelegate callbackDelegate, final String authRequestUrl, final String authHeader) throws URISyntaxException {
|
||||||
|
final BaseResource httpResource = new BaseResource(authRequestUrl);
|
||||||
|
httpResource.delegate = new SyncResourceDelegate(httpResource) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addHeaders(HttpRequestBase request, DefaultHttpClient client) {
|
||||||
|
// Make reference to request, to abort if necessary.
|
||||||
|
httpRequest = request;
|
||||||
|
client.log.enableDebug(true);
|
||||||
|
request.setHeader(new BasicHeader("User-Agent", GlobalConstants.USER_AGENT));
|
||||||
|
// Host header is not set for some reason, so do it explicitly.
|
||||||
|
try {
|
||||||
|
URI authServerUri = new URI(authRequestUrl);
|
||||||
|
request.setHeader(new BasicHeader("Host", authServerUri.getHost()));
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
Logger.error(LOG_TAG, "Malformed uri, will be caught elsewhere.", e);
|
||||||
|
}
|
||||||
|
request.setHeader(new BasicHeader("Authorization", authHeader));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleHttpResponse(HttpResponse response) {
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
try {
|
||||||
|
switch (statusCode) {
|
||||||
|
case 200:
|
||||||
|
callbackDelegate.handleSuccess(true);
|
||||||
|
break;
|
||||||
|
case 401:
|
||||||
|
callbackDelegate.handleSuccess(false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
callbackDelegate.handleFailure(response);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
BaseResource.consumeEntity(response.getEntity());
|
||||||
|
Logger.info(LOG_TAG, "Released entity.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleHttpProtocolException(ClientProtocolException e) {
|
||||||
|
Logger.error(LOG_TAG, "Client protocol error.", e);
|
||||||
|
callbackDelegate.handleError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleHttpIOException(IOException e) {
|
||||||
|
Logger.error(LOG_TAG, "I/O exception.");
|
||||||
|
callbackDelegate.handleError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleTransportException(GeneralSecurityException e) {
|
||||||
|
Logger.error(LOG_TAG, "Transport exception.");
|
||||||
|
callbackDelegate.handleError(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AccountAuthenticator.runOnThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
httpResource.get();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public String makeAuthHeader(String usernameHash, String password) {
|
||||||
|
try {
|
||||||
|
return "Basic " + Base64.encodeBase64String((usernameHash + ":" + password).getBytes("UTF-8"));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
Logger.debug(LOG_TAG, "Unsupported encoding: UTF-8.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String makeAuthRequestUrl(String authServer, String usernameHash) {
|
||||||
|
return authServer + Constants.AUTH_SERVER_VERSION + usernameHash + "/" + Constants.AUTH_SERVER_SUFFIX;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.sync.setup.auth;
|
||||||
|
|
||||||
|
public enum AuthenticationResult {
|
||||||
|
SUCCESS, FAILURE_USERNAME, FAILURE_PASSWORD, FAILURE_SERVER, FAILURE_OTHER
|
||||||
|
}
|
12
mobile/android/base/sync/setup/auth/AuthenticatorStage.java
Normal file
12
mobile/android/base/sync/setup/auth/AuthenticatorStage.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.sync.setup.auth;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
public interface AuthenticatorStage {
|
||||||
|
public void execute(AccountAuthenticator aa) throws URISyntaxException, UnsupportedEncodingException;
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.sync.setup.auth;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.sync.Logger;
|
||||||
|
import org.mozilla.gecko.sync.net.BaseResource;
|
||||||
|
import org.mozilla.gecko.sync.net.SyncResourceDelegate;
|
||||||
|
import org.mozilla.gecko.sync.setup.Constants;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||||
|
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
|
||||||
|
|
||||||
|
public class EnsureUserExistenceStage implements AuthenticatorStage {
|
||||||
|
private final String LOG_TAG = "EnsureUserExistence";
|
||||||
|
|
||||||
|
public interface EnsureUserExistenceStageDelegate {
|
||||||
|
public void handleSuccess();
|
||||||
|
public void handleFailure(AuthenticationResult result);
|
||||||
|
public void handleError(Exception e);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void execute(final AccountAuthenticator aa) throws URISyntaxException,
|
||||||
|
UnsupportedEncodingException {
|
||||||
|
final EnsureUserExistenceStageDelegate callbackDelegate = new EnsureUserExistenceStageDelegate() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSuccess() {
|
||||||
|
// User exists; now determine auth node.
|
||||||
|
Log.d(LOG_TAG, "handleSuccess()");
|
||||||
|
aa.runNextStage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleFailure(AuthenticationResult result) {
|
||||||
|
aa.abort(result, new Exception("Failure in EnsureUser"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleError(Exception e) {
|
||||||
|
Logger.info(LOG_TAG, "Error checking for user existence.");
|
||||||
|
aa.abort(AuthenticationResult.FAILURE_SERVER, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
String userRequestUrl = aa.nodeServer + Constants.AUTH_NODE_PATHNAME + Constants.AUTH_NODE_VERSION + aa.username;
|
||||||
|
final BaseResource httpResource = new BaseResource(userRequestUrl);
|
||||||
|
httpResource.delegate = new SyncResourceDelegate(httpResource) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleHttpResponse(HttpResponse response) {
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
switch(statusCode) {
|
||||||
|
case 200:
|
||||||
|
try {
|
||||||
|
InputStream content = response.getEntity().getContent();
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(content, "UTF-8"), 1024);
|
||||||
|
String inUse = reader.readLine();
|
||||||
|
BaseResource.consumeReader(reader);
|
||||||
|
reader.close();
|
||||||
|
// Removed Logger.debug inUse, because stalling.
|
||||||
|
if (inUse.equals("1")) { // Username exists.
|
||||||
|
callbackDelegate.handleSuccess();
|
||||||
|
} else { // User does not exist.
|
||||||
|
Logger.info(LOG_TAG, "No such user.");
|
||||||
|
callbackDelegate.handleFailure(AuthenticationResult.FAILURE_USERNAME);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.error(LOG_TAG, "Failure in content parsing.", e);
|
||||||
|
callbackDelegate.handleFailure(AuthenticationResult.FAILURE_OTHER);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: // No other response is acceptable.
|
||||||
|
callbackDelegate.handleFailure(AuthenticationResult.FAILURE_OTHER);
|
||||||
|
}
|
||||||
|
Logger.debug(LOG_TAG, "Consuming entity.");
|
||||||
|
BaseResource.consumeEntity(response.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleHttpProtocolException(ClientProtocolException e) {
|
||||||
|
callbackDelegate.handleError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleHttpIOException(IOException e) {
|
||||||
|
callbackDelegate.handleError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleTransportException(GeneralSecurityException e) {
|
||||||
|
callbackDelegate.handleError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
// Make request.
|
||||||
|
AccountAuthenticator.runOnThread(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
httpResource.get();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
126
mobile/android/base/sync/setup/auth/FetchUserNodeStage.java
Normal file
126
mobile/android/base/sync/setup/auth/FetchUserNodeStage.java
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.sync.setup.auth;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.sync.Logger;
|
||||||
|
import org.mozilla.gecko.sync.net.BaseResource;
|
||||||
|
import org.mozilla.gecko.sync.net.SyncResourceDelegate;
|
||||||
|
import org.mozilla.gecko.sync.setup.Constants;
|
||||||
|
|
||||||
|
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||||
|
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
|
||||||
|
|
||||||
|
public class FetchUserNodeStage implements AuthenticatorStage {
|
||||||
|
private final String LOG_TAG = "FetchUserNodeStage";
|
||||||
|
|
||||||
|
public interface FetchNodeStageDelegate {
|
||||||
|
public void handleSuccess(String url);
|
||||||
|
public void handleFailure(HttpResponse response);
|
||||||
|
public void handleError(Exception e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(final AccountAuthenticator aa) throws URISyntaxException {
|
||||||
|
|
||||||
|
FetchNodeStageDelegate callbackDelegate = new FetchNodeStageDelegate() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSuccess(String server) {
|
||||||
|
if (server == null) { // No separate auth node; use server url.
|
||||||
|
Logger.debug(LOG_TAG, "Using server as auth node.");
|
||||||
|
aa.authServer = aa.nodeServer;
|
||||||
|
aa.runNextStage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!server.endsWith("/")) {
|
||||||
|
server += "/";
|
||||||
|
}
|
||||||
|
aa.authServer = server;
|
||||||
|
aa.runNextStage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleFailure(HttpResponse response) {
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
Logger.debug(LOG_TAG, "Failed to fetch user node, with status " + statusCode);
|
||||||
|
aa.abort(AuthenticationResult.FAILURE_OTHER, new Exception("HTTP " + statusCode + " error."));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleError(Exception e) {
|
||||||
|
Logger.debug(LOG_TAG, "Error in fetching node.");
|
||||||
|
aa.abort(AuthenticationResult.FAILURE_OTHER, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
String nodeRequestUrl = aa.nodeServer + Constants.AUTH_NODE_PATHNAME + Constants.AUTH_NODE_VERSION + aa.username + "/" + Constants.AUTH_NODE_SUFFIX;
|
||||||
|
Logger.debug(LOG_TAG, "nodeUrl: " + nodeRequestUrl);
|
||||||
|
final BaseResource httpResource = makeFetchNodeRequest(callbackDelegate, nodeRequestUrl);
|
||||||
|
// Make request on separate thread.
|
||||||
|
AccountAuthenticator.runOnThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
httpResource.get();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private BaseResource makeFetchNodeRequest(final FetchNodeStageDelegate callbackDelegate, String fetchNodeUrl) throws URISyntaxException {
|
||||||
|
// Fetch node containing user.
|
||||||
|
final BaseResource httpResource = new BaseResource(fetchNodeUrl);
|
||||||
|
httpResource.delegate = new SyncResourceDelegate(httpResource) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleHttpResponse(HttpResponse response) {
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
switch(statusCode) {
|
||||||
|
case 200:
|
||||||
|
try {
|
||||||
|
InputStream content = response.getEntity().getContent();
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(content, "UTF-8"), 1024);
|
||||||
|
String server = reader.readLine();
|
||||||
|
callbackDelegate.handleSuccess(server);
|
||||||
|
BaseResource.consumeReader(reader);
|
||||||
|
reader.close();
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
callbackDelegate.handleError(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
callbackDelegate.handleError(e);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 404: // Does not support auth nodes, use server instead.
|
||||||
|
callbackDelegate.handleSuccess(null);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// No other acceptable states.
|
||||||
|
callbackDelegate.handleFailure(response);
|
||||||
|
}
|
||||||
|
BaseResource.consumeEntity(response.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleHttpProtocolException(ClientProtocolException e) {
|
||||||
|
callbackDelegate.handleError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleHttpIOException(IOException e) {
|
||||||
|
callbackDelegate.handleError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleTransportException(GeneralSecurityException e) {
|
||||||
|
callbackDelegate.handleError(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return httpResource;
|
||||||
|
}
|
||||||
|
}
|
@ -168,6 +168,12 @@ sync/setup/activities/ActivityUtils.java
|
|||||||
sync/setup/activities/SetupFailureActivity.java
|
sync/setup/activities/SetupFailureActivity.java
|
||||||
sync/setup/activities/SetupSuccessActivity.java
|
sync/setup/activities/SetupSuccessActivity.java
|
||||||
sync/setup/activities/SetupSyncActivity.java
|
sync/setup/activities/SetupSyncActivity.java
|
||||||
|
sync/setup/auth/AccountAuthenticator.java
|
||||||
|
sync/setup/auth/AuthenticateAccountStage.java
|
||||||
|
sync/setup/auth/AuthenticationResult.java
|
||||||
|
sync/setup/auth/AuthenticatorStage.java
|
||||||
|
sync/setup/auth/EnsureUserExistenceStage.java
|
||||||
|
sync/setup/auth/FetchUserNodeStage.java
|
||||||
sync/setup/Constants.java
|
sync/setup/Constants.java
|
||||||
sync/setup/InvalidSyncKeyException.java
|
sync/setup/InvalidSyncKeyException.java
|
||||||
sync/setup/SyncAccounts.java
|
sync/setup/SyncAccounts.java
|
||||||
|
@ -73,4 +73,7 @@
|
|||||||
<string name="sync_new_tab">&new_tab;</string>
|
<string name="sync_new_tab">&new_tab;</string>
|
||||||
|
|
||||||
<!-- Incorrect settings and changing credentials. -->
|
<!-- Incorrect settings and changing credentials. -->
|
||||||
<string name="sync.new.recoverykey.status.incorrect">&sync.new.recoverykey.status.incorrect;</string>
|
<string name="sync_invalidcreds_label">&sync.invalidcreds.label;</string>
|
||||||
|
<string name="sync_invalidserver_label">&sync.invalidserver.label;</string>
|
||||||
|
<string name="sync_verifying_label">&sync.verifying.label;</string>
|
||||||
|
<string name="sync_new_recoverykey_status_incorrect">&sync.new.recoverykey.status.incorrect;</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user