The next generation of the Teknik Services. Written in ASP.NET. https://www.teknik.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1346 lines
55 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using Teknik.Areas.Users.Models;
using Teknik.Areas.Users.ViewModels;
using Teknik.Controllers;
using Teknik.Utilities;
using Teknik.Areas.Users.Utility;
using Teknik.Filters;
using QRCoder;
using TwoStepsAuthenticator;
using Teknik.Attributes;
using Teknik.Utilities.Cryptography;
using Microsoft.Extensions.Logging;
using Teknik.Configuration;
using Teknik.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Net;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using System.Threading.Tasks;
using Teknik.Logging;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Identity;
using IdentityModel.Client;
using System.Net.Http;
using Newtonsoft.Json.Linq;
using Teknik.Security;
using Microsoft.IdentityModel.Tokens;
using IdentityModel;
using System.Security.Cryptography;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Http;
using IdentityServer4.Models;
namespace Teknik.Areas.Users.Controllers
{
[Authorize]
[Area("User")]
public class UserController : DefaultController
{
private static readonly UsedCodesManager usedCodesManager = new UsedCodesManager();
private const string _AuthSessionKey = "AuthenticatedUser";
private readonly IHttpContextAccessor _httpContextAccessor;
private ISession _session => _httpContextAccessor.HttpContext.Session;
public LogoutSessionManager _logoutSessions { get; }
public UserController(ILogger<Logger> logger, Config config, TeknikEntities dbContext, LogoutSessionManager logoutSessions, IHttpContextAccessor httpContextAccessor) : base(logger, config, dbContext)
{
_logoutSessions = logoutSessions;
_httpContextAccessor = httpContextAccessor;
}
[HttpGet]
public IActionResult Login(string returnUrl)
{
// Let's double check their email and git accounts to make sure they exist
string email = UserHelper.GetUserEmailAddress(_config, User.Identity.Name);
if (_config.EmailConfig.Enabled && !UserHelper.UserEmailExists(_config, email))
{
//UserHelper.AddUserEmail(_config, email, model.Password);
}
if (_config.GitConfig.Enabled && !UserHelper.UserGitExists(_config, User.Identity.Name))
{
//UserHelper.AddUserGit(_config, User.Identity.Name, model.Password);
}
if (!string.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
return Redirect(Url.SubRouteUrl("www", "Home.Index"));
}
[HttpGet]
public async Task Logout()
{
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Logout(string logout_token)
{
Response.Headers.Add("Cache-Control", "no-cache, no-store");
Response.Headers.Add("Pragma", "no-cache");
try
{
var user = await ValidateLogoutToken(logout_token);
// these are the sub & sid to signout
var sub = user.FindFirst("sub")?.Value;
var sid = user.FindFirst("sid")?.Value;
_logoutSessions.Add(sub, sid);
return Ok();
}
catch { }
return BadRequest();
}
private async Task<ClaimsPrincipal> ValidateLogoutToken(string logoutToken)
{
var claims = await ValidateJwt(logoutToken);
if (claims.FindFirst("sub") == null && claims.FindFirst("sid") == null) throw new Exception("Invalid logout token");
var nonce = claims.FindFirstValue("nonce");
if (!String.IsNullOrWhiteSpace(nonce)) throw new Exception("Invalid logout token");
var eventsJson = claims.FindFirst("events")?.Value;
if (String.IsNullOrWhiteSpace(eventsJson)) throw new Exception("Invalid logout token");
var events = JObject.Parse(eventsJson);
var logoutEvent = events.TryGetValue("http://schemas.openid.net/event/backchannel-logout");
if (logoutEvent == null) throw new Exception("Invalid logout token");
return claims;
}
private async Task<ClaimsPrincipal> ValidateJwt(string jwt)
{
// read discovery document to find issuer and key material
var disco = await DiscoveryClient.GetAsync(_config.UserConfig.IdentityServerConfig.Authority);
var keys = new List<SecurityKey>();
foreach (var webKey in disco.KeySet.Keys)
{
var e = Base64Url.Decode(webKey.E);
var n = Base64Url.Decode(webKey.N);
var key = new RsaSecurityKey(new RSAParameters { Exponent = e, Modulus = n })
{
KeyId = webKey.Kid
};
keys.Add(key);
}
var parameters = new TokenValidationParameters
{
ValidIssuer = disco.Issuer,
ValidAudience = _config.UserConfig.IdentityServerConfig.ClientId,
IssuerSigningKeys = keys,
NameClaimType = JwtClaimTypes.Name,
RoleClaimType = JwtClaimTypes.Role
};
var handler = new JwtSecurityTokenHandler();
handler.InboundClaimTypeMap.Clear();
var user = handler.ValidateToken(jwt, parameters, out var _);
return user;
}
[AllowAnonymous]
public IActionResult GetPremium()
{
ViewBag.Title = "Get a Premium Account - " + _config.Title;
GetPremiumViewModel model = new GetPremiumViewModel();
return View(model);
}
[HttpGet]
[AllowAnonymous]
public IActionResult Register(string inviteCode, string ReturnUrl)
{
RegisterViewModel model = new RegisterViewModel();
model.InviteCode = inviteCode;
model.ReturnUrl = ReturnUrl;
return View("/Areas/User/Views/User/ViewRegistration.cshtml", model);
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Register([Bind(Prefix = "Register")]RegisterViewModel model)
{
model.Error = false;
model.ErrorMessage = string.Empty;
if (ModelState.IsValid)
{
if (_config.UserConfig.RegistrationEnabled)
{
if (!model.Error && !UserHelper.ValidUsername(_config, model.Username))
{
model.Error = true;
model.ErrorMessage = "That username is not valid";
}
if (!model.Error && !(await UserHelper.UsernameAvailable(_dbContext, _config, model.Username)))
{
model.Error = true;
model.ErrorMessage = "That username is not available";
}
if (!model.Error && model.Password != model.ConfirmPassword)
{
model.Error = true;
model.ErrorMessage = "Passwords must match";
}
// Validate the Invite Code
if (!model.Error && _config.UserConfig.InviteCodeRequired && string.IsNullOrEmpty(model.InviteCode))
{
model.Error = true;
model.ErrorMessage = "An Invite Code is required to register";
}
if (!model.Error && !string.IsNullOrEmpty(model.InviteCode) && _dbContext.InviteCodes.Where(c => c.Code == model.InviteCode && c.Active && c.ClaimedUser == null).FirstOrDefault() == null)
{
model.Error = true;
model.ErrorMessage = "Invalid Invite Code";
}
if (!model.Error)
{
try
{
await UserHelper.CreateAccount(_dbContext, _config, Url, model.Username, model.Password, model.RecoveryEmail, model.InviteCode);
}
catch (Exception ex)
{
model.Error = true;
model.ErrorMessage = ex.GetFullMessage(true);
}
if (!model.Error)
{
return Redirect(Url.SubRouteUrl("user", "User.Login", new { returnUrl = model.ReturnUrl }));
}
}
}
if (!model.Error)
{
model.Error = true;
model.ErrorMessage = "User Registration is Disabled";
}
}
else
{
model.Error = true;
model.ErrorMessage = "Missing Required Fields";
}
return GenerateActionResult(new { error = model.ErrorMessage }, View("/Areas/User/Views/User/ViewRegistration.cshtml", model));
}
// GET: Profile/Profile
[AllowAnonymous]
public async Task<IActionResult> ViewProfile(string username)
{
if (string.IsNullOrEmpty(username))
{
username = User.Identity.Name;
}
ProfileViewModel model = new ProfileViewModel();
ViewBag.Title = "User Does Not Exist - " + _config.Title;
ViewBag.Description = "The User does not exist";
try
{
User user = UserHelper.GetUser(_dbContext, username);
if (user != null)
{
ViewBag.Title = username + "'s Profile - " + _config.Title;
ViewBag.Description = "Viewing " + username + "'s Profile";
model.UserID = user.UserId;
model.Username = user.Username;
if (_config.EmailConfig.Enabled)
{
model.Email = string.Format("{0}@{1}", user.Username, _config.EmailConfig.Domain);
}
// Get the user claims for this user
model.IdentityUserInfo = await IdentityHelper.GetIdentityUserInfo(_config, user.Username);
model.LastSeen = UserHelper.GetLastAccountActivity(_dbContext, _config, user.Username, model.IdentityUserInfo);
model.UserSettings = user.UserSettings;
model.BlogSettings = user.BlogSettings;
model.UploadSettings = user.UploadSettings;
model.Uploads = _dbContext.Uploads.Where(u => u.UserId == user.UserId).OrderByDescending(u => u.DateUploaded).ToList();
model.Pastes = _dbContext.Pastes.Where(u => u.UserId == user.UserId).OrderByDescending(u => u.DatePosted).ToList();
model.ShortenedUrls = _dbContext.ShortenedUrls.Where(s => s.UserId == user.UserId).OrderByDescending(s => s.DateAdded).ToList();
model.Vaults = _dbContext.Vaults.Where(v => v.UserId == user.UserId).OrderByDescending(v => v.DateCreated).ToList();
return View(model);
}
model.Error = true;
model.ErrorMessage = "The user does not exist";
}
catch (Exception ex)
{
model.Error = true;
model.ErrorMessage = ex.GetFullMessage(true);
}
return View(model);
}
public IActionResult ViewServiceData()
{
string username = User.Identity.Name;
ViewServiceDataViewModel model = new ViewServiceDataViewModel();
ViewBag.Title = "User Does Not Exist - " + _config.Title;
ViewBag.Description = "The User does not exist";
try
{
User user = UserHelper.GetUser(_dbContext, username);
if (user != null)
{
ViewBag.Title = "Service Data - " + _config.Title;
ViewBag.Description = "Viewing all of your service data";
model.Uploads = _dbContext.Uploads.Where(u => u.UserId == user.UserId).OrderByDescending(u => u.DateUploaded).ToList();
model.Pastes = _dbContext.Pastes.Where(u => u.UserId == user.UserId).OrderByDescending(u => u.DatePosted).ToList();
model.ShortenedUrls = _dbContext.ShortenedUrls.Where(s => s.UserId == user.UserId).OrderByDescending(s => s.DateAdded).ToList();
model.Vaults = _dbContext.Vaults.Where(v => v.UserId == user.UserId).OrderByDescending(v => v.DateCreated).ToList();
return View(model);
}
model.Error = true;
model.ErrorMessage = "The user does not exist";
}
catch (Exception ex)
{
model.Error = true;
model.ErrorMessage = ex.GetFullMessage(true);
}
return View(model);
}
public IActionResult Settings()
{
return Redirect(Url.SubRouteUrl("user", "User.ProfileSettings"));
}
public IActionResult ProfileSettings()
{
string username = User.Identity.Name;
User user = UserHelper.GetUser(_dbContext, username);
if (user != null)
{
ViewBag.Title = "Profile Settings - " + _config.Title;
ViewBag.Description = "Your " + _config.Title + " Profile Settings";
ProfileSettingsViewModel model = new ProfileSettingsViewModel();
model.Page = "Profile";
model.UserID = user.UserId;
model.Username = user.Username;
model.About = user.UserSettings.About;
model.Quote = user.UserSettings.Quote;
model.Website = user.UserSettings.Website;
return View("/Areas/User/Views/User/Settings/ProfileSettings.cshtml", model);
}
return new StatusCodeResult(StatusCodes.Status403Forbidden);
}
public IActionResult AccountSettings()
{
string username = User.Identity.Name;
User user = UserHelper.GetUser(_dbContext, username);
if (user != null)
{
ViewBag.Title = "Account Settings - " + _config.Title;
ViewBag.Description = "Your " + _config.Title + " Account Settings";
AccountSettingsViewModel model = new AccountSettingsViewModel();
model.Page = "Account";
model.UserID = user.UserId;
model.Username = user.Username;
return View("/Areas/User/Views/User/Settings/AccountSettings.cshtml", model);
}
return new StatusCodeResult(StatusCodes.Status403Forbidden);
}
public async Task<IActionResult> SecuritySettings()
{
string username = User.Identity.Name;
User user = UserHelper.GetUser(_dbContext, username);
if (user != null)
{
ViewBag.Title = "Security Settings - " + _config.Title;
ViewBag.Description = "Your " + _config.Title + " Security Settings";
SecuritySettingsViewModel model = new SecuritySettingsViewModel();
model.Page = "Security";
model.UserID = user.UserId;
model.Username = user.Username;
// Get the user secure info
IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, user.Username);
//model.TrustedDeviceCount = user.TrustedDevices.Count;
//model.AuthTokens = new List<AuthTokenViewModel>();
//foreach (AuthToken token in user.AuthTokens)
//{
// AuthTokenViewModel tokenModel = new AuthTokenViewModel();
// tokenModel.AuthTokenId = token.AuthTokenId;
// tokenModel.Name = token.Name;
// tokenModel.LastDateUsed = token.LastDateUsed;
// model.AuthTokens.Add(tokenModel);
//}
model.PgpPublicKey = userInfo.PGPPublicKey;
model.RecoveryEmail = userInfo.RecoveryEmail;
if (userInfo.RecoveryVerified.HasValue)
model.RecoveryVerified = userInfo.RecoveryVerified.Value;
if (userInfo.TwoFactorEnabled.HasValue)
model.TwoFactorEnabled = userInfo.TwoFactorEnabled.Value;
return View("/Areas/User/Views/User/Settings/SecuritySettings.cshtml", model);
}
return new StatusCodeResult(StatusCodes.Status403Forbidden);
}
public async Task<IActionResult> DeveloperSettings()
{
string username = User.Identity.Name;
User user = UserHelper.GetUser(_dbContext, username);
if (user != null)
{
ViewBag.Title = "Developer Settings - " + _config.Title;
ViewBag.Description = "Your " + _config.Title + " Developer Settings";
DeveloperSettingsViewModel model = new DeveloperSettingsViewModel();
model.Page = "Developer";
model.UserID = user.UserId;
model.Username = user.Username;
model.AuthTokens = new List<AuthTokenViewModel>();
model.Clients = new List<ClientViewModel>();
//foreach (AuthToken token in user.AuthTokens)
//{
// AuthTokenViewModel tokenModel = new AuthTokenViewModel();
// tokenModel.AuthTokenId = token.AuthTokenId;
// tokenModel.Name = token.Name;
// tokenModel.LastDateUsed = token.LastDateUsed;
// model.AuthTokens.Add(tokenModel);
//}
Client[] clients = await IdentityHelper.GetClients(_config, username);
foreach (Client client in clients)
{
model.Clients.Add(new ClientViewModel()
{
Id = client.ClientId,
Name = client.ClientName,
RedirectURI = string.Join(',', client.RedirectUris),
PostLogoutRedirectURI = string.Join(',', client.PostLogoutRedirectUris),
AllowedScopes = client.AllowedScopes
});
}
return View("/Areas/User/Views/User/Settings/DeveloperSettings.cshtml", model);
}
return new StatusCodeResult(StatusCodes.Status403Forbidden);
}
public IActionResult InviteSettings()
{
string username = User.Identity.Name;
User user = UserHelper.GetUser(_dbContext, username);
if (user != null)
{
ViewBag.Title = "Invite Settings - " + _config.Title;
ViewBag.Description = "Your " + _config.Title + " Invite Settings";
InviteSettingsViewModel model = new InviteSettingsViewModel();
model.Page = "Invite";
model.UserID = user.UserId;
model.Username = user.Username;
List<InviteCodeViewModel> availableCodes = new List<InviteCodeViewModel>();
List<InviteCodeViewModel> claimedCodes = new List<InviteCodeViewModel>();
if (user.OwnedInviteCodes != null)
{
foreach (InviteCode inviteCode in user.OwnedInviteCodes.Where(c => c.Active))
{
InviteCodeViewModel inviteCodeViewModel = new InviteCodeViewModel();
inviteCodeViewModel.ClaimedUser = inviteCode.ClaimedUser;
inviteCodeViewModel.Active = inviteCode.Active;
inviteCodeViewModel.Code = inviteCode.Code;
inviteCodeViewModel.InviteCodeId = inviteCode.InviteCodeId;
inviteCodeViewModel.Owner = inviteCode.Owner;
if (inviteCode.ClaimedUser == null)
availableCodes.Add(inviteCodeViewModel);
if (inviteCode.ClaimedUser != null)
claimedCodes.Add(inviteCodeViewModel);
}
}
model.AvailableCodes = availableCodes;
model.ClaimedCodes = claimedCodes;
return View("/Areas/User/Views/User/Settings/InviteSettings.cshtml", model);
}
return new StatusCodeResult(StatusCodes.Status403Forbidden);
}
public IActionResult BlogSettings()
{
string username = User.Identity.Name;
User user = UserHelper.GetUser(_dbContext, username);
if (user != null)
{
ViewBag.Title = "Blog Settings - " + _config.Title;
ViewBag.Description = "Your " + _config.Title + " Blog Settings";
BlogSettingsViewModel model = new BlogSettingsViewModel();
model.Page = "Blog";
model.UserID = user.UserId;
model.Username = user.Username;
model.Title = user.BlogSettings.Title;
model.Description = user.BlogSettings.Description;
return View("/Areas/User/Views/User/Settings/BlogSettings.cshtml", model);
}
return new StatusCodeResult(StatusCodes.Status403Forbidden);
}
public IActionResult UploadSettings()
{
string username = User.Identity.Name;
User user = UserHelper.GetUser(_dbContext, username);
if (user != null)
{
ViewBag.Title = "Upload Settings - " + _config.Title;
ViewBag.Description = "Your " + _config.Title + " Upload Settings";
UploadSettingsViewModel model = new UploadSettingsViewModel();
model.Page = "Upload";
model.UserID = user.UserId;
model.Username = user.Username;
model.Encrypt = user.UploadSettings.Encrypt;
return View("/Areas/User/Views/User/Settings/UploadSettings.cshtml", model);
}
return new StatusCodeResult(StatusCodes.Status403Forbidden);
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ViewRawPGP(string username)
{
ViewBag.Title = username + "'s Public Key - " + _config.Title;
ViewBag.Description = "The PGP public key for " + username;
IdentityUserInfo userClaims = await IdentityHelper.GetIdentityUserInfo(_config, username);
if (!string.IsNullOrEmpty(userClaims.PGPPublicKey))
{
return Content(userClaims.PGPPublicKey, "text/plain");
}
return new StatusCodeResult(StatusCodes.Status404NotFound);
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult EditBlog(BlogSettingsViewModel settings)
{
if (ModelState.IsValid)
{
try
{
User user = UserHelper.GetUser(_dbContext, User.Identity.Name);
if (user != null)
{
// Blogs
user.BlogSettings.Title = settings.Title;
user.BlogSettings.Description = settings.Description;
UserHelper.EditAccount(_dbContext, _config, user);
return Json(new { result = true });
}
return Json(new { error = "User does not exist" });
}
catch (Exception ex)
{
return Json(new { error = ex.GetFullMessage(true) });
}
}
return Json(new { error = "Invalid Parameters" });
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult EditProfile(ProfileSettingsViewModel settings)
{
if (ModelState.IsValid)
{
try
{
User user = UserHelper.GetUser(_dbContext, User.Identity.Name);
if (user != null)
{
// Profile Info
user.UserSettings.Website = settings.Website;
user.UserSettings.Quote = settings.Quote;
user.UserSettings.About = settings.About;
UserHelper.EditAccount(_dbContext, _config, user);
return Json(new { result = true });
}
return Json(new { error = "User does not exist" });
}
catch (Exception ex)
{
return Json(new { error = ex.GetFullMessage(true) });
}
}
return Json(new { error = "Invalid Parameters" });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditSecurity(SecuritySettingsViewModel settings)
{
if (ModelState.IsValid)
{
try
{
User user = UserHelper.GetUser(_dbContext, User.Identity.Name);
if (user != null)
{
// PGP Key valid?
if (!string.IsNullOrEmpty(settings.PgpPublicKey) && !PGP.IsPublicKey(settings.PgpPublicKey))
{
return Json(new { error = "Invalid PGP Public Key" });
}
// Get the user secure info
IdentityUserInfo userInfo = await IdentityHelper.GetIdentityUserInfo(_config, user.Username);
if (userInfo.PGPPublicKey != settings.PgpPublicKey)
{
var result = await IdentityHelper.UpdatePGPPublicKey(_config, user.Username, settings.PgpPublicKey);
if (!result.Success)
return Json(new { error = result.Message });
}
if (userInfo.RecoveryEmail != settings.RecoveryEmail)
{
var token = await IdentityHelper.UpdateRecoveryEmail(_config, user.Username, settings.RecoveryEmail);
// If they have a recovery email, let's send a verification
if (!string.IsNullOrEmpty(settings.RecoveryEmail))
{
string resetUrl = Url.SubRouteUrl("user", "User.ResetPassword", new { Username = user.Username });
string verifyUrl = Url.SubRouteUrl("user", "User.VerifyRecoveryEmail", new { Username = user.Username, Code = WebUtility.UrlEncode(token) });
UserHelper.SendRecoveryEmailVerification(_config, user.Username, settings.RecoveryEmail, resetUrl, verifyUrl);
}
}
//if (!settings.TwoFactorEnabled && (!userInfo.TwoFactorEnabled.HasValue || userInfo.TwoFactorEnabled.Value))
//{
// var result = await IdentityHelper.Disable2FA(_config, user.Username);
// if (!result.Success)
// return Json(new { error = result.Message });
//}
//UserHelper.EditAccount(_dbContext, _config, user, changePass, settings.NewPassword);
//if (!oldTwoFactor && settings.TwoFactorEnabled)
//{
// return Json(new { result = new { checkAuth = true, key = newKey, qrUrl = Url.SubRouteUrl("user", "User.Action", new { action = "GenerateAuthQrCode", key = newKey }) } });
//}
return Json(new { result = true });
}
return Json(new { error = "User does not exist" });
}
catch (Exception ex)
{
return Json(new { error = ex.GetFullMessage(true) });
}
}
return Json(new { error = "Invalid Parameters" });
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult EditUpload(UploadSettingsViewModel settings)
{
if (ModelState.IsValid)
{
try
{
User user = UserHelper.GetUser(_dbContext, User.Identity.Name);
if (user != null)
{
// Profile Info
user.UploadSettings.Encrypt = settings.Encrypt;
UserHelper.EditAccount(_dbContext, _config, user);
return Json(new { result = true });
}
return Json(new { error = "User does not exist" });
}
catch (Exception ex)
{
return Json(new { error = ex.GetFullMessage(true) });
}
}
return Json(new { error = "Invalid Parameters" });
}
public async Task<IActionResult> ChangePassword(AccountSettingsViewModel settings)
{
if (ModelState.IsValid)
{
try
{
User user = UserHelper.GetUser(_dbContext, User.Identity.Name);
if (user != null)
{
// Did they enter their old password
if (string.IsNullOrEmpty(settings.CurrentPassword))
return Json(new { error = "You must enter your current password" });
// Did they enter a new password
if (string.IsNullOrEmpty(settings.NewPassword) || string.IsNullOrEmpty(settings.NewPasswordConfirm))
return Json(new { error = "You must enter your new password" });
// Old Password Valid?
if (!(await UserHelper.UserPasswordCorrect(_config, user.Username, settings.CurrentPassword)))
return Json(new { error = "Invalid Original Password" });
// The New Password Match?
if (settings.NewPassword != settings.NewPasswordConfirm)
return Json(new { error = "New Password must match confirmation" });
// Are password resets enabled?
if (!_config.UserConfig.PasswordResetEnabled)
return Json(new { error = "Password resets are disabled" });
// Change their password
await UserHelper.ChangeAccountPassword(_dbContext, _config, user.Username, settings.CurrentPassword, settings.NewPassword);
return Json(new { result = true });
}
return Json(new { error = "User does not exist" });
}
catch (Exception ex)