Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Banning Users #650

Merged
merged 34 commits into from
Mar 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a2db120
Added migration file
davidnsai Feb 22, 2023
0293227
Added new feilds to the UserModel
davidnsai Feb 22, 2023
9886eb4
Added methods to modify the ban status of a user
davidnsai Feb 22, 2023
cae9014
Added exception for banned users
davidnsai Feb 22, 2023
1af7fd3
Added banned user checks on access tokens
davidnsai Feb 22, 2023
2bb6a4f
Added banned user checks on attempting to log in
davidnsai Feb 22, 2023
d40e090
Added language translations for banned user
davidnsai Feb 22, 2023
ddfed19
Fixed coding standard fail
davidnsai Feb 22, 2023
00654ed
cs fix
davidnsai Feb 22, 2023
b8625de
Added user model to list of imported classes
davidnsai Feb 22, 2023
c9963dd
Removed extra whitespace
davidnsai Feb 22, 2023
d6c2425
ran composer cs-fix
davidnsai Feb 22, 2023
5cddb94
Added a bannable trait
davidnsai Feb 22, 2023
28206d1
Completed banning logic
davidnsai Feb 22, 2023
1bc78de
Added docs for banning feature
davidnsai Feb 22, 2023
0e27d3b
Fixed failing unit test
davidnsai Feb 22, 2023
7088052
Update docs/banning_users.md
davidnsai Feb 23, 2023
ad2984f
Update src/Filters/SessionAuth.php
davidnsai Feb 23, 2023
70ab02f
Update src/Language/fa/Auth.php
davidnsai Feb 23, 2023
f256b69
Update src/Language/ja/Auth.php
davidnsai Feb 24, 2023
a186793
Merge branch 'codeigniter4:develop' into develop
davidnsai Feb 24, 2023
8591f67
Merge branch 'codeigniter4:develop' into develop
davidnsai Mar 8, 2023
5fdc155
Removed migration adding banned and banned_message fields
davidnsai Mar 8, 2023
040f17a
Removed banned fields from user model
davidnsai Mar 8, 2023
9d54ac5
Implemented ban and unban logic
davidnsai Mar 8, 2023
6371431
FIxed docs on banning users
davidnsai Mar 8, 2023
df48197
removed unneccessary (bool)
davidnsai Mar 8, 2023
206fe9a
Reverted redirect route for when a user is not activated
davidnsai Mar 8, 2023
c00b84e
Added test for getBanMessage()
davidnsai Mar 9, 2023
ed74635
Removed unnecesary whitespaces from the docs
davidnsai Mar 9, 2023
88bb918
Update docs/banning_users.md
davidnsai Mar 10, 2023
524a6c9
Update docs/banning_users.md
davidnsai Mar 10, 2023
fb1e851
Update docs/banning_users.md
davidnsai Mar 10, 2023
7134871
Update docs/banning_users.md
davidnsai Mar 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions docs/banning_users.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Banning Users

Shield provides a way to ban users from your application. This is useful if you need to prevent a user from logging in, or logging them out in the event that they breach your terms of service.

- [Checking if the User is Banned](#check-if-a-user-is-banned)
- [Banning a User](#banning-a-user)
- [Unbanning a User](#unbanning-a-user)
- [Getting the Reason for Ban ](#getting-the-reason-for-ban)

### Check if a User is Banned

You can check if a user is banned using `isBanned()` method on the `User` entity. The method returns a boolean `true`/`false`.

```php
if ($user->isBanned()) {
//...
}
```

davidnsai marked this conversation as resolved.
Show resolved Hide resolved
### Banning a User

To ban a user from the application, the `ban(?string $message = null)` method can be called on the `User` entity. The method takes an optional string as a parameter. The string acts as the reason for the ban.

```php
// banning a user without passing a message
$user->ban();
// banning a user with a message and reason for the ban passed.
$user->ban('Your reason for banning the user here');
```

### Unbanning a User

Unbanning a user can be done using the `unBan()` method on the `User` entity. This method will also reset the `status_message` property.

```php
$user->unBan();
```

### Getting the Reason for Ban

The reason for the ban can be obtained user the `getBanMessage()` method on the `User` entity.

```php
$user->getBanMessage();
```
5 changes: 5 additions & 0 deletions src/Authentication/AuthenticationException.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public static function forInvalidUser(): self
return new self(lang('Auth.invalidUser'));
}

public static function forBannedUser(): self
{
return new self(lang('Auth.invalidUser'));
}

public static function forNoEntityProvided(): self
{
return new self(lang('Auth.noUserEntity'), 500);
Expand Down
9 changes: 9 additions & 0 deletions src/Authentication/Authenticators/AccessTokens.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ public function attempt(array $credentials): Result

$user = $result->extraInfo();

if ($user->isBanned()) {
$this->user = null;

return new Result([
'success' => false,
'reason' => $user->getBanMessage() ?? lang('Auth.bannedUser'),
]);
}

$user = $user->setAccessToken(
$user->getAccessToken($this->getBearerToken())
);
Expand Down
9 changes: 9 additions & 0 deletions src/Authentication/Authenticators/Session.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ public function attempt(array $credentials): Result
/** @var User $user */
$user = $result->extraInfo();

if ($user->isBanned()) {
$this->user = null;

return new Result([
'success' => false,
'reason' => $user->getBanMessage() ?? lang('Auth.bannedUser'),
]);
}

$this->user = $user;

// Update the user's last used date on their password identity.
Expand Down
2 changes: 2 additions & 0 deletions src/Entities/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use CodeIgniter\Shield\Models\LoginModel;
use CodeIgniter\Shield\Models\UserIdentityModel;
use CodeIgniter\Shield\Traits\Activatable;
use CodeIgniter\Shield\Traits\Bannable;
use CodeIgniter\Shield\Traits\Resettable;

/**
Expand All @@ -29,6 +30,7 @@ class User extends Entity
use HasAccessTokens;
use Resettable;
use Activatable;
use Bannable;

/**
* @var UserIdentity[]|null
Expand Down
9 changes: 9 additions & 0 deletions src/Filters/SessionAuth.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ public function before(RequestInterface $request, $arguments = null)

// Block inactive users when Email Activation is enabled
$user = $authenticator->getUser();

if ($user->isBanned()) {
$error = $user->getBanMessage() ?? lang('Auth.logOutBannedUser');
$authenticator->logout();

return redirect()->to(config('Auth')->logoutRedirect())
->with('error', $error);
}

if ($user !== null && ! $user->isActivated()) {
$authenticator->logout();

Expand Down
2 changes: 2 additions & 0 deletions src/Language/de/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
'unknownAuthenticator' => '{0} ist kein gültiger Authentifikator.',
'unknownUserProvider' => 'Der zu verwendende User Provider konnte nicht ermittelt werden.',
'invalidUser' => 'Der angegebene Benutzer kann nicht gefunden werden.',
'bannedUser' => '(To be translated) Can not log you in as you are currently banned.',
'logOutBannedUser' => '(To be translated) You have been logged out because you have been banned.',
'badAttempt' => 'Sie konnten nicht angemeldet werden. Bitte überprüfen Sie Ihre Anmeldedaten.',
'noPassword' => 'Kann einen Benutzer ohne Passwort nicht validieren.',
'invalidPassword' => 'Sie können nicht angemeldet werden. Bitte überprüfen Sie Ihr Passwort.',
Expand Down
2 changes: 2 additions & 0 deletions src/Language/en/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
'unknownAuthenticator' => '{0} is not a valid authenticator.',
'unknownUserProvider' => 'Unable to determine the User Provider to use.',
'invalidUser' => 'Unable to locate the specified user.',
'bannedUser' => 'Can not log you in as you are currently banned.',
'logOutBannedUser' => 'You have been logged out because you have been banned.',
'badAttempt' => 'Unable to log you in. Please check your credentials.',
'noPassword' => 'Cannot validate a user without a password.',
'invalidPassword' => 'Unable to log you in. Please check your password.',
Expand Down
2 changes: 2 additions & 0 deletions src/Language/es/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
'unknownAuthenticator' => '{0} no es un handler válido.',
'unknownUserProvider' => 'No podemos determinar que Proveedor de Usuarios usar.',
'invalidUser' => 'No podemos localizar este usuario.',
'bannedUser' => '(To be translated) Can not log you in as you are currently banned.',
'logOutBannedUser' => '(To be translated) You have been logged out because you have been banned.',
'badAttempt' => 'No puedes entrar. Por favor, comprueba tus creenciales.',
'noPassword' => 'No se puede validar un usuario sin una contraseña.',
'invalidPassword' => 'No uedes entrar. Por favor, comprueba tu contraseña.',
Expand Down
2 changes: 2 additions & 0 deletions src/Language/fa/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
'unknownAuthenticator' => '{0} احراز هویت معتبری نمی باشد.',
'unknownUserProvider' => 'قادر به تعیین ارائه دهنده کاربر برای استفاده نیست.',
'invalidUser' => 'قادر به پیداکردن کاربر مشخص شده نیست',
'bannedUser' => 'در حال حاضر نمی توانید وارد شوید، چون مسدود شده اید.',
'logOutBannedUser' => 'شما به دلیل مسدود شدن، از سیستم خارج شده اید.',
'badAttempt' => 'امکان ورود به سیستم نیست. لطفا اعتبارنامه خود را بررسی کنید.',
'noPassword' => 'تایید کاربر بدون رمز عبور ممکن نیست.',
'invalidPassword' => 'ناتوان در ورود به سیستم. لطفا رمز عبور خود را بررسی کنید.',
Expand Down
2 changes: 2 additions & 0 deletions src/Language/fr/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
'unknownAuthenticator' => '{0} n\'est pas un authentificateur valide.',
'unknownUserProvider' => 'Impossible de déterminer le User Provider à utiliser.',
'invalidUser' => 'Impossible de trouver l\'utilisateur.',
'bannedUser' => '(To be translated) Can not log you in as you are currently banned.',
'logOutBannedUser' => '(To be translated) You have been logged out because you have been banned.',
'badAttempt' => 'Connexion impossible. Veuillez vérifier les informations saisies.',
'noPassword' => 'Impossible de valider un utilisateur sans mot de passe.',
'invalidPassword' => 'Connexion impossible. Veuillez vérifier votre mot de passe.',
Expand Down
2 changes: 2 additions & 0 deletions src/Language/id/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
'unknownAuthenticator' => '{0} bukan otentikator yang sah.',
'unknownUserProvider' => 'Tidak dapat menentukan Penyedia Pengguna yang akan digunakan.',
'invalidUser' => 'Tidak dapat menemukan pengguna yang spesifik.',
'bannedUser' => '(To be translated) Can not log you in as you are currently banned.',
'logOutBannedUser' => '(To be translated) You have been logged out because you have been banned.',
'badAttempt' => 'Anda tidak dapat masuk. Harap periksa kredensial Anda.',
'noPassword' => 'Tidak dapat memvalidasi pengguna tanpa kata sandi.',
'invalidPassword' => 'Anda tidak dapat masuk. Harap periksa kata sandi Anda.',
Expand Down
2 changes: 2 additions & 0 deletions src/Language/it/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
'unknownAuthenticator' => '{0} non è un autenticatore valido.',
'unknownUserProvider' => 'Impossibile determinare lo User Provider da usare.',
'invalidUser' => 'Impossibile trovere l\'utente specificato.',
'bannedUser' => '(To be translated) Can not log you in as you are currently banned.',
'logOutBannedUser' => '(To be translated) You have been logged out because you have been banned.',
'badAttempt' => 'Impossibile accedere. Si prega di verificare le proprie credenziali.',
'noPassword' => 'Impossibile validare un utente senza una password.',
'invalidPassword' => 'Impossibile accedere. Si prega di verificare la propria password.',
Expand Down
2 changes: 2 additions & 0 deletions src/Language/ja/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
'unknownAuthenticator' => '{0} は有効なオーセンティケーターではありません。', // '{0} is not a valid authenticator.',
'unknownUserProvider' => '使用するユーザープロバイダーを決定できません。', // 'Unable to determine the User Provider to use.',
'invalidUser' => '指定されたユーザーを見つけることができません。', // 'Unable to locate the specified user.',
'bannedUser' => '現在あなたはアクセスが禁止されているため、ログインできません。',
'logOutBannedUser' => 'アクセスが禁止されたため、ログアウトされました。',
'badAttempt' => 'ログインできません。認証情報を確認してください。', // 'Unable to log you in. Please check your credentials.',
'noPassword' => 'パスワードのないユーザーは認証できません。', // 'Cannot validate a user without a password.',
'invalidPassword' => 'ログインできません。パスワードを確認してください。', // 'Unable to log you in. Please check your password.',
Expand Down
2 changes: 2 additions & 0 deletions src/Language/pt-BR/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
'unknownAuthenticator' => '{0} não é um autenticador válido.',
'unknownUserProvider' => 'Não foi possível determinar o provedor de usuário a ser usado.',
'invalidUser' => 'Não foi possível localizar o usuário especificado.',
'bannedUser' => '(To be translated) Can not log you in as you are currently banned.',
'logOutBannedUser' => '(To be translated) You have been logged out because you have been banned.',
'badAttempt' => 'Não foi possível fazer login. Por favor, verifique suas credenciais.',
'noPassword' => 'Não é possível validar um usuário sem uma senha.',
'invalidPassword' => 'Não foi possível fazer login. Por favor, verifique sua senha.',
Expand Down
2 changes: 2 additions & 0 deletions src/Language/sk/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
'unknownAuthenticator' => '{0} nie je platný autentifikátor.',
'unknownUserProvider' => 'Nie je možné určiť poskytovateľa používateľa, ktorý sa má použiť.',
'invalidUser' => 'Nie je možné nájsť zadaného používateľa.',
'bannedUser' => '(To be translated) Can not log you in as you are currently banned.',
'logOutBannedUser' => '(To be translated) You have been logged out because you have been banned.',
'badAttempt' => 'Prihlásenie zlyhalo. Skontrolujte svoje prihlasovacie údaje.',
'noPassword' => 'Nie je možné overiť používateľa bez hesla.',
'invalidPassword' => 'Prihlásenie zlyhalo. Skontrolujte svoje heslo.',
Expand Down
2 changes: 2 additions & 0 deletions src/Language/tr/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
'unknownAuthenticator' => '{0} geçerli bir kimlik doğrulayıcı değil.',
'unknownUserProvider' => 'Kullanılacak Kullanıcı Sağlayıcı belirlenemiyor.',
'invalidUser' => 'Belirtilen kullanıcı bulunamadı.',
'bannedUser' => '(To be translated) Can not log you in as you are currently banned.',
'logOutBannedUser' => '(To be translated) You have been logged out because you have been banned.',
'badAttempt' => 'Oturumunuz açılamıyor. Lütfen kimlik bilgilerinizi kontrol edin.',
'noPassword' => 'Parola olmadan bir kullanıcı doğrulanamaz.',
'invalidPassword' => 'Oturumunuz açılamıyor. Lütfen şifrenizi kontrol edin.',
Expand Down
58 changes: 58 additions & 0 deletions src/Traits/Bannable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace CodeIgniter\Shield\Traits;

trait Bannable
{
/**
* Is the user banned?
*/
public function isBanned(): bool
{
return $this->status && $this->status === 'banned';
}

/**
* Ban the user from logging in.
*
* @return $this
*/
public function ban(?string $message = null): self
{
$this->status = 'banned';
$this->status_message = $message;

$users = auth()->getProvider();

$users->save($this);

return $this;
}

/**
* Unban the user and allow them to login
*
* @return $this
*/
public function unBan(): self
{
$this->status = null;
$this->status_message = null;

$users = auth()->getProvider();

$users->save($this);

return $this;
}

/**
* Returns the ban message.
*/
public function getBanMessage(): ?string
davidnsai marked this conversation as resolved.
Show resolved Hide resolved
{
return $this->status_message;
}
}
29 changes: 29 additions & 0 deletions tests/Authorization/AuthorizableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -326,4 +326,33 @@ public function testCreatedAtIfDefaultLocaleSetFaWithAddGroup(): void
Locale::setDefault($currentLocale);
Time::setTestNow();
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test doesn't cover fully or at least 80%, however I don't mind.

public function testBanningUser(): void
{
$this->assertFalse($this->user->isBanned());

$this->user->ban();

$this->assertTrue($this->user->isBanned());
}

public function testUnbanningUser(): void
{
$this->user->ban();

$this->assertTrue($this->user->isBanned());

$this->user->unBan();

$this->assertFalse($this->user->isBanned());
}

public function testGetBanMessage(): void
{
$this->assertNull($this->user->getBanMessage());

$this->user->ban('You are banned');

$this->assertSame('You are banned', $this->user->getBanMessage());
}
}