I want to give the user access according to the user role.
I have two user roles. they are Admin and user.
I write my controller like this.
[Authorize(Roles = "Admin")] // my Problem is here. I don't know how to set the current user role
public ActionResult Index()
{
var query = from company in db.tblCompanies
select company;
return View(query.ToList());
}
But I don't know how to set the Roles = "Admin" after cutomer login.
In my user tale I have Roles coloum and it can save Admin or user.
But I don't know how to set and where should I set Roles = "Admin".
Based on your question what I get is you want to set the currently logged user to some role. So here is my answer to that.
To Add a User to a Role:
Roles.AddUserToRole(userName, roleName);
To Remove a User from a Role:
Roles.RemoveUserFromRole(userName, roleName);
Reference Links:
SO - How to Assign roles to User while creating their account
MSDN - Implementing a Custom Role Provider
MSDN - Roles.AddUserToRole Method
MSDN - Roles.RemoveUserFromRole Method
Take a look at this :
http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-7
You basically assign roles to your users via the ASP.NET Configuration website. Once done the user - role mapping is handled by default.
Related
I have an ASP.NET core MVC site with a SQLite data connection using ASP.NET core authorization like this:
// Startup.ConfigureServices
services.AddAuthorization(e =>
{
e.AddPolicy(Policies.UserRead, b => b.RequireRole("Editor", "Root"));
}
This is one of the policies which restricts access to site with user information. (Policies.UserRead is a constant string). This policy is then applied to the view like this:
[Authorize(Policies.UserRead)]
public async Task<IActionResult> Index()
{
}
This works great and only users with the role Editor or Root can access the view. But problems arises when the role of a user is changed while logged in. e.g.
User A (role Editor) logs in and uses Index() - success
User B (role Root) logs in and removes the role Editor from user A
User A uses Index() - still success
You would expect that user A can not access Index() anymore because he no longer as the role Editor. Buuuut he still can - as long as he does not log out and log back in again, because relogging fixes this issue. It seems like somebody (I think ClaimsPrincipal is the culprit here) cached the role - which would be OK if I knew how to invalidate the cache...
Role changing code:
// get the user whos role is changed
var targetUser = await _context.Users.SingleOrDefaultAsync(m => m.Id == model.Id);
if (targetUser == null) return NotFound();
// get the user who changes the role
var sourceUser = await _userManager.GetUserAsync(User);
if (sourceUser == null) return RedirectToAction("Index");
// remove the current role
await _userManager.RemoveFromRoleAsync(targetUser, targetUser.Role.ToString());
// add to the new role
await _userManager.AddToRoleAsync(targetUser, model.Role.ToString());
// update & save the changes
_context.Update(targetUser);
await _context.SaveChangesAsync();
This is basically the code I use to change the role of a user (I cut out the view/model parts because they are irrelevant). Notes:
targetUser and sourceUser are both ApplicationUser (which implements IdentityUser).
_userManger is - who would have thought - of the type UserManager<ApplicationManger>
I tried to relog the user using SignInManger<> but it seems like you can only log out the current user - which would be the user changing the role and not the user whose role would be changed.
What am I missing? It would be nice if the user would not have to do anything (e.g. sign back in) in order to "refresh" the user role.
The problem is that the user's role claims are stored in the cookie(default implementation of aspnet identity) so unless the user sign out even if the user's roles change, authorization result does not change. The solution is to use ValidateAsync event. Example exists in the official docs.
Another possible solution is to exclude role claims from cookie and use claims transformation.
For this you need to override the CreateAsync method of UserClaimsPrincipalFactory see this article how to change claims. Then you can use claims transformation to add role claims.
In my MVC application user registration is implemented in two ways; the user register and then approved by the Administrator; or the Administrator can create a user. My question is: will it be possible to send a temporary password and then the user has to change it after first login, or can I flag this user to use external authentication first time.
I would appreciate your suggestions.
You can define a UserAccount class like this:
public class UserAccount
{
public int AccountId { get; set;}
public UserAccountState AccountState { get; set; }
public Guid ActivationCode { get; set; }
public string Password { get; set; }
}
Where UserAccountState is
public enum UserAccountState
{
PendingActivation = 0,
UsingTempPassword = 1
Normal = 2
}
When a new user just signed up. You can put his account to the PendingActivation state and send him a link to activate the account, something like this
www.MySite.com/Activate?code=F3D17EE
When user clicks on the link, you match the user account with the code, and do the following:
Generate a temp password for the account, e.g "TempPass12"
Change the account state to UsingTempPassword
Show the following message to user
"Your account is now activated. Click here to login with your temp password TempPass12"
After user login to your site with the temp password, your code should detect that the UserAccountState is in the UsingTempPassword state and subsequently redirect the user to the change password page.
After a new password is provided by the user, the account can be put to the Normal state.
Add a column in your password table, something like 'ForceToChangePassword'. Check that column every time an user logged in, if it was set to true, redirect user to the change password page.
My opinion is to use roles than using new columns, and checking things every time user logged in as it is not good when we thinking about performances.
Create three new roles it could be
Created - User created by admin
Registered - User registered by them self
Approved - Approved by admin
In your case if the user registered them self, then add them to ROLE 'Registered'. If the user created by admin then add them to ROLE 'Created'. Once admin approved or user change there password first time login, then you can add them to ROlE 'Approved'.
Then you can handle user self registration and admin creation controller actions to add users to correct ROlE.
There is a column called 'EmailConfirmed' already there, so that you can use that column for your purpose. Update that column when the user approved or successfully change the password on first login.
As you know that password field is nullable, so that you don't need to insert temporary passwords, (but you could if you want). You can keep password field as null and update it when the user first login. You need to change your views to support this scenario.
You can use asp.net identity framework supported methods in order to achieve this.
GenerateEmailConfirmationTokenAsync
GenerateEmailConfirmationToken
IsEmailConfirmedAsync
ConfirmEmailAsync
This role based scenario may help you to categorize users depending on there role and restrict access easily using [Authorize(Role = "RoleName")].
Let me know if you need anymore details.
Hope this helps.
I am using ASP.NET Idenetity in my project.
My Users table contains a column "IsAdmin" to identify if a user is admin or not.
In the below code
[Authorize(Roles = "Admin")]
public ActionResult Index()
{
}
I want to authorize user as admin from table Users.IsAdmin=true. Not from UserRoles table.
Please suggest how to do this?
I see 3 options:
Implement IUserRoleStore for ASP.net Identity - it allows you to override IsInRole method
When authenticating check if IsAdmin = true and add Admin role to this user (so you could use default Authorize attribute).
Write a custom Authorization attribute that will contain all the required authorization logic.
I have searched through many SO and other pages but still couldn't find a similar case.
My application is using SimpleMembership. It has role-based user menu and most actions are with [Authorize(Roles = "aaa,bbb")] attribute.
After the application build or leaving the browser idle over some time, user will be redirected to login page from whatever link is clicked. Strange thing is that the login name is still valid to be shown but its without the role names it attached.
Users are able to access actions with [Authorize] attribute in the above situation. I am wondering if there is anything I am missing, or I can do to keep the role information alive as long as the authentication is valid?
Here is code from the controller action to render the menu:
public ActionResult Menu()
{
if (Roles.IsUserInRole("Admin"))
{
return View("MenuAdmin");
}
if (Roles.IsUserInRole("Advising"))
{
return View("MenuAdvising");
}
if (Roles.IsUserInRole("StudentDevelopment"))
{
return View("MenuStudentDevelopment");
}...
Customized _logonPartial to display the login name and roles
> #if (Request.IsAuthenticated) {
<text>
Logged in: #Html.ActionLink(User.Identity.Name, "Manage", "Account", routeValues: null, htmlAttributes: new { #class = "username", title = "Manage" }) as
#String.Join(",", Roles.GetRolesForUser(User.Identity.Name))
#using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm" })) {
#Html.AntiForgeryToken()
Log off
}
</text>
The role information is available as long as the user is logged in. In your case you are calling:
Roles.GetRolesForUser(User.Identity.Name)
to get the list of roles. This actually hits the database to get the roles, so as long us the user name is available in Identity it will return roles, assuming that the user has roles assigned to them. And the user name should be available if the user id authenticated. You are checking Request.IsAuthenticated. It is probably better to check User.Identity.IsAuthenticated.
There is a way to get the roles without querying the database using the claims. And in general I would not put such logic and access to system variables in your Views. I would use this approach in my controller action to get the roles.
var prinicpal = (ClaimsPrincipal)Thread.CurrentPrincipal;
var roles = prinicpal.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value);
ViewBag.Roles = roles;
Either put the information you need in your Views in the the ViewBag or create a model that is passed to the View. This approach for getting roles does not require a round trip to the database.
Update to Address Comments
The standard AuthorizeAttribute for MVC 4 uses the configured membership and role provider (in your case SimpleMembership) to get the roles. It does not assume claims where used so it goes to the database every time. In fact it has to go the database twice, once to get the user ID for the logged in user and then to get the roles based on that user ID. Not very efficient. Surprisingly it does this even if you do not pass in any roles as parameters, where it does not need to authorize the user against roles and should just authenticate. I verified this with a profiler.
To make it more efficient and not require going to the database you could write a custom AuthorizeAttribute to use claims instead. Here is an article that describes how to do this, with links to example source code.
I cannot explain the scenario you are describing, which occurs right after a build. But if you create a custom attribute and use it instead you will be able to debug and see what exactly is going on. Plus I would not worry too much about it as it should not be a typical scenario you would see in production.
I am developing a site and have 3 basic user roles. The roles are "admin", "manager", and "user". I have blocked basic users from certain pages, but allow admin and managers access to others. I have created a seperate page to delete users, however, I do not want to allow someone from the role of "manager" to be able to delete someone from the role of "admin". "user" roles do not have access to this page so I'm not worried about that. I have a drop down list that shows all the users and a button to remove the selected user and it is working. I would just like to add the security of not allowing "manager" roles to delete someone from an "admin" role.
Here is the code that I have so far for the onClick event:
string adminuser;
usertodelete = usersddl.SelectedItem.ToString();
if (Roles.GetRolesForUser(usertodelete) = "admin")
adminuser = "admin";
if (Roles.IsUserInRole("admin") && User.IsInRole("manager"))
statuslbl.Text = "You do not have sufficient privileges to remove this user. Only Administrator's can remove administrators from the system.";
else
{
System.Web.Security.Membership.DeleteUser(usertodelete);
Response.Redirect("~/Account/DeleteAccount.aspx");
}
I know my if statements are wrong at this point in finding and assigning a certain user and checking their role.
I would just like to add the security of not allowing "manager" roles to delete someone from an "admin" role.
if (Roles.GetRolesForUser(userToDelete).Contains("admin") && !User.IsInRole("admin"))
{ // only allow admins to remove other admins.
statuslbl.Text = "You do not have sufficient privileges to remove this user. Only Administrator's can remove administrators from the system.";
} else {
}