diff --git a/apiv1.go b/apiv1.go index b82c103..2d8ed08 100644 --- a/apiv1.go +++ b/apiv1.go @@ -5,9 +5,11 @@ import ( "database/sql" "encoding/json" "fmt" + "github.com/brianvoe/gofakeit/v6" log "github.com/sirupsen/logrus" "net/http" "net/http/httputil" + "strconv" "strings" "time" ) @@ -31,14 +33,19 @@ func apiV1Main(w http.ResponseWriter, r *http.Request) { logRequestDebug(httputil.DumpRequest(r, true)) w.Header().Set("Content-Type", "application/json;charset=UTF-8") + //track browser, and take session from cookie + session := loan.Session{} + apiV1InitSessionByBrowserId(w, r, &session) + //try session login first, if not an empty session will be created - session, e := apiV1InitSession(r) + e := apiV1InitSessionByHttpHeader(r, &session) if e != nil { log.Warnf("Fail to InitSession %+v", session) apiV1Client403Error(w, r) return } session.RenewIfExpireSoon() + session.SetRemote(r) //make sure they are using latest remote //we have a session now, either guest or valid user //search through handler @@ -53,29 +60,78 @@ func apiV1Main(w http.ResponseWriter, r *http.Request) { return } } + //Catch for all + e = session.Write() //finish this session to DB apiV1DumpRequest(w, r, &session) } -func apiV1InitSession(r *http.Request) (session loan.Session, e error) { - session = loan.Session{} - sid := r.Header.Get("Biukop-Session") - e = session.Retrieve(r) - if e == nil { //we got existing session - e = session.ValidateRequest(r) - if e != nil { // not successfully validated - log.Warnf("failed session login %+v, %s", session, time.Now().Format(time.RFC1123)) +func apiV1InitSessionByBrowserId(w http.ResponseWriter, r *http.Request, session *loan.Session) { + var mid string + inCookie, e := r.Cookie("mid") + if e == nil { + mid = inCookie.Value + } else { + mid = strconv.Itoa(int(time.Now().Unix())) + "-" + gofakeit.UUID() + } + + var sid string + inCookie, e = r.Cookie("session") + if e == nil { + sid = inCookie.Value + if sid != "" { + e = session.Read(sid) + if e == nil { + if mid != session.Get("mid") { + session.MarkEmpty() + } + } + } else { //create a new session session.InitGuest(time.Now().Add(loan.DefaultSessionDuration)) - e = nil - } //else, we have logged this user in - } else if e == sql.ErrNoRows { - log.Warn("DB has no corresponding session ", sid) - session.InitGuest(time.Now().Add(loan.DefaultSessionDuration)) + session.Add("mid", mid) + } + } + + //add tracking cookie + expiration := time.Now().Add(365 * 24 * time.Hour) + cookie := http.Cookie{Name: "session", Value: session.Id, Expires: expiration} + http.SetCookie(w, &cookie) + + cookie = http.Cookie{Name: "mid", Value: mid, Expires: expiration} + http.SetCookie(w, &cookie) + +} + +func apiV1InitSessionByHttpHeader(r *http.Request, ss *loan.Session) (e error) { + sid := r.Header.Get("Biukop-Session") + + //make sure session id is given + if sid != "" { + //Try to retrieve a copy of DB session + trial := loan.Session{} + e = trial.Retrieve(r) + if e == nil { //we got existing session from DB + e = trial.ValidateRequest(r) + if e != nil { // db session does not match request + log.Warnf("failed session login %+v, %s", ss, time.Now().Format(time.RFC1123)) + ss.InitGuest(time.Now().Add(loan.DefaultSessionDuration)) + e = nil + } else { //else, we have logged this user + *ss = trial //overwrite the incoming session + } + } else if e == sql.ErrNoRows { //db does not have required session. + log.Warn("DB has no corresponding session ", sid) + } else { // retrieve has error + log.Warnf("Retrieve Session %s encountered error %s", sid, e.Error()) + } + } + + //if there is not session incoming + if ss.IsEmpty() { //cookie may have initialized a session + ss.InitGuest(time.Now().Add(loan.DefaultSessionDuration)) e = nil //we try to init an empty one - } else { - log.Warnf("Retrieve Session %s encountered error %s", sid, e.Error()) } - session.SetRemote(r) //make sure they are using latest remote + return }