diff --git a/.gitignore b/.gitignore index 846c3d1..71618cd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ bin obj /.vscode /.vs/FIT5032-Assignment +packages/ +FIT5032-Assignment/App_Data/FIT5032_Assignment.mdf +FIT5032-Assignment/App_Data/FIT5032_Assignment_log.ldf diff --git a/FIT5032-Assignment/App_Data/FIT5032_Assignment.mdf b/FIT5032-Assignment/App_Data/FIT5032_Assignment.mdf deleted file mode 100644 index d9156e2..0000000 Binary files a/FIT5032-Assignment/App_Data/FIT5032_Assignment.mdf and /dev/null differ diff --git a/FIT5032-Assignment/App_Data/FIT5032_Assignment_log.ldf b/FIT5032-Assignment/App_Data/FIT5032_Assignment_log.ldf deleted file mode 100644 index 91b6b32..0000000 Binary files a/FIT5032-Assignment/App_Data/FIT5032_Assignment_log.ldf and /dev/null differ diff --git a/FIT5032-Assignment/App_Start/RouteConfig.cs b/FIT5032-Assignment/App_Start/RouteConfig.cs index c710d87..a6befc8 100644 --- a/FIT5032-Assignment/App_Start/RouteConfig.cs +++ b/FIT5032-Assignment/App_Start/RouteConfig.cs @@ -20,11 +20,11 @@ namespace FIT5032_Assignment defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); - /*routes.MapRoute( - name: "Default", + routes.MapRoute( + name: "HomeAlternative", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } - );*/ + ); } } } diff --git a/FIT5032-Assignment/Controllers/HomeController.cs b/FIT5032-Assignment/Controllers/HomeController.cs index 018c0e4..eb55f8d 100644 --- a/FIT5032-Assignment/Controllers/HomeController.cs +++ b/FIT5032-Assignment/Controllers/HomeController.cs @@ -3,11 +3,27 @@ using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; +using System.Data.Entity; +using FIT5032_Assignment.Models; +using System.Security.Cryptography; +using System.Text; +using BCryptNet = BCrypt.Net.BCrypt; namespace FIT5032_Assignment.Controllers { public class HomeController : Controller { + private Database1Entities db = new Database1Entities(); + + private string GenerateRandomString(int length) + { + const string validChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + var rng = new RNGCryptoServiceProvider(); + var bytes = new byte[length]; + rng.GetBytes(bytes); + return new string(bytes.Select(x => validChars[x % validChars.Length]).ToArray()); + } + public ActionResult Index() { return View(); @@ -17,9 +33,94 @@ namespace FIT5032_Assignment.Controllers { return View(); } + + // POST /CreateAccount + [HttpPost] + public ActionResult CreateAccount(Models.CreateAccountForm model) + { + if (!ModelState.IsValid) + { + ModelState.AddModelError("emailaddress", "Form not valid"); + return View(model); + } + + // Email address is not valid + // Use regular expression to verify email address + if (!System.Text.RegularExpressions.Regex.IsMatch(model.emailaddress, @"^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$")) + { + ModelState.AddModelError("emailaddress", "Email address is not valid"); + return View(model); + } + + // If email address existed in database + var users = db.Credentials.Where(res => (res.uniqueIdCode == model.emailaddress) && (res.provider == 0)); + if (users.Count() > 0) + { + ModelState.AddModelError("emailaddress", "Email address existed"); + return View(model); + } + + // MD5 hash email to get avatar from gravatar + var md5 = MD5.Create(); + byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(model.emailaddress); + byte[] hash = md5.ComputeHash(inputBytes); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < hash.Length; i++) + { + sb.Append(hash[i].ToString("X2")); // X2 means uppercase + } + string avatarUrl = "https://www.gravatar.com/avatar/" + sb.ToString(); + + // Create a new credential and a new user + string userUuid = Guid.NewGuid().ToString(); + Users user = new Users + { + uuid = userUuid, + displayName = model.fullname, + role = Int16.Parse(model.role), + avatar = avatarUrl + }; + Credentials credential = new Credentials + { + uuid = Guid.NewGuid().ToString(), + user = userUuid, + uniqueIdCode = model.emailaddress, + provider = 0, + }; + + // Assign a new session for user + // Generate 32-bit random string as session token + string token = GenerateRandomString(32); + + // Use bcrypt to hash token + string hashedToken = BCryptNet.HashPassword(token); + + string sessionUuid = Guid.NewGuid().ToString(); + + Sessions session = new Sessions + { + uuid = sessionUuid, + user = userUuid, + token = token, + alias = "Web", + validFrom = DateTime.Now, + validTo = DateTime.Now.AddDays(7) + }; + + // Add them into database + db.Users.Add(user); + db.Credentials.Add(credential); + db.Sessions.Add(session); + db.SaveChanges(); + + // write cookie (`session`) + HttpCookie cookie = new HttpCookie("session", sessionUuid + ":" + token); + cookie.Expires = DateTime.Now.AddDays(7); + Response.Cookies.Add(cookie); + + return RedirectToAction("Index"); + } } - // POST /CreateAccount - } \ No newline at end of file diff --git a/FIT5032-Assignment/FIT5032-Assignment.csproj b/FIT5032-Assignment/FIT5032-Assignment.csproj index 82563bc..cbea7b2 100644 --- a/FIT5032-Assignment/FIT5032-Assignment.csproj +++ b/FIT5032-Assignment/FIT5032-Assignment.csproj @@ -46,6 +46,9 @@ 4 + + ..\packages\BCrypt.Net-Next.4.0.3\lib\net48\BCrypt.Net-Next.dll + ..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll @@ -54,8 +57,21 @@ + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + @@ -252,8 +268,11 @@ + + + + - diff --git a/FIT5032-Assignment/FIT5032-Assignment.csproj.user b/FIT5032-Assignment/FIT5032-Assignment.csproj.user index 8ab049b..ed08c2e 100644 --- a/FIT5032-Assignment/FIT5032-Assignment.csproj.user +++ b/FIT5032-Assignment/FIT5032-Assignment.csproj.user @@ -17,7 +17,7 @@ False True FIT5032_Assignment.Models.Database1Entities - True + False False MvcViewScaffolder root/Common/MVC/View diff --git a/FIT5032-Assignment/Models/CreateAccountForm.cs b/FIT5032-Assignment/Models/CreateAccountForm.cs index 596885b..a6b1ba3 100644 --- a/FIT5032-Assignment/Models/CreateAccountForm.cs +++ b/FIT5032-Assignment/Models/CreateAccountForm.cs @@ -18,6 +18,6 @@ namespace FIT5032_Assignment.Models [Required] [Display(Name = "Which role do you want to assign?")] - public Int16 role { get; set; } + public String role { get; set; } } } \ No newline at end of file diff --git a/FIT5032-Assignment/Views/Home/CreateAccount.cshtml b/FIT5032-Assignment/Views/Home/CreateAccount.cshtml index 4bac6ee..123d1d6 100644 --- a/FIT5032-Assignment/Views/Home/CreateAccount.cshtml +++ b/FIT5032-Assignment/Views/Home/CreateAccount.cshtml @@ -12,40 +12,45 @@ { @Html.AntiForgeryToken() -
-

CreateAccountForm

-
- @Html.ValidationSummary(true, "", new { @class = "text-danger" }) -
- @Html.LabelFor(model => model.emailaddress, htmlAttributes: new { @class = "control-label col-md-2" }) -
- @Html.EditorFor(model => model.emailaddress, new { htmlAttributes = new { @class = "form-control" } }) - @Html.ValidationMessageFor(model => model.emailaddress, "", new { @class = "text-danger" }) -
-
- -
- @Html.LabelFor(model => model.fullname, htmlAttributes: new { @class = "control-label col-md-2" }) -
- @Html.EditorFor(model => model.fullname, new { htmlAttributes = new { @class = "form-control" } }) - @Html.ValidationMessageFor(model => model.fullname, "", new { @class = "text-danger" }) -
-
- -
- @Html.LabelFor(model => model.role, htmlAttributes: new { @class = "control-label col-md-2" }) -
- @Html.EditorFor(model => model.role, new { htmlAttributes = new { @class = "form-control" } }) - @Html.ValidationMessageFor(model => model.role, "", new { @class = "text-danger" }) -
-
- -
-
- -
+
+

CreateAccountForm

+
+ @Html.ValidationSummary(true, "", new { @class = "text-danger" }) +
+ @Html.LabelFor(model => model.emailaddress, htmlAttributes: new { @class = "control-label col-md-2" }) +
+ @Html.EditorFor(model => model.emailaddress, new { htmlAttributes = new { @class = "form-control" } }) + @Html.ValidationMessageFor(model => model.emailaddress, "", new { @class = "text-danger" })
+ +
+ @Html.LabelFor(model => model.fullname, htmlAttributes: new { @class = "control-label col-md-2" }) +
+ @Html.EditorFor(model => model.fullname, new { htmlAttributes = new { @class = "form-control" } }) + @Html.ValidationMessageFor(model => model.fullname, "", new { @class = "text-danger" }) +
+
+ + +
+ @Html.LabelFor(model => model.role, htmlAttributes: new { @class = "control-label col-md-2" }) +
+ +
+
+ +
+
+

Next, you need to assign a passkey for authentication. Don't worry, it's safer than a password and uses your existing screen-unlocking authentication methods.

+ +
+
+ +
}
@@ -55,3 +60,20 @@ @section Scripts { @Scripts.Render("~/bundles/jqueryval") } + + + diff --git a/FIT5032-Assignment/packages.config b/FIT5032-Assignment/packages.config index ab95822..d855b54 100644 --- a/FIT5032-Assignment/packages.config +++ b/FIT5032-Assignment/packages.config @@ -1,6 +1,7 @@  + @@ -14,5 +15,9 @@ + + + + \ No newline at end of file diff --git a/paket.dependencies b/paket.dependencies new file mode 100644 index 0000000..c02799e --- /dev/null +++ b/paket.dependencies @@ -0,0 +1,4 @@ +source https://api.nuget.org/v3/index.json + +storage: none +framework: net5.0, netstandard2.0, netstandard2.1 \ No newline at end of file