I have created a antiforgery attribute class to decorate my GenericBaseController class:
[AttributeUsage(AttributeTargets.Class)]
public class ValidateAntiForgeryTokenAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var request = filterContext.HttpContext.Request;
// Only validate POSTs
if (request.HttpMethod == WebRequestMethods.Http.Post)
{
// Ajax POSTs and normal form posts have to be treated differently when it comes
// to validating the AntiForgeryToken
if (request.IsAjaxRequest())
{
var antiForgeryCookie = request.Cookies[AntiForgeryConfig.CookieName];
var cookieValue = antiForgeryCookie != null
? antiForgeryCookie.Value
: null;
AntiForgery.Validate(cookieValue, request.Headers["__RequestVerificationToken"]);
}
else
{
new ValidateAntiForgeryTokenAttribute()
.OnAuthorization(filterContext);
}
}
}
}
(reference link http://richiban.uk/2013/02/06/validating-net-mvc-4-anti-forgery-tokens-in-ajax-requests/ )
once a normal POST call in application is done (not ajax), I always get a StackOverflowException.
Application without ValidateAntiForgeryTokenAttribute works fine.
If I debug the code inside this class, after a post request, flow keeps going trough the line
new ValidateAntiForgeryTokenAttribute()
.OnAuthorization(filterContext);
infinitely.
People in linked article assure that this implementation works, so I'm wondering why I'm getting this problem.
Is it really supposed to create a new ValidateAntiForgeryTokenAttribute when the request is not ajax ?
Boiled down to the problem, your code is:
public class ValidateAntiForgeryTokenAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if ( evaluateCondition() )
{}
else
{
new ValidateAntiForgeryTokenAttribute()
.OnAuthorization(filterContext);
}
}
}
The problem
Your call is recursive in the else block:
The class you are calling the method on is ValidateAntiForgeryTokenAttribute.
In your else block you have
new ValidateAntiForgeryTokenAttribute()
.OnAuthorization(filterContext);
which, given that the calling method is
public override void OnAuthorization(AuthorizationContext filterContext)
means that you will keep calling OnAuthorization (i.e. the same method) on new instances of a ValidateAntiForgeryTokenAttribute.
Solution
In the example you posted, the situation was slightly different - the name of the class is ValidateAntiForgeryTokenOnAllPosts whereas yours is ValidateAntiForgeryTokenAttribute, so the call is not recursive since the method is not calling itself with the same arguments.
You have three options - I'm not sure which is best for your situation (I'm thinking the first one):
Change your Attribute name to ValidateAntiForgeryTokenOnAllPosts to match the name in the example you posted.
Explicitly state that you want System.Web.Mvc.ValidateAntiForgeryTokenAttribute by changing the block to say
new System.Web.Mvc.ValidateAntiForgeryTokenAttribute()
.OnAuthorization(filterContext);
Since you are overriding ValidateAntiForgeryTokenAttribute, you can call the base method, i.e.
else
{
base.OnAuthorization(filterContext);
}
Related
I am working on a project that is a web application. The application should have a single button on the page that redirects the user back to the same page. In 1 of every 5 (or thereabout) occasions when you press the button the program should throw some exception and log it.
It should catch the exception and do three things: Send an email, write the error in a file and write it to debug window.
My controller so far:
public class HomeController : Controller
{
public ActionResult Index()
{
try
{
RedirectToAction("Index");
}
catch (ArgumentException e)
{
}
Random RandNumber = new Random();
int rand = RandNumber.Next(1000);
if(rand % 5 == 0)
{
throw new System.ArgumentException("This is a random excpetion");
}
return View();
}
}
The idea is the to have the class Logger that declares a collection of the class LogMedia and loops through all the instances.
Class Logger:
public class Logger
{
List<LogMedia> m_loggers = new List<LogMedia>();
void LogException(Exception ex)
{
foreach(var i in m_loggers)
{
//Loop through all Log Media instances
}
}
Class LogMedia
class LogMedia
{
public virtual void LogMessage(string Message); //virtual function that doesen't do anything
}
class OutputWindowLogMedia:LogMedia
{
public override void LogMessage(string Message)
{
System.Diagnostics.Debug.WriteLine(Message);
}
}
class TextFileLogMedia : LogMedia
{
public override void LogMessage(string Message)
{
File.AppendAllText("c:\\Temp\\Log.txt", Message);
}
}
class EmailLogMedia : LogMedia
{
public override void LogMessage(string Message)
{
//send email
}
}
}
My questions to you are the following:
Will my controller work as it stands now with what I am trying to implement? I am especially skeptical of my try catch..
What is the best way to loop though these three instances?
Do I have the right class declerations for Log Media, i.e. should I create new classes(that inherits LogMedia) for each instance and then a function that overrides the virtual function in LogMedia.
It's unclear if you want this behavior on this particular action, the controller, or the entire app. That said, unless there is some specific recovery code you want to build into your logic, I wouldn't pollute my action code with the try-catch.
There are two options the MVC framework provides to handle errors:
First, you can override OnException in a specific controller:
protected override void OnException(ExceptionContext filterContext)
{
// do your logging here
// set this flag if you want to stop the exception from bubbling
filterContext.ExceptionHandled = true;
}
Second, you can create an error handling filter:
public class MyExceptionFilterAttribute :
System.Web.Mvc.FilterAttribute,
System.Web.Mvc.IExceptionFilter
{
public void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
// same error handling logic as the controller override
}
}
A filter can either be added to the global filters list, or applied to an action method like this:
[MyExceptionFilter]
public ActionResult Index()
{
}
Edit: forgot to mention your logger structure. Your approach of having Logger loop over multiple instances of LogMedia is a good, common approach to supporting multiple logging mechanisms with one common interface (log4net appenders for example). That said, have you considered using an existing, proven framework for your logging needs? You get a thoroughly tested framework to run, and a skill that will carry over to future endeavours.
Edit2: after your comment, I took a closer look at your code instead of your exception trapping approach. In your Index action, you're redirecting to Index with no condition checking. This is going to end up as a constant redirect loop (until IIS stops you). Since this is an assignment, I don't want to give too much away, but you will need some logic on the server side to detect a button click and redirect back to your Index. Consider another Index action method that accepts HttpPost (your current action would be the HttpGet handler).
I'm using ravendb as storage backend. Since it uses unit of work pattern I need to open session, perform actions, save results and close session. I want to keep my code clean and don't call session opening and closing explicitly in each action, so I put this code to OnActionExecuting and OnActionExecuted methods, like this:
#region RavenDB's specifics
public IDocumentSession DocumentSession { get; set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.IsChildAction)
{
return;
}
this.DocumentSession = Storage.Instance.OpenSession();
base.OnActionExecuting(filterContext);
}
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.IsChildAction)
{
return;
}
if (this.DocumentSession != null && filterContext.Exception == null)
{
this.DocumentSession.SaveChanges();
}
this.DocumentSession.Dispose();
base.OnActionExecuted(filterContext);
}
#endregion
But some actions require connection to ravendb and come don't. So I've decided to create custom attribute and mark methods need to have DocumentSession opened with it. Here is an example:
//
// GET: /Create
[DataAccess]
public ActionResult Create()
{
return View();
}
And I stuck. My plan was to retrieve actions' attributes in the OnActionExecuted method and if [DataAccess] is present, open DocumentSession.
In the OnActionExecuted I can retrieve action name (method's name) via filterContext.ActionDescriptor.ActionName statement. But how I can retrieve method's attributes of the given class using reflection?
I found out that it might be Attribute.GetCustomAttributes call, but closest I got — I need to have MemberInfo object of the method. But how I can get this MemberInfo for method given by name?
If you inherit your custom attribute from FilterAttribute, it will have OnActionExecuted and OnActionExecuting methods. And it will be executed before general OnActionExecuted and OnActionExecuting.
Example:
public class DataAccessAttribute: FilterAttribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.IsChildAction)
{
return;
}
var controller = (YourControllerType)filterContext.Controller;
controller.DocumentSession = Storage.Instance.OpenSession();
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.IsChildAction)
{
return;
}
var controller = (YourControllerType)filterContext.Controller;
documentSession = controller.DocumentSession;
if (documentSession != null && filterContext.Exception == null)
{
documentSession.SaveChanges();
}
documentSession.Dispose();
}
Why not have your DataAccess attribute inherit from ActionFilterAttribute so you can put the ActionExecuting/Executed methods on the attribute instead of the Controller?
Example of how to do it with NHibernate by using an action filter to set the session on a base controller. It's done using NHibernate, but very similar to what you'd need to do and it's written by Ayende who's one of the RavenDB authors I believe.
Currently I have this line of code at the top of my master page
#{
MyApp.Domain.Concrete.FullUserProfile fullUser = (MyApp.Domain.Concrete.FullUserProfile)HttpContext.Current.Session[Membership.GetUser().ProviderUserKey.ToString()];
}
This gets the user's profile information in the cache, but my question is will this always be available? What if the user is logged in for a long time or something. How do I make it so his or her information isn't stored in session it will retrieve it from database again? I have a hard time understanding because I don't know if all this code should be in the view, and if it is how u would call a method from the code behind in order to trigger the database call and saving to the cache. The reason I didn't want to put this in the controller it seemed repetitive.
You could do this:
#{
MyApp.Domain.Concrete.FullUserProfile fullUser = null;
if (Membership.GetUser() != null && Session[Membership.GetUser().ProviderUserKey.ToString()] != null) {
//Logged in
fullUser = (MyApp.Domain.Concrete.FullUserProfile)HttpContext.Current.Session[Membership.GetUser().ProviderUserKey.ToString()];
} else {
//Not logged in logic
}
}
Or you could try this, but I'm not entirely sure because I don't use the MembershipProvider system a lot:
if (Page.User.Identity.IsAuthenticated)
{
//Logged in
}
else
{
//Not logged in
}
But, if you're doing that you can just set the forms authentication required for whatever pages/directories you need them to be logged in your web.config.
If you need to write code that will be used by several or all of your controllers and/or actions, you can create filters and write inside them the logic you need, lastly you just need to apply your filter at Controller or Action level.
For more info:
http://msdn.microsoft.com/en-us/library/dd410056(v=vs.90).aspx
Example:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CustomFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// execution order: 1
// your actions here
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
// execution order: 2
base.OnActionExecuted(filterContext);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
// execution order: 3
base.OnResultExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
// execution order: 4
base.OnResultExecuted(filterContext);
}
}
Notice the execution order. The members of IActionFilter are executed before the action and after the action has returned an object inheriting from ActionResult (but the result has not been parsed yet, which means that the HTML has not been generated yet). At this point the members of IResultFilter are executed, before and after the ActionResult object generates the HTML
You would usually have a ViewModel that contains the data of the user you need to display.
An action filter would fill the data, because you are worried about repetition
I would not store the user in session state. Its hard to keep it in sync. (session and authorization time out at different times)
To check for authorization you do not need to access the profile use:
HttpContext.Current.User.Identity.IsAuthenticated
I have several WebMethods in a C# ASP.NET web app. I'd like to change the behavior of all of them to validate each request. Imagine the code below:
[WebMethod]
public static void DoSomething()
{
if (ValidateRequest())
{
HttpContext.Current.Response.StatusCode = 400;
// do some other stuff
return;
}
// rest of method
}
I've noticed of course that the ValidateRequest() method call is common to a large portion of my WebMethods. Is there anyway I can wire this up so that all WebMethods automatically have this same behavior? Could I add a second attribute to the method to accomplish this?
Add the validate request in the Begin Request of your Global.asax file.
Now, you need some sort of code to check if the request should be validated.
I'm unsure how to do this in webforms... But, what I'd do is:
use the RequestPath property (and get the method and class name if they match your service URL)
HttpContext.Current.Request.Path;
Then I would create a method attribute and perhaps use reflection to see if the request should be validated. (see link below)
http://msdn.microsoft.com/en-us/library/z919e8tw.aspx
This way from this point on you just need to tag your method with your "[Validate]" attribute and it all should just work.
public class Global : HttpApplication
{
protected void Application_BeginRequest(object sender, EventArgs e)
{
if(ShouldValidate() && !IsValidRequest()){
//add your custom error status here perhaps
Response.StatusCode = 400
Response.StatusDescription = "Something Bad happened"
HttpContext.Current.Response.End()
}
}
I was asking a related question but messed the title up and no-one would understand it. Since I am able now to ask the question more precisely, I decided to reformulate it in a new question and close the old one. Sorry for that.
So what I want to do is passing data (my custom user's nickname as stored in the db) to the LoginUserControl. This login gets rendered from the master page via Html.RenderPartial(), so what I really need to do is making sure that, say ViewData["UserNickname"] is present on every call. But I don't want to populate ViewData["UserNickname"] in each and every action of every controller, so I decided to use this approach and create an abstract base controller which will do the work for me, like so:
public abstract class ApplicationController : Controller
{
private IUserRepository _repUser;
public ApplicationController()
{
_repUser = RepositoryFactory.getUserRepository();
var loggedInUser = _repUser.FindById(User.Identity.Name); //Problem!
ViewData["LoggedInUser"] = loggedInUser;
}
}
This way, whatever my deriving Controller does, the user information will already be present.
So far, so good. Now for the problem:
I can't call User.Identity.Name because User is already null. This is not the case in all of my deriving controllers, so this is specific for the abstract base controller.
I am setting the User.Identity.Name via FormsAuthentication at another place in the code, but I think this can't be the problem - afaik User.Identity.Name can be null, but not User itself.
It looks to me like the HttpContext is not available (since also null ;-) and that I am missing a simple yet important point here. Can anyone give me some hints? I would really appreciate it.
The answer to this problem is actually quite simple. I can't execute the code from within the constructor for reasons pointed out by Raimond, but I can do it outside the constructor.
So what I did was overriding onActionExecuting() in the base controller class (I created a custom Attribute for it, but just overriding the method should also work) and then do my user lookup from there.
Now it works as expected and I have no repeated code.
The User property is not assigned until after the Controller has been instantiated, but you can gain early access from your constructor with:
System.Web.HttpContext.Current.User
My guess would be that the Controller's base constructor is not filling in the User, but that it is only known later when the ControllerContext is set for the Controller. You should check this in the documentation about the lifecycle of an MVC application, (the one here will probably do, although it might be a bit out of date since it's for the preview version), or just check the source code of MVC.
from the code that I have of MVC (also a preview version, but that should be fine):
(In Controller)
public IPrincipal User {
get {
return HttpContext == null ? null : HttpContext.User;
}
}
...
public HttpContextBase HttpContext {
get {
return ControllerContext == null ? null : ControllerContext.HttpContext;
}
}
I don't see en an implementation of a default constructor in the code.
That would prove that the ControllerContext is null at the time of construction.
So you should execute your code somewhere else.
Can you grab this using something like:
HttpContext currentContext = HttpContext.Current;
string userName = currentContext.User.Identity.Name;
Or is the HttpContext always empty??
Could you set the httpContext through the constructor of the abstract class? and use it this way?
Thanks Raimond. I was too tired to see the obvious.
#Keeney: Yes the context is always null. Raimond pointed out why. Thanks anyway, I didn't see why too :-)
My current working solution (albeit not what I wanted) is a Attribute that I use to decorate all my controller actions. Here is the implementation:
public class MasterPageDataAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
IUserRepository _repUser = RepositoryFactory.getUserRepository();
IPrincipal siteUser = filterContext.Controller.ControllerContext.HttpContext.User;
User loggedInUser = null;
if (siteUser == null || siteUser.Identity.Name == null)
{
//do nothing
}
else
{
loggedInUser = _repUser.findUserById(siteUser.Identity.Name);
}
filterContext.Controller.ViewData["LoggedInUser"] = loggedInUser ?? new User { Nickname = "Guest" };
}
}
I will be looking into how to get that code executed in a way that follows the DRY principle, since using attributes for that definitely means repeating oneself. Maybe some sort of interceptor (interesting idea) or hook might help.
Cheers for that.
I am doing this in a basecontroller implementation and it works as expected.
public abstract class BaseController : Controller
{
public bool LoggedOn
{
get { return User.Identity.IsAuthenticated; }
}
}
This always returns true or false for me so User != null
to Masterfu:
I did something similiar with your help, wish that can help latter visitors.
In my case, i need to create reposiotry of controllers for different users, yet in the constructor of controllers, (principal)User is not ready. So i created a attribute for controllers:
[CreateRepositoryByUser]
public class MFCController : Controller
{
protected MFCRepository _repository
{
get { return ViewData["repository"] as MFCRepository; }
}
...
the _repository, indeed, is not a private variable of controller, but somethign create by the attribute:
public class CreateRepositoryByUser : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
CreateRepository(filterContext);
}
public static void CreateRepository(ActionExecutingContext filterContext)
{
if (filterContext.Controller.ViewData["repository"] == null)
{
filterContext.Controller.ViewData["repository"] =
MFCRepository.CreateMFCRepository(filterContext.Controller.ControllerContext.HttpContext.User);
}
}
}
I put codes of creating the repository in a seperate method, in case of that other attributes may want to use (principal)User before this attribute being triggered.
Calling from a constructor is too soon in the MVC pipeline.
Moving code to OnAuthorization, you get authorized user in a parameter. Worked for me!
From your example I would do something like this:
public abstract class ApplicationController : Controller {
private IUserRepository _repUser;
protected override void OnAuthorization(AuthorizationContext filterContext)
{
_repUser = RepositoryFactory.getUserRepository();
var loggedInUser = _repUser.FindById(filterContext.HttpContext.User.Identity.Name); //Problem!
ViewData["LoggedInUser"] = loggedInUser;
}
}
Inject IPrincipal if you need User in the constructor.
// startup.cs
// Inject IPrincipal
services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);
Then add as IPrincipal in your constructor. Note that it is guaranteed to be ClaimsPrincipal with ASPNET - because that's what HttpContext.User is.
Similar question
Select Project -> press F4 -> anonymous login -> false | windows authentication - > True