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/19 18:03:39 UTC

[3/3] cxf-fediz git commit: More work on Fediz OIDC integration

More work on Fediz OIDC integration


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

Branch: refs/heads/master
Commit: 4df66f377b13230e3dc26c810902c9e5061b958c
Parents: 82a028c
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Fri Feb 19 17:03:18 2016 +0000
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Fri Feb 19 17:03:18 2016 +0000

----------------------------------------------------------------------
 services/idp/pom.xml                            |   5 +
 .../idp/beans/SigninParametersCacheAction.java  |   4 +
 .../idp/beans/TrustedIdpProtocolAction.java     |   6 +-
 .../TrustedIdpOIDCProtocolHandler.java          | 306 +++++++++++++++++++
 .../flows/federation-signin-response.xml        |   4 +
 .../flows/federation-validate-request.xml       |  20 +-
 6 files changed, 341 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/4df66f37/services/idp/pom.xml
----------------------------------------------------------------------
diff --git a/services/idp/pom.xml b/services/idp/pom.xml
index 2db7b8e..6607080 100644
--- a/services/idp/pom.xml
+++ b/services/idp/pom.xml
@@ -146,6 +146,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-rs-security-sso-oidc</artifactId>
+            <version>${cxf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
             <artifactId>cxf-rt-transports-http</artifactId>
             <version>${cxf.version}</version>
         </dependency>

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/4df66f37/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/SigninParametersCacheAction.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/SigninParametersCacheAction.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/SigninParametersCacheAction.java
index a357895..a3226bb 100644
--- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/SigninParametersCacheAction.java
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/SigninParametersCacheAction.java
@@ -29,6 +29,7 @@ import org.apache.cxf.fediz.core.exception.ProcessingException;
 import org.apache.cxf.fediz.service.idp.domain.Application;
 import org.apache.cxf.fediz.service.idp.domain.Idp;
 import org.apache.cxf.fediz.service.idp.util.WebUtils;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
@@ -80,6 +81,9 @@ public class SigninParametersCacheAction {
         if (uuidKey == null) {
             uuidKey = (String)WebUtils.getAttributeFromFlowScope(context, SAMLSSOConstants.RELAY_STATE);
         }
+        if (uuidKey == null) {
+            uuidKey = (String)WebUtils.getAttributeFromFlowScope(context, OAuthConstants.STATE);
+        }
         @SuppressWarnings("unchecked")
         Map<String, Object> signinParams =
             (Map<String, Object>)WebUtils.getAttributeFromExternalContext(context, uuidKey);

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/4df66f37/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/TrustedIdpProtocolAction.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/TrustedIdpProtocolAction.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/TrustedIdpProtocolAction.java
index 614d196..7289f01 100644
--- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/TrustedIdpProtocolAction.java
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/TrustedIdpProtocolAction.java
@@ -93,9 +93,11 @@ public class TrustedIdpProtocolAction {
             throw new IllegalStateException("No ProtocolHandler found for '" + protocol + "'");
         }
         URL redirectUrl = protocolHandler.processSignInResponse(requestContext, idpConfig, trustedIdp);
-        LOG.info("Redirect required?", (redirectUrl != null));
+        LOG.info("Redirect required? {}", (redirectUrl != null));
         if (redirectUrl != null) {
-            return redirectUrl.toString();
+            String redirectUrlStr = redirectUrl.toString();
+            LOG.info("Redirect URL: {}", redirectUrlStr);
+            return redirectUrlStr;
         }
         return null;
     }

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/4df66f37/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java
new file mode 100644
index 0000000..28cc37e
--- /dev/null
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java
@@ -0,0 +1,306 @@
+/**
+ * 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.fediz.service.idp.protocols;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.w3c.dom.Element;
+import org.apache.cxf.fediz.core.FederationConstants;
+import org.apache.cxf.fediz.core.config.FedizContext;
+import org.apache.cxf.fediz.core.config.TrustManager;
+import org.apache.cxf.fediz.core.config.jaxb.AudienceUris;
+import org.apache.cxf.fediz.core.config.jaxb.CertificateStores;
+import org.apache.cxf.fediz.core.config.jaxb.ContextConfig;
+import org.apache.cxf.fediz.core.config.jaxb.FederationProtocolType;
+import org.apache.cxf.fediz.core.config.jaxb.KeyStoreType;
+import org.apache.cxf.fediz.core.config.jaxb.TrustManagersType;
+import org.apache.cxf.fediz.core.config.jaxb.TrustedIssuerType;
+import org.apache.cxf.fediz.core.config.jaxb.TrustedIssuers;
+import org.apache.cxf.fediz.core.config.jaxb.ValidationType;
+import org.apache.cxf.fediz.core.exception.ProcessingException;
+import org.apache.cxf.fediz.core.processor.FederationProcessorImpl;
+import org.apache.cxf.fediz.core.processor.FedizProcessor;
+import org.apache.cxf.fediz.core.processor.FedizRequest;
+import org.apache.cxf.fediz.core.processor.FedizResponse;
+import org.apache.cxf.fediz.service.idp.domain.Idp;
+import org.apache.cxf.fediz.service.idp.domain.TrustedIdp;
+import org.apache.cxf.fediz.service.idp.spi.TrustedIdpProtocolHandler;
+import org.apache.cxf.fediz.service.idp.util.WebUtils;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
+import org.apache.cxf.ws.security.tokenstore.SecurityToken;
+import org.apache.wss4j.common.crypto.CertificateStore;
+import org.apache.xml.security.exceptions.Base64DecodingException;
+import org.apache.xml.security.stax.impl.util.IDGenerator;
+import org.apache.xml.security.utils.Base64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.webflow.execution.RequestContext;
+
+@Component
+public class TrustedIdpOIDCProtocolHandler implements TrustedIdpProtocolHandler {
+    
+    public static final String PROTOCOL = "openid-connect-1.0";
+
+    private static final Logger LOG = LoggerFactory.getLogger(TrustedIdpOIDCProtocolHandler.class);
+
+    @Override
+    public boolean canHandleRequest(HttpServletRequest request) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public String getProtocol() {
+        return PROTOCOL;
+    }
+
+    @Override
+    public URL mapSignInRequest(RequestContext context, Idp idp, TrustedIdp trustedIdp) {
+        
+        try {
+            StringBuilder sb = new StringBuilder();
+            sb.append(trustedIdp.getUrl());
+            sb.append("?");
+            sb.append("response_type").append('=');
+            sb.append("code"); //TODO
+            sb.append("&");
+            sb.append("client_id").append('=');
+            sb.append("consumer-id"); //TODO
+            sb.append("&");
+            sb.append("redirect_uri").append('=');
+            sb.append(URLEncoder.encode(idp.getIdpUrl().toString(), "UTF-8"));
+            sb.append("&");
+            sb.append("scope").append('=');
+            sb.append("openid");
+            
+            String wctx = context.getFlowScope().getString(FederationConstants.PARAM_CONTEXT);
+            if (wctx != null) {
+                sb.append("&").append("state").append('=');
+                sb.append(wctx);
+            }
+            
+            /*
+            String wfresh = context.getFlowScope().getString(FederationConstants.PARAM_FRESHNESS);
+            if (wfresh != null) {
+                sb.append("&").append(FederationConstants.PARAM_FRESHNESS).append('=');
+                sb.append(URLEncoder.encode(wfresh, "UTF-8"));
+            }
+            String wctx = context.getFlowScope().getString(FederationConstants.PARAM_CONTEXT);
+            if (wctx != null) {
+                sb.append("&").append(FederationConstants.PARAM_CONTEXT).append('=');
+                sb.append(wctx);
+            }
+             */
+            return new URL(sb.toString());
+        } catch (MalformedURLException ex) {
+            LOG.error("Invalid Redirect URL for Trusted Idp", ex);
+            throw new IllegalStateException("Invalid Redirect URL for Trusted Idp");
+        } catch (UnsupportedEncodingException ex) {
+            LOG.error("Invalid Redirect URL for Trusted Idp", ex);
+            throw new IllegalStateException("Invalid Redirect URL for Trusted Idp");
+        }
+    }
+    
+    @Override
+    public URL processSignInResponse(RequestContext context, Idp idp, TrustedIdp trustedIdp) {
+        String code = (String) WebUtils.getAttributeFromFlowScope(context,
+                                                                 OAuthConstants.CODE_RESPONSE_TYPE);
+        if (code == null) {
+            return null;
+        }
+        
+        try {
+            StringBuilder sb = new StringBuilder();
+            // sb.append(trustedIdp.getUrl());
+            sb.append("http://localhost:8080/auth/realms/realmb/protocol/openid-connect/token"); // TODO
+            sb.append("?");
+            sb.append("grant_type").append('=');
+            sb.append("authorization_code");
+            sb.append("&");
+            sb.append("code").append('=');
+            sb.append(code);
+            sb.append("&");
+            sb.append("redirect_uri").append('=');
+            sb.append(URLEncoder.encode(idp.getIdpUrl().toString(), "UTF-8"));
+            sb.append("&");
+            sb.append("client_id").append('=');
+            sb.append("consumer-id"); //TODO
+            // sb.append("&");
+            
+            // TODOString state = (String) WebUtils.getAttributeFromFlowScope(context,
+            //                                                          OAuthConstants.STATE);
+            // sb.append("state").append('=');
+            // sb.append(state);
+            
+            return new URL(sb.toString());
+        } catch (MalformedURLException ex) {
+            LOG.error("Invalid Redirect URL for Trusted Idp", ex);
+            throw new IllegalStateException("Invalid Redirect URL for Trusted Idp");
+        } catch (UnsupportedEncodingException ex) {
+            LOG.error("Invalid Redirect URL for Trusted Idp", ex);
+            throw new IllegalStateException("Invalid Redirect URL for Trusted Idp");
+        }
+    }
+
+    @Override
+    public SecurityToken mapSignInResponse(RequestContext context, Idp idp, TrustedIdp trustedIdp) {
+
+        try {
+            String whr = (String) WebUtils.getAttributeFromFlowScope(context,
+                                                                     FederationConstants.PARAM_HOME_REALM);
+    
+            if (whr == null) {
+                LOG.warn("Home realm is null");
+                throw new IllegalStateException("Home realm is null");
+            }
+    
+            String wresult = (String) WebUtils.getAttributeFromFlowScope(context,
+                                                                         FederationConstants.PARAM_RESULT);
+    
+            if (wresult == null) {
+                LOG.warn("Parameter wresult not found");
+                throw new IllegalStateException("No security token issued");
+            }
+    
+            FedizContext fedContext = getFedizContext(idp, trustedIdp);
+    
+            FedizRequest wfReq = new FedizRequest();
+            wfReq.setAction(FederationConstants.ACTION_SIGNIN);
+            wfReq.setResponseToken(wresult);
+    
+            FedizProcessor wfProc = new FederationProcessorImpl();
+            FedizResponse wfResp = wfProc.processRequest(wfReq, fedContext);
+    
+            fedContext.close();
+    
+            Element e = wfResp.getToken();
+    
+            // Create new Security token with new id. 
+            // Parameters for freshness computation are copied from original IDP_TOKEN
+            String id = IDGenerator.generateID("_");
+            SecurityToken idpToken = new SecurityToken(id,
+                                                       wfResp.getTokenCreated(), wfResp.getTokenExpires());
+    
+            idpToken.setToken(e);
+            LOG.info("[IDP_TOKEN={}] for user '{}' created from [RP_TOKEN={}] issued by home realm [{}/{}]",
+                     id, wfResp.getUsername(), wfResp.getUniqueTokenId(), whr, wfResp.getIssuer());
+            LOG.debug("Created date={}", wfResp.getTokenCreated());
+            LOG.debug("Expired date={}", wfResp.getTokenExpires());
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Validated 'wresult' : "
+                    + System.getProperty("line.separator") + wresult);
+            }
+            return idpToken;
+        } catch (IllegalStateException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            LOG.warn("Unexpected exception occured", ex);
+            throw new IllegalStateException("Unexpected exception occured: " + ex.getMessage());
+        }
+    }
+    
+    
+    private FedizContext getFedizContext(Idp idpConfig,
+            TrustedIdp trustedIdpConfig) throws ProcessingException {
+
+        ContextConfig config = new ContextConfig();
+
+        config.setName("whatever");
+
+        // Configure certificate store
+        String certificate = trustedIdpConfig.getCertificate();
+        boolean isCertificateLocation = !certificate.startsWith("-----BEGIN CERTIFICATE");
+        if (isCertificateLocation) {
+            CertificateStores certStores = new CertificateStores();
+            TrustManagersType tm0 = new TrustManagersType();
+            KeyStoreType ks0 = new KeyStoreType();
+            ks0.setType("PEM");
+            // ks0.setType("JKS");
+            // ks0.setPassword("changeit");
+            ks0.setFile(trustedIdpConfig.getCertificate());
+            tm0.setKeyStore(ks0);
+            certStores.getTrustManager().add(tm0);
+            config.setCertificateStores(certStores);
+        }
+        
+        // Configure trusted IDP
+        TrustedIssuers trustedIssuers = new TrustedIssuers();
+        TrustedIssuerType ti0 = new TrustedIssuerType();
+        ti0.setCertificateValidation(ValidationType.PEER_TRUST);
+        ti0.setName(trustedIdpConfig.getName());
+        // ti0.setSubject(".*CN=www.sts.com.*");
+        trustedIssuers.getIssuer().add(ti0);
+        config.setTrustedIssuers(trustedIssuers);
+
+        FederationProtocolType protocol = new FederationProtocolType();
+        config.setProtocol(protocol);
+
+        AudienceUris audienceUris = new AudienceUris();
+        audienceUris.getAudienceItem().add(idpConfig.getRealm());
+        config.setAudienceUris(audienceUris);
+
+        FedizContext fedContext = new FedizContext(config);
+        if (!isCertificateLocation) {
+            CertificateStore cs = null;
+            
+            X509Certificate cert;
+            try {
+                cert = parseCertificate(trustedIdpConfig.getCertificate());
+            } catch (Exception ex) {
+                LOG.error("Failed to parse trusted certificate", ex);
+                throw new ProcessingException("Failed to parse trusted certificate");
+            }
+            cs = new CertificateStore(Collections.singletonList(cert).toArray(new X509Certificate[0]));
+            
+            TrustManager tm = new TrustManager(cs);
+            fedContext.getCertificateStores().add(tm);
+        }
+        
+        fedContext.init();
+        return fedContext;
+    }
+    
+    private X509Certificate parseCertificate(String certificate)
+        throws CertificateException, Base64DecodingException, IOException {
+        
+        //before decoding we need to get rod off the prefix and suffix
+        byte [] decoded = Base64.decode(certificate.replaceAll("-----BEGIN CERTIFICATE-----", "").
+                                        replaceAll("-----END CERTIFICATE-----", ""));
+
+        try (InputStream is = new ByteArrayInputStream(decoded)) {
+            return (X509Certificate)CertificateFactory.getInstance("X.509").generateCertificate(is);
+        }
+    }
+    
+
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/4df66f37/services/idp/src/main/webapp/WEB-INF/flows/federation-signin-response.xml
----------------------------------------------------------------------
diff --git a/services/idp/src/main/webapp/WEB-INF/flows/federation-signin-response.xml b/services/idp/src/main/webapp/WEB-INF/flows/federation-signin-response.xml
index 46da2cb..a66d0b8 100644
--- a/services/idp/src/main/webapp/WEB-INF/flows/federation-signin-response.xml
+++ b/services/idp/src/main/webapp/WEB-INF/flows/federation-signin-response.xml
@@ -29,6 +29,8 @@
     <input name="wresult" />
     <input name="RelayState" />
     <input name="SAMLResponse" />
+    <input name="state" />
+    <input name="code" />
 
     <on-start>
         <!-- restore 'wreply','wtrealm','whr' for current 'wctx' -->
@@ -78,5 +80,7 @@
 
     <!-- abnormal exit point : Http 500 Internal Server Error -->
     <end-state id="scInternalServerError" />
+    
+    <end-state id="redirectToTrustedIDP" />
 
 </flow>

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/4df66f37/services/idp/src/main/webapp/WEB-INF/flows/federation-validate-request.xml
----------------------------------------------------------------------
diff --git a/services/idp/src/main/webapp/WEB-INF/flows/federation-validate-request.xml b/services/idp/src/main/webapp/WEB-INF/flows/federation-validate-request.xml
index 4ea0d9a..8d0934c 100644
--- a/services/idp/src/main/webapp/WEB-INF/flows/federation-validate-request.xml
+++ b/services/idp/src/main/webapp/WEB-INF/flows/federation-validate-request.xml
@@ -34,14 +34,18 @@
             <set name="flowScope.wreq" value="requestParameters.wreq" />
             <set name="flowScope.RelayState" value="requestParameters.RelayState" />
             <set name="flowScope.SAMLResponse" value="requestParameters.SAMLResponse" />
+            <set name="flowScope.state" value="requestParameters.state" />
+            <set name="flowScope.code" value="requestParameters.code" />
             <evaluate expression="requestScope.getString('wauth','default')"
                 result="flowScope.wauth" />
             <set name="flowScope.idpConfig" value="config.getIDP(null)" />
         </on-entry>
         <if test="requestParameters.wa == 'wsignout1.0' or requestParameters.wa == 'wsignoutcleanup1.0'"
             then="selectSignOutProcess" />
-        <if test="requestParameters.wa == 'wsignin1.0'" then="selectWsFedProcess" 
-            else="selectSAMLProcess" /> 
+        <if test="requestParameters.wa == 'wsignin1.0'" then="selectWsFedProcess" />
+        <if test="requestParameters.SAMLResponse != null" then="selectSAMLProcess"
+            else="signinResponse"
+        /> 
     </decision-state>
 
     <decision-state id="selectWsFedProcess">
@@ -103,6 +107,8 @@
         <input name="wresult" value="flowScope.wresult" />
         <input name="RelayState" value="flowScope.RelayState" />
         <input name="SAMLResponse" value="flowScope.SAMLResponse" />
+        <input name="state" value="flowScope.state" />
+        <input name="code" value="flowScope.code" />
 
         <output name="wtrealm" />
         <output name="wreply" />
@@ -119,6 +125,7 @@
         </transition>
         <transition on="viewBadRequest" to="viewBadRequest" />
         <transition on="scInternalServerError" to="scInternalServerError" />
+        <transition on="redirectToTrustedIDP" to="redirectToTrustedIDP" />
     </subflow-state>
 
     <!-- produce RP security token (as String type) -->
@@ -240,4 +247,13 @@
         </on-entry>
     </end-state>
 
+    <end-state id="formResponseView" view="signinresponseform">
+        <on-entry>
+            <evaluate expression="flowScope.signinResponseUrl" result="requestScope.fedAction" />
+            <evaluate expression="flowScope.wtrealm" result="requestScope.fedWTrealm" />
+            <evaluate expression="flowScope.wctx" result="requestScope.fedWCtx" />
+            <evaluate expression="flowScope.rpToken" result="requestScope.fedWResult" />
+        </on-entry>
+    </end-state>
+    
 </flow>