package main import ( "crypto/rand" "fmt" "github.com/gomodule/redigo/redis" "golang.org/x/crypto/bcrypt" "log" "strconv" "strings" "time" ) var globalPool *redis.Pool const userPrefix string = "user:" const devPrefix string = "dev:" const confirmPrefix string = "confirm:" func newPool() *redis.Pool { return &redis.Pool{ // Maximum number of idle connections in the pool. MaxIdle: 80, // max number of connections MaxActive: 12000, // Dial is an application supplied function for creating and // configuring a connection. Dial: func() (redis.Conn, error) { c, err := redis.Dial("tcp", getenv("REDIS_CONNECTION_STRING", ":6379")) if err != nil { panic(err.Error()) } return c, err }, } } // ping tests connectivity for redis (PONG should be returned) func ping(c redis.Conn) error { // Send PING command to Redis // PING command returns a Redis "Simple String" // Use redis.String to convert the interface type to string s, err := redis.String(c.Do("PING")) if err != nil { return err } logit("PING Response = " + s) // Output: PONG return nil } type Dev struct { Deveui string Alias string SmsAlarmactive string Smsnumber string EmailAlarmactive string Email string Greenzone string ActiveUntil string // Abo bezahlt bis TT.MM.YYYY } func initDB() { // newPool returns a pointer to a redis.Pool pool := newPool() // get a connection from the globalPool (redis.Conn) conn := pool.Get() defer conn.Close() globalPool = pool // wir machen einen Connection Test ping(conn) // Wir legen einen initialen Admin User an, falls es diesen noch nicht gibt if checkUserAvailable("joerg.lehmann@nbit.ch") { insertUser("joerg.lehmann@nbit.ch", "changeme123", "Y") } } func closeDB() { globalPool.Close() } func getUsers() []string { res := []string{} conn := globalPool.Get() defer conn.Close() logit("getUsers") users, err := redis.Strings(conn.Do("KEYS", userPrefix+"*")) if err == nil { logit("getUsers successful!") res = users } else { log.Print(err) } return res } func updateTrackerSettings(trackerSettings Dev) error { conn := globalPool.Get() defer conn.Close() // SET object _, err := conn.Do("HMSET", devPrefix+trackerSettings.Deveui, "alias", trackerSettings.Alias, "smsalarmactive", trackerSettings.SmsAlarmactive, "smsnumber", trackerSettings.Smsnumber, "emailalarmactive", trackerSettings.EmailAlarmactive, "email", trackerSettings.Email, "greenzone", trackerSettings.Greenzone) if err != nil { return err } return nil } func updateTrackerGreenzone(deveui string, greenzone string) error { conn := globalPool.Get() defer conn.Close() // SET object _, err := conn.Do("HMSET", devPrefix+deveui, "greenzone", greenzone) if err != nil { return err } return nil } func checkUserAvailable(username string) bool { logit("checkUserAvailable: User: " + username) conn := globalPool.Get() defer conn.Close() res, err := redis.Int(conn.Do("EXISTS", userPrefix+username)) if err == nil { logit("Result of EXISTS: " + strconv.Itoa(res)) return res == 0 } else { logit("checkUserAvailable: Error to query Key Value Store") return false } } func getMyDevs(username string) []string { res := []string{} if username == "" { return res } conn := globalPool.Get() defer conn.Close() logit("getMyDevs: User: " + username) mydevs, err := redis.String(conn.Do("HGET", userPrefix+username, "my_devs")) if err == nil { logit("getMyDevs: mydevs: " + mydevs) res = strings.Split(mydevs, ",") } else { log.Print(err) } return res } func getDevAlias(deveui string) string { res := deveui if deveui == "" { return res } conn := globalPool.Get() defer conn.Close() logit("getDevAlias: Deveui: " + deveui) alias, err := redis.String(conn.Do("HGET", devPrefix+deveui, "alias")) if err == nil { logit("getDevAlias: alias: " + alias) res = alias } else { log.Print(err) } return res } func getDevSmsalarmactive(deveui string) string { res := "0" if deveui == "" { return res } conn := globalPool.Get() defer conn.Close() logit("getDevSmslarmactive: Deveui: " + deveui) smsalarmactive, err := redis.String(conn.Do("HGET", devPrefix+deveui, "smsalarmactive")) if err == nil { logit("getDevSmsalarmactive: smsalarmactive: " + smsalarmactive) res = smsalarmactive } else { log.Print(err) } return res } func getDevSmsnumber(deveui string) string { res := "+4179XXXXXXX" if deveui == "" { return res } conn := globalPool.Get() defer conn.Close() logit("getDevSmsnumber: Deveui: " + deveui) smsnumber, err := redis.String(conn.Do("HGET", devPrefix+deveui, "smsnumber")) if err == nil { logit("getDevSmsnumber: smsnumber: " + smsnumber) res = smsnumber } else { log.Print(err) } return res } func getDevEmailalarmactive(deveui string) string { res := "0" if deveui == "" { return res } conn := globalPool.Get() defer conn.Close() logit("getDevEmailarmactive: Deveui: " + deveui) emailalarmactive, err := redis.String(conn.Do("HGET", devPrefix+deveui, "emailalarmactive")) if err == nil { logit("getDevEmailarmactive: emailalarmactive: " + emailalarmactive) res = emailalarmactive } else { log.Print(err) } return res } func getDevEmail(deveui string) string { res := "" if deveui == "" { return res } conn := globalPool.Get() defer conn.Close() logit("getDevEmail: Deveui: " + deveui) email, err := redis.String(conn.Do("HGET", devPrefix+deveui, "email")) if err == nil { logit("getDevEmail: email: " + email) res = email } else { log.Print(err) } return res } func getDevGreenzone(deveui string) string { res := "" if deveui == "" { return res } conn := globalPool.Get() defer conn.Close() logit("getDevGreenzone: Deveui: " + deveui) greenzone, err := redis.String(conn.Do("HGET", devPrefix+deveui, "greenzone")) if err == nil { logit("getDevGreenzone: greenzone: " + greenzone) res = greenzone } else { log.Print(err) } return res } func getActiveUntil(deveui string) string { res := "" if deveui == "" { return res } conn := globalPool.Get() defer conn.Close() logit("getActiveUntil: Deveui: " + deveui) activeuntil, err := redis.String(conn.Do("HGET", devPrefix+deveui, "active_until")) if err == nil { logit("getActiveUntil: activeuntil: " + activeuntil) res = activeuntil } else { log.Print(err) } return res } func getYearlyAboCost(deveui string) int { res := 0 logit("getYearlyAboCost: Deveui: " + deveui) conn := globalPool.Get() defer conn.Close() // first we get the default yearlyAboCostDefault, err := redis.Int(conn.Do("HGET", "settings", "default_yearly_abo_cost")) if err == nil { yearlyAboCost, err := redis.Int(conn.Do("HGET", devPrefix+deveui, "yearly_abo_cost")) if err == nil { res = yearlyAboCost } else { res = yearlyAboCostDefault } } else { log.Print(err) } return res } func AboExpired(deveui string) bool { active_until := getActiveUntil(deveui) layout := "02.01.2006" t, _ := time.Parse(layout, active_until) return t.Before(time.Now()) } func prolongActivation(deveui string, years int) (string, error) { conn := globalPool.Get() defer conn.Close() active_until_old, err := redis.String(conn.Do("HGET", devPrefix+deveui, "active_until")) if err == nil { logit("prolongActivation: active_until: " + active_until_old) } else { log.Print(err) } layout := "02.01.2006" t, err := time.Parse(layout, active_until_old) if err != nil { fmt.Println(err) } fmt.Println(t.Unix()) var t_new time.Time t_new = t.AddDate(years, 0, 0) active_until_new := t_new.Format(layout) // SET object _, err1 := conn.Do("HMSET", devPrefix+deveui, "active_until", active_until_new) if err1 != nil { return "", err1 } return active_until_new, nil } func randString(n int) string { const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" var bytes = make([]byte, n) rand.Read(bytes) for i, b := range bytes { bytes[i] = alphanum[b%byte(len(alphanum))] } return string(bytes) } func insertUser(username, password, is_admin string) { conn := globalPool.Get() defer conn.Close() logit("insertUser: " + username) pwd := []byte(password) hashedPassword, err := bcrypt.GenerateFromPassword(pwd, bcrypt.DefaultCost) if err != nil { logit("insertUser: Error with bcrypt.GenerateFromPassword, User: " + username) return } confirm_id := "" _, err = conn.Do("HMSET", userPrefix+username, "password", string(hashedPassword), "new_password", string(hashedPassword), "confirm_id", confirm_id, "last_login", "", "my_devs", "") if err != nil { logit("insertUser: Error inserting User: " + username) return } } func updateUser(username, password string) { conn := globalPool.Get() defer conn.Close() logit("updateUser: " + username) pwd := []byte(password) hashedPassword, err := bcrypt.GenerateFromPassword(pwd, bcrypt.DefaultCost) if err != nil { logit("updateUser: Error with bcrypt.GenerateFromPassword, User: " + username) return } confirm_id := randString(30) _, err = conn.Do("HMSET", userPrefix+username, "new_password", string(hashedPassword), "confirm_id", confirm_id) if err != nil { logit("updateUser: Error updateing User: " + username) return } _, err = conn.Do("SET", confirmPrefix+confirm_id, username) if err != nil { logit("updateUser: Error inserting confirm_id: " + confirm_id + ": " + username) return } sendEmailConfirm(username, confirm_id) } func checkLoginCredentials(username, password string) bool { conn := globalPool.Get() defer conn.Close() logit("checkLoginCredentials: called with username,password: " + username + "," + password) pwd, err := redis.String(conn.Do("HGET", userPrefix+username, "password")) if err == nil { logit("checkLoginCredentials: pwd: " + pwd + " CMD: HGET " + userPrefix + username + userPrefix + username + " confirm_id") cid, err := redis.String(conn.Do("HGET", userPrefix+username, "confirm_id")) if err == nil { logit("checkLoginCredentials: cid: " + cid) if !(err != nil && cid != "") { logit("checkLoginCredentials: pwd: " + pwd) if err != nil { return false } } } } else { log.Print(err) return false } hashedPassword := []byte(pwd) err = bcrypt.CompareHashAndPassword(hashedPassword, []byte(password)) return err == nil } func updateLoginTime(username string) { conn := globalPool.Get() defer conn.Close() _, err := conn.Do("HSET", userPrefix+username, "last_login", time.Now().UTC().Format("2006-01-02 15:04:05")) if err != nil { logit("updateUser: Error updateing User: " + username) return } } func confirmUser(confirm_id string) bool { conn := globalPool.Get() defer conn.Close() u, err := redis.String(conn.Do("GET", confirmPrefix+confirm_id)) if err != nil { logit("confirmUser: Error with searching confirm_id: " + confirm_id) return false } new_password, err := redis.String(conn.Do("HGET", userPrefix+u, "new_password")) if err != nil { logit("confirmUser: Error with getting new_password: " + u) return false } _, err = conn.Do("HMSET", userPrefix+u, "confirm_id", "", "password", new_password) if err != nil { logit("confirmUser: Error updateing User: " + u) return false } _, err = conn.Do("DEL", confirmPrefix+confirm_id) if err != nil { logit("confirmUser: Error deleting confirm_id: " + confirm_id) return false } return true } func InsertAlert(prefix string, deveui string, email string, threshold int) { conn := globalPool.Get() defer conn.Close() _, err := conn.Do("SET", prefix+deveui+":"+email, threshold) if err != nil { logit("InsertAlert: Error inserting: " + prefix + deveui + ":" + email) } } func DeleteAlert(prefix string, deveui string, email string) { conn := globalPool.Get() defer conn.Close() exists, _ := redis.Bool(conn.Do("EXISTS", prefix+deveui+":"+email)) if exists { _, err := conn.Do("DEL", prefix+deveui+":"+email) if err != nil { logit("DeleteAlert: Error deleting: " + prefix + deveui + ":" + email) } } } func AlarmNotAlreadySent(prefix string, deveui string, email string, threshold int) bool { conn := globalPool.Get() defer conn.Close() exists, _ := redis.Bool(conn.Do("EXISTS", prefix+deveui+":"+email)) if !exists { return true } alarm_threshold, _ := redis.Int(conn.Do("GET", prefix+deveui+":"+email)) return threshold != alarm_threshold }