org.mitre.svmp.activities.SvmpActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.mitre.svmp.activities.SvmpActivity.java

Source

/*
 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();
    }
}