{"id":11,"date":"2008-04-27T15:04:00","date_gmt":"2008-04-27T19:04:00","guid":{"rendered":"http:\/\/everysport.net\/GamePlan3\/Default.aspx?tabid=489&amp;EntryID=7"},"modified":"2009-06-17T09:36:52","modified_gmt":"2009-06-17T13:36:52","slug":"using-commerce-servers-upmmembershipprovider-with-dotnetnuke","status":"publish","type":"post","link":"https:\/\/archive.blogs.harvard.edu\/brandonhaynes\/2008\/04\/27\/using-commerce-servers-upmmembershipprovider-with-dotnetnuke\/","title":{"rendered":"Using Commerce Server&#8217;s UpmMembershipProvider with DotNetNuke"},"content":{"rendered":"<p>For those that are not familiar with the UpmMembershipProvider membership provider, it is a part of Microsoft&#8217;s Commerce Server 2007 product, and is the only provider that offers interoperability with Commerce Server.\u00a0 It replaces the older AuthManager functionality from Commerce Server 2002.\u00a0 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.\u00a0 Read more about the UpmMembershipProvider in <a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/microsoft.commerceserver.runtime.profiles.upmmembershipprovider.aspx\"><span style=\"color: #669966\">Microsoft&#8217;s MSDN documentation<\/span><\/a>.<\/p>\n<p><a href=\"http:\/\/www.dotnetnuke.com\/\"><span style=\"color: #669966\">DotNetNuke<\/span><\/a> (DNN) is one such application that uses the ASP.NET membership system.\u00a0 Because I&#8217;ve been working so much with it lately, I decided to investigate whether these two strangers might be successfully introduced &#8212; and indeed they can.\u00a0 <a href=\"http:\/\/www.google.com\/search?q=UpmMembershipProvider+DotNetNuke&amp;rls=com.microsoft:en-us&amp;ie=UTF-8&amp;oe=UTF-8&amp;startIndex=&amp;startPage=1\"><span style=\"color: #669966\">Google<\/span><\/a> tells me that I am the only person to date who has successfully accomplished this task (not that surprising, given its relatively esoteric nature).<\/p>\n<p>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 <a href=\"http:\/\/en.wikipedia.org\/wiki\/Gotcha_%28programming%29\"><span style=\"color: #669966\">gotcha<\/span><\/a>.\u00a0\u00a0Herein I address these in order.<!--more--><\/p>\n<h2>Configuring Commerce Server<\/h2>\n<p>Commerce Server uses the GeneralInfo.email_address value of the UserObject profile object by default for authentication.\u00a0 This is great, except that DotNetNuke uses a generic login name during registration.\u00a0 While it&#8217;s possible to configure DNN to use e-mail addresses throughout its registration system, that&#8217;s a topic for a later date and beyond the scope of the task at hand.<\/p>\n<p>We are thus forced to move Commerce Server away from e-mail addresses in favor of a more generic login name.\u00a0 If you haven&#8217;t already configured your Commerce Server installation to use such a login name, you&#8217;ll need to do so.\u00a0 Amy describes this process in her newsgroup post <a href=\"http:\/\/groups.google.com.sg\/group\/microsoft.public.commerceserver.general\/msg\/dae61f2735df12d2?dmode=source&amp;hl=en\"><span style=\"color: #669966\">here<\/span><\/a>.\u00a0 Thanks Amy!<\/p>\n<h2>Web.Config Integration<\/h2>\n<p>Most developers will know that the first task in installing a new provider is updating the application&#8217;s web.config file with details about the provider.\u00a0 Accordingly, we add the following to our web.config:<\/p>\n<pre>        <span class=\"kwrd\">&lt;<\/span><span class=\"html\">membership<\/span> <span class=\"attr\">defaultProvider<\/span><span class=\"kwrd\">=\"UpmMembershipProviderAdapter\"<\/span>\r\n                    <span class=\"attr\">userIsOnlineTimeWindow<\/span><span class=\"kwrd\">=\"15\"<\/span><span class=\"kwrd\">&gt;<\/span>\r\n            <span class=\"kwrd\">&lt;<\/span><span class=\"html\">providers<\/span><span class=\"kwrd\">&gt;<\/span>\r\n                <span class=\"kwrd\">&lt;<\/span><span class=\"html\">clear<\/span><span class=\"kwrd\">\/&gt;<\/span>\r\n                <span class=\"kwrd\">&lt;<\/span><span class=\"html\">add<\/span> <span class=\"attr\">name<\/span><span class=\"kwrd\">=\"UpmMembershipProviderAdapter\"<\/span>\r\n                     <span class=\"attr\">enablePasswordReset<\/span><span class=\"kwrd\">=\"true\"<\/span>\r\n                     <span class=\"attr\">logonNameProperty<\/span><span class=\"kwrd\">=\"GeneralInfo.login_name\"<\/span>\r\n                     <span class=\"attr\">enableCreateDate<\/span><span class=\"kwrd\">=\"true\"<\/span>\r\n                     <span class=\"attr\">enableEmailAddress<\/span><span class=\"kwrd\">=\"true\"<\/span>\r\n                     <span class=\"attr\">enableLastLoginDate<\/span><span class=\"kwrd\">=\"true\"<\/span>\r\n                     <span class=\"attr\">profileDefinition<\/span><span class=\"kwrd\">=\"UserObject\"<\/span>\r\n                     <span class=\"attr\">requiresApproval<\/span><span class=\"kwrd\">=\"true\"<\/span>\r\n                     <span class=\"attr\">minRequiredPasswordLength<\/span><span class=\"kwrd\">=\"5\"<\/span>\r\n                     <span class=\"attr\">minRequiredNonalphanumericCharacters<\/span><span class=\"kwrd\">=\"0\"<\/span>\r\n                     <span class=\"attr\">maxInvalidPasswordAttempts<\/span><span class=\"kwrd\">=\"5\"<\/span>\r\n                     <span class=\"attr\">passwordAttemptWindows<\/span><span class=\"kwrd\">=\"15\"<\/span>\r\n                     <span class=\"attr\">passwordLockoutPeriod<\/span><span class=\"kwrd\">=\"10\"<\/span>\r\n                    <span class=\"attr\">type<\/span><span class=\"kwrd\">=\"<strong>BrandonHaynes.Membership.UpmMembershipProviderAdapter<\/strong>\"<\/span><span class=\"kwrd\">\/&gt;<\/span>\r\n            <span class=\"kwrd\">&lt;\/<\/span><span class=\"html\">providers<\/span><span class=\"kwrd\">&gt;<\/span>\r\n        <span class=\"kwrd\">&lt;\/<\/span><span class=\"html\">membership<\/span><span class=\"kwrd\">&gt;<\/span><\/pre>\n<p>Two comments about this entry:<\/p>\n<ol>\n<li>Most attributes are straightforward and default here, including: enablePasswordReset, loginNameProperty, enableCreateDate, enableEmailAddress, enableLastLoginDate, profileDefinition.\u00a0 Note that the UpmMembershipProvider does not support many of the attributes available in the AspNetSqlMembershipProvider; see the <a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/ms917174.aspx\"><span style=\"color: #669966\">MSDN documentation<\/span><\/a> for more details.\u00a0 This can be a source of frustration, as the provider does not complain when an unsupported attribute is supplied.<\/li>\n<li>I did not directly use the UpmMembershipProvider class located in the Microsoft.CommerceServer.Runtime assembly.\u00a0 The reason for this is discussed below as a <a href=\"http:\/\/en.wikipedia.org\/wiki\/Gotcha_%28programming%29\"><span style=\"color: #669966\">gotcha<\/span><\/a>.<\/li>\n<\/ol>\n<p>Additionally, you&#8217;ll need to copy over all the required Commerce Server files to your DNN installation.\u00a0 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).\u00a0 These files include:<\/p>\n<blockquote>\n<ul>\n<li>All files in the pipelines subdirectory<\/li>\n<li>bin\/CommerceMessageManager.dll,<\/li>\n<li>The de-DE, EN, en-us, fr-fr, and ja-JP subdirectories of the bin folder.<\/li>\n<li>OrderObjectMappings.xml<\/li>\n<li>OrderPipelineMappings.xml<\/li>\n<li>csapp.ini<\/li>\n<\/ul>\n<\/blockquote>\n<p>Naturally, the default DotNetNuke web.config must have the required Commerce Server sections added.\u00a0 This includes {sectionGroup[@name = &#8216;CommerceServer&#8217;], compilation\/assemblies, system.web\/httpModules, and\u00a0 CommerceServer} elements.\u00a0 Look to the Commerce Server SDK StarterSite for a model web.config, or use the one that I provide below.<\/p>\n<p>That&#8217;s it with the trivial part.\u00a0 Not so bad at all!<\/p>\n<h2>Adapting the Provider<\/h2>\n<p>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.\u00a0 Unfortunately, DotNetNuke makes calls to one of these unsupported properties &#8212; PasswordQuestion &#8212; regardless of the provider configuration.\u00a0 Without modifying the core, there is no way to declaratively remedy this problem.<\/p>\n<p>Because of this issue, we are forced to <a href=\"http:\/\/en.wikipedia.org\/wiki\/Decorator_pattern\"><span style=\"color: #669966\">decorate<\/span><\/a> the UpmMembershipUser object so that it does not throw, and <a href=\"http:\/\/en.wikipedia.org\/wiki\/Adapter_pattern\"><span style=\"color: #669966\">adapt<\/span><\/a> the UpmMembershipProvider such that it returns our newly decorated user objects.<\/p>\n<h3>Decorating the UpmMembershipUser<\/h3>\n<p>Our <a href=\"http:\/\/en.wikipedia.org\/wiki\/Decorator_pattern\"><span style=\"color: #669966\">decoration<\/span><\/a> is very straightforward and by-the-book:<\/p>\n<pre>    <span class=\"kwrd\">class<\/span> UpmMembershipUserAdapterDecorator : MembershipUser\r\n        {\r\n        <span class=\"kwrd\">private<\/span> MembershipUser decoratedMembershipUser;\r\n\r\n        <span class=\"kwrd\">internal<\/span> UpmMembershipUserAdapterDecorator(MembershipUser decoratedMembershipUser)\r\n            {\r\n            <span class=\"kwrd\">this<\/span>.decoratedMembershipUser = decoratedMembershipUser;\r\n            }\r\n\r\n        <span class=\"kwrd\">internal<\/span> MembershipUser DecoratedMembershipUser\r\n            {\r\n            get { <span class=\"kwrd\">return<\/span> decoratedMembershipUser; }\r\n            }\r\n\r\n        <span class=\"preproc\">#region<\/span> Decorated Overrides\r\n\r\n<strong>        <span class=\"kwrd\">public<\/span> <span class=\"kwrd\">override<\/span> <span class=\"kwrd\">string<\/span> PasswordQuestion\r\n            {\r\n            get\r\n                {\r\n                <span class=\"kwrd\">if<\/span> (System.Web.Security.Membership.Providers[decoratedMembershipUser.ProviderName].RequiresQuestionAndAnswer)\r\n                    <span class=\"kwrd\">return<\/span> decoratedMembershipUser.PasswordQuestion;\r\n                <span class=\"kwrd\">else<\/span>\r\n                    <span class=\"kwrd\">return<\/span> <span class=\"kwrd\">null<\/span>;\r\n                }\r\n            }\r\n<\/strong>        <span class=\"preproc\">#endregion<\/span>\r\n\r\n        <span class=\"preproc\">#region<\/span> Delegated Overrides ...\r\n        }<\/pre>\n<p>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).\u00a0 The &#8220;Delegated Overrides&#8221; region implements the rest of the MembershipUser methods, passing the call to the decorated MembershipUser as per the pattern.<\/p>\n<h3><\/h3>\n<h3>Adapting the UpmMembershipProvider<\/h3>\n<p>Next, we <a href=\"http:\/\/en.wikipedia.org\/wiki\/Adapter_pattern\"><span style=\"color: #669966\">adapt<\/span><\/a> the UpmMembershipProvider class so that it returns DecoratedMembershipUsers (instead of UpmMembershpUsers):<\/p>\n<pre>    <span class=\"kwrd\">public<\/span> <span class=\"kwrd\">class<\/span> UpmMembershipProviderAdapter : UpmMembershipProvider\r\n        {\r\n        <span class=\"kwrd\">public<\/span> UpmMembershipProviderAdapter() : <span class=\"kwrd\">base<\/span>() { }\r\n\r\n        <span class=\"preproc\">#region<\/span> Overridden Functions\r\n\r\n        <span class=\"kwrd\">public<\/span> <span class=\"kwrd\">override<\/span> MembershipUser CreateUser(<span class=\"kwrd\">string<\/span> username, <span class=\"kwrd\">string<\/span> password,\r\n        <span class=\"kwrd\">string<\/span> email, <span class=\"kwrd\">string<\/span> passwordQuestion, <span class=\"kwrd\">string<\/span> passwordAnswer, <span class=\"kwrd\">bool<\/span> isApproved,\r\n        <span class=\"kwrd\">object<\/span> providerUserKey, <span class=\"kwrd\">out<\/span> MembershipCreateStatus status)\r\n            {\r\n            <span class=\"kwrd\">return<\/span> DecorateUser(<span class=\"kwrd\">base<\/span>.CreateUser(username, password, email,\r\n                 passwordQuestion, passwordAnswer, isApproved, providerUserKey,\r\n                <span class=\"kwrd\">out<\/span> status));\r\n            }\r\n\r\n        <span class=\"kwrd\">public<\/span> <span class=\"kwrd\">override<\/span> MembershipUser GetUser(<span class=\"kwrd\">object<\/span> providerUserKey, <span class=\"kwrd\">bool<\/span> userIsOnline)\r\n            {\r\n            <span class=\"kwrd\">return<\/span> DecorateUser(<span class=\"kwrd\">base<\/span>.GetUser(providerUserKey, userIsOnline));\r\n            }\r\n\r\n        <span class=\"kwrd\">public<\/span> <span class=\"kwrd\">override<\/span> MembershipUser GetUser(<span class=\"kwrd\">string<\/span> username, <span class=\"kwrd\">bool<\/span> userIsOnline)\r\n            {\r\n            <span class=\"kwrd\">return<\/span> DecorateUser(<span class=\"kwrd\">base<\/span>.GetUser(username, userIsOnline));\r\n            }\r\n\r\n        <span class=\"kwrd\">public<\/span> <span class=\"kwrd\">override<\/span> <span class=\"kwrd\">void<\/span> UpdateUser(MembershipUser user)\r\n            {\r\n            <span class=\"kwrd\">base<\/span>.UpdateUser(ExtractDecoratedUser(user));\r\n            }\r\n        <span class=\"preproc\">#endregion<\/span>\r\n\r\n        <span class=\"preproc\">#region<\/span> Private Functions\r\n\r\n        <span class=\"kwrd\">private<\/span> <span class=\"kwrd\">static<\/span> MembershipUser DecorateUser(MembershipUser decoratedUser)\r\n            {\r\n            <span class=\"kwrd\">if<\/span> (decoratedUser != <span class=\"kwrd\">null<\/span>)\r\n                decoratedUser =\r\n                <span class=\"kwrd\">new<\/span> UpmMembershipUserAdapterDecorator(decoratedUser);\r\n\r\n            <span class=\"kwrd\">return<\/span> decoratedUser;\r\n            }\r\n\r\n        <span class=\"kwrd\">private<\/span> <span class=\"kwrd\">static<\/span> MembershipUser ExtractDecoratedUser(MembershipUser user)\r\n            {\r\n            UpmMembershipUserAdapterDecorator decoratedUser =\r\n                user <span class=\"kwrd\">as<\/span> UpmMembershipUserAdapterDecorator;\r\n\r\n            <span class=\"kwrd\">if<\/span> (decoratedUser != <span class=\"kwrd\">null<\/span>)\r\n                user = decoratedUser.DecoratedMembershipUser;\r\n\r\n            <span class=\"kwrd\">return<\/span> user;\r\n            }\r\n\r\n        <span class=\"preproc\">#endregion<\/span>\r\n        }<\/pre>\n<p>This adaptation is also very straightforward.\u00a0 The methods that return MembershipUsers (CreateUser, GetUser, UpdateUser) decorate the UpmMembershipUser returned by the base implementation.\u00a0 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&#8217;s discretion.<\/p>\n<h2><\/h2>\n<h2>Closing Comments<\/h2>\n<p>Despite a few configuration hassles, coaxing the UpmMembershipProvider to play nicely with DotNetNuke was surprisingly easy.\u00a0 I&#8217;ve made the relevant files available as a download for anyone desiring to duplicate this integration.<\/p>\n<p>I&#8217;d appreciate feedback and comments (and of course questions!) for anyone who chooses to duplicate my efforts.\u00a0 Good luck!<\/p>\n<p>B<\/p>\n<h3>Attachments<\/h3>\n<ul>\n<li><a href=\"http:\/\/brandonhaynes.org\/Downloads\/UpmMembershipProvider\/BrandonHaynes.Integration Web.Config for DNN 4.8.1.zip\">Integration Sample web.config for UpmMembershipAdapter and DotNetNuke<\/a><\/li>\n<li><a href=\"http:\/\/brandonhaynes.org\/Downloads\/UpmMembershipProvider\/BrandonHaynes.UpmMembershipProviderAdapter.zip\">UpmMembershipProviderAdapter C# Project<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>For those that are not familiar with the UpmMembershipProvider membership provider, it is a part of Microsoft&#8217;s Commerce Server 2007 product, and is the only provider that offers interoperability with Commerce Server.\u00a0 It replaces the older AuthManager functionality from Commerce Server 2002.\u00a0 More importantly, it provides an implementation of the System.Web.Membership.MembershipProvider class, and may thus [&hellip;]<\/p>\n","protected":false},"author":1933,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3062,3063,142],"tags":[3074,3075,3070,3076],"class_list":["post-11","post","type-post","status-publish","format-standard","hentry","category-dotnetnuke-dnn-content-management-system","category-microsoft-commerce-server","category-technology","tag-commerce-server","tag-commerce-server-2007","tag-dotnetnuke","tag-upmmembershipprovider"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/archive.blogs.harvard.edu\/brandonhaynes\/wp-json\/wp\/v2\/posts\/11","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/archive.blogs.harvard.edu\/brandonhaynes\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/archive.blogs.harvard.edu\/brandonhaynes\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/archive.blogs.harvard.edu\/brandonhaynes\/wp-json\/wp\/v2\/users\/1933"}],"replies":[{"embeddable":true,"href":"https:\/\/archive.blogs.harvard.edu\/brandonhaynes\/wp-json\/wp\/v2\/comments?post=11"}],"version-history":[{"count":2,"href":"https:\/\/archive.blogs.harvard.edu\/brandonhaynes\/wp-json\/wp\/v2\/posts\/11\/revisions"}],"predecessor-version":[{"id":105,"href":"https:\/\/archive.blogs.harvard.edu\/brandonhaynes\/wp-json\/wp\/v2\/posts\/11\/revisions\/105"}],"wp:attachment":[{"href":"https:\/\/archive.blogs.harvard.edu\/brandonhaynes\/wp-json\/wp\/v2\/media?parent=11"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/archive.blogs.harvard.edu\/brandonhaynes\/wp-json\/wp\/v2\/categories?post=11"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/archive.blogs.harvard.edu\/brandonhaynes\/wp-json\/wp\/v2\/tags?post=11"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}