re-refactor code

This commit is contained in:
Dane Everitt
2021-01-25 20:28:24 -08:00
parent ab86fb703a
commit f3a6ee7a45
18 changed files with 390 additions and 556 deletions

View File

@@ -1,138 +1,16 @@
package router
import (
"io"
"net/http"
"strings"
"emperror.dev/errors"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/router/middleware"
"github.com/pterodactyl/wings/server"
)
type Middleware struct {
serverManager server.Manager
}
// A custom handler function allowing for errors bubbled up by c.Error() to be returned in a
// standardized format with tracking UUIDs on them for easier log searching.
func (m *Middleware) ErrorHandler() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
err := c.Errors.Last()
if err == nil || err.Err == nil {
return
}
tracked := NewTrackedError(err.Err)
// If there is a server in the context for this request pull it out so that we can
// track the error specifically for that server.
if s, ok := c.Get("server"); ok {
tracked = NewServerError(err.Err, s.(*server.Server))
}
// This error occurs if you submit invalid JSON data to an endpoint.
if err.Err.Error() == io.EOF.Error() {
c.JSON(c.Writer.Status(), gin.H{"error": "A JSON formatted body is required for this endpoint."})
return
}
tracked.Abort(c)
return
}
}
// Set the access request control headers on all of the requests.
func (m *Middleware) SetAccessControlHeaders() gin.HandlerFunc {
origins := config.Get().AllowedOrigins
location := config.Get().PanelLocation
return func(c *gin.Context) {
c.Header("Access-Control-Allow-Credentials", "true")
c.Header("Access-Control-Allow-Methods", "GET, POST, PATCH, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Accept, Accept-Encoding, Authorization, Cache-Control, Content-Type, Content-Length, Origin, X-Real-IP, X-CSRF-Token")
o := c.GetHeader("Origin")
if o != location {
for _, origin := range origins {
if origin != "*" && o != origin {
continue
}
c.Header("Access-Control-Allow-Origin", origin)
c.Next()
return
}
}
c.Header("Access-Control-Allow-Origin", location)
c.Next()
}
}
// Authenticates the request token against 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 (m *Middleware) RequireAuthorization() gin.HandlerFunc {
token := config.Get().AuthenticationToken
return func(c *gin.Context) {
auth := strings.SplitN(c.GetHeader("Authorization"), " ", 2)
if len(auth) != 2 || auth[0] != "Bearer" {
c.Header("WWW-Authenticate", "Bearer")
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "The required authorization heads were not present in the request.",
})
return
}
// All requests to Wings must be authorized with the authentication token present in
// the Wings configuration file. Remeber, all requests to Wings come from the Panel
// backend, or using a signed JWT for temporary authentication.
if auth[1] == token {
c.Next()
return
}
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"error": "You are not authorized to access this endpoint.",
})
}
}
func (m *Middleware) WithServerManager() gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("servermanager", m.serverManager)
}
}
func ExtractServerManager(c *gin.Context) server.Manager {
if s, ok := c.Get("servermanager"); ok {
if srvs, ok := s.(server.Manager); ok {
return srvs
}
}
return nil
}
// Ensure that the requested server exists in this setup. Returns a 404 if we cannot
// locate it.
func (m *Middleware) ServerExists() gin.HandlerFunc {
return func(c *gin.Context) {
u, err := uuid.Parse(c.Param("server"))
if err == nil {
if s := m.serverManager.Get(u.String()); s != nil {
c.Set("server", s)
c.Next()
return
}
}
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
"error": "The resource you requested does not exist.",
})
}
}
// Returns the server instance from the gin context. If there is no server set in the
// context (e.g. calling from a controller not protected by ServerExists) this function
// will panic.
// ExtractServer returns the server instance from the gin context. If there is
// no server set in the context (e.g. calling from a controller not protected
// by ServerExists) this function will panic.
//
// This function is deprecated. Use middleware.ExtractServer.
func ExtractServer(c *gin.Context) *server.Server {
if s, ok := c.Get("server"); ok {
return s.(*server.Server)
}
panic(errors.New("cannot extract server, missing on gin context"))
return middleware.ExtractServer(c)
}

View File

@@ -159,6 +159,15 @@ func AttachRequestID() gin.HandlerFunc {
}
}
// AttachServerManager attaches the server manager to the request context which
// allows routes to access the underlying server collection.
func AttachServerManager(m *server.Manager) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("manager", m)
c.Next()
}
}
// CaptureAndAbort aborts the request and attaches the provided error to the gin
// context so it can be reported properly. If the error is missing a stacktrace
// at the time it is called the stack will be attached.
@@ -239,9 +248,13 @@ func SetAccessControlHeaders() gin.HandlerFunc {
// the server ID in the fields list.
func ServerExists() gin.HandlerFunc {
return func(c *gin.Context) {
s := server.GetServers().Find(func(s *server.Server) bool {
return c.Param("server") == s.Id()
})
var s *server.Server
if c.Param("server") != "" {
manager := ExtractManager(c)
s = manager.Find(func(s *server.Server) bool {
return c.Param("server") == s.Id()
})
}
if s == nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "The requested resource does not exist on this instance."})
return
@@ -313,3 +326,11 @@ func ExtractServer(c *gin.Context) *server.Server {
}
return v.(*server.Server)
}
// ExtractManager returns the server manager instance set on the request context.
func ExtractManager(c *gin.Context) *server.Manager {
if v, ok := c.Get("manager"); ok {
return v.(*server.Manager)
}
panic("middleware/middleware: cannot extract server manager: not present in context")
}

View File

@@ -4,21 +4,22 @@ import (
"github.com/apex/log"
"github.com/gin-gonic/gin"
"github.com/pterodactyl/wings/router/middleware"
"github.com/pterodactyl/wings/server"
)
// Configure configures the routing infrastructure for this daemon instance.
func Configure() *gin.Engine {
func Configure(m *server.Manager) *gin.Engine {
gin.SetMode("release")
router := gin.New()
router.Use(gin.Recovery())
router.Use(middleware.AttachRequestID(), middleware.CaptureErrors(), middleware.SetAccessControlHeaders())
router.Use(middleware.AttachServerManager(m))
// @todo log this into a different file so you can setup IP blocking for abusive requests and such.
// This should still dump requests in debug mode since it does help with understanding the request
// lifecycle and quickly seeing what was called leading to the logs. However, it isn't feasible to mix
// this output in production and still get meaningful logs from it since they'll likely just be a huge
// spamfest.
router.Use()
router.Use(gin.LoggerWithFormatter(func(params gin.LogFormatterParams) string {
log.WithFields(log.Fields{
"client_ip": params.ClientIP,

View File

@@ -8,13 +8,14 @@ import (
"strconv"
"github.com/gin-gonic/gin"
"github.com/pterodactyl/wings/router/middleware"
"github.com/pterodactyl/wings/router/tokens"
"github.com/pterodactyl/wings/server/backup"
)
// Handle a download request for a server backup.
func getDownloadBackup(c *gin.Context) {
serverManager := ExtractServerManager(c)
manager := middleware.ExtractManager(c)
token := tokens.BackupPayload{}
if err := tokens.ParseToken([]byte(c.Query("token")), &token); err != nil {
@@ -22,8 +23,8 @@ func getDownloadBackup(c *gin.Context) {
return
}
s := serverManager.Get(token.ServerUuid)
if s == nil || !token.IsUniqueRequest() {
s, ok := manager.Get(token.ServerUuid)
if !ok || !token.IsUniqueRequest() {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
"error": "The requested resource was not found on this server.",
})
@@ -59,16 +60,15 @@ func getDownloadBackup(c *gin.Context) {
// Handles downloading a specific file for a server.
func getDownloadFile(c *gin.Context) {
serverManager := ExtractServerManager(c)
manager := middleware.ExtractManager(c)
token := tokens.FilePayload{}
if err := tokens.ParseToken([]byte(c.Query("token")), &token); err != nil {
NewTrackedError(err).Abort(c)
return
}
s := serverManager.Get(token.ServerUuid)
if s == nil || !token.IsUniqueRequest() {
s, ok := manager.Get(token.ServerUuid)
if !ok || !token.IsUniqueRequest() {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
"error": "The requested resource was not found on this server.",
})

View File

@@ -11,6 +11,7 @@ import (
"github.com/apex/log"
"github.com/gin-gonic/gin"
"github.com/pterodactyl/wings/router/downloader"
"github.com/pterodactyl/wings/router/middleware"
"github.com/pterodactyl/wings/router/tokens"
"github.com/pterodactyl/wings/server"
)
@@ -190,8 +191,7 @@ func postServerReinstall(c *gin.Context) {
// Deletes a server from the wings daemon and dissociate it's objects.
func deleteServer(c *gin.Context) {
s := ExtractServer(c)
sm := ExtractServerManager(c)
s := middleware.ExtractServer(c)
// Immediately suspend the server to prevent a user from attempting
// to start it while this process is running.
@@ -235,7 +235,9 @@ func deleteServer(c *gin.Context) {
}
}(s.Filesystem().Path())
sm.Remove(s)
middleware.ExtractManager(c).Remove(func(server *server.Server) bool {
return server.Id() == s.Id()
})
// Deallocate the reference to this server.
s = nil

View File

@@ -487,7 +487,7 @@ func postServerChmodFile(c *gin.Context) {
}
func postServerUploadFiles(c *gin.Context) {
serverManager := ExtractServerManager(c)
manager := middleware.ExtractManager(c)
token := tokens.UploadPayload{}
if err := tokens.ParseToken([]byte(c.Query("token")), &token); err != nil {
@@ -495,8 +495,8 @@ func postServerUploadFiles(c *gin.Context) {
return
}
s := serverManager.Get(token.ServerUuid)
if s == nil || !token.IsUniqueRequest() {
s, ok := manager.Get(token.ServerUuid)
if !ok || !token.IsUniqueRequest() {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
"error": "The requested resource was not found on this server.",
})

View File

@@ -7,13 +7,14 @@ import (
"github.com/gin-gonic/gin"
ws "github.com/gorilla/websocket"
"github.com/pterodactyl/wings/router/middleware"
"github.com/pterodactyl/wings/router/websocket"
)
// Upgrades a connection to a websocket and passes events along between.
func getServerWebsocket(c *gin.Context) {
serverManager := ExtractServerManager(c)
s := serverManager.Get(c.Param("server"))
manager := middleware.ExtractManager(c)
s, _ := manager.Get(c.Param("server"))
handler, err := websocket.GetHandler(s, c.Writer, c.Request)
if err != nil {
NewServerError(err, s).Abort(c)

View File

@@ -9,6 +9,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/installer"
"github.com/pterodactyl/wings/router/middleware"
"github.com/pterodactyl/wings/system"
)
@@ -27,8 +28,7 @@ func getSystemInformation(c *gin.Context) {
// Returns all of the servers that are registered and configured correctly on
// this wings instance.
func getAllServers(c *gin.Context) {
serverManager := ExtractServerManager(c)
c.JSON(http.StatusOK, serverManager.GetAll())
c.JSON(http.StatusOK, middleware.ExtractManager(c).All())
}
// Creates a new server on the wings daemon and begins the installation process
@@ -52,8 +52,8 @@ func postCreateServer(c *gin.Context) {
// Plop that server instance onto the request so that it can be referenced in
// requests from here-on out.
serverManager := ExtractServerManager(c)
serverManager.Add(install.Server())
manager := middleware.ExtractManager(c)
manager.Add(install.Server())
// Begin the installation process in the background to not block the request
// cycle. If there are any errors they will be logged and communicated back

View File

@@ -25,6 +25,7 @@ import (
"github.com/pterodactyl/wings/api"
"github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/installer"
"github.com/pterodactyl/wings/router/middleware"
"github.com/pterodactyl/wings/router/tokens"
"github.com/pterodactyl/wings/server"
"github.com/pterodactyl/wings/system"
@@ -323,19 +324,20 @@ func postTransfer(c *gin.Context) {
i.Server().Events().Publish(server.TransferLogsEvent, output)
}
serverManager := ExtractServerManager(c)
manager := middleware.ExtractManager(c)
// Mark the server as transferring to prevent problems later on during the process and
// then push the server into the global server collection for this instance.
i.Server().SetTransferring(true)
serverManager.Add(i.Server())
manager.Add(i.Server())
defer func(s *server.Server) {
// In the event that this transfer call fails, remove the server from the global
// server tracking so that we don't have a dangling instance.
if err := data.sendTransferStatus(!hasError); hasError || err != nil {
sendTransferLog("Server transfer failed, check Wings logs for additional information.")
s.Events().Publish(server.TransferStatusEvent, "failure")
serverManager.Remove(s)
manager.Remove(func(match *server.Server) bool {
return match.Id() == s.Id()
})
// If the transfer status was successful but the request failed, act like the transfer failed.
if !hasError && err != nil {