Here is the basic outline of what I'm doing.
I use the "username" to get the IPrincipal.
I use the MemoryCache/ObjectCache to I'm only hitting the database, every 60 minutes. If you need it "per login" and not "per user".. (if your principal definition changes often or you need to code for the possibility, just change the cache-key to something that is username AND session based.
Note, I cannot stand using "IsInRole" in any app that isn't your kid's soccer club. (I don't have any kids, its a metaphor).
namespace MyProduct.MyApplication.WebServices.Controllers
{
/// <summary>
/// Performs operations with "Item" list
/// </summary>
public class MyItemController : BaseApiController
{
[Authorize]
public IEnumerable<Item> Get()
{
string username = User.Identity.Name;
IPrincipalCache cache = new PrincipalCache(); /* use injection - Unity, this hard coding for demo purposes */
MyCustomPrincipal princ = cache.GetMyCustomPrincipal(username);
if ( ! princ.HasRight("USER_CAN_GET_ITEMS")) /* Note my MyCustomPrincipal has this method...out of the "box" you'll have "IsInRole('MyRoleName') */
{
return null;
}
return itemsService.GetItems(); }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Caching;
using System.Web;
using MyProduct.MyApplication.Infrastructure.Security;
namespace MyProduct.MyApplication.WebServices.Security.Caching
{
public interface IPrincipalCache
{
MyCustomPrincipal GetMyCustomPrincipal(string userName);
}
public class PrincipalCache : IPrincipalCache
{
public MyCustomPrincipal GetMyCustomPrincipal(string userName)
{
string cacheKey = "MyCustomPrincipalCacheKey" + userName;
MyCustomPrincipal cachedOrFreshPrincipal = GetFromCache<MyCustomPrincipal>(cacheKey, () =>
{
return new MyCustomPrincipal(); /* Go to the method/datalayer/ and hydrate a MyCustomPrincipal */
});
return cachedOrFreshPrincipal;
}
private TEntity GetFromCache<TEntity>(string key, Func<TEntity> valueFactory) where TEntity : class
{
ObjectCache cache = MemoryCache.Default;
var newValue = new Lazy<TEntity>(valueFactory);
CacheItemPolicy policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(60) }; /* Most people will do stuff for less than one hour I guess */
//The line below returns existing item or adds the new value if it doesn't exist
var value = cache.AddOrGetExisting(key, newValue, policy) as Lazy<TEntity>;
return (value ?? newValue).Value; // Lazy<T> handles the locking itself
}
}
}