I'm trying to unit test a page in my ASP.NET MVC application that, when posted to, will delete a user's item. I want to restrict this page so that it can only be posted to by the owner of said item. Originally, I wanted to just stick a quick check in the controller that checked if the HttpContext.Current.User.Identity.Name is equal to the owner of the item, but I quickly realized that unit testing this would be difficult.
Should I create an interface that provides a way to access the current logged-in user's name?
You can use this code
Controller CreateControllerAs(string userName) {
var mock = new Mock<ControllerContext>();
mock.SetupGet(p => p.HttpContext.User.Identity.Name).Returns(userName);
mock.SetupGet(p => p.HttpContext.Request.IsAuthenticated).Returns(true);
var controller = new SomeController();
controller.ControllerContext = mock.Object;
return controller;
}
It uses moq lib
The last line of your question is probably the quickest solution, however, I don't think you need to create the interface, HttpContext.Current.User is already an IPrincipal and HttpContext.Current.User.Identity is already an IIdentity.
If your checking logic uses either of those interfaces, the controller can be mocked to pass an IIdentity that you create instead of the HttpContext.Current.User.Identity
Hope that helps!
I use Thread.CurrentPrincipal.Identity.Name. It's trivial to create a GenericPrincipal for a unit test and attach it.
Related
I have Unit tests for MVC6 controllers where the controller has the [AllowAnonymous] attribute. However a number of my controllers do not have this attribute and I am unable to test those controllers. Also some controllers which allow anonymous access return different data sets depending upon whether the user is authenticated or anonymous.
I have not been able to determine how to set the User details for use in a test and am beginning to think it may not be possible. I hope this is not the case.
Jerrie Pelser posted this exact issue in his blog in Oct. However since then the framework has changed and it 'almost' works. I did find a solution to the problem and posted it in the comment on his blog.
http://www.jerriepelser.com/blog/unit-testing-controllers-aspnet5
Basically read the blog and my comments at the end, then create your controller like this.
var controller = new MyController()
{
ActionContext = new ActionContext
{
HttpContext = httpContext
},
Url = new UrlHelper(),
TempData = Mock.Of<ITempDataDictionary>()
};
Just curious what would be the best way to do this?
used to using
if (User.IsInRole("Administrator"))
with an older program of mine,
Using the new Kendo templete( new to me ) for MVC4-VS2012 Projects it created everything w/o simplemembership, so i wrote my own login system with help of some youtube videos and documentations.
It is very flexible and works, just curious on how I can check if a user is in a Roll Via A view ( like if (User.IsInRole("Administrator")) ) would have done. And or the best way to do so ( possibly in controller )
You can continue to use User.IsInRole(""), you simply need to set HttpContext.User with the correct principal that has a list of roles.
You can create your own Authorization Attribute that would take care of this:
var websiteRoles = userRepo.GetRoles(HttpContext.User.Identity.Name);
var identity = filterContext.HttpContext.User.Identity as FormsIdentity;
filterContext.HttpContext.User = new System.Security.Principal.GenericPrincipal(identity, websiteRoles.ToArray());
Follow up:
Your GetRoles() method can be implemented any way you like, you simply need to have a list of roles that the user belongs to. You will use this list to create the Principal object to set the User to.
Using this approach will allow you to use User.IsInRole()
I've had a thorough search around but really can't find anything addressing the scenario I'm facing (oddly because I'd have thought it's quite a common thing to do).
Background
I'm creating an application with ASP.NET MVC 4 and Entity Framework 5 Code First. For the purpose of this question, think of it as a blogging application with posts and users.
Project
The post model requires that every post have a corresponding UserId.
With the ASP.NET MVC 4 Membership it is easy to find the username of the person logged in with
User.Identity.Name.
This isn't ideal, we want the ID, but a query such as this can search the db for the name and get the ID.
db.UserProfiles.Single(a => a.UserName == User.Identity.Name);
Problem
The problem arises when trying to create a post. Model.IsValid is false, as no UserId is being passed in from the view. Obviously, as the user isn't expected to enter their ID.
I've tried putting the ID value into the ViewBag and using a #Html.Hidden() field in the view, however I've had no success with this. Model.IsValid always returns false.
Should this information be input through the create view? Or should it be done directly in the controller? Its quite a frustrating problem as I have the information and just need to figure how to pass it into the model.
CONTROLLER CODE
This is basically just the default scaffolded code. The commented code is how I tried setting the model value directly from the controller, however that was little more than trial and error.
//
// POST: /Post/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Post post)
{
if (ModelState.IsValid)
{
//var userId = db.UserProfiles.Single(a => a.UserName == User.Identity.Name);
//post.User.UserId = userId.UserId;
db.Posts.Add(post);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(post);
}
Be careful with hidden fields. Anyone could put whatever value they want in that field (i.e. they could spoof another user). You'd be better off caching the ID in the session at login, and using that value.
This is a typical case where you want to create an EditModel as a data transfer object (DTO) between your view and controller layers.
Create a class BlogPostEditModel that has all properties you need the user to fill in when creating a new blog post. Then, map this type (e.g. using AutoMapper) to your BlogPost entity, and fill in the user ID as well.
To use built-in validation such as Model.IsValid(), put the data annotations attributes on the DTO instead.
Honestly, I would have the value assigned via the controller. If you had someone messing with your html via Firebug, they could actually change the id before it was passed and submitted to your form. I would remove it from your Create view and submit from the controller.
I am new to MVC ASP.NET and needed to call an action from one controller in an action of another controller. There I created object of controller and called my required action as below,
controllerOne co = new controllerOne();
co.requiredFunction();
but one of my senior advised me to not use this approach, which kills the MVC pattern use its given ActionInvoker.InvokeAction() function to call function of other controller and I am using now as below,
public class HomeController : Controller
{
this.ActionInvoker.InvokeAction(new System.Web.Mvc.ControllerContext(
this.ControllerContext.RequestContext, new controllerOne()),
"requiredAction");
}
This works fine, but I don't know if the way I am using ActionInvoker.InvokeAction() is correct. I searched to find any example but I could find any.
So my question is: Am I using it correctly?
To do this many people would create a service that encapsulates this requiredFunction and inject the service through it's interface into both controllers
use this
var ctrl= new MyController();
ctrl.ControllerContext = ControllerContext;
//call action
return ctrl.Action();
I have a little question to community. Does ASP.NET MVC allow to execute an action of another controller without RedirectToAction() method and without creation of an instance of this controller?
This is impossible. For an instance method to execute there must be an instance to execute in. Action methods are simply methods like all other methods, so you always need an instance to call the method.
Do you mean you wish to have a View which returns nothing. No view. No nadda?
If so, u can return a EmptyResult ViewResult class...
HTH.
You can call it through the Reflection technic, But It's Not Recommended. Shortly because it's not in the request/response/controller context.
Personally, I prefer to have an internal static method(bla bla){...} and call it where ever i want.
But if you drop the
without creation of an instance of this controller
Clause, Then you can use this methodology. But as it explains, It's Not Recommended Too. the following is summarized:
var controller = new FooController();
controller.ControllerContext = new ControllerContext(this.ControllerContext.RequestContext, controller);
var jsonResult = controller.BlaMethod(someInputParams);