auth
2014-09-01
Parent:1aa3a85ff853
auth/session.go.old
Rough out tokens and begin the memstore. Rough out the Token type for working with OAuth2 access and refresh tokens. Rough out the TokenStore interface that dictates how Tokens will be stored and retrieved. Write tests for the successful (in the working-as-intended sense) calls to TokenStore. Begin a Memstore type that stores data in memory. Implement the TokenStore interface for Memstore.
| paddy@23 | 1 package auth |
| paddy@23 | 2 |
| paddy@23 | 3 import ( |
| paddy@23 | 4 "errors" |
| paddy@23 | 5 "net/http" |
| paddy@23 | 6 "net/url" |
| paddy@23 | 7 "time" |
| paddy@23 | 8 |
| paddy@23 | 9 "strings" |
| paddy@23 | 10 "secondbit.org/uuid" |
| paddy@23 | 11 ) |
| paddy@23 | 12 |
| paddy@23 | 13 const sessionCookie = "session" |
| paddy@23 | 14 |
| paddy@23 | 15 var ( |
| paddy@23 | 16 ErrSessionNotFound = errors.New("Session not found.") |
| paddy@23 | 17 ) |
| paddy@23 | 18 |
| paddy@23 | 19 type Session struct { |
| paddy@23 | 20 Token string |
| paddy@23 | 21 Profile uuid.ID |
| paddy@23 | 22 Expires time.Time |
| paddy@23 | 23 Created time.Time |
| paddy@23 | 24 IP string |
| paddy@23 | 25 } |
| paddy@23 | 26 |
| paddy@23 | 27 type SessionStore interface { |
| paddy@23 | 28 GetSession(token string) (Session, error) |
| paddy@23 | 29 GetAllSessions(userID uuid.ID, num, page int) ([]Session, error) |
| paddy@23 | 30 SaveSession(session Session) error |
| paddy@23 | 31 DeleteSession(sessionID uuid.ID) error |
| paddy@23 | 32 } |
| paddy@23 | 33 |
| paddy@23 | 34 func validateSession(r *http.Request, c Context) error { |
| paddy@23 | 35 cookie, err := r.Cookie(sessionCookie) |
| paddy@23 | 36 if err == http.ErrNoCookie { |
| paddy@23 | 37 return ErrSessionNotFound |
| paddy@23 | 38 } |
| paddy@23 | 39 _, err = c.Sessions.GetSession(cookie.Value) |
| paddy@23 | 40 return err |
| paddy@23 | 41 } |
| paddy@23 | 42 |
| paddy@23 | 43 func HandleLoginRequest(w http.ResponseWriter, r *http.Request, ctx Context) { |
| paddy@23 | 44 if r.Method == "GET" { |
| paddy@23 | 45 ctx.RenderLogin(w, r) |
| paddy@23 | 46 return |
| paddy@23 | 47 } else if r.Method != "POST" { |
| paddy@23 | 48 // TODO: return bad method error |
| paddy@23 | 49 return |
| paddy@23 | 50 } |
| paddy@23 | 51 |
| paddy@23 | 52 if r.FormValue("username") == "" || r.FormValue("password") == "" { |
| paddy@23 | 53 // TODO: return unauthenticated error |
| paddy@23 | 54 return |
| paddy@23 | 55 } |
| paddy@23 | 56 id, err := ctx.Profiles.GetProfile(r.FormValue("username"), r.FormValue("password")) |
| paddy@23 | 57 if err != nil { |
| paddy@23 | 58 if err == ErrProfileNotFound { |
| paddy@23 | 59 // TODO: return unauthenticated error |
| paddy@23 | 60 return |
| paddy@23 | 61 } |
| paddy@23 | 62 // TODO: return internal server error |
| paddy@23 | 63 return |
| paddy@23 | 64 } |
| paddy@23 | 65 session := Session{ |
| paddy@23 | 66 Token: newToken(), |
| paddy@23 | 67 Profile: id, |
| paddy@23 | 68 Expires: time.Now().Add(ctx.Config.SessionLength), |
| paddy@23 | 69 Created: time.Now(), |
| paddy@23 | 70 IP: r.Header.Get(ctx.Config.RequestIPHeader), |
| paddy@23 | 71 } |
| paddy@23 | 72 err = ctx.Sessions.SetSession(session) |
| paddy@23 | 73 if err != nil { |
| paddy@23 | 74 // TODO: return internal server error |
| paddy@23 | 75 return |
| paddy@23 | 76 } |
| paddy@23 | 77 http.SetCookie(w, &http.Cookie{ |
| paddy@23 | 78 Name: sessionCookie, |
| paddy@23 | 79 Value: session.Token, |
| paddy@23 | 80 Expires: session.Expires, |
| paddy@23 | 81 Secure: true, |
| paddy@23 | 82 HttpOnly: true, |
| paddy@23 | 83 }) |
| paddy@23 | 84 |
| paddy@23 | 85 redirectString := r.URL.Query().Get("redirect_to") |
| paddy@23 | 86 if redirectString != "" { |
| paddy@23 | 87 redirectURI, err := url.Parse(redirectString) |
| paddy@23 | 88 if err != nil { |
| paddy@23 | 89 // TODO: render a bad request error |
| paddy@23 | 90 return |
| paddy@23 | 91 } |
| paddy@23 | 92 if !strings.HasSuffix("."+ctx.Config.LoginRedirectDomain, redirectURI.Host) && redirectURI.Host != ctx.Config.LoginRedirectDomain { |
| paddy@23 | 93 // TODO: render a bad request error |
| paddy@23 | 94 return |
| paddy@23 | 95 } |
| paddy@23 | 96 } else { |
| paddy@23 | 97 redirectString = "https://" + ctx.Config.LoginRedirectDomain |
| paddy@23 | 98 } |
| paddy@23 | 99 http.Redirect(w, r, redirectString, http.StatusFound) |
| paddy@23 | 100 return |
| paddy@23 | 101 } |