package test;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.Principal;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.ChoiceCallback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextInputCallback;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import com.iplanet.am.util.SystemProperties;
import com.iplanet.dpro.session.service.SessionService;
import com.iplanet.sso.SSOException;
import com.iplanet.sso.SSOToken;
import com.iplanet.sso.SSOTokenManager;
import com.sun.identity.authentication.AuthContext;
import com.sun.identity.authentication.spi.AuthLoginException;
import com.sun.identity.idm.AMIdentity;
import com.sun.identity.idm.IdRepoException;
import com.sun.identity.idm.IdUtils;


public class RemoteLoginTest {
	public static void main(String[] args) throws SSOException, IdRepoException, Exception {
		Properties props = new Properties();
		props.load(new FileInputStream("/home/dave/opensso-client/AMConfig-client.properties"));
		SystemProperties.initializeProperties(props);
		RemoteLoginTest login = new RemoteLoginTest();
		SessionService sessServ = SessionService.getSessionService();
		System.out.println("Got session service: "+sessServ);
		SSOTokenManager manager = SSOTokenManager.getInstance();
		AuthContext lc = login.getAuthContext();
		if (login.login(lc)) {
			dumpSubject(lc.getSubject());
			SSOToken token = lc.getSSOToken();
			try {
				AMIdentity ident = IdUtils.getIdentity(token);
				dumpIdentity(ident);
			} finally {
				manager.destroyToken(token);
			}
			login.logout(lc);
		}
	}

    private static void dumpIdentity(AMIdentity ident) throws SSOException, IdRepoException {
        System.out.println("Identity: "+ident);
        System.out.println(" - name: "+ident.getName());
        System.out.println(" - realm: "+ident.getRealm());
        Set attrNames = new HashSet();
        attrNames.addAll(Arrays.asList(new String[] {
        	"givenname",
        	"userpassword",
        	"id",
        	"uid",
        	"sn",
        	"contactAddress.premise"
        }));
        Map attrs = ident.getAttributes(attrNames);
        System.out.println(" - attributes: "+attrs);
        Set premiseValues = (Set)attrs.get("contactAddress.premise");
        premiseValues.add("47a");
        ident.setAttributes(attrs);
        ident.store();
    }

    private static void dumpSubject(Subject subj) {
        System.out.println("Subject "+subj);
        Set<Principal> principals = subj.getPrincipals();
        for (Principal principal : principals) {
            dumpPrincipal(principal);
        }
    }

    private static void dumpPrincipal(Principal principal) {
        System.out.println("Principal: " + principal.getName());
        System.out.println(" - class: "+principal.getClass());
    }

    protected AuthContext getAuthContext()
        throws AuthLoginException {
        AuthContext lc = new AuthContext("test");
        debugMessage("module instance names: "+lc.getModuleInstanceNames());
        lc.login();
        debugMessage("Obtained login context");
        return lc;
    }

    private void addLoginCallbackMessage(Callback[] callbacks)
    throws UnsupportedCallbackException {
        int i = 0;
        try {
            for (i = 0; i < callbacks.length; i++) {
                if (callbacks[i] instanceof TextOutputCallback) {
                    handleTextOutputCallback((TextOutputCallback)callbacks[i]);
                } else if (callbacks[i] instanceof NameCallback) {
                    handleNameCallback((NameCallback)callbacks[i]);
                } else if (callbacks[i] instanceof PasswordCallback) {
                    handlePasswordCallback((PasswordCallback)callbacks[i]);
                } else if (callbacks[i] instanceof TextInputCallback) {
                    handleTextInputCallback((TextInputCallback)callbacks[i]);
                } else if (callbacks[i] instanceof ChoiceCallback) {
                    handleChoiceCallback((ChoiceCallback)callbacks[i]);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new UnsupportedCallbackException(callbacks[i],e.getMessage());
        }
    }
    
    private void handleTextOutputCallback(TextOutputCallback toc) {
        debugMessage("Got TextOutputCallback");
        // display the message according to the specified type
        
        switch (toc.getMessageType()) {
            case TextOutputCallback.INFORMATION:
                debugMessage(toc.getMessage());
                break;
            case TextOutputCallback.ERROR:
                debugMessage("ERROR: " + toc.getMessage());
                break;
            case TextOutputCallback.WARNING:
                debugMessage("WARNING: " + toc.getMessage());
                break;
            default:
                debugMessage("Unsupported message type: " +
                    toc.getMessageType());
        }
    }
    
    private void handleNameCallback(NameCallback nc)
        throws IOException {
        // prompt the user for a username
        System.out.print(nc.getPrompt());
        System.out.flush();
        nc.setName((new BufferedReader
            (new InputStreamReader(System.in))).readLine());
    }
    
    private void handleTextInputCallback(TextInputCallback tic)
        throws IOException {
        // prompt for text input
        System.out.print(tic.getPrompt());
        System.out.flush();
        tic.setText((new BufferedReader
            (new InputStreamReader(System.in))).readLine());
    }
    
    private void handlePasswordCallback(PasswordCallback pc)
        throws IOException {
        // prompt the user for sensitive information
        System.out.print(pc.getPrompt());
        System.out.flush();
        String passwd = (new BufferedReader(new InputStreamReader(System.in))).
            readLine();
        pc.setPassword(passwd.toCharArray());
    }
    
    private void handleChoiceCallback(ChoiceCallback cc)
        throws IOException {
        // ignore the provided defaultValue
        System.out.print(cc.getPrompt());
        
        String[] strChoices = cc.getChoices();
        for (int j = 0; j < strChoices.length; j++) {
            System.out.print("choice[" + j + "] : " + strChoices[j]);
        }
        System.out.flush();
        cc.setSelectedIndex(Integer.parseInt((new BufferedReader
            (new InputStreamReader(System.in))).readLine()));
    }
    
    protected boolean login(AuthContext lc)
        throws UnsupportedCallbackException {
        boolean succeed = false;
        Callback[] callbacks = null;
        
        // get information requested from module
        while (lc.hasMoreRequirements()) {
            callbacks = lc.getRequirements();
            if (callbacks != null) {
                addLoginCallbackMessage(callbacks);
                lc.submitRequirements(callbacks);
            }
        }
        
        if (lc.getStatus() == AuthContext.Status.SUCCESS) {
            System.out.println("Login succeeded.");
            succeed = true;
        } else if (lc.getStatus() == AuthContext.Status.FAILED) {
            System.out.println("Login failed. (error message: "+lc.getErrorMessage()+")");
            if (lc.getLoginException() != null) {
        	    lc.getLoginException().printStackTrace(System.out);
            }
        } else {
            System.out.println("Unknown status: " + lc.getStatus());
        }
        
        return succeed;
    }
    
    protected void logout(AuthContext lc)
        throws AuthLoginException {
        lc.logout();
        System.out.println("Logged Out!!");
    }
    
    static void debugMessage(String msg) {
        System.out.println(msg);
    }
}
