/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * WebServiceSecurity.java
 *
 * Created on April 9, 2004, 2:28 PM
 */

package com.sun.enterprise.security.wss;

import java.util.HashMap;
import java.util.Set;

import com.sun.enterprise.security.jauth.*;
import com.sun.enterprise.security.ClientSecurityContext;
import com.sun.enterprise.security.SecurityContext;
import com.sun.enterprise.security.audit.AuditManager;
import com.sun.enterprise.security.audit.AuditManagerFactory;
    
import java.security.Principal;
import javax.security.auth.Subject;
import javax.xml.soap.SOAPMessage;
import javax.servlet.http.HttpServletRequest;

import com.sun.logging.*;
import java.util.logging.*;

import com.sun.enterprise.deployment.WebServiceEndpoint;
import com.sun.enterprise.webservice.WsUtil;

/**
 *
 * Load Container auth spi.
 * This is for JAXRPC only.
 * @author  Harpreet Singh
 */

public class WebServiceSecurity {

    private static Logger _logger=null;
    static {
        _logger = LogDomains.getLogger(LogDomains.SECURITY_LOGGER);
    }

    private static AuditManager auditManager =
            AuditManagerFactory.getAuditManagerInstance();

    // keys to shared state (for things like session keys) in SOAPMessageCOntext
    private static final String SHARED_CLIENT_STATE = 
        "com.sun.enterprise.security.jauth.ClientHashMap";

    private static final String SHARED_SERVER_STATE = 
        "com.sun.enterprise.security.jauth.ServerHashMap";

    private WebServiceSecurity () {
    }
    
    // when called by jaxrpc SystemHandlerDelegate
    public static boolean 
	validateRequest(javax.xml.rpc.handler.soap.SOAPMessageContext context, 
			ServerAuthContext sAC)
        throws AuthException 
    {
	boolean rvalue = true;
	SOAPAuthParam param = 
	    new SOAPAuthParam(WsUtil.getMessage(context), null);

	// put sharedState in MessageContext for use by secureResponse
	HashMap sharedState = new HashMap();
	context.setProperty(SHARED_SERVER_STATE, sharedState);

	try {
	    rvalue = validateRequest(param, sharedState, sAC);
	} catch(PendingException pe){
            _logger.log(Level.FINE,
			"Container-auth: wss: Error validating request  ",pe);
	    context.setMessage(param.getResponse());
	    rvalue = false;
	} catch(FailureException fe){
            _logger.log(Level.FINE,
			"Container-auth: wss: Error validating request  ",fe);
	    context.setMessage(param.getResponse());
	    throw fe;
        }
	return rvalue;
    }

    private static boolean 
	validateRequest(AuthParam param, HashMap sharedState, 
			ServerAuthContext sAC)
        throws AuthException 
    {
	boolean rvalue = true;

        if(_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE,
            "Container Auth: ServerAuthContext.validateRequest");
        }
	
	Subject subject = null;
	boolean firstAuthentication = true;
	SecurityContext sc = SecurityContext.getCurrent();
	if (sc == null || sc.didServerGenerateCredentials()) {
	    subject = new Subject();
	} else {
	    subject = sc.getSubject();
	    firstAuthentication = false;
	}

	sAC.validateRequest((AuthParam)param, subject, sharedState);

	if (rvalue && firstAuthentication) {
	    Set principalSet = subject.getPrincipals();
	    // must be at least one new principal to establish
	    // non-default security contex
	    if (principalSet != null && !principalSet.isEmpty()) {
		// define and add initiator to Subject - note that this may add
		// a second principal (of type PrincipalImpl) for initiator.
		String initiator = ((Principal)principalSet.iterator().next()).
		    getName();
		SecurityContext newSC = new SecurityContext(initiator,subject);
		SecurityContext.setCurrent(newSC);
	    }
	}

        return rvalue;
    }
    
    // when called by jaxrpc SystemHandlerDelegate
    public static void 
	secureResponse(javax.xml.rpc.handler.soap.SOAPMessageContext context, 
		       ServerAuthContext sAC)
        throws AuthException 
    {
	secureResponse(WsUtil.getMessage(context),
		       (HashMap) context.getProperty(SHARED_SERVER_STATE),
		       sAC);
    }

    private static void 
	secureResponse(SOAPMessage response, HashMap sharedState,
		       ServerAuthContext sAC)
        throws AuthException
    {
        if(_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE,
            "Container Auth: ServerAuthContext.secureResponse");
        }

	// subject may change if runAs identity differs from caller's.
	// Therefore, session state is saved in sharedState not subject
	SecurityContext sc = SecurityContext.getCurrent();
	Subject subject = sc.getSubject();

	SOAPAuthParam param = new SOAPAuthParam(null, response);

        try{
            sAC.secureResponse((AuthParam)param, subject, sharedState);
        } finally {
	    sAC.disposeSubject(subject,sharedState);
	}

        return;
    }



    // when called by jaxrpc Handler
    public static void 
	secureRequest(javax.xml.rpc.handler.soap.SOAPMessageContext context, 
		      ClientAuthContext cAC, boolean isAppClient)
        throws AuthException 
    {
	// put sharedState in MessageContext for use by validateResponse
	HashMap sharedState = new HashMap();
        sharedState.put(javax.xml.ws.handler.MessageContext.WSDL_SERVICE,
            context.getProperty(javax.xml.ws.handler.MessageContext.WSDL_SERVICE));
	context.setProperty(SHARED_CLIENT_STATE, sharedState);

	secureRequest
	    (WsUtil.getMessage(context), sharedState, cAC, isAppClient);
    }

    private static void 
	secureRequest(SOAPMessage request, HashMap sharedState,
		      ClientAuthContext cAC, boolean isAppClient) 
        throws AuthException 
    {

        if(_logger.isLoggable(Level.FINE)) {
            _logger.log(Level.FINE,
            "Container Auth: ClientAuthContext.secureRequest");
        }

	SOAPAuthParam param = new SOAPAuthParam(request, null);

	Subject subject = null;
	if (isAppClient) {
	    ClientSecurityContext sc = ClientSecurityContext.getCurrent();
	    if (sc != null) {
		subject = sc.getSubject();
	    }
	} else {
	    SecurityContext sc = SecurityContext.getCurrent();
	    if (sc != null && !sc.didServerGenerateCredentials()) {
		// make sure we don't use default unauthenticated subject, 
		// so that module cannot change this important (constant) 
		// subject.
		subject = sc.getSubject();
	    }
	}
	if (subject == null) subject = new Subject();
	
	cAC.secureRequest ( param, subject, sharedState);
    }
    
    // when called by jaxrpc Handler
    public static boolean 
	validateResponse(javax.xml.rpc.handler.soap.SOAPMessageContext context,
			 ClientAuthContext cAC)
        throws AuthException 
    {
	return validateResponse
	    (WsUtil.getMessage(context),
	     (HashMap) context.getProperty(SHARED_CLIENT_STATE), cAC);
    }

    private static boolean 
	validateResponse(SOAPMessage response, HashMap sharedState, 
			 ClientAuthContext cAC) 
        throws AuthException 
    {
        boolean rvalue = true;

	// get a subject to be filled in with the principals of the responder
	Subject responderSubject = new Subject();

	SOAPAuthParam param = new SOAPAuthParam(null, response);

        try{
            cAC.validateResponse( param, responderSubject, sharedState);
        } catch(AuthException ae){
            _logger.log(Level.SEVERE,
			"Container-auth: wss: Error validating response ", ae);
	    rvalue = false;
            throw ae;
        } finally {
	    cAC.disposeSubject(responderSubject,sharedState);
	}
        
        return rvalue;
    }

    // when called by jaxrpc SystemHandlerDelegate
    public static void auditInvocation
    (javax.xml.rpc.handler.soap.SOAPMessageContext context, 
    WebServiceEndpoint endpoint, boolean status) {

	if (auditManager.isAuditOn()) {

	    // TODO: replace the string literal with the correct constant
	    // MessageContextProperties.HTTP_SERVLET_REQUEST);

	    HttpServletRequest req = (HttpServletRequest)context.getProperty
		("com.sun.xml.rpc.server.http.HttpServletRequest");
       
	    String uri = null;

	    if( req != null ) {
		uri = req.getRequestURI();
	    }
	    
	    String epName = null;

	    if( endpoint != null ) {
		epName = endpoint.getEndpointName();
	    }

	    auditManager.webServiceInvocation
		( ((uri==null) ? "(no uri)" : uri), 
		  ((epName==null) ? "(no endpoint)" : epName), 
		  status);
	}
    }
}
