diff --git a/README b/README index a2c66c7..7bea4f4 100644 --- a/README +++ b/README @@ -109,9 +109,8 @@ The fields are as follows: - `writableGroups`: if true, then the API can modify group description files; by default, group files are treated as read-only; - - `publicServer`: if true, then cross-origin access to the server is - allowed. This makes the server vulnerable to cross-origin scripting - attacks, but is necessary in some cases. + - `allowOrigin` is an array that contains the list of HTTP origins that + are allowed to access the server. - `proxyURL`: if running behind a reverse proxy, this specifies the root URL that will be visible outside the proxy. diff --git a/group/group.go b/group/group.go index 43d71fc..4a87046 100644 --- a/group/group.go +++ b/group/group.go @@ -916,11 +916,12 @@ type Configuration struct { modTime time.Time `json:"-"` fileSize int64 `json:"-"` - PublicServer bool `json:"publicServer"` - CanonicalHost string `json:"canonicalHost"` - ProxyURL string `json:"proxyURL"` - WritableGroups bool `json:"writableGroups"` - Users map[string]UserDescription + CanonicalHost string `json:"canonicalHost"` + AllowOrigin []string `json:"allowOrigin"` + AllowAdminOrigin []string `json:"allowAdminOrigin"` + ProxyURL string `json:"proxyURL"` + WritableGroups bool `json:"writableGroups"` + Users map[string]UserDescription // obsolete fields Admin []ClientPattern `json:"admin"` diff --git a/webserver/webserver.go b/webserver/webserver.go index 7dff183..267c315 100644 --- a/webserver/webserver.go +++ b/webserver/webserver.go @@ -502,29 +502,58 @@ func failAuthentication(w http.ResponseWriter, realm string) { http.Error(w, "Haha!", http.StatusUnauthorized) } -var wsUpgrader = websocket.Upgrader{ - HandshakeTimeout: 30 * time.Second, +func CheckOrigin(w http.ResponseWriter, r *http.Request, admin bool) bool { + if w != nil { + w.Header().Add("Vary", "Origin") + } + + origins := r.Header["Origin"] + if len(origins) == 0 { + return true + } + origin := origins[0] + + ok := false + o, err := url.Parse(origin) + if err == nil && strings.EqualFold(o.Host, r.Host) { + ok = true + } else { + conf, err := group.GetConfiguration() + if err != nil { + return false + } + + allow := conf.AllowOrigin + if admin { + allow = conf.AllowAdminOrigin + } + for _, a := range allow { + if strings.EqualFold(origin, a) { + ok = true + break + } + } + } + + if !ok { + return false + } + + if w != nil { + w.Header().Add("Access-Control-Allow-Origin", origin) + } + return true } -var wsPublicUpgrader = websocket.Upgrader{ +var wsUpgrader = websocket.Upgrader{ HandshakeTimeout: 30 * time.Second, CheckOrigin: func(r *http.Request) bool { - return true + return CheckOrigin(nil, r, false) }, } func wsHandler(w http.ResponseWriter, r *http.Request) { - conf, err := group.GetConfiguration() - if err != nil { - httpError(w, err) - return - } - upgrader := wsUpgrader - if conf.PublicServer { - upgrader = wsPublicUpgrader - } - - conn, err := upgrader.Upgrade(w, r, nil) + conn, err := wsUpgrader.Upgrade(w, r, nil) if err != nil { log.Printf("Websocket upgrade: %v", err) return diff --git a/webserver/whip.go b/webserver/whip.go index a1bdd8b..bbc3dab 100644 --- a/webserver/whip.go +++ b/webserver/whip.go @@ -165,15 +165,7 @@ func whipEndpointHandler(w http.ResponseWriter, r *http.Request) { return } - conf, err := group.GetConfiguration() - if err != nil { - httpError(w, err) - return - } - - if conf.PublicServer { - w.Header().Set("Access-Control-Allow-Origin", "*") - } + CheckOrigin(w, r, false) if r.Method == "OPTIONS" { w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, POST") @@ -309,15 +301,7 @@ func whipResourceHandler(w http.ResponseWriter, r *http.Request) { } } - conf, err := group.GetConfiguration() - if err != nil { - httpError(w, err) - return - } - - if conf.PublicServer { - w.Header().Set("Access-Control-Allow-Origin", "*") - } + CheckOrigin(w, r, false) if r.Method == "OPTIONS" { w.Header().Set("Access-Control-Allow-Methods",