mirror of
https://github.com/argoproj/argo-cd
synced 2026-05-04 23:28:20 +00:00
* Add auth check and bypass for authentication * Disallow blank passwords * Mitigate timing attacks * Factor out authentication/token gen code * Tweaked token validation code to log claims * Add missing internal gRPC client endpoints * Add first draft of login command * Add login command to root commands * Get login working * Generalize command utils for unmarshaling * Centralize utils for CLI YAML/JSON parsing * Read/write local config now * Initialize map * Revert server files for now * Fix casing * Restore commented test, thanks @alexmt * No need to mitigate timing attacks on blank passwords, thanks @alexmt * Rm redundant type declaration, thanks @alexmt * Improve error checks * Rm unnecessary conversion, thanks @alexmt * Fix comment * Don't return error when config doesn't exist
101 lines
4.1 KiB
Go
101 lines
4.1 KiB
Go
package password
|
|
|
|
import (
|
|
"crypto/subtle"
|
|
"fmt"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
// PasswordHasher is an interface type to declare a general-purpose password management tool.
|
|
type PasswordHasher interface {
|
|
HashPassword(string) (string, error)
|
|
VerifyPassword(string, string) bool
|
|
}
|
|
|
|
// DummyPasswordHasher is for testing ONLY. DO NOT USE in a production context.
|
|
type DummyPasswordHasher struct{}
|
|
|
|
// BcryptPasswordHasher handles password hashing with Bcrypt. Create with `0` as the work factor to default to bcrypt.DefaultCost at hashing time. The Cost field represents work factor.
|
|
type BcryptPasswordHasher struct {
|
|
Cost int
|
|
}
|
|
|
|
var _ PasswordHasher = DummyPasswordHasher{}
|
|
var _ PasswordHasher = BcryptPasswordHasher{0}
|
|
|
|
// PreferredHashers holds the list of preferred hashing algorithms, in order of most to least preferred. Any password that does not validate with the primary algorithm will be considered "stale." DO NOT ADD THE DUMMY HASHER FOR USE IN PRODUCTION.
|
|
var preferredHashers = []PasswordHasher{
|
|
BcryptPasswordHasher{},
|
|
}
|
|
|
|
// HashPasswordWithHashers hashes an entered password using the first hasher in the provided list of hashers.
|
|
func hashPasswordWithHashers(password string, hashers []PasswordHasher) (string, error) {
|
|
// Even though good hashers will disallow blank passwords, let's be explicit that ALL BLANK PASSWORDS ARE INVALID. Full stop.
|
|
if password == "" {
|
|
return "", fmt.Errorf("blank passwords are not allowed")
|
|
}
|
|
return hashers[0].HashPassword(password)
|
|
}
|
|
|
|
// VerifyPasswordWithHashers verifies an entered password against a hashed password using one or more algorithms. It returns whether the hash is "stale" (i.e., was verified using something other than the FIRST hasher specified).
|
|
func verifyPasswordWithHashers(password, hashedPassword string, hashers []PasswordHasher) (bool, bool) {
|
|
// Even though good hashers will disallow blank passwords, let's be explicit that ALL BLANK PASSWORDS ARE INVALID. Full stop.
|
|
if password == "" {
|
|
return false, false
|
|
}
|
|
|
|
valid, stale := false, false
|
|
|
|
for idx, hasher := range hashers {
|
|
if hasher.VerifyPassword(password, hashedPassword) {
|
|
valid = true
|
|
if idx > 0 {
|
|
stale = true
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
return valid, stale
|
|
}
|
|
|
|
// HashPassword hashes against the current preferred hasher.
|
|
func HashPassword(password string) (string, error) {
|
|
return hashPasswordWithHashers(password, preferredHashers)
|
|
}
|
|
|
|
// VerifyPassword verifies an entered password against a hashed password and returns whether the hash is "stale" (i.e., was verified using the FIRST preferred hasher above).
|
|
func VerifyPassword(password, hashedPassword string) (valid, stale bool) {
|
|
valid, stale = verifyPasswordWithHashers(password, hashedPassword, preferredHashers)
|
|
return
|
|
}
|
|
|
|
// HashPassword creates a one-way digest ("hash") of a password. In the case of Bcrypt, a pseudorandom salt is included automatically by the underlying library.
|
|
func (h DummyPasswordHasher) HashPassword(password string) (string, error) {
|
|
return password, nil
|
|
}
|
|
|
|
// VerifyPassword validates whether a one-way digest ("hash") of a password was created from a given plaintext password.
|
|
func (h DummyPasswordHasher) VerifyPassword(password, hashedPassword string) bool {
|
|
return 1 == subtle.ConstantTimeCompare([]byte(password), []byte(hashedPassword))
|
|
}
|
|
|
|
// HashPassword creates a one-way digest ("hash") of a password. In the case of Bcrypt, a pseudorandom salt is included automatically by the underlying library. For security reasons, the work factor is always at _least_ bcrypt.DefaultCost.
|
|
func (h BcryptPasswordHasher) HashPassword(password string) (string, error) {
|
|
cost := h.Cost
|
|
if cost < bcrypt.DefaultCost {
|
|
cost = bcrypt.DefaultCost
|
|
}
|
|
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), cost)
|
|
if err != nil {
|
|
hashedPassword = []byte("")
|
|
}
|
|
return string(hashedPassword), err
|
|
}
|
|
|
|
// VerifyPassword validates whether a one-way digest ("hash") of a password was created from a given plaintext password.
|
|
func (h BcryptPasswordHasher) VerifyPassword(password, hashedPassword string) bool {
|
|
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
|
|
return err == nil
|
|
}
|