Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit 3cb49147a61053c7fc082e4d65a5a758649d0f70 @stesla committed Mar 5, 2012
Showing with 199 additions and 0 deletions.
  1. +4 −0 main.go
  2. +116 −0 user.go
  3. +79 −0 user_test.go
4 main.go
@@ -0,0 +1,4 @@
+package main
+
+func main() {
+}
116 user.go
@@ -0,0 +1,116 @@
+package main
+
+import (
+ "bytes"
+ "crypto/rand"
+ "crypto/sha1"
+ "errors"
+ "sync"
+)
+
+type User struct {
+ Name string
+ pwhash []byte
+}
+
+type UserStore struct {
+ users map[string]User
+ rw sync.RWMutex
+}
+
+func NewUserStore() *UserStore {
+ return &UserStore{users: make(map[string]User)}
+}
+
+func (s *UserStore) Create(name, password string) (*User, error) {
+ if name == "" {
+ return nil, errors.New("blank username")
+ }
+ if password == "" {
+ return nil, errors.New("blank password")
+ }
+ s.rw.Lock()
+ defer s.rw.Unlock()
+ if _, present := s.users[name]; present {
+ return nil, errors.New("name already in use")
+ }
+ user := &User{Name: name}
+ if err := user.SetPassword(password); err != nil {
+ return nil, err
+ }
+ s.users[name] = *user
+ return user, nil
+}
+
+func (s *UserStore) Get(name string, user *User) error {
+ if user == nil {
+ return errors.New("nil user")
+ }
+ s.rw.RLock()
+ defer s.rw.RUnlock()
+ var present bool
+ *user, present = s.users[name]
+ if !present {
+ return errors.New("not found")
+ }
+ return nil
+}
+
+func (s *UserStore) Update(user *User) error {
+ if user == nil {
+ return errors.New("nil user")
+ }
+ if user.Name == "" {
+ return errors.New("blank username")
+ }
+ if user.pwhash == nil {
+ return errors.New("nil password")
+ }
+ s.rw.Lock()
+ defer s.rw.Unlock()
+ s.users[user.Name] = *user
+ return nil
+}
+
+const saltLength = 4
+
+func hashPassword(salt []byte, password string) ([]byte, error) {
+ slt := make([]byte, saltLength)
+ if salt == nil {
+ if n, err := rand.Read(slt); err != nil {
+ return nil, err
+ } else if n < saltLength {
+ return nil, errors.New("not enough salt")
+ }
+ } else {
+ if copy(slt, salt) < saltLength {
+ return nil, errors.New("not enough salt")
+ }
+ }
+ hash := sha1.New()
+ if _, err := hash.Write(slt); err != nil {
+ return nil, err
+ }
+ if _, err := hash.Write([]byte(password)); err != nil {
+ return nil, err
+ }
+ return hash.Sum(slt), nil
+}
+
+func (u User) Authenticate(password string) error {
+ if b, err := hashPassword(u.pwhash[:saltLength], password); err != nil {
+ return err
+ } else if !bytes.Equal(b, u.pwhash) {
+ return errors.New("invalid password")
+ }
+ return nil
+}
+
+func (u *User) SetPassword(password string) error {
+ if pw, err := hashPassword(nil, password); err != nil {
+ return err
+ } else {
+ u.pwhash = pw
+ }
+ return nil
+}
79 user_test.go
@@ -0,0 +1,79 @@
+package main
+
+import (
+ "testing"
+)
+
+func setupStore(t *testing.T) *UserStore {
+ store := NewUserStore()
+ if _, err := store.Create("user1", "pw1"); err != nil {
+ t.Fatal("setupStore:", err)
+ }
+ return store
+}
+
+func TestCreate(t *testing.T) {
+ store := setupStore(t)
+ if _, err := store.Create("","pw"); err == nil {
+ t.Fatal("expected error: blank name")
+ }
+
+ if _, err := store.Create("name", ""); err == nil {
+ t.Fatal("expected error: blank password")
+ }
+
+ if _, err := store.Create("user1", "pw2"); err == nil {
+ t.Fatal("expected create to fail: user already exists")
+ }
+
+ if _, err := store.Create("user2", "pw2"); err != nil {
+ t.Fatal("Create:", err)
+ }
+}
+
+func TestAuthenticate(t *testing.T) {
+ var user User
+
+ store := setupStore(t)
+
+ if err := store.Get("user1", &user); err != nil {
+ t.Fatal("Get:", err)
+ }
+ if err := user.Authenticate("pw1"); err != nil {
+ t.Fatalf("expected authentication to pass:", err)
+ }
+ if user.Authenticate("pw2") == nil {
+ t.Fatalf("expected authentication to fail")
+ }
+}
+
+func TestUpdate(t *testing.T) {
+ var user1, user2 User
+
+ store := setupStore(t)
+
+ if store.Update(nil) == nil {
+ t.Fatal("expected error: nil user")
+ }
+ if store.Update(&User{pwhash: []byte{}}) == nil {
+ t.Fatal("expected error: blank username")
+ }
+ if store.Update(&User{Name: "foo"}) == nil {
+ t.Fatal("expected error: nil password")
+ }
+ if err := store.Get("user1", &user1); err != nil {
+ t.Fatal("Get:", err)
+ }
+ if err := user1.SetPassword("newpass"); err != nil {
+ t.Fatal("SetPassword:", err)
+ }
+ if err := store.Update(&user1); err != nil {
+ t.Fatal("Update:", err)
+ }
+ if err := store.Get("user1", &user2); err != nil {
+ t.Fatal("Get:", err)
+ }
+ if err := user2.Authenticate("newpass"); err != nil {
+ t.Fatal("expected authentication to pass:", err)
+ }
+}

0 comments on commit 3cb4914

Please sign in to comment.