How can I Implement a Custom RoleProvider? - asp.net-mvc

I'm trying to implement a custom RoleProvider in my ASP.NET MVC application.
I've created a custom MembershipProvider and it works, in that I'm able to successfully validate the user. The next step is to implement the RoleProvider to restrict access to certian Controllers to Admin users only.
Can anyone provide me with a quick outline of the steps I need to take?
The point that I'm at now is I have my controller with the Authorize filter, like so:
[Authorize(Roles="Admin")]
public class AdminOnlyController : Controller
{
// stuff
}
and I have my CustomRoleProvider class, with the following method along with a load of not-implemented Methods:
public override string[] GetRolesForUser(string username)
{
if (username == "dave")
{
return new string[] { "Admin" };
}
}
I think I need to add the user to the Role somehow but I don't know how to do that. Ideally the end result would be a scenario where unauthorized users can't access certain controllers, and I in my Views I could determine whether to show links with something like:
if (User.IsInRole("Admin"))
{
// show links to Admin Controllers
}
Can anyone point me in the right direction?

I used this as as base line for a custom role manager: http://davidhayden.com/blog/dave/archive/2007/10/17/CreateCustomRoleProviderASPNETRolePermissionsSecurity.aspx
Should work in MVC or Web Forms.
UPDATE: Since that web page no longer exists, you could try this one instead. The basic requirements are that you need to implement the RoleProvider interface, namely:
void AddUsersToRoles(string[] usernames, string[] roleNames)
string[] GetRolesForUser(string id)
bool RoleExists(string roleName)

For the not-implemented methods, be sure to throw a NotImplementedException. This should help you figure out which methods are needed in your custom provider to get the job done.
I suspect you'll have to implement IsUserInRole.

Related

Add roles to ADFS IPrincipal

I have been looking for answer to this question for a few days now, but I have not found any success. I would post the links, but it would probably take up the entire page.
So here is what I have...
I have an MVC application, which uses the WC-Federation protocol. I have been able to configure the application, so that it authenticates the users, and returns the claims from ADFS. This works perfect. I can also extract all the claims with no issues. But I am doing this within one of the actions in the controller.
And here is what I want to do...
I want to use ADFS to authenticate the user, but I want to use my own internal roles to authorize the user to have access to specific controllers (e.g. [Authorize(Roles = "CoolRole")]). I want to be able to do this, because I already have a Web API that uses OAuth 2.0, with a backend SQL Server database to manage users and roles (internal and external user.) I now want a secure portal that will allow internal users to access the data with a single-sign-on experience. Looking at the Controller model, I noticed there are some properties associated with the authentication process (OnAuthentication, OnAuthenticationChallenge) and one for the authorization process (OnAuthorization.)
I don't necessarily need the code, but I feel like I've hit a brick all, and I need to be pointed in the right direction.
UPDATE
I tried this:
protected override void OnAuthorization(
System.Web.Mvc.AuthorizationContext filterContext)
{
//Private class to create a new IPrincipal based on my AppUserMgr
var user = _setCurrentUser(
(ClaimsIdentity)filterContext.HttpContext.User.Identity);
filterContext.HttpContext.User = user;
base.OnAuthorization(filterContext);
}
This returned a 401 (Unauthorized) response.
and...
protected override void OnAuthentication(
System.Web.Mvc.Filters.AuthenticationContext filterContext)
{
//Private class to create a new IPrincipal based on my AppUserMgr
var user = _setCurrentUser(
(ClaimsIdentity)filterContext.HttpContext.User.Identity);
filterContext.Principal = user;
base.OnAuthorization(filterContext);
}
This just calls the STS numerous times, before it fails. I even tried swapping after the assignment to after the base is called in both. No luck.
Prior to the previous ones, I also tried to add an AuthorizeFilter to the control, but that didn't help:
http://pratapreddypilaka.blogspot.in/2012/03/custom-filters-in-mvc-authorization.html
I found this link: http://brockallen.com/2013/01/17/adding-custom-roles-to-windows-roles-in-asp-net-using-claims/
From there, I guessed my way through
Here is the basics of what I did:
I ended up overriding the OnAuthentication method of the Controller, but still made sure to call the base. I did this from within an extended class. Here is the concept:
public class AdfsController : Controller
{
//Some code for adding the AppUserManager (used Unity)
protected override void OnAuthentication(
System.Web.Mvc.Filters.AuthenticationContext filterContext)
{
base.OnAuthentication(filterContext);
//Private method to set the Principal
_setCurrentUser(filterContext.Principal);
}
private void _setCurrentUser(IPrincipal principal)
{
//Put code to find to use your ApplicationUserManager or
//dbContext. roles is a string array
foreach(var role in roles)
{
((ClaimsIdentity)((ClaimsPrincipal)principal).Identity)
.AddClaim(new Claim(ClaimTypes.Role, role));
}
}
}
In the Controller, you can now add the follow:
public class HomeController : AdfsController
{
//I used a magic string for demo, but store these in my globals class
[Authorize(Roles = "CoolRole")]
public ActionResult Index()
{
return View();
}
}
I tested this by checking a role assigned to the current user, and that worked! Then I changed the role to something like "reject", which the user was not assigned; and I received a 401 Unauthorized.
ADFS is the authentication/token service in Azure. to enable the Roles Based Authentication, you can use Azure RBAC (Role Based Access Controll) service to basically Augment the claims that you get back from the ADFS and add the roles that you get back from RBAC to the token, and use the same token in your API so lock down or secure the backend with that augmented token...
here is the reference for RBAC:
http://azure.microsoft.com/en-in/documentation/articles/role-based-access-control-configure/

MVC Windows authentication - authorise(permissions) for entire site

I've seen that the AuthoriseAttribute can work for each individual controller, what i would like to do is set the entire site permissions to one AD group, is that easy to do or should i just copy and paste the authattrib line to each controller?
Thanks
As #asawyer mentioned using global filter for your case is good practise. For another part of your question in comment: in the global filter where do i specify what AD groups are allowed to use the site? you can specify roles in OnAuthorization method of your custom authorize attribute, smth like:
public class MyAuthAttribute: AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
Roles = 'ad role1, ad role2...'; //Roles is AuthorizeAttribute member
base.OnAuthorization(filterContext);
}
}
and than use it like:
GlobalFilterCollection.Add(new MyAuthAttribute());
in global.asax or w/e else

What is Webform's "UrlAuthorizationModule.CheckUrlAccessForPrincipal" equivalent for MVC?

I got a problem as i am writing a custom SSO solution for my company. To mkae it simple, i've made a custom authentication httpmodule that intercepts all requests so as to check user authentication state. If not authenticated, user is redirected to my custom sso login page.
The thing is, when user is not authenticated, i'd like to check if he can access the requested page/resource... With Webforms, no problem, i add an authorization block in web.config, and i use UrlAuthorizationModule.CheckUrlAccessForPrincipal with an anonymous user. Everything works fine...
But when i apply my module to an MVC (3) web site, this does not work anymore (for obvious reasons, like the possibility to access the same controller and/or action from differents urls when using routing, and because authorizations are made through controller attributes).
How can I achieve this ?? I've been searching all day long, didn't find anything about that :/
ASP.NET MVC 3 Internet Application template includes a basic AccountController which implements the following actions (along with the associated models and views):
LogOn
Register
ChangePassword / ChangePasswordSuccess
You simply need the [Authorize] attribute on the Actions or classes you wish to secure. But if you need something really custom you can do something like I've done.
I created a custom class to override security in my application.
public class AuthorizeActivityAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
HttpContext currentContext = HttpContext.Current;
//Do your custom authentication stuff here and return true or false depending on results
Return true;
}
}
And now in my Controller I have the following:
[AuthorizeActivity]
public ActionResult Index()
{
ViewBag.Message = "Welcome";
return View();
}
I had the same problem.
See solution here: MVC equivalent of Webforms "UrlAuthorizationModule.CheckUrlAccessForPrincipal"
You would have to read the information from the other controller. This
can be done by instantiating its context and the Descriptor, then
instantiating the AuthorizationContext for that controller and read
the filter info.

Good approach for globally accessing the Logged-in User

I want to be able to globally access the logged-in user in (Controllers, HtmlHelpers and other helper classes) throughout the whole application without getting exceptions that User is null and with the ability to handle guests (not-logged-in users).
Should this be handled within my UsersRepository? If so, how to? or is there a better approach?
You can create custom identity and principal classes. Your custom identity class can extend whatever you are currently using and can then simply add the extra information you need.
Then in a global action filter, simply overwrite the current principal with your customized one. Then you can access it from anywhere like a normal identity, but if you need your additional information, you simply cast it to your custom identity class. Which will grant you access to your additional information.
You can write a custom action filter that is executed on every request (you register it as a global filter). This filter would load the user (from the user´s repository for example) and put it the http context for example or in the ViewData.
EDIT:
Ok, the code for the filter could look like this (in this case, it loads the user to the ViewData collection). I didn´t consider anonymous users here.
public class LoadUserToViewDataAttribute : ActionFilterAttribute
{
private IUserRepository _userRepository;
public LoadUserToViewDataAttribute(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller;
var userName = filterContext.HttpContext.User.Identity.Name;
var user = _repository.GetUser(userName);
controller.ViewData.Add("CurrentUser", user);
}
}

ASP.NET MVC 1.0 Authentication

I realise that I can prevent unauthenticated users from accessing views at controller level by applying the [Authorize] attribute and can also filter views down to individual users or roles using this. However, my question is regarding doing the opposite... Is there a way to deny authenticated users from certain views without having to manually add in checks to see if they're authenticated in the opening lines of the controller code? Ideally an [Unauthorized] attribute or an equivalent if such a thing exists?
The reason for this is that I don't want authenticated users to be able to visit the account creation pages of the site I'm working on, as well as other resources. I realise I could check them in the controller explicitly but I'd prefer to decorate the controller methods if at all possible.
Thanks :)
This is along the lines of what LukLed was referring to:
public class UnAuthorizedAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
bool excludeCondition = false;
if (excludeCondition)
filterContext.Result = new HttpUnauthorizedResult();
else
base.OnAuthorization(filterContext);
}
}
Simply put in the logic for your excludeCondition. You can also to choose to do things like redirect to other views. Just mark your code with [UnAuthorized]
You can write your own authorization filter. Inherit from FilterAttribute and implement IAuthorizationFilter. Call it UnauthorizedAttibute and you will be able to use it like [Authorize].
Hear You can read about filters:
http://www.asp.net/LEARN/mvc/tutorial-14-cs.aspx
A simple way to accomplish this? Just leave the action untagged, and start with:
If(Request.IsAuthenticated)
// redirect somewhere, or return another view...
this could also be accomplished fairly simply if you are already using a roleprovider. then your actions would just need to be filtered by the appropriate role:
[Authorize(Roles = "Admin, Editor")]

Resources