tests:adds testing to portfolio access policy
This commit is contained in:
@@ -15,7 +15,7 @@ class PortfolioPolicy
|
|||||||
{
|
{
|
||||||
$pivot = $portfolio->users()->where('user_id', $user->id)->first();
|
$pivot = $portfolio->users()->where('user_id', $user->id)->first();
|
||||||
|
|
||||||
return $pivot;
|
return !!$pivot;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-1
@@ -358,7 +358,7 @@
|
|||||||
"By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.": "By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.",
|
"By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.": "By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.",
|
||||||
|
|
||||||
"Hey again!": "Hey again!",
|
"Hey again!": "Hey again!",
|
||||||
"Before you can get started with Investbrain, you'll want to create a password:": "Before you can get started with Investbrain, you'll want to create a password:",
|
"Before you can get started with Investbrain, let's complete your profile:": "Before you can get started with Investbrain, let's complete your profile:",
|
||||||
"Or login with SSO:": "Or login with SSO:",
|
"Or login with SSO:": "Or login with SSO:",
|
||||||
"Create Password": "Create Password"
|
"Create Password": "Create Password"
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -358,7 +358,7 @@
|
|||||||
"By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.": "Al eliminar el acceso de esta persona, ya no podrá ver este portafolio. Perderán el acceso inmediatamente.",
|
"By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.": "Al eliminar el acceso de esta persona, ya no podrá ver este portafolio. Perderán el acceso inmediatamente.",
|
||||||
|
|
||||||
"Hey again!": "¡Oye de nuevo!",
|
"Hey again!": "¡Oye de nuevo!",
|
||||||
"Before you can get started with Investbrain, you'll want to create a password:": "Antes de poder comenzar a utilizar Investbrain, deberá crear una cuenta:",
|
"Before you can get started with Investbrain, let's complete your profile:": "Antes de poder comenzar a utilizar Investbrain, deberá crear una cuenta:",
|
||||||
"Or login with SSO:": "O iniciar sesión mediante SSO:",
|
"Or login with SSO:": "O iniciar sesión mediante SSO:",
|
||||||
"Create Password": "Crear Contraseña"
|
"Create Password": "Crear Contraseña"
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
</x-slot:logo>
|
</x-slot:logo>
|
||||||
|
|
||||||
<h1 class="text-2xl font-bold mb-4">{{ __('Hey again!') }} 👋</h1>
|
<h1 class="text-2xl font-bold mb-4">{{ __('Hey again!') }} 👋</h1>
|
||||||
<p class="mb-2">{{ __('Before you can get started with Investbrain, you\'ll want to create a password:') }}</p>
|
<p class="mb-2">{{ __('Before you can get started with Investbrain, let\'s complete your profile:') }}</p>
|
||||||
|
|
||||||
@livewire('invited-onboarding-form', [
|
@livewire('invited-onboarding-form', [
|
||||||
'portfolio' => $portfolio,
|
'portfolio' => $portfolio,
|
||||||
|
|||||||
@@ -126,6 +126,7 @@ new class extends Component {
|
|||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="border-primary border rounded-sm px-2 py-5 mb-2">
|
<div class="border-primary border rounded-sm px-2 py-5 mb-2">
|
||||||
|
@if ($portfolio->owner)
|
||||||
<x-list-item
|
<x-list-item
|
||||||
:item="$portfolio->owner"
|
:item="$portfolio->owner"
|
||||||
avatar="profile_photo_url"
|
avatar="profile_photo_url"
|
||||||
@@ -145,6 +146,7 @@ new class extends Component {
|
|||||||
{{ __('Owner') }}
|
{{ __('Owner') }}
|
||||||
</x-slot:sub-value>
|
</x-slot:sub-value>
|
||||||
</x-list-item>
|
</x-list-item>
|
||||||
|
@endif
|
||||||
|
|
||||||
@foreach (collect($this->portfolio?->users)->where('pivot.owner', '!=', 1) as $user)
|
@foreach (collect($this->portfolio?->users)->where('pivot.owner', '!=', 1) as $user)
|
||||||
<x-list-item
|
<x-list-item
|
||||||
|
|||||||
+1
-1
@@ -47,4 +47,4 @@ Route::get('/privacy', [PrivacyPolicyController::class, 'show'])->name('policy.s
|
|||||||
Route::get('auth/verify/{connected_account}', [ConnectedAccountController::class, 'verify'])->name('oauth.verify_connected_account');
|
Route::get('auth/verify/{connected_account}', [ConnectedAccountController::class, 'verify'])->name('oauth.verify_connected_account');
|
||||||
|
|
||||||
Route::get('auth/{provider}', [ConnectedAccountController::class, 'redirectToProvider'])->name('oauth.redirect');
|
Route::get('auth/{provider}', [ConnectedAccountController::class, 'redirectToProvider'])->name('oauth.redirect');
|
||||||
Route::get('auth/{provider}/callback', [ConnectedAccountController::class, 'handleProviderCallback']);
|
Route::get('auth/{provider}/callback', [ConnectedAccountController::class, 'handleProviderCallback'])->name('oauth.callback');
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests;
|
||||||
|
|
||||||
|
use Tests\TestCase;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\ConnectedAccount;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Laravel\Socialite\Facades\Socialite;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use App\Http\Controllers\ConnectedAccountController;
|
||||||
|
|
||||||
|
class ConnectedAccountTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
protected $controller;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->controller = new ConnectedAccountController();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_handle_provider_callback_with_already_connected_account()
|
||||||
|
{
|
||||||
|
$provider = 'github';
|
||||||
|
config(['services.enabled_login_providers' => 'github,google']);
|
||||||
|
|
||||||
|
// Create a user and a connected account for the provider
|
||||||
|
$user = User::create([
|
||||||
|
'name' => 'Alice Smith',
|
||||||
|
'email' => 'alice@example.com',
|
||||||
|
'email_verified_at' => now(),
|
||||||
|
]);
|
||||||
|
$providerUser = (object)[
|
||||||
|
'id' => '67890',
|
||||||
|
'name' => 'Alice Smith',
|
||||||
|
'email' => 'alice@example.com',
|
||||||
|
'token' => '15932t8',
|
||||||
|
'tokenSecret' => null,
|
||||||
|
'refreshToken' => null,
|
||||||
|
'expiresIn' => null,
|
||||||
|
];
|
||||||
|
ConnectedAccount::forceCreate([
|
||||||
|
'provider' => $provider,
|
||||||
|
'provider_id' => $providerUser->id,
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'token' => $providerUser->token,
|
||||||
|
'verified_at' => now()
|
||||||
|
]);
|
||||||
|
|
||||||
|
Socialite::shouldReceive('driver')
|
||||||
|
->with($provider)
|
||||||
|
->andReturnSelf()
|
||||||
|
->shouldReceive('user')
|
||||||
|
->andReturn($providerUser);
|
||||||
|
|
||||||
|
$response = $this->get(route('oauth.callback', ['provider' => $provider]));
|
||||||
|
|
||||||
|
$this->assertTrue(Auth::check());
|
||||||
|
$this->assertEquals($user->id, Auth::id());
|
||||||
|
|
||||||
|
$response->assertRedirect(route('dashboard'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_handle_provider_callback_with_new_user()
|
||||||
|
{
|
||||||
|
$provider = 'github';
|
||||||
|
config(['services.enabled_login_providers' => 'github,google']);
|
||||||
|
$providerUser = (object)[
|
||||||
|
'id' => '12345',
|
||||||
|
'name' => 'John Doe',
|
||||||
|
'email' => 'john@example.com',
|
||||||
|
'token' => 'token',
|
||||||
|
'tokenSecret' => null,
|
||||||
|
'refreshToken' => null,
|
||||||
|
'expiresIn' => null,
|
||||||
|
];
|
||||||
|
|
||||||
|
Socialite::shouldReceive('driver')
|
||||||
|
->with($provider)
|
||||||
|
->andReturnSelf()
|
||||||
|
->shouldReceive('user')
|
||||||
|
->andReturn($providerUser);
|
||||||
|
|
||||||
|
$response = $this->get(route('oauth.callback', ['provider' => $provider]));
|
||||||
|
|
||||||
|
$user = User::where('email', 'john@example.com')->first();
|
||||||
|
$this->assertNotNull($user);
|
||||||
|
$this->assertEquals('John Doe', $user->name);
|
||||||
|
|
||||||
|
$connectedAccount = ConnectedAccount::first();
|
||||||
|
$this->assertNotNull($connectedAccount);
|
||||||
|
$this->assertEquals('github', $connectedAccount->provider);
|
||||||
|
$this->assertEquals('12345', $connectedAccount->provider_id);
|
||||||
|
$this->assertNotNull($connectedAccount->verified_at);
|
||||||
|
|
||||||
|
$this->assertTrue(Auth::check());
|
||||||
|
$response->assertRedirect(route('dashboard'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_handle_provider_callback_with_existing_account()
|
||||||
|
{
|
||||||
|
$provider = 'github';
|
||||||
|
config(['services.enabled_login_providers' => 'github,google']);
|
||||||
|
User::create([
|
||||||
|
'name' => 'Jane Doe',
|
||||||
|
'email' => 'jane@example.com',
|
||||||
|
'email_verified_at' => now(),
|
||||||
|
]);
|
||||||
|
$providerUser = (object)[
|
||||||
|
'id' => '54321',
|
||||||
|
'name' => 'Jane Doe',
|
||||||
|
'email' => 'jane@example.com',
|
||||||
|
'token' => 'token',
|
||||||
|
'tokenSecret' => null,
|
||||||
|
'refreshToken' => null,
|
||||||
|
'expiresIn' => null,
|
||||||
|
];
|
||||||
|
|
||||||
|
Socialite::shouldReceive('driver')
|
||||||
|
->with($provider)
|
||||||
|
->andReturnSelf()
|
||||||
|
->shouldReceive('user')
|
||||||
|
->andReturn($providerUser);
|
||||||
|
|
||||||
|
$response = $this->get(route('oauth.callback', ['provider' => $provider]));
|
||||||
|
|
||||||
|
$connectedAccount = ConnectedAccount::first();
|
||||||
|
$this->assertNotNull($connectedAccount);
|
||||||
|
$this->assertEquals('github', $connectedAccount->provider);
|
||||||
|
$this->assertEquals('54321', $connectedAccount->provider_id);
|
||||||
|
$this->assertNull($connectedAccount->verified_at);
|
||||||
|
|
||||||
|
$response->assertRedirect(route('login'));
|
||||||
|
$response->assertSessionHas('status', 'Account already exists. Check your email to connect your GitHub account.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_verify_connected_account()
|
||||||
|
{
|
||||||
|
$user = User::create([
|
||||||
|
'name' => 'Alice Smith',
|
||||||
|
'email' => 'alice@example.com',
|
||||||
|
'email_verified_at' => null,
|
||||||
|
]);
|
||||||
|
$connectedAccount = ConnectedAccount::forceCreate([
|
||||||
|
'provider' => 'github',
|
||||||
|
'provider_id' => '12345',
|
||||||
|
'token' => '0283523',
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'verified_at' => null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->assertNull($connectedAccount->verified_at);
|
||||||
|
|
||||||
|
$response = $this->get(route('oauth.verify_connected_account', ['connected_account' => $connectedAccount->id]));
|
||||||
|
|
||||||
|
$connectedAccount->refresh();
|
||||||
|
|
||||||
|
$this->assertNotNull($connectedAccount->verified_at);
|
||||||
|
$this->assertNotNull($connectedAccount->user);
|
||||||
|
|
||||||
|
$response->assertRedirect(route('dashboard'));
|
||||||
|
$response->assertSessionHas('toast');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests;
|
||||||
|
|
||||||
|
use Tests\TestCase;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Models\Portfolio;
|
||||||
|
use App\Policies\PortfolioPolicy;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
class PortfolioPolicyTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
protected $policy;
|
||||||
|
protected $user;
|
||||||
|
protected $portfolio;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->policy = new PortfolioPolicy();
|
||||||
|
|
||||||
|
$this->user = User::factory()->create();
|
||||||
|
|
||||||
|
Auth::login($this->user);
|
||||||
|
$this->portfolio = Portfolio::factory()->create();
|
||||||
|
|
||||||
|
// Attach the users to the portfolio
|
||||||
|
$this->portfolio->users()->syncWithoutDetaching([
|
||||||
|
$this->user->id => [
|
||||||
|
'full_access' => false,
|
||||||
|
'owner' => false,
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_stranger_access_viaweb()
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$result = $this->actingAs($user)->get(route('portfolio.show', ['portfolio' => $this->portfolio]));
|
||||||
|
|
||||||
|
$result->assertStatus(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_stranger_access_via_policy()
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
|
||||||
|
$result = $this->policy->readOnly($user, $this->portfolio);
|
||||||
|
$this->assertFalse($result, 'User should not have readonly access');
|
||||||
|
|
||||||
|
$result = $this->policy->fullAccess($user, $this->portfolio);
|
||||||
|
$this->assertFalse($result, 'User should not have full access');
|
||||||
|
|
||||||
|
$result = $this->policy->owner($user, $this->portfolio);
|
||||||
|
$this->assertFalse($result, 'User should not have owner access');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_read_only_policy()
|
||||||
|
{
|
||||||
|
$result = $this->policy->readOnly($this->user, $this->portfolio);
|
||||||
|
$this->assertTrue($result, 'User should have read-only access');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_read_only_via_web()
|
||||||
|
{
|
||||||
|
$result = $this->actingAs($this->user)->get(route('portfolio.show', ['portfolio' => $this->portfolio]));
|
||||||
|
|
||||||
|
$result->assertStatus(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_full_access_policy_with_full_access()
|
||||||
|
{
|
||||||
|
// Update pivot table to give full access
|
||||||
|
$this->portfolio->users()->updateExistingPivot($this->user->id, [
|
||||||
|
'full_access' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$result = $this->policy->fullAccess($this->user, $this->portfolio);
|
||||||
|
$this->assertTrue($result, 'User should have full access');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_full_access_policy_without_full_access()
|
||||||
|
{
|
||||||
|
// Check that the user doesn't have full access
|
||||||
|
$result = $this->policy->fullAccess($this->user, $this->portfolio);
|
||||||
|
$this->assertFalse($result, 'User should not have full access');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_owner_policy_when_user_is_owner()
|
||||||
|
{
|
||||||
|
// Update pivot table to make the user the owner
|
||||||
|
$this->portfolio->users()->updateExistingPivot($this->user->id, [
|
||||||
|
'owner' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$result = $this->policy->owner($this->user, $this->portfolio);
|
||||||
|
$this->assertTrue($result, 'User should be the owner');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_owner_policy_when_user_is_not_owner()
|
||||||
|
{
|
||||||
|
// Check that the user is not the owner
|
||||||
|
$result = $this->policy->owner($this->user, $this->portfolio);
|
||||||
|
$this->assertFalse($result, 'User should not be the owner');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user