2016-09-05 20:03:58 +00:00
|
|
|
package server
|
2016-08-28 03:59:17 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/kolide/kolide-ose/kolide"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
"golang.org/x/net/context"
|
|
|
|
)
|
|
|
|
|
2016-09-01 04:51:38 +00:00
|
|
|
func (svc service) NewUser(ctx context.Context, p kolide.UserPayload) (*kolide.User, error) {
|
2016-09-14 16:11:06 +00:00
|
|
|
user, err := userFromPayload(p, svc.config.Auth.SaltKeySize, svc.config.Auth.BcryptCost)
|
2016-08-28 03:59:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2016-09-01 04:51:38 +00:00
|
|
|
user, err = svc.ds.NewUser(user)
|
2016-08-28 03:59:17 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return user, nil
|
|
|
|
}
|
|
|
|
|
2016-09-01 04:51:38 +00:00
|
|
|
func (svc service) User(ctx context.Context, id uint) (*kolide.User, error) {
|
|
|
|
return svc.ds.UserByID(id)
|
2016-08-29 00:29:56 +00:00
|
|
|
}
|
|
|
|
|
2016-09-01 04:51:38 +00:00
|
|
|
func (svc service) ChangePassword(ctx context.Context, userID uint, old, new string) error {
|
|
|
|
user, err := svc.User(ctx, userID)
|
2016-08-29 00:29:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-08-29 17:04:07 +00:00
|
|
|
if err := user.ValidatePassword(old); err != nil {
|
2016-09-01 04:51:38 +00:00
|
|
|
return fmt.Errorf("current password validation failed: %v", err)
|
2016-08-29 17:04:07 +00:00
|
|
|
}
|
2016-09-14 16:11:06 +00:00
|
|
|
hashed, salt, err := hashPassword(new, svc.config.Auth.SaltKeySize, svc.config.Auth.BcryptCost)
|
2016-08-29 00:29:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
user.Salt = salt
|
|
|
|
user.Password = hashed
|
2016-09-01 04:51:38 +00:00
|
|
|
return svc.saveUser(user)
|
2016-08-29 00:29:56 +00:00
|
|
|
}
|
|
|
|
|
2016-09-01 04:51:38 +00:00
|
|
|
func (svc service) UpdateAdminRole(ctx context.Context, userID uint, isAdmin bool) error {
|
|
|
|
user, err := svc.User(ctx, userID)
|
2016-08-29 17:04:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
user.Admin = isAdmin
|
2016-09-01 04:51:38 +00:00
|
|
|
return svc.saveUser(user)
|
2016-08-29 17:04:07 +00:00
|
|
|
}
|
|
|
|
|
2016-09-01 04:51:38 +00:00
|
|
|
func (svc service) UpdateUserStatus(ctx context.Context, userID uint, password string, enabled bool) error {
|
|
|
|
user, err := svc.User(ctx, userID)
|
2016-08-29 17:04:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
user.Enabled = enabled
|
2016-09-01 04:51:38 +00:00
|
|
|
return svc.saveUser(user)
|
2016-08-29 17:04:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-29 00:29:56 +00:00
|
|
|
// saves user in datastore.
|
|
|
|
// doesn't need to be exposed to the transport
|
|
|
|
// the service should expose actions for modifying a user instead
|
2016-09-01 04:51:38 +00:00
|
|
|
func (svc service) saveUser(user *kolide.User) error {
|
|
|
|
return svc.ds.SaveUser(user)
|
2016-08-29 00:29:56 +00:00
|
|
|
}
|
|
|
|
|
2016-08-28 03:59:17 +00:00
|
|
|
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{
|
2016-09-08 22:57:05 +00:00
|
|
|
Username: *p.Username,
|
|
|
|
Email: *p.Email,
|
|
|
|
Admin: falseIfNil(p.Admin),
|
|
|
|
AdminForcedPasswordReset: falseIfNil(p.AdminForcedPasswordReset),
|
|
|
|
Salt: salt,
|
|
|
|
Enabled: true,
|
|
|
|
Password: hashed,
|
2016-08-28 03:59:17 +00:00
|
|
|
}, 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
|
|
|
|
}
|