using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Localization; using PrivaPub.ClientModels; using PrivaPub.ClientModels.Resources; using PrivaPub.ClientModels.User; using PrivaPub.Extensions; using PrivaPub.Models.User; using PrivaPub.Resources; using PrivaPub.Services; using PrivaPub.StaticServices; using System.ComponentModel.DataAnnotations; namespace PrivaPub.Controllers.ClientToServer { [ApiController, Route("clientapi/user")] public class RootUserController : ControllerBase { readonly IRootUsersService UsersService; readonly AuthTokenManager AuthTokenManager; readonly ILogger Logger; readonly IStringLocalizer Localizer; public RootUserController(IRootUsersService usersService, AuthTokenManager authTokenManager, IStringLocalizer localizer, ILogger logger) { UsersService = usersService; AuthTokenManager = authTokenManager; Localizer = localizer; Logger = logger; } #region User endpoints [HttpPost, Route("/clientapi/user/signup"), Authorize(Policy = Policies.IsUser), AllowAnonymous] public async Task SignUp(LoginForm signUpForm) { if (User.Identity?.IsAuthenticated ?? false) return Redirect("/"); var result = new WebResult(); if (!ModelState.IsValid) return BadRequest(result.Invalidate(Localizer["Invalid model."])); try { result = await UsersService.SignUpAsync(signUpForm); if (!result.IsValid) return StatusCode(result.StatusCode, result); (var user, var userSettings) = ((RootUser, ViewAvatarServer))result.Data; var jwtUser = AuthTokenManager.GenerateToken(user, userSettings); Logger.LogInformation( $"{nameof(SignUp)}();IP:[{HttpContext.Connection?.RemoteIpAddress}];\nUser-Agent:[{Request.Headers["User-Agent"]}];\nUserId:[{user.ID}]"); return Ok(jwtUser); } catch (Exception ex) { Logger.LogError(ex, $"{nameof(User)}.{nameof(SignUp)}()"); return BadRequest(result.Invalidate(ex.Message)); } } [HttpPost, Route("/clientapi/user/login"), Authorize(Policy = Policies.IsUser), AllowAnonymous] public async Task Login(LoginForm loginForm) { if (User.Identity?.IsAuthenticated ?? false) return Redirect("/discussions"); var result = new WebResult(); if (!ModelState.IsValid) return BadRequest(result.Invalidate(Localizer["Invalid model."])); try { result = await UsersService.LoginAsync(loginForm); if (!result.IsValid) return StatusCode(result.StatusCode, result); var (user, userSettings) = ((RootUser, ViewAvatarServer))result.Data; var jwtUser = AuthTokenManager.GenerateToken(user, userSettings); Logger.LogInformation( $"{nameof(Login)}();IP:[{HttpContext.Connection?.RemoteIpAddress}];\nUser-Agent:[{Request.Headers["User-Agent"]}];\nUserId:[{user.ID}]"); return Ok(jwtUser); } catch (Exception ex) { Logger.LogError(ex, $"{nameof(User)}.{nameof(Login)}()"); return BadRequest(result.Invalidate(ex.Message)); } } //[HttpPost, Authorize(Policy = Policies.IsUser), AllowAnonymous] //public async Task InvitationSignUp(InvitationLoginForm signUpForm) //{ // if (User.Identity?.IsAuthenticated ?? false) return Redirect("/discussions"); // var result = new WebResult(); // if (!ModelState.IsValid) // return BadRequest(result.Invalidate(Localizer["Invalid model."])); // try // { // result = await DiscussionsService.GetInvitationConfiguration(signUpForm.InvitationCode, includePassword: true); // if (!result.IsValid) // return StatusCode(result.StatusCode, result); // var configuration = result.Data as InvitationConfiguration; // if (configuration.IsPasswordRequired && signUpForm.InvitationPassword != configuration.Password) // { // result.Invalidate(Localizer["Invalid password."], StatusCodes.Status406NotAcceptable); // return StatusCode(result.StatusCode, result); // } // result = await UsersService.SignUpAsync(new LoginForm // { // Username = signUpForm.Username, // Password = signUpForm.Password, // LightThemeIndexColour = signUpForm.ThemeIndexColour, // ThemeIsDarkMode = signUpForm.ThemeIsDarkMode, // InvitationPassword = signUpForm.InvitationPassword // }, signUpForm.InvitationCode, configuration.IsPasswordRequired); // if (!result.IsValid) // return StatusCode(result.StatusCode, result); // (var user, var userSettings) = ((User, ViewUserSettings))result.Data; // var jwtUser = AuthTokenManager.GenerateToken(user, userSettings); // Logger.LogInformation( // $"{nameof(InvitationSignUp)}();IP:[{HttpContext.Connection?.RemoteIpAddress}];\nUser-Agent:[{Request.Headers["User-Agent"]}];\nUserId:[{user.ID}]"); // return Ok(jwtUser); // } // catch (Exception ex) // { // Logger.LogError(ex, $"{nameof(User)}.{nameof(InvitationSignUp)}()"); // return BadRequest(result.Invalidate(ex.Message)); // } //} //[HttpPost, Authorize(Policy = Policies.IsUser), AllowAnonymous] //public async Task InvitationLogin(InvitationLoginForm loginForm) //{ // if (User.Identity?.IsAuthenticated ?? false) return Redirect("/discussions" + loginForm.InvitationCode); // var result = new WebResult(); // if (!ModelState.IsValid) // return BadRequest(result.Invalidate(Localizer["Invalid model."])); // try // { // result = await DiscussionsService.GetInvitationConfiguration(loginForm.InvitationCode, includePassword: true); // if (!result.IsValid) // return StatusCode(result.StatusCode, result); // var configuration = result.Data as InvitationConfiguration; // if (configuration.IsPasswordRequired && loginForm.InvitationPassword != configuration.Password) // { // result.Invalidate(Localizer["Invalid password."], StatusCodes.Status406NotAcceptable); // return StatusCode(result.StatusCode, result); // } // result = await UsersService.LoginAsync(new LoginForm // { // Username = loginForm.Username, // Password = loginForm.Password, // ThemeIsDarkMode = loginForm.ThemeIsDarkMode, // LightThemeIndexColour = loginForm.ThemeIndexColour, // InvitationPassword = loginForm.InvitationPassword // }, loginForm.InvitationCode); // if (!result.IsValid) // return StatusCode(result.StatusCode, result); // var (user, userSettings/*, userCurrentTier*/) = ((User, ViewUserSettings/*, UserCurrentTier*/))result.Data; // var jwtUser = AuthTokenManager.GenerateToken(user, userSettings, 0 // /*userCurrentTier == null ? default : (userCurrentTier.EndPeriod - DateTime.UtcNow).Days*/); // Logger.LogInformation( // $"{nameof(InvitationLogin)}();IP:[{HttpContext.Connection?.RemoteIpAddress}];\nUser-Agent:[{Request.Headers["User-Agent"]}];\nUserId:[{user.ID}]"); // return Ok(jwtUser); // } // catch (Exception ex) // { // Logger.LogError(ex, $"{nameof(User)}.{nameof(InvitationLogin)}()"); // return BadRequest(result.Invalidate(ex.Message)); // } //} [HttpGet, Route("/clientapi/user/logout"), Authorize(Policy = Policies.IsUser)] public IActionResult Logout() { var result = new WebResult(); try { return Ok(); } catch (Exception ex) { Logger.LogError(ex, $"{nameof(User)}.{nameof(Logout)}()"); return BadRequest(result.Invalidate(ex.Message)); } } [HttpPost, Route("/clientapi/user/update"), Authorize(Policy = Policies.IsUser)] public async Task UpdateUser(UserForm userEmailForm) { var result = new WebResult(); if (!ModelState.IsValid) return BadRequest(result.Invalidate(Localizer["Invalid model."])); try { result = await UsersService.UpdateUserAsync(userEmailForm, User.GetUserId()); if (!result.IsValid) return StatusCode(result.StatusCode, result); return Ok(); } catch (Exception ex) { Logger.LogError(ex, $"{nameof(User)}.{nameof(UpdateUser)}()"); return BadRequest(result.Invalidate(ex.Message)); } } [HttpPost, Route("/clientapi/user/update/settings"), Authorize(Policy = Policies.IsUser)] public async Task UpdateUserSettings(ViewAvatarServer userSettings) { var result = new WebResult(); if (!ModelState.IsValid) return BadRequest(result.Invalidate(Localizer["Invalid model."])); try { result = await UsersService.UpdateUserSettingsAsync(userSettings, User.GetUserId()); if (!result.IsValid) return StatusCode(result.StatusCode, result); return Ok(); } catch (Exception ex) { Logger.LogError(ex, $"{nameof(User)}.{nameof(UpdateUserSettings)}()"); return BadRequest(result.Invalidate(ex.Message)); } } [HttpPost, Route("/clientapi/user/update/password"), Authorize(Policy = Policies.IsUser)] public async Task UpdatePassword(UserPasswordForm userPasswordForm) { var result = new WebResult(); if (!ModelState.IsValid) return BadRequest(result.Invalidate(Localizer["Invalid model."])); try { result = await UsersService.UpdateUserPasswordAsync(userPasswordForm, User.GetUserId()); if (!result.IsValid) return StatusCode(result.StatusCode, result); return Ok(); } catch (Exception ex) { Logger.LogError(ex, $"{nameof(User)}.{nameof(UpdatePassword)}()"); return BadRequest(result.Invalidate(ex.Message)); } } //[HttpGet, Authorize(Policy = Policies.IsUser)] //public async Task GetUser() //{ // var result = new WebResult(); // try // { // result = await UsersService.GetUserAsync(User.GetUserId()); // if (!result.IsValid) // return StatusCode(result.StatusCode, result); // return Ok(result.Data); // } // catch (Exception ex) // { // Logger.LogError(ex, $"{nameof(User)}.{nameof(GetUser)}()"); // return BadRequest(result.Invalidate(ex.Message)); // } //} [HttpGet, Route("/clientapi/user/settings"), Authorize(Policy = Policies.IsUser)] public async Task GetUserSettings() { var result = new WebResult(); try { result = await UsersService.GetUserSettingsAsync(User.GetUserId()); if (!result.IsValid) return StatusCode(result.StatusCode, result); return Ok(result.Data); } catch (Exception ex) { Logger.LogError(ex, $"{nameof(User)}.{nameof(GetUserSettings)}()"); return BadRequest(result.Invalidate(ex.Message)); } } [HttpPost, Route("/clientapi/user/recover/password"), AllowAnonymous] public async Task RecoverPassword(PasswordRecoveryForm passwordRecoveryForm) { var result = new WebResult(); if (!ModelState.IsValid) return BadRequest(result.Invalidate(Localizer["Invalid model."])); try { var host = $"{Request.Scheme}://{Request.Host.Host}"; result = await UsersService.SetupAndSendRecoveryEmail(passwordRecoveryForm, host); if (!result.IsValid) return StatusCode(result.StatusCode, result); return Ok(result); } catch (Exception ex) { Logger.LogError(ex, $"{nameof(User)}.{nameof(RecoverPassword)}()"); return BadRequest(result.Invalidate(ex.Message)); } } [HttpPost, Route("/clientapi/user/recover/valid"), AllowAnonymous] public async Task IsValidRecoveryCode( [FromBody, Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ErrorsResource)), Display(Name = "RecoveryCode", ResourceType = typeof(FieldsNameResource))] string recoveryCode) { var result = new WebResult(); try { result = await UsersService.IsValidRecoveryCode(recoveryCode); if (!result.IsValid) return StatusCode(result.StatusCode, result); return Ok(result.Data); } catch (Exception ex) { Logger.LogError(ex, $"{nameof(User)}.{nameof(IsValidRecoveryCode)}()"); return BadRequest(result.Invalidate(ex.Message)); } } [HttpPost, Route("/clientapi/user/recover/update/password"), AllowAnonymous] public async Task ChangePassword(NewPasswordForm newPasswordForm) { var result = new WebResult(); if (!ModelState.IsValid) return BadRequest(result.Invalidate(Localizer["Invalid model."])); try { result = await UsersService.ChangePassword(newPasswordForm); if (!result.IsValid) return StatusCode(result.StatusCode, result); return Ok(result.Data); } catch (Exception ex) { Logger.LogError(ex, $"{nameof(User)}.{nameof(ChangePassword)}()"); return BadRequest(result.Invalidate(ex.Message)); } } //[HttpDelete, Route("delete"), Authorize(Policy = Policies.IsUser)] //public async Task RemoveSelf() //{ // var result = new WebResult(); // try // { // //result = await UsersService.RemoveUserAsync(User.GetUserId()); // if (!result.IsValid) // return StatusCode(result.StatusCode, result); // await HttpContext.SignOutAsync(); // return Ok(); // } // catch (Exception ex) // { // Logger.LogError(ex, $"{nameof(User)}.{nameof(RemoveSelf)}()"); // return BadRequest(result.Invalidate(ex.Message)); // } //} #endregion User endpoints #region Auth refresh [HttpGet, Route("/clientapi/user/sniff/again"), Authorize(Policy = Policies.IsUser)] public async Task SniffAgain() { return Ok(); } #endregion } }