/*
 *  Copyright (c) 2000-2003 Yale University. All rights reserved.
 *
 *  THIS SOFTWARE IS PROVIDED "AS IS," AND ANY EXPRESS OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE EXPRESSLY
 *  DISCLAIMED. IN NO EVENT SHALL YALE UNIVERSITY OR ITS EMPLOYEES BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED, THE COSTS OF
 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH
 *  DAMAGE.
 *
 *  Redistribution and use of this software in source or binary forms,
 *  with or without modification, are permitted, provided that the
 *  following conditions are met:
 *
 *  1. Any redistribution must include the above copyright notice and
 *  disclaimer and this list of conditions in any related documentation
 *  and, if feasible, in the redistributed software.
 *
 *  2. Any redistribution must include the acknowledgment, "This product
 *  includes software developed by Yale University," in any related
 *  documentation and, if feasible, in the redistributed software.
 *
 *  3. The names "Yale" and "Yale University" must not be used to endorse
 *  or promote products derived from this software.
 */

package org.jasig.cas.client.validation;

import org.jasig.cas.client.validation.Cas20ProxyTicketValidator;

import javax.security.auth.spi.LoginModule;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.TextInputCallback;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.xml.parsers.ParserConfigurationException;
import org.jasig.cas.client.validation.Assertion;

import java.util.Map;
import java.security.Principal;
import java.io.IOException;

import org.xml.sax.SAXException;

/**
 * This class implements a JAAS <code>LoginModule</code> that defers authentication
 * to CAS. See the
 * <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/jaas/JAASRefGuide.html">
 * JAAS documentation</a> for details about configuration and architecture.
 * <p>
 * The calling application's <code>CallbackHandler</code> MUST return the <strong>ticket</strong> for a
 * <code>TextInputCallback</code> whose prompt is "ticket".
 * <p>
 * The CAS <strong>service</strong> MAY be hard-coded into the configuration; if it is not,
 * the calling application's <code>CallbackHandler</code> MAY return the <strong>service</strong>
 * in a <code>TextInputCallback</code> whose prompt is "service".
 * <p>
 * The <strong>cas_validate_url</strong> MUST be hard-coded in the configuration
 * <p>
 * Sample configuration:
 *
 * <pre>
 * Application
 * {
 *      edu.yale.its.tp.cas.client.jaas.CASLoginModule sufficient
 *          cas_validate_url="https://cas.server.edu/cas/serviceValidate"
 *          service="https://my.application.edu/login";
 * }
 *
 * </pre>
 *
 * <p>
 *
 * It is up to the application to decide when and where to put the link
 * or redirect to "https://cas.server.edu/cas/login?service=https://my.application.edu/login"
 * <p>
 *
 * Sample handling code behind "https://my.application.edu/login"
 *
 * <pre>
 *      CallbackHandler handler = new MyCallbackHandler(...);
 *      Subject subject = new Subject();
 *      LoginContext lc = new LoginContext("Application", subject,handler);
 *      lc.login();
 *      Set set = subject.getPrincipals();
 *      String username = null;
 *      for(Iterator iterator=set.iterator(); iterator.hasNext();){
 *          username = ((Principal) iterator.next()).getName();
 *      }
 *      //if username is not null, authentication was successful
 * </pre>
 *
 * Code for <code>MyCallBackHandler</code>
 *
 * <pre>
 * public class MyCallBackHandler implements CallbackHandler {
 *      public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
 *          for(int i = 0; i < callbacks.length; i++) {
 *              if(callbacks[i] instanceof TextInputCallback){
 *                  TextInputCallback text = (TextInputCallback)callbacks[i];
 *                  if("ticket".equals(text.getPrompt())){
 *                      text.setText(getTicket());
 *                  }
 *                  else if("service".equals(text.getPrompt())){
 *                      text.setText(getService());
 *                  }
 *              }
 *              else{
 *                  throw(new UnsupportedCallbackException(callbacks[i],
 *                      "Callback class not supported"));
 *              }
 *          }
 *      }
 *
 *      public String getTicket(){
 *          //insert your logic for retrieving the ticket; perhaps
 *          //you have constructed this handler with a ServletRequest,
 *          //and simply return request.getParameter("ticket");
 *      }
 *
 *      public String getService(){
 *          //optional - service can be hardcoded in the configuration.
 *          //This might be useful for applications that might use dynamically generated service IDs
 *      }
 * }
 * </pre>
 * @author Alex Vigdor av317@columbia.edu
 * @version $Revision: 1.1 $
 */
public class CASLoginModule implements LoginModule{
    protected Subject subject;
    protected CallbackHandler callbackHandler;
    protected String casValidateUrl;
    protected String service;
    protected Principal principal;

    /**
     *
     * @param subject
     * @param callbackHandler
     * @param sharedState
     * @param options can contain <ul>
     *      <li><strong>cas_validate_url</strong> (required)</li>
     *      <li><strong>service</strong> (optional)</li>
     * </ul>
     *
     */
    public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options){
        this.subject=subject;
        this.callbackHandler=callbackHandler;
        this.casValidateUrl=(String)options.get("cas_validate_url");
        this.service = (String)options.get("service");
		System.out.println("cas_validate_url : "  + this.casValidateUrl);
		System.out.println("service : "  + this.service);
    }

    public boolean login() throws LoginException{

		System.out.println("Inside login()" );

		Callback[] callbacks;
        TextInputCallback ticketCallback = new TextInputCallback("ticket");
		System.out.println("ticketCallback : " + ticketCallback);
        TextInputCallback serviceCallback=null;
        if(service==null || "".equals(service.trim())){
            //the service has not been hardcoded, so give the application
            // a callback which can be used to specify it
            serviceCallback = new TextInputCallback("service");
			callbacks = new Callback[] {ticketCallback,serviceCallback};

			System.out.println("serviceCallback : " + serviceCallback);
			System.out.println("callbacks : " + callbacks);

        }
        else{
            callbacks = new Callback[] {ticketCallback};
			System.out.println("callbacks : " + callbacks);
			System.out.println("Classname : " + callbackHandler.getClass().getName());
        }

        try{
            callbackHandler.handle(callbacks);
        }
        catch(IOException e){
			System.out.println("In First Catch block / IOException");
            throw new LoginException(e.getMessage());
        }
        catch(UnsupportedCallbackException e){
			System.out.println("In Second Catch block / UnsupportedCallbackException");
            throw new LoginException(e.getMessage());
        }
        String ticket = ticketCallback.getText();
		System.out.println("ticket : " + ticket);
        if(ticket!=null && !ticket.trim().equals("")){
            if(serviceCallback!=null){
                service = serviceCallback.getText();
            }
            Cas20ProxyTicketValidator pv = new Cas20ProxyTicketValidator("https://report.amrita.edu:8443/cas");
			 Assertion ass=null;

            if(service!=null &&  !("".equals(service.trim()))){
              //  pv.setService(service);
            //}
            //pv.setServiceTicket(ticket);
            try{
               ass= pv.validate(ticket,service);
			   System.out.println("Assertion : " + ass);
            }
            catch(Exception e){
                throw new LoginException(e.getMessage());
            }
        /*    catch(SAXException e){
                throw new LoginException(e.getMessage());
            }
            catch(ParserConfigurationException e){
                throw new LoginException(e.getMessage());
            }
			*/
		}
            //if(pv.isAuthenticationSuccesful()){

                final String name = ass.getPrincipal().getName();
                principal = new Principal(){
                    public String getName(){
                        return name;
                    }
              };
                //authentication successful
                return true;
            //}
        }
        //authentication failed
        throw new FailedLoginException("Login failed.");
    }

    public boolean commit() throws LoginException{
        if(principal!=null){
            subject.getPrincipals().add(principal);
            return true;
        }
        return false;
    }

    public boolean abort() throws LoginException{
        if(principal!=null){
            principal = null;
            return true;
        }
        return false;
    }

    public boolean logout() throws LoginException{
        if(principal!=null){
            subject.getPrincipals().remove(principal);
            return true;
        }
        return false;
    }
}
