Husk
Husk

Reputation: 1

Saving text into arrays C# forms

So I'm trying to create a username and password system in C# forms, I have a second form for creating your account, where you can enter what you want your Username and corresponding Password to be. I have 2 separate arrays, one for usernames one for passwords, and I want whatever the user types into the textbox for Username and Password to be held in their respective arrays, so I can associate them with one another. How would I go about saving what is written in the textbox, to an array?

Upvotes: 0

Views: 255

Answers (2)

John Alexiou
John Alexiou

Reputation: 29274

In C# arrays are best utilized when the number of items is known, and fixed. Otherwise a general List<T> would suffice to add/remove items.

I think what you are asking is how to develop a data model for registered users, and how to interact with this model using the UI.

There are several pieces to this puzzle:

1. Data Model

Start with a class to hold user information (LoginUser below) and a class to hold a list of authorized users (UserRegistry below).

data

Add methods to load/save the user registry in Xml or whatever storage you desire. In my example below I am using the builtin XmlSerializer to go from data to file and back.

The full code in my example is

/// <summary>
/// The user registry.
/// </summary>
[Serializable()]
[XmlType(AnonymousType = true)]
[XmlRoot(ElementName = "Users", Namespace = "", IsNullable = false)]
public partial class UserRegistry
{

    public UserRegistry()
    {
        this.Users= new List<LoginUser>();
    }

    /// <summary>
    /// The list of users
    /// </summary>
    [XmlElement("User")]
    public List<LoginUser> Users { get; set; }

    /// <summary>
    /// Find the user in the list that matches the userId
    /// </summary>
    public LoginUser this[string userId]
    {
        get
        {
            return Users.FirstOrDefault((item) => item.UserId.ToLowerInvariant().Equals(userId.ToLowerInvariant()));
        }
    }

    public void Add(LoginUser user)
    {
        this.Users.Add(user);
    }
    /// <summary>
    /// Load a user list from an xml string.
    /// </summary>
    public static UserRegistry FromXml(string xml)
    {
        var fs = new StringReader(xml);
        var xs = new XmlSerializer(typeof(UserRegistry));
        var list = xs.Deserialize(fs) as UserRegistry;
        fs.Close();
        return list;
    }

    /// <summary>
    /// Save a user list to an xml string.
    /// </summary>
    public string SaveToXml()
    {
        var fs = new StringWriter();
        var xs = new XmlSerializer(typeof(UserRegistry));
        xs.Serialize(fs, this);
        fs.Close();
        return fs.ToString();
    }
}

/// <summary>
/// A user entry
/// </summary>
[Serializable()]
[XmlType(AnonymousType = true)]
public partial class LoginUser
{
    public static readonly LoginUser Empty = new LoginUser()
    {
        UserId=string.Empty,
        Salt  = string.Empty,
        PasswordHash = string.Empty
    };
    /// <summary>
    /// The list of users
    /// </summary>
    [XmlAttribute("id")]
    public string UserId
    {
        get;
        set;
    }

    /// <summary>
    /// The salted hash of the password
    /// </summary>
    [XmlAttribute("pwd")]
    public string PasswordHash
    {
        get;
        set;
    }
    /// <summary>
    /// The salt value used in the hash.
    /// </summary>
    [XmlAttribute("salt")]
    public string Salt
    {
        get;
        set;
    }
}

and a sample corresponding XML file

// File: "UserList.xml"
<?xml version="1.0" encoding="utf-8" ?>
<Users>
  <User id="[email protected]" pwd="Ei9p5Kiy8h7cy0nAxNimukxRhH4=" salt="OrAx1kBufa8=" />
  <User id="[email protected]" pwd="teeHjCrXzCxWosgZVTMWWeIvaso=" salt="hxQPkMK8t+0=" />
</Users>

Note that both registered users above share the same password, and by using a random "salt" to it, the password hash differs. The above implementation isn't the most secure, but it is a basic step above storing passwords as text. The password I used to generate both hashes above is the string "12345678".

2. Login Form

The basic login form contains text boxes for the username and password

login

it also checks the password length before enabling the OK button. This button contains the code that builds a LoginUser from the UI.

The form has two properties, LoginUser for the current user and UserRegistry for the list of registered users. The list is populated when the form loads from the XML file. The current user is empty initially. I am enabling autocomplete on the username textbox for convenience, using the list of registered users.

public partial class LoginForm1 : Form
{
    public LoginUser User { get; set; }
    public UserRegistry UserRegistry { get; private set; }

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        this.UserRegistry = UserRegistry.FromXml(File.ReadAllText("UserList.xml"));
        var usernames = UserRegistry.Users.Select((item) => item.UserId).ToArray();
        userNameTextBox.AutoCompleteSource = AutoCompleteSource.CustomSource;
        userNameTextBox.AutoCompleteCustomSource.AddRange(usernames);
        this.User = LoginUser.Empty;
    }

    // other code omitted
}

The main part of the code is checking for the password and registering a new user. I have two methods for these, IsUserValid() and RegisterUser() that are called from the OK button handler.

    public static bool IsUserValid(LoginUser user, string password)
    {
        var salt = Convert.FromBase64String(user.Salt);
        var pwd = Encoding.UTF7.GetBytes(password);
        var buffer = new byte[pwd.Length + salt.Length];
        Array.Copy(pwd, 0, buffer, 0, pwd.Length);
        Array.Copy(salt, 0, buffer, pwd.Length, salt.Length);
        var hash = sha1.ComputeHash(buffer);
        if (user.PasswordHash.Equals(Convert.ToBase64String(hash)))
        {
            return true;
        }
        return false;
    }

    public static LoginUser RegisterUser(string userName, string password)
    {
        var salt = new byte[8];
        rng.GetBytes(salt);
        var pwd = Encoding.UTF7.GetBytes(password);
        var buffer = new byte[pwd.Length + salt.Length];
        Array.Copy(pwd, 0, buffer, 0, pwd.Length);
        Array.Copy(salt, 0, buffer, pwd.Length, salt.Length);
        var hash = sha1.ComputeHash(buffer);
        return new LoginUser()
        {
            UserId = userName,
            Salt = Convert.ToBase64String(salt),
            PasswordHash = Convert.ToBase64String(hash)
        };
    }

    private void okButton_Click(object sender, EventArgs e)
    {
        this.User = UserRegistry[userNameTextBox.Text];
        if (this.User!=null)
        {
            if (IsUserValid(User, passwordTextBox.Text))
            {
                this.DialogResult = DialogResult.OK;
                this.Close();
            }
            else
            {
                passwordTextBox.Clear();
                MessageBox.Show($"Incorrect password. Try Again.", "Login");
            }
        }
        else
        {
            if (MessageBox.Show($"User {userNameTextBox.Text} Not Found. Do you want to register?",
                "Login", MessageBoxButtons.YesNo)==DialogResult.Yes)
            {
                this.User = RegisterUser(userNameTextBox.Text, passwordTextBox.Text);
                this.UserRegistry.Add(User);
                MessageBox.Show($"User {userNameTextBox.Text} Registered.", "Login");
                this.DialogResult = DialogResult.OK;
                File.WriteAllText("UserList.xml", UserRegistry.SaveToXml());
                this.Close();
            }
        }            
    }

3. Validation

The main form must call the login form and if the user is valid, it will return a valid entry into the User property and return DialogResult.OK from the form .ShowDialog() method.

public partial class MDIParent1 : Form
{
    public MDIParent1()
    {
        InitializeComponent();
    }
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        LoginForm1 login = new LoginForm1();
        login.StartPosition = FormStartPosition.CenterScreen;
        if (login.ShowDialog()==DialogResult.OK)
        {
            // Valid user
            userNameToolStripLabel.Text = login.User.UserId;
            return;
        }
        this.Close();
    }
}

In my example, I am using the identity of the user on the bottom right of the main form.

mdi

Upvotes: 0

Mohammed Hassan
Mohammed Hassan

Reputation: 36

Instead of Arrays you can use C# - Hashtable

C# includes Hashtable collection in System.Collections namespace, which is similar to generic Dictionary collection. The Hashtable collection stores key-value pairs. It optimizes lookups by computing the hash code of each key and stores it in a different bucket internally and then matches the hash code of the specified key at the time of accessing values. Here is an example that demonstrates your idea

using System;
using System.Collections;
using System.Collections.Generic;

Hashtable ht = new Hashtable();

protected void btnLogin_Click(object sender, EventArgs e)
{
    ht.Add(txtUserName.Text, txtPassword.Text);    
}

Access Hashtable:

You can retrive the value of an existing key from the Hashtable using indexer. Please note that the hashtable indexer requires a key.

Hashtable ht = new Hashtable();

ht.Add(1, "One");
ht.Add(2, "Two");
ht.Add(3, "Three");
ht.Add(4, "Four");
ht.Add("Fv", "Five");
ht.Add(8.5F, 8.5F);

string strValue1 = (string)ht[2];
string strValue2 = (string)ht["Fv"];
float fValue = (float) ht[8.5F];

Console.WriteLine(strValue1);
Console.WriteLine(strValue2);
Console.WriteLine(fValue);

Output:

Two
Five
8.5 

Upvotes: 1

Related Questions