0013188: set interval for user password change
authorPhilipp Schüle <p.schuele@metaways.de>
Mon, 12 Jun 2017 12:21:07 +0000 (14:21 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Tue, 13 Jun 2017 15:03:49 +0000 (17:03 +0200)
* user passwords must be changed after X days
* default is 0 days (= never)
* only working for non-LDAP setups

!usermanual

https://forge.tine20.org/view.php?id=13188

Change-Id: If1c38caf4103fdc7a19e97f459bd6bcd25b86442
Reviewed-on: http://gerrit.tine20.com/customers/4860
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
Tested-by: Jenkins CI (http://ci.tine20.com/)
tests/tine20/Tinebase/User/SqlTest.php
tine20/Setup/Controller.php
tine20/Setup/js/AuthenticationPanel.js
tine20/Tinebase/Config.php
tine20/Tinebase/Model/FullUser.php
tine20/Tinebase/Model/User.php
tine20/Tinebase/Server/Abstract.php
tine20/Tinebase/User/Sql.php

index eb77afd..fb59459 100644 (file)
@@ -5,7 +5,7 @@
  * @package     Tinebase
  * @subpackage  Account
  * @license     http://www.gnu.org/licenses/agpl.html
- * @copyright   Copyright (c) 2008-2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2008-2017 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Lars Kneschke <l.kneschke@metaways.de>
  */
 
@@ -59,6 +59,7 @@ class Tinebase_User_SqlTest extends TestCase
     /**
      * try to add an account
      *
+     * @return Tinebase_Model_FullUser
      */
     public function testAddUser()
     {
@@ -410,4 +411,36 @@ class Tinebase_User_SqlTest extends TestCase
         
         return $user;
     }
+
+    /**
+     * @see 0013188: set interval for user password change
+     */
+    public function testPasswordMustChange()
+    {
+        $user = $this->testAddUser();
+        self::assertTrue($user->mustChangePassword());
+
+        $user->setPassword(Tinebase_Record_Abstract::generateUID(20));
+        // refetch
+        $user = Tinebase_User::getInstance()->getFullUserById($user->getId());
+        self::assertFalse($user->mustChangePassword());
+
+        // change days config to 10
+        Tinebase_Config::getInstance()->set(Tinebase_Config::PASSWORD_POLICY_CHANGE_AFTER, 10);
+
+        // set password change: 11 days ago)
+        $now = Tinebase_DateTime::now();
+        $accountData['last_password_change'] = $now->subDay(11)->get(Tinebase_Record_Abstract::ISO8601LONG);
+        $accountsTable = new Tinebase_Db_Table(array('name' => SQL_TABLE_PREFIX . 'accounts'));
+        $where = array(
+            $accountsTable->getAdapter()->quoteInto(
+                $accountsTable->getAdapter()->quoteIdentifier('id') . ' = ?', $user->getId()
+            )
+        );
+        $accountsTable->update($accountData, $where);
+
+        // refetch
+        $user = Tinebase_User::getInstance()->getFullUserById($user->getId());
+        self::assertTrue($user->mustChangePassword(), 'user should need pw change: ' . print_r($user->toArray(), true));
+    }
 }
index a69071f..4ec5332 100644 (file)
@@ -1210,6 +1210,7 @@ class Setup_Controller
             Tinebase_Config::PASSWORD_POLICY_MIN_UPPERCASE_CHARS => 0,
             Tinebase_Config::PASSWORD_POLICY_MIN_SPECIAL_CHARS   => 0,
             Tinebase_Config::PASSWORD_POLICY_MIN_NUMBERS         => 0,
+            Tinebase_Config::PASSWORD_POLICY_CHANGE_AFTER        => 0,
         );
 
         $result = array();
index 7ca2379..84c82fe 100644 (file)
@@ -541,6 +541,14 @@ Tine.Setup.AuthenticationPanel = Ext.extend(Tine.Tinebase.widgets.form.ConfigPan
             }, {
                 name: 'password_pwPolicyMinNumbers',
                 fieldLabel: this.app.i18n._('Minimum numbers')
+            }, {
+                name: 'password_pwPolicyChangeAfter',
+                fieldLabel: this.app.i18n._('Change password after ... days'),
+                strategy: {
+                    xtype: 'number',
+                    minValue: 0,
+                    maxValue: 365
+                },
             },
             Ext.applyIf({
                 name: 'password_pwPolicyForbidUsername',
index 9597a8b..ac310e6 100644 (file)
@@ -362,7 +362,14 @@ class Tinebase_Config extends Tinebase_Config_Abstract
      * @var string
      */
     const PASSWORD_POLICY_FORBID_USERNAME = 'pwPolicyForbidUsername';
-    
+
+    /**
+     * PASSWORD_POLICY_CHANGE_AFTER
+     *
+     * @var string
+     */
+    const PASSWORD_POLICY_CHANGE_AFTER = 'pwPolicyChangeAfter';
+
     /**
      * AUTOMATIC_BUGREPORTS
      *
@@ -1214,6 +1221,17 @@ class Tinebase_Config extends Tinebase_Config_Abstract
             'setByAdminModule'      => FALSE,
             'setBySetupModule'      => TRUE,
         ),
+        self::PASSWORD_POLICY_CHANGE_AFTER => array(
+        //_('Change Password After ... Days')
+            'label'                 => 'Change Password After ... Days',
+        //_('Users need to change their passwords after defined number of days')
+            'description'           => 'Users need to change their passwords after defined number of days',
+            'type'                  => 'integer',
+            'clientRegistryInclude' => FALSE,
+            'setByAdminModule'      => FALSE,
+            'setBySetupModule'      => TRUE,
+            'default'               => 0,
+        ),
         self::AUTOMATIC_BUGREPORTS => array(
                                    //_('Automatic bugreports')
             'label'                 => 'Automatic bugreports',
index 637c1a9..20b103b 100644 (file)
@@ -35,6 +35,7 @@
  * @property    Tinebase_Model_EmailUser    emailUser
  * @property    Tinebase_Model_EmailUser    imapUser
  * @property    Tinebase_Model_EmailUser    smtpUser
+ * @property    Tinebase_DateTime           accountLastPasswordChange      date when password was last changed
  *
  */
 class Tinebase_Model_FullUser extends Tinebase_Model_User
@@ -268,7 +269,7 @@ class Tinebase_Model_FullUser extends Tinebase_Model_User
                     if (Tinebase_Core::isLogLevel(Zend_Log::NOTICE)) Tinebase_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ 
                         . ' User ' . $this->accountLoginName . ' has to change his pw: it got never set by user');
                         
-                    return true;;
+                    return true;
                     
                 } else if (isset($this->sambaSAM->pwdLastSet) && $this->sambaSAM->pwdLastSet instanceof DateTime) {
                     $dateToCompare = $this->sambaSAM->pwdLastSet;
@@ -295,10 +296,19 @@ class Tinebase_Model_FullUser extends Tinebase_Model_User
      */
     protected function _sqlPasswordChangeNeeded()
     {
-        return empty($this->accountLastPasswordChange);
+        if (empty($this->accountLastPasswordChange)) {
+            return true;
+        }
+        $passwordChangeDays = Tinebase_Config::getInstance()->get(Tinebase_Config::PASSWORD_POLICY_CHANGE_AFTER);
+
+        if ($passwordChangeDays > 0) {
+            $now = Tinebase_DateTime::now();
+            return $this->accountLastPasswordChange->isEarlier($now->subDay($passwordChangeDays));
+        } else {
+            return false;
+        }
     }
-    
-    
+
     /**
      * return the public informations of this user only
      *
index e61c8e1..b1d3e82 100644 (file)
@@ -233,15 +233,11 @@ class Tinebase_Model_User extends Tinebase_Record_Abstract
      *
      * @param string $_password
      * @return void
-     * @todo write test for that
      */
     public function setPassword($_password)
     {
         $backend = Tinebase_User::getInstance();
-        
-        $result = $backend->setPassword($this->accountId, $_password);
-        
-        return $result;
+        $backend->setPassword($this->accountId, $_password);
     }
     
     /**
index 2412d8f..f5f2656 100644 (file)
@@ -140,6 +140,7 @@ abstract class Tinebase_Server_Abstract implements Tinebase_Server_Interface
         } catch (Tinebase_Exception_NotFound $tenf) {
             // session might be invalid, destroy it
             Tinebase_Session::destroyAndRemoveCookie();
+            $userApplications = array();
         }
 
         $definitions = array();
index 6918dab..e8010bc 100644 (file)
@@ -21,7 +21,6 @@ class Tinebase_User_Sql extends Tinebase_User_Abstract
 {
     use Tinebase_Controller_Record_ModlogTrait;
 
-
     /**
      * Model name
      *
@@ -369,6 +368,8 @@ class Tinebase_User_Sql extends Tinebase_User_Abstract
      * get user select
      *
      * @return Zend_Db_Select
+     *
+     * TODO get available fields from schema
      */
     protected function _getUserSelectObject()
     {
@@ -477,7 +478,7 @@ class Tinebase_User_Sql extends Tinebase_User_Abstract
         $newPassword = new Tinebase_Model_UserPassword($accountData, true);
         $this->_writeModLog($newPassword, $oldPassword);
     }
-    
+
     /**
      * set password in plugins
      *