-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(authorization): Add bcrypt password support to v1 authorizations
This commit extends the `v1/authorization` package to support passwords associated with a token. The summary of changes include: * authorization.Service implements influxdb.PasswordsService * Setting passwords for authorizations * Verifying (comparing) passwords for a given authorization * A service to cache comparing passwords, using a weaker hash that will live in memory only. This implementation is copied from InfluxDB 1.x * Extended HTTP service to set a password using /private/legacy/authorizations/{id}/password Closes #
- Loading branch information
1 parent
9dd37a1
commit 1f6f433
Showing
18 changed files
with
880 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package all | ||
|
||
import "github.com/influxdata/influxdb/v2/kv/migration" | ||
|
||
var Migration0009_LegacyAuthPasswordBuckets = migration.CreateBuckets( | ||
"Create legacy auth password bucket", | ||
[]byte("legacy/authorizationPasswordv1")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package authorization | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
crand "crypto/rand" | ||
"crypto/sha256" | ||
"io" | ||
"sync" | ||
|
||
"github.com/influxdata/influxdb/v2" | ||
) | ||
|
||
// An implementation of influxdb.PasswordsService that will perform | ||
// ComparePassword requests at a reduced cost under certain | ||
// conditions. See ComparePassword for further information. | ||
// | ||
// The cache is only valid for the duration of the process. | ||
type CachingPasswordsService struct { | ||
inner influxdb.PasswordsService | ||
|
||
mu sync.RWMutex // protects concurrent access to authCache | ||
authCache map[influxdb.ID]authUser | ||
} | ||
|
||
func NewCachingPasswordsService(inner influxdb.PasswordsService) *CachingPasswordsService { | ||
return &CachingPasswordsService{inner: inner, authCache: make(map[influxdb.ID]authUser)} | ||
} | ||
|
||
var _ influxdb.PasswordsService = (*CachingPasswordsService)(nil) | ||
|
||
func (c *CachingPasswordsService) SetPassword(ctx context.Context, id influxdb.ID, password string) error { | ||
err := c.inner.SetPassword(ctx, id, password) | ||
if err == nil { | ||
c.mu.Lock() | ||
delete(c.authCache, id) | ||
c.mu.Unlock() | ||
} | ||
return err | ||
} | ||
|
||
// ComparePassword will attempt to perform the comparison using a lower cost hashing function | ||
// if influxdb.ContextHasPasswordCacheOption returns true for ctx. | ||
func (c *CachingPasswordsService) ComparePassword(ctx context.Context, id influxdb.ID, password string) error { | ||
c.mu.RLock() | ||
au, ok := c.authCache[id] | ||
c.mu.RUnlock() | ||
if ok { | ||
// verify the password using the cached salt and hash | ||
if bytes.Equal(c.hashWithSalt(au.salt, password), au.hash) { | ||
return nil | ||
} | ||
|
||
// fall through to requiring a full bcrypt hash for invalid passwords | ||
} | ||
|
||
err := c.inner.ComparePassword(ctx, id, password) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if salt, hashed, err := c.saltedHash(password); err == nil { | ||
c.mu.Lock() | ||
c.authCache[id] = authUser{salt: salt, hash: hashed} | ||
c.mu.Unlock() | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (c *CachingPasswordsService) CompareAndSetPassword(ctx context.Context, id influxdb.ID, old, new string) error { | ||
err := c.inner.CompareAndSetPassword(ctx, id, old, new) | ||
if err == nil { | ||
c.mu.Lock() | ||
delete(c.authCache, id) | ||
c.mu.Unlock() | ||
} | ||
return err | ||
} | ||
|
||
// NOTE(sgc): This caching implementation was lifted from the 1.x source | ||
// https://github.com/influxdata/influxdb/blob/c1e11e732e145fc1a356535ddf3dcb9fb732a22b/services/meta/client.go#L390-L406 | ||
|
||
const ( | ||
// SaltBytes is the number of bytes used for salts. | ||
SaltBytes = 32 | ||
) | ||
|
||
type authUser struct { | ||
salt []byte | ||
hash []byte | ||
} | ||
|
||
// hashWithSalt returns a salted hash of password using salt. | ||
func (c *CachingPasswordsService) hashWithSalt(salt []byte, password string) []byte { | ||
hasher := sha256.New() | ||
hasher.Write(salt) | ||
hasher.Write([]byte(password)) | ||
return hasher.Sum(nil) | ||
} | ||
|
||
// saltedHash returns a salt and salted hash of password. | ||
func (c *CachingPasswordsService) saltedHash(password string) (salt, hash []byte, err error) { | ||
salt = make([]byte, SaltBytes) | ||
if _, err := io.ReadFull(crand.Reader, salt); err != nil { | ||
return nil, nil, err | ||
} | ||
|
||
return salt, c.hashWithSalt(salt, password), nil | ||
} |
Oops, something went wrong.