Completed
Push — master ( 6eda29...c67cfb )
by Rain
08:12 queued 02:08
created

FroxlorChangePasswordDriver::generatePassword()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 12
nc 1
nop 1
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
class FroxlorChangePasswordDriver implements \RainLoop\Providers\ChangePassword\ChangePasswordInterface
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4
{
5
	/**
6
	 * @var string
7
	 */
8
	private $sDsn = '';
9
10
	/**
11
	 * @var string
12
	 */
13
	private $sUser = '';
14
15
	/**
16
	 * @var string
17
	 */
18
	private $sPassword = '';
19
20
	/**
21
	 * @var string
22
	 */
23
	private $sAllowedEmails = '';
24
25
	/**
26
	 * @var \MailSo\Log\Logger
27
	 */
28
	private $oLogger = null;
29
30
	/**
31
	 * @param string $sDsn
32
	 * @param string $sUser
33
	 * @param string $sPassword
34
	 *
35
	 * @return \FroxlorChangePasswordDriver
36
	 */
37
	public function SetConfig($sDsn, $sUser, $sPassword)
38
	{
39
		$this->sDsn = $sDsn;
40
		$this->sUser = $sUser;
41
		$this->sPassword = $sPassword;
42
43
		return $this;
44
	}
45
46
	/**
47
	 * @param string $sAllowedEmails
48
	 *
49
	 * @return \FroxlorChangePasswordDriver
50
	 */
51
	public function SetAllowedEmails($sAllowedEmails)
52
	{
53
		$this->sAllowedEmails = $sAllowedEmails;
54
		return $this;
55
	}
56
57
	/**
58
	 * @param \MailSo\Log\Logger $oLogger
59
	 *
60
	 * @return \FroxlorChangePasswordDriver
61
	 */
62
	public function SetLogger($oLogger)
63
	{
64
		if ($oLogger instanceof \MailSo\Log\Logger)
0 ignored issues
show
Bug introduced by
The class MailSo\Log\Logger does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
65
		{
66
			$this->oLogger = $oLogger;
67
		}
68
69
		return $this;
70
	}
71
72
	/**
73
	 * @param \RainLoop\Account $oAccount
74
	 *
75
	 * @return bool
76
	 */
77
	public function PasswordChangePossibility($oAccount)
78
	{
79
		return $oAccount && $oAccount->Email() &&
80
			\RainLoop\Plugins\Helper::ValidateWildcardValues($oAccount->Email(), $this->sAllowedEmails);
81
	}
82
83
	/**
84
	 * @param \RainLoop\Account $oAccount
85
	 * @param string $sPrevPassword
86
	 * @param string $sNewPassword
87
	 *
88
	 * @return bool
89
	 */
90
	public function ChangePassword(\RainLoop\Account $oAccount, $sPrevPassword, $sNewPassword)
91
	{
92
		if ($this->oLogger)	{
93
			$this->oLogger->Write('Froxlor: Try to change password for '.$oAccount->Email());
94
		}
95
96
		$bResult = false;
97
		if (!empty($this->sDsn) && 0 < \strlen($this->sUser) && 0 < \strlen($this->sPassword) && $oAccount)
98
		{
99
			try
100
			{
101
				$oPdo = new \PDO($this->sDsn, $this->sUser, $this->sPassword);
102
				$oPdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
103
104
				$oStmt = $oPdo->prepare('SELECT password_enc, id FROM mail_users WHERE username = ? LIMIT 1');
105
				if ($oStmt->execute(array($oAccount->IncLogin())))
106
				{
107
					$aFetchResult = $oStmt->fetchAll(\PDO::FETCH_ASSOC);
108
					if (\is_array($aFetchResult) && isset($aFetchResult[0]['password_enc'], $aFetchResult[0]['id']))
109
					{
110
						$sDbPassword = \stripslashes($aFetchResult[0]['password_enc']);
111
112
						if ( $this->validatePasswordLogin( $sDbPassword, $sPrevPassword ) ) {
113
							$sEncNewPassword = $this->cryptPassword($sNewPassword, 3);
114
							$oStmt = $oPdo->prepare('UPDATE mail_users SET password_enc = ?,password = ? WHERE id = ?');
115
							$bResult = (bool) $oStmt->execute(
116
								array($sEncNewPassword, $sNewPassword, $aFetchResult[0]['id']));
117
						}
118
					}
119
				}
120
			}
121
			catch (\Exception $oException)
122
			{
123
				if ($this->oLogger)
124
				{
125
					$this->oLogger->WriteException($oException);
126
				}
127
			}
128
		}
129
130
		return $bResult;
131
	}
132
133
	/**
134
	 * @param string $sPassword
135
	 * @return string
136
	 */
137
	private function cryptPassword($sPassword, $type = 3)
138
	{
139
		return $this->makeCryptPassword($sPassword,$type);
140
	}
141
142
	/**
143
	 * This file is part of the Froxlor project.
144
	 * Copyright (c) 2010 the Froxlor Team (see authors).
145
	 *
146
	 * For the full copyright and license information, please view the COPYING
147
	 * file that was distributed with this source code. You can also view the
148
	 * COPYING file online at http://files.froxlor.org/misc/COPYING.txt
149
	 *
150
	 * @copyright  (c) the authors
151
	 * @author     Michal Wojcik <[email protected]>
152
	 * @author     Michael Kaufmann <[email protected]>
153
	 * @author     Froxlor team <[email protected]> (2010-)
154
	 * @license    GPLv2 http://files.froxlor.org/misc/COPYING.txt
155
	 * @package    Functions
156
	 *
157
	 */
158
159
	/**
160
	 * Make crypted password from clear text password
161
	 *
162
	 * @author Michal Wojcik <[email protected]>
163
	 * @author Michael Kaufmann <[email protected]>
164
	 * @author Froxlor team <[email protected]> (2010-)
165
	 *
166
	 * 0 - default crypt (depenend on system configuration)
167
	 * 1 - MD5 $1$
168
	 * 2 - BLOWFISH $2a$ | $2y$07$ (on php 5.3.7+)
169
	 * 3 - SHA-256 $5$ (default)
170
	 * 4 - SHA-512 $6$
171
	 *
172
	 * @param string $password Password to be crypted
173
	 *
174
	 * @return string encrypted password
175
	 */
176
	private function makeCryptPassword ($password,$type = 3) {
177
		switch ($type) {
178
			case 0:
179
				$cryptPassword = \crypt($password);
180
				break;
181 View Code Duplication
			case 1:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
182
				$cryptPassword = \crypt($password, '$1$' . $this->generatePassword(true).  $this->generatePassword(true));
183
				break;
184
			case 2:
185
				if (\version_compare(\phpversion(), '5.3.7', '<')) {
186
					$cryptPassword = \crypt($password, '$2a$' . $this->generatePassword(true).  $this->generatePassword(true));
187
				} else {
188
					// Blowfish hashing with a salt as follows: "$2a$", "$2x$" or "$2y$",
189
					// a two digit cost parameter, "$", and 22 characters from the alphabet "./0-9A-Za-z"
190
					$cryptPassword = \crypt(
191
						$password,
192
						'$2y$07$' . \substr($this->generatePassword(true).$this->generatePassword(true).$this->generatePassword(true), 0, 22)
193
					);
194
				}
195
				break;
196 View Code Duplication
			case 3:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197
				$cryptPassword = \crypt($password, '$5$' . $this->generatePassword(true).  $this->generatePassword(true));
198
				break;
199 View Code Duplication
			case 4:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
200
				$cryptPassword = \crypt($password, '$6$' . $this->generatePassword(true).  $this->generatePassword(true));
201
				break;
202
			default:
203
				$cryptPassword = \crypt($password);
204
				break;
205
		}
206
207
		return $cryptPassword;
208
	}
209
210
	/**
211
	 * Generates a random password
212
	 *
213
	 * @param boolean $isSalt
214
	 *            optional, create a hash for a salt used in makeCryptPassword because crypt() does not like some special characters in its salts, default is false
215
	 */
216
	private function generatePassword($isSalt = false)
217
	{
218
	    $alpha_lower = 'abcdefghijklmnopqrstuvwxyz';
219
	    $alpha_upper = \strtoupper($alpha_lower);
220
	    $numeric = '0123456789';
221
	    $special = '!?<>§$%&+#=';
0 ignored issues
show
Unused Code introduced by
$special is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
222
	    $length = 10;
223
224
	    $pw = $this->special_shuffle($alpha_lower);
225
	    $n = \floor(($length) / 4);
226
	    $pw .= \mb_substr($this->special_shuffle($alpha_upper), 0, $n);
227
	    $pw .= \mb_substr($this->special_shuffle($numeric), 0, $n);
228
	    $pw = \mb_substr($pw, - $length);
229
	    return $this->special_shuffle($pw);
230
	}
231
232
	/**
233
	 * multibyte-character safe shuffle function
234
	 *
235
	 * @param string $str
236
	 *
237
	 * @return string
238
	 */
239
	private function special_shuffle($str = null)
240
	{
241
	    $len = \mb_strlen($str);
242
	    $sploded = array();
243
	    while ($len -- > 0) {
244
		$sploded[] = \mb_substr($str, $len, 1);
245
	    }
246
	    \shuffle($sploded);
247
	    return \join('', $sploded);
248
	}
249
250
	/**
251
	 * Function validatePasswordLogin
252
	 *
253
	 * compare user password-hash with given user-password
254
	 * and check if they are the same
255
	 * additionally it updates the hash if the system settings changed
256
	 * or if the very old md5() sum is used
257
	 *
258
	 * @param array $userinfo user-data from table
0 ignored issues
show
Bug introduced by
There is no parameter named $userinfo. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
259
	 * @param string $password the password to validate
260
	 * @param string $table either panel_customers or panel_admins
0 ignored issues
show
Bug introduced by
There is no parameter named $table. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
261
	 * @param string $uid user-id-field in $table
0 ignored issues
show
Bug introduced by
There is no parameter named $uid. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
262
	 *
263
	 * @return boolean
264
	 */
265
	private function validatePasswordLogin($pwd_hash, $password = null) {
266
267
	        $systype = 3; // SHA256
268
	        $update_hash = false;
0 ignored issues
show
Unused Code introduced by
$update_hash is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
269
	        // check for good'ole md5
270
	        if (\strlen($pwd_hash) == 32 && \ctype_xdigit($pwd_hash)) {
271
	                $pwd_check = \md5($password);
272
	                $update_hash = true;
0 ignored issues
show
Unused Code introduced by
$update_hash is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
273
	        } else {
274
	                // cut out the salt from the hash
275
	                $pwd_salt = \str_replace(\substr(\strrchr($pwd_hash, "$"), 1), "", $pwd_hash);
276
	                // create same hash to compare
277
	                $pwd_check = \crypt($password, $pwd_salt);
278
	                // check whether the hash needs to be updated
279
	                $hash_type_chk = \substr($pwd_hash, 0, 3);
280
	                if (($systype == 1 && $hash_type_chk != '$1$') || // MD5
281
	                        ($systype == 2 && $hash_type_chk != '$2$') || // BLOWFISH
282
	                        ($systype == 3 && $hash_type_chk != '$5$') || // SHA256
283
	                        ($systype == 4 && $hash_type_chk != '$6$')    // SHA512
284
	                ) {
285
	                        $update_hash = true;
0 ignored issues
show
Unused Code introduced by
$update_hash is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
286
	                }
287
	        }
288
289
	        return $pwd_check;
290
	}
291
292
293
}
294