fleet/server/service_users.go
Zachary Wasserman 885db1a597 Refactoring for config patterns (#159)
This PR refactors most of the codebase to use the new config patterns implemented in #149. Now the core service keeps a copy of the KolideConfig struct, and service methods can reference the configuration in that struct when they need it. The most significant refactoring is in the sessions code, separating the business logic from the storage layer.
2016-09-14 09:11:06 -07:00

122 lines
2.9 KiB
Go

package server
import (
"crypto/rand"
"encoding/base64"
"fmt"
"github.com/kolide/kolide-ose/kolide"
"golang.org/x/crypto/bcrypt"
"golang.org/x/net/context"
)
func (svc service) NewUser(ctx context.Context, p kolide.UserPayload) (*kolide.User, error) {
user, err := userFromPayload(p, svc.config.Auth.SaltKeySize, svc.config.Auth.BcryptCost)
if err != nil {
return nil, err
}
user, err = svc.ds.NewUser(user)
if err != nil {
return nil, err
}
return user, nil
}
func (svc service) User(ctx context.Context, id uint) (*kolide.User, error) {
return svc.ds.UserByID(id)
}
func (svc service) ChangePassword(ctx context.Context, userID uint, old, new string) error {
user, err := svc.User(ctx, userID)
if err != nil {
return err
}
if err := user.ValidatePassword(old); err != nil {
return fmt.Errorf("current password validation failed: %v", err)
}
hashed, salt, err := hashPassword(new, svc.config.Auth.SaltKeySize, svc.config.Auth.BcryptCost)
if err != nil {
return err
}
user.Salt = salt
user.Password = hashed
return svc.saveUser(user)
}
func (svc service) UpdateAdminRole(ctx context.Context, userID uint, isAdmin bool) error {
user, err := svc.User(ctx, userID)
if err != nil {
return err
}
user.Admin = isAdmin
return svc.saveUser(user)
}
func (svc service) UpdateUserStatus(ctx context.Context, userID uint, password string, enabled bool) error {
user, err := svc.User(ctx, userID)
if err != nil {
return err
}
user.Enabled = enabled
return svc.saveUser(user)
}
// saves user in datastore.
// doesn't need to be exposed to the transport
// the service should expose actions for modifying a user instead
func (svc service) saveUser(user *kolide.User) error {
return svc.ds.SaveUser(user)
}
func userFromPayload(p kolide.UserPayload, keySize, cost int) (*kolide.User, error) {
hashed, salt, err := hashPassword(*p.Password, keySize, cost)
if err != nil {
return nil, err
}
return &kolide.User{
Username: *p.Username,
Email: *p.Email,
Admin: falseIfNil(p.Admin),
AdminForcedPasswordReset: falseIfNil(p.AdminForcedPasswordReset),
Salt: salt,
Enabled: true,
Password: hashed,
}, nil
}
func hashPassword(plaintext string, keySize, cost int) ([]byte, string, error) {
salt, err := generateRandomText(keySize)
if err != nil {
return nil, "", err
}
withSalt := []byte(fmt.Sprintf("%s%s", plaintext, salt))
hashed, err := bcrypt.GenerateFromPassword(withSalt, cost)
if err != nil {
return nil, "", err
}
return hashed, salt, nil
}
// generateRandomText return a string generated by filling in keySize bytes with
// random data and then base64 encoding those bytes
func generateRandomText(keySize int) (string, error) {
key := make([]byte, keySize)
_, err := rand.Read(key)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(key), nil
}
// helper to convert a bool pointer false
func falseIfNil(b *bool) bool {
if b == nil {
return false
}
return *b
}