I have this repetitive code in all the controllers:
var user = Session["_User"] as User;
if (user== null)
{
return RedirectToAction("Index");
}
How can I refactor this? should i add this to an attribute or make a Session Wrapper?
What would be the best approach?
extend your all controllers with a MasterController and override OnActionExecuting method as follows
public class MasterController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var user = Session["_User"] as User;
if (user== null)
{
if(filterContext.ActionDescriptor.ActionName != "Index" || filterContext.ActionDescriptor.ControllerDescriptor.ControllerName != "ControllerThatContainIndexAction")
{
filterContext.Result = this.RedirectToAction("Index");
return;
}
}
base.OnActionExecuting(filterContext);
}
}
Description
There are many ways. I would create a own Controller.
Than you inherit the other controllers from this.
Sample
public class BaseController : Controller
{
public User User { get; private set; }
public BaseController()
{
this.User = Session["_User"] as User;
}
}
public class HomeController: BaseController
{
// your action methods
}
Related
I have a base controller which sole purpose is to get an int value from HttpContext.Session and make it available to all inheriting controllers.
Now I'm trying to redirect to the login view when said value is not set and the user tries to access a restricted view without being logged in.
This is what I've got so far:
public class BaseController : Controller
{
protected int? BranchId
{
get { return (HttpContext.Session.GetInt32("BranchId") as int?); }
set {}
}
public BaseController() {}
}
public class RedirectingActionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
if (BranchId < 1) // BUT BranchId DOES NOT EXIST IN THIS CONTEXT
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "Home",
action = "Login"
}));
}
}
}
public class EmployeesController : BaseController
{
private readonly VaktlisteContext db;
private readonly IMapper auto;
public EmployeesController(VaktlisteContext context, IMapper mapper)
{
db = context;
auto = mapper;
}
[RedirectingAction]
public async Task<IActionResult> Index()
{
Branch branch = await db.Branches.Include(e => e.Employees)
.Where(b => b.Id == BranchId).FirstOrDefaultAsync();
if (branch == null) return NotFound();
BranchViewModel vm = auto.Map<BranchViewModel>(branch);
return View(vm);
}
}
I have read this question and answer, but wasn't able to work out how the solution could be applicable in my case.
Any suggestions?
you can not access to BranchId attribute on RedirectingActionAttribute class directly because it is member of BaseController class.
try this code:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
if ((filterContext.Controller as BaseController).BranchId < 1)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "Home",
action = "Login"
}));
}
}
Protected prop is protected, when you defined a property as protected access then you can not expect to access it in derived class!!.
But about accessing property in action
public class ValidateActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller as BaseController ;
if (controller == null)
{
throw new InvalidOperationException("It is not EmployeesController!");
}
var propVal= controller.PropertyName;
//check propVal here
}
}
but you can also use Session like context.HttpContext.Session in ActionFilter
I have the following BaseApiController:
public class BaseApiController : ApiController
{
public readonly Current _current { get; private set; }
}
All my ApiControllers inherit from this one.
I need to do a validation in all of my methods inside my ApiControllers, that checks if the userId passed match the current HttpContext.Current.User.Identity.
[Route("{userId}/cars")]
public HttpResponseMessage GetCars(int userId)
{
if (userId != _current.UserId)
{
return Request.CreateErrorResponse(HttpStatusCode.Forbidden, "Unauthorized");
}
}
Is there anyway to do it on the BaseApiController, so that I can avoid doing this validation on all of the endpoints that receive the userId as argument?
You can create custom validation attribute based on ActionFilterAttribute to achieve what you need.
For your case it can look like this:
public class UserAccessCheckAttribute: ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
var controller = actionContext.ControllerContext.Controller as BaseApiController;
object requestUserIdObj;
if (controller != null && actionContext.ActionArguments.TryGetValue("userId", out requestUserIdObj))
{
var userId = (int) requestUserIdObj;
if (userId != controller._current.UserId)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, "Unauthorized");
}
}
}
}
After that you can decorate either controllers or specific actions where you need to perform user id check with this attribute:
[Authorize]
[RoutePrefix("api/Account")]
[UserAccessCheck] //check user id for all actions in controller
public class AccountController : BaseApiController
{
//....
}
public class ValuesController : BaseApiController
{
//....
[UserAccessCheck] //check user id for specific action only
public IEnumerable<string> Get()
{
//...
}
}
After that no additional code in your actions required
I need to implement a ActionFilterAttribute [POST] ActionResult() in the controller. The problem is that I try to “redirect” to a page if validation failed... But it does not work. Validation runs, but then returns to the ActionResult() next line and finally when the view is returned, only then “redirected” to the page listed in the validation. Ultimately what I need is to stop the ActionResult() statements and “redirect” to the page listed in the validation. I tried OnActionExecuting() and OnActionExecuted() but does not work any
I need to...
filterContext.HttpContext.Response.Redirect (loginUrl, true);
Run away, “redirecting” the page indicated
My code:
[HelperSomeValidations("")]
[HttpPost]
public ActionResult Create(Pais pais)
{
try
{
PaisBLL.saveNew(pais);
}
catch (Exception ex)
{
ViewBag.error = ex;
return View(“Error”);
}
return RedirectToAction(“Index”);
}
public class HelperSomeValidations : ActionFilterAttribute
{
public HelperSomeValidations(String permiso)
{
this.permiso = permiso;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var user = filterContext.HttpContext.Session["coco"];
if (user == null) //validates if the user just login
{
//send them off to the login page
var url = new UrlHelper(filterContext.RequestContext);
var loginUrl = url.Content(“~/Usuario/Login”);
filterContext.HttpContext.Response.Redirect(loginUrl, true);
}
else
{
if (permission != “”)
{
//does some validations with “permission”
}
}
}
}
Thks!
I know this doesn't solve the problem you have posted but I feel it's a better solution. I would personally use an AuthoriseAttribute here instead as this is what it's designed todo.
public class Authorise : AuthorizeAttribute
{
private readonly string _permissionSystemName;
public Authorise()
{
}
public Authorise(string permissionSystemName)
{
_permissionSystemName = permissionSystemName;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//DO some logic and return True or False based on whether they are allowed or not.
return false;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
area = filterContext.HttpContext.Request.RequestContext.RouteData.Values["area"],
controller = "Generic",
action = "PermissionDenied"
})
);
}
}
Usage would be along the lines of:
[Authorise("SomePermissionName")]
public class MyController : Controller
{
}
Instead of calling filterContext.HttpContext.Response.Redirect(loginUrl, true), you need to set the filterContext.Result to a RedirectResult.
I have the following requirement to implement the Access Control list
public class SecurityObject{
public string Key{get;set;}
public string DisplayName{get;set;}
public bool isAllowed{get;set;}
}
public class Role{
List<SecurityObject> AccessibleObjects{get;set;}
}
Currently I use forms authentication for basic authorization. Below is my code
Global.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
public override void Init()
{
this.PostAuthenticateRequest += new
EventHandler(MvcApplication_PostAuthenticateRequest);
base.Init();
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
HttpCookie authCookie =
HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
string encTicket = authCookie.Value;
if (!String.IsNullOrEmpty(encTicket))
{
FormsAuthenticationTicket ticket =
FormsAuthentication.Decrypt(encTicket);
string[] userData = ticket.UserData.Split(new string[] { "___" },
StringSplitOptions.None);
string[] roles = null;
if (userData.Length > 1)
{
roles = userData[1].Split(',');
}
MyCustomIdentity identity = new MyCustomIdentity(ticket);
GenericPrincipal principle = new GenericPrincipal(identity, roles);
HttpContext.Current.User = principle;
}
}
}}
My current controller class
public class AdminController : Controller
{
[HttpPost, Authorize, ValidateAntiForgeryToken]
public ActionResult SaveUser(UserDetailViewModel viewModel)
{
}
}
My Target controller class
public class AdminController : Controller
{
[HttpPost, Authorize(ACLKey="USR_SAVE"), ValidateAntiForgeryToken]
public ActionResult SaveUser(UserDetailViewModel viewModel)
{
}
}
I want my action method to be decorated with ACLKey and I would like to check whether the User Role has the given key and based on that I need to execute or return HttpUnauthorizedResult page, even for Ajax requests from jQuery.
I referred many like Customizing authorization in ASP.NET MVC But i didnt find a way to execute both forms authentication and my custom ACLKey check.
How do i parse the value USR_SAVE and process custom authentication using CustomAuthorizeFilter?
You can try like this
public class FeatureAuthenticationAttribute : FilterAttribute, IAuthorizationFilter
{
public string AllowFeature { get; set; }
public void OnAuthorization(AuthorizationContext filterContext)
{
var filterAttribute = filterContext.ActionDescriptor.GetFilterAttributes(true)
.Where(a => a.GetType() ==
typeof(FeatureAuthenticationAttribute));
if (filterAttribute != null)
{
foreach (FeatureAuthenticationAttribute attr in filterAttribute)
{
AllowFeature = attr.AllowFeature;
}
List<Role> roles =
((User)filterContext.HttpContext.Session["CurrentUser"]).Roles;
bool allowed = SecurityHelper.IsAccessible(AllowFeature, roles);
if (!allowed)
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
}
In you action method
[FeatureAuthentication(AllowFeature="USR_SAVE")]
public ActionResult Index()
{
}
Hope this will help you!
You can use a filter attribute:
public class ACLCheckAttribute : FilterAttribute, IActionFilter
In OnActionExecuting, you can grab USR_SAVE. Without knowing where it comes from, I would assume that it comes from:
The Form: you can grab any form values from the context passed into ONActionExecuting, by navigating to the HttpContext.Request.Form collection
Session, etc.: HttpContext would also have these.
The action method: From an attribute, using the context passed in for the action, it has a list of ActionParameters that can be accessed like a dictionary, allowing you to check and extract your value
If somewhere else, please comment where. You can apply this attribute to a controller or method, or globally set it by adding it to the globalfilters collection (GlobalFilters.Filters.Add()), or in the FilterConfig file in the App_Start folder.
I'm using an ActionFilterAttribute to do custom authentication logic. The Attribute will only be used on a derived Controller class that contains my authentication logic.
Here's my Controller, derived from my custom controller class, and a sample attribute:
public class MyController : CustomControllerBase
{
[CustomAuthorize(UserType = UserTypes.Admin)]
public ActionResult DoSomethingSecure()
{
return View();
}
}
Here's an example of my ActionFilterAttribute:
public class CustomAuthorizeAttribute : ActionFilterAttribute
{
public MyUserTypes UserType { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
myUser user = ((CustomControllerBase)filterContext.Controller).User;
if(!user.isAuthenticated)
{
filterContext.RequestContext.HttpContext.Response.StatusCode = 401;
}
}
}
Works great.
Here's the question: Can I demand that this attribute ONLY be used on Actions in my custom controller type?
You can put the ActionFilter on the class itself. All actions in the class will realize the ActionFilter.
[CustomAuthorize]
public class AuthorizedControllerBase : CustomControllerBase
{
}
public class OpenAccessControllerBase : CustomControllerBase
{
}
public class MyRealController : AuthorizedControllerBase
{
// GET: /myrealcontroller/index
public ActionResult Index()
{
return View();
}
}
Based on the comments and the constraints of my system, I took a hybrid approach. Basically, if the request comes through via a cached route or the "User" is not set for any reason, authentication fails in the proper way.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
private MyUser User { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
//Lazy loads the user in the controller.
User = ((MyControllerBase)filterContext.Controller).User;
base.OnAuthorization(filterContext);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool isAuthorized = false;
string retLink = httpContext.Request.Url.AbsolutePath;
if(User != null)
{
isAuthorized = User.IsValidated;
}
if (!isAuthorized)
{
//If the current request is coming in via an AJAX call,
//simply return a basic 401 status code, otherwise,
//redirect to the login page.
if (httpContext.Request.IsAjaxRequest())
{
httpContext.Response.StatusCode = 401;
}
else
{
httpContext.Response.Redirect("/login?retlink=" + retLink);
}
}
return isAuthorized;
}
}