mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
fix: use GitLab API v4 for OAuth and gate env copy by update policy
Socialite's GitLab provider fetches the user from /api/v3/user, which GitLab removed in 11.0 (410 Gone). Add App\Auth\GitlabProvider that reads /api/v4/user with a bearer token and register it in the socialite helper, for both gitlab.com and self-hosted base URLs. Environment variable copyValue now checks the 'update' policy on the variable instead of the member role of the current team, so members can no longer copy values after switching to a team they own. Remove the redundant member check from ShowHardcoded. Add tests for the GitLab v4 user lookup and the team-switch copy case.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Auth;
|
||||
|
||||
use GuzzleHttp\RequestOptions;
|
||||
use Laravel\Socialite\Two\GitlabProvider as SocialiteGitlabProvider;
|
||||
|
||||
/**
|
||||
* Socialite's GitLab provider reads the user from /api/v3/user, which GitLab
|
||||
* removed in 11.0 (410 Gone). Read it from API v4 instead.
|
||||
*/
|
||||
class GitlabProvider extends SocialiteGitlabProvider
|
||||
{
|
||||
/**
|
||||
* @param string $token
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
protected function getUserByToken($token)
|
||||
{
|
||||
$response = $this->getHttpClient()->get($this->host.'/api/v4/user', [
|
||||
RequestOptions::HEADERS => ['Authorization' => 'Bearer '.$token],
|
||||
]);
|
||||
|
||||
return json_decode((string) $response->getBody(), true);
|
||||
}
|
||||
}
|
||||
@@ -174,7 +174,7 @@ class Show extends Component
|
||||
|
||||
public function copyValue(): ?string
|
||||
{
|
||||
if ($this->env->is_shown_once || (auth()->user()?->isMember() ?? true)) {
|
||||
if ($this->env->is_shown_once || (auth()->user()?->cannot('update', $this->env) ?? true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,10 +40,6 @@ class ShowHardcoded extends Component
|
||||
|
||||
public function copyValue(): ?string
|
||||
{
|
||||
if (auth()->user()?->isMember() ?? true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$environmentVariable = EnvironmentVariable::make([
|
||||
'value' => $this->value,
|
||||
'is_preview' => $this->isPreview,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<?php
|
||||
|
||||
use App\Auth\GitlabProvider;
|
||||
use App\Auth\Oidc\OidcConfig;
|
||||
use App\Models\OauthSetting;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
use Laravel\Socialite\Two\BitbucketProvider;
|
||||
use Laravel\Socialite\Two\GithubProvider;
|
||||
use Laravel\Socialite\Two\GitlabProvider;
|
||||
use SocialiteProviders\Discord\Provider;
|
||||
use SocialiteProviders\Manager\Config;
|
||||
|
||||
|
||||
@@ -123,6 +123,22 @@ test('members get no copy button and no value', function () {
|
||||
->assertReturned(null);
|
||||
});
|
||||
|
||||
test('members get no value after switching to a team they own', function (string $variableType) {
|
||||
$member = User::factory()->create();
|
||||
$this->team->members()->attach($member, ['role' => 'member']);
|
||||
$ownedTeam = Team::factory()->create();
|
||||
$ownedTeam->members()->attach($member, ['role' => 'owner']);
|
||||
$this->actingAs($member);
|
||||
$env = $variableType === 'shared'
|
||||
? SharedEnvironmentVariable::create(['key' => 'TEAM_WIDE', 'value' => 'team-wide-secret', 'type' => 'team', 'team_id' => $this->team->id])
|
||||
: createEnvironmentVariable();
|
||||
|
||||
$component = Livewire::test(Show::class, ['env' => $env, 'type' => 'application']);
|
||||
session(['currentTeam' => $ownedTeam]);
|
||||
|
||||
$component->call('copyValue')->assertReturned(null);
|
||||
})->with(['application variable' => 'application', 'shared variable' => 'shared']);
|
||||
|
||||
test('locked variables get no copy button and no value', function () {
|
||||
Livewire::test(Show::class, ['env' => createEnvironmentVariable(['is_shown_once' => true]), 'type' => 'application'])
|
||||
->assertDontSeeHtml('Copy value')
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
use App\Models\OauthSetting;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Middleware;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
it('reads the GitLab user from API v4 with a bearer token', function (?string $baseUrl, string $expectedUrl) {
|
||||
OauthSetting::create([
|
||||
'provider' => 'gitlab',
|
||||
'client_id' => 'client-id',
|
||||
'client_secret' => 'client-secret',
|
||||
'redirect_uri' => 'https://coolify.example.com/auth/gitlab/callback',
|
||||
'base_url' => $baseUrl,
|
||||
'enabled' => true,
|
||||
]);
|
||||
|
||||
$requests = [];
|
||||
$handler = HandlerStack::create(new MockHandler([
|
||||
new Response(200, ['Content-Type' => 'application/json'], json_encode([
|
||||
'id' => 42,
|
||||
'username' => 'gitlab-user',
|
||||
'name' => 'GitLab User',
|
||||
'email' => 'user@example.com',
|
||||
'avatar_url' => null,
|
||||
'confirmed_at' => '2026-01-10T09:05:22Z',
|
||||
])),
|
||||
]));
|
||||
$handler->push(Middleware::history($requests));
|
||||
|
||||
$gitlabUser = get_socialite_provider('gitlab')
|
||||
->setHttpClient(new Client(['handler' => $handler]))
|
||||
->userFromToken('gitlab-access-token');
|
||||
|
||||
$request = $requests[0]['request'];
|
||||
expect((string) $request->getUri())->toBe($expectedUrl)
|
||||
->and($request->getHeaderLine('Authorization'))->toBe('Bearer gitlab-access-token')
|
||||
->and($gitlabUser->getId())->toBe(42)
|
||||
->and($gitlabUser->getEmail())->toBe('user@example.com')
|
||||
->and($gitlabUser->user['confirmed_at'])->toBe('2026-01-10T09:05:22Z');
|
||||
})->with([
|
||||
'gitlab.com' => [null, 'https://gitlab.com/api/v4/user'],
|
||||
'self-hosted GitLab' => ['https://gitlab.example.com/', 'https://gitlab.example.com/api/v4/user'],
|
||||
]);
|
||||
Reference in New Issue
Block a user