How can I "inject" the UserId into my model request? - c#

My Employee request:
namespace API.Requests {
public class EmployeeRequest {
public string Name { get; set; }
...
}
public static class Extensions {
public static Employee ToEntity(this EmployeeRequest request) => new Employee {
Name = request.Name,
//UserId = ...
...
};
}
So in my controller I have:
[HttpPost]
public async Task<ActionResult> Poststring( [FromForm] EmployeeRequest request )
{
var employee = request.ToEntity();
return Ok(employee.UserId);
}
So, what I need is that inside the ToEntity function modify the UserId field through the UserManager so that the entity that I get after executing the ToEntity function includes the UserId.
Thank you! :D

So, what I need is that inside the ToEntity function modify the UserId
field through the UserManager so that the entity that I get after
executing the ToEntity function includes the UserId.
Just try this code:
public class HomeController : Controller
{
private readonly UserManager<IdentityUser> _userManager;
public HomeController(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
[HttpPost]
public async Task<ActionResult> Poststring([FromForm] EmployeeRequest request)
{
var employee = await request.ToEntity(_userManager, User);
return Ok(employee.UserId);
}
}
public static class Extensions
{
public async static Task<Employee> ToEntity(this EmployeeRequest request,
UserManager<IdentityUser> _userManager, ClaimsPrincipal user) => new Employee
{
UserId = (await _userManager.GetUserAsync(user)).Id.ToString(),
Name = request.Name,
};
}

Related

How to pass Enum as string parameter to Authorize attribute?

It's my first project to ASP.NET Core Authentication and Authorization and I get this error when I'm trying to pass Enum to [Authorize] attribute :
Error CS1503 Argument 1: cannot convert from
'BasicAuthAPI.Entities.Role' to
'string'
Here is my controller method which gives this error:
[Authorize(Role.Admin)]
[HttpGet]
public IActionResult GetAll()
{
var users = _userService.GetAll();
return Ok(users);
}
Role enum:
public enum Role
{
Admin,
User
}
User Entity:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public Role Role { get; set; }
[JsonIgnore]
public string PasswordHash { get; set; }
}
And the _userService which I have mentioned in controller:
public class UserService : IUserService
{
private DataContext _context;
private IJwtUtils _jwtUtils;
private readonly AppSettings _appSettings;
public UserService(
DataContext context,
IJwtUtils jwtUtils,
IOptions<AppSettings> appSettings)
{
_context = context;
_jwtUtils = jwtUtils;
_appSettings = appSettings.Value;
}
public AuthenticateResponse Authenticate(AuthenticateRequest model)
{
var user = _context.Users.SingleOrDefault(x => x.Username == model.Username);
// validate
if (user == null || !BCryptNet.Verify(model.Password, user.PasswordHash))
throw new AppException("Username or password is incorrect");
// authentication successful so generate jwt token
var jwtToken = _jwtUtils.GenerateJwtToken(user);
return new AuthenticateResponse(user, jwtToken);
}
public IEnumerable<User> GetAll()
{
return _context.Users;
}
public User GetById(int id)
{
var user = _context.Users.Find(id);
if (user == null) throw new KeyNotFoundException("User not found");
return user;
}
}
How can I pass the Admin Role to [Authorize] attribute?
Either use string constants
public static class Role
{
public static string Admin = "Admin";
public static string User = "User";
}
or you can use nameof
[Authorize(nameof(Role.Admin))]
You can just call .ToString()
[Authorize(Role.Admin.ToString())]
[HttpGet]
public IActionResult GetAll()
{
var users = _userService.GetAll();
return Ok(users);
}
Looking at the answer from Alexander I have found the following SO post which highlights the difference between nameof and ToString: What is the difference between MyEnum.Item.ToString() and nameof(MyEnum.Item)?

Use UserManager as interface

I'm using ASP.NET Core identity
and I want to observe DIP and I create a interface
public interface IUserManagerService : IDisposable
{
Task<IdentityResult> CreateAsync(User user, string password);
}
and implementation class is
public class UserManagerService : UserManager<User>, IUserManagerService
{
public UserManagerService()
: base()
{
}
}
now I don't know what parameters should pass to base class
how can I fix that ?
From your description, you want to create a service with the UserManager, then use it to manage users. If that is the case, you could refer the following sample code:
public interface IUserManagerService:IDisposable
{
Task<IdentityResult> CreateAsync(User user, string password);
}
public class UserManagerService : IUserManagerService
{
//request the UserManger service and call the relate methods.
private readonly UserManager<IdentityUser> usermanager;
public UserManagerService(UserManager<IdentityUser> userManager)
{
usermanager = userManager;
}
public async Task<IdentityResult> CreateAsync(User user, string password)
{
var identityuser = new IdentityUser { UserName = user.UserName, Email = user.Email };
var result = await usermanager.CreateAsync(identityuser, password);
return result;
}
public void Dispose()
{
Console.WriteLine("- IUserManagerService was disposed!");
}
}
The User model:
public class User
{
public string UserName { get; set; }
public string Email { get; set; }
}
Then, registers the IUserManagerService service with the concrete type UserManagerService:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
services.AddScoped<IUserManagerService, UserManagerService>();
Then, in the MVC controller, request the IUserManagerService service, and call its method to create a user:
public class HomeController : Controller
{
private readonly IUserManagerService userManagerService;
public HomeController(IUserManagerService userManager)
{
userManagerService = userManager;
}
public IActionResult Index()
{
var newuser = new User() { UserName = "Tom", Email = "tom121#hotmail.com" };
var password = "Password123!";
var result = userManagerService.CreateAsync(newuser, password);
if (result.Result.Succeeded)
{
//do some thing
}
return View();
}
The result as below:
More detail information, refer Dependency injection in ASP.NET Core

Two HttpGet on the same route in one controller [duplicate]

I want to build truly RESTful web service so don't want to leverage RPC-style, so have currently this:
[HttpGet]
[ActionName(nameof(GetByParticipant))]
public async Task<IActionResult> GetByParticipant([FromQuery]string participantId, [FromQuery]string participantType, [FromQuery]string programName)
{
}
[HttpGet]
[ActionName(nameof(GetByProgram))]
public async Task<IActionResult> GetByProgram([FromQuery]string programName)
{
}
And I believe that would work in ASP.NET Web API. But I'm getting an exception:
AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:
TermsController.GetByParticipant (ParticipantTerms.Api)
TermsController.GetByProgram (ParticipantTerms.Api)
Neither of the attributes actually help:
[HttpGet]
[ActionName]
[FromQuery]
You can do this using an IActionConstraint.
Here is an example:
public class ExactQueryParamAttribute : Attribute, IActionConstraint
{
private readonly string[] keys;
public ExactQueryParamAttribute(params string[] keys)
{
this.keys = keys;
}
public int Order => 0;
public bool Accept(ActionConstraintContext context)
{
var query = context.RouteContext.HttpContext.Request.Query;
return query.Count == keys.Length && keys.All(key => query.ContainsKey(key));
}
}
[HttpGet]
[ActionName(nameof(GetByParticipant))]
[ExactQueryParam("participantId", "participantType", "programName")]
public async Task<IActionResult> GetByParticipant([FromQuery]string participantId, [FromQuery]string participantType, [FromQuery]string programName)
{
}
[HttpGet]
[ActionName(nameof(GetByProgram))]
[ExactQueryParam("programName")]
public async Task<IActionResult> GetByProgram([FromQuery]string programName)
{
}
When using from query you need to uniquely differentiate the actions' routes otherwise you will get the ambiguous action exception. Reason being api/action?participantId=1&participantType=2 is the same as api/action?programName=x
Suggestion:
public class ParticipantQuery {
public string participantId { get; set; }
public string participantType { get; set; }
public string programName { get; set; }
}
[Route("api/[controller]")]
public class TermsController : Controller {
[HttpGet("participants")] //GET api/terms/participants?participantId=123&....
[ActionName(nameof(GetByParticipant))]
public async Task<IActionResult> GetByParticipant([FromQuery]ParticipantQuery model) {
//...
}
[HttpGet("programs/{programName}")]//GET api/terms/programs/name
[ActionName(nameof(GetByProgram))]
public async Task<IActionResult> GetByProgram(string programName) {
//...
}
}
Or you can use one action that encapsulates the available parameters and branch the result based on the provided members
public class GetTermsQuery {
public string participantId { get; set; }
public string participantType { get; set; }
public string programName { get; set; }
}
[Route("api/[controller]")]
public class TermsController : Controller {
[HttpGet] //GET api/terms?participantId=123&....
public async Task<IActionResult> Get([FromQuery]GetTermsQuery model) {
//...
}
}
I've spent all day trying to do this and Andrew Radford's solution and this was perfect!
[HttpGet]
[ExactQueryParam("active")]
public IEnumerable<UserSelectAllSprocResult.DataRow> GetAll(
[FromQuery] bool active)
{
return ...
}
[HttpGet]
[ExactQueryParam("active", "companyId")]
public IEnumerable<UserSelectAllByCompanySprocResult.DataRow> GetByCompanyId(
[FromQuery] bool active, [FromQuery] int companyId)
{
return ...;
}

How do I use multiple base classes for my CRUD operation in ASP.NET Razor pages

I designed an ASP.NET Core Razor Pages application implementing dropdown list class and using the class as a base for Create, Read and Update classes.
Now I want to implement anonymous authentication and I have created another class for this which should ideally be the base class for the Create, Read and Update classes. When I tried to add it, the system says I cannot use 2 base classes.
How can I use multiple base classes in ASP.NET Core Razor (MVVM)
I tried using both classes but that triggered an error stating I cannot use more than one base class
My dropdown list base class
public class GLRefPageModel: PageModel
{
public SelectList GLRefNameSL { get; set; }
public void PopulateGLRefDropDownList(strMaterialsTransactContext _context, object selectedGLRef = null)
{
var GLRefsQuery = from d in _context.GLRef
select d;
GLRefNameSL = new SelectList(GLRefsQuery.AsNoTracking(), "ID", "Description", selectedGLRef);
}
}
My Authentication base class
public class DI_BasePageModel : PageModel
{
protected ApplicationDbContext Context { get; }
protected IAuthorizationService AuthorizationService { get; }
protected UserManager<IdentityUser> UserManager { get; }
public DI_BasePageModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager) : base()
{
Context = context;
UserManager = userManager;
AuthorizationService = authorizationService;
}
}
My Edit Class
public class EditModel : GLRefPageModel
{
private readonly strMaterialsTransact.Models.strMaterialsTransactContext _context;
public EditModel(strMaterialsTransact.Models.strMaterialsTransactContext context)
{
_context = context;
}
[BindProperty]
public strMovement strMovement { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
if (strMovement == null)
{
return NotFound();
}
//select the current GLRef
PopulateGLRefDropDownList(_context, strMovement.GLRefID);
return Page();
}
public async Task<IActionResult> OnPostAsync(int? id)
{
if (!ModelState.IsValid)
{
return Page();
}
var strMovementToUpdate = await _context.strMovement.FindAsync(id);
if (await TryUpdateModelAsync<strMovement>(
strMovementToUpdate,
"strmovement", //prefix for form value
s => s.ID, s => s.TransactionDate, s => s.QtyFromStore, s => s.IDPartNbr,
s => s.QtyToStore, s => s.GLRefID, s => s.ShopOrder, s => s.TransactionReason, s => s.TransactionReason,
s => s.OwnerID, s => s.TimeLastAccessed, s => s.Initials, s => s.LastUser))
{
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
//**Select GLRef if TryUpdateModelAsync fails
PopulateGLRefDropDownList(_context, strMovementToUpdate.GLRefID);
return Page();
}
}
I expect to me able to call the base class for my dropdown list operation in my Create, Update and Read operations and also be able to call (and use) the class for the anonymous authentication exercise
I have been able to solve the problem by using composition design pattern all the classes into the DI_BasePageModel class as follows:
public class DI_BasePageModel : PageModel
{
protected ApplicationDbContext Context { get; }
protected IAuthorizationService AuthorizationService { get; }
protected UserManager<IdentityUser> UserManager { get; }
public DI_BasePageModel(
ApplicationDbContext context,
IAuthorizationService authorizationService,
UserManager<IdentityUser> userManager) : base()
{
Context = context;
UserManager = userManager;
AuthorizationService = authorizationService;
}
public SelectList GLRefNameSL { get; set; }
public void PopulateGLRefDropDownList(ApplicationDbContext _context, object selectedGLRef = null)
{
var GLRefsQuery = from d in _context.GLRef
select d;
GLRefNameSL = new SelectList(GLRefsQuery.AsNoTracking(), "GLRefID", "Description", selectedGLRef);
}
Thanks for pointing it out Chris Pratt

OverrideAuthorization attribute in .NETCore

In the controller code below, only users who are in the "Administrator" role can access the GetData() action method, because of the controller-level AuthorizeAttribute. But I also want users who only are in "Manager" role to have access to the GetData() action method.
[Authorize(Roles = "Administrator")]
Public class AdminController : Controller
{
[Authorize(Roles = "Administrator, Manager")]
public IActionResult GetData()
{
}
}
Is there an option like OverrideAuthorization attribute available in .NET Core framework to achieve this requirement?
Was able to find a solution after long time of analysis on the Authorization assemblies.
In the startup.cs file, add the Authorization as follows:
services.AddAuthorization(options =>
{
var roles = new List<string>{ Role.Administrator, Role.Manager};
var requirement =
new List<IAuthorizationRequirement> {new AdminManagerAuthorizationOverrideOthers(roles) };
var sharedAuthentication =
new AuthorizationPolicy(requirement,
new List<string>());
options.AddPolicy(name: "AdminManager", policy: sharedAuthentication);
options.AddPolicy(name: "Administrator", configurePolicy: policy => policy.RequireAssertion(e =>
{
if (e.Resource is AuthorizationFilterContext afc)
{
var noPolicy = afc.Filters.OfType<AuthorizeFilter>().Any(p =>
p.Policy.Requirements.Count == 1 &&
p.Policy.Requirements.Single() is AdminManagerAuthorizationOverrideOthers);
if (noPolicy)
return true;
}
return e.User.IsInRole(Role.Administrator);
}));
});
Create a class in any namespace that Inherits "RolesAuthorizationRequirement" from "Microsoft.AspNetCore.Authorization.Infrastructure" namespace as follows:
public class AdminManagerAuthorizationOverrideOthers : RolesAuthorizationRequirement
{
public AdminManagerAuthorizationOverrideOthers(IEnumerable<string> allowedRoles) : base(allowedRoles)
{
}
}
Then, decorate the controller and action method as follows:
[Authorize(Policy = "Administrator")]
Public class AdminController : Controller
{
public IActionResult GetData()
{
}
[Authorize(Policy = "AdminManager")]
public IActionResult AdministratorOnly()
{
}
}
Ideally, you want to narrow down the restriction to Action Method, because in Controller Initialization step, it checks Controller's Authorize filter first before Action filters.
[Authorize(Roles = "Administrator, Manager")]
Public class AdminController : Controller
{
public IActionResult GetData()
{
}
[Authorize(Roles = "Administrator")]
public IActionResult AdministratorOnly()
{
}
}
In ASP.NET Core 2.1 you can do it. Check this: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-2.1
You can also lock down a controller but allow anonymous,
unauthenticated access to individual actions.
[Authorize(Roles = "Admin,Employee")] // admin or employee
public class XController : Controller
{
[Authorize(Roles = "Admin")] // only admin
public ActionResult ActionX() { ... }
[AllowAnonymous] // anyone
public ActionResult ActionX() { ... }
}
All above is right, i just want to give a full example easy for all
My case is Asp.Net Core 3.1
Startup.js (ConfigureServices):
services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
config.User.RequireUniqueEmail = false; // óíèêàëüíûé email
config.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 -._#+";
config.SignIn.RequireConfirmedEmail = false;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddUserManager<UserManager<ApplicationUser>>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddDefaultTokenProviders();
services.AddAuthorization(options =>
{
options.AddPolicy("User", policy => {
policy.RequireClaim("User");
});
options.AddPolicy("Admin", policy => {
policy.RequireRole("Admin");
});
});
services.AddScoped<IAuthorizationHandler, RolesAuthorizationHandler>();
Startup.js (Configure):
app.UseAuthentication();
app.UseAuthorization();
Controller:
[Authorize(Policy = "Admin")]
public class RoleController : Controller
Handler-Example:
public class RolesAuthorizationHandler : AuthorizationHandler<RolesAuthorizationRequirement>, IAuthorizationHandler
{
private readonly RoleManager<IdentityRole> _roleManager;
private readonly UserManager<ApplicationUser> _userManager;
public RolesAuthorizationHandler(RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager)
{
_roleManager = roleManager;
_userManager = userManager;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
RolesAuthorizationRequirement requirement)
{
if (context.User == null || !context.User.Identity.IsAuthenticated)
{
context.Fail();
return Task.CompletedTask;
}
var validRole = false;
if (requirement.AllowedRoles == null ||
requirement.AllowedRoles.Any() == false)
{
validRole = true;
}
else
{
var claims = context.User.Claims;
//var userName = claims.FirstOrDefault(c => c.Type == "UserName").Value;
var allowedRoles = requirement.AllowedRoles;
var loggedInUserTask = _userManager.GetUserAsync(context.User);
loggedInUserTask.Wait();
var user = loggedInUserTask.Result;
var roles = _userManager.GetRolesAsync(user);
roles.Wait();
var roleList = roles.Result;
validRole = roleList.Where(p => allowedRoles.Contains(p.ToString())).Any();
}
if (validRole)
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
I While updating a project that used to exist, I moved the old user table to the user table in the new identity database. Later, I defined roles at the table level for them, and with the RoleManager I wrote in this way, I left his next administration to the step. Quite successful. In my case, many people probably updated their old projects. However, I did not have such a post and wanted to share it.
The following section is for them:
public class RoleAssignViewModel
{
public string RoleId { get; set; }
public string RoleName { get; set; }
public bool HasAssign { get; set; }
}
public class RoleViewModel
{
[Required(ErrorMessage = "Fill the role.")]
[Display(Name = "Role Name")]
public string Name { get; set; }
}
[Authorize(Policy = "Admin")]
public class RoleController : Controller
{
private readonly RoleManager<IdentityRole> _roleManager;
private readonly UserManager<ApplicationUser> _userManager;
public RoleController(RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager)
{
_roleManager = roleManager;
_userManager = userManager;
}
public async Task<IActionResult> RoleAssign(string id)
{
ApplicationUser user = await _userManager.FindByIdAsync(id);
List<IdentityRole> allRoles = _roleManager.Roles.ToList();
List<string> userRoles = await _userManager.GetRolesAsync(user) as List<string>;
List<RoleAssignViewModel> assignRoles = new List<RoleAssignViewModel>();
allRoles.ForEach(role => assignRoles.Add(new RoleAssignViewModel
{
HasAssign = userRoles.Contains(role.Name),
RoleId = role.Id,
RoleName = role.Name
}));
return View(assignRoles);
}
[HttpPost]
public async Task<ActionResult> RoleAssign(List<RoleAssignViewModel> modelList, string id)
{
ApplicationUser user = await _userManager.FindByIdAsync(id);
foreach (RoleAssignViewModel role in modelList)
{
if (role.HasAssign)
await _userManager.AddToRoleAsync(user, role.RoleName);
else
await _userManager.RemoveFromRoleAsync(user, role.RoleName);
}
return RedirectToAction("Index", "User");
}
public IActionResult RoleList()
{
return View(_roleManager.Roles.ToList());
}
public async Task<IActionResult> DeleteRole(string id)
{
IdentityRole role = await _roleManager.FindByIdAsync(id);
IdentityResult result = await _roleManager.DeleteAsync(role);
if (result.Succeeded)
{
//Başarılı...
}
return RedirectToAction("Index");
}
public async Task<IActionResult> CreateRole(string id)
{
if (id != null)
{
IdentityRole role = await _roleManager.FindByIdAsync(id);
return View(new RoleViewModel
{
Name = role.Name
});
}
return View();
}
[HttpPost]
public async Task<IActionResult> CreateRole(RoleViewModel model, string id)
{
IdentityResult result = null;
if (id != null)
{
IdentityRole role = await _roleManager.FindByIdAsync(id);
role.Name = model.Name;
result = await _roleManager.UpdateAsync(role);
}
else
result = await _roleManager.CreateAsync(new IdentityRole { Name = model.Name });
if (result.Succeeded)
{
//Başarılı...
}
return View();
}
//[Authorize]
public IActionResult UserRoleList()
{
return View(_userManager.Users);
}
}
Found something here I am using: https://github.com/dotnet/aspnetcore/issues/8149#issuecomment-471927034
/// <summary>
/// https://github.com/dotnet/aspnetcore/issues/8149#issuecomment-471927034
/// </summary>
public class OverrideFilter : ActionFilterAttribute
{
public Type Type { get; set; }
}
public class OverrideFilterProvider : IFilterProvider
{
public int Order => 1;
public void OnProvidersExecuted(FilterProviderContext context) { }
public void OnProvidersExecuting(FilterProviderContext context)
{
if (context.ActionContext.ActionDescriptor.FilterDescriptors != null)
{
//Check whether the method has any OverrideFilter
var overrideFilters = context.Results.Where(filterItem => filterItem.Filter is OverrideFilter).ToList();
foreach (var overrideFilter in overrideFilters)
{
//Remove the filters of the corresponding type, but with smaller scope
context.Results.RemoveAll(filterItem =>
filterItem.Descriptor.Filter.GetType() == ((OverrideFilter)overrideFilter.Filter).Type &&
filterItem.Descriptor.Scope < overrideFilter.Descriptor.Scope);
}
}
}
}
public class OverrideAuthorization : OverrideFilter
{
public OverrideAuthorization()
{
Type = typeof(AuthorizeFilter);
}
}
/// <summary>
/// https://stackoverflow.com/questions/16606281/linq-to-remove-certain-elements-from-a-ilistt-based-on-a-ilistint
/// </summary>
public static class IListExt
{
public static int RemoveAll<T>(this IList<T> list, Predicate<T> match)
{
int count = 0;
for (int i = list.Count - 1; i >= 0; i--)
{
if (match(list[i]))
{
++count;
list.RemoveAt(i);
}
}
return count;
}
}
Finally we inject it as follows (I am not sure this is the right wat to inject it, but it works);
services.TryAddEnumerable(ServiceDescriptor.Singleton<IFilterProvider, OverrideFilterProvider>());
Use like
[Authorize(Policy = "ControllerPolicy")
public class MyController : Controller
{
[OverrideAuthorization]
[Authorize(Policy = "ActionPolicy")]
public IActionResult MyAction()
{
//Only ActionPolicy will be applied, while ControllerPolicy will be ignored
}
}

Categories