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