Combine WindowsAuthentication with rules stored in DB - c#

I have an ASP.NET MVC5 application that uses WindowsAuhhentication to authenticate the user. Now I need to add a security layer to this application and would like to base this on the standard MVC security model and use the AuthorizeAttribute. This relies on User.IsInRole, but currently this will return the groups that the user belongs to. I do not want to have to store roles as groups in the AD, instead I would like to just have the roles for each user stored in my DB.
So the question is, how do I override the IsInRole method in the WindowsPrincipal, or can I create a CustomPricipal that does what I want?
I have found lots of information on similar topics but most of them seem to reference MVC4 and from what I can gather the entire security model has changed between MVC4 and MVC5. So what is the best way of doing that now?
All help and pointers much appreciated.
Cheers Mike
P.S. And if anyone has any idea how to best incorporate EF, IOC and caching into this then that would be great.

I have found MVC5 Asp.Identity to be really developer friendly compared to the old membership provider... which might be the reason there is a lack of documentation at this time. It is actually intuitive.
If your authentication rules reside in a database EntityFramework will convert stored procedures into Complex Types. After you do that you could create an 'AuthenticationService' service layer and use DI to inject the complex types into Asp.Identity as needed.
To customize Asp.Net Identity all you have to do is add properties to IdentityModels.cs and AccountViewModels.cs, by default Asp.Identity uses the ApplicationDbContext which you have to do absolutely nothing to configure.
In addition, you can access the users information in a similar manner to User.IsInRole.

OK, this is what I have done. I would really like peoples feedback as to best practice and improvements I could make.
I created a new Principal derived from WindowsPrincipal and with an overridden IsInRole Method.
public class QSDPrincipal : WindowsPrincipal
{
private readonly IUserService userService;
public QSDPrincipal(WindowsIdentity ntIdentity,
IUserService userService) :
base(ntIdentity)
{
this.userService = userService;
}
public override bool IsInRole(string role)
{
return userService.CurrentUserIsInRole(role);
}
}
This uses DI to populate the userService object that lives in my BL layer. So I had to configure the container to build this properly.
container.RegisterType<WindowsIdentity>(new HierarchicalLifetimeManager(),
new InjectionFactory(x => (WindowsIdentity)HttpContext.Current.User.Identity));
container.RegisterType<IPrincipal, QSDPrincipal>(new HierarchicalLifetimeManager());
The I use the DependencyResolved to create my new Principal in the PostAuthenticateRequest event.
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
var newUser = (IPrincipal)DependencyResolver.Current.GetService(typeof(IPrincipal));
HttpContext.Current.User = newUser;
}
Then in the UserService itself I implement a method and implement some simple caching so it only makes one DB query per request.
public bool CurrentUserIsInRole(string role)
{
return CurrentUserRoles.Contains(role);
}
private IEnumerable<string> currentUserRoles;
public IEnumerable<string> CurrentUserRoles
{
get
{
if (currentUserRoles == null)
{
var user = GetCurrentUser();
currentUserRoles = new List<string>
{
user.Role.Name
};
}
return currentUserRoles;
}
}
And that is it and it all seems to work.
Thoughts and improvements much appreciated.
Cheers Mike

Related

Authorization in ASP.net5

I am trying to see if there is something "out of the box" in ASP.net5 for authorization for my application needs. I am using a group/permission based approach for authorization. Using Identity3 I am using Role as Group and then I have created permissions from this. Each permission has a resource that it links to and 1 or more values, like:
Resource = Page, Permissions = Add, Update, View, Delete
Another complication is that the groups have dynamic names, and dynamic permissions!!
I have started to read about authorization in ASP.net5 and it seems that I have found something called Policies, which sound good. It seems to force you to use Claims, which is possible if I use a ClaimsTransformer to get all my permissions and add them as claims from the Db. But am I right in thinking that I would have to create a policy for each Permission, on each resource? That seems like a lot of setup.
Is there anything that I do not know about is already built in ASP.net5 that I could use? Like an attribute like this
[Authorize("Page", "Delete")]
Which I could add to the PageController Delete method.
If I have to use some sort of service and DI that into the controller to implement this, then that would be fine as well.
There is a ClaimsPrincipalPermissionAttribute that can fit to your requirements.
Or you can implement your own AuthorizeAttribute.
I use AspNet.Security.OpenIdConnect.Server for authorization. But you can also have a look at OpenIddict
In any case you can add the Authorize attribute to any method you want like this
[Authorize(Roles = "Administrator,SimpleUser,AnOtherRole")]
public void MyMethod() {}
Resource based authorization might fulfill your needs, but I am a little confused with the page being the resource, rather than what the page acts upon.
Taking your Page/Delete combination, I would imagine that rather than the resource being Page, your Page Delete action takes a parameter, indicating the page that is to be deleted? (If this is not the case then this approach isn't going to work of course)
In this case you'd do something like
[Authorize]
public class PageController : Controller
{
IAuthorizationService _authorizationService;
public PageController(IAuthorizationService authorizationService)
{
_authorizationService = authorizationService;
}
public Delete(int pageId)
{
var page = pageRepo.GetPage(pageId);
if (await authorizationService.AuthorizeAsync(User, page, Operations.Delete))
{
return View(page);
}
else
{
return new ChallengeResult();
}
}
}
In order to enable this you're write a handler based on page and an Operations requirement (or any old requirement, but a parameterized operations requirement means you can write a single handler and branch accordingly).
We tried very hard to move away from putting data in the attribute, and move it into requirements, because data in attributes is, to be frank, a maintenance nightmare.
One other thing to note; as handlers are resolved through DI you could inject your user to permissions resolver into the handler, which would avoid using claims transformation.
ASP.NET provides authentication mechanism out of the box which is easy to use, example:
public class HomeController : Controller
{
[Authorize]
public ActionResult Index()
{
ViewBag.Message = "This can be viewed only by authenticated users only";
return View();
}
[Authorize(Roles="admin")]
public ActionResult AdminIndex()
{
ViewBag.Message = "This can be viewed only by users in Admin role only";
return View();
}
}
Check this tutorial
Or if you want more sophisticated mechanism you can implement your own memberhsip provider based on the ASP.NET Membership Provider

Pass identity user to services

We are developing a web application in which the user can register orders, customers, etc. and later review them. We have services that are used by MVC controllers in order to interface with the web UI.
Now we face the problem of multiple users: each service should be provided the currently authorised user Id, so all operations (CRUD and bussiness logic) will only be allowed for that user id. How is it supposed to be passed?
I am thinking about having a parameter passed to my IDataService (base class for services), which is instantiated by the WhateverController, which in turn has access to the User.Identity.GetUserId() method, BUT as I am using an IoC container (Ninject) I don't know how to do that. I guess that IDataService needs a reference to a IUserInfoProvider, so it can call IUserInfoProvider.GetUserId(). Then I can inject somehow an implementation based on Identity and having the current web context information, pretty much in the same way that the Controller must be instantiated.
Question is: how to get that data?
A simpler solution, of course, would be to do it by hand in each Controller constructor, but there should be a more automatic and elegant way to solve this.
EDIT: After some more reasearch, thanks to the answer of Cuong Le, the question I had to ask was, in fact, "how to inject the UserManager from the current context?".
However, in order to decouple my services layer from MVC, I created an IUserInfoProvider, which provides access to the authenticated user data. The implementation based in Identity and the UserManager lies in the Web UI (MVC) project, so it has a IPrincipal as suggested by Cuong Le, and an ApplicationUserManager, all injected using Ninject.
The following interface abstract the user information from Identity and the UserManager.
public interface IUserInfoProvider<T>
{
string GetUserId();
T GetUserData();
}
Here is the implementation in the MVC project using Identity and UserManager.
public class IdentityUserInfoProvider : IUserInfoProvider<DatosEmpresa>
{
private readonly ApplicationUserManager _userManager;
private readonly IPrincipal _user;
public IdentityUserInfoProvider(ApplicationUserManager userManager, IPrincipal user)
{
_userManager = userManager;
_user = user;
}
public string GetUserId()
{
return _user.Identity.GetUserId();
}
public DatosEmpresa GetUserData()
{
return _userManager.FindById(_user.Identity.GetUserId()).DatosEmpresa;
}
}
And the Ninject configuration bit
kernel.Bind<IUserInfoProvider<DatosEmpresa>>().To<IdentityUserInfoProvider>();
kernel.Bind<IPrincipal>()
.ToMethod(ctx => HttpContext.Current.User)
.InRequestScope();
kernel.Bind<ApplicationUserManager>()
.ToMethod(ctx => HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>())
.InRequestScope();
Then I can use an IUserInfoProvider inside any service object and it gets the correct user.
The simple solution is you can put IPrincipal into NInject Container:
kernel.Bind<IPrincipal>().ToMethod(context => HttpContext.Current.User);
So in your ServiceBase you can inject IPrincipal via either property or contructor, like this:
class ServiceBase
{
[Inject]
public IPrincipal User { get; set; }
}
Now you can get information from this property.

Is a Role Provider implementation necessary?

I am struggling a little bit to completely understand what is the correct way to implement a windows authentication and role based authorization scheme into an MVC4 application. When the user accesses the (intranet) website I currently have the following method check the user name against the against a database table
List<string> permissionList =
PermissionBo.GetUserPermissionsList(PermissionBo.ParseUserName(User.Identity.Name));
Permissions permissions = new Permissions(permissionList);
Then the following if state adds a role to a user object:
if (permissions.IsAdmin)
{
if(!Roles.RoleExists("UtilitiesToolAdmin"))
{
Roles.CreateRole("UtilitiesToolAdmin");
}
if(!Roles.IsUserInRole(User.Identity.Name, "UtilitiesToolAdmin"))
{
Roles.AddUsersToRole(new string[] { User.Identity.Name }, "UtilitiesToolAdmin");
}
}
I feel like this may be an incorrect way to go about implementing this, but I am not sure where I am going wrong. Is this sufficient to begin using the authorize attribute like so:
[Authorize(Roles="UtilitiesToolAdmin")]
public static void Foo()
{
return "Bar"
}
If not what am I missing?
If all you are doing is simple role checking, a custom Role Provider might be a bit of an overkill (Role Providers also provide facilities for managing the roles themselves). What you will end up with is a class full of
throw new NotImplementedException();
Instead, consider creating a custom user principal. The IPrincipal interface defines an IsInRole method that returns a bool. This is where you would put your custom role checks. The advantage of the custom user principal is that now all of the built in ASP.NET role-checking goodies should "just work" as long as you replace the default user principal object with your custom one early enough in the lifecycle.
This SO answer has one of the best examples I've seen of using a custom user principal with an MVC application.

AuthorizationContext.Principal is changing from ClaimsPrincipal to GenericPrincipal automatically

I dont know how come the context principle is changing in AuthorisationManager. My code is like
public class AuthorisationManager : ClaimsAuthorizationManager
{
public override bool CheckAccess(AuthorizationContext context)
{
var resource = context.Resource.First().Value;
var action = context.Action.First().Value;
return context.Principal.HasClaim(resource, action);
}
public override void LoadCustomConfiguration(System.Xml.XmlNodeList nodelist)
{
base.LoadCustomConfiguration(nodelist);
}
}
I have list of items in GUI. It works fine first time but when I select second item the context.Principle is chnaged to GenericPrinciple.
Any idea will be helpfull on this.
OK - WPF.
Yeah I vaguely remember that there is some "feature" in WPF around Thread.CurrentPrincipal.
try
Thread.CurrentPrincipal = principal
AppDomain.CurrentDomain.SetThreadPrincipal(principal);
IIRC you maybe have to do that in the App class (ctor?).
"return context.Principal.HasClaim(resource, action);"
Well - typically there is no 1:1 corellation of claims and authorizatin "decisions". Also in typical scenarios claims only hold identity data - something data can be used later to base authorization decisions on. The authorization manager then uses its own data management to make those deicions.
Now since this is a client application (i didn't know that it was WPF) you may do things a little differently. In server applications your approach would scale very well.

Architecture Question: Services

VS2K8, C#. I currently have a solution with the following projects:
Core - POCO domain objects and data interfaces
Data - Repository pattern. Implements the interfaces defined in Core. Also has the mapping classes for Fluent NHibernate.
Infrastructure - Used for dependency injection, configuring nhibernate, etc.
Tests - Tests [tests for Core, Data, etc.]
Web - MVC2 web project
Now, with that being said, I'm trying to determine the best course of action for adding things like: Joining a mailing list, a contact information submission, etc.
I don't believe these should be in web. And I don't think they need to be placed in Data, save for when saving the mailing list information and contact information, fwiw.
It sounds like this should be placed on the Core level. With that said, if placed in Core it would rely on saving to the database. I'm a bit perplexed on where to place this and how to architect it. What route what you guys take?
Is this something as simple as just creating an interface on the Core level called MailingList with a method called JoinMailingList(emailAddress), and then implementing that interface on Data? This doesn't sound like the best route as it's a business concern. Thoughts?
Add a services library and include service interfaces in your core library.
public interface IMailingListService
{
void Subscribe(string email);
void Unsubscribe(string email);
}
public interface IMailingListRepository
{
MailingList LoadMailingList();
void SaveMailingList(MailingList list);
}
public class MailingListService: IMailingListService
{
private IMailingListRepository _repository;
public MailingList(IMailingListRepository repository)
{
_repository = repository;
}
public void Subscribe(string email)
{
var list = _repository.LoadMailingList();
list.Subscribe(email);
_repository.SaveMailingList(list);
}
}

Categories

Resources