i have created the controller :
[Authorize]
[AcceptVerbs(HttpVerbs.Delete)]
public ActionResult Delete(int id)
{
try
{
db.DeleteObject(db.AEROLINEA.FirstOrDefault(x => x.AEROLINEAID == id));
db.SaveChanges();
}
catch { /* TODO:Display message*/ }
return View();
}
if i execute in firebug the next javascript anyone logged could delete an airline even if he doesnt have permissions to delete
var action = "/Airline/Delete/" + recordId;
var request = new Sys.Net.WebRequest();
request.set_httpVerb("DELETE");
request.set_url(action);
request.add_completed(deleteCompleted);
request.invoke();
HOw can avoid this issue???
You can filter the the roles:
Example:
[Authorize(Roles="Admin")]
[AcceptVerbs(HttpVerbs.Delete)]
public ActionResult Delete(int id)
{
try
{
db.DeleteObject(db.AEROLINEA.FirstOrDefault(x => x.AEROLINEAID == id));
db.SaveChanges();
}
catch { /* TODO:Display message*/ }
return View();
}
Or use the AntiforgeryToken with a juicy salt at the View..
[Authorize] without parameters allows you to indicate that a user must be logged in. You also can specify users/roles, authorized to access your action
Related
I'm developing a simple Custom Role-based Web Application using ASP.Net MVC, In my login Action, I'm creating a Profile session as below:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
using (HostingEnvironment.Impersonate())
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
var employeeProfile = AccountBal.Instance.GetEmployee(loginId);
Session["Profile"] = employeeProfile;
FormsAuthentication.SetAuthCookie(model.UserName, true);
}
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", #"The user name or password provided is incorrect.");
return View(model);
}
}
And I'm checking this or using this session in all Controller Actions as below:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateOrEdit(MyModel model)
{
var employee = (Employee) Session["Profile"];
if (employee == null)
return RedirectToAction("Login", "Account");
if (ModelState.IsValid)
{
// Functionality goes here....
}
}
Is there any way I can move this piece of session checking code in a base class or centralized class? so that, I do not need to check it every time in a Controller Actions instead I will access the properties directly
say,
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateOrEdit(MyModel model)
{
var employee = _profileBase.GetCurrentProfile();
if (employee == null)
return RedirectToAction("Login", "Account");
if (ModelState.IsValid)
{
// Functionality goes here....
}
}
Create a base controller that contains your GetCurrentProfile method to retrieve current user profile like
public class BaseController : Controller
{
public Employee GetCurrentProfile()
{
return (Employee)Session["Profile"];
}
public bool SetCurrentProfile(Employee emp)
{
Session["Profile"] = emp;
return true;
}
}
And inherit your desired controller with above BaseController and access your GetCurrentProfile method like below
public class HomeController : BaseController
{
public ActionResult SetProfile()
{
var emp = new Employee { ID = 1, Name = "Abc", Mobile = "123" };
//Set your profile here
if (SetCurrentProfile(emp))
{
//Do code after set employee profile
}
return View();
}
public ActionResult GetProfile()
{
//Get your profile here
var employee = GetCurrentProfile();
return View();
}
}
GetCurrentProfile and SetCurrentProfile directly available to your desired controller because we directly inherit it from BaseController.
You may usetry/catch in above code snippet.
Try once may it help you
Here is my issue.
I have 2 roles one for "Administrator" and the last for "Client". In my HomeController I have the action "Index" which I wish to be accessed only by "Administrator" role. So when I have x user authenticated that has not "Administrator" role, I want him not to be redirected to the login page (set in my web config file) but to an particular action. Here is my code
[Authorize(Roles="Administrator")]
public ActionResult Index()
{
List<object> users = new List<object>();
foreach (MembershipUser u in Membership.GetAllUsers())
{
users.Add(u);
}
return View(users);
}
protected override void OnAuthorization(AuthorizationContext filterContext)
{
if (User.Identity.IsAuthenticated && !User.IsInRole("Administrator"))
{
if (filterContext.RequestContext.HttpContext.Request.RawUrl == "/")
{
filterContext.Result = new RedirectResult(Url.Action("Denied"));
}
//filterContext.HttpContext.Response.Redirect("/Home/Denied");
}
else
{
base.OnAuthorization(filterContext);
}
}
//this action is for any authenticated user
[Authorize]
public ActionResult Denied()
{
return View(User.Identity.Name);
}
This issue throws System.invalidoperationexception and ~/Views/Home/xyz.cshtml does not exist. Of course the views url should be ~/Views/Home/denied.cshtml
For information "xyz" is the UserName authenticated. How can I do redirection?
Thanks
Cast your model to object.
[Authorize]
public ActionResult Denied()
{
return View((object)User.Identity.Name);
}
your if statement might be off a little. try this
if (!User.Identity.IsAuthenticated || !User.IsInRole("Administrator"))
So I am currently using a ViewBag set in the login to determine if they can see admin only stuff. This is done this way because Roles.CreateRole, Membership.CreateUser and Roles.AddUserToRole is disabled because we use ModelFirst ASP.net.
public ActionResult Login(LoginModel model, string returnUrl)
{
ViewBag.Admin = false;
if (model.IsValid(model.UserName, model.Password))
{
ViewBag.Admin = (bool)model.currentLoggedInEmployee.IsAdmin;
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Login data is incorrect!");
return View(model);
}
}
Then we simply use:
#if (ViewBag.Admin == true) {
<li>#Html.ActionLink("Administration", "Index", "Administration")</li>
}
to only show these buttons to admins. This works.
Now what we want, is to make sure only administrators can run some functions, by doing something similar to the normal
[Authenticate(Roles="Admin")]
[HttpPost]
public ActionResult Create(FormCollection collection)
{
// TODO: Add insert logic here
}
But because we don't have any "Roles" we can not do it like this. We need to use the ViewBag.Admin value to authorize people to use these functions. Question is, how can this be done?
I would recommend rolling your own AuthorizeAttribute and from there you can determine whether or not the current logged in user is an admin or not.
When you create your authentication cookie add some additional information (i.e. the admin flag) e.g.
public ActionResult Login(LoginModel model, string returnUrl)
{
if (model.IsValid(model.UserName, model.Password))
{
var ticket = new FormsAuthenticationTicket(1,
model.UserName,
DateTime.Now,
DateTime.Now.AddMinutes(30),
model.RememberMe,
model.currentLoggedInEmployee.IsAdmin, // user data
FormsAuthentication.FormsCookiePath);
// Encrypt the ticket.
string encTicket = FormsAuthentication.Encrypt(ticket);
// Create the cookie.
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));
// Redirect back to original URL.
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Login data is incorrect!");
return View(model);
}
}
Create a custom authorize attribute to authenticate the logged in user against the role e.g.
public class AdminOnlyAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.Current.User.Identity.IsAuthenticated)
{
var ticket = ((FormsIdentity)User.Identity).Ticket;
return (bool)ticket.UserData;
}
else
{
return false;
}
}
}
Then decorate your action as:
[AdminOnly]
[HttpPost]
public ActionResult Create(FormCollection collection)
{
// TODO: add insert logic here
}
I'm working with ASP.NET MVC 4. When we build a project, they add an AccountController by default. You can create an acoount and log in right away... but after they've logged in, try to type http://localhost:port/Account/LogOn. You still stay at the Login page with the login form.
I have tried to redirect the user by using this code:
//
// GET: /Account/LogOn
[AllowAnonymous]
public ActionResult LogOn()
{
if(Membership.GetUser().UserName != null)
RedirectToAction("Index", "Home");
return ContextDependentView();
}
The code works fine until I log out, and then this code no longer works. Please show me the right way to do this.
Fixed:
//
// GET: /Account/LogOn
[AllowAnonymous]
public ActionResult LogOn()
{
if(User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Home");
}
else
{
return ContextDependentView();
}
}
You don't have to copy over the rights-chechking code - do write a filter instead and use it multiple times with no code duplication.
public class GuestsOnlyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting( ActionExecutingContext filterContext )
{
if( User.Identity.IsAuthenticated )
{
filterContext.Result = new ViewResult { ViewName = "RestrictedArea" };
}
}
}
In your controller
// GET: /Account/LogOn
[GuestsOnly]
public ActionResult LogOn() { ... }
I have a the following methods in an MVC Controller which redirect to the login page when a user is not logged in.
[Authorize]
public ActionResult Search() {
return View();
}
[Authorize]
public ActionResult Edit() {
return View();
}
Is there a quick/easy/standard way to redirect the second action to a different login page other than the page defined in the web.config file?
Or do I have to do something like
public ActionResult Edit() {
if (IsUserLoggedIn)
return View();
else
return ReturnRedirect("/Login2");
}
I think it is possible by creating a custom authorization filter:
public class CustomAuthorization : AuthorizeAttribute
{
public string LoginPage { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.HttpContext.Response.Redirect(LoginPage);
}
base.OnAuthorization(filterContext);
}
}
In your action:
[CustomAuthorization(LoginPage="~/Home/Login1")]
public ActionResult Search()
{
return View();
}
[CustomAuthorization(LoginPage="~/Home/Login2")]
public ActionResult Edit()
{
return View();
}
Web.config based forms authentication does not have such a functionality built-in (this applies to both WinForms and MVC). You have to handle it yourself (either through an HttpModule or ActionFilter, the method you mentioned or any other method)
I implemented the accepted answer by user434917 and even though I was being redirected correctly, I was also receiving the error "Server cannot set status after HTTP headers have been sent." in the server log. After searching, I found this post (answer by Mattias Jakobsson) that solved the problem. I combined the answers to get this solution.
Create a custom authorization filter:
using System.Web.Mvc;
using System.Web.Routing;
namespace SomeNamespace.CustomFilters
{
public class CustomAuthorization : AuthorizeAttribute
{
public string ActionValue { get; set; }
public string AreaValue { get; set; }
public string ControllerValue { get; set; }
public override void OnAuthorization(AuthorizationContext context)
{
base.OnAuthorization(context);
if (context.HttpContext.User.Identity.IsAuthenticated == false)
{
var routeValues = new RouteValueDictionary();
routeValues["area"] = AreaValue;
routeValues["controller"] = ControllerValue;
routeValues["action"] = ActionValue;
context.Result = new System.Web.Mvc.RedirectToRouteResult(routeValues);
}
}
}
}
Then on your controller, use the customer attribute.
[CustomAuthorization(ActionValue = "actionName", AreaValue = "areaName", ControllerValue = "controllerName")]
public class SomeControllerController : Controller
{
//DO WHATEVER
}
Yeah pretty easy! Lets say you have 2 different type of users. First typenormal users, the other one is administrators. You would like to make them login from different pages. You also want them to be able to access different ActionResults.
First you have add two different schemes. In these schemes you will define your different login pages and other options you want.
in startup.cs
services.AddAuthentication("UserSceheme").AddCookie("UserScheme", config =>
{
config.LoginPath = "/UsersLogin/Login/";
config.Cookie.Name = "UsersCookie";
});
services.AddAuthentication("AdminScheme").AddCookie("AdminScheme", config =>
{
config.LoginPath = "/AdminLogin/Login/";
config.Cookie.Name = "AdminsCookie";
});
Then you will define two policies. Here I called them UserAccess and AdminAccess Each policy will use the sceheme that I point.
In startup.cs just after schemes add those below.
services.AddAuthorization(options =>
{
options.AddPolicy("UserAccess", policy =>
{
policy.AuthenticationSchemes.Add("UserScheme");
policy.RequireAuthenticatedUser();
});
options.AddPolicy("AdminAccess", policy =>
{
policy.AuthenticationSchemes.Add("AdminScheme");
policy.RequireAuthenticatedUser();
});
});
The last thing we have to do is again telling the scheme we want to use when login.
var userPrincipal = new ClaimsPrincipal(new[] {
new ClaimsIdentity(loginClaims, "ServiceCenter")
});
HttpContext.SignInAsync("AdminScheme",userPrincipal);
Thats it! Now we can use these just like this; This will redirect you to the users login page.
[Authorize(Policy = "UserAccess")]
public IActionResult Index()
{
return View();
}
If you have some places that you want both user types to be able to access all you have to do;
[Authorize(Policy = "AdminAccess")]
[Authorize(Policy = "UserAccess")]
public IActionResult Index()
{
return View();
}
And finally for log-out you also have to point the scheme
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync("AdminScheme");
return View();
}
Thats it!