diff --git a/.antigravitycli/feaeef25-a553-4345-ba51-5eb86eb0eee0.json b/.antigravitycli/feaeef25-a553-4345-ba51-5eb86eb0eee0.json
new file mode 120000
index 0000000..8929b7d
--- /dev/null
+++ b/.antigravitycli/feaeef25-a553-4345-ba51-5eb86eb0eee0.json
@@ -0,0 +1 @@
+C:/Users/ataka/.gemini/config/projects/feaeef25-a553-4345-ba51-5eb86eb0eee0.json
\ No newline at end of file
diff --git a/CodingSandbox.slnx b/CodingSandbox.slnx
index a58f56b..7d7ad6b 100644
--- a/CodingSandbox.slnx
+++ b/CodingSandbox.slnx
@@ -1,3 +1,4 @@
-
+
+
diff --git a/UserPermissionTest_CS_WinForms/IUserRepository.cs b/UserPermissionTest_CS_WinForms/IUserRepository.cs
new file mode 100644
index 0000000..ab3f873
--- /dev/null
+++ b/UserPermissionTest_CS_WinForms/IUserRepository.cs
@@ -0,0 +1,12 @@
+using System.Collections.Generic;
+
+namespace UserPermissionTest_CS_WinForms
+{
+ public interface IUserRepository
+ {
+ List LoadUsers();
+ void SaveUsers(List users);
+ List LoadPermissions();
+ void SavePermissions(List permissions);
+ }
+}
diff --git a/UserPermissionTest_CS_WinForms/JsonUserRepository.cs b/UserPermissionTest_CS_WinForms/JsonUserRepository.cs
new file mode 100644
index 0000000..7541664
--- /dev/null
+++ b/UserPermissionTest_CS_WinForms/JsonUserRepository.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Newtonsoft.Json;
+
+namespace UserPermissionTest_CS_WinForms
+{
+ public class JsonUserRepository : IUserRepository
+ {
+ private const string UsersFile = "users.json";
+ private const string PermissionsFile = "permissions.json";
+
+ public List LoadUsers()
+ {
+ if (!File.Exists(UsersFile))
+ {
+ var defaults = GetDefaultUsers();
+ SaveUsers(defaults);
+ return defaults;
+ }
+
+ try
+ {
+ string json = File.ReadAllText(UsersFile);
+ var users = JsonConvert.DeserializeObject>(json);
+ if (users == null) return GetDefaultUsers();
+
+ // Dynamic Migration: If any loaded user has a plain text password, hash it and re-save
+ bool modified = false;
+ foreach (var user in users)
+ {
+ if (user.Password.Length != 64 || !IsHexString(user.Password))
+ {
+ user.Password = PasswordHasher.HashPassword(user.Password);
+ modified = true;
+ }
+ }
+
+ if (modified)
+ {
+ SaveUsers(users);
+ }
+
+ return users;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Error reading users JSON, falling back to defaults: " + ex.Message);
+ return GetDefaultUsers();
+ }
+ }
+
+ private bool IsHexString(string str)
+ {
+ if (string.IsNullOrEmpty(str)) return false;
+ foreach (char c in str)
+ {
+ if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')))
+ return false;
+ }
+ return true;
+ }
+
+ public void SaveUsers(List users)
+ {
+ try
+ {
+ string json = JsonConvert.SerializeObject(users, Formatting.Indented);
+ File.WriteAllText(UsersFile, json);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Error writing users JSON: " + ex.Message);
+ }
+ }
+
+ public List LoadPermissions()
+ {
+ if (!File.Exists(PermissionsFile))
+ {
+ var defaults = GetDefaultPermissions();
+ SavePermissions(defaults);
+ return defaults;
+ }
+
+ try
+ {
+ string json = File.ReadAllText(PermissionsFile);
+ var permissions = JsonConvert.DeserializeObject>(json);
+ return permissions ?? GetDefaultPermissions();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Error reading permissions JSON, falling back to defaults: " + ex.Message);
+ return GetDefaultPermissions();
+ }
+ }
+
+ public void SavePermissions(List permissions)
+ {
+ try
+ {
+ string json = JsonConvert.SerializeObject(permissions, Formatting.Indented);
+ File.WriteAllText(PermissionsFile, json);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Error writing permissions JSON: " + ex.Message);
+ }
+ }
+
+ private List GetDefaultUsers()
+ {
+ return new List
+ {
+ new User
+ {
+ Username = "admin",
+ FullName = "System Administrator",
+ Password = PasswordHasher.HashPassword("admin"),
+ Permissions = GetDefaultPermissions()
+ },
+ new User
+ {
+ Username = "user",
+ FullName = "Standard User",
+ Password = PasswordHasher.HashPassword("user"),
+ Permissions = new List { "View Dashboard" }
+ }
+ };
+ }
+
+ private List GetDefaultPermissions()
+ {
+ return new List
+ {
+ "View Dashboard",
+ "Edit Settings",
+ "Manage Users",
+ "Full Control",
+ "Delete Transactions"
+ };
+ }
+ }
+}
diff --git a/UserPermissionTest_CS_WinForms/LoginDialog.Designer.cs b/UserPermissionTest_CS_WinForms/LoginDialog.Designer.cs
new file mode 100644
index 0000000..ade586b
--- /dev/null
+++ b/UserPermissionTest_CS_WinForms/LoginDialog.Designer.cs
@@ -0,0 +1,218 @@
+namespace UserPermissionTest_CS_WinForms
+{
+ partial class LoginDialog
+ {
+ private System.ComponentModel.IContainer components = null;
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ private void InitializeComponent()
+ {
+ this.pnlHeader = new System.Windows.Forms.Panel();
+ this.lblTitle = new System.Windows.Forms.Label();
+ this.lblSubtitle = new System.Windows.Forms.Label();
+ this.lblUsername = new System.Windows.Forms.Label();
+ this.txtUsername = new System.Windows.Forms.TextBox();
+ this.lblPassword = new System.Windows.Forms.Label();
+ this.txtPassword = new System.Windows.Forms.TextBox();
+ this.btnTogglePassword = new System.Windows.Forms.Button();
+ this.btnLogin = new System.Windows.Forms.Button();
+ this.btnCancel = new System.Windows.Forms.Button();
+ this.lblError = new System.Windows.Forms.Label();
+ this.pnlHeader.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // pnlHeader
+ //
+ this.pnlHeader.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(41)))), ((int)(((byte)(59)))));
+ this.pnlHeader.Controls.Add(this.lblTitle);
+ this.pnlHeader.Controls.Add(this.lblSubtitle);
+ this.pnlHeader.Dock = System.Windows.Forms.DockStyle.Top;
+ this.pnlHeader.Location = new System.Drawing.Point(0, 0);
+ this.pnlHeader.Name = "pnlHeader";
+ this.pnlHeader.Size = new System.Drawing.Size(380, 80);
+ this.pnlHeader.TabIndex = 0;
+ //
+ // lblTitle
+ //
+ this.lblTitle.AutoSize = true;
+ this.lblTitle.Font = new System.Drawing.Font("Segoe UI Semibold", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblTitle.ForeColor = System.Drawing.Color.White;
+ this.lblTitle.Location = new System.Drawing.Point(20, 15);
+ this.lblTitle.Name = "lblTitle";
+ this.lblTitle.Size = new System.Drawing.Size(126, 30);
+ this.lblTitle.TabIndex = 0;
+ this.lblTitle.Text = "User Sign In";
+ //
+ // lblSubtitle
+ //
+ this.lblSubtitle.AutoSize = true;
+ this.lblSubtitle.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblSubtitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(148)))), ((int)(((byte)(163)))), ((int)(((byte)(184)))));
+ this.lblSubtitle.Location = new System.Drawing.Point(22, 45);
+ this.lblSubtitle.Name = "lblSubtitle";
+ this.lblSubtitle.Size = new System.Drawing.Size(189, 15);
+ this.lblSubtitle.TabIndex = 1;
+ this.lblSubtitle.Text = "Please enter your credentials to login";
+ //
+ // lblUsername
+ //
+ this.lblUsername.AutoSize = true;
+ this.lblUsername.Font = new System.Drawing.Font("Segoe UI Semibold", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblUsername.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(71)))), ((int)(((byte)(85)))), ((int)(((byte)(105)))));
+ this.lblUsername.Location = new System.Drawing.Point(30, 100);
+ this.lblUsername.Name = "lblUsername";
+ this.lblUsername.Size = new System.Drawing.Size(69, 17);
+ this.lblUsername.TabIndex = 1;
+ this.lblUsername.Text = "Username";
+ //
+ // txtUsername
+ //
+ this.txtUsername.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(250)))), ((int)(((byte)(252)))));
+ this.txtUsername.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtUsername.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(15)))), ((int)(((byte)(23)))), ((int)(((byte)(42)))));
+ this.txtUsername.Location = new System.Drawing.Point(30, 122);
+ this.txtUsername.Name = "txtUsername";
+ this.txtUsername.Size = new System.Drawing.Size(320, 27);
+ this.txtUsername.TabIndex = 2;
+ //
+ // lblPassword
+ //
+ this.lblPassword.AutoSize = true;
+ this.lblPassword.Font = new System.Drawing.Font("Segoe UI Semibold", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblPassword.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(71)))), ((int)(((byte)(85)))), ((int)(((byte)(105)))));
+ this.lblPassword.Location = new System.Drawing.Point(30, 160);
+ this.lblPassword.Name = "lblPassword";
+ this.lblPassword.Size = new System.Drawing.Size(66, 17);
+ this.lblPassword.TabIndex = 3;
+ this.lblPassword.Text = "Password";
+ //
+ // txtPassword
+ //
+ this.txtPassword.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(250)))), ((int)(((byte)(252)))));
+ this.txtPassword.Font = new System.Drawing.Font("Segoe UI", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtPassword.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(15)))), ((int)(((byte)(23)))), ((int)(((byte)(42)))));
+ this.txtPassword.Location = new System.Drawing.Point(30, 182);
+ this.txtPassword.Name = "txtPassword";
+ this.txtPassword.PasswordChar = '•';
+ this.txtPassword.Size = new System.Drawing.Size(285, 27);
+ this.txtPassword.TabIndex = 4;
+ //
+ // btnTogglePassword
+ //
+ this.btnTogglePassword.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(245)))), ((int)(((byte)(249)))));
+ this.btnTogglePassword.Cursor = System.Windows.Forms.Cursors.Hand;
+ this.btnTogglePassword.FlatAppearance.BorderSize = 0;
+ this.btnTogglePassword.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.btnTogglePassword.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnTogglePassword.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(71)))), ((int)(((byte)(85)))), ((int)(((byte)(105)))));
+ this.btnTogglePassword.Location = new System.Drawing.Point(320, 182);
+ this.btnTogglePassword.Name = "btnTogglePassword";
+ this.btnTogglePassword.Size = new System.Drawing.Size(30, 27);
+ this.btnTogglePassword.TabIndex = 8;
+ this.btnTogglePassword.Text = "👁";
+ this.btnTogglePassword.UseVisualStyleBackColor = false;
+ this.btnTogglePassword.Click += new System.EventHandler(this.btnTogglePassword_Click);
+ //
+ // btnLogin
+ //
+ this.btnLogin.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(99)))), ((int)(((byte)(102)))), ((int)(((byte)(241)))));
+ this.btnLogin.Cursor = System.Windows.Forms.Cursors.Hand;
+ this.btnLogin.FlatAppearance.BorderSize = 0;
+ this.btnLogin.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.btnLogin.Font = new System.Drawing.Font("Segoe UI Semibold", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnLogin.ForeColor = System.Drawing.Color.White;
+ this.btnLogin.Location = new System.Drawing.Point(230, 245);
+ this.btnLogin.Name = "btnLogin";
+ this.btnLogin.Size = new System.Drawing.Size(120, 36);
+ this.btnLogin.TabIndex = 5;
+ this.btnLogin.Text = "Login";
+ this.btnLogin.UseVisualStyleBackColor = false;
+ this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click);
+ //
+ // btnCancel
+ //
+ this.btnCancel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(245)))), ((int)(((byte)(249)))));
+ this.btnCancel.Cursor = System.Windows.Forms.Cursors.Hand;
+ this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.btnCancel.FlatAppearance.BorderSize = 0;
+ this.btnCancel.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.btnCancel.Font = new System.Drawing.Font("Segoe UI Semibold", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnCancel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(71)))), ((int)(((byte)(85)))), ((int)(((byte)(105)))));
+ this.btnCancel.Location = new System.Drawing.Point(100, 245);
+ this.btnCancel.Name = "btnCancel";
+ this.btnCancel.Size = new System.Drawing.Size(120, 36);
+ this.btnCancel.TabIndex = 6;
+ this.btnCancel.Text = "Cancel";
+ this.btnCancel.UseVisualStyleBackColor = false;
+ this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
+ //
+ // lblError
+ //
+ this.lblError.AutoSize = true;
+ this.lblError.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblError.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(239)))), ((int)(((byte)(68)))), ((int)(((byte)(68)))));
+ this.lblError.Location = new System.Drawing.Point(30, 215);
+ this.lblError.Name = "lblError";
+ this.lblError.Size = new System.Drawing.Size(167, 15);
+ this.lblError.TabIndex = 7;
+ this.lblError.Text = "Invalid username or password!";
+ this.lblError.Visible = false;
+ //
+ // LoginDialog
+ //
+ this.AcceptButton = this.btnLogin;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.Color.White;
+ this.CancelButton = this.btnCancel;
+ this.ClientSize = new System.Drawing.Size(380, 305);
+ this.Controls.Add(this.lblError);
+ this.Controls.Add(this.btnCancel);
+ this.Controls.Add(this.btnLogin);
+ this.Controls.Add(this.txtPassword);
+ this.Controls.Add(this.btnTogglePassword);
+ this.Controls.Add(this.lblPassword);
+ this.Controls.Add(this.txtUsername);
+ this.Controls.Add(this.lblUsername);
+ this.Controls.Add(this.pnlHeader);
+ this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "LoginDialog";
+ this.ShowIcon = false;
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Sign In";
+ this.pnlHeader.ResumeLayout(false);
+ this.pnlHeader.PerformLayout();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Panel pnlHeader;
+ private System.Windows.Forms.Label lblTitle;
+ private System.Windows.Forms.Label lblSubtitle;
+ private System.Windows.Forms.Label lblUsername;
+ private System.Windows.Forms.TextBox txtUsername;
+ private System.Windows.Forms.Label lblPassword;
+ private System.Windows.Forms.TextBox txtPassword;
+ private System.Windows.Forms.Button btnTogglePassword;
+ private System.Windows.Forms.Button btnLogin;
+ private System.Windows.Forms.Button btnCancel;
+ private System.Windows.Forms.Label lblError;
+ }
+}
diff --git a/UserPermissionTest_CS_WinForms/LoginDialog.cs b/UserPermissionTest_CS_WinForms/LoginDialog.cs
new file mode 100644
index 0000000..49d16a2
--- /dev/null
+++ b/UserPermissionTest_CS_WinForms/LoginDialog.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Windows.Forms;
+
+namespace UserPermissionTest_CS_WinForms
+{
+ public partial class LoginDialog : Form
+ {
+ public LoginDialog()
+ {
+ InitializeComponent();
+ }
+
+ private void btnLogin_Click(object sender, EventArgs e)
+ {
+ lblError.Visible = false;
+ string username = txtUsername.Text.Trim();
+ string password = txtPassword.Text;
+
+ if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
+ {
+ lblError.Text = "Please fill in all fields.";
+ lblError.Visible = true;
+ return;
+ }
+
+ if (SessionManager.Login(username, password))
+ {
+ this.DialogResult = DialogResult.OK;
+ this.Close();
+ }
+ else
+ {
+ lblError.Text = "Invalid username or password!";
+ lblError.Visible = true;
+ txtPassword.Clear();
+ txtPassword.Focus();
+ }
+ }
+
+ private void btnCancel_Click(object sender, EventArgs e)
+ {
+ this.DialogResult = DialogResult.Cancel;
+ this.Close();
+ }
+
+ private void btnTogglePassword_Click(object sender, EventArgs e)
+ {
+ if (txtPassword.PasswordChar == '•')
+ {
+ txtPassword.PasswordChar = '\0'; // Show password
+ btnTogglePassword.Text = "🙈";
+ }
+ else
+ {
+ txtPassword.PasswordChar = '•'; // Mask password
+ btnTogglePassword.Text = "👁";
+ }
+ }
+ }
+}
diff --git a/UserPermissionTest_CS_WinForms/MainForm.Designer.cs b/UserPermissionTest_CS_WinForms/MainForm.Designer.cs
new file mode 100644
index 0000000..92d5b3b
--- /dev/null
+++ b/UserPermissionTest_CS_WinForms/MainForm.Designer.cs
@@ -0,0 +1,319 @@
+namespace UserPermissionTest_CS_WinForms
+{
+ partial class MainForm
+ {
+ private System.ComponentModel.IContainer components = null;
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ private void InitializeComponent()
+ {
+ this.pnlHeader = new System.Windows.Forms.Panel();
+ this.lblTitle = new System.Windows.Forms.Label();
+ this.lblSubtitle = new System.Windows.Forms.Label();
+ this.pnlSidebar = new System.Windows.Forms.Panel();
+ this.btnUsers = new System.Windows.Forms.Button();
+ this.btnLogout = new System.Windows.Forms.Button();
+ this.btnLogin = new System.Windows.Forms.Button();
+ this.pnlContent = new System.Windows.Forms.Panel();
+ this.pnlSessionCard = new System.Windows.Forms.Panel();
+ this.lstUserPermissions = new System.Windows.Forms.ListBox();
+ this.lblPermissionsHeader = new System.Windows.Forms.Label();
+ this.pnlDivider = new System.Windows.Forms.Panel();
+ this.lblFullNameValue = new System.Windows.Forms.Label();
+ this.lblFullNameLabel = new System.Windows.Forms.Label();
+ this.lblUsernameValue = new System.Windows.Forms.Label();
+ this.lblUsernameLabel = new System.Windows.Forms.Label();
+ this.lblStatusBadge = new System.Windows.Forms.Label();
+ this.lblSessionHeader = new System.Windows.Forms.Label();
+ this.pnlHeader.SuspendLayout();
+ this.pnlSidebar.SuspendLayout();
+ this.pnlContent.SuspendLayout();
+ this.pnlSessionCard.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // pnlHeader
+ //
+ this.pnlHeader.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(41)))), ((int)(((byte)(59)))));
+ this.pnlHeader.Controls.Add(this.lblTitle);
+ this.pnlHeader.Controls.Add(this.lblSubtitle);
+ this.pnlHeader.Dock = System.Windows.Forms.DockStyle.Top;
+ this.pnlHeader.Location = new System.Drawing.Point(0, 0);
+ this.pnlHeader.Name = "pnlHeader";
+ this.pnlHeader.Size = new System.Drawing.Size(684, 80);
+ this.pnlHeader.TabIndex = 0;
+ //
+ // lblTitle
+ //
+ this.lblTitle.AutoSize = true;
+ this.lblTitle.Font = new System.Drawing.Font("Segoe UI Semibold", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblTitle.ForeColor = System.Drawing.Color.White;
+ this.lblTitle.Location = new System.Drawing.Point(20, 15);
+ this.lblTitle.Name = "lblTitle";
+ this.lblTitle.Size = new System.Drawing.Size(325, 30);
+ this.lblTitle.TabIndex = 0;
+ this.lblTitle.Text = "Authentication && Security Portal";
+ //
+ // lblSubtitle
+ //
+ this.lblSubtitle.AutoSize = true;
+ this.lblSubtitle.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblSubtitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(148)))), ((int)(((byte)(163)))), ((int)(((byte)(184)))));
+ this.lblSubtitle.Location = new System.Drawing.Point(22, 45);
+ this.lblSubtitle.Name = "lblSubtitle";
+ this.lblSubtitle.Size = new System.Drawing.Size(342, 15);
+ this.lblSubtitle.TabIndex = 1;
+ this.lblSubtitle.Text = "Secure WinForms Session Sandbox - Target Framework .NET 4.8.1";
+ //
+ // pnlSidebar
+ //
+ this.pnlSidebar.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(250)))), ((int)(((byte)(252)))));
+ this.pnlSidebar.Controls.Add(this.btnUsers);
+ this.pnlSidebar.Controls.Add(this.btnLogout);
+ this.pnlSidebar.Controls.Add(this.btnLogin);
+ this.pnlSidebar.Dock = System.Windows.Forms.DockStyle.Left;
+ this.pnlSidebar.Location = new System.Drawing.Point(0, 80);
+ this.pnlSidebar.Name = "pnlSidebar";
+ this.pnlSidebar.Padding = new System.Windows.Forms.Padding(15);
+ this.pnlSidebar.Size = new System.Drawing.Size(180, 301);
+ this.pnlSidebar.TabIndex = 1;
+ //
+ // btnUsers
+ //
+ this.btnUsers.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(245)))), ((int)(((byte)(249)))));
+ this.btnUsers.Cursor = System.Windows.Forms.Cursors.Hand;
+ this.btnUsers.Dock = System.Windows.Forms.DockStyle.Top;
+ this.btnUsers.FlatAppearance.BorderSize = 0;
+ this.btnUsers.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.btnUsers.Font = new System.Drawing.Font("Segoe UI Semibold", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnUsers.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(51)))), ((int)(((byte)(65)))), ((int)(((byte)(85)))));
+ this.btnUsers.Location = new System.Drawing.Point(15, 107);
+ this.btnUsers.Name = "btnUsers";
+ this.btnUsers.Size = new System.Drawing.Size(150, 46);
+ this.btnUsers.TabIndex = 2;
+ this.btnUsers.Text = "👥 Manage Users";
+ this.btnUsers.UseVisualStyleBackColor = false;
+ this.btnUsers.Click += new System.EventHandler(this.btnUsers_Click);
+ //
+ // btnLogout
+ //
+ this.btnLogout.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(254)))), ((int)(((byte)(242)))), ((int)(((byte)(242)))));
+ this.btnLogout.Cursor = System.Windows.Forms.Cursors.Hand;
+ this.btnLogout.Dock = System.Windows.Forms.DockStyle.Top;
+ this.btnLogout.FlatAppearance.BorderSize = 0;
+ this.btnLogout.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.btnLogout.Font = new System.Drawing.Font("Segoe UI Semibold", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnLogout.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(239)))), ((int)(((byte)(68)))), ((int)(((byte)(68)))));
+ this.btnLogout.Location = new System.Drawing.Point(15, 61);
+ this.btnLogout.Name = "btnLogout";
+ this.btnLogout.Size = new System.Drawing.Size(150, 46);
+ this.btnLogout.TabIndex = 1;
+ this.btnLogout.Text = "🚪 Logout";
+ this.btnLogout.UseVisualStyleBackColor = false;
+ this.btnLogout.Click += new System.EventHandler(this.btnLogout_Click);
+ //
+ // btnLogin
+ //
+ this.btnLogin.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(99)))), ((int)(((byte)(102)))), ((int)(((byte)(241)))));
+ this.btnLogin.Cursor = System.Windows.Forms.Cursors.Hand;
+ this.btnLogin.Dock = System.Windows.Forms.DockStyle.Top;
+ this.btnLogin.FlatAppearance.BorderSize = 0;
+ this.btnLogin.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.btnLogin.Font = new System.Drawing.Font("Segoe UI Semibold", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnLogin.ForeColor = System.Drawing.Color.White;
+ this.btnLogin.Location = new System.Drawing.Point(15, 15);
+ this.btnLogin.Name = "btnLogin";
+ this.btnLogin.Size = new System.Drawing.Size(150, 46);
+ this.btnLogin.TabIndex = 0;
+ this.btnLogin.Text = "🔑 Login";
+ this.btnLogin.UseVisualStyleBackColor = false;
+ this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click);
+ //
+ // pnlContent
+ //
+ this.pnlContent.BackColor = System.Drawing.Color.White;
+ this.pnlContent.Controls.Add(this.pnlSessionCard);
+ this.pnlContent.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.pnlContent.Location = new System.Drawing.Point(180, 80);
+ this.pnlContent.Name = "pnlContent";
+ this.pnlContent.Padding = new System.Windows.Forms.Padding(20);
+ this.pnlContent.Size = new System.Drawing.Size(504, 301);
+ this.pnlContent.TabIndex = 2;
+ //
+ // pnlSessionCard
+ //
+ this.pnlSessionCard.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(250)))), ((int)(((byte)(252)))));
+ this.pnlSessionCard.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+ this.pnlSessionCard.Controls.Add(this.lstUserPermissions);
+ this.pnlSessionCard.Controls.Add(this.lblPermissionsHeader);
+ this.pnlSessionCard.Controls.Add(this.pnlDivider);
+ this.pnlSessionCard.Controls.Add(this.lblFullNameValue);
+ this.pnlSessionCard.Controls.Add(this.lblFullNameLabel);
+ this.pnlSessionCard.Controls.Add(this.lblUsernameValue);
+ this.pnlSessionCard.Controls.Add(this.lblUsernameLabel);
+ this.pnlSessionCard.Controls.Add(this.lblStatusBadge);
+ this.pnlSessionCard.Controls.Add(this.lblSessionHeader);
+ this.pnlSessionCard.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.pnlSessionCard.Location = new System.Drawing.Point(20, 20);
+ this.pnlSessionCard.Name = "pnlSessionCard";
+ this.pnlSessionCard.Padding = new System.Windows.Forms.Padding(15);
+ this.pnlSessionCard.Size = new System.Drawing.Size(464, 261);
+ this.pnlSessionCard.TabIndex = 0;
+ //
+ // lstUserPermissions
+ //
+ this.lstUserPermissions.BackColor = System.Drawing.Color.White;
+ this.lstUserPermissions.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+ this.lstUserPermissions.Font = new System.Drawing.Font("Segoe UI", 9.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lstUserPermissions.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(51)))), ((int)(((byte)(65)))), ((int)(((byte)(85)))));
+ this.lstUserPermissions.FormattingEnabled = true;
+ this.lstUserPermissions.ItemHeight = 17;
+ this.lstUserPermissions.Location = new System.Drawing.Point(200, 75);
+ this.lstUserPermissions.Name = "lstUserPermissions";
+ this.lstUserPermissions.SelectionMode = System.Windows.Forms.SelectionMode.None;
+ this.lstUserPermissions.Size = new System.Drawing.Size(240, 155);
+ this.lstUserPermissions.TabIndex = 8;
+ //
+ // lblPermissionsHeader
+ //
+ this.lblPermissionsHeader.AutoSize = true;
+ this.lblPermissionsHeader.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblPermissionsHeader.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(116)))), ((int)(((byte)(139)))));
+ this.lblPermissionsHeader.Location = new System.Drawing.Point(200, 55);
+ this.lblPermissionsHeader.Name = "lblPermissionsHeader";
+ this.lblPermissionsHeader.Size = new System.Drawing.Size(121, 15);
+ this.lblPermissionsHeader.TabIndex = 7;
+ this.lblPermissionsHeader.Text = "ASSIGNED PRIVILEGES";
+ //
+ // pnlDivider
+ //
+ this.pnlDivider.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(226)))), ((int)(((byte)(232)))), ((int)(((byte)(240)))));
+ this.pnlDivider.Location = new System.Drawing.Point(180, 50);
+ this.pnlDivider.Name = "pnlDivider";
+ this.pnlDivider.Size = new System.Drawing.Size(1, 180);
+ this.pnlDivider.TabIndex = 6;
+ //
+ // lblFullNameValue
+ //
+ this.lblFullNameValue.AutoSize = true;
+ this.lblFullNameValue.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblFullNameValue.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(15)))), ((int)(((byte)(23)))), ((int)(((byte)(42)))));
+ this.lblFullNameValue.Location = new System.Drawing.Point(15, 160);
+ this.lblFullNameValue.Name = "lblFullNameValue";
+ this.lblFullNameValue.Size = new System.Drawing.Size(14, 19);
+ this.lblFullNameValue.TabIndex = 5;
+ this.lblFullNameValue.Text = "-";
+ //
+ // lblFullNameLabel
+ //
+ this.lblFullNameLabel.AutoSize = true;
+ this.lblFullNameLabel.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblFullNameLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(116)))), ((int)(((byte)(139)))));
+ this.lblFullNameLabel.Location = new System.Drawing.Point(15, 140);
+ this.lblFullNameLabel.Name = "lblFullNameLabel";
+ this.lblFullNameLabel.Size = new System.Drawing.Size(68, 15);
+ this.lblFullNameLabel.TabIndex = 4;
+ this.lblFullNameLabel.Text = "FULL NAME";
+ //
+ // lblUsernameValue
+ //
+ this.lblUsernameValue.AutoSize = true;
+ this.lblUsernameValue.Font = new System.Drawing.Font("Segoe UI Semibold", 10.5F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblUsernameValue.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(15)))), ((int)(((byte)(23)))), ((int)(((byte)(42)))));
+ this.lblUsernameValue.Location = new System.Drawing.Point(15, 100);
+ this.lblUsernameValue.Name = "lblUsernameValue";
+ this.lblUsernameValue.Size = new System.Drawing.Size(95, 19);
+ this.lblUsernameValue.TabIndex = 3;
+ this.lblUsernameValue.Text = "Not Logged In";
+ //
+ // lblUsernameLabel
+ //
+ this.lblUsernameLabel.AutoSize = true;
+ this.lblUsernameLabel.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblUsernameLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(116)))), ((int)(((byte)(139)))));
+ this.lblUsernameLabel.Location = new System.Drawing.Point(15, 80);
+ this.lblUsernameLabel.Name = "lblUsernameLabel";
+ this.lblUsernameLabel.Size = new System.Drawing.Size(68, 15);
+ this.lblUsernameLabel.TabIndex = 2;
+ this.lblUsernameLabel.Text = "USERNAME";
+ //
+ // lblStatusBadge
+ //
+ this.lblStatusBadge.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(239)))), ((int)(((byte)(68)))), ((int)(((byte)(68)))));
+ this.lblStatusBadge.Font = new System.Drawing.Font("Segoe UI Semibold", 8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblStatusBadge.ForeColor = System.Drawing.Color.White;
+ this.lblStatusBadge.Location = new System.Drawing.Point(15, 45);
+ this.lblStatusBadge.Name = "lblStatusBadge";
+ this.lblStatusBadge.Size = new System.Drawing.Size(95, 22);
+ this.lblStatusBadge.TabIndex = 1;
+ this.lblStatusBadge.Text = "SIGNED OUT";
+ this.lblStatusBadge.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // lblSessionHeader
+ //
+ this.lblSessionHeader.AutoSize = true;
+ this.lblSessionHeader.Font = new System.Drawing.Font("Segoe UI Semibold", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblSessionHeader.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(41)))), ((int)(((byte)(59)))));
+ this.lblSessionHeader.Location = new System.Drawing.Point(12, 12);
+ this.lblSessionHeader.Name = "lblSessionHeader";
+ this.lblSessionHeader.Size = new System.Drawing.Size(183, 21);
+ this.lblSessionHeader.TabIndex = 0;
+ this.lblSessionHeader.Text = "Active Session Statistics";
+ //
+ // MainForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.Color.White;
+ this.ClientSize = new System.Drawing.Size(684, 381);
+ this.Controls.Add(this.pnlContent);
+ this.Controls.Add(this.pnlSidebar);
+ this.Controls.Add(this.pnlHeader);
+ this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+ this.MaximizeBox = false;
+ this.Name = "MainForm";
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Authentication & Directory Sandbox";
+ this.Load += new System.EventHandler(this.MainForm_Load);
+ this.pnlHeader.ResumeLayout(false);
+ this.pnlHeader.PerformLayout();
+ this.pnlSidebar.ResumeLayout(false);
+ this.pnlContent.ResumeLayout(false);
+ this.pnlSessionCard.ResumeLayout(false);
+ this.pnlSessionCard.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Panel pnlHeader;
+ private System.Windows.Forms.Label lblTitle;
+ private System.Windows.Forms.Label lblSubtitle;
+ private System.Windows.Forms.Panel pnlSidebar;
+ private System.Windows.Forms.Button btnLogin;
+ private System.Windows.Forms.Button btnLogout;
+ private System.Windows.Forms.Button btnUsers;
+ private System.Windows.Forms.Panel pnlContent;
+ private System.Windows.Forms.Panel pnlSessionCard;
+ private System.Windows.Forms.Label lblSessionHeader;
+ private System.Windows.Forms.Label lblStatusBadge;
+ private System.Windows.Forms.Label lblUsernameValue;
+ private System.Windows.Forms.Label lblUsernameLabel;
+ private System.Windows.Forms.Label lblFullNameValue;
+ private System.Windows.Forms.Label lblFullNameLabel;
+ private System.Windows.Forms.Panel pnlDivider;
+ private System.Windows.Forms.ListBox lstUserPermissions;
+ private System.Windows.Forms.Label lblPermissionsHeader;
+ }
+}
diff --git a/UserPermissionTest_CS_WinForms/MainForm.cs b/UserPermissionTest_CS_WinForms/MainForm.cs
new file mode 100644
index 0000000..71d95eb
--- /dev/null
+++ b/UserPermissionTest_CS_WinForms/MainForm.cs
@@ -0,0 +1,153 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace UserPermissionTest_CS_WinForms
+{
+ public partial class MainForm : Form
+ {
+ public MainForm()
+ {
+ InitializeComponent();
+ }
+
+ private void MainForm_Load(object sender, EventArgs e)
+ {
+ // Subscribe to session state change notifications
+ SessionManager.SessionStateChanged += UpdateSessionUi;
+
+ // Trigger initial UI update
+ UpdateSessionUi();
+ }
+
+ private void UpdateSessionUi()
+ {
+ if (this.InvokeRequired)
+ {
+ this.Invoke(new Action(UpdateSessionUi));
+ return;
+ }
+
+ var user = SessionManager.CurrentUser;
+
+ if (user == null)
+ {
+ // Signed Out State
+ lblStatusBadge.Text = "SIGNED OUT";
+ lblStatusBadge.BackColor = Color.FromArgb(239, 68, 68); // Red
+ lblUsernameValue.Text = "Not Logged In";
+ lblUsernameValue.ForeColor = Color.FromArgb(100, 116, 139); // Slate-400
+ lblFullNameValue.Text = "-";
+
+ lstUserPermissions.Items.Clear();
+ lstUserPermissions.Items.Add("(Sign in to view permissions)");
+
+ btnLogin.Enabled = true;
+ btnLogout.Enabled = false;
+
+ // Authorization: Lock directory when not logged in
+ btnUsers.Enabled = false;
+ btnUsers.Text = "🔒 Manage Users";
+ btnUsers.BackColor = Color.FromArgb(241, 245, 249);
+ }
+ else
+ {
+ // Signed In State
+ lblStatusBadge.Text = "SIGNED IN";
+ lblStatusBadge.BackColor = Color.FromArgb(16, 185, 129); // Green
+ lblUsernameValue.Text = user.Username;
+ lblUsernameValue.ForeColor = Color.FromArgb(15, 23, 42); // Navy-900
+ lblFullNameValue.Text = user.FullName;
+
+ lstUserPermissions.Items.Clear();
+ if (user.Permissions.Count == 0)
+ {
+ lstUserPermissions.Items.Add("(No permissions assigned)");
+ }
+ else
+ {
+ foreach (var permission in user.Permissions)
+ {
+ lstUserPermissions.Items.Add("✓ " + permission);
+ }
+ }
+
+ btnLogin.Enabled = false;
+ btnLogout.Enabled = true;
+
+ // Authorization: Check if user has 'Manage Users' or 'Full Control'
+ bool hasAccess = user.Permissions.Contains("Manage Users") || user.Permissions.Contains("Full Control");
+ if (hasAccess)
+ {
+ btnUsers.Enabled = true;
+ btnUsers.Text = "👥 Manage Users";
+ btnUsers.BackColor = Color.FromArgb(241, 245, 249);
+ }
+ else
+ {
+ btnUsers.Enabled = false;
+ btnUsers.Text = "🔒 Manage Users (Locked)";
+ btnUsers.BackColor = Color.FromArgb(241, 245, 249);
+ }
+ }
+ }
+
+ private void btnLogin_Click(object sender, EventArgs e)
+ {
+ using (var loginDialog = new LoginDialog())
+ {
+ if (loginDialog.ShowDialog(this) == DialogResult.OK)
+ {
+ MessageBox.Show(
+ $"Welcome back, {SessionManager.CurrentUser?.FullName}!",
+ "Sign In Successful",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information);
+ }
+ }
+ }
+
+ private void btnLogout_Click(object sender, EventArgs e)
+ {
+ if (SessionManager.CurrentUser != null)
+ {
+ string username = SessionManager.CurrentUser.Username;
+ SessionManager.Logout();
+ MessageBox.Show(
+ $"User '{username}' has been successfully logged out.",
+ "Signed Out",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information);
+ }
+ }
+
+ private void btnUsers_Click(object sender, EventArgs e)
+ {
+ var user = SessionManager.CurrentUser;
+ if (user == null || (!user.Permissions.Contains("Manage Users") && !user.Permissions.Contains("Full Control")))
+ {
+ MessageBox.Show(
+ "Security Exception: You do not possess the required credentials ('Manage Users' or 'Full Control') to access directory configuration.",
+ "Access Denied",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Stop);
+ return;
+ }
+
+ using (var userSettings = new UserSettings())
+ {
+ userSettings.ShowDialog(this);
+ }
+
+ // After closing settings, refresh the session UI (in case the current user's profile was changed)
+ UpdateSessionUi();
+ }
+
+ protected override void OnFormClosing(FormClosingEventArgs e)
+ {
+ // Unsubscribe to prevent reference leaks
+ SessionManager.SessionStateChanged -= UpdateSessionUi;
+ base.OnFormClosing(e);
+ }
+ }
+}
diff --git a/UserPermissionTest_CS_WinForms/PasswordHasher.cs b/UserPermissionTest_CS_WinForms/PasswordHasher.cs
new file mode 100644
index 0000000..875668b
--- /dev/null
+++ b/UserPermissionTest_CS_WinForms/PasswordHasher.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace UserPermissionTest_CS_WinForms
+{
+ public static class PasswordHasher
+ {
+ ///
+ /// Hashes the plain-text password using SHA-256.
+ ///
+ public static string HashPassword(string password)
+ {
+ if (password == null) throw new ArgumentNullException(nameof(password));
+
+ using (SHA256 sha256 = SHA256.Create())
+ {
+ byte[] bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(password));
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ builder.Append(bytes[i].ToString("x2"));
+ }
+ return builder.ToString();
+ }
+ }
+
+ ///
+ /// Verifies whether the entered plain-text password matches the stored SHA-256 hash.
+ ///
+ public static bool VerifyPassword(string enteredPassword, string storedHash)
+ {
+ if (enteredPassword == null || storedHash == null) return false;
+
+ string hashedInput = HashPassword(enteredPassword);
+ return hashedInput.Equals(storedHash, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}
diff --git a/UserPermissionTest_CS_WinForms/Program.cs b/UserPermissionTest_CS_WinForms/Program.cs
new file mode 100644
index 0000000..1defdcb
--- /dev/null
+++ b/UserPermissionTest_CS_WinForms/Program.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Windows.Forms;
+
+namespace UserPermissionTest_CS_WinForms
+{
+ static class Program
+ {
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new MainForm());
+ }
+ }
+}
diff --git a/UserPermissionTest_CS_WinForms/SessionManager.cs b/UserPermissionTest_CS_WinForms/SessionManager.cs
new file mode 100644
index 0000000..90a3e0e
--- /dev/null
+++ b/UserPermissionTest_CS_WinForms/SessionManager.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace UserPermissionTest_CS_WinForms
+{
+ public static class SessionManager
+ {
+ private static readonly IUserRepository UserRepository = new JsonUserRepository();
+
+ public static User? CurrentUser { get; private set; }
+
+ public static List Users { get; private set; } = new List();
+
+ public static List AvailablePermissions { get; private set; } = new List();
+
+ public static event Action? SessionStateChanged;
+ public static event Action? UsersUpdated;
+
+ static SessionManager()
+ {
+ // Load state from repository
+ Users = UserRepository.LoadUsers();
+ AvailablePermissions = UserRepository.LoadPermissions();
+ }
+
+ public static bool Login(string username, string password)
+ {
+ var user = Users.FirstOrDefault(u =>
+ u.Username.Equals(username, StringComparison.OrdinalIgnoreCase));
+
+ if (user != null && PasswordHasher.VerifyPassword(password, user.Password))
+ {
+ CurrentUser = user;
+ SessionStateChanged?.Invoke();
+ return true;
+ }
+ return false;
+ }
+
+ public static void Logout()
+ {
+ if (CurrentUser != null)
+ {
+ CurrentUser = null;
+ SessionStateChanged?.Invoke();
+ }
+ }
+
+ public static void AddUser(User user)
+ {
+ Users.Add(user);
+ UserRepository.SaveUsers(Users);
+ UsersUpdated?.Invoke();
+ }
+
+ public static void UpdateUser(string oldUsername, User updatedUser)
+ {
+ var index = Users.FindIndex(u => u.Username.Equals(oldUsername, StringComparison.OrdinalIgnoreCase));
+ if (index >= 0)
+ {
+ Users[index] = updatedUser;
+ // If updated user is currently logged in, update current user too
+ if (CurrentUser != null && CurrentUser.Username.Equals(oldUsername, StringComparison.OrdinalIgnoreCase))
+ {
+ CurrentUser = updatedUser;
+ SessionStateChanged?.Invoke();
+ }
+ UserRepository.SaveUsers(Users);
+ UsersUpdated?.Invoke();
+ }
+ }
+
+ public static void DeleteUser(string username)
+ {
+ var user = Users.FirstOrDefault(u => u.Username.Equals(username, StringComparison.OrdinalIgnoreCase));
+ if (user != null)
+ {
+ Users.Remove(user);
+ // If deleted user is logged in, log out
+ if (CurrentUser != null && CurrentUser.Username.Equals(username, StringComparison.OrdinalIgnoreCase))
+ {
+ CurrentUser = null;
+ SessionStateChanged?.Invoke();
+ }
+ UserRepository.SaveUsers(Users);
+ UsersUpdated?.Invoke();
+ }
+ }
+
+ public static void AddPermission(string permission)
+ {
+ if (!AvailablePermissions.Contains(permission))
+ {
+ AvailablePermissions.Add(permission);
+ UserRepository.SavePermissions(AvailablePermissions);
+ UsersUpdated?.Invoke();
+ }
+ }
+ }
+}
diff --git a/UserPermissionTest_CS_WinForms/User.cs b/UserPermissionTest_CS_WinForms/User.cs
new file mode 100644
index 0000000..e5e884d
--- /dev/null
+++ b/UserPermissionTest_CS_WinForms/User.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+
+namespace UserPermissionTest_CS_WinForms
+{
+ public class User
+ {
+ public string Username { get; set; } = string.Empty;
+ public string FullName { get; set; } = string.Empty;
+ public string Password { get; set; } = string.Empty;
+ public List Permissions { get; set; } = new List();
+
+ public User Clone()
+ {
+ return new User
+ {
+ Username = this.Username,
+ FullName = this.FullName,
+ Password = this.Password,
+ Permissions = new List(this.Permissions)
+ };
+ }
+ }
+}
diff --git a/UserPermissionTest_CS_WinForms/UserPermissionTest_CS_WinForms.csproj b/UserPermissionTest_CS_WinForms/UserPermissionTest_CS_WinForms.csproj
new file mode 100644
index 0000000..b42f4cb
--- /dev/null
+++ b/UserPermissionTest_CS_WinForms/UserPermissionTest_CS_WinForms.csproj
@@ -0,0 +1,16 @@
+
+
+
+ WinExe
+ net481
+ true
+ latest
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/UserPermissionTest_CS_WinForms/UserSettings.Designer.cs b/UserPermissionTest_CS_WinForms/UserSettings.Designer.cs
new file mode 100644
index 0000000..9947932
--- /dev/null
+++ b/UserPermissionTest_CS_WinForms/UserSettings.Designer.cs
@@ -0,0 +1,402 @@
+namespace UserPermissionTest_CS_WinForms
+{
+ partial class UserSettings
+ {
+ private System.ComponentModel.IContainer components = null;
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ private void InitializeComponent()
+ {
+ this.pnlHeader = new System.Windows.Forms.Panel();
+ this.lblTitle = new System.Windows.Forms.Label();
+ this.lblSubtitle = new System.Windows.Forms.Label();
+ this.splitContainer = new System.Windows.Forms.SplitContainer();
+ this.btnDeleteUser = new System.Windows.Forms.Button();
+ this.btnAddUser = new System.Windows.Forms.Button();
+ this.lstUsers = new System.Windows.Forms.ListBox();
+ this.lblUsersList = new System.Windows.Forms.Label();
+ this.grpPermissions = new System.Windows.Forms.GroupBox();
+ this.btnAddNewPermission = new System.Windows.Forms.Button();
+ this.txtNewPermission = new System.Windows.Forms.TextBox();
+ this.lblNewPermission = new System.Windows.Forms.Label();
+ this.chkPermissions = new System.Windows.Forms.CheckedListBox();
+ this.btnSaveUser = new System.Windows.Forms.Button();
+ this.txtPassword = new System.Windows.Forms.TextBox();
+ this.btnTogglePassword = new System.Windows.Forms.Button();
+ this.lblPassword = new System.Windows.Forms.Label();
+ this.txtFullName = new System.Windows.Forms.TextBox();
+ this.lblFullName = new System.Windows.Forms.Label();
+ this.txtUsername = new System.Windows.Forms.TextBox();
+ this.lblUsername = new System.Windows.Forms.Label();
+ this.lblDetailsHeader = new System.Windows.Forms.Label();
+ this.pnlHeader.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
+ this.splitContainer.Panel1.SuspendLayout();
+ this.splitContainer.Panel2.SuspendLayout();
+ this.splitContainer.SuspendLayout();
+ this.grpPermissions.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // pnlHeader
+ //
+ this.pnlHeader.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(41)))), ((int)(((byte)(59)))));
+ this.pnlHeader.Controls.Add(this.lblTitle);
+ this.pnlHeader.Controls.Add(this.lblSubtitle);
+ this.pnlHeader.Dock = System.Windows.Forms.DockStyle.Top;
+ this.pnlHeader.Location = new System.Drawing.Point(0, 0);
+ this.pnlHeader.Name = "pnlHeader";
+ this.pnlHeader.Size = new System.Drawing.Size(834, 80);
+ this.pnlHeader.TabIndex = 0;
+ //
+ // lblTitle
+ //
+ this.lblTitle.AutoSize = true;
+ this.lblTitle.Font = new System.Drawing.Font("Segoe UI Semibold", 16F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblTitle.ForeColor = System.Drawing.Color.White;
+ this.lblTitle.Location = new System.Drawing.Point(20, 15);
+ this.lblTitle.Name = "lblTitle";
+ this.lblTitle.Size = new System.Drawing.Size(262, 30);
+ this.lblTitle.TabIndex = 0;
+ this.lblTitle.Text = "UserSettings && Directory";
+ //
+ // lblSubtitle
+ //
+ this.lblSubtitle.AutoSize = true;
+ this.lblSubtitle.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblSubtitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(148)))), ((int)(((byte)(163)))), ((int)(((byte)(184)))));
+ this.lblSubtitle.Location = new System.Drawing.Point(22, 45);
+ this.lblSubtitle.Name = "lblSubtitle";
+ this.lblSubtitle.Size = new System.Drawing.Size(325, 15);
+ this.lblSubtitle.TabIndex = 1;
+ this.lblSubtitle.Text = "Manage user credentials, personal details, and security roles.";
+ //
+ // splitContainer
+ //
+ this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.splitContainer.Location = new System.Drawing.Point(0, 80);
+ this.splitContainer.Name = "splitContainer";
+ //
+ // splitContainer.Panel1
+ //
+ this.splitContainer.Panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(250)))), ((int)(((byte)(252)))));
+ this.splitContainer.Panel1.Controls.Add(this.btnDeleteUser);
+ this.splitContainer.Panel1.Controls.Add(this.btnAddUser);
+ this.splitContainer.Panel1.Controls.Add(this.lstUsers);
+ this.splitContainer.Panel1.Controls.Add(this.lblUsersList);
+ this.splitContainer.Panel1.Padding = new System.Windows.Forms.Padding(15);
+ //
+ // splitContainer.Panel2
+ //
+ this.splitContainer.Panel2.BackColor = System.Drawing.Color.White;
+ this.splitContainer.Panel2.Controls.Add(this.grpPermissions);
+ this.splitContainer.Panel2.Controls.Add(this.btnSaveUser);
+ this.splitContainer.Panel2.Controls.Add(this.txtPassword);
+ this.splitContainer.Panel2.Controls.Add(this.btnTogglePassword);
+ this.splitContainer.Panel2.Controls.Add(this.lblPassword);
+ this.splitContainer.Panel2.Controls.Add(this.txtFullName);
+ this.splitContainer.Panel2.Controls.Add(this.lblFullName);
+ this.splitContainer.Panel2.Controls.Add(this.txtUsername);
+ this.splitContainer.Panel2.Controls.Add(this.lblUsername);
+ this.splitContainer.Panel2.Controls.Add(this.lblDetailsHeader);
+ this.splitContainer.Panel2.Padding = new System.Windows.Forms.Padding(20);
+ this.splitContainer.Size = new System.Drawing.Size(834, 461);
+ this.splitContainer.SplitterDistance = 250;
+ this.splitContainer.TabIndex = 1;
+ //
+ // btnDeleteUser
+ //
+ this.btnDeleteUser.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(254)))), ((int)(((byte)(242)))), ((int)(((byte)(242)))));
+ this.btnDeleteUser.Cursor = System.Windows.Forms.Cursors.Hand;
+ this.btnDeleteUser.FlatAppearance.BorderSize = 0;
+ this.btnDeleteUser.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.btnDeleteUser.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnDeleteUser.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(239)))), ((int)(((byte)(68)))), ((int)(((byte)(68)))));
+ this.btnDeleteUser.Location = new System.Drawing.Point(130, 410);
+ this.btnDeleteUser.Name = "btnDeleteUser";
+ this.btnDeleteUser.Size = new System.Drawing.Size(105, 36);
+ this.btnDeleteUser.TabIndex = 3;
+ this.btnDeleteUser.Text = "Delete User";
+ this.btnDeleteUser.UseVisualStyleBackColor = false;
+ this.btnDeleteUser.Click += new System.EventHandler(this.btnDeleteUser_Click);
+ //
+ // btnAddUser
+ //
+ this.btnAddUser.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(99)))), ((int)(((byte)(102)))), ((int)(((byte)(241)))));
+ this.btnAddUser.Cursor = System.Windows.Forms.Cursors.Hand;
+ this.btnAddUser.FlatAppearance.BorderSize = 0;
+ this.btnAddUser.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.btnAddUser.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnAddUser.ForeColor = System.Drawing.Color.White;
+ this.btnAddUser.Location = new System.Drawing.Point(15, 410);
+ this.btnAddUser.Name = "btnAddUser";
+ this.btnAddUser.Size = new System.Drawing.Size(105, 36);
+ this.btnAddUser.TabIndex = 2;
+ this.btnAddUser.Text = "+ Add User";
+ this.btnAddUser.UseVisualStyleBackColor = false;
+ this.btnAddUser.Click += new System.EventHandler(this.btnAddUser_Click);
+ //
+ // lstUsers
+ //
+ this.lstUsers.BackColor = System.Drawing.Color.White;
+ this.lstUsers.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
+ this.lstUsers.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lstUsers.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(15)))), ((int)(((byte)(23)))), ((int)(((byte)(42)))));
+ this.lstUsers.FormattingEnabled = true;
+ this.lstUsers.ItemHeight = 17;
+ this.lstUsers.Location = new System.Drawing.Point(15, 40);
+ this.lstUsers.Name = "lstUsers";
+ this.lstUsers.Size = new System.Drawing.Size(220, 359);
+ this.lstUsers.TabIndex = 1;
+ this.lstUsers.SelectedIndexChanged += new System.EventHandler(this.lstUsers_SelectedIndexChanged);
+ //
+ // lblUsersList
+ //
+ this.lblUsersList.AutoSize = true;
+ this.lblUsersList.Font = new System.Drawing.Font("Segoe UI Semibold", 11F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblUsersList.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(51)))), ((int)(((byte)(65)))), ((int)(((byte)(85)))));
+ this.lblUsersList.Location = new System.Drawing.Point(15, 12);
+ this.lblUsersList.Name = "lblUsersList";
+ this.lblUsersList.Size = new System.Drawing.Size(114, 20);
+ this.lblUsersList.TabIndex = 0;
+ this.lblUsersList.Text = "Users Directory";
+ //
+ // grpPermissions
+ //
+ this.grpPermissions.Controls.Add(this.btnAddNewPermission);
+ this.grpPermissions.Controls.Add(this.txtNewPermission);
+ this.grpPermissions.Controls.Add(this.lblNewPermission);
+ this.grpPermissions.Controls.Add(this.chkPermissions);
+ this.grpPermissions.Font = new System.Drawing.Font("Segoe UI Semibold", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.grpPermissions.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(51)))), ((int)(((byte)(65)))), ((int)(((byte)(85)))));
+ this.grpPermissions.Location = new System.Drawing.Point(240, 50);
+ this.grpPermissions.Name = "grpPermissions";
+ this.grpPermissions.Size = new System.Drawing.Size(320, 335);
+ this.grpPermissions.TabIndex = 8;
+ this.grpPermissions.TabStop = false;
+ this.grpPermissions.Text = "User Permissions";
+ //
+ // btnAddNewPermission
+ //
+ this.btnAddNewPermission.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(245)))), ((int)(((byte)(249)))));
+ this.btnAddNewPermission.Cursor = System.Windows.Forms.Cursors.Hand;
+ this.btnAddNewPermission.FlatAppearance.BorderSize = 0;
+ this.btnAddNewPermission.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.btnAddNewPermission.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnAddNewPermission.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(71)))), ((int)(((byte)(85)))), ((int)(((byte)(105)))));
+ this.btnAddNewPermission.Location = new System.Drawing.Point(240, 292);
+ this.btnAddNewPermission.Name = "btnAddNewPermission";
+ this.btnAddNewPermission.Size = new System.Drawing.Size(65, 27);
+ this.btnAddNewPermission.TabIndex = 3;
+ this.btnAddNewPermission.Text = "Add";
+ this.btnAddNewPermission.UseVisualStyleBackColor = false;
+ this.btnAddNewPermission.Click += new System.EventHandler(this.btnAddNewPermission_Click);
+ //
+ // txtNewPermission
+ //
+ this.txtNewPermission.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(250)))), ((int)(((byte)(252)))));
+ this.txtNewPermission.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtNewPermission.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(15)))), ((int)(((byte)(23)))), ((int)(((byte)(42)))));
+ this.txtNewPermission.Location = new System.Drawing.Point(15, 293);
+ this.txtNewPermission.Name = "txtNewPermission";
+ this.txtNewPermission.Size = new System.Drawing.Size(220, 25);
+ this.txtNewPermission.TabIndex = 2;
+ //
+ // lblNewPermission
+ //
+ this.lblNewPermission.AutoSize = true;
+ this.lblNewPermission.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblNewPermission.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(116)))), ((int)(((byte)(139)))));
+ this.lblNewPermission.Location = new System.Drawing.Point(15, 275);
+ this.lblNewPermission.Name = "lblNewPermission";
+ this.lblNewPermission.Size = new System.Drawing.Size(127, 13);
+ this.lblNewPermission.TabIndex = 1;
+ this.lblNewPermission.Text = "Define New Permission:";
+ //
+ // chkPermissions
+ //
+ this.chkPermissions.BackColor = System.Drawing.Color.White;
+ this.chkPermissions.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.chkPermissions.CheckOnClick = true;
+ this.chkPermissions.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.chkPermissions.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(15)))), ((int)(((byte)(23)))), ((int)(((byte)(42)))));
+ this.chkPermissions.FormattingEnabled = true;
+ this.chkPermissions.Location = new System.Drawing.Point(15, 25);
+ this.chkPermissions.Name = "chkPermissions";
+ this.chkPermissions.Size = new System.Drawing.Size(290, 240);
+ this.chkPermissions.TabIndex = 0;
+ //
+ // btnSaveUser
+ //
+ this.btnSaveUser.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(16)))), ((int)(((byte)(185)))), ((int)(((byte)(129)))));
+ this.btnSaveUser.Cursor = System.Windows.Forms.Cursors.Hand;
+ this.btnSaveUser.FlatAppearance.BorderSize = 0;
+ this.btnSaveUser.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.btnSaveUser.Font = new System.Drawing.Font("Segoe UI Semibold", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnSaveUser.ForeColor = System.Drawing.Color.White;
+ this.btnSaveUser.Location = new System.Drawing.Point(20, 395);
+ this.btnSaveUser.Name = "btnSaveUser";
+ this.btnSaveUser.Size = new System.Drawing.Size(540, 40);
+ this.btnSaveUser.TabIndex = 7;
+ this.btnSaveUser.Text = "Save Profile Changes";
+ this.btnSaveUser.UseVisualStyleBackColor = false;
+ this.btnSaveUser.Click += new System.EventHandler(this.btnSaveUser_Click);
+ //
+ // txtPassword
+ //
+ this.txtPassword.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(250)))), ((int)(((byte)(252)))));
+ this.txtPassword.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtPassword.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(15)))), ((int)(((byte)(23)))), ((int)(((byte)(42)))));
+ this.txtPassword.Location = new System.Drawing.Point(20, 205);
+ this.txtPassword.Name = "txtPassword";
+ this.txtPassword.PasswordChar = '•';
+ this.txtPassword.Size = new System.Drawing.Size(165, 25);
+ this.txtPassword.TabIndex = 6;
+ //
+ // btnTogglePassword
+ //
+ this.btnTogglePassword.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(241)))), ((int)(((byte)(245)))), ((int)(((byte)(249)))));
+ this.btnTogglePassword.Cursor = System.Windows.Forms.Cursors.Hand;
+ this.btnTogglePassword.FlatAppearance.BorderSize = 0;
+ this.btnTogglePassword.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.btnTogglePassword.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.btnTogglePassword.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(71)))), ((int)(((byte)(85)))), ((int)(((byte)(105)))));
+ this.btnTogglePassword.Location = new System.Drawing.Point(190, 205);
+ this.btnTogglePassword.Name = "btnTogglePassword";
+ this.btnTogglePassword.Size = new System.Drawing.Size(30, 25);
+ this.btnTogglePassword.TabIndex = 9;
+ this.btnTogglePassword.Text = "👁";
+ this.btnTogglePassword.UseVisualStyleBackColor = false;
+ this.btnTogglePassword.Click += new System.EventHandler(this.btnTogglePassword_Click);
+ //
+ // lblPassword
+ //
+ this.lblPassword.AutoSize = true;
+ this.lblPassword.Font = new System.Drawing.Font("Segoe UI Semibold", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblPassword.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(71)))), ((int)(((byte)(85)))), ((int)(((byte)(105)))));
+ this.lblPassword.Location = new System.Drawing.Point(20, 185);
+ this.lblPassword.Name = "lblPassword";
+ this.lblPassword.Size = new System.Drawing.Size(66, 17);
+ this.lblPassword.TabIndex = 5;
+ this.lblPassword.Text = "Password";
+ //
+ // txtFullName
+ //
+ this.txtFullName.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(250)))), ((int)(((byte)(252)))));
+ this.txtFullName.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtFullName.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(15)))), ((int)(((byte)(23)))), ((int)(((byte)(42)))));
+ this.txtFullName.Location = new System.Drawing.Point(20, 145);
+ this.txtFullName.Name = "txtFullName";
+ this.txtFullName.Size = new System.Drawing.Size(200, 25);
+ this.txtFullName.TabIndex = 4;
+ //
+ // lblFullName
+ //
+ this.lblFullName.AutoSize = true;
+ this.lblFullName.Font = new System.Drawing.Font("Segoe UI Semibold", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblFullName.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(71)))), ((int)(((byte)(85)))), ((int)(((byte)(105)))));
+ this.lblFullName.Location = new System.Drawing.Point(20, 125);
+ this.lblFullName.Name = "lblFullName";
+ this.lblFullName.Size = new System.Drawing.Size(69, 17);
+ this.lblFullName.TabIndex = 3;
+ this.lblFullName.Text = "Full Name";
+ //
+ // txtUsername
+ //
+ this.txtUsername.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(250)))), ((int)(((byte)(252)))));
+ this.txtUsername.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.txtUsername.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(15)))), ((int)(((byte)(23)))), ((int)(((byte)(42)))));
+ this.txtUsername.Location = new System.Drawing.Point(20, 85);
+ this.txtUsername.Name = "txtUsername";
+ this.txtUsername.Size = new System.Drawing.Size(200, 25);
+ this.txtUsername.TabIndex = 2;
+ //
+ // lblUsername
+ //
+ this.lblUsername.AutoSize = true;
+ this.lblUsername.Font = new System.Drawing.Font("Segoe UI Semibold", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblUsername.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(71)))), ((int)(((byte)(85)))), ((int)(((byte)(105)))));
+ this.lblUsername.Location = new System.Drawing.Point(20, 65);
+ this.lblUsername.Name = "lblUsername";
+ this.lblUsername.Size = new System.Drawing.Size(75, 17);
+ this.lblUsername.TabIndex = 1;
+ this.lblUsername.Text = "User Name";
+ //
+ // lblDetailsHeader
+ //
+ this.lblDetailsHeader.AutoSize = true;
+ this.lblDetailsHeader.Font = new System.Drawing.Font("Segoe UI Semibold", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.lblDetailsHeader.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(30)))), ((int)(((byte)(41)))), ((int)(((byte)(59)))));
+ this.lblDetailsHeader.Location = new System.Drawing.Point(16, 12);
+ this.lblDetailsHeader.Name = "lblDetailsHeader";
+ this.lblDetailsHeader.Size = new System.Drawing.Size(150, 21);
+ this.lblDetailsHeader.TabIndex = 0;
+ this.lblDetailsHeader.Text = "User Profile Details";
+ //
+ // UserSettings
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.Color.White;
+ this.ClientSize = new System.Drawing.Size(834, 541);
+ this.Controls.Add(this.splitContainer);
+ this.Controls.Add(this.pnlHeader);
+ this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "UserSettings";
+ this.ShowIcon = false;
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Directory & Security Manager";
+ this.Load += new System.EventHandler(this.UserSettings_Load);
+ this.pnlHeader.ResumeLayout(false);
+ this.pnlHeader.PerformLayout();
+ this.splitContainer.Panel1.ResumeLayout(false);
+ this.splitContainer.Panel1.PerformLayout();
+ this.splitContainer.Panel2.ResumeLayout(false);
+ this.splitContainer.Panel2.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.splitContainer)).EndInit();
+ this.splitContainer.ResumeLayout(false);
+ this.grpPermissions.ResumeLayout(false);
+ this.grpPermissions.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Panel pnlHeader;
+ private System.Windows.Forms.Label lblTitle;
+ private System.Windows.Forms.Label lblSubtitle;
+ private System.Windows.Forms.SplitContainer splitContainer;
+ private System.Windows.Forms.Label lblUsersList;
+ private System.Windows.Forms.ListBox lstUsers;
+ private System.Windows.Forms.Button btnDeleteUser;
+ private System.Windows.Forms.Button btnAddUser;
+ private System.Windows.Forms.Label lblDetailsHeader;
+ private System.Windows.Forms.TextBox txtUsername;
+ private System.Windows.Forms.Label lblUsername;
+ private System.Windows.Forms.TextBox txtPassword;
+ private System.Windows.Forms.Label lblPassword;
+ private System.Windows.Forms.TextBox txtFullName;
+ private System.Windows.Forms.Label lblFullName;
+ private System.Windows.Forms.Button btnSaveUser;
+ private System.Windows.Forms.GroupBox grpPermissions;
+ private System.Windows.Forms.CheckedListBox chkPermissions;
+ private System.Windows.Forms.Button btnAddNewPermission;
+ private System.Windows.Forms.TextBox txtNewPermission;
+ private System.Windows.Forms.Label lblNewPermission;
+ private System.Windows.Forms.Button btnTogglePassword;
+ }
+}
diff --git a/UserPermissionTest_CS_WinForms/UserSettings.cs b/UserPermissionTest_CS_WinForms/UserSettings.cs
new file mode 100644
index 0000000..528d202
--- /dev/null
+++ b/UserPermissionTest_CS_WinForms/UserSettings.cs
@@ -0,0 +1,385 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace UserPermissionTest_CS_WinForms
+{
+ public partial class UserSettings : Form
+ {
+ private string? _editingUsername = null; // null means we are adding a new user, otherwise contains the original username of the user being edited.
+ private bool _isPopulatingFields = false;
+
+ public UserSettings()
+ {
+ InitializeComponent();
+ }
+
+ private void UserSettings_Load(object sender, EventArgs e)
+ {
+ RefreshUserList();
+
+ // Register to updates to keep UI synchronized in real-time
+ SessionManager.UsersUpdated += SessionManager_UsersUpdated;
+
+ if (lstUsers.Items.Count > 0)
+ {
+ lstUsers.SelectedIndex = 0;
+ }
+ else
+ {
+ ClearDetailsForm();
+ }
+ }
+
+ private void SessionManager_UsersUpdated()
+ {
+ // Safeguard for cross-thread calls, though in WinForms it's usually on the UI thread
+ if (this.InvokeRequired)
+ {
+ this.Invoke(new Action(RefreshUserList));
+ }
+ else
+ {
+ RefreshUserList();
+ }
+ }
+
+ private void RefreshUserList()
+ {
+ _isPopulatingFields = true;
+
+ string? currentSelection = lstUsers.SelectedItem?.ToString();
+ lstUsers.Items.Clear();
+
+ foreach (var user in SessionManager.Users)
+ {
+ lstUsers.Items.Add(user.Username);
+ }
+
+ // Restore selection if possible
+ if (currentSelection != null && lstUsers.Items.Contains(currentSelection))
+ {
+ lstUsers.SelectedItem = currentSelection;
+ }
+ else if (lstUsers.Items.Count > 0)
+ {
+ lstUsers.SelectedIndex = 0;
+ }
+ else
+ {
+ ClearDetailsForm();
+ }
+
+ _isPopulatingFields = false;
+ RefreshPermissionList();
+ }
+
+ private void RefreshPermissionList()
+ {
+ // Save currently checked permissions
+ var checkedPerms = new HashSet();
+ for (int i = 0; i < chkPermissions.Items.Count; i++)
+ {
+ if (chkPermissions.GetItemChecked(i))
+ {
+ string? itemText = chkPermissions.Items[i]?.ToString();
+ if (itemText != null)
+ {
+ checkedPerms.Add(itemText);
+ }
+ }
+ }
+
+ chkPermissions.Items.Clear();
+ foreach (var permission in SessionManager.AvailablePermissions)
+ {
+ chkPermissions.Items.Add(permission);
+ }
+
+ // If we are editing a user, check their specific permissions
+ if (_editingUsername != null)
+ {
+ var user = SessionManager.Users.FirstOrDefault(u => u.Username.Equals(_editingUsername, StringComparison.OrdinalIgnoreCase));
+ if (user != null)
+ {
+ for (int i = 0; i < chkPermissions.Items.Count; i++)
+ {
+ string? permissionName = chkPermissions.Items[i].ToString();
+ if (permissionName != null && user.Permissions.Contains(permissionName))
+ {
+ chkPermissions.SetItemChecked(i, true);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Otherwise, restore the checks that the administrator was in the middle of selecting
+ for (int i = 0; i < chkPermissions.Items.Count; i++)
+ {
+ string? permissionName = chkPermissions.Items[i].ToString();
+ if (permissionName != null && checkedPerms.Contains(permissionName))
+ {
+ chkPermissions.SetItemChecked(i, true);
+ }
+ }
+ }
+ }
+
+ private void lstUsers_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (_isPopulatingFields) return;
+
+ string? selectedUsername = lstUsers.SelectedItem?.ToString();
+ if (selectedUsername != null)
+ {
+ var user = SessionManager.Users.FirstOrDefault(u => u.Username.Equals(selectedUsername, StringComparison.OrdinalIgnoreCase));
+ if (user != null)
+ {
+ PopulateUserDetails(user);
+ }
+ }
+ else
+ {
+ ClearDetailsForm();
+ }
+ }
+
+ private void PopulateUserDetails(User user)
+ {
+ _isPopulatingFields = true;
+ _editingUsername = user.Username;
+
+ txtUsername.Text = user.Username;
+ txtFullName.Text = user.FullName;
+
+ // Mask the actual password on display with a special visual placeholder
+ txtPassword.Text = "●●●●●●●●";
+ txtPassword.PasswordChar = '•';
+ btnTogglePassword.Text = "👁";
+
+ // Update permissions CheckedListBox
+ for (int i = 0; i < chkPermissions.Items.Count; i++)
+ {
+ string? permissionName = chkPermissions.Items[i].ToString();
+ bool hasPermission = permissionName != null && user.Permissions.Contains(permissionName);
+ chkPermissions.SetItemChecked(i, hasPermission);
+ }
+
+ lblDetailsHeader.Text = $"User Profile: {user.Username}";
+ btnDeleteUser.Enabled = true;
+ _isPopulatingFields = false;
+ }
+
+ private void ClearDetailsForm()
+ {
+ _isPopulatingFields = true;
+ _editingUsername = null;
+
+ txtUsername.Text = string.Empty;
+ txtFullName.Text = string.Empty;
+ txtPassword.Text = string.Empty;
+ txtPassword.PasswordChar = '•';
+ btnTogglePassword.Text = "👁";
+
+ for (int i = 0; i < chkPermissions.Items.Count; i++)
+ {
+ chkPermissions.SetItemChecked(i, false);
+ }
+
+ lblDetailsHeader.Text = "New User Profile Setup";
+ btnDeleteUser.Enabled = false;
+ _isPopulatingFields = false;
+ }
+
+ private void btnAddUser_Click(object sender, EventArgs e)
+ {
+ lstUsers.ClearSelected();
+ ClearDetailsForm();
+ txtUsername.Focus();
+ }
+
+ private void btnDeleteUser_Click(object sender, EventArgs e)
+ {
+ if (_editingUsername == null) return;
+
+ var result = MessageBox.Show(
+ $"Are you sure you want to delete the user '{_editingUsername}'?",
+ "Confirm Deletion",
+ MessageBoxButtons.YesNo,
+ MessageBoxIcon.Warning);
+
+ if (result == DialogResult.Yes)
+ {
+ SessionManager.DeleteUser(_editingUsername);
+ }
+ }
+
+ private void btnSaveUser_Click(object sender, EventArgs e)
+ {
+ string username = txtUsername.Text.Trim();
+ string fullName = txtFullName.Text.Trim();
+ string password = txtPassword.Text;
+
+ if (string.IsNullOrEmpty(username))
+ {
+ MessageBox.Show("Username cannot be empty.", "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ txtUsername.Focus();
+ return;
+ }
+
+ if (string.IsNullOrEmpty(fullName))
+ {
+ MessageBox.Show("Full Name cannot be empty.", "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ txtFullName.Focus();
+ return;
+ }
+
+ if (string.IsNullOrEmpty(password))
+ {
+ MessageBox.Show("Password cannot be empty.", "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ txtPassword.Focus();
+ return;
+ }
+
+ // Check duplicate username
+ bool isNewUser = _editingUsername == null;
+ bool usernameExists = SessionManager.Users.Any(u => u.Username.Equals(username, StringComparison.OrdinalIgnoreCase));
+
+ // Determine final password hash to save
+ string finalPasswordHash;
+ if (isNewUser)
+ {
+ if (usernameExists)
+ {
+ MessageBox.Show($"A user with username '{username}' already exists.", "Duplicate Username", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ txtUsername.Focus();
+ return;
+ }
+
+ if (password == "●●●●●●●●")
+ {
+ MessageBox.Show("Please enter a valid password for the new user.", "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ txtPassword.Focus();
+ return;
+ }
+
+ finalPasswordHash = PasswordHasher.HashPassword(password);
+
+ var newUser = new User
+ {
+ Username = username,
+ FullName = fullName,
+ Password = finalPasswordHash,
+ Permissions = GetCheckedPermissions()
+ };
+
+ SessionManager.AddUser(newUser);
+ MessageBox.Show($"User '{username}' created successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
+
+ // Select the newly created user
+ lstUsers.SelectedItem = username;
+ }
+ else
+ {
+ // Editing existing user
+ if (!username.Equals(_editingUsername, StringComparison.OrdinalIgnoreCase) && usernameExists)
+ {
+ MessageBox.Show($"A user with username '{username}' already exists.", "Duplicate Username", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ txtUsername.Focus();
+ return;
+ }
+
+ var existingUser = SessionManager.Users.FirstOrDefault(u => u.Username.Equals(_editingUsername, StringComparison.OrdinalIgnoreCase));
+ if (password == "●●●●●●●●" || string.IsNullOrEmpty(password))
+ {
+ // Keep existing hashed password
+ finalPasswordHash = existingUser != null ? existingUser.Password : PasswordHasher.HashPassword("admin");
+ }
+ else
+ {
+ // Hash new password
+ finalPasswordHash = PasswordHasher.HashPassword(password);
+ }
+
+ var updatedUser = new User
+ {
+ Username = username,
+ FullName = fullName,
+ Password = finalPasswordHash,
+ Permissions = GetCheckedPermissions()
+ };
+
+ string originalName = _editingUsername!;
+ SessionManager.UpdateUser(originalName, updatedUser);
+ MessageBox.Show($"User details for '{username}' updated successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
+
+ // Keep the selection updated
+ lstUsers.SelectedItem = username;
+ }
+ }
+
+ private void btnTogglePassword_Click(object sender, EventArgs e)
+ {
+ if (txtPassword.PasswordChar == '•')
+ {
+ txtPassword.PasswordChar = '\0'; // Show password
+ btnTogglePassword.Text = "🙈";
+ }
+ else
+ {
+ txtPassword.PasswordChar = '•'; // Mask password
+ btnTogglePassword.Text = "👁";
+ }
+ }
+
+ private List GetCheckedPermissions()
+ {
+ var list = new List();
+ for (int i = 0; i < chkPermissions.Items.Count; i++)
+ {
+ if (chkPermissions.GetItemChecked(i))
+ {
+ string? permissionName = chkPermissions.Items[i].ToString();
+ if (permissionName != null)
+ {
+ list.Add(permissionName);
+ }
+ }
+ }
+ return list;
+ }
+
+ private void btnAddNewPermission_Click(object sender, EventArgs e)
+ {
+ string newPerm = txtNewPermission.Text.Trim();
+ if (string.IsNullOrEmpty(newPerm))
+ {
+ MessageBox.Show("Please enter a permission name.", "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ txtNewPermission.Focus();
+ return;
+ }
+
+ if (SessionManager.AvailablePermissions.Contains(newPerm, StringComparer.OrdinalIgnoreCase))
+ {
+ MessageBox.Show($"Permission '{newPerm}' already exists in the system.", "Duplicate Permission", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ txtNewPermission.Focus();
+ return;
+ }
+
+ SessionManager.AddPermission(newPerm);
+ txtNewPermission.Clear();
+ RefreshPermissionList();
+
+ MessageBox.Show($"New permission '{newPerm}' registered. You can now assign it to users.", "Permission Registered", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ }
+
+ protected override void OnFormClosing(FormClosingEventArgs e)
+ {
+ // Unsubscribe to prevent memory leaks
+ SessionManager.UsersUpdated -= SessionManager_UsersUpdated;
+ base.OnFormClosing(e);
+ }
+ }
+}
diff --git a/UserPermissionTesting_CS_WinForms/App.config b/UserPermissionTesting_CS_WinForms/App.config
deleted file mode 100644
index aee9adf..0000000
--- a/UserPermissionTesting_CS_WinForms/App.config
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/UserPermissionTesting_CS_WinForms/Form1.Designer.cs b/UserPermissionTesting_CS_WinForms/Form1.Designer.cs
deleted file mode 100644
index ecfa8e3..0000000
--- a/UserPermissionTesting_CS_WinForms/Form1.Designer.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-namespace UserPermissionTesting_CS_WinForms
-{
- partial class Form1
- {
- ///
- /// Required designer variable.
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// Clean up any resources being used.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing)
- {
- if (disposing && (components != null))
- {
- components.Dispose();
- }
- base.Dispose(disposing);
- }
-
- #region Windows Form Designer generated code
-
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent()
- {
- this.components = new System.ComponentModel.Container();
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(800, 450);
- this.Text = "Form1";
- }
-
- #endregion
- }
-}
-
diff --git a/UserPermissionTesting_CS_WinForms/Form1.cs b/UserPermissionTesting_CS_WinForms/Form1.cs
deleted file mode 100644
index 80c8397..0000000
--- a/UserPermissionTesting_CS_WinForms/Form1.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Data;
-using System.Drawing;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-
-namespace UserPermissionTesting_CS_WinForms
-{
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- }
- }
-}
diff --git a/UserPermissionTesting_CS_WinForms/Program.cs b/UserPermissionTesting_CS_WinForms/Program.cs
deleted file mode 100644
index 27dd9de..0000000
--- a/UserPermissionTesting_CS_WinForms/Program.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-
-namespace UserPermissionTesting_CS_WinForms
-{
- internal static class Program
- {
- ///
- /// The main entry point for the application.
- ///
- [STAThread]
- static void Main()
- {
- Application.EnableVisualStyles();
- Application.SetCompatibleTextRenderingDefault(false);
- Application.Run(new Form1());
- }
- }
-}
diff --git a/UserPermissionTesting_CS_WinForms/Properties/AssemblyInfo.cs b/UserPermissionTesting_CS_WinForms/Properties/AssemblyInfo.cs
deleted file mode 100644
index 1125d81..0000000
--- a/UserPermissionTesting_CS_WinForms/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("UserPermissionTesting_CS_WinForms")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("UserPermissionTesting_CS_WinForms")]
-[assembly: AssemblyCopyright("Copyright © 2026")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("64cf267a-0840-43c4-a915-2fe9d9874530")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/UserPermissionTesting_CS_WinForms/Properties/Resources.Designer.cs b/UserPermissionTesting_CS_WinForms/Properties/Resources.Designer.cs
deleted file mode 100644
index 9a1b708..0000000
--- a/UserPermissionTesting_CS_WinForms/Properties/Resources.Designer.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace UserPermissionTesting_CS_WinForms.Properties
-{
-
-
- ///
- /// A strongly-typed resource class, for looking up localized strings, etc.
- ///
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resources
- {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resources()
- {
- }
-
- ///
- /// Returns the cached ResourceManager instance used by this class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager
- {
- get
- {
- if ((resourceMan == null))
- {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("UserPermissionTesting_CS_WinForms.Properties.Resources", typeof(Resources).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- ///
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture
- {
- get
- {
- return resourceCulture;
- }
- set
- {
- resourceCulture = value;
- }
- }
- }
-}
diff --git a/UserPermissionTesting_CS_WinForms/Properties/Resources.resx b/UserPermissionTesting_CS_WinForms/Properties/Resources.resx
deleted file mode 100644
index af7dbeb..0000000
--- a/UserPermissionTesting_CS_WinForms/Properties/Resources.resx
+++ /dev/null
@@ -1,117 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
\ No newline at end of file
diff --git a/UserPermissionTesting_CS_WinForms/Properties/Settings.Designer.cs b/UserPermissionTesting_CS_WinForms/Properties/Settings.Designer.cs
deleted file mode 100644
index 1f9f927..0000000
--- a/UserPermissionTesting_CS_WinForms/Properties/Settings.Designer.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace UserPermissionTesting_CS_WinForms.Properties
-{
-
-
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
- internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
- {
-
- private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
-
- public static Settings Default
- {
- get
- {
- return defaultInstance;
- }
- }
- }
-}
diff --git a/UserPermissionTesting_CS_WinForms/Properties/Settings.settings b/UserPermissionTesting_CS_WinForms/Properties/Settings.settings
deleted file mode 100644
index 3964565..0000000
--- a/UserPermissionTesting_CS_WinForms/Properties/Settings.settings
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/UserPermissionTesting_CS_WinForms/UserPermissionTesting_CS_WinForms.csproj b/UserPermissionTesting_CS_WinForms/UserPermissionTesting_CS_WinForms.csproj
deleted file mode 100644
index 92fb92d..0000000
--- a/UserPermissionTesting_CS_WinForms/UserPermissionTesting_CS_WinForms.csproj
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
- Debug
- AnyCPU
- {64CF267A-0840-43C4-A915-2FE9D9874530}
- WinExe
- UserPermissionTesting_CS_WinForms
- UserPermissionTesting_CS_WinForms
- v4.8.1
- 512
- true
- true
-
-
- AnyCPU
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
- AnyCPU
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Form
-
-
- Form1.cs
-
-
-
-
- ResXFileCodeGenerator
- Resources.Designer.cs
- Designer
-
-
- True
- Resources.resx
-
-
- SettingsSingleFileGenerator
- Settings.Designer.cs
-
-
- True
- Settings.settings
- True
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/winforms_security_sandbox.md b/winforms_security_sandbox.md
new file mode 100644
index 0000000..993dec1
--- /dev/null
+++ b/winforms_security_sandbox.md
@@ -0,0 +1,320 @@
+# 📄 .NET 4.8.1 WinForms Authentication & Security Sandbox Manual
+
+Welcome to the technical documentation of the C# WinForms Authentication & Security Sandbox. This manual is organized into two complete sections: **Section 1 in English** and **Bölüm 2 in Turkish**.
+
+---
+
+# SECTION 1: English (Technical Manual)
+
+A premium, modern desktop authentication and user management sandbox designed for C# WinForms targeting **.NET Framework 4.8.1**.
+
+## 🏗 Architecture & Design
+
+The application utilizes a clean Separation of Concerns (SoC) model with shared service layers and event-driven updates.
+
+```mermaid
+graph TD
+ classDef main fill:#1E293B,stroke:#0EA5E9,stroke-width:2px,color:#fff;
+ classDef service fill:#0F172A,stroke:#6366F1,stroke-width:2px,color:#fff;
+ classDef model fill:#1E293B,stroke:#10B981,stroke-width:2px,color:#fff;
+
+ MainForm[MainForm
Dashboard]:::main
+ LoginDialog[LoginDialog
Sign In]:::main
+ UserSettings[UserSettings
Directory Manager]:::main
+
+ SessionManager[SessionManager
State & Events]:::service
+ User[User Model]:::model
+
+ MainForm -->|Subscribes to Events| SessionManager
+ MainForm -->|Opens dialog| LoginDialog
+ MainForm -->|Opens dialog| UserSettings
+
+ LoginDialog -->|Authenticates| SessionManager
+ UserSettings -->|Modifies directories| SessionManager
+
+ SessionManager -->|Manages| User
+```
+
+---
+
+## 📂 Project Directory Structure
+
+Here are the key files created for this project in [UserPermissionTest_CS_WinForms](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms):
+
+* 📄 **[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.
+* 📄 **[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.
+* 📄 **[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.
+* 🖥 **MainForm**:
+ * [MainForm.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/MainForm.cs): Event listener updates for active sessions.
+ * [MainForm.Designer.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/MainForm.Designer.cs): Beautiful dashboard designer controls.
+* 🖥 **LoginDialog**:
+ * [LoginDialog.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/LoginDialog.cs): Modal sign-in form verification logic with password visibility toggle.
+ * [LoginDialog.Designer.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/LoginDialog.Designer.cs): Compact slate-themed form controls.
+* 🖥 **UserSettings**:
+ * [UserSettings.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/UserSettings.cs): Directory editor, user details modifier, and dynamic system permissions register. Supports secure password placeholders and toggles.
+ * [UserSettings.Designer.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/UserSettings.Designer.cs): Side-by-side catalog and profile fields.
+
+---
+
+## 🗄️ Repository Pattern & PostgreSQL Migration Path
+
+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**:
+
+```mermaid
+graph LR
+ SessionManager -->|Uses| IUserRepository
+ IUserRepository <|.. JsonUserRepository
+ IUserRepository <|.. PostgreSqlUserRepository
+
+ style IUserRepository fill:#1E293B,stroke:#F59E0B,stroke-width:2px,color:#fff
+ style JsonUserRepository fill:#1E293B,stroke:#10B981,stroke-width:2px
+ style PostgreSqlUserRepository fill:#1E293B,stroke:#0EA5E9,stroke-width:2px,stroke-dasharray: 5 5
+```
+
+### Decoupled Interfaces:
+* **[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.
+
+### 🐘 PostgreSQL Migration Steps:
+When you decide to migrate to PostgreSQL, you **only** need to follow these simple steps:
+1. Install `Npgsql` (PostgreSQL Client) and a mapper like `EntityFramework` or `Dapper` via NuGet.
+2. Implement a new class adhering to `IUserRepository`:
+ ```csharp
+ public class PostgreSqlUserRepository : IUserRepository
+ {
+ private readonly string _connectionString;
+ public PostgreSqlUserRepository(string connString) => _connectionString = connString;
+
+ public List LoadUsers() { /* DB query */ }
+ public void SaveUsers(List users) { /* DB save */ }
+ public List LoadPermissions() { /* DB query */ }
+ public void SavePermissions(List permissions) { /* DB save */ }
+ }
+ ```
+3. Swap a single line in **[SessionManager.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/SessionManager.cs)**:
+ ```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.
+
+---
+
+## 🔒 Role-Based Access Control (RBAC)
+
+The application enforces fine-grained authorization constraints on dashboard actions based on active session permissions:
+
+* **Locked State (Not Logged In)**:
+ * The `Manage Users` button is completely disabled and marked as `🔒 Manage Users`.
+* **Unauthorized State (Logged In without privilege)**:
+ * If a user logs in (e.g., `user`) but does not have the **`Manage Users`** or **`Full Control`** permission:
+ * The directory button is locked and updated to `🔒 Manage Users (Locked)`.
+ * Client-side defensive checks verify permissions upon any attempt to click the control, displaying a security alert if breached.
+* **Authorized State (Logged In with privilege)**:
+ * If a user logs in (e.g., `admin`) and has the **`Manage Users`** or **`Full Control`** permission:
+ * The directory button becomes fully active and updates to `👥 Manage Users`.
+
+---
+
+## 🔐 Cryptographic Security & Password UX
+
+The application implements enterprise-level password security and high-fidelity visibility controls:
+
+* **SHA-256 One-Way Hashing**:
+ * Passwords are never stored or processed in plain text. **[PasswordHasher.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/PasswordHasher.cs)** computes a secure SHA-256 hash using `System.Security.Cryptography` for database-compatible security.
+* **On-the-Fly Hashing Migration**:
+ * The file parser in **`JsonUserRepository`** dynamically intercepts old plain-text database profiles on startup. If a legacy plain-text password is encountered, it hashes it instantly and saves the migrated database back to disk transparently.
+* **Secure Administrator Directory Workflows**:
+ * When loading user profiles in `UserSettings`, the password is never sent to the UI. Instead, a protective bullet mask `●●●●●●●●` is shown.
+ * If the administrator edits other details but leaves the password field containing the mask `●●●●●●●●`, the system detects this and keeps the current hashed password completely intact.
+ * If the administrator writes a new string, it hashes and overrides it.
+* **Interactive Password Toggles (Show/Hide)**:
+ * An elegant, borderless eye button (`btnTogglePassword` with `👁`/`🙈` states) is added adjacent to password boxes in `LoginDialog` and `UserSettings` to dynamically toggle characters between masked bullets `'•'` and plain text.
+
+---
+
+## 🎨 Visual Design Choices
+
+* **Harmonious Palettes**:
+ * Header Panels: Slate Navy (`#1E293B`)
+ * Buttons: Indigo Purple (`#6366F1`) and Emerald Green (`#10B981`)
+ * Backgrounds: White (`#FFFFFF`) with contrasting light slate fills (`#F8FAFC`, `#F1F5F9`)
+* **Typography**: Clean `Segoe UI` fonts utilizing structured bold/semi-bold weight hierarchy for high legibility.
+* **Badges & Lists**: Green/Red status badge for signed-in sessions and bulleted checklists displaying privileges.
+
+---
+
+## 🧪 Pre-Configured Test Credentials
+
+For quick testing, the sandbox initializes with the following default users:
+
+1. **Administrator User**
+ * **Username**: `admin`
+ * **Password**: `admin`
+ * **Permissions**: `View Dashboard`, `Edit Settings`, `Manage Users`, `Full Control`
+
+2. **Standard User**
+ * **Username**: `user`
+ * **Password**: `user`
+ * **Permissions**: `View Dashboard`
+
+---
+---
+
+# BÖLÜM 2: Türkçe (Teknik Kılavuz)
+
+C# WinForms ortamında geliştirilen ve **.NET Framework 4.8.1** sürümünü hedefleyen modern, yüksek güvenlikli kullanıcı oturum ve yetki yönetim laboratuvarı belgesidir.
+
+## 🏗 Mimari Tasarım ve Akış
+
+Uygulama, veri katmanını kullanıcı arayüzünden tamamen ayıran temiz bir Sorumlulukların Ayrılması (SoC) mimarisi ve olay güdümlü (event-driven) güncelleme akışları kullanır.
+
+```mermaid
+graph TD
+ classDef main fill:#1E293B,stroke:#0EA5E9,stroke-width:2px,color:#fff;
+ classDef service fill:#0F172A,stroke:#6366F1,stroke-width:2px,color:#fff;
+ classDef model fill:#1E293B,stroke:#10B981,stroke-width:2px,color:#fff;
+
+ MainForm[MainForm
Dashboard / Panel]:::main
+ LoginDialog[LoginDialog
Giriş Ekranı]:::main
+ UserSettings[UserSettings
Dizin ve Ayarlar]:::main
+
+ SessionManager[SessionManager
Durum ve Olaylar]:::service
+ User[User Modeli]:::model
+
+ MainForm -->|Olaylara Abone Olur| SessionManager
+ MainForm -->|Açılır Dialog| LoginDialog
+ MainForm -->|Açılır Dialog| UserSettings
+
+ LoginDialog -->|Kimlik Doğrular| SessionManager
+ UserSettings -->|Dizini Düzenler| SessionManager
+
+ SessionManager -->|Yönetir| User
+```
+
+---
+
+## 📂 Proje Dizin Yapısı
+
+[UserPermissionTest_CS_WinForms](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms) dizininde oluşturulan temel bileşenler şunlardır:
+
+* 📄 **[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ı.
+* 📄 **[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ı.
+* 📄 **[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ı.
+* 🖥 **MainForm**:
+ * [MainForm.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/MainForm.cs): Aktif oturum ve yetki durumu değişikliklerini dinleyen ana panel kodları.
+ * [MainForm.Designer.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/MainForm.Designer.cs): Tasarım arayüzü ve yerleşim bileşenleri.
+* 🖥 **LoginDialog**:
+ * [LoginDialog.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/LoginDialog.cs): Şifre gizleme/gösterme özellikli modern kullanıcı giriş doğrulama mantığı.
+ * [LoginDialog.Designer.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/LoginDialog.Designer.cs): Slate temalı giriş formu tasarım detayları.
+* 🖥 **UserSettings**:
+ * [UserSettings.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/UserSettings.cs): Kullanıcı listeleme, ekleme, silme ve dinamik izin tanımlama kodları. Maskelenmiş yönetici şifre düzenleme korumasını barındırır.
+ * [UserSettings.Designer.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/UserSettings.Designer.cs): Yan yana katalog ve profil giriş alanları.
+
+---
+
+## 🗄️ Depo Tasarım Deseni (Repository Pattern) & PostgreSQL Geçiş Yolu
+
+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:
+
+```mermaid
+graph LR
+ SessionManager -->|Kullanır| IUserRepository
+ IUserRepository <|.. JsonUserRepository
+ IUserRepository <|.. PostgreSqlUserRepository
+
+ style IUserRepository fill:#1E293B,stroke:#F59E0B,stroke-width:2px,color:#fff
+ style JsonUserRepository fill:#1E293B,stroke:#10B981,stroke-width:2px
+ style PostgreSqlUserRepository fill:#1E293B,stroke:#0EA5E9,stroke-width:2px,stroke-dasharray: 5 5
+```
+
+### Soyut Katmanlar:
+* **[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.
+* **[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.
+
+### 🐘 PostgreSQL Geçiş Adımları:
+Veritabanı kurulumunuzu yaptığınızda **yalnızca** şu basit adımları izlemeniz yeterli olacaktır:
+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
+ public class PostgreSqlUserRepository : IUserRepository
+ {
+ private readonly string _connectionString;
+ public PostgreSqlUserRepository(string connString) => _connectionString = connString;
+
+ public List LoadUsers() { /* PostgreSQL SELECT sorgusu */ }
+ public void SaveUsers(List users) { /* PostgreSQL Kaydetme */ }
+ public List LoadPermissions() { /* DB yetki listesi sorgusu */ }
+ public void SavePermissions(List 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:
+ ```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.
+
+---
+
+## 🔒 Rol Tabanlı Yetki Kontrolü (RBAC)
+
+Uygulama, giriş yapan kullanıcının aktif izinlerine göre arayüzdeki işlemleri dinamik olarak kısıtlar:
+
+* **Oturum Yokken (Kilitli Durum)**:
+ * Kullanıcı yönetim butonu devre dışı kalır ve `🔒 Manage Users` metniyle gösterilir.
+* **Oturum Var Ancak İzin Yetersizken (Yetkisiz Durum)**:
+ * Giriş yapan kullanıcının yetkileri arasında `Manage Users` veya `Full Control` **yoksa**:
+ * Buton pasif kalır ve durum bilgisini belli etmek için `🔒 Manage Users (Locked)` olarak güncellenir.
+ * Tıklama olayında arka planda yetki kontrolü tekrarlanır ve yetkisiz erişim teşebbüslerinde güvenlik uyarısı verilir.
+* **Oturum Var ve Yetki Yeterliyken (Yetkili Durum)**:
+ * Kullanıcı yetkili ise buton anında aktifleşir, simgesi ve metni `👥 Manage Users` olarak güncellenir ve tıklanabilir hale gelir.
+
+---
+
+## 🔐 Kriptografik Güvenlik & Parola Deneyimi (UX)
+
+Uygulama üst düzey parola güvenliği standartları ve gelişmiş gizleme/gösterme yetenekleriyle donatılmıştır:
+
+* **SHA-256 Tek Yönlü Kriptolama**:
+ * Şifreler diskte asla düz metin olarak saklanmaz. **[PasswordHasher.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/PasswordHasher.cs)** sınıfı, `System.Security.Cryptography` kütüphanesini kullanarak parolaları SHA-256 formatında şifreler.
+* **Arka Planda Dinamik Otomatik Göç (Auto Hashing Migration)**:
+ * `JsonUserRepository` dosya okuma aşamasında eski/açık metin parola kayıtları algılarsa, bunları **arka planda otomatik olarak SHA-256 hash formatına dönüştürür** ve diske güvenli şekilde geri yazar.
+* **Güvenli Yönetim Ekranı Parola İşlemleri**:
+ * Kullanıcı detayları yüklendiğinde gerçek şifre hash'i arayüze gönderilmez; yerine `●●●●●●●●` şeklinde güvenli bir maske gösterilir.
+ * Şifre alanını `●●●●●●●●` olarak bıraktığınızda sistem şifrenin değiştirilmediğini algılar ve **mevcut hash'i aynen korur**. Yeni bir şifre yazıldığında ise bunu şifreleyerek kaydeder.
+* **Şifre Göster/Gizle Butonları (Eye Toggles)**:
+ * `LoginDialog` ve `UserSettings` şifre kutularının sağına eklenen modern kenarlıksız göz butonu (`btnTogglePassword` ile `👁`/`🙈` durumları) şifrenin maskelenmesi `'•'` ve düz metin gösterilmesi arasında dinamik geçiş sağlar.
+
+---
+
+## 🎨 Görsel Tasarım Tercihleri
+
+* **Özel Uyumlu Renk Paleti**:
+ * Üst Bilgi Panelleri: Modern Koyu Lacivert/Siyah (`#1E293B`)
+ * Etkin Butonlar: İndigo Moru (`#6366F1`) ve Zümrüt Yeşili (`#10B981`)
+ * Form Zeminleri: Beyaz (`#FFFFFF`) ve kontrast açık gri/slate dolgular (`#F8FAFC`, `#F1F5F9`)
+* **Tipografi**: Browser varsayılanları yerine temiz, okunaklılığı yüksek yarı kalın/kalın yapılandırılmış `Segoe UI` yazı tipleri kullanılmıştır.
+* **Durum Bildirimleri**: Oturum durumunu gösteren Yeşil/Kırmızı dinamik rozetler ve izin listesinde zengin onay sembolleri (`✓`).
+
+---
+
+## 🧪 Hazır Test Kullanıcı Bilgileri
+
+Hızlı test işlemleri için sistem başlangıçta şu hesapları barındırır:
+
+1. **Yönetici Kullanıcı (Tüm Yetkilere Sahip)**
+ * **Kullanıcı Adı**: `admin`
+ * **Parola**: `admin`
+ * **Yetkileri**: `View Dashboard`, `Edit Settings`, `Manage Users`, `Full Control`
+
+2. **Standart Kullanıcı**
+ * **Kullanıcı Adı**: `user`
+ * **Parola**: `user`
+ * **Yetkileri**: `View Dashboard`