diff --git a/src/bunker.go b/src/bunker.go index fbb474f..ae7308d 100644 --- a/src/bunker.go +++ b/src/bunker.go @@ -109,6 +109,7 @@ type userJSON struct { loginIdx string emailIdx string phoneIdx string + token string } type tokenAuthResult struct { @@ -218,7 +219,9 @@ func (e mainEnv) setupRouter() *httprouter.Router { router.GET("/v1/userapp/token/:token", e.userappList) router.GET("/v1/userapps", e.appList) - router.POST("/v1/session/:mode/:address", e.newSession) + router.GET("/v1/session/:session", e.getSession) + router.POST("/v1/session/:session", e.createSession) + router.POST("/v1/session/:mode/:address", e.newUserSession) router.GET("/v1/session/:mode/:address", e.getUserSessions) router.GET("/v1/metrics", e.metrics) diff --git a/src/sessions_api.go b/src/sessions_api.go index 7075b4a..abb2ac6 100644 --- a/src/sessions_api.go +++ b/src/sessions_api.go @@ -6,13 +6,70 @@ import ( "net/http" "reflect" "strings" - + uuid "github.com/hashicorp/go-uuid" "github.com/julienschmidt/httprouter" "github.com/paranoidguy/databunker/src/storage" "go.mongodb.org/mongo-driver/bson" ) -func (e mainEnv) newSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { +func (e mainEnv) createSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + session := ps.ByName("session") + event := audit("create session", session, "session", session) + defer func() { event.submit(e.db) }() + if enforceUUID(w, session, event) == false { + //returnError(w, r, "bad session format", nil, event) + return + } + authResult := e.enforceAdmin(w, r) + if authResult == "" { + return + } + expiration := e.conf.Policy.MaxSessionRetentionPeriod + parsedData, err := getJSONPost(r, e.conf.Sms.DefaultCountry) + if err != nil { + returnError(w, r, "failed to decode request body", 405, err, event) + return + } + if len(parsedData.jsonData) == 0 { + returnError(w, r, "empty request body", 405, nil, event) + return + } + var userBson bson.M + if len(parsedData.loginIdx) > 0 { + userBson, err = e.db.lookupUserRecordByIndex("login", parsedData.loginIdx, e.conf) + } else if len(parsedData.emailIdx) > 0 { + userBson, err = e.db.lookupUserRecordByIndex("email", parsedData.emailIdx, e.conf) + } else if len(parsedData.phoneIdx) > 0 { + userBson, err = e.db.lookupUserRecordByIndex("phone", parsedData.phoneIdx, e.conf) + } else if len(parsedData.token) > 0 { + userBson, err = e.db.lookupUserRecord(parsedData.token) + } + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + userTOKEN := "" + if userBson != nil { + userTOKEN = userBson["token"].(string) + event.Record = userTOKEN + } + sessionUUID, err := uuid.GenerateUUID() + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + sessionID, err := e.db.createSessionRecord(sessionUUID, userTOKEN, expiration, parsedData.jsonData) + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + fmt.Fprintf(w, `{"status":"ok","session":"%s"}`, sessionID) + return +} + +func (e mainEnv) newUserSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { address := ps.ByName("address") mode := ps.ByName("mode") event := audit("create user session", address, mode, address) @@ -66,7 +123,12 @@ func (e mainEnv) newSession(w http.ResponseWriter, r *http.Request, ps httproute returnError(w, r, "internal error", 405, err, event) return } - sessionID, err := e.db.createSessionRecord(userTOKEN, expiration, jsonData) + sessionUUID, err := uuid.GenerateUUID() + if err != nil { + returnError(w, r, "internal error", 405, err, event) + return + } + sessionID, err := e.db.createSessionRecord(sessionUUID, userTOKEN, expiration, jsonData) if err != nil { returnError(w, r, "internal error", 405, err, event) return @@ -80,12 +142,6 @@ func (e mainEnv) newSession(w http.ResponseWriter, r *http.Request, ps httproute func (e mainEnv) getUserSessions(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { address := ps.ByName("address") mode := ps.ByName("mode") - - if mode == "session" { - e.db.store.DeleteExpired(storage.TblName.Sessions, "session", address) - e.getSession(w, r, address) - return - } event := audit("get all user sessions", address, mode, address) defer func() { event.submit(e.db) }() @@ -137,16 +193,19 @@ func (e mainEnv) getUserSessions(w http.ResponseWriter, r *http.Request, ps http return } -func (e mainEnv) getSession(w http.ResponseWriter, r *http.Request, session string) { +func (e mainEnv) getSession(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + session := ps.ByName("session") event := audit("get session", session, "session", session) defer func() { event.submit(e.db) }() - when, record, userTOKEN, err := e.db.getUserSession(session) + when, record, userTOKEN, err := e.db.getSession(session) if err != nil { returnError(w, r, err.Error(), 405, err, event) return } - event.Record = userTOKEN + if len(userTOKEN) > 0 { + event.Record = userTOKEN + } if e.enforceAuth(w, r, event) == "" { return } diff --git a/src/sessions_db.go b/src/sessions_db.go index 20b3eaf..bbc7f73 100644 --- a/src/sessions_db.go +++ b/src/sessions_db.go @@ -6,7 +6,6 @@ import ( "fmt" "time" - uuid "github.com/hashicorp/go-uuid" "github.com/paranoidguy/databunker/src/storage" "go.mongodb.org/mongo-driver/bson" ) @@ -16,7 +15,7 @@ type sessionEvent struct { Data string `json:"data"` } -func (dbobj dbcon) createSessionRecord(userTOKEN string, expiration string, data []byte) (string, error) { +func (dbobj dbcon) createSessionRecord(sessionUUID string, userTOKEN string, expiration string, data []byte) (string, error) { var endtime int32 var err error if len(expiration) > 0 { @@ -25,14 +24,15 @@ func (dbobj dbcon) createSessionRecord(userTOKEN string, expiration string, data return "", err } } - encodedStr, err := dbobj.userEncrypt(userTOKEN, data) + recordKey, err := generateRecordKey() if err != nil { return "", err } - sessionUUID, err := uuid.GenerateUUID() + encoded, err := encrypt(dbobj.masterKey, recordKey, data) if err != nil { return "", err } + encodedStr := base64.StdEncoding.EncodeToString(encoded) bdoc := bson.M{} now := int32(time.Now().Unix()) bdoc["token"] = userTOKEN @@ -40,14 +40,20 @@ func (dbobj dbcon) createSessionRecord(userTOKEN string, expiration string, data bdoc["endtime"] = endtime bdoc["when"] = now bdoc["data"] = encodedStr - _, err = dbobj.store.CreateRecord(storage.TblName.Sessions, &bdoc) - if err != nil { - return "", err + bdoc["key"] = base64.StdEncoding.EncodeToString(recordKey) + record, err := dbobj.store.GetRecord(storage.TblName.Sessions, "session", sessionUUID) + if record == nil || len(record) == 0 { + _, err = dbobj.store.CreateRecord(storage.TblName.Sessions, &bdoc) + if err != nil { + return "", err + } + return sessionUUID, nil } + dbobj.store.UpdateRecord(storage.TblName.Sessions, "session", sessionUUID, &bdoc) return sessionUUID, nil } -func (dbobj dbcon) getUserSession(sessionUUID string) (int32, []byte, string, error) { +func (dbobj dbcon) getSession(sessionUUID string) (int32, []byte, string, error) { record, err := dbobj.store.GetRecord(storage.TblName.Sessions, "session", sessionUUID) if err != nil { return 0, nil, "", err @@ -63,41 +69,38 @@ func (dbobj dbcon) getUserSession(sessionUUID string) (int32, []byte, string, er when := record["when"].(int32) userTOKEN := record["token"].(string) encData0 := record["data"].(string) - decrypted, err := dbobj.userDecrypt(userTOKEN, encData0) + recordKey0 := record["key"].(string) + recordKey, err := base64.StdEncoding.DecodeString(recordKey0) if err != nil { - return 0, nil, "", err + return 0, nil, "", err + } + encData, err := base64.StdEncoding.DecodeString(encData0) + if err != nil { + return 0, nil, "", err + } + decrypted, err := decrypt(dbobj.masterKey, recordKey, encData) + if err != nil { + return 0, nil, "", err } return when, decrypted, userTOKEN, err } func (dbobj dbcon) getUserSessionsByToken(userTOKEN string, offset int32, limit int32) ([]string, int64, error) { - - userBson, err := dbobj.lookupUserRecord(userTOKEN) - if userBson == nil || err != nil { - // not found - return nil, 0, err - } - userKey := userBson["key"].(string) - recordKey, err := base64.StdEncoding.DecodeString(userKey) - if err != nil { - return nil, 0, err - } - count, err := dbobj.store.CountRecords(storage.TblName.Sessions, "token", userTOKEN) if err != nil { return nil, 0, err } - records, err := dbobj.store.GetList(storage.TblName.Sessions, "token", userTOKEN, offset, limit, "") if err != nil { return nil, 0, err } - var results []string for _, element := range records { when := element["when"].(int32) session := element["session"].(string) encData0 := element["data"].(string) + recordKey0 := element["key"].(string) + recordKey, _ := base64.StdEncoding.DecodeString(recordKey0) encData, _ := base64.StdEncoding.DecodeString(encData0) decrypted, _ := decrypt(dbobj.masterKey, recordKey, encData) sEvent := fmt.Sprintf(`{"when":%d,"session":"%s","data":%s}`, when, session, string(decrypted)) diff --git a/src/sharedrecords_api.go b/src/sharedrecords_api.go index 874d264..a24746e 100644 --- a/src/sharedrecords_api.go +++ b/src/sharedrecords_api.go @@ -106,7 +106,7 @@ func (e mainEnv) getRecord(w http.ResponseWriter, r *http.Request, ps httprouter if len(recordInfo.appName) > 0 { resultJSON, err = e.db.getUserApp(recordInfo.token, recordInfo.appName) } else if len(recordInfo.session) > 0 { - _, resultJSON, _, err = e.db.getUserSession(recordInfo.session) + _, resultJSON, _, err = e.db.getSession(recordInfo.session) } else { resultJSON, err = e.db.getUser(recordInfo.token) } diff --git a/src/storage/mysql-storage.go b/src/storage/mysql-storage.go index 8dea974..ca067bd 100644 --- a/src/storage/mysql-storage.go +++ b/src/storage/mysql-storage.go @@ -1075,6 +1075,7 @@ func (dbobj MySQLDB) initSessions() error { `CREATE TABLE IF NOT EXISTS sessions (`+ `token TINYTEXT,`+ `session TINYTEXT,`+ + "`key` TINYTEXT,"+ `data TEXT,`+ `endtime int,`+ "`when` int);", diff --git a/src/storage/sqlite-storage.go b/src/storage/sqlite-storage.go index eb0f217..8a03311 100644 --- a/src/storage/sqlite-storage.go +++ b/src/storage/sqlite-storage.go @@ -1059,6 +1059,7 @@ func (dbobj SQLiteDB) initSessions() error { queries := []string{`CREATE TABLE IF NOT EXISTS sessions ( token STRING, session STRING, + key STRING, data TEXT, endtime int, ` + "`when` int);", diff --git a/src/utils.go b/src/utils.go index 8329403..0aa1539 100644 --- a/src/utils.go +++ b/src/utils.go @@ -523,9 +523,10 @@ func getJSONPost(r *http.Request, defaultCountry string) (userJSON, error) { if value, ok := records["phone"]; ok { result.phoneIdx = normalizePhone(getIndexString(value), defaultCountry) } - + if value, ok := records["token"]; ok { + result.token = value.(string) + } result.jsonData, err = json.Marshal(records) - return result, err }