feat: Dynamically set session cookie domain based on the public URL.
This commit is contained in:
parent
3b980a7ff2
commit
7aa99b43d8
70
accountsvc.log
Normal file
70
accountsvc.log
Normal file
@ -0,0 +1,70 @@
|
||||
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
|
||||
- using env: export GIN_MODE=release
|
||||
- using code: gin.SetMode(gin.ReleaseMode)
|
||||
|
||||
time=2026-02-07T02:23:47.924+08:00 level=WARN msg="xray sync is disabled in configuration; agent mode will still attempt to manage xray config"
|
||||
time=2026-02-07T02:23:47.924+08:00 level=INFO msg="configured cors" allowedOrigins="[http://localhost:3000 http://127.0.0.1:3000]"
|
||||
time=2026-02-07T02:23:47.987+08:00 level=WARN msg="root account bootstrapped from environment variable" email=admin@svc.plus
|
||||
time=2026-02-07T02:23:48.035+08:00 level=INFO msg="demo read-only user created" username=Demo email=demo@svc.plus
|
||||
time=2026-02-07T02:23:48.080+08:00 level=INFO msg="sandbox experience user created" email=sandbox@svc.plus
|
||||
time=2026-02-07T02:23:48.080+08:00 level=INFO msg="token service initialized" auth_enabled=true
|
||||
time=2026-02-07T02:23:48.082+08:00 level=INFO msg="loaded agents from store" count=1
|
||||
[GIN-debug] GET /healthz --> account/api.RegisterRoutes.func1 (4 handlers)
|
||||
[GIN-debug] POST /api/auth/register --> account/api.(*handler).register-fm (4 handlers)
|
||||
[GIN-debug] POST /api/auth/register/verify --> account/api.(*handler).verifyEmail-fm (4 handlers)
|
||||
[GIN-debug] POST /api/auth/register/send --> account/api.(*handler).sendEmailVerification-fm (4 handlers)
|
||||
[GIN-debug] POST /api/auth/login --> account/api.(*handler).login-fm (4 handlers)
|
||||
[GIN-debug] POST /api/auth/token/exchange --> account/api.(*handler).exchangeToken-fm (4 handlers)
|
||||
[GIN-debug] GET /api/auth/oauth/login/:provider --> account/api.(*handler).oauthLogin-fm (4 handlers)
|
||||
[GIN-debug] GET /api/auth/oauth/callback/:provider --> account/api.(*handler).oauthCallback-fm (4 handlers)
|
||||
[GIN-debug] POST /api/auth/token/refresh --> account/api.(*handler).refreshToken-fm (4 handlers)
|
||||
[GIN-debug] GET /api/auth/mfa/status --> account/api.(*handler).mfaStatus-fm (4 handlers)
|
||||
[GIN-debug] GET /api/auth/session --> account/api.(*handler).session-fm (6 handlers)
|
||||
[GIN-debug] DELETE /api/auth/session --> account/api.(*handler).deleteSession-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/mfa/totp/provision --> account/api.(*handler).provisionTOTP-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/mfa/totp/verify --> account/api.(*handler).verifyTOTP-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/mfa/disable --> account/api.(*handler).disableMFA-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/password/reset --> account/api.(*handler).requestPasswordReset-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/password/reset/confirm --> account/api.(*handler).confirmPasswordReset-fm (6 handlers)
|
||||
[GIN-debug] GET /api/auth/subscriptions --> account/api.(*handler).listSubscriptions-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/subscriptions --> account/api.(*handler).upsertSubscription-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/subscriptions/cancel --> account/api.(*handler).cancelSubscription-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/config/sync --> account/api.(*handler).syncConfig-fm (6 handlers)
|
||||
[GIN-debug] GET /api/auth/admin/settings --> account/api.(*handler).getAdminSettings-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/admin/settings --> account/api.(*handler).updateAdminSettings-fm (6 handlers)
|
||||
[GIN-debug] GET /api/auth/admin/users/metrics --> account/api.(*handler).adminUsersMetrics-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/admin/users --> account/api.(*handler).createCustomUser-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/admin/users/:userId/role --> account/api.(*handler).updateUserRole-fm (6 handlers)
|
||||
[GIN-debug] DELETE /api/auth/admin/users/:userId/role --> account/api.(*handler).resetUserRole-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/admin/users/:userId/pause --> account/api.(*handler).pauseUser-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/admin/users/:userId/resume --> account/api.(*handler).resumeUser-fm (6 handlers)
|
||||
[GIN-debug] DELETE /api/auth/admin/users/:userId --> account/api.(*handler).deleteUser-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/admin/users/:userId/renew-uuid --> account/api.(*handler).renewProxyUUID-fm (6 handlers)
|
||||
[GIN-debug] GET /api/auth/admin/blacklist --> account/api.(*handler).listBlacklist-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/admin/blacklist --> account/api.(*handler).addToBlacklist-fm (6 handlers)
|
||||
[GIN-debug] DELETE /api/auth/admin/blacklist/:email --> account/api.(*handler).removeFromBlacklist-fm (6 handlers)
|
||||
[GIN-debug] GET /api/auth/admin/sandbox/binding --> account/api.(*handler).getSandboxBinding-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/admin/sandbox/bind --> account/api.(*handler).bindSandboxNode-fm (6 handlers)
|
||||
[GIN-debug] GET /api/auth/sandbox/binding --> account/api.(*handler).getSandboxBindingPublic-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/admin/assume --> account/api.(*handler).adminAssume-fm (6 handlers)
|
||||
[GIN-debug] POST /api/auth/admin/assume/revert --> account/api.(*handler).adminAssumeRevert-fm (6 handlers)
|
||||
[GIN-debug] GET /api/auth/admin/assume/status --> account/api.(*handler).adminAssumeStatus-fm (6 handlers)
|
||||
[GIN-debug] GET /api/auth/users --> account/api.(*handler).listUsers-fm (6 handlers)
|
||||
[GIN-debug] GET /api/internal/public-overview --> account/api.(*handler).internalPublicOverview-fm (5 handlers)
|
||||
[GIN-debug] GET /api/admin/users/metrics --> account/api.(*handler).adminUsersMetrics-fm (6 handlers)
|
||||
[GIN-debug] GET /api/admin/agents/status --> account/api.(*handler).adminAgentStatus-fm (6 handlers)
|
||||
[GIN-debug] POST /api/admin/users --> account/api.(*handler).createCustomUser-fm (6 handlers)
|
||||
[GIN-debug] POST /api/admin/users/:userId/pause --> account/api.(*handler).pauseUser-fm (6 handlers)
|
||||
[GIN-debug] POST /api/admin/users/:userId/resume --> account/api.(*handler).resumeUser-fm (6 handlers)
|
||||
[GIN-debug] DELETE /api/admin/users/:userId --> account/api.(*handler).deleteUser-fm (6 handlers)
|
||||
[GIN-debug] POST /api/admin/users/:userId/renew-uuid --> account/api.(*handler).renewProxyUUID-fm (6 handlers)
|
||||
[GIN-debug] GET /api/admin/blacklist --> account/api.(*handler).listBlacklist-fm (6 handlers)
|
||||
[GIN-debug] POST /api/admin/blacklist --> account/api.(*handler).addToBlacklist-fm (6 handlers)
|
||||
[GIN-debug] DELETE /api/admin/blacklist/:email --> account/api.(*handler).removeFromBlacklist-fm (6 handlers)
|
||||
[GIN-debug] GET /api/admin/sandbox/binding --> account/api.(*handler).getSandboxBinding-fm (6 handlers)
|
||||
[GIN-debug] POST /api/admin/sandbox/bind --> account/api.(*handler).bindSandboxNode-fm (6 handlers)
|
||||
[GIN-debug] GET /api/agent-server/v1/nodes --> account/api.(*handler).listAgentNodes-fm (4 handlers)
|
||||
[GIN-debug] GET /api/agent-server/v1/users --> account/api.(*handler).listAgentUsers-fm (4 handlers)
|
||||
[GIN-debug] POST /api/agent-server/v1/status --> account/api.(*handler).reportAgentStatus-fm (4 handlers)
|
||||
[GIN-debug] GET /api/agent/nodes --> account/api.(*handler).listAgentNodes-fm (4 handlers)
|
||||
time=2026-02-07T02:23:48.082+08:00 level=INFO msg="starting account service" addr=127.0.0.1:8080 tls=false
|
||||
19
api/api.go
19
api/api.go
@ -280,6 +280,11 @@ func RegisterRoutes(r *gin.Engine, opts ...Option) {
|
||||
|
||||
authGroup.GET("/mfa/status", h.mfaStatus)
|
||||
|
||||
// Sandbox binding read endpoint.
|
||||
// Used by the Console Guest/Demo experience. Must be readable either via a
|
||||
// normal user session or via the internal service token.
|
||||
authGroup.GET("/sandbox/binding", h.getSandboxBindingPublic)
|
||||
|
||||
// Protected routes requiring authentication
|
||||
authProtected := authGroup.Group("")
|
||||
if h.tokenService != nil {
|
||||
@ -323,9 +328,6 @@ func RegisterRoutes(r *gin.Engine, opts ...Option) {
|
||||
authProtected.GET("/admin/sandbox/binding", h.getSandboxBinding)
|
||||
authProtected.POST("/admin/sandbox/bind", h.bindSandboxNode)
|
||||
|
||||
// Public read of sandbox binding for demo/sandbox user experience.
|
||||
authProtected.GET("/sandbox/binding", h.getSandboxBindingPublic)
|
||||
|
||||
// Root-only identity switch to sandbox@svc.plus (hard-coded allowlist).
|
||||
authProtected.POST("/admin/assume", h.adminAssume)
|
||||
authProtected.POST("/admin/assume/revert", h.adminAssumeRevert)
|
||||
@ -337,6 +339,7 @@ func RegisterRoutes(r *gin.Engine, opts ...Option) {
|
||||
internalGroup := r.Group("/api/internal")
|
||||
internalGroup.Use(auth.InternalAuthMiddleware())
|
||||
internalGroup.GET("/public-overview", h.internalPublicOverview)
|
||||
// internalGroup.GET("/sandbox/guest", h.internalSandboxGuest)
|
||||
|
||||
// Public /api routes for admin/management (expected by frontend at /api/admin/...)
|
||||
apiGroup := r.Group("/api")
|
||||
@ -2735,13 +2738,9 @@ func (h *handler) isReadOnlyAccount(user *store.User) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Default policy: Open default 演示模式 (demo mode) as read-only group.
|
||||
// If the user is NOT an admin/operator, default to read-only for safety in this specific "open demo" context.
|
||||
// Modify this logic if you want standard "User" role to be write-capable.
|
||||
// For now, based on request "开放默认的 演示模式", we assume standard users might be treated as demo visitors.
|
||||
// However, usually RoleUser should be writable. The prompt says "Only admin@svc.plus can modify config".
|
||||
// This implies EVERYONE else is read-only.
|
||||
return true
|
||||
// Default policy: Allow modification for regular users.
|
||||
// We only restrict explicitly flagged "demo" or "sandbox" identities or users assigned to a specific "ReadOnly Role".
|
||||
return false
|
||||
}
|
||||
|
||||
func isRootUser(user *store.User) bool {
|
||||
|
||||
26
api/internal_service_token.go
Normal file
26
api/internal_service_token.go
Normal file
@ -0,0 +1,26 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const internalServiceTokenHeader = "X-Service-Token"
|
||||
|
||||
func isInternalServiceRequest(c *gin.Context) bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
token := strings.TrimSpace(c.GetHeader(internalServiceTokenHeader))
|
||||
if token == "" {
|
||||
return false
|
||||
}
|
||||
expected := strings.TrimSpace(os.Getenv("INTERNAL_SERVICE_TOKEN"))
|
||||
if expected == "" {
|
||||
return false
|
||||
}
|
||||
return token == expected
|
||||
}
|
||||
|
||||
@ -14,8 +14,14 @@ import (
|
||||
// It is intentionally readable by any authenticated user so demo/sandbox users
|
||||
// do not depend on localStorage browser state.
|
||||
func (h *handler) getSandboxBindingPublic(c *gin.Context) {
|
||||
if _, ok := h.requireAuthenticatedUser(c); !ok {
|
||||
return
|
||||
// This endpoint is used by the Console Guest/Demo flow.
|
||||
// Allow it when either:
|
||||
// - the caller is an authenticated user (normal case), or
|
||||
// - the caller is the trusted Console BFF (internal service token).
|
||||
if !isInternalServiceRequest(c) {
|
||||
if _, ok := h.requireAuthenticatedUser(c); !ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if h.db == nil {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user