This commit is contained in:
2020-03-05 20:05:05 +00:00
parent 078a19f4bf
commit 0612a70ee5
2 changed files with 108 additions and 0 deletions

View File

@@ -233,6 +233,85 @@ namespace AyaNova.Api.Controllers
return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
}
/// <summary>
/// Change Password
/// </summary>
/// <remarks>
/// <param name="changecreds"></param>
/// <returns></returns>
[HttpPost("ChangePassword")]
public async Task<IActionResult> ChangePassword([FromBody] AuthController.ChangePasswordParam changecreds)
{
if (!serverState.IsOpen)
{
return StatusCode(503, new ApiErrorResponse(ApiErrorCode.API_CLOSED, null, serverState.Reason));
}
if (!ModelState.IsValid)
{
return BadRequest(new ApiErrorResponse(ModelState));
}
int nFailedAuthDelay = 3000;//should be just long enough to make brute force a hassle but short enough to not annoy people who just mistyped their creds to login
if (string.IsNullOrWhiteSpace(changecreds.OldPassword) || string.IsNullOrWhiteSpace(changecreds.LoginName))
{
metrics.Measure.Meter.Mark(MetricsRegistry.FailedLoginMeter);
//Make a failed pw wait
await Task.Delay(nFailedAuthDelay);
return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
}
if (string.IsNullOrWhiteSpace(changecreds.NewPassword))
{
return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, "NewPassword"));
}
if (changecreds.NewPassword != changecreds.ConfirmPassword)
{
return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_INVALID_VALUE, "NewPassword", "NewPassword does not match ConfirmPassword"));
}
//Multiple users are allowed the same password and login
//Salt will differentiate them so get all users that match login, then try to match pw
var users = await ct.User.AsNoTracking().Where(m => m.Login == changecreds.LoginName).ToListAsync();
foreach (User u in users)
{
string hashed = Hasher.hash(u.Salt, changecreds.OldPassword);
if (hashed == u.Password)
{
//If the user is inactive they may not login
if (!u.Active)
{
//respond like bad creds so as not to leak information
return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
}
//fetch and update user
//Instantiate the business object handler
UserBiz biz = UserBiz.GetBiz(ct, HttpContext);
await biz.ChangePasswordAsync(u.Id, changecreds.NewPassword);
return NoContent();
}
}
//No users matched, it's a failed login
//Make a failed pw wait
await Task.Delay(nFailedAuthDelay);
return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
}
//------------------------------------------------------
public class CredentialsParam
@@ -245,5 +324,19 @@ namespace AyaNova.Api.Controllers
}
public class ChangePasswordParam
{
[System.ComponentModel.DataAnnotations.Required]
public string LoginName { get; set; }
[System.ComponentModel.DataAnnotations.Required]
public string OldPassword { get; set; }
[System.ComponentModel.DataAnnotations.Required]
public string NewPassword { get; set; }
[System.ComponentModel.DataAnnotations.Required]
public string ConfirmPassword { get; set; }
}
}//eoc
}//eons

View File

@@ -196,6 +196,21 @@ namespace AyaNova.Biz
}
//put
internal async Task<bool> ChangePasswordAsync(long userId, string newPassword)
{
User dbObj = await ct.User.FirstOrDefaultAsync(m => m.Id == userId);
dbObj.Password = Hasher.hash(dbObj.Salt, newPassword);
await ct.SaveChangesAsync();
//Log modification and save context
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct);
return true;
}
private async Task SearchIndexAsync(User obj, bool isNew)
{
//SEARCH INDEXING