Skip to content
This repository
Browse code

BUGFIX Keep Member.PasswordEncryption setting on empty passwords

This will prevent empty passwords to set the encryption to 'none',
which in turn will store any subsequent password changes in cleartext.
Reproduceable e.g. with ConfirmedPasswordField and setCanBeEmpty(true).
  • Loading branch information...
commit eecd34868f6f4a1ba2ac69f062a0c8f5dbfa0862 1 parent 3e27d27
Ingo Schommer authored January 06, 2013
119  security/Security.php
@@ -5,7 +5,7 @@
5 5
  * @subpackage security
6 6
  */
7 7
 class Security extends Controller {
8  
-
  8
+	
9 9
 	/**
10 10
 	 * Default user name. Only used in dev-mode by {@link setDefaultAdmin()}
11 11
 	 * 
@@ -82,7 +82,7 @@ class Security extends Controller {
82 82
 	 * @var array|string
83 83
 	 */
84 84
 	protected static $default_message_set = '';
85  
-	
  85
+
86 86
 	/**
87 87
 	 * Get location of word list file
88 88
 	 */
@@ -130,22 +130,22 @@ static function set_default_message_set($messageSet) {
130 130
 	 * If you don't provide a messageSet, a default will be used.
131 131
 	 *
132 132
 	 * @param Controller $controller The controller that you were on to cause the permission
133  
-	 *              failure.
  133
+	 *                               failure.
134 134
 	 * @param string|array $messageSet The message to show to the user. This
135  
-	 *                                  can be a string, or a map of different
136  
-	 *                                  messages for different contexts.
137  
-	 *                                  If you pass an array, you can use the
138  
-	 *                                  following keys:
139  
-	 *                                    - default: The default message
140  
-	 *                                    - logInAgain: The message to show
141  
-	 *                                                  if the user has just
142  
-	 *                                                  logged out and the
143  
-	 *                                    - alreadyLoggedIn: The message to
144  
-	 *                                                       show if the user
145  
-	 *                                                       is already logged
146  
-	 *                                                       in and lacks the
147  
-	 *                                                       permission to
148  
-	 *                                                       access the item.
  135
+	 *                                 can be a string, or a map of different
  136
+	 *                                 messages for different contexts.
  137
+	 *                                 If you pass an array, you can use the
  138
+	 *                                 following keys:
  139
+	 *                                   - default: The default message
  140
+	 *                                   - logInAgain: The message to show
  141
+	 *                                                 if the user has just
  142
+	 *                                                 logged out and the
  143
+	 *                                   - alreadyLoggedIn: The message to
  144
+	 *                                                      show if the user
  145
+	 *                                                      is already logged
  146
+	 *                                                      in and lacks the
  147
+	 *                                                      permission to
  148
+	 *                                                      access the item.
149 149
 	 *
150 150
 	 * The alreadyLoggedIn value can contain a '%s' placeholder that will be replaced with a link
151 151
 	 * to log in.
@@ -233,7 +233,7 @@ static function permissionFailure($controller = null, $messageSet = null) {
233 233
 	}
234 234
 
235 235
 
236  
-  /**
  236
+	/**
237 237
 	 * Get the login form to process according to the submitted data
238 238
 	 */
239 239
 	protected function LoginForm() {
@@ -255,7 +255,7 @@ protected function LoginForm() {
255 255
 	}
256 256
 
257 257
 
258  
-  /**
  258
+	/**
259 259
 	 * Get the login forms for all available authentication methods
260 260
 	 *
261 261
 	 * @return array Returns an array of available login forms (array of Form
@@ -269,8 +269,8 @@ protected function GetLoginForms()
269 269
 
270 270
 		$authenticators = Authenticator::get_authenticators();
271 271
 		foreach($authenticators as $authenticator) {
272  
-		  array_push($forms,
273  
-								 call_user_func(array($authenticator, 'get_login_form'),
  272
+			array_push($forms,
  273
+						call_user_func(array($authenticator, 'get_login_form'),
274 274
 																$this));
275 275
 		}
276 276
 
@@ -293,9 +293,9 @@ public static function Link($action = null) {
293 293
 	 * Log the currently logged in user out
294 294
 	 *
295 295
 	 * @param bool $redirect Redirect the user back to where they came.
296  
-	 *                         - If it's false, the code calling logout() is
297  
-	 *                           responsible for sending the user where-ever
298  
-	 *                           they should go.
  296
+	 *                       - If it's false, the code calling logout() is
  297
+	 *                         responsible for sending the user where-ever
  298
+	 *                         they should go.
299 299
 	 */
300 300
 	public function logout($redirect = true) {
301 301
 		$member = Member::currentUser();
@@ -328,15 +328,15 @@ public function login() {
328 328
 			Requirements::css($customCSS);
329 329
 		}
330 330
 
331  
-		$tmpPage = new Page();
332  
-		$tmpPage->Title = _t('Security.LOGIN', 'Log in');
333  
-		$tmpPage->URLSegment = "Security";
334  
-		// Disable ID-based caching  of the log-in page by making it a random number
335  
-		$tmpPage->ID = -1 * rand(1,10000000);
  331
+			$tmpPage = new Page();
  332
+			$tmpPage->Title = _t('Security.LOGIN', 'Log in');
  333
+			$tmpPage->URLSegment = "Security";
  334
+			// Disable ID-based caching  of the log-in page by making it a random number
  335
+			$tmpPage->ID = -1 * rand(1,10000000);
336 336
 
337 337
 		$controller = new Page_Controller($tmpPage);
338  
-		$controller->init();
339  
-		//Controller::$currentController = $controller;
  338
+			$controller->init();
  339
+			//Controller::$currentController = $controller;
340 340
 
341 341
 		$content = '';
342 342
 		$forms = $this->GetLoginForms();
@@ -418,12 +418,12 @@ public function lostpassword() {
418 418
 		Requirements::javascript(SAPPHIRE_DIR . '/javascript/prototype_improvements.js');
419 419
 		Requirements::javascript(THIRDPARTY_DIR . '/scriptaculous/effects.js');
420 420
 
421  
-		$tmpPage = new Page();
422  
-		$tmpPage->Title = _t('Security.LOSTPASSWORDHEADER', 'Lost Password');
423  
-		$tmpPage->URLSegment = 'Security';
424  
-		$tmpPage->ID = -1; // Set the page ID to -1 so we dont get the top level pages as its children
  421
+			$tmpPage = new Page();
  422
+			$tmpPage->Title = _t('Security.LOSTPASSWORDHEADER', 'Lost Password');
  423
+			$tmpPage->URLSegment = 'Security';
  424
+			$tmpPage->ID = -1; // Set the page ID to -1 so we dont get the top level pages as its children
425 425
 		$controller = new Page_Controller($tmpPage);
426  
-		$controller->init();
  426
+			$controller->init();
427 427
 
428 428
 		$customisedController = $controller->customise(array(
429 429
 			'Content' => 
@@ -478,15 +478,15 @@ public function passwordsent($request) {
478 478
 		Requirements::javascript(SAPPHIRE_DIR . '/javascript/prototype_improvements.js');
479 479
 		Requirements::javascript(THIRDPARTY_DIR . '/scriptaculous/effects.js');
480 480
 
481  
-		$tmpPage = new Page();
482  
-		$tmpPage->Title = _t('Security.LOSTPASSWORDHEADER');
483  
-		$tmpPage->URLSegment = 'Security';
484  
-		$tmpPage->ID = -1; // Set the page ID to -1 so we dont get the top level pages as its children
  481
+			$tmpPage = new Page();
  482
+			$tmpPage->Title = _t('Security.LOSTPASSWORDHEADER');
  483
+			$tmpPage->URLSegment = 'Security';
  484
+			$tmpPage->ID = -1; // Set the page ID to -1 so we dont get the top level pages as its children
485 485
 		$controller = new Page_Controller($tmpPage);
486  
-		$controller->init();
  486
+			$controller->init();
487 487
 
488 488
 		$email = Convert::raw2xml($request->param('ID') . '.' . $request->getExtension());
489  
-		
  489
+
490 490
 		$customisedController = $controller->customise(array(
491 491
 			'Title' => sprintf(_t('Security.PASSWORDSENTHEADER', "Password reset link sent to '%s'"), $email),
492 492
 			'Content' =>
@@ -529,12 +529,12 @@ public static function getPasswordResetLink($member, $autologinToken) {
529 529
 	 * @return string Returns the "change password" page as HTML code.
530 530
 	 */
531 531
 	public function changepassword() {
532  
-		$tmpPage = new Page();
533  
-		$tmpPage->Title = _t('Security.CHANGEPASSWORDHEADER', 'Change your password');
534  
-		$tmpPage->URLSegment = 'Security';
535  
-		$tmpPage->ID = -1; // Set the page ID to -1 so we dont get the top level pages as its children
  532
+			$tmpPage = new Page();
  533
+			$tmpPage->Title = _t('Security.CHANGEPASSWORDHEADER', 'Change your password');
  534
+			$tmpPage->URLSegment = 'Security';
  535
+			$tmpPage->ID = -1; // Set the page ID to -1 so we dont get the top level pages as its children
536 536
 		$controller = new Page_Controller($tmpPage);
537  
-		$controller->init();
  537
+			$controller->init();
538 538
 
539 539
 		// Extract the member from the URL.
540 540
 		$member = null;
@@ -673,8 +673,8 @@ static function findAnAdministrator() {
673 673
 			$adminGroup = $adminGroup->First();
674 674
 
675 675
 			if($adminGroup->Members()->First()) {
676  
-				$member = $adminGroup->Members()->First();
677  
-			}
  676
+			$member = $adminGroup->Members()->First();
  677
+		}
678 678
 		}
679 679
 
680 680
 		if(!$adminGroup) {
@@ -835,18 +835,9 @@ public static function get_password_encryption_algorithm() {
835 835
 	 * @see encrypt_passwords()
836 836
 	 * @see set_password_encryption_algorithm()
837 837
 	 */
838  
-	static function encrypt_password($password, $salt = null, $algorithm = null, $member = null) {
839  
-		if(
840  
-			// if the password is empty, don't encrypt
841  
-			strlen(trim($password)) == 0  
842  
-			// if no algorithm is provided and no default is set, don't encrypt
843  
-			|| (!$algorithm && self::$encryptPasswords == false)
844  
-		) {
845  
-			$algorithm = 'none';
846  
-		} else {
847  
-			// Fall back to the default encryption algorithm
848  
-			if(!$algorithm) $algorithm = self::$encryptionAlgorithm;
849  
-		} 
  838
+	public static function encrypt_password($password, $salt = null, $algorithm = null, $member = null) {
  839
+		// Fall back to the default encryption algorithm
  840
+		if(!$algorithm) $algorithm = self::$encryptionAlgorithm;
850 841
 		
851 842
 		$e = PasswordEncryptor::create_for_algorithm($algorithm);
852 843
 
@@ -870,7 +861,7 @@ static function encrypt_password($password, $salt = null, $algorithm = null, $me
870 861
 	public static function database_is_ready() {
871 862
 		// Used for unit tests
872 863
 		if(self::$force_database_is_ready !== NULL) return self::$force_database_is_ready;
873  
-		
  864
+
874 865
 		$requiredTables = ClassInfo::dataClassesFor('Member');
875 866
 		$requiredTables[] = 'Group';
876 867
 		$requiredTables[] = 'Permission';
@@ -878,7 +869,7 @@ public static function database_is_ready() {
878 869
 		foreach($requiredTables as $table) {
879 870
 			// if any of the tables aren't created in the database
880 871
 			if(!ClassInfo::hasTable($table)) return false;
881  
-		
  872
+
882 873
 			// if any of the tables don't have all fields mapped as table columns
883 874
 			$dbFields = DB::fieldList($table);
884 875
 			if(!$dbFields) return false;
@@ -929,5 +920,5 @@ public static function default_login_dest() {
929 920
 		return self::$default_login_dest;
930 921
 	}
931 922
 
932  
-}
  923
+	}
933 924
 ?>
17  tests/security/MemberTest.php
@@ -114,6 +114,23 @@ function testDefaultPasswordEncryptionDoesntChangeExistingMembers() {
114 114
 		
115 115
 		Security::set_password_encryption_algorithm($origAlgo);
116 116
 	}
  117
+
  118
+	public function testKeepsEncryptionOnEmptyPasswords() {
  119
+		$member = new Member();
  120
+		$member->Password = 'mypassword';
  121
+		$member->PasswordEncryption = 'sha1_v2.4';
  122
+		$member->write();
  123
+		
  124
+		$member->Password = '';
  125
+		$member->write();
  126
+		
  127
+		$this->assertEquals(
  128
+			$member->PasswordEncryption, 
  129
+			'sha1_v2.4'
  130
+		);
  131
+		$result = $member->checkPassword('');
  132
+		$this->assertTrue($result->valid());
  133
+	}
117 134
 	
118 135
 	function testSetPassword() {
119 136
 		$member = $this->objFromFixture('Member', 'test');

0 notes on commit eecd348

Please sign in to comment.
Something went wrong with that request. Please try again.