I have a small problem related to action redirecting. I want to prevent users from being able to access information concerning a specific area in the site using an override of the OnActionExecuting in my BaseController class, which is the base class for all my controllers. Method body:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Request.IsAuthenticated && (User as Eagle.Security.EaglePrincipal != null) && Session != null && Session["LastKnownGoodArea"] != null && filterContext.ActionDescriptor.ActionName != "InvalidPermission")
{
var currentArea = Principal.CurrentCenter.CODEFORM_CSE;
if (currentArea != Session["LastKnownGoodArea"].ToString())
RedirectToActionPermanent("InvalidPermission", "Account", new { target = 0, redirectURL = null as string });
else
base.OnActionExecuting(filterContext);
}
}
However, this does not redirect to the specified action. What am I doing wrong? What other approach, if any, would you guys suggest?
Thanks,
Silviu
What Dave commented is right ! In addition this should be the syntax you are looking for :-
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Request.IsAuthenticated && (User as Eagle.Security.EaglePrincipal != null) && Session != null && Session["LastKnownGoodArea"] != null && filterContext.ActionDescriptor.ActionName != "InvalidPermission")
{
var currentArea = Principal.CurrentCenter.CODEFORM_CSE;
if (currentArea != Session["LastKnownGoodArea"].ToString())
{
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new
{
controller = "InvalidPermission",
action = "Account",
target = 0,
}));
filterContext.Result.ExecuteResult(filterContext);
}
else
{
base.OnActionExecuting(filterContext);
}
}
}
I want to prevent users from being able to access information concerning a specific area in the site using an override of the OnActionExecuting in my BaseController class, which is the base class for all my controllers.
Why did you choose to use OnActionExecuting for this? You're executing this if-statement on every request, I would recommend to use the Authorize attribute for the specific actions:
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var user = User as Eagle.Security.EaglePrincipal;
if(httpContext.User.Identity.IsAuthenticated && user != null)
{
var currentArea = Principal.CurrentCenter.CODEFORM_CSE;
var lastKnownArea = Session["LastKnownGoodArea"];
if (lastKnowArea == null)
return false;
return currentArea.Equals(lastKnownArea.ToString());
}
return base.AuthorizeCore(httpContext);
}
}
In your web.config you can configure redirects like:
<customErrors mode="RemoteOnly">
<error statusCode="403" redirect="/InvalidPermission/Account" />
</customErrors>
If you want control over the UnAuthorized request you can always choose to override the HandleUnauthorizedRequest method
Here is the final solution:
var currentArea = Principal.CurrentCenter.CODEFORM_CSE;
if (currentArea != Session["LastKnownGoodArea"].ToString())
{
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new
{
controller = "Account",
action = "InvalidPermission",
area = "",
target = 0,
redirectURL = ""
}));
}
else
{
base.OnActionExecuting(filterContext);
}
Thank you both for your input, you helped alot!
Cheers!
You can't redirect to Action from a filter because it is not creating an Action Result yet. You can only create a new route. I'm not completely sure of syntax you need. I threw this together as an example of way to go.
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Request.IsAuthenticated && (User as Eagle.Security.EaglePrincipal != null) && Session != null && Session["LastKnownGoodArea"] != null && filterContext.ActionDescriptor.ActionName != "InvalidPermission")
{
var currentArea = Principal.CurrentCenter.CODEFORM_CSE;
if (currentArea != Session["LastKnownGoodArea"].ToString())
filterContext.Result = new RedirectToRouteResult(
new System.Web.Routing.RouteValueDictionary {
{"controller", "InvalidPermission"}, {"action", "Account"}, {target =0}, {redirectURL = null as string }
else
base.OnActionExecuting(filterContext);
}
}
Related
I have a problem with my MVC project! The goal is to set a session var in order to pass it to all the controllers:
inside my xUserController,
Session["UserId"] = 52;
Session.Timeout = 30;
string SessionUserId = ((Session != null) && (Session["UserId"] != null)) ? Session["UserId"].ToString() : "";
//SessionUserId ="52"
But within the ChatMessageController
[HttpPost]
public ActionResult AddMessageToConference(int? id,ChatMessageModels _model){
var response = new NzilameetingResponse();
string SessionUserId = ((Session != null) && (Session["UserId"] != null)) ? Session["UserId"].ToString() : "";
//...
}
return Json(response, "text/json", JsonRequestBehavior.AllowGet);
}
SessionUserId = ""
So, Why this ? How to set the session variable to be global within all my controllers ??
There can be only two reasons of such behavior: the first one is that your session is over and the second is that you rewrite you session variable from another place in your application.
Wthout any additional code there is nothing to say more.
Here is how I solved the issue
I know this is not the best way to do it but it helped me:
First I have created a base controller as follows
public class BaseController : Controller
{
private static HttpSessionStateBase _mysession;
internal protected static HttpSessionStateBase MySession {
get { return _mysession; }
set { _mysession = value; }
}
}
then I changed all my controllers' codes in other to let them inherit from the Base Controller class.
Then I overrode the "OnActionExecuting" method as below :
public class xUserController : BaseController
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
BaseController.MySession = Session;
base.OnActionExecuting(filterContext);
}
[HttpPost]
public ActionResult LogIn(FormCollection form)
{
//---KillFormerSession();
var response = new NzilameetingResponse();
Session["UserId"] = /*entity.Id_User*/_model.Id_User;
return Json(response, "text/json", JsonRequestBehavior.AllowGet);
}
}
Finally, I've changed the way I call session variables.
string SessionUserId = ((BaseController.MySession != null) && (BaseController.MySession["UserId"] != null)) ? BaseController.MySession["UserId"].ToString() : "";
instead of
string SessionUserId = ((Session != null) && (Session["UserId"] != null)) ? Session["UserId"].ToString() : "";
now it works and my session vars can walk across all controllers.
I have security in my MVC application set up with an authorize attribute...
public class UserLoggedInAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Session["UserId"] == null)
{
var values = new { controller = "Home", action = "Index" };
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(values));
}
}
}
Then I also have a .ashx HttpHandler which is called by jQuery upload control....
public class UploadFile : IHttpHandler, IReadOnlySessionState
{
...
private bool EnsureSecureTransaction(HttpContext context)
{
if (context.Session["UserId"] == null)
{
return false;
}
else
{
return true;
}
return true;
}
}
When EnsureSecureTransaction() gets called the session is coming back null. But session that is read in my MVC action its not. I notice that I'm taking session from the filterContext though.
I have tried to change all the code to try and reference HttpContext.Current.Session like this
[HttpPost]
public ActionResult Logon(AdminModel model)
{
if (model.UserName == "x" && model.Password == "x")
{
HttpContext.Session["UserId"] = "true";
return RedirectToAction("CreateBlog", "Blog");
}
return View;
}
private bool EnsureSecureTransaction(HttpContext context)
{
if (context.Session["UserId"] == null)
{
return false;
}
else
{
return true;
}
return true;
}
But basically when I hit the EnsureSecureTransaction() block its still saying my Session["UserId"] is null and therefor not autehenticating the call to the .ashx file correctly.
Anyone know why this is? Whats the actual difference between AuthorizationContext and HttpContext with regards to the session they carry and how do I get round this problem?
I am having issues with frequent Session Time Out.
I want to write a common filter that I could use on each controller, filter should redirect the user to login and after log in back to from where user sent the last request.
You could try something like this:
public class SessionExpireAttribute : ActionFilterAttribute {
public override void OnActionExecuted(ActionExecutedContext filterContext) {
base.OnActionExecuted(filterContext);
}
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if (filterContext.HttpContext.Session != null) {
if (filterContext.HttpContext.Session.IsNewSession) {
var sessionCookie = filterContext.HttpContext.Request.Headers["Cookie"];
if ((sessionCookie != null) && (sessionCookie.IndexOf("ASP.NET_SessionId") >= 0)) {
// redirect to login
}
}
}
}
}
There's more here than meets the eye. Here's a more complete OnActionExecuting that uses the same concept already discussed above but adds a bit more. See inline comments for more info. The "InitializeSession" being called is a custom function which creates the basic attributes needed in Session State for running the site. "AlertWarning" is a Helper routine for displaying alerts. Everything else is boilerplate code.
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var bRequiresAuthorization =
(filterContext.ActionDescriptor.GetCustomAttributes(typeof(AuthorizeAttribute), false).Length > 0) ||
(filterContext.Controller.GetType().GetCustomAttributes(typeof(AuthorizeAttribute), false).Length > 0);
if (filterContext.HttpContext.Session != null)
{
if (filterContext.HttpContext.Session.IsNewSession)
{
//New session. Initialize Session State
bool b = InitializeSession(null);
if (bRequiresAuthorization )
{
//Action requested requires authorized access. User needs to authenticate this
//new session first, so redirect to login
string cookie = filterContext.HttpContext.Request.Headers["Cookie"];
if ( (cookie != null) && (cookie.IndexOf("_SessionId=") >= 0) )
{
//An expired session cookie still resides on this PC, so first alert user that session is expired
AlertWarning("Session timed out due to inactivity. Please log in again.");
}
filterContext.Result = RedirectToAction("LogOut", "Authentication");
}
}
}
base.OnActionExecuting(filterContext);
}
Have you tried the existing Authorize filter?
as mentioned above .. try this
public class SessionExpireAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if (filterContext.HttpContext.Session != null) {
if (filterContext.HttpContext.Session.IsNewSession) {
filterContext.Result = new RedirectResult("/");//redirect to home page
}
}
}
}
and then apply this filter over the action or controller [SessionExpire]
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.
I modify the default route rule a little bit as below:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id= (string)null } // Parameter defaults
);
Then I can set url as:
/Controller/Action/myParam
/Home/Index/MyParam
The default Action Index would be:
public ActionResult Index(string id)
{
//....
}
I can get the param in action. But I want to get the param in OnActionExecuting. How can I do it?
You should be able to access it with :
public override void OnActionExecuting(ActionExecutingContext filterContext) {
string id = filterContext.RouteData.Values["id"];
//...
}
It can be accessible from ActionArguments inside OnActionExecuting.
public override void OnActionExecuting(ActionExecutingContext context) {
string id = context.ActionArguments["id"].ToString();
//...
}
if you want to get controller, action, and all parameters, you can do this
var valuesStr = new StringBuilder();
if (ctx.RouteData != null && ctx.RouteData.Values != null)
foreach (var v in ctx.RouteData.Values)
valuesStr.AppendFormat("/{0}", v.Value);
_logger.Info("executing {0}", valuesStr.ToString());
which results in the whole path
results with:
"/Get/Customer/215840"
it should work on multiple parameters just as well.
I use the following code to retrieve and compare the parameters passed to an action (.net core 3.1).
var vals = filterContext.ActionArguments.Values;
var fistobj = vals.FirstOrDefault();
var val = fistobj.GetType().GetProperties().FirstOrDefault(x => string.Equals(x.Name, "nameParameter", StringComparison.OrdinalIgnoreCase)).GetValue(fistobj);
if (val == null || val.ToString() != "value parameter")
{
filterContext.Result = new JsonResult(ExecuteResult.Fail(JanException.Parameter.API00001));
//base.OnActionExecuting(filterContext);
return;
}
More details for OnActionExecuting and a custom Attribute InitializingActionAttribute
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
ControllerActionDescriptor controlActionDescriptor = (ControllerActionDescriptor)filterContext.ActionDescriptor;
var attributes = controlActionDescriptor.MethodInfo.CustomAttributes;
if (attributes.Any(a => a.AttributeType == typeof(InitializingActionAttribute)))
{
var vals = filterContext.ActionArguments.Values;
var fistobj = vals.FirstOrDefault();
var val = fistobj.GetType().GetProperties().FirstOrDefault(x => string.Equals(x.Name, "nameParameter", StringComparison.OrdinalIgnoreCase)).GetValue(fistobj);
if (val == null || val.ToString() != "value parameter")
{
filterContext.Result = new JsonResult(ExecuteResult.Fail(JanException.Parameter.API00001));
//base.OnActionExecuting(filterContext);
return;
}
}
base.OnActionExecuting(filterContext);
}
From your filterContext you should be able to get whatever you need.
public class MyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Do your stuff here
}
}
[MyAttribute]
public ActionResult Index(string id)
{
//....
}