mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 10:42:08 +00:00
Merge branch '2.0' into 116-add-loginauth-token-feature-to-net-api
This commit is contained in:
@@ -12,6 +12,8 @@ AS
|
|||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON;
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
DECLARE @Inserted TABLE (UserAccountID UNIQUEIDENTIFIER);
|
||||||
|
|
||||||
INSERT INTO UserAccount
|
INSERT INTO UserAccount
|
||||||
(
|
(
|
||||||
Username,
|
Username,
|
||||||
@@ -20,6 +22,7 @@ BEGIN
|
|||||||
DateOfBirth,
|
DateOfBirth,
|
||||||
Email
|
Email
|
||||||
)
|
)
|
||||||
|
OUTPUT INSERTED.UserAccountID INTO @Inserted
|
||||||
VALUES
|
VALUES
|
||||||
(
|
(
|
||||||
@Username,
|
@Username,
|
||||||
@@ -29,5 +32,5 @@ BEGIN
|
|||||||
@Email
|
@Email
|
||||||
);
|
);
|
||||||
|
|
||||||
SELECT @UserAccountId AS UserAccountId;
|
SELECT @UserAccountId = UserAccountID FROM @Inserted;
|
||||||
END;
|
END;
|
||||||
|
|||||||
@@ -27,10 +27,9 @@ BEGIN
|
|||||||
THROW 50000, 'Failed to create user account.', 1;
|
THROW 50000, 'Failed to create user account.', 1;
|
||||||
END
|
END
|
||||||
|
|
||||||
|
INSERT INTO dbo.UserCredential
|
||||||
EXEC dbo.usp_RotateUserCredential
|
(UserAccountId, Hash)
|
||||||
@UserAccountId = @UserAccountId_,
|
VALUES (@UserAccountId_, @Hash);
|
||||||
@Hash = @Hash;
|
|
||||||
|
|
||||||
IF @@ROWCOUNT = 0
|
IF @@ROWCOUNT = 0
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|||||||
@@ -14,9 +14,11 @@
|
|||||||
Version="1.3.1"
|
Version="1.3.1"
|
||||||
/>
|
/>
|
||||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.3" />
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.3" />
|
||||||
|
<PackageReference Include="dbup" Version="5.0.41" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Database.Core\Database.Core.csproj" />
|
||||||
<ProjectReference Include="..\..\Repository\Repository.Core\Repository.Core.csproj" />
|
<ProjectReference Include="..\..\Repository\Repository.Core\Repository.Core.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using DBSeed;
|
using DBSeed;
|
||||||
using Microsoft.Data.SqlClient;
|
using Microsoft.Data.SqlClient;
|
||||||
|
using DbUp;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -14,8 +16,47 @@ try
|
|||||||
await using var connection = new SqlConnection(connectionString);
|
await using var connection = new SqlConnection(connectionString);
|
||||||
await connection.OpenAsync();
|
await connection.OpenAsync();
|
||||||
|
|
||||||
|
// drop and recreate the database
|
||||||
|
var useMaster = connection.CreateCommand();
|
||||||
|
useMaster.CommandText = "USE master;";
|
||||||
|
await useMaster.ExecuteNonQueryAsync();
|
||||||
|
|
||||||
|
var dbName = "Biergarten";
|
||||||
|
var dropDb = connection.CreateCommand();
|
||||||
|
dropDb.CommandText = $@"
|
||||||
|
IF DB_ID(N'{dbName}') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
ALTER DATABASE [{dbName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
|
||||||
|
DROP DATABASE [{dbName}];
|
||||||
|
END";
|
||||||
|
await dropDb.ExecuteNonQueryAsync();
|
||||||
|
|
||||||
|
var createDb = connection.CreateCommand();
|
||||||
|
createDb.CommandText = $@"CREATE DATABASE [{dbName}];";
|
||||||
|
await createDb.ExecuteNonQueryAsync();
|
||||||
|
await connection.CloseAsync();
|
||||||
|
await connection.OpenAsync();
|
||||||
|
|
||||||
|
|
||||||
Console.WriteLine("Connected to database.");
|
Console.WriteLine("Connected to database.");
|
||||||
|
|
||||||
|
Console.WriteLine("Starting migrations...");
|
||||||
|
|
||||||
|
// Run Database.Core migrations (embedded resources) via DbUp
|
||||||
|
var migrationAssembly = Assembly.Load("Database.Core");
|
||||||
|
var upgrader = DeployChanges
|
||||||
|
.To.SqlDatabase(connectionString)
|
||||||
|
.WithScriptsEmbeddedInAssembly(migrationAssembly)
|
||||||
|
.LogToConsole()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var upgradeResult = upgrader.PerformUpgrade();
|
||||||
|
if (!upgradeResult.Successful)
|
||||||
|
throw upgradeResult.Error;
|
||||||
|
|
||||||
|
Console.WriteLine("Migrations completed.");
|
||||||
|
|
||||||
|
|
||||||
ISeeder[] seeders =
|
ISeeder[] seeders =
|
||||||
[
|
[
|
||||||
new LocationSeeder(),
|
new LocationSeeder(),
|
||||||
@@ -30,6 +71,7 @@ try
|
|||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Seed completed successfully.");
|
Console.WriteLine("Seed completed successfully.");
|
||||||
|
await connection.CloseAsync();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -9,11 +9,8 @@ using Microsoft.Data.SqlClient;
|
|||||||
|
|
||||||
namespace DBSeed
|
namespace DBSeed
|
||||||
{
|
{
|
||||||
|
|
||||||
internal class UserSeeder : ISeeder
|
internal class UserSeeder : ISeeder
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
private static readonly IReadOnlyList<(
|
private static readonly IReadOnlyList<(
|
||||||
string FirstName,
|
string FirstName,
|
||||||
string LastName
|
string LastName
|
||||||
@@ -129,36 +126,38 @@ namespace DBSeed
|
|||||||
int createdCredentials = 0;
|
int createdCredentials = 0;
|
||||||
int createdVerifications = 0;
|
int createdVerifications = 0;
|
||||||
|
|
||||||
|
|
||||||
foreach (var (firstName, lastName) in SeedNames)
|
foreach (var (firstName, lastName) in SeedNames)
|
||||||
{
|
{
|
||||||
// create the user in the database
|
// prepare user fields
|
||||||
var userAccountId = Guid.NewGuid();
|
var username = $"{firstName[0]}.{lastName}";
|
||||||
await AddUserAccountAsync(connection, new UserAccount
|
var email = $"{firstName}.{lastName}@thebiergarten.app";
|
||||||
{
|
var dob = GenerateDateOfBirth(rng);
|
||||||
UserAccountId = userAccountId,
|
|
||||||
FirstName = firstName,
|
|
||||||
LastName = lastName,
|
|
||||||
Email = $"{firstName}.{lastName}@thebiergarten.app",
|
|
||||||
Username = $"{firstName[0]}.{lastName}",
|
|
||||||
DateOfBirth = GenerateDateOfBirth(rng)
|
|
||||||
});
|
|
||||||
createdUsers++;
|
|
||||||
|
|
||||||
// add user credentials
|
// generate a password and hash it
|
||||||
if (!await HasUserCredentialAsync(connection, userAccountId))
|
string pwd = generator.Generate(
|
||||||
{
|
length: 64,
|
||||||
string pwd = generator.Generate(
|
numberOfDigits: 10,
|
||||||
length: 64,
|
numberOfSymbols: 10
|
||||||
numberOfDigits: 10,
|
);
|
||||||
numberOfSymbols: 10
|
string hash = GeneratePasswordHash(pwd);
|
||||||
);
|
|
||||||
string hash = GeneratePasswordHash(pwd);
|
// register the user (creates account + credential)
|
||||||
await AddUserCredentialAsync(connection, userAccountId, hash);
|
var userAccountId = await RegisterUserAsync(
|
||||||
createdCredentials++;
|
connection,
|
||||||
}
|
username,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
dob,
|
||||||
|
email,
|
||||||
|
hash
|
||||||
|
);
|
||||||
|
createdUsers++;
|
||||||
|
createdCredentials++;
|
||||||
|
|
||||||
// add user verification
|
// add user verification
|
||||||
if (await HasUserVerificationAsync(connection, userAccountId)) continue;
|
if (await HasUserVerificationAsync(connection, userAccountId)) continue;
|
||||||
|
|
||||||
await AddUserVerificationAsync(connection, userAccountId);
|
await AddUserVerificationAsync(connection, userAccountId);
|
||||||
createdVerifications++;
|
createdVerifications++;
|
||||||
}
|
}
|
||||||
@@ -168,19 +167,34 @@ namespace DBSeed
|
|||||||
Console.WriteLine($"Added {createdVerifications} user verifications.");
|
Console.WriteLine($"Added {createdVerifications} user verifications.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task AddUserAccountAsync(SqlConnection connection, UserAccount ua)
|
private static async Task<Guid> RegisterUserAsync(
|
||||||
|
SqlConnection connection,
|
||||||
|
string username,
|
||||||
|
string firstName,
|
||||||
|
string lastName,
|
||||||
|
DateTime dateOfBirth,
|
||||||
|
string email,
|
||||||
|
string hash
|
||||||
|
)
|
||||||
{
|
{
|
||||||
await using var command = new SqlCommand("usp_CreateUserAccount", connection);
|
await using var command = new SqlCommand("dbo.USP_RegisterUser", connection);
|
||||||
command.CommandType = CommandType.StoredProcedure;
|
command.CommandType = CommandType.StoredProcedure;
|
||||||
|
|
||||||
command.Parameters.Add("@UserAccountId", SqlDbType.UniqueIdentifier).Value = ua.UserAccountId;
|
var idParam = new SqlParameter("@UserAccountId_", SqlDbType.UniqueIdentifier)
|
||||||
command.Parameters.Add("@Username", SqlDbType.NVarChar, 100).Value = ua.Username;
|
{
|
||||||
command.Parameters.Add("@FirstName", SqlDbType.NVarChar, 100).Value = ua.FirstName;
|
Direction = ParameterDirection.Output
|
||||||
command.Parameters.Add("@LastName", SqlDbType.NVarChar, 100).Value = ua.LastName;
|
};
|
||||||
command.Parameters.Add("@Email", SqlDbType.NVarChar, 256).Value = ua.Email;
|
command.Parameters.Add(idParam);
|
||||||
command.Parameters.Add("@DateOfBirth", SqlDbType.Date).Value = ua.DateOfBirth;
|
|
||||||
|
command.Parameters.Add("@Username", SqlDbType.VarChar, 64).Value = username;
|
||||||
|
command.Parameters.Add("@FirstName", SqlDbType.NVarChar, 128).Value = firstName;
|
||||||
|
command.Parameters.Add("@LastName", SqlDbType.NVarChar, 128).Value = lastName;
|
||||||
|
command.Parameters.Add("@DateOfBirth", SqlDbType.DateTime).Value = dateOfBirth;
|
||||||
|
command.Parameters.Add("@Email", SqlDbType.VarChar, 128).Value = email;
|
||||||
|
command.Parameters.Add("@Hash", SqlDbType.NVarChar, -1).Value = hash;
|
||||||
|
|
||||||
await command.ExecuteNonQueryAsync();
|
await command.ExecuteNonQueryAsync();
|
||||||
|
return (Guid)idParam.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GeneratePasswordHash(string pwd)
|
private static string GeneratePasswordHash(string pwd)
|
||||||
@@ -199,39 +213,6 @@ namespace DBSeed
|
|||||||
return $"{Convert.ToBase64String(salt)}:{Convert.ToBase64String(hash)}";
|
return $"{Convert.ToBase64String(salt)}:{Convert.ToBase64String(hash)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<bool> HasUserCredentialAsync(
|
|
||||||
SqlConnection connection,
|
|
||||||
Guid userAccountId
|
|
||||||
)
|
|
||||||
{
|
|
||||||
const string sql = $"""
|
|
||||||
SELECT 1
|
|
||||||
FROM dbo.UserCredential
|
|
||||||
WHERE UserAccountId = @UserAccountId;
|
|
||||||
""";
|
|
||||||
await using var command = new SqlCommand(sql, connection);
|
|
||||||
command.Parameters.AddWithValue("@UserAccountId", userAccountId);
|
|
||||||
object? result = await command.ExecuteScalarAsync();
|
|
||||||
return result is not null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task AddUserCredentialAsync(
|
|
||||||
SqlConnection connection,
|
|
||||||
Guid userAccountId,
|
|
||||||
string hash
|
|
||||||
)
|
|
||||||
{
|
|
||||||
await using var command = new SqlCommand(
|
|
||||||
"dbo.USP_AddUserCredential",
|
|
||||||
connection
|
|
||||||
);
|
|
||||||
command.CommandType = CommandType.StoredProcedure;
|
|
||||||
command.Parameters.AddWithValue("@UserAccountId", userAccountId);
|
|
||||||
command.Parameters.AddWithValue("@Hash", hash);
|
|
||||||
|
|
||||||
await command.ExecuteNonQueryAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<bool> HasUserVerificationAsync(
|
private static async Task<bool> HasUserVerificationAsync(
|
||||||
SqlConnection connection,
|
SqlConnection connection,
|
||||||
Guid userAccountId
|
Guid userAccountId
|
||||||
@@ -258,11 +239,11 @@ namespace DBSeed
|
|||||||
connection
|
connection
|
||||||
);
|
);
|
||||||
command.CommandType = CommandType.StoredProcedure;
|
command.CommandType = CommandType.StoredProcedure;
|
||||||
command.Parameters.AddWithValue("@UserAccountID", userAccountId);
|
command.Parameters.AddWithValue("@UserAccountID_", userAccountId);
|
||||||
|
|
||||||
await command.ExecuteNonQueryAsync();
|
await command.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DateTime GenerateDateOfBirth(Random random)
|
private static DateTime GenerateDateOfBirth(Random random)
|
||||||
{
|
{
|
||||||
int age = 19 + random.Next(0, 30);
|
int age = 19 + random.Next(0, 30);
|
||||||
|
|||||||
Reference in New Issue
Block a user