Skip to content
Merged
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
3 changes: 2 additions & 1 deletion src/Auth/Eloquent/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Statamic\Auth\Eloquent;

use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
Expand Down Expand Up @@ -370,7 +371,7 @@ public function lastLogin()
return null;
}

return $date instanceof Carbon ? $date : Carbon::createFromFormat($this->model()->getDateFormat(), $date);
return $date instanceof CarbonInterface ? $date : Carbon::createFromFormat($this->model()->getDateFormat(), $date);
}

public function setLastLogin($time)
Expand Down
5 changes: 3 additions & 2 deletions src/Auth/WebAuthn/Passkey.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Statamic\Auth\WebAuthn;

use Carbon\Carbon;
use Carbon\CarbonInterface;
use ParagonIE\ConstantTime\Base64UrlSafe;
use Statamic\Contracts\Auth\Passkey as Contract;
use Statamic\Contracts\Auth\User as UserContract;
Expand Down Expand Up @@ -68,15 +69,15 @@ public function lastLogin(): ?Carbon
return $this->lastLogin;
}

public function setLastLogin(null|int|Carbon $time): Contract
public function setLastLogin(null|int|CarbonInterface $time): Contract
{
if ($time === null) {
$this->lastLogin = null;

return $this;
}

$this->lastLogin = $time instanceof Carbon ? $time : Carbon::createFromTimestamp($time);
$this->lastLogin = is_int($time) ? Carbon::createFromTimestamp($time) : Carbon::instance($time);

return $this;
}
Expand Down
3 changes: 2 additions & 1 deletion src/Contracts/Auth/Passkey.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Statamic\Contracts\Auth;

use Carbon\Carbon;
use Carbon\CarbonInterface;
use Webauthn\PublicKeyCredentialSource;

interface Passkey
Expand All @@ -23,7 +24,7 @@ public function setCredential(array|PublicKeyCredentialSource $credential): self

public function lastLogin(): ?Carbon;

public function setLastLogin(null|int|Carbon $time): self;
public function setLastLogin(null|int|CarbonInterface $time): self;

public function save(): bool;

Expand Down
25 changes: 25 additions & 0 deletions tests/Auth/Eloquent/EloquentUserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

namespace Tests\Auth\Eloquent;

use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use DateTime;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Date;
use ParagonIE\ConstantTime\Base64UrlSafe;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
Expand Down Expand Up @@ -310,6 +314,27 @@ public function it_gets_the_timestamps_property_from_the_model()
$this->assertFalse($user->timestamps);
}

#[Test]
public function it_gets_the_last_login_when_the_app_uses_immutable_dates()
{
Date::use(CarbonImmutable::class);

// The Control Panel's Localize middleware changes the format too.
Date::setToStringFormat(DateTime::ATOM);

$this->beforeApplicationDestroyed(function () {
Date::useDefault();
Date::resetToStringFormat();
});

$user = $this->makeUser();
$user->model()->mergeCasts(['last_login' => 'datetime']);
$user->setLastLogin(now());

$this->assertInstanceOf(CarbonInterface::class, $user->lastLogin());
$this->assertEquals('2019-11-21 23:39:29', $user->lastLogin()->format('Y-m-d H:i:s'));
}

#[Test]
public function it_gets_super_correctly_on_the_model()
{
Expand Down
22 changes: 22 additions & 0 deletions tests/Auth/WebAuthn/EloquentPasskeyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
namespace Tests\Auth\WebAuthn;

use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Date;
use ParagonIE\ConstantTime\Base64UrlSafe;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
Expand Down Expand Up @@ -157,6 +159,26 @@ public function it_updates_existing_passkey()
]);
}

#[Test]
public function it_saves_a_passkey_with_a_last_login_when_the_app_uses_immutable_dates()
{
Date::use(CarbonImmutable::class);

$this->beforeApplicationDestroyed(fn () => Date::useDefault());

$user = User::make()->email('test@example.com')->set('name', 'Test User');
$user->save();

$passkey = (new Passkey)
->setName('My Passkey')
->setUser($user)
->setCredential($this->createTestCredential())
->setLastLogin(now());

$this->assertTrue($passkey->save());
$this->assertEquals('2019-11-21 23:39:29', $passkey->lastLogin()->format('Y-m-d H:i:s'));
}

#[Test]
public function it_saves_multiple_passkeys_for_same_user()
{
Expand Down
17 changes: 17 additions & 0 deletions tests/Auth/WebAuthn/PasskeyTests.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Tests\Auth\WebAuthn;

use Carbon\Carbon;
use Carbon\CarbonImmutable;
use PHPUnit\Framework\Attributes\Test;
use Statamic\Contracts\Auth\Passkey;
use Statamic\Facades\User;
Expand Down Expand Up @@ -31,6 +32,22 @@ public function it_gets_last_login()
$this->assertEquals('2024-01-15 10:30:00', $passkey->lastLogin()->format('Y-m-d H:i:s'));
}

#[Test]
public function it_sets_last_login_from_an_immutable_date()
{
$user = tap(User::make()->email('test@example.com')->data(['name' => 'John Smith']))->save();
$credential = $this->createTestCredential();

$passkey = $this->newPasskey()
->setName('My Passkey')
->setUser($user)
->setCredential($credential)
->setLastLogin(CarbonImmutable::create(2024, 1, 15, 10, 30, 0));

$this->assertInstanceOf(Carbon::class, $passkey->lastLogin());
$this->assertEquals('2024-01-15 10:30:00', $passkey->lastLogin()->format('Y-m-d H:i:s'));
}

#[Test]
public function it_handles_null_last_login()
{
Expand Down
Loading