Added user activity list
[wsti_pai.git] / Projects / Controllers / AccountController.cs
1 using System;
2 using System.Globalization;
3 using System.Linq;
4 using System.Security.Claims;
5 using System.Threading.Tasks;
6 using System.Web;
7 using System.Web.Mvc;
8 using Microsoft.AspNet.Identity;
9 using Microsoft.AspNet.Identity.Owin;
10 using Microsoft.Owin.Security;
11 using Projects.Models;
12
13 namespace Projects.Controllers
14 {
15     [Authorize]
16     public class AccountController : Controller
17     {
18         private ApplicationSignInManager _signInManager;
19         private ApplicationUserManager _userManager;
20
21         public AccountController()
22         {
23         }
24
25         public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
26         {
27             UserManager = userManager;
28             SignInManager = signInManager;
29         }
30
31         public ApplicationSignInManager SignInManager
32         {
33             get
34             {
35                 return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
36             }
37             private set 
38             { 
39                 _signInManager = value; 
40             }
41         }
42
43         public ApplicationUserManager UserManager
44         {
45             get
46             {
47                 return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
48             }
49             private set
50             {
51                 _userManager = value;
52             }
53         }
54
55         //
56         // GET: /Account/Login
57         [AllowAnonymous]
58         public ActionResult Login(string returnUrl)
59         {
60             ViewBag.ReturnUrl = returnUrl;
61             return View();
62         }
63
64         //
65         // POST: /Account/Login
66         [HttpPost]
67         [AllowAnonymous]
68         [ValidateAntiForgeryToken]
69         public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
70         {
71             if (!ModelState.IsValid)
72             {
73                 return View(model);
74             }
75
76             // This doesn't count login failures towards account lockout
77             // To enable password failures to trigger account lockout, change to shouldLockout: true
78             var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
79             switch (result)
80             {
81                 case SignInStatus.Success:
82                     return RedirectToLocal(returnUrl);
83                 case SignInStatus.LockedOut:
84                     return View("Lockout");
85                 case SignInStatus.RequiresVerification:
86                     return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
87                 case SignInStatus.Failure:
88                 default:
89                     ModelState.AddModelError("", "Invalid login attempt.");
90                     return View(model);
91             }
92         }
93
94         //
95         // GET: /Account/VerifyCode
96         [AllowAnonymous]
97         public async Task<ActionResult> VerifyCode(string provider, string returnUrl, bool rememberMe)
98         {
99             // Require that the user has already logged in via username/password or external login
100             if (!await SignInManager.HasBeenVerifiedAsync())
101             {
102                 return View("Error");
103             }
104             return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
105         }
106
107         //
108         // POST: /Account/VerifyCode
109         [HttpPost]
110         [AllowAnonymous]
111         [ValidateAntiForgeryToken]
112         public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model)
113         {
114             if (!ModelState.IsValid)
115             {
116                 return View(model);
117             }
118
119             // The following code protects for brute force attacks against the two factor codes. 
120             // If a user enters incorrect codes for a specified amount of time then the user account 
121             // will be locked out for a specified amount of time. 
122             // You can configure the account lockout settings in IdentityConfig
123             var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent:  model.RememberMe, rememberBrowser: model.RememberBrowser);
124             switch (result)
125             {
126                 case SignInStatus.Success:
127                     return RedirectToLocal(model.ReturnUrl);
128                 case SignInStatus.LockedOut:
129                     return View("Lockout");
130                 case SignInStatus.Failure:
131                 default:
132                     ModelState.AddModelError("", "Invalid code.");
133                     return View(model);
134             }
135         }
136
137         //
138         // GET: /Account/Register
139         [AllowAnonymous]
140         public ActionResult Register()
141         {
142             return View();
143         }
144
145         //
146         // POST: /Account/Register
147         [HttpPost]
148         [AllowAnonymous]
149         [ValidateAntiForgeryToken]
150         public async Task<ActionResult> Register(RegisterViewModel model)
151         {
152             if (ModelState.IsValid)
153             {
154                 var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
155                 var result = await UserManager.CreateAsync(user, model.Password);
156                 if (result.Succeeded)
157                 {
158                     await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
159                     
160                     // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
161                     // Send an email with this link
162                     // string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
163                     // var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
164                     // await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");
165
166                     return RedirectToAction("Index", "Home");
167                 }
168                 AddErrors(result);
169             }
170
171             // If we got this far, something failed, redisplay form
172             return View(model);
173         }
174
175         //
176         // GET: /Account/ConfirmEmail
177         [AllowAnonymous]
178         public async Task<ActionResult> ConfirmEmail(string userId, string code)
179         {
180             if (userId == null || code == null)
181             {
182                 return View("Error");
183             }
184             var result = await UserManager.ConfirmEmailAsync(userId, code);
185             return View(result.Succeeded ? "ConfirmEmail" : "Error");
186         }
187
188         //
189         // GET: /Account/ForgotPassword
190         [AllowAnonymous]
191         public ActionResult ForgotPassword()
192         {
193             return View();
194         }
195
196         //
197         // POST: /Account/ForgotPassword
198         [HttpPost]
199         [AllowAnonymous]
200         [ValidateAntiForgeryToken]
201         public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
202         {
203             if (ModelState.IsValid)
204             {
205                 var user = await UserManager.FindByNameAsync(model.Email);
206                 if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
207                 {
208                     // Don't reveal that the user does not exist or is not confirmed
209                     return View("ForgotPasswordConfirmation");
210                 }
211
212                 // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
213                 // Send an email with this link
214                 // string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
215                 // var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);               
216                 // await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>");
217                 // return RedirectToAction("ForgotPasswordConfirmation", "Account");
218             }
219
220             // If we got this far, something failed, redisplay form
221             return View(model);
222         }
223
224         //
225         // GET: /Account/ForgotPasswordConfirmation
226         [AllowAnonymous]
227         public ActionResult ForgotPasswordConfirmation()
228         {
229             return View();
230         }
231
232         //
233         // GET: /Account/ResetPassword
234         [AllowAnonymous]
235         public ActionResult ResetPassword(string code)
236         {
237             return code == null ? View("Error") : View();
238         }
239
240         //
241         // POST: /Account/ResetPassword
242         [HttpPost]
243         [AllowAnonymous]
244         [ValidateAntiForgeryToken]
245         public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
246         {
247             if (!ModelState.IsValid)
248             {
249                 return View(model);
250             }
251             var user = await UserManager.FindByNameAsync(model.Email);
252             if (user == null)
253             {
254                 // Don't reveal that the user does not exist
255                 return RedirectToAction("ResetPasswordConfirmation", "Account");
256             }
257             var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
258             if (result.Succeeded)
259             {
260                 return RedirectToAction("ResetPasswordConfirmation", "Account");
261             }
262             AddErrors(result);
263             return View();
264         }
265
266         //
267         // GET: /Account/ResetPasswordConfirmation
268         [AllowAnonymous]
269         public ActionResult ResetPasswordConfirmation()
270         {
271             return View();
272         }
273
274         //
275         // POST: /Account/ExternalLogin
276         [HttpPost]
277         [AllowAnonymous]
278         [ValidateAntiForgeryToken]
279         public ActionResult ExternalLogin(string provider, string returnUrl)
280         {
281             // Request a redirect to the external login provider
282             return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
283         }
284
285         //
286         // GET: /Account/SendCode
287         [AllowAnonymous]
288         public async Task<ActionResult> SendCode(string returnUrl, bool rememberMe)
289         {
290             var userId = await SignInManager.GetVerifiedUserIdAsync();
291             if (userId == null)
292             {
293                 return View("Error");
294             }
295             var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId);
296             var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
297             return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
298         }
299
300         //
301         // POST: /Account/SendCode
302         [HttpPost]
303         [AllowAnonymous]
304         [ValidateAntiForgeryToken]
305         public async Task<ActionResult> SendCode(SendCodeViewModel model)
306         {
307             if (!ModelState.IsValid)
308             {
309                 return View();
310             }
311
312             // Generate the token and send it
313             if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
314             {
315                 return View("Error");
316             }
317             return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
318         }
319
320         //
321         // GET: /Account/ExternalLoginCallback
322         [AllowAnonymous]
323         public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
324         {
325             var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
326             if (loginInfo == null)
327             {
328                 return RedirectToAction("Login");
329             }
330
331             // Sign in the user with this external login provider if the user already has a login
332             var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
333             switch (result)
334             {
335                 case SignInStatus.Success:
336                     return RedirectToLocal(returnUrl);
337                 case SignInStatus.LockedOut:
338                     return View("Lockout");
339                 case SignInStatus.RequiresVerification:
340                     return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
341                 case SignInStatus.Failure:
342                 default:
343                     // If the user does not have an account, then prompt the user to create an account
344                     ViewBag.ReturnUrl = returnUrl;
345                     ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
346                     return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
347             }
348         }
349
350         //
351         // POST: /Account/ExternalLoginConfirmation
352         [HttpPost]
353         [AllowAnonymous]
354         [ValidateAntiForgeryToken]
355         public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
356         {
357             if (User.Identity.IsAuthenticated)
358             {
359                 return RedirectToAction("Index", "Manage");
360             }
361
362             if (ModelState.IsValid)
363             {
364                 // Get the information about the user from the external login provider
365                 var info = await AuthenticationManager.GetExternalLoginInfoAsync();
366                 if (info == null)
367                 {
368                     return View("ExternalLoginFailure");
369                 }
370                 var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
371                 var result = await UserManager.CreateAsync(user);
372                 if (result.Succeeded)
373                 {
374                     result = await UserManager.AddLoginAsync(user.Id, info.Login);
375                     if (result.Succeeded)
376                     {
377                         await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
378                         return RedirectToLocal(returnUrl);
379                     }
380                 }
381                 AddErrors(result);
382             }
383
384             ViewBag.ReturnUrl = returnUrl;
385             return View(model);
386         }
387
388         //
389         // POST: /Account/LogOff
390         [HttpPost]
391         [ValidateAntiForgeryToken]
392         public ActionResult LogOff()
393         {
394             AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
395             return RedirectToAction("Index", "Home");
396         }
397
398         //
399         // GET: /Account/ExternalLoginFailure
400         [AllowAnonymous]
401         public ActionResult ExternalLoginFailure()
402         {
403             return View();
404         }
405
406         protected override void Dispose(bool disposing)
407         {
408             if (disposing)
409             {
410                 if (_userManager != null)
411                 {
412                     _userManager.Dispose();
413                     _userManager = null;
414                 }
415
416                 if (_signInManager != null)
417                 {
418                     _signInManager.Dispose();
419                     _signInManager = null;
420                 }
421             }
422
423             base.Dispose(disposing);
424         }
425
426         #region Helpers
427         // Used for XSRF protection when adding external logins
428         private const string XsrfKey = "XsrfId";
429
430         private IAuthenticationManager AuthenticationManager
431         {
432             get
433             {
434                 return HttpContext.GetOwinContext().Authentication;
435             }
436         }
437
438         private void AddErrors(IdentityResult result)
439         {
440             foreach (var error in result.Errors)
441             {
442                 ModelState.AddModelError("", error);
443             }
444         }
445
446         private ActionResult RedirectToLocal(string returnUrl)
447         {
448             if (Url.IsLocalUrl(returnUrl))
449             {
450                 return Redirect(returnUrl);
451             }
452             return RedirectToAction("Index", "Home");
453         }
454
455         internal class ChallengeResult : HttpUnauthorizedResult
456         {
457             public ChallengeResult(string provider, string redirectUri)
458                 : this(provider, redirectUri, null)
459             {
460             }
461
462             public ChallengeResult(string provider, string redirectUri, string userId)
463             {
464                 LoginProvider = provider;
465                 RedirectUri = redirectUri;
466                 UserId = userId;
467             }
468
469             public string LoginProvider { get; set; }
470             public string RedirectUri { get; set; }
471             public string UserId { get; set; }
472
473             public override void ExecuteResult(ControllerContext context)
474             {
475                 var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
476                 if (UserId != null)
477                 {
478                     properties.Dictionary[XsrfKey] = UserId;
479                 }
480                 context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
481             }
482         }
483         #endregion
484     }
485 }