influxdb/bolt/passwords.go

111 lines
3.0 KiB
Go

package bolt
import (
"context"
"fmt"
bolt "github.com/coreos/bbolt"
platform "github.com/influxdata/influxdb"
"golang.org/x/crypto/bcrypt"
)
// MinPasswordLength is the shortest password we allow into the system.
const MinPasswordLength = 8
var (
// EIncorrectPassword is returned when any password operation fails in which
// we do not want to leak information.
EIncorrectPassword = &platform.Error{
Code: platform.EForbidden,
Msg: "your username or password is incorrect",
}
// EShortPassword is used when a password is less than the minimum
// acceptable password length.
EShortPassword = &platform.Error{
Code: platform.EInvalid,
Msg: "passwords must be at least 8 characters long",
}
)
// CorruptUserIDError is used when the ID was encoded incorrectly previously.
// This is some sort of internal server error.
func CorruptUserIDError(name string, err error) error {
return &platform.Error{
Code: platform.EInternal,
Msg: fmt.Sprintf("User ID for %s has been corrupted; Err: %v", name, err),
Op: "bolt/setPassword",
}
}
var _ platform.PasswordsService = (*Client)(nil)
// SetPassword stores the password hash associated with a user.
func (c *Client) SetPassword(ctx context.Context, name string, password string) error {
return c.db.Update(func(tx *bolt.Tx) error {
return c.setPassword(ctx, tx, name, password)
})
}
// HashCost currently using the default cost of bcrypt
var HashCost = bcrypt.DefaultCost
func (c *Client) setPassword(ctx context.Context, tx *bolt.Tx, name string, password string) error {
if len(password) < MinPasswordLength {
return EShortPassword
}
hash, err := bcrypt.GenerateFromPassword([]byte(password), HashCost)
if err != nil {
return err
}
u, pe := c.findUserByName(ctx, tx, name)
if pe != nil {
return EIncorrectPassword
}
encodedID, err := u.ID.Encode()
if err != nil {
return CorruptUserIDError(name, err)
}
return tx.Bucket(userpasswordBucket).Put(encodedID, hash)
}
// ComparePassword compares a provided password with the stored password hash.
func (c *Client) ComparePassword(ctx context.Context, name string, password string) error {
return c.db.View(func(tx *bolt.Tx) error {
return c.comparePassword(ctx, tx, name, password)
})
}
func (c *Client) comparePassword(ctx context.Context, tx *bolt.Tx, name string, password string) error {
u, pe := c.findUserByName(ctx, tx, name)
if pe != nil {
return pe
}
encodedID, err := u.ID.Encode()
if err != nil {
return err
}
hash := tx.Bucket(userpasswordBucket).Get(encodedID)
if err := bcrypt.CompareHashAndPassword(hash, []byte(password)); err != nil {
// User exists but the password was incorrect
return EIncorrectPassword
}
return nil
}
// CompareAndSetPassword replaces the old password with the new password if thee old password is correct.
func (c *Client) CompareAndSetPassword(ctx context.Context, name string, old string, new string) error {
return c.db.Update(func(tx *bolt.Tx) error {
if err := c.comparePassword(ctx, tx, name, old); err != nil {
return err
}
return c.setPassword(ctx, tx, name, new)
})
}