Restrict upgrade-status endpoint to authenticated root team members

- Add auth:sanctum middleware to /api/upgrade-status route
- Check user belongs to root team (id 0) before returning status
- Return 403 if user is not authorized
- Update frontend to send credentials with fetch request
- Update OpenAPI docs with 401/403 responses

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai 2025-12-12 21:16:36 +01:00
parent dc9f612df4
commit 3cc416a806
3 changed files with 41 additions and 3 deletions

View file

@ -189,9 +189,12 @@ public function healthcheck(Request $request)
#[OA\Get(
summary: 'Upgrade Status',
description: 'Get the current upgrade status. Returns the step and message from the upgrade process.',
description: 'Get the current upgrade status. Returns the step and message from the upgrade process. Only available to root team members.',
path: '/upgrade-status',
operationId: 'upgrade-status',
security: [
['bearerAuth' => []],
],
responses: [
new OA\Response(
response: 200,
@ -204,6 +207,19 @@ public function healthcheck(Request $request)
new OA\Property(property: 'message', type: 'string', example: 'Pulling Docker images'),
]
)),
new OA\Response(
response: 401,
ref: '#/components/responses/401',
),
new OA\Response(
response: 403,
description: 'You are not allowed to view upgrade status.',
content: new OA\JsonContent(
type: 'object',
properties: [
new OA\Property(property: 'message', type: 'string', example: 'You are not allowed to view upgrade status.'),
]
)),
new OA\Response(
response: 400,
ref: '#/components/responses/400',
@ -212,6 +228,12 @@ public function healthcheck(Request $request)
)]
public function upgradeStatus(Request $request)
{
// Only root team members can view upgrade status
$user = auth()->user();
if (! $user || $user->currentTeam()->id !== 0) {
return response()->json(['message' => 'You are not allowed to view upgrade status.'], 403);
}
$statusFile = '/data/coolify/source/.upgrade-status';
if (! file_exists($statusFile)) {

View file

@ -286,11 +286,17 @@ class="w-24 dark:bg-coolgray-200 dark:hover:bg-coolgray-300">Cancel
// Poll upgrade status API for real progress
this.checkUpgradeStatusInterval = setInterval(() => {
fetch('/api/upgrade-status')
fetch('/api/upgrade-status', {
credentials: 'same-origin',
headers: {
'Accept': 'application/json',
}
})
.then(response => {
if (response.ok) {
return response.json();
}
// Auth errors (401/403) or service down - switch to health check
throw new Error('Service unavailable');
})
.then(data => {

View file

@ -19,11 +19,21 @@
use Illuminate\Support\Facades\Route;
Route::get('/health', [OtherController::class, 'healthcheck']);
Route::get('/upgrade-status', [OtherController::class, 'upgradeStatus']);
Route::group([
'prefix' => 'v1',
], function () {
Route::get('/health', [OtherController::class, 'healthcheck']);
});
Route::group([
'middleware' => ['auth:sanctum'],
], function () {
Route::get('/upgrade-status', [OtherController::class, 'upgradeStatus']);
});
Route::group([
'middleware' => ['auth:sanctum'],
'prefix' => 'v1',
], function () {
Route::get('/upgrade-status', [OtherController::class, 'upgradeStatus']);
});