@@ -208,7 +208,7 @@ public UserInformation GetUserInformation(HttpRequest request)
208208
209209 // get the full name of the user
210210 string fullName = user . DisplayName ?? user . Name ?? user . SamAccountName ;
211-
211+
212212 // get all groups of which the user is a member (checks all domains and local machine groups)
213213 var groupInformation = UserInformation . GetAllUserGroups ( user ) ;
214214
@@ -857,6 +857,7 @@ out IntPtr phToken
857857 public const int ERROR_INVALID_WORKSTATION = 1329 ; // the user is not allowed to log on to this workstation
858858 public const int ERROR_PASSWORD_EXPIRED = 1330 ; // the user's password has expired
859859 public const int ERROR_ACCOUNT_DISABLED = 1331 ; // the user account is disabled
860+ public const int ERROR_PASSWORD_MUST_CHANGE = 1907 ; // the user account password must change before signing in
860861
861862 [ DllImport ( "kernel32.dll" , SetLastError = true ) ]
862863 [ return : MarshalAs ( UnmanagedType . Bool ) ]
@@ -944,10 +945,135 @@ public static Tuple<bool, string> ValidateCredentials(string username, string pa
944945 return Tuple . Create ( false , Resources . WebResources . Login_PasswordExpiredError ) ;
945946 case ERROR_ACCOUNT_DISABLED :
946947 return Tuple . Create ( false , Resources . WebResources . Login_AccountDisabledError ) ;
948+ case ERROR_PASSWORD_MUST_CHANGE :
949+ return Tuple . Create ( false , Resources . WebResources . Login_PasswordMustChange ) ;
947950 default :
948951 return Tuple . Create ( false , "An unknown error occurred: " + errorCode ) ;
949952 }
950953 }
951954 }
955+
956+ [ DllImport ( "Netapi32.dll" , SetLastError = true ) ]
957+ public static extern int NetUserChangePassword (
958+ [ In ] string domainname ,
959+ [ In ] string username ,
960+ [ In ] string oldpassword ,
961+ [ In ] string newpassword
962+ ) ;
963+ public static Tuple < bool , string > ChangeCredentials ( string username , string oldPassword , string newPassword , string domain )
964+ {
965+ if ( domain . Trim ( ) == Environment . MachineName )
966+ {
967+ domain = null ; // for local machine
968+ }
969+
970+ string entryUrl = null ;
971+
972+ // if the user is on the local machine, we can use the WinNT provider to change the password
973+ if ( string . IsNullOrEmpty ( domain ) )
974+ {
975+ entryUrl = "WinNT://" + Environment . MachineName + "/" + username + ",user" ;
976+ }
977+ // othwerwise, we need to find the user's distinguished name in the domain
978+ // so we can use the LDAP provider to change the password
979+ else
980+ {
981+ string userDistinguishedName = null ;
982+ string ldapPath = "LDAP://" + domain ;
983+ try
984+ {
985+
986+ using ( DirectoryEntry searchRoot = new DirectoryEntry ( ldapPath ) )
987+ {
988+ using ( DirectorySearcher searcher = new DirectorySearcher ( searchRoot ) )
989+ {
990+ searcher . Filter = "(&(objectClass=user)(sAMAccountName=" + username + "))" ;
991+ searcher . PropertiesToLoad . Add ( "distinguishedName" ) ;
992+
993+ SearchResult result = searcher . FindOne ( ) ;
994+ if ( result != null && result . Properties . Contains ( "distinguishedName" ) )
995+ {
996+ userDistinguishedName = result . Properties [ "distinguishedName" ] [ 0 ] . ToString ( ) ;
997+ }
998+ }
999+ }
1000+ }
1001+ catch ( Exception ex )
1002+ {
1003+ return Tuple . Create ( false , "The domain cannot be accessed." ) ;
1004+ }
1005+
1006+ if ( string . IsNullOrEmpty ( userDistinguishedName ) )
1007+ {
1008+ return Tuple . Create ( false , "User could not be found in the domain: " + domain ) ;
1009+ }
1010+
1011+ entryUrl = "LDAP://" + domain + "/" + userDistinguishedName ;
1012+ }
1013+
1014+ // get the user's directory entry and then attempt to change the password
1015+ using ( DirectoryEntry user = new DirectoryEntry ( entryUrl ) )
1016+ {
1017+ // if the user is not found, throw an exception
1018+ if ( user == null )
1019+ {
1020+ return Tuple . Create ( false , "The user could not be found." ) ;
1021+ }
1022+
1023+ // change the password
1024+ {
1025+ try
1026+ {
1027+ user . Invoke ( "ChangePassword" , new object [ ] { oldPassword , newPassword } ) ;
1028+ user . CommitChanges ( ) ;
1029+ return Tuple . Create ( true , ( string ) null ) ;
1030+ }
1031+ catch ( System . Reflection . TargetInvocationException ex )
1032+ {
1033+ // if the password change fails, return false with an error message
1034+ if ( ex . InnerException != null )
1035+ {
1036+ // if there is a constraint violation, try the PrincipalContext method
1037+ if ( ex . InnerException is System . DirectoryServices . DirectoryServicesCOMException )
1038+ {
1039+ try
1040+ {
1041+ if ( string . IsNullOrEmpty ( domain ) )
1042+ {
1043+ using ( var pc = new PrincipalContext ( ContextType . Machine ) )
1044+ using ( var userPrincipal = UserPrincipal . FindByIdentity ( pc , IdentityType . SamAccountName , username ) )
1045+ {
1046+ userPrincipal . ChangePassword ( oldPassword , newPassword ) ;
1047+ userPrincipal . Save ( ) ;
1048+ return Tuple . Create ( true , ( string ) null ) ;
1049+ }
1050+ }
1051+ else
1052+ {
1053+ using ( var pc = new PrincipalContext ( ContextType . Domain , domain ?? Environment . MachineName ) )
1054+ using ( var userPrincipal = UserPrincipal . FindByIdentity ( pc , IdentityType . SamAccountName , username ) )
1055+ {
1056+ userPrincipal . ChangePassword ( oldPassword , newPassword ) ;
1057+ userPrincipal . Save ( ) ;
1058+ return Tuple . Create ( true , ( string ) null ) ;
1059+ }
1060+ }
1061+ }
1062+ catch ( Exception pEx )
1063+ {
1064+ return Tuple . Create ( false , pEx . Message ) ;
1065+ }
1066+ }
1067+ return Tuple . Create ( false , ex . InnerException . Message ) ;
1068+ }
1069+ throw ex ; // rethrow if there is no inner exception - we don't know what went wrong
1070+ }
1071+ catch ( Exception ex )
1072+ {
1073+ return Tuple . Create ( false , ex . Message ) ;
1074+ }
1075+ }
1076+ }
1077+ }
9521078 }
9531079}
0 commit comments