I am using asp.net mvc 5 to develop a web application.But i got some error while playing with the users account.I have Created a user account and i want to delete it but facing the following issue while doing it with ajax-jquery.
I am trying to delete a user account using ajax request but it gives the error for null object reference as Owin Context is not initiated due to ajax call.
public class AdmissionsController : Controller
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
public AdmissionsController()
{
}
public AdmissionsController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
{
UserManager = userManager;
SignInManager = signInManager;
}
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
//function to delete user account
public async Task<object> DeleteUser(string id)
{
ApplicationDbContext context = new ApplicationDbContext();
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var user = await _userManager.FindByIdAsync(id);
var logins = user.Logins;
var rolesForUser = await _userManager.GetRolesAsync(id);
using (var transaction = context.Database.BeginTransaction())
{
foreach (var login in logins.ToList())
{
await _userManager.RemoveLoginAsync(login.UserId, new UserLoginInfo(login.LoginProvider, login.ProviderKey));
}
if (rolesForUser.Count() > 0)
{
foreach (var item in rolesForUser.ToList())
{
// item should be the name of the role
var result = await _userManager.RemoveFromRoleAsync(user.Id, item);
}
}
await _userManager.DeleteAsync(user);
transaction.Commit();
}
return new{success=true};
}
}
And my ajax call as follow :
var userid = $(this).attr("data-StudentId");
var success = false;
$.ajax({
method: "Get",
url: "/Area/Admissions/DeleteUser",
data: { "id": userid },
success: function (response) {
if (response.Success == true) {
success = true;
}
else if (response.Success == false) {
//window.location.reload();
}
},
error: function (response) {
notificationMSG("fa fa-thumbs-up", "error", "Error", response.message);
}
});
How can i resolve this issue???
I am getting the error at the following line:
var user = await _userManager.FindByIdAsync(id);
This is just a wild guess, but it seems to me that you do not instantiate _userManager. You have a corresponding property where this creation takes place. However, in the line with error you use private field. Try to use the property:
var user = await UserManager.FindByIdAsync(id);
This will call the getter where you get this manager from the owin context.
Related
currently I'm building web apps for local. When I create new user, I'm setup the user with Default Password. And I want to, if the User login with the Default Password it will redirect to Change Password Page. And User will not be able to access any page until they change the password.
My current workaround is checking in each controller, is there any Smart way to do this?
Here's some code after doing login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login([FromForm] LoginViewModel vm, string returnUrl = null)
{
if (ModelState.IsValid)
{
var result = await repository.Login(vm);
if (result.IsSuccess)
{
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, result.Data,
new AuthenticationProperties
{
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddDays(1),
IsPersistent = false
});
if (vm.Password != Password.DefaultPassword)
{
return RedirectToLocal(returnUrl);
}
else
{
return RedirectToAction(nameof(UserController.NewPassword));
}
}
else
{
ViewBag.ErrorMessage = result.ErrorMessage;
}
}
return View(vm);
}
For the other Controller, we always check the Password from session. If the Password is same as Default Password we redirect it to NewPassword Page
Thanks in advance!
You can store user password using session as below
HttpContext.Session.SetString("Password", vm.password);
Then create a filter to check if the user login with the Default Password or not
public class PasswordFilter: IAuthorizationFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ISession _session;
public PasswordFilter(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
_session = _httpContextAccessor.HttpContext.Session;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
if (_session.GetString("Password") == Password.DefaultPassword)
{
context.Result = new RedirectResult(nameof(UserController.NewPassword));
}
}
}
Finally, you can use this filter by adding this attribute to your controllers
[TypeFilter(typeof(PasswordFilter))]
I hope this approach achieves your goal.
As #JHBonarius said, my current workaround now is to use Custom Middleware for Redirecting to New Password Page.
My middleware look like this:
public class CheckPasswordMiddleware
{
private readonly RequestDelegate next;
public CheckPasswordMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Path != "/User/NewPassword" && context.User.Identity.IsAuthenticated)
{
var userId = context.User.Identity.GetUserId();
if (!string.IsNullOrEmpty(userId))
{
var dbContext = context.RequestServices.GetRequiredService<DatabaseContext>();
var passwordHash = await dbContext.User.Where(x => x.Id.ToLower() == userId.ToLower()).Select(x => x.Password).FirstOrDefaultAsync();
if (Hash.Verify(Password.DefaultPassword, passwordHash))
{
context.Response.Redirect("/User/NewPassword");
}
}
}
await next(context);
}
}
and now I can get rid the check in my other controller, and my Login will just look like this:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login([FromForm] LoginViewModel vm, string returnUrl = null)
{
if (ModelState.IsValid)
{
var result = await repository.Login(vm);
if (result.IsSuccess)
{
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, result.Data,
new AuthenticationProperties
{
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddDays(1),
IsPersistent = false
});
return RedirectToLocal(returnUrl);
}
else
{
ViewBag.ErrorMessage = result.ErrorMessage;
}
}
return View(vm);
}
Hope this help anyone who want to achieve the same goals.
Thanks!
I'm using ASP.Net MVC Core where I have the following code where it is a daily task that sets users as "Expired" if their subscription is over.
But how can I also check and force them to logout if they are currently logged in?
public interface IExpirationJob
{
Task SetExpired();
}
public class ExpirationJob : IExpirationJob
{
private readonly ApplicationDbContext _db;
private readonly IEmailSender _emailSender;
public ExpirationJob(ApplicationDbContext db, IEmailSender emailSender)
{
_db = db;
_emailSender = emailSender;
}
public async Task SetExpired()
{
foreach(var item in _db.Institution)
{
if (item.SubscriptionEndDate != null)
{
if (item.SubscriptionEndDate == DateTime.Today)
{
item.Status = SD.StatusExpired;
//Here I want to check if the user is logged in, then force logout should be done.
}
}
}
await _db.SaveChangesAsync();
}
}
Any help is highly appreciated.
You can add a SecurityStamp property of type GUID to users model and set SecurityStamp to cookie or jwt token.
then when a user login, you must change the SecurityStamp value to a new value and save SecurityStamp to cookie and any time user send a request to application you must check SecurityStamp saved in cookie with SecurityStamp of users in database. and if these properties wasn't equal togeter you must reject user and set sign out user.
public static async Task ValidateAsync(CookieValidatePrincipalContext context)
{
context = context ?? throw new ArgumentNullException(nameof(context));
var claimsIdentity = context.Principal.Identity as ClaimsIdentity;
if(claimsIdentity?.Claims == null || !claimsIdentity.Claims.Any())
{
await RejectPrincipal();
return;
}
UserManager<IdentityUser> userManager = context.HttpContext.RequestServices.GetRequiredService<UserManager<IdentityUser>>();
var user = await userManager.FindByNameAsync(context.Principal.FindFirstValue(ClaimTypes.NameIdentifier));
if (user == null || user.SecurityStamp != context.Principal.FindFirst(new ClaimsIdentityOptions().SecurityStampClaimType)?.Value)
{
await RejectPrincipal();
return;
}
async Task RejectPrincipal()
{
context.RejectPrincipal();
await context.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
In startup class pass ValidateAsync method to OnValidatePrincipal and set ValidationInterval to zero.
services.ConfigureApplicationCookie(options =>
{
//
options.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = ValidateAsync
};
}).Configure<SecurityStampValidatorOptions>(options =>
{
options.ValidationInterval = TimeSpan.Zero;
});
finally in your method just update SecurityStamp value like this:
public async Task SetExpired()
{
foreach(var item in _db.Institution)
{
if (item.SubscriptionEndDate != null)
{
if (item.SubscriptionEndDate == DateTime.Today)
{
item.Status = SD.StatusExpired;
//Here I want to check if the user is logged in, then force logout should be done.
Guid securityStamp = Guid.NewGuid();
item.SecurityStamp = securityStamp;
}
}
}
await _db.SaveChangesAsync();
}
I am using ASP.NET Core 3.1 and context.Resource as AuthorizationFilterContext cannot be used to access the routeValues or queryString. I have to use IHttpContextAccessor to get the routeValues["id"] but IHttpContextAccessor does not have Result property to set RedirectToPage("/View", new {id = "...."})
public class NoEditOrDeleteSuperUserHandler : AuthorizationHandler<ManageSuperAdminRequirement>
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly UserManager<ApplicationUser> _userManager;
public NoEditOrDeleteSuperUserHandler(IHttpContextAccessor httpContextAccessor, UserManager<ApplicationUser> userManager)
{
this._httpContextAccessor = httpContextAccessor;
this._userManager = userManager;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ManageSuperAdminRequirement requirement)
{
var userId = _httpContextAccessor.HttpContext.Request.RouteValues["id"].ToString();
var selectedUser = _userManager.FindByIdAsync(userId).GetAwaiter().GetResult();
if (_userManager.IsInRoleAsync(selectedUser,"SuperAdmin").GetAwaiter().GetResult() == false)
{
context.Succeed(requirement);
return Task.CompletedTask;
}
context.Fail();
var Response = _httpContextAccessor.HttpContext.Response;
var message= Encoding.UTF8.GetBytes("User with Super Admin role cannot be edited");
**// return RedirectToPage("/View", new {id = id});**
Response.OnStarting(async () =>
{
_httpContextAccessor.HttpContext.Response.StatusCode = 429;
await Response.Body.WriteAsync(message, 0, message.Length);
});
return Task.CompletedTask;
}
}
I am using Custom Policy Authorization and when Authorization fails I want to redirect the user to returnUrl instead of Access Denied page
I found myself a solution to my above question regarding how to redirect a user to referer page after having a failed Custom Authorization check.
Please if there is any better way to do this please guide me.
1 - I removed the below code from HandleRequirementAsync and let it redirect to AccessDenied Page.
var Response = _httpContextAccessor.HttpContext.Response;
var message= Encoding.UTF8.GetBytes("User with Super Admin role cannot be edited");
**// return RedirectToPage("/View", new {id = id});**
Response.OnStarting(async () =>
{
_httpContextAccessor.HttpContext.Response.StatusCode = 429;
await Response.Body.WriteAsync(message, 0, message.Length);
});
2 - In AccessDeniedModel class I added below code:
[TempData]
public string Message { get; set; }
public void OnGet()
{
Message = "Access Denied: You do not have access to this resource.";
HttpContext.Response.Redirect(HttpContext.Request.Headers["Referer"]);
}
4 - In /User/Roles/View.cshtml.cs I defined a model public property
[TempData]
public string Message { get; set; }
3 - In /User/Roles/View.cshtml I added <p>#Model.Message</p> to display the message to the user.
Hello im using a DTO for a single value(Id) & trying to post to Db using ApiController but on button click I keep getting error 400 that is referring me to xhr.send error.
(im using asp.net core 2.1 )
Code :
#section Scripts{
<script type="text/javascript">
$(document)
.ready(function() {
$(".js-toggle-HandShake")
.click(function(e) {
var button = $(e.target);
console.log(button.attr("data-QuettaOfferId")); //Value=24 >> OK
$.post("/Api/HandShake/", { QuettaOfferId: button.attr("data-QuettaOfferId") })
// Error in > POST https://localhost:44339/Api/HandShake/ 400 () &
//in jquery>> xhr.send( options.hasContent && options.data || null );
.done(function() {
button
.text("Chousen");
})
.fail(function() {
alert("Something failed");
});
});
});
</script>
}
& the ApiController code
[Microsoft.AspNetCore.Mvc.Route("api/[controller]")]
[ApiController]
[Microsoft.AspNetCore.Authorization.Authorize]
public class HandShakeController : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly UserManager<IdentityUser> _userManager;
// private readonly IHostingEnvironment hostingEnvironment;
public HandShakeController(ApplicationDbContext context ,UserManager<IdentityUser> userManager/*, IHostingEnvironment environment*/)
{
_context = context;
_userManager = userManager;
//hostingEnvironment = environment;
}
[Microsoft.AspNetCore.Mvc.HttpPost]
// public IHttpActionResult HandShakes(HandShakeDto dto)
public IActionResult HandShakes(HandShakeDto dto)
{
var userId = _userManager.GetUserId(User);
var check = _context.Quetta.Where(u => u.SiteUserId == userId);
if ( _context.handShakes.Any(f => f.QuettaOfferId == dto.QuettaOfferId))
return BadRequest("Some error Msg");
if (check.Any())
{
var hand = new HandShake
{
QuettaOfferId = dto.QuettaOfferId
};
try
{
_context.handShakes.Add(hand);
_context.SaveChangesAsync();
return Ok();
}
catch (Exception e)
{
Console.WriteLine(e);
return BadRequest("Some error Msg");
}
}
else{
return BadRequest("");}
// Check if the user id that publish the ed = login user.
//if so add the offer to selected table,
}
}
im using asp.net core 2.1 & strongly suspect that the problem is in the ApiController but im not sure.
The DTO
public class HandShakeDto
{
public int QuettaOfferId { get; set; }
}
Try replacing
$.post("/Api/Hand/", { QuettaOfferId: button.attr("data-QuettaOfferId") })
By
$.post("/Api/HandShake/", { QuettaOfferId: button.attr("data-QuettaOfferId") })
as your api controller name is HandShakeController
When i am running this code entering valid username and password, still it giving me 404.When i debug the code Username is showing null.
How to write unit test to test the variables inside the controller's Login method, to test the Identity is Null or not.(claims Identity added)
I have written the test code for model state.
I am not getting any clue to initiate the test code. It would be great if someone pushes up.
public class AccountsControllers : Controller
{
private readonly ApplicationContext appDbContext;
private readonly IApplicationContext iappDbContext;
private readonly UserManager<ApplicationUser> userManager;
public AccountsControllers(ApplicationContext appDb
,UserManager<ApplicationUser> um,
IApplicationContext iappdb)
{
userManager = um;
appDbContext = appDb;
iappDbContext = iappdb;
}
[Route("api/login")]
[HttpPost
public async Task<IActionResult> Login([FromBody]LoginViewModel credentials)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
if (identity == null)
{
return BadRequest(Errors.AddErrorToModelState("login_failure", "Invalid username or password.", ModelState));
}
var response = new
{
id = identity.Claims.Single(c => c.Type == "id").Value,
auth_token = await jwtFactory.GenerateEncodedToken(credentials.UserName, identity),
expires_in = (int)jwtOptions.ValidFor.TotalSeconds
};
var json = JsonConvert.SerializeObject(response,serializerSettings);
return new OkObjectResult(json);
}
public async Task<ClaimsIdentity> GetClaimsIdentity(string userName,
string password)
{
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
{
// get the user to verifty
var userToVerify = await userManager.FindByNameAsync(userName);
if (userToVerify != null)
{
// check the credentials
if (await userManager.CheckPasswordAsync(userToVerify, password))
{
return await Task.FromResult(jwtFactory.GenerateClaimsIdentity(userName, userToVerify.Id));
}
}
}
return await Task.FromResult<ClaimsIdentity>(null);
}
}