Create User

This commit is contained in:
Astrian Zheng 2023-09-15 10:54:42 +10:00
parent 72b29d9c78
commit fc38cb0651
11 changed files with 194 additions and 40 deletions

3
.gitignore vendored
View File

@ -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

View File

@ -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 }
);*/
);
}
}
}

View File

@ -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();
@ -18,8 +34,93 @@ 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);
}
// POST /CreateAccount
// 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");
}
}
}

View File

@ -46,6 +46,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="BCrypt.Net-Next, Version=4.0.3.0, Culture=neutral, PublicKeyToken=1e11be04b6288443, processorArchitecture=MSIL">
<HintPath>..\packages\BCrypt.Net-Next.4.0.3\lib\net48\BCrypt.Net-Next.dll</HintPath>
</Reference>
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll</HintPath>
</Reference>
@ -54,8 +57,21 @@
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Security" />
<Reference Include="System.Web.DynamicData" />
@ -252,8 +268,11 @@
<Content Include="Views\Home\Contact.cshtml" />
<Content Include="Views\Home\Index.cshtml" />
<Content Include="Views\Home\CreateAccount.cshtml" />
<Content Include="Views\Test.cshtml" />
</ItemGroup>
<ItemGroup>
<Folder Include="Views\Test\" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Content Include="Content\bootstrap.rtl.min.css.map" />
<Content Include="Content\bootstrap.rtl.css.map" />

View File

@ -17,7 +17,7 @@
<WebStackScaffolding_IsPartialViewSelected>False</WebStackScaffolding_IsPartialViewSelected>
<WebStackScaffolding_IsReferencingScriptLibrariesSelected>True</WebStackScaffolding_IsReferencingScriptLibrariesSelected>
<WebStackScaffolding_DbContextTypeFullName>FIT5032_Assignment.Models.Database1Entities</WebStackScaffolding_DbContextTypeFullName>
<WebStackScaffolding_IsViewGenerationSelected>True</WebStackScaffolding_IsViewGenerationSelected>
<WebStackScaffolding_IsViewGenerationSelected>False</WebStackScaffolding_IsViewGenerationSelected>
<WebStackScaffolding_IsAsyncSelected>False</WebStackScaffolding_IsAsyncSelected>
<View_SelectedScaffolderID>MvcViewScaffolder</View_SelectedScaffolderID>
<View_SelectedScaffolderCategoryPath>root/Common/MVC/View</View_SelectedScaffolderCategoryPath>

View File

@ -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; }
}
}

View File

@ -12,7 +12,7 @@
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<div class="form-horizontal">
<h4>CreateAccountForm</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@ -32,20 +32,25 @@
</div>
</div>
<!-- Dropdown menu -->
<div class="form-group">
@Html.LabelFor(model => model.role, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.role, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.role, "", new { @class = "text-danger" })
<select class="form-control" id="role" name="role">
<option value="1">Patient</option>
<option value="2">Doctor</option>
</select>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
<p>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.</p>
<input type="submit" value="Set Passkey" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@ -55,3 +60,20 @@
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script type="module">
import {Passage, TokenStore} from "https://cdn.passage.id/passage-js.js"
export class MyTokenStore extends TokenStore {
getAuthToken() {
// get token from a cookie named "session"
const authToken = document.cookie
.split('; ')
.find((row) => row.startsWith('session='))
?.split('=')[1];
return Promise.resolve(authToken);
}
};
const psg = new Passage('seT6IaaZLyXcPBmd00SmPvTb', { MyTokenStore });
</script>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Antlr" version="3.5.0.2" targetFramework="net48" />
<package id="BCrypt.Net-Next" version="4.0.3" targetFramework="net48" />
<package id="bootstrap" version="5.2.3" targetFramework="net48" />
<package id="EntityFramework" version="6.4.4" targetFramework="net48" />
<package id="jQuery" version="3.4.1" targetFramework="net48" />
@ -14,5 +15,9 @@
<package id="Microsoft.Web.Infrastructure" version="2.0.1" targetFramework="net48" />
<package id="Modernizr" version="2.8.3" targetFramework="net48" />
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net48" />
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
<package id="System.Memory" version="4.5.4" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.3" targetFramework="net48" />
<package id="WebGrease" version="1.6.0" targetFramework="net48" />
</packages>

4
paket.dependencies Normal file
View File

@ -0,0 +1,4 @@
source https://api.nuget.org/v3/index.json
storage: none
framework: net5.0, netstandard2.0, netstandard2.1