Skip to content

Commit de6a0de

Browse files
romanetarsmarcet
andauthored
feat: add more feedback to UI on inactive and/or unverified accounts (#78)
* feat: propagate can_login to UI and give more feedback Signed-off-by: romanetar <[email protected]> * feat: add more feedback to UI on inactive and/or unverified accounts Signed-off-by: romanetar <[email protected]> * fix: login flow ui tweaks Signed-off-by: romanetar <[email protected]> * fix: PR review feedback Signed-off-by: romanetar <[email protected]> * feat: add login attempt counter (#82) * feat: add login attempt counter Change-Id: Icd5ceb7f886ffa918449c872047ce4f279ee9c81 * fix: remove hard coded test value Change-Id: Ib765702819565ec82e1bc60361aee27aa492350b --------- Signed-off-by: romanetar <[email protected]> Co-authored-by: sebastian marcet <[email protected]>
1 parent b12f8b5 commit de6a0de

File tree

11 files changed

+313
-57
lines changed

11 files changed

+313
-57
lines changed

app/Http/Controllers/UserController.php

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
use App\ModelSerializers\SerializerRegistry;
2121
use Auth\Exceptions\AuthenticationException;
2222
use Auth\Exceptions\UnverifiedEmailMemberException;
23+
use App\Services\Auth\IUserService as AuthUserService;
2324
use Exception;
25+
use Illuminate\Http\Request as LaravelRequest;
2426
use Illuminate\Support\Facades\Request;
2527
use Illuminate\Support\Facades\Log;
2628
use Illuminate\Support\Facades\Redirect;
@@ -85,6 +87,10 @@ final class UserController extends OpenIdController
8587
* @var IUserService
8688
*/
8789
private $user_service;
90+
/**
91+
* @var AuthUserService
92+
*/
93+
private $auth_user_service;
8894
/**
8995
* @var IUserActionService
9096
*/
@@ -131,6 +137,7 @@ final class UserController extends OpenIdController
131137
* @param ITrustedSitesService $trusted_sites_service
132138
* @param DiscoveryController $discovery
133139
* @param IUserService $user_service
140+
* @param AuthUserService $auth_user_service
134141
* @param IUserActionService $user_action_service
135142
* @param IClientRepository $client_repository
136143
* @param IApiScopeRepository $scope_repository
@@ -149,6 +156,7 @@ public function __construct
149156
ITrustedSitesService $trusted_sites_service,
150157
DiscoveryController $discovery,
151158
IUserService $user_service,
159+
AuthUserService $auth_user_service,
152160
IUserActionService $user_action_service,
153161
IClientRepository $client_repository,
154162
IApiScopeRepository $scope_repository,
@@ -159,15 +167,14 @@ public function __construct
159167
LoginHintProcessStrategy $login_hint_process_strategy
160168
)
161169
{
162-
163-
164170
$this->openid_memento_service = $openid_memento_service;
165171
$this->oauth2_memento_service = $oauth2_memento_service;
166172
$this->auth_service = $auth_service;
167173
$this->server_configuration_service = $server_configuration_service;
168174
$this->trusted_sites_service = $trusted_sites_service;
169175
$this->discovery = $discovery;
170176
$this->user_service = $user_service;
177+
$this->auth_user_service = $auth_user_service;
171178
$this->user_action_service = $user_action_service;
172179
$this->client_repository = $client_repository;
173180
$this->scope_repository = $scope_repository;
@@ -257,14 +264,16 @@ public function getAccount()
257264

258265
$user = $this->auth_service->getUserByUsername($email);
259266

260-
if (is_null($user) || !$user->canLogin())
267+
if (is_null($user))
261268
throw new EntityNotFoundException();
262269

263270
return $this->ok(
264271
[
272+
'is_active' => $user->isActive(),
273+
'is_verified' => $user->isEmailVerified(),
265274
'pic' => $user->getPic(),
266275
'full_name' => $user->getFullName(),
267-
'has_password_set' => $user->hasPasswordSet()
276+
'has_password_set' => $user->hasPasswordSet(),
268277
]
269278
);
270279
} catch (ValidationException $ex) {
@@ -354,9 +363,41 @@ public function emitOTP()
354363
}
355364
}
356365

366+
/**
367+
* @return \Illuminate\Http\JsonResponse|mixed
368+
*/
369+
public function resendVerificationEmail(LaravelRequest $request)
370+
{
371+
try {
372+
$payload = $request->all();
373+
$validator = Validator::make($payload, [
374+
'email' => 'required|string|email|max:255'
375+
]);
376+
377+
if (!$validator->passes()) {
378+
return $this->error412($validator->getMessageBag()->getMessages());
379+
}
380+
$this->auth_user_service->resendVerificationEmail($payload);
381+
return $this->ok();
382+
}
383+
catch (ValidationException $ex) {
384+
Log::warning($ex);
385+
return $this->error412($ex->getMessages());
386+
}
387+
catch (EntityNotFoundException $ex) {
388+
Log::warning($ex);
389+
return $this->error404();
390+
}
391+
catch (Exception $ex) {
392+
Log::error($ex);
393+
return $this->error500($ex);
394+
}
395+
}
396+
357397
public function postLogin()
358398
{
359399
$max_login_attempts_2_show_captcha = $this->server_configuration_service->getConfigValue("MaxFailed.LoginAttempts.2ShowCaptcha");
400+
$max_login_failed_attempts = intval($this->server_configuration_service->getConfigValue("MaxFailed.Login.Attempts"));
360401
$login_attempts = 0;
361402
$username = '';
362403
$user = null;
@@ -443,13 +484,15 @@ public function postLogin()
443484
(
444485
[
445486
'max_login_attempts_2_show_captcha' => $max_login_attempts_2_show_captcha,
487+
'max_login_failed_attempts' => $max_login_failed_attempts,
446488
'login_attempts' => $login_attempts,
447489
'error_message' => $ex->getMessage(),
448490
'user_fullname' => !is_null($user) ? $user->getFullName() : "",
449491
'user_pic' => !is_null($user) ? $user->getPic(): "",
450492
'user_verified' => true,
451493
'username' => $username,
452-
'flow' => $flow
494+
'flow' => $flow,
495+
'user_is_active' => !is_null($user) ? ($user->isActive() ? 1 : 0) : 0
453496
]
454497
);
455498
}
@@ -459,6 +502,7 @@ public function postLogin()
459502
// validator errors
460503
$response_data = [
461504
'max_login_attempts_2_show_captcha' => $max_login_attempts_2_show_captcha,
505+
'max_login_failed_attempts' => $max_login_failed_attempts,
462506
'login_attempts' => $login_attempts,
463507
'validator' => $validator,
464508
];
@@ -470,7 +514,8 @@ public function postLogin()
470514
if(!is_null($user)){
471515
$response_data['user_fullname'] = $user->getFullName();
472516
$response_data['user_pic'] = $user->getPic();
473-
$response_data['user_verified'] = true;
517+
$response_data['user_verified'] = 1;
518+
$response_data['user_is_active'] = $user->isActive() ? 1 : 0;
474519
}
475520

476521
return $this->login_strategy->errorLogin
@@ -485,9 +530,10 @@ public function postLogin()
485530

486531
$response_data = [
487532
'max_login_attempts_2_show_captcha' => $max_login_attempts_2_show_captcha,
533+
'max_login_failed_attempts' => $max_login_failed_attempts,
488534
'login_attempts' => $login_attempts,
489535
'username' => $username,
490-
'error_message' => $ex1->getMessage()
536+
'error_message' => $ex1->getMessage(),
491537
];
492538

493539
if (is_null($user) && isset($data['username'])) {
@@ -497,7 +543,8 @@ public function postLogin()
497543
if(!is_null($user)){
498544
$response_data['user_fullname'] = $user->getFullName();
499545
$response_data['user_pic'] = $user->getPic();
500-
$response_data['user_verified'] = true;
546+
$response_data['user_verified'] = 1;
547+
$response_data['user_is_active'] = $user->isActive() ? 1 : 0;
501548
}
502549

503550
return $this->login_strategy->errorLogin

app/Services/Auth/UserService.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,14 +218,18 @@ public function registerUser(array $payload, ?OAuth2OTP $otp = null):User
218218
* @param string $token
219219
* @return User
220220
* @throws EntityNotFoundException
221-
* @throws ValidationException
221+
* @throws ValidationException|\Exception
222222
*/
223223
public function verifyEmail(string $token): User
224224
{
225225
return $this->tx_service->transaction(function () use ($token) {
226226
$user = $this->user_repository->getByVerificationEmailToken($token);
227-
if (is_null($user))
227+
228+
if (is_null($user) || !$user->isActive()) {
229+
Log::warning(sprintf("UserService::verifyEmail user with id %s is not active", $user->getId()));
228230
throw new EntityNotFoundException();
231+
}
232+
229233
$user->verifyEmail();
230234

231235
try {

app/Services/SecurityPolicies/LockUserCounterMeasure.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public function trigger(array $params = [])
7777
$user_id = $params["user_id"];
7878
$user = $this->repository->getById($user_id);
7979
$max_login_failed_attempts = intval($this->server_configuration->getConfigValue("MaxFailed.Login.Attempts"));
80-
if (!is_null($user) && $user instanceof User) {
80+
if ($user instanceof User) {
8181
//apply lock policy
8282
if (intval($user->getLoginFailedAttempt()) < $max_login_failed_attempts) {
8383
$this->user_service->updateFailedLoginAttempts($user->getId());

app/libs/Auth/Factories/UserFactory.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
* See the License for the specific language governing permissions and
1212
* limitations under the License.
1313
**/
14-
use Auth\Group;
1514
use Auth\User;
1615
use Illuminate\Support\Facades\Auth;
1716

app/libs/Auth/Models/User.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,6 +1854,8 @@ public function activate():void {
18541854
if(!$this->active) {
18551855
$this->active = true;
18561856
$this->spam_type = self::SpamTypeHam;
1857+
// reset it
1858+
$this->login_failed_attempt = 0;
18571859
Event::dispatch(new UserSpamStateUpdated(
18581860
$this->getId()
18591861
)
@@ -1886,6 +1888,7 @@ public function verifyEmail(bool $send_email_verified_notice = true)
18861888
$this->spam_type = self::SpamTypeHam;
18871889
$this->active = true;
18881890
$this->lock = false;
1891+
$this->login_failed_attempt = 0;
18891892
$this->email_verified_date = new \DateTime('now', new \DateTimeZone('UTC'));
18901893

18911894
if($send_email_verified_notice)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from 'react';
2+
import { Snackbar } from '@material-ui/core';
3+
import MuiAlert from '@material-ui/lab/Alert';
4+
5+
function Alert(props) {
6+
return <MuiAlert elevation={6} variant="filled" {...props} />;
7+
}
8+
9+
const CustomSnackbar = ({ message, severity = 'info', onClose }) => {
10+
return (
11+
<Snackbar
12+
open={message !== null}
13+
autoHideDuration={8000}
14+
onClose={onClose}
15+
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}>
16+
<Alert onClose={onClose} severity={severity}>
17+
{message}
18+
</Alert>
19+
</Snackbar>
20+
);
21+
};
22+
export default CustomSnackbar;

resources/js/login/actions.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,11 @@ export const emitOTP = (email, token, connection = 'email', send='code') => {
1919

2020
return postRawRequest(window.EMIT_OTP_ENDPOINT)(params, {'X-CSRF-TOKEN': token});
2121
}
22+
23+
export const resendVerificationEmail = (email, token) => {
24+
const params = {
25+
email: email
26+
};
27+
28+
return postRawRequest(window.RESEND_VERIFICATION_EMAIL_ENDPOINT)(params, {'X-CSRF-TOKEN': token});
29+
}

0 commit comments

Comments
 (0)