Overview

This article shows how to develop a simple ASP.NET-based application for the purpose of providing non-IT management staff (in other words a marketing team leader) the ability to reset a delegate's Windows domain account password. The purpose of such an application is to reduce the number of service desk tickets relating to account password resets and enhance the productivity of an end user by forestalling the superfluous and prolonged delays that often occur during the traditional password reset process.

Logging

You will notice that I do not specify the logging of the password reset information in this article. Whereas it is incredibly important to log who is resetting what, or who is attempting to reset what, I believe that explaining the logging process in this article would have only served to distract the reader from the actual password reset task. Since logging is relatively simple and due to the fact that many businesses log information in various ways, I chose to omit that procedure from the article.

Language guide

When I refer to the delegate, I am referring to the user that requires their password to be reset. When I refer to the manager, I am referring to the manager that is listed against the delegates account in the Active Directory. When I refer to the user, I am referring to the user that is attempting to reset the delegate's password via the web form.

Setup guide

Our first step is to edit our Web.config file to utilize Windows authentication.

  1. <authentication mode ="Windows" />
  2. lt;authorization>
  3. <deny users="?"/> <!--Prevent Anonymous Authorization-->
  4. lt;/authorization

Once the Web.config file has been edited, we will need to add some components, three text boxes and two buttons, to our web form. The following image details how I have chosen to name these components on my form.

Now that the proper components have been added to our form, we will need to add a reference to the System.DirectoryServices.dll. We do this by adding a reference via Visual Studio's Reference Manager and by adding the following using statement to our code:

  1. using System.DirectoryServices;

We are now ready to start work on the password reset (btnReset) itself. Since we will need to compare the Delegate's Manager's username against the name of the User who is requesting the password reset, it is essential that we capture the username of the User requesting the password reset. We are able to do this with the following lines of code:

  1. //Get User's Username
  2. string strRawUser = HttpContext.Current.User.Identity.Name;
  3. //Remove Domain Name From User's Username ('TEST\bgates' will become 'bgates')
  4. string[] strSplitUser = strRawUser.Split(new char[] { '\\' });
  5. string strUsername = strSplitUser[1];

Once this username has been captured, we need to have something to compare it to. To gather the Delegate's Manager's username from the Active Directory, we need to perform two separate searches. The first will capture the Manager's distinguished name from the Delegate's Active Directory user profile and the second will resolve the Manager's username from this distinguished name.

  1. //Get Manager's Distinguished Name From Active Directory
  2. string strConn = "LDAP://dc=test,dc=local";
  3. DirectorySearcher dSearch = new DirectorySearcher(strConn);
  4. dSearch.Filter = "(sAMAccountName=" + txtUsername.Text + ")";
  5. SearchResult sResult = dSearch.FindOne();
  6. DirectoryEntry dResult = sResult.GetDirectoryEntry();
  7. string strRawManager = dResult.Properties["manager"][0].ToString();
  8. //Get Manager's Username From Distinguished Name
  9. DirectorySearcher diSearch = new DirectorySearcher(strConn);
  10. diSearch.Filter = "(distinguishedName=" + strRawManager + ")";
  11. SearchResult seResult = diSearch.FindOne();
  12. DirectoryEntry diResult = seResult.GetDirectoryEntry();
  13. String strManager = diResult.Properties["sAMAccountName"][0].ToString();

We now need to compare the user's username to the manager's username. In the event that the user's username does in fact match the manager's username (meaning that the user is the manager), we should proceed to check the delegate's account status, since we do not wish to allow managers to reset passwords for deactivated accounts.

  1. //If User's Username Is The Same As Manager's Username
  2. if (strUsername == strManager)
  3. {
  4. //Get Delegate Account Status
  5. int intFlags = (int)dResult.Properties["userAccountControl"].Value;

If the delegate's account has been disabled, we will display an error stating as such.

  1. //If Delegate's Account Is Disabled
  2. if (intFlags == 514)
  3. {
  4. //Show Error Message
  5. string strDisabled = "Selected Account Disabled. Unable To Reset Password.";
  6. ClientScript.RegisterStartupScript(this.GetType(), "myalert", "alert('" + strDisabled + "');", true);
  7. }

However, if the delegate's account is enabled, we are able to proceed to the next step of the password reset process. In this next step, we will need to confirm that the user has correctly entered the password on the form and that the txtPassword field matches the txtConfirmPW field.

If the txtPassword field matches the txtConfirmPW field, we should reset the delegate's password, confirm that the account is unlocked and ensure that the delegate is prompted to reset their password on the first logon. We also display a success message to the user.

  1. else
  2. {
  3. //If Password And Confirmation Fields Match (On Form)
  4. string strPassword = txtPassword.Text;
  5. string strConfirmPW = txtConfirm.Text;
  6. if (strPassword == strConfirmPW)
  7. {
  8. //Reset Delegate's Password, Unlock Account, Ensure User Has To Reset Password On Next Login
  9. dResult.Invoke("SetPassword", strPassword);
  10. dResult.Properties["LockOutTime"].Value = 0;
  11. dResult.Properties["pwdLastSet"].Value = 0;
  12. //Commit Changes
  13. dResult.CommitChanges();
  14. //Show Success Message
  15. string strSuccess = "Congratulations! Password Reset Successfully.";
  16. ClientScript.RegisterStartupScript(this.GetType(), "myalert", "alert('" + strSuccess + "');", true);
  17. }

If the txtPassword field does not match the txtConfirmPW field however, we will need to display an error asking the user to check the data on the form.

  1. //If Password Does Not Match Confirmation Field (On Form)
  2. else
  3. {
  4. //Show Error Message
  5. string strNoMatch = "Entered Passwords Do Not Match. Please Try Again.";
  6. ClientScript.RegisterStartupScript(this.GetType(), "myalert", "alert('" + strNoMatch + "');", true);
  7. }

The final part of our password reset button's code should address what happens if the user's username does not match the manager's username. In the event that these two usernames do not match, we simply need to display an error message to the user.

  1. //If User's Username Does Not Match Manager's Username
  2. else
  3. {
  4. //Show Failure Message
  5. string strFailure = "You Do Not Have Permission To Perform This Action.";
  6. ClientScript.RegisterStartupScript(this.GetType(), "myalert", "alert('" + strFailure + "');", true);
  7. }

The final part of our code refers to that pesky Clear Form button (btnClear). You are able to clear the form in a number of ways, however I find the simplest way is to simply reset the web form.

  1. //Clear Form
  2. protected void btnClear_Click(object sender, EventArgs e)
  3. {
  4. //Refresh Form
  5. Response.Redirect(Request.RawUrl);
  6. }

Full Code

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Web.UI;
  6. using System.Web.UI.WebControls;
  7. using System.DirectoryServices;
  8. namespace ADReset
  9. {
  10. public partial class WebForm1 : System.Web.UI.Page
  11. {
  12. //Reset Password
  13. protected void btnReset_Click(object sender, EventArgs e)
  14. {
  15. //Get User's Username
  16. string strRawUser = HttpContext.Current.User.Identity.Name;
  17. //Remove Domain Name From User's Username ('TEST\bgates' will become 'bgates')
  18. string[] strSplitUser = strRawUser.Split(new char[] { '\\' });
  19. string strUsername = strSplitUser[1];
  20. //Get Manager's Distinguished Name From Active Directory
  21. string strConn = "LDAP://dc=test,dc=local";
  22. DirectorySearcher dSearch = new DirectorySearcher(strConn);
  23. dSearch.Filter = "(sAMAccountName=" + txtUsername.Text + ")";
  24. SearchResult sResult = dSearch.FindOne();
  25. DirectoryEntry dResult = sResult.GetDirectoryEntry();
  26. string strRawManager = dResult.Properties["manager"][0].ToString();
  27. //Get Manager's Username From Distinguished Name
  28. DirectorySearcher diSearch = new DirectorySearcher(strConn);
  29. diSearch.Filter = "(distinguishedName=" + strRawManager + ")";
  30. SearchResult seResult = diSearch.FindOne();
  31. DirectoryEntry diResult = seResult.GetDirectoryEntry();
  32. string strManager = diResult.Properties["sAMAccountName"][0].ToString();
  33. //If User's Username Is The Same As Manager's Username
  34. if (strUsername == strManager)
  35. {
  36. //Get Delegate Account Status
  37. int intFlags = (int)dResult.Properties["userAccountControl"].Value;
  38. //If Delegate's Account Is Disabled
  39. if (intFlags == 514)
  40. {
  41. //Show Error Message
  42. string strDisabled = "Selected Account Disabled. Unable To Reset Password.";
  43. ClientScript.RegisterStartupScript(this.GetType(), "myalert", "alert('" + strDisabled + "');", true);
  44. }
  45. else
  46. {
  47. //If Password And Confirmation Fields Match (On Form)
  48. string strPassword = txtPassword.Text;
  49. string strConfirmPW = txtConfirm.Text;
  50. if (strPassword == strConfirmPW)
  51. {
  52. //Reset Delegate's Password, Unlock Account, Ensure User Has To Reset Password On Next Login
  53. dResult.Invoke("SetPassword", strPassword);
  54. dResult.Properties["LockOutTime"].Value = 0;
  55. dResult.Properties["pwdLastSet"].Value = 0;
  56. //Commit Changes
  57. dResult.CommitChanges();
  58. //Show Success Message
  59. string strSuccess = "Congratulations! Password Reset Successfully.";
  60. ClientScript.RegisterStartupScript(this.GetType(), "myalert", "alert('" + strSuccess + "');", true);
  61. }
  62. //If Password Does Not Match Confirmation Field (On Form)
  63. else
  64. {
  65. //Show Error Message
  66. string strNoMatch = "Entered Passwords Do Not Match. Please Try Again.";
  67. ClientScript.RegisterStartupScript(this.GetType(), "myalert", "alert('" + strNoMatch + "');", true);
  68. }
  69. }
  70. }
  71. //If User's Username Does Not Match Manager's Username
  72. else
  73. {
  74. //Show Failure Message
  75. string strFailure = "You Do Not Have Permission To Perform This Action.";
  76. ClientScript.RegisterStartupScript(this.GetType(), "myalert", "alert('" + strFailure + "');", true);
  77. }
  78. }
  79. //Clear Form
  80. protected void btnClear_Click(object sender, EventArgs e)
  81. {
  82. //Refresh Form
  83. Response.Redirect(Request.RawUrl);
  84. }
  85. }
  86. }