I have just created a custom Authorize method so that a User on the website I am building, is only able to see his/her view.
public class UserAuthorize : AuthorizeAttribute
{
public string Username { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
return false;
}
string username = httpContext.User.Identity.Name;
if(new RolesBL().IsUserInRole(username, 1))//1 is Admin
{
return true;
}
return Username.Equals(username);
}
}
I am having trouble passing through the data from the controller to this method
[UserAuthorize(Username = username)]
public ActionResult Details(string username)
{
User u = new UsersBL().GetUser(username);
return View(u);
}
How can I pass the username in the View's parameter to the Authorize method as well.
Thanks and Regards
This is not a case where using authorization makes sense. You should instead prevent the user from being able to pass in an argument to override the user that they are logged in as.
public ActionResult Details()
{
User u = new UsersBL().GetUser(this.User.Identity.Name);
return View(u);
}
Your query then acts as a filter to ensure only the logged in user's information is seen.
If you need a super user to be able to view/edit each user, then you would need to use a role to ensure that only users in that role can edit other users. But in that case, the standard AuthorizeAttribute will suffice.
public ActionResult Details()
{
User u = new UsersBL().GetUser(this.User.Identity.Name);
return View(u);
}
[Authorize(Roles = "SuperUser")]
public ActionResult Details(string username)
{
User u = new UsersBL().GetUser(username);
return View(u);
}
The arguments in an [Attribute] declaration are all defined at compile-time, and must therefore be constants. You can't "pass in" the run-time value of an action parameter.
Besides which, the attribute will be used prior to the action method being invoked (to determine whether the action can be invoked) - and so that parameter won't even be instantiated.
You might however be able to access the username value within the UserAuthorize attribute implementation, since the httpContext parameter gives you access to the request details, which presumably contains the user name as part of the query string.
Related
Using ASP.NET MVC 5 and Entity Framework. How can I secure my application so I cant access other users data?
To do CRUD stuff I have index, create, edit, delete methods in FooController so I can use:
/Foo/
to view my information I click one and get
/Foo/Details/5
When I type in 3 in the browser I get someone else's information.
/Foo/Details/3
How can I secure this from being accessed everywhere? I use Owin Identity and are logged into the application.
You could write a custom authorization filter by deriving from the AuthorizeAttribute class and inside you could check whether the currently authenticated user has access to the requested resource.
For example:
public class MyAuthorizeAttribute: AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
return false;
}
string id = httpContext.Request["id"];
string currentUser = httpContext.User.Identity.Name;
return HasAccessToResource(id, currentUser);
}
private bool HasAccessToResource(string id, string currentUser)
{
// You know what to do here => check in your backend whether the
// current user is authorized to access the specified resource id
throw new NotImplementedException();
}
}
and then decorate your controllers/actions with this custom attribute:
[MyAuthorize]
public ActionResult Delete(string id)
{
// if you get that far, the current user is owner of the requested resource
...
}
I was looking for an implementation / example of loading and authorizing a resource at a controller level. I am looking for the same functionality as load_and_authorize_resource in the cancan gem in ruby on rails.
Has anyone come across one / have an example how to implement something similar using Mvc .Net attributes?
Thanks!
The load_and_authorize_resource behaviour
With rails, controller and model names are linked up by convention. The attribute load_and_authorize_resource takes that to its advantage. When an action is hit that requires an instance of a resource, the load_and_authorize_resource verifies whether the instance of the resource can be accessed. If it can, it will load it up in an instance variable, if it cant, it will return a 404 or any error behaviour you have configured the attribute to produce.
For example, if I have a resource picture, and only user that own a certain picture can edit the picture's name.
So we would have a Edit action, which obviously would have a pictureId of the picture you want to edit. load_and_authorize_resource would verify whether the current context/user has access to the resource.
Here is a small video introduction of the module.
I am not aware of the existence of such plugin for ASP.NET MVC. To mimic it's functionality you could write a custom Authorize attribute though:
public class LoadAndAuthorizeResourceAttribute : AuthorizeAttribute
{
private class ModelDescriptor
{
public string Name { get; set; }
public Type ModelType { get; set; }
}
private const string ModelTypeKey = "__ModelTypeKey__";
public override void OnAuthorization(AuthorizationContext filterContext)
{
var parameters = filterContext.ActionDescriptor.GetParameters();
if (parameters.Length > 0)
{
// store the type of the action parameter so that we could access it later
// in the AuthorizeCore method
filterContext.HttpContext.Items[ModelTypeKey] = new ModelDescriptor
{
Name = parameters[0].ParameterName,
ModelType = parameters[0].ParameterType,
};
}
base.OnAuthorization(filterContext);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
// the user is not authenticated or authorized => no need to continue
return false;
}
// get the currently authenticated username
string username = httpContext.User.Identity.Name;
// get the id of the resource that he is trying to manipulate
// the id should be sent either as part of the query string or the routes
string id = httpContext.Request.RequestContext.RouteData.Values["id"] as string;
// get the action param type
var modelDescriptor = httpContext.Items[ModelTypeKey] as ModelDescriptor;
if (modelDescriptor == null)
{
throw new InvalidOperationException("The controller action that was decorated with this attribute must take a model as argument");
}
// now load the corresponding entity from your database given the
// username, id and type
object model = LoadModel(id, username, modelDescriptor.ModelType);
if (model == null)
{
// the model that satisfies the given criteria was not found in the database
return false;
}
httpContext.Request.RequestContext.RouteData.Values[modelDescriptor.Name] = model;
return true;
}
private object LoadModel(string id, string username, Type modelType)
{
// TODO: depending on how you are querying your database
// you should load the corresponding model here or return null
// if not found
throw new NotImplementedException();
}
}
and now you could have a controller action that is decorated with this attribute:
[LoadAndAuthorizeResource]
public ActionResult Edit(Picture model)
{
... if we get that far the user is authorized to modify this model
}
I have the following structure in my DB:
DomainEntities:
+EntityID
+Name
+ParentID
+...
Users:
+UserID
+Username
+...
Roles:
+RoleID
+Name
UserRolesAssociation:
+RoleID
+UserID
+EntityID
So i want to use MVC's built in authorization attribute to filter action in my controllers that are made by different members.
I what to be able to say if user1 makes a delete action on entity1 or any entity under it i can see if he has the right role to do that and filter the action accordingly.
What would be the best practice to tackle that topic ?
Should i create my own permissions engine that will provide me the answers i need or can i use the existing capabilities ?
What would be the best practice to tackle that topic ?
A custom [Authorize] seems like a good place to implement this logic.
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
// the use ris not authenticated or not authorized - no need to continue
return false;
}
string username = httpContext.User.Identity.Name;
// read the entity id that this user is attempting to manipulate
string entityId = (string)httpContext.Request.RequestContext.RouteData.Values["id"] ?? httpContext.Request["id"];
return IsAllowed(username, entityId);
}
private bool IsAllowed(string username, string entityId)
{
// You know what to do here - hit the database and check whether
// the current user is the owner of the entity
throw new NotImplementedException();
}
}
and then:
[HttpDelete]
[MyAuthorize]
public ActionResult Delete(int id)
{
...
}
I've a UI with Jquery which makes a call to MVC using Ajax request.
I would like to validate each request against the userProfile (custom class which holds account number, ID etc).
Could anyone please suggest whether it is possible to create custom Authorize Attribute to validate that both request and userprofile are same?
I would then like to do something like below:
[AuthorizeUser]
public ActionResult GetMyConsumption(string accountNumber)
{
.....
return View();
}
You could write a custom Authorize attribute:
public class AuthorizeUserAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
{
// The user is not authorized => no need to continue
return false;
}
// At this stage we know that the user is authorized => we can fetch
// the username
string username = httpContext.User.Identity.Name;
// Now let's fetch the account number from the request
string account = httpContext.Request["accountNumber"];
// All that's left is to verify if the current user is the owner
// of the account
return IsAccountOwner(username, account);
}
private bool IsAccountOwner(string username, string account)
{
// TODO: query the backend to perform the necessary verifications
throw new NotImplementedException();
}
}
How can I include a user regardless of his role, dependent on a matching userID, and not always same user:
[Authorize(Roles="Group1") AND userID=uniqueID]
You won't be able to do this with the default AuthorizeAttribute. You will need to extend AuthorizeAttribute with a custom class that adds the user behavior. Typically it uses named users, but you could provide an alternative. Normally if you supply both Users and Roles, it will require that the user be in the list of users and have one of the indicated roles.
public class UserOrRoleAuthorizeAttribute : AuthorizeAttribute
{
public int? SuperUserID { get; set; }
protected override bool AuthorizeCore( HttpContextBase httpContext )
{
if (base.AuthorizeCore( httpContext ))
{
var userid == ...get id of current user from db...
return userid == this.SuperUserID;
}
return false;
}
}
Used as:
[UserOrRoleAuthorize(Roles="Admin",SuperUserID=15)]
public ActionResult SomeAction() ...
Note that you could also add in some way of specifing where to look for the id for this action, .i.e.,
Table="Admins",Column="AdminID",MatchProperty="Company",MatchParameter="company"
then put some code into the attribute to look up the value in the property table and column and comparing it to the specified RouteValue entry instead of hard-coding it.
You could write a custom Authorize filter (implement IAuthorizationFilter)
Your custom Authorize filter could take the userId as a parameter.
Something like
public class
YourAuthorizeFilterAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public string UserId { get; set; }
public void OnAuthorization(AuthorizationContext filterContext)
{
if(filterContext.HttpContext.User.Identity.Name != UserId &&
!filterContext.HttpContext.User.IsInRole(base.Roles))
{
filterContext.Result = new RedirectResult("/Account/LogOn");
}
}
}
Then use your own filter like so
[YourAuthorizeFilter(UserId = "theuser", Roles ="Group1")]
Kindness,
Dan