|
|
|
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)
|
|