MVC custom roleprovider how to hook it up to HttpContext.Current.User.IsInRole("myrole") - asp.net-mvc

I have an MVC app and I wrote a custom roleprovider for it as shown:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using VectorCheck.Models;
namespace VectorCheck.Security
{
public class MyRoleProvider : RoleProvider
{
private VectorCheckRepository<User> _repository { get; set; }
public MyRoleProvider()
{
_repository = new VectorCheckRepository<User>();
}
public MyRoleProvider(VectorCheckRepository<User> repository)
{
_repository = repository;
}
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override string ApplicationName
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override void CreateRole(string roleName)
{
throw new NotImplementedException();
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
throw new NotImplementedException();
}
public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
throw new NotImplementedException();
}
public override string[] GetAllRoles()
{
throw new NotImplementedException();
}
public override string[] GetRolesForUser(string username)
{
var user = _repository.GetUser(username);
return new string[] { user.Role.Name };
}
public override string[] GetUsersInRole(string roleName)
{
throw new NotImplementedException();
}
public override bool IsUserInRole(string username, string roleName)
{
var user = _repository.GetUser(username);
return string.Compare(user.Role.Name, roleName, true) == 0;
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override bool RoleExists(string roleName)
{
throw new NotImplementedException();
}
}
}
This works really well with restricting access to controllers and actions using:
[Authorize(Roles = "Administrator")]
above the controller or action.
I also want restricted access to some things in the view though using:
HttpContext.Current.User.IsInRole("Administrator")
This method isn't part of my roleprovider though so isn't getting overridden.
Does anyone know how to do it for this method as well?

If you've hooked your RoleProvider as the role provider for the application in web.config, then this should work automatically; the framework will create a RolePrincipal for an authenticated user at the start of the request that will call the GetRolesForUser method on your role provider, passing the name from the IIdentity as the user name.
The framework implementation of RolePrincipal's IsInRole(string role) method is something like this (I've added comments)
public bool IsInRole(string role)
{
if (_Identity == null)
throw new ProviderException(SR.GetString(SR.Role_Principal_not_fully_constructed));
if (!_Identity.IsAuthenticated || role == null)
return false;
role = role.Trim();
if (!IsRoleListCached) {
_Roles.Clear();
// here the RoleProvider is used to get the roles for the user
// and are cached in a collection on the RolePrincipal so that
// they are only fetched once per request
string[] roles = Roles.Providers[_ProviderName].GetRolesForUser(Identity.Name);
foreach(string roleTemp in roles)
if (_Roles[roleTemp] == null)
_Roles.Add(roleTemp, String.Empty);
_IsRoleListCached = true;
_CachedListChanged = true;
}
return _Roles[role] != null;
}
Set a breakpoint inside of your RoleProvider GetRolesForUser method to ensure that it is being called correctly and also inspect the IPrincipal (HttpContext.Current.User) to ensure that it is of type RolePrincipal for an authenticated user.

Sorry I am late to the party here;
For the benefit of other people with the same problem - Russ Cam's answer is spot on to finding the answer.
In my case, my custom roleManager did not have 'enabled="true" and cacheRolesInCookie="true". This seemed to stop the GetRolesForUser being called.
Working Code For the web.config:
<roleManager defaultProvider="CustomUserRolesMVCRoleProvider" enabled="true" cacheRolesInCookie="true">
Really Good Tutorial on this topic at http://www.brianlegg.com/post/2011/05/09/Implementing-your-own-RoleProvider-and-MembershipProvider-in-MVC-3.aspx

Related

Trigger authorization validation manually

I've a custom AuthorizeAttribute in my website. It has some logic about the Result created for unathorized requests.
In some cases, I want to trigger its validation manually*. I don't know if its possible. As I haven't found how to do that, I thought that I could extract the logic to get the Result to a diferrent method, and call it when I want. But then I don't know how to execute the ActionResult (outside de controllers).
How can I do to manually execute authorize validation? If not possible, how can I do to execute an ActionResult outside a controller?
*I need to trigger it manually because some request may pass the validation (because the session is created) and then, when accessing my services, found that the session was closed by someone else. I wouldn't like to add a call to the services in OnAuthorization to reduce services calls.
I'm not sure if its the best, but I've found a way to get it working (still listening for better answers).
When I call the services and notice that the work session has expired, all I do is removing the active user in the web session.
My custom authorize attribute also implements IResultFilter and IExceptionFilter.
In both OnResultExecuted and OnException I validate the active user once more. If the session was removed, then apply the same ActionResult that I would apply in OnAuthorization.
Here is the final class:
public class CustomAuthorizeAttribute : AuthorizeAttribute, IResultFilter, IExceptionFilter
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
ActionResult result = Validate(filterContext.HttpContext);
if (result != null)
filterContext.Result = result;
}
public void OnResultExecuted(ResultExecutedContext filterContext)
{
ActionResult result = Validate(filterContext.HttpContext);
if (result != null)
filterContext.Result = result;
}
public void OnResultExecuting(ResultExecutingContext filterContext)
{
}
public void OnException(ExceptionContext filterContext)
{
ActionResult result = Validate(filterContext.HttpContext);
if (result != null)
{
filterContext.Result = result;
filterContext.ExceptionHandled = true;
}
}
public static ActionResult Validate(HttpContextBase httpContext)
{
if (UserActiveInSession)
return null;
// Different rules to build an ActionResult for this specific case.
}
}
I did not get Diego answer's, But Just simply answering the title, I got it to work like that, You can use it as attribute on controllers actions and also trigger it manually at any place in C# or in Razor views.
namespace SomeNameSpace
{
public class CustomAuthorizeAttributeMVC : AuthorizeAttribute
{
private readonly string[] rolesParams;
public CustomAuthorizeAttributeMVC(params string[] roles)
{
this.rolesParams = roles;
}
public bool IsAuthorized { get {
//Do your authorization logic here and return true if the current user has permission/role for the passed "rolesParams"
string[] allowedRoles = new string[] {"role 1", "role 2", "role 3"};
return allowedRoles.Intersect(rolesParams).Any(); //for the example
}
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return this.IsAuthorized;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
//...
}
}
public class AuthorizeHelper
{
public static bool HasPermission(params string[] roles)
{
return new CustomAuthorizeAttributeMVC(roles).IsAuthorized;
}
}
}
Usage example:
[CustomAuthorizeAttributeMVC("role 2")]
public ActionResult SomeAction()
{
return Content("Authorized !");
}
public ActionResult SomeOtherAction()
{
if(AuthorizeHelper.HasPermission("role 2"))
{
return Content("Authorized !");
}
return Content("401 Not Authorized !");
}
And as said, it can be used in Razor views by calling it normally
#if(AuthorizeHelper.HasPermission("role 2")) {
//...
}
Thanks

Intellisense not working but I have binded table values from SQL DB

I have binded two table values from SQL DB but when I tried to use those table values using Entities it’s not showing in Intellisense. I tried a lot but I failed to get those values in Intellisense. Please help me to fix that. Sorry If I’m using any terms wrong. Please see the below two pictures which shows my problem.
Pic 1 : I have binded two tables in skEntities
Pic 2: Intellisense not working
CODE:
Cs file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using Roll_set_MVC.Models;
namespace Roll_set_MVC
{
public class MyRoleProvider : RoleProvider
{
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override string ApplicationName
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override void CreateRole(string roleName)
{
throw new NotImplementedException();
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
throw new NotImplementedException();
}
public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
throw new NotImplementedException();
}
public override string[] GetAllRoles()
{
throw new NotImplementedException();
}
public override string[] GetRolesForUser(int username)
{
using (skEntities objContext = new skEntities())
{
var objUser = objContext.users.FirstOrDefault(x => x.UserID == username);
if (objUser == null)
{
return null;
}
else
{
string[] ret = objUser.Roles.Select(x => x.RoleName).ToArray();
return ret;
}
}
}
public override string[] GetUsersInRole(string roleName)
{
throw new NotImplementedException();
}
public override bool IsUserInRole(string username, string roleName)
{
throw new NotImplementedException();
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override bool RoleExists(string roleName)
{
throw new NotImplementedException();
}
}
}
I fixed my problem with the help of dotnetom. As he said I tried to access the Roles on User object and not on skEntities. Later I changed my code as
public override string[] GetRolesForUser(int username)
{
using (skEntities objContext = new skEntities())
{
var objUser = objContext.users.FirstOrDefault(x => x.UserID == username);
if (objUser == null)
{
return null;
}
else
{
string[] ret = objContext.Roles.Select(x => x.RoleName).ToArray();
return ret;
}
}
}
I changed objContext instead of objUser and It works fine now.

Get Session from HttpActionContext

I'm trying to create a permission attribute to configure in each action of my controllers so this custom attribute should take the sessionId from the user.
My code is like that:
public class PermissionChecker: ActionFilterAttribute
{
private int _permissionId { get; set; }
private IUserSelectorService _userService { get; set; }
public PermissionChecker(int permissionId)
{
_permissionId = permissionId;
_userService = new UserSelectorService();
}
public PermissionChecker(int permissionId, IUserSelectorService userService)
{
_permissionId = permissionId;
_userService = userService;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (_userService.HasPermission(_permissionId, /* here I must pass the session["Id"]*/)){
base.OnActionExecuting(actionContext);
return;
}
throw new HttpException(401, "Unauthorized");
}
}
Use this
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.HttpContext.Session != null)
{
var id = filterContext.HttpContext.Session["Id"];
}
}
EDIT
Given the fact that you're using MVC 4 and you don't have
public override void OnActionExecuting(ActionExecutingContext filterContext)
Try using
System.Web.HttpContext.Current.Session
if you are trying to access using ActionFilterAttribute then OnActionExecting event it wont give the accessibility of HttpContext with System.Web.Http.
Instead of that If you are trying to access using System.Web.Mvc it will provide you the current session with onActionExecting event with help of ActionExecutingContext class.

How to implement IIdentity for a custom User object in ASP.NET MVC?

In my ASP.NET MVC app, I'm trying to create a custom HttpContent.User object. I've started by creating a Member class, which implements IPrincioal.
public class Member : IPrincipal
{
public string Id { get; set; }
public IIdentity Identity { get; set; }
public bool IsInRole(string role) { throw new NotImplementedException(); }
...
}
Then at authentication time I set HttpContext.User to an instance of a Member class:
FormsAuthentication.SetAuthCookie(email, false);
HttpContext.User = member;
Then later I want to check if the user is authenticated, like so:
if (User.Identity.IsAuthenticated) { ... }
That's where I'm stuck. I'm not sure what I need to do for the public IIdentity Identity property on the instance of the Member. So that I can use the HttpContext.User object something like this:
IsAuthenticated = HttpContext.User.Identity.IsAuthenticated;
ViewBag.IsAuthenticated = IsAuthenticated;
if (IsAuthenticated) {
CurrentMember = (Member)HttpContext.User;
ViewBag.CurrentMember = CurrentMember;
}
A Principal is not something you can just set once when writing the auth cookie and forget later. During subsequent requests, the auth cookie is read and the IPrincipal / IIdentity is reconstructed before executing an action method. When that happens, trying to cast the HttpContext.User to your custom Member type will throw an exception.
One option would be to intercept in an ActionFilter, and just wrap the standard implementation.
public class UsesCustomPrincipalAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var systemPrincipal = filterContext.HttpContext.User;
var customPrincipal = new Member(systemPrincipal)
{
Id = "not sure where this comes from",
};
filterContext.HttpContext.User = customPrincipal;
}
}
public class Member : IPrincipal
{
private readonly IPrincipal _systemPrincipal;
public Member(IPrincipal principal)
{
if (principal == null) throw new ArgumentNullException("principal");
_systemPrincipal = principal;
}
public string Id { get; set; }
public IIdentity Identity { get { return _systemPrincipal.Identity; } }
public bool IsInRole(string role)
{
return _systemPrincipal.IsInRole(role);
}
}
This way, you're not losing anything that comes out of the box with the default IPrincipal and IIdentity implementations. You can still invoke IsAuthenticated on the IIdentity, or even IsInRole(string) on the IPrincipal. The only thing you're gaining is the extra Id property on your custom IPrincipal implementation (though I'm not sure where this comes from or why you need it).

Custom Validation for Duplicate UserName in DB

If you have better approach to handle custom Validation please let me know. I don't want service layer for this please.
Read below 5th option what I want.
I have
1 - IUserRepository -> bool IsUserRegistered(string userName);
2 - UserRepository with Method
readonly EFDBContainer _db = new EFDBContainer();
public bool IsUserRegistered(string userName)
{
return _db.Users.Any(d => d.UserName == userName);
}
3 - Ninject --> UserController is DI
public static void RegisterServices(IKernel kernel)
{
kernel.Bind<IUserRepository>().To<UserRepositary>();
}
4 - UserController
private readonly IUserRepository _repository;
public ProfileController(IUserRepository repository)
{
_repository = repository;
}
Create Method on Controller
HttpPost]
public ActionResult Create(string confirmButton, User user)
{
if (ModelState.IsValid)
{
try
{
_repository.Create(user); --> This calling Create Method below before this EnsureValid is Called
return //Do Redirection
}
catch (RuleViolationException)
{
this.UpdateModelStateWithViolations(user, ViewData.ModelState);
}
}
return //to View;
}
Create Method from Repository
public void Create(User user)
{
user.EnsureValid(); --> Go to User object and do validation
//Add object to DB
}
5 - What I want:
Here I want DI so that I can call 1st IsUserRegistered interface method on User object
IsUserRegistered below is not working right now. I need a way to use the Interface
public partial class User: IRuleEntity
{
public List<RuleViolation> GetRuleViolations()
{
List<RuleViolation> validationIssues = new List<RuleViolation>();
if (IsUserRegistered(userName))
validationIssues.Add(new RuleViolation("UserName", UserName, "Username already exists. Please enter a different user name."));
return validationIssues;
}
public void EnsureValid()
{
List<RuleViolation> issues = GetRuleViolations();
if (issues.Count != 0)
throw new RuleViolationException("Business Rule Violations", issues);
}
}
Write your own validation attribute and add it to the user name.
See http://www.planetgeek.ch/2010/11/13/official-ninject-mvc-extension-gets-support-for-mvc3/. It explains how to inject dependencies into validators.
See also the sample application that comes with the Ninject MVC extension it has an example of a validator that has a dependency. https://github.com/ninject/ninject.web.mvc

Resources