You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2012/07/18 00:14:36 UTC

svn commit: r1362686 - in /cxf/trunk: api/src/main/java/org/apache/cxf/common/security/ rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/ rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/ systests/jaxrs/src/test/java/org...

Author: sergeyb
Date: Tue Jul 17 22:14:35 2012
New Revision: 1362686

URL: http://svn.apache.org/viewvc?rev=1362686&view=rev
Log:
[CXF-4430] SpnegoAuthSupplier updates, also adding Kerberos interceptor and filter

Added:
    cxf/trunk/api/src/main/java/org/apache/cxf/common/security/SimpleSecurityContext.java   (with props)
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthOutInterceptor.java   (with props)
    cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthenticationFilter.java   (with props)
    cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/AbstractSpnegoAuthSupplier.java   (with props)
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/BookKerberosServer.java   (with props)
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/JAXRSKerberosBookTest.java   (with props)
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/kerberos.cfg
    cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/kerberosClient.xml   (with props)
Modified:
    cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/SpnegoAuthSupplier.java

Added: cxf/trunk/api/src/main/java/org/apache/cxf/common/security/SimpleSecurityContext.java
URL: http://svn.apache.org/viewvc/cxf/trunk/api/src/main/java/org/apache/cxf/common/security/SimpleSecurityContext.java?rev=1362686&view=auto
==============================================================================
--- cxf/trunk/api/src/main/java/org/apache/cxf/common/security/SimpleSecurityContext.java (added)
+++ cxf/trunk/api/src/main/java/org/apache/cxf/common/security/SimpleSecurityContext.java Tue Jul 17 22:14:35 2012
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file 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.apache.cxf.common.security;
+
+import java.security.Principal;
+
+import org.apache.cxf.security.SecurityContext;
+
+public class SimpleSecurityContext implements SecurityContext {
+    private SimplePrincipal principal;
+    public SimpleSecurityContext(String name) {
+        this.principal = new SimplePrincipal(name);
+    }
+    
+    public Principal getUserPrincipal() {
+        return principal;
+    }
+
+    public boolean isUserInRole(String role) {
+        return false;
+    }
+
+}

Propchange: cxf/trunk/api/src/main/java/org/apache/cxf/common/security/SimpleSecurityContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/api/src/main/java/org/apache/cxf/common/security/SimpleSecurityContext.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthOutInterceptor.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthOutInterceptor.java?rev=1362686&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthOutInterceptor.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthOutInterceptor.java Tue Jul 17 22:14:35 2012
@@ -0,0 +1,104 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file 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.apache.cxf.jaxrs.security;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cxf.configuration.security.AuthorizationPolicy;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.phase.Phase;
+import org.apache.cxf.phase.PhaseInterceptor;
+import org.apache.cxf.transport.http.auth.AbstractSpnegoAuthSupplier;
+
+public class KerberosAuthOutInterceptor extends AbstractSpnegoAuthSupplier 
+    implements PhaseInterceptor<Message> {
+
+    private String phase = Phase.MARSHAL;
+    private AuthorizationPolicy policy;
+    
+    public KerberosAuthOutInterceptor() {
+        
+    }
+    public KerberosAuthOutInterceptor(String phase) {
+        this.phase = phase;
+    }
+    
+    public void handleMessage(Message message) throws Fault {
+        URL currentURL = getCurrentURL(message);
+        String value = super.getAuthorization(getPolicy(), 
+                                              currentURL, 
+                                              message);
+        Map<String, List<String>> headers = 
+            CastUtils.cast((Map<?, ?>)message.get(Message.PROTOCOL_HEADERS));
+        if (headers == null) {
+            headers = new HashMap<String, List<String>>();
+            message.put(Message.PROTOCOL_HEADERS, headers);
+        }
+        headers.put("Authorization", Collections.singletonList(value));
+    }
+    
+    private URL getCurrentURL(Message message) {
+        try {
+            return new URL((String)message.get(Message.ENDPOINT_ADDRESS));
+        } catch (MalformedURLException ex) {
+            // is not expected to happen
+            throw new RuntimeException(ex);
+        }
+    }
+
+    public void handleFault(Message message) {
+        // complete
+    }
+
+    public Set<String> getAfter() {
+        return Collections.emptySet();
+    }
+
+    public Set<String> getBefore() {
+        return Collections.emptySet();
+    }
+
+    public String getId() {
+        return getClass().getName();
+    }
+
+    public String getPhase() {
+        return phase;
+    }
+
+    public Collection<PhaseInterceptor<? extends Message>> getAdditionalInterceptors() {
+        return Collections.emptySet();
+    }
+    public AuthorizationPolicy getPolicy() {
+        return policy;
+    }
+    public void setPolicy(AuthorizationPolicy policy) {
+        this.policy = policy;
+    }
+
+}

Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthOutInterceptor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthOutInterceptor.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthenticationFilter.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthenticationFilter.java?rev=1362686&view=auto
==============================================================================
--- cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthenticationFilter.java (added)
+++ cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthenticationFilter.java Tue Jul 17 22:14:35 2012
@@ -0,0 +1,183 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file 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.apache.cxf.jaxrs.security;
+
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.List;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.common.security.SimpleSecurityContext;
+import org.apache.cxf.common.util.Base64Exception;
+import org.apache.cxf.common.util.Base64Utility;
+import org.apache.cxf.jaxrs.ext.MessageContext;
+import org.apache.cxf.jaxrs.ext.RequestHandler;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
+import org.apache.cxf.security.SecurityContext;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+
+public class KerberosAuthenticationFilter implements RequestHandler {
+
+    private static final String NEGOTIATE_SCHEME = "Negotiate";
+    private static final String PROPERTY_USE_KERBEROS_OID = "auth.spnego.useKerberosOid";
+    private static final String KERBEROS_OID = "1.2.840.113554.1.2.2";
+    private static final String SPNEGO_OID = "1.3.6.1.5.5.2";
+    
+    private MessageContext messageContext;
+    private CallbackHandler callbackHandler;
+    private String loginContextName;
+    private String servicePrincipalName;
+    private String realm;
+    
+    public Response handleRequest(Message m, ClassResourceInfo resourceClass) {
+        
+        List<String> authHeaders = messageContext.getHttpHeaders()
+            .getRequestHeader(HttpHeaders.AUTHORIZATION);
+        if (authHeaders.size() != 1) {
+            throw new WebApplicationException(getFaultResponse());
+        }
+        String[] authPair = authHeaders.get(0).split(" ");
+        if (authPair.length != 2 || !NEGOTIATE_SCHEME.equalsIgnoreCase(authPair[0])) {
+            throw new WebApplicationException(getFaultResponse());
+        }
+                
+        byte[] serviceTicket = getServiceTicket(authPair[1]);
+        
+        try {
+            Subject serviceSubject = loginAndGetSubject();
+            
+            GSSContext gssContext = createGSSContext();
+
+            Subject.doAs(serviceSubject, new ValidateServiceTicketAction(gssContext, serviceTicket));
+            
+            final String clientName = gssContext.getSrcName().toString();            
+            m.put(SecurityContext.class, new SimpleSecurityContext(clientName));
+            
+        } catch (LoginException e) {
+            throw new WebApplicationException(getFaultResponse());
+        } catch (GSSException e) {
+            throw new WebApplicationException(getFaultResponse());
+        } catch (PrivilegedActionException e) {
+            throw new WebApplicationException(getFaultResponse());
+        }
+        
+        return null;
+    }
+
+    protected GSSContext createGSSContext() throws GSSException {
+        boolean useKerberosOid = MessageUtils.isTrue(
+            messageContext.getContextualProperty(PROPERTY_USE_KERBEROS_OID));
+        Oid oid = new Oid(useKerberosOid ? KERBEROS_OID : SPNEGO_OID);
+
+        GSSManager gssManager = GSSManager.getInstance();
+        
+        String spn = getCompleteServicePrincipalName();
+        GSSName gssService = gssManager.createName(spn, null);
+        
+        return gssManager.createContext(gssService.canonicalize(oid), 
+                   oid, null, GSSContext.DEFAULT_LIFETIME);
+    }
+    
+    protected Subject loginAndGetSubject() throws LoginException {
+        
+        // The login without a callback can work if
+        // - Kerberos keytabs are used with a principal name set in the JAAS config
+        // - TGT cache is available and either a principalName is set in the JAAS config
+        //   or Kerberos is integrated into the OS logon process
+        //   meaning that a process which runs this code has the
+        //   user identity  
+        
+        LoginContext lc = callbackHandler != null 
+            ? new LoginContext(loginContextName, callbackHandler) : new LoginContext(loginContextName);
+        lc.login();
+        return lc.getSubject();
+    }
+    
+    private byte[] getServiceTicket(String encodedServiceTicket) {
+        try {
+            return Base64Utility.decode(encodedServiceTicket);
+        } catch (Base64Exception ex) {
+            throw new WebApplicationException(getFaultResponse());
+        }
+    }
+    
+    private static Response getFaultResponse() {
+        return Response.status(401).header(HttpHeaders.WWW_AUTHENTICATE, NEGOTIATE_SCHEME).build();
+    }
+    
+    protected String getCompleteServicePrincipalName() {
+        String name = servicePrincipalName == null 
+            ? "HTTP/" + messageContext.getUriInfo().getBaseUri().getHost() : servicePrincipalName;
+        if (realm != null) {            
+            name += "@" + realm;
+        }
+        return name;
+            
+            
+    }
+    
+    @Context
+    public void setMessageContext(MessageContext context) {
+        this.messageContext = context;
+    }
+    
+    public void setLoginContextName(String contextName) {
+        this.loginContextName = contextName;
+    }
+
+    public void setServicePrincipalName(String servicePrincipalName) {
+        this.servicePrincipalName = servicePrincipalName;
+    }
+
+    public void setRealm(String realm) {
+        this.realm = realm;
+    }
+
+    public void setCallbackHandler(CallbackHandler callbackHandler) {
+        this.callbackHandler = callbackHandler;
+    }
+
+    private final class ValidateServiceTicketAction implements PrivilegedExceptionAction<byte[]> {
+        private final GSSContext context;
+        private final byte[] token;
+
+        private ValidateServiceTicketAction(GSSContext context, byte[] token) {
+            this.context = context;
+            this.token = token;
+        }
+
+        public byte[] run() throws GSSException {
+            return context.acceptSecContext(token, 0, token.length);
+        }
+    }
+}

Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthenticationFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/security/KerberosAuthenticationFilter.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/AbstractSpnegoAuthSupplier.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/AbstractSpnegoAuthSupplier.java?rev=1362686&view=auto
==============================================================================
--- cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/AbstractSpnegoAuthSupplier.java (added)
+++ cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/AbstractSpnegoAuthSupplier.java Tue Jul 17 22:14:35 2012
@@ -0,0 +1,208 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file 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.apache.cxf.transport.http.auth;
+
+import java.net.URL;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.Base64Utility;
+import org.apache.cxf.configuration.security.AuthorizationPolicy;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+
+public abstract class AbstractSpnegoAuthSupplier {
+    protected static final Logger LOG = LogUtils.getL7dLogger(AbstractSpnegoAuthSupplier.class);
+    
+    /**
+     * Can be set on the client properties. If set to true then the kerberos oid is used
+     * instead of the default spnego OID
+     */
+    private static final String PROPERTY_USE_KERBEROS_OID = "auth.spnego.useKerberosOid";
+    private static final String PROPERTY_REQUIRE_MUTUAL_AUTH = "auth.spnego.requireMutualAuth";
+    private static final String PROPERTY_REQUIRE_CRED_DELEGATION = "auth.spnego.requireCredDelegation";
+    
+    private static final String KERBEROS_OID = "1.2.840.113554.1.2.2";
+    private static final String SPNEGO_OID = "1.3.6.1.5.5.2";
+
+    private String servicePrincipalName;
+    private String realm;
+    private boolean mutualAuth;
+    private boolean credDelegation;
+    
+    
+    public String getAuthorization(AuthorizationPolicy authPolicy,
+                                   URL currentURL,
+                                   Message message) {
+        if (!HttpAuthHeader.AUTH_TYPE_NEGOTIATE.equals(authPolicy.getAuthorizationType())) {
+            return null;
+        }
+        try {
+            String spn = getCompleteServicePrincipalName(currentURL);
+            
+            boolean useKerberosOid = MessageUtils.isTrue(
+                message.getContextualProperty(PROPERTY_USE_KERBEROS_OID));
+            Oid oid = new Oid(useKerberosOid ? KERBEROS_OID : SPNEGO_OID);
+
+            byte[] token = getToken(authPolicy, spn, oid, message);
+            return HttpAuthHeader.AUTH_TYPE_NEGOTIATE + " " + Base64Utility.encode(token);
+        } catch (LoginException e) {
+            throw new RuntimeException(e.getMessage(), e);
+        } catch (GSSException e) {
+            throw new RuntimeException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Create and return service ticket token
+     * 
+     * @param authPolicy
+     * @param context
+     * @return
+     * @throws GSSException
+     * @throws LoginException
+     */
+    private byte[] getToken(AuthorizationPolicy authPolicy,
+                            final GSSContext context) throws GSSException,
+        LoginException {
+        final byte[] token = new byte[0];
+
+        if (authPolicy.getUserName() == null || authPolicy.getUserName().trim().length() == 0) {
+            return context.initSecContext(token, 0, token.length);
+        }
+
+        LoginContext lc = new LoginContext(authPolicy.getAuthorization(), getUsernamePasswordHandler(
+            authPolicy.getUserName(), authPolicy.getPassword()));
+        lc.login();
+        
+        try {
+            return (byte[])Subject.doAs(lc.getSubject(), new CreateServiceTicketAction(context, token));
+        } catch (PrivilegedActionException e) {
+            if (e.getCause() instanceof GSSException) {
+                throw (GSSException) e.getCause();
+            }
+            LOG.log(Level.SEVERE, "initSecContext", e);
+            return null;
+        }
+    }
+
+    /**
+     * Create and return a service ticket token for a given service principal
+     * name
+     * 
+     * @param authPolicy
+     * @param spn
+     * @return service ticket token
+     * @throws GSSException
+     * @throws LoginException
+     */
+    private byte[] getToken(AuthorizationPolicy authPolicy, 
+                            String spn, 
+                            Oid oid,
+                            Message message) throws GSSException, 
+        LoginException {
+        GSSManager manager = GSSManager.getInstance();
+        GSSName serverName = manager.createName(spn, null);
+
+        GSSContext context = manager
+                .createContext(serverName.canonicalize(oid), oid, null, GSSContext.DEFAULT_LIFETIME);
+        context.requestMutualAuth(isMutualAuthRequired(message));
+        context.requestCredDeleg(isCredDelegationRequired(message));
+
+        return getToken(authPolicy, context);
+    }
+    
+    protected boolean isMutualAuthRequired(Message message) { 
+        Object prop = message.getContextualProperty(PROPERTY_REQUIRE_MUTUAL_AUTH);
+        return prop == null ? mutualAuth : MessageUtils.isTrue(prop);
+    }
+    
+    protected boolean isCredDelegationRequired(Message message) { 
+        Object prop = message.getContextualProperty(PROPERTY_REQUIRE_CRED_DELEGATION);
+        return prop == null ? credDelegation : MessageUtils.isTrue(prop);
+    }
+
+    protected String getCompleteServicePrincipalName(URL currentURL) {
+        String name = servicePrincipalName == null 
+            ? "HTTP/" + currentURL.getHost() : servicePrincipalName;
+        if (realm != null) {            
+            name += "@" + realm;
+        }
+        return name;
+            
+            
+    }
+    
+    public void setServicePrincipalName(String servicePrincipalName) {
+        this.servicePrincipalName = servicePrincipalName;
+    }
+
+    public void setRealm(String realm) {
+        this.realm = realm;
+    }
+    
+    private final class CreateServiceTicketAction implements PrivilegedExceptionAction<byte[]> {
+        private final GSSContext context;
+        private final byte[] token;
+
+        private CreateServiceTicketAction(GSSContext context, byte[] token) {
+            this.context = context;
+            this.token = token;
+        }
+
+        public byte[] run() throws GSSException {
+            return context.initSecContext(token, 0, token.length);
+        }
+    }
+    
+    public static CallbackHandler getUsernamePasswordHandler(final String username, final String password) {
+        final CallbackHandler handler = new CallbackHandler() {
+
+            public void handle(final Callback[] callback) {
+                for (int i = 0; i < callback.length; i++) {
+                    if (callback[i] instanceof NameCallback) {
+                        final NameCallback nameCallback = (NameCallback) callback[i];
+                        nameCallback.setName(username);
+                    } else if (callback[i] instanceof PasswordCallback) {
+                        final PasswordCallback passCallback = (PasswordCallback) callback[i];
+                        passCallback.setPassword(password.toCharArray());
+                    }
+                }
+            }
+        };
+        return handler;
+    }
+
+}

Propchange: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/AbstractSpnegoAuthSupplier.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/AbstractSpnegoAuthSupplier.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Modified: cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/SpnegoAuthSupplier.java
URL: http://svn.apache.org/viewvc/cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/SpnegoAuthSupplier.java?rev=1362686&r1=1362685&r2=1362686&view=diff
==============================================================================
--- cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/SpnegoAuthSupplier.java (original)
+++ cxf/trunk/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/SpnegoAuthSupplier.java Tue Jul 17 22:14:35 2012
@@ -19,42 +19,13 @@
 package org.apache.cxf.transport.http.auth;
 
 import java.net.URL;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
 
-import org.apache.cxf.common.logging.LogUtils;
-import org.apache.cxf.common.util.Base64Utility;
 import org.apache.cxf.configuration.security.AuthorizationPolicy;
 import org.apache.cxf.message.Message;
-import org.ietf.jgss.GSSContext;
-import org.ietf.jgss.GSSException;
-import org.ietf.jgss.GSSManager;
-import org.ietf.jgss.GSSName;
-import org.ietf.jgss.Oid;
-
-public class SpnegoAuthSupplier implements HttpAuthSupplier {
-    /**
-     * Can be set on the jaxws:properties. If set to true then the kerberos oid is used
-     * instead of the default spnego OID
-     */
-    private static final String PROPERTY_USE_KERBEROS_OID = "auth.spnego.useKerberosOid";
-    private static final String KERBEROS_OID = "1.2.840.113554.1.2.2";
-    private static final String SPNEGO_OID = "1.3.6.1.5.5.2";
 
-    private static final Logger LOG = LogUtils.getL7dLogger(SpnegoAuthSupplier.class);
+public class SpnegoAuthSupplier extends AbstractSpnegoAuthSupplier 
+    implements HttpAuthSupplier {
 
-    private LoginContext lc;
-    
     public boolean requiresRequestCaching() {
         return false;
     }
@@ -63,117 +34,7 @@ public class SpnegoAuthSupplier implemen
                                     URL currentURL,
                                     Message message,
                                     String fullHeader) {
-        if (!HttpAuthHeader.AUTH_TYPE_NEGOTIATE.equals(authPolicy.getAuthorizationType())) {
-            return null;
-        }
-        try {
-            String spn = "HTTP/" + currentURL.getHost();
-            LOG.fine("Adding authorization service ticket for service principal name: " + spn);
-            
-            String userKerbOidSt = (String)message.getContextualProperty(PROPERTY_USE_KERBEROS_OID);
-            boolean useKerberosOid = "true".equals(userKerbOidSt);
-            Oid oid = new Oid(useKerberosOid ? KERBEROS_OID : SPNEGO_OID);
-
-            byte[] token = getToken(authPolicy, spn, oid);
-            return HttpAuthHeader.AUTH_TYPE_NEGOTIATE + " " + Base64Utility.encode(token);
-        } catch (LoginException e) {
-            throw new RuntimeException(e.getMessage(), e);
-        } catch (GSSException e) {
-            throw new RuntimeException(e.getMessage(), e);
-        }
-    }
-
-    /**
-     * Create and return service ticket token
-     * 
-     * @param authPolicy
-     * @param context
-     * @return
-     * @throws GSSException
-     * @throws LoginException
-     */
-    private byte[] getToken(AuthorizationPolicy authPolicy, final GSSContext context) throws GSSException,
-            LoginException {
-        final byte[] token = new byte[0];
-
-        if (authPolicy.getUserName() == null || authPolicy.getUserName().trim().length() == 0) {
-            return context.initSecContext(token, 0, token.length);
-        }
-
-        if (lc == null) {
-            lc = new LoginContext(authPolicy.getAuthorization(), getUsernamePasswordHandler(
-                authPolicy.getUserName(), authPolicy.getPassword()));
-            lc.login();
-        }
-
-        try {
-            return (byte[])Subject.doAs(lc.getSubject(), new CreateServiceTicketAction(context, token));
-        } catch (PrivilegedActionException e) {
-            if (e.getCause() instanceof GSSException) {
-                throw (GSSException) e.getCause();
-            }
-            LOG.log(Level.SEVERE, "initSecContext", e);
-            return null;
-        }
-    }
-
-    /**
-     * Create and return a service ticket token for a given service principal
-     * name
-     * 
-     * @param authPolicy
-     * @param spn
-     * @return service ticket token
-     * @throws GSSException
-     * @throws LoginException
-     */
-    private byte[] getToken(AuthorizationPolicy authPolicy, String spn, Oid oid) throws GSSException, 
-        LoginException {
-        GSSManager manager = GSSManager.getInstance();
-        GSSName serverName = manager.createName(spn, null);
-
-        GSSContext context = manager
-                .createContext(serverName.canonicalize(oid), oid, null, GSSContext.DEFAULT_LIFETIME);
-        // TODO Do we need mutual auth. Will the code we have really work with
-        // mutual auth?
-        context.requestMutualAuth(true);
-        // TODO Credential delegation could be a security hole if it was not
-        // intended. Both settings should be configurable
-        context.requestCredDeleg(true);
-
-        return getToken(authPolicy, context);
-    }
-
-    private final class CreateServiceTicketAction implements PrivilegedExceptionAction<byte[]> {
-        private final GSSContext context;
-        private final byte[] token;
-
-        private CreateServiceTicketAction(GSSContext context, byte[] token) {
-            this.context = context;
-            this.token = token;
-        }
-
-        public byte[] run() throws GSSException {
-            return context.initSecContext(token, 0, token.length);
-        }
-    }
-    
-    public static CallbackHandler getUsernamePasswordHandler(final String username, final String password) {
-        final CallbackHandler handler = new CallbackHandler() {
-
-            public void handle(final Callback[] callback) {
-                for (int i = 0; i < callback.length; i++) {
-                    if (callback[i] instanceof NameCallback) {
-                        final NameCallback nameCallback = (NameCallback) callback[i];
-                        nameCallback.setName(username);
-                    } else if (callback[i] instanceof PasswordCallback) {
-                        final PasswordCallback passCallback = (PasswordCallback) callback[i];
-                        passCallback.setPassword(password.toCharArray());
-                    }
-                }
-            }
-        };
-        return handler;
+        return super.getAuthorization(authPolicy, currentURL, message);
     }
 
 }

Added: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/BookKerberosServer.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/BookKerberosServer.java?rev=1362686&view=auto
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/BookKerberosServer.java (added)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/BookKerberosServer.java Tue Jul 17 22:14:35 2012
@@ -0,0 +1,82 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file 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.apache.cxf.systest.jaxrs.security;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.jaxrs.security.KerberosAuthenticationFilter;
+import org.apache.cxf.systest.jaxrs.BookStore;
+import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
+import org.apache.cxf.testutil.common.TestUtil;
+    
+public class BookKerberosServer extends AbstractBusTestServerBase {
+    public static final String PORT = TestUtil.getPortNumber("jaxrs-kerberos");
+    
+    protected void run() {
+        
+        JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+        sf.setResourceClasses(BookStore.class);
+        //default lifecycle is per-request, change it to singleton
+        sf.setResourceProvider(BookStore.class,
+                               new SingletonResourceProvider(new BookStore()));
+        KerberosAuthenticationFilter filter = new KerberosAuthenticationFilter();
+        filter.setLoginContextName("KerberosServer");
+        filter.setCallbackHandler(getCallbackHandler("HTTP/localhost", "http"));
+        sf.setProvider(filter);
+        sf.setAddress("http://localhost:" + PORT + "/");
+      
+        sf.create();        
+    }
+
+    public static void main(String[] args) {
+        try {
+            BookKerberosServer s = new BookKerberosServer();
+            s.start();
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            System.exit(-1);
+        } finally {
+            System.out.println("done!");
+        }
+    }
+    
+    public static CallbackHandler getCallbackHandler(final String username, final String password) {
+        final CallbackHandler handler = new CallbackHandler() {
+
+            public void handle(final Callback[] callback) {
+                for (int i = 0; i < callback.length; i++) {
+                    if (callback[i] instanceof NameCallback) {
+                        final NameCallback nameCallback = (NameCallback) callback[i];
+                        nameCallback.setName(username);
+                    } else if (callback[i] instanceof PasswordCallback) {
+                        final PasswordCallback passCallback = (PasswordCallback) callback[i];
+                        passCallback.setPassword(password.toCharArray());
+                    }
+                }
+            }
+        };
+        return handler;
+    }
+}

Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/BookKerberosServer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/BookKerberosServer.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/JAXRSKerberosBookTest.java
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/JAXRSKerberosBookTest.java?rev=1362686&view=auto
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/JAXRSKerberosBookTest.java (added)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/JAXRSKerberosBookTest.java Tue Jul 17 22:14:35 2012
@@ -0,0 +1,92 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file 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.apache.cxf.systest.jaxrs.security;
+
+import org.apache.cxf.configuration.security.AuthorizationPolicy;
+import org.apache.cxf.interceptor.LoggingOutInterceptor;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.jaxrs.security.KerberosAuthOutInterceptor;
+import org.apache.cxf.systest.jaxrs.Book;
+import org.apache.cxf.systest.jaxrs.BookStore;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.apache.cxf.transport.http.auth.HttpAuthHeader;
+
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+
+public class JAXRSKerberosBookTest extends AbstractBusClientServerTestBase {
+    public static final String PORT = BookKerberosServer.PORT;
+
+    private static final String KERBEROS_CONFIG_FILE =
+        "org/apache/cxf/systest/jaxrs/security/kerberosClient.xml";
+    
+    @BeforeClass
+    public static void startServers() throws Exception {
+        String jaasConfig = JAXRSKerberosBookTest.class
+            .getResource("/org/apache/cxf/systest/jaxrs/security/kerberos.cfg").toURI().getPath();
+        System.setProperty("java.security.auth.login.config", jaasConfig);
+
+        assertTrue("server did not launch correctly",
+                   launchServer(BookKerberosServer.class, true));
+    }
+    
+    @Test
+    @Ignore
+    public void testGetBookWithConfigInHttpConduit() throws Exception {
+        doTestGetBook123Proxy(KERBEROS_CONFIG_FILE);
+    }
+    
+    private void doTestGetBook123Proxy(String configFile) throws Exception {
+        BookStore bs = JAXRSClientFactory.create("http://localhost:" + PORT, BookStore.class, 
+                configFile);
+        WebClient.getConfig(bs).getOutInterceptors().add(new LoggingOutInterceptor());
+        // just to verify the interface call goes through CGLIB proxy too
+        assertEquals("http://localhost:" + PORT, WebClient.client(bs).getBaseURI().toString());
+        Book b = bs.getBook("123");
+        assertEquals(b.getId(), 123);
+        b = bs.getBook("123");
+        assertEquals(b.getId(), 123);
+    }
+    
+    @Test
+    @Ignore
+    public void testGetBookWithInterceptor() throws Exception {
+        WebClient wc = WebClient.create("http://localhost:" + PORT + "/bookstore/books/123");
+        
+        KerberosAuthOutInterceptor kbInterceptor = new KerberosAuthOutInterceptor();
+        
+        AuthorizationPolicy policy = new AuthorizationPolicy();
+        policy.setAuthorizationType(HttpAuthHeader.AUTH_TYPE_NEGOTIATE);
+        policy.setAuthorization("KerberosClient");
+        policy.setUserName("alice");
+        policy.setPassword("alice");
+        
+        kbInterceptor.setPolicy(policy);
+        
+        WebClient.getConfig(wc).getOutInterceptors().add(new LoggingOutInterceptor());
+        WebClient.getConfig(wc).getOutInterceptors().add(kbInterceptor);
+        
+        Book b = wc.get(Book.class);
+        assertEquals(b.getId(), 123);
+    }
+}

Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/JAXRSKerberosBookTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/JAXRSKerberosBookTest.java
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Added: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/kerberos.cfg
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/kerberos.cfg?rev=1362686&view=auto
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/kerberos.cfg (added)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/kerberos.cfg Tue Jul 17 22:14:35 2012
@@ -0,0 +1,6 @@
+KerberosClient {
+    com.sun.security.auth.module.Krb5LoginModule required client=TRUE;
+};
+KerberosServer {
+    com.sun.security.auth.module.Krb5LoginModule required storeKey=true;
+};

Added: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/kerberosClient.xml
URL: http://svn.apache.org/viewvc/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/kerberosClient.xml?rev=1362686&view=auto
==============================================================================
--- cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/kerberosClient.xml (added)
+++ cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/kerberosClient.xml Tue Jul 17 22:14:35 2012
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file 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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:http="http://cxf.apache.org/transports/http/configuration"
+       xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration"
+       xmlns:sec="http://cxf.apache.org/configuration/security"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans                 http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://cxf.apache.org/transports/http/configuration         http://cxf.apache.org/schemas/configuration/http-conf.xsd
+        http://cxf.apache.org/transports/http-jetty/configuration   http://cxf.apache.org/schemas/configuration/http-jetty.xsd
+        http://cxf.apache.org/configuration/security                http://cxf.apache.org/schemas/configuration/security.xsd
+        ">
+
+	<!-- -->
+    <!-- HTTP/S configuration for proxy clients -->
+    <!-- -->
+    <http:conduit name="{http://jaxrs.systest.cxf.apache.org/}BookStore.http-conduit">
+        <http:client ConnectionTimeout="3000000" ReceiveTimeout="3000000"/>
+        <http:authorization>
+          <sec:UserName>alice</sec:UserName>
+          <sec:Password>alice</sec:Password>
+          <sec:AuthorizationType>Negotiate</sec:AuthorizationType>
+          <sec:Authorization>KerberosClient</sec:Authorization>
+        </http:authorization>
+    </http:conduit>
+    
+    <!-- -->
+    <!-- HTTP/S configuration for web clients -->
+    <!--
+    
+    <http:conduit name="\{https://localhost\:.*\}WebClient\.http-conduit">
+        <http:client ConnectionTimeout="3000000" ReceiveTimeout="3000000"/>
+        <authorization>
+          <AuthorizationType>Negotiate</AuthorizationType>
+          <Authorization>KerberosClient</Authorization>
+          <UserName>alice</UserName>
+          <Password>alice</Password>
+        </authorization>
+    </http:conduit>
+    -->
+</beans>

Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/kerberosClient.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/kerberosClient.xml
------------------------------------------------------------------------------
    svn:keywords = Rev Date

Propchange: cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/security/kerberosClient.xml
------------------------------------------------------------------------------
    svn:mime-type = text/xml