added postgresql integration for user permissions
This commit is contained in:
@@ -6,7 +6,7 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
{
|
{
|
||||||
List<User> LoadUsers();
|
List<User> LoadUsers();
|
||||||
void SaveUsers(List<User> users);
|
void SaveUsers(List<User> users);
|
||||||
List<string> LoadPermissions();
|
List<Permission> LoadPermissions();
|
||||||
void SavePermissions(List<string> permissions);
|
void SavePermissions(List<Permission> permissions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<string> LoadPermissions()
|
public List<Permission> LoadPermissions()
|
||||||
{
|
{
|
||||||
if (!File.Exists(PermissionsFile))
|
if (!File.Exists(PermissionsFile))
|
||||||
{
|
{
|
||||||
@@ -86,7 +86,7 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
string json = File.ReadAllText(PermissionsFile);
|
string json = File.ReadAllText(PermissionsFile);
|
||||||
var permissions = JsonConvert.DeserializeObject<List<string>>(json);
|
var permissions = JsonConvert.DeserializeObject<List<Permission>>(json);
|
||||||
return permissions ?? GetDefaultPermissions();
|
return permissions ?? GetDefaultPermissions();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -96,7 +96,7 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SavePermissions(List<string> permissions)
|
public void SavePermissions(List<Permission> permissions)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -118,27 +118,27 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
Username = "admin",
|
Username = "admin",
|
||||||
FullName = "System Administrator",
|
FullName = "System Administrator",
|
||||||
Password = PasswordHasher.HashPassword("admin"),
|
Password = PasswordHasher.HashPassword("admin"),
|
||||||
Permissions = GetDefaultPermissions()
|
Permissions = new List<int> { 1, 2, 3, 4, 5 }
|
||||||
},
|
},
|
||||||
new User
|
new User
|
||||||
{
|
{
|
||||||
Username = "user",
|
Username = "user",
|
||||||
FullName = "Standard User",
|
FullName = "Standard User",
|
||||||
Password = PasswordHasher.HashPassword("user"),
|
Password = PasswordHasher.HashPassword("user"),
|
||||||
Permissions = new List<string> { "View Dashboard" }
|
Permissions = new List<int> { 1 }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<string> GetDefaultPermissions()
|
private List<Permission> GetDefaultPermissions()
|
||||||
{
|
{
|
||||||
return new List<string>
|
return new List<Permission>
|
||||||
{
|
{
|
||||||
"View Dashboard",
|
new Permission { Id = 1, Name = "View Dashboard" },
|
||||||
"Edit Settings",
|
new Permission { Id = 2, Name = "Edit Settings" },
|
||||||
"Manage Users",
|
new Permission { Id = 3, Name = "Manage Users" },
|
||||||
"Full Control",
|
new Permission { Id = 4, Name = "Full Control" },
|
||||||
"Delete Transactions"
|
new Permission { Id = 5, Name = "Delete Transactions" }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,9 +66,11 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var permission in user.Permissions)
|
foreach (var permissionId in user.Permissions)
|
||||||
{
|
{
|
||||||
lstUserPermissions.Items.Add("✓ " + permission);
|
var permObj = SessionManager.AvailablePermissions.FirstOrDefault(p => p.Id == permissionId);
|
||||||
|
string permName = permObj != null ? permObj.Name : $"Unknown (ID: {permissionId})";
|
||||||
|
lstUserPermissions.Items.Add("✓ " + permName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +78,11 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
btnLogout.Enabled = true;
|
btnLogout.Enabled = true;
|
||||||
|
|
||||||
// Authorization: Check if user has 'Manage Users' or 'Full Control'
|
// Authorization: Check if user has 'Manage Users' or 'Full Control'
|
||||||
bool hasAccess = user.Permissions.Contains("Manage Users") || user.Permissions.Contains("Full Control");
|
var manageUsersPerm = SessionManager.AvailablePermissions.FirstOrDefault(p => p.Name.Equals("Manage Users", StringComparison.OrdinalIgnoreCase));
|
||||||
|
var fullControlPerm = SessionManager.AvailablePermissions.FirstOrDefault(p => p.Name.Equals("Full Control", StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
bool hasAccess = (manageUsersPerm != null && user.Permissions.Contains(manageUsersPerm.Id)) ||
|
||||||
|
(fullControlPerm != null && user.Permissions.Contains(fullControlPerm.Id));
|
||||||
if (hasAccess)
|
if (hasAccess)
|
||||||
{
|
{
|
||||||
btnUsers.Enabled = true;
|
btnUsers.Enabled = true;
|
||||||
@@ -124,7 +130,15 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
private void btnUsers_Click(object sender, EventArgs e)
|
private void btnUsers_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var user = SessionManager.CurrentUser;
|
var user = SessionManager.CurrentUser;
|
||||||
if (user == null || (!user.Permissions.Contains("Manage Users") && !user.Permissions.Contains("Full Control")))
|
var manageUsersPerm = SessionManager.AvailablePermissions.FirstOrDefault(p => p.Name.Equals("Manage Users", StringComparison.OrdinalIgnoreCase));
|
||||||
|
var fullControlPerm = SessionManager.AvailablePermissions.FirstOrDefault(p => p.Name.Equals("Full Control", StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
bool hasAccess = user != null && (
|
||||||
|
(manageUsersPerm != null && user.Permissions.Contains(manageUsersPerm.Id)) ||
|
||||||
|
(fullControlPerm != null && user.Permissions.Contains(fullControlPerm.Id))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasAccess)
|
||||||
{
|
{
|
||||||
MessageBox.Show(
|
MessageBox.Show(
|
||||||
"Security Exception: You do not possess the required credentials ('Manage Users' or 'Full Control') to access directory configuration.",
|
"Security Exception: You do not possess the required credentials ('Manage Users' or 'Full Control') to access directory configuration.",
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace UserPermissionTest_CS_WinForms
|
||||||
|
{
|
||||||
|
public class Permission
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Permission Clone()
|
||||||
|
{
|
||||||
|
return new Permission
|
||||||
|
{
|
||||||
|
Id = this.Id,
|
||||||
|
Name = this.Name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,444 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Npgsql;
|
||||||
|
|
||||||
|
namespace UserPermissionTest_CS_WinForms
|
||||||
|
{
|
||||||
|
public class PostgreSqlUserRepository : IUserRepository
|
||||||
|
{
|
||||||
|
private readonly string _connectionString;
|
||||||
|
|
||||||
|
public PostgreSqlUserRepository(string connectionString)
|
||||||
|
{
|
||||||
|
_connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
|
||||||
|
InitializeDatabaseSchema();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeDatabaseSchema()
|
||||||
|
{
|
||||||
|
using (var conn = new NpgsqlConnection(_connectionString))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
// Dynamic Migration Drop: If available_permissions exists with old text primary key structure
|
||||||
|
bool needsRebuild = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Check if the table has the 'id' column. If not, this throws an exception, triggering the rebuild!
|
||||||
|
using (var cmd = new NpgsqlCommand("SELECT id FROM available_permissions LIMIT 1;", conn))
|
||||||
|
{
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
needsRebuild = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsRebuild)
|
||||||
|
{
|
||||||
|
using (var cmd = new NpgsqlCommand(@"
|
||||||
|
DROP TABLE IF EXISTS user_permissions;
|
||||||
|
DROP TABLE IF EXISTS available_permissions;", conn))
|
||||||
|
{
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create users table
|
||||||
|
using (var cmd = new NpgsqlCommand(@"
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
username VARCHAR(50) PRIMARY KEY,
|
||||||
|
fullname VARCHAR(100) NOT NULL,
|
||||||
|
password VARCHAR(255) NOT NULL
|
||||||
|
);", conn))
|
||||||
|
{
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create available_permissions table (ID-based)
|
||||||
|
using (var cmd = new NpgsqlCommand(@"
|
||||||
|
CREATE TABLE IF NOT EXISTS available_permissions (
|
||||||
|
id INT PRIMARY KEY,
|
||||||
|
name VARCHAR(100) UNIQUE NOT NULL
|
||||||
|
);", conn))
|
||||||
|
{
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create user_permissions table (mapping username to permission_id)
|
||||||
|
using (var cmd = new NpgsqlCommand(@"
|
||||||
|
CREATE TABLE IF NOT EXISTS user_permissions (
|
||||||
|
username VARCHAR(50) REFERENCES users(username) ON DELETE CASCADE,
|
||||||
|
permission_id INT REFERENCES available_permissions(id) ON DELETE CASCADE,
|
||||||
|
PRIMARY KEY (username, permission_id)
|
||||||
|
);", conn))
|
||||||
|
{
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Auto-Seed default available permissions if empty
|
||||||
|
using (var cmd = new NpgsqlCommand("SELECT COUNT(*) FROM available_permissions;", conn))
|
||||||
|
{
|
||||||
|
long count = (long)cmd.ExecuteScalar();
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
foreach (var perm in GetDefaultPermissions())
|
||||||
|
{
|
||||||
|
using (var insertCmd = new NpgsqlCommand(@"
|
||||||
|
INSERT INTO available_permissions (id, name)
|
||||||
|
VALUES (@id, @name)
|
||||||
|
ON CONFLICT (id) DO NOTHING;", conn))
|
||||||
|
{
|
||||||
|
insertCmd.Parameters.AddWithValue("@id", perm.Id);
|
||||||
|
insertCmd.Parameters.AddWithValue("@name", perm.Name);
|
||||||
|
insertCmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Auto-Seed default users if empty
|
||||||
|
using (var cmd = new NpgsqlCommand("SELECT COUNT(*) FROM users;", conn))
|
||||||
|
{
|
||||||
|
long count = (long)cmd.ExecuteScalar();
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
foreach (var user in GetDefaultUsers())
|
||||||
|
{
|
||||||
|
using (var insertCmd = new NpgsqlCommand(@"
|
||||||
|
INSERT INTO users (username, fullname, password)
|
||||||
|
VALUES (@username, @fullname, @password)
|
||||||
|
ON CONFLICT (username) DO NOTHING;", conn))
|
||||||
|
{
|
||||||
|
insertCmd.Parameters.AddWithValue("@username", user.Username);
|
||||||
|
insertCmd.Parameters.AddWithValue("@fullname", user.FullName);
|
||||||
|
insertCmd.Parameters.AddWithValue("@password", user.Password);
|
||||||
|
insertCmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Self-healing permissions: check if 'admin' exists but has 0 permissions, and restore
|
||||||
|
using (var checkCmd = new NpgsqlCommand("SELECT COUNT(*) FROM users WHERE username = 'admin';", conn))
|
||||||
|
{
|
||||||
|
long adminExists = (long)checkCmd.ExecuteScalar();
|
||||||
|
if (adminExists > 0)
|
||||||
|
{
|
||||||
|
using (var permCountCmd = new NpgsqlCommand("SELECT COUNT(*) FROM user_permissions WHERE username = 'admin';", conn))
|
||||||
|
{
|
||||||
|
long adminPermsCount = (long)permCountCmd.ExecuteScalar();
|
||||||
|
if (adminPermsCount == 0)
|
||||||
|
{
|
||||||
|
foreach (int permId in new int[] { 1, 2, 3, 4, 5 })
|
||||||
|
{
|
||||||
|
using (var insertCmd = new NpgsqlCommand(@"
|
||||||
|
INSERT INTO user_permissions (username, permission_id)
|
||||||
|
VALUES ('admin', @permId)
|
||||||
|
ON CONFLICT DO NOTHING;", conn))
|
||||||
|
{
|
||||||
|
insertCmd.Parameters.AddWithValue("@permId", permId);
|
||||||
|
insertCmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Self-healing permissions: check if 'user' exists but has 0 permissions, and restore
|
||||||
|
using (var checkCmd = new NpgsqlCommand("SELECT COUNT(*) FROM users WHERE username = 'user';", conn))
|
||||||
|
{
|
||||||
|
long userExists = (long)checkCmd.ExecuteScalar();
|
||||||
|
if (userExists > 0)
|
||||||
|
{
|
||||||
|
using (var permCountCmd = new NpgsqlCommand("SELECT COUNT(*) FROM user_permissions WHERE username = 'user';", conn))
|
||||||
|
{
|
||||||
|
long userPermsCount = (long)permCountCmd.ExecuteScalar();
|
||||||
|
if (userPermsCount == 0)
|
||||||
|
{
|
||||||
|
using (var insertCmd = new NpgsqlCommand(@"
|
||||||
|
INSERT INTO user_permissions (username, permission_id)
|
||||||
|
VALUES ('user', 1)
|
||||||
|
ON CONFLICT DO NOTHING;", conn))
|
||||||
|
{
|
||||||
|
insertCmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<User> LoadUsers()
|
||||||
|
{
|
||||||
|
var users = new List<User>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var conn = new NpgsqlConnection(_connectionString))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
// Read users
|
||||||
|
using (var cmd = new NpgsqlCommand("SELECT username, fullname, password FROM users;", conn))
|
||||||
|
using (var reader = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
users.Add(new User
|
||||||
|
{
|
||||||
|
Username = reader.GetString(0),
|
||||||
|
FullName = reader.GetString(1),
|
||||||
|
Password = reader.GetString(2),
|
||||||
|
Permissions = new List<int>()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read permission IDs for each user
|
||||||
|
foreach (var user in users)
|
||||||
|
{
|
||||||
|
using (var cmd = new NpgsqlCommand("SELECT permission_id FROM user_permissions WHERE username = @username;", conn))
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("@username", user.Username);
|
||||||
|
using (var reader = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
user.Permissions.Add(reader.GetInt32(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error loading users from PostgreSQL: " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback if no users exist in database
|
||||||
|
if (users.Count == 0)
|
||||||
|
{
|
||||||
|
var defaults = GetDefaultUsers();
|
||||||
|
SaveUsers(defaults);
|
||||||
|
return defaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveUsers(List<User> users)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var conn = new NpgsqlConnection(_connectionString))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
using (var trans = conn.BeginTransaction())
|
||||||
|
{
|
||||||
|
// 1. Delete users from database that are no longer in the input list
|
||||||
|
var currentInDb = new List<string>();
|
||||||
|
using (var cmd = new NpgsqlCommand("SELECT username FROM users;", conn))
|
||||||
|
using (var reader = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
currentInDb.Add(reader.GetString(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var usernamesToKeep = new HashSet<string>(users.Select(u => u.Username), StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var dbUser in currentInDb)
|
||||||
|
{
|
||||||
|
if (!usernamesToKeep.Contains(dbUser))
|
||||||
|
{
|
||||||
|
using (var cmd = new NpgsqlCommand("DELETE FROM users WHERE username = @username;", conn))
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("@username", dbUser);
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Insert or Update users and sync permissions
|
||||||
|
foreach (var user in users)
|
||||||
|
{
|
||||||
|
using (var cmd = new NpgsqlCommand(@"
|
||||||
|
INSERT INTO users (username, fullname, password)
|
||||||
|
VALUES (@username, @fullname, @password)
|
||||||
|
ON CONFLICT (username)
|
||||||
|
DO UPDATE SET fullname = EXCLUDED.fullname, password = EXCLUDED.password;", conn))
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("@username", user.Username);
|
||||||
|
cmd.Parameters.AddWithValue("@fullname", user.FullName);
|
||||||
|
cmd.Parameters.AddWithValue("@password", user.Password);
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-sync permissions: clear old permissions and write current ones
|
||||||
|
using (var cmd = new NpgsqlCommand("DELETE FROM user_permissions WHERE username = @username;", conn))
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("@username", user.Username);
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var permissionId in user.Permissions)
|
||||||
|
{
|
||||||
|
using (var cmd = new NpgsqlCommand(@"
|
||||||
|
INSERT INTO user_permissions (username, permission_id)
|
||||||
|
VALUES (@username, @permissionId)
|
||||||
|
ON CONFLICT DO NOTHING;", conn))
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("@username", user.Username);
|
||||||
|
cmd.Parameters.AddWithValue("@permissionId", permissionId);
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trans.Commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error saving users to PostgreSQL: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Permission> LoadPermissions()
|
||||||
|
{
|
||||||
|
var permissions = new List<Permission>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var conn = new NpgsqlConnection(_connectionString))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
using (var cmd = new NpgsqlCommand("SELECT id, name FROM available_permissions ORDER BY id;", conn))
|
||||||
|
using (var reader = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
permissions.Add(new Permission
|
||||||
|
{
|
||||||
|
Id = reader.GetInt32(0),
|
||||||
|
Name = reader.GetString(1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error loading permissions from PostgreSQL: " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions.Count == 0)
|
||||||
|
{
|
||||||
|
var defaults = GetDefaultPermissions();
|
||||||
|
SavePermissions(defaults);
|
||||||
|
return defaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SavePermissions(List<Permission> permissions)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var conn = new NpgsqlConnection(_connectionString))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
|
||||||
|
using (var trans = conn.BeginTransaction())
|
||||||
|
{
|
||||||
|
// Sync system permissions
|
||||||
|
var currentInDb = new List<int>();
|
||||||
|
using (var cmd = new NpgsqlCommand("SELECT id FROM available_permissions;", conn))
|
||||||
|
using (var reader = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
currentInDb.Add(reader.GetInt32(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var permissionsToKeep = new HashSet<int>(permissions.Select(p => p.Id));
|
||||||
|
foreach (var dbPermId in currentInDb)
|
||||||
|
{
|
||||||
|
if (!permissionsToKeep.Contains(dbPermId))
|
||||||
|
{
|
||||||
|
using (var cmd = new NpgsqlCommand("DELETE FROM available_permissions WHERE id = @id;", conn))
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("@id", dbPermId);
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var perm in permissions)
|
||||||
|
{
|
||||||
|
using (var cmd = new NpgsqlCommand(@"
|
||||||
|
INSERT INTO available_permissions (id, name)
|
||||||
|
VALUES (@id, @name)
|
||||||
|
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name;", conn))
|
||||||
|
{
|
||||||
|
cmd.Parameters.AddWithValue("@id", perm.Id);
|
||||||
|
cmd.Parameters.AddWithValue("@name", perm.Name);
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trans.Commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error saving permissions to PostgreSQL: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<User> GetDefaultUsers()
|
||||||
|
{
|
||||||
|
return new List<User>
|
||||||
|
{
|
||||||
|
new User
|
||||||
|
{
|
||||||
|
Username = "admin",
|
||||||
|
FullName = "System Administrator",
|
||||||
|
Password = PasswordHasher.HashPassword("admin"),
|
||||||
|
Permissions = new List<int> { 1, 2, 3, 4, 5 }
|
||||||
|
},
|
||||||
|
new User
|
||||||
|
{
|
||||||
|
Username = "user",
|
||||||
|
FullName = "Standard User",
|
||||||
|
Password = PasswordHasher.HashPassword("user"),
|
||||||
|
Permissions = new List<int> { 1 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Permission> GetDefaultPermissions()
|
||||||
|
{
|
||||||
|
return new List<Permission>
|
||||||
|
{
|
||||||
|
new Permission { Id = 1, Name = "View Dashboard" },
|
||||||
|
new Permission { Id = 2, Name = "Edit Settings" },
|
||||||
|
new Permission { Id = 3, Name = "Manage Users" },
|
||||||
|
new Permission { Id = 4, Name = "Full Control" },
|
||||||
|
new Permission { Id = 5, Name = "Delete Transactions" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,13 +6,13 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
{
|
{
|
||||||
public static class SessionManager
|
public static class SessionManager
|
||||||
{
|
{
|
||||||
private static readonly IUserRepository UserRepository = new JsonUserRepository();
|
private static readonly IUserRepository UserRepository = new PostgreSqlUserRepository("Host=127.0.0.1;Port=5432;Database=postgres;Username=postgres;Password=postgres");
|
||||||
|
|
||||||
public static User? CurrentUser { get; private set; }
|
public static User? CurrentUser { get; private set; }
|
||||||
|
|
||||||
public static List<User> Users { get; private set; } = new List<User>();
|
public static List<User> Users { get; private set; } = new List<User>();
|
||||||
|
|
||||||
public static List<string> AvailablePermissions { get; private set; } = new List<string>();
|
public static List<Permission> AvailablePermissions { get; private set; } = new List<Permission>();
|
||||||
|
|
||||||
public static event Action? SessionStateChanged;
|
public static event Action? SessionStateChanged;
|
||||||
public static event Action? UsersUpdated;
|
public static event Action? UsersUpdated;
|
||||||
@@ -88,11 +88,13 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddPermission(string permission)
|
public static void AddPermission(string permissionName)
|
||||||
{
|
{
|
||||||
if (!AvailablePermissions.Contains(permission))
|
if (!AvailablePermissions.Any(p => p.Name.Equals(permissionName, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
AvailablePermissions.Add(permission);
|
int newId = AvailablePermissions.Count > 0 ? AvailablePermissions.Max(p => p.Id) + 1 : 1;
|
||||||
|
var newPerm = new Permission { Id = newId, Name = permissionName };
|
||||||
|
AvailablePermissions.Add(newPerm);
|
||||||
UserRepository.SavePermissions(AvailablePermissions);
|
UserRepository.SavePermissions(AvailablePermissions);
|
||||||
UsersUpdated?.Invoke();
|
UsersUpdated?.Invoke();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
public string Username { get; set; } = string.Empty;
|
public string Username { get; set; } = string.Empty;
|
||||||
public string FullName { get; set; } = string.Empty;
|
public string FullName { get; set; } = string.Empty;
|
||||||
public string Password { get; set; } = string.Empty;
|
public string Password { get; set; } = string.Empty;
|
||||||
public List<string> Permissions { get; set; } = new List<string>();
|
public List<int> Permissions { get; set; } = new List<int>();
|
||||||
|
|
||||||
public User Clone()
|
public User Clone()
|
||||||
{
|
{
|
||||||
@@ -17,7 +17,7 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
Username = this.Username,
|
Username = this.Username,
|
||||||
FullName = this.FullName,
|
FullName = this.FullName,
|
||||||
Password = this.Password,
|
Password = this.Password,
|
||||||
Permissions = new List<string>(this.Permissions)
|
Permissions = new List<int>(this.Permissions)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
|
<PackageReference Include="Npgsql" Version="8.0.9" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -77,16 +77,16 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
|
|
||||||
private void RefreshPermissionList()
|
private void RefreshPermissionList()
|
||||||
{
|
{
|
||||||
// Save currently checked permissions
|
// Save currently checked permission IDs
|
||||||
var checkedPerms = new HashSet<string>();
|
var checkedPermIds = new HashSet<int>();
|
||||||
for (int i = 0; i < chkPermissions.Items.Count; i++)
|
for (int i = 0; i < chkPermissions.Items.Count; i++)
|
||||||
{
|
{
|
||||||
if (chkPermissions.GetItemChecked(i))
|
if (chkPermissions.GetItemChecked(i))
|
||||||
{
|
{
|
||||||
string? itemText = chkPermissions.Items[i]?.ToString();
|
var permission = chkPermissions.Items[i] as Permission;
|
||||||
if (itemText != null)
|
if (permission != null)
|
||||||
{
|
{
|
||||||
checkedPerms.Add(itemText);
|
checkedPermIds.Add(permission.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,8 +105,8 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < chkPermissions.Items.Count; i++)
|
for (int i = 0; i < chkPermissions.Items.Count; i++)
|
||||||
{
|
{
|
||||||
string? permissionName = chkPermissions.Items[i].ToString();
|
var permission = chkPermissions.Items[i] as Permission;
|
||||||
if (permissionName != null && user.Permissions.Contains(permissionName))
|
if (permission != null && user.Permissions.Contains(permission.Id))
|
||||||
{
|
{
|
||||||
chkPermissions.SetItemChecked(i, true);
|
chkPermissions.SetItemChecked(i, true);
|
||||||
}
|
}
|
||||||
@@ -118,8 +118,8 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
// Otherwise, restore the checks that the administrator was in the middle of selecting
|
// Otherwise, restore the checks that the administrator was in the middle of selecting
|
||||||
for (int i = 0; i < chkPermissions.Items.Count; i++)
|
for (int i = 0; i < chkPermissions.Items.Count; i++)
|
||||||
{
|
{
|
||||||
string? permissionName = chkPermissions.Items[i].ToString();
|
var permission = chkPermissions.Items[i] as Permission;
|
||||||
if (permissionName != null && checkedPerms.Contains(permissionName))
|
if (permission != null && checkedPermIds.Contains(permission.Id))
|
||||||
{
|
{
|
||||||
chkPermissions.SetItemChecked(i, true);
|
chkPermissions.SetItemChecked(i, true);
|
||||||
}
|
}
|
||||||
@@ -162,8 +162,8 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
// Update permissions CheckedListBox
|
// Update permissions CheckedListBox
|
||||||
for (int i = 0; i < chkPermissions.Items.Count; i++)
|
for (int i = 0; i < chkPermissions.Items.Count; i++)
|
||||||
{
|
{
|
||||||
string? permissionName = chkPermissions.Items[i].ToString();
|
var permission = chkPermissions.Items[i] as Permission;
|
||||||
bool hasPermission = permissionName != null && user.Permissions.Contains(permissionName);
|
bool hasPermission = permission != null && user.Permissions.Contains(permission.Id);
|
||||||
chkPermissions.SetItemChecked(i, hasPermission);
|
chkPermissions.SetItemChecked(i, hasPermission);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,17 +334,17 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<string> GetCheckedPermissions()
|
private List<int> GetCheckedPermissions()
|
||||||
{
|
{
|
||||||
var list = new List<string>();
|
var list = new List<int>();
|
||||||
for (int i = 0; i < chkPermissions.Items.Count; i++)
|
for (int i = 0; i < chkPermissions.Items.Count; i++)
|
||||||
{
|
{
|
||||||
if (chkPermissions.GetItemChecked(i))
|
if (chkPermissions.GetItemChecked(i))
|
||||||
{
|
{
|
||||||
string? permissionName = chkPermissions.Items[i].ToString();
|
var permission = chkPermissions.Items[i] as Permission;
|
||||||
if (permissionName != null)
|
if (permission != null)
|
||||||
{
|
{
|
||||||
list.Add(permissionName);
|
list.Add(permission.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,7 +361,7 @@ namespace UserPermissionTest_CS_WinForms
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SessionManager.AvailablePermissions.Contains(newPerm, StringComparer.OrdinalIgnoreCase))
|
if (SessionManager.AvailablePermissions.Any(p => p.Name.Equals(newPerm, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
MessageBox.Show($"Permission '{newPerm}' already exists in the system.", "Duplicate Permission", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
MessageBox.Show($"Permission '{newPerm}' already exists in the system.", "Duplicate Permission", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
txtNewPermission.Focus();
|
txtNewPermission.Focus();
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ Here are the key files created for this project in [UserPermissionTest_CS_WinFor
|
|||||||
|
|
||||||
* 📄 **[UserPermissionTest_CS_WinForms.csproj](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/UserPermissionTest_CS_WinForms.csproj)**: SDK-style project file configured for `net481` with Windows Forms enabled and `Newtonsoft.Json` package installed.
|
* 📄 **[UserPermissionTest_CS_WinForms.csproj](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/UserPermissionTest_CS_WinForms.csproj)**: SDK-style project file configured for `net481` with Windows Forms enabled and `Newtonsoft.Json` package installed.
|
||||||
* 📄 **[Program.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/Program.cs)**: Main execution entry point.
|
* 📄 **[Program.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/Program.cs)**: Main execution entry point.
|
||||||
* 📄 **[User.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/User.cs)**: Object model for users holding Username, Full Name, Password (hashed), and Permissions list.
|
* 📄 **[User.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/User.cs)**: Object model for users holding Username, Full Name, Password (hashed), and Permissions list (ID-based, List<int>).
|
||||||
|
* 📄 **[Permission.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/Permission.cs)**: Object model for system permissions holding Id (int) and Name (string).
|
||||||
* 📄 **[SessionManager.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/SessionManager.cs)**: Dynamic state and session service utilizing cross-form event notifications (`SessionStateChanged`, `UsersUpdated`).
|
* 📄 **[SessionManager.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/SessionManager.cs)**: Dynamic state and session service utilizing cross-form event notifications (`SessionStateChanged`, `UsersUpdated`).
|
||||||
* 📄 **[PasswordHasher.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/PasswordHasher.cs)**: Cryptographic utility providing one-way SHA-256 password hashing and validation.
|
* 📄 **[PasswordHasher.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/PasswordHasher.cs)**: Cryptographic utility providing one-way SHA-256 password hashing and validation.
|
||||||
* 🖥 **MainForm**:
|
* 🖥 **MainForm**:
|
||||||
@@ -58,9 +59,9 @@ Here are the key files created for this project in [UserPermissionTest_CS_WinFor
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🗄️ Repository Pattern & PostgreSQL Migration Path
|
## 🗄️ Repository Pattern & PostgreSQL Database Integration
|
||||||
|
|
||||||
To address future scalability and facilitate a seamless transition to a relational database like **PostgreSQL**, the data layer has been decoupled using the **Repository Pattern**:
|
To address enterprise-level data persistence, the data layer has been decoupled using the **Repository Pattern** and is **fully integrated with PostgreSQL** using the `Npgsql` database driver:
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph LR
|
graph LR
|
||||||
@@ -69,37 +70,21 @@ graph LR
|
|||||||
IUserRepository <|.. PostgreSqlUserRepository
|
IUserRepository <|.. PostgreSqlUserRepository
|
||||||
|
|
||||||
style IUserRepository fill:#1E293B,stroke:#F59E0B,stroke-width:2px,color:#fff
|
style IUserRepository fill:#1E293B,stroke:#F59E0B,stroke-width:2px,color:#fff
|
||||||
style JsonUserRepository fill:#1E293B,stroke:#10B981,stroke-width:2px
|
style JsonUserRepository fill:#1E293B,stroke:#10B981,stroke-width:2px,stroke-dasharray: 5 5
|
||||||
style PostgreSqlUserRepository fill:#1E293B,stroke:#0EA5E9,stroke-width:2px,stroke-dasharray: 5 5
|
style PostgreSqlUserRepository fill:#1E293B,stroke:#0EA5E9,stroke-width:2px
|
||||||
```
|
```
|
||||||
|
|
||||||
### Decoupled Interfaces:
|
### Decoupled Implementations:
|
||||||
* **[IUserRepository.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/IUserRepository.cs)**: Contract layer. Declares `LoadUsers`, `SaveUsers`, `LoadPermissions`, and `SavePermissions`.
|
* **[IUserRepository.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/IUserRepository.cs)**: Contract layer. Declares `LoadUsers`, `SaveUsers`, `LoadPermissions`, and `SavePermissions`.
|
||||||
* **[JsonUserRepository.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/JsonUserRepository.cs)**: Local file-based (JSON) storage provider using `Newtonsoft.Json`. Writes/reads database parameters to local files `users.json` / `permissions.json` inside output build directories.
|
* **[PostgreSqlUserRepository.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/PostgreSqlUserRepository.cs)**: **Active Production Provider**. Connects to a local PostgreSQL instance, handles automatic schema initialization (tables `users`, `user_permissions`, `available_permissions`), and uses robust atomic transactions for synchronization.
|
||||||
|
* **[JsonUserRepository.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/JsonUserRepository.cs)**: Alternative file-based (JSON) storage provider using `Newtonsoft.Json`. Writes/reads database parameters to local files `users.json` / `permissions.json` inside output build directories.
|
||||||
|
|
||||||
### 🐘 PostgreSQL Migration Steps:
|
### 🐘 Active PostgreSQL Configuration:
|
||||||
When you decide to migrate to PostgreSQL, you **only** need to follow these simple steps:
|
The database connection is managed directly via Dependency Injection in **[SessionManager.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/SessionManager.cs)**:
|
||||||
1. Install `Npgsql` (PostgreSQL Client) and a mapper like `EntityFramework` or `Dapper` via NuGet.
|
|
||||||
2. Implement a new class adhering to `IUserRepository`:
|
|
||||||
```csharp
|
```csharp
|
||||||
public class PostgreSqlUserRepository : IUserRepository
|
private static readonly IUserRepository UserRepository = new PostgreSqlUserRepository("Host=127.0.0.1;Port=5432;Database=postgres;Username=postgres;Password=postgres");
|
||||||
{
|
|
||||||
private readonly string _connectionString;
|
|
||||||
public PostgreSqlUserRepository(string connString) => _connectionString = connString;
|
|
||||||
|
|
||||||
public List<User> LoadUsers() { /* DB query */ }
|
|
||||||
public void SaveUsers(List<User> users) { /* DB save */ }
|
|
||||||
public List<string> LoadPermissions() { /* DB query */ }
|
|
||||||
public void SavePermissions(List<string> permissions) { /* DB save */ }
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
3. Swap a single line in **[SessionManager.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/SessionManager.cs)**:
|
* **Seamless Interchangeability**: Switching back to JSON file storage is as simple as commenting out the PostgreSQL line and uncommenting the JSON class. None of the Windows Forms (`MainForm`, `LoginDialog`, `UserSettings`) require any code edits.
|
||||||
```csharp
|
|
||||||
// Old: private static readonly IUserRepository UserRepository = new JsonUserRepository();
|
|
||||||
// New:
|
|
||||||
private static readonly IUserRepository UserRepository = new PostgreSqlUserRepository("Host=localhost;Database=mydb;Username=postgres;Password=pwd");
|
|
||||||
```
|
|
||||||
4. **Zero UI Code Modification**: None of the Windows Forms (`MainForm`, `LoginDialog`, `UserSettings`) will require any edits. The application will immediately pull from and write to PostgreSQL.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -203,7 +188,8 @@ graph TD
|
|||||||
|
|
||||||
* 📄 **[UserPermissionTest_CS_WinForms.csproj](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/UserPermissionTest_CS_WinForms.csproj)**: `net481` hedefleyen, Windows Forms etkinleştirilmiş ve `Newtonsoft.Json` bağımlılığı yüklenmiş SDK stili modern proje dosyası.
|
* 📄 **[UserPermissionTest_CS_WinForms.csproj](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/UserPermissionTest_CS_WinForms.csproj)**: `net481` hedefleyen, Windows Forms etkinleştirilmiş ve `Newtonsoft.Json` bağımlılığı yüklenmiş SDK stili modern proje dosyası.
|
||||||
* 📄 **[Program.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/Program.cs)**: Uygulamanın ana giriş noktası.
|
* 📄 **[Program.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/Program.cs)**: Uygulamanın ana giriş noktası.
|
||||||
* 📄 **[User.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/User.cs)**: Kullanıcı adı, Ad Soyad, Parola (Hash formatında) ve İzin listesini barındıran veri sınıfı.
|
* 📄 **[User.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/User.cs)**: Kullanıcı adı, Ad Soyad, Parola (Hash formatında) ve İzin listesini (ID tabanlı, List<int>) barındıran veri sınıfı.
|
||||||
|
* 📄 **[Permission.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/Permission.cs)**: Sistemdeki yetkilerin benzersiz kimliğini (Id) ve metnini (Name) barındıran veri sınıfı.
|
||||||
* 📄 **[SessionManager.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/SessionManager.cs)**: Formlar arasında gerçek zamanlı olay bildirimleri (`SessionStateChanged`, `UsersUpdated`) sunan merkezi statik oturum yöneticisi.
|
* 📄 **[SessionManager.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/SessionManager.cs)**: Formlar arasında gerçek zamanlı olay bildirimleri (`SessionStateChanged`, `UsersUpdated`) sunan merkezi statik oturum yöneticisi.
|
||||||
* 📄 **[PasswordHasher.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/PasswordHasher.cs)**: SHA-256 tek yönlü parola kriptolama ve doğrulama yardımcı sınıfı.
|
* 📄 **[PasswordHasher.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/PasswordHasher.cs)**: SHA-256 tek yönlü parola kriptolama ve doğrulama yardımcı sınıfı.
|
||||||
* 🖥 **MainForm**:
|
* 🖥 **MainForm**:
|
||||||
@@ -218,9 +204,9 @@ graph TD
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🗄️ Depo Tasarım Deseni (Repository Pattern) & PostgreSQL Geçiş Yolu
|
## 🗄️ Depo Tasarım Deseni (Repository Pattern) & Aktif PostgreSQL Entegrasyonu
|
||||||
|
|
||||||
Gelecekte uygulamanın SQL veritabanına taşınmasını kolaylaştırmak ve ölçeklenebilirlik sağlamak adına veri erişim katmanı tamamen **Repository Deseni** ile soyutlaştırılmıştır:
|
Veri erişim katmanı tamamen **Repository Deseni** ile soyutlaştırılmış ve `Npgsql` veritabanı sürücüsü kullanılarak **aktif olarak PostgreSQL veritabanına bağlanmıştır**:
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph LR
|
graph LR
|
||||||
@@ -229,37 +215,21 @@ graph LR
|
|||||||
IUserRepository <|.. PostgreSqlUserRepository
|
IUserRepository <|.. PostgreSqlUserRepository
|
||||||
|
|
||||||
style IUserRepository fill:#1E293B,stroke:#F59E0B,stroke-width:2px,color:#fff
|
style IUserRepository fill:#1E293B,stroke:#F59E0B,stroke-width:2px,color:#fff
|
||||||
style JsonUserRepository fill:#1E293B,stroke:#10B981,stroke-width:2px
|
style JsonUserRepository fill:#1E293B,stroke:#10B981,stroke-width:2px,stroke-dasharray: 5 5
|
||||||
style PostgreSqlUserRepository fill:#1E293B,stroke:#0EA5E9,stroke-width:2px,stroke-dasharray: 5 5
|
style PostgreSqlUserRepository fill:#1E293B,stroke:#0EA5E9,stroke-width:2px
|
||||||
```
|
```
|
||||||
|
|
||||||
### Soyut Katmanlar:
|
### Depo Gerçekleştirmeleri:
|
||||||
* **[IUserRepository.cs](file:///D:/02_Programming/CSharp/CodingSandbox/IUserRepository.cs)**: Tanım arayüzüdür. `LoadUsers`, `SaveUsers`, `LoadPermissions` ve `SavePermissions` metot imzalarını içerir.
|
* **[IUserRepository.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/IUserRepository.cs)**: Tanım arayüzüdür. `LoadUsers`, `SaveUsers`, `LoadPermissions` ve `SavePermissions` imzalarını içerir.
|
||||||
* **[JsonUserRepository.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/JsonUserRepository.cs)**: Dosya tabanlı (JSON) veri saklayıcıdır. Çıktı dizinindeki `users.json` ve `permissions.json` dosyalarını okur/yazar.
|
* **[PostgreSqlUserRepository.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/PostgreSqlUserRepository.cs)**: **Aktif Veritabanı Sağlayıcısı**. PostgreSQL servisine bağlanır, sistem tablolarını (`users`, `user_permissions`, `available_permissions`) başlangıçta otomatik olarak oluşturur ve güvenli veritabanı işlemleri için `Transaction` tabanlı veri senkronizasyonu yapar.
|
||||||
|
* **[JsonUserRepository.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/JsonUserRepository.cs)**: Alternatif dosya tabanlı (JSON) veri saklayıcıdır. Çıktı dizinindeki `users.json` ve `permissions.json` dosyalarını okur/yazar.
|
||||||
|
|
||||||
### 🐘 PostgreSQL Geçiş Adımları:
|
### 🐘 Aktif PostgreSQL Yapılandırması:
|
||||||
Veritabanı kurulumunuzu yaptığınızda **yalnızca** şu basit adımları izlemeniz yeterli olacaktır:
|
Veritabanı bağlantısı **[SessionManager.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/SessionManager.cs)** dosyasında tek bir satır üzerinden Dependency Injection ile yönetilir:
|
||||||
1. `Npgsql` (PostgreSQL Client) ve `EntityFramework` veya `Dapper` kütüphanesini NuGet ile projenize yükleyin.
|
|
||||||
2. `IUserRepository` arayüzünü uygulayan yeni bir repository sınıfı yazın:
|
|
||||||
```csharp
|
```csharp
|
||||||
public class PostgreSqlUserRepository : IUserRepository
|
private static readonly IUserRepository UserRepository = new PostgreSqlUserRepository("Host=127.0.0.1;Port=5432;Database=postgres;Username=postgres;Password=postgres");
|
||||||
{
|
|
||||||
private readonly string _connectionString;
|
|
||||||
public PostgreSqlUserRepository(string connString) => _connectionString = connString;
|
|
||||||
|
|
||||||
public List<User> LoadUsers() { /* PostgreSQL SELECT sorgusu */ }
|
|
||||||
public void SaveUsers(List<User> users) { /* PostgreSQL Kaydetme */ }
|
|
||||||
public List<string> LoadPermissions() { /* DB yetki listesi sorgusu */ }
|
|
||||||
public void SavePermissions(List<string> permissions) { /* DB yetki kaydı */ }
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
3. **[SessionManager.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/SessionManager.cs)** dosyasındaki tek satırı güncelleyin:
|
* **Kusursuz Değiştirilebilirlik**: JSON tabanlı dosya saklama modülüne geri geçmek isterseniz, PostgreSQL satırını yorum satırı yapıp JSON satırını açmanız yeterlidir. Arayüz kodlarında (`MainForm`, `LoginDialog` veya `UserSettings`) tek bir satır dahi değişmez.
|
||||||
```csharp
|
|
||||||
// Eski JSON: private static readonly IUserRepository UserRepository = new JsonUserRepository();
|
|
||||||
// Yeni PostgreSQL:
|
|
||||||
private static readonly IUserRepository UserRepository = new PostgreSqlUserRepository("Host=localhost;Database=mydb;Username=postgres;Password=pwd");
|
|
||||||
```
|
|
||||||
4. **Sıfır Arayüz Değişikliği**: `MainForm`, `LoginDialog` veya `UserSettings` formlarındaki tek bir satır kod dahi değiştirilmez. Uygulama verileri anında PostgreSQL üzerinden okuyup yazacaktır.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user