mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
fix(team): send working invitation links to users without a password
Re-inviting an email after its invitation expired, or re-inviting a user who never set a password, sent a link that needs a login the user cannot complete. Unused invited accounts now get a magic link again, and changing another user's password no longer ends the current session.
This commit is contained in:
@@ -70,21 +70,6 @@ class InviteLink extends Component
|
||||
if ($member_emails->contains($this->email)) {
|
||||
return handleError(livewire: $this, customErrorMessage: "$this->email is already a member of ".currentTeam()->name.'.');
|
||||
}
|
||||
$uuid = new_public_id(32);
|
||||
$link = $this->invitationUrl('team.invitation.show', ['uuid' => $uuid]);
|
||||
$user = User::whereEmail($this->email)->first();
|
||||
|
||||
if (is_null($user)) {
|
||||
$password = Str::password();
|
||||
$user = User::create([
|
||||
'name' => str($this->email)->before('@'),
|
||||
'email' => $this->email,
|
||||
'password' => Hash::make($password),
|
||||
'force_password_reset' => true,
|
||||
]);
|
||||
$token = Crypt::encryptString("{$user->email}@@@{$uuid}@@@{$password}");
|
||||
$link = $this->invitationUrl('auth.link', ['token' => $token]);
|
||||
}
|
||||
$invitation = TeamInvitation::ownedByCurrentTeam()->whereEmail($this->email)->first();
|
||||
if (! is_null($invitation)) {
|
||||
$invitationValid = $invitation->isValid();
|
||||
@@ -95,6 +80,28 @@ class InviteLink extends Component
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the user after the expired invitation is removed, because that can delete an unused account.
|
||||
$uuid = new_public_id(32);
|
||||
$link = $this->invitationUrl('team.invitation.show', ['uuid' => $uuid]);
|
||||
$user = User::whereEmail($this->email)->first();
|
||||
|
||||
// A user who never set a password cannot log in, so they get a magic link with a new temporary password.
|
||||
if (is_null($user) || $user->isUnusedInvitedAccount()) {
|
||||
$password = Str::password();
|
||||
if (is_null($user)) {
|
||||
$user = User::create([
|
||||
'name' => str($this->email)->before('@'),
|
||||
'email' => $this->email,
|
||||
'password' => Hash::make($password),
|
||||
'force_password_reset' => true,
|
||||
]);
|
||||
} else {
|
||||
$user->forceFill(['password' => Hash::make($password)])->save();
|
||||
}
|
||||
$token = Crypt::encryptString("{$user->email}@@@{$uuid}@@@{$password}");
|
||||
$link = $this->invitationUrl('auth.link', ['token' => $token]);
|
||||
}
|
||||
|
||||
$invitation = TeamInvitation::firstOrCreate([
|
||||
'team_id' => currentTeam()->id,
|
||||
'uuid' => $uuid,
|
||||
|
||||
+13
-11
@@ -216,19 +216,21 @@ class User extends Authenticatable implements PasskeyUser, SendsEmail
|
||||
*/
|
||||
public function deleteIfNotVerifiedAndForcePasswordReset(): void
|
||||
{
|
||||
if ($this->hasVerifiedEmail() || $this->force_password_reset !== true) {
|
||||
return;
|
||||
if ($this->isUnusedInvitedAccount()) {
|
||||
$this->delete();
|
||||
}
|
||||
}
|
||||
|
||||
if (TeamInvitation::whereEmail($this->email)->exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->belongsToNonPersonalTeam()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->delete();
|
||||
/**
|
||||
* Whether an invitation created this account and nobody uses it: the owner never set a password,
|
||||
* it has no pending invitations, and it belongs to no team other than its own personal team.
|
||||
*/
|
||||
public function isUnusedInvitedAccount(): bool
|
||||
{
|
||||
return ! $this->hasVerifiedEmail()
|
||||
&& $this->force_password_reset === true
|
||||
&& ! TeamInvitation::whereEmail($this->email)->exists()
|
||||
&& ! $this->belongsToNonPersonalTeam();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,20 +3,23 @@
|
||||
namespace App\Traits;
|
||||
|
||||
use App\Actions\User\RevokeUserTeamTokens;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
trait DeletesUserSessions
|
||||
{
|
||||
/**
|
||||
* Delete all sessions for the current user.
|
||||
* Delete all sessions for this user.
|
||||
* This will force the user to log in again on all devices.
|
||||
*/
|
||||
public function deleteAllSessions(): void
|
||||
{
|
||||
// Invalidate the current session
|
||||
Session::invalidate();
|
||||
Session::regenerateToken();
|
||||
// Invalidate the current session only when it belongs to this user, not to an admin who changes it
|
||||
if (Auth::id() === $this->id) {
|
||||
Session::invalidate();
|
||||
Session::regenerateToken();
|
||||
}
|
||||
DB::table('sessions')->where('user_id', $this->id)->delete();
|
||||
RevokeUserTeamTokens::forUser($this->id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user