Author: sergeyb
Date: Tue Sep 6 14:38:36 2011
New Revision: 1165687
URL: http://svn.apache.org/viewvc?rev=1165687&view=rev
Log:
[CXF-3588] Prototyping some code for validating SAML tokens and using them for
ACL decisions
Added:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claim.java
(with props)
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claims.java
(with props)
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Subject.java
(with props)
cxf/trunk/rt/rs/security/xml/src/test/java/org/
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/authorization/
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/authorization/ClaimsAuthorizingInterceptorTest.java
(with props)
Modified:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SAMLUtils.java
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedOutInterceptor.java
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java
cxf/trunk/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/saml/SamlCallbackHandler.java
Modified:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java?rev=1165687&r1=1165686&r2=1165687&view=diff
==============================================================================
---
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java
(original)
+++
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/AbstractSamlInHandler.java
Tue Sep 6 14:38:36 2011
@@ -35,6 +35,7 @@ import javax.ws.rs.core.Response;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
+
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.jaxrs.ext.RequestHandler;
@@ -42,10 +43,12 @@ import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageUtils;
import org.apache.cxf.rs.security.common.CryptoLoader;
import org.apache.cxf.rs.security.common.SecurityUtils;
+import org.apache.cxf.rs.security.saml.authorization.SecurityContextProvider;
+import
org.apache.cxf.rs.security.saml.authorization.SecurityContextProviderImpl;
+import org.apache.cxf.security.SecurityContext;
import org.apache.cxf.security.transport.TLSSessionInfo;
import org.apache.cxf.ws.security.SecurityConstants;
import org.apache.ws.security.WSSConfig;
-import org.apache.ws.security.WSSecurityEngineResult;
import org.apache.ws.security.handler.RequestData;
import org.apache.ws.security.handler.WSHandlerConstants;
import org.apache.ws.security.saml.SAMLKeyInfo;
@@ -54,6 +57,7 @@ import org.apache.ws.security.saml.ext.O
import org.apache.ws.security.validate.Credential;
import org.apache.ws.security.validate.SamlAssertionValidator;
import org.apache.ws.security.validate.Validator;
+import org.apache.xml.security.signature.XMLSignature;
public abstract class AbstractSamlInHandler implements RequestHandler {
@@ -66,11 +70,17 @@ public abstract class AbstractSamlInHand
}
private Validator samlValidator = new SamlAssertionValidator();
+ private SecurityContextProvider scProvider = new
SecurityContextProviderImpl();
public void setValidator(Validator validator) {
samlValidator = validator;
}
+ public void setSecurityContextProvider(SecurityContextProvider p) {
+ scProvider = p;
+ }
+
+
protected void validateToken(Message message, InputStream tokenStream) {
Document doc = null;
@@ -109,10 +119,10 @@ public abstract class AbstractSamlInHand
}
Certificate[] tlsCerts = getTLSCertificates(message);
- if (!checkHolderOfKey(assertion, null, tlsCerts)) {
+ if (!checkHolderOfKey(message, assertion, tlsCerts)) {
throwFault("Holder Of Key claim fails", null);
}
- if (!checkSenderVouches(assertion, null, tlsCerts)) {
+ if (!checkSenderVouches(message, assertion, tlsCerts)) {
throwFault("Sender vouchers claim fails", null);
}
if (!checkBearer(assertion, tlsCerts)) {
@@ -123,11 +133,19 @@ public abstract class AbstractSamlInHand
// from the xml-sig envelope which this assertion must be
contained in
throwFault("Unsigned Assertion can only be validated with
two-way TLS", null);
}
+ setSecurityContext(message, assertion);
+
} catch (Exception ex) {
throwFault("Assertion can not be validated", ex);
}
}
+ protected void setSecurityContext(Message message, AssertionWrapper
wrapper) {
+ if (scProvider != null) {
+ SecurityContext sc = scProvider.getSecurityContext(message,
wrapper);
+ message.setContent(SecurityContext.class, sc);
+ }
+ }
private Certificate[] getTLSCertificates(Message message) {
TLSSessionInfo tlsInfo = message.get(TLSSessionInfo.class);
@@ -142,83 +160,58 @@ public abstract class AbstractSamlInHand
throw ex != null ? new WebApplicationException(ex, response) : new
WebApplicationException(response);
}
- // TODO: Most of this code can make it into rt/security to minimize the
duplication
- // between ws/security and rs/security
-
- // WSSecurityEngineResult is HashMap extension and can be used as such
/**
* Check the sender-vouches requirements against the received assertion.
The SAML
* Assertion and the request body must be signed by the same signature.
*/
private boolean checkSenderVouches(
+ Message message,
AssertionWrapper assertionWrapper,
- List<WSSecurityEngineResult> signedResults,
Certificate[] tlsCerts
) {
//
// If we have a 2-way TLS connection, then we don't have to check that
the
- // assertion + SOAP body are signed
+ // assertion + body are signed
+
+ // If no body is available (ex, with GET) then consider validating that
+ // the base64-encoded token is signed by the same signature
//
if (tlsCerts != null && tlsCerts.length > 0) {
return true;
}
- return false;
-// List<String> confirmationMethods =
assertionWrapper.getConfirmationMethods();
-// for (String confirmationMethod : confirmationMethods) {
-// if (OpenSAMLUtil.isMethodSenderVouches(confirmationMethod)) {
-// if (signedResults == null || signedResults.isEmpty()) {
-// return false;
-// }
-// if (!checkAssertionAndBodyAreSigned(assertionWrapper)) {
-// return false;
-// }
-// }
-// }
-// return true;
+ List<String> confirmationMethods =
assertionWrapper.getConfirmationMethods();
+ for (String confirmationMethod : confirmationMethods) {
+ if (OpenSAMLUtil.isMethodSenderVouches(confirmationMethod)) {
+ XMLSignature sig = message.getContent(XMLSignature.class);
+ if (sig == null) {
+ return false;
+ }
+ try {
+ byte[] sigValue1 = sig.getSignatureValue();
+ byte[] sigValue2 = assertionWrapper.getSignatureValue();
+ return Arrays.equals(sigValue1, sigValue2);
+ } catch (Exception ex) {
+ return false;
+ }
+ }
+ }
+ return true;
}
- private boolean checkBearer(AssertionWrapper assertionWrapper,
Certificate[] tlsCerts) {
-
- // Check Recipient attribute. Perhaps, if STS validator is injected,
then it can forward
- // this assertion to IDP which will confirm being Recipient
-
- // It seems if we have a signed assertion and a payload then bearer
may get validated same
- // way as sender-vouches.
-
- if (tlsCerts != null && tlsCerts.length > 0) {
- return true;
- }
-
-
- return false;
-
-
- //List<String> confirmationMethods =
assertionWrapper.getConfirmationMethods();
- // for (String confirmationMethod : confirmationMethods) {
- // if (isMethodBearer(confirmationMethod)) {
- //
- // }
- // }
-
-
- }
- //private boolean isMethodBearer(String confirmMethod) {
- // return confirmMethod != null &&
confirmMethod.startsWith("urn:oasis:names:tc:SAML:")
- // && confirmMethod.endsWith(":cm:bearer");
- //}
- public boolean checkHolderOfKey(AssertionWrapper assertionWrapper,
- List<WSSecurityEngineResult> signedResults,
+ public boolean checkHolderOfKey(Message message,
+ AssertionWrapper assertionWrapper,
Certificate[] tlsCerts) {
List<String> confirmationMethods =
assertionWrapper.getConfirmationMethods();
for (String confirmationMethod : confirmationMethods) {
if (OpenSAMLUtil.isMethodHolderOfKey(confirmationMethod)) {
- if (tlsCerts == null && (signedResults == null ||
signedResults.isEmpty())) {
+ XMLSignature sig = message.getContent(XMLSignature.class);
+ if (tlsCerts == null || sig == null) {
return false;
}
SAMLKeyInfo subjectKeyInfo =
assertionWrapper.getSubjectKeyInfo();
- if (!compareCredentials(subjectKeyInfo, signedResults,
tlsCerts)) {
+ if (!compareCredentials(subjectKeyInfo, sig, tlsCerts)) {
return false;
}
}
@@ -236,12 +229,11 @@ public abstract class AbstractSamlInHand
*/
private boolean compareCredentials(
SAMLKeyInfo subjectKeyInfo,
- List<WSSecurityEngineResult> signedResults,
+ XMLSignature sig,
Certificate[] tlsCerts
) {
X509Certificate[] subjectCerts = subjectKeyInfo.getCerts();
PublicKey subjectPublicKey = subjectKeyInfo.getPublicKey();
- byte[] subjectSecretKey = subjectKeyInfo.getSecret();
//
// Try to match the TLS certs first
@@ -257,13 +249,10 @@ public abstract class AbstractSamlInHand
//
// Now try the message-level signatures
//
- for (WSSecurityEngineResult signedResult : signedResults) {
+ try {
X509Certificate[] certs =
-
(X509Certificate[])signedResult.get(WSSecurityEngineResult.TAG_X509_CERTIFICATES);
- PublicKey publicKey =
-
(PublicKey)signedResult.get(WSSecurityEngineResult.TAG_PUBLIC_KEY);
- byte[] secretKey =
- (byte[])signedResult.get(WSSecurityEngineResult.TAG_SECRET);
+ new X509Certificate[] {sig.getKeyInfo().getX509Certificate()};
+ PublicKey publicKey = sig.getKeyInfo().getPublicKey();
if (certs != null && certs.length > 0 && subjectCerts != null
&& subjectCerts.length > 0 &&
certs[0].equals(subjectCerts[0])) {
return true;
@@ -271,33 +260,26 @@ public abstract class AbstractSamlInHand
if (publicKey != null && publicKey.equals(subjectPublicKey)) {
return true;
}
- if (checkSecretKey(secretKey, subjectSecretKey, signedResult)) {
- return true;
- }
+ } catch (Exception ex) {
+ // ignore
}
+
return false;
}
- private boolean checkSecretKey(
- byte[] secretKey,
- byte[] subjectSecretKey,
- WSSecurityEngineResult signedResult
- ) {
- if (secretKey != null && subjectSecretKey != null
- && Arrays.equals(secretKey, subjectSecretKey)) {
- return true;
-// else {
-// Principal principal =
-//
(Principal)signedResult.get(WSSecurityEngineResult.TAG_PRINCIPAL);
-// if (principal instanceof WSDerivedKeyTokenPrincipal) {
-// secretKey =
((WSDerivedKeyTokenPrincipal)principal).getSecret();
-// if (Arrays.equals(secretKey, subjectSecretKey)) {
-// return true;
-// }
-// }
-// }
+ private boolean checkBearer(AssertionWrapper assertionWrapper,
Certificate[] tlsCerts) {
+ List<String> confirmationMethods =
assertionWrapper.getConfirmationMethods();
+ for (String confirmationMethod : confirmationMethods) {
+ if (isMethodBearer(confirmationMethod)) {
+ // do some more validation - time based, etc
+ return true;
+ }
}
- return false;
+ return true;
}
+ private boolean isMethodBearer(String confirmMethod) {
+ return confirmMethod != null &&
confirmMethod.startsWith("urn:oasis:names:tc:SAML:")
+ && confirmMethod.endsWith(":cm:bearer");
+ }
}
Modified:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SAMLUtils.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SAMLUtils.java?rev=1165687&r1=1165686&r2=1165687&view=diff
==============================================================================
---
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SAMLUtils.java
(original)
+++
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SAMLUtils.java
Tue Sep 6 14:38:36 2011
@@ -20,6 +20,8 @@ package org.apache.cxf.rs.security.saml;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
import java.util.logging.Logger;
import javax.security.auth.callback.CallbackHandler;
@@ -31,11 +33,18 @@ import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageUtils;
import org.apache.cxf.rs.security.common.CryptoLoader;
import org.apache.cxf.rs.security.common.SecurityUtils;
+import org.apache.cxf.rs.security.saml.assertion.Claim;
+import org.apache.cxf.rs.security.saml.assertion.Claims;
+import org.apache.cxf.rs.security.saml.assertion.Subject;
import org.apache.cxf.ws.security.SecurityConstants;
import org.apache.ws.security.WSPasswordCallback;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.saml.ext.AssertionWrapper;
import org.apache.ws.security.saml.ext.SAMLParms;
+import org.opensaml.saml2.core.Attribute;
+import org.opensaml.saml2.core.AttributeStatement;
+import org.opensaml.saml2.core.NameID;
+import org.opensaml.xml.XMLObject;
public final class SAMLUtils {
private static final Logger LOG =
@@ -45,6 +54,43 @@ public final class SAMLUtils {
}
+ public static Subject getSubject(Message message, AssertionWrapper
assertionW) {
+ org.opensaml.saml2.core.Subject s = assertionW.getSaml2().getSubject();
+ Subject subject = new Subject();
+ NameID nameId = s.getNameID();
+ subject.setNameQualifier(nameId.getNameQualifier());
+ // if format is transient then we may need to use STSClient
+ // to request an alternate name from IDP
+ subject.setNameFormat(nameId.getFormat());
+
+ subject.setName(nameId.getValue());
+ subject.setSpId(nameId.getSPProvidedID());
+ subject.setSpQualifier(nameId.getSPNameQualifier());
+ return subject;
+ }
+
+
+ public static Claims getClaims(AssertionWrapper assertionW) {
+ // Should we just do a simple DOM parsing without even relying on
+ // OpenSaml
+ List<Claim> claims = new ArrayList<Claim>();
+ List<AttributeStatement> statements =
assertionW.getSaml2().getAttributeStatements();
+ for (AttributeStatement as : statements) {
+ for (Attribute atr : as.getAttributes()) {
+ Claim claim = new Claim();
+ claim.setName(atr.getName());
+ claim.setNameFormat(atr.getNameFormat());
+ claim.setFriendlyName(atr.getFriendlyName());
+ for (XMLObject o : atr.getAttributeValues()) {
+ String attrValue = o.getDOM().getTextContent();
+ claim.getValues().add(attrValue);
+ }
+ claims.add(claim);
+ }
+ }
+ return new Claims(claims);
+ }
+
public static AssertionWrapper createAssertion(Message message) throws
Fault {
CallbackHandler handler = SecurityUtils.getCallbackHandler(
message, SAMLUtils.class,
SecurityConstants.SAML_CALLBACK_HANDLER);
Modified:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedOutInterceptor.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedOutInterceptor.java?rev=1165687&r1=1165686&r2=1165687&view=diff
==============================================================================
---
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedOutInterceptor.java
(original)
+++
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/SamlEnvelopedOutInterceptor.java
Tue Sep 6 14:38:36 2011
@@ -94,11 +94,10 @@ public class SamlEnvelopedOutInterceptor
root.appendChild(docEl);
if (signLater) {
- // it appears all the above manipulation with
- // adopting and removing nodes
- // leaves some stale refs/state and thus the digest ends uo being
wrong
- // on the server side if XML sig is applied later in the enveloped
mode
- // TODO: this is not critical now - but figure iut if we can avoid
copying
+ // It appears adopting and removing nodes
+ // leaves some stale refs/state with adopted nodes and thus the
digest ends up
+ // being wrong on the server side if XML sig is applied later in
the enveloped mode
+ // TODO: this is not critical now - but figure out if we can avoid
copying
// DOMs
CachedOutputStream bos = new CachedOutputStream();
DOMUtils.writeXml(newDoc, bos);
Added:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claim.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claim.java?rev=1165687&view=auto
==============================================================================
---
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claim.java
(added)
+++
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claim.java
Tue Sep 6 14:38:36 2011
@@ -0,0 +1,90 @@
+/**
+ * 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.rs.security.saml.assertion;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class Claim {
+ public static final String DEFAULT_ROLE_NAME =
+ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role";
+ public static final String DEFAULT_NAME_FORMAT =
+ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims";
+
+ private String nameFormat;
+ private String name;
+ private String friendlyName;
+
+ private List<String> values = new ArrayList<String>();
+
+ public Claim() {
+
+ }
+
+ public Claim(String nameFormat, String name) {
+ this.nameFormat = nameFormat;
+ this.name = name;
+ }
+
+ public Claim(String nameFormat, String name, String value) {
+ this(nameFormat, name, Collections.singletonList(value));
+ }
+
+ public Claim(String nameFormat, String name, List<String> values) {
+ this.nameFormat = nameFormat;
+ this.name = name;
+ this.values = values;
+ }
+
+ public void setNameFormat(String nameFormat) {
+ this.nameFormat = nameFormat;
+ }
+
+ public String getNameFormat() {
+ return nameFormat;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setFriendlyName(String friendlyName) {
+ this.friendlyName = friendlyName;
+ }
+
+ public String getFriendlyName() {
+ return friendlyName;
+ }
+
+ public void setValues(List<String> values) {
+ this.values = values;
+ }
+
+ public List<String> getValues() {
+ return values;
+ }
+
+
+
+}
Propchange:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claim.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claim.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claims.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claims.java?rev=1165687&view=auto
==============================================================================
---
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claims.java
(added)
+++
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claims.java
Tue Sep 6 14:38:36 2011
@@ -0,0 +1,62 @@
+/**
+ * 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.rs.security.saml.assertion;
+
+import java.util.List;
+
+public class Claims {
+
+ private List<Claim> claims;
+
+ public Claims(List<Claim> claims) {
+ this.claims = claims;
+ }
+
+ public List<Claim> getClaims() {
+ return claims;
+ }
+
+ public Claim findClaimByFriendlyName(String friendlyName) {
+ for (Claim c : claims) {
+ if (c.getFriendlyName().equals(friendlyName)) {
+ return c;
+ }
+ }
+ return null;
+ }
+
+ public Claim findClaimByName(String name) {
+ for (Claim c : claims) {
+ if (c.getName().equals(name)) {
+ return c;
+ }
+ }
+ return null;
+ }
+
+ public Claim findClaimByFormatAndName(String format, String name) {
+ for (Claim c : claims) {
+ if (c.getName().equals(name)
+ && c.getNameFormat().equals(format)) {
+ return c;
+ }
+ }
+ return null;
+ }
+}
Propchange:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claims.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Claims.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Added:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Subject.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Subject.java?rev=1165687&view=auto
==============================================================================
---
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Subject.java
(added)
+++
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Subject.java
Tue Sep 6 14:38:36 2011
@@ -0,0 +1,85 @@
+/**
+ * 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.rs.security.saml.assertion;
+
+public class Subject {
+ private String nameFormat;
+ private String nameQualifier;
+ private String name;
+ private String spQualifier;
+ private String spId;
+
+ private String alternateName;
+
+ public Subject() {
+
+ }
+
+ public Subject(String name) {
+ this.name = name;
+ }
+
+ public Subject(String nameFormat, String name) {
+ this.nameFormat = nameFormat;
+ this.name = name;
+ }
+
+ public void setNameFormat(String nameFormat) {
+ this.nameFormat = nameFormat;
+ }
+ public String getNameFormat() {
+ return nameFormat;
+ }
+ public void setNameQualifier(String nameQualifier) {
+ this.nameQualifier = nameQualifier;
+ }
+ public String getNameQualifier() {
+ return nameQualifier;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public String getName() {
+ return name;
+ }
+
+ public void setSpId(String spId) {
+ this.spId = spId;
+ }
+
+ public String getSpId() {
+ return spId;
+ }
+
+ public void setSpQualifier(String spQualifier) {
+ this.spQualifier = spQualifier;
+ }
+
+ public String getSpQualifier() {
+ return spQualifier;
+ }
+
+ public void setAlternateName(String alternateName) {
+ this.alternateName = alternateName;
+ }
+
+ public String getAlternateName() {
+ return alternateName;
+ }
+}
Propchange:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Subject.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/saml/assertion/Subject.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Modified:
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java?rev=1165687&r1=1165686&r2=1165687&view=diff
==============================================================================
---
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java
(original)
+++
cxf/trunk/rt/rs/security/xml/src/main/java/org/apache/cxf/rs/security/xml/XmlSigInHandler.java
Tue Sep 6 14:38:36 2011
@@ -49,11 +49,15 @@ import org.apache.xml.security.utils.Con
public class XmlSigInHandler extends AbstractXmlSecInHandler implements
RequestHandler {
private boolean removeSignature = true;
+ private boolean persistSignature = true;
public void setRemoveSignature(boolean remove) {
this.removeSignature = remove;
}
+ public void setPersistSignature(boolean persist) {
+ this.persistSignature = persist;
+ }
public Response handleRequest(Message message, ClassResourceInfo
resourceClass) {
@@ -105,6 +109,9 @@ public class XmlSigInHandler extends Abs
// validate trust
new TrustValidator().validateTrust(crypto, cert,
keyInfo.getPublicKey());
+ if (persistSignature) {
+ message.put(XMLSignature.class, signature);
+ }
} catch (Exception ex) {
throwFault("Signature validation failed", ex);
}
Added:
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/authorization/ClaimsAuthorizingInterceptorTest.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/authorization/ClaimsAuthorizingInterceptorTest.java?rev=1165687&view=auto
==============================================================================
---
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/authorization/ClaimsAuthorizingInterceptorTest.java
(added)
+++
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/authorization/ClaimsAuthorizingInterceptorTest.java
Tue Sep 6 14:38:36 2011
@@ -0,0 +1,283 @@
+/**
+ * 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.rs.security.saml.authorization;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.cxf.interceptor.security.AccessDeniedException;
+import org.apache.cxf.interceptor.security.SecureAnnotationsInterceptor;
+import org.apache.cxf.message.ExchangeImpl;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageImpl;
+import org.apache.cxf.rs.security.saml.assertion.Subject;
+import org.apache.cxf.security.SecurityContext;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class ClaimsAuthorizingInterceptorTest extends Assert {
+
+ private ClaimsAuthorizingInterceptor interceptor;
+
+ @Before
+ public void setUp() {
+ interceptor = new ClaimsAuthorizingInterceptor();
+ interceptor.setNameAliases(
+ Collections.singletonMap("authentication",
"http://authentication"));
+ interceptor.setFormatAliases(
+ Collections.singletonMap("claims", "http://claims"));
+ interceptor.setSecuredObject(new TestService());
+
+ }
+
+ @Test
+ public void testClaimDefaultNameAndFormat() throws Exception {
+ doTestClaims("claimWithDefaultNameAndFormat",
+ createDefaultClaim("admin", "user"),
+ createClaim("http://authentication", "http://claims",
"password"));
+ try {
+ doTestClaims("claimWithDefaultNameAndFormat",
+ createDefaultClaim("user"),
+ createClaim("http://authentication", "http://claims",
"password"));
+ fail("AccessDeniedException expected");
+ } catch (AccessDeniedException ex) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testClaimMatchAll() throws Exception {
+ doTestClaims("claimMatchAll",
+ createDefaultClaim("admin", "manager"),
+ createClaim("http://authentication", "http://claims",
"password"));
+ try {
+ doTestClaims("claimMatchAll",
+ createDefaultClaim("admin"),
+ createClaim("http://authentication", "http://claims",
"password"));
+ doTestClaims("claimMatchAll",
+ createDefaultClaim("manager"),
+ createClaim("http://authentication", "http://claims",
"password"));
+ fail("AccessDeniedException expected");
+ } catch (AccessDeniedException ex) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testMissingExpectedClaim() throws Exception {
+ doTestClaims("claimWithDefaultNameAndFormat",
+ createDefaultClaim("admin"),
+ createClaim("http://authentication", "http://claims",
"password"));
+ try {
+ doTestClaims("claimWithDefaultNameAndFormat",
+ createDefaultClaim("admin"));
+ fail("AccessDeniedException expected");
+ } catch (AccessDeniedException ex) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testExtraNonExpectedClaim() throws Exception {
+ doTestClaims("claimWithDefaultNameAndFormat",
+ createDefaultClaim("admin", "user"),
+ createClaim("http://authentication", "http://claims",
"password"),
+ createClaim("http://extra/claims", "http://claims",
"claim"));
+ }
+
+ @Test
+ public void testClaimSpecificNameAndFormat() throws Exception {
+ doTestClaims("claimWithSpecificNameAndFormat",
+ createClaim("http://cxf/roles", "http://claims", "admin",
"user"),
+ createClaim("http://authentication", "http://claims",
"password"));
+ try {
+ doTestClaims("claimWithSpecificNameAndFormat",
+ createDefaultClaim("admin", "user"),
+ createClaim("http://authentication", "http://claims",
"password"));
+ fail("AccessDeniedException expected");
+ } catch (AccessDeniedException ex) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testClaimLaxMode() throws Exception {
+ doTestClaims("claimLaxMode",
+ createClaim("http://authentication", "http://claims",
"password"));
+ doTestClaims("claimLaxMode");
+ try {
+ doTestClaims("claimLaxMode",
+ createClaim("http://authentication", "http://claims",
"smartcard"));
+ fail("AccessDeniedException expected");
+ } catch (AccessDeniedException ex) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testMultipleClaims() throws Exception {
+ doTestClaims("multipleClaims",
+ createDefaultClaim("admin"),
+ createClaim("http://authentication", "http://claims",
"smartcard"),
+ createClaim("http://location", "http://claims", "UK"));
+ doTestClaims("multipleClaims",
+ createDefaultClaim("admin"),
+ createClaim("http://authentication", "http://claims",
"password"),
+ createClaim("http://location", "http://claims", "USA"));
+ try {
+ doTestClaims("multipleClaims",
+ createDefaultClaim("admin"),
+ createClaim("http://authentication", "http://claims",
"unsecuretransport"),
+ createClaim("http://location", "http://claims", "UK"));
+ fail("AccessDeniedException expected");
+ } catch (AccessDeniedException ex) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testUserInRoleAndClaims() throws Exception {
+ SecureAnnotationsInterceptor in = new SecureAnnotationsInterceptor();
+ in.setAnnotationClassName(SecureRole.class.getName());
+ in.setSecuredObject(new TestService2());
+
+ Message m = prepareMessage(TestService2.class, "test",
+ createDefaultClaim("admin"),
+ createClaim("a", "b", "c"));
+
+ in.handleMessage(m);
+
+ ClaimsAuthorizingInterceptor in2 = new ClaimsAuthorizingInterceptor();
+ org.apache.cxf.rs.security.saml.assertion.Claim claim =
+ new org.apache.cxf.rs.security.saml.assertion.Claim("a", "b", "c");
+ in2.setClaims(Collections.singletonMap("test",
+ Collections.singletonList(
+ new ClaimBean(claim))));
+ in2.handleMessage(m);
+
+ try {
+ in.handleMessage(prepareMessage(TestService2.class, "test",
+ createDefaultClaim("user")));
+ fail("AccessDeniedException expected");
+ } catch (AccessDeniedException ex) {
+ // expected
+ }
+ }
+
+
+ private void doTestClaims(String methodName,
+ org.apache.cxf.rs.security.saml.assertion.Claim... claim)
+ throws Exception {
+ Message m = prepareMessage(TestService.class, methodName, claim);
+ interceptor.handleMessage(m);
+ }
+
+ private Message prepareMessage(Class<?> cls,
+ String methodName,
+ org.apache.cxf.rs.security.saml.assertion.Claim... claim)
+ throws Exception {
+ List<org.apache.cxf.rs.security.saml.assertion.Claim> claims =
+ new
ArrayList<org.apache.cxf.rs.security.saml.assertion.Claim>(Arrays.asList(claim));
+ SecurityContext sc = new SAMLSecurityContext(
+ new Subject("user"), claims);
+ Message m = new MessageImpl();
+ m.setExchange(new ExchangeImpl());
+ m.put(SecurityContext.class, sc);
+ m.put("org.apache.cxf.resource.method",
+ cls.getMethod(methodName, new Class[]{}));
+ return m;
+ }
+
+ private org.apache.cxf.rs.security.saml.assertion.Claim createDefaultClaim(
+ String... values) {
+ return
createClaim(org.apache.cxf.rs.security.saml.assertion.Claim.DEFAULT_ROLE_NAME,
+
org.apache.cxf.rs.security.saml.assertion.Claim.DEFAULT_NAME_FORMAT,
+ values);
+ }
+
+ private org.apache.cxf.rs.security.saml.assertion.Claim createClaim(
+ String name, String format, String... values) {
+ org.apache.cxf.rs.security.saml.assertion.Claim claim =
+ new org.apache.cxf.rs.security.saml.assertion.Claim();
+ claim.setName(name);
+ claim.setNameFormat(format);
+ claim.setValues(Arrays.asList(values));
+ return claim;
+ }
+
+ @Claim(name = "authentication", format = "claims",
+ value = "password")
+ public static class TestService {
+ // default name and format are used
+ @Claim({"admin", "manager" })
+ public void claimWithDefaultNameAndFormat() {
+
+ }
+
+ // explicit name and format
+ @Claim(name = "http://cxf/roles", format = "http://claims",
+ value = {"admin", "manager" })
+ public void claimWithSpecificNameAndFormat() {
+
+ }
+
+ @Claim(name = "http://authentication", format = "http://claims",
+ value = "password", mode = ClaimMode.LAX)
+ public void claimLaxMode() {
+
+ }
+
+ @Claims({
+ @Claim(name = "http://location", format = "http://claims",
+ value = {"UK", "USA" }),
+ @Claim(value = {"admin", "manager" }),
+ @Claim(name = "authentication", format = "claims",
+ value = {"password", "smartcard" })
+ })
+ public void multipleClaims() {
+
+ }
+
+ // user must have both admin and manager roles, default is 'or'
+ @Claim(value = {"admin", "manager" },
+ matchAll = true)
+ public void claimMatchAll() {
+
+ }
+ }
+ public static class TestService2 {
+ @SecureRole("admin")
+ public void test() {
+
+ }
+ }
+ @Target(ElementType.METHOD)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface SecureRole {
+ String[] value();
+ }
+}
Propchange:
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/authorization/ClaimsAuthorizingInterceptorTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
cxf/trunk/rt/rs/security/xml/src/test/java/org/apache/cxf/rs/security/saml/authorization/ClaimsAuthorizingInterceptorTest.java
------------------------------------------------------------------------------
svn:keywords = Rev Date
Modified:
cxf/trunk/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/saml/SamlCallbackHandler.java
URL:
http://svn.apache.org/viewvc/cxf/trunk/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/saml/SamlCallbackHandler.java?rev=1165687&r1=1165686&r2=1165687&view=diff
==============================================================================
---
cxf/trunk/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/saml/SamlCallbackHandler.java
(original)
+++
cxf/trunk/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/saml/SamlCallbackHandler.java
Tue Sep 6 14:38:36 2011
@@ -29,9 +29,14 @@ import javax.security.auth.callback.Unsu
import org.apache.ws.security.saml.ext.SAMLCallback;
import org.apache.ws.security.saml.ext.bean.AttributeBean;
import org.apache.ws.security.saml.ext.bean.AttributeStatementBean;
+import org.apache.ws.security.saml.ext.bean.AuthDecisionStatementBean;
+import org.apache.ws.security.saml.ext.bean.AuthDecisionStatementBean.Decision;
+import org.apache.ws.security.saml.ext.bean.AuthenticationStatementBean;
+import org.apache.ws.security.saml.ext.bean.ConditionsBean;
import org.apache.ws.security.saml.ext.bean.SubjectBean;
import org.apache.ws.security.saml.ext.builder.SAML1Constants;
import org.apache.ws.security.saml.ext.builder.SAML2Constants;
+import org.joda.time.DateTime;
import org.opensaml.common.SAMLVersion;
/**
@@ -62,7 +67,8 @@ public class SamlCallbackHandler impleme
} else {
callback.setSamlVersion(SAMLVersion.VERSION_11);
}
- callback.setIssuer("sts");
+ callback.setIssuer("https://idp.example.org/SAML2");
+
String subjectName = "uid=sts-client,o=mock-sts.com";
String subjectQualifier = "www.mock-sts.com";
if (!saml2 &&
SAML2Constants.CONF_SENDER_VOUCHES.equals(confirmationMethod)) {
@@ -72,13 +78,33 @@ public class SamlCallbackHandler impleme
new SubjectBean(
subjectName, subjectQualifier, confirmationMethod
);
+ // SubjectConfirmationData - not possible to set yet
callback.setSubject(subjectBean);
+ ConditionsBean conditions = new ConditionsBean();
+ conditions.setAudienceURI("https://sp.example.com/SAML2");
+ callback.setConditions(conditions);
+
+ AuthDecisionStatementBean authDecBean = new
AuthDecisionStatementBean();
+ authDecBean.setDecision(Decision.INDETERMINATE);
+
callback.setAuthDecisionStatementData(Collections.singletonList(authDecBean));
+
+ AuthenticationStatementBean authBean = new
AuthenticationStatementBean();
+ authBean.setSubject(subjectBean);
+ authBean.setAuthenticationInstant(new DateTime());
+ authBean.setSessionIndex("123456");
+ // AuthnContextClassRef is not set
+ authBean.setAuthenticationMethod(
+
"urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport");
+ callback.setAuthenticationStatementData(
+ Collections.singletonList(authBean));
+
AttributeStatementBean attrBean = new AttributeStatementBean();
attrBean.setSubject(subjectBean);
AttributeBean attributeBean = new AttributeBean();
attributeBean.setSimpleName("subject-role");
+
attributeBean.setQualifiedName("urn:oid:1.3.6.1.4.1.5923.1.1.1.1");
attributeBean.setAttributeValues(Collections.singletonList("system-user"));
attrBean.setSamlAttributes(Collections.singletonList(attributeBean));
callback.setAttributeStatementData(Collections.singletonList(attrBean));