Wednesday, August 1, 2012

Using Active Directory with Broadleaf Commerce

Broadleaf Commerce, an enterprise class, open source eCommerce framework is built on The Spring Framework.  As such, Spring Security is a natural choice to provide security for Broadleaf Commerce, even when using an authentication mechanism like Active Directory.  Broadleaf Commerce provides a few entities that make up the basic domain and foundation for security.  These entities include:
  • AdminUser (representing a user of the administrative portal)
  • AdminRole (a named entity representing a mapping of AdminUser to multiple AdminPerssions)
  • AdminPermission (a named entity representing a specific Broadleaf permission)
With this configuration you can manage security quite effectively from the Broadleaf database, requiring administrators to log in and gain access to features of the administrative console depending on their roles.  However, many companies wish to maintain user credentials and roles in an LDAP server.  Active Directory is a very popular LDAP server for many organizations.  So how can one use Active Directory to authenticate and authorize administrative users in Broadleaf Commerce?

The answer goes back to Spring Security.  Spring Security provides an LDAP module.  It also provides an extension to specifically deal with Active Directory.  It is a simple matter of configuration to secure the application with Spring Security and Active Directory.  However, we need the principal and roles that are returned from LDAP to map to Broadleaf-specific users.  In order to facilitate this, Broadleaf has implemented a bean called BroadleafActiveDirectoryUserDetailsMapper and a Servlet Filter called AdminExternalLoginStateFilter.  These beans provide a hook for Spring Security to authenticate against LDAP, map the appropriate user details and roles, look up the local admin user from the Broadleaf database and map the roles (or Granted Authorities) from Active Directory to Broadleaf-specific role, and provision a user record in the Broadleaf database if one does not exist.

Assuming a successful login, after authentication, the BroadleafActiveDirectoryUserDetailsMapper, will map the roles to the authenticated user.  It will also create a new instance of BroadleafExternalAuthenticationUserDetails (which extends Spring's UserDetails) and set that as the details of the Authentication object.  This allows us to pass some extra information to Broadleaf like first name, last name, email, etc.  After authentication, the AdminExternalLoginStateFilter will take the Authentication object from Spring, associated with the current user, and:
  1. Look up the user, by user name, from the database
  2. Create a new user if one does not exist
  3. Set the properties from the BroadleafExternalAuthenticationUserDetails
  4. Delete any roles that were associated with the user at last login
  5. Assign new roles to the user based on their current authentication
This allows Broadleaf to happily continue to operate with its own entities.  But it also ensures that authentication happens via LDAP, and that all data roles associated with the user are entirely refreshed from the source system (i.e. LDAP) at the time of login.

There are some configuration options for this to control how this happens.  In the application context file that defines security in Broadleaf (typically /WEB-INF/applicationContext-security.xml), we have three main beans that we need to change:

<bean id="blUserDetailsMapper" class="org.broadleafcommerce.profile.core.security.ldap.BroadleafActiveDirectoryUserDetailsMapper">
    <property name="useEmailAddressAsUsername" value="true"/>
    <property name="roleNameSubstitutions">
        <map>
            <entry key="Marketing_Admin" value="ADMIN,CRM"></entry>
        </map>
    </property>
</bean>

<bean id="adAuthProvider" class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
    <constructor-arg value="mycompany.com"/>
    <constructor-arg value="ldap://ldap.mycompany.com:389/"/>
    <property name="userDetailsContextMapper" ref="blUserDetailsMapper"/>
    <property name="useAuthenticationRequestCredentials" value="true" />
    <property name="convertSubErrorCodesToExceptions" value="true" />
</bean>

<sec:authentication-manager alias="blAuthenticationManager">
    <sec:authentication-provider ref="adAuthProvider" />
</sec:authentication-manager>


First, the blUserDetailsMapper is a custom Broadleaf hook that Spring LDAP invokes to provide custom functionality to map the user details.  Typically there will only be one of these types of beans configured in an application.  The "useEmailAddressAsUsername" property, if set to true, tells this component to use the "mail" attribute returned from LDAP as the user name rather than the user name returned from LDAP.  This may be useful if you want to use email addresses as username identifiers in Broadleaf, regardless of the Active Directory user name.  If set to false, the AD username will be used as the principal.  The "roleNameSubstitutions" property is a map that allows you to override Granted Authorities from LDAP with more appropriate authorities in Broadleaf.  In this example, if AD returns a Granted Authority of "Marketing_Admin", then the authorities presented to Broadleaf will include "ADMIN" and "CRM".  Of course, the "ADMIN" and "CRM" roles need to be configured in the "BLC_ADMIN_ROLE" table.


Next, the bean named "adAuthProvider" is a standard Spring LDAP configuration.  For more information on configuring Spring LDAP, look here.  This bean is an authentication provider that knows how to talk to an Active Directory server, and specifically allows you to use authentication request credentials to bind to the AD server.  It also allows mapping of specific Active Directory error codes to friendlier exceptions.


Finally, the configuration for the authentication-manager sets up a Spring authentication manager, which will use the "adAuthProvider" bean to do the authentication.  All other configurations are standard Spring Security configurations.  Check the documentation for more information on configuring Spring Security.


The "blAuthenticationManager" is a standard Spring Security component that allows for a number of AuthenticationProviders.  In this case, we specify that the ActiveDirectoryLdapAuthenticationProvider is the only authentication provider to use.  Spring will now delegate all username/password authentication requests to this Authentication Provider.


So, to recap the process, here's how it all works:
  1. User browses to login page
  2. User submits username and password
  3. Spring Security delegates to the ActiveDirectoryLdapAuthenticationProvider
  4. If authentication is successful, Spring delegates to the BroadleafActiveDirectoryUserDetailsMapper to map the specific user details and roles from LDAP to a Broadleaf Principal
  5. The AdminExternalLoginStateFilter queries the Spring Security API, retrieves the BroadleafExternalAuthenticationUserDetails, creates a new Broadleaf Admin User if one doesn't exist, deletes all existing roles and permissions if one does exist, and then creates new roles and permissions based on the most recent LDAP credentials
Of course, this functionality is especially suited for use with Admin Users.  Many companies will want their internal administrators' credentials stored in LDAP, and their customer profiles stored directly in Broadleaf's tables.  That said, there is currently no functionality to update the LDAP server or create new users in LDAP from within Broadleaf.  As for using this approach with customers, it is almost the same setup.  The only changes would be that the roles would be mapped differently, and that the a different Servlet Filter would have to be created, similar to the AdminExternalLoginStateFilter, to provision a new Customer in the Broadleaf database if one didn't exist.

Because Broadleaf uses Spring Security, which is a pluggable security model, and because Broadleaf allows you to override and control the components and configurations, you can effectively use any security mechanism that you like.  I've discussed using LDAP and Active Directory here.  However, using a similar approach, custom security, single sign on, or almost any other authentication mechanism can be used with Broadleaf.


Wednesday, December 21, 2011

Broadleaf Commerce deployed on Cloud Foundry

I have been working a lot lately with Broadleaf Commerce and wondered what it would take to deploy it on VMware's Cloud Foundry.  There are already a number of blogs and sources of documentation out there describing the simplicity and steps to deploy a Java / Spring web application to Cloud Foundry.  However, there are a few key considerations with Broadleaf that I wanted to test and talk about.  And besides, I wanted to try it all out for myself.


First, a little background...
Broadleaf, in my opinion, is the best enterprise class, open source eCommerce framework currently available.  It is built in Java on top of proven frameworks such as The Spring Framework and Hibernate, among other things.  Broadleaf Commerce provides a set of jar files, about 130 database tables, a domain model, a DAO layer, business services, a workflow engine, and an admin console right out of the box.  But Broadleaf Commerce is a framework rather than a product, meaning that you can use it and extend it to build your own eCommerce application.  It does not dictate your UI or user experience in any way.  Rather it leaves it up to the user of the framework to develop this part of the application using Broadleaf Commerce as the back-end eCommerce engine.  It is completely extensible, and allows you to completely customize and override the data model and functionality as required.  More on that in a minute.


VMware's Cloud Foundry is a new Platform as a Service, or PaaS, for deploying and running web applications in the cloud.  It is one of the developments that came out of VMware's acquisition of SpringSource.  It provides a simple approach for deploying applications without needing to think too much about infrastructure or scalability.  In a nutshell, Cloud Foundry is an open source PaaS that wraps and abstracts the complexity of networking, software installation, configuration, security, deployment, and scaling of JVM-based web applications such as Java Spring applications, Groovy/Grails, Ruby, Scala, etc.  It provides tools for deploying and managing your cloud instance in the form of a command line interface, and the SpringSource Tool Suite (STS).  Cloud Foundry provides a number of data and messaging services out of the box, including MySQL, PostgreSQL, MongoDB, RabbitMQ, and Redis.  Cloud Foundry is open source in the sense that the source is freely available.  So technically, you should be able to run a Cloud Foundry VM on any number of cloud providers such as Amazon EC2 or Rackspace Cloud. But Cloud Foundry is making it simple to deploy an application to CloudFoundry.com without too much setup.  Cloud Foundry also provides what they call Micro Cloud Foundry, a small instance of the Cloud Foundry platform that you can run and interact with locally using VMware Player, Workstation, or Fusion.


I should note that Cloud Foundry services are still in beta at this point and pricing has not been released yet.


The challenge...
As I mentioned, there is quite a bit of good documentation out there about deploying a Java / Spring web application to Cloud Foundry.  However, most of them use the typical "Hello World" example, that avoids some of the more complicated concerns.


Broadleaf Commerce provides a demo application on Github at https://github.com/BroadleafCommerce/BroadleafCommerceDemoSite.  The demo application represents an example of how one might use the Broadleaf Commerce Framework to build their own application.    When the source is downloaded, it allows you to build and run the demo application directly from an IDE or command line.  The resulting war file is about 72MB, a much larger war than most Hello World applications.


What's involved in building the Broadleaf demo?  Not much more than downloading and importing the source as a Maven project, building it, and running an Ant script to start up a local HSQLDB and Jetty servlet container.  As I mentioned, Broadleaf Commerce is a framework, and provides a set of jars and default configurations, including Spring and Hibernate configurations.  It also provides a set of out-of-the-box JPA entities that, of course, map to standard database tables.  So if the jars contain Spring application context files, persistence.xml files, and compiled classes, how do you customize it?  That's part of the magic of Broadleaf.  It uses a custom application context called MergeApplicationContext to merge and override configurations and bean definitions such as data sources and even things like business services.  As long as you use the same Spring bean ids and reference an implementation of the same interface you can override any standard or default component of Broadleaf Commerce.  The MergeApplicationContext essentially "overlays" your bean definition on the default Broadleaf configuration and overrides what Broadleaf provides out of the box with what you define.  Pretty cool, huh?


This application context merge behavior is well defined and well understood in servlet containers such as Tomcat, and in application servers such as Glassfish, JBoss, WebLogic, and WebSphere.  However, when deploying to Cloud Foundry, you don't choose a servlet container.  Incidentally, it's Tomcat under the covers, but you don't really need to understand where / how the application is deployed.  That's part of the appeal of CloudFoundry.  Since there are a number of abstractions that Cloud Foundry prescribes (e.g. all applications, whether Spring, Ruby, or other are abstracted into a "Droplet" and treated the same way for at least part of the deployment process), I wondered if deploying to Cloud Foundry would interfere in some way with the unique application context merge process (since Broadleaf "hijacks" the Spring lifecycle to accomplish the merge).


Broadleaf Commerce also has a nice, sophisticated environment variable injection mechanism for replacing properties in the Spring application context, depending on the environment.  This is similar to what Spring will be providing with Spring Profiles.  Again, I wanted to ensure that this mechanism worked when deploying to Cloud Foundry.


The steps...
Again, there are a number of great blogs on deploying a Spring application to Cloud Foundry.  I am not going to reiterate these steps and configurations.  However, with respect to the Broadleaf Commerce demo, I do want to summarize the steps with a few additional considerations.


  1. Download Broadleaf Commerce demo code, a fictitious coffee storefront, from Github
  2. Open the project in your favorite IDE (I used IntelliJ)
  3. Add the Cloud Foundry dependency to the pom file
  4. Modify the application context to use a special CloudFoundry data source configuration.  In particular, change the existing datasource configuration to use <cloud:data-source id="webDS"/>.  The cloud namespace is new and has to be added to the top of the application context configuration file.
  5. Modify the JPA Persistence Unit configuration (a.k.a. Hibernate) to use a MySQL dialect (instead of the demo default HSQLDB)
  6. Build the war file (Maven package command)
  7. Deploy the war to CloudFoundry
The observations...
Well, I can say that I was definitely able to deploy the Broadleaf demo application to Cloud Foundry.  I was able to deploy both to a Micro instance running locally and to the hosted CloudFoundry.com site.  I was a bit surprised how little I had to change and how easy it ended up being.  I had a few challenges that were difficult to troubleshoot.  Of course I made some of my own silly mistakes, like forgetting to change the Hibernate dialect.  I also found, by looking at the logs after a deployment failure, that I needed 1G of memory for this application, instead of the 512M that is the default.  But when I got past my own configuration errors and everything seemed to be working properly, I got an error from the vmc client that said, "Error: Application 'broadleafdemo's state is undetermined, not enough information available".  There were no errors in the logs, and I'm still not entirely sure what caused this.  I checked the state of the application and it was not running.  I deleted the application and tried again.  I tweaked some settings, consulted Google, changed from MySQL to PostgreSQL and back again.  And I continued to get the same result.  Finally, I deployed it, got the same dubious message, and walked away from it.  A few minutes later I came back and it was working.


My guess is that it had to do with the size and complexity of the application.  At 71MB it took a little longer to upload (which is typical of a Spring-based web application where the "app server" is inside the application rather than vice versa as is the case with a Java EE or EJB style of application).  The startup process takes about a minute because of the number of Spring beans and merging of contexts.  In addition this particular application is configured to allow Hibernate to build the database tables on startup.  Also, since this is a demo application, there is an import.sql file in the application that Hibernate uses to seed the database.


Since there were no errors in the logs and patience solved the problem, I can only guess that there was some sort of timeout waiting for the application to start.  Of course, this was confusing. But once I got past it everything worked like a charm and I was able to browse the catalog, add to cart, and do all of the things you would expect on an eCommerce demo site.


As for the things I wanted to verify, Cloud Foundry did not interfere with Broadleaf's unique application context merge process.  Cloud Foundry APIs also allow you to specify environment variables.  These can be used by Broadleaf Commerce to determine the appropriate property value replacements at startup.


Conclusion...
Broadleaf Commerce, even though it's open source, is a formidable player in the enterprise eCommerce space.  My understanding is that it is being used by some large online retailers including The Container Store, Pep Boys, Ganz (Webkins), Waste Management, and likely many others that I'm not aware of.  In order to use the full suite of Broadleaf Commerce features in the cloud, especially on Cloud Foundry, some additional changes will be required.  For example, Broadleaf uses JMS by default for asynchronous processing.  Of course this is highly configurable.  Using Broadleaf's merge functionality and Spring AMQP, overriding this behavior to use RabbitMQ would be quite easy.  Also, Broadleaf Commerce is designed with PCI compliance in mind.  However, the default functionality is reserved for a dedicated data center.  In order to maintain security and compliance with PCI rules in the cloud, it would be best to avoid accepting payment information directly and use a service such as PayPal, Blue Pay, or others that allow you to redirect users to those services for payment processing.  I have seen this approach used with Broadleaf Commerce and it works very well.


Cloud Foundry is still in beta, but is definitely on the right track as far as providing a simple, scalable, application platform for deploying and running an eCommerce storefront or any type of web application that runs in the Java Runtime Environment.  The Broadleaf demo is a relatively large and complex application and deploying it to Cloud Foundry was surprisingly straight forward, requiring few changes.  I would expect that Cloud Foundry will continue to improve its error handling and messaging to the client.  I also expect that more back-end services will be added over time, increasing the potential level of sophistication of applications hosted on Cloud Foundry.  One concern I have at this point is the scalability of the back-end services. Scaling the application tier is as simple as issuing a command.  Back end services such as PostgreSQL and MySQL are not scaled so easily.  Again, I would imagine that this will be addressed by the folks at Cloud Foundry in time.


In the end, deploying Broadleaf Commerce on Cloud Foundry was quite easy and as Cloud Foundry matures the two of them together look to provide a very feasible eCommerce platform.