coolify/app/Livewire/Team/InviteLink.php

123 lines
4.3 KiB
PHP
Raw Permalink Normal View History

2023-06-09 13:55:21 +00:00
<?php
2023-12-07 18:06:32 +00:00
namespace App\Livewire\Team;
2023-06-09 13:55:21 +00:00
use App\Models\TeamInvitation;
use App\Models\User;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
2023-09-15 09:19:36 +00:00
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
2024-06-10 20:43:34 +00:00
use Livewire\Component;
use Visus\Cuid2\Cuid2;
2023-06-09 13:55:21 +00:00
class InviteLink extends Component
{
use AuthorizesRequests;
2023-06-09 13:55:21 +00:00
public string $email;
2024-06-10 20:43:34 +00:00
2023-06-12 10:00:01 +00:00
public string $role = 'member';
2024-05-31 09:03:43 +00:00
protected $rules = [
'email' => 'required|email',
'role' => 'required|string',
];
2024-06-10 20:43:34 +00:00
2023-06-09 13:55:21 +00:00
public function mount()
{
$this->email = isDev() ? 'test3@example.com' : '';
2023-06-09 13:55:21 +00:00
}
2023-06-12 10:00:01 +00:00
public function viaEmail()
{
$this->generateInviteLink(sendEmail: true);
2023-06-12 10:00:01 +00:00
}
2023-09-15 09:19:36 +00:00
public function viaLink()
{
$this->generateInviteLink(sendEmail: false);
2023-09-15 09:19:36 +00:00
}
2024-06-10 20:43:34 +00:00
private function generateInviteLink(bool $sendEmail = false)
2023-06-09 13:55:21 +00:00
{
try {
$this->authorize('manageInvitations', currentTeam());
2024-05-31 09:03:43 +00:00
$this->validate();
fix: critical privilege escalation in team invitation system This commit addresses a critical security vulnerability where low-privileged users (members) could invite high-privileged users (admins/owners) to teams, allowing them to escalate their own privileges through password reset. Root Causes Fixed: 1. TeamPolicy authorization checks were commented out, allowing all team members to manage invitations instead of just admins/owners 2. Missing role elevation checks in InviteLink component allowed members to invite users with higher privileges Security Fixes: 1. app/Policies/TeamPolicy.php - Uncommented and enforced authorization checks for: * update() - Only admins/owners can update team settings * delete() - Only admins/owners can delete teams * manageMembers() - Only admins/owners can manage team members * viewAdmin() - Only admins/owners can view admin panel * manageInvitations() - Only admins/owners can manage invitations 2. app/Livewire/Team/InviteLink.php - Added explicit role elevation checks to prevent: * Members from inviting admins or owners * Admins from inviting owners (defense-in-depth) - Validates that inviter has sufficient privileges for target role Test Coverage: 1. tests/Feature/TeamPolicyTest.php - 24 comprehensive tests covering all policy methods - Tests for owner, admin, member, and non-member access - Specific tests for the privilege escalation vulnerability 2. tests/Feature/TeamInvitationPrivilegeEscalationTest.php - 11 tests covering all role elevation scenarios - Tests member → admin/owner escalation (blocked) - Tests admin → owner escalation (blocked) - Tests valid invitation paths for each role Impact: - Prevents privilege escalation attacks - Protects all Coolify instances from unauthorized access - Enforces proper role hierarchy in team management References: - Identified by Aikido AI whitebox pentest service - CVE: Pending assignment - Severity: Critical 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 09:42:25 +00:00
// Prevent privilege escalation: users cannot invite someone with higher privileges
$userRole = auth()->user()->role();
if (is_null($userRole) || ($userRole === 'member' && in_array($this->role, ['admin', 'owner']))) {
fix: critical privilege escalation in team invitation system This commit addresses a critical security vulnerability where low-privileged users (members) could invite high-privileged users (admins/owners) to teams, allowing them to escalate their own privileges through password reset. Root Causes Fixed: 1. TeamPolicy authorization checks were commented out, allowing all team members to manage invitations instead of just admins/owners 2. Missing role elevation checks in InviteLink component allowed members to invite users with higher privileges Security Fixes: 1. app/Policies/TeamPolicy.php - Uncommented and enforced authorization checks for: * update() - Only admins/owners can update team settings * delete() - Only admins/owners can delete teams * manageMembers() - Only admins/owners can manage team members * viewAdmin() - Only admins/owners can view admin panel * manageInvitations() - Only admins/owners can manage invitations 2. app/Livewire/Team/InviteLink.php - Added explicit role elevation checks to prevent: * Members from inviting admins or owners * Admins from inviting owners (defense-in-depth) - Validates that inviter has sufficient privileges for target role Test Coverage: 1. tests/Feature/TeamPolicyTest.php - 24 comprehensive tests covering all policy methods - Tests for owner, admin, member, and non-member access - Specific tests for the privilege escalation vulnerability 2. tests/Feature/TeamInvitationPrivilegeEscalationTest.php - 11 tests covering all role elevation scenarios - Tests member → admin/owner escalation (blocked) - Tests admin → owner escalation (blocked) - Tests valid invitation paths for each role Impact: - Prevents privilege escalation attacks - Protects all Coolify instances from unauthorized access - Enforces proper role hierarchy in team management References: - Identified by Aikido AI whitebox pentest service - CVE: Pending assignment - Severity: Critical 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 09:42:25 +00:00
throw new \Exception('Members cannot invite admins or owners.');
}
if ($userRole === 'admin' && $this->role === 'owner') {
2024-10-24 18:57:30 +00:00
throw new \Exception('Admins cannot invite owners.');
}
fix: critical privilege escalation in team invitation system This commit addresses a critical security vulnerability where low-privileged users (members) could invite high-privileged users (admins/owners) to teams, allowing them to escalate their own privileges through password reset. Root Causes Fixed: 1. TeamPolicy authorization checks were commented out, allowing all team members to manage invitations instead of just admins/owners 2. Missing role elevation checks in InviteLink component allowed members to invite users with higher privileges Security Fixes: 1. app/Policies/TeamPolicy.php - Uncommented and enforced authorization checks for: * update() - Only admins/owners can update team settings * delete() - Only admins/owners can delete teams * manageMembers() - Only admins/owners can manage team members * viewAdmin() - Only admins/owners can view admin panel * manageInvitations() - Only admins/owners can manage invitations 2. app/Livewire/Team/InviteLink.php - Added explicit role elevation checks to prevent: * Members from inviting admins or owners * Admins from inviting owners (defense-in-depth) - Validates that inviter has sufficient privileges for target role Test Coverage: 1. tests/Feature/TeamPolicyTest.php - 24 comprehensive tests covering all policy methods - Tests for owner, admin, member, and non-member access - Specific tests for the privilege escalation vulnerability 2. tests/Feature/TeamInvitationPrivilegeEscalationTest.php - 11 tests covering all role elevation scenarios - Tests member → admin/owner escalation (blocked) - Tests admin → owner escalation (blocked) - Tests valid invitation paths for each role Impact: - Prevents privilege escalation attacks - Protects all Coolify instances from unauthorized access - Enforces proper role hierarchy in team management References: - Identified by Aikido AI whitebox pentest service - CVE: Pending assignment - Severity: Critical 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 09:42:25 +00:00
$this->email = strtolower($this->email);
2023-08-22 15:44:49 +00:00
$member_emails = currentTeam()->members()->get()->pluck('email');
2023-06-12 10:00:01 +00:00
if ($member_emails->contains($this->email)) {
2024-06-10 20:43:34 +00:00
return handleError(livewire: $this, customErrorMessage: "$this->email is already a member of ".currentTeam()->name.'.');
2023-06-12 10:00:01 +00:00
}
2023-09-15 09:19:36 +00:00
$uuid = new Cuid2(32);
2024-06-10 20:43:34 +00:00
$link = url('/').config('constants.invitation.link.base_url').$uuid;
2023-09-15 09:19:36 +00:00
$user = User::whereEmail($this->email)->first();
2023-06-12 10:00:01 +00:00
2023-09-15 09:19:36 +00:00
if (is_null($user)) {
$password = Str::password();
$user = User::create([
'name' => str($this->email)->before('@'),
2023-09-15 09:19:36 +00:00
'email' => $this->email,
'password' => Hash::make($password),
'force_password_reset' => true,
]);
$token = Crypt::encryptString("{$user->email}@@@$password");
$link = route('auth.link', ['token' => $token]);
}
$invitation = TeamInvitation::whereEmail($this->email)->first();
2024-06-10 20:43:34 +00:00
if (! is_null($invitation)) {
2023-09-15 09:19:36 +00:00
$invitationValid = $invitation->isValid();
if ($invitationValid) {
return handleError(livewire: $this, customErrorMessage: "Pending invitation already exists for $this->email.");
2023-06-09 13:55:21 +00:00
} else {
$invitation->delete();
}
}
2023-06-12 10:00:01 +00:00
2023-09-15 09:19:36 +00:00
$invitation = TeamInvitation::firstOrCreate([
2023-08-22 15:44:49 +00:00
'team_id' => currentTeam()->id,
2023-06-12 10:00:01 +00:00
'uuid' => $uuid,
2023-06-09 13:55:21 +00:00
'email' => $this->email,
2023-06-12 10:00:01 +00:00
'role' => $this->role,
2023-06-09 13:55:21 +00:00
'link' => $link,
2023-09-15 09:19:36 +00:00
'via' => $sendEmail ? 'email' : 'link',
2023-06-09 13:55:21 +00:00
]);
2023-09-15 09:19:36 +00:00
if ($sendEmail) {
2024-07-24 19:11:12 +00:00
$mail = new MailMessage;
2023-09-15 09:19:36 +00:00
$mail->view('emails.invitation-link', [
'team' => currentTeam()->name,
'invitation_link' => $link,
]);
2024-06-10 20:43:34 +00:00
$mail->subject('You have been invited to '.currentTeam()->name.' on '.config('app.name').'.');
2023-09-15 09:19:36 +00:00
send_user_an_email($mail, $this->email);
2024-02-22 13:53:42 +00:00
$this->dispatch('success', 'Invitation sent via email.');
2023-12-07 18:06:32 +00:00
$this->dispatch('refreshInvitations');
2024-06-10 20:43:34 +00:00
2023-09-15 09:19:36 +00:00
return;
2023-06-12 11:10:34 +00:00
} else {
2023-12-07 18:06:32 +00:00
$this->dispatch('success', 'Invitation link generated.');
$this->dispatch('refreshInvitations');
2023-06-12 10:00:01 +00:00
}
2023-06-09 13:55:21 +00:00
} catch (\Throwable $e) {
$error_message = $e->getMessage();
if ($e->getCode() === '23505') {
$error_message = 'Invitation already sent.';
}
2024-06-10 20:43:34 +00:00
return handleError(error: $e, livewire: $this, customErrorMessage: $error_message);
2023-06-09 13:55:21 +00:00
}
}
}