socialnetworknews-api

git clone git://archive.git.mtrnord.blog/SocialNetworkNews/socialnetworknews-api.git
Log | Files | Refs | LICENSE

twitter.go (6684B)


      1 package login
      2 
      3 import (
      4 	"encoding/json"
      5 	"fmt"
      6 	"github.com/SocialNetworkNews/SocialNetworkNews_API/api/util"
      7 	"github.com/SocialNetworkNews/SocialNetworkNews_API/db"
      8 	"github.com/dghubble/gologin"
      9 	libLogin "github.com/dghubble/gologin/oauth1"
     10 	oauth1Login "github.com/dghubble/gologin/oauth1"
     11 	"github.com/dghubble/gologin/twitter"
     12 	"github.com/dghubble/oauth1"
     13 	"github.com/dghubble/sessions"
     14 	"github.com/dgraph-io/badger"
     15 	"github.com/satori/go.uuid"
     16 	"log"
     17 	"net/http"
     18 )
     19 
     20 var TConfig *oauth1.Config
     21 
     22 const (
     23 	sessionName    = "ssn-app"
     24 	sessionSecret  = "006b2294-201d-4283-9114-149b3347b264"
     25 	sessionUserKey = "twitterID"
     26 )
     27 
     28 // sessionStore encodes and decodes session data stored in signed cookies
     29 var sessionStore = sessions.NewCookieStore([]byte(sessionSecret), nil)
     30 
     31 func getUserUUID(id string) (string, error) {
     32 	var uuidS string
     33 	dataDB, err := db.OpenDB()
     34 	if err != nil {
     35 		return "", err
     36 	}
     37 
     38 	DBerr := dataDB.View(func(txn *badger.Txn) error {
     39 		data, err := db.Get(txn, []byte(fmt.Sprintf("users|T|%s|uuid", id)))
     40 		uuidS = fmt.Sprintf("%s", data)
     41 		return err
     42 	})
     43 
     44 	return uuidS, DBerr
     45 }
     46 
     47 func saveTUser(id, accessToken, accessSecret, name string, data []byte) error {
     48 	dataDB, err := db.OpenDB()
     49 	if err != nil {
     50 		return err
     51 	}
     52 
     53 	puuid, UUIDerr := uuid.NewV4()
     54 	if UUIDerr != nil {
     55 		return UUIDerr
     56 	}
     57 	newUUID := puuid.String()
     58 
     59 	return dataDB.Update(func(txn *badger.Txn) error {
     60 		ATerr := txn.Set([]byte(fmt.Sprintf("users|T|%s|accessToken", id)), []byte(accessToken))
     61 		if ATerr != nil {
     62 			return ATerr
     63 		}
     64 
     65 		ASerr := txn.Set([]byte(fmt.Sprintf("users|T|%s|accessSecret", id)), []byte(accessSecret))
     66 		if ASerr != nil {
     67 			return ASerr
     68 		}
     69 
     70 		Nerr := txn.Set([]byte(fmt.Sprintf("users|username|T|%s", newUUID)), []byte(name))
     71 		if Nerr != nil {
     72 			return Nerr
     73 		}
     74 
     75 		IDerr := txn.Set([]byte(fmt.Sprintf("users|id|T|%s", newUUID)), []byte(id))
     76 		if IDerr != nil {
     77 			return IDerr
     78 		}
     79 
     80 		UUIDDBerr := txn.Set([]byte(fmt.Sprintf("users|T|%s|uuid", id)), []byte(newUUID))
     81 		if UUIDDBerr != nil {
     82 			return UUIDDBerr
     83 		}
     84 
     85 		return txn.Set([]byte(fmt.Sprintf("users|T|%s|data", id)), []byte(data))
     86 	})
     87 }
     88 
     89 func checkUserExists(id string) (bool, error) {
     90 	exists := true
     91 	dataDB, err := db.OpenDB()
     92 	if err != nil {
     93 		return true, err
     94 	}
     95 
     96 	dbErr := dataDB.View(func(txn *badger.Txn) error {
     97 		_, QueryErr := txn.Get([]byte(fmt.Sprintf("users|%s", id)))
     98 		if QueryErr != nil && QueryErr != badger.ErrKeyNotFound {
     99 			return QueryErr
    100 		}
    101 		if QueryErr == badger.ErrKeyNotFound {
    102 			exists = false
    103 		}
    104 		return nil
    105 	})
    106 	return exists, dbErr
    107 }
    108 
    109 // issueSession issues a cookie session after successful Twitter login
    110 func IssueSession() http.Handler {
    111 	fn := func(w http.ResponseWriter, req *http.Request) {
    112 		ctx := req.Context()
    113 		twitterUser, err := twitter.UserFromContext(ctx)
    114 		if err != nil {
    115 			http.Error(w, err.Error(), http.StatusInternalServerError)
    116 			return
    117 		}
    118 		if exists, err := checkUserExists(twitterUser.IDStr); !exists {
    119 			if err != nil {
    120 				http.Error(w, err.Error(), http.StatusInternalServerError)
    121 				return
    122 			}
    123 
    124 			accessToken, accessSecret, err := oauth1Login.AccessTokenFromContext(ctx)
    125 			if err != nil {
    126 				http.Error(w, err.Error(), http.StatusInternalServerError)
    127 				return
    128 			}
    129 
    130 			b, err := json.Marshal(twitterUser)
    131 			if err != nil {
    132 				http.Error(w, err.Error(), http.StatusInternalServerError)
    133 			}
    134 
    135 			SErr := saveTUser(twitterUser.IDStr, accessToken, accessSecret, twitterUser.ScreenName, b)
    136 			if SErr != nil {
    137 				http.Error(w, SErr.Error(), http.StatusInternalServerError)
    138 				return
    139 			}
    140 		}
    141 
    142 		// 2. Implement a success handler to issue some form of session
    143 		session := sessionStore.New(sessionName)
    144 		session.Values[sessionUserKey] = twitterUser.IDStr
    145 		session.Save(w)
    146 
    147 		uuidS, err := getUserUUID(twitterUser.IDStr)
    148 		if err != nil {
    149 			http.Error(w, err.Error(), http.StatusInternalServerError)
    150 			return
    151 		}
    152 		w.Header().Set("UUID", uuidS)
    153 		w.Header().Set("API-VERSION", util.APIVersion)
    154 		domain := req.Host
    155 		log.Println("domain:", domain)
    156 		w.WriteHeader(http.StatusOK)
    157 	}
    158 	return http.HandlerFunc(fn)
    159 }
    160 
    161 // LogoutHandler destroys the session on POSTs and redirects to home.
    162 func LogoutHandler(w http.ResponseWriter, req *http.Request) {
    163 	if req.Method == "POST" {
    164 		sessionStore.Destroy(w, sessionName)
    165 	}
    166 	http.Redirect(w, req, "/", http.StatusFound)
    167 }
    168 
    169 // IsAuthenticated returns true if the user has a signed session cookie.
    170 func IsAuthenticated(req *http.Request) bool {
    171 	if _, err := sessionStore.Get(req, sessionName); err == nil {
    172 		return true
    173 	}
    174 	return false
    175 }
    176 
    177 // IsAuthenticatedHandleFunc returns 200 if the user has a signed session cookie.
    178 func IsAuthenticatedHandleFunc(w http.ResponseWriter, req *http.Request) {
    179 	w.Header().Set("API-VERSION", util.APIVersion)
    180 	if IsAuthenticated(req) {
    181 		Reqsession, err := sessionStore.Get(req, sessionName)
    182 		if err != nil {
    183 			http.Error(w, err.Error(), http.StatusInternalServerError)
    184 			return
    185 		}
    186 		twitterUserID := Reqsession.Values[sessionUserKey]
    187 		switch twitterUserID.(type) {
    188 		case string:
    189 			// We know this is a string
    190 			uuidS, err := getUserUUID(twitterUserID.(string))
    191 			if err != nil {
    192 				http.Error(w, err.Error(), http.StatusInternalServerError)
    193 				return
    194 			}
    195 			w.Header().Set("UUID", uuidS)
    196 			w.WriteHeader(http.StatusOK)
    197 		}
    198 
    199 		w.WriteHeader(http.StatusUnauthorized)
    200 	} else {
    201 		w.WriteHeader(http.StatusUnauthorized)
    202 	}
    203 }
    204 
    205 // LoginHandler handles Twitter login requests by obtaining a request token and
    206 // redirecting to the authorization URL.
    207 func LoginHandler(config *oauth1.Config, failure http.Handler) http.Handler {
    208 	// oauth1.LoginHandler -> oauth1.AuthRedirectHandler
    209 	success := AuthRedirectHandler(config, failure)
    210 	return oauth1Login.LoginHandler(config, success, failure)
    211 }
    212 
    213 // AuthRedirectHandler reads the request token from the ctx and redirects
    214 // to the authorization URL.
    215 func AuthRedirectHandler(config *oauth1.Config, failure http.Handler) http.Handler {
    216 	if failure == nil {
    217 		failure = gologin.DefaultFailureHandler
    218 	}
    219 	fn := func(w http.ResponseWriter, req *http.Request) {
    220 		domain := req.Host
    221 		log.Println("domain:", domain)
    222 		config.CallbackURL = "https://" + domain + "/login/twitter/callback"
    223 		TConfig = config
    224 		ctx := req.Context()
    225 		requestToken, _, err := libLogin.RequestTokenFromContext(ctx)
    226 		if err != nil {
    227 			ctx = gologin.WithError(ctx, err)
    228 			failure.ServeHTTP(w, req.WithContext(ctx))
    229 			return
    230 		}
    231 		authorizationURL, err := config.AuthorizationURL(requestToken)
    232 		if err != nil {
    233 			ctx = gologin.WithError(ctx, err)
    234 			failure.ServeHTTP(w, req.WithContext(ctx))
    235 			return
    236 		}
    237 		http.Redirect(w, req, authorizationURL.String(), http.StatusFound)
    238 	}
    239 	return http.HandlerFunc(fn)
    240 }