| @@ -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 | |||
| } | |||