If you go into admin in EPiServer and then "Set Access Rights for", you will fail to choose your Identity 2.0 roles. Reason for this is that the Access Rights GUI in EPiServer are closely connected to the old membership provider, which you disable when you implement the ASP.NET Identity.
Edit:
As Per suggested in the comment field, it's also possible to to implement this using built in provider instead of using StructureMap. So by add the following code in web.config and add the IdentitySecurityEntityProvider.cs to the project it will also work. Thanks :-)
<episerver.framework>
<securityEntity>
<providers>
<add name="IdentityProvider" type="TestSite.Business.AccessRights.IdentitySecurityEntityProvider, TestSite" />
</providers>
</securityEntity>
<appData basePath="App_Data" />
<scanAssembly forceBinFolderScan="true" />
<virtualRoles addClaims="true">
<providers>
Solution
First you need to setup the DI/IOC to override the “SecurityEntityProvider” and replace it with “IdentitySecurityEntityProvider” class. The setup below show how to do that.
ServiceLocationConfiguration.cs
using System;
using EPiServer.Security;
using StructureMap;
using TestSite.Business.AccessRights;
namespace TestSite.Business.ServiceLocation
{
public static class ServiceLocationConfiguration
{
public static Action<ConfigurationExpression> Current
{
get
{
return configuration =>
{
configuration.For<SecurityEntityProvider>().Use(() => new IdentitySecurityEntityProvider());
};
}
}
}
}
DependenciesInitializationModule.cs
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.ServiceLocation;
namespace TestSite.Business.ServiceLocation
{
[InitializableModule]
[ModuleDependency(typeof(ServiceContainerInitialization))]
public class DependenciesInitializationModule : IConfigurableModule
{
public void Initialize(InitializationEngine context)
{
}
public void Preload(string[] parameters) { }
public void Uninitialize(InitializationEngine context)
{
//Add uninitialization logic
}
public void ConfigureContainer(ServiceConfigurationContext context)
{
context.Container.Configure(ServiceLocationConfiguration.Current);
}
}
}
Disclaimer: The code isn't tested on large database with a lot of users and groups.
IdentitySecurityEntityProvider.cs
using System;
using System.Collections.Generic;
using System.Linq;
using EPiServer.Security;
using TestSite.Models.Account;
namespace TestSite.Business.AccessRights
{
public class IdentitySecurityEntityProvider : PagingSupportingSecurityEntityProvider
{
private readonly ApplicationDbContext _db = new ApplicationDbContext();
public override IEnumerable<string> GetRolesForUser(string userName)
{
var theUser = _db.Users.FirstOrDefault(u => u.UserName.Equals(userName, StringComparison.CurrentCultureIgnoreCase));
return theUser == null ? Enumerable.Empty<string>() : theUser.Roles.Select(role => _db.Roles.Find(role.RoleId).Name.ToString()).AsEnumerable();
}
public override IEnumerable<SecurityEntity> Search(string partOfValue, string claimType)
{
int totalCount;
return this.Search(partOfValue, claimType, 0, int.MaxValue, out totalCount);
}
public override IEnumerable<SecurityEntity> Search(string partOfValue, string claimType, int startIndex, int maxRows, out int totalCount)
{
totalCount = 0;
switch (claimType)
{
case "http://schemas.microsoft.com/ws/2008/06/identity/claims/role":
return this.SearchRoles(partOfValue, startIndex, maxRows, out totalCount);
case "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name":
case "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier":
return this.SearchUsers(partOfValue, startIndex, maxRows, out totalCount);
case "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress":
if (!string.IsNullOrEmpty(partOfValue))
return this.SearchEmails(partOfValue, startIndex, maxRows, out totalCount);
else
return Enumerable.Empty<SecurityEntity>();
default:
return Enumerable.Empty<SecurityEntity>();
}
}
private IEnumerable<SecurityEntity> SearchEmails(string partOfName, int startIndex, int maxRows, out int totalCount)
{
var usersList = _db.Users.ToList();
var users = usersList.Where(a => a.Email != null && a.Email.ToLower().Contains(partOfName.ToLower())).ToList();
var entityList = users.Select(user => new SecurityEntity(user.Email)).ToList();
totalCount = entityList.Count;
return entityList;
}
private IEnumerable<SecurityEntity> SearchUsers(string partOfName, int startIndex, int maxRows, out int totalCount)
{
totalCount = 0;
if (_db.Users == null) return Enumerable.Empty<SecurityEntity>();
var usersList = _db.Users.ToList();
var entityList = !string.IsNullOrEmpty(partOfName) ? usersList.Where(a => a.UserName.ToLower().Contains(partOfName.ToLower())).ToList().Select(user => new SecurityEntity(user.UserName)).ToList() : usersList.Select(user => new SecurityEntity(user.UserName)).ToList();
totalCount = entityList.Count;
return entityList;
}
private IEnumerable<SecurityEntity> SearchRoles(string partOfName, int startIndex, int maxRows, out int totalCount)
{
totalCount = 0;
if (_db.Roles == null) return Enumerable.Empty<SecurityEntity>();
var rolesList = _db.Roles.ToList();
var roles = rolesList.Where(a => a.Name.ToLower().Contains(partOfName.ToLower())).ToList();
var entityList = roles.Select(role => new SecurityEntity(role.Name)).ToList();
totalCount = entityList.Count;
return entityList;
}
}
}
<location path="EPiServer/CMS/admin">
<system.web>
<authorization>
<allow roles="WebAdmins, Administrators, CustomGroup" />
<deny users="*" />
</authorization>
</system.web>
</location>
Then you
can click add “Users/ group button” in the “Set Access rights” module. Choose
users or group and then add it with the green arrows and click OK.
Just what we are looking into. I love the EPiServer community sometimes. :)
ReplyDeleteThanks for sharing. For reference there are 2 built in providers for the Access Rights UI, one is for the membership system and the other allows you to synchronize any identity which is what is used in federated authentication.You read more about that provider here: http://world.episerver.com/documentation/Items/Developers-Guide/EPiServer-CMS/9/Security/federated-security/
ReplyDeleteThanks, I'll look into it.
DeleteAlso love that EPiServer responds to blogs.. :)
ReplyDeleteAm confused about the solution. Shouldn't you have asp.net identity with a custom member ship provider so that all the existing functionalities provided by episerver be reused easily?
ReplyDeleteWhen you introduce asp.net identity into your solution you will disable the membership provider. After Episerver added support for OWIN, you can handle security differently in Episerver. That's what these blog posts are about. Read yourself up on OWIN -> https://coding.abel.nu/2014/06/asp-net-identity-and-owin-overview/
Delete