Last active
October 30, 2017 19:15
-
-
Save kschaper/0efb060c671ea550976317f58c4afaab to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"html/template" | |
"log" | |
"net/http" | |
"github.com/gorilla/mux" | |
"github.com/gorilla/sessions" | |
) | |
type invitation struct { | |
email string // invitee's email address provided by an existing user | |
code string // generated code for this email, redeemable once | |
} | |
var code = "5d41402abc4b2a76b9719d911017c592" | |
// invitations "db" with one created record | |
var invitations = []*invitation{ | |
&invitation{ | |
email: "[email protected]", | |
code: code, | |
}, | |
} | |
func getInvitation(code string) *invitation { | |
fmt.Println(code, invitations) | |
for _, i := range invitations { | |
if i.code == code { | |
return i | |
} | |
} | |
return nil | |
} | |
type user struct { | |
id int | |
username, email string | |
password string // in real code I store a bcrypt hash instead | |
} | |
// users "db" | |
var users []*user | |
func userExists(id int) bool { | |
for _, u := range users { | |
if u.id == id { | |
return true | |
} | |
} | |
return false | |
} | |
// session stuff | |
// TODO replace keys by values generated by `securecookie.GenerateRandomKey()` | |
// see http://www.gorillatoolkit.org/pkg/sessions#NewCookieStore | |
var store = sessions.NewCookieStore([]byte("authentication-key"), []byte("encryption-key")) | |
func getSession(w http.ResponseWriter, r *http.Request) *sessions.Session { | |
session, err := store.Get(r, "session-name") | |
if err != nil { | |
http.Error(w, "Internal Server Error", http.StatusInternalServerError) | |
return nil | |
} | |
return session | |
} | |
// templates | |
const formTpl = ` | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>accept invitation</title> | |
</head> | |
<body> | |
<form action="/signup" method="post"> | |
<p>username: <input type="text" name="username"> enter anything</p> | |
<p>password: <input type="password" name="password"> enter anything</p> | |
<p> | |
<input type="hidden" name="code" value="{{.Code}}"> | |
<input type="submit" value="sign up"> | |
</p> | |
</form> | |
</body> | |
</html> | |
` | |
const appTpl = ` | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>app</title> | |
</head> | |
<body> | |
<h1>hello</h1> | |
</body> | |
</html> | |
` | |
// handlers | |
var formHandler = func(w http.ResponseWriter, r *http.Request) { | |
var data struct{ Code string } | |
data.Code = mux.Vars(r)["code"] | |
t := template.Must(template.New("form").Parse(formTpl)) | |
if err := t.Execute(w, data); err != nil { | |
http.Error(w, "Internal Server Error", http.StatusInternalServerError) | |
} | |
} | |
var acceptionHandler = func(w http.ResponseWriter, r *http.Request) { | |
var ( | |
code = r.FormValue("code") | |
username = r.FormValue("username") | |
password = r.FormValue("password") | |
) | |
if code == "" || username == "" || password == "" { | |
log.Println("required form value missing", code, username, password) | |
http.Error(w, "Bad Request", http.StatusBadRequest) | |
return | |
} | |
invitation := getInvitation(code) | |
if invitation == nil { | |
log.Println("invitation not found") | |
http.Error(w, "Bad Request", http.StatusBadRequest) | |
return | |
} | |
user := &user{123, username, invitation.email, password} | |
users = append(users, user) | |
fmt.Println(users) | |
session := getSession(w, r) | |
session.Values["user_id"] = user.id | |
session.Save(r, w) | |
http.Redirect(w, r, "/app", http.StatusFound) | |
} | |
var appHandler = func(w http.ResponseWriter, r *http.Request) { | |
t := template.Must(template.New("app").Parse(appTpl)) | |
if err := t.Execute(w, nil); err != nil { | |
http.Error(w, "Internal Server Error", http.StatusInternalServerError) | |
} | |
} | |
func authMiddleware(next func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { | |
return func(w http.ResponseWriter, r *http.Request) { | |
session := getSession(w, r) | |
userID, found := session.Values["user_id"] | |
if !found { | |
log.Println("no user_id found in session") | |
http.Error(w, "unauthorised", http.StatusUnauthorized) | |
return | |
} | |
if userExists(userID.(int)) == false { | |
log.Println("user unknown") | |
http.Error(w, "unauthorised", http.StatusUnauthorized) | |
return | |
} | |
store.MaxAge(604800) // renew session 1 week | |
store.Save(r, w, session) | |
next(w, r) | |
} | |
} | |
func main() { | |
r := mux.NewRouter() | |
// no authentication required | |
r.HandleFunc("/signup/{code:[a-z0-9]{32}}", formHandler).Methods("GET") | |
r.HandleFunc("/signup", acceptionHandler).Methods("POST") | |
// authentication required | |
r.HandleFunc("/app", authMiddleware(appHandler)).Methods("GET") | |
http.Handle("/", r) | |
log.Printf("open http://localhost:3000/signup/%s in your browser, please", code) | |
if err := http.ListenAndServe("localhost:3000", r); err != nil { | |
log.Panicln(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment