fleet/server/service_sessions.go
Victor Vrantchan 45d6b1e32f Auth errors (#185)
Return well formatted authentication errors to the client
Log the reason for an error serveside but return a masked/generic reason to the client
Assert go errors by behavior rather than type.
2016-09-20 15:22:54 -04:00

165 lines
3.8 KiB
Go

package server
import (
"crypto/rand"
"encoding/base64"
"strings"
"time"
"github.com/kolide/kolide-ose/datastore"
"github.com/kolide/kolide-ose/kolide"
"golang.org/x/net/context"
)
func (svc service) Login(ctx context.Context, username, password string) (*kolide.User, string, error) {
user, err := svc.userByEmailOrUsername(username)
switch err {
case nil:
case datastore.ErrNotFound:
return nil, "", authError{reason: "no such user", clientReason: "bad credentials"}
default:
return nil, "", err
}
if !user.Enabled {
return nil, "", authError{reason: "account disabled"}
}
if err := user.ValidatePassword(password); err != nil {
return nil, "", authError{reason: "bad password", clientReason: "bad credentials"}
}
token, err := svc.makeSession(user.ID)
if err != nil {
return nil, "", err
}
return user, token, nil
}
func (svc service) userByEmailOrUsername(username string) (*kolide.User, error) {
if strings.Contains(username, "@") {
return svc.ds.UserByEmail(username)
}
return svc.ds.User(username)
}
// makeSession is a helper that creates a new session after authentication
func (svc service) makeSession(id uint) (string, error) {
sessionKeySize := svc.config.Session.KeySize
key := make([]byte, sessionKeySize)
_, err := rand.Read(key)
if err != nil {
return "", err
}
session := &kolide.Session{
UserID: id,
Key: base64.StdEncoding.EncodeToString(key),
AccessedAt: time.Now().UTC(),
}
session, err = svc.ds.NewSession(session)
if err != nil {
return "", err
}
tokenString, err := kolide.GenerateJWT(session.Key, svc.config.Auth.JwtKey)
if err != nil {
return "", err
}
return tokenString, nil
}
func (svc service) Logout(ctx context.Context) error {
// this should not return an error if the user wasn't logged in
return svc.DestroySession(ctx)
}
func (svc service) DestroySession(ctx context.Context) error {
vc, err := viewerContextFromContext(ctx)
if err != nil {
return err
}
session, err := svc.ds.FindSessionByID(vc.SessionID())
if err != nil {
return err
}
return svc.ds.DestroySession(session)
}
func (svc service) GetInfoAboutSessionsForUser(ctx context.Context, id uint) ([]*kolide.Session, error) {
var validatedSessions []*kolide.Session
sessions, err := svc.ds.FindAllSessionsForUser(id)
if err != nil {
return validatedSessions, err
}
for _, session := range sessions {
if svc.validateSession(session) == nil {
validatedSessions = append(validatedSessions, session)
}
}
return validatedSessions, nil
}
func (svc service) DeleteSessionsForUser(ctx context.Context, id uint) error {
return svc.ds.DestroyAllSessionsForUser(id)
}
func (svc service) GetInfoAboutSession(ctx context.Context, id uint) (*kolide.Session, error) {
session, err := svc.ds.FindSessionByID(id)
if err != nil {
return nil, err
}
err = svc.validateSession(session)
if err != nil {
return nil, err
}
return session, nil
}
func (svc service) GetSessionByKey(ctx context.Context, key string) (*kolide.Session, error) {
session, err := svc.ds.FindSessionByKey(key)
if err != nil {
return nil, err
}
err = svc.validateSession(session)
if err != nil {
return nil, err
}
return session, nil
}
func (svc service) DeleteSession(ctx context.Context, id uint) error {
session, err := svc.ds.FindSessionByID(id)
if err != nil {
return err
}
return svc.ds.DestroySession(session)
}
func (svc service) validateSession(session *kolide.Session) error {
if session == nil {
return kolide.ErrNoActiveSession
}
sessionDuration := svc.config.Session.Duration
// duration 0 = unlimited
if sessionDuration != 0 && time.Since(session.AccessedAt) >= sessionDuration {
err := svc.ds.DestroySession(session)
if err != nil {
return err
}
return kolide.ErrSessionExpired
}
return svc.ds.MarkSessionAccessed(session)
}