added ws authentication

This commit is contained in:
Luan Estradioto
2024-08-15 02:38:06 -03:00
parent c2ea8996ee
commit 548fc21e40
6 changed files with 238 additions and 161 deletions

View File

@@ -29,7 +29,10 @@ class RunCommand extends Component
}
return $server->definedResources()
->filter(fn ($resource) => str_starts_with($resource->status, 'running:'))
->filter(function ($resource) {
$status = method_exists($resource, 'realStatus') ? $resource->realStatus() : (method_exists($resource, 'status') ? $resource->status() : 'exited');
return str_starts_with($status, 'running:');
})
->map(function ($resource) use ($server) {
$container_name = $resource->uuid;

32
package-lock.json generated
View File

@@ -10,6 +10,8 @@
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"alpinejs": "3.14.0",
"cookie": "^0.6.0",
"dotenv": "^16.4.5",
"ioredis": "5.4.1",
"node-pty": "^1.0.0",
"tailwindcss-scrollbar": "0.1.0",
@@ -18,7 +20,7 @@
"devDependencies": {
"@vitejs/plugin-vue": "4.5.1",
"autoprefixer": "10.4.19",
"axios": "1.7.2",
"axios": "^1.7.4",
"laravel-echo": "1.16.1",
"laravel-vite-plugin": "0.8.1",
"postcss": "8.4.38",
@@ -783,10 +785,11 @@
}
},
"node_modules/axios": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
"integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
"integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
"dev": true,
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
@@ -956,6 +959,15 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -1016,6 +1028,18 @@
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
},
"node_modules/dotenv": {
"version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
"integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.692",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.692.tgz",

View File

@@ -8,7 +8,7 @@
"devDependencies": {
"@vitejs/plugin-vue": "4.5.1",
"autoprefixer": "10.4.19",
"axios": "1.7.2",
"axios": "^1.7.4",
"laravel-echo": "1.16.1",
"laravel-vite-plugin": "0.8.1",
"postcss": "8.4.38",
@@ -23,6 +23,8 @@
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"alpinejs": "3.14.0",
"cookie": "^0.6.0",
"dotenv": "^16.4.5",
"ioredis": "5.4.1",
"node-pty": "^1.0.0",
"tailwindcss-scrollbar": "0.1.0",

View File

@@ -34,8 +34,9 @@
function initializeWebSocket() {
if (!socket || socket.readyState === WebSocket.CLOSED) {
const url = "{{ str_replace(['http://', 'https://'], '', config('app.url')) }}" || window.location.hostname;
socket = new WebSocket((window.location.protocol === 'https:' ? 'wss://' : 'ws://') +
"{{ str_replace(['http://', 'https://'], '', config('app.url')) }}" +
url +
':6002/terminal');
socket.onmessage = handleSocketMessage;
socket.onerror = (e) => {

View File

@@ -154,6 +154,12 @@ Route::middleware(['auth', 'verified'])->group(function () {
});
Route::get('/command-center', CommandCenterIndex::class)->name('command-center');
Route::post('/terminal/auth', function () {
if (auth()->check()) {
return response()->json(['authenticated' => true], 200);
}
return response()->json(['authenticated' => false], 401);
})->name('terminal.auth');
Route::prefix('invitations')->group(function () {
Route::get('/{uuid}', [Controller::class, 'accept_invitation'])->name('team.invitation.accept');

View File

@@ -1,6 +1,9 @@
import { WebSocketServer } from 'ws';
import http from 'http';
import pty from 'node-pty';
import axios from 'axios';
import cookie from 'cookie';
import 'dotenv/config'
const server = http.createServer((req, res) => {
if (req.url === '/ready') {
@@ -12,7 +15,45 @@ const server = http.createServer((req, res) => {
}
});
const wss = new WebSocketServer({ server, path: '/terminal' });
const verifyClient = async (info, callback) => {
const cookies = cookie.parse(info.req.headers.cookie || '');
const origin = new URL(info.origin);
const protocol = origin.protocol;
const xsrfToken = cookies['XSRF-TOKEN'];
// Generate session cookie name based on APP_NAME
const appName = process.env.APP_NAME || 'laravel';
const sessionCookieName = `${appName.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase()}_session`;
const laravelSession = cookies[sessionCookieName];
// Verify presence of required tokens
if (!laravelSession || !xsrfToken) {
return callback(false, 401, 'Unauthorized: Missing required tokens');
}
try {
// Authenticate with Laravel backend
const response = await axios.post(`${protocol}//coolify/terminal/auth`, null, {
headers: {
'Cookie': `${sessionCookieName}=${laravelSession}`,
'X-XSRF-TOKEN': xsrfToken
},
});
if (response.status === 200) {
// Authentication successful
callback(true);
} else {
callback(false, 401, 'Unauthorized: Invalid credentials');
}
} catch (error) {
console.error('Authentication error:', error.message);
callback(false, 500, 'Internal Server Error');
}
};
const wss = new WebSocketServer({ server, path: '/terminal', verifyClient: verifyClient });
const userSessions = new Map();
wss.on('connection', (ws) => {