Add command sending endpoint and simpliy logic for authing requests

This commit is contained in:
Dane Everitt 2019-09-05 21:08:03 -07:00
parent 9a4f1672f7
commit 923a9fbcab
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53

71
http.go
View File

@ -40,6 +40,10 @@ type Router struct {
token string
}
func (rt *Router) AuthenticateRequest(h httprouter.Handle) httprouter.Handle {
return rt.AuthenticateToken(rt.AuthenticateServer(h))
}
// Middleware to protect server specific routes. This will ensure that the server exists and
// is in a state that allows it to be exposed to the API.
func (rt *Router) AuthenticateServer(h httprouter.Handle) httprouter.Handle {
@ -56,10 +60,8 @@ func (rt *Router) AuthenticateServer(h httprouter.Handle) httprouter.Handle {
// Authenticates the request token aganist the given permission string, ensuring that
// if it is a server permission, the token has control over that server. If it is a global
// token, this will ensure that the request is using a properly signed global token.
func (rt *Router) AuthenticateToken(permission string, h httprouter.Handle) httprouter.Handle {
func (rt *Router) AuthenticateToken(h httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
t := strings.Split(permission, ":")[0]
// Adds support for using this middleware on the websocket routes for servers. Those
// routes don't support Authorization headers, per the spec, so we abuse the socket
// protocol header and use that to pass the authorization token along to Wings without
@ -75,25 +77,13 @@ func (rt *Router) AuthenticateToken(permission string, h httprouter.Handle) http
return
}
if t != "i" && t != "s" {
zap.S().Warnw("could not match a permission string", zap.String("permission", permission), zap.String("route", r.URL.String()))
// If for whatever reason we didn't match a permission string just
// return a 404. This should only ever happen because of developer error.
http.NotFound(w, r)
return
}
// Try to match the request aganist the global token for the Daemon, regardless
// of the permission type. If nothing is matched we will fall through to the Panel
// API to try and validate permissions for a server.
if t == "s" || t == "i" {
if auth[1] == rt.token {
h(w, r, ps)
return
}
}
// Happens because we don't have any of the server handling code here.
http.Error(w, "not implemented", http.StatusNotImplemented)
@ -378,6 +368,32 @@ func (rt *Router) routeServerDeleteFile(w http.ResponseWriter, r *http.Request,
w.WriteHeader(http.StatusNoContent)
}
func (rt *Router) routeServerSendCommand(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
s := rt.Servers.Get(ps.ByName("server"))
defer r.Body.Close()
if running, err := s.Environment.IsRunning(); !running || err != nil {
http.Error(w, "cannot send commands to a stopped instance", http.StatusBadGateway)
return
}
data := rt.ReaderToBytes(r.Body)
commands, dt, _, _ := jsonparser.Get(data, "commands")
if dt != jsonparser.Array {
http.Error(w, "commands must be an array of strings", http.StatusUnprocessableEntity)
return
}
for _, command := range commands {
if err := s.Environment.SendCommand(string(command)); err != nil {
zap.S().Warnw("failed to send command to server", zap.Any("command", command), zap.Error(err))
return
}
}
w.WriteHeader(http.StatusNoContent)
}
func (rt *Router) ReaderToBytes(r io.Reader) []byte {
buf := bytes.Buffer{}
buf.ReadFrom(r)
@ -390,18 +406,19 @@ func (rt *Router) ConfigureRouter() *httprouter.Router {
router := httprouter.New()
router.GET("/", rt.routeIndex)
router.GET("/api/servers", rt.AuthenticateToken("i:servers", rt.routeAllServers))
router.GET("/api/servers/:server", rt.AuthenticateToken("s:view", rt.AuthenticateServer(rt.routeServer)))
router.GET("/api/servers/:server/logs", rt.AuthenticateToken("s:logs", rt.AuthenticateServer(rt.routeServerLogs)))
router.GET("/api/servers/:server/files/contents", rt.AuthenticateToken("s:files", rt.AuthenticateServer(rt.routeServerFileRead)))
router.GET("/api/servers/:server/files/list-directory", rt.AuthenticateToken("s:files", rt.AuthenticateServer(rt.routeServerListDirectory)))
router.PUT("/api/servers/:server/files/rename", rt.AuthenticateToken("s:files", rt.AuthenticateServer(rt.routeServerRenameFile)))
router.POST("/api/servers/:server/files/copy", rt.AuthenticateToken("s:files", rt.AuthenticateServer(rt.routeServerCopyFile)))
router.POST("/api/servers/:server/files/write", rt.AuthenticateToken("s:files", rt.AuthenticateServer(rt.routeServerWriteFile)))
router.POST("/api/servers/:server/files/create-directory", rt.AuthenticateToken("s:files", rt.AuthenticateServer(rt.routeServerCreateDirectory)))
router.POST("/api/servers/:server/files/delete", rt.AuthenticateToken("s:files", rt.AuthenticateServer(rt.routeServerDeleteFile)))
router.POST("/api/servers/:server/power", rt.AuthenticateToken("s:power", rt.AuthenticateServer(rt.routeServerPower)))
router.GET("/api/servers", rt.AuthenticateToken(rt.routeAllServers))
router.GET("/api/servers/:server", rt.AuthenticateRequest(rt.routeServer))
router.GET("/api/servers/:server/ws", rt.AuthenticateRequest(rt.routeWebsocket))
router.GET("/api/servers/:server/logs", rt.AuthenticateRequest(rt.routeServerLogs))
router.GET("/api/servers/:server/files/contents", rt.AuthenticateRequest(rt.routeServerFileRead))
router.GET("/api/servers/:server/files/list-directory", rt.AuthenticateRequest(rt.routeServerListDirectory))
router.PUT("/api/servers/:server/files/rename", rt.AuthenticateRequest(rt.routeServerRenameFile))
router.POST("/api/servers/:server/files/copy", rt.AuthenticateRequest(rt.routeServerCopyFile))
router.POST("/api/servers/:server/files/write", rt.AuthenticateRequest(rt.routeServerWriteFile))
router.POST("/api/servers/:server/files/create-directory", rt.AuthenticateRequest(rt.routeServerCreateDirectory))
router.POST("/api/servers/:server/files/delete", rt.AuthenticateRequest(rt.routeServerDeleteFile))
router.POST("/api/servers/:server/power", rt.AuthenticateRequest(rt.routeServerPower))
router.POST("/api/servers/:server/commands", rt.AuthenticateRequest(rt.routeServerSendCommand))
router.GET("/api/servers/:server/ws", rt.AuthenticateToken("s:websocket", rt.AuthenticateServer(rt.routeWebsocket)))
return router
}