From commits-return-16734-apmail-cxf-commits-archive=cxf.apache.org@cxf.apache.org Thu Nov 3 23:05:28 2011 Return-Path: X-Original-To: apmail-cxf-commits-archive@www.apache.org Delivered-To: apmail-cxf-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id C20E37633 for ; Thu, 3 Nov 2011 23:05:28 +0000 (UTC) Received: (qmail 18587 invoked by uid 500); 3 Nov 2011 23:05:28 -0000 Delivered-To: apmail-cxf-commits-archive@cxf.apache.org Received: (qmail 18532 invoked by uid 500); 3 Nov 2011 23:05:28 -0000 Mailing-List: contact commits-help@cxf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cxf.apache.org Delivered-To: mailing list commits@cxf.apache.org Received: (qmail 18524 invoked by uid 99); 3 Nov 2011 23:05:28 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 03 Nov 2011 23:05:28 +0000 X-ASF-Spam-Status: No, hits=-1994.3 required=5.0 tests=ALL_TRUSTED,HTML_MESSAGE,MIME_HTML_ONLY X-Spam-Check-By: apache.org Received: from [140.211.11.22] (HELO thor.apache.org) (140.211.11.22) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 03 Nov 2011 23:05:22 +0000 Received: from thor (localhost [127.0.0.1]) by thor.apache.org (8.13.8+Sun/8.13.8) with ESMTP id pA3N50q6004893 for ; Thu, 3 Nov 2011 23:05:00 GMT Date: Thu, 3 Nov 2011 19:05:00 -0400 (EDT) From: confluence@apache.org To: commits@cxf.apache.org Message-ID: <21701622.26686.1320361500024.JavaMail.confluence@thor> Subject: [CONF] Apache CXF Documentation > JAX-RS SAML MIME-Version: 1.0 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Auto-Submitted: auto-generated X-Virus-Checked: Checked by ClamAV on apache.org

JAX-RS SAML

Page edited by Sergey Beryozkin


Changes (3)

=20 =20
=20 <= /tr> =20
...
Now, what is interesting is t= o see if it is possible to use these claims with Role-Based Access-Control = (for example, with endpoints relying on @RolesAllowed annotations) as well = as with the more complex authorization logic (for example, let this resourc= e be invoked only if Subject used a password to get authenticated at IDP). =

h2. Claims Based Access Control

CXF JAX-RS offers an extensio= n letting users to enforce a new fine-grained Claims Based Access Control b= ased on [Claim|http://svn.apache.org/repos/asf/cxf/trunk/rt/rs/security/xml= /src/main/java/org/apache/cxf/rs/security/saml/authorization/Claim.java] an= d [Claims|http://svn.apache.org/repos/asf/cxf/trunk/rt/rs/security/xml/src/= main/java/org/apache/cxf/rs/security/saml/authorization/Claims.java] annota= tions as well as [ClaimMode|http://svn.apache.org/repos/asf/cxf/trunk/rt/rs= /security/xml/src/main/java/org/apache/cxf/rs/security/saml/authorization/C= laimMode.java] enum class.

More to follow...
Here is a simple code fragment:
{code:java}
import org.apache= .cxf.rs.security.saml.authorization.Claim;
import org.apache.cxf.rs.sec= urity.saml.authorization.Claims;

@Path("/bookstore") public class SecureClaimBookStore {

@POST
@Path("= ;/books")
@Produces("application/xml")
@Cons= umes("application/xml")
@Claims({
@Claim({&q= uot;admin" }),
@Claim(name =3D "http://claims/authent= ication-format",
format =3D "http://claims/au= thentication",
value =3D {"fingertip", &= quot;smartcard" })
})
public Book addBook(Book book) {=
return book;
}

}
{code}

SecureCl= aimBookStore.addBook(Book) can only be invoked if Subject meets the followi= ng requirement: it needs to have a Claim with a value "admin" and= another Claim confirming that it got authenticated using either a 'fin= gertip' or 'smartcard' method. Note that @Claim({"admin&qu= ot;}) has no name and format classifiers set - it relies on default name an= d format values, namely "http://schemas.xmlsoap.org/ws/2005/05/identit= y/claims/role" and "http://schemas.xmlsoap.org/ws/2005/05/identit= y/claims" respectively. These default values may change in the future = depending on which claims are found to be used most often - but as you can = see you can always provide name and format values which will scope a given = claim value.

Note that in the above example, a Claim with the name= "http://claims/authentication-format" has two values, 'finge= rtip' and 'smartcard'. By default, in order to meet this Claim,= Subject needs to have a Claim which has either a 'fingertip' or &#= 39;smartcard' value. If it is expected that Subject needs to have a Cla= im which has both 'fingertip' and 'smartcard' values, then = the following change needs to be done:

{code:java}
import org.= apache.cxf.rs.security.saml.authorization.Claim;
import org.apache.cxf.= rs.security.saml.authorization.Claims;

@Path("/bookstore"= ;)
public class SecureClaimBookStore {

@POST
@Path= ("/books")
@Produces("application/xml")
= @Consumes("application/xml")
@Claims({
@Cla= im({"admin" }),
@Claim(name =3D "http://claims/a= uthentication-format",
format =3D "http://cla= ims/authentication",
value =3D {"fingertip&qu= ot;, "smartcard" },
matchAll =3D true)
= })
public Book addBook(Book book) {
return book;
= }

}
{code}

Claims can be specified using individua= l @Claim annotation, they can be set at the class level and overridden at t= he method level and finally a lax mode of check can be specified:

= {code:java}
import org.apache.cxf.rs.security.saml.authorization.Claim;=
import org.apache.cxf.rs.security.saml.authorization.Claims;

= @Path("/bookstore")
@Claim({"user"})
public cla= ss SecureClaimBookStore {

@POST
@Path("/books&quo= t;)
@Produces("application/xml")
@Consumes("= application/xml")
@Claims({
@Claim({"admin&q= uot; }),
@Claim(name =3D "http://claims/authentication-for= mat",
format =3D "http://claims/authenticatio= n",
value =3D {"fingertip", "smartc= ard" },
matchAll =3D true)
})
publi= c Book addBook(Book book) {
return book;
}

= @GET
@Claim(name =3D "http://claims/authentication-format&quo= t;,
format =3D "http://claims/authentication"= ,
value =3D {"password" },
= mode =3D ClaimMode.LAX)
public Book getBook() {
//... =
}

@GET
public BookList getBookList() {
= //...
}


}
{code}

In the above examp= le, getBookList() can be invoked if Subject has a Claim with the value &quo= t;user"; addBook() has it overridden - "admin" is expected a= nd the authentication format Claim too; getBook() can be invoked if Subject= has a Claim with the value "user" and it also must have the auth= entication format Claim with the value "password" - or no such Cl= aim at all.

Server Configuration Example:
{code:xml}
=
<bean id=3D"serviceBeanClaims" class=3D"org.apache.cx= f.systest.jaxrs.security.saml.SecureClaimBookStore"/>
<bean = id=3D"samlEnvHandler" class=3D"org.apache.cxf.rs.security.sa= ml.SamlEnvelopedInHandler">
<property name=3D"security= ContextProvider">
<bean class=3D"org.apache.cxf.sys= test.jaxrs.security.saml.CustomSecurityContextProvider"/>
<= /property>
</bean>

<bean id=3D"claimsHandler&= quot;
class=3D"org.apache.cxf.rs.security.saml.authorization= .ClaimsAuthorizingFilter">
<property name=3D"secure= dObject" ref=3D"serviceBeanClaims"/>
</bean>=

<jaxrs:server address=3D"/saml-claims">
= <jaxrs:serviceBeans>
<ref bean=3D"serviceBea= nClaims"/>
</jaxrs:serviceBeans>
<ja= xrs:providers>
<ref bean=3D"samlEnvHandler"/&= gt;
<ref bean=3D"claimsHandler"/>
= </jaxrs:providers>
</jaxrs:server>
{code}

h2. = Role Based Access Control

Full Content

JAX-RS: SAML

Introduction

CXF 2.5.0 introduces an initial support for working with SAML2 assertions. So far the main focus has been put on making sure SA= ML assertions can be included in HTTP requests targeted at application endp= oints: embedded inside XML payloads or passed as encoded HTTP header or for= m values. Support for advanced SAML features such as Web Browser SSO Profil= e will be coming in due time.

Maven dependencies

<dependency>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-rt-rs-security-xml<=
span class=3D"code-tag"></artifactId>
  <version>2.5.0</version>
</dependency>

This module depends on CXF WS-Security and Apache WSS4J modules, due to = them containing a lot of useful utility code.
We will see in time if it will make sense to exclude such dependencies or n= ot.

Enveloped SAML asser= tions

Payload:

<env:Envelope xmln=
s:env=3D"http://org.apache.cxf/rs/env">

<Book ID=3D"67ca6441=
-0c4e-4430-af0e-9463ce9226aa">
  <id>125<=
;/id>
  <name>CXF&=
lt;/name>
</Book>
<ds:Signature xmln=
s:ds=3D"http://www.w3.org/2000/09/xmldsig=
#">
  <!-- Book signat=
ure, omitted for brewity -->
</ds:Signature>

<!-- SAML assertio=
n with an enveloped signature -->=20
<saml2:Assertion x=
mlns:saml2=3D"urn:oasis:names:tc:SAML:2.0=
:assertion" xmlns:xs=3D"http://www.w3.org/2001/XMLSchema" xmlns:xsi=3D"http://www.w3.=
org/2001/XMLSchema-instance" ID=3D"_62D57=
4706635C0B9F413203247720501" IssueInstant=3D"2011-11-03T12:52:52.050Z" Version=3D"=
2.0" xsi:type=3D"saml2:AssertionType">

<saml2:Issuer>https://idp.example.org=
/SAML2</saml2:Issuer>

<ds:Signature xmln=
s:ds=3D"http://www.w3.org/2000/09/xmldsig=
#">
   <ds:SignedInfo>
    <ds:CanonicalizationMethod Algorithm=3D"http://www.w3.org/2001/10/xml-exc-c14n#"/>=
;
    <ds:SignatureMethod Algorithm=3D"http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
    <ds:Reference URI=3D"#_62D574706635C0B9F413203247720501">
      <ds:Transforms>
       <ds:Transform Algorithm=3D"http://www.w3.org/2000/09/xmldsig#enveloped-signature"=
/>
       <ds:Transform Algorithm=3D"http://www.w3.org/2001/10/xml-exc-c14n#">
         <ec:InclusiveNamespaces xmlns:ec=3D"http://www.w3.=
org/2001/10/xml-exc-c14n#" PrefixList=3D"=
xs"/>
       </ds:Transform>
      </ds:Transforms>
      <ds:DigestMethod Algorithm=3D"http://www.w3.org/2000/09/xmldsig#sha1"/>
      <ds:DigestValue>IDD9nFocVm/7FpU=
biGI3ZvpY2ps=3D</ds:DigestValue>
    </ds:Reference>
   </ds:SignedInfo>
   <ds:SignatureValue>JA2I7u/SmNsXGgW=
NdrLSovkipiM3JmGHsmpoP0EeIOwPwnLMx0WvV0C3xNGNiT1jOBe2uv8+WchtPoppGTC2JTJVX/=
t8PmKQCYZo4kVJo6Nmsjbn5kp7ejWuOYynvrUheQeTLU8e5CQmuS6L4VYaMVV2ETtb0VvpKjoQK=
HOC+co=3D</ds:SignatureValue>
   <ds:KeyInfo>
    <ds:X509Data>
     <ds:X509Certificate><!-- Omitted for brewity --&g=
t; </ds:X509Certificate>
    </ds:X509Data>
   </ds:KeyInfo>
 </ds:Signature>

 <saml2:Subject>
   <saml2:NameID Format=3D"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" NameQu=
alifier=3D"www.mock-sts.com">ui=
d=3Dsts-client,o=3Dmock-sts.com</saml2:NameID&g=
t;
   <saml2:SubjectConfirmation Method=3D"urn:oasis:names:tc:SAML:2.0:cm:sender-vouches"/&=
gt;
 </saml2:Subject>

 <saml2:Conditions NotBefore=3D"2011-11-03T12:52:52.063Z" NotOnOrAfter=3D"2011-11-03T12:52:52.063Z">
  <saml2:AudienceRestriction>
   <saml2:Audience>https://sp.example=
.com/SAML2</saml2:Audience>
  </saml2:AudienceRestriction>
 </saml2:Conditions>
 <saml2:AuthnStatement AuthnInstant=3D"2011-11-03T12:52:51.981Z" SessionIndex=3D"123456">
    <saml2:AuthnContext><saml2:AuthnContextClassRef/></saml2:AuthnContext>
 </saml2:AuthnStatement>

 <saml2:AttributeStatement>
    <saml2:Attribute FriendlyName=3D"subject-=
role"=20
                     Name=3D"http://schemas.xmls=
oap.org/ws/2005/05/identity/claims/role"=20
                     NameFormat=3D"http://schema=
s.xmlsoap.org/ws/2005/05/identity/claims">
       <saml2:AttributeValue xsi:type=3D"xs:string">user</saml2:AttributeValue>
    </saml2:Attribute>
    <saml2:Attribute Name=3D"http://claims/au=
thentication"=20
                     NameFormat=3D"http://claims=
/authentication-format">
       <saml2:AttributeValue xsi:type=3D"xs:string">password</saml2:AttributeValue>
    </saml2:Attribute>
 </saml2:AttributeStatement>
</saml2:Assertion>
</env:Envelope>

Note that Book and SAML assertion are individually signed but the envelo= pe wrapper itself is not.

Here is another payload showing the whole enveloped signed including Boo= k and SAML Assertion, this time only a single signature will be available:<= /p>

<env:Envelope xmln=
s:env=3D"http://org.apache.cxf/rs/env" ID=3D"e795cdd1-c19d-4a5c-8d86-e8a781af4787"=
>

<saml2:Assertion x=
mlns:saml2=3D"urn:oasis:names:tc:SAML:2.0=
:assertion" xmlns:xsi=3D"http://www.w3.org/2001/XMLSchema-instance" ID=3D=
"_C76E3D5BBEE4C4D87913203281641141" Issue=
Instant=3D"2011-11-03T13:49:24.114Z" Vers=
ion=3D"2.0" xsi:type=3D"saml2:AssertionType">
<saml2:Issuer>https://idp.example.org=
/SAML2</saml2:Issuer>
<saml2:Subject>
<saml2:NameID Format=3D"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" NameQuali=
fier=3D"www.mock-sts.com">uid=
=3Dsts-client,o=3Dmock-sts.com</saml2:NameID>=
;
<saml2:SubjectConfirmation Method=3D"urn:oasis:names:tc:SAML:2.0:cm:sender-vouches"/>=

</saml2:Subject>
<saml2:Conditions NotBefore=3D"2011-11-03T13:49:24.127Z" NotOnOrAfter=3D"2011-11-03T13:49:24.127Z">
<saml2:AudienceRestriction>
<saml2:Audience>https://sp.example.co=
m/SAML2</saml2:Audience>
</saml2:AudienceRestriction>
</saml2:Conditions>
<saml2:AuthnStatement AuthnInstant=3D"2011-11-03T13:49:24.044Z" SessionIndex=3D"123456">
<saml2:AuthnContext>
<saml2:AuthnContextClassRef/>
</saml2:AuthnContext>
</saml2:AuthnStatement>
<saml2:AttributeStatement>
<saml2:Attribute FriendlyName=3D"subject-role" Name=3D"http:/=
/schemas.xmlsoap.org/ws/2005/05/identity/claims/role" NameFormat=3D<=
span class=3D"code-quote">"http://schemas.xmlsoap.org/ws/2005/05/identity/c=
laims">
<saml2:AttributeValue xmlns:xs=3D"http://www.w3.org/2001/XM=
LSchema" xsi:type=3D"xs:string">=
;user</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name=3D"http://claims/authentication" NameFormat=3D"http://claims/authentication-format">
<saml2:AttributeValue xmlns:xs=3D"http://www.w3.org/2001/XM=
LSchema" xsi:type=3D"xs:string">=
;password</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>

<Book>
<id>125</=
id>
<name>CXF<=
;/name>
</Book>

<ds:Signature xmln=
s:ds=3D"http://www.w3.org/2000/09/xmldsig=
#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm=3D"http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/&=
gt;<ds:SignatureMethod Algorithm=3D"http://www.w3.org/2000/09/xmldsig#rsa-sha1"/&=
gt;<ds:Reference URI=3D"#e795cdd1-c19d-4a5c-8d86-e8a781af4787"><ds:Transforms><d=
s:Transform Algorithm=3D"http://www.w3.org/2000/=
09/xmldsig#enveloped-signature"/>=
<ds:Transform Algorithm=3D"http://www.w3.org/=
2001/10/xml-exc-c14n#"/></ds:T=
ransforms><ds:DigestMethod Algorithm=
=3D"http://www.w3.org/2000/09/xmldsig#sha1"/><ds:DigestValue>GR1pHd2=
JpxYiCzl6ouCmTZjq/AA=3D</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:Signatur=
eValue>C2qUDOFwart2GHFjX6kB3E3z73AMXtRR/6Qjgyp6XP/vTn/Fr2epDNub3q=
+gNdT0KgjLE2rSynM3QTcpHov9C8l9a8VQquItaalr0XA7BJcxdFMxB7KEATKR9XtrmIEkiw9ef=
M8M83iVux/ufCOWrt0Te2RLz+nRwzyEY49VQOQ=3D</ds:S=
ignatureValue><ds:KeyInfo><=
span class=3D"code-tag"><ds:X509Data>=
<ds:X509Certificate><!-- Omitted for brewity --></ds:X509Certificate></d=
s:X509Data><ds:KeyValue><ds:RSAKeyValue><ds:Modulus>vu747/VShQ85f16DGSc4Ixh9PVpGguyEqrCsK8q9XHOYX9l9/=
g5wEC6ZcR2FwfNsoaHcKNPjd5sSTzVtBWmQjfBEfIqwTR7vuihOxyNTwEzVwIJzvo7p8/aYxk+V=
dBtQxq4UweIcf/iFkUbM1cZ1oiXRQzciRBi+C1BQCQE0qzs=3D=
</ds:Modulus><ds:Exponent>AQAB</ds:Exponent></ds:RSAKeyValue></ds:KeyV=
alue></ds:KeyInfo></ds:Signature></e=
nv:Envelope>

Server configuration fragment:

    <bean id=3D"serv=
iceBean" class=3D"org.apache.cxf.systest.=
jaxrs.security.BookStore"/>
    <bean id=3D"saml=
Handler" class=3D"org.apache.cxf.rs.secur=
ity.saml.SamlEnvelopedInHandler"/>
   =20
    <!-- only need=
ed if the detached signature signing the application data is expected -->=
;=20
    <bean id=3D"xmlS=
igHandler" class=3D"org.apache.cxf.rs.sec=
urity.xml.XmlSigInHandler"/>
   =20
   =20
    <jaxrs:server=20
       address=3D"https://localhost:${testutil.p=
orts.jaxrs-saml}/samlxml">=20
       <jaxrs:serviceBeans>
          <ref bean=3D"serviceBean"/>
       </jaxrs:serviceBeans>
       <jaxrs:providers>
          <ref bean=3D"xmlSigHandler"/>
          <ref bean=3D"samlHandler"/>
       </jaxrs:providers>
      =20
       <jaxrs:properties>
           <entry key=3D"ws-security.signatur=
e.properties"=20
                  value=3D"org/apache/cxf/systes=
t/jaxrs/security/alice.properties"/>
       </jaxrs:properties>
       =20
    </jaxrs:server>

Client code:

private WebClient createWebClient(String address,=20
                                  boolean selfSigned) {
  JAXRSClientFactoryBean bean =3D new J=
AXRSClientFactoryBean();
  bean.setAddress(address);
 =20
  Map<String, Object> properties =3D new HashMap<String, Object>();
  properties.put("ws-security.callback-handler"<=
/span>,=20
                "org.apache.cxf.systest.jaxrs.se=
curity.saml.KeystorePasswordCallback");
  properties.put("ws-security.saml-callback-hand=
ler",=20
                 "org.apache.cxf.systest.jaxrs.s=
ecurity.saml.SamlCallbackHandler");
  properties.put("ws-security.signature.username=
", "alice");
  properties.put("ws-security.signature.properti=
es",=20
                 "org/apache/cxf/systest/jaxrs/s=
ecurity/alice.properties");
  if (selfSigned) {
     properties.put("ws-security.self-sign-saml-=
assertion", "true");
  }
  bean.setProperties(properties);
       =20
  bean.getOutInterceptors().add(new Sam=
lEnvelopedOutInterceptor(!selfSigned));
  XmlSigOutInterceptor xmlSig =3D new X=
mlSigOutInterceptor();
  if (selfSigned) {
      xmlSig.setStyle(XmlSigOutInterceptor.DETACHED_SIG);
  }
  return bean.createWebClient();
}

In the above code, the "ws-security.self-sign-saml-assertion" property, = if set to true, will require SamlEnvelopedOutInterceptor to get a SAML asse= rtion self-signed, by adding an enveloped signature to it. When we also nee= d to sign the application payload such as Book we need to make sure that a = detached XML signature for Book is created. When the whole envelope is sign= ed then SamlEnvelopedOutInterceptor needs to be placed before XmlSigOutInte= rceptor hence the "new SamlEnvelopedOutInterceptor(!selfSigned)" constructo= r is invoked.

SAML ass= ertions in Authorization header

Logging output:

Address: https://localhost:9000/samlheader/boo=
kstore/books/123
Http-Method: GET
Headers: {Accept=3D[application/xml], Authorization=3D[SAML eJydV1mTokgQfu9=
fYTCPrs2htGKMHVEcKq2gKOLxsoFQAsqhFAjNr99CW1ud7t2ZjdAwMisr68s7/YnMwGfaACEYJ1=
4UVmSxQ/z9wjUlBrRYiWZZiWVYlqPrDFVnmhTbwL80UZERSqEcosQMkw7BUDRdwx+qrtP1dp1qs=
41nLLciKgaMEVaLRZ4popIHfojapyc7RBqH7chEHmqHZgBRO7HaU6AM21iybV7wXO7kqEO4SbJv=
k2SWZc9Z/TmKHZKhKJpcKMOp5cLA/JT1/lu45p3AWxDfQl47ed/DDvHgDB0zidefZ+7J4vi11Iu=
wYs/eP8PcDPY+PGkvoTM/yTvZnzZqTz0nNJM0hh/g7O8MoUiKI7GMjTznB3G9C2053EQnUjDDKP=
Qs0/cKs4SnwMSN7ArwnSj2Ejf41miaKhXXYG7VLLoR/iDIe2i/qegOYYzMGnJN+kPXBG5gDLE7K=
7OJ3CF+/HcKna7psRmiTRQH6J78MywwPEI/2kO7hi4mfcD6fYVfeOn1J7Tacmj5KfKOUC2TdG9a=
EFXGMdx4+dBDOPVzdEk7aP1RAMhbeA/k2Rui50CU/J/g3ATmrMQw/RS+Lod0s8c74oavDxsCSou=
eGs8H4zUQlp0TgFvhE+Ma1jP5kJDXBDrfABTXCxR7+UJ5clXM0XjN8LG9MQxG57bTMfB9rUkaXU=
NKJgsRzKl+f8R2q0qr/sLB+Ub3oGEPhrIMJTegkBOM+0E4nbCLjVXYXO6MHXYhDLMWtGjKtRtNG=
tirfrioTvXhhnM2zalRXdXDlVVPg2Oe0Sp4Ge/eWgdRiXQwOiZWtZEfjtSwm1aH46xzNecGf2nS=
AL5fzVuwFCeaiXklhLItbHAFJvBVkWWhtxUEsBw5IJN54MjS1Jg4QAcq7+wO7s7rcRnFA23WBSI=
olImSSdpSNDRtIGV71+p1t2Zvlq7rb+GTomWZ4JwOh1Km+uvAysUtUHhHNXig6PxcbawC1VX4xk=
LUrUwRpUzRAf7F326EeUoD8/KRDoonRdcylY4ypZB0hZd6gJ5JgqsMlgveXTKuPwy491UhKQqIz=
me5Iq7mbKhojUwEJxBYveGue/72aaULfFg8miR1ARjxWw1kznKHgUvgmDYbOLhTV2uxG/pF7E2t=
hpy73NjY95z0XTrEAnoatA7coj9aLjifIx02k4SXlTVhutlGRZHZtwbqeGuzaKoXRsLPA2274aW=
NfMj0SfOYeu4of1f1TCqMTH4rno5Rc98izWW+qxo2n2j5oTHLoGxtSK+7m60V2lrRkbeYaIXlTX=
ivKtC8JmgSdSiQADIJAFNpKuIuk3FQnowJNeX5KOvJ8lzfcbMFtRrPfE6b7TjJmKmz6YwbLWhDn=
+hgVgalP5EkUQdDx/HRmlGxr9yjVdcyUVu+PQ2ilYxJtfQTrwGx9I87zHZBtbVHg6ThhGtv1ysM=
Snf203nPmufzAQZYtBKZCV/cLmCP9Nbo981Gj3ty64gKc43RYVbACblrOoFjMEhutOqqEy/7gR4=
MB6bIzwuT2YN0lYqu1m/1gOS+mbtuMuDH1aokcLGq7ldP4eHQz/P6Yc0kc4Y9TBK+EIMBx9COw4=
2VKFCsZnqYaOfqeMz4K/NcE+RttdxV02ViTtP1FlrJhSwbqCxWuri/mcn3459+pk8cz65tTqLtN=
ER7aGEY0CYqpRYtxTMQk3GHKJtgEFm7GkrQsxUFxGvq2R1M1Czfg2HyV9S5Pb4M6DOWB6BCFG68=
8sVyDzq33X/fUqygjWBow7h2jFK8VaBTX//SeKzb9krFqK=
JGCQ+xafCbvYl+wXsTFhqFoxhsktLKb+Uu6kFqe2WbnuD2HXtW+dDj0XVzQZ+LC/bI/eJyFX5k3=
CkmH236fCtxw2mCsyXAvq+cyH9dEvFOgI2dQlQuiTJ2Zd4haKbeYF+IO534qQTmyVc8wcfLIp5T=
5A3m2xvkV9CuihJs1TpN4PcnlW6MPWD772XO4BXxHNdaHPnwnI3XgYxOiyV6xlMYt7P9aTJnqBz=
OLIk/no3Ve8k7afmmFyDyU8OlJP6XHuIXxKdpdrPV5njlxkehg4sDb7ZXj9zJv/7C/tUTd9Z+WG=
Fiv5Z4LPO8rn9hz5eSH8X9R+j3ONJZFNu/b8Ej59cwY1CFiLtLmYCfmXvhdIgyKXENBh7ubfCmv=
q9/El7/AXoseyE=3D], ...}

Note that the Authorization header has an encoded SAML Assertion as its = value. The original SAML assertion has been optionally compressed using a d= eflated encoding and then base64-encoded. This encoded value can be signed = itself - but it is not currently possible.

Server configuration is similar to the one from the Enveloped SAML Asser= tions section, the only difference is that a SAML handler needs to be repla= ced:

    <bean id=3D"serv=
iceBean" class=3D"org.apache.cxf.systest.=
jaxrs.security.BookStore"/>
    <bean id=3D"saml=
Handler" class=3D"org.apache.cxf.rs.secur=
ity.saml.SamlHeaderInHandler"/>
   =20
    <!-- same as i=
n the Enveloped SAML Assertions section -->=20

Client code:

private WebClient createWebClient(String address,=20
                                  boolean selfSigned) {
  JAXRSClientFactoryBean bean =3D new J=
AXRSClientFactoryBean();
  bean.setAddress(address);
 =20
  Map<String, Object> properties =3D new HashMap<String, Object>();
  properties.put("ws-security.callback-handler"<=
/span>,=20
                "org.apache.cxf.systest.jaxrs.se=
curity.saml.KeystorePasswordCallback");
  properties.put("ws-security.saml-callback-hand=
ler",=20
                 "org.apache.cxf.systest.jaxrs.s=
ecurity.saml.SamlCallbackHandler");
  properties.put("ws-security.signature.username=
", "alice");
  properties.put("ws-security.signature.properti=
es",=20
                 "org/apache/cxf/systest/jaxrs/s=
ecurity/alice.properties");
  if (selfSigned) {
     properties.put("ws-security.self-sign-saml-=
assertion", "true");
  }
  bean.setProperties(properties);
       =20
  bean.getOutInterceptors().add(new Sam=
lHeaderOutInterceptor());
 =20
  return bean.createWebClient();
}

SAML assertions a= s Form values

Logging output:

Address: https://localhost:9000/samlform/books=
tore/books
Encoding: ISO-8859-1
Http-Method: POST
Content-Type: application/x-www-form-urlencoded
Headers: {Accept=3D[application/xml], Cache-Control=3D[no-cache], connectio=
n=3D[keep-alive], Content-Length=3D[2206], content-type=3D[application/x-ww=
w-form-urlencoded], Host=3D[localhost:9000], Pragma=3D[no-cache], User-Agen=
t=3D[Apache CXF ${project.version}]}
Payload: name=3DCXF&id=3D125&SAMLToken=3DeJydV1tzqkgQfs+vsDiPWcNFjW=
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3D

Note that only form 'name' and 'id' fields will remain after the SAML ha= ndler processes a SAML assertion encoded in the SAMLToken form field. The o= riginal SAML assertion has been optionally compressed using a deflated enco= ding and then base64-encoded. This encoded value can be signed - but it is = not currently possible.

Server configuration is similar to the one from the Enveloped SAML Asser= tions section, the only difference is that a SAML handler needs to be repla= ced:

    <bean id=3D"serv=
iceBean" class=3D"org.apache.cxf.systest.=
jaxrs.security.BookStore"/>
    <bean id=3D"saml=
Handler" class=3D"org.apache.cxf.rs.secur=
ity.saml.SamlFormInHandler"/>
   =20
    <!-- same as i=
n the Enveloped SAML Assertions section -->=20

The client code is the same as in the SAML assertions in Authorization h= eader section except than an instance of SamlFormOutInterceptor has to be r= egistered:

bean.getOutInterceptors().add(new SamlF=
ormOutInterceptor());

Creating SAML Asserti= ons

If you use CXF JAX-RS client API to experiment with SAML then all you ne= ed to do is to register an appropriate out interceptor as shown in the abov= e code fragments. The interceptor will ensure that a SAML assertion is crea= ted and added inside the XML envelope, as a form or HTTP header value.
All of the SAML output interceptors depend on a "ws-security.saml-callback-= handler" property linking to a custom javax.security.auth.callback.Callback= implementation which in its handle(Callbacks) method provides the informat= ion which is needed to create a SAML assertion to a org.apache.ws.security.= saml.ext.SAMLCallback Callback instance, for example, see this custom implementation.

More involved cases with SAML assertions being created by identity provi= ders will be supported, with the help of CXF (WS) STSClient when needed.

SAML Assertion Valid= ation

When SAML assertions are received on the server side, they are validated= to make sure that the enveloped signatures are correct. SubjectConfirmatio= n methods (sender-vouches, holder-of-key, bearer) are also checked.
The validation can be delegated to STS if needed. By default, server side S= AML handlers have a "samlValidator" property set to an instance of org.apac= he.ws.security.validate.SamlAssertionValidator which does a thorough valida= tion of the assertion. If needed org.apache.cxf.ws.security.trust.STSSamlAs= sertionValidator can be set instead which will use STS to validate the asse= rtion.
Custom validators extending WSS4J SamlAssertionValidator and doing the addi= tional application-specific validation can be registered if needed.

Note the fact that the default validation relies a lot on the code heavi= ly utilized by the WS-Security implementation should be of no concern - it = is an example of the integration on its own in order to get the validation = done. For example, WS-* STS are heavily used in the enterprise today and it= simply makes a complete sense to rely on it to validate a SAML assertion i= f it is possible.

SubjectConfirmation sender-vouches and holder-of-key methods can be easi= ly validated with enveloped SAML assertions given that the embedded SAML si= gnatures and key info can be checked against the signature used to sign the= envelope or a custom payload like Book.

At the moment these methods can not be properly validated when the asser= tion is provided in a header or in the form, the additional signature signi= ng the encoded SAML token will be needed - this will be supported in due ti= me. Use "bearer" in those cases.

SAML Authorization

SAML assertions may contain so-called claims which are represented by a = sequence of SAML AttributeStatements containing one or more Attributes, for= example:

<saml2:Assertion>
 <!-- ... -->
 <saml2:AttributeStatement>
    <saml2:Attribute NameFormat=3D"http://sch=
emas.xmlsoap.org/ws/2005/05/identity/claims"
                 Name=3D"http://schemas.xmlsoap.=
org/ws/2005/05/identity/claims/role"=20
                 FriendlyName=3D"subject-role">
       <saml2:AttributeValue xmlns:xs=3D"http://www.w3.org/=
2001/XMLSchema" xsi:type=3D"xs:string">user</saml2:AttributeValue>
    </saml2:Attribute>
    <saml2:Attribute NameFormat=3D"http://cla=
ims/authentication"
                     Name=3D"http://claims/authe=
ntication-format">
        <saml2:AttributeValue xmlns:xs=3D"http://www.w3.org=
/2001/XMLSchema" xsi:type=3D"xs:string">password</saml2:AttributeValue&=
gt;
    </saml2:Attribute>
 </saml2:AttributeStatement>
 <!-- ... -->
</saml2:Assertion>

An individual claim is scoped by NameFormat and Name attribute. NameForm= at is similar to a namespace, while Name identifies what the value of this = claim represents, for example, in the above fragment two claims are provide= d, one has a value "user" which represents a role of the assertion's Subjec= t, another one has a value of "password" which identifies the way Subject a= uthenticated itself, i.e, Subject provided its password (presumably to IDP)= .

Now, what is interesting is to see if it is possible to use these claims= with Role-Based Access-Control (for example, with endpoints relying on @Ro= lesAllowed annotations) as well as with the more complex authorization logi= c (for example, let this resource be invoked only if Subject used a passwor= d to get authenticated at IDP).

Claims Based Access= Control

CXF JAX-RS offers an extension letting users to enforce a new fine-grain= ed Claims Based Access Control based on = Claim and Claims annotations as= well as ClaimMode enum class. =

Here is a simple code fragment:

import org.apache.cxf.rs.security.saml.=
authorization.Claim;
import org.apache.cxf.rs.security.saml.=
authorization.Claims;

@Path("/bookstore")
public class SecureClaimBookStore {
   =20
    @POST
    @Path("/books")
    @Produces("application/xml")
    @Consumes("application/xml")
    @Claims({=20
        @Claim({"admin" }),
        @Claim(name =3D "http://claims/authentication-format",=20
               format =3D "http://claims/authentication",=20
               value =3D {"fingertip", "smartcard" })
    })
    public Book addBook(Book book) {
        return book;
    }
   =20
}

SecureClaimBookStore.addBook(Book) can only be invoked if Subject meets = the following requirement: it needs to have a Claim with a value "admin" an= d another Claim confirming that it got authenticated using either a 'finger= tip' or 'smartcard' method. Note that @Claim(

Unknown macro: {"admin"= ;}

) has no name and format classifiers set - it relies on default name and= format values, namely "http://schemas.xmlsoap.org/ws/2005/05/identity/clai= ms/role" and "http://schemas.xmlsoap.org/ws/2005/05/identity/claims" respec= tively. These default values may change in the future depending on which cl= aims are found to be used most often - but as you can see you can always pr= ovide name and format values which will scope a given claim value.

Note that in the above example, a Claim with the name "http://claims/aut= hentication-format" has two values, 'fingertip' and 'smartcard'. By default= , in order to meet this Claim, Subject needs to have a Claim which has eith= er a 'fingertip' or 'smartcard' value. If it is expected that Subject needs= to have a Claim which has both 'fingertip' and 'smartcard' values, then th= e following change needs to be done:

import org.apache.cxf.rs.security.saml.=
authorization.Claim;
import org.apache.cxf.rs.security.saml.=
authorization.Claims;

@Path("/bookstore")
public class SecureClaimBookStore {
   =20
    @POST
    @Path("/books")
    @Produces("application/xml")
    @Consumes("application/xml")
    @Claims({=20
        @Claim({"admin" }),
        @Claim(name =3D "http://claims/authentication-format",=20
               format =3D "http://claims/authentication",=20
               value =3D {"fingertip", "smartcard" },
               matchAll =3D true)
    })
    public Book addBook(Book book) {
        return book;
    }
   =20
}

Claims can be specified using individual @Claim annotation, they can be = set at the class level and overridden at the method level and finally a lax= mode of check can be specified:

import org.apache.cxf.rs.security.saml.=
authorization.Claim;
import org.apache.cxf.rs.security.saml.=
authorization.Claims;

@Path("/bookstore")
@Claim({"user"})
public class SecureClaimBookStore {
   =20
    @POST
    @Path("/books")
    @Produces("application/xml")
    @Consumes("application/xml")
    @Claims({=20
        @Claim({"admin" }),
        @Claim(name =3D "http://claims/authentication-format",=20
               format =3D "http://claims/authentication",=20
               value =3D {"fingertip", "smartcard" },
               matchAll =3D true)
    })
    public Book addBook(Book book) {
        return book;
    }

    @GET
    @Claim(name =3D "http://claims/authentication-format",=20
               format =3D "http://claims/authentication",=20
               value =3D {"password" },
               mode =3D ClaimMode.LAX)
    public Book getBook() {
        //...
    }

    @GET
    public BookList getBookList() {
        //...
    }
   =20
   =20
}

In the above example, getBookList() can be invoked if Subject has a Clai= m with the value "user"; addBook() has it overridden - "admin" is expected = and the authentication format Claim too; getBook() can be invoked if Subjec= t has a Claim with the value "user" and it also must have the authenticatio= n format Claim with the value "password" - or no such Claim at all.

Server Configuration Example:


<bean id=3D"serviceB=
eanClaims" class=3D"org.apache.cxf.systes=
t.jaxrs.security.saml.SecureClaimBookStore"/>
<bean id=3D"samlEnvH=
andler" class=3D"org.apache.cxf.rs.securi=
ty.saml.SamlEnvelopedInHandler">
 <property name=3D"s=
ecurityContextProvider">
    <bean class=3D"o=
rg.apache.cxf.systest.jaxrs.security.saml.CustomSecurityContextProvider"/>
 </property>
</bean>
   =20
<bean id=3D"claimsHandler"=20
     class=3D"org.apache.cxf.rs.security.saml.au=
thorization.ClaimsAuthorizingFilter">
    <property name=3D"securedObject" ref=3D"serviceBeanClaims=
"/>  =20
</bean>

<jaxrs:server address=3D"/saml-claims">=20
       <jaxrs:serviceBeans>
          <ref bean=3D"serviceBeanClaims"/>
       </jaxrs:serviceBeans>
       <jaxrs:providers>
          <ref bean=3D"samlEnvHandler"/>
          <ref bean=3D"claimsHandler"/>
       </jaxrs:providers>
</jaxrs:server>

Role Based Access Con= trol