Skip to content

Commit 9431344

Browse files
committed
Merge pull request #11 from samtuke/master
Improved configuration file handling
2 parents 17898ff + 9d45285 commit 9431344

14 files changed

+272
-85
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ composer.lock
22
config.ini
33
config.php
44
.idea/
5+
core/phpunit.xml

core/Admin.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,16 @@ function validateLogin( $plainPass, $username )
270270
'admin' => null
271271
);
272272

273+
// Hash the supplied password for comparison
274+
$encPass = $this->pass->encrypt( $plainPass );
275+
273276
$result = $this->adminModel->getAdminByUsername( $username );
274277

275-
$adminEntity = $this->adminEntityFromArray( $result );
278+
// If an admin was found with that username
279+
if( $result ) {
276280

277-
$encPass = $this->pass->encrypt( $plainPass );
281+
$adminEntity = $this->adminEntityFromArray( $result );
282+
}
278283

279284
/*
280285
* TODO: this should not happen imo, can this be removed
@@ -288,16 +293,21 @@ function validateLogin( $plainPass, $username )
288293
$result = Sql_Query_Params($query, array($login));
289294
}*/
290295

291-
if ( $adminEntity->disabled ) {
296+
if ( ! $result ) {
297+
$return['error'] = 'Admin not found'; // If admin not found
298+
} elseif( $adminEntity->disabled ) {
292299
// FIXME: translation / formatting via s() removed
293300
$return['error'] = 'Your account has been disabled';
294-
} elseif ($encPass == $adminEntity->encPass) {
301+
} elseif ( $encPass == $adminEntity->encPass ) {
295302
$return['result'] = true;
296303
$return['error'] = '';
297304
$return['admin'] = $adminEntity;
298-
} else {
305+
} elseif ( $encPass != $adminEntity->encPass ) {
299306
// FIXME: translation / formatting via s() removed
300307
$return['error'] = 'Incorrect password';
308+
} else {
309+
// FIXME: translation / formatting via s() removed
310+
$return['error'] = 'Unknown error';
301311
}
302312
return $return;
303313
}

core/Config.php

Lines changed: 89 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,98 @@
77

88
class Config
99
{
10+
public $configFileOrigin;
1011
private $running_config = array();
1112
private $default_config = array();
1213

14+
public function parseIniFile( $configFile )
15+
{
16+
// Load the config file
17+
$parsed = parse_ini_file( $configFile );
18+
if( ! is_array( $parsed ) ) {
19+
throw new \Exception( 'Could not parse specified ini config file: ' . $configFile );
20+
}
21+
return $parsed;
22+
}
23+
1324
/**
1425
* Default constructor
1526
* load configuration from database each new session
1627
* TODO: probably not a good idea when using an installation with multiple subscribers
1728
*/
18-
public function __construct($config_file)
19-
{
20-
/**
29+
public function __construct( $configFile = NULL )
30+
{
31+
/**
2132
* Constants used for debugging and developping
2233
*/
23-
defined('DEBUG') ? null : define('DEBUG', true);
24-
defined('PHPLIST_DEVELOPER_EMAIL') ? null : define('PHPLIST_DEVELOPER_EMAIL', '[email protected]');
25-
defined('PHPLIST_DEV_VERSION') ? null : define('PHPLIST_DEV_VERSION', true);
26-
defined('PHPLIST_VERSION') ? null : define('PHPLIST_VERSION', '4.0.0 dev');
27-
28-
//do we have a configuration saved in session?
29-
if (isset($_SESSION['running_config'])) {
30-
$this->running_config = $_SESSION['running_config'];
34+
defined('DEBUG') ? null : define('DEBUG', true);
35+
defined('PHPLIST_DEVELOPER_EMAIL') ? null : define('PHPLIST_DEVELOPER_EMAIL', '[email protected]');
36+
defined('PHPLIST_DEV_VERSION') ? null : define('PHPLIST_DEV_VERSION', true);
37+
defined('PHPLIST_VERSION') ? null : define('PHPLIST_VERSION', '4.0.0 dev');
38+
39+
// Find the config file to use
40+
$foundConfigFile = $this->findConfigFile( $configFile );
41+
// Check config file is valid
42+
$this->validateConfigFile( $foundConfigFile );
43+
// Load the config file path as an ini file
44+
$this->running_config = $this->parseIniFile( $this->configFilePath );
45+
//Initialise further config
46+
$this->initConfig();
47+
}
48+
49+
/**
50+
* Find the right config file to use
51+
*/
52+
public function findConfigFile( $configFile )
53+
{
54+
// If no config file path provided
55+
if( $configFile !== NULL ) {
56+
$this->configFileOrigin = "supplied file path";
57+
$this->configFilePath = $configFile;
58+
} else { // If no config file specified, look for one
59+
// determine which config file to use
60+
if (
61+
isset( $_SESSION['running_config'] )
62+
&& count( $_SESSION['running_config'] ) > 15
63+
) { // do we have a configuration saved in session?
64+
// If phpList is being used as a library, the config file may be set in session
65+
$this->configFileOrigin = "session";
66+
$this->configFilePath = $_SESSION['running_config'];
67+
68+
} elseif( isset( $GLOBALS['configfile'] ) ) { // Is a phpList 3 config file stored in globals?
69+
70+
$this->configFileOrigin = "globals: phpList3 ini file path";
71+
$this->configFilePath = $GLOBALS['configfile'];
72+
73+
} elseif( isset( $GLOBALS['phplist4-ini-config-file-path'] ) ) { // Is a phpList 4 config file stored in globals?
74+
$this->configFileOrigin = "globals: phpList4 ini file path";
75+
$this->configFilePath = $GLOBALS['phplist4-ini-config-file-path'];
76+
77+
} else {
78+
throw new \Exception( 'Could not find config file, none specified' );
79+
}
80+
}
81+
return $this->configFilePath;
82+
}
83+
84+
/**
85+
* Check that config file is valid
86+
* @param string $configFilePath Path to check
87+
*/
88+
public function validateConfigFile( $configFilePath )
89+
{
90+
if ( ! is_string( $configFilePath ) ) {
91+
throw new \Exception( 'Config file path is not a string (' . gettype( $configFilePath ) . ')' );
92+
} elseif ( ! is_file( $configFilePath ) ) {
93+
throw new \Exception( 'Config file is not a file: ' . $configFilePath );
94+
} elseif( ! filesize( $configFilePath ) > 20 ) {
95+
throw new \Exception( 'Config file too small: ' . $configFilePath );
96+
} elseif( ! parse_ini_file( $configFilePath ) ) {
97+
throw new \Exception( 'Config file not an INI file: ' . $configFilePath );
3198
} else {
32-
if (is_file($config_file) && filesize($config_file) > 20) {
33-
//load from config file
34-
$this->running_config = parse_ini_file($config_file);
35-
//Initialise further config
36-
$this->initConfig();
37-
} else{
38-
throw new \Exception('Cannot find config file: ' . $config_file);
39-
}
99+
return true;
40100
}
41-
}
101+
}
42102

43103
/**
44104
* Run this after db has been initialized, so we get the config from inside the database as well.
@@ -53,7 +113,7 @@ public function runAfterDBInitialised(Database $db){
53113
* @param Language $lan
54114
*/
55115
public function runAfterLanguageInitialised(Language $lan){
56-
$this->loadDefaultConfig($lan);
116+
// $this->loadDefaultConfig($lan);
57117
}
58118

59119
/**
@@ -322,12 +382,16 @@ private function initConfig()
322382
$this->running_config['TLD_REFETCH_TIMEOUT'] = 15552000; ## 180 days, about 6 months
323383
$this->running_config['SHOW_UNSUBSCRIBELINK'] = true;
324384

325-
if (function_exists('hash_algos') && !in_array($this->running_config['ENCRYPTION_ALGO'], hash_algos())) {
326-
throw new \Exception('Encryption algorithm "' . $this->running_config['ENCRYPTION_ALGO'] . '" not supported, change your configuration');
385+
// Check if desired hashing algo is supported by server
386+
if (
387+
function_exists( 'hash_algos' )
388+
&& !in_array( $this->running_config['ENCRYPTION_ALGO'], hash_algos() )
389+
) {
390+
throw new \Exception( 'Encryption algorithm "' . $this->running_config['ENCRYPTION_ALGO'] . '" not supported, change your configuration' );
327391
}
328-
## remember the length of a hashed string
329-
$this->running_config['hash_length'] = strlen(hash($this->running_config['ENCRYPTION_ALGO'],'some text'));
330392

393+
// check and store the length of a hash using the desired algo
394+
$this->running_config['hash_length'] = strlen( hash( $this->running_config['ENCRYPTION_ALGO'], 'some text' ) );
331395

332396
$this->running_config['NUMATTACHMENTS'] = 1;
333397
$this->running_config['FCKIMAGES_DIR'] = 'uploadimages';
@@ -1079,5 +1143,4 @@ private function loadDefaultConfig(Language $lan){
10791143

10801144
);
10811145
}
1082-
10831146
}

core/Model/AdminModel.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ public function __construct( \phplist\Config $config, \phplist\helper\Database $
2525
$this->db = $db;
2626
}
2727

28+
/**
29+
* Fetch all details of an Admin with a given username
30+
* @param strong $username username of admin to fetch
31+
*/
2832
public function getAdminByUsername( $username )
2933
{
3034
$result = $this->db->query(
@@ -40,7 +44,7 @@ public function getAdminByUsername( $username )
4044
, $username
4145
)
4246
);
43-
47+
4448
return $result->fetch( \PDO::FETCH_ASSOC );
4549
}
4650
}

core/Pass.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@
44

55
class Pass {
66

7+
protected $config;
8+
9+
/**
10+
* @param Config $config
11+
* @param helper\Database $db
12+
*/
13+
public function __construct( Config $config )
14+
{
15+
$this->config = $config;
16+
}
17+
718
/**
819
* Encrypt a plaintext password using the best available algorithm
920
* @todo: check php5.5 password api
@@ -12,7 +23,7 @@ class Pass {
1223
* @param string $desiredAlgo Name of desiresd algo
1324
* @return string $encPass Encrypted password
1425
*/
15-
public function encrypt( $plainPass, $desiredAlgo = 'md5' )
26+
public function encrypt( $plainPass, $desiredAlgo = NULL )
1627
{
1728
// If no password was supplied, return empty
1829
// FIXME: Either log this event, or throw an exception, so client code
@@ -21,6 +32,12 @@ public function encrypt( $plainPass, $desiredAlgo = 'md5' )
2132
return '';
2233
}
2334

35+
// If no hashing algo was specified
36+
if( $desiredAlgo == NULL ) {
37+
// Fetch default hashing algo from config file
38+
$desiredAlgo = $this->config->get( 'ENCRYPTION_ALGO' );
39+
}
40+
2441
if ( function_exists( 'hash' ) ) {
2542
if (
2643
! in_array(
@@ -37,8 +54,10 @@ public function encrypt( $plainPass, $desiredAlgo = 'md5' )
3754
} else {
3855
$algo = $desiredAlgo;
3956
}
57+
// Hash the password using desired algo
4058
$encPass = hash( $algo, $plainPass );
4159
} else {
60+
//. Hash the password using a fallback default
4261
$encPass = md5( $plainPass );
4362
}
4463
return $encPass;

core/phpunit.example.xml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<phpunit colors='true' bootstrap='./tests/phpunit-bootstrap.php'>
2+
<testsuites>
3+
<testsuite name='Primary'>
4+
<directory>./tests</directory>
5+
</testsuite>
6+
</testsuites>
7+
<php>
8+
<!-- Constants -->
9+
<const name='API_LOGIN_USERNAME' value='admin'/>
10+
<const name='API_LOGIN_PASSWORD' value='phplist'/>
11+
<!-- Absolute path to system's temporary directory -->
12+
<const name='TMP_PATH' value='/tmp'/>
13+
<!-- Name of the folder housing phpList -->
14+
<const name='PAGE_ROOT' value='/lists'/>
15+
<!-- Name of the admin directory -->
16+
<const name='ADMIN_PATH' value='/admin'/>
17+
18+
<!-- Variables -->
19+
<var name="DB_HOST" value="localhost" />
20+
<var name="DB_PORT" value="3306" />
21+
// !! Change these vars to fit your local config !!
22+
23+
// Path to phpList 4 configuration file
24+
<var name="phplist4-ini-config-file-path" value="/path/to/config.ini" />
25+
26+
// Database configuration
27+
<var name="DB_NAME" value="" />
28+
<var name="DB_USER" value="" />
29+
<var name="DB_PASSWD" value="" />
30+
31+
// Admin user credentials
32+
<var name="admin_username" value="admin" />
33+
<var name="admin_password" value="phplist" />
34+
35+
// Other configuration parameters
36+
<const name='API_URL_BASE_PATH' value='http://path-to-phplist.local'/>
37+
</php>
38+
</phpunit>

core/phpunit.xml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<php>
88
<!-- Constants -->
99
<const name='API_LOGIN_USERNAME' value='admin'/>
10-
<const name='API_LOGIN_PASSWORD' value='phplist'/>
10+
<const name='API_LOGIN_PASSWORD' value='admin'/>
1111
<!-- Absolute path to system's temporary directory -->
1212
<const name='TMP_PATH' value='/tmp'/>
1313
<!-- Name of the folder housing phpList -->
@@ -20,14 +20,17 @@
2020
<var name="DB_PORT" value="3306" />
2121
// !! Change these vars to fit your local config !!
2222

23+
// Path to phpList 4 configuration file
24+
<var name="phplist4-ini-config-file-path" value="/var/www/rapinew/plugins/restapi2/vendor/phplist/phplist4-core/config.ini" />
25+
2326
// Database configuration
2427
<var name="DB_NAME" value="pl" />
2528
<var name="DB_USER" value="pl" />
2629
<var name="DB_PASSWD" value="pl" />
2730

2831
// Admin user credentials
2932
<var name="admin_username" value="admin" />
30-
<var name="admin_password" value="phplist" />
33+
<var name="admin_password" value="admin" />
3134

3235
// Other configuration parameters
3336
<const name='API_URL_BASE_PATH' value='http://path-to-phplist.local'/>

core/services.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ services:
1111
EmailAddress:
1212
class: phpList\EmailAddress
1313
arguments: [@Config, %emailaddress.address%]
14+
EmailUtil:
15+
class: phpList\EmailUtil
1416
Config:
1517
class: phpList\Config
1618
arguments: [%config.configfile%]
@@ -22,6 +24,7 @@ services:
2224
arguments: [@Database, @Config]
2325
Pass:
2426
class: phpList\Pass
27+
arguments: [@Config]
2528
phpList:
2629
class: phpList\phpList
2730
arguments: [@Config, @Database, @Language, @Util]
@@ -70,6 +73,6 @@ services:
7073
# NOTE: the classname.parameter syntax is just a Symfony convention; parameter
7174
# names are handles as single simple strings
7275
parameters:
73-
config.configfile: configfile
76+
config.configfile: null
7477
emailaddress.address: emailaddress
7578
password.password: password

0 commit comments

Comments
 (0)