0013314: allow users to change pin
authorPhilipp Schüle <p.schuele@metaways.de>
Fri, 7 Jul 2017 08:15:39 +0000 (10:15 +0200)
committersstamer <s.stamer@metaways.de>
Mon, 10 Jul 2017 13:11:05 +0000 (15:11 +0200)
https://forge.tine20.org/view.php?id=13314

Change-Id: If61675dbaba1f86e38d8fd79b76d06fd4b65943f
Reviewed-on: http://gerrit.tine20.com/customers/5058
Tested-by: Philipp Schüle <p.schuele@metaways.de>
Reviewed-by: sstamer <s.stamer@metaways.de>
Tested-by: sstamer <s.stamer@metaways.de>
tests/tine20/Tinebase/Frontend/JsonTest.php
tests/tine20/Tinebase/PreferenceTest.php
tine20/Tinebase/Controller.php
tine20/Tinebase/Frontend/Json.php
tine20/Tinebase/Model/Filter/Id.php
tine20/Tinebase/js/MainMenu.js
tine20/Tinebase/js/PasswordChangeDialog.js

index 9d36e0f..27a74d3 100644 (file)
@@ -710,7 +710,22 @@ class Tinebase_Frontend_JsonTest extends TestCase
         
         $this->assertEquals(0, $result['totalCount']);
     }
-    
+
+    /**
+     * @see 0013314: allow users to change pin
+     */
+    public function testChangePin()
+    {
+        $result = $this->_instance->changePin('', '1234');
+        self::assertTrue($result['success']);
+
+        $result = $this->_instance->changePin('', '1234');
+        self::assertFalse($result['success']);
+
+        $result = $this->_instance->changePin('1234', '5678');
+        self::assertTrue($result['success']);
+    }
+
     /******************** protected helper funcs ************************/
     
     /**
index d1a3e08..a9909e5 100644 (file)
@@ -326,7 +326,7 @@ class Tinebase_PreferenceTest extends TestCase
         self::assertEquals(1, count($fixedCalendarsPref['value']), 'did not find personal container in value: '
             . print_r($fixedCalendarsPref, true));
         $container = $fixedCalendarsPref['value'][0];
-        self::assertTrue(is_array($container));
+        self::assertTrue(is_array($container), 'container not resolved: ' . print_r($fixedCalendarsPref, true));
         self::assertEquals($personalContainer->getId(), $container['id']);
         self::assertTrue(isset($fixedCalendarsPref['uiconfig']), 'uiconfig missing: '
             . print_r($fixedCalendarsPref, true));
index d3b9f3b..e87e25b 100644 (file)
@@ -419,20 +419,32 @@ class Tinebase_Controller extends Tinebase_Controller_Event
      * @throws  Tinebase_Exception_AccessDenied
      * @throws  Tinebase_Exception_InvalidArgument
      */
-    public function changePassword($_oldPassword, $_newPassword)
+    public function changePassword($_oldPassword, $_newPassword, $_pwType = 'password')
     {
-        if (! Tinebase_Config::getInstance()->get(Tinebase_Config::PASSWORD_CHANGE, TRUE)) {
+        if ($_pwType === 'password' && ! Tinebase_Config::getInstance()->get(Tinebase_Config::PASSWORD_CHANGE, TRUE)) {
             throw new Tinebase_Exception_AccessDenied('Password change not allowed.');
         }
-        
-        $loginName = Tinebase_Core::getUser()->accountLoginName;
-        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . " change password for $loginName");
-        
-        if (!Tinebase_Auth::getInstance()->isValidPassword($loginName, $_oldPassword)) {
-            throw new Tinebase_Exception_InvalidArgument('Old password is wrong.');
+
+        $user = Tinebase_Core::getUser();
+        $loginName = $user->accountLoginName;
+        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+            . " change $_pwType for $loginName");
+
+        if ($_pwType === 'password') {
+            if (!Tinebase_Auth::getInstance()->isValidPassword($loginName, $_oldPassword)) {
+                throw new Tinebase_Exception_InvalidArgument('Old password is wrong.');
+            }
+            Tinebase_User::getInstance()->setPassword($user, $_newPassword, true, false);
+        } else {
+            $validateOldPin = Tinebase_Auth::validateSecondFactor($loginName, $_oldPassword, array(
+                'active' => true,
+                'provider' => 'Tine20',
+            ));
+            if ($validateOldPin !== Tinebase_Auth::SUCCESS) {
+                throw new Tinebase_Exception_InvalidArgument('Old pin is wrong.');
+            }
+            Tinebase_User::getInstance()->setPin($user, $_newPassword);
         }
-        
-        Tinebase_User::getInstance()->setPassword(Tinebase_Core::getUser(), $_newPassword, true, false);
     }
     
     /**
index 79d8773..8041e0b 100644 (file)
@@ -177,22 +177,45 @@ class Tinebase_Frontend_Json extends Tinebase_Frontend_Json_Abstract
      */
     public function changePassword($oldPassword, $newPassword)
     {
+        return $this->_changePwOrPin($oldPassword, $newPassword);
+    }
+
+    /**
+     * @param        $oldPassword
+     * @param        $newPassword
+     * @param string $pwType
+     * @return array
+     */
+    protected function _changePwOrPin($oldPassword, $newPassword, $pwType = 'password')
+    {
         $response = array(
             'success'      => TRUE
         );
-        
+
         try {
-            Tinebase_Controller::getInstance()->changePassword($oldPassword, $newPassword);
+            Tinebase_Controller::getInstance()->changePassword($oldPassword, $newPassword, $pwType);
         } catch (Tinebase_Exception $e) {
             $response = array(
                 'success'      => FALSE,
                 'errorMessage' => "New password could not be set! Error: " . $e->getMessage()
             );
         }
-        
+
         return $response;
     }
-    
+
+    /**
+     * change pin of user
+     *
+     * @param  string $oldPassword the old password
+     * @param  string $newPassword the new password
+     * @return array
+     */
+    public function changePin($oldPassword, $newPassword)
+    {
+        return $this->_changePwOrPin($oldPassword, $newPassword, 'pin');
+    }
+
     /**
      * clears state
      *
@@ -740,6 +763,8 @@ class Tinebase_Frontend_Json extends Tinebase_Frontend_Json_Abstract
         $registryData =  array(
             'modSsl'           => Tinebase_Auth::getConfiguredBackend() == Tinebase_Auth::MODSSL,
             'secondFactor'     => $secondFactorConfig && $secondFactorConfig->active && $secondFactorConfig->login,
+            'secondFactorPinChangeAllowed' => $secondFactorConfig
+                && $secondFactorConfig->active && $secondFactorConfig->provider && $secondFactorConfig->provider === 'Tine20',
             'serviceMap'       => $tbFrontendHttp->getServiceMap(),
             'locale'           => array(
                 'locale'   => $locale->toString(),
index f91f10f..155aeb2 100644 (file)
@@ -192,7 +192,7 @@ class Tinebase_Model_Filter_Id extends Tinebase_Model_Filter_Abstract
             } elseif (isset($this->_options['modelName'])) {
                 $this->_controller = Tinebase_Core::getApplicationInstance($this->_options['modelName']);
             } else {
-                Tinebase_Core::getLogger()->INFO(__METHOD__ . '::' . __LINE__
+                if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__
                     . ' No modelName or controller defined in filter options, can not resolve record.');
                 return null;
             }
index d2902a8..5dd42a4 100644 (file)
@@ -108,9 +108,13 @@ Tine.Tinebase.MainMenu = Ext.extend(Ext.Toolbar, {
             this.userActions = [
                 this.action_editProfile,
                 this.action_showPreferencesDialog,
-                this.action_changePassword,
-                this.action_notificationPermissions
+                this.action_notificationPermissions,
+                this.action_changePassword
             ];
+
+            if (Tine.Tinebase.registry.get('secondFactorPinChangeAllowed')) {
+                this.userActions = this.userActions.concat(this.action_changePin);
+            }
             
             if (Tine.Tinebase.registry.get('userAccountChanged')) {
                 this.action_returnToOriginalUser = new Tine.widgets.account.ChangeAccountAction({
@@ -187,7 +191,14 @@ Tine.Tinebase.MainMenu = Ext.extend(Ext.Toolbar, {
             disabled: (! Tine.Tinebase.configManager.get('changepw')),
             iconCls: 'action_password'
         });
-        
+
+        this.action_changePin = new Ext.Action({
+            text: i18n._('Change PIN'),
+            handler: this.onChangePin,
+            //disabled: (! Tine.Tinebase.configManager.get('changepin')),
+            iconCls: 'action_password'
+        });
+
         this.action_logout = new Ext.Action({
             text: i18n._('Logout'),
             tooltip:  String.format(i18n._('Logout from {0}'), Tine.title),
@@ -246,7 +257,17 @@ Tine.Tinebase.MainMenu = Ext.extend(Ext.Toolbar, {
         var passwordDialog = new Tine.Tinebase.PasswordChangeDialog();
         passwordDialog.show();
     },
-    
+
+    /**
+     * @private
+     */
+    onChangePin: function() {
+        var passwordDialog = new Tine.Tinebase.PasswordChangeDialog({
+            pwType: 'pin',
+        });
+        passwordDialog.show();
+    },
+
     /**
      * @private
      */
index 97380d2..4f415e3 100644 (file)
@@ -3,7 +3,7 @@
  * 
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Philipp Schuele <p.schuele@metaways.de>
- * @copyright   Copyright (c) 2010 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2010-2017 Metaways Infosystems GmbH (http://www.metaways.de)
  *
  */
  
@@ -26,10 +26,17 @@ Tine.Tinebase.PasswordChangeDialog = Ext.extend(Ext.Window, {
     layout: 'fit',
     plain: true,
     title: null,
+    // password or pin
+    pwType: 'password',
 
     initComponent: function() {
         this.currentAccount = Tine.Tinebase.registry.get('currentAccount');
-        this.title = (this.title !== null) ? this.title : String.format(i18n._('Change Password For "{0}"'), this.currentAccount.accountDisplayName);
+        this.passwordLabel = this.pwType === 'pin' ? i18n._('PIN') : i18n._('Password');
+        this.title = (this.title !== null) ? this.title : String.format(
+            i18n._('Change {0} For "{1}"'),
+            this.passwordLabel,
+            this.currentAccount.accountDisplayName
+        );
         
         this.items = new Ext.FormPanel({
             bodyStyle: 'padding:5px;',
@@ -44,15 +51,15 @@ Tine.Tinebase.PasswordChangeDialog = Ext.extend(Ext.Window, {
             },
             items: [{
                 id: 'oldPassword',
-                fieldLabel: i18n._('Old Password'),
+                fieldLabel: String.format(i18n._('Old {0}'), this.passwordLabel),
                 name:'oldPassword'
             },{
                 id: 'newPassword',
-                fieldLabel: i18n._('New Password'),
+                fieldLabel: String.format(i18n._('New {0}'), this.passwordLabel),
                 name:'newPassword'
             },{
                 id: 'newPasswordSecondTime',
-                fieldLabel: i18n._('Repeat new Password'),
+                fieldLabel: String.format(i18n._('Repeat new {0}'), this.passwordLabel),
                 name:'newPasswordSecondTime'
             }],
             buttons: [{
@@ -72,9 +79,9 @@ Tine.Tinebase.PasswordChangeDialog = Ext.extend(Ext.Window, {
                         if (values.newPassword == values.newPasswordSecondTime) {
                             Ext.Ajax.request({
                                 waitTitle: i18n._('Please Wait!'),
-                                waitMsg: i18n._('changing password...'),
+                                waitMsg: String.format(i18n._('Changing {0}'), this.passwordLabel),
                                 params: {
-                                    method: 'Tinebase.changePassword',
+                                    method: this.pwType === 'pin' ? 'Tinebase.changePin' : 'Tinebase.changePassword',
                                     oldPassword: values.oldPassword,
                                     newPassword: values.newPassword
                                 },
@@ -84,16 +91,18 @@ Tine.Tinebase.PasswordChangeDialog = Ext.extend(Ext.Window, {
                                         Ext.getCmp('changePassword_window').close();
                                         Ext.MessageBox.show({
                                             title: i18n._('Success'),
-                                            msg: i18n._('Your password has been changed.'),
+                                            msg: String.format(i18n._('Your {0} has been changed.'), this.passwordLabel),
                                             buttons: Ext.MessageBox.OK,
                                             icon: Ext.MessageBox.INFO
                                         });
-                                        Ext.Ajax.request({
-                                            params: {
-                                                method: 'Tinebase.updateCredentialCache',
-                                                password: values.newPassword
-                                            }
-                                        });
+                                        if (this.pwType === 'password') {
+                                            Ext.Ajax.request({
+                                                params: {
+                                                    method: 'Tinebase.updateCredentialCache',
+                                                    password: values.newPassword
+                                                }
+                                            });
+                                        }
                                     } else {
                                         Ext.MessageBox.show({
                                             title: i18n._('Failure'),
@@ -102,18 +111,20 @@ Tine.Tinebase.PasswordChangeDialog = Ext.extend(Ext.Window, {
                                             icon: Ext.MessageBox.ERROR  
                                         });
                                     }
-                                }
+                                },
+                                scope: this
                             });
                         } else {
                             Ext.MessageBox.show({
                                 title: i18n._('Failure'),
-                                msg: i18n._('The new passwords mismatch, please correct them.'),
+                                msg: String.format(i18n._('{0} mismatch, please correct.'), this.passwordLabel),
                                 buttons: Ext.MessageBox.OK,
                                 icon: Ext.MessageBox.ERROR 
                             });
                         }
                     }
-                }
+                },
+                scope: this
             }]
         });