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)
{
...
}
Related
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.
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
...
}
In my ASP.net MVC project I've got (among other roles) moderators and users. I want to give the moderators the option to "see current page as user".
My approach is to create a ActionFilterAttribute and overload OnActionExecuting & OnResultExecuted as the page is then rendered for the given user.
The first idea there was to juggle with the Roles:
OnActionExecuting {
... //various checks, if role exist, if user want to switch
var tempRoles = Roles.getRolesForUser(user);
filterContext.HttpContext.Items["tempRole"] = tempRoles;
Roles.RemoveUserFromRoles(user, tempRoles)
Roles.AddUserToRole(user, targetRole);
}
and then
OnResultExecuted {
//if switched view
{
Roles.RemoveUserFromRole(user,targetRole)
Roles.AddUserToRoles(filterContext.HttpContext.Items["tempRole"])
}
This works, but in a worst case scenario the roles are gone, so i prefer to not touch them...
My second idea was to create a dummy user add him to the userroles sign the moderator into this account with FormsAuthentication.SetAuthCookie(dummyUser, true) and revert everything in the OnResultExecuted, so in a worst case scenario the user is in the dummyRole (where he can logout) and the dummyUser is in the Database.
After debugging and researching I realised that SetAuthCookie requires a Redirect to come into effect - so it doesn't work this way.
The questions:
Is there a way to force SetAuthCookie to come into affect without a redirect
Any other suggestion/approaches how to accomplish this "see page as other user"?
If my first idea is the only solution, how do i make it foolproof?
Ahoi Christian,
you could decorate the class SqlRoleProvider and add it to the role manager.
See Sample Role-Provider Implementation:
http://msdn.microsoft.com/en-us/library/tksy7hd7%28v=vs.100%29.aspx
The decorated SqlRoleProvider could overwrite the IsUserInRole method and thereby implement impersonation functionality.
edit: I have added the code below:
public class MyRoleProvider : SqlRoleProvider
{
private static ConcurrentDictionary<string, string> impersonationList;
public MyRoleProvider() : base()
{
impersonationList = new ConcurrentDictionary<string, string>();
}
public static void startImpersonate(string username, string rolename)
{
impersonationList.TryAdd(username,rolename);
}
public override string[] GetRolesForUser(string username) {
if (impersonationList.ContainsKey(username))
return new string[] { impersonationList[username] };
else
return base.GetRolesForUser(username);
}
public static void stopImpersonate(string username)
{
string rolename;
impersonationList.TryRemove(username, out rolename);
}
}
I'm building a Asp.net MVC3 aplication (with Razor) and I have a Data Base that have information about users and roles.
This is simplified scheme of my DB.
User(IDUser, Login, Password);
Role(IDRole, Name);
UserInRole(IDUser, IDRole); //Many to Many
Looks like this:
I read about use AuthorizeAttribute, to control pages for loged users, and with specific roles and I research about use My DB to control users and roles. So my questions is:
Is possible use my DB to manage users and roles and use [Authorize] in my actions? [If yes how i do that?]
Is possible use session in the place of cookie to manage login and use the Authorization native Asp.net MVC3? [if yes, how i do that? if no how use session otherwise?]
If possible please post code examples.
Not sure if I understood correctly, but you want to use the [Authorize] attribute to work with your custom users database?
If that's the case, there are somethings to check:
To simply allow/deny based whether the user is authorized or not, the stock [Authorize] attribute will work just fine. The custom logic goes in your Login action, where you will check the database with the given credentials and issue the cookie accordingly. Something like:
public ActionResult Login(string username, string password)
{
bool isValid = //check the database with the given username and password
if(isValid)
{
FormsAuthentication.SetAuthCookie(username, false);
return RedirectToAction("...");
}else
{
return View();
}
}
If you want to also control access based on roles, I would say there are 2 immediate ways:
Implement a custom Membership and Role providers, something I don't like as I find them pretty useless, and always end up redoing the logic in my respositories
Implement a custom AuthorizeAttribute, like
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//Check based on these 2 properties:
// this.Roles
// this.Users
//against httpContext.User
//return true or false to indicate access or deny
}
}
Thanks Pedro. Based in your post I build this to use SESSION:
public class CustomAutorizeAttribute : AuthorizeAttribute
{
public List<string> Roles { get; set; }
public CustomAutorizeAttribute()
{
}
public CustomAutorizeAttribute(params string[] Roles)
{
this.Roles = new List<string>();
this.Roles.AddRange(Roles);
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
User user = (User)httpContext.Session["user"];
if (user != null)
{
if (Roles != null)
{
foreach (var role in user.Roles)
{
if (Roles.Exists(e => e == role)) return true;
}
return false; // User don't have any hole
}
else
{
return true; // The Attribute is used without roles.
}
}
else return false; // Not logged.
}
}
Post here to hope others.
I am building a simple CMS in which roles are set dynamically in the admin panel. The existing way of authorizing a controller method, adding [Authorize(Roles="admin")] for example, is therefore no longer sufficient. The role-action relationship must be stored in the database, so that end users can easily give/take permissions to/from others in the admin panel. How can I implement this?
If you want to take control of the authorization process, you should subclass AuthorizeAttribute and override the AuthorizeCore method. Then simply decorate your controllers with your CmsAuthorizeAttribute instead of the default.
public class CmsAuthorizeAttribute : AuthorizeAttribute
{
public override virtual bool AuthorizeCore(HttpContextBase httpContext)
{
IPrincipal user = httpContext.User;
IIdentity identity = user.Identity;
if (!identity.IsAuthenticated) {
return false;
}
bool isAuthorized = true;
// TODO: perform custom authorization against the CMS
return isAuthorized;
}
}
The downside to this is that you won't have access to ctor-injected IoC, so you'll have to request any dependencies from the container directly.
That is exactly what the ASP.NET membership / profile stuff does for you. And it works with the Authorize attribute.
If you want to roll your own you could create a custom action filter that mimics the behavior of the standard Authorize action filter does. Pseudo code below.
public MyAuthorizeAttribute : ActionFilterAttribute
{
public string MyRole { get; set; }
public void OnActionExecuting(ControllerContext context)
{
if (!(bool)Session["userIsAuthenticated"])
{
throw new AuthenticationException("Must log in.");
}
if (!Session["userRoles"].Contains(MyRole))
{
throw new AuthenticationException("Must have role " + MyRole);
}
}
}
The role - action relationship must be
stored in the database
You will have to check your security within the controller method, unless you want to subclass AuthorizeAttribute so that it looks up the roles from the database for you.