using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using RSecurityBackend.Models.Auth.Memory; using RSecurityBackend.Models.Auth.ViewModels; using RSecurityBackend.Models.Generic; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; using RSecurityBackend.Services; using Microsoft.AspNetCore.Identity.UI.Services; using RSecurityBackend.Models.Auth.Db; using RSecurityBackend.Models.Image; using System.IO; using Microsoft.Extensions.Configuration; using Audit.WebApi; namespace RSecurityBackend.Controllers { /// /// User login/logout/register/... /// [Produces("application/json")] [Route("api/users")] public abstract class AppUserControllerBase : Controller { /// /// login /// /// loginViewModel /// LoggedOnUserModel [HttpPost] [AllowAnonymous] [Route("login")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(LoggedOnUserModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task Login( [AuditIgnore] [FromBody] LoginViewModel loginViewModel ) { string clientIPAddress = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString(); RServiceResult res = await _appUserService.Login(loginViewModel, clientIPAddress); if(res.Result == null) { return BadRequest(res.ExceptionString); } return Ok(res.Result); } /// /// renew an expired session /// /// user session id /// LoggedOnUserModel [HttpPut] [AllowAnonymous] [Route("relogin/{sessionId}")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(LoggedOnUserModel))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task ReLogin( Guid sessionId ) { string clientIPAddress = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString(); RServiceResult res = await _appUserService.ReLogin(sessionId, clientIPAddress); if (res.Result == null) { return BadRequest(res.ExceptionString); } return Ok(res.Result); } /// /// Logout user (users need user:delothersession to logout other users) /// /// /// User Session Id /// [HttpDelete] [Authorize] [Route("delsession")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task Logout( Guid userId, Guid sessionId ) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); if (loggedOnUserId != userId) { RServiceResult canLogoutAllUsers = await _userPermissionChecker.Check ( loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value), SecurableItem.UserEntityShortName, SecurableItem.DelOtherUserSessionOperationShortName ); if (!string.IsNullOrEmpty(canLogoutAllUsers.ExceptionString)) return BadRequest(canLogoutAllUsers.ExceptionString); if (!canLogoutAllUsers.Result) return Forbid(); } RServiceResult res = await _appUserService.Logout(userId, sessionId); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } return Ok(res.Result); } /// /// Check if my session is valid /// /// [HttpGet] [Authorize] [Route("checkmysession")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task IsSessionValid(Guid sessionId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _appUserService.SessionExists(loggedOnUserId, sessionId); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } return Ok(res.Result); } /// /// All Users Information (if user does not have user:view permission list only contains him/her information) /// /// All Users Information [HttpGet] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task Get() { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult canViewAllUsersInformation = await _userPermissionChecker.Check ( loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value), SecurableItem.UserEntityShortName, SecurableItem.ViewAllOperationShortName ); if (!string.IsNullOrEmpty(canViewAllUsersInformation.ExceptionString)) return BadRequest(canViewAllUsersInformation.ExceptionString); if (canViewAllUsersInformation.Result) { RServiceResult usersInfo = await _appUserService.GetAllUsersInformation(); if (usersInfo.Result == null) { return BadRequest(usersInfo.ExceptionString); } return Ok(usersInfo.Result); } else { RServiceResult userInfo = await _appUserService.GetUserInformation(loggedOnUserId); if (userInfo.Result == null) { if (string.IsNullOrEmpty(userInfo.ExceptionString)) return NotFound(); return BadRequest(userInfo.ExceptionString); } return Ok(new PublicRAppUser[] { userInfo.Result }); } } /// /// returns user information (if user does not have user:view permission trying to view other users' information fails with a forbidden error) /// /// user id /// user information [HttpGet("{id}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(PublicRAppUser))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task Get(Guid id) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); if(loggedOnUserId != id) { RServiceResult canViewAllUsersInformation = await _userPermissionChecker.Check ( loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value), SecurableItem.UserEntityShortName, SecurableItem.ViewAllOperationShortName ); if (!string.IsNullOrEmpty(canViewAllUsersInformation.ExceptionString)) return BadRequest(canViewAllUsersInformation.ExceptionString); if (!canViewAllUsersInformation.Result) return Forbid(); } RServiceResult userInfo = await _appUserService.GetUserInformation(id); if (userInfo.Result == null) { if (string.IsNullOrEmpty(userInfo.ExceptionString)) return NotFound(); return BadRequest(userInfo.ExceptionString); } return Ok(userInfo.Result); } /// /// add a new user (if you are trying to add an admin user you yourself should be admin) /// /// if passsword is sent empty system genrates one for it which could be retrieved from returned record /// id/generated password if required could be retrieved from return value [HttpPost] [Authorize(Policy = SecurableItem.UserEntityShortName + ":" + SecurableItem.AddOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(RegisterRAppUser))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public virtual async Task Post([FromBody]RegisterRAppUser newUserInfo) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); if(newUserInfo.IsAdmin) { RServiceResult isAdmin = await _appUserService.IsAdmin(loggedOnUserId); if (!string.IsNullOrEmpty(isAdmin.ExceptionString)) return BadRequest(isAdmin.ExceptionString); if (!isAdmin.Result) return Forbid();//Only admin users can create admin users } RServiceResult result = await _appUserService.AddUser(newUserInfo); if (result.Result == null) return BadRequest(result.ExceptionString); RegisterRAppUser registerRAppUser = new RegisterRAppUser() { Email = result.Result.Email, Status = result.Result.Status, FirstName = result.Result.FirstName, SureName = result.Result.SureName, Id = result.Result.Id, IsAdmin = newUserInfo.IsAdmin, PhoneNumber = newUserInfo.PhoneNumber, RImageId = newUserInfo.RImageId, Username = newUserInfo.Username }; return Ok(registerRAppUser); } /// /// update existing user (if you are trying to update an admin user you yourself should be admin) (if user does not have user:modify permission trying to modify other users' information fails with a forbidden error) /// /// user id /// existingUserInfo.id could be passed empty and it is ignored completely, if password is sent empty it does not has effect /// true if succeeds [HttpPut("{id}")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public virtual async Task Put(Guid id, [FromBody]RegisterRAppUser existingUserInfo) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult isAdmin = await _appUserService.IsAdmin(loggedOnUserId); if (!string.IsNullOrEmpty(isAdmin.ExceptionString)) return BadRequest(isAdmin.ExceptionString); RServiceResult userInfo = await _appUserService.GetUserInformation(id); if (!isAdmin.Result) { if (!string.IsNullOrEmpty(userInfo.ExceptionString)) return BadRequest(userInfo.ExceptionString); if (existingUserInfo.IsAdmin) return Forbid();//You should be admin to make other users admin RServiceResult isEditingUserAdmin = await _appUserService.IsAdmin(id); if (!string.IsNullOrEmpty(isEditingUserAdmin.ExceptionString)) return BadRequest(isEditingUserAdmin.ExceptionString); if(isEditingUserAdmin.Result) return Forbid();//You can not modify admin users. if (loggedOnUserId != id) { RServiceResult canViewAllUsersInformation = await _userPermissionChecker.Check ( loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value), SecurableItem.UserEntityShortName, SecurableItem.ModifyOperationShortName ); if (!string.IsNullOrEmpty(canViewAllUsersInformation.ExceptionString)) return BadRequest(canViewAllUsersInformation.ExceptionString); if (!canViewAllUsersInformation.Result) return Forbid(); } } if (loggedOnUserId == id && userInfo.Result.Username != existingUserInfo.Username) return BadRequest("You can not change your username!"); if (loggedOnUserId == id && (existingUserInfo.Status != RAppUserStatus.Active)) return BadRequest("You can not disable yourself!"); if (loggedOnUserId == id && !string.IsNullOrEmpty(existingUserInfo.Password)) return BadRequest("Please use setmypassword method to change your own password."); RServiceResult res = await _appUserService.ModifyUser(id, existingUserInfo); if (!res.Result) return BadRequest(res.ExceptionString); return Ok(true); } /// /// set my password /// /// /// [HttpPost] [Authorize] [Route("setmypassword")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public virtual async Task SetMyPassword([AuditIgnore][FromBody]SetPasswordModel model) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _appUserService.ChangePassword(loggedOnUserId, model.OldPassword, model.NewPassword); if (!res.Result) return BadRequest(res.ExceptionString); return Ok(true); } /// /// delete user (only admin users can delete other admin users, a user cannot delete himself/herself) /// /// user id /// true if succeeds [HttpDelete("{id}")] [Authorize(Policy = SecurableItem.UserEntityShortName + ":" + SecurableItem.DeleteOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task Delete(Guid id) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult isAdmin = await _appUserService.IsAdmin(loggedOnUserId); if (!string.IsNullOrEmpty(isAdmin.ExceptionString)) return BadRequest(isAdmin.ExceptionString); if (!isAdmin.Result) { RServiceResult isDeletingUserAdmin = await _appUserService.IsAdmin(id); if (!string.IsNullOrEmpty(isDeletingUserAdmin.ExceptionString)) return BadRequest(isDeletingUserAdmin.ExceptionString); if (isDeletingUserAdmin.Result) return Forbid();//You can not delete admin users. } if(loggedOnUserId == id) { return BadRequest("SOS! Suicide attempt detected!"); } RServiceResult res = await _appUserService.DeleteUser(id); if (!res.Result) { return BadRequest(res.ExceptionString); } return Ok(true); } /// /// Checks if user is admin (if user does not have user:viewall permission list it will be failed for any user id other than himself/herself) /// /// [HttpGet] [Authorize] [Route("isadmin")] [Authorize(Policy = SecurableItem.UserEntityShortName + ":" + SecurableItem.ViewAllOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task IsAdmin(Guid userId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); RServiceResult res = await _appUserService.IsAdmin(userId); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } return Ok(res.Result); } /// /// View User Sessions (user needs user:sessions permission to view other users sessions) /// /// [HttpGet] [Route("sessions")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(IEnumerable))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task GetUserSessions(Guid? userId) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); if (loggedOnUserId != userId) { RServiceResult canViewAllUsersInformation = await _userPermissionChecker.Check ( loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value), SecurableItem.UserEntityShortName, SecurableItem.SessionsOperationShortName ); if (!string.IsNullOrEmpty(canViewAllUsersInformation.ExceptionString)) return BadRequest(canViewAllUsersInformation.ExceptionString); if (!canViewAllUsersInformation.Result) return Forbid(); } RServiceResult sessionsInfo = await _appUserService.GetUserSessions(userId); if (sessionsInfo.Result == null) { return BadRequest(sessionsInfo.ExceptionString); } return Ok(sessionsInfo.Result); } /// /// Set User Image (via FormData, specifying userId as 'id' in formData ) - if Files.count is 0 image would be removed - (users need user:modify to change other users image) /// /// new image id [HttpPost] [Route("image")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] [ProducesResponseType((int)HttpStatusCode.Forbidden, Type = typeof(string))] public async Task SetUserImage() { try { if(!Request.Form.TryGetValue("id", out Microsoft.Extensions.Primitives.StringValues tmp)) { return BadRequest("id is null"); } Guid userId = new Guid(tmp); Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); if (loggedOnUserId != userId) { RServiceResult canViewAllUsersInformation = await _userPermissionChecker.Check ( loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value), SecurableItem.UserEntityShortName, SecurableItem.ModifyOperationShortName ); if (!string.IsNullOrEmpty(canViewAllUsersInformation.ExceptionString)) return BadRequest(canViewAllUsersInformation.ExceptionString); if (!canViewAllUsersInformation.Result) return Forbid(); } RServiceResult res = await _appUserService.SetUserImage(userId, Request.Form.Files); if(!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } if(res.Result == null) { return Ok(""); } return Ok(res.Result.ToString()); } catch(Exception exp) { return BadRequest(exp.ToString()); } } /// /// Get User Image in base 64 /// /// /// [HttpGet] [Authorize]//no specific permission [Route("base64image")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public async Task GetUserImageBase64(Guid id) { try { RServiceResult img = await _appUserService.GetUserImage(id); if (!string.IsNullOrEmpty(img.ExceptionString)) { return BadRequest(img.ExceptionString); } if (img.Result == null) return new ObjectResult(string.Empty); RServiceResult imgPath = _imageFileService.GetImagePath(img.Result); if (!string.IsNullOrEmpty(imgPath.ExceptionString)) return BadRequest(imgPath.ExceptionString); return new ObjectResult(Convert.ToBase64String(System.IO.File.ReadAllBytes(imgPath.Result))); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// Get User Image in base 64 /// /// /// [HttpGet] [Authorize]//no specific permission [Route("image")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(FileStreamResult))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Unauthorized)] public async Task GetUserImage(Guid id) { try { RServiceResult img = await _appUserService.GetUserImage(id); if(!string.IsNullOrEmpty(img.ExceptionString)) { return BadRequest(img.ExceptionString); } if (img.Result == null) return NotFound(); Response.GetTypedHeaders().LastModified = img.Result.LastModified; var requestHeaders = Request.GetTypedHeaders(); if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= img.Result.LastModified) { return StatusCode(StatusCodes.Status304NotModified); } RServiceResult imgPath = _imageFileService.GetImagePath(img.Result); if (!string.IsNullOrEmpty(imgPath.ExceptionString)) return BadRequest(imgPath.ExceptionString); return new FileStreamResult(new FileStream(imgPath.Result, FileMode.Open, FileAccess.Read), img.Result.ContentType); } catch (Exception exp) { return BadRequest(exp.ToString()); } } /// /// returns user roles (if user does not have user:view permission trying to view other users' information fails with a forbidden error) /// /// user id /// user roles [HttpGet("{id}/roles")] [Authorize] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string[]))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task GetUserRoles(Guid id) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); if (loggedOnUserId != id) { RServiceResult canViewAllUsersInformation = await _userPermissionChecker.Check ( loggedOnUserId, new Guid(User.Claims.FirstOrDefault(c => c.Type == "SessionId").Value), SecurableItem.UserEntityShortName, SecurableItem.ViewAllOperationShortName ); if (!string.IsNullOrEmpty(canViewAllUsersInformation.ExceptionString)) return BadRequest(canViewAllUsersInformation.ExceptionString); if (!canViewAllUsersInformation.Result) return Forbid(); } RServiceResult> roles = await _appUserService.GetUserRoles(id); if (!string.IsNullOrEmpty(roles.ExceptionString)) return BadRequest(roles.ExceptionString); return Ok(roles.Result.ToArray()); } /// /// remove user from role /// /// user id /// /// true if succeeds [HttpDelete("{id}/roles/{role}")] [Authorize(Policy = SecurableItem.UserEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task RemoveFromRole(Guid id, string role) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); if(loggedOnUserId == id) { return BadRequest("You cannot modify your own roles."); } RServiceResult isAdmin = await _appUserService.IsAdmin(loggedOnUserId); if (!string.IsNullOrEmpty(isAdmin.ExceptionString)) return BadRequest(isAdmin.ExceptionString); if (!isAdmin.Result) { RServiceResult isDeletingUserAdmin = await _appUserService.IsAdmin(id); if (!string.IsNullOrEmpty(isDeletingUserAdmin.ExceptionString)) return BadRequest(isDeletingUserAdmin.ExceptionString); if (isDeletingUserAdmin.Result) return Forbid();//You can not delete admin users roles. } RServiceResult res = await _appUserService.RemoveFromRole(id, role); if (!res.Result) { return BadRequest(res.ExceptionString); } return Ok(true); } /// /// add user to role /// /// user id /// /// true if succeeds [HttpPost("{id}/roles/{role}")] [Authorize(Policy = SecurableItem.UserEntityShortName + ":" + SecurableItem.ModifyOperationShortName)] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.Forbidden)] public async Task AddToRole(Guid id, string role) { Guid loggedOnUserId = new Guid(User.Claims.FirstOrDefault(c => c.Type == "UserId").Value); if (loggedOnUserId == id) { return BadRequest("You cannot modify your own roles."); } RServiceResult isAdmin = await _appUserService.IsAdmin(loggedOnUserId); if (!string.IsNullOrEmpty(isAdmin.ExceptionString)) return BadRequest(isAdmin.ExceptionString); if (!isAdmin.Result) { RServiceResult isDeletingUserAdmin = await _appUserService.IsAdmin(id); if (!string.IsNullOrEmpty(isDeletingUserAdmin.ExceptionString)) return BadRequest(isDeletingUserAdmin.ExceptionString); if (isDeletingUserAdmin.Result) return Forbid();//You can not delete admin users roles. } RServiceResult res = await _appUserService.AddToRole(id, role); if (!res.Result) { return BadRequest(res.ExceptionString); } return Ok(true); } /// /// get a captcha image for signup or forgot password /// /// captchaimageid - display it using api/rimages/captchaimageid.jpg [HttpGet] [AllowAnonymous] [Route("captchaimage")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(Guid))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task GenerateCaptchImage() { RServiceResult img = await _captchaService.Generate(); if (!string.IsNullOrEmpty(img.ExceptionString)) { return BadRequest(img.ExceptionString); } return Ok(img.Result.Id); } /// /// signup /// /// signUpViewModel /// result [HttpPost] [AllowAnonymous] [Route("signup")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task SignUp( [FromBody] UnverifiedSignUpViewModel signUpViewModel ) { if (!IsSignupEnabled()) return BadRequest("ثبت نام غیرفعال است."); RServiceResult captchaRes = await _captchaService.Evaluate(signUpViewModel.CaptchaImageId, signUpViewModel.CaptchaValue); if (!string.IsNullOrEmpty(captchaRes.ExceptionString)) return BadRequest(captchaRes.ExceptionString); if(!captchaRes.Result) return BadRequest("مقدار تصویر امنیتی درست وارد نشده است."); string clientIPAddress = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString(); RServiceResult res = await _appUserService.SignUp(signUpViewModel.Email, clientIPAddress, signUpViewModel.ClientAppName, signUpViewModel.Language); if (res.Result == null) { return BadRequest(res.ExceptionString); } try { await _emailSender.SendEmailAsync ( signUpViewModel.Email, GetSignUpEmailSubject(res.Result.Secret), GetSignUpEmailHtmlContent(res.Result.Secret) ); } catch(Exception exp) { return BadRequest("Error sending email: " + exp.ToString()); } return Ok(true); } /// /// Sign Up Email Subject /// /// /// subject /// /// protected virtual string GetSignUpEmailSubject(string secretCode) { return $"Ganjoor SignUp Code:{secretCode}"; } /// /// Sign Up Email Html Content /// /// /// html content protected virtual string GetSignUpEmailHtmlContent(string secretCode) { return $"{SignupCallbackUrl}?secret={secretCode}"; } /// /// verify signup / forgot password /// /// /// /// associated secret email [HttpGet] [AllowAnonymous] [Route("verify")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task VerifySignUp(RVerifyQueueType type, string secret) { RServiceResult res = await _appUserService.RetrieveEmailFromQueueSecret(type, secret); if (!string.IsNullOrEmpty(res.ExceptionString)) { return BadRequest(res.ExceptionString); } if (string.IsNullOrWhiteSpace(res.Result)) { return NotFound(); } return Ok(res.Result); } /// /// finalize signup process /// /// /// [HttpPost] [AllowAnonymous] [Route("finalizesignup")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task FinalizeSignUp([AuditIgnore][FromBody]VerifiedSignUpViewModel newUserInfo) { RServiceResult result = await _appUserService.FinalizeSignUp(newUserInfo.Email, newUserInfo.Secret, newUserInfo.Password, newUserInfo.FirstName, newUserInfo.SureName); if (!result.Result) return BadRequest(result.ExceptionString); return Ok(true); } /// /// start forgot password process by email /// /// signUpViewModel /// result [HttpPost] [AllowAnonymous] [Route("forgotpassword")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] public async Task ForgotPassword( [FromBody] UnverifiedSignUpViewModel fpwdViewModel ) { RServiceResult captchaRes = await _captchaService.Evaluate(fpwdViewModel.CaptchaImageId, fpwdViewModel.CaptchaValue); if (!string.IsNullOrEmpty(captchaRes.ExceptionString)) return BadRequest(captchaRes.ExceptionString); if (!captchaRes.Result) return BadRequest("مقدار تصویر امنیتی درست وارد نشده است."); string clientIPAddress = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString(); RServiceResult res = await _appUserService.ForgotPassword(fpwdViewModel.Email, clientIPAddress, fpwdViewModel.ClientAppName, fpwdViewModel.Language); if (res.Result == null) { return BadRequest(res.ExceptionString); } try { await _emailSender.SendEmailAsync ( fpwdViewModel.Email, GetForgotPasswordEmailSubject( res.Result.Secret), GetForgotPasswordEmailHtmlContent(res.Result.Secret) ); } catch (Exception exp) { return BadRequest("Error sending email: " + exp.ToString()); } return Ok(true); } /// /// Forgot Password Email Subject /// /// /// subject /// /// protected virtual string GetForgotPasswordEmailSubject(string secretCode) { return $"Ganjoor Forgot Password Code:{secretCode}"; } /// /// Forgot Password Email Html Content /// /// /// html content protected virtual string GetForgotPasswordEmailHtmlContent(string secretCode) { return $"{ForgotPasswordCallbackUrl}?secret={secretCode}"; } /// /// Is Sign-up enabled? /// /// protected virtual bool IsSignupEnabled() { return false; } /// /// reset password /// /// /// [HttpPost] [AllowAnonymous] [Route("resetpassword")] [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(bool))] [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] [ProducesResponseType((int)HttpStatusCode.NotFound)] public async Task ResetPassword([AuditIgnore][FromBody]ResetPasswordViewModel pwd) { string clientIPAddress = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString(); RServiceResult result = await _appUserService.ResetPassword(pwd.Email, pwd.Secret, pwd.Password, clientIPAddress); if (!result.Result) return BadRequest(result.ExceptionString); return Ok(true); } /// /// signup callback url /// protected string SignupCallbackUrl { get { return $"{Configuration.GetSection("UserSignupClientCallBack")["Url"]}"; } } /// /// forgot password call back url /// protected string ForgotPasswordCallbackUrl { get { return $"{Configuration.GetSection("ForgotPasswordClientCallBack")["Url"]}"; } } /// /// IAppUserService instance /// protected IAppUserService _appUserService; /// /// for client IP resolution /// protected IHttpContextAccessor _httpContextAccessor; /// /// IUserPermissionChecker instance /// protected IUserPermissionChecker _userPermissionChecker; /// /// IEmailSender instance /// protected IEmailSender _emailSender; /// /// Image File Service /// protected readonly IImageFileService _imageFileService; /// /// Captcha service /// protected readonly ICaptchaService _captchaService; /// /// Configuration /// protected IConfiguration Configuration { get; } /// /// constructor /// /// /// /// /// /// /// /// public AppUserControllerBase(IConfiguration configuration, IAppUserService appUserService, IHttpContextAccessor httpContextAccessor, IUserPermissionChecker userPermissionChecker, IEmailSender emailSender, IImageFileService imageFileService, ICaptchaService captchaService) { Configuration = configuration; _appUserService = appUserService; _userPermissionChecker = userPermissionChecker; _httpContextAccessor = httpContextAccessor; _emailSender = emailSender; _imageFileService = imageFileService; _captchaService = captchaService; } } }