added "UserPermissionTesting_CS_WinForms" project

This commit is contained in:
Atakan Kayman
2026-05-31 15:08:26 +03:00
parent 1ea9de0042
commit 374067dd2e
26 changed files with 2213 additions and 427 deletions
@@ -0,0 +1 @@
C:/Users/ataka/.gemini/config/projects/feaeef25-a553-4345-ba51-5eb86eb0eee0.json
+2 -1
View File
@@ -1,3 +1,4 @@
<Solution> <Solution>
<Project Path="UserPermissionTesting_CS_WinForms/UserPermissionTesting_CS_WinForms.csproj" /> <Project Path="UserPermissionTest_CS_WinForms\UserPermissionTest_CS_WinForms.csproj" />
</Solution> </Solution>
@@ -0,0 +1,12 @@
using System.Collections.Generic;
namespace UserPermissionTest_CS_WinForms
{
public interface IUserRepository
{
List<User> LoadUsers();
void SaveUsers(List<User> users);
List<string> LoadPermissions();
void SavePermissions(List<string> permissions);
}
}
@@ -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<User> LoadUsers()
{
if (!File.Exists(UsersFile))
{
var defaults = GetDefaultUsers();
SaveUsers(defaults);
return defaults;
}
try
{
string json = File.ReadAllText(UsersFile);
var users = JsonConvert.DeserializeObject<List<User>>(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<User> 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<string> LoadPermissions()
{
if (!File.Exists(PermissionsFile))
{
var defaults = GetDefaultPermissions();
SavePermissions(defaults);
return defaults;
}
try
{
string json = File.ReadAllText(PermissionsFile);
var permissions = JsonConvert.DeserializeObject<List<string>>(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<string> 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<User> GetDefaultUsers()
{
return new List<User>
{
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<string> { "View Dashboard" }
}
};
}
private List<string> GetDefaultPermissions()
{
return new List<string>
{
"View Dashboard",
"Edit Settings",
"Manage Users",
"Full Control",
"Delete Transactions"
};
}
}
}
+218
View File
@@ -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;
}
}
@@ -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 = "👁";
}
}
}
}
+319
View File
@@ -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;
}
}
+153
View File
@@ -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);
}
}
}
@@ -0,0 +1,39 @@
using System;
using System.Security.Cryptography;
using System.Text;
namespace UserPermissionTest_CS_WinForms
{
public static class PasswordHasher
{
/// <summary>
/// Hashes the plain-text password using SHA-256.
/// </summary>
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();
}
}
/// <summary>
/// Verifies whether the entered plain-text password matches the stored SHA-256 hash.
/// </summary>
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);
}
}
}
+16
View File
@@ -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());
}
}
}
@@ -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<User> Users { get; private set; } = new List<User>();
public static List<string> AvailablePermissions { get; private set; } = new List<string>();
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();
}
}
}
}
+24
View File
@@ -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<string> Permissions { get; set; } = new List<string>();
public User Clone()
{
return new User
{
Username = this.Username,
FullName = this.FullName,
Password = this.Password,
Permissions = new List<string>(this.Permissions)
};
}
}
}
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net481</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
</ItemGroup>
</Project>
+402
View File
@@ -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;
}
}
@@ -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<string>();
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<string> GetCheckedPermissions()
{
var list = new List<string>();
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);
}
}
}
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8.1" />
</startup>
</configuration>
-40
View File
@@ -1,40 +0,0 @@
namespace UserPermissionTesting_CS_WinForms
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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
}
}
@@ -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();
}
}
}
@@ -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
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
@@ -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")]
@@ -1,71 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace UserPermissionTesting_CS_WinForms.Properties
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 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()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[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;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}
@@ -1,117 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -1,30 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
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;
}
}
}
}
@@ -1,7 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>
@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{64CF267A-0840-43C4-A915-2FE9D9874530}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>UserPermissionTesting_CS_WinForms</RootNamespace>
<AssemblyName>UserPermissionTesting_CS_WinForms</AssemblyName>
<TargetFrameworkVersion>v4.8.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Form1.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Form1.Designer.cs">
<DependentUpon>Form1.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
+320
View File
@@ -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<br/>Dashboard]:::main
LoginDialog[LoginDialog<br/>Sign In]:::main
UserSettings[UserSettings<br/>Directory Manager]:::main
SessionManager[SessionManager<br/>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<User> LoadUsers() { /* DB query */ }
public void SaveUsers(List<User> users) { /* DB save */ }
public List<string> LoadPermissions() { /* DB query */ }
public void SavePermissions(List<string> permissions) { /* DB save */ }
}
```
3. Swap a single line in **[SessionManager.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/SessionManager.cs)**:
```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<br/>Dashboard / Panel]:::main
LoginDialog[LoginDialog<br/>Giriş Ekranı]:::main
UserSettings[UserSettings<br/>Dizin ve Ayarlar]:::main
SessionManager[SessionManager<br/>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<User> LoadUsers() { /* PostgreSQL SELECT sorgusu */ }
public void SaveUsers(List<User> users) { /* PostgreSQL Kaydetme */ }
public List<string> LoadPermissions() { /* DB yetki listesi sorgusu */ }
public void SavePermissions(List<string> permissions) { /* DB yetki kaydı */ }
}
```
3. **[SessionManager.cs](file:///D:/02_Programming/CSharp/CodingSandbox/UserPermissionTest_CS_WinForms/SessionManager.cs)** dosyasındaki tek satırı güncelleyin:
```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`