Custom Authorization with Parameters Web API - asp.net-mvc

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);
}

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?

How to create a unique session for a user: ASP.NET

I have been learning ASP.NET and came to point where I realised that my web application is creating only a static session for all the users, that is if one logs out all the users are logged out, and sometimes the session is even swapped (lets say userA logs in and right after userB logs in, when the userA refreshes he is seeing the data of userB).
My SessionManager class is as below
SessionManager.cs
public class SessionManager
{
#region Private Data
private static String USER_KEY = "user";
#endregion
public static Employee CurrentUser
{
get;
set;
}
public static string UserType
{
get;
set;
}
public static Int32 SessionTimeout
{
get
{
return System.Web.HttpContext.Current.Session.Timeout;
}
}
public static String GetUserFullName()
{
if (SessionManager.CurrentUser != null)
return SessionManager.CurrentUser.FirstName;
else
return null;
}
public static Boolean IsUserLoggedIn
{
get
{
if (SessionManager.CurrentUser != null)
return true;
else
return false;
}
}
#region Methods
public static void AbandonSession()
{
for (int i = 0; i < System.Web.HttpContext.Current.Session.Count; i++)
{
System.Web.HttpContext.Current.Session[i] = null;
}
System.Web.HttpContext.Current.Session.Abandon();
}
#endregion
}
Login Controller:
[HttpPost]
public ActionResult Index(String txtUserName, String txtPassword)
if (User.Identity.IsAuthenticated)
{
return View();
}
else
{
if (ModelState.IsValid)
{
Employee obj = (from o in db.Employees
where o.Email == txtUserName && o.Password == txtPassword
select o).FirstOrDefault();
if (obj != null)
{
var dh = db.Departments.Where(x => x.LeadBy == obj.EmployeeId).FirstOrDefault();
var tl = db.Teams.Where(x => x.LeadBy == obj.EmployeeId).FirstOrDefault();
if (dh == null && tl == null)
{
Session["UserType"] = "EMP";
}
else if (dh != null && tl != null)
{
Session["UserType"] = "DH&TL";
}
else if (dh != null)
{
Session["UserType"] = "DH";
}
else if (tl != null)
{
Session["UserType"] = "TL";
}
SessionManager.CurrentUser = obj; //how can I create different obj for different users here?
var currentEnrollID = SessionManager.CurrentUser.EnrollNumber;
var currentEmployeeID = SessionManager.CurrentUser.EmployeeId;
var currentEmpName = SessionManager.CurrentUser.FirstName + " " + SessionManager.CurrentUser.LastName;
I have been using sessions like this in the whole application so a different approach would be hectic to amend the changes.
public ActionResult Logout()
{
if (SessionManager.IsUserLoggedIn)
{
SessionManager.CurrentUser.EmployeeId = 0;
SessionManager.AbandonSession();
Session.Clear();
Session.Abandon();
Session.RemoveAll();
}
return RedirectToAction("Index","Login");
}
This is not related to ASP.NET, but it is more on how static members work.
The real issue is your SessionsManager, which contains static methods that you store values every time the user logs-in. This means the same instance is shared across different session in the application.
I have an update SessionManager you can see below. I have stored the SessionManager object in the session object so that as long the session is alive. It will return the same instance by session when you call it using SessionManager.Current.
public class SessionManager {
#region Private Data
private static String USER_KEY = "user";
#endregion
public static SessionManager Current {
get{
if (HttpContext.Current.Session[USER_KEY] != null) {
return (SessionManager) HttpContext.Current.Session[USER_KEY];
} else {
var sess = new SessionManager ();
HttpContext.Current.Session[USER_KEY] = sess;
return sess;
}
}
}
public Employee CurrentUser {
get;
set;
}
public string UserType {
get;
set;
}
public Int32 SessionTimeout {
get {
return System.Web.HttpContext.Current.Session.Timeout;
}
}
public String GetUserFullName () {
if (SessionManager.Current.CurrentUser != null)
return SessionManager.Current.CurrentUser.FirstName;
else
return null;
}
public Boolean IsUserLoggedIn {
get {
if (SessionManager.Current.CurrentUser != null)
return true;
else
return false;
}
}
#region Methods
public void AbandonSession () {
for (int i = 0; i < System.Web.HttpContext.Current.Session.Count; i++) {
System.Web.HttpContext.Current.Session[i] = null;
}
System.Web.HttpContext.Current.Session.Abandon ();
}
#endregion
}

MVC IPrincipal User from WebViewPage is null

I've create a base class for my Views like this:
public abstract class BaseViewPage : WebViewPage
{
public virtual new CustomPrincipal User
{
get
{
if (base.User == null) return null;
return base.User as CustomPrincipal;
}
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new CustomPrincipal User
{
get
{
if (base.User == null) return null;
return base.User as CustomPrincipal;
}
}
public override void Execute()
{
throw new NotImplementedException();
}
}
and in my model I have:
public class SecureAreaModel : BaseViewPage
{
public int MyUserID
{
get { return User.ID; }
private set { }
}
public SecureAreaModel(ControllerContext controllerContext)
{
}
public override void Execute()
{
throw new NotImplementedException();
}
}
I want to use the propertiy MyUserID but I receive this error:
Error
At this point the user is autenticated
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
JavaScriptSerializer serializer = new JavaScriptSerializer();
CustomPrincipalSerializeModel serializeModel = serializer.Deserialize<CustomPrincipalSerializeModel>(authTicket.UserData);
CustomPrincipal customer = new CustomPrincipal(serializeModel.Email);
customer.ID = serializeModel.ID;
customer.Email = serializeModel.Email;
customer.FirstName = serializeModel.FirstName;
customer.LastName = serializeModel.LastName;
customer.Roles = serializeModel.Roles;
HttpContext.Current.User = customer;
}
else
{
HttpContext.Current.User = new CustomPrincipal(string.Empty);
}
}
Any help will be appreciated! Thx

nopCommerce Error: Child actions are not allowed to perform redirect actions

I'm using MVC nopCommerce and developing custom plugin which override existing functionality of HomepageBestSellers (action of ProductController which is attributed as [ChildActionOnly]).
FilterProvider:
namespace Nop.Plugin.Product.BestSellers.Filters
{
public class BestSellersFilterProvider : IFilterProvider
{
private readonly IActionFilter _actionFilter;
public BestSellersFilterProvider(IActionFilter actionFilter)
{
_actionFilter = actionFilter;
}
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (actionDescriptor.ControllerDescriptor.ControllerType == typeof(ProductController) && actionDescriptor.ActionName.Equals("HomepageBestSellers"))
{
return new Filter[]
{
new Filter(_actionFilter, FilterScope.Action, null)
};
}
return new Filter[] { };
}
}
}
Action Filter:
namespace Nop.Plugin.Product.BestSellers.Filters
{
public class BestSellersFilter : ActionFilterAttribute
{
private readonly ISettingService _settingService;
private readonly IStoreService _storeService;
private readonly IWorkContext _workContext;
public BestSellersFilter(ISettingService settingService,
IStoreService storeService, IWorkContext workContext)
{
this._settingService = settingService;
this._storeService = storeService;
this._workContext = workContext;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//load settings for a chosen store scope and ensure that we have 2 (or more) stores
var storeScope = 0;
if (_storeService.GetAllStores().Count < 2)
storeScope = 0;
var storeId = _workContext.CurrentCustomer.GetAttribute<int>(SystemCustomerAttributeNames.AdminAreaStoreScopeConfiguration);
var store = _storeService.GetStoreById(storeId);
storeScope = store != null ? store.Id : 0;
var bestSellersSettings = _settingService.LoadSetting<BestSellersSettings>(storeScope);
if (bestSellersSettings.IsBestSellersEnabled)
{
filterContext.Result = new RedirectResult("Plugins/BestSellersProducts/PublicInfo");
}
else
base.OnActionExecuting(filterContext);
}
}
}
I am getting the following error on filterContext.Result = new RedirectResult("Plugins/BestSellersProducts/PublicInfo"); this line:
Child actions are not allowed to perform redirect actions.
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: Child actions are not allowed to perform redirect actions.
UPDATE:
Changed BestSellersFilter.cs according to answer.
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var storeScope = 0;
if (_storeService.GetAllStores().Count < 2)
storeScope = 0;
var storeId = _workContext.CurrentCustomer.GetAttribute<int>(SystemCustomerAttributeNames.AdminAreaStoreScopeConfiguration);
var store = _storeService.GetStoreById(storeId);
storeScope = store != null ? store.Id : 0;
var featuredProductsSettings = _settingService.LoadSetting<FeaturedProductsSettings>(storeScope);
if (featuredProductsSettings.IsFeaturedProductsEnabled)
{
var products = _productService.GetAllProductsDisplayedOnHomePage();
BestSellersController objResult = new BestSellersController();
filterContext.Result = new ContentResult { Content = objResult.PublicInfoPlugin() };
//base.OnActionExecuting(filterContext);
}
else
base.OnActionExecuting(filterContext);
}
Changed BestSellersController.cs according to answer.
public string PublicInfoPlugin()
{
var featuredProductsSettings = _settingService.LoadSetting<FeaturedProductsSettings>(_storeContext.CurrentStore.Id);
if (featuredProductsSettings.IsFeaturedProductsEnabled)
{
var products = _productService.GetAllProductsDisplayedOnHomePage();
//ACL and store mapping
products = products.Where(p => _aclService.Authorize(p) && _storeMappingService.Authorize(p)).ToList();
//availability dates
products = products.Where(p => p.IsAvailable()).ToList();
if (products.Count == 0)
return "";
var model = PrepareProductOverviewModels(products.Take(featuredProductsSettings.ShowFeaturedProductsNumber)).ToList();
return RenderPartialViewToString("~/Plugins/Product.FeaturedProducts/Views/ProductFeaturedProducts/PublicInfo.cshtml", model);
}
return "";
}
Now getting null values from all the private objects in PublicInfoPlugin method.
Instead of a RedirectResult, set it to a ContentResult and set the Content property to the output from your plugin's action:
filterContext.Result = new ContentResult { Content = "Load your plugin's action here" };

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