Skip to content

Commit 6b8711b

Browse files
committed
Merge branch 'develop' into phoenix
2 parents ff48ce3 + 0f151d0 commit 6b8711b

File tree

11 files changed

+385
-78
lines changed

11 files changed

+385
-78
lines changed

etc/config.sample.json

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,18 @@
4141
}
4242
},
4343
"email": {
44-
"recipient_from": "",
45-
"recipient_reply_to": "",
46-
"smtp_host": "smtp.example.com",
44+
"recipient_from": [
45+
"root@localhost",
46+
"BNETDocs"
47+
],
48+
"recipient_reply_to": [
49+
"root@localhost",
50+
"root"
51+
],
52+
"smtp_host": "localhost",
4753
"smtp_password": "",
48-
"smtp_port": 587,
49-
"smtp_tls": true,
54+
"smtp_port": 25,
55+
"smtp_tls": false,
5056
"smtp_user": ""
5157
},
5258
"memcache": {

src/controllers/User/Activate.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,16 @@ public function &run( Router &$router, View &$view, array &$args ) {
4141
}
4242

4343
if ( $model->user ) {
44-
$model->user->invalidateVerificationToken();
4544
$user_token = $model->user->getVerificationToken();
4645

4746
if ( $user_token === $model->token ) {
47+
$model->user->invalidateVerificationToken();
48+
4849
if (!$model->user->setVerified()) {
4950
$model->error = 'INTERNAL_ERROR';
5051
} else {
5152
$model->error = false;
53+
5254
Logger::logEvent(
5355
EventTypes::USER_VERIFIED,
5456
$model->user_id,

src/controllers/User/Register.php

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,25 @@ protected function tryRegister(Router &$router, UserRegisterModel &$model) {
172172

173173
}
174174

175-
if ($success) {
175+
if (!$success) {
176+
$model->error = 'INTERNAL_ERROR';
177+
} else {
178+
$model->error = false;
179+
180+
Logger::logEvent(
181+
EventTypes::USER_CREATED,
182+
$user_id,
183+
getenv("REMOTE_ADDR"),
184+
json_encode([
185+
"error" => $model->error,
186+
"requirements" => $req,
187+
"email" => $email,
188+
"username" => $username,
189+
"display_name" => null,
190+
"options_bitmask" => 0,
191+
])
192+
);
193+
176194
$state = new StdClass();
177195

178196
$mail = new PHPMailer( true ); // true enables exceptions
@@ -195,14 +213,20 @@ protected function tryRegister(Router &$router, UserRegisterModel &$model) {
195213
$mail->Port = $mail_config->smtp_port;
196214

197215
//Recipients
198-
if (!empty($mail_config->recipient_from)) {
199-
$mail->setFrom($mail_config->recipient_from, 'BNETDocs');
216+
if (isset($mail_config->recipient_from[0])) {
217+
$mail->setFrom(
218+
$mail_config->recipient_from[0],
219+
$mail_config->recipient_from[1]
220+
);
200221
}
201222

202223
$mail->addAddress($email, $username);
203224

204-
if (!empty($mail_config->recipient_reply_to)) {
205-
$mail->addReplyTo($mail_config->recipient_reply_to);
225+
if (isset($mail_config->recipient_reply_to[0])) {
226+
$mail->addReplyTo(
227+
$mail_config->recipient_reply_to[0],
228+
$mail_config->recipient_reply_to[1]
229+
);
206230
}
207231

208232
// Content
@@ -239,25 +263,6 @@ protected function tryRegister(Router &$router, UserRegisterModel &$model) {
239263
$model->error = "EMAIL_FAILURE";
240264
}
241265
}
242-
243-
if (!$success) {
244-
$model->error = "INTERNAL_ERROR";
245-
} else {
246-
$model->error = false;
247-
}
248-
Logger::logEvent(
249-
EventTypes::USER_CREATED,
250-
$user_id,
251-
getenv("REMOTE_ADDR"),
252-
json_encode([
253-
"error" => $model->error,
254-
"requirements" => $req,
255-
"email" => $email,
256-
"username" => $username,
257-
"display_name" => null,
258-
"options_bitmask" => 0,
259-
])
260-
);
261266
}
262267

263268
}

src/controllers/User/ResetPassword.php

Lines changed: 217 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,239 @@
22

33
namespace BNETDocs\Controllers\User;
44

5-
use \BNETDocs\Models\User\ResetPassword as UserResetPasswordModel;
65
use \CarlBennett\MVC\Libraries\Common;
76
use \CarlBennett\MVC\Libraries\Controller;
87
use \CarlBennett\MVC\Libraries\Router;
8+
use \CarlBennett\MVC\Libraries\Template;
99
use \CarlBennett\MVC\Libraries\View;
1010

11+
use \BNETDocs\Libraries\CSRF;
12+
use \BNETDocs\Libraries\EventTypes;
13+
use \BNETDocs\Libraries\Exceptions\UserNotFoundException;
14+
use \BNETDocs\Libraries\Logger;
15+
use \BNETDocs\Libraries\User;
16+
17+
use \BNETDocs\Models\User\ResetPassword as UserResetPasswordModel;
18+
19+
use \PHPMailer\PHPMailer\Exception;
20+
use \PHPMailer\PHPMailer\PHPMailer;
21+
22+
use \InvalidArgumentException;
23+
use \StdClass;
24+
1125
class ResetPassword extends Controller {
26+
const RET_FAILURE = 0;
27+
const RET_SUCCESS = 1;
28+
const RET_EMAIL = 2;
1229

13-
public function &run(Router &$router, View &$view, array &$args) {
30+
public function &run( Router &$router, View &$view, array &$args ) {
31+
32+
if ( $router->getRequestMethod() == 'GET' ) {
33+
$data = $router->getRequestQueryArray();
34+
} else {
35+
$data = $router->getRequestBodyArray();
36+
}
1437

1538
$model = new UserResetPasswordModel();
1639

17-
$view->render($model);
40+
$model->error = null;
41+
$model->csrf_id = mt_rand();
42+
$model->csrf_token = CSRF::generate( $model->csrf_id );
43+
$model->pw1 = isset( $data[ 'pw1' ]) ? $data[ 'pw1' ] : null;
44+
$model->pw2 = isset( $data[ 'pw2' ]) ? $data[ 'pw2' ] : null;
45+
$model->token = isset( $data[ 't' ]) ? $data[ 't' ] : null;
46+
$model->user = null;
47+
$model->username = isset( $data[ 'username' ]) ? $data[ 'username' ] : null;
48+
49+
if ( $router->getRequestMethod() == 'POST' ) {
50+
$ret = $this->doPasswordReset( $model, $data );
51+
if ( $ret !== self::RET_EMAIL ) {
52+
Logger::logEvent(
53+
EventTypes::USER_PASSWORD_RESET,
54+
( $model->user ? $model->user->getId() : null ),
55+
getenv( 'REMOTE_ADDR' ),
56+
json_encode([
57+
'error' => $model->error,
58+
'user' => ( $model->user ? true : false ),
59+
'username' => $model->username,
60+
])
61+
);
62+
}
63+
}
64+
65+
$view->render( $model );
1866

1967
$model->_responseCode = 200;
20-
$model->_responseHeaders["Content-Type"] = $view->getMimeType();
68+
$model->_responseHeaders[ 'Content-Type' ] = $view->getMimeType();
2169
$model->_responseTTL = 0;
2270

2371
return $model;
2472

2573
}
2674

75+
protected function doPasswordReset( UserResetPasswordModel &$model, &$data ) {
76+
$model->error = 'INTERNAL_ERROR';
77+
78+
$csrf_id = isset( $data[ 'csrf_id' ]) ? $data[ 'csrf_id' ] : null;
79+
$csrf_token = (
80+
isset( $data[ 'csrf_token' ]) ? $data[ 'csrf_token' ] : null
81+
);
82+
$csrf_valid = CSRF::validate( $csrf_id, $csrf_token );
83+
84+
if ( !$csrf_valid ) {
85+
$model->error = 'INVALID_CSRF';
86+
return self::RET_FAILURE;
87+
}
88+
CSRF::invalidate( $csrf_id );
89+
90+
if ( empty( $model->username )) {
91+
$model->error = 'EMPTY_USERNAME';
92+
return self::RET_FAILURE;
93+
}
94+
95+
try {
96+
$model->user = new User( User::findIdByUsername( $model->username ));
97+
} catch ( UserNotFoundException $e ) {
98+
$model->user = null;
99+
} catch ( InvalidArgumentException $e ) {
100+
$model->user = null;
101+
}
102+
103+
if ( !$model->user ) {
104+
$model->error = 'USER_NOT_FOUND';
105+
return self::RET_FAILURE;
106+
}
107+
108+
if ( empty( $model->token )) {
109+
$state = new StdClass();
110+
111+
$mail = new PHPMailer( true ); // true enables exceptions
112+
$mail_config = Common::$config->email;
113+
114+
$state->mail &= $mail;
115+
$state->token = $model->user->getVerificationToken();
116+
$state->user = $model->user;
117+
118+
try {
119+
//Server settings
120+
$mail->Timeout = 10; // default is 300 per RFC2821 $ 4.5.3.2
121+
$mail->SMTPDebug = 0;
122+
$mail->isSMTP();
123+
$mail->Host = $mail_config->smtp_host;
124+
$mail->SMTPAuth = !empty($mail_config->smtp_user);
125+
$mail->Username = $mail_config->smtp_user;
126+
$mail->Password = $mail_config->smtp_password;
127+
$mail->SMTPSecure = $mail_config->smtp_tls ? 'tls' : '';
128+
$mail->Port = $mail_config->smtp_port;
129+
130+
//Recipients
131+
if (isset($mail_config->recipient_from[0])) {
132+
$mail->setFrom(
133+
$mail_config->recipient_from[0],
134+
$mail_config->recipient_from[1]
135+
);
136+
}
137+
138+
$mail->addAddress($model->user->getEmail(), $model->user->getName());
139+
140+
if (isset($mail_config->recipient_reply_to[0])) {
141+
$mail->addReplyTo(
142+
$mail_config->recipient_reply_to[0],
143+
$mail_config->recipient_reply_to[1]
144+
);
145+
}
146+
147+
// Content
148+
$mail->isHTML(true);
149+
$mail->Subject = 'Reset Password';
150+
$mail->CharSet = PHPMailer::CHARSET_UTF8;
151+
152+
ob_start();
153+
(new Template($state, 'Email/User/ResetPassword.rich'))->render();
154+
$mail->Body = ob_get_clean();
155+
156+
ob_start();
157+
(new Template($state, 'Email/User/ResetPassword.plain'))->render();
158+
$mail->AltBody = ob_get_clean();
159+
160+
$mail->send();
161+
162+
$model->error = false;
163+
164+
Logger::logEvent(
165+
EventTypes::EMAIL_SENT,
166+
$model->user->getId(),
167+
getenv( 'REMOTE_ADDR' ),
168+
json_encode([
169+
'from' => $mail->From,
170+
'to' => $mail->getToAddresses(),
171+
'reply_to' => $mail->getReplyToAddresses(),
172+
'subject' => $mail->Subject,
173+
'content_type' => $mail->ContentType,
174+
'body' => $mail->Body,
175+
'alt_body' => $mail->AltBody,
176+
])
177+
);
178+
179+
} catch (\Exception $e) {
180+
$model->error = 'EMAIL_FAILURE';
181+
}
182+
183+
return self::RET_EMAIL;
184+
}
185+
186+
if ( $model->token !== $model->user->getVerificationToken() ) {
187+
$model->error = 'INVALID_TOKEN';
188+
return self::RET_FAILURE;
189+
}
190+
191+
if ( $model->pw1 !== $model->pw2 ) {
192+
$model->error = 'PASSWORD_MISMATCH';
193+
return self::RET_FAILURE;
194+
}
195+
196+
$req = Common::$config->bnetdocs->user_register_requirements;
197+
$pwlen = strlen( $model->pw1 );
198+
199+
if ( is_numeric( $req->password_length_max )
200+
&& $pwlen > $req->password_length_max ) {
201+
$model->error = 'PASSWORD_TOO_LONG';
202+
return self::RET_FAILURE;
203+
}
204+
205+
if ( is_numeric( $req->password_length_min )
206+
&& $pwlen < $req->password_length_min ) {
207+
$model->error = 'PASSWORD_TOO_SHORT';
208+
return self::RET_FAILURE;
209+
}
210+
211+
if ( !$req->password_allow_email
212+
&& stripos( $model->pw1, $model->user->getEmail() )) {
213+
$model->error = 'PASSWORD_CONTAINS_EMAIL';
214+
return self::RET_FAILURE;
215+
}
216+
217+
if ( !$req->password_allow_username
218+
&& stripos( $model->pw1, $model->user->getUsername() )) {
219+
$model->error = 'PASSWORD_CONTAINS_USERNAME';
220+
return self::RET_FAILURE;
221+
}
222+
223+
// --
224+
$model->user->invalidateVerificationToken();
225+
// --
226+
227+
if ( $model->user->getAcl( User::OPTION_DISABLED )) {
228+
$model->error = 'USER_DISABLED';
229+
return self::RET_FAILURE;
230+
}
231+
232+
if (!$model->user->changePassword( $model->pw1 )) {
233+
$model->error = 'INTERNAL_ERROR';
234+
return self::RET_FAILURE;
235+
}
236+
237+
$model->error = false;
238+
return self::RET_SUCCESS;
239+
}
27240
}

src/models/User/ResetPassword.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ class ResetPassword extends Model {
88

99
public $csrf_id;
1010
public $csrf_token;
11-
public $email;
1211
public $error;
12+
public $token;
13+
public $user;
14+
public $username;
1315

1416
}

src/templates/Email/User/Register.plain.phtml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
namespace BNETDocs\Templates\Email\User;
33
use \CarlBennett\MVC\Libraries\Common;
4-
require("./Email/header.plain.inc.phtml");
4+
require('./Email/header.plain.inc.phtml');
55
?>
66
Welcome to BNETDocs!
77

@@ -13,4 +13,4 @@ or copy and paste the link below into your browser to activate your account.
1313

1414
Note: This link will only be available for 24 hours after registering. If you
1515
wait until it expires, you will need to complete a password reset.
16-
<?php require("./Email/footer.plain.inc.phtml"); ?>
16+
<?php require('./Email/footer.plain.inc.phtml'); ?>

0 commit comments

Comments
 (0)