mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-20 02:17:09 +00:00
Merge remote-tracking branch 'origin/next' into v5-parallel-inertia-react
This commit is contained in:
commit
7b317c44c4
6 files changed
+145
-26
No files matched your search
@@ -9,6 +9,7 @@ use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Once;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
@@ -77,6 +78,55 @@ it('accepts a valid magic link invitation only once and rotates the temporary pa
|
||||
$this->assertGuest();
|
||||
});
|
||||
|
||||
it('accepts a magic link when opened from a different public origin', function () {
|
||||
[$team, $user, $password, $token] = createInvitationLinkFixture();
|
||||
|
||||
$this->get('https://coolify.example.com/auth/link?token='.urlencode($token))
|
||||
->assertRedirect(route('dashboard'));
|
||||
|
||||
$this->assertAuthenticatedAs($user);
|
||||
$this->assertDatabaseMissing('team_invitations', ['email' => $user->email]);
|
||||
expect($user->teams()->where('team_id', $team->id)->exists())->toBeTrue();
|
||||
|
||||
$user->refresh();
|
||||
expect(Hash::check($password, $user->password))->toBeFalse();
|
||||
});
|
||||
|
||||
it('keeps the invited user authenticated after rotating the temporary password with database sessions', function () {
|
||||
$this->withMiddleware([CheckForcePasswordReset::class, DecideWhatToDoWithUser::class]);
|
||||
Config::set('session.driver', 'database');
|
||||
|
||||
[$team, $user, $password, $token] = createInvitationLinkFixture();
|
||||
|
||||
$this->get(route('auth.link', ['token' => $token]))
|
||||
->assertRedirect(route('dashboard'));
|
||||
|
||||
expect(DB::table('sessions')->where('user_id', $user->id)->exists())->toBeTrue();
|
||||
|
||||
$this->get(route('dashboard'))
|
||||
->assertRedirect(route('auth.force-password-reset'));
|
||||
|
||||
$this->assertAuthenticatedAs($user);
|
||||
expect($user->teams()->where('team_id', $team->id)->exists())->toBeTrue();
|
||||
|
||||
$user->refresh();
|
||||
expect(Hash::check($password, $user->password))->toBeFalse();
|
||||
});
|
||||
|
||||
it('rejects a magic link when the stored invitation token differs', function () {
|
||||
[, $user, , $token, $invitation] = createInvitationLinkFixture();
|
||||
$differentToken = Crypt::encryptString("{$user->email}@@@{$invitation->uuid}@@@different-password");
|
||||
|
||||
$invitation->forceFill([
|
||||
'link' => route('auth.link', ['token' => $differentToken]),
|
||||
])->save();
|
||||
|
||||
$this->get(route('auth.link', ['token' => $token]))
|
||||
->assertRedirect(route('login'));
|
||||
|
||||
$this->assertGuest();
|
||||
});
|
||||
|
||||
it('rejects a magic link when the invitation was revoked', function () {
|
||||
[, $user, , $token, $invitation] = createInvitationLinkFixture();
|
||||
$invitation->delete();
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
|
||||
use App\Livewire\Team\InviteLink;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Team;
|
||||
use App\Models\TeamInvitation;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Livewire\Livewire;
|
||||
@@ -9,6 +11,8 @@ use Livewire\Livewire;
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
InstanceSettings::unguarded(fn () => InstanceSettings::query()->updateOrCreate(['id' => 0], ['fqdn' => null]));
|
||||
|
||||
// Create a team with owner, admin, and member
|
||||
$this->team = Team::factory()->create();
|
||||
|
||||
@@ -161,6 +165,47 @@ describe('privilege escalation prevention', function () {
|
||||
]);
|
||||
});
|
||||
|
||||
test('new user invitation magic link uses instance fqdn when configured', function () {
|
||||
InstanceSettings::unguarded(fn () => InstanceSettings::query()->updateOrCreate(
|
||||
['id' => 0],
|
||||
['fqdn' => 'https://coolify.example.com']
|
||||
));
|
||||
|
||||
$this->actingAs($this->owner);
|
||||
session(['currentTeam' => $this->team]);
|
||||
|
||||
Livewire::test(InviteLink::class)
|
||||
->set('email', 'fqdn-invitee@example.com')
|
||||
->set('role', 'member')
|
||||
->call('viaLink')
|
||||
->assertDispatched('success');
|
||||
|
||||
$invitation = TeamInvitation::whereEmail('fqdn-invitee@example.com')->firstOrFail();
|
||||
|
||||
expect($invitation->link)->toStartWith('https://coolify.example.com/auth/link?token=');
|
||||
});
|
||||
|
||||
test('new user invitation magic link falls back to route url when instance fqdn is not configured', function () {
|
||||
InstanceSettings::unguarded(fn () => InstanceSettings::query()->updateOrCreate(
|
||||
['id' => 0],
|
||||
['fqdn' => null]
|
||||
));
|
||||
|
||||
$this->actingAs($this->owner);
|
||||
session(['currentTeam' => $this->team]);
|
||||
|
||||
Livewire::test(InviteLink::class)
|
||||
->set('email', 'fallback-invitee@example.com')
|
||||
->set('role', 'member')
|
||||
->call('viaLink')
|
||||
->assertDispatched('success');
|
||||
|
||||
$invitation = TeamInvitation::whereEmail('fallback-invitee@example.com')->firstOrFail();
|
||||
|
||||
$expectedPrefix = route('auth.link', ['token' => '']);
|
||||
expect($invitation->link)->toStartWith($expectedPrefix);
|
||||
});
|
||||
|
||||
test('member cannot bypass policy by calling viaEmail', function () {
|
||||
// Login as member
|
||||
$this->actingAs($this->member);
|
||||
|
||||
Reference in new issue
Block a user