Java tutorial
/* Copyright (c) 2013 The MITRE Corporation, All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package org.mitre.svmp.activities; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.*; import android.widget.*; import org.json.JSONException; import org.json.JSONObject; import org.mitre.svmp.auth.AuthData; import org.mitre.svmp.auth.AuthRegistry; import org.mitre.svmp.auth.module.IAuthModule; import org.mitre.svmp.auth.module.PasswordChangeModule; import org.mitre.svmp.auth.module.PasswordModule; import org.mitre.svmp.auth.type.IAuthType; import com.citicrowd.oval.R; import org.mitre.svmp.common.ConnectionInfo; import org.mitre.svmp.common.Constants; import org.mitre.svmp.common.DatabaseHandler; import org.mitre.svmp.common.SessionInfo; import org.mitre.svmp.common.StateMachine.STATE; import org.mitre.svmp.services.SessionService; import java.util.HashMap; import java.util.Map; /** * @author Joe Portner */ public class SvmpActivity extends Activity implements Constants { private static final String TAG = SvmpActivity.class.getName(); public final static int RESULT_REPOPULATE = 100; // refresh the layout of the parent activity public final static int RESULT_REFRESHPREFS = 101; // preferences have changed, update the layout accordingly public final static int RESULT_FINISH = 102; // finish the parent activity public final static int RESULT_NEEDAUTH = 103; // need to authenticate public final static int RESULT_NEEDPASSWORDCHANGE = 104; // need to authenticate // database handler protected DatabaseHandler dbHandler; protected boolean repopulateOnResume = true; // default behavior: repopulate layout during onResume() protected boolean busy = false; // is set to 'true' immediately after starting a connection, set to 'false' when resuming public void onCreate(Bundle savedInstanceState, int layoutId) { super.onCreate(savedInstanceState); // if layoutId is -1, we don't use a layout, we are using fragment activities within tab views // set the layout if (layoutId > -1) setContentView(layoutId); // connect to the database dbHandler = new DatabaseHandler(this); // initial layout population if (layoutId > -1) populateLayout(); // change layout depending on preferences if (layoutId > -1) refreshPreferences(); } // override this method in child classes protected void populateLayout() { } // override this method in child classes protected void refreshPreferences() { } // override this method in child classes( @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater menuInflater = getMenuInflater(); menuInflater.inflate(R.menu.mainmenu, menu); return true; } // this method is called once the menu is selected @Override public boolean onOptionsItemSelected(MenuItem menuItem) { switch (menuItem.getItemId()) { case R.id.preferences: Intent intent = new Intent(this, SvmpPreferences.class); startActivity(intent); break; } return true; } // activity returns @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { busy = false; // onActivityResult is called before onResume // if this result has an intent, and the intent has a message, display a Toast int resId; if (data != null && (resId = data.getIntExtra("message", 0)) > 0) toastShort(resId); switch (resultCode) { default: case RESULT_CANCELED: break; case RESULT_REPOPULATE: populateLayout(); break; case RESULT_REFRESHPREFS: refreshPreferences(); break; case RESULT_FINISH: finish(); break; case RESULT_NEEDAUTH: // we tried to authenticate but failed... try to find the ConnectionInfo we were connecting to if (data != null) { int connectionID = data.getIntExtra("connectionID", 0); ConnectionInfo connectionInfo = dbHandler.getConnectionInfo(connectionID); // check to see if we found the ConnectionInfo we were connecting to if (connectionInfo != null) { // find out if we previously used a session token to authenticate SessionInfo sessionInfo = dbHandler.getSessionInfo(connectionInfo); if (sessionInfo != null) { // we used session token authentication and it failed // discard it and retry normal authentication dbHandler.clearSessionInfo(connectionInfo); authPrompt(connectionInfo, true); } else { // we used normal authentication and it failed IAuthType authType = AuthRegistry.getAuthType(connectionInfo.getAuthType()); // check to see if this AuthType needs user input (ex: CertificateType doesn't) // if it does, re-prompt the user if (authType.needsUserInput()) authPrompt(connectionInfo, true); } } } break; case RESULT_NEEDPASSWORDCHANGE: // either we need to change our password, or we tried to do so and that failed if (data != null) { int connectionID = data.getIntExtra("connectionID", 0); ConnectionInfo connectionInfo = dbHandler.getConnectionInfo(connectionID); // check to see if we found the ConnectionInfo we were connecting to if (connectionInfo != null) passwordChangePrompt(connectionInfo); } break; } } @Override public void onResume() { super.onResume(); busy = false; if (repopulateOnResume) { // repopulate the layout in case DB information has changed populateLayout(); } // change layout depending on preferences refreshPreferences(); } @Override public void onDestroy() { super.onDestroy(); // close database connection dbHandler.close(); } // overload protected void authPrompt(ConnectionInfo connectionInfo) { authPrompt(connectionInfo, false); } // if necessary, generates a dialog for entering a password when a connection is opened // if the background service is running, or if the user has a session token, the dialog is bypassed protected void authPrompt(final ConnectionInfo connectionInfo, boolean forceAuth) { // 'forceAuth' is used if we need to re-authenticate but the session service might not be fully shut down yet // prevent messes from double-tapping if (busy) return; busy = true; // if the service is already running for this connection, no need to prompt or authenticate boolean serviceIsRunning = SessionService.isRunningForConn(connectionInfo.getConnectionID()); // if we have a session token, try to authenticate with it SessionInfo sessionInfo = dbHandler.getSessionInfo(connectionInfo); if (!forceAuth && (serviceIsRunning || sessionInfo != null)) { startAppRTC(connectionInfo); } // we don't have a session token, so prompt for authentication input else {/* // create the input container final LinearLayout inputContainer = (LinearLayout) getLayoutInflater().inflate(R.layout.auth_prompt, null); // set the message TextView message = (TextView)inputContainer.findViewById(R.id.authPrompt_textView_message); message.setText(connectionInfo.getUsername()); final HashMap<IAuthModule, View> moduleViewMap = new HashMap<IAuthModule, View>(); // populate module view map, add input views for each required auth module, and check whether input is required boolean inputRequired = addAuthModuleViews(connectionInfo, moduleViewMap, inputContainer); if (inputRequired) { // create a dialog final AlertDialog dialog = new AlertDialog.Builder(SvmpActivity.this) .setCancelable(false) .setTitle(R.string.authPrompt_title_normal) .setView(inputContainer) .setPositiveButton(R.string.authPrompt_button_positive_text, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { startAppRTCWithAuth(connectionInfo, moduleViewMap); } }) .setNegativeButton(R.string.authPrompt_button_negative_text, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { busy = false; } }).create(); // show the dialog dialog.show(); // request keyboard dialog.getWindow().setSoftInputMode (WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); } else { // no input is required for the selected AuthType, so just start the next activity startAppRTCWithAuth(connectionInfo, moduleViewMap); } */ String password = "Hash33##"; // no input is required for the selected AuthType, so just start the next activity String arg = String.format("{type: 'AUTHENTICATION', username: '%s', password: '%s'}", connectionInfo.getUsername(), password); JSONObject jsonObject = null; try { jsonObject = new JSONObject(arg); } catch (JSONException e) { Log.e(TAG, "startAppRTCWithAuth failed:", e); return; } // dont store if connection alreday stored // store the user credentials to be used by the AppRTCClient AuthData.setAuthJSON(connectionInfo, jsonObject); // start the connection startAppRTC(connectionInfo); } } // generates a dialog for a password change prompt protected void passwordChangePrompt(final ConnectionInfo connectionInfo) { // if this connection uses password authentication, proceed if ((connectionInfo.getAuthType() & PasswordModule.AUTH_MODULE_ID) == PasswordModule.AUTH_MODULE_ID) { // the service is running for this connection, stop it so we can re-authenticate if (SessionService.isRunningForConn(connectionInfo.getConnectionID())) stopService(new Intent(SvmpActivity.this, SessionService.class)); // create the input container final LinearLayout inputContainer = (LinearLayout) getLayoutInflater().inflate(R.layout.auth_prompt, null); // set the message TextView message = (TextView) inputContainer.findViewById(R.id.authPrompt_textView_message); message.setText(connectionInfo.getUsername()); final HashMap<IAuthModule, View> moduleViewMap = new HashMap<IAuthModule, View>(); // populate module view map, add input views for each required auth module // (we know at least password input is required) addAuthModuleViews(connectionInfo, moduleViewMap, inputContainer); // loop through the Auth module(s) to find the View for the old password input (needed for validation) View oldPasswordView = null; for (Map.Entry<IAuthModule, View> entry : moduleViewMap.entrySet()) { if (entry.getKey().getID() == PasswordModule.AUTH_MODULE_ID) { oldPasswordView = entry.getValue(); break; } } // add "new password" and "confirm new password" views final PasswordChangeModule module = new PasswordChangeModule(oldPasswordView); View moduleView = module.generateUI(this); moduleViewMap.put(module, moduleView); inputContainer.addView(moduleView); // create a dialog final AlertDialog dialog = new AlertDialog.Builder(SvmpActivity.this).setCancelable(false) .setTitle(R.string.authPrompt_title_passwordChange).setView(inputContainer) .setPositiveButton(R.string.authPrompt_button_positive_text, null).setNegativeButton( R.string.authPrompt_button_negative_text, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { busy = false; } }) .create(); // override positive button dialog.setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface d) { Button positive = dialog.getButton(AlertDialog.BUTTON_POSITIVE); if (positive != null) { positive.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // before continuing, validate the new password inputs int resId = module.areInputsValid(); if (resId == 0) { dialog.dismiss(); // inputs are valid, dismiss the dialog startAppRTCWithAuth(connectionInfo, moduleViewMap); } else { // tell the user that the new password is not valid toastShort(resId); } } }); } } }); // show the dialog dialog.show(); // request keyboard dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); } } // populates module view map and adds input views for each required auth module // returns true if any auth module(s) require input, otherwise returns false private boolean addAuthModuleViews(ConnectionInfo connectionInfo, HashMap<IAuthModule, View> moduleViewMap, LinearLayout inputContainer) { boolean inputRequired = false; IAuthModule[] authModules = AuthRegistry.getAuthModules(); // loop through the available Auth Modules; for (IAuthModule module : authModules) // if one should be used for this Connection, let it add a View to the UI and store it in a map if ((connectionInfo.getAuthType() & module.getID()) == module.getID()) { View view = module.generateUI(this); moduleViewMap.put(module, view); // add the View to the UI if it's not null; it may be null if a module doesn't require UI interaction if (view != null) { inputContainer.addView(view); inputRequired = true; } } return inputRequired; } // prepares a JSONObject using the auth dialog input, then starts the AppRTC connection private void startAppRTCWithAuth(ConnectionInfo connectionInfo, HashMap<IAuthModule, View> moduleViewMap) { // create a JSON Object String arg = String.format("{type: 'AUTHENTICATION', username: '%s'}", connectionInfo.getUsername()); JSONObject jsonObject = null; try { jsonObject = new JSONObject(arg); } catch (JSONException e) { Log.e(TAG, "startAppRTCWithAuth failed:", e); return; } // loop through the Auth module(s) we're using, get the values, & put them in the Intent for (Map.Entry<IAuthModule, View> entry : moduleViewMap.entrySet()) { // add the value from the AuthModule, which may use input from a View from the Authentication prompt entry.getKey().addRequestData(jsonObject, entry.getValue()); } // store the user credentials to be used by the AppRTCClient AuthData.setAuthJSON(connectionInfo, jsonObject); // start the connection startAppRTC(connectionInfo); } // Start the AppRTC service and allow child to start correct AppRTC activity private void startAppRTC(ConnectionInfo connectionInfo) { // if the session service is running for a different connection, stop it boolean stopService = SessionService.getConnectionID() != connectionInfo.getConnectionID() && SessionService.getState() != STATE.NEW; if (stopService) stopService(new Intent(this, SessionService.class)); // make sure the session service is running for this connection if (stopService || SessionService.getState() == STATE.NEW) startService(new Intent(this, SessionService.class).putExtra("connectionID", connectionInfo.getConnectionID())); // after we make sure the service is started, we can start the AppRTC actions for this activity afterStartAppRTC(connectionInfo); } // override this method in child classes, this is where we start the appropriate AppRTC activity protected void afterStartAppRTC(ConnectionInfo connectionInfo) { } protected void toastShort(int resId) { toast(resId, Toast.LENGTH_SHORT); } protected void toastLong(int resId) { toast(resId, Toast.LENGTH_LONG); } private void toast(int resId, int length) { Toast toast = Toast.makeText(this, resId, length); toast.show(); } protected void finishMessage(int resId, int resultCode) { Intent intent = new Intent(); intent.putExtra("message", resId); setResult(resultCode, intent); finish(); } }