Skip to content

Commit cf3e95b

Browse files
committed
Implement user account activation
1 parent 3c34650 commit cf3e95b

File tree

7 files changed

+117
-10
lines changed

7 files changed

+117
-10
lines changed

src/controllers/User/Activate.php

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
namespace BNETDocs\Controllers\User;
44

55
use \BNETDocs\Models\User\Activate as UserActivateModel;
6+
7+
use \BNETDocs\Libraries\EventTypes;
8+
use \BNETDocs\Libraries\Exceptions\UserNotFoundException;
9+
use \BNETDocs\Libraries\Logger;
10+
use \BNETDocs\Libraries\User;
11+
612
use \CarlBennett\MVC\Libraries\Common;
713
use \CarlBennett\MVC\Libraries\Controller;
814
use \CarlBennett\MVC\Libraries\Router;
@@ -12,12 +18,42 @@ class Activate extends Controller {
1218

1319
public function &run( Router &$router, View &$view, array &$args ) {
1420

21+
$data = $router->getRequestQueryArray();
22+
1523
$model = new UserActivateModel();
1624

17-
$data = $router->getRequestQueryArray();
25+
$model->error = 'INVALID_TOKEN';
26+
$model->token = isset( $data[ 't' ]) ? $data[ 't' ] : null;
27+
$model->user_id = isset( $data[ 'u' ]) ? $data[ 'u' ] : null;
28+
29+
if ( !is_null( $model->user_id )) {
30+
$model->user_id = (int) $model->user_id;
31+
}
32+
33+
try {
34+
$model->user = new User( $model->user_id );
35+
} catch ( UserNotFoundException $ex ) {
36+
$model->user = null;
37+
}
38+
39+
if ( $model->user ) {
40+
$model->user->invalidateVerificationToken();
41+
$user_token = $model->user->getVerificationToken();
1842

19-
$model->token = isset( $data[ 't' ] ) ? $data[ 't' ] : null;
20-
$model->error = 'INVALID_TOKEN';
43+
if ( $user_token === $model->token ) {
44+
if (!$model->user->setVerified()) {
45+
$model->error = 'INTERNAL_ERROR';
46+
} else {
47+
$model->error = false;
48+
Logger::logEvent(
49+
EventTypes::USER_VERIFIED,
50+
$model->user_id,
51+
getenv( 'REMOTE_ADDR' ),
52+
json_encode([ 'error' => $model->error ])
53+
);
54+
}
55+
}
56+
}
2157

2258
$view->render( $model );
2359

src/controllers/User/Register.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ protected function tryRegister(Router &$router, UserRegisterModel &$model) {
180180

181181
$state->mail &= $mail;
182182
$state->token = ( $user ? $user->getVerificationToken() : null );
183+
$state->user_id = $user_id;
183184

184185
try {
185186
//Server settings

src/libraries/User.php

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ public static function create(
279279
public static function createPassword($password, &$hash, &$salt) {
280280
$pepper = Common::$config->bnetdocs->user_password_pepper;
281281

282-
$gmp = gmp_init(microtime(true)*10000);
282+
$gmp = gmp_init(time());
283283
$gmp = gmp_mul($gmp, mt_rand());
284284
$gmp = gmp_mul($gmp, gmp_random_bits(64));
285285
$salt = strtoupper(gmp_strval($gmp, 36));
@@ -506,7 +506,7 @@ public function getVerificationToken() {
506506
$value = Common::$cache->get($key);
507507

508508
if ($value === false) {
509-
$gmp = gmp_init(microtime(true)*10000);
509+
$gmp = gmp_init(time());
510510
$gmp = gmp_mul($gmp, mt_rand());
511511
$gmp = gmp_mul($gmp, gmp_random_bits(64));
512512

@@ -684,4 +684,58 @@ public function setAcl($acl, $value) {
684684
}
685685
}
686686

687+
public function setVerified() {
688+
$this->invalidateVerificationToken();
689+
690+
$tz = new DateTimeZone( 'Etc/UTC' );
691+
$dt = new DateTime($this->created_datetime);
692+
$dt->setTimezone($tz);
693+
694+
$verified_datetime = $dt;
695+
$options_bitmask = $this->options_bitmask | self::OPTION_VERIFIED;
696+
697+
if ( !isset( Common::$database )) {
698+
Common::$database = DatabaseDriver::getDatabaseObject();
699+
}
700+
701+
$successful = false;
702+
703+
try {
704+
705+
$stmt = Common::$database->prepare('
706+
UPDATE `users` SET
707+
`options_bitmask` = :bits,
708+
`verified_datetime` = :dt
709+
WHERE `id` = :user_id;
710+
');
711+
$stmt->bindParam(
712+
':dt', $verified_datetime->format( 'Y-m-d H:i:s' ), PDO::PARAM_STR
713+
);
714+
$stmt->bindParam(':bits', $options_bitmask, PDO::PARAM_INT);
715+
$stmt->bindParam(':user_id', $this->id, PDO::PARAM_INT);
716+
$successful = $stmt->execute();
717+
$stmt->closeCursor();
718+
if ($successful) {
719+
$this->verified_datetime = $verified_datetime;
720+
$this->options_bitmask = $options_bitmask;
721+
$key = 'bnetdocs-user-' . $this->id;
722+
$obj = Common::$cache->get($key);
723+
if ($obj !== false) {
724+
$obj = unserialize($obj);
725+
$obj->verified_datetime = $this->verified_datetime;
726+
$obj->options_bitmask = $this->options_bitmask;
727+
$obj = serialize($obj);
728+
Common::$cache->set($key, $obj, 300);
729+
}
730+
}
731+
732+
} catch (PDOException $e) {
733+
734+
throw new QueryException('Cannot set user as verified', $e);
735+
736+
} finally {
737+
return $successful;
738+
}
739+
}
740+
687741
}

src/models/User/Activate.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
class Activate extends Model {
88

9-
public $token;
109
public $error;
10+
public $token;
11+
public $user;
12+
public $user_id;
1113

1214
}

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
<?php require("./Email/header.plain.inc.phtml"); ?>
1+
<?php
2+
namespace BNETDocs\Templates\Email\User;
3+
use \CarlBennett\MVC\Libraries\Common;
4+
require("./Email/header.plain.inc.phtml");
5+
?>
26
Welcome to BNETDocs!
37

48
Your account requires activation before being able to use this service. Click
59
or copy and paste the link below into your browser to activate your account.
610

7-
https://bnetdocs.org/user/activate?t=<?php echo rawurlencode($this->getContext()->token); ?>
11+
<?php echo Common::relativeUrlToAbsolute('/user/activate?u=' . rawurlencode($this->getContext()->user_id) . '&t=' . rawurlencode($this->getContext()->token)); ?>
12+
813

914
Note: This link will only be available for 24 hours after registering. If you
1015
wait until it expires, you will need to complete a password reset.
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
<?php require("./Email/header.rich.inc.phtml"); ?>
1+
<?php
2+
namespace BNETDocs\Templates\Email\User;
3+
use \CarlBennett\MVC\Libraries\Common;
4+
require("./Email/header.rich.inc.phtml");
5+
$url = Common::relativeUrlToAbsolute('/user/activate?u=' . rawurlencode($this->getContext()->user_id) . '&t=' . rawurlencode($this->getContext()->token));
6+
?>
27
<p style="font-family:sans-serif;font-weight:bold;">Welcome to BNETDocs!</p>
38
<p style="font-family:sans-serif;">Your account requires activation before being able to use this service. Click or copy and paste the link below into your browser to activate your account.</p>
4-
<p style="font-family:sans-serif;"><a href="https://bnetdocs.org/user/activate?t=<?php echo rawurlencode($this->getContext()->token); ?>" rel="external">https://bnetdocs.org/user/activate?t=<?php echo rawurlencode($this->getContext()->token); ?></a></p>
9+
<p style="font-family:sans-serif;"><a href="<?=$url?>" rel="external"><?=filter_var($url, FILTER_SANITIZE_FULL_SPECIAL_CHARS)?></a></p>
510
<p style="font-family:sans-serif;"><strong>Note:</strong> This link will only be available for 24 hours after registering. If you wait until it expires, you will need to complete a password reset.</p>
611
<?php require("./Email/footer.rich.inc.phtml"); ?>

src/templates/User/Activate.phtml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ switch ( $this->getContext()->error ) {
1313
case 'INVALID_TOKEN':
1414
$message = 'The token is expired or invalid and therefore cannot be used.';
1515
break;
16+
case 'INTERNAL_ERROR':
17+
$message = 'An internal error occurred while processing your request. '
18+
. 'Our staff has been notified of the issue. Try again later.';
19+
break;
1620
default:
1721
$message = $this->getContext()->error;
1822
}

0 commit comments

Comments
 (0)