Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 11 additions & 1 deletion src/Http/Controllers/Admin/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,17 @@ public function linkProvider(Request $request)
*/
protected function credentials(Request $request): array
{
return array_merge($request->only($this->username(), 'password'), ['published' => 1]);
$credentials = $request->only($this->username(), 'password');

$email = $credentials[$this->username()] ?? null;

if ($this->username() === 'email' && is_string($email)) {
$credentials[$this->username()] = function ($query) use ($email) {
$query->whereRaw('LOWER(email) = ?', [mb_strtolower($email)]);
};
}

return array_merge($credentials, ['published' => 1]);
}

protected function autologin(): bool
Expand Down
34 changes: 34 additions & 0 deletions tests/integration/LoginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace A17\Twill\Tests\Integration;

use A17\Twill\Models\User;
use Illuminate\Support\Facades\DB;
use PragmaRX\Google2FA\Google2FA;

class LoginTest extends TestCase
Expand All @@ -29,6 +30,39 @@ public function testCanLogin(): void
$this->assertSee('Logout');
}

public function testCanLoginWithDifferentEmailCase(): void
{
// Force a case-sensitive collation so this test doesn't pass by
// accident on a DB whose default collation already happens to be
// case-insensitive (e.g. MySQL's utf8mb4_0900_ai_ci).
$usersTable = (new User())->getTable();
$passwordResetsTable = config('twill.password_resets_table', 'twill_password_resets');

$originalCollation = DB::selectOne(
'SELECT COLLATION_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?',
[$usersTable, 'email']
)->COLLATION_NAME;

DB::statement('SET FOREIGN_KEY_CHECKS=0');
DB::statement("ALTER TABLE {$usersTable} MODIFY email VARCHAR(255) COLLATE utf8mb4_bin");
DB::statement("ALTER TABLE {$passwordResetsTable} MODIFY email VARCHAR(255) COLLATE utf8mb4_bin");
DB::statement('SET FOREIGN_KEY_CHECKS=1');

try {
$this->loginAs(
strtoupper($this->superAdmin()->email),
$this->superAdmin()->unencrypted_password
);

$this->assertAuthenticated();
} finally {
DB::statement('SET FOREIGN_KEY_CHECKS=0');
DB::statement("ALTER TABLE {$usersTable} MODIFY email VARCHAR(255) COLLATE {$originalCollation}");
DB::statement("ALTER TABLE {$passwordResetsTable} MODIFY email VARCHAR(255) COLLATE {$originalCollation}");
DB::statement('SET FOREIGN_KEY_CHECKS=1');
}
}

public function testCannotLoginWhenUserDisabled(): void
{
unset($this->superAdmin->unencrypted_password);
Expand Down
Loading