November 11, 2013

Apache CXF FEDIZ 1.1.0 released

The CXF community has released the new Version of Apache CXF Fediz. Fediz helps you to secure your web applications and delegate security context to the underlying application which can be used for impersonation when calling other Web Services. With Fediz, authentication is externalized from your web application to an identity provider installed as a dedicated server component. The supported standard is WS-Federation Passive Requestor Profile.

The following features has been added:

  • Fediz IDP supports Resource and Requestor IDP role, Home Realm Discovery Service, ...
  • SAML Holder-Of-Key supported
  • Encrypted SAML Tokens supported
  • Support for Jetty, Websphere and Spring Security 2.0/3.1
  • Publish WS-Federation Metadata document for RP and IDP

The major contribution is the refactoring of the IDP to leverage the functionality and flexibility provided by Spring Web Flow and Spring Security. I wrote about this new feature here. More details to come like customizing the signin flow, etc.

Release notes are available here.

For more information see:


Features to come in the next release:
  • Integration with CXF JAX-RS
  • SAML-P support
Feel free to raise enhancement requests and issues in the JIRA project

Thank you for all support and feedback!

September 4, 2013

Fediz 1.1 introduces Federation across Security Domains (Part 2)

In the first blog on this topic I explained how to add security domains to the Fediz STS which is based on the Apache CXF STS. This blog focuses on how to set up the Fediz IDP thus you can access an application but authenticate either against the requestor IDP of realm A or realm B.

I posted a blog some time back about WS-Federation across several companies and explained the benefits and key requirements here. I recommend to read that blog first.

Here a quick summary of the goals and requirements by introducing WS-Federation for Web Application Single-Sign-On:

  • Application doesn't care about authentication
  • Application doesn't care against which authentication system the user has been authenticated
  • Application doesn't care where the user attributes are coming from
  • Application has got a trusted relationship with a single IDP/STS instance which ensures the user is authenticated and the user attributes are provided in one syntax and semantic independent from which source they are coming from
  • Application must be able to enforce certain authentication methods (ex. strong authentication)
  • Application must be able to enforce certain user attributes in the security token

In the last blog, we've prepared the STS which is responsible to issue tokens for the two realms REALMA and REALMB. Further, the STS is able to federate between the two realms by mapping the identities (attribute type of Relationship bean configuration. This means, a person must have an identity in realm A (ex. bob) and an identity in realm B (ex. BOB). To keep it simple, the mapping is based to lowercase the username. Another option is to federate/map the claims which doesn't require that a person must have an identity in both realms. This requires more effort to set up a demo and might be provided in the future for illustration purposes, but all required functionality and extension points are available.

Notes:

  • The Fediz IDP doesn't support yet to host several realms in one WAR file. Therefore, two Servlet Containers must be set up to deploy the IDP war file.

    In B2B scenarios this won't be required anyway as the B2B partner has got an IDP attached to its realm as well as the application service provider has got an IDP attached to its realm. It might make sense in scenarios where the company itself has got more than one authentication systems in place.

  • For ease of use, the Fediz IDP and Fediz STS are deployed into the same Serlvet Container and therefore accessed with the same TCP port. Due to the fact, that the Fediz STS supports both realms in one WAR file, the Fediz STS WAR fediz-idp-sts.war is just deployed twice.

Install Apache CXF Fediz IDP

The Fediz IDP is provided as part of the subproject Fediz in Apache CXF. The new version provides the Apache Maven profile realm-a (Default) and realm-b which packages the IDP either for security domain REALM A and REALM B.

You can either download the sources of the Fediz IDP from the following location

git clone git://git.apache.org/cxf-fediz.git

or

svn co https://svn.apache.org/repos/asf/cxf/fediz/trunk
You can build the IDP for realm A with the following Maven command

mvn clean install -Prealm-a
The IDP is packaged as a WAR file at services/idp/target/fediz-idp.war

You can build the IDP for realm B with the following Maven command

mvn clean install -Prealm-b
The IDP is packaged as a WAR file at services/idp/target/fediz-idp-remote.war

Note: The WAR file and therefore the servlet context name is not fediz-idp but fediz-idp-remote. This is required because cookies are bound to the hostname (ex. localhost) and the path (ex. fediz-idp). If the servlet context is the same, cookies are overwritten between the two IDP instances. There are also other options to bound the WAR file with a specific servlet context name but this relies on the Servlet Container provider.

Last option is to download the archive from the snapshot maven repository. Keep in mind that this IDP is built with the profile realm-a. Only the following files must be updated for realm B:

  • WEB-INF/applicationContext.xml

    Change

    <import resource="idp-config-realma.xml" />

    to

    <import resource="idp-config-realmb.xml" />
  • WEB-INF/classes/realm.properties

    Change

    realm.STS_URI=REALMA

    to

    realm.STS_URI=REALMB
  • WEB-INF/web.xml

    Change

    <param-value>urn:org:apache:cxf:fediz:idp:realm-A</param-value>

    to

    <param-value>urn:org:apache:cxf:fediz:idp:realm-B</param-value>

Note: The built WAR files require that IDP Realm A is deployed into a Servlet Container running on port 9443 and IDP Realm B is deployed into a Servlet Container running on port 12443. If you want to change that then you must update the file WEB-INF/classes/realm.properties or at source location services/idp/src/main/resources/.

Update Relying Party (Application)

I assume you have installed fedizhellworld example into a supported Serlvet Container. If not, install the fedizhellworld example based on the README.txt.

The following steps must be processed to establish the trust with the IDP Realm A:

  • Issuer URL must be Realm A (ex. https://localhost:9443/fediz-idp/federation )
  • Signer certificate of Realm A must be imported into the truststore because new certificates are used for realm A and realm B
    You can either copy the file stsstore.jks from the STS archive within WEB-INF/classes or copy the file realma.cert from the STS archive from the same location.

Run the demo

You have now deployed the IDP realm A (fediz-idp.war) into a Servlet Container listening on port 9443 and IDP realm B (fediz-idp-remote.war) into a Servlet Container listening on port 12443. Finally, the Relying Party has been updated to trust the IDP Realm A.

When you now access the fedizhelloworld application (ex. https://localhost:8443/fedizhelloworld/secure/fedservlet) you get redirected to the IDP Realm A and the following web page is shown

You can now choose in which domain you would like to authenticate. Choose the IDP in the list and click Select Home Realm. If you choose Realm B you get redirected to IDP Realm B and username/password popup is shown (use uppercase username and passwords) or IDP Realm A shows username/password popup (use lowercase username and passwords).

If you choose Realm B and enter the user ALICE and the password ECILA you finally see the user alice (lowercase) shown in the application. You know why? Exactly, the STS does the identity mapping when the REALM A is requested a SAML token for the application fedizhelloworld on-behalf-of the SAML token issued by Realm B.

Notes

  • The IDP will ask you only once to choose your home realm. The choice is stored in the cookie FEDIZ_HOME_REALM
  • If your home realm is realm B and you're successfully authenticated, the IDP realm A won't redirect you to realm B the next time, as the original SAML token of realm B is cached in realm A.

IDP Configurations

The design of the IDP changed (Spring Web Flow and Spring Security), new features are implemented and thus the configuration is completely different to version 1.0. I don't want to go into too much details in this blog, instead I'll update the Apache CXF Fediz Wiki soon.
Use form based login
Change the spring security configuration in WEB-INF/security-config.xml and set the following configuration:
    <!--<security:http-basic />-->
    <security:form-login />
Configure an additional Relying Party
The main configuration file of the IDP is idp-config-realma.xml. This file contains configurations for the IDP, trusted IDPs and Relying Parties.

If you want to configure an additional application add a ServiceConfig bean and add it to the map services in the IDP configuration bean.

  <bean id="idp-realmA" class="org.apache.cxf.fediz.service.idp.model.IDPConfig">
    ...
    <property name="services">
      <util:map>
        <entry key="urn:org:apache:cxf:fediz:fedizhelloworld" value-ref="srv-fedizhelloworld" />
      </util:map>
    </property>
    ...

  <bean id="srv-fedizhelloworld" class="org.apache.cxf.fediz.service.idp.model.ServiceConfig">
    <property name="realm" value="urn:org:apache:cxf:fediz:fedizhelloworld" />
    <property name="protocol" value="http://docs.oasis-open.org/wsfed/federation/200706" />
    <property name="serviceDisplayName" value="Fedizhelloworld" />
    <property name="serviceDescription" value="Web Application to illustrate WS-Federation" />
    <property name="role" value="ApplicationServiceType" />
    <property name="tokenType" value="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0" />
    <property name="lifeTime" value="3600" />
    <property name="requestedClaims">
      <util:list>
        <bean class="org.apache.cxf.fediz.service.idp.model.RequestClaim">
          <property name="claimType" value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" />
          <property name="optional" value="false" />
        </bean>
        <bean class="org.apache.cxf.fediz.service.idp.model.RequestClaim">
          <property name="claimType" value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" />
          <property name="optional" value="false" />
        </bean>
        <bean class="org.apache.cxf.fediz.service.idp.model.RequestClaim">
          <property name="claimType" value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" />
          <property name="optional" value="false" />
        </bean>
        <bean class="org.apache.cxf.fediz.service.idp.model.RequestClaim">
          <property name="claimType" value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role" />
          <property name="optional" value="true" />
        </bean>
      </util:list>
    </property>
  </bean>
It's important that the attribute realm matches with the realm configured in the application (fediz-config.xml). The attributes tokenType, lifeTime and requestedClaims are optional. Only the protocol defined in the above bean is supported by the IDP and therefore the default value. The remaining attributes are ignored but used in the future like the Web GUI.
Configure an additional Trusted IDP
If you want to configure an additional trusted IDP add a TrustedIDPConfig bean and add it to the map trustedIDPs in the IDP configuration bean.
  <bean id="idp-realmA" class="org.apache.cxf.fediz.service.idp.model.IDPConfig">
    ...
    <property name="trustedIDPs">
      <util:map>
        <entry key="urn:org:apache:cxf:fediz:idp:realm-B" value-ref="trusted-idp-realmB" />
      </util:map>
    </property>
    ...

  <bean id="trusted-idp-realmB" class="org.apache.cxf.fediz.service.idp.model.TrustedIDPConfig">
    <property name="realm" value="urn:org:apache:cxf:fediz:idp:realm-B" />
    <property name="cacheTokens" value="true" />
    <property name="url" value="https://localhost:${realmB.port}/fediz-idp-remote/federation" />
    <property name="certificate" value="realmb.cert" />
    <property name="trustType" value="PEER_TRUST" />  <!-- Required for Fediz Core, Process SignInResponse -->
    <property name="protocol" value="http://docs.oasis-open.org/wsfed/federation/200706" />
    <property name="federationType" value="FederateIdentity" /> <!-- Required for STS Relationship -->
    <property name="name" value="REALM B" />
    <property name="description" value="IDP of Realm B" />
  </bean>
It's important that the filename of the PEM encoded certificate is configured which has been used to sign the SAML token by the trusted IDP. The attribute name is required for informational purposes whereas the remaining attributes are ignored but used in the future like the Web GUI.


Even there is much more to say I finish this post and write about other features of the new Fediz IDP in the near future.

Please post feedback and ideas to the CXF mailing list.

Thank you for all support and feedback!

September 3, 2013

Fediz 1.1 introduces Federation across Security Domains (Part 1)

Fediz 1.1 introduces a bunch of new features like support for Jetty, Spring Security and Websphere. But a lot of effort has been put into the Fediz IDP. I'd like to thank Thierry Beucher for his valuable contributions to the new release of the Fediz IDP.

The new features are:

  • Federation Metadata

    The IDP supports publishing the WS-Federation Metadata document which allows to more easily integrate the IDP into platforms which support referencing a Metadata document. Metadata consists of the signing certificate, the provided claims, etc.

  • Spring Web Flow support

    The IDP has been refactored to use Spring Web Flow to manage the federation flow. This provides flexibility to be able to customize the IDP to company's specific requirements. The IDP is secured by Spring Security to get the benefits and flexibility of Spring Security.

  • Resource IDP and Home Realm Discovery

    This is the major new feature. The IDP is able to figure out from which security domain/realm the browser request is coming from to redirect the sign-in request to the requestor IDP which does the authentication and issues a token which is sent to the Resource IDP. The Resource IDP will then either map the principal from one security domain to the target security domain and get claims information of the mapped principal or transform the claims information and finally issue a new token for the relying party (application).

    More complex scenarios described in this blog are now possible.

In this post, I'd like to focus on the last feature which I splitted in two posts. The first one focuses on extending the STS to support more than one security domain/realm and the second on the IDP.

Install Apache CXF Fediz IDP

The Fediz STS is provided as part of the subproject Fediz in Apache CXF. The new version provides the Apache Maven profile realms which packages an STS with two security domains called REALM A and REALM B.

You can either download the sources of the Fediz STS from the following location

git clone git://git.apache.org/cxf-fediz.git

or

svn co https://svn.apache.org/repos/asf/cxf/fediz/trunk
You can build the whole project with the following Maven command

mvn clean install
The STS is packaged as a WAR file at services/sts/target/fediz-idp-sts.war

or download the archive from the snapshot maven repository.

The URL of the STS has the following syntax depending on the hostname and port of your servlet engine. https://localhost:port/fediz-idp-sts/?wsdl The page will list the different URL's for the web service endpoints of the STS:

  • https://localhost:9443/fediz-idp-sts/REALMA/STSServiceTransportUT
    STS endpoint for REALM A which requires username/password authentication
  • https://localhost:9443/fediz-idp-sts/REALMA/STSServiceTransport
    STS endpoint for REALM A with requires on-behalf-of saml token (implicit identiy mapping is done)
  • https://localhost:9443/fediz-idp-sts/REALMB/STSServiceTransportUT
    STS endpoint for REALM B which requires username/password authentication
  • https://localhost:9443/fediz-idp-sts/REALMB/STSServiceTransport
    STS endpoint for REALM B with requires on-behalf-of saml token (implicit identiy mapping is done)
In the context of federation, you request a token from your requestor STS/IDP. In this example, either REALM-A or REALM-B with username/password (.../fediz-idp-sts/<realm>/STSServiceTransportUT). After authentication, you request (implicitly as part of the federation protocol) a specific token of the security domain/realm where the application is conntected to. This use case makes use of the on-behalf-of feature of the STS (.../fediz-idp-sts/<realm>/STSServiceTransport).

The following users are set up in realm A and realm B:

UserPassword
Realm A
aliceeclia
bobbob
teddet
Realm B
ALICEECLIA
BOBBOB
TEDDET

As you might imagine, the identity mapping implementation is fairly easy as you just lower- or uppercase the principal name. More information about how to plug in different identity mappings see below in Identity Mapper.

How to add a new security domain to the STS

If you customize the realms or add an additional realm, the following steps are required. You can either customize the deployed WAR fediz-idp-sts.war of the downloaded archive or make updates to the sources
Note: You could also make use of the Overlay feature of Apache Maven.
  • Define a new realm C

    Some beans must be updated and added to the Spring configuration file cxf-transport.xml. The changes for a new realm are highlighted in bold.

    Source location: src/realms/webapps/WEB-INF/
    Deploy location: /WEB-INF/

      <util:map id="realms">
        <entry key="REALMA" value-ref="realmA"/>
        <entry key="REALMB" value-ref="realmB"/>
        <entry key="REALMC" value-ref="realmC"/>
      </util:map>
    
      <bean id="realmA" class="org.apache.cxf.sts.token.realm.SAMLRealm">
        <property name="issuer" value="STS Realm A"/>
        <property name="signaturePropertiesFile" value="stsKeystoreA.properties" />
        <property name="callbackHandlerClass" value="org.apache.cxf.fediz.service.sts.PasswordCallbackHandler" />
      </bean>
    
      <bean id="realmB" class="org.apache.cxf.sts.token.realm.SAMLRealm">
        <property name="issuer" value="STS Realm B"/>
        <property name="signaturePropertiesFile" value="stsKeystoreB.properties" />
        <property name="callbackHandlerClass" value="org.apache.cxf.fediz.service.sts.PasswordCallbackHandler" />
      </bean>
    
      <bean id="realmC" class="org.apache.cxf.sts.token.realm.SAMLRealm">
        <property name="issuer" value="STS Realm C"/>
        <property name="signaturePropertiesFile" value="stsKeystoreC.properties" />
        <property name="callbackHandlerClass" value="org.apache.cxf.fediz.service.sts.PasswordCallbackHandler" />
      </bean>
    
    
      <util:list id="relationships">
        <bean class="org.apache.cxf.sts.token.realm.Relationship">
          <property name="sourceRealm" value="REALMA" />
          <property name="targetRealm" value="REALMB"/>
          <property name="identityMapper" ref="identityMapper" />
          <property name="type" value="FederatedIdentity" />
        </bean>
        <bean class="org.apache.cxf.sts.token.realm.Relationship">
          <property name="sourceRealm" value="REALMB" />
          <property name="targetRealm" value="REALMA"/>
          <property name="identityMapper" ref="identityMapper" />
          <property name="type" value="FederatedIdentity" />
        </bean>
    
        <bean class="org.apache.cxf.sts.token.realm.Relationship">
          <property name="sourceRealm" value="REALMA" />
          <property name="targetRealm" value="REALMC"/>
          <property name="identityMapper" ref="identityMapper" />
          <property name="type" value="FederatedIdentity" />
        </bean>
    
      </util:list>
    
    
    In this case, only a trust relationship from realm A to realm C is established and it's based on identity mapping.
  • Generate signing certificate

    The new realm signs the SAML assertion with a different signer certificate.

    keytool -genkeypair -keyalg RSA -validity 3600 -alias realmc -keystore stsrealm_c.jks -dname "cn=REALMC" -keypass realmc -storepass storepass
    keytool -export -keystore stsrealm_c.jks -storepass storepass -export -alias realmc -file realmc.cert
    
    The keystore has to be added
    Source location: src/realms/resources
    Deploy location: /WEB-INF/classes
  • Import certificate into truststore ststrust.jks

    The certificate must be added to the truststore thus another realm is able to validate the SAML assertion signed by realm C.

    Note: The above federation relationship definitions do not cover the use case that another realm must trust realm C but I recommend to add the certificate for completeness reasons.

    keytool -import -trustcacerts -keystore ststrust.jks -storepass storepass -alias realmc -file realmc.cert -noprompt
    
    The truststore has to be updated at
    Source location: src/realms/resources
    Deploy location: /WEB-INF/classes
  • Configure signer certificate

    Above, the signer certificate is configured for the SAML Realm C in the attribute signaturePropertiesFile of the bean realmC. The properties file has to look like this:

    org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
    org.apache.ws.security.crypto.merlin.keystore.type=jks
    org.apache.ws.security.crypto.merlin.keystore.password=storepass
    org.apache.ws.security.crypto.merlin.keystore.alias=realmc
    org.apache.ws.security.crypto.merlin.file=stsrealm_c.jks
    
    Note: Copy&paste an existing stsKeystore properties file.

    The properties file has to be added here:
    Source location: src/realms/resources
    Deploy location: /WEB-INF/classes

  • Define JAX-WS endpoint in STS

    Two JAX-WS endpoints are required depending on whether you need to support username/password authentication in the STS (WSDL Port TransportUT_Port) and/or issue a SAML token on-behalf-of another SAML token (WSDL Port Transport_Port).

    The former requires an authentication backend which is file based in the Fediz STS. The file based authentication backend relies on the UsernamePasswordCallbackHandler which uses a spring map with user name and password entries (see beans upCallBackHandlerRealmC and REALMC).

      <jaxws:endpoint id="transportSTSRealmC" implementor="#transportSTSProviderBean"
        address="/REALMC/STSServiceTransport" wsdlLocation="/WEB-INF/wsdl/ws-trust-1.4-service.wsdl"
        xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
        serviceName="ns1:SecurityTokenService" endpointName="ns1:Transport_Port">
        <jaxws:properties>
        </jaxws:properties>
      </jaxws:endpoint>
    
      <jaxws:endpoint id="transportSTSRealmCUT" implementor="#transportSTSProviderBean"
        address="/REALMC/STSServiceTransportUT" wsdlLocation="/WEB-INF/wsdl/ws-trust-1.4-service.wsdl"
        xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
        serviceName="ns1:SecurityTokenService" endpointName="ns1:TransportUT_Port">
        <jaxws:properties>
          <entry key="ws-security.callback-handler" value-ref="upCallBackHandlerRealmC" />
        </jaxws:properties>
      </jaxws:endpoint>
      
      <bean id="upCallBackHandlerRealmC"
        class="org.apache.cxf.fediz.service.sts.UsernamePasswordCallbackHandler">
        <property name="passwords" ref="REALMC" />
      </bean>
      
      <util:map id="REALMC">
        <entry key="user1"
          value="pw1" />
      </util:map>
    
    The bean which defines the username and passwords is defined in the imported spring configuration file passwords.xml. The claims of the users in the two realms are defined in userClaims.xml. You can find more information about managing the claims in an older blog.

    If you want to configure an LDAP backend check the following blog post:

  • Identity mapper

    If you add a new realm, the Identity Mapper provided in org.apache.cxf.fediz.service.sts.realms.IdentityMapperImpl must be enhanced. In practise, you access a database, LDAP or web service to get the mapping. You must implement the interface org.apache.cxf.sts.IdentityMapper to customize the identity mapping to your needs.

Customize realm concept

  • The Fediz STS is based on the Apache CXF STS. The STS supports several realms where each realm is connected to an authentication server like LDAP, file (Testing), database or a custom authentication server. The service consumer must know from which STS realm he requests a token from by choosing the appropriate URL. The distinction is based on the syntax in the URL.

    The syntax in the Fediz STS is https://:/fediz-idp-sts/<REALM>/.... The distinction is after the servlet context fediz-idp-sts. This behaviour is implemented in org.apache.cxf.fediz.service.sts.realms.UriRealmParser and configured in the bean customRealmParser.

    You can implement a different distinction based on the machine name, http port or servlet context. Maybe you prefer to deploy the realms in different JVMs. You just have to implement the interface org.apache.cxf.sts.RealmParser.

  • When you request a token on-behalf-of another SAML token, the STS must be able to figure out from which realm the token has been issued. As above, the STS provides an interface which must be implement to change the default behaviour of the Fediz STS. The default behaviour is implemented in org.apache.cxf.fediz.service.sts.realms.SamlRealmCodec and expects the realm encoded in the CN (Common Name) of the signer certificate. This class implements the interface org.apache.cxf.sts.token.realm.SAMLRealmCodec and can be customized. You have to update the bean samlRealmCodec or update the bean which references this bean.

Please post feedback and ideas to the CXF mailing list.

Thank you for all support and feedback!

May 21, 2013

Logging in Apache CXF STS enhanced

This extension will be available in CXF release 2.7.6 which is not yet available. But you can run tests with the SNAPSHOT build till this version is released. Please provide feedback to the CXF mailing list.

Different logging frameworks (SLF4J, Log4J, Logback, JUL) can be used to log events for Apache CXF STS. The configuration allows to define which logger should log messages till to which log level. That works fine to drill down generic issues but it doesn't help too much to know whether a certain user could successfully log in or had any specific issues. Further, the WS-Trust interface is very generic. Therefore, the same user can request tokens but for different applications using different credential types. If a log in error occurs some context information is required to easily drill down user specific issues.

Based on the experience of a customer deployment, the following information is helpful to figure out how often a user requested a token and under which circumstances:

  • AppliesTo
    For which application did the user request a token
  • Source IP
    From which machine did the application request a token for a user
  • Claims
    Which claims did the user request for an application
  • Security Header
    How did the user try to log in (Username/password, Kerberos, X509, ...)
  • Realm
    For which security domain did the user request a token
  • etc.
All this information are available within the core classes of the STS and thus not customizable without patching these classes. The next release of CXF will provide a customizable logging/auditing functionality to fulfill various requirements.

Spring Eventing

The Spring framework provides an eventing mechanism which is designed for simple communication between Spring beans. Instead of introducing a new eventing mechanism to push data to a class which processes the data and writes it to a log file the new feature leverages the usage of the Spring framework in the CXF STS. How Spring eventing works is described on the following blog. If you don't want to delay STS related processing you can publish the events asynchronously which is described here.

CXF STS custom Application Events

Depending on the STS operation called, a different object with context information is created in the CXF STS. The following table summarizes the defined bindings in the WS-Trust specification, the CXF related context object as well a link with more information about this binding:

BindingContext objcectSpring EventDocumentation
IssueTokenProviderParametersSTSIssueSuccessEvent
STSIssueFailureEvent
blog
ValidateTokenValidatorParametersSTSValidateSuccessEvent
STSValidateFailureEvent
blog
CancelTokenCancellerParametersSTSCancelSuccessEvent
STSCancelFailureEvent
blog
RenewTokenRenewerParametersSTSRenewSuccessEvent
STSRenewFailureEvent
blog

The different binding implementations support the interface ApplicationEventPublisherAware thus they can publish events about a successful or failed request. You have to provide an implementation of ApplicationListener to listen to Spring Application Events. Due to the usage of generics you can specify which events you want to listen to. All the above STS specific events inherit the abstract class AbstractSTSEvent. If you want to listen to all STS events then you must provide an implementation like this:

public class AllSTSEventsListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(AbstractSTSEvent event) {
        // do whatever you want here
    }
}
If you want to listen to all successful issue events you must use the generic STSIssueSuccessEvent. The STS provides the LoggerListener which listens to all STS events and uses the CXF Logging API to write the log message. All you have to do is configure the following bean in the STS application context configuration (ex. cxf-transport.xml):
<bean id="loggerListener" class="org.apache.cxf.sts.event.LoggerListener" />

If you want to configure another Application listeners, just add a bean configuration and you're done.

The LoggerListener is able to log the following context information:

  • TIME
    Creation time of the event
  • OPERATION
    STS binding/operation
  • WS_SEC_PRINCIPAL
    Principal in WS-Security token
  • STATUS
    Successful/failed request
  • DURATION
    Processing time
  • TOKENTYPE
    Token type requested (SAML 1.1, SAML 2.0, etc)
  • REALM
    Security domain
  • APPLIESTO
    Application for which token is requested
  • CLAIMS
    Claims requested
  • ACTAS_PRINCIPAL
    Principal of ActAs token
  • ONBEHALFOF_PRINCIPAL
    Principal of On-Behalf-Of token
  • VALIDATE_PRINCIPAL
    Principal of Validate token
  • CANCEL_PRINCIPAL
    Principal of Cancel token
  • RENEW_PRINCIPAL
    Principal of Renew token
  • REMOTE_HOST
    Hostname/IP which requested the token
  • REMOTE_PORT
    Source Port which requested the token
  • URL
    STS URL used to request token
The LoggerListener provides the following properties to customize its behaviour:

NameTypeMandatoryDefaultDescription
logFieldnameBooleanNoNoShould the fieldname be logged, ex. OPERATION=issue
dateFormatStringNogetDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)Format of the date
logLevelStringNoFINEWhich log level should be used?
logStacktraceBooleanNoNoIn case of an error, shall the stacktrace be logged?
fieldOrderList<String>NoTIME
STATUS
DURATION
REMOTE_HOST
REMOTE_PORT
OPERATION
URL
REALM
WS_SEC_PRINCIPAL
ONBEHALFOF_PRINCIPAL
ACTAS_PRINCIPAL
VALIDATE_PRINCIPAL
CANCEL_PRINCIPAL
RENEW_PRINCIPAL
TOKENTYPE
APPLIESTO
CLAIMS
EXCEPTION
Order of context fields to be logged

If you want that all LoggerListener related log messages are written into a different file (ex. audit.log) I highly recommend to not use Java Util Logging as it's not so easy to configure a dedicated handler/appender for one logger.

  1. Configure Log4J as the logging framework in CXF (see here)
  2. Add the log4j dependency to your POM
  3. Configure the logger and appender
    log4j.rootLogger=INFO, CONSOLE, LOGFILE
    log4j.logger.org.apache.cxf.sts.event.LoggerListener=DEBUG, AUDIT
    
    # CONSOLE is set to be a ConsoleAppender using a PatternLayout.
    log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
    log4j.appender.CONSOLE.Threshold=INFO
    log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
    log4j.appender.CONSOLE.layout.ConversionPattern=%d [%t] %-5p %c %x - %m%n
    
    # AUDIT is set to be a File appender using a PatternLayout.
    log4j.appender.AUDIT=org.apache.log4j.FileAppender
    log4j.appender.AUDIT.File=${catalina.base}/logs/audit.log
    log4j.appender.AUDIT.Append=true
    log4j.appender.AUDIT.Threshold=DEBUG
    log4j.appender.AUDIT.layout=org.apache.log4j.PatternLayout
    log4j.appender.AUDIT.layout.ConversionPattern=%m%n
    
The audit log file looks like this if configured as above:
5/10/13 8:59:59 AM;SUCCESS;2839ms;127.0.0.1;57378;Issue;https://localhost:9443/fediz-idp-sts/STSService;null;alice;null;null;http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0;https://localhost:8081/doubleit/services/doubleittransportsaml1claims;null;null;

Enjoy.

May 14, 2013

LDAP support enhanced for CXF STS 2.7.5

I described in a previous blog how to configure the CXF STS for an LDAP directory for authentication and to retrieve user claims (attributes). The new release 2.7.5 of CXF provides extended support for roles managed in a LDAP directory. In previous versions, the LdapClaimsHandler added groups as roles if the groups were assigned to a multi-value attribute of the user. The new release provides an LdapGroupClaimsHandler which supports the case where an attribute of the groups lists the users who belong to this group. Further, it introduces the semantic of an application role. A user might have the role "User" for application X and role "Manager" and "User" for application Y.

The STS provides the semantic of an application with the AppliesTo parameter which is a URI. If you request a SAML token which includes the roles for a specific application (ex. MyApp), you get User and Manager back. A mapping is required in the STS to map the AppliesTo URI (URL or URN) to a String value like MyApp.

The sub-project Fediz provides in 1.1 (not released yet) a Maven profile to build the STS with an LDAP backend (instead of managing users/claims in a file). You can have a look at the ldap.xmlhere. The following configuration configures the LdapClaimsHandler and LdapGroupClaimsHandler. There is nothing special for the LdapClaimsHandler. The LdapGroupClaimsHandler also uses the Spring LdapContextSource and LdapTemplate.

    <util:list id="claimHandlerList">
        <ref bean="userClaimsHandler" />
        <ref bean="groupClaimsHandler" />
    </util:list>

    <bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
        <property name="url" value="ldap://localhost:389/" />
        <property name="userDn" value="uid=admin,ou=system" />
        <property name="password" value="secret" />
    </bean>

    <bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
        <constructor-arg ref="contextSource" />
    </bean>

    <util:map id="claimsToLdapAttributeMapping">
        <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
            value="givenName" />
        <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"
            value="sn" />
        <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
            value="mail" />
        <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country"
            value="c" />
        <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode"
            value="postalCode" />
        <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress"
            value="postalAddress" />                        
        <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/locality"
            value="town" />
        <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/stateorprovince"
            value="st" />
        <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/gender"
            value="gender" />
        <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth"
            value="dateofbirth" />                                                
        <entry key="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"
            value="member" />
    </util:map>

    <bean id="userClaimsHandler" class="org.apache.cxf.sts.claims.LdapClaimsHandler">
        <property name="ldapTemplate" ref="ldapTemplate" />
        <property name="claimsLdapAttributeMapping" ref="claimsToLdapAttributeMapping" />
        <property name="userBaseDN" value="ou=users,dc=fediz,dc=org" />
        <property name="userNameAttribute" value="uid" />
    </bean>
    
    <util:map id="appliesToScopeMapping">
        <entry key="urn:org:apache:cxf:fediz:fedizhelloworld"
            value="Example" />
    </util:map>
    
    <bean id="groupClaimsHandler" class="org.apache.cxf.sts.claims.LdapGroupClaimsHandler">
        <property name="ldapTemplate" ref="ldapTemplate" />
        <property name="userBaseDN" value="ou=users,dc=fediz,dc=org" />
        <property name="userNameAttribute" value="uid" />
        <property name="groupBaseDN" value="ou=groups,dc=fediz,dc=org" />
        <property name="appliesToScopeMapping" ref="appliesToScopeMapping" />
    </bean>
    
    <jaxws:endpoint id="transportSTS1" implementor="#transportSTSProviderBean"
        address="/STSService" wsdlLocation="/WEB-INF/wsdl/ws-trust-1.4-service.wsdl"
        xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
        serviceName="ns1:SecurityTokenService" endpointName="ns1:TransportUT_Port">
        <jaxws:properties>
            <entry key="ws-security.ut.validator">
                <bean class="org.apache.ws.security.validate.JAASUsernameTokenValidator">
                    <property name="contextName" value="LDAP" />
                </bean>
            </entry>
        </jaxws:properties>
    </jaxws:endpoint>
I've highlighted the important beans to support the mapping of groups to (application) roles. The bean LdapGroupClaimsHandler has got the following attributes:

NameMandatoryDefaultDescription
ldapTemplateYesN.A.The Spring LDAP template
groupBaseDNYesN.A.The base group context where the search starts
groupObjectClassNogroupOfNamesObject class for groups. Used for search filter.
groupMemeberAttributeNomemberThe group attribute where the list of users are stored
groupURINohttp://schemas.xmlsoap.org/ws/2005/05/identity/claims/roleThe SAML attribute name where the roles should be stored
groupNameGlobalFilterNoROLEDefault uses the CN of the group as role name
groupNameScopedFilterNoSCOPE_ROLEDefault cuts the SCOPE and the underscore of the CN of the group
appliesToScopeMappingNoN.A.The mapping is required if application specific roles must be supported
userNameAttributeNocnUser id attribute. Only required if LDAP is not used for authentication and thus the DN of the user must be resolved first. Used for search filter.
userObjectClassNopersonObject class for users. Only required if LDAP is not used for authentication and thus the DN of the user must be resolved first. Used for search filter.

The bean appliesToScopeMapping defines the mapping of the URI in the AppliesTo variable to a Name as URI's are not valid within a CN of an LDAP group.

One example for the usage of groupNameScopedFilter. One more example. Let's assume you use the same LDAP directory for the application environemnt development and pre-production and defines the following naming convention for application roles:
DEV_<Application>_<ROLE>_Group and UAT_<Application>_<ROLE>_Group The groupNameScopedFilter will look like this DEV_SCOPE_ROLE_Group (assumption: Different STS instances are deployed for development and pre-production).

The following table lists a few group examples and how the role value will look like in the SAML attribute. The assumption is that the AppliesTo element is urn:org:apache:cxf:fediz:fedizhelloworld which maps to the scope Example (see configuration example above) and the groupNameScopedFilter is configured like DEV_SCOPE_ROLE_Group:

Group CNRole name
DEV_Example_User_GroupUser
DEV_Example_Admin_GroupAdmin
DEV_Example2_User_Groupignored
UAT_Example_User_Groupignored
INFR_Citrix_Accessignored

Last but not least I'd like to comment the default value of userNameAttribute which is CN. As per recommendation (5.4) the CN is typically the person's fullname and therefore doesn't fit for the user id (login name). Due to the reason that the LdapClaimsHandler had the cn as default value I wanted to keep that in sync and change it in the next non-patch release of CXF.

If you face issues or like more functionality send a message to the CXF mailing list or open a JIRA issue.

April 22, 2013

Full Spring Security Support in Apache CXF Fediz

Full Spring Security Support in Apache CXF Fediz

New features are going to be added in the next version 1.1 of Fediz. I described here how to configure the new Fediz plugin for Spring Security with Container Managed Security (Pre-Authentication in Spring Security terms). The current snapshot version of Fediz 1.1 provides also full/native Spring Security support which means the Servlet Container runs unauthenticated (no security constraints defined in web.xml) and Spring Security enforces authentication.

You can either download the sources here:

git clone git://git.apache.org/cxf-fediz.git

or

svn co https://svn.apache.org/repos/asf/cxf/fediz/trunk

or download it from the snapshot maven repository.

A new example springWebapp has been added to the distribution to show this.

As in the Pre-Authentication case, the application can get access to the Spring Security Context like this: SecurityContextHolder.getContext().getAuthentication(); The Authentication object is of instance FederationAuthenticationToken provides the following methods.

>
MethodClassDescription
getCredentialsElementIssues Security Token (ex. SAML Assertion)
getDetailsWebAuthenticationDetailsAuthentication details like IP, Session ID
getNameStringAuthenticated user name
getAuthoritiesCollection<? extends GrantedAuthority>List of roles
getUserDetailsFederationUserExtends the standard Spring User class with method getClaims()

You can get more information from the Fediz Wiki how to configure Spring Security or have a look at the example here. The example shows how to configure Fediz for Spring Security and how to use the Spring Security API in your application code. Please post feedback and ideas to the CXF mailing list or the JIRA task FEDIZ-39.

Apache CXF Fediz is a subproject of Apache CXF. Fediz helps you to secure your web applications and delegate security enforcement to the underlying application server. With Fediz, authentication is externalized from your web application to an identity provider installed as a dedicated server component. The supported standard is WS-Federation Passive Requestor Profile.

Thank you for all support and feedback!

March 4, 2013

SSO and Fine Grained Authorization in the Cloud

In February 2013, I was at ApacheCon NA 2013 in Portland, Oregon, US where I learned a lot about several Apache projects.

My presentation was about SSO and Fine Grained Authorization in the Cloud. I gave an introduction about application security 10-15 years ago and how to address challanges with Cloud deployment using Apache CXF Fediz.

Here are the slides from my talk:

February 13, 2013

Spring Security support added in Apache CXF Fediz

Initial support for Spring Security in Apache CXF Fediz added

New features are going to be added in the next version 1.1. The next feature ready for testing is the support for Spring Security for version 3.1

You can either download the sources here:

git clone git://git.apache.org/cxf-fediz.git

or

svn co https://svn.apache.org/repos/asf/cxf/fediz/trunk

or download it from the snapshot maven repository.

The Fediz Spring Plugin supports integration with the Spring Pre-Authentication scenario as described here.

A new example springPreauthWebapp has been added to the distribution to show this.

I'd like to highlight two things.

1) You can get access to the Spring Security Context like this: SecurityContextHolder.getContext().getAuthentication(); The Authentication interfaces provides the following methods.

>
MethodClassDescription
getCredentialsElementIssues Security Token (ex. SAML Assertion)
getDetailsPreAuthenticatedGrantedAuthoritiesWebAuthenticationDetailsAuthentication details like IP, Session ID
getNameStringAuthenticated user name
getAuthoritiesCollection<? extends GrantedAuthority>List of roles
getPrincipalFederationUserExtends the standard Spring User class with method getClaims()

Here is an example where the information of the Authentication object is logged:

getCredentials: [saml2:Assertion: null] getDetails: org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails@1c07a: RemoteIpAddress: 127.0.0.1; SessionId: go3xw6sxzqr5w02gn85elfgv; [ROLE_USER] getName: alice getAuthorities: [ROLE_USER] getPrincipal: org.apache.cxf.fediz.spring.FederationUser@5899680: Username: alice; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER

2) You can define rules who can access which resource as illustrated in the following snippet of applicationContext-security.xml of the new example springPreauthWebapp Please post feedback and ideas to the CXF mailing list or the JIRA task FEDIZ-38 and FEDIZ-39.

Apache CXF Fediz is a subproject of Apache CXF. Fediz helps you to secure your web applications and delegate security enforcement to the underlying application server. With Fediz, authentication is externalized from your web application to an identity provider installed as a dedicated server component. The supported standard is WS-Federation Passive Requestor Profile.

Thank you for all support and feedback!