Implement CORS for the administrative interface.

This commit is contained in:
Juliusz Chroboczek
2025-06-13 15:00:16 +02:00
parent 7204889085
commit a6410b5a68
4 changed files with 54 additions and 20 deletions
+3
View File
@@ -112,6 +112,9 @@ The fields are as follows:
- `allowOrigin` is an array that contains the list of HTTP origins that - `allowOrigin` is an array that contains the list of HTTP origins that
are allowed to access the server. are allowed to access the server.
- `allowAdminOrigin` is like `allowOrigin`, but applies to the
administrative interface.
- `proxyURL`: if running behind a reverse proxy, this specifies the root - `proxyURL`: if running behind a reverse proxy, this specifies the root
URL that will be visible outside the proxy. URL that will be visible outside the proxy.
+47 -9
View File
@@ -104,6 +104,19 @@ func getJSON(w http.ResponseWriter, r *http.Request, v any) bool {
return false return false
} }
func apiCORS(w http.ResponseWriter, r *http.Request, methods string) bool {
CheckOrigin(w, r, true)
if r.Method == "OPTIONS" {
w.Header().Set("Access-Control-Allow-Methods",
"OPTIONS, "+methods)
w.Header().Set("Access-Control-Allow-Headers",
"Authorization, Content-Type",
)
return true
}
return false
}
func apiHandler(w http.ResponseWriter, r *http.Request) { func apiHandler(w http.ResponseWriter, r *http.Request) {
if !strings.HasPrefix(r.URL.Path, "/galene-api/") { if !strings.HasPrefix(r.URL.Path, "/galene-api/") {
http.NotFound(w, r) http.NotFound(w, r)
@@ -112,11 +125,14 @@ func apiHandler(w http.ResponseWriter, r *http.Request) {
first, kind, rest := splitPath(r.URL.Path[len("/galene-api"):]) first, kind, rest := splitPath(r.URL.Path[len("/galene-api"):])
if first == "/v0" && kind == ".stats" && rest == "" { if first == "/v0" && kind == ".stats" && rest == "" {
if apiCORS(w, r, "HEAD, GET") {
return
}
if !checkAdmin(w, r) { if !checkAdmin(w, r) {
return return
} }
if r.Method != "HEAD" && r.Method != "GET" { if r.Method != "HEAD" && r.Method != "GET" {
methodNotAllowed(w, "HEAD", "GET") methodNotAllowed(w, "HEAD, GET")
return return
} }
w.Header().Set("cache-control", "no-cache") w.Header().Set("cache-control", "no-cache")
@@ -139,11 +155,14 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request, pth string) {
} }
g := first[1:] g := first[1:]
if g == "" && kind == "" { if g == "" && kind == "" {
if apiCORS(w, r, "HEAD, GET") {
return
}
if !checkAdmin(w, r) { if !checkAdmin(w, r) {
return return
} }
if r.Method != "HEAD" && r.Method != "GET" { if r.Method != "HEAD" && r.Method != "GET" {
methodNotAllowed(w, "HEAD", "GET") methodNotAllowed(w, "HEAD, GET")
return return
} }
groups, err := group.GetDescriptionNames() groups, err := group.GetDescriptionNames()
@@ -178,6 +197,9 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request, pth string) {
return return
} }
if apiCORS(w, r, "HEAD, GET, PUT, DELETE") {
return
}
if !checkAdmin(w, r) { if !checkAdmin(w, r) {
return return
} }
@@ -248,7 +270,7 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request, pth string) {
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
return return
} }
methodNotAllowed(w, "HEAD", "GET", "PUT", "DELETE") methodNotAllowed(w, "HEAD, GET, PUT, DELETE")
return return
} }
@@ -258,11 +280,14 @@ func usersHandler(w http.ResponseWriter, r *http.Request, g, pth string) {
return return
} }
if pth == "/" { if pth == "/" {
if apiCORS(w, r, "HEAD, GET") {
return
}
if !checkAdmin(w, r) { if !checkAdmin(w, r) {
return return
} }
if r.Method != "HEAD" && r.Method != "GET" { if r.Method != "HEAD" && r.Method != "GET" {
methodNotAllowed(w, "HEAD", "GET") methodNotAllowed(w, "HEAD, GET")
return return
} }
users, etag, err := group.GetUsers(g) users, etag, err := group.GetUsers(g)
@@ -310,6 +335,9 @@ func specialUserHandler(w http.ResponseWriter, r *http.Request, g, pth string, w
} }
func userHandler(w http.ResponseWriter, r *http.Request, g, user string, wildcard bool) { func userHandler(w http.ResponseWriter, r *http.Request, g, user string, wildcard bool) {
if apiCORS(w, r, "HEAD, GET, PUT, DELETE") {
return
}
if !checkAdmin(w, r) { if !checkAdmin(w, r) {
return return
} }
@@ -378,11 +406,14 @@ func userHandler(w http.ResponseWriter, r *http.Request, g, user string, wildcar
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
return return
} }
methodNotAllowed(w, "HEAD", "GET", "PUT", "DELETE") methodNotAllowed(w, "HEAD, GET, PUT, DELETE")
return return
} }
func passwordHandler(w http.ResponseWriter, r *http.Request, g, user string, wildcard bool) { func passwordHandler(w http.ResponseWriter, r *http.Request, g, user string, wildcard bool) {
if apiCORS(w, r, "PUT, POST, DELETE") {
return
}
if !checkPasswordAdmin(w, r, g, user, wildcard) { if !checkPasswordAdmin(w, r, g, user, wildcard) {
return return
} }
@@ -433,7 +464,7 @@ func passwordHandler(w http.ResponseWriter, r *http.Request, g, user string, wil
return return
} }
methodNotAllowed(w, "PUT", "POST", "DELETE") methodNotAllowed(w, "PUT, POST, DELETE")
return return
} }
@@ -442,6 +473,9 @@ type jwkset = struct {
} }
func keysHandler(w http.ResponseWriter, r *http.Request, g string) { func keysHandler(w http.ResponseWriter, r *http.Request, g string) {
if apiCORS(w, r, "HEAD, GET") {
return
}
if !checkAdmin(w, r) { if !checkAdmin(w, r) {
return return
} }
@@ -481,7 +515,7 @@ func keysHandler(w http.ResponseWriter, r *http.Request, g string) {
return return
} }
methodNotAllowed(w, "PUT", "DELETE") methodNotAllowed(w, "PUT, DELETE")
return return
} }
@@ -490,9 +524,13 @@ func tokensHandler(w http.ResponseWriter, r *http.Request, g, pth string) {
http.NotFound(w, r) http.NotFound(w, r)
return return
} }
if apiCORS(w, r, "HEAD, GET, POST, PUT, DELETE") {
return
}
if !checkAdmin(w, r) { if !checkAdmin(w, r) {
return return
} }
// check that the group exists // check that the group exists
_, err := group.GetDescription(g) _, err := group.GetDescription(g)
if err != nil { if err != nil {
@@ -541,7 +579,7 @@ func tokensHandler(w http.ResponseWriter, r *http.Request, g, pth string) {
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
return return
} }
methodNotAllowed(w, "HEAD", "GET", "POST") methodNotAllowed(w, "HEAD, GET, POST")
return return
} }
@@ -637,6 +675,6 @@ func tokensHandler(w http.ResponseWriter, r *http.Request, g, pth string) {
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
return return
} }
methodNotAllowed(w, "HEAD", "GET", "PUT", "DELETE") methodNotAllowed(w, "HEAD, GET, PUT, DELETE")
return return
} }
+2 -9
View File
@@ -146,15 +146,8 @@ func httpError(w http.ResponseWriter, err error) {
http.StatusInternalServerError) http.StatusInternalServerError)
} }
func methodNotAllowed(w http.ResponseWriter, methods ...string) { func methodNotAllowed(w http.ResponseWriter, methods string) {
ms := "" w.Header().Set("Allow", "OPTIONS, " + methods)
for _, m := range methods {
if ms != "" {
ms = ms + ", "
}
ms = ms + m
}
w.Header().Set("Allow", ms)
http.Error(w, "method not allowed", http.StatusMethodNotAllowed) http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
} }
+2 -2
View File
@@ -178,7 +178,7 @@ func whipEndpointHandler(w http.ResponseWriter, r *http.Request) {
} }
if r.Method != "POST" { if r.Method != "POST" {
methodNotAllowed(w, "OPTIONS", "POST") methodNotAllowed(w, "POST")
return return
} }
@@ -323,7 +323,7 @@ func whipResourceHandler(w http.ResponseWriter, r *http.Request) {
} }
if r.Method != "PATCH" { if r.Method != "PATCH" {
methodNotAllowed(w, "OPTIONS", "DELETE", "PATCH") methodNotAllowed(w, "DELETE, PATCH")
return return
} }