Hi,
I have written a little web application to allow staff that aren't on our domain to be able to change their password online.
The application uses a custom script I have written that allows passwords to be changed and does other things such as searches etc.
I have all of this in a class library and I use it in other applications as well so I know the code is fine.
So when someone logs in to the web application my custom class authenticates them (I know you can use windows authenitcation but I use this for a lot more). This works fine during debug and and when deployed on the server.
The next feature I have used out of my class is the ability to change your password, you enter your current password and your new password and the function does a few things:
First it does an annoymous bind and searches your common name
Then it searches your common name to get your full DN
Finally it passes the DN and your current and new password
This then performs the password reset function and returns bool.
This password reset feature works fine during debug, but then doesn't work once deployed.
To further test it wasn't a server issue I installed one of my apps that has this feature on the server (the code is identical) and it worked fine.
Can anyone help?
Andy
Loading
andy mcinnesPosted Apr 25, 2012, 1:41 PM
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, userIsOnline: true);
If anyone could shed any light as to why this wouldn't work it would be interesting.
I have just excluded it as the password is validated using my model, do you think it could be to do with the fact that I removed the reigstration model so no details are stored for a user any more? Also if so do you think I should get it do the registration as well as part of the first login, then the password change facilitiy could also be applied as well and hence any queries on the above line of code would work?
It would also be interesting to know where Membership and membershipuser exists, is this part of the framework?
Final question why would the above line have just skipped when run on my local machine without picking up an error whilst debugging, where as on the server the error showed up?
Sam HobbsPosted Apr 24, 2012, 5:49 PM
I know that ASP does not allow all error information to be shown in the browser in a production environment, at least not unless you specify otherwise. That is for security reasons; error messages can disclose information useful for hackers. So if you are not familiar with that then you should learn, even if it is not relevant to this problem. You can look for articles in this web site; I assume there are some very useful and relevant articles for you.
I am sorry I cannot help you more but I think you should at least eliminate the above as a possibility at least and hopefully it will lead to diagnosis and resolution of the problem.
andy mcinnesPosted Apr 24, 2012, 5:14 PM
Sorry I meant error.cshtml
This is what is on error.cshtml and this shows the error that is displayed.
@model System.Web.Mvc.HandleErrorInfo
@{
ViewBag.Title = "Error";
}
Error.
An error occurred while processing your request.
As I said the URL stays the same i.e. www.website.com/Accounts/ChangePassword not /Error.
The error is caught I am guessing in the Account Controller, here:
}
// If we got this far, something failed, redisplay form
return View(model);
Logically thinking about it the only way you could end up here is if the modelstate in not valid:
if (ModelState.IsValid)
{
//password model code
}
// If we got this far, something failed, redisplay form
return View(model);
How or why would a modelstate be invalid when deployed on a live server?
Thanks again for any help you can offer,
Andy
Sam HobbsPosted Apr 24, 2012, 3:55 PM
Probably part of the miscommunication is that you are saying "error message" and that is ambiguous. Sometimes an error message is something your program generates (right?) and sometimes it is something your program gets from Windows or whatever. I don't care about the error messages that your program generates. You need to tell us what error message your program is getting. You have not provided that.
andy mcinnesPosted Apr 24, 2012, 11:50 AM
This line here:
// If we got this far, something failed, redisplay form
return View(model);
}
This is my model code, I will try to explain what it is doing...
I hold a public string called error which I assign the error message to so that I can access it from the account controller if it fails.
The function itself returns bool, so true for success, false for fail.
You pass in a string array containing the ip of the directory service, a dn with sufficient privillages to change a password (which I have), the password for this account, then the full dn of the account you want to modify the password for, and finally the new password.
The variables are all set.
You connect to the server And then bind using the privallege account.
You then perform a password modify command against the account.
As this is using try and catch the code will continue if successful, generating a success message.
Finally you disconnect from the server and return true.
The catch returns the specific error message and false.
public class SetPassword
{
public string error;
public bool pwdChange(String[] args)
{
if (args.Length != 5)
{
Console.WriteLine("Insufficent details: Account values are missing please try again.", "Unable to Reset Password");
return false;
}
int ldapPort = LdapConnection.DEFAULT_PORT;
int ldapVersion = LdapConnection.Ldap_V3;
String ldapHost = args[0];
String loginDN = args[1];
String password = args[2];
String modifyDN = args[3];
String newPassword = args[4];
LdapConnection lc = new LdapConnection();
/* To set a user's password,
* -- User should have administrator privileges
* -- Specify the new password value to be set
* -- Specify the modify type (replace for this operation)
* -- Add the new value and type to the modification set
* -- Call LdapConnection modify method to set the password
*/
try
{
// connect to the server
lc.Connect(ldapHost, ldapPort);
// authenticate to the server
lc.Bind(ldapVersion, loginDN, password);
LdapAttribute attributePassword = new LdapAttribute("userPassword",
newPassword);
lc.Modify(modifyDN, new LdapModification(
LdapModification.REPLACE, attributePassword));
Console.WriteLine("Successfully set the user's password");
// disconnect with the server
lc.Disconnect();
return true;
}
catch (LdapException e)
{
if (e.ResultCode == LdapException.NO_SUCH_OBJECT)
{
Console.WriteLine("Error: No such entry");
error = "Error1: No such entry";
}
else if (e.ResultCode ==
LdapException.INSUFFICIENT_ACCESS_RIGHTS)
{
Console.WriteLine("Error: Insufficient rights");
error = "Error2: Insufficient rights";
}
else
{
Console.WriteLine("Error: " + e.ToString());
error = "Error3: " + e.ToString();
}
return false;
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.ToString());
error = "Error4: " + e.ToString();
return false;
}
}
}
public class ChangePasswordModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
andy mcinnesPosted Apr 23, 2012, 5:11 PM
I have tried to detail the data flow to you and where the errors are being trapped. In terms of the errors I purposely entered I tried incorrect password and incorrect new password details (i.e. used in the last 16 password changes). These will return specific NDS errors. Instead the application is not showing the errors on the view from the modelstate, it is trapping the error as a default error on the error.aspx.
When I debug the application everything works perfectly, the errors get returned to the view not a default error page. Also the change password facility works perfectly whilst debugging. To me this says the code is fine, are there any differences I should know about that occur between development environments and live servers?
If I can supply anything that is more specific/useful please can you help me, I am very new to websites (this is my first) so if you can give me instructions of the information you may need and or how to get it in order to resolve this problem I would really appreciate it.
Thanks in advance,
Andy
Sam HobbsPosted Apr 23, 2012, 1:36 PM
andy mcinnesPosted Apr 23, 2012, 4:11 AM
Sorry if it seems a bit vague, but to me there seems no reason why this isn't working. I will try to provide a little more description...
When you login to the site it uses the class library I have written, this is where the password change function is also. As you login the application then takes you through to the password reset form.
The account controller deals with the login and password change views and this is the password change part:
// GET: /Account/ChangePassword
public ActionResult ChangePassword()
{
return View();
}
//
// POST: /Account/ChangePassword
[HttpPost]
public ActionResult ChangePassword(ChangePasswordModel model)
{
if (ModelState.IsValid)
{
//getDN dn = new getDN();
Global.gloG.grabDN(User.Identity.Name);
string myDN = Global.gloG.gotDN;
string[] arg = new string[5];
arg[0] = "172.19.0.223";
arg[1] = myDN;
arg[2] = model.OldPassword;
arg[3] = myDN;
arg[4] = model.NewPassword;
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, userIsOnline: true);
//SetPassword pwd = new SetPassword();
// ChangePassword will throw an exception rather
// than return false in certain failure scenarios.
bool changePasswordSucceeded;
try
{
changePasswordSucceeded = Global.gloP.pwdChange(arg);
}
catch (Exception)
{
changePasswordSucceeded = false;
}
if (changePasswordSucceeded)
{
return RedirectToAction("ChangePasswordSuccess");
}
else
{
ModelState.AddModelError("", Global.gloP.error + " - " + myDN + " - " + currentUser.UserName); //"The current password is incorrect or the new password is invalid."
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
I have tried calling the class elements by using both a new instance of the class
getDN dn = new getDN();
dn.grabDN(User.Identity.Name);
and calling a global instance
Global.gloG.grabDN(User.Identity.Name);
and neither method makes any difference.
As I have said the part that is really tripping me up is the fact that this ***WORKS*** locally i.e. whilst debugging, but once deployed on the server it doesn't work. I have also tested an application with the exact same class library for changing passwords on the server and that worked without issue which rules out a network issue I would say? So by doesn't work here is what happens...
As you can see in the account controler there is a Modelstate.AddModelError(...)
Originally this just had a fixed error message as can be seen by the commented text to the right of the modelstateerror. The purpose of this was so I could return the specific error messages back from the password change function. I decided to this I would define a public string variable and add the error messages to this, then access the variable from the Global call. This can be seen as Global.gloP.error.
Whilst debugging locally I purposely put errors in to test this worked, and it did returning specific errors to the view via the modelstate.addmodelerror.
This is perhaps to the more knowledgable an interesting fact... Once deployed on the server, errors were NOT returned to the view, instead the applications error.aspx message was displayed.
As I am very new to all things website, I'm not entirely sure what calls the error.aspx page or why it would call in this instance, although I will say it doesn't divert to the error.aspx URL it just displays the view under the changepassword url.
If this still isn't helpful please can you guide me towards where I can supply more useful information?
Many thanks,
Andy
Sam HobbsPosted Apr 20, 2012, 6:14 PM