org.forgerock.openam.authentication.modules.impersonation.ImpersonationModule.java Source code

Java tutorial

Introduction

Here is the source code for org.forgerock.openam.authentication.modules.impersonation.ImpersonationModule.java

Source

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2009 Sun Microsystems Inc. All Rights Reserved
 *
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License). You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at
 * https://opensso.dev.java.net/public/CDDLv1.0.html or
 * opensso/legal/CDDLv1.0.txt
 * See the License for the specific language governing
 * permission and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at opensso/legal/CDDLv1.0.txt.
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 */

/*
 * author Javed Shah
 * 
 */

package org.forgerock.openam.authentication.modules.impersonation;

import com.sun.identity.authentication.spi.AMLoginModule;
import com.sun.identity.idm.AMIdentity;
import com.sun.identity.idm.AMIdentityRepository;
import com.sun.identity.idm.IdRepoException;
import com.sun.identity.idm.IdSearchControl;
import com.sun.identity.idm.IdSearchResults;
import com.sun.identity.idm.IdType;
import com.sun.identity.shared.debug.Debug;
import com.iplanet.sso.SSOException;
import com.sun.identity.shared.datastruct.CollectionHelper;
import com.iplanet.sso.SSOException;
import com.iplanet.sso.SSOToken;
import com.sun.identity.authentication.util.ISAuthConstants;
import com.sun.identity.authentication.spi.AuthLoginException;
import com.sun.identity.authentication.spi.InvalidPasswordException;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.login.LoginException;
import javax.servlet.http.Cookie;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;

import org.apache.http.HttpEntity;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.json.simple.JSONArray;
import org.json.simple.parser.JSONParser;
import org.json.simple.JSONObject;
import org.json.simple.parser.ParseException;

import javax.ws.rs.core.HttpHeaders;

import java.util.Map;

import org.json.simple.parser.JSONParser;
import org.json.simple.JSONObject;

public class ImpersonationModule extends AMLoginModule {

    private static final String AUTH_MODULE_NAME = "amAuthImpersonation";

    private static final Debug debug = Debug.getInstance(AUTH_MODULE_NAME);
    // orders defined in the callbacks file
    private String userResponse;
    protected String validatedUserID;
    private String userName;
    private Subject subject = null;
    private String groupName;
    private String question;
    private String resourceSet;

    private String policyRealm;
    private String authnRealm;
    private String policySet;
    private String checkGroupMembership;
    private String openamServer;

    private Map options;
    private ResourceBundle bundle;
    public Map currentConfig;
    private Map sharedState;
    private String currentConfigName;
    // Name of the resource bundle
    private final static String amAuthImpersonation = "amAuthImpersonation";
    private static final String AUTHLEVEL = "iplanet-am-auth-impersonation-auth-level";
    private static final String ATTR_NAME = "iplanet-am-auth-impersonation-group-name";
    private static final String IMPERSONATION_ID_STRING = "iplanet-am-auth-impersonation-id";
    private static final String RESOURCE_SET = "iplanet-am-auth-resource-set";
    private static final String POLICY_REALM = "iplanet-am-auth-policy-realm";
    private static final String AUTHN_REALM = "iplanet-am-auth-authentication-realm";
    private static final String POLICY_SET = "iplanet-am-auth-policy-set-name";
    private static final String CHECK_GROUP_MEMBERSHIP = "iplanet-am-auth-check-group-membership";
    private static final String OPENAM_SERVER = "iplanet-am-auth-openam-server";

    /**
     * Constructs an instance of the ChallengeResponseModule.
     */
    public ImpersonationModule() {
        super();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void init(Subject subject, Map sharedState, Map options) {
        String authLevel = CollectionHelper.getMapAttr(options, AUTHLEVEL);

        if (authLevel != null) {
            try {
                setAuthLevel(Integer.parseInt(authLevel));
            } catch (Exception e) {
                debug.error("Unable to set auth level " + authLevel, e);
            }
        }
        currentConfig = options;
        currentConfigName = (String) options.get(ISAuthConstants.MODULE_INSTANCE_NAME);
        try {
            userName = (String) sharedState.get(getUserKey());
        } catch (Exception e) {
            debug.error("Adaptive.init() : " + "Unable to set userName : ", e);
        }
        this.options = options;
        initParams();

        System.out.println("username -> " + userName);
        System.out.println("sharedState -> " + sharedState);
        System.out.println("options -> " + options);

        this.sharedState = sharedState;
        this.subject = subject;

        bundle = amCache.getResBundle(amAuthImpersonation, getLoginLocale());
    }

    private void initParams() {
        groupName = getOption(options, ATTR_NAME);
        System.out.println("attr -> " + groupName);

        question = getOption(options, IMPERSONATION_ID_STRING);
        System.out.println("q -> " + question);

        resourceSet = getOption(options, RESOURCE_SET);
        System.out.println("resourceSet -> " + resourceSet);

        policyRealm = getOption(options, POLICY_REALM);
        System.out.println("policyRealm -> " + policyRealm);

        authnRealm = getOption(options, AUTHN_REALM);
        System.out.println("authnRealm -> " + authnRealm);

        policySet = getOption(options, POLICY_SET);
        System.out.println("policySet -> " + policySet);

        checkGroupMembership = getOption(options, CHECK_GROUP_MEMBERSHIP);
        System.out.println("checkGroupMembership -> " + checkGroupMembership);

        openamServer = getOption(options, OPENAM_SERVER);
        System.out.println("openamServer -> " + openamServer);

    }

    protected String getOption(Map m, String i) {
        return CollectionHelper.getMapAttr(m, i);
    }

    protected boolean getOptionAsBoolean(Map m, String i) {
        String s = null;
        s = CollectionHelper.getMapAttr(m, i);
        return Boolean.parseBoolean(s);
    }

    protected int getOptionAsInteger(Map m, String i) {
        String s = null;
        int retVal = 0;

        s = CollectionHelper.getMapAttr(m, i);
        if (s != null) {
            retVal = Integer.parseInt(s);
        }
        return retVal;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int process(Callback[] callbacks, int state) throws LoginException {

        System.out.println("INSIDE process of ImpersonationModule, state: " + state);

        if (debug.messageEnabled()) {
            debug.message("ImpersonationModule::process state: " + state);
        }
        int nextState = ISAuthConstants.LOGIN_SUCCEED;
        switch (state) {
        case 4:
            System.out.println("state 4");
            // error condition, show page
            throw new AuthLoginException("Incorrect authorization!");
        case 1:
            substituteUIStrings();
            //nextState = ISAuthConstants.LOGIN_SUCCEED;
            nextState = 2;
            break;
        case 3:
            userName = ((NameCallback) callbacks[0]).getName();
            String userPassword = String.valueOf(((PasswordCallback) callbacks[1]).getPassword());
            if (userPassword == null || userPassword.length() == 0) {
                if (debug.messageEnabled()) {
                    debug.message("Impersonation.process: Password is null/empty");
                }
                throw new InvalidPasswordException("amAuth", "invalidPasswd", null);
            }
            //store username password both in success and failure case
            storeUsernamePasswd(userName, userPassword);

            AMIdentityRepository idrepo = getAMIdentityRepository(getRequestOrg());
            Callback[] idCallbacks = new Callback[2];
            try {
                idCallbacks = callbacks;
                boolean success = idrepo.authenticate(idCallbacks);
                // proceed if admin authenticated
                if (success) {

                    validatedUserID = null;
                    // 1. Search for group membership
                    if (checkGroupMembership.equalsIgnoreCase("true")) {

                        AMIdentity amIdentity = getGroupIdentity(groupName);
                        Set<String> attr = (Set<String>) amIdentity.getAttribute("uniqueMember");
                        Iterator<String> i = attr.iterator();
                        // check if sign on user is memberof group 
                        while (i.hasNext()) {
                            try {
                                String member = (String) i.next();
                                System.out.println("value of attribute: " + member);
                                // check previously authenticated user is a memberof
                                userName = (String) sharedState.get(getUserKey());
                                System.out.println("userName to check: " + userName);
                                if (member.indexOf(userName) != -1) {
                                    System.out.println("match found! admin: " + userName
                                            + " allowed to impersonate user: " + userResponse);

                                    // for sanity, ensure the supplied userid is a valid one
                                    try {
                                        validatedUserID = userResponse; // create session for the userid provided by admin 
                                        AMIdentity impersonatedId = getIdentity(validatedUserID);
                                        // optionally, we default to LOGIN_SUCCEED
                                        //nextState = ISAuthConstants.LOGIN_SUCCEED;

                                    } catch (Exception ex) {
                                        System.out.println("Exception thrown validating impersonated userid " + ex);
                                        throw new AuthLoginException(
                                                "EImpersonationModule: Exception thrown validating impersonated userid");
                                    }
                                    break;
                                }
                            } catch (Exception e) {
                                System.out.println("Cannot parse json. " + e);
                                throw new AuthLoginException(
                                        "Cannot parse json..unable to read attribtue value using amIdentity");
                            }
                        }
                        if (checkGroupMembership.equalsIgnoreCase("true") && validatedUserID == null) {
                            // Admin was not authorized to impersonate other users
                            nextState = 4;
                            throw new AuthLoginException("Admin was not authorized to impersonate other users");
                        }
                    }

                    // 2. Check for policy evaluation
                    // get the ssoToken first, for use with the REST call
                    String url = openamServer + "/json/" + authnRealm + "/authenticate";
                    HttpClient httpClient = HttpClientBuilder.create().build();
                    HttpPost postRequest = new HttpPost(url);
                    String cookie = "";
                    try {

                        System.out.println("BEFORE policy1 eval...");

                        postRequest.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");

                        // TBD: replace with admin provided username and password- stick this code into the impersonate auth module
                        postRequest.setHeader("X-OpenAM-Username", userName);
                        postRequest.setHeader("X-OpenAM-Password", userPassword);

                        StringEntity input = new StringEntity("{}");
                        input.setContentType("application/json");
                        postRequest.setEntity(input);

                        HttpResponse response = httpClient.execute(postRequest);

                        String json = EntityUtils.toString(response.getEntity(), "UTF-8");
                        System.out.println("json/" + authnRealm + "/authenticate response-> " + json);
                        try {
                            JSONParser parser = new JSONParser();
                            Object resultObject = parser.parse(json);

                            if (resultObject instanceof JSONArray) {
                                JSONArray array = (JSONArray) resultObject;
                                for (Object object : array) {
                                    JSONObject obj = (JSONObject) object;
                                    System.out.println("jsonarray-> " + obj);
                                }

                            } else if (resultObject instanceof JSONObject) {
                                JSONObject obj = (JSONObject) resultObject;
                                System.out.println("tokenId-> " + obj.get("tokenId"));
                                cookie = (String) obj.get("tokenId");
                            }

                        } catch (Exception e) {
                            // TODO: handle exception
                            nextState = 4;
                        }
                        System.out.println("AFTER policy1 eval...");
                        // Headers
                        org.apache.http.Header[] headers = response.getAllHeaders();
                        for (int j = 0; j < headers.length; j++) {
                            System.out.println(headers[j]);
                        }

                    } catch (Exception e) {
                        System.err.println("Fatal  error: " + e.getMessage());
                        e.printStackTrace();
                        nextState = 4;
                    }

                    System.out.println("BEFORE policy2 eval...");

                    /*Cookie[] cookies = getHttpServletRequest().getCookies();
                        
                        
                    if (cookies != null) {
                      for (int m = 0; m < cookies.length; m++) {
                         System.out.println(cookies[m].getName() +":"+cookies[m].getValue());
                        if (cookies[m].getName().equalsIgnoreCase("iPlanetDirectoryPro")) {
                          cookie = cookies[m].getValue();
                          break;
                        }
                      }
                    }*/
                    url = openamServer + "/json/" + policyRealm + "/policies?_action=evaluate";
                    //httpClient = HttpClientBuilder.create().build();
                    postRequest = new HttpPost(url);
                    try {

                        postRequest.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
                        postRequest.setHeader("iPlanetDirectoryPro", cookie);

                        StringEntity input = new StringEntity(
                                "{\"resources\": [\"" + new URL(resourceSet) + "\"],\"application\":\"" + policySet
                                        + "\", \"subject\": {\"ssoToken\":\"" + cookie + "\"}}");

                        System.out.println("stringentity-> " + getStringFromInputStream(input.getContent()));
                        input.setContentType("application/json");
                        postRequest.setEntity(input);

                        HttpResponse response = httpClient.execute(postRequest);

                        String json = EntityUtils.toString(response.getEntity(), "UTF-8");
                        System.out.println("json/" + policyRealm + "/policies?_action=evaluate response-> " + json);
                        try {
                            JSONParser parser = new JSONParser();
                            Object resultObject = parser.parse(json);

                            if (resultObject instanceof JSONArray) {
                                JSONArray array = (JSONArray) resultObject;
                                for (Object object : array) {
                                    JSONObject obj = (JSONObject) object;
                                    System.out.println("jsonarray-> " + obj);

                                    JSONObject actions = (JSONObject) obj.get("actions");
                                    Boolean actionGet = (Boolean) actions.get("GET");
                                    Boolean actionPost = (Boolean) actions.get("POST");
                                    System.out.println("actionGet : " + actionGet);
                                    System.out.println("actionPost : " + actionPost);

                                    if (actionGet != null && actionGet.equals(true) && actionPost != null
                                            && actionPost.equals(true)) {
                                        nextState = ISAuthConstants.LOGIN_SUCCEED;
                                    } else {
                                        System.out.println("actionget and actionpost are not true");
                                        nextState = 4;
                                    }
                                }

                            } else {
                                // something went wrong!
                                System.out.println("resultObject is not a JSONArray");
                                nextState = 4;
                            }

                        } catch (Exception e) {
                            // TODO: handle exception
                            nextState = 4;
                            System.out.println("exception invoking json parsing routine");
                            e.printStackTrace();
                        }
                        System.out.println("AFTER policy2 eval...");
                        // Headers
                        org.apache.http.Header[] headers = response.getAllHeaders();
                        for (int j = 0; j < headers.length; j++) {
                            System.out.println(headers[j]);
                        }

                        // logout the administrator
                        url = openamServer + "/json/" + authnRealm + "/sessions/?_action=logout";
                        System.out.println("destroying admin session: " + url);
                        postRequest = new HttpPost(url);
                        try {
                            postRequest.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
                            postRequest.setHeader("iPlanetDirectoryPro", cookie);
                            response = httpClient.execute(postRequest);
                            try {
                                JSONParser parser = new JSONParser();
                                Object resultObject = parser.parse(json);
                                if (resultObject instanceof JSONArray) {
                                    JSONArray array = (JSONArray) resultObject;
                                    for (Object object : array) {
                                        JSONObject obj = (JSONObject) object;
                                        System.out.println("logout response-array-> " + obj);
                                    }
                                } else {
                                    JSONObject obj = (JSONObject) resultObject;
                                    System.out.println("logout response-> " + obj);
                                }
                            } catch (Exception e) {
                                System.out.println("unable to read logout json response");
                                e.printStackTrace();
                            }

                        } catch (Exception e) {
                            System.out.println(
                                    "Issue destroying administrator's session, still proceeding with impersonation");
                            e.printStackTrace();

                        }

                    } catch (Exception e) {
                        System.err.println("Fatal  error: " + e.getMessage());
                        e.printStackTrace();
                        nextState = 4;
                    }

                    // else of admin successful login   
                } else {
                    System.out.println("username:password read from callback: " + userName + " : " + userPassword);
                    nextState = 4;
                    throw new AuthLoginException(amAuthImpersonation, "authFailed", null);
                }
            } catch (com.sun.identity.idm.IdRepoException idrepox) {
                System.out.println("IdRepoException thrown " + idrepox);
                nextState = 4;
                throw new AuthLoginException("IdRepoException thrown from Impersonation module");
            } catch (SSOException ssoe) {
                System.out.println("SSOException thrown " + ssoe);
                nextState = 4;
                throw new AuthLoginException("SSOException thrown from ImpersonationModule module");
            }

            break;
        case 2:
            javax.security.auth.callback.NameCallback response = (javax.security.auth.callback.NameCallback) callbacks[0];
            userResponse = new String(response.getName());
            // check the response against OpenDJ
            System.out.println("user to impersonate : state 2: " + userResponse);

            nextState = 3;

            break;
        default:
            throw new AuthLoginException("invalid state");

        }
        return nextState;
    }

    /**
     * Gets the user's AMIdentity from LDAP.
     *
     * @param userName The user's name.
     * @return The AMIdentity for the user.
     */
    public AMIdentity getIdentity(String userName) {
        AMIdentity amIdentity = null;
        AMIdentityRepository amIdRepo = getAMIdentityRepository(getRequestOrg());

        IdSearchControl idsc = new IdSearchControl();
        idsc.setAllReturnAttributes(true);
        Set<AMIdentity> results = Collections.EMPTY_SET;

        try {
            idsc.setMaxResults(0);
            IdSearchResults searchResults = amIdRepo.searchIdentities(IdType.USER, userName, idsc);
            if (searchResults != null) {
                results = searchResults.getSearchResults();
                System.out.println("results: " + results);
            }

            if (results.isEmpty()) {
                throw new IdRepoException("getIdentity : User " + userName + " is not found");
            } else if (results.size() > 1) {
                throw new IdRepoException("getIdentity : More than one user found for the userName " + userName);
            }

            amIdentity = results.iterator().next();
        } catch (IdRepoException e) {
            debug.error("Error searching Identities with username : " + userName, e);
        } catch (SSOException e) {
            debug.error("Module exception : ", e);
        }

        return amIdentity;
    }

    /**
     * Gets the group's AMIdentity from LDAP.
     *
     * @param groupName The group name.
     * @return The AMIdentity for the group.
     */
    public AMIdentity getGroupIdentity(String groupName) {
        AMIdentity amIdentity = null;
        AMIdentityRepository amIdRepo = getAMIdentityRepository(getRequestOrg());

        IdSearchControl idsc = new IdSearchControl();
        idsc.setAllReturnAttributes(true);
        Set<AMIdentity> results = Collections.EMPTY_SET;

        try {
            idsc.setMaxResults(0);
            IdSearchResults searchResults = amIdRepo.searchIdentities(IdType.GROUP, groupName, idsc);
            if (searchResults != null) {
                results = searchResults.getSearchResults();
                System.out.println("results: " + results);
            }

            if (results.isEmpty()) {
                throw new IdRepoException("getIdentity : group " + groupName + " is not found");
            } else if (results.size() > 1) {
                throw new IdRepoException(
                        "getIdentity : More than one result found for the groupName " + groupName);
            }

            amIdentity = results.iterator().next();
        } catch (IdRepoException e) {
            debug.error("Error searching Identities with groupName : " + groupName, e);
        } catch (SSOException e) {
            debug.error("Module exception : ", e);
        }

        return amIdentity;
    }

    // convert InputStream to String
    private static String getStringFromInputStream(InputStream is) {

        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();

        String line;
        try {

            br = new BufferedReader(new InputStreamReader(is));
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return sb.toString();

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Principal getPrincipal() {
        return new ImpersonationModulePrincipal(validatedUserID);
    }

    private void substituteUIStrings() throws AuthLoginException {
        // Get service specific attribute configured in OpenAM
        System.out.println("question from config: " + question);

        Callback[] crquestion = getCallback(2);

        replaceCallback(2, 0, new NameCallback(question));
    }

}