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