Permalink
Browse files

#3008: add password policies

- added new password config options to Tinebase
- moved changepw to password config
- created input fields in setup
- added setup test
- implemented savePasswordSettings
- enforce pw policy (including test)
- add update script for changepw

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

Change-Id: I5e70c358e854286d7a4645bb2728ec937d054de9
Reviewed-on: https://gerrit.tine20.org/tine20/671
Tested-by: jenkins user
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
Tested-by: Philipp Schüle <p.schuele@metaways.de>
  • Loading branch information...
1 parent 0b001a1 commit 2b367da71653e5264ba955d79f9d75ce15a9c64b @pschuele pschuele committed May 29, 2012
View
@@ -8,6 +8,7 @@
nbproject
tine20/config.inc.php
tests/tine20/phpunitconfig.inc.php
+tests/setup/phpunitconfig.inc.php
tine20/*/*/*FAT*
tine20/*/js/*-lang-*
tine20/Tinebase/js/Locale/build/*.js
@@ -256,6 +256,37 @@ public function testSaveConfig()
}
/**
+ * testSavePasswordSettings
+ *
+ * @see 0003008: add password policies
+ */
+ public function testSavePasswordSettings()
+ {
+ $testAuthenticationData = $this->_json->loadAuthenticationData();
+
+ $testAuthenticationData['password']['changepw'] = TRUE;
+ $testAuthenticationData['password']['pwPolicyActive'] = TRUE;
+ $testAuthenticationData['password']['pwPolicyMinLength'] = 1;
+ $testAuthenticationData['password']['pwPolicyMinWordChars'] = 1;
+
+ $result = $this->_json->saveAuthentication($testAuthenticationData);
+
+ $this->assertEquals(1, $result['success']);
+
+ $testAuthenticationData = $this->_json->loadAuthenticationData();
+ $this->assertTrue(isset($testAuthenticationData['password']), 'pw settings not found: ' . print_r($testAuthenticationData, TRUE));
+ $configs = array(
+ 'changepw',
+ 'pwPolicyActive',
+ 'pwPolicyMinLength',
+ 'pwPolicyMinWordChars',
+ );
+ foreach ($configs as $config) {
+ $this->assertEquals(1, $testAuthenticationData['password'][$config], 'pw setting ' . $config . ' not found: ' . print_r($testAuthenticationData['password'], TRUE));
+ }
+ }
+
+ /**
* _uninstallAllApps helper
*/
protected function _uninstallAllApps()
View
@@ -19,7 +19,15 @@
/*
* Set white / black lists
*/
-PHPUnit_Util_Filter::addDirectoryToFilter(dirname(__FILE__));
+$phpUnitVersion = explode(' ',PHPUnit_Runner_Version::getVersionString());
+if (version_compare($phpUnitVersion[1], "3.6.0") >= 0) {
+ $filter = new PHP_CodeCoverage_Filter();
+ $filter->addDirectoryToBlacklist(dirname(__FILE__));
+} else if (version_compare($phpUnitVersion[1], "3.5.0") >= 0) {
+ PHP_CodeCoverage_Filter::getInstance()->addDirectoryToBlacklist(dirname(__FILE__));
+} else {
+ PHPUnit_Util_Filter::addDirectoryToFilter(dirname(__FILE__));
+}
/*
* Set include path
@@ -48,6 +48,8 @@ protected function setUp()
{
$this->_originalBackendConfiguration = Tinebase_User::getBackendConfiguration();
$this->_originalBackendType = Tinebase_User::getConfiguredBackend();
+
+ Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
}
/**
@@ -58,12 +60,19 @@ protected function setUp()
*/
protected function tearDown()
{
+ Tinebase_TransactionManager::getInstance()->rollBack();
+ Tinebase_Config::getInstance()->clearCache();
+
+ // needs to be reverted because we use Tinebase_User as a singleton
Tinebase_User::setBackendType($this->_originalBackendType);
Tinebase_User::deleteBackendConfiguration();
Tinebase_User::setBackendConfiguration($this->_originalBackendConfiguration);
Tinebase_User::saveBackendConfiguration();
}
+ /**
+ * testSaveBackendConfiguration
+ */
public function testSaveBackendConfiguration()
{
Tinebase_User::setBackendType(Tinebase_User::LDAP);
@@ -77,6 +86,9 @@ public function testSaveBackendConfiguration()
$this->assertNotEquals($rawConfigBefore, $rawConfigAfter);
}
+ /**
+ * testSetBackendConfiguration
+ */
public function testSetBackendConfiguration()
{
Tinebase_User::setBackendType(Tinebase_User::LDAP);
@@ -118,6 +130,9 @@ public function testDeleteBackendConfiguration()
$this->assertTrue(count(Tinebase_User::getBackendConfiguration()) == 0, 'should be empty: ' . print_r(Tinebase_User::getBackendConfiguration(), TRUE));
}
+ /**
+ * get backend cfg defaults
+ */
public function testGetBackendConfigurationDefaults()
{
$defaults = Tinebase_User::getBackendConfigurationDefaults();
@@ -130,4 +145,36 @@ public function testGetBackendConfigurationDefaults()
$this->assertTrue(array_key_exists('host', $defaults));
$this->assertFalse(array_key_exists(Tinebase_User::LDAP, $defaults));
}
+
+ /**
+ * testPasswordPolicy
+ *
+ * @see 0003008: add password policies
+ */
+ public function testPasswordPolicy()
+ {
+ // activate pw policies
+ $policy = array(
+ Tinebase_Config::PASSWORD_POLICY_ACTIVE => TRUE,
+ Tinebase_Config::PASSWORD_POLICY_ONLYASCII => TRUE,
+ Tinebase_Config::PASSWORD_POLICY_MIN_LENGTH => 20,
+ Tinebase_Config::PASSWORD_POLICY_MIN_WORD_CHARS => 4,
+ Tinebase_Config::PASSWORD_POLICY_MIN_UPPERCASE_CHARS => 3,
+ Tinebase_Config::PASSWORD_POLICY_MIN_SPECIAL_CHARS => 2,
+ Tinebase_Config::PASSWORD_POLICY_MIN_NUMBERS => 3,
+ );
+ foreach ($policy as $key => $value) {
+ Tinebase_Config::getInstance()->set($key, $value);
+ }
+
+ $sclever = Tinebase_User::getInstance()->getUserByLoginName('sclever');
+
+ try {
+ Tinebase_User::getInstance()->setPassword($sclever, 'nOve!ry1leverPw2');
+ $this->fail('Expected Tinebase_Exception_PasswordPolicyViolation');
+ } catch (Tinebase_Exception_PasswordPolicyViolation $tppv) {
+ $this->assertContains('Password failed to match the following policy requirements: '.
+ 'pwPolicyMinLength|pwPolicyMinUppercaseChars|pwPolicyMinSpecialChars|pwPolicyMinNumbers', $tppv->getMessage());
+ }
+ }
}
View
@@ -41,14 +41,14 @@ class Admin_Config extends Tinebase_Config_Abstract
* the constructor
*
* don't use the constructor. use the singleton
- */
+ */
private function __construct() {}
/**
* the constructor
*
* don't use the constructor. use the singleton
- */
+ */
private function __clone() {}
/**
@@ -787,7 +787,8 @@ public function loadAuthenticationData()
return array(
'authentication' => $this->_getAuthProviderData(),
'accounts' => $this->_getAccountsStorageData(),
- 'redirectSettings' => $this->_getRedirectSettings()
+ 'redirectSettings' => $this->_getRedirectSettings(),
+ 'password' => $this->_getPasswordSettings(),
);
}
@@ -837,6 +838,10 @@ protected function _updateAuthentication($_authenticationData)
$this->_updateRedirectSettings($_authenticationData['redirectSettings']);
}
+ if (isset($_authenticationData['password'])) {
+ $this->_updatePasswordSettings($_authenticationData['password']);
+ }
+
if (isset($_authenticationData['acceptedTermsVersion'])) {
$this->saveAcceptedTerms($_authenticationData['acceptedTermsVersion']);
}
@@ -971,6 +976,18 @@ protected function _updateRedirectSettings($_data)
}
/**
+ * update pw settings
+ *
+ * @param array $data
+ */
+ protected function _updatePasswordSettings($data)
+ {
+ foreach ($data as $config => $value) {
+ Tinebase_Config::getInstance()->set($config, $value);
+ }
+ }
+
+ /**
*
* get auth provider data
*
@@ -1019,6 +1036,34 @@ protected function _getRedirectSettings()
}
/**
+ * get password settings
+ *
+ * @return array
+ *
+ * @todo should use generic mechanism to fetch setup related configs
+ */
+ protected function _getPasswordSettings()
+ {
+ $configs = array(
+ Tinebase_Config::PASSWORD_CHANGE,
+ Tinebase_Config::PASSWORD_POLICY_ACTIVE,
+ Tinebase_Config::PASSWORD_POLICY_ONLYASCII,
+ Tinebase_Config::PASSWORD_POLICY_MIN_LENGTH,
+ Tinebase_Config::PASSWORD_POLICY_MIN_WORD_CHARS,
+ Tinebase_Config::PASSWORD_POLICY_MIN_UPPERCASE_CHARS,
+ Tinebase_Config::PASSWORD_POLICY_MIN_SPECIAL_CHARS,
+ Tinebase_Config::PASSWORD_POLICY_MIN_NUMBERS,
+ );
+
+ $result = array();
+ foreach ($configs as $config) {
+ $result[$config] = Tinebase_Config::getInstance()->get($config, 0);
+ }
+
+ return $result;
+ }
+
+ /**
* get email config
*
* @return array
@@ -340,8 +340,6 @@ public function getAllRegistryData()
'packageString' => TINE20SETUP_PACKAGESTRING,
'releaseTime' => TINE20SETUP_RELEASETIME
),
- // no password changing in setup
- 'changepw' => FALSE,
);
return $registryData;
@@ -451,13 +451,7 @@ Tine.Setup.AuthenticationPanel = Ext.extend(Tine.Tinebase.widgets.form.ConfigPan
name: 'accounts_Sql_defaultAdminGroupName',
fieldLabel: this.app.i18n._('Default admin group name')
//allowEmpty: false
- },
- Ext.applyIf({
- name: 'accounts_Sql_changepw',
- fieldLabel: this.app.i18n._('User can change password'),
- store: [['1', this.app.i18n._('Yes')], ['0', this.app.i18n._('No')]],
- value: '0'
- }, commonComboConfig)]
+ }]
}, {
id: this.accountsStorageIdPrefix + 'Ldap',
layout: 'form',
@@ -546,13 +540,7 @@ Tine.Setup.AuthenticationPanel = Ext.extend(Tine.Tinebase.widgets.form.ConfigPan
}, {
name: 'accounts_Ldap_defaultAdminGroupName',
fieldLabel: this.app.i18n._('Default admin group name')
- },
- Ext.applyIf({
- name: 'accounts_Ldap_changepw',
- fieldLabel: this.app.i18n._('Allow user to change her password?'),
- store: [['1', this.app.i18n._('Yes')], ['0', this.app.i18n._('No')]],
- value: '0'
- }, commonComboConfig),
+ },
Ext.applyIf({
name: 'accounts_Ldap_readonly',
fieldLabel: this.app.i18n._('Readonly access'),
@@ -565,6 +553,57 @@ Tine.Setup.AuthenticationPanel = Ext.extend(Tine.Tinebase.widgets.form.ConfigPan
xtype: 'fieldset',
collapsible: false,
autoHeight: true,
+ title: this.app.i18n._('Password Settings'),
+ defaults: {
+ width: 300,
+ xtype: 'uxspinner',
+ tabIndex: this.getTabIndex,
+ strategy: {
+ xtype: 'number',
+ minValue: 0,
+ maxValue: 64
+ },
+ value: 0
+ },
+ items: [
+ Ext.applyIf({
+ name: 'password_changepw',
+ fieldLabel: this.app.i18n._('User can change password'),
+ store: [[1, this.app.i18n._('Yes')], [0, this.app.i18n._('No')]],
+ value: 1
+ }, commonComboConfig),
+ Ext.applyIf({
+ name: 'password_pwPolicyActive',
+ fieldLabel: this.app.i18n._('Enable password policy'),
+ store: [[1, this.app.i18n._('Yes')], [0, this.app.i18n._('No')]],
+ value: 0
+ }, commonComboConfig),
+ Ext.applyIf({
+ name: 'password_pwPolicyOnlyASCII',
+ fieldLabel: this.app.i18n._('Only ASCII'),
+ store: [[1, this.app.i18n._('Yes')], [0, this.app.i18n._('No')]],
+ value: 0
+ }, commonComboConfig), {
+ name: 'password_pwPolicyMinLength',
+ fieldLabel: this.app.i18n._('Minimum length')
+ }, {
+ name: 'password_pwPolicyMinWordChars',
+ fieldLabel: this.app.i18n._('Minimum word chars')
+ }, {
+ name: 'password_pwPolicyMinUppercaseChars',
+ fieldLabel: this.app.i18n._('Minimum uppercase chars')
+ }, {
+ name: 'password_pwPolicyMinSpecialChars',
+ fieldLabel: this.app.i18n._('Minimum special chars')
+ }, {
+ name: 'password_pwPolicyMinNumbers',
+ fieldLabel: this.app.i18n._('Minimum numbers')
+ }
+ ]
+ }, {
+ xtype: 'fieldset',
+ collapsible: false,
+ autoHeight: true,
title: this.app.i18n._('Redirect Settings'),
defaults: {
width: 300,
Oops, something went wrong.

0 comments on commit 2b367da

Please sign in to comment.