I'm trying to use ASP.NET MVC to have a web app that will take details about a parent item and allow uploads and sharing of files which are associated with and stored under the name of the parent item.
The files I want to protect are stored in this way:
~/Files/{Item-GUID}/{Filename}.{ext}
The Item-Guid can be used to query the db for security permissions for the item. (users are logged as AD SIDs)
I need to know how to have ASP.Net respond to file requests for these files in the path ~/Files/ and use the /Item-GUID/ to check security permissions before serving the file to the user, and throw authentication errors if the user is not logged in or does not have access to the parent item.
I would appreciate any links or advice on where should I start here.
Thanks In Advance.
I use often create a custom permission class that derives from the AuthorizeAttribute class. This is one way you can create a custom permissions filter on any controller action.
public MyPermissionsFilter : AuthorizeAttribute
{
private readonly string _permissionName;
public PermissionsFilter(string permissionName)
{
_permissionName = permissionName;
}
}
Override the OnAuthorization method.
public override void OnAuthorization(AuthorizationContext filterContext)
{
//Perform Check with _permissionName
//Redirect to error / unauthorised
}
Then decorate your controller action.
[HttpPost]
[PermissionsFilter("PermissionName")]
public void SaveFile(HttpPostedFileBase file)
{
//Do file stuff
}
I hope that's what you want and is of some help.
Addition : I've renamed GetFile to SaveFile.. and this is what is should have been.
I've also been thinking about this again and it may not be the best solution for you. If you need to check a users permissions to access a single file based upon it's GUID, you might be better having the security check called in the SaveFile method that receives the file base parameter. You can get the guid from the file name then to pass to the permissions check. If it fails then simply redirect to the appropriate view.
Related
I have a role system where the Admin can create new roles and grant Read, Write, Edit and Delete rights based on the page menu (each page menu has it's own controller) then assign that role to the user.
Users can also have multiple roles but i got that covered. The only problem I am facing is this: since Role assigning can be done dynamically from within the application i can't use the [Authorize(Roles = Role.Admin)] attribute over controller actions.
Instead i was thinking of creating [Read], [Write], [Edit] and [Delete] attributes that i would put over the actions to let me know what that action is for and then allow or deny users to call that action.
Would this be the correct approach? What would i need to have in those custom attributes and how would i allow/deny user to call them? Anyone have any similar experience?
Let's say i have Home and Admin controller and Role1 and Role2 with following rights:
Would something like the above approach works? How do I define custom access for controller actions?
I want to avoid having all the actions in the database and simplify the administration on the site.
In this way administration can be done on R/W/E/D flags on controller level instead of Role1 can all Home/Index, Home/GetRecords etc... action level.
I have same requirement for my project. In my project I have to allow user to access some pages and some pages to admin. Mostly admin can access everything, but I have to block user to access some pages/Action. I did below thing.
a. Create one class to Authenticate user/admin.
b. Create one class for User and inherited that class from "ActionFilterAttribute", and override the OnActionExecuting method.
c. Create one class for Admin and did samething as mentioned in "b", but for admin.
d. put the Authentication class name above the class name or method depending on access.
see sample code(step b). I removed some code, but you can get the idea.
public class AuthenticateUser : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext context)
{
//Do session check.
if (HttpContext.Current.Session["Id"] == null || String.IsNullOrEmpty(HttpContext.Current.Session["Id"].ToString()))
{
HttpContext.Current.Response.StatusCode = HttpStatusCode.Forbidden.GetHashCode();
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new JsonResult { Data = new { Status = HttpStatusCode.Forbidden, LogonRequired = true, result = "Session Expired" }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
else
{
//redirect to login page
UrlHelper oUrl = new UrlHelper(HttpContext.Current.Request.RequestContext);
string s = oUrl.Action("", "UserInfo/");
HttpContext.Current.Response.Redirect(s);
}
}
//If usertype is User then allow it. If user type is admin then return redirect.
//Redirect code if admin
UrlHelper oUrl = new UrlHelper(HttpContext.Current.Request.RequestContext);
string s = oUrl.Action("", "UserInfo/");
HttpContext.Current.Response.Redirect(s);
}
}
I would define it differently, just give each user a set of roles.
Define each user with more than one role, if someone has access to write, you set a role for him to write, if he can also read you can add another role - read.
Now when you define your actions you can use this format[Authorize(Roles = Role.Read)]. Set each action by the access right it requires and you are done.
Use claims. each public controller action claim that you need to define.
Then have your roles defined by the list of claims/operations they can do.
Here's a great explanation here Role-based access control (RBAC) vs. Claims-based access control (CBAC) in ASP.NET MVC
I'm working on an ASP.NET MVC application where administrators can add new users and flag them to complete additional information before they can use other features of the site. For example, we have a "ForcePasswordReset" bool, and a requirement to complete Security Questions. We're not using Active Directory for these users.
Ultimately this is the behavior I'd like to implement:
Direct any logged in user who is required to change password to the
ChangePassword view. And if that user clicks on other links, funnel
him back to the ChangePassword view.
Same scenario for users who must change their security questions.
Initially I placed the checks directly into a Login Controller ActionResult. But this only accounts for the Login action. An abbreviated code sample is below.
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
// ...
// Does the user need to complete some missing information?
if (externalUser.IsSecurityQuestionInfoComplete == false)
return RedirectToAction("ChangeSecurityQuestions", "MyInfo");
if (externalUser.ForcePasswordReset)
return RedirectToAction("ChangePassword", "MyInfo");
// Login was successful
return RedirectToLocal(returnUrl);
}
}
One problem with this approach is that there are other hyperlinks presented in those targeted views, where the user could navigate away from the targeted interface. So for example, a user directed to the ChangeSecurityQuestions view could just click away from it.
Logged-in users can change those settings at any time. I could create duplicate views for changing passwords and security questions that are fashioned just for this scenario, where the user is being forced to update these values. In the interest of staying DRY and reducing maintenance, I'd like to use the same views for both scenarios (users who just want to edit that info, and users who are being forced to edit that info). But, trying to stay DRY in this respect may be wrongheaded if the alternative is worse.
I started to write a method within a helper class to divert these users, trying something like this.
/// <summary>
/// Check scenarios where the ExternalUser needs to complete some missing information, and divert as necessary.
/// </summary>
public static void divertExternalUserAsRequired(Controller controller, ExternalUser externalUser)
{
if (externalUser.IsSecurityQuestionInfoComplete == false)
return controller.RedirectToAction("ChangeSecurityQuestions", "MyInfo");
if (externalUser.ForcePasswordReset)
return controller.RedirectToAction("ChangePassword", "MyInfo");
}
But that RedirectToAction is inaccessible due to protection level. Moreover, that doesn't appear to be recommended (Is it possible to use RedirectToAction() inside a custom AuthorizeAttribute class?). And I don't like the idea of junking up my controllers by pasting this check all over the place.
UtilityHelpers.divertExternalUserAsRequired(this, externalUser);
What is the recommended approach to handling this scenario? I would perfer something that's more globally implemented, where the check can run when any relevant view loads.
Thanks for your help.
If I'm understanding your question correctly then you've got a few options available to you.
One option is to check the necessary conditions within Application_BeginRequest in your Global.asax.cs class. This method is called at the very beginning of every request and if the condition fails then you can load a different controller action like so:
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (!externalUser.IsSecurityQuestionInfoComplete)
{
var routeData = new RouteData();
routeData.Values["action"] = "MyInfo";
routeData.Values["controller"] = "ChangeSecurityQuestions";
RequestContext requestContext = new RequestContext(new HttpContextWrapper(Context), routeData);
IController errorController = new ChangeSecurityQuestionsController();
errorController.Execute(requestContext);
requestContext.HttpContext.Response.End();
}
}
Another option available to you is to create and register a global action filter. As you mentioned in your question, you don't like the idea of littering your controllers with these condition checks. By registering a global action filter your controllers can remain completely unaware of the action filter being performed against it. All you need to do is register your action filter within Global.asax.cs like so:
protected void Application_Start()
{
...
GlobalFilters.Filters.Add(new SecurityQuestionCompleteFilter());
...
}
I hope this helps.
I have a ASP.NET MVC 4 application. I am trying to implement a solution where I check if a user can access a view, if not then display an error. If the user can access a view then I need to check if that user has read access or read and edit access to that view. If the user has read access then just a normal details view is displayed, if read and edit access then the user can see a details view or can edit the data.
Is something like this possible? I have tried looking through a couple of starter kits that I found on Codeplex but I can't find something like I want. How would this be implemented? If possible, if anyone knows of any sample project that I can download then I will appreciate. I like to work through code, I learn more this way.
I want this all to be database-driven.
You should look more into the AuthorizeAttribute and how to use roles. Basically you give your users roles (that may be fetched from the database) and for every view you wish to limit access in your application you add the Authorize attribute together with the roles. By default I think you can only limit to access or no access but you can probably override and write your custom attribute to give you the behavior you are interested in.
Alternatively, you can manage the user privileges in your controllers. Since you know the authenticated users roles and have access to them you can perform the check in the controller and create the view from that (disabled inputs etc.)
Related
ASP.Net MVC: Can the AuthorizeAttribute be overriden?
Granular permissions with certain requirements for an MVC site
As #Marcus said, you should use Attribute. When action starts, you can check user's role, he has access or not:
public class AttributeForTestAttribute : ActionFilterAttribute
{
public int RoleCanAccess { get; set; }
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
//your validation here..
//for example:
if(_currentUser.Role < RoleHasAccess )
{
//user has not access to this action, redirect him to home page.
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary { { "controller", "Home" }, { "action", "Index" }, { "returnUri", filterContext.HttpContext.Request.RawUrl } });
}
else
{
// user has access to this action
}
}
}
And in controller use your Attribute:
[AttributeForTest(RoleHasAccess = 2)]
public ActionResult SaveProduct(Product product)
{
}
I have 3 different types of users (with different roles) interacting on my web application, they all perform some task - some can be exactly the same e.g. create a quote others can be unique to that specific user e.g. sign off quote.
For more clarity 3 types of users: Client, Supplier, Customer.
Client or Customer can create a quote, however only the Customer can sign off a quote.
How do I ensure my application allows clients to access client speficic controllers and suppliers to access supplier specific controllers or areas. Via Custom Attributes? Do I store the type of user inside a cookie? Is this safe? or Session state? As soon as someone logs onto the system I send back a LoggedOnDTO object on which I store Username, UserID, and type of user....
NOTE: I went away from asp.net build in way of creating users, I have my own custom tables with my custom mechanism for logging into the system. I have a registered Model Bindiner that looks for the prefix and I send in a strongly typed object to each action...
Sample code:
[HttpGet]
public ActionResult AddComment(int quoteid, ClientUserDTO loggedonclientuser)
{
}
[HttpGet]
public ActionResult AddCommentSupplier(int quoteid, Supplier loggedonsuppluser)
{
}
EDIT: This method for some reason seems so much simpler... Is there something wrong with it? Any possible security issues? Threading?
My session controller is:
if (_authService.isValidUser(model))
{
var data = _authService.GetAuthenticationCookieDetails(model);
AuthenticateCookie.AddDetailsToCookie(data);
return Redirect(Url.Action("Index", "Activity"));
}
When I create my cookie... I can simple store "ClientUser", "Supplier" or whatever role they are inside the cookie.
Then I can create an Attribute and read in the cookie data to see if they are a valid user e.g.
public class ClientAuthorizationAttribute : AuthorizeAttribute
{
public bool AlwaysAllowLocalRequests = false;
protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
{
if (AlwaysAllowLocalRequests && httpContext.Request.IsLocal)
{
bool authorized = false;
var result = UserDetails.GetTypeFromTicket(httpContext.User.Identity as FormsIdentity);
if (result.Equals("client", StringComparison.OrdinalIgnoreCase))
{
authorized = true;
}
//throw no access exception?
return authorized;
}
return base.AuthorizeCore(httpContext);
}
}
Register the attribute under my base controller and I have a simple working solution???
Write a custom MembershipProvider and a Custom RoleProvider then you can decorate your controler class or specific methods with the attribute
<Authorize(Roles:="ROLENAME")>
You can learn how to make that your asp mvc use the custom membershiprovider in this question It's really easy.
Edited:
The way you did it looks right, but I think you take the long way. Implementing your own MembershipProvider and your own Roleprovider will take you no more than 20 minutes... and you will have the benefits of being working with a well tested and documented system and still having the benefits of use your own database tables for the login. In a simple login system, you only have to write two functions in the roleprovider (GetRolesForUser and IsUserInRole) and only one function in the membershipprovider (ValidateUser) and you will get your system working.
If you want, I can put somewhere (maybe pastebin) a well commented versiĆ³n of a membershipProvider as well of a roleprovider that i'm using in a simple app (they're made in vb.net but i'm sure it will not be a problem)
You can also write generic code in the base controller instead of decorating each action with Authorize attribute. Please refer below url.
Good practice to do common authorization in a custom controller factory?
custom-controller-factory/5361846#5361846
I already know about User and Role-based security in ASP.NET MVC. But now I need something a little more granular.
Let's say I have a list of documents, some of which the user is authorized for, some not. Each document has a corresponding record in a documents table in a database. Documents can be downloaded for viewing, if the user has security access. Documents can also be added, if you have the role. Each document has an URL, and each document list has an URL.
I would like to security trim the list so that the user only sees those documents for which he is authorized. But I also need to authenticate the URL requests for these lists and documents, since there is nothing preventing a user from bookmarking a document they no longer have access to, or simply typing the URL into the browser.
Is the built-in role-based security model suitable for this, or do I need to create separate, table-based security? Can I put the security in my repository, so that the returned records are already trimmed, or should it be part of the controller? Do I need a security attribute to validate the controller request, or should I just put it in the controller method as the first few lines of code?
#Robert, I think you've already answered your own question when you said you should trim them (before) they reach the view. So in your Business logic, as a preference over the repository, you might want to do a lamda to trim off the excess so to speak.
Im my opinion I would never return any records to the view that the user wasn't allowed to see. Why increase risk and traffic?
As for the bookmarks I think there you're going to need to do some business logic preventing them from going to the url when access no longer exists.
I thought the controller was simply there to service the data to the page and not to have any logic as such so I'd prefer the business layer approach for this one as it does appear to be a business rule.
That might not be what you had in mind but unless there is a better approach it's the one I would use.
I'll try to explain how I intended to implement this in my project. The requirement is similar as yours: Users have Roles which have Permissions and everything can change from Permission definition, Role's Permission list, and User's Role list etc. So in one moment it's possible that User has access to something and in another, if Administrator alter something, he does not have access.
Before I put some code, I'll answer to your questions.
Do I need to create separate,
table-based security?
-Yes
Can I put the security in my
repository, so that the returned
records are already trimmed, or should
it be part of the controller?
-I think security should be a part of business logic so I would put it somewhere in between controller and repository.
Do I need a security attribute to
validate the controller request?
-In my project, I've put it in attribute, but sometimes i need to access it from controller to, but since that I keep security logic in business layer, I don't think it is a problem.
First attribute is simple attribute that just allows logged users to execute action:
public class LoggedUserFilterAttribute : ActionFilterAttribute
{
public bool Logged { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!SessionManager.IsUserLogged)
{
filterContext.Result = new RedirectToRouteResult(GetRedirectToNotLoggedRouteValues());
this.Logged = false;
}
else
this.Logged = true;
}
public RouteValueDictionary GetRedirectToNotAuthorizedRouteValues()
{
RouteValueDictionary routeValues = new RouteValueDictionary();
routeValues.Add("action", "NotAuthorized");
routeValues.Add("controller", "Authorization");
return routeValues;
}
public RouteValueDictionary GetRedirectToNotLoggedRouteValues()
{
RouteValueDictionary routeValues = new RouteValueDictionary();
routeValues.Add("action", "NotLogged");
routeValues.Add("controller", "Authorization");
return routeValues;
}
}
and then I have, for example, attribute which allows only SuperUsers to access it:
public class SuperUserFilterAttribute : LoggedUserFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
if (Logged)
{
MyBaseController controller = filterContext.Controller as MyBaseController;
if (controller == null)
throw new Exception("Please use MyBaseController instead of built in Controller");
User loggedUser = controller.Model.UserBO.GetUserByID(SessionManager.LoggedUser.UserID);
if(!loggedUser.IsSuperUser)
{
filterContext.Result = new RedirectToRouteResult(GetRedirectToNotAuthorizedRouteValues());
}
}
}
}
The MyBaseController is class that inherits Controller and has an instance of Model class which represent container for business objects. In controllers action body, if needed I check users rights on current entity and depending on that I return proper view:
[LoggedUserFilter]
public ActionResult LoadSomeEntity(int customerServiceID,int entityID)
{
UserRights userPermissionsView = Model.SecurityBO.GetUsersRightsOnEntity(SessionManager.LoggedUser.UserID, entityID);
if(userPermissionsView.Write)
return View("EditEntity",Model.EntityBO.GetEntityByID(entityID));
if(userPermissionsView.Read)
return View("ViewEntity",Model.EntityBO.GetEntityByID(entityID));
return View("NotAuthorized");
}
p.s. I'm not sure if I can suggest anything to someone that obviously has much more experience that me :), so if I'm spamming, I apologize for that.