From 303e6ca22943847cb09e1163d32872331a25852d Mon Sep 17 00:00:00 2001 From: Mihai Parparita Date: Mon, 28 Jul 2014 22:25:40 -0700 Subject: [PATCH] Persit OAuth token in datastore. --- TODO | 3 +- app/config/session.json.SAMPLE | 2 +- app/githop.go | 65 ++++++++++++++++++++++++++++++---- 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/TODO b/TODO index 77fb1dd..2de1f7b 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,6 @@ TODO -- Persist OAuth token in the datastore and use session cookies +- Break up github.go +- Loop over registered accounts and send them email - Flash message and sign out when OAuth token has expired/is invalid - Handle pagination for user repository list - Handle pagination for user organization list diff --git a/app/config/session.json.SAMPLE b/app/config/session.json.SAMPLE index e70d30a..26c8757 100644 --- a/app/config/session.json.SAMPLE +++ b/app/config/session.json.SAMPLE @@ -2,5 +2,5 @@ "AuthenticationKey": "REPLACE_ME_WITH_A_32_BYTE_BASE_64_ENCODED_KEY_BYES", "EncryptionKey": "REPLACE_ME_WITH_A_32_BYTE_BASE_64_ENCODED_KEY_BYTES", "CookieName": "session", - "TokenKey": "token_temp" + "UserIdKey": "user_id" } diff --git a/app/githop.go b/app/githop.go index ca50494..339ea45 100644 --- a/app/githop.go +++ b/app/githop.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "encoding/base64" + "encoding/gob" "encoding/json" "html/template" "io/ioutil" @@ -15,6 +16,7 @@ import ( "time" "appengine" + "appengine/datastore" "appengine/memcache" "appengine/urlfetch" @@ -33,7 +35,39 @@ type SessionConfig struct { AuthenticationKey string EncryptionKey string CookieName string - TokenKey string + UserIdKey string +} + +type Account struct { + GitHubUserId int `datastore:",noindex"` + // The datastore API doesn't store maps, and the token contains one. We + // thefore store a gob-serialized version instead. + OAuthTokenSerialized []byte + OAuthToken oauth.Token `datastore:"-,"` +} + +func GetAccount(c appengine.Context, gitHubUserId int) (*Account, error) { + key := datastore.NewKey(c, "Account", "", int64(gitHubUserId), nil) + account := new(Account) + err := datastore.Get(c, key, account) + if err != nil { + return nil, err + } + r := bytes.NewBuffer(account.OAuthTokenSerialized) + err = gob.NewDecoder(r).Decode(&account.OAuthToken) + return account, err +} + +func (account *Account) Put(c appengine.Context) error { + w := new(bytes.Buffer) + err := gob.NewEncoder(w).Encode(&account.OAuthToken) + if err != nil { + return err + } + account.OAuthTokenSerialized = w.Bytes() + key := datastore.NewKey(c, "Account", "", int64(account.GitHubUserId), nil) + _, err = datastore.Put(c, key, account) + return err } type RepoDigest struct { @@ -165,7 +199,7 @@ func signOutHandler(w http.ResponseWriter, r *http.Request) { func indexHandler(w http.ResponseWriter, r *http.Request) { session, _ := sessionStore.Get(r, sessionConfig.CookieName) - tokenEncoded, ok := session.Values[sessionConfig.TokenKey].(string) + userId, ok := session.Values[sessionConfig.UserIdKey].(int) if !ok { signInUrl, _ := router.Get("sign-in").URL() var data = map[string]string{ @@ -176,14 +210,20 @@ func indexHandler(w http.ResponseWriter, r *http.Request) { } return } - token, err := decodeOAuthToken(tokenEncoded) + account, err := GetAccount(appengine.NewContext(r), userId) + if account == nil { + // Can't look up the account, session cookie must be invalid, clear it. + indexUrl, _ := router.Get("sign-out").URL() + http.Redirect(w, r, indexUrl.String(), http.StatusFound) + return + } if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } oauthTransport := githubOAuthTransport(r) - oauthTransport.Token = token + oauthTransport.Token = &account.OAuthToken githubClient := github.NewClient(oauthTransport.Client()) user, _, err := githubClient.Users.Get("") @@ -257,14 +297,27 @@ func githubOAuthCallbackHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusInternalServerError) return } - tokenEncoded, err := encodeOAuthToken(token) + + oauthTransport.Token = token + githubClient := github.NewClient(oauthTransport.Client()) + user, _, err := githubClient.Users.Get("") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + account := &Account{ + GitHubUserId: *user.ID, + OAuthToken: *token, + } + err = account.Put(appengine.NewContext(r)) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } session, _ := sessionStore.Get(r, sessionConfig.CookieName) - session.Values[sessionConfig.TokenKey] = tokenEncoded + session.Values[sessionConfig.UserIdKey] = user.ID session.Save(r, w) indexUrl, _ := router.Get("index").URL() http.Redirect(w, r, indexUrl.String(), http.StatusFound)