diff --git a/server/errors/errors.go b/server/errors/errors.go deleted file mode 100644 index 8c424a4421..0000000000 --- a/server/errors/errors.go +++ /dev/null @@ -1,62 +0,0 @@ -package errors - -import "net/http" - -// Kolide's internal representation for errors. It can be used to wrap another -// error (stored in Err), and additionally contains fields for public -// (PublicMessage) and private (PrivateMessage) error messages as well as the -// HTTP status code (StatusCode) corresponding to the error. Extra holds extra -// information that will be inserted as top level key/value pairs in the error -// response. -type KolideError struct { - Err error - StatusCode int - PublicMessage string - PrivateMessage string - Extra map[string]interface{} -} - -// Implementation of error interface -func (e *KolideError) Error() string { - return e.PublicMessage -} - -// Create a new KolideError specifying the public and private messages. The -// status code will be set to 500. -func New(publicMessage, privateMessage string) *KolideError { - return &KolideError{ - StatusCode: http.StatusInternalServerError, - PublicMessage: publicMessage, - PrivateMessage: privateMessage, - } -} - -// Create a new KolideError specifying the HTTP status, and public and private -// messages. -func NewWithStatus(status int, publicMessage, privateMessage string) *KolideError { - return &KolideError{ - StatusCode: status, - PublicMessage: publicMessage, - PrivateMessage: privateMessage, - } -} - -// Create a new KolideError from an error type. The public message and status -// code should be specified, while the private message will be drawn from -// err.Error() -func NewFromError(err error, status int, publicMessage string) *KolideError { - return &KolideError{ - Err: err, - StatusCode: status, - PublicMessage: publicMessage, - PrivateMessage: err.Error(), - } -} - -// Wrap a server error with the extra KolideError decorations -func InternalServerError(err error) *KolideError { - return NewFromError(err, http.StatusInternalServerError, "Internal server error") -} - -// The status code returned for validation errors. Inspired by the Github API. -const StatusUnprocessableEntity = 422 diff --git a/server/errors/errors_test.go b/server/errors/errors_test.go deleted file mode 100644 index 43e2907696..0000000000 --- a/server/errors/errors_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package errors - -import ( - "errors" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNew(t *testing.T) { - kolideErr := New("Public message", "Private message") - - expect := &KolideError{ - Err: nil, - StatusCode: http.StatusInternalServerError, - PublicMessage: "Public message", - PrivateMessage: "Private message", - } - assert.Equal(t, expect, kolideErr) -} - -func TestNewWithStatus(t *testing.T) { - kolideErr := NewWithStatus(http.StatusUnauthorized, "Public message", "Private message") - - expect := &KolideError{ - Err: nil, - StatusCode: http.StatusUnauthorized, - PublicMessage: "Public message", - PrivateMessage: "Private message", - } - assert.Equal(t, expect, kolideErr) -} - -func TestNewFromError(t *testing.T) { - err := errors.New("Foo error") - kolideErr := NewFromError(err, StatusUnprocessableEntity, "Public error") - - assert.Equal(t, "Public error", kolideErr.Error()) - - expect := &KolideError{ - Err: err, - StatusCode: StatusUnprocessableEntity, - PublicMessage: "Public error", - PrivateMessage: "Foo error", - } - assert.Equal(t, expect, kolideErr) -} - -// These types and functions for performing an unordered comparison on a -// []map[string]string] as parsed from the error JSON -type errorField map[string]string -type errorFields []errorField - -func (e errorFields) Len() int { - return len(e) -} - -func (e errorFields) Less(i, j int) bool { - return e[i]["field"] <= e[j]["field"] && - e[i]["code"] <= e[j]["code"] && - e[i]["message"] <= e[j]["message"] -} - -func (e errorFields) Swap(i, j int) { - e[i], e[j] = e[j], e[i] -} diff --git a/server/kolide/sessions.go b/server/kolide/sessions.go index 6dcd2789f3..88a3cd33da 100644 --- a/server/kolide/sessions.go +++ b/server/kolide/sessions.go @@ -3,31 +3,9 @@ package kolide import ( "time" - jwt "github.com/dgrijalva/jwt-go" - "github.com/kolide/kolide-ose/server/errors" "golang.org/x/net/context" ) -const publicErrorMessage string = "Session error" - -var ( - // An error returned by SessionStore.Get() if no session record was found - // in the database - ErrNoActiveSession = errors.New(publicErrorMessage, "Active session is not present in the database") - - // An error returned by SessionStore methods when no session object has - // been created yet but the requested action requires one - ErrSessionNotCreated = errors.New(publicErrorMessage, "The session has not been created") - - // An error returned by SessionStore.Get() when a session is requested but - // it has expired - ErrSessionExpired = errors.New(publicErrorMessage, "The session has expired") - - // An error returned by SessionStore which indicates that the token - // or it's content were malformed - ErrSessionMalformed = errors.New(publicErrorMessage, "The session token was malformed") -) - // SessionStore is the abstract interface that all session backends must // conform to. type SessionStore interface { @@ -74,28 +52,3 @@ type Session struct { UserID uint `db:"user_id"` Key string } - -//////////////////////////////////////////////////////////////////////////////// -// JSON Web Tokens -//////////////////////////////////////////////////////////////////////////////// - -// Given a session key create a JWT to be delivered to the client -func GenerateJWT(sessionKey, jwtKey string) (string, error) { - token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "session_key": sessionKey, - }) - - return token.SignedString([]byte(jwtKey)) -} - -// ParseJWT attempts to parse a JWT token in serialized string form into a -// JWT token in a deserialized jwt.Token struct. -func ParseJWT(token, jwtKey string) (*jwt.Token, error) { - return jwt.Parse(token, func(t *jwt.Token) (interface{}, error) { - method, ok := t.Method.(*jwt.SigningMethodHMAC) - if !ok || method != jwt.SigningMethodHS256 { - return nil, errors.New(publicErrorMessage, "Unexpected signing method") - } - return []byte(jwtKey), nil - }) -} diff --git a/server/kolide/sessions_test.go b/server/kolide/sessions_test.go deleted file mode 100644 index ca3fcc1f05..0000000000 --- a/server/kolide/sessions_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package kolide - -import ( - "testing" - - jwt "github.com/dgrijalva/jwt-go" - "github.com/stretchr/testify/assert" -) - -func TestGenerateJWT(t *testing.T) { - tokenString, err := GenerateJWT("4", "") - assert.Nil(t, err) - - token, err := ParseJWT(tokenString, "") - assert.Nil(t, err) - - claims, ok := token.Claims.(jwt.MapClaims) - assert.True(t, ok) - assert.True(t, token.Valid) - - sessionKey := claims["session_key"].(string) - assert.Equal(t, "4", sessionKey) -} diff --git a/server/service/service_sessions.go b/server/service/service_sessions.go index eafbbdc1c2..d7e3453b46 100644 --- a/server/service/service_sessions.go +++ b/server/service/service_sessions.go @@ -6,8 +6,10 @@ import ( "strings" "time" + jwt "github.com/dgrijalva/jwt-go" "github.com/kolide/kolide-ose/server/contexts/viewer" "github.com/kolide/kolide-ose/server/kolide" + "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -57,12 +59,12 @@ func (svc service) makeSession(id uint) (string, error) { session, err = svc.ds.NewSession(session) if err != nil { - return "", err + return "", errors.Wrap(err, "creating new session") } - tokenString, err := kolide.GenerateJWT(session.Key, svc.config.Auth.JwtKey) + tokenString, err := generateJWT(session.Key, svc.config.Auth.JwtKey) if err != nil { - return "", err + return "", errors.Wrap(err, "generating JWT token") } return tokenString, nil @@ -146,7 +148,10 @@ func (svc service) DeleteSession(ctx context.Context, id uint) error { func (svc service) validateSession(session *kolide.Session) error { if session == nil { - return kolide.ErrNoActiveSession + return authError{ + reason: "active session not present", + clientReason: "session error", + } } sessionDuration := svc.config.Session.Duration @@ -154,10 +159,22 @@ func (svc service) validateSession(session *kolide.Session) error { if sessionDuration != 0 && time.Since(session.AccessedAt) >= sessionDuration { err := svc.ds.DestroySession(session) if err != nil { - return err + return errors.Wrap(err, "destroying session") + } + return authError{ + reason: "expired session", + clientReason: "session error", } - return kolide.ErrSessionExpired } return svc.ds.MarkSessionAccessed(session) } + +// Given a session key create a JWT to be delivered to the client +func generateJWT(sessionKey, jwtKey string) (string, error) { + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "session_key": sessionKey, + }) + + return token.SignedString([]byte(jwtKey)) +} diff --git a/server/service/service_sessions_test.go b/server/service/service_sessions_test.go index 35d72445e6..5f08ad7eb9 100644 --- a/server/service/service_sessions_test.go +++ b/server/service/service_sessions_test.go @@ -5,6 +5,7 @@ import ( "time" "github.com/kolide/kolide-ose/server/config" + "github.com/kolide/kolide-ose/server/contexts/token" "github.com/kolide/kolide-ose/server/datastore/inmem" "github.com/kolide/kolide-ose/server/kolide" "github.com/stretchr/testify/assert" @@ -57,5 +58,32 @@ func TestAuthenticate(t *testing.T) { "access time should be set with current time at session creation") }) } - +} + +func TestGenerateJWT(t *testing.T) { + jwtKey := "" + tokenString, err := generateJWT("4", jwtKey) + require.Nil(t, err) + + svc := authViewerService{} + viewer, err := authViewer( + context.Background(), + jwtKey, + token.Token(tokenString), + svc, + ) + require.Nil(t, err) + require.NotNil(t, viewer) +} + +type authViewerService struct { + kolide.Service +} + +func (authViewerService) GetSessionByKey(ctx context.Context, key string) (*kolide.Session, error) { + return &kolide.Session{}, nil +} + +func (authViewerService) User(ctx context.Context, uid uint) (*kolide.User, error) { + return &kolide.User{}, nil }