get roles attribute of controller in OnActionExecuting in mvc - c#

I want to read the filter attributes of controller in OnActionExecuting method.
for this I have written this code but this empty array.
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var getActionName = filterContext.ActionDescriptor.ActionName;
var getControllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var getUserName = User.Identity.Name;
var getUserRoles = Roles.GetRolesForUser(getUserName);
foreach (var filter in filterContext.ActionDescriptor.GetCustomAttributes(typeof(Roles), false))
{
var desiredValue = filter.ToString();
}
//some business logic here
}
}
this is my controller
[Authorize(Roles = "Admin")]
public class AdminController : BaseController
{
public ActionResult Index()
{
return View();
}
}
I want to get list of allowed roles for executing controller.

You can use GetFilterAttributes method of ActionDescriptor or ControllerDescriptor:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var filters = new List<FilterAttribute>();
filters.AddRange(filterContext.ActionDescriptor.GetFilterAttributes(false));
filters.AddRange(filterContext.ActionDescriptor.ControllerDescriptor.GetFilterAttributes(false));
var roles = filters.OfType<AuthorizeAttribute>().Select(f => f.Roles);
...
}

Related

Pass TempData to ActionFilter RedirectToAction

I want to set a ViewBag for second an action from the first action by using ActionFilter.
In the first Action i do the following :
TempData["Test"] = "Test";
return RedirectToAction("Action2", new { values = values });
Then in IActionFilter :
public class HelpertestActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
}
public void OnActionExecuting(ActionExecutingContext context)
{
var controller = context.Controller as Controller;
if (controller != null)
{
if (controller.TempData["Test"] != null)
{
controller.ViewBag.Notification = controller.TempData["Test"];
}
}
}
}
But in ActionFilter OnActionExecuting, TempData["Test"] is always null.
I have followed this article
After some try, there is no errors in my code, except in the startup configuration.
In Startup.Configure() the app.UseCookiePolicy() has to be after app.UseMVC() to work as expected.

Access viewcontext from httpcontext

I want to get ViewData value from httpcontext.
My function:
[LogActionFilter]
public ActionResult Edit(int id = 0)
{
var obj = getObjFromDb(id);
ViewData["abc"] = obj.name;
return View(obj);
}
My action filter where I want to to access ViewData value:
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var a= filterContext.HttpContext.Items["abc"]; //null
var b = filterContext.HttpContext.Request.RequestContext.HttpContext.Items["abc"]; //null
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var a= filterContext.HttpContext.Items["abc"]; //null
var b = filterContext.HttpContext.Request.RequestContext.HttpContext.Items["abc"]; //null
}
}
How can I access the value of ViewData from HttpContext?
You can use session to pass value into your OnActionExecuted filter. However you can't pass anything from your action to OnActionExecution because its executed before your action.
[LogActionFilter]
public ActionResult Edit(int id = 0)
{
var obj = getObjFromDb(id);
Session["abc"] = obj.name;
return View(obj);
}
In filter:
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var a = filterContext.HttpContext.Session["abc"];
/// a should have a value
}
Thanks to Stephen Muecke,
ViewData is not available in HttpContext
So I changed my logic, instead of getting viewdata value, I fetched data from database in action filter.

Inject A C# Function to all of ActionResult

We have a Asp.net MVC Project that it has over 1000 ActionResult and I need to add all of them a C# Function for check value of Session.
So What is your offer to do this?
These two following lines has some example of my Controller and ActionResult :
public partial class CRMController : Controller
{
public ActionResult OrganizationCategory()
{
//I want add a C# function here
}
}
public partial class BaseInfoController : Controller
{
public ActionResult Lead()
{
//I Want Add a C# Function here
}
}
You can use ActionFilters for this.
The base ActionFilterAttribute class has the following methods that
you can override:
OnActionExecuting – This method is called before a controller action
is executed. OnActionExecuted – This method is called after a
controller action is executed. OnResultExecuting – This method is
called before a controller action result is executed. OnResultExecuted
– This method is called after a controller action result is executed.
Here full code example !
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Log("OnActionExecuting", filterContext.RouteData);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Log("OnActionExecuted", filterContext.RouteData);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Log("OnResultExecuting", filterContext.RouteData);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Log("OnResultExecuted", filterContext.RouteData);
}
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");
}
}
[LogActionFilter]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
}
Please create a action filter attribute, in that check sessions. Then create a Base Controller, then apply this attribute in that controller. Then inherit this base controller with your business controllers.
public class MySessionCheckFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
//Check Session Method()
//if(SessionNotAvaliable)
//{
// throw new businessException;
//}
base.OnActionExecuting(context);
}
}
[MySessionCheckFilterAttribute]
public class BaseController:Controller
{
}
public class YourController_One: BaseController
{
//Do anything
}
public class YourController_Two : BaseController
{
//Do anything
}
Since your function is on checking the Session, i thought it could relates to an authorization process. If so, you can try with AuthorizeAttribute
Example: Checking the Session["username"] on every Function that tagged with [AuthorizeAttribute]:
public class SessionAuthAttribute : AuthorizeAttribute
{
public SessionAuthAttribute() { }
public override void OnAuthorization(AuthorizationContext filterContext)
{
//base.OnAuthorization(filterContext);
var userID = filterContext.HttpContext.Session["username"];
if (userID == null)
{
filterContext.Result = new RedirectResult("/Home");
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
controller = "Home",
action = "Index"
})
);
}
}
In Controller:
[SessionAuthAttribute] //Applied for whole Controller
public class HomeController : Controller
{
[SessionAuthAttribute] //Applied for 1 function
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
}
EDIT: You can create this class in folder Attribute of your MVC Project

C# API Controller Custom Filter with HttpActionContext Redirect to controller?

Is there a way to create a custom filter with an API controller to redirect to a MVC controller?
After looking around a bit his is what i have.
public class APIHasOneOfThesePermissions : ActionFilterAttribute
{
protected UserManager<ApplicationUser> UserManager { get; set; }
private SAMPortal.DAL.SAMPortalContext db = new DAL.SAMPortalContext();
public string[] Permissions { get; set; }
public APIHasOneOfThesePermissions(string[] Permissions)
{
this.UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(this.db));
this.Permissions = Permissions;
}
public override void OnActionExecuting(HttpActionContext filterContext)
{
string userID = HttpContext.Current.User.Identity.GetUserId();
var CurrUser = db.Users.Include(u => u.Role.Permissions).Where(user => user.Id.Equals(userID)).FirstOrDefault();
bool hasPermission = false;
foreach (string x in Permissions)
{
if (hasPermission == false)
{
hasPermission = CurrUser.HasPermission(x);
}
}
if (hasPermission == false)
{
filterContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
base.OnActionExecuting(filterContext);
}
}
However when i execute the code it doesn't redirect them to the error page. Ideally i would like to redirect to a specify non-API controller is that possible?
I've created AuthorizeRedirectAttribute in one of my projects like this:
using System;
using System.Net;
using System.Web.Mvc;
namespace MyNamespace.Attributes
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeRedirectAttribute : AuthorizeAttribute
{
public string RedirectUrl = "~/Error/Forbidden403";
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
var httpContext = filterContext.RequestContext.HttpContext;
var request = httpContext.Request;
var response = httpContext.Response;
// If AJAX request, just return appropriate code
if (request.IsAjaxRequest())
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
response.StatusCode = (int)HttpStatusCode.Forbidden;
else
response.StatusCode = (int)HttpStatusCode.Unauthorized;
response.SuppressFormsAuthenticationRedirect = true;
response.End();
}
// Otherwise check if authenticated, and if not redirect to specified url
if (httpContext.User.Identity.IsAuthenticated)
{
httpContext.Response.Redirect(RedirectUrl);
}
}
}
}
Then I've used it like this
[AuthorizeRedirect(Roles = "Administrator")]
public class MyController : Controller
{
}
In this case I've decorated whole controller with this attribute. It can also be applied to single controller function, if necessary. Basically what it does is, it checks whether logged on user is in role Administrator. If it is not, user is redirected to "~/Error/Forbidden403" action (returning simple view displaying user has not enough permissions). Hope it helps.
You could also implement checking your own permissions, as you did in your code.

Redirect From Action Filter Attribute

What is the best way to do a redirect in an ActionFilterAttribute. I have an ActionFilterAttribute called IsAuthenticatedAttributeFilter and that checked the value of a session variable. If the variable is false, I want the application to redirect to the login page. I would prefer to redirect using the route name SystemLogin however any redirect method at this point would be fine.
Set filterContext.Result
With the route name:
filterContext.Result = new RedirectToRouteResult("SystemLogin", routeValues);
You can also do something like:
filterContext.Result = new ViewResult
{
ViewName = SharedViews.SessionLost,
ViewData = filterContext.Controller.ViewData
};
If you want to use RedirectToAction:
You could make a public RedirectToAction method on your controller (preferably on its base controller) that simply calls the protected RedirectToAction from System.Web.Mvc.Controller. Adding this method allows for a public call to your RedirectToAction from the filter.
public new RedirectToRouteResult RedirectToAction(string action, string controller)
{
return base.RedirectToAction(action, controller);
}
Then your filter would look something like:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = (SomeControllerBase) filterContext.Controller;
filterContext.Result = controller.RedirectToAction("index", "home");
}
Alternatively to a redirect, if it is calling your own code, you could use this:
actionContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(new { controller = "Home", action = "Error" })
);
actionContext.Result.ExecuteResult(actionContext.Controller.ControllerContext);
It is not a pure redirect but gives a similar result without unnecessary overhead.
I am using MVC4, I used following approach to redirect a custom html screen upon authorization breach.
Extend AuthorizeAttribute say CutomAuthorizer
override the OnAuthorization and HandleUnauthorizedRequest
Register the CustomAuthorizer in the RegisterGlobalFilters.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomAuthorizer());
}
upon identifying the unAuthorized access call HandleUnauthorizedRequestand redirect to the concerned controller action as shown below.
public class CustomAuthorizer : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
bool isAuthorized = IsAuthorized(filterContext); // check authorization
base.OnAuthorization(filterContext);
if (!isAuthorized && !filterContext.ActionDescriptor.ActionName.Equals("Unauthorized", StringComparison.InvariantCultureIgnoreCase)
&& !filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.Equals("LogOn", StringComparison.InvariantCultureIgnoreCase))
{
HandleUnauthorizedRequest(filterContext);
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result =
new RedirectToRouteResult(
new RouteValueDictionary{{ "controller", "LogOn" },
{ "action", "Unauthorized" }
});
}
}
It sounds like you want to re-implement, or possibly extend, AuthorizeAttribute. If so, you should make sure that you inherit that, and not ActionFilterAttribute, in order to let ASP.NET MVC do more of the work for you.
Also, you want to make sure that you authorize before you do any of the real work in the action method - otherwise, the only difference between logged in and not will be what page you see when the work is done.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
// Do whatever checking you need here
// If you want the base check as well (against users/roles) call
base.OnAuthorization(filterContext);
}
}
There is a good question with an answer with more details here on SO.
Try the following snippet, it should be pretty clear:
public class AuthorizeActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(FilterExecutingContext filterContext)
{
HttpSessionStateBase session = filterContext.HttpContext.Session;
Controller controller = filterContext.Controller as Controller;
if (controller != null)
{
if (session["Login"] == null)
{
filterContext.Cancel = true;
controller.HttpContext.Response.Redirect("./Login");
}
}
base.OnActionExecuting(filterContext);
}
}
Here is a solution that also takes in account if you are using Ajax requests.
using System;
using System.Web.Mvc;
using System.Web.Routing;
namespace YourNamespace{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeCustom : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext context) {
if (YourAuthorizationCheckGoesHere) {
string area = "";// leave empty if not using area's
string controller = "ControllerName";
string action = "ActionName";
var urlHelper = new UrlHelper(context.RequestContext);
if (context.HttpContext.Request.IsAjaxRequest()){ // Check if Ajax
if(area == string.Empty)
context.HttpContext.Response.Write($"<script>window.location.reload('{urlHelper.Content(System.IO.Path.Combine(controller, action))}');</script>");
else
context.HttpContext.Response.Write($"<script>window.location.reload('{urlHelper.Content(System.IO.Path.Combine(area, controller, action))}');</script>");
} else // Non Ajax Request
context.Result = new RedirectToRouteResult(new RouteValueDictionary( new{ area, controller, action }));
}
base.OnActionExecuting(context);
}
}
}
This works for me (asp.net core 2.1)
using JustRide.Web.Controllers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace MyProject.Web.Filters
{
public class IsAuthenticatedAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.User.Identity.IsAuthenticated)
context.Result = new RedirectToActionResult(nameof(AccountController.Index), "Account", null);
}
}
}
[AllowAnonymous, IsAuthenticated]
public IActionResult Index()
{
return View();
}
you could inherit your controller then use it inside your action filter
inside your ActionFilterAttribute class:
if( filterContext.Controller is MyController )
if(filterContext.HttpContext.Session["login"] == null)
(filterContext.Controller as MyController).RedirectToAction("Login");
inside your base controller:
public class MyController : Controller
{
public void RedirectToAction(string actionName) {
base.RedirectToAction(actionName);
}
}
Cons. of this is to change all controllers to inherit from "MyController" class

Categories

Resources