Initial commit
[wsti_pai.git] / Projects / Controllers / ManageController.cs
1 using System;
2 using System.Linq;
3 using System.Threading.Tasks;
4 using System.Web;
5 using System.Web.Mvc;
6 using Microsoft.AspNet.Identity;
7 using Microsoft.AspNet.Identity.Owin;
8 using Microsoft.Owin.Security;
9 using Projects.Models;
10
11 namespace Projects.Controllers
12 {
13     [Authorize]
14     public class ManageController : Controller
15     {
16         private ApplicationSignInManager _signInManager;
17         private ApplicationUserManager _userManager;
18
19         public ManageController()
20         {
21         }
22
23         public ManageController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
24         {
25             UserManager = userManager;
26             SignInManager = signInManager;
27         }
28
29         public ApplicationSignInManager SignInManager
30         {
31             get
32             {
33                 return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
34             }
35             private set 
36             { 
37                 _signInManager = value; 
38             }
39         }
40
41         public ApplicationUserManager UserManager
42         {
43             get
44             {
45                 return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
46             }
47             private set
48             {
49                 _userManager = value;
50             }
51         }
52
53         //
54         // GET: /Manage/Index
55         public async Task<ActionResult> Index(ManageMessageId? message)
56         {
57             ViewBag.StatusMessage =
58                 message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
59                 : message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
60                 : message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set."
61                 : message == ManageMessageId.Error ? "An error has occurred."
62                 : message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added."
63                 : message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
64                 : "";
65
66             var userId = User.Identity.GetUserId();
67             var model = new IndexViewModel
68             {
69                 HasPassword = HasPassword(),
70                 PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
71                 TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
72                 Logins = await UserManager.GetLoginsAsync(userId),
73                 BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId)
74             };
75             return View(model);
76         }
77
78         //
79         // POST: /Manage/RemoveLogin
80         [HttpPost]
81         [ValidateAntiForgeryToken]
82         public async Task<ActionResult> RemoveLogin(string loginProvider, string providerKey)
83         {
84             ManageMessageId? message;
85             var result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey));
86             if (result.Succeeded)
87             {
88                 var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
89                 if (user != null)
90                 {
91                     await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
92                 }
93                 message = ManageMessageId.RemoveLoginSuccess;
94             }
95             else
96             {
97                 message = ManageMessageId.Error;
98             }
99             return RedirectToAction("ManageLogins", new { Message = message });
100         }
101
102         //
103         // GET: /Manage/AddPhoneNumber
104         public ActionResult AddPhoneNumber()
105         {
106             return View();
107         }
108
109         //
110         // POST: /Manage/AddPhoneNumber
111         [HttpPost]
112         [ValidateAntiForgeryToken]
113         public async Task<ActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
114         {
115             if (!ModelState.IsValid)
116             {
117                 return View(model);
118             }
119             // Generate the token and send it
120             var code = await UserManager.GenerateChangePhoneNumberTokenAsync(User.Identity.GetUserId(), model.Number);
121             if (UserManager.SmsService != null)
122             {
123                 var message = new IdentityMessage
124                 {
125                     Destination = model.Number,
126                     Body = "Your security code is: " + code
127                 };
128                 await UserManager.SmsService.SendAsync(message);
129             }
130             return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
131         }
132
133         //
134         // POST: /Manage/EnableTwoFactorAuthentication
135         [HttpPost]
136         [ValidateAntiForgeryToken]
137         public async Task<ActionResult> EnableTwoFactorAuthentication()
138         {
139             await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true);
140             var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
141             if (user != null)
142             {
143                 await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
144             }
145             return RedirectToAction("Index", "Manage");
146         }
147
148         //
149         // POST: /Manage/DisableTwoFactorAuthentication
150         [HttpPost]
151         [ValidateAntiForgeryToken]
152         public async Task<ActionResult> DisableTwoFactorAuthentication()
153         {
154             await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), false);
155             var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
156             if (user != null)
157             {
158                 await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
159             }
160             return RedirectToAction("Index", "Manage");
161         }
162
163         //
164         // GET: /Manage/VerifyPhoneNumber
165         public async Task<ActionResult> VerifyPhoneNumber(string phoneNumber)
166         {
167             var code = await UserManager.GenerateChangePhoneNumberTokenAsync(User.Identity.GetUserId(), phoneNumber);
168             // Send an SMS through the SMS provider to verify the phone number
169             return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
170         }
171
172         //
173         // POST: /Manage/VerifyPhoneNumber
174         [HttpPost]
175         [ValidateAntiForgeryToken]
176         public async Task<ActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model)
177         {
178             if (!ModelState.IsValid)
179             {
180                 return View(model);
181             }
182             var result = await UserManager.ChangePhoneNumberAsync(User.Identity.GetUserId(), model.PhoneNumber, model.Code);
183             if (result.Succeeded)
184             {
185                 var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
186                 if (user != null)
187                 {
188                     await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
189                 }
190                 return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess });
191             }
192             // If we got this far, something failed, redisplay form
193             ModelState.AddModelError("", "Failed to verify phone");
194             return View(model);
195         }
196
197         //
198         // GET: /Manage/RemovePhoneNumber
199         public async Task<ActionResult> RemovePhoneNumber()
200         {
201             var result = await UserManager.SetPhoneNumberAsync(User.Identity.GetUserId(), null);
202             if (!result.Succeeded)
203             {
204                 return RedirectToAction("Index", new { Message = ManageMessageId.Error });
205             }
206             var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
207             if (user != null)
208             {
209                 await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
210             }
211             return RedirectToAction("Index", new { Message = ManageMessageId.RemovePhoneSuccess });
212         }
213
214         //
215         // GET: /Manage/ChangePassword
216         public ActionResult ChangePassword()
217         {
218             return View();
219         }
220
221         //
222         // POST: /Manage/ChangePassword
223         [HttpPost]
224         [ValidateAntiForgeryToken]
225         public async Task<ActionResult> ChangePassword(ChangePasswordViewModel model)
226         {
227             if (!ModelState.IsValid)
228             {
229                 return View(model);
230             }
231             var result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
232             if (result.Succeeded)
233             {
234                 var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
235                 if (user != null)
236                 {
237                     await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
238                 }
239                 return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess });
240             }
241             AddErrors(result);
242             return View(model);
243         }
244
245         //
246         // GET: /Manage/SetPassword
247         public ActionResult SetPassword()
248         {
249             return View();
250         }
251
252         //
253         // POST: /Manage/SetPassword
254         [HttpPost]
255         [ValidateAntiForgeryToken]
256         public async Task<ActionResult> SetPassword(SetPasswordViewModel model)
257         {
258             if (ModelState.IsValid)
259             {
260                 var result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
261                 if (result.Succeeded)
262                 {
263                     var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
264                     if (user != null)
265                     {
266                         await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
267                     }
268                     return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess });
269                 }
270                 AddErrors(result);
271             }
272
273             // If we got this far, something failed, redisplay form
274             return View(model);
275         }
276
277         //
278         // GET: /Manage/ManageLogins
279         public async Task<ActionResult> ManageLogins(ManageMessageId? message)
280         {
281             ViewBag.StatusMessage =
282                 message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
283                 : message == ManageMessageId.Error ? "An error has occurred."
284                 : "";
285             var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
286             if (user == null)
287             {
288                 return View("Error");
289             }
290             var userLogins = await UserManager.GetLoginsAsync(User.Identity.GetUserId());
291             var otherLogins = AuthenticationManager.GetExternalAuthenticationTypes().Where(auth => userLogins.All(ul => auth.AuthenticationType != ul.LoginProvider)).ToList();
292             ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1;
293             return View(new ManageLoginsViewModel
294             {
295                 CurrentLogins = userLogins,
296                 OtherLogins = otherLogins
297             });
298         }
299
300         //
301         // POST: /Manage/LinkLogin
302         [HttpPost]
303         [ValidateAntiForgeryToken]
304         public ActionResult LinkLogin(string provider)
305         {
306             // Request a redirect to the external login provider to link a login for the current user
307             return new AccountController.ChallengeResult(provider, Url.Action("LinkLoginCallback", "Manage"), User.Identity.GetUserId());
308         }
309
310         //
311         // GET: /Manage/LinkLoginCallback
312         public async Task<ActionResult> LinkLoginCallback()
313         {
314             var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
315             if (loginInfo == null)
316             {
317                 return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
318             }
319             var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
320             return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
321         }
322
323         protected override void Dispose(bool disposing)
324         {
325             if (disposing && _userManager != null)
326             {
327                 _userManager.Dispose();
328                 _userManager = null;
329             }
330
331             base.Dispose(disposing);
332         }
333
334 #region Helpers
335         // Used for XSRF protection when adding external logins
336         private const string XsrfKey = "XsrfId";
337
338         private IAuthenticationManager AuthenticationManager
339         {
340             get
341             {
342                 return HttpContext.GetOwinContext().Authentication;
343             }
344         }
345
346         private void AddErrors(IdentityResult result)
347         {
348             foreach (var error in result.Errors)
349             {
350                 ModelState.AddModelError("", error);
351             }
352         }
353
354         private bool HasPassword()
355         {
356             var user = UserManager.FindById(User.Identity.GetUserId());
357             if (user != null)
358             {
359                 return user.PasswordHash != null;
360             }
361             return false;
362         }
363
364         private bool HasPhoneNumber()
365         {
366             var user = UserManager.FindById(User.Identity.GetUserId());
367             if (user != null)
368             {
369                 return user.PhoneNumber != null;
370             }
371             return false;
372         }
373
374         public enum ManageMessageId
375         {
376             AddPhoneSuccess,
377             ChangePasswordSuccess,
378             SetTwoFactorSuccess,
379             SetPasswordSuccess,
380             RemoveLoginSuccess,
381             RemovePhoneSuccess,
382             Error
383         }
384
385 #endregion
386     }
387 }