AccountController.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. using Abp;
  2. using Abp.AspNetCore.Mvc.Authorization;
  3. using Abp.Configuration;
  4. using Abp.Configuration.Startup;
  5. using Abp.Domain.Uow;
  6. using Abp.Extensions;
  7. using Abp.Notifications;
  8. using Abp.Threading;
  9. using Abp.Timing;
  10. using Abp.UI;
  11. using Abp.Web.Models;
  12. using Abp.Zero.Configuration;
  13. using Microsoft.AspNetCore.Identity;
  14. using Microsoft.AspNetCore.Mvc;
  15. using System.Diagnostics;
  16. using System.Security.Claims;
  17. using Abp.Auditing;
  18. using VberAdmin.Authorization;
  19. using VberAdmin.Authorization.Users;
  20. using VberAdmin.Controllers;
  21. using VberAdmin.Identity;
  22. using VberAdmin.MultiTenancy;
  23. using VberAdmin.Web.Models.Account;
  24. using VberZero.AppService.Authorization;
  25. using VberZero.Authorization;
  26. using VberZero.Authorization.Users;
  27. using VberZero.BaseSystem.MultiTenancy;
  28. using VberZero.BaseSystem.Users;
  29. using VberZero.MultiTenancy;
  30. namespace VberAdmin.Web.Controllers;
  31. [DisableAuditing]
  32. public class AccountController : VberAdminControllerBase
  33. {
  34. private readonly UserManager _userManager;
  35. private readonly TenantManager _tenantManager;
  36. private readonly IMultiTenancyConfig _multiTenancyConfig;
  37. private readonly IUnitOfWorkManager _unitOfWorkManager;
  38. private readonly VzLoginResultTypeHelper _vzLoginResultTypeHelper;
  39. private readonly LogInManager _logInManager;
  40. private readonly SignInManager _signInManager;
  41. private readonly UserRegistrationManager _userRegistrationManager;
  42. private readonly ITenantCache _tenantCache;
  43. private readonly INotificationPublisher _notificationPublisher;
  44. public AccountController(
  45. UserManager userManager,
  46. IMultiTenancyConfig multiTenancyConfig,
  47. TenantManager tenantManager,
  48. IUnitOfWorkManager unitOfWorkManager,
  49. VzLoginResultTypeHelper vzLoginResultTypeHelper,
  50. LogInManager logInManager,
  51. SignInManager signInManager,
  52. UserRegistrationManager userRegistrationManager,
  53. ITenantCache tenantCache,
  54. INotificationPublisher notificationPublisher)
  55. {
  56. _userManager = userManager;
  57. _multiTenancyConfig = multiTenancyConfig;
  58. _tenantManager = tenantManager;
  59. _unitOfWorkManager = unitOfWorkManager;
  60. _vzLoginResultTypeHelper = vzLoginResultTypeHelper;
  61. _logInManager = logInManager;
  62. _signInManager = signInManager;
  63. _userRegistrationManager = userRegistrationManager;
  64. _tenantCache = tenantCache;
  65. _notificationPublisher = notificationPublisher;
  66. }
  67. #region Login / Logout
  68. public ActionResult Login(string userNameOrEmailAddress = "", string returnUrl = "", string successMessage = "")
  69. {
  70. if (string.IsNullOrWhiteSpace(returnUrl))
  71. {
  72. returnUrl = GetAppHomeUrl();
  73. }
  74. return View(new LoginFormViewModel
  75. {
  76. ReturnUrl = returnUrl,
  77. IsMultiTenancyEnabled = _multiTenancyConfig.IsEnabled,
  78. IsSelfRegistrationAllowed = IsSelfRegistrationEnabled(),
  79. MultiTenancySide = AbpSession.MultiTenancySide
  80. });
  81. }
  82. [HttpPost]
  83. [UnitOfWork]
  84. public virtual async Task<JsonResult> Login(LoginViewModel loginModel, string returnUrl = "", string returnUrlHash = "")
  85. {
  86. returnUrl = NormalizeReturnUrl(returnUrl);
  87. if (!string.IsNullOrWhiteSpace(returnUrlHash))
  88. {
  89. returnUrl = returnUrl + returnUrlHash;
  90. }
  91. var tenancyName = await _userManager.GetTenancyNameAsync(loginModel.UsernameOrEmailOrPhone);
  92. var loginResult = await GetLoginResultAsync(loginModel.UsernameOrEmailOrPhone, loginModel.Password, tenancyName);
  93. await _signInManager.SignInAsync(loginResult.Identity, loginModel.RememberMe);
  94. await UnitOfWorkManager.Current.SaveChangesAsync();
  95. return Json(new AjaxResponse { TargetUrl = returnUrl });
  96. }
  97. public async Task<ActionResult> Logout()
  98. {
  99. await _signInManager.SignOutAsync();
  100. return RedirectToAction("Login");
  101. }
  102. private async Task<AbpLoginResult> GetLoginResultAsync(string usernameOrEmailOrPhone, string password, string tenancyName)
  103. {
  104. var loginResult = await _logInManager.LoginAsync(usernameOrEmailOrPhone, password, tenancyName);
  105. switch (loginResult.Result)
  106. {
  107. case VzLoginResultType.Success:
  108. return loginResult;
  109. default:
  110. throw _vzLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(loginResult.Result, usernameOrEmailOrPhone, tenancyName);
  111. }
  112. }
  113. #endregion Login / Logout
  114. #region Register
  115. public ActionResult Register()
  116. {
  117. return RegisterView(new RegisterViewModel());
  118. }
  119. private ActionResult RegisterView(RegisterViewModel model)
  120. {
  121. ViewBag.IsMultiTenancyEnabled = _multiTenancyConfig.IsEnabled;
  122. return View("Register", model);
  123. }
  124. private bool IsSelfRegistrationEnabled()
  125. {
  126. if (!AbpSession.TenantId.HasValue)
  127. {
  128. return false; // No registration enabled for host users!
  129. }
  130. return true;
  131. }
  132. [HttpPost]
  133. [UnitOfWork]
  134. public async Task<ActionResult> Register(RegisterViewModel model)
  135. {
  136. try
  137. {
  138. ExternalLoginInfo externalLoginInfo = null;
  139. if (model.IsExternalLogin)
  140. {
  141. externalLoginInfo = await _signInManager.GetExternalLoginInfoAsync();
  142. if (externalLoginInfo == null)
  143. {
  144. throw new Exception("Can not external login!");
  145. }
  146. model.UserName = model.EmailAddress;
  147. model.Password = VberZero.BaseSystem.Users.User.CreateRandomPassword();
  148. }
  149. else
  150. {
  151. if (model.UserName.IsNullOrEmpty() || model.Password.IsNullOrEmpty())
  152. {
  153. throw new UserFriendlyException(L("FormIsNotValidMessage"));
  154. }
  155. }
  156. var user = await _userRegistrationManager.RegisterAsync(
  157. model.Name,
  158. model.Surname,
  159. model.EmailAddress,
  160. model.UserName,
  161. model.Password,
  162. true // Assumed email address is always confirmed. Change this if you want to implement email confirmation.
  163. );
  164. // Getting tenant-specific settings
  165. var isEmailConfirmationRequiredForLogin = await SettingManager.GetSettingValueAsync<bool>(AbpZeroSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin);
  166. if (model.IsExternalLogin)
  167. {
  168. Debug.Assert(externalLoginInfo != null);
  169. if (string.Equals(externalLoginInfo.Principal.FindFirstValue(ClaimTypes.Email), model.EmailAddress, StringComparison.OrdinalIgnoreCase))
  170. {
  171. user.IsEmailConfirmed = true;
  172. }
  173. user.Logins = new List<UserLogin>
  174. {
  175. new UserLogin
  176. {
  177. LoginProvider = externalLoginInfo.LoginProvider,
  178. ProviderKey = externalLoginInfo.ProviderKey,
  179. TenantId = user.TenantId
  180. }
  181. };
  182. }
  183. await _unitOfWorkManager.Current.SaveChangesAsync();
  184. Debug.Assert(user.TenantId != null);
  185. var tenant = await _tenantManager.GetByIdAsync(user.TenantId.Value);
  186. // Directly login if possible
  187. if (user.IsActive && (user.IsEmailConfirmed || !isEmailConfirmationRequiredForLogin))
  188. {
  189. AbpLoginResult loginResult;
  190. if (externalLoginInfo != null)
  191. {
  192. loginResult = await _logInManager.LoginAsync(externalLoginInfo, tenant.TenancyName);
  193. }
  194. else
  195. {
  196. loginResult = await GetLoginResultAsync(user.UserName, model.Password, tenant.TenancyName);
  197. }
  198. if (loginResult.Result == VzLoginResultType.Success)
  199. {
  200. if (loginResult.Identity != null) await _signInManager.SignInAsync(loginResult.Identity, false);
  201. return Redirect(GetAppHomeUrl());
  202. }
  203. Logger.Warn("New registered user could not be login. This should not be normally. login result: " + loginResult.Result);
  204. }
  205. return View("RegisterResult", new RegisterResultViewModel
  206. {
  207. TenancyName = tenant.TenancyName,
  208. NameAndSurname = user.Name + " " + user.Surname,
  209. UserName = user.UserName,
  210. EmailAddress = user.EmailAddress,
  211. IsEmailConfirmed = user.IsEmailConfirmed,
  212. IsActive = user.IsActive,
  213. IsEmailConfirmationRequiredForLogin = isEmailConfirmationRequiredForLogin
  214. });
  215. }
  216. catch (UserFriendlyException ex)
  217. {
  218. ViewBag.ErrorMessage = ex.Message;
  219. return View("Register", model);
  220. }
  221. }
  222. #endregion Register
  223. #region External Login
  224. [HttpPost]
  225. [ValidateAntiForgeryToken]
  226. public ActionResult ExternalLogin(string provider, string returnUrl)
  227. {
  228. //var redirectUrl =
  229. Url.Action(
  230. "ExternalLoginCallback",
  231. "Account",
  232. new
  233. {
  234. ReturnUrl = returnUrl
  235. });
  236. return Challenge(
  237. // TODO: ...?
  238. // new Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties
  239. // {
  240. // Items = { { "LoginProvider", provider } },
  241. // RedirectUri = redirectUrl
  242. // },
  243. provider
  244. );
  245. }
  246. [UnitOfWork]
  247. public virtual async Task<ActionResult> ExternalLoginCallback(string returnUrl, string remoteError = null)
  248. {
  249. returnUrl = NormalizeReturnUrl(returnUrl);
  250. if (remoteError != null)
  251. {
  252. Logger.Error("Remote Error in ExternalLoginCallback: " + remoteError);
  253. throw new UserFriendlyException(L("CouldNotCompleteLoginOperation"));
  254. }
  255. var externalLoginInfo = await _signInManager.GetExternalLoginInfoAsync();
  256. if (externalLoginInfo == null)
  257. {
  258. Logger.Warn("Could not get information from external login.");
  259. return RedirectToAction(nameof(Login));
  260. }
  261. await _signInManager.SignOutAsync();
  262. var tenancyName = GetTenancyNameOrNull();
  263. var loginResult = await _logInManager.LoginAsync(externalLoginInfo, tenancyName);
  264. switch (loginResult.Result)
  265. {
  266. case VzLoginResultType.Success:
  267. await _signInManager.SignInAsync(loginResult.Identity, false);
  268. return Redirect(returnUrl);
  269. case VzLoginResultType.UnknownExternalLogin:
  270. return await RegisterForExternalLogin(externalLoginInfo);
  271. default:
  272. throw _vzLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(
  273. loginResult.Result,
  274. externalLoginInfo.Principal.FindFirstValue(ClaimTypes.Email) ?? externalLoginInfo.ProviderKey,
  275. tenancyName
  276. );
  277. }
  278. }
  279. private async Task<ActionResult> RegisterForExternalLogin(ExternalLoginInfo externalLoginInfo)
  280. {
  281. var email = externalLoginInfo.Principal.FindFirstValue(ClaimTypes.Email);
  282. var nameinfo = ExternalLoginInfoHelper.GetNameAndSurnameFromClaims(externalLoginInfo.Principal.Claims.ToList());
  283. var viewModel = new RegisterViewModel
  284. {
  285. EmailAddress = email,
  286. Name = nameinfo.name,
  287. Surname = nameinfo.surname,
  288. IsExternalLogin = true,
  289. ExternalLoginAuthSchema = externalLoginInfo.LoginProvider
  290. };
  291. if (nameinfo.name != null &&
  292. nameinfo.surname != null &&
  293. email != null)
  294. {
  295. return await Register(viewModel);
  296. }
  297. return RegisterView(viewModel);
  298. }
  299. [UnitOfWork]
  300. protected virtual async Task<List<Tenant>> FindPossibleTenantsOfUserAsync(UserLoginInfo login)
  301. {
  302. List<User> allUsers;
  303. using (_unitOfWorkManager.Current.DisableFilter(AbpDataFilters.MayHaveTenant))
  304. {
  305. allUsers = await _userManager.FindAllAsync(login);
  306. }
  307. return allUsers
  308. .Where(u => u.TenantId != null)
  309. .Select(u => AsyncHelper.RunSync(() => _tenantManager.FindByIdAsync(u.TenantId.Value)))
  310. .ToList();
  311. }
  312. #endregion External Login
  313. #region Helpers
  314. public ActionResult RedirectToAppHome()
  315. {
  316. return RedirectToAction("Index", "Home");
  317. }
  318. public string GetAppHomeUrl()
  319. {
  320. return Url.Action("Index", "Home");
  321. }
  322. #endregion Helpers
  323. #region Common
  324. private string GetTenancyNameOrNull()
  325. {
  326. if (!AbpSession.TenantId.HasValue)
  327. {
  328. return null;
  329. }
  330. return _tenantCache.GetOrNull(AbpSession.TenantId.Value)?.TenancyName;
  331. }
  332. private string NormalizeReturnUrl(string returnUrl, Func<string> defaultValueBuilder = null)
  333. {
  334. if (defaultValueBuilder == null)
  335. {
  336. defaultValueBuilder = GetAppHomeUrl;
  337. }
  338. if (returnUrl.IsNullOrEmpty())
  339. {
  340. return defaultValueBuilder();
  341. }
  342. if (Url.IsLocalUrl(returnUrl))
  343. {
  344. return returnUrl;
  345. }
  346. return defaultValueBuilder();
  347. }
  348. #endregion Common
  349. #region Etc
  350. /// <summary>
  351. /// This is a demo code to demonstrate sending notification to default tenant admin and host admin uers.
  352. /// Don't use this code in production !!!
  353. /// </summary>
  354. /// <param name="message"></param>
  355. /// <returns></returns>
  356. [AbpMvcAuthorize]
  357. public async Task<ActionResult> TestNotification(string message = "")
  358. {
  359. if (message.IsNullOrEmpty())
  360. {
  361. message = "This is a test notification, created at " + Clock.Now;
  362. }
  363. var defaultTenantAdmin = new UserIdentifier(1, 2);
  364. var hostAdmin = new UserIdentifier(null, 1);
  365. await _notificationPublisher.PublishAsync(
  366. "App.SimpleMessage",
  367. new MessageNotificationData(message),
  368. severity: NotificationSeverity.Info,
  369. userIds: new[] { defaultTenantAdmin, hostAdmin }
  370. );
  371. return Content("Sent notification: " + message);
  372. }
  373. #endregion Etc
  374. }