In our project management application, we need to keep access rights (per project for each user) in a separate table in db. IE.
User A can create tasks in Project #1,
User A can only read task in Project #2
To succeed that, we extend our AspNetUserRoles table by adding a new coloumn(projectID).
By default, in razor view, we can check user roles with User.IsInRole("CanCreateTask"). We need to extend this method and want to check the user role for specific project Ie. User.IsInRole("CanCreateTask", project.Id)
EDITED: Also i need to check user access in controller with project.Id
[Authorize(Roles = "CanCreateTask")]
Did some research on net but could not find any solution.
You could create an extension method, extending the User class.
public static bool IsInRole(this User user, string role, int projectID)
{
var isInRole = user.IsInRole(role);
var hasRoleInProject = // Logic for deciding if it is in the role for this project
return isInRole && hasRoleInProject;
}
This method would be called like this:
user.IsInRole("CanCreateTask", project.Id);
Related
I have been looking for a way to get a complete Userobject based on username in the default Identity model of AspNet MVC for any user. I am using asp.net Identity 2.
Through some google searches the following is the closest I came without directly querying to the database.
var user = UserManager.Users.FirstOrDefault(u => u.UserName == userName);
However UserManager requires a generic type of UserManager<TUser> and I have no clue what gereric type I am supposed to fill out here, or how I am even supposed to make this work. I'd prefer using the default asp.net functions so I don't have to query to the database myself.
I'm going to assume by default you mean the set up Visual Studio gives you when you choose
an ASP.NET Web Application with MVC and individual user accounts.
You will need the following using statement
using Microsoft.AspNet.Identity;
I believe you want the FindByName method or the FindByNameAsync method. It is a generic method so you can call it like this
var user = userManager.FindByName<ApplicationUser, string>("{the username}");
where in this case the type of the user primary key is string. But you should be able to drop the generic type parameters and just call it like this
var user = userManager.FindByName("{the username}");
If you aren't using this default set up the thing to do would be to find the type that your userManager inherits from. In visual studio you can do this by hovering over the ApplicationUserManager and pressing F12. In the default case it looks like this
public class ApplicationUserManager: UserManager<ApplicationUser>
{
//...
}
But whatever the type that the UserManager has is what you need.
ApplicationDbContext context = new ApplicationDbContext();
string CurrentUser = User.Identity.Name;
var applicationUser = await context.Users
.Where(a => a.Email.Equals(CurrentUser)).FirstOrDefaultAsync();
I'm building a web app (asp.net mvc),
where i'm using the attribute [Authorize] on GET and Post.
For example:
[Authorize]
public ActionResult EditClient(string id)
{
//Do Stuff
}
I now want to look to ensure that the logged in user, can only access data that belongs to that user\account?
But I'm not sure how to do this, does .Net already provide methods\attributes to use?
For example, this is how I would get a client:
[Authorize]
public ActionResult EditClient(string id)
{
var user= new Token(this.User.Identity.Name);
//user.id
//user.accountId
//So does this Client belong to the same account as the user is in?
//We know the client and user both belong to an account(id)
//Are we allowed to return the below?
var client = _clientService.GetClient(id);
//client.id
//client.accountId
}
As mentioned not to sure what best practice\options I should apply, obviously I know I should apply this kind of logic in most places?
Ideas? Sample?
There are many ways you could achieve this. for example you could create a custom attribute that takes in the parameter and checks the resource belongs to the requesting user. This could get complex as you'd have many different attributes for each type of entity you are accessing.
You probably want other validation rules such as the requested client even exists (i.e. non existing id) I would extract a bunch of rules out such as entity exists, requested entity belongs to authorised user, entity is editable etc etc and inject that into your actions before performing changes or returning said entities, you could throw custom exceptions depending on which validation fails and then send a generic 500, or 400 down to the user with minimal error details (no stack trace). So your action could look something like:
[Authorize]
public ActionResult EditClient(string id)
{
editClientValidator.Validate(id);
var user= new Token(this.User.Identity.Name);
//user.id
//user.accountId
//So does this Client belong to the same account as the user is in?
//We know the client and user both belong to an account(id)
//Are we allowed to return the below?
var client = _clientService.GetClient(id);
//client.id
//client.accountId
}
Where the EditClientValidator class contains your custom rules for editing a client. Alternatively you could create an attribute essentially doing the same thing but only for access (client belongs to the authenticated user)
I find lots of information about Identity but nothing specifically addressing this very common scenario.
I have a controller named ShowAccount() that should display the account data of the currently logged in user, and prevent him from seeing anything but its own account.
Also unauthenticated users should not be able to access this functionality at all.
How do I achieve this?
Thanks
Unauthenticated Users
K, I'll start with the simpler request, to block unauthenticated user from having access at all to your controller just add this attribute:
[Authorize]
above your controller, or if you want to allow some\disable some functions in the controller you can place it above the specific function.
In case you want to block your entire controller and allow just a few functions you can use this attribute:
[AllowAnonymous]
Limit user access to his own data
I'm doing something similar in one of my project so I thought it might help, nothing fancy, I would love to hear a better option myself.
For your 2nd issue, I assume that you have a model that stores data and that data has some kind relation to the UserID (foreign key maybe?).
What you can do is in your controler - filter the data you send back to the user, i.e on the view instead of returning:
return View(db.MyDB.ToList());
return:
MyDBClass data = db.MyDB.Where(u => u.UserID == GetUserID()).ToList();
return View(data);
Assume GetUserID() is a function that gives you the current user ID, in case you use the default authentication in MVC I can share it here as well.
This solution tho is not complete, you need to continue enforcing it in any other actions such as edit\delete\create or what ever other actions you support, you need to always check that the user is accessing only his data by comparing between the userID saved in the DB to the one in the request.
Hope this helps.
I had a similar challenge but I got mine
public ActionResult Create()
{
return View();
}
// POST: ArtistGig/Create
[HttpPost]
public ActionResult Create(ArtistGig artistGig)
{
var userid = User.Identity.GetUserId();
///
var artist = db.ArtistHubs.SingleOrDefault(a => a.ApplicationUserId == userid).Id;
artistGig.ArtistHubId = artist;
db.ArtistGigs.Add(artistGig);
db.SaveChanges();
return RedirectToAction("Index");
}
User.Identity.GetUseId is to query for the loged in user's Id according to the DbContext you are using
Can someone describe to me just how you delete roles with asp.net identity. I tried the following, but it did not work and I received a Specified Method is not supported error:
public async Task DeleteRole(string role)
{
// delete role
var roleStore = new RoleStore<IdentityRole>(new Context());
await roleStore.DeleteAsync(new IdentityRole(role));
}
Not sure if this is referring to something with my async logic, or specifically with asp.net identity itself. Nevertheless can someone demonstrate to me how to make this work correctly. There is virtually no documentation available on the new identity system for asp.net at this time.
The Identity context (IdentityDbContext) contains the role store. So you would (assuming AppDb is your context):
var role = AppDb.Roles.Where(d => d.Name == "my role name").FirstOrDefault();
AppDb.Roles.Remove(role);
AppDb.SaveChanges();
You basically treat it as a normal EntityFramework DbSet, it's inherited from the IdentityDbContext.
I know that is an old question, but I've tried a way that doesn't touch store (or DbContext) directly.
I've used it in ASP.NET Core 2.1
var role = await _roleManager.FindByNameAsync(roleName);
var result = await _roleManager.DeleteAsync(role);
needless to say that it:
delete role that assigned to user -> AspNetUserRoles
delete role's claims -> AspNetRoleClaims
delete role itself -> AspNetRoles
When I implement the RoleProvider class and call Roles.IsUserInRole(string username, string roleName), code execution first goes to the method 'GetRolesForUser(string username)'. Why is this? I don't want to iterate all roles when I am just looking for the single value of whether that user belongs in one role. Is this a limitation of .NET's role provider class or is there something I can do to control the execution of code a bit more?
Here's the calling code
if (Roles.IsUserInRole(CurrentUser.UserName, "Teacher")) {
And here's the implementation of IsUserInRole
public override bool IsUserInRole(string username, string roleName) { return true; }
But the code GetRolesForUser always gets implemented first:
public override string[] GetRolesForUser(string username) {
string[] roles = GetAllRoles();
List<string> userRoles = new List<string>();
foreach (string role in roles) {
if (IsUserInRole(username, role)) {
userRoles.Add(role);
}
}
return userRoles.ToArray();
}
The RoleProvider.IsUserInRole(username, password) is used for checking roles for a given user which is not the current loggon user(for current logon user, it also use the Principal.IsInRole instead). And for RolePrincipal, it always use the GetRolesForUser to cache the roles and do the role checking among the cached role list. (source)
can user Roles.Provider.IsUserInRole instead of Roles.IsUserInRole
There's a layer Microsoft's role provider solution that enables caching a user's roles in a cookie so it doesn't need to call the provider's GetRolesForUser method. I believe the cookie caching is part of the Roles class, so as long as you implement from the RoleProvider base class, it should be compatible. It's worth a look at the code in reflector to get an idea of how MS implements their own abstract classes, and what the static helper classes do (Roles and Membership)
Try adding cacheRolesInCookie="true" to the roleManager element in your config file, and see if the flow changes.
Since you're using your own implementation of a RoleProvider, you can also override the IsUserInRole method and provide your own implementation of checking if a user is in a role.
UPDATE: This block of code gets called inside the Roles.IsUserInRole method:
IPrincipal currentUser = GetCurrentUser();
if (((currentUser != null) && (currentUser is RolePrincipal)) && ((((RolePrincipal) currentUser).ProviderName == Provider.Name) && StringUtil.EqualsIgnoreCase(username, currentUser.Identity.Name)))
{
flag = currentUser.IsInRole(roleName);
}
else
{
flag = Provider.IsUserInRole(username, roleName);
}
The else block is what will call your custom provider's IsUserInRole method.
So the roles for your user have not yet been added to the Principal object. If you just haven't gotten around to that step yet, OK. If not, make sure you do that. It will make sure that every time you call Roles.IsUserInRole, or User.IsInRole that those functions will use an in-memory cache of the roles for the user (once loaded) instead of having to go to the database every time. (Although the base role provider and Roles manager class should take care of this for you.)
Can you verify the config file settings for the role provider? Also, what version of .net are you using? Are you manually managing the login process or are you using the .net login control? Have you implemented a custom Roles class? Or are you using the System.Web.Security.Roles?