From 3a65f409d13f9c0cff67dce003473d031c10edef Mon Sep 17 00:00:00 2001 From: Jakob Schrettenbrunner Date: Wed, 2 Aug 2017 21:35:15 +0200 Subject: [PATCH] rewrite auth don't disclose auth denial reasons in response --- api/auth.go | 100 +++++++++++++++++++++++++++++++--------------- api/auth_test.go | 6 +-- control/server.go | 8 ++-- 3 files changed, 75 insertions(+), 39 deletions(-) diff --git a/api/auth.go b/api/auth.go index 354dd58..00768e5 100644 --- a/api/auth.go +++ b/api/auth.go @@ -2,7 +2,6 @@ package api import ( "net/http" - "strings" "github.com/Pterodactyl/wings/config" "github.com/Pterodactyl/wings/control" @@ -13,53 +12,88 @@ import ( const ( accessTokenHeader = "X-Access-Token" 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 { 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 // permission is a permission string describing the required permission to access the route func AuthHandler(permission string) gin.HandlerFunc { return func(c *gin.Context) { requestToken := c.Request.Header.Get(accessTokenHeader) requestServer := c.Request.Header.Get(accessServerHeader) + var server control.Server - 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 { + if requestToken == "" { 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() } } diff --git a/api/auth_test.go b/api/auth_test.go index 58afe36..bb33ba7 100644 --- a/api/auth_test.go +++ b/api/auth_test.go @@ -50,16 +50,16 @@ func TestAuthHandler(t *testing.T) { responded, rec := requestMiddlewareWith("g:test", "existingkey", "") 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) { loadConfiguration(t, true) - responded, rec := requestMiddlewareWith("g:test", "existingkey", "notexistingserver") + responded, rec := requestMiddlewareWith("g:testnotexisting", "existingkey", "notexistingserver") 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) { diff --git a/control/server.go b/control/server.go index 9a817fe..87c25b4 100644 --- a/control/server.go +++ b/control/server.go @@ -3,7 +3,6 @@ package control import ( "encoding/json" "errors" - "fmt" "io/ioutil" "strings" @@ -110,13 +109,16 @@ func loadServerConfiguration(path string) (*server, error) { if err := json.Unmarshal(file, server); err != nil { return nil, err } - fmt.Println(server) return server, nil } // GetServer returns the server identified by the provided uuid 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