You are viewing a read-only archive of the Blogs.Harvard network. Learn more.

Using Commerce Server’s UpmMembershipProvider with DotNetNuke

For those that are not familiar with the UpmMembershipProvider membership provider, it is a part of Microsoft’s Commerce Server 2007 product, and is the only provider that offers interoperability with Commerce Server.  It replaces the older AuthManager functionality from Commerce Server 2002.  More importantly, it provides an implementation of the System.Web.Membership.MembershipProvider class, and may thus be used in any ASP.NET application that utilizes the standard membership system.  Read more about the UpmMembershipProvider in Microsoft’s MSDN documentation.

DotNetNuke (DNN) is one such application that uses the ASP.NET membership system.  Because I’ve been working so much with it lately, I decided to investigate whether these two strangers might be successfully introduced — and indeed they can.  Google tells me that I am the only person to date who has successfully accomplished this task (not that surprising, given its relatively esoteric nature).

It turns out that this integration consists of three tasks: (1) configuring Commerce Server appropriately, (2) the routine tasks that one would expect in using the new provider, (3) and a small integration gotcha.  Herein I address these in order.

Configuring Commerce Server

Commerce Server uses the GeneralInfo.email_address value of the UserObject profile object by default for authentication.  This is great, except that DotNetNuke uses a generic login name during registration.  While it’s possible to configure DNN to use e-mail addresses throughout its registration system, that’s a topic for a later date and beyond the scope of the task at hand.

We are thus forced to move Commerce Server away from e-mail addresses in favor of a more generic login name.  If you haven’t already configured your Commerce Server installation to use such a login name, you’ll need to do so.  Amy describes this process in her newsgroup post here.  Thanks Amy!

Web.Config Integration

Most developers will know that the first task in installing a new provider is updating the application’s web.config file with details about the provider.  Accordingly, we add the following to our web.config:

        <membership defaultProvider="UpmMembershipProviderAdapter"
                    userIsOnlineTimeWindow="15">
            <providers>
                <clear/>
                <add name="UpmMembershipProviderAdapter"
                     enablePasswordReset="true"
                     logonNameProperty="GeneralInfo.login_name"
                     enableCreateDate="true"
                     enableEmailAddress="true"
                     enableLastLoginDate="true"
                     profileDefinition="UserObject"
                     requiresApproval="true"
                     minRequiredPasswordLength="5"
                     minRequiredNonalphanumericCharacters="0"
                     maxInvalidPasswordAttempts="5"
                     passwordAttemptWindows="15"
                     passwordLockoutPeriod="10"
                    type="BrandonHaynes.Membership.UpmMembershipProviderAdapter"/>
            </providers>
        </membership>

Two comments about this entry:

  1. Most attributes are straightforward and default here, including: enablePasswordReset, loginNameProperty, enableCreateDate, enableEmailAddress, enableLastLoginDate, profileDefinition.  Note that the UpmMembershipProvider does not support many of the attributes available in the AspNetSqlMembershipProvider; see the MSDN documentation for more details.  This can be a source of frustration, as the provider does not complain when an unsupported attribute is supplied.
  2. I did not directly use the UpmMembershipProvider class located in the Microsoft.CommerceServer.Runtime assembly.  The reason for this is discussed below as a gotcha.

Additionally, you’ll need to copy over all the required Commerce Server files to your DNN installation.  These can come from the StarterSite or CSharpSite (obviously this choice will impact your mappings, and any production application will likely have customized these files).  These files include:

  • All files in the pipelines subdirectory
  • bin/CommerceMessageManager.dll,
  • The de-DE, EN, en-us, fr-fr, and ja-JP subdirectories of the bin folder.
  • OrderObjectMappings.xml
  • OrderPipelineMappings.xml
  • csapp.ini

Naturally, the default DotNetNuke web.config must have the required Commerce Server sections added.  This includes {sectionGroup[@name = ‘CommerceServer’], compilation/assemblies, system.web/httpModules, and  CommerceServer} elements.  Look to the Commerce Server SDK StarterSite for a model web.config, or use the one that I provide below.

That’s it with the trivial part.  Not so bad at all!

Adapting the Provider

As mentioned above, the UpmMembershipProvider does not support some of the configuration properties available in its MembershipProvider base class, and throws a NotSupportedException when any of these methods/properties are called.  Unfortunately, DotNetNuke makes calls to one of these unsupported properties — PasswordQuestion — regardless of the provider configuration.  Without modifying the core, there is no way to declaratively remedy this problem.

Because of this issue, we are forced to decorate the UpmMembershipUser object so that it does not throw, and adapt the UpmMembershipProvider such that it returns our newly decorated user objects.

Decorating the UpmMembershipUser

Our decoration is very straightforward and by-the-book:

    class UpmMembershipUserAdapterDecorator : MembershipUser
        {
        private MembershipUser decoratedMembershipUser;

        internal UpmMembershipUserAdapterDecorator(MembershipUser decoratedMembershipUser)
            {
            this.decoratedMembershipUser = decoratedMembershipUser;
            }

        internal MembershipUser DecoratedMembershipUser
            {
            get { return decoratedMembershipUser; }
            }

        #region Decorated Overrides

        public override string PasswordQuestion
            {
            get
                {
                if (System.Web.Security.Membership.Providers[decoratedMembershipUser.ProviderName].RequiresQuestionAndAnswer)
                    return decoratedMembershipUser.PasswordQuestion;
                else
                    return null;
                }
            }
        #endregion

        #region Delegated Overrides ...
        }

Here we decorate the PasswordQuestion property such that it relies on base implementation when RequiresQuestionAndAnswer is activated in the web.config, and null if it is unsupported (instead of throwing).  The “Delegated Overrides” region implements the rest of the MembershipUser methods, passing the call to the decorated MembershipUser as per the pattern.

Adapting the UpmMembershipProvider

Next, we adapt the UpmMembershipProvider class so that it returns DecoratedMembershipUsers (instead of UpmMembershpUsers):

    public class UpmMembershipProviderAdapter : UpmMembershipProvider
        {
        public UpmMembershipProviderAdapter() : base() { }

        #region Overridden Functions

        public override MembershipUser CreateUser(string username, string password,
        string email, string passwordQuestion, string passwordAnswer, bool isApproved,
        object providerUserKey, out MembershipCreateStatus status)
            {
            return DecorateUser(base.CreateUser(username, password, email,
                 passwordQuestion, passwordAnswer, isApproved, providerUserKey,
                out status));
            }

        public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
            {
            return DecorateUser(base.GetUser(providerUserKey, userIsOnline));
            }

        public override MembershipUser GetUser(string username, bool userIsOnline)
            {
            return DecorateUser(base.GetUser(username, userIsOnline));
            }

        public override void UpdateUser(MembershipUser user)
            {
            base.UpdateUser(ExtractDecoratedUser(user));
            }
        #endregion

        #region Private Functions

        private static MembershipUser DecorateUser(MembershipUser decoratedUser)
            {
            if (decoratedUser != null)
                decoratedUser =
                new UpmMembershipUserAdapterDecorator(decoratedUser);

            return decoratedUser;
            }

        private static MembershipUser ExtractDecoratedUser(MembershipUser user)
            {
            UpmMembershipUserAdapterDecorator decoratedUser =
                user as UpmMembershipUserAdapterDecorator;

            if (decoratedUser != null)
                user = decoratedUser.DecoratedMembershipUser;

            return user;
            }

        #endregion
        }

This adaptation is also very straightforward.  The methods that return MembershipUsers (CreateUser, GetUser, UpdateUser) decorate the UpmMembershipUser returned by the base implementation.  Note that I included a private ExtractDecoratedUser helper function to aid in debugging, but as it is not called internally it may be safely omitted at a developer’s discretion.

Closing Comments

Despite a few configuration hassles, coaxing the UpmMembershipProvider to play nicely with DotNetNuke was surprisingly easy.  I’ve made the relevant files available as a download for anyone desiring to duplicate this integration.

I’d appreciate feedback and comments (and of course questions!) for anyone who chooses to duplicate my efforts.  Good luck!

B

Attachments

Be Sociable, Share!

6 Comments

  1. ze

    December 17, 2008 @ 6:51 pm

    1

    Brandon – I am trying to do something similar, integrate dnn with cs 2007. I cannot find the download link to your files. Can you please point me to it

    Thanks

  2. Noorin

    May 28, 2009 @ 1:25 pm

    2

    Hi Brandon,

    I am trying to use UPM of CS in DNN. Your post was really helpful to get to a level. But now even I could not find the download link. Am I missing something? Can you please send me the link to download your code?

    Thanks,
    Noorin

  3. Brandon Haynes

    May 29, 2009 @ 10:34 am

    3

    Hi Norrin and Ze,

    Subsequent to my post, I ran into some IPR issues with the UpmMembershipAdapter code that have taken some time to resolve. I will look at getting the download re-enabled if feasible; in the meantime, I believe there is enough context above to recreate.

    Brandon

  4. Noorin

    June 2, 2009 @ 4:43 pm

    4

    Hi Brandon,

    Thanks for your quick reply!
    The steps mentioned above are good enough to create a UPM. But I got stuck at one point. I am not sure what the below line from web.config is refering to:
    “type=”BrandonHaynes.Membership.UpmMembershipProviderAdapter”
    Is this your own dll? Or is this dll referring to the new project I’ll be creating to add the code mentioned above? Let me know if I am missing something.

    Thanks again!

    -Noorin

  5. Brandon Haynes

    June 17, 2009 @ 9:43 am

    5

    I am pleased to announce that I have resolved the IPR issues associated with the adapters outlined herein, and have re-enabled the download links. Though the sample web.config is targeted at DotNetNuke version 4.8.1 (the latest version available at time of authorship), the relevant details may be incorporated into any newer version (e.g., 4.9.4 or 5.1).

    @Noorin: The type referenced is the assembly containing the membership provider decorator. If you have compiled your own, you will need to reference that qualified type. If you are using the assembly I have attached here, nothing should need to change.

  6. From the Desk of Brandon Haynes » Downloads: Commerce Server UpmMembershipProvider and DotNetNuke

    June 17, 2009 @ 9:59 am

    6

    […] Slightly more than a year ago, I detailed the steps necessary to integrate Commerce Server 2007 — and specifically the UpmMembershipProvider membership subsystem — into the DotNetNuke web application framework.  This entry is located here: Using Commerce Server’s UpmMembershipProvider with DotNetNuke. […]

Log in