rewrite auth
don't disclose auth denial reasons in response
This commit is contained in:
parent
6e18e4a1ce
commit
3a65f409d1
100
api/auth.go
100
api/auth.go
|
@ -2,7 +2,6 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Pterodactyl/wings/config"
|
"github.com/Pterodactyl/wings/config"
|
||||||
"github.com/Pterodactyl/wings/control"
|
"github.com/Pterodactyl/wings/control"
|
||||||
|
@ -13,53 +12,88 @@ import (
|
||||||
const (
|
const (
|
||||||
accessTokenHeader = "X-Access-Token"
|
accessTokenHeader = "X-Access-Token"
|
||||||
accessServerHeader = "X-Access-Server"
|
accessServerHeader = "X-Access-Server"
|
||||||
|
|
||||||
|
// ContextVarServer is the gin.Context field containing the requested server (gin.Context.Get())
|
||||||
|
ContextVarServer = "server"
|
||||||
|
// ContextVarAuth is the gin.Context field containing the authorizationManager
|
||||||
|
// for the request (gin.Context.Get())
|
||||||
|
ContextVarAuth = "auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
type responseError struct {
|
type responseError struct {
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AuthorizationManager handles permission checks
|
||||||
|
type AuthorizationManager interface {
|
||||||
|
hasPermission(string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type authorizationManager struct {
|
||||||
|
token string
|
||||||
|
server control.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ AuthorizationManager = &authorizationManager{}
|
||||||
|
|
||||||
|
func newAuthorizationManager(token string, server control.Server) *authorizationManager {
|
||||||
|
return &authorizationManager{
|
||||||
|
token: token,
|
||||||
|
server: server,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *authorizationManager) hasPermission(permission string) bool {
|
||||||
|
if permission == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
prefix := permission[:1]
|
||||||
|
if prefix == "c" {
|
||||||
|
return config.Get().ContainsAuthKey(a.token)
|
||||||
|
}
|
||||||
|
if a.server == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if prefix == "g" {
|
||||||
|
return config.Get().ContainsAuthKey(a.token)
|
||||||
|
}
|
||||||
|
if prefix == "s" {
|
||||||
|
return a.server.HasPermission(a.token, permission)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// AuthHandler returns a HandlerFunc that checks request authentication
|
// AuthHandler returns a HandlerFunc that checks request authentication
|
||||||
// permission is a permission string describing the required permission to access the route
|
// permission is a permission string describing the required permission to access the route
|
||||||
func AuthHandler(permission string) gin.HandlerFunc {
|
func AuthHandler(permission string) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
requestToken := c.Request.Header.Get(accessTokenHeader)
|
requestToken := c.Request.Header.Get(accessTokenHeader)
|
||||||
requestServer := c.Request.Header.Get(accessServerHeader)
|
requestServer := c.Request.Header.Get(accessServerHeader)
|
||||||
|
var server control.Server
|
||||||
|
|
||||||
if requestToken != "" {
|
if requestToken == "" {
|
||||||
// c: master controller, permissions not related to specific server
|
|
||||||
if strings.HasPrefix(permission, "c:") {
|
|
||||||
if config.Get().ContainsAuthKey(requestToken) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// All other permission strings not starting with c: require a server to be provided
|
|
||||||
if requestServer != "" {
|
|
||||||
server := control.GetServer(requestServer)
|
|
||||||
if server != nil {
|
|
||||||
if strings.HasPrefix(permission, "g:") {
|
|
||||||
if config.Get().ContainsAuthKey(requestToken) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(permission, "s:") {
|
|
||||||
if server.HasPermission(requestToken, permission) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
c.JSON(http.StatusNotFound, responseError{"Server defined in " + accessServerHeader + " is not known."})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
c.JSON(http.StatusBadRequest, responseError{"No " + accessServerHeader + " header provided."})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Debug("Token missing in request.")
|
log.Debug("Token missing in request.")
|
||||||
c.JSON(http.StatusBadRequest, responseError{"No " + accessTokenHeader + " header provided."})
|
c.JSON(http.StatusBadRequest, responseError{"Missing required " + accessTokenHeader + " header."})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusForbidden, responseError{"You are do not have permission to perform this action."})
|
if requestServer != "" {
|
||||||
|
server = control.GetServer(requestServer)
|
||||||
|
//fmt.Println(server)
|
||||||
|
if server == nil {
|
||||||
|
log.WithField("serverUUID", requestServer).Error("Auth: Requested server not found.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auth := newAuthorizationManager(requestToken, server)
|
||||||
|
|
||||||
|
if auth.hasPermission(permission) {
|
||||||
|
c.Set(ContextVarServer, server)
|
||||||
|
c.Set(ContextVarAuth, auth)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusForbidden, responseError{"You do not have permission to perform this action."})
|
||||||
c.Abort()
|
c.Abort()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,16 +50,16 @@ func TestAuthHandler(t *testing.T) {
|
||||||
responded, rec := requestMiddlewareWith("g:test", "existingkey", "")
|
responded, rec := requestMiddlewareWith("g:test", "existingkey", "")
|
||||||
|
|
||||||
assert.False(t, responded)
|
assert.False(t, responded)
|
||||||
assert.Equal(t, http.StatusBadRequest, rec.Code)
|
assert.Equal(t, http.StatusForbidden, rec.Code)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("rejects not existing server", func(t *testing.T) {
|
t.Run("rejects not existing server", func(t *testing.T) {
|
||||||
loadConfiguration(t, true)
|
loadConfiguration(t, true)
|
||||||
|
|
||||||
responded, rec := requestMiddlewareWith("g:test", "existingkey", "notexistingserver")
|
responded, rec := requestMiddlewareWith("g:testnotexisting", "existingkey", "notexistingserver")
|
||||||
|
|
||||||
assert.False(t, responded)
|
assert.False(t, responded)
|
||||||
assert.Equal(t, http.StatusNotFound, rec.Code)
|
assert.Equal(t, http.StatusForbidden, rec.Code)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("accepts server with existing g: key", func(t *testing.T) {
|
t.Run("accepts server with existing g: key", func(t *testing.T) {
|
||||||
|
|
|
@ -3,7 +3,6 @@ package control
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -110,13 +109,16 @@ func loadServerConfiguration(path string) (*server, error) {
|
||||||
if err := json.Unmarshal(file, server); err != nil {
|
if err := json.Unmarshal(file, server); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fmt.Println(server)
|
|
||||||
return server, nil
|
return server, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetServer returns the server identified by the provided uuid
|
// GetServer returns the server identified by the provided uuid
|
||||||
func GetServer(uuid string) Server {
|
func GetServer(uuid string) Server {
|
||||||
return servers[uuid]
|
server := servers[uuid]
|
||||||
|
if server == nil {
|
||||||
|
return nil // https://golang.org/doc/faq#nil_error
|
||||||
|
}
|
||||||
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new Server
|
// NewServer creates a new Server
|
||||||
|
|
Loading…
Reference in New Issue
Block a user