diff --git a/cli/src/commands/status.zig b/cli/src/commands/status.zig index 992bd3b..15912fc 100644 --- a/cli/src/commands/status.zig +++ b/cli/src/commands/status.zig @@ -17,7 +17,7 @@ const UserContext = struct { fn authenticateUser(allocator: std.mem.Allocator, config: Config) !UserContext { // Validate API key by making a simple API call to the server - const ws_url = try std.fmt.allocPrint(allocator, "ws://{s}:9103/ws", .{config.worker_host}); + const ws_url = try std.fmt.allocPrint(allocator, "ws://{s}:9101/ws", .{config.worker_host}); defer allocator.free(ws_url); // Try to connect with the API key to validate it diff --git a/cli/src/net/ws.zig b/cli/src/net/ws.zig index 7a91d26..1d4b6d2 100644 --- a/cli/src/net/ws.zig +++ b/cli/src/net/ws.zig @@ -59,7 +59,8 @@ pub const Client = struct { if (pos < path_start) { const port_start = pos + 1; const port_end = std.mem.indexOfPos(u8, url, port_start, "/") orelse url.len; - port = try std.fmt.parseInt(u16, url[port_start..port_end], 10); + const port_str = url[port_start..port_end]; + port = try std.fmt.parseInt(u16, port_str, 10); } } @@ -73,7 +74,6 @@ pub const Client = struct { std.log.warn("TLS (wss://) support requires additional TLS library integration", .{}); return error.TLSNotSupported; } - // Perform WebSocket handshake try handshake(allocator, stream, host, url, api_key); @@ -473,8 +473,35 @@ pub const Client = struct { std.debug.print("Tasks: {d} total, {d} queued, {d} running, {d} failed, {d} completed\n", .{ total, queued, running, failed, completed }); } } else { - // Handle plain text response - std.debug.print("Server response: {s}\n", .{message}); + // Handle plain text response - filter out non-printable characters + var clean_msg = allocator.alloc(u8, message.len) catch { + std.debug.print("Server response: [binary data - {d} bytes]\n", .{message.len}); + return; + }; + defer allocator.free(clean_msg); + + var clean_len: usize = 0; + for (message) |byte| { + // Skip WebSocket frame header bytes and non-printable chars + if (byte >= 32 and byte <= 126) { // printable ASCII only + clean_msg[clean_len] = byte; + clean_len += 1; + } + } + + // Look for common error messages in the cleaned data + if (clean_len > 0) { + const cleaned = clean_msg[0..clean_len]; + if (std.mem.indexOf(u8, cleaned, "Insufficient permissions") != null) { + std.debug.print("Insufficient permissions to view jobs\n", .{}); + } else if (std.mem.indexOf(u8, cleaned, "Authentication failed") != null) { + std.debug.print("Authentication failed\n", .{}); + } else { + std.debug.print("Server response: {s}\n", .{cleaned}); + } + } else { + std.debug.print("Server response: [binary data - {d} bytes]\n", .{message.len}); + } return; } } diff --git a/configs/environments/config-local.yaml b/configs/environments/config-local.yaml index 0d021c8..52bc377 100644 --- a/configs/environments/config-local.yaml +++ b/configs/environments/config-local.yaml @@ -12,6 +12,23 @@ auth: - admin permissions: '*': true + researcher_user: + hash: ef92b778ba7a6c8f2150019a5678047b6a9a2b95cef8189518f9b35c54d2e3ae + admin: false + roles: + - researcher + permissions: + 'experiments': true + 'datasets': true + analyst_user: + hash: ee24de8207189fa4c7f251212f06e8e44080043952b92c568215b831705b7359 + admin: false + roles: + - analyst + permissions: + 'experiments': true + 'datasets': true + 'reports': true server: address: ":9101" @@ -21,7 +38,13 @@ server: security: rate_limit: enabled: false - ip_whitelist: [] + ip_whitelist: + - "127.0.0.1" + - "::1" + - "localhost" + - "172.16.0.0/12" + - "192.168.0.0/16" + - "10.0.0.0/8" # Prometheus metrics metrics: diff --git a/docker-compose.yml b/docker-compose.yml index 572172d..1763a22 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,7 +28,7 @@ services: volumes: - ./data:/data/experiments - ./logs:/logs - - ./configs/config-no-tls.yaml:/app/configs/config.yaml + - ./configs/environments/config-local.yaml:/app/configs/config.yaml depends_on: redis: condition: service_healthy