Merge pull request #549 from brennie/dev/bcrypt-htpasswd
Support bcrypt passwords in htpasswd
This commit is contained in:
		
						commit
						a94b0a8b25
					
				|  | @ -58,7 +58,10 @@ | |||
| [[projects]] | ||||
|   branch = "master" | ||||
|   name = "github.com/pquerna/cachecontrol" | ||||
|   packages = [".","cacheobject"] | ||||
|   packages = [ | ||||
|     ".", | ||||
|     "cacheobject" | ||||
|   ] | ||||
|   revision = "0dec1b30a0215bb68605dfc568e8855066c9202d" | ||||
| 
 | ||||
| [[projects]] | ||||
|  | @ -70,30 +73,60 @@ | |||
| [[projects]] | ||||
|   branch = "master" | ||||
|   name = "golang.org/x/crypto" | ||||
|   packages = ["ed25519","ed25519/internal/edwards25519"] | ||||
|   packages = [ | ||||
|     "bcrypt", | ||||
|     "blowfish", | ||||
|     "ed25519", | ||||
|     "ed25519/internal/edwards25519" | ||||
|   ] | ||||
|   revision = "9f005a07e0d31d45e6656d241bb5c0f2efd4bc94" | ||||
| 
 | ||||
| [[projects]] | ||||
|   branch = "master" | ||||
|   name = "golang.org/x/net" | ||||
|   packages = ["context","context/ctxhttp"] | ||||
|   packages = [ | ||||
|     "context", | ||||
|     "context/ctxhttp" | ||||
|   ] | ||||
|   revision = "9dfe39835686865bff950a07b394c12a98ddc811" | ||||
| 
 | ||||
| [[projects]] | ||||
|   branch = "master" | ||||
|   name = "golang.org/x/oauth2" | ||||
|   packages = [".","google","internal","jws","jwt"] | ||||
|   packages = [ | ||||
|     ".", | ||||
|     "google", | ||||
|     "internal", | ||||
|     "jws", | ||||
|     "jwt" | ||||
|   ] | ||||
|   revision = "9ff8ebcc8e241d46f52ecc5bff0e5a2f2dbef402" | ||||
| 
 | ||||
| [[projects]] | ||||
|   branch = "master" | ||||
|   name = "google.golang.org/api" | ||||
|   packages = ["admin/directory/v1","gensupport","googleapi","googleapi/internal/uritemplates"] | ||||
|   packages = [ | ||||
|     "admin/directory/v1", | ||||
|     "gensupport", | ||||
|     "googleapi", | ||||
|     "googleapi/internal/uritemplates" | ||||
|   ] | ||||
|   revision = "8791354e7ab150705ede13637a18c1fcc16b62e8" | ||||
| 
 | ||||
| [[projects]] | ||||
|   name = "google.golang.org/appengine" | ||||
|   packages = [".","internal","internal/app_identity","internal/base","internal/datastore","internal/log","internal/modules","internal/remote_api","internal/urlfetch","urlfetch"] | ||||
|   packages = [ | ||||
|     ".", | ||||
|     "internal", | ||||
|     "internal/app_identity", | ||||
|     "internal/base", | ||||
|     "internal/datastore", | ||||
|     "internal/log", | ||||
|     "internal/modules", | ||||
|     "internal/remote_api", | ||||
|     "internal/urlfetch", | ||||
|     "urlfetch" | ||||
|   ] | ||||
|   revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" | ||||
|   version = "v1.0.0" | ||||
| 
 | ||||
|  | @ -105,13 +138,17 @@ | |||
| 
 | ||||
| [[projects]] | ||||
|   name = "gopkg.in/square/go-jose.v2" | ||||
|   packages = [".","cipher","json"] | ||||
|   packages = [ | ||||
|     ".", | ||||
|     "cipher", | ||||
|     "json" | ||||
|   ] | ||||
|   revision = "f8f38de21b4dcd69d0413faf231983f5fd6634b1" | ||||
|   version = "v2.1.3" | ||||
| 
 | ||||
| [solve-meta] | ||||
|   analyzer-name = "dep" | ||||
|   analyzer-version = 1 | ||||
|   inputs-digest = "efab48a0e196c2a849bfbe9aa02d2ae28d281ce1bfe9f23720d648858eefc8e6" | ||||
|   inputs-digest = "b502c41a61115d14d6379be26b0300f65d173bdad852f0170d387ebf2d7ec173" | ||||
|   solver-name = "gps-cdcl" | ||||
|   solver-version = 1 | ||||
|  |  | |||
|  | @ -38,3 +38,7 @@ | |||
| [[constraint]] | ||||
|   name = "gopkg.in/fsnotify.v1" | ||||
|   version = "~1.2.0" | ||||
| 
 | ||||
| [[constraint]] | ||||
|   branch = "master" | ||||
|   name = "golang.org/x/crypto" | ||||
|  |  | |||
							
								
								
									
										22
									
								
								htpasswd.go
								
								
								
								
							
							
						
						
									
										22
									
								
								htpasswd.go
								
								
								
								
							|  | @ -7,10 +7,12 @@ import ( | |||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 
 | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| ) | ||||
| 
 | ||||
| // lookup passwords in a htpasswd file
 | ||||
| // The entries must have been created with -s for SHA encryption
 | ||||
| // Lookup passwords in a htpasswd file
 | ||||
| // Passwords must be generated with -B for bcrypt or -s for SHA1.
 | ||||
| 
 | ||||
| type HtpasswdFile struct { | ||||
| 	Users map[string]string | ||||
|  | @ -47,14 +49,20 @@ func (h *HtpasswdFile) Validate(user string, password string) bool { | |||
| 	if !exists { | ||||
| 		return false | ||||
| 	} | ||||
| 	if realPassword[:5] == "{SHA}" { | ||||
| 
 | ||||
| 	shaPrefix := realPassword[:5] | ||||
| 	if shaPrefix == "{SHA}" { | ||||
| 		shaValue := realPassword[5:] | ||||
| 		d := sha1.New() | ||||
| 		d.Write([]byte(password)) | ||||
| 		if realPassword[5:] == base64.StdEncoding.EncodeToString(d.Sum(nil)) { | ||||
| 			return true | ||||
| 		return shaValue == base64.StdEncoding.EncodeToString(d.Sum(nil)) | ||||
| 	} | ||||
| 	} else { | ||||
| 		log.Printf("Invalid htpasswd entry for %s. Must be a SHA entry.", user) | ||||
| 
 | ||||
| 	bcryptPrefix := realPassword[:4] | ||||
| 	if bcryptPrefix == "$2a$" || bcryptPrefix == "$2b$" || bcryptPrefix == "$2x$" || bcryptPrefix == "$2y$" { | ||||
| 		return bcrypt.CompareHashAndPassword([]byte(realPassword), []byte(password)) == nil | ||||
| 	} | ||||
| 
 | ||||
| 	log.Printf("Invalid htpasswd entry for %s. Must be a SHA or bcrypt entry.", user) | ||||
| 	return false | ||||
| } | ||||
|  |  | |||
|  | @ -2,11 +2,14 @@ package main | |||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| ) | ||||
| 
 | ||||
| func TestHtpasswd(t *testing.T) { | ||||
| func TestSHA(t *testing.T) { | ||||
| 	file := bytes.NewBuffer([]byte("testuser:{SHA}PaVBVZkYqAjCQCu6UBL2xgsnZhw=\n")) | ||||
| 	h, err := NewHtpasswd(file) | ||||
| 	assert.Equal(t, err, nil) | ||||
|  | @ -14,3 +17,21 @@ func TestHtpasswd(t *testing.T) { | |||
| 	valid := h.Validate("testuser", "asdf") | ||||
| 	assert.Equal(t, valid, true) | ||||
| } | ||||
| 
 | ||||
| func TestBcrypt(t *testing.T) { | ||||
| 	hash1, err := bcrypt.GenerateFromPassword([]byte("password"), 1) | ||||
| 	hash2, err := bcrypt.GenerateFromPassword([]byte("top-secret"), 2) | ||||
| 	assert.Equal(t, err, nil) | ||||
| 
 | ||||
| 	contents := fmt.Sprintf("testuser1:%s\ntestuser2:%s\n", hash1, hash2) | ||||
| 	file := bytes.NewBuffer([]byte(contents)) | ||||
| 
 | ||||
| 	h, err := NewHtpasswd(file) | ||||
| 	assert.Equal(t, err, nil) | ||||
| 
 | ||||
| 	valid := h.Validate("testuser1", "password") | ||||
| 	assert.Equal(t, valid, true) | ||||
| 
 | ||||
| 	valid = h.Validate("testuser2", "top-secret") | ||||
| 	assert.Equal(t, valid, true) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										2
									
								
								main.go
								
								
								
								
							
							
						
						
									
										2
									
								
								main.go
								
								
								
								
							|  | @ -52,7 +52,7 @@ func main() { | |||
| 	flagSet.String("client-id", "", "the OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"") | ||||
| 	flagSet.String("client-secret", "", "the OAuth Client Secret") | ||||
| 	flagSet.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)") | ||||
| 	flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -s\" for SHA encryption") | ||||
| 	flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -s\" for SHA encryption or \"htpasswd -B\" for bcrypt encryption") | ||||
| 	flagSet.Bool("display-htpasswd-form", true, "display username / password login form if an htpasswd file is provided") | ||||
| 	flagSet.String("custom-templates-dir", "", "path to custom html templates") | ||||
| 	flagSet.String("footer", "", "custom footer string. Use \"-\" to disable default footer.") | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue