source: KernelRecordsMVC.Web/Controllers/AccountController.cs@ 1dcdb2c

main
Last change on this file since 1dcdb2c was 1dcdb2c, checked in by mmilevski <markomilevski3@…>, 3 weeks ago

Changed controllers to include transactions, Implemented password hashing.

  • Property mode set to 100644
File size: 10.7 KB
RevLine 
[fa0fbaf]1using System.Security.Claims;
[08aefc6]2using KernelRecordsMVC.Application.ViewModels;
[fa0fbaf]3using KernelRecordsMVC.Infrastructure.Data;
4using KernelRecordsMVC.Models;
5using Microsoft.AspNetCore.Authentication;
6using Microsoft.AspNetCore.Mvc;
7using Microsoft.EntityFrameworkCore;
[1dcdb2c]8using Microsoft.AspNetCore.Identity;
[08aefc6]9
[fa0fbaf]10namespace KernelRecordsMVC.Web.Controllers;
[08aefc6]11
12public class AccountController : Controller
13{
14 private readonly KernelRecordsContext _context;
[1dcdb2c]15 private readonly IPasswordHasher<User> _passwordHasher;
[08aefc6]16
[1dcdb2c]17 public AccountController(KernelRecordsContext context,IPasswordHasher<User> passwordHasher)
[08aefc6]18 {
19 _context = context;
[1dcdb2c]20 _passwordHasher = passwordHasher;
[08aefc6]21 }
22
[fa0fbaf]23
24 // =========================================================
[08aefc6]25 // REGISTER - GET
[fa0fbaf]26 // =========================================================
[08aefc6]27
28 [HttpGet]
29 public IActionResult Register()
30 {
31 return View();
32 }
33
34
[fa0fbaf]35 // =========================================================
[08aefc6]36 // REGISTER - POST
[fa0fbaf]37 // =========================================================
[08aefc6]38
39 [HttpPost]
40 [ValidateAntiForgeryToken]
41 public IActionResult Register(RegisterViewModel model)
42 {
43 if (!ModelState.IsValid)
44 return View(model);
45
46 if (_context.Users.Any(x => x.Username == model.Username))
47 {
48 ModelState.AddModelError(
49 "Username",
50 "Username is already taken.");
51
52 return View(model);
53 }
54
55 if (_context.Users.Any(x => x.Email == model.Email))
56 {
57 ModelState.AddModelError(
58 "Email",
59 "Email is already registered.");
60
61 return View(model);
62 }
63
[1dcdb2c]64 using var transaction =
65 _context.Database.BeginTransaction();
[fa0fbaf]66
[1dcdb2c]67 try
[08aefc6]68 {
[1dcdb2c]69 var user = new User
70 {
71 Email = model.Email,
72 Username = model.Username,
73 DateCreated = DateTime.Today,
74 ShippingAddress = model.ShippingAddress,
75 TelephoneNumber = model.TelephoneNumber
76 };
77
78 user.Password =
79 _passwordHasher.HashPassword(
80 user,
81 model.Password);
82
83 _context.Users.Add(user);
84 _context.SaveChanges();
85
86 var consumer = new Consumer
87 {
88 UserId = user.UserId,
89 PointsCollected = 0
90 };
91
92 _context.Consumers.Add(consumer);
93 _context.SaveChanges();
94
95 transaction.Commit();
96
97 return RedirectToAction(nameof(Login));
98 }
99 catch
100 {
101 transaction.Rollback();
102 throw;
103 }
[08aefc6]104 }
105
106
[fa0fbaf]107 // =========================================================
[08aefc6]108 // LOGIN - GET
[fa0fbaf]109 // =========================================================
[08aefc6]110
111 [HttpGet]
112 public IActionResult Login()
113 {
114 return View();
115 }
116
117
[fa0fbaf]118 // =========================================================
[08aefc6]119 // LOGIN - POST
[fa0fbaf]120 // =========================================================
[08aefc6]121
122 [HttpPost]
123 [ValidateAntiForgeryToken]
[fa0fbaf]124 public async Task<IActionResult> Login(
125 LoginViewModel model)
[08aefc6]126 {
127 if (!ModelState.IsValid)
128 return View(model);
129
130 var user = _context.Users
131 .FirstOrDefault(x =>
[1dcdb2c]132 x.Username == model.Username);
[08aefc6]133
134 if (user == null)
135 {
136 ModelState.AddModelError(
137 "",
138 "Invalid username or password.");
139
140 return View(model);
141 }
142
[1dcdb2c]143 var result =
144 _passwordHasher.VerifyHashedPassword(
145 user,
146 user.Password,
147 model.Password);
148
149 if (result == PasswordVerificationResult.Failed)
150 {
151 ModelState.AddModelError(
152 "",
153 "Invalid username or password.");
154
155 return View(model);
156 }
157
[fa0fbaf]158
159 // =====================================================
160 // DETERMINE ACCOUNT TYPE
161 // =====================================================
162
163 var admin = _context.Admins
164 .FirstOrDefault(x => x.UserId == user.UserId);
165
166 string role;
167
168 if (admin != null)
169 {
170 role = "Admin";
171 }
172 else
173 {
174 role = "Consumer";
175 }
176
177
178 // =====================================================
179 // CREATE CLAIMS
180 // =====================================================
181
182 var claims = new List<Claim>
183 {
184 new Claim(
185 ClaimTypes.NameIdentifier,
186 user.UserId.ToString()),
187
188 new Claim(
189 ClaimTypes.Name,
190 user.Username),
191
192 new Claim(
193 ClaimTypes.Email,
194 user.Email),
195
196 new Claim(
197 ClaimTypes.Role,
198 role)
199 };
200
201
202 if (admin != null)
203 {
204 claims.Add(
205 new Claim(
206 "AdminType",
207 admin.Type.ToString()));
208 }
209
210
211 var identity = new ClaimsIdentity(
212 claims,
213 "Cookies");
214
215 var principal =
216 new ClaimsPrincipal(identity);
217
218
219 // =====================================================
220 // SIGN IN
221 // =====================================================
222
223 await HttpContext.SignInAsync(
224 "Cookies",
225 principal);
226
227
228 // =====================================================
229 // KEEP SESSION FOR EXISTING CODE
230 // =====================================================
231
[08aefc6]232 HttpContext.Session.SetInt32(
233 "UserId",
234 checked((int)user.UserId));
235
236 HttpContext.Session.SetString(
237 "Username",
238 user.Username);
239
[fa0fbaf]240 HttpContext.Session.SetString(
241 "Role",
242 role);
[08aefc6]243
244 if (admin != null)
245 {
246 HttpContext.Session.SetString(
247 "AdminType",
248 admin.Type.ToString());
249 }
[fa0fbaf]250
[08aefc6]251
252 return RedirectToAction(
253 "Index",
254 "Home");
255 }
256
257
[fa0fbaf]258 // =========================================================
259 // PROFILE - GET
260 // =========================================================
261
262 [HttpGet]
263 public IActionResult Profile()
264 {
265 // Get logged-in user's ID from authentication claims.
266 var userIdClaim = User.FindFirst(
267 ClaimTypes.NameIdentifier);
268
269 if (userIdClaim == null)
270 return RedirectToAction(nameof(Login));
271
272 if (!long.TryParse(
273 userIdClaim.Value,
274 out var userId))
275 {
276 return RedirectToAction(nameof(Login));
277 }
278
279
280 // Load user + consumer information.
281 var user = _context.Users
282 .Include(x => x.Consumer)
283 .FirstOrDefault(x =>
284 x.UserId == userId);
285
286 if (user == null)
287 return NotFound();
288
289
290 var model = new ProfileViewModel
291 {
292 UserId = user.UserId,
293
294 Username = user.Username,
295
296 Email = user.Email,
297
298 TelephoneNumber =
299 user.TelephoneNumber,
300
301 ShippingAddress =
302 user.ShippingAddress,
303
304 DateCreated =
305 user.DateCreated,
306
307 PointsCollected =
308 user.Consumer?.PointsCollected ?? 0
309 };
310
311
312 return View(model);
313 }
314
315
316 // =========================================================
317 // PROFILE - POST
318 // =========================================================
319
320 [HttpPost]
321 [ValidateAntiForgeryToken]
322 public async Task<IActionResult> Profile(
323 ProfileViewModel model)
324 {
325 // Get the currently authenticated user.
326 var userIdClaim = User.FindFirst(
327 ClaimTypes.NameIdentifier);
328
329 if (userIdClaim == null)
330 return RedirectToAction(nameof(Login));
331
332 if (!long.TryParse(
333 userIdClaim.Value,
334 out var userId))
335 {
336 return RedirectToAction(nameof(Login));
337 }
338
339
340 if (!ModelState.IsValid)
341 return View(model);
342
343
344 var user = _context.Users
345 .FirstOrDefault(x =>
346 x.UserId == userId);
347
348 if (user == null)
349 return NotFound();
350
351
352 // =====================================================
353 // CHECK EMAIL
354 // =====================================================
355
356 var emailExists = _context.Users.Any(x =>
357 x.Email == model.Email &&
358 x.UserId != userId);
359
360 if (emailExists)
361 {
362 ModelState.AddModelError(
363 nameof(model.Email),
364 "This email is already registered.");
365
366 return View(model);
367 }
368
369
370 // =====================================================
371 // UPDATE USER
372 // =====================================================
373
374 user.Email = model.Email;
375
376 user.TelephoneNumber =
377 model.TelephoneNumber;
378
379 user.ShippingAddress =
380 model.ShippingAddress;
381
382
383 _context.SaveChanges();
384
385
386 TempData["Success"] =
387 "Your profile has been updated successfully.";
388
389
390 // =====================================================
391 // REFRESH EMAIL CLAIM
392 // =====================================================
393
394 var claims = new List<Claim>
395 {
396 new Claim(
397 ClaimTypes.NameIdentifier,
398 user.UserId.ToString()),
399
400 new Claim(
401 ClaimTypes.Name,
402 user.Username),
403
404 new Claim(
405 ClaimTypes.Email,
406 user.Email),
407
408 new Claim(
409 ClaimTypes.Role,
410 User.IsInRole("Admin")
411 ? "Admin"
412 : "Consumer")
413 };
414
415
416 var identity = new ClaimsIdentity(
417 claims,
418 "Cookies");
419
420 var principal =
421 new ClaimsPrincipal(identity);
422
423
424 await HttpContext.SignInAsync(
425 "Cookies",
426 principal);
427
428
429 // Keep session synchronized.
430 HttpContext.Session.SetString(
431 "Username",
432 user.Username);
433
434
435 return RedirectToAction(nameof(Profile));
436 }
437
438
439 // =========================================================
[08aefc6]440 // LOGOUT
[fa0fbaf]441 // =========================================================
[08aefc6]442
[fa0fbaf]443 [HttpPost]
444 [ValidateAntiForgeryToken]
445 public async Task<IActionResult> Logout()
[08aefc6]446 {
[fa0fbaf]447 await HttpContext.SignOutAsync("Cookies");
448
[08aefc6]449 HttpContext.Session.Clear();
450
451 return RedirectToAction(
452 "Index",
453 "Home");
454 }
455}
Note: See TracBrowser for help on using the repository browser.