Add ReadOnly option for HTTPHandler

This commit is contained in:
Ken Hibino 2022-02-26 11:47:29 -08:00
parent ade2baceaf
commit c04e63d3f7
2 changed files with 25 additions and 0 deletions

View File

@ -40,6 +40,9 @@ type Options struct {
// This field is optional. If this field is set, asynqmon will query the Prometheus server // This field is optional. If this field is set, asynqmon will query the Prometheus server
// to get the time series data about queue metrics and show them in the web UI. // to get the time series data about queue metrics and show them in the web UI.
PrometheusAddress string PrometheusAddress string
// Set ReadOnly to true to restrict user to view-only mode.
ReadOnly bool
} }
// HTTPHandler is a http.Handler for asynqmon application. // HTTPHandler is a http.Handler for asynqmon application.
@ -111,6 +114,7 @@ func muxRouter(opts Options, rc redis.UniversalClient, inspector *asynq.Inspecto
} }
api := router.PathPrefix("/api").Subrouter() api := router.PathPrefix("/api").Subrouter()
// Queue endpoints. // Queue endpoints.
api.HandleFunc("/queues", newListQueuesHandlerFunc(inspector)).Methods("GET") api.HandleFunc("/queues", newListQueuesHandlerFunc(inspector)).Methods("GET")
api.HandleFunc("/queues/{qname}", newGetQueueHandlerFunc(inspector)).Methods("GET") api.HandleFunc("/queues/{qname}", newGetQueueHandlerFunc(inspector)).Methods("GET")
@ -190,6 +194,11 @@ func muxRouter(opts Options, rc redis.UniversalClient, inspector *asynq.Inspecto
// Time series metrics endpoints. // Time series metrics endpoints.
api.HandleFunc("/metrics", newGetMetricsHandlerFunc(http.DefaultClient, opts.PrometheusAddress)).Methods("GET") api.HandleFunc("/metrics", newGetMetricsHandlerFunc(http.DefaultClient, opts.PrometheusAddress)).Methods("GET")
// Restrict APIs when running in read-only mode.
if opts.ReadOnly {
api.Use(restrictToReadOnly)
}
// Everything else, route to uiAssetsHandler. // Everything else, route to uiAssetsHandler.
router.NotFoundHandler = &uiAssetsHandler{ router.NotFoundHandler = &uiAssetsHandler{
rootPath: opts.RootPath, rootPath: opts.RootPath,
@ -197,6 +206,19 @@ func muxRouter(opts Options, rc redis.UniversalClient, inspector *asynq.Inspecto
staticDirPath: "ui/build", staticDirPath: "ui/build",
indexFileName: "index.html", indexFileName: "index.html",
prometheusAddr: opts.PrometheusAddress, prometheusAddr: opts.PrometheusAddress,
readOnly: opts.ReadOnly,
} }
return router return router
} }
// restrictToReadOnly is a middleware function to restrict users to perform only GET requests.
func restrictToReadOnly(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" && r.Method != "" {
http.Error(w, fmt.Sprintf("API Server is running in read-only mode: %s request is not allowed", r.Method), http.StatusMethodNotAllowed)
return
}
h.ServeHTTP(w, r)
})
}

View File

@ -20,6 +20,7 @@ type uiAssetsHandler struct {
staticDirPath string staticDirPath string
indexFileName string indexFileName string
prometheusAddr string prometheusAddr string
readOnly bool
} }
// ServeHTTP inspects the URL path to locate a file within the static dir // ServeHTTP inspects the URL path to locate a file within the static dir
@ -62,9 +63,11 @@ func (h *uiAssetsHandler) renderIndexFile(w http.ResponseWriter) error {
data := struct { data := struct {
RootPath string RootPath string
PrometheusAddr string PrometheusAddr string
ReadOnly bool
}{ }{
RootPath: h.rootPath, RootPath: h.rootPath,
PrometheusAddr: h.prometheusAddr, PrometheusAddr: h.prometheusAddr,
ReadOnly: h.readOnly,
} }
return tmpl.Execute(w, data) return tmpl.Execute(w, data)
} }