Skip to content

Commit

Permalink
auth: passwords can be maked as hashed (bcrypt)
Browse files Browse the repository at this point in the history
Passwords stored in auth file can be bcrypt hashes of the password
passed via the basic auth of the received request.

However unmakred passwords will be considered plain text thus
maintaining backward compatibility.

Added testcases.

Closes #395
  • Loading branch information
sum12 committed Mar 16, 2018
1 parent f6672dd commit 03dcfc4
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 5 deletions.
26 changes: 21 additions & 5 deletions auth/credential_store.go
Expand Up @@ -5,6 +5,8 @@ package auth
import (
"encoding/json"
"io"

"golang.org/x/crypto/bcrypt"
)

// BasicAuther is the interface an object must support to return basic auth information.
Expand All @@ -16,20 +18,23 @@ type BasicAuther interface {
type Credential struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
Hashed *bool `json:"hashed,omitempty"`
Perms []string `json:"perms,omitempty"`
}

// CredentialsStore stores authentication and authorization information for all users.
type CredentialsStore struct {
store map[string]string
perms map[string]map[string]bool
store map[string]string
perms map[string]map[string]bool
isbcrypted map[string]bool
}

// NewCredentialsStore returns a new instance of a CredentialStore.
func NewCredentialsStore() *CredentialsStore {
return &CredentialsStore{
store: make(map[string]string),
perms: make(map[string]map[string]bool),
store: make(map[string]string),
perms: make(map[string]map[string]bool),
isbcrypted: make(map[string]bool),
}
}

Expand All @@ -53,6 +58,9 @@ func (c *CredentialsStore) Load(r io.Reader) error {
for _, p := range cred.Perms {
c.perms[cred.Username][p] = true
}
if cred.Hashed != nil && *cred.Hashed {
c.isbcrypted[cred.Username] = true
}
}

// Read closing bracket.
Expand All @@ -67,7 +75,15 @@ func (c *CredentialsStore) Load(r io.Reader) error {
// Check returns true if the password is correct for the given username.
func (c *CredentialsStore) Check(username, password string) bool {
pw, ok := c.store[username]
return ok && password == pw
if !ok {
return false
}
if _, ok = c.isbcrypted[username]; ok {
err := bcrypt.CompareHashAndPassword([]byte(pw), []byte(password))
return err == nil
} else {
return password == pw
}
}

// CheckRequest returns true if b contains a valid username and password.
Expand Down
53 changes: 53 additions & 0 deletions auth/credential_store_test.go
Expand Up @@ -162,6 +162,59 @@ func Test_AuthPermsLoadSingle(t *testing.T) {
}
}

func Test_AuthLoadHashedSingleRequest(t *testing.T) {
const jsonStream = `
[
{
"username": "username1",
"password": "$2a$10$fKRHxrEuyDTP6tXIiDycr.nyC8Q7UMIfc31YMyXHDLgRDyhLK3VFS",
"hashed": true
},
{"username": "username2", "password": "password2", "hashed":false}
]
`

store := NewCredentialsStore()
if err := store.Load(strings.NewReader(jsonStream)); err != nil {
t.Fatalf("failed to load multiple credentials: %s", err.Error())
}

b1 := &testBasicAuther{
username: "username1",
password: "password1",
ok: true,
}
b2 := &testBasicAuther{
username: "username2",
password: "password2",
ok: true,
}

b3 := &testBasicAuther{
username: "username1",
password: "wrong",
ok: true,
}
b4 := &testBasicAuther{
username: "username2",
password: "wrong",
ok: true,
}

if check := store.CheckRequest(b1); !check {
t.Fatalf("username1 (b1) credential not checked correctly via request")
}
if check := store.CheckRequest(b2); !check {
t.Fatalf("username2 (b2) credential not checked correctly via request")
}
if check := store.CheckRequest(b3); check {
t.Fatalf("username1 (b3) credential not checked correctly via request")
}
if check := store.CheckRequest(b4); check {
t.Fatalf("username2 (b4) credential not checked correctly via request")
}
}

func Test_AuthPermsRequestLoadSingle(t *testing.T) {
const jsonStream = `
[
Expand Down

0 comments on commit 03dcfc4

Please sign in to comment.