Better core logic for JWT; supports a more generic structure

This commit is contained in:
Dane Everitt 2020-04-05 18:44:16 -07:00
parent cf2ef1a173
commit ccbb119948
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
3 changed files with 59 additions and 34 deletions

33
router/tokens/parser.go Normal file
View File

@ -0,0 +1,33 @@
package tokens
import (
"github.com/gbrlsnchs/jwt/v3"
"github.com/pterodactyl/wings/config"
"time"
)
var alg *jwt.HMACSHA
type TokenData interface {
GetPayload() *jwt.Payload
}
// Validates the provided JWT against the known secret for the Daemon and returns the
// parsed data. This function DOES NOT validate that the token is valid for the connected
// server, nor does it ensure that the user providing the token is able to actually do things.
//
// This simply returns a parsed token.
func ParseToken(token []byte, data TokenData) error {
if alg == nil {
alg = jwt.NewHS256([]byte(config.Get().AuthenticationToken))
}
verifyOptions := jwt.ValidatePayload(
data.GetPayload(),
jwt.ExpirationTimeValidator(time.Now()),
)
_, err := jwt.Verify(token, alg, &data, verifyOptions)
return err
}

View File

@ -1,19 +1,24 @@
package websocket package tokens
import ( import (
"encoding/json" "encoding/json"
"github.com/gbrlsnchs/jwt/v3" "github.com/gbrlsnchs/jwt/v3"
) )
type TokenPayload struct { type WebsocketPayload struct {
jwt.Payload jwt.Payload
UserID json.Number `json:"user_id"` UserID json.Number `json:"user_id"`
ServerUUID string `json:"server_uuid"` ServerUUID string `json:"server_uuid"`
Permissions []string `json:"permissions"` Permissions []string `json:"permissions"`
} }
// Returns the JWT payload.
func (p *WebsocketPayload) GetPayload() *jwt.Payload {
return &p.Payload
}
// Checks if the given token payload has a permission string. // Checks if the given token payload has a permission string.
func (p *TokenPayload) HasPermission(permission string) bool { func (p *WebsocketPayload) HasPermission(permission string) bool {
for _, k := range p.Permissions { for _, k := range p.Permissions {
if k == permission { if k == permission {
return true return true

View File

@ -7,6 +7,7 @@ import (
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/pterodactyl/wings/config" "github.com/pterodactyl/wings/config"
"github.com/pterodactyl/wings/router/tokens"
"github.com/pterodactyl/wings/server" "github.com/pterodactyl/wings/server"
"go.uber.org/zap" "go.uber.org/zap"
"net/http" "net/http"
@ -28,11 +29,26 @@ const (
type Handler struct { type Handler struct {
Connection *websocket.Conn Connection *websocket.Conn
JWT *TokenPayload `json:"-"` JWT *tokens.WebsocketPayload `json:"-"`
server *server.Server server *server.Server
mutex sync.Mutex mutex sync.Mutex
} }
// Parses a JWT into a websocket token payload.
func NewTokenPayload(token []byte) (*tokens.WebsocketPayload, error) {
payload := tokens.WebsocketPayload{}
err := tokens.ParseToken(token, &payload)
if err != nil {
return nil, err
}
if !payload.HasPermission(PermissionConnect) {
return nil, errors.New("not authorized to connect to this socket")
}
return &payload, nil
}
// Returns a new websocket handler using the context provided. // Returns a new websocket handler using the context provided.
func GetHandler(s *server.Server, w http.ResponseWriter, r *http.Request) (*Handler, error) { func GetHandler(s *server.Server, w http.ResponseWriter, r *http.Request) (*Handler, error) {
upgrader := websocket.Upgrader{ upgrader := websocket.Upgrader{
@ -56,35 +72,6 @@ func GetHandler(s *server.Server, w http.ResponseWriter, r *http.Request) (*Hand
}, nil }, nil
} }
// Validates the provided JWT against the known secret for the Daemon and returns the
// parsed data.
//
// This function DOES NOT validate that the token is valid for the connected server, nor
// does it ensure that the user providing the token is able to actually do things.
func ParseJWT(token []byte) (*TokenPayload, error) {
var payload TokenPayload
if alg == nil {
alg = jwt.NewHS256([]byte(config.Get().AuthenticationToken))
}
now := time.Now()
verifyOptions := jwt.ValidatePayload(
&payload.Payload,
jwt.ExpirationTimeValidator(now),
)
_, err := jwt.Verify(token, alg, &payload, verifyOptions)
if err != nil {
return nil, err
}
if !payload.HasPermission(PermissionConnect) {
return nil, errors.New("not authorized to connect to this socket")
}
return &payload, nil
}
func (h *Handler) SendJson(v *Message) error { func (h *Handler) SendJson(v *Message) error {
// Do not send JSON down the line if the JWT on the connection is not // Do not send JSON down the line if the JWT on the connection is not
// valid! // valid!
@ -194,7 +181,7 @@ func (h *Handler) HandleInbound(m Message) error {
switch m.Event { switch m.Event {
case AuthenticationEvent: case AuthenticationEvent:
{ {
token, err := ParseJWT([]byte(strings.Join(m.Args, ""))) token, err := NewTokenPayload([]byte(strings.Join(m.Args, "")))
if err != nil { if err != nil {
return err return err
} }