asp.net mvc, multiple CustomAuthorize Role Providers - asp.net-mvc

I am trying to lock my application down to each method where by users and admin can see the whole controller but only administrators can create, delete, and edit.
I have created a CustomAuthorize class which holds the method:
public class CustomAuthorize : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
Debug.WriteLine("Show me the filterContext " + filterContext);
if (filterContext.Result is HttpUnauthorizedResult)
{
Debug.WriteLine("Why is this going to the redirect to AccessDenied?");
filterContext.Result = new RedirectResult("~/AccessDenied/Index");
}
}
}
My RoleProvider Class:
namespace Office.WebUI.Security {
public class OfficeRoleProvider : 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(string username)
{
Debug.WriteLine("Get the username" + username);
using (EFDbContext db = new EFDbContext()) >
{
User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, >stringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase));
//User user = db.TaskPrivilege.FirstOrDefault(u => >u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase));
if (user != null)
{
var roles = from ur in user.UserRoles
from r in db.Roles
where user.role.RoleID == r.RoleID
//where ur.RoleID == r.RoleID
select r.Name;
if (roles != null)
return roles.ToArray();
else
return new string[] { }; ;
}
else
{
return new string[] { }; ;
}
}
}
public override string[] GetUsersInRole(string roleName)
{
throw new NotImplementedException();
}
public override bool IsUserInRole(string username, string roleName)
{
Debug.WriteLine("The username is " + username + " And the Role it is using is >" + roleName);
using (EFDbContext db = new EFDbContext())
{
User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase));
if (user != null)
{
var roles = from ur in user.UserRoles
from r in db.Roles
where user.role.RoleID == r.RoleID
//where ur.RoleID == r.RoleID
select r.Name;
if (user != null)
return roles.Any(r => r.Equals(roleName, >StringComparison.CurrentCultureIgnoreCase));
else
return false;
}
else
{
return false;
}
}
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override bool RoleExists(string roleName)
{
throw new NotImplementedException();
}
}
}
In my Database I have a Roles table that holds Role and RoleID as columns, with 2 values Administrator and User as roles with a roleID as the PK, 1 = Administrator & 2 = User.
Another table called Users, that contains UserID, Username and role_RoleID
When I assign a user to role_RoleID as 1 (administrator) and lock down a controller using
[CustomAuthorize(Roles = "Administrator"]
This works.
When I assign a role_RoldID = 2 and lock down using:
[CustomAuthorize(Roles = "User"]
This doesn't work?
Also another question is, how do I assign 2 roles to a controller?, I then want to lock down each method to 1 User Role.
Hope that makes sense and I hope someone can help me please.
Thanks
Steven
Basically I have a controller which I have put
[CustomAuthorize(Roles = "Administrators, Support")]
to lock it down so both roles can see the controller.
Then I have create/delete and edit methods I have:
[CustomAuthorize(Roles = "Administrators")]
so basically I want to show the whole controller to both roles but only administrators can create/edit/delete.
When I assign a roleID in the user table = 2(User) the whole application breaks for that user. Assigning RoldID = 1 (Administators) it works for all the people that is assigned roldID 1.
Currently I have the methods
public override string[] GetRolesForUser(string username)
{
using (EFDbContext db = new EFDbContext())
{
User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase) || u.Email.Equals(username, StringComparison.CurrentCultureIgnoreCase));
if (user != null)
{
var roles = from ur in user.UserRoles
from r in db.Roles
where user.role.RoleID == r.RoleID
select r.Name;
if (roles != null)
return roles.ToArray();
else
return new string[] { }; ;
}
else
{
return new string[] { }; ;
}
}
}
> public override bool IsUserInRole(string username, string roleName)
> {
>
> using (EFDbContext db = new EFDbContext())
> {
> User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase)
> || u.Email.Equals(username,
> StringComparison.CurrentCultureIgnoreCase));
>
> if (user != null)
> {
> var roles = from ur in user.UserRoles
> from r in db.Roles
> where user.role.RoleID == r.RoleID
> //where ur.RoleID == r.RoleID
> select r.Name;
> if (user != null)
> return roles.Any(r => r.Equals(roleName, >StringComparison.CurrentCultureIgnoreCase));
> else
> return false;
> }
> else
> {
> return false;
> }
> }
> }
I know I have to change the IsUserInRole method so it handles a array parameter like so, public override bool IsUserInRole(string username, string[] roleName)
But don't know how to handle that in the linq and mainly in the IsUserInRole method:
return roles.Any(r => r.Equals(roleName, >StringComparison.CurrentCultureIgnoreCase));
Currently it is failing at the GetRolesForUser method, at the where clause i believe,
where user.role.RoleID == r.RoleID
the error message is displayed below:
There is already an open DataReader associated with this Command which must be closed first.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.
Source Error:
Line 66: var roles = from ur in user.UserRoles Line 67: from r in db.Roles Line 68: where user.role.RoleID == r.RoleID
Thanks

Multiple roles can be done as such:
[CustomAuthorize(Roles = "Administrator, User")]
But seeing you've written this code I'd think you'd know this already?
If all you want to do is keeps Users from getting in your create/edit/delete functions, why not use the default authorisation functions? meaning, put [CustomAuthorize(Roles = "Administrator")] above edit/create/delete and [CustomAuthorize(Roles = "Administrator, User")] above anything else...
I'm guessing the IsUserInRole gets called on authorisation? This method should take a string[] roleName array, and not just a string.
Edit:
change
var roles = from ur in user.UserRoles
from r in db.Roles
where user.role.RoleID == r.RoleID
//where ur.RoleID == r.RoleID
select r.Name;
to
var roles = user.UserRoles.select(q=>q.Roles.Name);

Related

ASP.NET MVC creating context instance inside ActionFilter

The system is built in ASP.NET MVC and has a number of ActionFilters that deal with various features such as if the current logged on user has permission to view a page or if the system is in maintenance mode etc. All of these ActionFilters inherit from a base ActionFilter that has a method for retrieving the current logged on user, it cannot persist the user as their permissions may get changed while they're logged in. Therefore, the DB has to be called the retrieve the logged on user.
Previously the code looked like this:
public class BaseRedirectingAction : ActionFilterAttribute
{
private readonly IUserAuthenticationRepository _userAuthenticationRepository = new UserAuthenticationRepository();
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
}
internal void Redirect(ActionExecutingContext context, RouteValueDictionary keyValues)
{
context.Result = new RedirectToRouteResult(keyValues);
context.Result.ExecuteResult(context.Controller.ControllerContext);
}
internal User GetCurrentUser()
{
if (HttpContext.Current.User.Identity is ClaimsIdentity identity)
{
var claims = identity.Claims;
var userIdClaim = claims.FirstOrDefault(x => x.Type.ToLower() == "userid");
var userAuthenticationIdClaim = claims.FirstOrDefault(x => x.Type.ToLower() == "userauthenticationid");
if (userIdClaim != null)
{
var userAuthenticationId = int.Parse(userAuthenticationIdClaim.Value);
// Declared repository caching value so doesn't pick up updates
var userAuthentication = _userAuthenticationRepository.FindOne(x => x.Id == userAuthenticationId);
var currentLoggedInUser = userIdClaim != null ? userAuthentication.Users.FirstOrDefault(x => x.Id == int.Parse(userIdClaim.Value)) : userAuthentication.Users.FirstOrDefault(x => x.DefaultAccount);
return currentLoggedInUser;
}
}
return null;
}
}
Due to the DB request being cached it had to be changed to:
public class BaseRedirectingAction : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
}
internal void Redirect(ActionExecutingContext context, RouteValueDictionary keyValues)
{
context.Result = new RedirectToRouteResult(keyValues);
context.Result.ExecuteResult(context.Controller.ControllerContext);
}
internal User GetCurrentUser()
{
if (HttpContext.Current.User.Identity is ClaimsIdentity identity)
{
var claims = identity.Claims;
var userIdClaim = claims.FirstOrDefault(x => x.Type.ToLower() == "userid");
var userAuthenticationIdClaim = claims.FirstOrDefault(x => x.Type.ToLower() == "userauthenticationid");
if (userIdClaim != null)
{
var userAuthenticationId = int.Parse(userAuthenticationIdClaim.Value);
// Locally created doesn't cause cached result
var userAuthenticationRepository = new UserAuthenticationRepository();
var userAuthentication = userAuthenticationRepository.FindOne(x => x.Id == userAuthenticationId);
var currentLoggedInUser = userIdClaim != null ? userAuthentication.Users.FirstOrDefault(x => x.Id == int.Parse(userIdClaim.Value)) : userAuthentication.Users.FirstOrDefault(x => x.DefaultAccount);
return currentLoggedInUser;
}
}
return null;
}
}
Will instantiating a new repository each time cause memory leaks or other issues?

Authorize as Roles = "Admin" during login

First of all i am new to MVC user authentication system. Code bellow is working fine for authenticate normal users but i wanted to log all user as per under MVC role based system. So admin user can only see admin controller and normal user cant see admin controller. I already made it on my admin controller i have added "[Authorize(Roles = "Admin")]" and i am also redirecting correctly to specific controller during login filter inside login controller. Now my issue is: How can i tell MVC "[Authorize(Roles = "Admin")]" is only accessed who has admin role? I mean how can i assign a user as admin from my login controller bellow? Ask any question if may have
Administrator Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Blexz.Controllers
{
[Authorize(Roles = "Admin")]
public class AdministratorController : Controller
{
// GET: Administrator
public ActionResult Index()
{
return View();
}
}
}
Login Controller:
//Login post
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(UserLogin login, string ReturnUrl="")
{
string Message = "";
using (BlexzWebDbEntities db = new BlexzWebDbEntities())
{
var v = db.Users.Where(x => x.Email == login.Email && x.IsEmailVerified == true).FirstOrDefault();
int RoleId = db.Users.Where(x => x.Email == login.Email).Select(x => x.RoleId).FirstOrDefault();
string RoleTypeName = db.Roles.Where(x => x.RoleId == RoleId).Select(x => x.RoleType).FirstOrDefault();
if (v != null)
{
if (string.Compare(Crypto.Hash(login.Password), v.PasswordHash) == 0)
{
int timeOut = login.RememberMe ? 43800 : 100; // 43800 == 1 month
var ticket = new FormsAuthenticationTicket(login.Email, login.RememberMe, timeOut);
string encrypted = FormsAuthentication.Encrypt(ticket);
var cookie = new System.Web.HttpCookie(FormsAuthentication.FormsCookieName, encrypted);
cookie.Expires = DateTime.Now.AddMinutes(timeOut);
cookie.HttpOnly = true;
Response.Cookies.Add(cookie);
if (Url.IsLocalUrl(ReturnUrl))
{
return Redirect(ReturnUrl);
}
else if (RoleTypeName == "Admin")
{
return RedirectToAction("Index", "Administrator");
}
else
{
return RedirectToAction("User", "Home");
}
}
else
{
Message = "Invalid Credential Provided";
}
}
else
{
Message = "Invalid Credential Provided";
}
}
ViewBag.Message = Message;
return View();
}
Remove FirstOrDefault from RoleTypeName selection and change it as
string[] RoleTypeName = db.Roles.Where(x => x.RoleId == RoleId).Select(x => x.RoleType);
and change the checking as
if (Url.IsLocalUrl(ReturnUrl))
{
return Redirect(ReturnUrl);
}
else if (RoleTypeName.Contains("Admin"))
{
return RedirectToAction("Index", "Administrator");
}
else
{
return RedirectToAction("User", "Home");
}
Change your ticket as shown below
var ticket = new FormsAuthenticationTicket(
version: 1,
name: UserName,
issueDate: DateTime.Now,
expiration: DateTime.Now.AddSeconds(httpContext.Session.Timeout),
isPersistent: false,
userData: String.Join(",", RoleTypeName));
and After that in global.asax you would do something like this:
public override void Init()
{
base.AuthenticateRequest += OnAuthenticateRequest;
}
private void OnAuthenticateRequest(object sender, EventArgs eventArgs)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
var cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
var decodedTicket = FormsAuthentication.Decrypt(cookie.Value);
var roles = decodedTicket.UserData.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries);
var principal = new GenericPrincipal(HttpContext.Current.User.Identity, roles);
HttpContext.Current.User = principal;
}
}

How to use Linq to Entities in ApplicationOAuthProvider asp.net web api2

this code return error does not support linq to entities, please help me
error return is
LINQ to Entities does not recognize the method 'System.String get_Item(System.String)' method, and this method cannot be translated into a store expression.
problem in this line
ApplicationUser user = dbContext.Users.Where(s => s.UserName == context.UserName && s.Password == context.Password && s.CodeID == data["CodeID"]);
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
private readonly string _publicClientId;
public ApplicationOAuthProvider(string publicClientId)
{
if (publicClientId == null)
{
throw new ArgumentNullException("publicClientId");
}
_publicClientId = publicClientId;
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var data = await context.Request.ReadFormAsync();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
ApplicationDbContext dbContext = new ApplicationDbContext();
//here problem
//does not support linq to entities
ApplicationUser user = dbContext.Users.Where(s => s.UserName == context.UserName && s.Password == context.Password && s.CodeID == data["CodeID"]);
//here problem
if (user == null)
{
context.SetError("invalid_grant", Resources.Resources.Invalid_UsernamePassword);
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager,
OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager,
CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Resource owner password credentials does not provide a client ID.
if (context.ClientId == null)
{
context.Validated();
}
return Task.FromResult<object>(null);
}
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == _publicClientId)
{
Uri expectedRootUri = new Uri(context.Request.Uri, "/");
if (expectedRootUri.AbsoluteUri == context.RedirectUri)
{
context.Validated();
}
}
return Task.FromResult<object>(null);
}
public static AuthenticationProperties CreateProperties(string Username)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "Username", Username }
};
return new AuthenticationProperties(data);
}
}
Linq to entities cannot convert this part:
s.CodeID == data["CodeID"]
Which retrieves the CodeID value from the data collection into a meaningful SQL statement.
What you have to do is store this value in a variable before your where expression like so:
var codeId = data["CodeID"];
and then modify your where to use it:
var codeId = data["CodeID"];
ApplicationUser user = dbContext.Users.Where(s => s.UserName == context.UserName && s.Password == context.Password && s.CodeID == codeId);
Now Linq can properly generate the SQL statement.

Custom Authorization with Parameters Web API

Can someone show me how to use the parameter in Customize AuthorizeAttribute?
Like this:
[Authorize(Role="Admin,Supervisor")]
[Authorize(User="Me,You")]
[Authorize(Action="abc,def")]
This is my code now and I dont have any idea yet how to add the parameter here.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
ApplicationDbContext _context = new ApplicationDbContext();
public override void OnAuthorization(HttpActionContext actionContext)
{
if (AuthorizeRequest(actionContext))
{
return;
}
HandleUnauthorizedRequest(actionContext);
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
if (((System.Web.HttpContext.Current.User).Identity).IsAuthenticated)
{
actionContext.Response = new HttpResponseMessage()
{
StatusCode = HttpStatusCode.Unauthorized,
Content = new StringContent("You are unauthorized to access this resource")
};
}
else
{
base.HandleUnauthorizedRequest(actionContext);
}
}
private bool AuthorizeRequest(HttpActionContext actionContext)
{
var action = actionContext.ActionDescriptor.ActionName;
var controller = actionContext.ControllerContext.ControllerDescriptor.ControllerName;
var currentUser = actionContext.RequestContext.Principal.Identity.GetUserId();
var user = _context.Users.Join(_context.UserAccesses, x => x.RoleId, y => y.RoleId, (x, y) =>
new { Id = x.Id, firstName = x.firstName, lastName = x.lastName, RoleId = x.RoleId, Controller = y.Controller,
Action = y.Action }).Where(z => z.Id == currentUser && z.Controller == controller && z.Action == action)
.SingleOrDefault();
if (user != null)
return true;
else
return false;
}
}
As you have extended the default implementation of Authorize, you need to use [CustomAuthorize(Role="Admin,Supervisor")]. This will set the roles. You can then access the Roles property directly in your code as they are contained in the parent AuthorizeAttribute which has been inherited.
public override void OnAuthorization(HttpActionContext actionContext)
{
var roles = Roles;
if (AuthorizeRequest(actionContext))
{
return;
}
HandleUnauthorizedRequest(actionContext);
}

Authorize Attribute Not Working with Roles MVC C#

I'm modifying a system written in c# MVC at the moment.
I've just built in an extra bit of functionality in the Administrator area that allows the administrator create a user account that has limited administrator functionality. I've put the following over each of the controllers for the new functionality:
[Authorize(Roles = "Administrator")]
However, if I log in using limited administrator account, and navigate to this page, it lets me through.
I'm stumped because I appear to be doing this the right way but I'm also fairly new to MVC, is there anything else I can check? I haven't changed anything in the web.config file so that should be ok.
I know there's limited information above, not looking for a ready-made solution, more advice on what I can check to correct the issue.
thanks
EDIT:
This is how the new role/account was created. Go easy too, this is a first ditch attempt, there's not much validation.
[Authorize(Roles = "Administrator")]
[HttpPost]
public ActionResult AddSalesManager(App.Web.Areas.Administrator.Models.SalesManager model, FormCollection formValues)
{
if (formValues["Cancel"] != null)
{
return RedirectToAction("Index");
}
if (!string.Equals(model.password, model.confirmpassword))
{
ModelState.AddModelError("password", "Password and Confirmation must match");
}
if (ModelState.IsValid)
{
using (ModelContainer ctn = new ModelContainer())
{
// First, create the user account inside the ASP.Net membership system.
//
Membership.ApplicationName = "App";
Roles.ApplicationName = "App";
if (!Roles.RoleExists("LimitedAdmin"))
Roles.CreateRole("LimitedAdmin");
// MembershipCreateStatus createStatus = MembershipService.CreateUser(model.email, model.password, model.email);
if (Membership.GetUser(model.email) == null)
{
Membership.CreateUser(model.email, model.password);
Roles.AddUserToRole(model.email, "LimitedAdmin");
}
}
}
return RedirectToAction("Index");
}
Role attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class PermissionsAttribute : ActionFilterAttribute
{
private readonly PermissionsType required;
public PermissionsAttribute()
{
}
public PermissionsAttribute(PermissionsType required)
{
this.required = required;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Log("OnActionExecuting", filterContext.RouteData);
HttpSessionStateBase session = filterContext.HttpContext.Session;
Controller controller = filterContext.Controller as Controller;
//This is uesd to redirect to same controller but differnect action
// controller.HttpContext.Response.Redirect("./Login");
var rjasthan = filterContext;
var URK = filterContext.HttpContext.Request.RawUrl;
if (session["UserPermissions"] != null)
{
if (!CheckPermissions((UserPermission)session["UserPermissions"]))
{
// this is used to signout from sesssion
// filterContext.HttpContext.GetOwinContext().Authentication.SignOut();
filterContext.Controller.TempData["AuthenticationMessages"] = "You are not authorized to access";
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary{
{ "controller", "Home" },{ "action", "UnAuthorizeAccess" }});
}
}
base.OnActionExecuting(filterContext);
}
protected bool CheckPermissions(UserPermission model)
{
bool result = false;
if (this.required == (PermissionsType.Add))
{
if (model.AddRight)
result = true;
}
else if (this.required == (PermissionsType.View))
{
if (model.ViewRight)
result = true;
}
else if (this.required == (PermissionsType.Edit))
{
if (model.EditRight)
result = true;
}
else if (this.required == (PermissionsType.Delete))
{
if (model.DeleteRight)
result = true;
}
else if (this.required == (PermissionsType.View | PermissionsType.Edit))
{
if (model.ViewRight && model.EditRight)
{
result = true;
}
}
else if (this.required == (PermissionsType.Add | PermissionsType.Edit))
{
if (model.AddRight && model.EditRight)
{
result = true;
}
}
return result;
}
private void Log(string methodName, RouteData routeData)
{
var controllerName = routeData.Values["controller"];
var actionName = routeData.Values["action"];
var message = String.Format("{0} controller:{1} action:{2}", methodName, controllerName, actionName);
Debug.WriteLine(message, "Action Filter Log");
}
}
[Flags]
public enum PermissionsType
{
View = (1 << 0),
Add = (1 << 1),
Edit = (1 << 2),
Delete = (1 << 3),
Admin = (View | Add | Edit | Delete)
}
[Permissions(PermissionsType.Add)]
public ActionResult Register()
{
return this.AjaxableView();
}
What do you expect from this code?
With this attribute you gain all users in the administrator role the right to execute this controller action no matter how limited the account is.

Resources