Blazor WebAssembly with identity server passing username through form - asp.net

I am new to Blazor WebAssembly. I have a simple page that allows users to register a company. It takes the company name, description and the username. At the moment the username is entered by the user but I want to automatically grab the username of the logged in user and post it without the need of user input. Here is my page:
#page "/companies/create"
#attribute [Authorize]
#inject HttpClient Http
#inject NavigationManager Navigation
<h3>Register your company</h3>
<AuthorizeView>
Hello, #context.User.Identity.Name!
</AuthorizeView>
<EditForm Model="Companies" OnValidSubmit="#HandleValidSubmit">
<DataAnnotationsValidator />
<div class="form-group">
<label class="control-label">Name</label>
<InputText #bind-Value="Companies.Name" class="form-control" />
<ValidationMessage For="#(() => Companies.Name)" />
</div>
<div class="form-group">
<label class="control-label">Description</label>
<InputText #bind-Value="Companies.Description" class="form-control" />
<ValidationMessage For="#(() => Companies.Description)" />
</div>
<div class="form-group">
<label class="control-label">Username</label>
<InputText #bind-Value="Companies.Username" class="form-control"/>
<ValidationMessage For="#(() => Companies.Username)" />
</div>
<button type="submit" class="btn btn-primary">
<i class="fas fa-save"></i> Register
</button>
</EditForm>
#code {
private Sprelo.Shared.Companies Companies { get; set; } = new Sprelo.Shared.Companies();
private async void HandleValidSubmit()
{
try
{
var response = await Http.PostAsJsonAsync($"/api/companies", Companies);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var companies = JsonConvert.DeserializeObject<Sprelo.Shared.Companies>(content);
Navigation.NavigateTo($"Companies/edit/{companies.Id}");
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
catch (Exception e)
{
}
}
}
This is my model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Sprelo.Shared
{
public class Companies
{
[Key]
public Guid Id { get; set; }
[Required]
public String Name { get; set; }
public String Description { get; set; }
public String Username { get; set; }
}
}
and this is an auto generated API controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Sprelo.Server.Data;
using Sprelo.Shared;
namespace Sprelo.Server.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class CompaniesController : ControllerBase
{
private readonly ApplicationDbContext _context;
public CompaniesController(ApplicationDbContext context)
{
_context = context;
}
// GET: api/Companies
[HttpGet]
public async Task<ActionResult<IEnumerable<Companies>>> GetCompanies()
{
return await _context.Companies.ToListAsync();
}
// GET: api/Companies/5
[HttpGet("{id}")]
public async Task<ActionResult<Companies>> GetCompanies(Guid id)
{
var companies = await _context.Companies.FindAsync(id);
if (companies == null)
{
return NotFound();
}
return companies;
}
// PUT: api/Companies/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPut("{id}")]
public async Task<IActionResult> PutCompanies(Guid id, Companies companies)
{
if (id != companies.Id)
{
return BadRequest();
}
_context.Entry(companies).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!CompaniesExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/Companies
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
public async Task<ActionResult<Companies>> PostCompanies(Companies companies)
{
_context.Companies.Add(companies);
await _context.SaveChangesAsync();
return CreatedAtAction("GetCompanies", new { id = companies.Id }, companies);
}
// DELETE: api/Companies/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteCompanies(Guid id)
{
var companies = await _context.Companies.FindAsync(id);
if (companies == null)
{
return NotFound();
}
_context.Companies.Remove(companies);
await _context.SaveChangesAsync();
return NoContent();
}
private bool CompaniesExists(Guid id)
{
return _context.Companies.Any(e => e.Id == id);
}
}
}
I have been struggling with this for quite a while, any help would be appriciated!

Inject the AuthenticationStateProvider object into your component like this:
#inject AuthenticationStateProvider authProvider
Override the OnInitialized life cycle method:
protected override void OnInitialized ()
{
var authenticationStateTask = await
authProvider.GetAuthenticationStateAsync();
var user = authenticationStateTask.User;
// Check if the user is authenticated
if (user.Identity.IsAuthenticated)
{
// Grab the user name and assign it to Companies.Username
Companies.Username = user.Identity.Name
}
}

Related

How to fix Invalid OperationException: Multiple constructors accepting all given argument types have been found in type 'System.Collections.Generic

I have seen variations to this question asked. But none of the answers seem to help me.
When trying to view a list of roles in my view after hitting the AddOrRemoveUsers button(see picture)
I get the following error message.
I cant find where the multiple constructors error seem to be. Am I missing something.
#page
#using ThreeTierAdvisementApp.Areas.Identity.Pages.Account.Administration
#using ThreeTierAdvisementApp.Data
#model List<UserRole>
<form method="post">
<div class="card">
<div class="card-header">
<h2>Add or remove users from this role</h2>
</div>
<div class="card-body">
#for(int i = 0; i<Model.Count; i++){
<div class="form-check m-1">
<input asp-for="#Model[i].IsSelected" class="form-check-input" />
<label class="form-check-label">
#Model[i].UserName
</label>
</div>
}
</div>
<div class="card-footer">
<input type="submit" value="Update" class="btn btn-primary"
style="width:auto" />
<a asp-action="EditRole" asp-route-id="UserId"
class="btn btn-primary" style="width:auto">Cancel</a>
</div>
</div>
</form>
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using ThreeTierAdvisementApp.Data;
namespace ThreeTierAdvisementApp.Areas.Identity.Pages.Account.Administration
{
public class EditUsersInRoleModel : PageModel
{
private readonly RoleManager<IdentityRole> _roleManager;
private readonly UserManager<DefaultUser> _userManager;
public EditUsersInRoleModel(RoleManager<IdentityRole> roleManager, UserManager<DefaultUser> userManager)
{
_roleManager = roleManager;
_userManager = userManager;
}
[BindProperty]
public UserRole RoleView { get; set; }
public async Task<IActionResult> OnGet(string roleId)
{
RoleView = new UserRole { UserId = roleId };
var role = await _roleManager.FindByIdAsync(roleId);
if (role == null)
{
return NotFound();
}
var model = new List<UserRole>();
foreach (var user in _userManager.Users)
{
var userRoleViewModel = new UserRole
{
UserId = user.Id,
UserName = user.UserName,
};
if (await _userManager.IsInRoleAsync(user, role.Name))
{
userRoleViewModel.IsSelected = true;
}
else
{
userRoleViewModel.IsSelected = false;
}
model.Add(userRoleViewModel);
}
return Page();
}
}
}
using Microsoft.AspNetCore.Identity;
using System.Security.Principal;
namespace ThreeTierAdvisementApp.Data
{
public class UserRole
{
public string UserId { get; set; }
public string UserName { get; set; }
public bool IsSelected { get; set; }
}
}
I am using asp.net 6 Razor page pattern but all the examples online are using the MVC pattern. Would appreciate some feedback on how to handle this.

Validation failed: InvalidRoleName

I have some problem with the code. I'm not able to create a role. The code seems fine to me, but I'm not able to find the error. With WriteLine I'm getting the following message:
Microsoft.AspNetCore.Identity.RoleManager: Warning: Role 38676182-f211-47d7-b69b-f190e4338123 validation failed: InvalidRoleName.
Why InvalidRoleName is invalid? It's just a string. Any help is appreciated.
c#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Sarpinos.Pages.Admin
{
public class CreateRoleModel : PageModel
{
private readonly RoleManager<IdentityRole> _roleManager;
public CreateRoleModel(RoleManager<IdentityRole> roleManager)
{
_roleManager = roleManager;
}
[BindProperty]
public List<IdentityRole> Roles { get; set; }
public void OnGet()
{
}
public async Task<IActionResult> OnPost()
{
if (ModelState.IsValid)
{
var newRole = new IdentityRole();
newRole.Id = Guid.NewGuid().ToString();
await _roleManager.CreateAsync(newRole);
Console.WriteLine($"{newRole.Id} {newRole.Name}");
}
return RedirectToPage("Dashboard");
}
}
}
cshtml:
#page
#model Sarpinos.Pages.Admin.CreateRoleModel
#{
ViewData["Title"] = "Register";
}
<div class="container">
<form asp-page="CreateRole" method="POST">
<div class="form-group">
<label asp-for="#Model.Roles">Role name:</label>
<input type="text" asp-for="#Model.Roles" class="form-control" placeholder="Enter role name" />
<span asp-validation-for="#Model.Roles" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-success">Create</button>
</form>
</div>
[BindProperty]
public List<IdentityRole> Roles { get; set; }
public void OnGet()
{
}
public async Task<IActionResult> OnPost()
{
if (ModelState.IsValid)
{
var newRole = new IdentityRole();
newRole.Id = Guid.NewGuid().ToString();
await _roleManager.CreateAsync(newRole);
Console.WriteLine($"{newRole.Id} {newRole.Name}");
}
return RedirectToPage("Dashboard");
}
According to your description, it seems that you want to create a new Role, but by using the above code, it looks that you are using a List<IdentityRole> to display the roles and in the post method, you didn't set the role name for the newRole. So, it might cause the issue.
To Create new Role, in the CreateRoleModel, you could create a new class which contains the Role related properties. For example:
public class CreateRoleModel : PageModel
{
private readonly RoleManager<IdentityRole> _roleManager;
public CreateRoleModel(RoleManager<IdentityRole> roleManager) { _roleManager = roleManager; }
public class NewRole
{
public string RoleName { get; set; }
}
[BindProperty]
public NewRole Input { get; set; }
public void OnGet()
{
Input = new NewRole(); //
}
public async Task<IActionResult> OnPostAsync()
{
if (ModelState.IsValid)
{
var newRole = new IdentityRole();
newRole.Id = Guid.NewGuid().ToString();
newRole.Name = Input.RoleName;
await _roleManager.CreateAsync(newRole);
Console.WriteLine($"{newRole.Id} {newRole.Name}");
}
return RedirectToPage("Home/Index");
}
}
Then, in the CreateRole.cshtml page, based on the NewRole class to insert a new role (you could change the namespace to yours).
#page
#addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
#model IdentitySample.Pages.CreateRoleModel
<div class="container">
<form method="POST">
#Html.AntiForgeryToken()
<div class="form-group">
<label asp-for="#Model.Input.RoleName">Role name:</label>
<input asp-for="#Model.Input.RoleName" class="form-control" placeholder="Enter role name" />
<span asp-validation-for="#Model.Input.RoleName" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-success">Create</button>
</form>
</div>

why not display text Required in Html after submit radio button empty?

1.It delivers when you press the answer in the first question why ?
2.not display text Required in Html after submit radio button empty in span tag.
<span asp-validation-for="#item.CheckAns" class="text-danger"></span>
views/Homecontroller1/Index
This is CSHTML
#model List<QuestionCommentViewModel>
#{
ViewData["Title"] = "Index";
}
<div class="container">
<div class="row">
<div class="col-lg-12">
<form asp-action="Index" method="post">
#foreach (var item in Model)
{
<h3>#item.OneQuestions.TextQ</h3>
#foreach (var r in item.ListChoices)
{
<input type="radio" id="#r.Id" name="#r.Qid" asp-for="#item.CheckAns" value="#r.TextCh">
<label for="#r.Id">#r.TextCh</label><br>
<span asp-validation-for="#item.CheckAns" class="text-danger"></span>
}
}
<input type="submit" value="submit" style="background-color:dodgerblue;Color:white; border-radius:5px; font-weight:bold;" />
</form>
</div>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
model
using System.Threading.Tasks;
using Task222SchoolApp.Models;
namespace Task222SchoolApp.Models
{
public class QuestionCommentViewModel
{
public Question OneQuestions { get; set; }
public IQueryable<Choice> ListChoices { get; set; }
[BindProperty, Required(ErrorMessage = "Please choose Answer.")]
public virtual string CheckAns { get; set; }
}
}
controller
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
using Task222SchoolApp.Models;
namespace WebApplication6.Controllers
{
public class HomeController1 : Controller
{
private readonly SchoolContext _context;
public HomeController1(SchoolContext context)
{
_context = context;
}
// GET: HomeController1
public IActionResult Index()
{
List<QuestionCommentViewModel> QCVM = new List<QuestionCommentViewModel>();
QCVM= GetQCVM();
// Console.WriteLine("qqqqqqq");
return View(QCVM);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(List<QuestionCommentViewModel> QCVM)
{
if (ModelState.IsValid)
{
}
return View();
}
private IQueryable<Question> getQustion()
{
var one = _context.Questions.Include(q => q.QidNavigation);
return one;
}
private List<QuestionCommentViewModel> GetQCVM()
{
Console.WriteLine("111222222222222222222");
var ListQuestion = getQustion();
List<QuestionCommentViewModel> QCVM=new List<QuestionCommentViewModel> ();
foreach (var item in ListQuestion)
{
var ch = _context.Choices.Include(q => q.QidNavigation).Where(q=>q.Qid==item.Id);
QCVM.Add(new QuestionCommentViewModel() { OneQuestions = item, ListChoices= ch });
// return QCVM;
}
return QCVM;
}
// GET: HomeController1/Details/5
public ActionResult Details(int id)
{
return View();
}
// GET: HomeController1/Create
public ActionResult Create()
{
return View();
}
// POST: HomeController1/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(IFormCollection collection)
{
try
{
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
// GET: HomeController1/Edit/5
public ActionResult Edit(int id)
{
return View();
}
// POST: HomeController1/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, IFormCollection collection)
{
try
{
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
// GET: HomeController1/Delete/5
public ActionResult Delete(int id)
{
return View();
}
// POST: HomeController1/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(int id, IFormCollection collection)
{
try
{
return RedirectToAction(nameof(Index));
}
catch
{
return View();
}
}
}
}
You need to change your code like below
#foreach (var item in Model)
{
<h3>#item.OneQuestions.TextQ</h3>
#foreach (var r in item.ListChoices)
{
<input type="radio" id="#r.Id" asp-for="#item.CheckAns" value="#r.TextCh">
<label for="#r.Id">#r.TextCh</label><br>
}
<span asp-validation-for="#item.CheckAns" class="text-danger"></span>
}
Test Result:

asp.net core - login page doesn't show error message

I have a problem, i new with asp.net core and i try to build Login page (after i finish to build sign up page that add new row to db with the correct information).
my problem is when i click on login button with the wrong details so i don't see the error message (LoginError) only after i click on login button again i see the LoginError message (from TempData), why?
is the right way? if not i will happy to try another way.
Login cshtml page:
#page
#model AutomationTool.Pages.Login.IndexModel
#{
ViewData["Title"] = "Index";
}
<h1>Login</h1>
<form method="post">
<div class="form-group">
<label asp-for="user.UserEmail"></label>
<input asp-for="user.UserEmail" class="form-control" />
<span class="text-danger" asp-validation-for="user.UserEmail"></span>
</div>
<div class="form-group">
<label asp-for="user.Password"></label>
<input asp-for="user.Password" class="form-control" />
<span class="text-danger" asp-validation-for="user.Password"></span>
</div>
<div>
#Model.LoginError
</div>
<div>
#Model.Message
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
Login cshtml.cs page:
namespace AutomationTool.Pages.Login
{
public class IndexModel : PageModel
{
private readonly IUserData userData;
[BindProperty]
public User user { get; set; }
[TempData]
public string Message { get; set; }
[TempData]
public string LoginError { get; set; }
public IndexModel(IUserData userData)
{
this.userData = userData;
}
public IActionResult OnGet()
{
return Page();
}
public IActionResult OnPost()
{
ModelState.Remove("User.Id");
ModelState.Remove("User.FirstName");
ModelState.Remove("User.LastName");
if (!ModelState.IsValid)
{
TempData["Message"] = "All fields required!";
return Page();
}
if (!userData.VerifyLogin(user.UserEmail, user.Password))
{
TempData["LoginError"] = "Something wrong try again!";
return Page();
}
else
{
return RedirectToPage("/Index");
}
}
}
}
thanks!
Here your Message and LoginError parameters have been read many times instead of once, so you should use [BindProperty] or [ViewData] instead of [TempData] to bind these two parameters.
The difference between them, you can refer to this.
Chang your code like this:
public class IndexModel : PageModel
{
private readonly IUserData userData;
[BindProperty]
public User user { get; set; }
[BindProperty]//[ViewData]
public string Message { get; set; }
[BindProperty]//[ViewData]
public string LoginError { get; set; }
public IndexModel(IUserData userData)
{
this.userData = userData;
}
public IActionResult OnGet()
{
return Page();
}
public IActionResult OnPost()
{
ModelState.Remove("User.Id");
ModelState.Remove("User.FirstName");
ModelState.Remove("User.LastName");
if (!ModelState.IsValid)
{
Message = "All fields required!";
return Page();
}
if (!userData.VerifyLogin(user.UserEmail, user.Password))
{
LoginError = "Something wrong try again!";
return Page();
}
else
{
return RedirectToPage("/Index");
}
}
}
Here is the test result:

AspNetCore.Identity, MySQL Database, already open DataReader, UserManager and RoleManager

I'm creating a web application with user authentication.
I use the Microsoft.AspNetCore.Identity.EntityFrameworkCore with UserManager<IdentityUser> and a RoleManager<IdentityRole> for managing the users and their roles.
When I use the localDB feature in Visual Studio with MultipleActiveResultSets=true in the connection string, everything works fine.
When I want to use a MySQL Server installed on my Ubuntu 16.04 Server, I can login. But as soon as the RoleManager first tries to connect to the database, I get the following error:
MySqlException: There is already an open DataReader associated with
this Connection which must be closed first
.
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using tsv_core.Models;
// For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
namespace tsv_core.Controllers
{
[Authorize(Roles = "Admins")]
public class AdminController : Controller
{
UserManager<AppUser> userManager;
private IUserValidator<AppUser> userValidator;
private IPasswordValidator<AppUser> passwordValidator;
private IPasswordHasher<AppUser> passwordHasher;
private IRequestLogger _logger;
public AdminController(UserManager<AppUser> usrMgr, IUserValidator<AppUser> userValid, IPasswordValidator<AppUser> passValid, IPasswordHasher<AppUser> passwordHash, IRequestLogger logger)
{
userManager = usrMgr;
userValidator = userValid;
passwordValidator = passValid;
passwordHasher = passwordHash;
_logger = logger;
}
public ViewResult Accounts() => View(userManager.Users);
public ViewResult Create() => View();
[HttpPost]
public async Task<IActionResult> Create(CreateModel model)
{
if (ModelState.IsValid)
{
AppUser user = new AppUser
{
UserName = model.Name,
Email = model.Email
};
IdentityResult result
= await userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
return RedirectToAction("Accounts");
}
else
{
foreach (IdentityError error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
}
}
return View(model);
}
[HttpPost]
public async Task<IActionResult> Delete(string id)
{
AppUser user = await userManager.FindByIdAsync(id);
if (user != null)
{
IdentityResult result = await userManager.DeleteAsync(user);
if (result.Succeeded)
{
return RedirectToAction("Index");
}
else
{
AddErrorsFromResult(result);
}
}
else
{
ModelState.AddModelError("", "User Not Found");
}
return View("Index", userManager.Users);
}
public async Task<IActionResult> Edit(string id)
{
AppUser user = await userManager.FindByIdAsync(id);
if (user != null)
{
return View(user);
}
else
{
return RedirectToAction("Index");
}
}
[HttpPost]
public async Task<IActionResult> Edit(string id, string email,
string password)
{
AppUser user = await userManager.FindByIdAsync(id);
if (user != null)
{
user.Email = email;
IdentityResult validEmail
= await userValidator.ValidateAsync(userManager,
user);
if (!validEmail.Succeeded)
{
AddErrorsFromResult(validEmail);
}
IdentityResult validPass = null;
if (!string.IsNullOrEmpty(password))
{
validPass = await passwordValidator.ValidateAsync(userManager,
user, password);
if (validPass.Succeeded)
{
user.PasswordHash = passwordHasher.HashPassword(user,
password);
}
else
{
AddErrorsFromResult(validPass);
}
}
if ((validEmail.Succeeded && validPass == null)
|| (validEmail.Succeeded
&& password != string.Empty && validPass.Succeeded))
{
IdentityResult result = await userManager.UpdateAsync(user);
if (result.Succeeded)
{
return RedirectToAction("Index");
}
else
{
AddErrorsFromResult(result);
}
}
}
else
{
ModelState.AddModelError("", "User Not Found");
}
return View(user);
}
private void AddErrorsFromResult(IdentityResult result)
{
foreach (IdentityError error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
}
public ViewResult RequestLogging()
{
return View(_logger.GetRequestRepository());
}
}
}
.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using tsv_core.Models;
using Microsoft.AspNetCore.Identity;
namespace tsv_core.Controllers
{
[Authorize(Roles = "Admins")]
public class AccountController : Controller
{
private UserManager<AppUser> userManager;
private SignInManager<AppUser> signInManager;
public AccountController(UserManager<AppUser> userMgr,
SignInManager<AppUser> signinMgr)
{
userManager = userMgr;
signInManager = signinMgr;
}
[AllowAnonymous]
public IActionResult Login(string returnUrl)
{
ViewBag.returnUrl = returnUrl;
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginModel details,
string returnUrl)
{
if (ModelState.IsValid)
{
AppUser user = await userManager.FindByEmailAsync(details.Email);
if (user != null)
{
await signInManager.SignOutAsync();
Microsoft.AspNetCore.Identity.SignInResult result =
await signInManager.PasswordSignInAsync(
user, details.Password, false, false);
if (result.Succeeded)
{
return Redirect(returnUrl ?? "/");
}
}
ModelState.AddModelError(nameof(LoginModel.Email), "Invalid user or password");
}
return View(details);
}
public async Task<IActionResult> Logout()
{
await signInManager.SignOutAsync();
return RedirectToAction("Index", "Home");
}
[AllowAnonymous]
public IActionResult AccessDenied()
{
return View();
}
}
}
.
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc;
using System.Linq;
using tsv_core.Models;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
namespace tsv_core.Controllers
{
[Authorize(Roles = "Admins")]
public class RoleAdminController : Controller
{
private RoleManager<IdentityRole> roleManager;
private UserManager<AppUser> userManager;
public RoleAdminController(RoleManager<IdentityRole> roleMgr, UserManager<AppUser> userMrg)
{
roleManager = roleMgr;
userManager = userMrg;
}
public ViewResult Index() => View(roleManager.Roles);
public IActionResult Create() => View();
[HttpPost]
public async Task<IActionResult> Create([Required]string name)
{
if (ModelState.IsValid)
{
IdentityResult result
= await roleManager.CreateAsync(new IdentityRole(name));
if (result.Succeeded)
{
return RedirectToAction("Index");
}
else
{
AddErrorsFromResult(result);
}
}
return View(name);
}
[HttpPost]
public async Task<IActionResult> Delete(string id)
{
IdentityRole role = await roleManager.FindByIdAsync(id);
if (role != null)
{
IdentityResult result = await roleManager.DeleteAsync(role);
if (result.Succeeded)
{
return RedirectToAction("Index");
}
else
{
AddErrorsFromResult(result);
}
}
else
{
ModelState.AddModelError("", "No role found");
}
return View("Index", roleManager.Roles);
}
public async Task<IActionResult> Edit(string id)
{
IdentityRole role = await roleManager.FindByIdAsync(id);
List<AppUser> members = new List<AppUser>();
List<AppUser> nonMembers = new List<AppUser>();
foreach (AppUser user in userManager.Users)
{
var list = await userManager.IsInRoleAsync(user, role.Name)
? members : nonMembers;
list.Add(user);
}
return View(new RoleEditModel
{
Role = role,
Members = members,
NonMembers = nonMembers
});
}
[HttpPost]
public async Task<IActionResult> Edit(RoleModificationModel model)
{
IdentityResult result;
if (ModelState.IsValid)
{
foreach (string userId in model.IdsToAdd ?? new string[] { })
{
AppUser user = await userManager.FindByIdAsync(userId);
if (user != null)
{
result = await userManager.AddToRoleAsync(user,
model.RoleName);
if (!result.Succeeded)
{
AddErrorsFromResult(result);
}
}
}
foreach (string userId in model.IdsToDelete ?? new string[] { })
{
AppUser user = await userManager.FindByIdAsync(userId);
if (user != null)
{
result = await userManager.RemoveFromRoleAsync(user,
model.RoleName);
if (!result.Succeeded)
{
AddErrorsFromResult(result);
}
}
}
}
if (ModelState.IsValid)
{
return RedirectToAction(nameof(Index));
}
else
{
return await Edit(model.RoleId);
}
}
private void AddErrorsFromResult(IdentityResult result)
{
foreach (IdentityError error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
}
}
}
.
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Razor.TagHelpers;
using tsv_core.Models;
namespace tsv_core.Infrastructure
{
[HtmlTargetElement("td", Attributes = "identity-role")]
public class RoleUsersTagHelper : TagHelper
{
private UserManager<AppUser> userManager;
private RoleManager<IdentityRole> roleManager;
public RoleUsersTagHelper(UserManager<AppUser> usermgr,
RoleManager<IdentityRole> rolemgr)
{
userManager = usermgr;
roleManager = rolemgr;
}
[HtmlAttributeName("identity-role")]
public string Role { get; set; }
public override async Task ProcessAsync(TagHelperContext context,
TagHelperOutput output)
{
IdentityRole role = await roleManager.FindByIdAsync(Role);;
List<string> names = new List<string>();
if (role != null)
{
foreach (var user in userManager.Users)
{
if (user != null && await userManager.IsInRoleAsync(user, role.Name))
{
names.Add(user.UserName);
}
}
}
output.Content.SetContent(names.Count == 0 ?
"No Users" : string.Join(", ", names));
}
}
}
.
#model IEnumerable<IdentityRole>
<div class="bg-primary panel-body"><h4>Roles</h4></div>
<div class="text-danger" asp-validation-summary="ModelOnly"></div>
<table class="table table-condensed table-bordered table-bordered">
<tr><th>ID</th><th>Name</th><th>Users</th><th></th></tr>
#if (Model.Count() == 0)
{
<tr><td colspan="4" class="text-center">No Roles</td></tr>
}
else
{
foreach (var role in Model)
{
<tr>
<td>#role.Id</td>
<td>#role.Name</td>
<td identity-role="#role.Id"></td>
<td>
<form asp-action="Delete" asp-route-id="#role.Id" method="post">
<a class="btn btn-sm btn-primary" asp-action="Edit"
asp-route-id="#role.Id">Edit</a>
<button type="submit"
class="btn btn-sm btn-danger">
Delete
</button>
</form>
</td>
</tr>
}
}
</table>
<a class="btn btn-primary" asp-action="Create">Create</a>
.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using tsv_core.Models;
using tsv_core.Infrastructure;
using MySQL.Data.EntityFrameworkCore.Extensions;
namespace tsv_core
{
public class Startup
{
IConfigurationRoot Configuration;
public Startup(IHostingEnvironment env)
{
Configuration = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json").Build();
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//services.AddDbContext<AppIdentityDbContext>(options => options.UseSqlServer(Configuration["Data:tsvIdentity:ConnectionString"]));
services.AddDbContext<AppIdentityDbContext>(options => options.UseMySQL(Configuration["Data:tsvIdentity:ConnectionString"]));
services.AddIdentity<AppUser, IdentityRole>(opts => opts.User.RequireUniqueEmail = true).AddEntityFrameworkStores<AppIdentityDbContext>();
services.AddTransient<IRequestLogger, TestRequestLogger>();
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentity();
//This position of the LoggingMiddleware is important!! If it would be placed before "app.UseStaticFiles();" the request paths to the static files would be logged too.
//If it would be placed behind app.UseMvc, it wouldn't log anything at all.
app.UseMiddleware<LoggingMiddleware>();
app.UseMvcWithDefaultRoute();
AppIdentityDbContext.CreateAdminAccount(app.ApplicationServices, Configuration).Wait();
}
}
}
These should be all relevant classes.
AppUser just inherits from IdentityUser and does not have any extra fields at the moment.
The first time the error appears hear:
ErrorExceptionPage
I have already searched for answers and came across topics like connection-pooling etc.
My problem is that I don't know exactly what EF Core already does for me, when it comes to pooling.
Do I need to Dispose the UserManager and the RoleManager after every use by using "using- blocks" or is there a completely different solution?

Resources