| "database/sql" | "database/sql" | ||||
| "encoding/json" | "encoding/json" | ||||
| "fmt" | "fmt" | ||||
| "github.com/brianvoe/gofakeit/v6" | |||||
| log "github.com/sirupsen/logrus" | log "github.com/sirupsen/logrus" | ||||
| "net/http" | "net/http" | ||||
| "net/http/httputil" | "net/http/httputil" | ||||
| "strconv" | |||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| ) | ) | ||||
| logRequestDebug(httputil.DumpRequest(r, true)) | logRequestDebug(httputil.DumpRequest(r, true)) | ||||
| w.Header().Set("Content-Type", "application/json;charset=UTF-8") | 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 | //try session login first, if not an empty session will be created | ||||
| session, e := apiV1InitSession(r) | |||||
| e := apiV1InitSessionByHttpHeader(r, &session) | |||||
| if e != nil { | if e != nil { | ||||
| log.Warnf("Fail to InitSession %+v", session) | log.Warnf("Fail to InitSession %+v", session) | ||||
| apiV1Client403Error(w, r) | apiV1Client403Error(w, r) | ||||
| return | return | ||||
| } | } | ||||
| session.RenewIfExpireSoon() | session.RenewIfExpireSoon() | ||||
| session.SetRemote(r) //make sure they are using latest remote | |||||
| //we have a session now, either guest or valid user | //we have a session now, either guest or valid user | ||||
| //search through handler | //search through handler | ||||
| return | return | ||||
| } | } | ||||
| } | } | ||||
| //Catch for all | //Catch for all | ||||
| e = session.Write() //finish this session to DB | |||||
| apiV1DumpRequest(w, r, &session) | 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)) | 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 | 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 | return | ||||
| } | } | ||||