Skip to content

Commit

Permalink
Merge pull request #53 from nnev/new_user
Browse files Browse the repository at this point in the history
new function to add new user
  • Loading branch information
koebi committed Dec 30, 2015
2 parents 56c4029 + 1d2a663 commit 97a2f83
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 2 deletions.
69 changes: 69 additions & 0 deletions http.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bytes"
"net/http"

"github.com/gorilla/mux"
Expand Down Expand Up @@ -64,6 +65,72 @@ func (k *Kasse) PostLoginPage(res http.ResponseWriter, req *http.Request) {
http.Redirect(res, req, redirect, http.StatusFound)
}

// GetNewUserPage renders the page to create a new user.
func (k *Kasse) GetNewUserPage(res http.ResponseWriter, req *http.Request) {
res.Header().Set("Content-Type", "text/html")

if err := ExecuteTemplate(res, TemplateInput{Title: "Create new user", Body: "newUser.html"}); err != nil {
k.log.Println("Could not render template:", err)
http.Error(res, "Internal error", http.StatusInternalServerError)
return
}
}

// PostNewUserPage receives a POST request with username and password and tries
// to create a new user. It will redirect to the first Flashvalue in the
// session on success, or to / if none is set and save the authenticated user
// in the session.
func (k *Kasse) PostNewUserPage(res http.ResponseWriter, req *http.Request) {
username := req.FormValue("username")
password := []byte(req.FormValue("password"))
confirm := []byte(req.FormValue("confirm"))

if username == "" || len(password) == 0 || len(confirm) == 0 {
// TODO: Write own Error function, that uses a template for better
// looking error pages. Also, redirect.
http.Error(res, "Neither username nor password can be empty", http.StatusBadRequest)
return
}

if !bytes.Equal(password, confirm) {
// TODO: Write own Error function, that uses a template for better
// looking error pages. Also, redirect.
http.Error(res, "Password and confirmation don't match", http.StatusBadRequest)
return
}

user, err := k.RegisterUser(username, password)
if err != nil && err != ErrUserExists {
k.log.Printf("Registering user %q failed:%v", username, err)
// TODO: Write own Error function, that uses a template for better
// looking error pages. Also, redirect.
http.Error(res, "Internal server error", http.StatusInternalServerError)
return
}

if err == ErrUserExists {
k.log.Println(err)
// TODO: Write own Error function, that uses a template for better
// looking error pages. Also, redirect.
http.Error(res, "User already exists.", http.StatusForbidden)
return
}

session, _ := k.sessions.Get(req, "nnev-kasse")
redirect := "/"
if v := session.Flashes(); len(v) > 0 {
if s, ok := v[0].(string); ok {
redirect = s
}
}
session.Values["user"] = user
if err := session.Save(req, res); err != nil {
k.log.Printf("Error saving session: %v", err)
}

http.Redirect(res, req, redirect, http.StatusFound)
}

// GetDashboard renders a basic dashboard, containing the most important
// information and actions for an account.
func (k *Kasse) GetDashboard(res http.ResponseWriter, req *http.Request) {
Expand Down Expand Up @@ -144,5 +211,7 @@ func (k *Kasse) Handler() http.Handler {
r.Methods("GET").Path("/login.html").HandlerFunc(k.GetLoginPage)
r.Methods("POST").Path("/login.html").HandlerFunc(k.PostLoginPage)
r.Methods("GET").Path("/logout.html").HandlerFunc(k.GetLogout)
r.Methods("GET").Path("/create_user.html").HandlerFunc(k.GetNewUserPage)
r.Methods("POST").Path("/create_user.html").HandlerFunc(k.PostNewUserPage)
return r
}
79 changes: 79 additions & 0 deletions http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,82 @@ func TestLogin(t *testing.T) {
}
}
}

func TestNewUser(t *testing.T) {
k := Kasse{db: createDB(t), log: testLogger(t)}
k.sessions = sessions.NewCookieStore([]byte("foobar"))
h := k.Handler()

jar, _ := cookiejar.New(nil)

tests := []struct {
// inputs
method string
url string
form url.Values

// expected outputs
code int
headers map[string]string
grep string
}{
// test for service being available
{"GET", "http://localhost:9000/", nil, http.StatusFound, map[string]string{"Location": "/login.html"}, ""},
// test for login page to be up
{"GET", "http://localhost:9000/login.html", nil, http.StatusOK, map[string]string{"Content-Type": "text/html"}, "<title>Login</title>"},
// test for create_user to exist
{"GET", "http://localhost:9000/create_user.html", nil, http.StatusOK, map[string]string{"Content-Type": "text/html"}, "<title>Create new user</title>"},
// test for working creation
{"POST", "http://localhost:9000/create_user.html", url.Values{"username": []string{"foo"}, "password": []string{"bar"}, "confirm": []string{"bar"}}, http.StatusFound, map[string]string{"Location": "/"}, ""},
// after creation, the user should already exist
{"POST", "http://localhost:9000/create_user.html", url.Values{"username": []string{"foo"}, "password": []string{"bar"}, "confirm": []string{"bar"}}, http.StatusForbidden, nil, "User already exists"},
// now trying to create user with empty name
{"POST", "http://localhost:9000/create_user.html", url.Values{"username": []string{""}, "password": []string{"bar"}, "confirm": []string{"bar"}}, http.StatusBadRequest, nil, "Neither username nor password can be empty"},
// now trying to create user with empty password
{"POST", "http://localhost:9000/create_user.html", url.Values{"username": []string{"joe"}, "password": []string{""}, "confirm": []string{"bar"}}, http.StatusBadRequest, nil, "Neither username nor password can be empty"},
// now trying to create user with nonmatching confirmation
{"POST", "http://localhost:9000/create_user.html", url.Values{"username": []string{"joe"}, "password": []string{"baz"}, "confirm": []string{"bar"}}, http.StatusBadRequest, nil, "Password and confirmation don't match"},
}

for _, tc := range tests {
var body io.Reader
if tc.form != nil {
body = strings.NewReader(tc.form.Encode())
}
req, err := http.NewRequest(tc.method, tc.url, body)
if err != nil {
t.Fatalf(`%s %s %v: %v`, tc.method, tc.url, tc.form, err)
}

if tc.form != nil {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
}
for _, c := range jar.Cookies(req.URL) {
t.Logf("Adding cookie %v", c)
req.AddCookie(c)
}

rec := httptest.NewRecorder()

h.ServeHTTP(rec, req)
if c := rec.Code; c != tc.code {
t.Fatalf(`%s %s %v has code %d, expected %d`, tc.method, tc.url, tc.form, c, tc.code)
}

for k, v := range tc.headers {
if gv := rec.HeaderMap.Get(k); gv != v {
t.Fatalf(`%s %s %v has header %q set to %q, expected %q`, tc.method, tc.url, tc.form, k, gv, v)
}
}

if !strings.Contains(rec.Body.String(), tc.grep) {
t.Fatalf("%s %s %v: Response does not contain %q\nFull Body:\n%s", tc.method, tc.url, tc.form, tc.grep, rec.Body.String())
}

res := createResponse(req, rec)
if c := res.Cookies(); len(c) > 0 {
t.Logf("Setting cookies %v", res.Cookies())
jar.SetCookies(req.URL, res.Cookies())
}
}
}
4 changes: 2 additions & 2 deletions templates/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<button class="mdl-button mdl-js-button mdl-button--colored" type="submit">
Login
</button>
<button class="mdl-button mdl-js-button mdl-button--colored" type="button">
<a href="/create_user.html" class="mdl-button mdl-js-button mdl-button--colored" type="button">
Neu erstellen
</button>
</a>
</form>
</div>
19 changes: 19 additions & 0 deletions templates/newUser.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<div class="mdl-card mdl-shadow--2dp" id="login-box">
<form method="POST">
<div class="mdl-textfield mdl-js-textfield">
<input class="mdl-textfield__input" type="text" name="username" />
<label class="mdl-textfield__label" for="username">Username</label>
</div>
<div class="mdl-textfield mdl-js-textfield">
<input class="mdl-textfield__input" type="password" name="password" />
<label class="mdl-textfield__label" for="password">Password</label>
</div>
<div class="mdl-textfield mdl-js-textfield">
<input class="mdl-textfield__input" type="password" name="confirm" />
<label class="mdl-textfield__label" for="confirm">Passwort bestätigen</label>
</div>
<button class="mdl-button mdl-js-button mdl-button--colored" type="submit">
Erstellen
</button>
</form>
</div>

0 comments on commit 97a2f83

Please sign in to comment.