So in my .net application I have three controllers
Home
Kitchen
Institution
When the user logs in, I get what controller the user can go. I have defined some roles and using those I get which user needs to go where.
So for example if a user Bob is a cook. When Bob logs in, he is taken to the kitchen dashboard. But if Bob types in the URL ../Home/Dashboard there is nothing stopping him for going in there. What should be done in order to restrict Bob accessing any other url?
Also, when the user logs in should I store the information about his role in session?
What is the best practice for this purpose?
You can handle all your requirements in an HttpPost Login Action. See comments for further instructios.
[HttpPost]
[AllowAnonymous]
public ActionResult Login(LoginModel model, string returnUrl)
{
// check first for field validations
if (!ModelState.IsValid)
return View(model);
// validate user agains database
var user = FindUser(model.UserName, model.Password);
if (user == null)
{
ModelState.AddModelError("", "Invalid username or password.");
return View(model)
}
// user is valid, sign in assuming forms authentication, however it's
// best practice to abstract following statement by use of some kind of authentication
// manager (refer to OWIN framework for a better approach).
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
// when returnUrl param is provided
if (!String.IsNullOrEmpty(returnUrl))
return Redirect(returnUrl);
// following 'if' - condition dependent on your domain models.
if (user.IsCook)
return RedirectToRoute(/*kitchen route*/);
else
return RedirectToRoute(/*dashboard*/);
}
Related
I am creating an application using MVC4. I am trying to work out how to capture when a user logs in.
I thought I could add some code in the login Action in Account controller..
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && _authProvider.Login(model.UserName, model.Password, true))
{
var currentUser = Membership.GetUser();
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
but the currentUser is always null? why is that? How can I fix it?
Thanks
Membership.GetUser() doesn't work because it uses the HttpContext.Current.Identity to retrieve the the user, but this will not get set until the next page refresh.
The reason is that your Login method most likely uses FormsAuthentication.SetCookie() (or some variation) to create an auth ticket, but that ticket has to be read on a page load to be valid. Since the current page is already loaded at this point, authentication is not valid until the next page load.
Regardless, I don't understand why you even need to do this. You already know the users name. It's right there, you pass it to the Login method... model.UserName.
I hope someone will be able to put me on the right track, been trying to resolve this now for hours.
I am currently in the process of redeveloping a web application and I would like to use the MVC4 attributes for managing access to the various parts of the application.
The issue I am having is that the Authentication & Permissions are all handled by middle-ware applications that the web app has to interface with.
I was wondering if even with this restriction would I be able to use the security attributes & letting the web app know that the user is Authenticated.
Yes, you will be able to use existing Authorize attribute. All you have to do is write a custom Membership and Role providers that will use your existing services instead of relying on the default SQL database.
If you don't want to go through all this hassle you could also write a custom authorization attribute (deriving from AuthorizeAttribute) and inside the AuthorizeCore method call your service to check whether the current user has the desired roles.
Definitely. Not only is it possible, but also it's pretty easy. And if you can think of ASP.NET Roles as "activities", then you don't need to derive anything; everything you need is built in.
These examples assume securityService is the service that communicates with your middle-ware applications, and has two methods, GetUser and GetUserRoles:
Your Login action method
[HttpPost]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (!ModelState.IsValid) return View();
var user = securityService.GetUser(model.Username, model.Password);
if (user == null)
{
ModelState.AddModelError("", "Username or password are incorrect.");
return View();
}
FormsAuthentication.SetAuthCookie(user.Username, model.Remember);
return Redirect(returnUrl);
}
In your Global.asax.cs
protected void Application_AuthenticateRequest()
{
if (Request.IsAuthenticated)
{
string username = User.Identity.Name;
string[] roles = securityService.GetUserRoles(username);
IIdentity identity = new GenericIdentity(username);
Context.User = new GenericPrincipal(identity, roles);
}
}
That's it. Login handles the authentication (when the user logs in), while Application_AuthenticateRequest handles the authorization (on every request). You then proceed to decorate your action methods with Authorize(Roles = "XYZ") making sure "XYZ" matches what comes back from your GetUserRoles method.
Want to set some role based dependencies (Ioc, Ninject). But after a successful login the role of the user is unknown. Where and when to apply the injection? Can I force the initialization of roles? Or do I have to fetch them myself?
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
DoRolebasedObjectBinding(model.UserName); // THIS IS THE WRONG PLACE!
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
The applicatin starts with the HomeContoller (no role set), after the succesful login the appllication is redirected to de HomeController (see standard code above) and now the role is set...
One trick is to force the roles to be initialized in the HomeController by adding all roles or an addtional "common" role
public class HomeController : Controller
{
[Authorize(Roles = "Common")]
public ActionResult Index()
{
DoRolebasedObjectBinding(User.Identity.Name);
But this forces a login an also forces the HomeController as a start point. Hopefully is an "elegant" place to call my DoRolebasedObjectBinding.
Based on Role based authentication in the new MVC 4 Internet template, the answer is rather simple: do it yourself:
var roles = Roles.Provider;
string[] rolesArr = roles.GetRolesForUser(username);
I have one View called SignIn that contains two partial views for authentication. One is for OpenID and the other is for logging in to my site using an internal account.
The Action for the OpenID form goes to an OpenIDSignIn() Action while the other just points to SignIn(). Everything works great except for when a user clicks a link to a view that requires them to be logged on [Authorize] etc..
I see the returnUrl in the QueryString however this value is not available to the current controller action, due to the fact that they will be either be logging in with OpenID or normally, thus caling the ActionResult associated with either of those views.
Just for clarification the returnUrl is the one that gets thrown in there from FormsAuthentication and is used when doing a RedirectFromLoginPage etc..
public ActionResult SignIn(string returnUrl)
{
if(!string.IsNullOrEmpty(returnUrl))
{
if(UrlUtil.IsLocalUrl(returnUrl))
{
Session.Add("ReturnUrl", returnUrl);
}
else
{
return RedirectToAction("SignIn", "Account");
}
}
return View();
}
This seems to be a viable workaround. However I hate session variables!
Im getting really lost on how to use HttpContext.User. I read everywhere that its great for FormAutherication, but i just cant see how it works. If i do something like this:
ControllerContext.HttpContext.User = new GenericPrincipal(GetUser(username, password), roles);
What does ControllerContext.HttpContext.User contain? and how do i access information about the user this way?
Im think that i have a Action like this:
public User GetUser(string username, string password)
{
try
{
var user = (from u in dm.Users
join r in dm.Roles
on u.Role_ID_FK equals r.RoleID
where u.Username.Equals(username) && u.Password.Equals(password)
select u).Single();
return user;
}
catch (Exception e)
{
return null;
}
}
And then if i want user information in my view, like the user name or role, i can call ControllerContext.HttpContext.User.Username in my View. But this is diffenrently the wrong way to look at it.
So can you guys give me a kick in the rigth direction or post a link to a site which can?
I'm not sure exactly what you are trying to do with the code you posted, but here's some help with HttpContext.User. In layman's terms it represents the current user requesting the particular page, and actually within your Controller you can just reference it as "User" without the prefix.
User.Identity will let you know if the user is authenticated, and if so their username and how they authenticated (Forms or Windows).
It's generally used to get the username of the user requesting the page so your controller actions can perform the correct duties. Something like:
public ActionResult Index()
{
//you should probably use the [Authorize] attribute on the Action Method
//but you could check for yourself whether the user is authenticated...
if (!User.Identity.IsAuthenticated)
return RedirectToAction("LogIn");
MyUser u = repository.GetUser(User.Identity.Name); //lookup user by username
ViewData["fullname"] = u.FullName; //whatever...
return View();
}
In this example, if the user hasn't been authenticated, they will be redirected to a LogOn page, and if they have been, the Action method is using the User.Identity.Name (which is the username they logged in with, or their Windows login) to lookup and return an instance of a MyUser object from your database and puts the user's full name in ViewData to be displayed.
In your login code use:
FormsAuthentication.SetAuthCookie("userName", remeberMe);
to set the authenticated user, then you can use
<%= User.Identity.Name %>
<%= User.IsInRole("role") %>