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>