Thursday, April 9, 2009

EPiServer Community role and membership providers

From various sources I have found out that a lot of people are having trouble with the configuration of role providers and membership providers for EPiServer Community and EPiServer Mail. In this blog post I will try to describe the different setups that are available.

The basic facts

The first thing you need to know is that both Community and Mail has to have the users and groups in the database, independent of which membership provider and role provider you are using. The reason is the the user is such a central concept in the Community case, and Mail is using the same user management as Community. Ok, now we got this settled – let’s move on!

The most basic configuration

In the default installation for Community and Mail, the system will be configured to use the role provider named EPiServerCommonRoleProvider and the membership provider EPiServerCommonMembershipProvider. This means that the system will authenticate against the EPiServer Common tables, which is used by both Mail and Community. The system will also get the user roles, or groups, from the EPiServer Common tables. I.e. the user/group management and access rights is entirely managed by EPiServer Common.

<roleManager enabled="true" defaultProvider="EPiServerCommonRoleProvider" cacheRolesInCookie="true">
    <providers>
        <clear/>
        <add name="EPiServerCommonRoleProvider" applicationName="EPiServerCommonApplication" type="EPiServer.Common.Web.Authorization.RoleProvider, EPiServer.Common.Web.Authorization"/>
    </providers>
</roleManager>
<membership defaultProvider="EPiServerCommonMembershipProvider" userIsOnlineTimeWindow="10">
    <providers>
        <clear/>
        <add name="EPiServerCommonMembershipProvider" applicationName="EPiServerCommonApplication" type="EPiServer.Common.Web.Authorization.MembershipProvider, EPiServer.Common.Web.Authorization"/>
    </providers>
</membership>

A little bit more advanced configuration

In this case we want to use external membership provider and role provider. In my example I’m going to use Windows providers, but these could easily be substituted with SQL providers or something else.

We start off by setting the WindowsRoleProvider as default role provider, nothing tricky here. However when specifying the membership provider we will not set the WindowsMembershipProvider as default. Remember that every user/group needs to exist in the EPiServer Common tables. To solve this we use the EPiServerCommonIntegrationMembershipProvider and set it as default membership provider. This provider has an attribute called “provider”, here you specify your underlying provider – in this case the WindowsMembershipProvider.

You will also specify the attributes “roleToSynchronizeX” where X is a number (has to be in sequence and start with 1). If a user logs in and gets authenticated the system will look at the user’s groups – if the user is member of any of the groups specified in the “roleToSynchronize” attributes, then the user and all of its groups will be copied to the EPiServer Common tables. Note that only the user’s groups will be copied, not the other users in these groups.

If you use the notation roleToSynchronize1=”*”, the user will be copied independent of the group memberships it has. Note that you have to have EPiServer Common 2.3 Hotfix 1 for this to work.

<roleManager enabled="true" defaultProvider="WindowsRoleProvider" cacheRolesInCookie="true">
    <providers>
        <clear/>
        <add name="WindowsRoleProvider" applicationName="EPiServerSample" type="EPiServer.Security.WindowsRoleProvider, EPiServer"/>
    </providers>
</roleManager>
<membership defaultProvider="EPiServerCommonIntegrationMembershipProvider" userIsOnlineTimeWindow="10">
    <providers>
        <clear/>                
        <add name="WindowsMembershipProvider" type="EPiServer.Security.WindowsMembershipProvider, EPiServer" deletePrefix="BUILTIN\" searchByEmail="true"/>                
        <add name="EPiServerCommonIntegrationMembershipProvider" applicationName="EPiServerCommonApplication" type="EPiServer.Common.Web.Authorization.IntegrationMembershipProvider, EPiServer.Common.Web.Authorization" provider="WindowsMembershipProvider" roleToSynchronize1="Group1" roleToSynchronize2="Group2" />
    </providers>
</membership>

Advanced configuration

The last type of configuration is where you want to use a series of providers, something we at EPiServer would call a multiplexing scenario. In this case will make us of the IntegrationMultiplexingMembershipProvider which can be found in EPiServer Common 2.3 Hotfix 1. This is actually a combination of the multiplexing provider found in EPiServer CMS and the integration provider mentioned above. This will be used in combination with the MultiplexingRoleProvider in EPiServer CMS.

When using this provider you will be able to specify several underlying providers. The system will try the providers one after another until either the user is authenticated or the the list of providers runs out.

In my example below I’m using the Windows provider as primary provider and SQL provider as secondary.

<roleManager enabled="true" defaultProvider="MultiplexingRoleProvider" cacheRolesInCookie="true">
    <providers>
        <clear/>
        <add name="MultiplexingRoleProvider" type="EPiServer.Security.MultiplexingRoleProvider, EPiServer" provider1="WindowsServerRoleProvider" provider2="SqlRoleProvider" providerMap1="WindowsServermembershipProvider" providerMap2="SqlMembershipProvider"/>
        <add name="WindowsRoleProvider" applicationName="EPiServerSample" type="EPiServer.Security.WindowsRoleProvider, EPiServer"/>
        <add name="SqlServerRoleProvider" connectionStringName="EPiServerDB" applicationName="EPiServerSample" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
    </providers>
</roleManager>
<membership defaultProvider="MultiplexingMembershipProvider" userIsOnlineTimeWindow="10">
    <providers>
        <clear/>
        <add name="MultiplexingMembershipProvider" type="EPiServer.Common.Web.Authorization.Multiplexing.IntegrationMultiplexingMembershipProvider, EPiServer.Common.Web.Authorization.Multiplexing" provider1="WindowsServerMembershipProvider" provider2="SqlMembershipProvider" roleToSynchronize1="*" />
        <add name="WindowsMembershipProvider" type="EPiServer.Security.WindowsMembershipProvider, EPiServer" deletePrefix="BUILTIN\" searchByEmail="true"/>
        <add name="SqlServerMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="EPiServerDB" requiresQuestionAndAnswer="false" applicationName="EPiServerSample" requiresUniqueEmail="true" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression=""/>
    </providers>
</membership>

13 comments:

steven said...

Hi Tom
It seems as though some of this happens during an install? or post installation.(I dont recall having an option to choose mem provider, rol provider, I assume it's using the default of the intergration providers)

You have mentioned in previous conversations that the groups should been copied to the relevant db tables.? This is assuming one has create epi groups in advance?
if you look at post "http://world.episerver.com/Forum/Pages/Thread.aspx?id=22469&epslanguage=en#RE:Installation%20issues" , they had similar post issues. i.e Post installation group assignment to use epi mail.

on the provider configs do you use the WAT(web admin tool to switch providers) or change it manually.? Incorrect changes there will be detrimental!!

There is also a bug with the Clone error I raised before.. windows membership provider and integration role provider.

There is also no clarity on the default MailAdmins, and MailEditors users and how to use them. (Create groups in episerver called the same names and assing users to that group and syncronize?)

The SQL solution table insert seems the solution for WebAdmins?

Which I'm give it a go..

Håkan Lindqvist said...

Hi Steven,

I will try to clarify the things you brought up:

The "Clone error" should not appear in an installation where EPiServerCommon 2.3 Hotfix 1 has been applied (http://world.episerver.com/en/Download/Items/Hotfixes/EPiServer-Common/Hotfix-1---EPiServer-Common-23/). This very problem is the main reason why Common 2.3 hotfix 1 is listed as an requirement for EPiServer Mail 4.4.


The groups MailEditors and MailAdmins will be set up in the EPiServer Common user/group database during installation. This should be enough if using the EPiServerCommonMembershipProvider.

If, however, using the integration provider or integration multiplexing provider, you will have to create matching groups in the user db that your membership provider uses.

The installation manual does mention this, but is very brief and omits the case where it is not possible to add these groups directly through user management in CMS, that is, if the membership provider in use is incapable of doing this. Relevant section of the manual: http://world.episerver.com/en/Documentation/Items/Installation-Instructions/EPiServer-Mail1/Installation-Instructions---EPiServer-Mail-440/#membershipProv

Anyway, the basic idea is that you need to add the users that should have access to EPiServer Mail to one of these groups: MailEditors ("editor access") or MailAdmins ("admin access").



Best regards,
Håkan Lindqvist

Tom Stenius said...

Thank you Håkan for answering the questions Steven had! My next blog post will be about access rights in Mail, and hopefully that will bring some extra clarity regarding these issues.

Steven - I hope you got the answers you needed from Håkan!

//Tom

Steven said...

Hi Håkan/Tom

Making some proper gap filling now. :-)
-The Clone() error has been resolved with the hotfix. (i.e pasting the new dll in bin for my CMS5 R2 SP1 site)

- on the hotfix front -> roleToSynchronize1="*" attribute. I assume that can only be for mem providers of type[type="EPiServer.Common.Web.Authorization.IntegrationMembershipProvider, EPiServer.Common.Web.Authorization"] ?
- Tom I look forward to your blog on access rights. I cant see the difference for MailAdmins and MailEditors access to EpiMail. all the functionality looks the same. I have specifically set the users to their relevant groups.(Admin or Editor. FYI.they share the sql created WebAdmins group to access episerver but that should be ok?)

Thanks

Steven

Steven said...

One thing I never got an answer on was if you guys use the WAT (VStudio Web Admin Tool) or clean out the mem providers and role providers manually and only use specific ones you need? My config area is jammed full with config.

S

Håkan Lindqvist said...

Hi Steven,

You are correct that roleToSynchronize1="*" only applies to the integration membership providers ("EPiServer.Common.Web.Authorization.IntegrationMembershipProvider, EPiServer.Common.Web.Authorization" and "EPiServer.Common.Web.Authorization.Multiplexing.IntegrationMultiplexingMembershipProvider, EPiServer.Common.Web.Authorization.Multiplexing").


Regarding the WAT, I personally edit the web.config manually for these changes.
I have, however, not looked into whether the WAT would work well for this.


/Håkan

Anonymous said...

on the hotfix.. shouldn't the dlls for the hotfix be apllied to the path C:\Program Files\EPiServer\CommonFramework\2.3.350.19\bin and not the site itself? Or Both.
That would mean each new site created will need the common hotfix added to site bin.? will they not include these hotfixes in future installs..?

Tom Stenius said...

Hi Anonymous,
Yes this is correct. If you copy the hotfix to "C:\Program Files\EPiServer..." then the Deployment Center will use these new files for every new deployed site.

Best regards,
Tom Stenius

Efe said...

afternoon Tom
I've migrated some users to the EPiServer common tables. The site uses an external members & roles DB.
However access to the EPiserverCommunity admin section relies on connecting the EPiServer common tables
I have an admin user that cannot access the EPiServerCommunity area if i set the web config to point to the external members & roles DB.

I've tried to implement the IntegrationMultiplexingMembershipProvider
so giving access toboth the episerver & external tables see below:
<roleManager enabled="true" defaultProvider="MultiplexingRoleProvider" cacheRolesInCookie="true">
<providers>
<clear />
<add name="MultiplexingRoleProvider" type="EPiServer.Security.MultiplexingRoleProvider, EPiServer" provider1="SqlServerRoleProvider" provider2="EPiServerCommonRoleProvider" providerMap1="SqlServerMembershipProvider" providerMap2="EPiServerCommonMembershipProvider" />
<add name="WindowsRoleProvider" applicationName="EPiServerSample" type="EPiServer.Security.WindowsRoleProvider, EPiServer" />
<add name="SqlServerRoleProvider" connectionStringName="MembershipDB" applicationName="mazda global" type="System.Web.Security.SqlRoleProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

<add name="EPiServerCommonRoleProvider" applicationName="EPiServerCommonApplication" type="EPiServer.Common.Web.Authorization.RoleProvider, EPiServer.Common.Web.Authorization" />
</providers>
</roleManager>
<membership defaultProvider="MultiplexingMembershipProvider" userIsOnlineTimeWindow="10">
<providers>
<clear />
<add name="MultiplexingMembershipProvider" type="EPiServer.Common.Web.Authorization.Multiplexing.IntegrationMultiplexingMembershipProvider, EPiServer.Common.Web.Authorization.Multiplexing" provider1="SqlServerMembershipProvider" provider2="EPiServerCommonMembershipProvider" roleToSynchronize1="Everyone" roleToSynchronize2="Registered Users" roleToSynchronize3="CommunityMembers" />
<add name="WindowsMembershipProvider" type="EPiServer.Security.WindowsMembershipProvider, EPiServer" deletePrefix="BUILTIN\" searchByEmail="true" />
<add name="SqlServerMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="MembershipDB" requiresQuestionAndAnswer="false" applicationName="global" requiresUniqueEmail="true" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression="" />
<add name="EPiServerCommonMembershipProvider" applicationName="EPiServerCommonApplication" type="EPiServer.Common.Web.Authorization.MembershipProvider, EPiServer.Common.Web.Authorization" />
<add name="EPiServerCommonIntegrationMembershipProvider" applicationName="EPiServerCommonApplication" type="EPiServer.Common.Web.Authorization.IntegrationMembershipProvider, EPiServer.Common.Web.Authorization" provider="EPiServerCommonMembershipProvider" roleToSynchronize1="Everyone" roleToSynchronize2="Registered Users" roleToSynchronize3="CommunityMembers" />
</providers>
</membership>

This should be able to verify the admin user from the episerver common tables and provide access, but it does not.
Is this assertion correct> as the admin user still cannot gain access
thanks in advance
Efe

aMac said...

Hi Tom,

Many thanks for your post, I have managed to get the above working using forms auth in the web.config. However what I would like to do is to get this working using windows auth as I would like to have my users automaticly login using their AD username. The problem that I am having is that the attributeMapUsername="sAMAccountName" has just the username e.g. "arnoldm" however the users windows login contains "DOMAIN\username" My understanding is that these need to match for the user to be able to login. Is there any way I can add the or remove the domain to get this to work?

Many thanks in advance

Arnold

Tom Stenius said...

Hi Arnold,
The Active Directory membership and role providers are known to cause some issues, something that Johan Olofsson fortunately has blogged about earlier.

If you start out with the code provided at:
http://labs.episerver.com/en/Blogs/Johano/Dates/2008/9/Some-ActiveDirectoryRoleProvider-issues/

And use that as a base (that overrides some things in the ActiveDirectory providers to strip domain names from usernames as well as fixing some issues with escaping of characters in role names), and then, in the WinActiveDirectoryMembershipProvider.cs found there replace the GetUser method with something along these lines:

public override MembershipUser GetUser(string username, bool userIsOnline)
{
string domain = "";

int bsIndex = username.IndexOf('\\');
if(bsIndex > 0)
{
domain = username.Substring(0, bsIndex + 1);
username = username.Substring(bsIndex + 1);
}



MembershipUser user = base.GetUser(username, userIsOnline);
return new MembershipUser(user.ProviderName, domain + user.UserName, user.ProviderUserKey, user.Email, user.PasswordQuestion, user.Comment, user.IsApproved, user.IsLockedOut, user.CreationDate, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue, DateTime.MinValue);
}

And then set up your site to use this modified WinActiveDirectoryMembershipProvider as your default membership provider, the special version of the ActiveDirectoryRoleProvider (from the blog post) as the default role provider, and make sure that the “EPiServer.Common.Web.Authorization.IntegrationHttpModule, EPiServer.Common.Web.Authorization” http module is active, it should work - see http://blog.tomstenius.com/2009/09/windows-authentication-in-episerver.html for more details.

What it does different is that it strips the domain from the username in the call to the ActiveDirectory membership provider, but then puts the domain back in the MembershipUser it returns to the system.

This way the usernames in Membership.GetUser().UserName and HttpContext.Current.User.Identity.Name will match, which is currently required for the Integration httpmodule to work.

Best regards,
Tom

Erik said...

Hi Tom! Nice post! :) I have one question. Is it really possible to combine windows and sql providers? I guess you then have to combine windows and forms login? Is it possible to first check for the windows credentials and otherwise present a login page or popup?
Thanks!
Erik Lidälv

Tom Stenius said...

Hi Erik! Thanks!
Sorry, for the late answer but I have moved to Sydney and have had a lot to.

Anyways, I cannot see how you could combine Forms and Windows authentication mode. However you can use both SQL membership/roleprovider and the Windows membership/roleprovider together under Forms authentication.

Hope that answers your question!