155 lines
5.2 KiB
C#
155 lines
5.2 KiB
C#
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Npgsql;
|
|
using SPTarkov.DI;
|
|
using SPTarkov.DI.Annotations;
|
|
|
|
namespace PersonalAuthMod;
|
|
|
|
[Injectable]
|
|
public class DatabaseManager
|
|
{
|
|
private readonly AuthConfig _config;
|
|
private readonly string _connectionString;
|
|
|
|
public DatabaseManager()
|
|
{
|
|
_config = AuthConfig.Load();
|
|
_connectionString = $"Host={_config.DbUrl};Port={_config.DbPort};Username={_config.DbUser};Password={_config.DbPassword};Database={_config.DbName}";
|
|
Console.WriteLine($"[PersonalAuthMod] DB Manager connecting to: Host={_config.DbUrl}, Port={_config.DbPort}, User={_config.DbUser}, DB={_config.DbName}");
|
|
}
|
|
|
|
public void Initialize()
|
|
{
|
|
try
|
|
{
|
|
using var conn = new NpgsqlConnection(_connectionString);
|
|
conn.Open();
|
|
|
|
// Create Users Table
|
|
using (var cmd = new NpgsqlCommand(@"
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id SERIAL PRIMARY KEY,
|
|
username VARCHAR(50) UNIQUE NOT NULL,
|
|
password_hash TEXT NOT NULL,
|
|
salt TEXT NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);", conn))
|
|
{
|
|
cmd.ExecuteNonQuery();
|
|
}
|
|
|
|
// Create Sessions Table
|
|
using (var cmd = new NpgsqlCommand(@"
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
session_id VARCHAR(100) PRIMARY KEY,
|
|
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);", conn))
|
|
{
|
|
cmd.ExecuteNonQuery();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Log error (using Console for now as we don't have logger injected here yet, or we can use DI)
|
|
Console.WriteLine($"[PersonalAuthMod] Database Initialization Failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
public bool RegisterUser(string username, string password)
|
|
{
|
|
try
|
|
{
|
|
using var conn = new NpgsqlConnection(_connectionString);
|
|
conn.Open();
|
|
|
|
// Check if user exists
|
|
using (var checkCmd = new NpgsqlCommand("SELECT COUNT(*) FROM users WHERE username = @u", conn))
|
|
{
|
|
checkCmd.Parameters.AddWithValue("u", username);
|
|
var count = (long)checkCmd.ExecuteScalar();
|
|
if (count > 0) return false;
|
|
}
|
|
|
|
// Hash Password
|
|
var salt = GenerateSalt();
|
|
var hash = HashPassword(password, salt);
|
|
|
|
// Insert User and get ID
|
|
using (var cmd = new NpgsqlCommand("INSERT INTO users (username, password_hash, salt) VALUES (@u, @p, @s)", conn))
|
|
{
|
|
cmd.Parameters.AddWithValue("u", username);
|
|
cmd.Parameters.AddWithValue("p", hash);
|
|
cmd.Parameters.AddWithValue("s", salt);
|
|
cmd.ExecuteNonQuery();
|
|
}
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[PersonalAuthMod] RegisterUser Failed: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public bool ValidateCredentials(string username, string password)
|
|
{
|
|
try
|
|
{
|
|
using var conn = new NpgsqlConnection(_connectionString);
|
|
conn.Open();
|
|
|
|
string storedHash, storedSalt;
|
|
using (var cmd = new NpgsqlCommand("SELECT password_hash, salt FROM users WHERE username = @u", conn))
|
|
{
|
|
cmd.Parameters.AddWithValue("u", username);
|
|
using var reader = cmd.ExecuteReader();
|
|
if (!reader.Read())
|
|
{
|
|
Console.WriteLine($"[PersonalAuthMod] ValidateCredentials Failed: User '{username}' not found in DB.");
|
|
return false;
|
|
}
|
|
storedHash = reader.GetString(0);
|
|
storedSalt = reader.GetString(1);
|
|
}
|
|
|
|
var hash = HashPassword(password, storedSalt);
|
|
if (hash != storedHash)
|
|
{
|
|
Console.WriteLine($"[PersonalAuthMod] ValidateCredentials Failed: Password mismatch for user '{username}'.");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[PersonalAuthMod] ValidateCredentials Failed: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private string GenerateSalt()
|
|
{
|
|
var bytes = new byte[16];
|
|
using (var rng = RandomNumberGenerator.Create())
|
|
{
|
|
rng.GetBytes(bytes);
|
|
}
|
|
return Convert.ToBase64String(bytes);
|
|
}
|
|
|
|
private string HashPassword(string password, string salt)
|
|
{
|
|
// Simple SHA256 with salt. Pepper is mentioned in requirements but simplified here to salt.
|
|
// If pepper is strictly required, we can add a hardcoded string constant.
|
|
var pepper = "spt-server-pepper";
|
|
using var sha256 = SHA256.Create();
|
|
var combined = password + salt + pepper;
|
|
var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(combined));
|
|
return Convert.ToBase64String(bytes);
|
|
}
|
|
}
|