You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2016/02/04 18:14:21 UTC

cxf git commit: Introducing a REST interface for the STS

Repository: cxf
Updated Branches:
  refs/heads/master 4cf721646 -> 89cdf0a99


Introducing a REST interface for the STS


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/89cdf0a9
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/89cdf0a9
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/89cdf0a9

Branch: refs/heads/master
Commit: 89cdf0a99492ca6fb7075a200f89a4a14e16b17f
Parents: 4cf7216
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Thu Feb 4 17:08:56 2016 +0000
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Thu Feb 4 17:08:56 2016 +0000

----------------------------------------------------------------------
 services/sts/sts-core/pom.xml                   |   7 +
 .../cxf/sts/rest/RESTSecurityTokenService.java  |  93 ++++++++
 .../sts/rest/RESTSecurityTokenServiceImpl.java  | 224 +++++++++++++++++++
 services/sts/systests/basic/pom.xml             |   6 +
 .../systest/sts/restunit/BasicAuthFilter.java   | 117 ++++++++++
 .../cxf/systest/sts/restunit/RESTUnitTest.java  | 149 ++++++++++++
 .../cxf/systest/sts/restunit/STSRESTServer.java |  46 ++++
 .../cxf/systest/sts/restunit/cxf-client.xml     |  33 +++
 .../cxf/systest/sts/restunit/cxf-rest-sts.xml   | 142 ++++++++++++
 9 files changed, 817 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/89cdf0a9/services/sts/sts-core/pom.xml
----------------------------------------------------------------------
diff --git a/services/sts/sts-core/pom.xml b/services/sts/sts-core/pom.xml
index 1f31c25..dcf229a 100644
--- a/services/sts/sts-core/pom.xml
+++ b/services/sts/sts-core/pom.xml
@@ -87,6 +87,13 @@
             <optional>true</optional>
         </dependency>
         <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/cxf/blob/89cdf0a9/services/sts/sts-core/src/main/java/org/apache/cxf/sts/rest/RESTSecurityTokenService.java
----------------------------------------------------------------------
diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/rest/RESTSecurityTokenService.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/rest/RESTSecurityTokenService.java
new file mode 100644
index 0000000..04cc0f6
--- /dev/null
+++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/rest/RESTSecurityTokenService.java
@@ -0,0 +1,93 @@
+/**
+ * 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.sts.rest;
+
+import java.util.List;
+
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType;
+
+@Path("/token")
+public interface RESTSecurityTokenService {
+
+    enum Action {
+        ISSUE("issue"),
+        VALIDATE("validate"),
+        RENEW("renew"),
+        CANCEL("cancel");
+        private String value;
+
+        private Action(String value) {
+            this.value = value;
+        }
+
+        public String getValue() {
+            return value;
+        }
+    }
+
+    /**
+     * @return Issues required token type with default token settings.
+     */
+    @GET
+    @Path("{tokenType}")
+    @Produces({
+        MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON
+    })
+    Response getToken(@PathParam("tokenType") String tokenType, @QueryParam("keyType") String keyType,
+        @QueryParam("claim") List<String> requestedClaims);
+
+    @POST
+    @Produces({
+        MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON
+    })
+    Response getToken(@QueryParam("action") @DefaultValue("issue") Action action, RequestSecurityTokenType request);
+
+    /**
+     * Same as {@link #getToken(Action, RequestSecurityTokenType)} with 'cancel' action.
+     * 
+     * @param request
+     * @return
+     */
+    @DELETE
+    @Path("/")
+    @Produces({
+        MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON
+    })
+    Response removeToken(RequestSecurityTokenType request);
+
+    @POST
+    @Path("KeyExchangeToken")
+    @Produces({
+        MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON
+    })
+    Response getKeyExchangeToken(RequestSecurityTokenType request);
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/89cdf0a9/services/sts/sts-core/src/main/java/org/apache/cxf/sts/rest/RESTSecurityTokenServiceImpl.java
----------------------------------------------------------------------
diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/rest/RESTSecurityTokenServiceImpl.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/rest/RESTSecurityTokenServiceImpl.java
new file mode 100644
index 0000000..6955931
--- /dev/null
+++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/rest/RESTSecurityTokenServiceImpl.java
@@ -0,0 +1,224 @@
+/**
+ * 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.sts.rest;
+
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.xml.bind.JAXBElement;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.cxf.helpers.DOMUtils;
+import org.apache.cxf.jaxrs.ext.MessageContext;
+import org.apache.cxf.phase.PhaseInterceptorChain;
+import org.apache.cxf.sts.QNameConstants;
+import org.apache.cxf.sts.STSConstants;
+import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider;
+import org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceImpl;
+import org.apache.cxf.ws.security.sts.provider.model.ClaimsType;
+import org.apache.cxf.ws.security.sts.provider.model.ObjectFactory;
+import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType;
+import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenType;
+import org.apache.cxf.ws.security.trust.STSUtils;
+import org.apache.wss4j.dom.WSConstants;
+
+public class RESTSecurityTokenServiceImpl extends SecurityTokenServiceImpl implements RESTSecurityTokenService {
+
+    public static final Map<String, String> DEFAULT_CLAIM_TYPE_MAP;
+
+    public static final Map<String, String> DEFAULT_TOKEN_TYPE_MAP;
+
+    private static final String CLAIM_TYPE = "ClaimType";
+    private static final String CLAIM_TYPE_NS = "http://schemas.xmlsoap.org/ws/2005/05/identity";
+
+    static {
+        DEFAULT_CLAIM_TYPE_MAP = new HashMap<String, String>();
+        DEFAULT_CLAIM_TYPE_MAP.put("emailaddress", CLAIM_TYPE_NS + "/claims/emailaddress");
+        DEFAULT_CLAIM_TYPE_MAP.put("role", CLAIM_TYPE_NS + "/claims/role");
+        DEFAULT_CLAIM_TYPE_MAP.put("surname", CLAIM_TYPE_NS + "/claims/surname");
+        DEFAULT_CLAIM_TYPE_MAP.put("givenname", CLAIM_TYPE_NS + "/claims/givenname");
+        DEFAULT_CLAIM_TYPE_MAP.put("name", CLAIM_TYPE_NS + "/claims/name");
+        DEFAULT_CLAIM_TYPE_MAP.put("upn", CLAIM_TYPE_NS + "/claims/upn");
+        DEFAULT_CLAIM_TYPE_MAP.put("nameidentifier", CLAIM_TYPE_NS + "/claims/nameidentifier");
+
+        DEFAULT_TOKEN_TYPE_MAP = new HashMap<String, String>();
+        DEFAULT_TOKEN_TYPE_MAP.put("saml", WSConstants.WSS_SAML2_TOKEN_TYPE);
+        DEFAULT_TOKEN_TYPE_MAP.put("saml2.0", WSConstants.WSS_SAML2_TOKEN_TYPE);
+        DEFAULT_TOKEN_TYPE_MAP.put("saml1.1", WSConstants.WSS_SAML_TOKEN_TYPE);
+        DEFAULT_TOKEN_TYPE_MAP.put("jwt", JWTTokenProvider.JWT_TOKEN_TYPE);
+        DEFAULT_TOKEN_TYPE_MAP.put("sct", STSUtils.TOKEN_TYPE_SCT_05_12);
+    }
+
+    @Context
+    private MessageContext messageContext;
+
+    private Map<String, String> claimTypeMap = DEFAULT_CLAIM_TYPE_MAP;
+
+    private Map<String, String> tokenTypeMap = DEFAULT_TOKEN_TYPE_MAP;
+
+    private String defaultKeyType = STSConstants.BEARER_KEY_KEYTYPE;
+
+    private List<String> defaultClaims;
+
+    private boolean requestClaimsOptional = true;
+
+    @Override
+    public Response getToken(String tokenType, String keyType, List<String> requestedClaims) {
+
+        if (tokenTypeMap != null && tokenTypeMap.containsKey(tokenType)) {
+            tokenType = tokenTypeMap.get(tokenType);
+        }
+        ObjectFactory of = new ObjectFactory();
+        RequestSecurityTokenType request = of.createRequestSecurityTokenType();
+
+        request.getAny().add(of.createTokenType(tokenType));
+
+        request.getAny().add(of.createRequestType("http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue"));
+
+        request.getAny().add(of.createKeyType(keyType != null
+            ? keyType
+            : defaultKeyType));
+
+        // Claims
+        if (requestedClaims == null) {
+            requestedClaims = defaultClaims;
+        }
+
+        if (requestedClaims != null) {
+            ClaimsType claimsType = of.createClaimsType();
+            claimsType.setDialect(CLAIM_TYPE_NS);
+            JAXBElement<ClaimsType> claims = of.createClaims(claimsType);
+            for (String claim : requestedClaims) {
+
+                if (claimTypeMap != null && claimTypeMap.containsKey(claim)) {
+                    claim = claimTypeMap.get(claim);
+                }
+
+                Document doc = DOMUtils.createDocument();
+                Element claimElement = doc.createElementNS(CLAIM_TYPE_NS, CLAIM_TYPE);
+                claimElement.setAttributeNS(null, "Uri", claim);
+                claimElement.setAttributeNS(null, "Optional", Boolean.toString(requestClaimsOptional));
+                claimsType.getAny().add(claimElement);
+            }
+            request.getAny().add(claims);
+        }
+
+        // OnBehalfOf
+        // User Authentication done with JWT or SAML?
+        //if (securityContext != null && securityContext.getUserPrincipal() != null) {
+            //TODO
+//            if (onBehalfOfToken != null) {
+//                OnBehalfOfType onBehalfOfType = of.createOnBehalfOfType();
+//                onBehalfOfType.setAny(onBehalfOfToken);
+//                JAXBElement<OnBehalfOfType> onBehalfOfElement = of.createOnBehalfOf(onBehalfOfType);
+//                request.getAny().add(onBehalfOfElement);
+//            }
+      //  }
+
+        // request.setContext(null);
+        return getToken(Action.ISSUE, request);
+    }
+
+    @Override
+    public Response getToken(Action action, RequestSecurityTokenType request) {
+        RequestSecurityTokenResponseType response;
+        switch (action) {
+        case VALIDATE:
+            response = validate(request);
+            break;
+        case RENEW:
+            response = renew(request);
+            break;
+        case CANCEL:
+            response = cancel(request);
+            break;
+        case ISSUE:
+        default:
+            response = issueSingle(request);
+            break;
+        }
+        
+        JAXBElement<RequestSecurityTokenResponseType> jaxbResponse = 
+            QNameConstants.WS_TRUST_FACTORY.createRequestSecurityTokenResponse(response);
+
+        return Response.ok(jaxbResponse).build();
+    }
+
+    @Override
+    public Response removeToken(RequestSecurityTokenType request) {
+        RequestSecurityTokenResponseType response = cancel(request);
+        return Response.ok(response).build();
+    }
+
+    @Override
+    public Response getKeyExchangeToken(RequestSecurityTokenType request) {
+        RequestSecurityTokenResponseType response = keyExchangeToken(request);
+        return Response.ok(response).build();
+    }
+
+    public Map<String, String> getTokenTypeMap() {
+        return tokenTypeMap;
+    }
+
+    public void setTokenTypeMap(Map<String, String> tokenTypeMap) {
+        this.tokenTypeMap = tokenTypeMap;
+    }
+
+    public String getDefaultKeyType() {
+        return defaultKeyType;
+    }
+
+    public void setDefaultKeyType(String defaultKeyType) {
+        this.defaultKeyType = defaultKeyType;
+    }
+
+    public boolean isRequestClaimsOptional() {
+        return requestClaimsOptional;
+    }
+
+    public void setRequestClaimsOptional(boolean requestClaimsOptional) {
+        this.requestClaimsOptional = requestClaimsOptional;
+    }
+
+    public Map<String, String> getClaimTypeMap() {
+        return claimTypeMap;
+    }
+
+    public void setClaimTypeMap(Map<String, String> claimTypeMap) {
+        this.claimTypeMap = claimTypeMap;
+    }
+    
+    @Override
+    protected Principal getPrincipal() {
+        return messageContext.getSecurityContext().getUserPrincipal();
+    }
+    
+    @Override
+    protected Map<String, Object> getMessageContext() {
+        return PhaseInterceptorChain.getCurrentMessage();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/89cdf0a9/services/sts/systests/basic/pom.xml
----------------------------------------------------------------------
diff --git a/services/sts/systests/basic/pom.xml b/services/sts/systests/basic/pom.xml
index ce40837..3fdc0fb 100644
--- a/services/sts/systests/basic/pom.xml
+++ b/services/sts/systests/basic/pom.xml
@@ -109,6 +109,12 @@
             <artifactId>hazelcast</artifactId>
             <version>${cxf.hazelcast.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.jaxrs</groupId>
+            <artifactId>jackson-jaxrs-json-provider</artifactId>
+            <scope>test</scope>
+        </dependency>
+
     </dependencies>
     <build>
         <testSourceDirectory>${basedir}/src/test/java</testSourceDirectory>

http://git-wip-us.apache.org/repos/asf/cxf/blob/89cdf0a9/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/restunit/BasicAuthFilter.java
----------------------------------------------------------------------
diff --git a/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/restunit/BasicAuthFilter.java b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/restunit/BasicAuthFilter.java
new file mode 100644
index 0000000..5c5616e
--- /dev/null
+++ b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/restunit/BasicAuthFilter.java
@@ -0,0 +1,117 @@
+/**
+ * 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.sts.restunit;
+
+import java.io.IOException;
+import java.security.Principal;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Response;
+
+import org.w3c.dom.Document;
+
+import org.apache.cxf.configuration.security.AuthorizationPolicy;
+import org.apache.cxf.helpers.DOMUtils;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.security.SecurityContext;
+import org.apache.wss4j.common.principal.WSUsernameTokenPrincipalImpl;
+import org.apache.wss4j.dom.WSConstants;
+import org.apache.wss4j.dom.handler.RequestData;
+import org.apache.wss4j.dom.message.token.UsernameToken;
+import org.apache.wss4j.dom.validate.Credential;
+import org.apache.wss4j.dom.validate.UsernameTokenValidator;
+
+/**
+ * A simple filter to validate a Basic Auth username/password via a CallbackHandler
+ */
+public class BasicAuthFilter implements ContainerRequestFilter {
+
+    private CallbackHandler callbackHandler;
+    
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+        Message message = JAXRSUtils.getCurrentMessage();
+        AuthorizationPolicy policy = message.get(AuthorizationPolicy.class);
+        
+        if (policy == null || policy.getUserName() == null || policy.getPassword() == null) {
+            requestContext.abortWith(
+                Response.status(401).header("WWW-Authenticate", "Basic realm=\"IdP\"").build());
+        }
+
+        try {
+            UsernameToken token = convertPolicyToToken(policy);
+            Credential credential = new Credential();
+            credential.setUsernametoken(token);
+            
+            RequestData data = new RequestData();
+            data.setMsgContext(message);
+            data.setCallbackHandler(callbackHandler);
+            UsernameTokenValidator validator = new UsernameTokenValidator();
+            credential = validator.validate(credential, data);
+            
+            // Create a Principal/SecurityContext
+            Principal p = null;
+            if (credential != null && credential.getPrincipal() != null) {
+                p = credential.getPrincipal();
+            } else {
+                p = new WSUsernameTokenPrincipalImpl(policy.getUserName(), false);
+                ((WSUsernameTokenPrincipalImpl)p).setPassword(policy.getPassword());
+            }
+            message.put(SecurityContext.class, createSecurityContext(p));
+        } catch (Exception ex) {
+            throw ExceptionUtils.toInternalServerErrorException(ex, null);
+        }
+    }
+
+    protected UsernameToken convertPolicyToToken(AuthorizationPolicy policy) 
+        throws Exception {
+
+        Document doc = DOMUtils.createDocument();
+        UsernameToken token = new UsernameToken(false, doc, 
+                                                WSConstants.PASSWORD_TEXT);
+        token.setName(policy.getUserName());
+        token.setPassword(policy.getPassword());
+        return token;
+    }
+    
+    protected SecurityContext createSecurityContext(final Principal p) {
+        return new SecurityContext() {
+
+            public Principal getUserPrincipal() {
+                return p;
+            }
+
+            public boolean isUserInRole(String arg0) {
+                return false;
+            }
+        };
+    }
+
+    public CallbackHandler getCallbackHandler() {
+        return callbackHandler;
+    }
+
+    public void setCallbackHandler(CallbackHandler callbackHandler) {
+        this.callbackHandler = callbackHandler;
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf/blob/89cdf0a9/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/restunit/RESTUnitTest.java
----------------------------------------------------------------------
diff --git a/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/restunit/RESTUnitTest.java b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/restunit/RESTUnitTest.java
new file mode 100644
index 0000000..0164017
--- /dev/null
+++ b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/restunit/RESTUnitTest.java
@@ -0,0 +1,149 @@
+/**
+ * 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.sts.restunit;
+
+import java.net.URL;
+import java.util.List;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.ws.rs.core.Response;
+import javax.xml.bind.JAXBElement;
+
+import org.w3c.dom.Element;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.systest.sts.common.SecurityTestUtil;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType;
+import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.CryptoFactory;
+import org.apache.wss4j.common.saml.SamlAssertionWrapper;
+import org.apache.wss4j.dom.WSDocInfo;
+import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
+import org.apache.wss4j.dom.handler.RequestData;
+import org.apache.wss4j.dom.processor.Processor;
+import org.apache.wss4j.dom.processor.SAMLTokenProcessor;
+import org.junit.BeforeClass;
+
+/**
+ * Some unit tests for the CXF STSClient Issue Binding.
+ */
+public class RESTUnitTest extends AbstractBusClientServerTestBase {
+    
+    static final String STSPORT = allocatePort(STSRESTServer.class);
+    
+    @BeforeClass
+    public static void startServers() throws Exception {
+        assertTrue(
+                   "Server failed to launch",
+                   // run the server in the same process
+                   // set this to false to fork
+                   launchServer(STSRESTServer.class, true)
+        );
+    }
+    
+    @org.junit.AfterClass
+    public static void cleanup() throws Exception {
+        SecurityTestUtil.cleanup();
+        stopAllServers();
+    }
+    
+    @org.junit.Test
+    public void testIssueSAML2Token() throws Exception {
+        SpringBusFactory bf = new SpringBusFactory();
+        URL busFile = RESTUnitTest.class.getResource("cxf-client.xml");
+
+        Bus bus = bf.createBus(busFile.toString());
+        SpringBusFactory.setDefaultBus(bus);
+        SpringBusFactory.setThreadDefaultBus(bus);
+        
+        String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token";
+        WebClient client = WebClient.create(address, "alice", "clarinet", busFile.toString());
+
+        client.type("application/xml").accept("application/xml");
+        client.path("saml2.0");
+        
+        Response response = client.get();
+        RequestSecurityTokenResponseType securityResponse = 
+            response.readEntity(RequestSecurityTokenResponseType.class);
+        
+        RequestedSecurityTokenType requestedSecurityToken = null;
+        for (Object obj : securityResponse.getAny()) {
+            if (obj instanceof JAXBElement<?>) {
+                JAXBElement<?> jaxbElement = (JAXBElement<?>)obj;
+                if ("RequestedSecurityToken".equals(jaxbElement.getName().getLocalPart())) {
+                    requestedSecurityToken = (RequestedSecurityTokenType)jaxbElement.getValue();
+                    break;
+                }
+            }
+        }
+        assertNotNull(requestedSecurityToken);
+        
+        // Process the token
+        List<WSSecurityEngineResult> results = processToken(requestedSecurityToken);
+
+        assertTrue(results != null && results.size() == 1);
+        SamlAssertionWrapper assertion = 
+            (SamlAssertionWrapper)results.get(0).get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
+        assertTrue(assertion != null);
+        assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null);
+        assertTrue(assertion.isSigned());
+
+        bus.shutdown(true);
+    }
+    
+    @org.junit.Test
+    public void testIssueJWTToken() throws Exception {
+        SpringBusFactory bf = new SpringBusFactory();
+        URL busFile = RESTUnitTest.class.getResource("cxf-client.xml");
+
+        Bus bus = bf.createBus(busFile.toString());
+        SpringBusFactory.setDefaultBus(bus);
+        SpringBusFactory.setThreadDefaultBus(bus);
+        
+        String address = "https://localhost:" + STSPORT + "/SecurityTokenService/token";
+        WebClient client = WebClient.create(address, "alice", "clarinet", busFile.toString());
+
+        client.type("application/json").accept("application/json");
+        client.path("jwt");
+        
+        client.get();
+    }
+    
+    private List<WSSecurityEngineResult> processToken(RequestedSecurityTokenType securityResponse)
+        throws Exception {
+        RequestData requestData = new RequestData();
+        requestData.setDisableBSPEnforcement(true);
+        CallbackHandler callbackHandler = new org.apache.cxf.systest.sts.common.CommonCallbackHandler();
+        requestData.setCallbackHandler(callbackHandler);
+        Crypto crypto = CryptoFactory.getInstance("serviceKeystore.properties");
+        requestData.setDecCrypto(crypto);
+        requestData.setSigVerCrypto(crypto);
+        
+        Processor processor = new SAMLTokenProcessor();
+        Element securityTokenElem = (Element)securityResponse.getAny();
+        return processor.handleToken(
+            securityTokenElem, requestData, new WSDocInfo(securityTokenElem.getOwnerDocument())
+        );
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/89cdf0a9/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/restunit/STSRESTServer.java
----------------------------------------------------------------------
diff --git a/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/restunit/STSRESTServer.java b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/restunit/STSRESTServer.java
new file mode 100644
index 0000000..1c3a72d
--- /dev/null
+++ b/services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/restunit/STSRESTServer.java
@@ -0,0 +1,46 @@
+/**
+ * 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.sts.restunit;
+
+import java.net.URL;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.bus.spring.SpringBusFactory;
+import org.apache.cxf.testutil.common.AbstractBusTestServerBase;
+
+public class STSRESTServer extends AbstractBusTestServerBase {
+
+    public STSRESTServer() {
+
+    }
+
+    protected void run()  {
+        URL busFile = STSRESTServer.class.getResource("cxf-rest-sts.xml");
+        Bus busLocal = new SpringBusFactory().createBus(busFile);
+        BusFactory.setDefaultBus(busLocal);
+        setBus(busLocal);
+
+        try {
+            new STSRESTServer();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/89cdf0a9/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/restunit/cxf-client.xml
----------------------------------------------------------------------
diff --git a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/restunit/cxf-client.xml b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/restunit/cxf-client.xml
new file mode 100644
index 0000000..892e5a5
--- /dev/null
+++ b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/restunit/cxf-client.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!--
+ 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:jaxws="http://cxf.apache.org/jaxws" xmlns:cxf="http://cxf.apache.org/core" xmlns:http="http://cxf.apache.org/transports/http/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-2.0.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd">
+    <cxf:bus>
+        <cxf:features>
+            <cxf:logging/>
+        </cxf:features>
+    </cxf:bus>
+    <http:conduit name="https://localhost:.*">
+        <http:tlsClientParameters disableCNCheck="true">
+            <sec:trustManagers>
+                <sec:keyStore type="jks" password="cspass" resource="clientstore.jks"/>
+            </sec:trustManagers>
+        </http:tlsClientParameters>
+    </http:conduit>
+</beans>

http://git-wip-us.apache.org/repos/asf/cxf/blob/89cdf0a9/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/restunit/cxf-rest-sts.xml
----------------------------------------------------------------------
diff --git a/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/restunit/cxf-rest-sts.xml b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/restunit/cxf-rest-sts.xml
new file mode 100644
index 0000000..9565d30
--- /dev/null
+++ b/services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/restunit/cxf-rest-sts.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0"?>
+<!--
+  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:cxf="http://cxf.apache.org/core" xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:sec="http://cxf.apache.org/configuration/security" xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration" xmlns:test="http://apache.org/hello_world_soap_http" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xmlns:jaxrs="http://cxf.apache.org/jaxrs" xsi:schemaLocation="         http://cxf.apache.org/core         http://cxf.apache.org/schemas/core.xsd         http://www.springframework.org/schema/beans         http://www.springframework.org/schema/beans/spring-beans-2.0.xsd         http://cxf.apache.org/jaxws                                              http://cxf.apache.org/schemas/jaxws.xsd         http://cxf.apache.org/configuration/security         http://cxf.apache.org/schemas/configuration/security.xsd         http://cxf.apache.org/transports/http-jetty/conf
 iguration         http://cxf.apache.org/schemas/configuration/http-jetty.xsd         http://www.springframework.org/schema/util         http://www.springframework.org/schema/util/spring-util-2.0.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">
+    
+    <import resource="classpath:META-INF/cxf/cxf.xml"/>
+    
+    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
+    <cxf:bus>
+        <cxf:features>
+            <cxf:logging/>
+        </cxf:features>
+    </cxf:bus>
+    
+    <bean id="hokDelegationHandler" class="org.apache.cxf.sts.token.delegation.HOKDelegationHandler"/>
+    <bean id="utDelegationHandler" class="org.apache.cxf.sts.token.delegation.UsernameTokenDelegationHandler"/>
+    
+    <util:list id="delegationHandlers">
+        <ref bean="hokDelegationHandler"/>
+        <ref bean="utDelegationHandler"/>
+    </util:list>
+    
+    <bean id="transportIssueDelegate" class="org.apache.cxf.sts.operation.TokenIssueOperation">
+        <property name="tokenProviders" ref="transportTokenProviders"/>
+        <property name="tokenValidators" ref="transportTokenValidators"/>
+        <property name="services" ref="transportService"/>
+        <property name="stsProperties" ref="transportSTSProperties"/>
+        <property name="delegationHandlers" ref="delegationHandlers"/>
+        <property name="claimsManager" ref="claimsManager"/>
+    </bean>
+    <bean id="transportValidateDelegate" class="org.apache.cxf.sts.operation.TokenValidateOperation">
+        <property name="tokenValidators" ref="transportTokenValidators"/>
+        <property name="stsProperties" ref="transportSTSProperties"/>
+    </bean>
+    <bean id="transportUTTokenValidator" class="org.apache.cxf.sts.token.validator.UsernameTokenValidator">
+    </bean>
+    <util:list id="transportTokenValidators">
+        <ref bean="transportSamlTokenValidator"/>
+        <ref bean="transportUTTokenValidator"/>
+    </util:list>
+    <util:list id="transportTokenProviders">
+        <ref bean="transportSamlTokenProvider"/>
+        <ref bean="transportJWTTokenProvider"/>
+    </util:list>
+    <bean id="transportSamlTokenValidator" class="org.apache.cxf.sts.token.validator.SAMLTokenValidator">
+    </bean>
+    <bean id="transportSamlTokenProvider" class="org.apache.cxf.sts.token.provider.SAMLTokenProvider">
+    </bean>
+    <bean id="transportJWTTokenProvider" class="org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider">
+    </bean>
+    <bean id="transportSTSProviderBean" class="org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider">
+        <property name="issueOperation" ref="transportIssueDelegate"/>
+        <property name="validateOperation" ref="transportValidateDelegate"/>
+    </bean>
+    <bean id="transportService" class="org.apache.cxf.sts.service.StaticService">
+        <property name="endpoints" ref="transportEndpoints"/>
+    </bean>
+    <util:list id="transportEndpoints">
+        <value>https://localhost:(\d)*/doubleit/services/doubleittransport.*
+        </value>
+    </util:list>
+    <bean id="transportSTSProperties" class="org.apache.cxf.sts.StaticSTSProperties">
+        <property name="signaturePropertiesFile" value="stsKeystore.properties"/>
+        <property name="signatureUsername" value="mystskey"/>
+        <property name="callbackHandlerClass" value="org.apache.cxf.systest.sts.common.CommonCallbackHandler"/>
+        <property name="encryptionPropertiesFile" value="stsKeystore.properties"/>
+        <property name="issuer" value="DoubleItSTSIssuer"/>
+        <property name="encryptionUsername" value="myservicekey"/>
+    </bean>
+    
+    <bean id="claimsManager" class="org.apache.cxf.sts.claims.ClaimsManager">
+        <property name="claimHandlers" ref="customClaimsHandler"/>
+    </bean>
+    <bean id="customClaimsHandler" class="org.apache.cxf.systest.sts.deployment.CustomClaimsHandler">
+    </bean>
+    
+    <bean id="restSTS" class="org.apache.cxf.sts.rest.RESTSecurityTokenServiceImpl">
+        <property name="issueSingleOperation" ref="transportIssueDelegate" />
+        <property name="validateOperation" ref="transportValidateDelegate" />
+    </bean>
+    
+    <bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
+        <property name="depthProperties">
+            <bean class="org.apache.cxf.staxutils.DocumentDepthProperties">
+                <property name="innerElementCountThreshold" value="500" />
+            </bean>
+        </property>
+    </bean>
+
+    <bean id="jsonProvider" class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider" />
+    
+    <bean id="callbackHandler" class="org.apache.cxf.systest.sts.common.CommonCallbackHandler"/>
+    <bean id="basicAuthFilter" class="org.apache.cxf.systest.sts.restunit.BasicAuthFilter">
+        <property name="callbackHandler" ref="callbackHandler"/>
+    </bean>
+   
+    <jaxrs:server id="stsRESTService"
+       depends-on="ClientAuthHttpsSettings" 
+       address="https://localhost:${testutil.ports.STSRESTServer}/SecurityTokenService">
+        <jaxrs:serviceBeans>
+            <ref bean="restSTS" />
+        </jaxrs:serviceBeans>
+        <jaxrs:providers>
+            <ref bean="jsonProvider" />
+            <ref bean="basicAuthFilter"/>
+        </jaxrs:providers>
+        <jaxrs:extensionMappings>
+            <entry key="json" value="application/json;charset=UTF-8" />
+            <entry key="xml" value="application/xml;charset=UTF-8" />
+        </jaxrs:extensionMappings>
+    </jaxrs:server>
+    
+    <httpj:engine-factory id="ClientAuthHttpsSettings" bus="cxf">
+        <httpj:engine port="${testutil.ports.STSRESTServer}">
+            <httpj:tlsServerParameters>
+                <sec:keyManagers keyPassword="skpass">
+                    <sec:keyStore type="jks" password="sspass" resource="servicestore.jks"/>
+                </sec:keyManagers>
+                <sec:trustManagers>
+                    <sec:keyStore type="jks" password="stsspass" resource="stsstore.jks"/>
+                </sec:trustManagers>
+                <sec:clientAuthentication want="false" required="false"/>
+            </httpj:tlsServerParameters>
+        </httpj:engine>
+    </httpj:engine-factory>
+</beans>