I am administrator of a small practice project web application, AngularJS front-end pulling its back-end data from a C#/.NET WebAPI, and I'm handling security using the SimpleMembershipProvider.
I suspect that the way I implemented said security is not the best (I'm told ASP.NET Identity is now the way to go?) but that's another question altogether.
The issue that I'm very bewilderingly running into is that I get occasional reports that on a given page load to display a particular user's data, it returns somebody else's. Reloading the page fixes the issue (evidently) and I haven't been able to duplicate the scenario myself, or figure out anything particularly consistent in the users to which this happens.
None of the information being displayed is at all sensitive in nature (the app's just a friendly front end for an already public third-party API) so I'm not in panic mode about this, but I am both concerned and confused and want it fixed.
Here is what one of my API controller endpoints looks like:
[Authorize]
public class UserController : ApiController
{
private static int _userId;
private readonly IUserProfileRepository _userProfileRepository;
public UserController()
{
_userProfileRepository = new UserProfileRepository(new DatabaseContext());
_userId = WebSecurity.GetUserId(User.Identity.Name);
}
public UserProfileDto Get()
{
return _userProfileRepository.GetUserProfileById(_userId).ToDto();
}
}
Any feedback on where I might be going wrong here or what might be causing the intermittant inconsistency would be very much appreciated. (Laughter also acceptable if the way I handled this is just really bad. :P )
Static class fields are shared by all instances/threads of the same AppDomain (in your case - process). Different http requests are processed by threads running in parallel. Any two threads running [almost] at the same time may (will) change the value of _userId. You are assigning _userId in the constructor of your controller, and a new instance of this controller is created for each http request that is to be responded to by UserController. Therefore, this assignment will happen multiple times.
You will have hard time replicating this problem, since you are a single user testing the code, hence there are no overlapping request threads.
Remove static specifier from the _userId field declaration of the controller class.
Note: make sure that DatabaseContext is disposed of. One place that can be used for this is the overriden Controller.Dispose.
Change the Get to retrieve the user id rather than from a static variable:
public UserProfileDto Get()
{
return _userProfileRepository.GetUserProfileById(WebSecurity.GetUserId(User.Identity.Name)).ToDto();
}
Related
This is a practice ASP.NET project I'm using to better understand a few techniques, and while I've got Dependency Injection working, its not working quite as I want it to. I have a class that I want to use to store a history, so every time the user hits a submit button, it displays a result, and after the second time it starts displaying the history. Anyway I added the history to the DI as a scoped service, thinking that would mean it would be created and then remain the same instance for the duration of the session for that user. However according to the debugger it looks like the list never gets bigger than one, and thats at the point of adding the item to the list. So the code.
The object
{
public class RollHistory : IRollHistory
{
public List<IRollMessage> Entries { get; set; } = new List<IRollMessage>();
}
}
The DI
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddTransient<IDiceTray, DiceTray>();
services.AddTransient<IRollMessage, RollMessage>();
services.AddScoped<IRollHistory, RollHistory>();
}
The Controller constructor
public HomeController(ILogger<HomeController> logger, IDiceTray diceTray, IRollMessage rollMessage, IRollHistory rollHistory)
{
_logger = logger;
_diceTray = diceTray;
_rollMessage = rollMessage;
_rollHistory = rollHistory;
}
And the code for when the button gets clicked
[HttpPost]
public IActionResult Index(DiceRollModel diceRoll)
{
_diceTray.DiceRoll(diceRoll.DiceType, diceRoll.DiceCount, diceRoll.Bonus, diceRoll.VantageType);
_rollMessage.RollMessages(_diceTray);
diceRoll.RollResult = _rollMessage;
_rollHistory.Entries.Add(_rollMessage);
diceRoll.History = _rollHistory.Entries;
return View(diceRoll);
}
It's worth noting I've tried to code this at least 4 different ways with and without DI, the only way it works is if I use AddSingleton, while this might not be an issue because this app is unlikely to ever be live, its a poor excuse not to do it right.
I believe “scope” is by default per request which would explain that each submit gets is own service.
“Doing stuff right” is of course to some extend a matter of opinion. But my opinion would clearly be that I would avoid server-side session to avoid problems with scaling to more than one instance. There are also ways to support shared state, but this is difficult. To me singletons are not a code smell either, but they have their own problems.
Your problem might be solved by storing whatever state you need in the browser either in a cookie or localStorage. Your service would then have request scope, but it would read user state from browser causing “user scope” for the data. (But don’t rely on browser state to persist and remember it is modifiable to the user.)
I am pretty much new in Asp.Net Core world and I am playing around with services now. I had an idea to create instance of class I created (named CourseEditor) that will be accessible through whole controller (in every action on that very controller). So I added the class as a Scoped service to Startup.cs and method ConfigureServices:
services.AddScoped<CourseEditor>();
Now I have my controller CourseEditorController.cs.
[Authorize(Roles = "Admin")]
[ViewLayout("_CourseEditorLayout")]
public class CourseEditorController : Controller
{
private CourseEditor _courseEditor;
private readonly SignInManager<IdentityUser> _signInManager;
public CourseEditorController(SignInManager<IdentityUser> signInManager, CourseEditor courseEditor)
{
_courseEditor = courseEditor;
_signInManager = signInManager;
}
[HttpGet]
public async Task<IActionResult> OpenCourse(int courseId)
{
Database db = new Database();
_courseEditor = await db.LoadCourseForEditByIDAsync(courseId);
return RedirectToAction("Index");
}
public IActionResult Index()
{
return View(_courseEditor);
}
public IActionResult EditHead()
{
return View(_courseEditor);
}
}
And I am quite stuck. Because every time I load into this controller, the _courseEditor is rewritten to default values. So now I am trying to figure out, how to alter the parameters of the service CourseEditor itself so it won't "reset" the _courseEditor every time I jump between actions.
So basically I am trying to alter the service CourseEditor.Title in controller CourseEditorController.cs because it's by default null and it's rewriting the _courseEditor.Title from actual text to null. Can I do that?
//Edit:
I forgot to explain how the Controller works. So basically when user moves to this "editor" controller, first it goes through action "OpenCourse" that will load all the data as _courseEditor.Title and stuff from MySQL Database. But as you can see, after that there is a RedirectToAction("Index"). So the _courseEditor is run through the constructor and there is everything set back to null since it's the value that was set when the program was initializing the service. Or at least I think this is happening.
So the solution is to add the service not as a Scoped service, but as a Singleton.
services.AddSingleton<CourseEditor>();
Unfortunately for me this does not solve anything since Singleton represents one instance for whole application which means every user will see data from this very instance of editor. They cannot create their "own" instance of editor.
One way how to reach that possibility of creating more instances is via ConcurrentDictionary (thank you #Legacy Code for the explanation). More details about this static dictionary is here in Asp.net core docs:
https://learn.microsoft.com/cs-cz/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=netcore-3.1
Second way is probably to just recover the data from database on every Action call.
Another way might be to use some kind of transporter as Cookies but this approach is not very secure since User can manipulate with cookies and it might be really hard to store a complex object there.
OutputCache is an easy way to improve performance in ASP.NET <=4.6 MVC. We should apply this attributes for actions that are very expensive, but between the first request and the cache being created we could have a lot of concurrent requests - and will consume the server resources that we are trying to protect (Memory, CPU, I/O).
If multiples requests are from the same user, by default, aspnet serialize
the requests as we can see in this link, The Downsides of ASP.NET Server Sate:
by default the ASP.NET pipeline will not process requests belonging to
the same session concurrently. It serialises them, i.e. it queues them
in the order that they were received so that they are processed
serially rather than in parallel.
So, in order to simulate this situation, we need to create multiples requests (from different users). It can be done by creating request from more than one browser or using any kind of workload with multiple threads.
So, I'm trying to inherits from OutputCacheAttribute and create something like OutputCacheConcurrentRequestAttribute and serialize the requests from multiples users. Have anyone tried to do that?
The idea is to Thread.Sleep all the subsequent requests for the same resource (Considering unique by the OutputCache properties and action) on OnActionExecuting while the first request is not finished.
Looking the IL in ILSpy, some methods, which could help me in this job, are private and internal, like those:
Assembly: System.Web.Mvc
Namespace: System.Web.Mvc
class OutputCacheAttribute
{
private GetChildActionUniqueId
private BuildUniqueIdFromActionParameters
}
internal class DescriptorUtil
I'm trying to not duplicate the code and not use reflector (For performance reasons).
Have anyone faced the same problem?
Edit 1
After some research I have found that the MVC Donut caching have faced a lot of difficults to inhertis the OutputCacheAttribute and they have rewritten it completely:
In terms of implementing donut caching, hooking into the
OutputCacheModule HttpModule would be very difficult/impossible, so
instead we must rewrite the OutputCacheAttribute completely
It was much more easy to complete my idea using the DonutOutputCacheAttribute.
Are you actually seeing this as a performance issue or is this just theoretical? I would caution that going too far down this road can hurt performance... maybe consider a solution that pre-warms the cache?
That said, you might have better luck with just a lock in your action you are trying to make just execute once. Create a variable, and do a lock on that variable in an action filter (before the action fires), and release in your action filter after the action fires, and that should serialize your requests.
public class SerializedOutputCacheAttribute : OutputCacheAttribute
{
private readonly object lockObject = new object();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Monitor.Enter(this.lockObject);
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
Monitor.Exit(this.lockObject);
}
}
You can then apply it to any action, and that action will be serialized but not affect other actions.
[SerializedOutputCache(Duration = 5000)]
public ActionResult About()
EDIT: This approach wont work because OutputCacheAttribute actually pushes to IIS for the caching, so the action will still fire multiple times.
I've got an ASP.NET that relies on some code that uses static constructors. The code in these type initializers sometimes fails. Let's say, for sake of argument, that the code is:
public static readonly string Thing = SomeSpecialCallThatRarelyFails();
Perhaps that's vile, but it cannot be changed. And this kinda code is in every controller, so ASP.NET can't create the controller and just sits there broken until someone comes along to restart it.
I understand this is the way it should be, because the problem may very well be non-transient and auto-restarting would create a loop. Or perhaps only one controller fails, so the app is still sort of alive. So I get the default behaviour to just keep returning the error. But in this particular case, let's pretend the best thing is to notice this failure and restart.
How can I automate the detection of this scenario and trigger a restart or recycle of the IIS app pool/AppDomain?
I've noticed that if I cause an exception on Application_Start, then the app will auto-restart. So one way is for me to iterate over all my types and try accessing them. If they have .cctor failures, then I'll crash Application_Start and ASP.NET will restart. But that's pretty hacky, plus it won't help if the actual request code references another type that I don't know about which throws on .cctor.
Is there a better way? Should I write a Web API filter and look for TypeInitializerException or something?
Just a thought. Is the 'rare failure' deterministic? Could it be solved by adding retry logic?
public static readonly string Thing = RetrySpecialCall();
private static string RetrySpecialCall()
{
while (true)
{
try
{
return SomeSpecialCallThatRarelyFails();
}
catch (Exception) {}
}
}
So here's a way to handle it in Web API 1:
In Application_Start, iterate over your controller types, calling System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor to force all your known type constructors to run. If it Application_Start fails, ASP.NET seems to restart.
Add an exception filter that looks for TypeInitializationExceptions. Then call HttpRuntime.UnloadAppDomain().
The two parts are needed as a controller failing to construct will not hit the exception filters.
With Web API 2 it seems like you could do it in one go by implementing System.Web.Http.ExceptionHandling.IExceptionLogger and registering it as a global service. Same logic: check for TypeInitializationException and UnloadAppDomain if so.
Hey there, I have my own Membership-Provider where I open a NHibernate-Session in the constructor:
public class OwnMembershipProvider : MembershipProvider
{
protected NHibernate.ISession HibSession;
public OwnMembershipProvider ()
{
HibSession = NHibernateTools.OpenSession();
}
//...
I figured out if I set a breakpoint into the constructor, it is called only once during application start. This gives me a headache since the same NHibernate-Session is used for each request, leading to funny things like "oh, i can't change my password" (the NHibernate-Session returns a cached user when calling ValidateUser(), which still contains the old password).
How can I force the framework to reconstruct the MemberShipProvider for each single request? Or how should it be done different?
Thx for any tipps
Opening a separate session in each method sounds like a good idea. Better yet, open one session and transaction for each HTTP request and then commit the transaction when request ends.