MVC3 logged in User can see only own stuff apart from Admin - asp.net

A novice to asp.net and mvc3. I am learning by setting myself challenges/developing an application. I tag all record tables where users interact with ProviderUserKey. Now I want to be able to restrict users logged in to be able to edit or delete their own records only but administrators can edit or delete any. I have been using scaffolding to generate controllers and views etc. for eg code for editing`// POST: /Post/Edit/5
[HttpPost]
public ActionResult Edit(PJpost pjpost)
{
if (ModelState.IsValid)
{
db.Entry(pjpost).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(pjpost);
}`
Any help will be highly appreciated.

If you have a generic Edit method/action and you would like to keep it that way, I would add a method in your controller somethink like ValidateOwnership(record). This method would need to verify if CurrentUser's ID is matching the one on the record and if user is a member of particular role - that can be done with RoleManager class. Method would return true/false.
When you got it ready just put the call to the method in your code after ModelState validation. It would look like this
[HttpPost]
public ActionResult Edit(PJpost pjpost)
{
if (ModelState.IsValid)
{
if(IsOwnershipValid(pjpost){
db.Entry(pjpost).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
else {
ModelState.AddModelError("ERROR","You're not allowed to do that");
return View(pjpost);
}
}
return View(pjpost);
}
EDIT :
So the OwnershipValidation could look like this :
public bool ValidateOwnership(Pjpost pjpost) {
if (pjpost.MemUID == Membership.GetUser().ProviderUserKey.ToString())
{
return true;
}
else
{
return false;
}
}
I hope this is what you meant.

You need to look in to user roles and the process of authorization usually, MVC provides a registration and log in in it template including the account controller. To restrict user access you have to assign roles to users.
the code behind it would look something like this.
[Authorize(Roles="admin")]
public ActionResult admin()
{
//Action gives an admin rights since the user is in the admin role
return View();
}
[Authorize(Roles="manager")]
public ActionResult manager()
{ //give a managers rights since user is im managers roles.
return View();
}

Related

How can I force the user login before go to any where in my application?

I'm working on the asp.net mvc5 project, my problem is forcing the user login then they can go anything on my site. I'm using Session to check user login or not (I'm not using Identity or Authorization).
Here is my Login code:
[HttpPost]
public ActionResult Login(User user)
{
if (IsValid(user.username, user.password))
{
Session["username"] = user.username;
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Login details are wrong.");
}
return View(user);
}
I can check the user login or not with Session but I cannot deny the user access to the link, if I have to do this manually, I will need to add the block code below to any action in every controller I have.
public ActionResult Index()
{
if (Session["username"] != null)
{
//then user can go anywhere with any link
}
else
{
//user cannot go to here without login
}
}
There is any way I can force the user to redirect to the login page (if they are not login) when they click on any link or type the link in the browser? I was searching about config the route or using filter config but it seems not to solve my problem. I hope there has the general way to do this without adding the check-code manually to each controller/action.
Thanks for reading my problem, I will be very appropriated any comment to help me solve this.
One possible way to tackle on this globally is to override the OnActionExecuting method on the controller, that does the check and redirects to the homepage if no user is logged or allows the action if there is a user.
This can be done on a base controller so that it applies to all the website (by making all controllers inherit from this one).
public abstract class LoginRequiredController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Session["username"] != null)
{
base.OnActionExecuting(filterContext);
}
else
{
//Redirect to the main page, or login, or somewhere else, without running the requested action
filterContext.Result = Redirect("/");
return;
}
}
}
Controllers inheriting from this one will check on each request that there is a user, and redirected to the main page in case there isn't one, without needing to copy/paste this check for each method.
Of course, the login controller should not inherit from it or it would be impossible to login at all.

Include JavaScript based on ActiveDirectory Membership

I've got a single page application and in the start page I've linked to the ActionResult "_ADMIN()"
#Html.Action("_ADMIN")
Depending on the user's group membership, this ActionResult returns either an EmptyResult or its view:
public ActionResult _ADMIN()
{
if (User.IsInRole("admin"))
{
return View();
}
else
{
return new EmptyResult();
}
}
Its view consists of this one line:
#Scripts.Render("~/bundles/admin")
This works but feels suboptimal. I could copy the start page and create seperate ones for non-admins and admins, but that would be even more suboptimal. I'm using MVC 4.
What's a better way to include a bundle of JavaScript only if the current user is in a certain Active Directory group?
You can add scripts depending on roles. Like that :
if (User.IsInRole("admin"))
{
#Scripts.Render("~/bundles/admin")
}
And on your Action method you add attribute then the user will be automatically redirected to the login page if it does not have rights. You can add this attribute also on whole class.
[Authorize(Roles = "admin")]
I would only show the link to admin users:
if (User.IsInRole("admin"))
{
#Html.Action("_ADMIN")
}
Then, in the event a non-admin tried to access the action method, I would block the request at the action method:
[Authorize(Roles = "admin")]
public ActionResult _ADMIN()
{
return View();
}

Managing a session's variable in a model class with asp.net mvc

I have an asp.net mvc4 application in which i have this snippet:
public bool Logout() {
try {
session["user"] = null;
return true;
}
catch {
return false;
}
}
when i put this code in a controller it's works but if i put it in a model class it didn't.
the problem is in the session["user"] = null; .
So how can i manage the session's variables in a model class?
This functionality should not be in a view model. The model should be used for passing data from controllers to views for displaying, and receiving submitted data from views.
See a question like What is a ViewModel in MVC to get a better explanation.
A logout function should be an action on a controller. Something like:
public ActionResult Logout()
{
Session["user"] = null;
// Redirect user to homepage
return Redirect("/");
}
In class access by the current context :
HttpContext.Current.Session["user"]....

Using object Helper Methods to implement authorization rules

I have the following:-
I am working on an asset management system using Asp.net MVC4 with windows authentication enabled.
The system allow to specify what actions a group of users can do(for example certain group can have the authority to add new physical asset , while they can only read certain logical asset, and so on).
So I found that using the build-in Asp.net role management, will not allow me to have the level of flexibility I want. So I decided to do the following:-
I have created a table named “group” representing the user groups. Where users are stored in active directory.
I have created a table named ”Security Role” which indicate what are the permission levels each group have on each asset type(edit, add, delete or view)per asset type.
Then on each action methods , I will use Helper methods to implement and check if certain users are within the related group that have the required permission ,, something such as
On the Car model object I will create a new helper method
Public bool HaveReadPermison(string userName) {
//check if this user is within a group than have Read permission on CARS, //OR is within a GROUP THAT HAVE HIGHER PERMISON SUCH AS EDIT OR ADD OR //DELETE.
}
Next, On the Action method, I will check if the user has the Read permission or not by calling the action method:-
public ActionResult ViewDetails(int id) { // to view transportation asset type
Car car = repository.GetCar(id);
if (!car.HaveReadPermision(User.Identity.Name)) {
if (car == null)
return View("NotFound");
else
return View(car);
}
else
return view (“Not Authorized”);
So can anyone advice if my approach will be valid or it will cause problem I am unaware about.
Regards
In my opinion, once you have decided to use the ASP membership and role providers you can keep leveraging them also for authorization, simply using the Authorize attribute. This will also allow to restrict access by user names and roles.
What the attribute won't do is Action-based authorization. In that case there are a few options but in my opinion this could be brilliantly resolved by a Custom Action Filter based loosely on the following code:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CheckUserPermissionsAttribute : ActionFilterAttribute
{
public string Model { get; set; }
public string Action { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var user = filterContext.HttpContext.User.Identity.Name; // or get from DB
if (!Can(user, Action, Model)) // implement this method based on your tables and logic
{
filterContext.Result = new HttpUnauthorizedResult("You cannot access this page");
}
base.OnActionExecuting(filterContext);
}
}
Yes, it is vaguely inspired to CanCan, which is a nice Ruby gem for this kind of things.
Returning Unauthorized (401) will also instruct your server to redirect to the login page if one is specified. You may want to work on that logic if you want to redirect somewhere else. In that case you should do:
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary { { "Controller", "Home" }, { "Action", "Index" } });
and choose the appropriate controller/action pair.
You can use the attribute like this:
[CheckUserPermissions(Action = "edit", Model = "car")]
public ActionResult Edit(int id = 0)
{
//..
}
Let me know if that works nicely for you.
The approach you took looks reasonable, but I would add few changes:
What if you forgot to call HaveReadPermision method? And checking authotization from Actions is not the cleanest solution either, that is not an Action reponsibility.
It is better to keep authorization logic separately. For instance you can create a decorator over you repository which will check the permissions of the current User:
public class AuthorizationDecorator: IRepository
{
public AuthorizationDecorator(IRepository realRepository, IUserProvider userProvider)
{
this.realRepository = realRepository;
this.userProvider = userProvider;
}
public Car GetCar(int id)
{
if(this.UserHaveReadPermission(this.userProvider.GetUserName(), Id))
{
return this.realRepository.GetCar(id);
}
else
{
throw new UserIsNotAuthorizedException();
}
}
private bool UserHaveReadPermission(string username, int id)
{
//do your authorization logic here
}
}
IUserProvider will return curent user name from httpRequest.
After doing the change you don't need to warry about authorization when writing Actions

A Contact page with ASP.NET MVC 3

I have a contact page and this page shall either show a form or a success message or a failure message, so basically something like this:
#model MyApp.Models.ContactData
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div>
...Some static content...
If page was opened the first time
-> Render a form here
Else If form was posted and data successfully processed
-> Render a success message here
Else If form was posted but error occurred during processing
-> Render a failure message here
...Some static content...
</div>
I don't know what's the best way to achieve this with MVC 3. Do I create three completely separate views (which is something I'd like to avoid because of the static content which would be the same for all three views)? Or could I create three partial views and then decide based on an additional flag I could put into the model class which partial view to render? Or can I inject somehow the partial views dynamically from the controller into the view?
The controller I have so far looks like this:
public class ContactController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(ContactData contactData)
{
if (ModelState.IsValid)
{
ContactService service = new ContactService();
bool result = service.Process(contactData);
return ?; // What do I return now? It must somehow depend on result.
}
else
return View(contactData));
}
}
I had a similar page and behaviour with ASP.NET WebForms and the solution was there to put the three variable blocks of markup into asp:Panel controls and then switch on or off the Visible flag of those panels from code-behind. I guess I need quite another approach with ASP.NET MVC to reach the same goal.
What is the best way?
Thank you for suggestions in advance!
You can try this way:
[HttpPost]
public ActionResult Index(Contact contactData)
{
if (ModelState.IsValid)
{
ContactService service = new ContactService();
if (service.Process(contactData))
{
TempData["Success"] = "Your success message.";
return RedirectToAction("Index");
}
else
{
TempData["Error"] = "Your fail message.";
}
}
return View(contact);
}
Perhaps use the ViewBag to help achieve all this. Of course it's a dynamic, so you can add & check for any prop you want/need/expect.
[HttpPost]
public ActionResult Index(ContactData contactData)
{
if (ModelState.IsValid)
{
ContactService service = new ContactService();
bool result = service.Process(contactData);
ViewBag.ContactSuccess = true;
}
else
{
ViewBag.ModelStateErr= "some err";
}
return View(contactData));
}
Then in your View:
if (ViewBag.ContactSuccess !=null && ((bool)ViewBag.ContactSuccess))
{
//thanks for posting!
}
else
{
if (ViewBag.ModelStateErr !=null)
{
//show that we have an err
}
else
{
//we have no err nor a 'true' contact success yet
//write out the form
}
}
Looks like that you can issue an ajax call on the client side, and based on the Json result, you can render different content from the client side.
I'd suggest coding up three different Views
index.cshtml
contactSuccess.cshtml
contactFail.cshtml
Then in your Controller, you'll have similar code as before
public class ContactController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(ContactData contactData)
{
if (ModelState.IsValid)
{
ContactService service = new ContactService();
bool result = service.Process(contactData);
return View("contactSuccess.cshtml");
}
else
return View("contactFail.cshtml", contactData);
}
}
This way each view has an independent and you don't have a big inline IF block in the middle of your markup.
Alternatively (and this is how I'd do it) you can have the index.cshtml contain three partials...
_ContactForm.cshtml
_ContactSuccess.cshtml
_ContactFail.cshtml
and then you can load the partial views into the index view, and even swap them out dynamically using AJAX.

Resources