IsUserInRole not working in CSHTML - asp.net-mvc-3

I have this code, but I don't know why this is not working properly. I have a custom role provider created.
#if (Roles.IsUserInRole(User.Identity.Name, "Manager"))
{
<li>#Html.ActionLink("User Management", "Index", "User")</li>
}
This is the custom code, the rest wasn't modified.
public override bool IsUserInRole(string username, string roleName)
{
UserRoleType usrt = (from usr in db.Users
join usrRole in db.UserRoles on usr.UserID equals usrRole.UserID
where usr.Email == username
select usrRole.UserRoleType).FirstOrDefault();
if (roleName.Split(',').Contains(usrt.UserRoleTypeName))
return true;
return false;
}
This does work when I do this:
UserRoleProvider roleProvider = System.Web.Security.Roles.Provider as UserRoleProvider;
if (roleProvider.IsUserInRole(httpContext.User.Identity.Name, Roles) || String.IsNullOrEmpty(Roles))
return true;
EDIT:
public override string[] GetRolesForUser(string roleName)
{
return db.UserRoleTypes.Select(u => u.UserRoleTypeName).ToArray();
}

Turns out GetRolesForUser was written in correctly and it was pulling all the Users so it authenticated based on ALL users, which means it was in the first place.
Re-read the documentation to get my answer here.
http://msdn.microsoft.com/en-us/library/system.web.security.roleprovider.getrolesforuser.aspx

It appears that User.IsInRole(rolename) uses MembershipProvider.GetRolesForUser(username)
It took me a while to figure that out.

Related

How are we meant to use IUserClaimsPrincipalFactory<T>?

I'm confused by the example in the documentation here that describes how to add claims using IUserClaimsPrincipalFactory.
The sample code shows how to extend the ApplicationUser class:
public class ApplicationUser : IdentityUser
{
public bool IsAdmin { get; set; }
}
...and then implement a UserClaimsPrincipalFactory that tests that property to determine which claims to add:
if (user.IsAdmin)
{
claims.Add(new Claim(JwtClaimTypes.Role, "admin"));
}
else
{
claims.Add(new Claim(JwtClaimTypes.Role, "user"));
}
It's not stated, but I think the implication is that something else (not shown) will set the IsAdmin property for a user in the database. I think they could have made that clear. (Also, it's disappointing that the example uses roles when there's so much confusion around roles versus claims, but I digress...)
Anyway, we have added some "role" claims to the user based on the value of that new IsAdmin property. So far, so good. What I don't understand is the next bit:
The additional claim can then be used in the app. In a Razor Page, the IAuthorizationService instance can be used to access the claim value.
Sounds like the Razor page is going to access our claim then - but here's the code:
#if ((await AuthorizationService.AuthorizeAsync(User, "IsAdmin")).Succeeded)
{
...
}
Is that really accessing the claim? It looks to me like it's accessing the IsAdmin property of the user instead. I don't see how the claim we added is referenced at all - unless there's something else that's not being explained.
That overload of AuthorizeAsync describes the last parameter as 'policyName'. Are we meant to assume that there's a policy called "IsAdmin" that checks for our new role claim?
What a terrible piece of documentation this is - and I'm ignoring the fact that it's also in the wrong place.
It's not stated, but I think the implication is that something else (not shown) will set the IsAdmin property for a user in the database.
You can set the IsAdmin where you want,For example you can set it when register.Here is a demo:
Input Model in register:
public class InputModel
{
...
public bool IsAdmin { get; set; }
}
Post handler:
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email ,IsAdmin=Input.IsAdmin};
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
//await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
// $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
Is that really accessing the claim? It looks to me like it's accessing the IsAdmin property of the user instead. I don't see how the claim we added is referenced at all - unless there's something else that's not being explained.
That overload of AuthorizeAsync describes the last parameter as 'policyName'. Are we meant to assume that there's a policy called "IsAdmin" that checks for our new role claim?
IsAdmin is a ploicy name in the code,you need to add a policy which name is IsAdmin,and check new role claim in it.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddAuthorization(options =>
{
options.AddPolicy("IsAdmin", policy => policy.RequireClaim("role", "admin"));
});
}
result:

Is validating a userId (or any other data extracted from an authentication token) necessary?

In my controller action as have something like this:
[HttpGet]
[ActionName("approve")]
[Authorize(Policy = "Approve")]
public IActionResult GetEntitiesToBeApproved()
{
var stringUserId = User.Claims.FirstOrDefault(c => c.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value;
Guid.TryParse(stringUserId, out var userId);
if (userId == default(Guid))
{
return StatusCode((int)HttpStatusCode.BadRequest, ConstantValues.InvalidUserId);
}
//service calls etc.
return Ok();
}
Is there any point in checking that the userId is valid (non-default) or can I skip it?
You can skip it, Authorize filter attribute check it for You.

Passing a variables in C#

I am trying to pass a variable that identifies a staff member from their login details to the next screen to populate the Tester ID box.
Would I be better using a global variable that is then read when the next screen is set up or would I be better to put it into a variable and send it to the next screen?
The login code sits as follows:
public Login()
{
InitializeComponent();
}
private void LoginButton_Click(object sender, EventArgs e)
{
String Username1 = StaffUsername.Text;
String Password1 = StaffPassword.Text;
String HardUser = "Test";
String HardPass = "Merlin123";
if (Username1 == "" && Password1 == "")
{
MessageBox.Show("Please Enter Login Id and Password");
}
else
{
if (Username1.Equals(HardUser) && Password1.Equals(HardPass))
{
this.Hide();
AddingClients M1 = new AddingClients();
M1.Show();
}
else{
this.Hide();
Login Lg = new Login();
Lg.Show();
MessageBox.Show("Incorrect Username or Password Entered");
}
}
}
}
}
I am using a hardcoded username and password for now, but in the actual program, I would have this call on a database and compare the username and the password and then go through to the next screen.
Would lit be better to have a global variable that the login action throws over to the next screen or would it be easier having a variable that the next screen reads and then populates the text box required? How would I go about this?
Thanks
The way you do this is to use the Thread.CurrentPrincipal.
Once the user is confirmed to be who they say they are, you can do:
private static void SignUserIn(string userName)
{
GenericIdentity identity = new GenericIdentity(userName, null);
Thread.CurrentPrincipal = new GenericPrincipal(identity);
}
Then whenever you need the userName, you use Thread.CurrentPrincipal.Identity.Name.
To extend this a little further, its probably best to abstract this a little bit, so you can swap in and out providers e.g. you might want to use Windows Authentication.
So you could do it like this:
public interface IAuthenticator
{
bool IsValidUser ( string username, string password );
IPrincipal SignInUser ( string username );
void SignOutCurrentUser();
}
public class DbAuthenticator : IAuthenticator
{
public bool IsValidUser ( string username, string password )
{
// Check user here and return bool if valid
}
public void SignInUser(string userName, string[] roles = null)
{
GenericIdentity identity = new GenericIdentity(userName, roles);
Thread.CurrentPrincipal = new GenericPrincipal(identity);
return Thread.CurrentPrincipal;
}
public void SignOutUser()
{
Thread.CurrentPrincipal = WindowsPrincipal.GetCurrent();
}
}
Then in your code, inject the authenticator using some sort of DI pattern. So MEF would be like this:
[Export(typeof(IAuthenticator))]
public interface IAuthenticator { }
And in your form:
[Import]
internal IAuthenticator authenticator;
private static void SignUserIn(string userName)
{
authenticator.SignUserIn(username);
}
You should have a User object and pass it to other pages and functions. That way they become more testable.
But you also need to store the object somewhere globally so you can retrieve the currently logged in user when needed. The problem with that is that piece of code then depends on that user storage existing/working, but if it's only one place it's okay.
PS: your title should be: pass the user on or store globally?

MVC3 Edit customer with username

I am trying to edit customer with username which is using User.Identity.Name.
I don't know how to write Where condition in controller.
It looks easy. Could you help me? thanks.
Here is my coding.
[Authorize]
public ActionResult Edit()
{
//the username gets username through User.Identity.Name.
string username = User.Identity.Name;
//How can I write below coding?
//In DB, it has userName field.
Customer customer = db.Customer.Where(userName = username);
return View(customer);
}
[HttpPost]
public ActionResult Edit(Customer customer)
{
if (ModelState.IsValid)
{
db.Entry(customer).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(customer);
}
You need to learn how lambda expressions work:
.Where(c => c.UserName == username)
c is the implicitly-typed parameter.
Also, if you want a single result, you should call FirstOrDefault() instead; Where() returns a sequence.
Customer customer = db.Customer.Single(c=>c.UserName == username)
throws exception if returns one than more matching element
or
Customer customer = db.Customer.SingleOrDefault(c=>c.UserName == username);
returns null if returns more than one matching element

Login Procedure that does not require a Email Address

I have been asked to create a site where the user isn't required to provide a email to login because of privacy issues. In the past I have simple said this isn't advisable but in this case the client has stringently requested it. My initial thoughts are to potentially create administrators with a email whom could create generic logins (username and a password) and pass them to members of there group on site. Then at least I have a point of contact for login resets and such.
Has anyone had any experience with such situations where they have needed to create logins without the use of a email address? Could you direct me towards any relevant materials or tutorials that may be of use. I'm using MVC3 to develop this project.
I hope I understand your question right and you want to implement a login using username and password instead of email adress and password.
In that case you would have to implement your own custom membership provider and a custom roleprovider if needed.
You want to check the following page for more information:
Custom Membership Provider # Codeproject
EDIT
Fyi you dont need to implement every function - just implement the ones you need.
Custom membership provider from some of my older mvc3 projects. Removed most of the not-implemented functions for shorter code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
namespace Domain.Models
{
public class PlatformMembershipProvider : MembershipProvider
{
public SalesModelContainer ******** = new SalesModelContainer();
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
var user = ********.UserSet.Single(s => s.Email == username);
if (user.Password == oldPassword)
{
user.Password = newPassword;
return true;
}
else
{
return false;
}
}
public override string GetUserNameByEmail(string email)
{
var user = ********.UserSet.Single(s => s.Email == email);
return user.CompanyName;
}
public override void UpdateUser(System.Web.Security.MembershipUser user)
{
throw new NotImplementedException();
}
//TODO: use MD5 for password encryption
public override bool ValidateUser(string username, string password)
{
bool returnValue;
var user = ********.UserSet.SingleOrDefault(s => (s.Email == username) && (s.Password == password));
if (user != null)
returnValue = true;
else
returnValue = false;
return returnValue;
}
}
}

Resources