Send errors back over the socket to users depending on permission
This commit is contained in:
		
							parent
							
								
									71d5b0fe83
								
							
						
					
					
						commit
						640f4b3a98
					
				
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -15,6 +15,7 @@ require (
 | 
			
		|||
	github.com/gabriel-vasile/mimetype v0.1.4
 | 
			
		||||
	github.com/gbrlsnchs/jwt/v3 v3.0.0-rc.0
 | 
			
		||||
	github.com/gogo/protobuf v1.2.1 // indirect
 | 
			
		||||
	github.com/google/uuid v1.1.1
 | 
			
		||||
	github.com/gorilla/websocket v1.4.0
 | 
			
		||||
	github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect
 | 
			
		||||
	github.com/julienschmidt/httprouter v1.2.0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -29,6 +29,8 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
 | 
			
		|||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
 | 
			
		||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
			
		||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
			
		||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 | 
			
		||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI=
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										57
									
								
								websocket.go
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								websocket.go
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -3,7 +3,9 @@ package main
 | 
			
		|||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/gbrlsnchs/jwt/v3"
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
	"github.com/julienschmidt/httprouter"
 | 
			
		||||
	"github.com/pterodactyl/wings/config"
 | 
			
		||||
| 
						 | 
				
			
			@ -19,9 +21,11 @@ import (
 | 
			
		|||
const (
 | 
			
		||||
	TokenExpiringEvent  = "token expiring"
 | 
			
		||||
	TokenExpiredEvent   = "token expired"
 | 
			
		||||
	AuthenticationEvent = "auth"
 | 
			
		||||
	SetStateEvent       = "set state"
 | 
			
		||||
	SendServerLogsEvent = "send logs"
 | 
			
		||||
	SendCommandEvent    = "send command"
 | 
			
		||||
	ErrorEvent          = "daemon error"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type WebsocketMessage struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -60,6 +64,7 @@ const (
 | 
			
		|||
	PermissionConnect       = "connect"
 | 
			
		||||
	PermissionSendCommand   = "send-command"
 | 
			
		||||
	PermissionSendPower     = "send-power"
 | 
			
		||||
	PermissionReceiveErrors = "receive-errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Checks if the given token payload has a permission string.
 | 
			
		||||
| 
						 | 
				
			
			@ -248,8 +253,7 @@ func (rt *Router) routeWebsocket(w http.ResponseWriter, r *http.Request, ps http
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		if err := handler.HandleInbound(j); err != nil {
 | 
			
		||||
			zap.S().Warnw("error handling inbound websocket request", zap.Error(err))
 | 
			
		||||
			break
 | 
			
		||||
			handler.SendErrorJson(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -264,6 +268,38 @@ func (wsh *WebsocketHandler) SendJson(v interface{}) error {
 | 
			
		|||
	return wsh.Connection.WriteJSON(v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sends an error back to the connected websocket instance by checking the permissions
 | 
			
		||||
// of the token. If the user has the "receive-errors" grant we will send back the actual
 | 
			
		||||
// error message, otherwise we just send back a standard error message.
 | 
			
		||||
func (wsh *WebsocketHandler) SendErrorJson(err error) error {
 | 
			
		||||
	wsh.Mutex.Lock()
 | 
			
		||||
	defer wsh.Mutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	message := "an unexpected error was encountered during the websocket lifecycle"
 | 
			
		||||
	if wsh.JWT != nil && wsh.JWT.HasPermission(PermissionReceiveErrors) {
 | 
			
		||||
		message = err.Error()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m, u := wsh.GetErrorMessage(message)
 | 
			
		||||
 | 
			
		||||
	wsm := WebsocketMessage{Event: ErrorEvent}
 | 
			
		||||
	wsm.Args = []string{m}
 | 
			
		||||
 | 
			
		||||
	zap.S().Warnw("an error was encountered in the websocket process", zap.String("error_identifier", u.String()), zap.Error(err))
 | 
			
		||||
 | 
			
		||||
	return wsh.Connection.WriteJSON(wsm)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Converts an error message into a more readable representation and returns a UUID
 | 
			
		||||
// that can be cross-referenced to find the specific error that triggered.
 | 
			
		||||
func (wsh *WebsocketHandler) GetErrorMessage(msg string) (string, uuid.UUID) {
 | 
			
		||||
	u, _ := uuid.NewRandom()
 | 
			
		||||
 | 
			
		||||
	m := fmt.Sprintf("Error Event [%s]: %s", u.String(), msg)
 | 
			
		||||
 | 
			
		||||
	return m, u
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Handle the inbound socket request and route it to the proper server action.
 | 
			
		||||
func (wsh *WebsocketHandler) HandleInbound(m WebsocketMessage) error {
 | 
			
		||||
	if !m.inbound {
 | 
			
		||||
| 
						 | 
				
			
			@ -277,6 +313,19 @@ func (wsh *WebsocketHandler) HandleInbound(m WebsocketMessage) error {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	switch m.Event {
 | 
			
		||||
	case AuthenticationEvent:
 | 
			
		||||
		{
 | 
			
		||||
			token, err := ParseJWT([]byte(strings.Join(m.Args, "")))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if token.HasPermission(PermissionConnect) {
 | 
			
		||||
				wsh.JWT = token
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	case SetStateEvent:
 | 
			
		||||
		{
 | 
			
		||||
			if !wsh.JWT.HasPermission(PermissionSendPower) {
 | 
			
		||||
| 
						 | 
				
			
			@ -328,6 +377,10 @@ func (wsh *WebsocketHandler) HandleInbound(m WebsocketMessage) error {
 | 
			
		|||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if wsh.Server.State == server.ProcessOfflineState {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return wsh.Server.Environment.SendCommand(strings.Join(m.Args, ""))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user