From 802e6aa9ed44742612a9b89e8f31bfc40f3bf01e Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Mon, 15 Oct 2012 00:41:14 +0530 Subject: [PATCH] Added Commons AccountManager (Authentication Provider!) Also updated version of java-mwapi to support cookie based auth --- AndroidManifest.xml | 23 +++- libs/java-mwapi.jar | Bin 682118 -> 683139 bytes .../wikimedia/commons/CommonsApplication.java | 17 ++- src/org/wikimedia/commons/ShareActivity.java | 1 + src/org/wikimedia/commons/UploadService.java | 5 +- .../commons/{ => auth}/LoginActivity.java | 77 ++++++++---- .../auth/WikiAccountAuthenticator.java | 119 ++++++++++++++++++ .../auth/WikiAccountAuthenticatorService.java | 23 ++++ 8 files changed, 227 insertions(+), 38 deletions(-) rename src/org/wikimedia/commons/{ => auth}/LoginActivity.java (55%) create mode 100644 src/org/wikimedia/commons/auth/WikiAccountAuthenticator.java create mode 100644 src/org/wikimedia/commons/auth/WikiAccountAuthenticatorService.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 2e4bada1b..e73336ac8 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -8,6 +8,9 @@ android:targetSdkVersion="15" /> + + + @@ -24,11 +27,27 @@ android:label="@string/title_activity_share" > + + - + + + + + + + + + + \ No newline at end of file diff --git a/libs/java-mwapi.jar b/libs/java-mwapi.jar index 33e68af74f048162777505cf9ba50b37b2ce6b01..77ce4d244f4d1f7a5a1fd0b383b1a90ca9e2001f 100644 GIT binary patch delta 3909 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 T008qFmp^C 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 { Activity context; ProgressDialog dialog; + String username; + String password; + @Override protected void onPostExecute(String result) { super.onPostExecute(result); - if(result.equals("Success")) { + if (result.equals("Success")) { dialog.cancel(); Toast successToast = Toast.makeText(context, R.string.login_success, Toast.LENGTH_SHORT); successToast.show(); + Account account = new Account(username, WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE); + boolean accountCreated = AccountManager.get(context).addAccountExplicitly(account, password, null); + + Bundle extras = context.getIntent().getExtras(); + if (extras != null) { + if (accountCreated) { // Pass the new account back to the account manager + AccountAuthenticatorResponse response = extras.getParcelable(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE); + Bundle authResult = new Bundle(); + authResult.putString(AccountManager.KEY_ACCOUNT_NAME, username); + authResult.putString(AccountManager.KEY_ACCOUNT_TYPE, WikiAccountAuthenticator.COMMONS_ACCOUNT_TYPE); + response.onResult(authResult); + } + } context.finish(); } else { Toast failureToast = Toast.makeText(context, R.string.login_failed, Toast.LENGTH_LONG); failureToast.show(); } - + } @Override protected void onPreExecute() { super.onPreExecute(); - dialog = new ProgressDialog(context); + dialog = new ProgressDialog(context); dialog.setIndeterminate(true); dialog.setTitle(getString(R.string.logging_in_title)); dialog.setMessage(getString(R.string.logging_in_message)); dialog.show(); } - + LoginTask(Activity context) { this.context = context; } - + @Override protected String doInBackground(String... params) { - String username = params[0]; - String password = params[1]; + username = params[0]; + password = params[1]; try { return app.getApi().login(username, password); } catch (IOException e) { @@ -64,24 +93,24 @@ public class LoginActivity extends Activity { return "Failure"; } } - + } - + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - app = (CommonsApplication)this.getApplicationContext(); + app = (CommonsApplication) this.getApplicationContext(); setContentView(R.layout.activity_login); - loginButton = (Button)findViewById(R.id.loginButton); - usernameEdit = (EditText)findViewById(R.id.loginUsername); - passwordEdit = (EditText)findViewById(R.id.loginPassword); + loginButton = (Button) findViewById(R.id.loginButton); + usernameEdit = (EditText) findViewById(R.id.loginUsername); + passwordEdit = (EditText) findViewById(R.id.loginPassword); final Activity that = this; loginButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String username = usernameEdit.getText().toString(); String password = passwordEdit.getText().toString(); - + LoginTask task = new LoginTask(that); task.execute(username, password); } @@ -94,18 +123,14 @@ public class LoginActivity extends Activity { return true; } - @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case android.R.id.home: - NavUtils.navigateUpFromSameTask(this); - return true; + case android.R.id.home: + NavUtils.navigateUpFromSameTask(this); + return true; } return super.onOptionsItemSelected(item); } - - - } diff --git a/src/org/wikimedia/commons/auth/WikiAccountAuthenticator.java b/src/org/wikimedia/commons/auth/WikiAccountAuthenticator.java new file mode 100644 index 000000000..1da6d5259 --- /dev/null +++ b/src/org/wikimedia/commons/auth/WikiAccountAuthenticator.java @@ -0,0 +1,119 @@ +package org.wikimedia.commons.auth; + +import java.io.IOException; + +import org.mediawiki.api.ApiResult; +import org.mediawiki.api.MWApi; +import org.wikimedia.commons.CommonsApplication; + +import android.accounts.AbstractAccountAuthenticator; +import android.accounts.Account; +import android.accounts.AccountAuthenticatorResponse; +import android.accounts.AccountManager; +import android.accounts.NetworkErrorException; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +public class WikiAccountAuthenticator extends AbstractAccountAuthenticator { + + public static final String COMMONS_ACCOUNT_TYPE = "org.wikimedia.commons"; + private Context context; + public WikiAccountAuthenticator(Context context) { + super(context); + this.context = context; + } + + @Override + public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException { + // TODO Auto-generated method stub + final Intent intent = new Intent(context, LoginActivity.class); + intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); + final Bundle bundle = new Bundle(); + bundle.putParcelable(AccountManager.KEY_INTENT, intent); + return bundle; + } + + @Override + public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException { + // TODO Auto-generated method stub + return null; + } + + @Override + public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { + // TODO Auto-generated method stub + return null; + } + + private String getAuthCookie(String username, String password) throws IOException { + MWApi api = ((CommonsApplication)context.getApplicationContext()).createMWApi(); + String result = api.login(username, password); + if(result.equals("Success")) { + return api.getAuthCookie(); + } else { + return null; + } + } + @Override + public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { + // If the caller requested an authToken type we don't support, then + // return an error + if (!authTokenType.equals(COMMONS_ACCOUNT_TYPE)) { + final Bundle result = new Bundle(); + result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType"); + return result; + } + + // Extract the username and password from the Account Manager, and ask + // the server for an appropriate AuthToken. + final AccountManager am = AccountManager.get(context); + final String password = am.getPassword(account); + if (password != null) { + String authCookie; + try { + authCookie = getAuthCookie(account.name, password); + } catch (IOException e) { + // Network error! + throw new NetworkErrorException(e); + } + if (authCookie != null) { + final Bundle result = new Bundle(); + result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); + result.putString(AccountManager.KEY_ACCOUNT_TYPE, COMMONS_ACCOUNT_TYPE); + result.putString(AccountManager.KEY_AUTHTOKEN, authCookie); + return result; + } + } + + // If we get here, then we couldn't access the user's password - so we + // need to re-prompt them for their credentials. We do that by creating + // an intent to display our AuthenticatorActivity panel. + final Intent intent = new Intent(context, LoginActivity.class); + intent.putExtra(LoginActivity.PARAM_USERNAME, account.name); + intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); + final Bundle bundle = new Bundle(); + bundle.putParcelable(AccountManager.KEY_INTENT, intent); + return bundle; + } + + @Override + public String getAuthTokenLabel(String authTokenType) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException { + final Bundle result = new Bundle(); + result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false); + return result; + } + + @Override + public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/org/wikimedia/commons/auth/WikiAccountAuthenticatorService.java b/src/org/wikimedia/commons/auth/WikiAccountAuthenticatorService.java new file mode 100644 index 000000000..c22c970aa --- /dev/null +++ b/src/org/wikimedia/commons/auth/WikiAccountAuthenticatorService.java @@ -0,0 +1,23 @@ +package org.wikimedia.commons.auth; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class WikiAccountAuthenticatorService extends Service{ + + private static WikiAccountAuthenticator wikiAccountAuthenticator = null; + + @Override + public IBinder onBind(Intent intent) { + if (!intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) { + return null; + } + + if(wikiAccountAuthenticator == null) { + wikiAccountAuthenticator = new WikiAccountAuthenticator(this); + } + return wikiAccountAuthenticator.getIBinder(); + } + +}