Skip to content

Commit

Permalink
Working on user roles.
Browse files Browse the repository at this point in the history
  • Loading branch information
apexskier committed Mar 19, 2014
1 parent b8349dc commit 894bd74
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 19 deletions.
51 changes: 41 additions & 10 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,26 @@ import (
"net/http"
)

// Role represents an interal role: a string mapped to an integer. Roles must
// be greater than zero.
type Role int

// UserData represents a single user. It contains the users username and email
// as well as a has of their username and password.
type UserData struct {
Username string
Email string
Hash []byte
Role string
}

// Authorizer structures contain the store of user session cookies a reference
// to a backend storage system.
type Authorizer struct {
cookiejar *sessions.CookieStore
backend AuthBackend
cookiejar *sessions.CookieStore
backend AuthBackend
defaultRole string
roles map[string]Role
}

// The AuthBackend interface defines a set of methods an AuthBackend must
Expand Down Expand Up @@ -64,10 +71,16 @@ func (a Authorizer) goBack(rw http.ResponseWriter, req *http.Request) {

// NewAuthorizer returns a new Authorizer given an AuthBackend and a cookie
// store key. If the key changes, logged in users will need to reauthenticate.
func NewAuthorizer(backend AuthBackend, key []byte) (a Authorizer) {
func NewAuthorizer(backend AuthBackend, key []byte, defaultRole string, roles map[string]Role) (Authorizer, error) {
var a Authorizer
a.cookiejar = sessions.NewCookieStore([]byte(key))
a.backend = backend
return a
a.roles = roles
a.defaultRole = defaultRole
if _, ok := roles[defaultRole]; !ok {
return a, errors.New("defaultRole not found in roles")
}
return a, nil
}

// Login logs a user in. They will be redirected to dest or to the last
Expand Down Expand Up @@ -101,24 +114,42 @@ func (a Authorizer) Login(rw http.ResponseWriter, req *http.Request, u string, p

// Register and save a new user. Returns an error and adds a message if the
// username is in use.
func (a Authorizer) Register(rw http.ResponseWriter, req *http.Request, u string, p string, e string) error {
if _, ok := a.backend.User(u); ok {
func (a Authorizer) Register(rw http.ResponseWriter, req *http.Request, user UserData, password string) error {
if user.Username == "" {
return errors.New("no username given")
}
if user.Email == "" {
return errors.New("no email given")
}
if user.Hash != nil {
return errors.New("hash will be overwritten")
}
if password == "" {
return errors.New("no password given")
}

// Validate username
if _, ok := a.backend.User(user.Username); ok {
a.addMessage(rw, req, "Username has been taken.")
return errors.New("user already exists")
}

hash, err := bcrypt.GenerateFromPassword([]byte(u+p), 8)
// Generate and save hash
hash, err := bcrypt.GenerateFromPassword([]byte(user.Username+password), 8)
if err != nil {
return errors.New("couldn't save password: " + err.Error())
}
user.Hash = hash

user := UserData{u, e, hash}
if user.Role == "" {
user.Role = a.defaultRole
}

err = a.backend.SaveUser(user)
if err != nil {
a.addMessage(rw, req, err.Error())
}
return nil
return err
}

// Update changes data for an existing user. Needs thought...
Expand Down Expand Up @@ -151,7 +182,7 @@ func (a Authorizer) Update(rw http.ResponseWriter, req *http.Request, p string,
email = user.Email
}

newuser := UserData{username, email, hash}
newuser := UserData{username, email, hash, user.Role}

err = a.backend.SaveUser(newuser)
if err != nil {
Expand Down
19 changes: 15 additions & 4 deletions auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ var (
func init() {
os.Remove(file)
b = NewGobFileAuthBackend(file)
a = NewAuthorizer(b, []byte("secret-key"))
roles := make(map[string]Role)
roles["user"] = 40
roles["admin"] = 80
a, _ = NewAuthorizer(b, []byte("testkey"), "user", roles)
t, _ := time.Parse("Mon, 02 Jan 2006 15:04:05 MST", "Mon, 07 Apr 2014 21:47:54 UTC")
authCookie = http.Cookie{
Name: "auth",
Expand All @@ -30,21 +33,29 @@ func init() {
}

func TestNewAuthorizer(t *testing.T) {
a = NewAuthorizer(b, []byte("testkey"))
roles := make(map[string]Role)
roles["user"] = 40
roles["admin"] = 80
_, err := NewAuthorizer(b, []byte("testkey"), "user", roles)
if err != nil {
t.Fatalf(err.Error())
}
}

func TestRegister(t *testing.T) {
rw := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/", nil)
err := a.Register(rw, req, "username", "password", "[email protected]")
newUser := UserData{Username:"username", Email:"[email protected]"}
err := a.Register(rw, req, newUser, "password")
if rw.Code != http.StatusOK {
t.Fatalf("Register: Wrong status code: %v", rw.Code)
}
if err != nil {
t.Fatalf("Register: error %v", err)
}

err = a.Register(rw, req, "username", "password", "[email protected]")
newUser2 := UserData{Username:"username", Email:"[email protected]"}
err = a.Register(rw, req, newUser2, "password")
if rw.Code != http.StatusOK {
t.Fatalf("Register: Wrong status code: %v", rw.Code)
}
Expand Down
5 changes: 4 additions & 1 deletion examples/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ var (

func main() {
backend = httpauth.NewGobFileAuthBackend("auth.gob")
aaa = httpauth.NewAuthorizer(backend, []byte("cookie-encryption-key"))
aaa, err = httpauth.NewAuthorizer(backend, []byte("cookie-encryption-key"))
if err != nil {
panic(err)
}

// set up routers and route handlers
r := mux.NewRouter()
Expand Down
8 changes: 4 additions & 4 deletions gobfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ func TestNewGobFileAuthBackend(t *testing.T) {
}

func TestSaveUser(t *testing.T) {
user := UserData{"username", "email", []byte("passwordhash")}
user := UserData{Username:"username", Email:"email", Hash:[]byte("passwordhash")}
b.SaveUser(user)

user2 := UserData{"username2", "email2", []byte("passwordhash2")}
user2 := UserData{Username:"username2", Email:"email2", Hash:[]byte("passwordhash2")}
b.SaveUser(user2)

if len(b.users) != 2 {
Expand Down Expand Up @@ -138,7 +138,7 @@ func TestUsers(t *testing.T) {
}

func TestUpdateUser_gob(t *testing.T) {
user2 := UserData{"username", "email", []byte("newpassword")}
user2 := UserData{Username:"username", Email:"email", Hash:[]byte("newpassword")}
if err := b.SaveUser(user2); err != nil {
t.Fatalf("SaveUser gob error: %v", err)
}
Expand All @@ -153,7 +153,7 @@ func TestUpdateUser_gob(t *testing.T) {
t.Fatal("User email not correct.")
}
if !bytes.Equal(u2.Hash, []byte("newpassword")) {
t.Fatal("User password not correct.")
t.Fatalf("User password not correct. Got %v, expected %v", u2.Hash, []byte("newpassword"))
}
}

Expand Down

0 comments on commit 894bd74

Please sign in to comment.