Add bump with ip

This commit is contained in:
Your Name 2020-06-04 16:02:46 -04:00
parent 7d96d46ac8
commit 1a31bb6b30
1 changed files with 92 additions and 97 deletions

189
main.go
View File

@ -2,19 +2,19 @@ package scsusers
import ( import (
"bytes" "bytes"
"crypto/rand"
"database/sql" "database/sql"
"encoding/base32"
"encoding/json"
"fmt" "fmt"
"github.com/jmoiron/sqlx"
"golang.org/x/crypto/bcrypt"
"html/template" "html/template"
"log" "log"
"crypto/rand"
"encoding/json"
"encoding/base32"
"net/smtp" "net/smtp"
"strings" "strings"
)
"github.com/jmoiron/sqlx"
"golang.org/x/crypto/bcrypt"
)
type templates struct { type templates struct {
Registration *template.Template Registration *template.Template
@ -26,14 +26,14 @@ type config struct {
SiteName string SiteName string
FromEmail string FromEmail string
Templates templates Templates templates
SMTPServer string SMTPServer string
db *sqlx.DB db *sqlx.DB
TablePrefix string TablePrefix string
} }
type UserData struct { type UserData struct {
Username string `json:"username"` Username string `json:"username"`
UserPerms map[string]string `json:"perms"` UserPerms map[string]string `json:"perms"`
UserSettings map[string]string `json:"settings"` UserSettings map[string]string `json:"settings"`
} }
@ -44,7 +44,7 @@ func Init(dbin *sqlx.DB, tp, sitename, fromaddr, smtpserver string) {
c.TablePrefix = tp c.TablePrefix = tp
c.SiteName = sitename c.SiteName = sitename
c.FromEmail = fromaddr c.FromEmail = fromaddr
c.SMTPServer=smtpserver c.SMTPServer = smtpserver
SetRegistrationTemplate("") SetRegistrationTemplate("")
SetAlertTemplate("") SetAlertTemplate("")
@ -52,7 +52,7 @@ func Init(dbin *sqlx.DB, tp, sitename, fromaddr, smtpserver string) {
} }
func UsernameAvailable(username string) bool { func UsernameAvailable(username string) bool {
if len(username)==0 { if len(username) == 0 {
return false return false
} }
var u string var u string
@ -91,8 +91,8 @@ func Register(username, email, ip string) bool {
return true return true
} }
log.Printf("scsusers.Register: Failed to send registration email, deleting user %s\n", username) log.Printf("scsusers.Register: Failed to send registration email, deleting user %s\n", username)
q=fmt.Sprintf("delete from %s_auth where username ILIKE $1 AND password=$2", c.TablePrefix) q = fmt.Sprintf("delete from %s_auth where username ILIKE $1 AND password=$2", c.TablePrefix)
_,err = c.db.Exec(q, username, string(crypt)) _, err = c.db.Exec(q, username, string(crypt))
if err != nil { if err != nil {
log.Printf("scsusers.Register: Failed to delete new user %s: %s\n", username, err.Error()) log.Printf("scsusers.Register: Failed to delete new user %s: %s\n", username, err.Error())
} }
@ -100,9 +100,9 @@ func Register(username, email, ip string) bool {
} }
func Login(username, password string) bool { func Login(username, password string) bool {
q:=fmt.Sprintf("select password from %s_auth where username ILIKE $1 AND status='active'",c.TablePrefix) q := fmt.Sprintf("select password from %s_auth where username ILIKE $1 AND status='active'", c.TablePrefix)
var crypt string var crypt string
err:=c.db.Get(&crypt, q, username) err := c.db.Get(&crypt, q, username)
if err != nil { if err != nil {
log.Printf("scsusers.Login: Failed login attempt for unknown username: %s\n", username) log.Printf("scsusers.Login: Failed login attempt for unknown username: %s\n", username)
return false return false
@ -117,33 +117,33 @@ func Login(username, password string) bool {
} }
func ChangePassword(username, oldpass, newpass string) bool { func ChangePassword(username, oldpass, newpass string) bool {
q:=fmt.Sprintf("select password from %s_auth where username ILIKE $1 AND status='active'",c.TablePrefix) q := fmt.Sprintf("select password from %s_auth where username ILIKE $1 AND status='active'", c.TablePrefix)
var crypt string var crypt string
err:=c.db.Get(&crypt, q, username) err := c.db.Get(&crypt, q, username)
if err != nil { if err != nil {
log.Println("scsusers.ChangePassword: Failed change attempt for unknown username: " + username) log.Println("scsusers.ChangePassword: Failed change attempt for unknown username: " + username)
return false return false
} }
if bcrypt.CompareHashAndPassword([]byte(crypt), []byte(oldpass)) != nil { if bcrypt.CompareHashAndPassword([]byte(crypt), []byte(oldpass)) != nil {
log.Printf("scsusers.ChangePassword: Failed password for %s\n", username) log.Printf("scsusers.ChangePassword: Failed password for %s\n", username)
return false return false
} }
newcrypt, err := bcrypt.GenerateFromPassword([]byte(newpass), 10) newcrypt, err := bcrypt.GenerateFromPassword([]byte(newpass), 10)
q=fmt.Sprintf("update %s_auth set password=$2 where username ILIKE $1", c.TablePrefix) q = fmt.Sprintf("update %s_auth set password=$2 where username ILIKE $1", c.TablePrefix)
_,err=c.db.Exec(q, username, newcrypt) _, err = c.db.Exec(q, username, newcrypt)
if err!= nil { if err != nil {
log.Printf("scsusers.ChangePassword: update failed for %s: %s\n", username, err.Error()) log.Printf("scsusers.ChangePassword: update failed for %s: %s\n", username, err.Error())
return false return false
} }
Bump(username) Bump(username)
return true return true
} }
func GetUserid(username string) int64 { func GetUserid(username string) int64 {
var i int64 var i int64
q:=fmt.Sprintf("select userid from %s_auth where username ILIKE $1", c.TablePrefix) q := fmt.Sprintf("select userid from %s_auth where username ILIKE $1", c.TablePrefix)
err:=c.db.Get(&i, q, username) err := c.db.Get(&i, q, username)
if err != nil { if err != nil {
log.Printf("scsusers.getUserId: Error loading user: %s : %s\n", username, err.Error()) log.Printf("scsusers.getUserId: Error loading user: %s : %s\n", username, err.Error())
return 0 return 0
@ -152,118 +152,117 @@ func GetUserid(username string) int64 {
} }
func LoadUser(username string) (UserData, error) { func LoadUser(username string) (UserData, error) {
var u UserData var u UserData
q:=fmt.Sprintf("select data from %s_userdata where username ILIKE $1", c.TablePrefix) q := fmt.Sprintf("select data from %s_userdata where username ILIKE $1", c.TablePrefix)
var d string var d string
err:=c.db.Get(d, q, username) err := c.db.Get(d, q, username)
if err != nil { if err != nil {
log.Printf("scsusers.LoadUser: Error loading user: %s : %s\n", username, err.Error()) log.Printf("scsusers.LoadUser: Error loading user: %s : %s\n", username, err.Error())
return u, err return u, err
} }
err=json.Unmarshal([]byte(d), &u) err = json.Unmarshal([]byte(d), &u)
if err != nil { if err != nil {
log.Printf("scsusers.LoadUser: Error decoding json on user %s. Unmarshal returned %s\n", username, err.Error()) log.Printf("scsusers.LoadUser: Error decoding json on user %s. Unmarshal returned %s\n", username, err.Error())
} }
return u,err return u, err
} }
func SaveUser(username string, d UserData) bool { func SaveUser(username string, d UserData) bool {
q:=fmt.Sprintf("update %s_userdata set data=$1 where username ILIKE $2") q := fmt.Sprintf("update %s_userdata set data=$1 where username ILIKE $2")
j, err:=json.Marshal(d) j, err := json.Marshal(d)
if err != nil { if err != nil {
log.Printf("scsusers.SaveUser: json.Marshal failed for username %s : %s\n", username, err.Error()) log.Printf("scsusers.SaveUser: json.Marshal failed for username %s : %s\n", username, err.Error())
return false return false
} }
_, err=c.db.Exec(q, username, j) _, err = c.db.Exec(q, username, j)
if err != nil { if err != nil {
log.Printf("scsusers.SaveUser: db.Exec failed for username %s : %s\n", username, err.Error()) log.Printf("scsusers.SaveUser: db.Exec failed for username %s : %s\n", username, err.Error())
return false return false
} }
return true return true
} }
func Bump(username string) { func Bump(username string, ip string) {
q:=fmt.Sprintf("update %s_auth set lastseen=CURRENT_TIMESTAMP where username ILIKE $1", c.TablePrefix) q := fmt.Sprintf("update %s_auth set lastseen=CURRENT_TIMESTAMP, set lastseenip=$2 where username ILIKE $1", c.TablePrefix)
_, err :=c.db.Exec(q, username) _, err := c.db.Exec(q, username, ip)
if err != nil { if err != nil {
log.Printf("scsusers.Bump: Error on user bump: %s : %s\n", username, err.Error()) log.Printf("scsusers.Bump: Error on user bump: %s : %s\n", username, err.Error())
} }
} }
type Metadata struct { type Metadata struct {
MetaKey string `db:meta_key` MetaKey string `db:meta_key`
MetaValue string `db:meta_value` MetaValue string `db:meta_value`
} }
func GetAllMeta(username string) (map[string]string) { func GetAllMeta(username string) map[string]string {
meta:=make(map[string]string) meta := make(map[string]string)
q:=fmt.Sprintf(`select meta_key, meta_value q := fmt.Sprintf(`select meta_key, meta_value
from %s_user_metadata where from %s_user_metadata where
user_id=(select userid from %s_auth where username ILIKE $1)`, user_id=(select userid from %s_auth where username ILIKE $1)`,
c.TablePrefix, c.TablePrefix) c.TablePrefix, c.TablePrefix)
rows,err:=c.db.Queryx(q, username) rows, err := c.db.Queryx(q, username)
if err != nil && err!=sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
log.Printf("scsusers.GetAllMeta: %s: %s\n", username, err.Error()) log.Printf("scsusers.GetAllMeta: %s: %s\n", username, err.Error())
return meta return meta
} }
var m Metadata var m Metadata
for rows.Next() { for rows.Next() {
err=rows.StructScan(&m) err = rows.StructScan(&m)
if err != nil { if err != nil {
log.Printf("scsusers.GetAllMeta: StructScan: %s\n", username, err.Error()) log.Printf("scsusers.GetAllMeta: StructScan: %s\n", username, err.Error())
return meta return meta
} }
meta[m.MetaKey]=m.MetaValue meta[m.MetaKey] = m.MetaValue
} }
return meta return meta
} }
func GetMeta(username string, metakey string) string { func GetMeta(username string, metakey string) string {
var v string var v string
q:=fmt.Sprintf(`select meta_value from %s_user_metadata where q := fmt.Sprintf(`select meta_value from %s_user_metadata where
user_id=(select userid from %s_auth where username ILIKE $1) AND meta_key=$2`, c.TablePrefix, c.TablePrefix) user_id=(select userid from %s_auth where username ILIKE $1) AND meta_key=$2`, c.TablePrefix, c.TablePrefix)
err:=c.db.Get(&v, q, username, metakey) err := c.db.Get(&v, q, username, metakey)
if err != nil && err!=sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
log.Printf("scsusers.GetMeta: %s - %s - %s\n", username, metakey, err.Error()) log.Printf("scsusers.GetMeta: %s - %s - %s\n", username, metakey, err.Error())
} }
if v=="" { if v == "" {
// get default user // get default user
err:=c.db.Get(&v, q, "//default//", metakey) err := c.db.Get(&v, q, "//default//", metakey)
if err != nil && err!=sql.ErrNoRows { if err != nil && err != sql.ErrNoRows {
log.Printf("scsusers.GetMeta: %s - %s - %s\n", username, metakey, err.Error()) log.Printf("scsusers.GetMeta: %s - %s - %s\n", username, metakey, err.Error())
} }
} }
return v return v
} }
func SetMeta(username string, metakey string, metavalue string) { func SetMeta(username string, metakey string, metavalue string) {
var err error var err error
if metavalue=="" { if metavalue == "" {
q:=fmt.Sprintf(`delete from %s_user_metadata where user_id=(select userid from %s_auth where username ILIKE $1) AND meta_key=$2`, q := fmt.Sprintf(`delete from %s_user_metadata where user_id=(select userid from %s_auth where username ILIKE $1) AND meta_key=$2`,
c.TablePrefix, c.TablePrefix) c.TablePrefix, c.TablePrefix)
_, err=c.db.Exec(q, username, metakey) _, err = c.db.Exec(q, username, metakey)
} else { } else {
q:=fmt.Sprintf(`insert into %s_user_metadata (user_id, meta_key, meta_value) VALUES q := fmt.Sprintf(`insert into %s_user_metadata (user_id, meta_key, meta_value) VALUES
((select userid from %s_auth where username ILIKE $1), $2, $3)`, c.TablePrefix, c.TablePrefix) ((select userid from %s_auth where username ILIKE $1), $2, $3)`, c.TablePrefix, c.TablePrefix)
_,err=c.db.Exec(q, username, metakey, metavalue) _, err = c.db.Exec(q, username, metakey, metavalue)
} }
if err != nil { if err != nil {
log.Printf("scsusers.SetMeta: %s %s %s %s\n", username, metakey, metavalue, err.Error()) log.Printf("scsusers.SetMeta: %s %s %s %s\n", username, metakey, metavalue, err.Error())
} }
} }
func RecoverByUsername(u string) { func RecoverByUsername(u string) {
var username, email string var username, email string
q:=fmt.Sprintf("select username, email from %s_auth where username ILIKE $1", c.TablePrefix) q := fmt.Sprintf("select username, email from %s_auth where username ILIKE $1", c.TablePrefix)
row:=c.db.QueryRow(q, u) row := c.db.QueryRow(q, u)
err:=row.Scan(&username, &email) err := row.Scan(&username, &email)
if err!=sql.ErrNoRows { if err != sql.ErrNoRows {
recoverycode:=randBytes(16) recoverycode := randBytes(16)
qq:=fmt.Sprintf("update %s_auth set recoverycode=$1, recoverytime=NOW() where username ILIKE $2", c.TablePrefix) qq := fmt.Sprintf("update %s_auth set recoverycode=$1, recoverytime=NOW() where username ILIKE $2", c.TablePrefix)
_,err:=c.db.Exec(qq, recoverycode, username) _, err := c.db.Exec(qq, recoverycode, username)
if err==nil { if err == nil {
sendRecoveryEmail(email, username, string(recoverycode)) sendRecoveryEmail(email, username, string(recoverycode))
} }
} }
@ -271,27 +270,26 @@ func RecoverByUsername(u string) {
func RecoverByEmail(e string) { func RecoverByEmail(e string) {
var username, email string var username, email string
q:=fmt.Sprintf("select username, email from %s_auth where email=$1", c.TablePrefix) q := fmt.Sprintf("select username, email from %s_auth where email=$1", c.TablePrefix)
row:=c.db.QueryRow(q, e) row := c.db.QueryRow(q, e)
err:=row.Scan(&username, &email) err := row.Scan(&username, &email)
if err!=sql.ErrNoRows { if err != sql.ErrNoRows {
recoverycode:=randBytes(16) recoverycode := randBytes(16)
qq:=fmt.Sprintf("update %s_auth set recoverycode=$1, recoverytime=NOW() where username ILIKE $2", c.TablePrefix) qq := fmt.Sprintf("update %s_auth set recoverycode=$1, recoverytime=NOW() where username ILIKE $2", c.TablePrefix)
_,err:=c.db.Exec(qq, recoverycode, username) _, err := c.db.Exec(qq, recoverycode, username)
if err==nil { if err == nil {
sendRecoveryEmail(email, username, string(recoverycode)) sendRecoveryEmail(email, username, string(recoverycode))
} }
} }
} }
func randBytes(n int) []byte { func randBytes(n int) []byte {
randomBytes := make([]byte, 32) randomBytes := make([]byte, 32)
_, err := rand.Read(randomBytes) _, err := rand.Read(randomBytes)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return []byte(base32.StdEncoding.EncodeToString(randomBytes)[:n]) return []byte(base32.StdEncoding.EncodeToString(randomBytes)[:n])
} }
func sendRegistrationEmail(recipient, username, password string) bool { func sendRegistrationEmail(recipient, username, password string) bool {
@ -321,13 +319,12 @@ func sendRegistrationEmail(recipient, username, password string) bool {
return true return true
} }
func sendAlertEmail(username, recipient, message string) bool { func sendAlertEmail(username, recipient, message string) bool {
data := struct { data := struct {
SiteName string SiteName string
FromEmail string FromEmail string
UserName string UserName string
Activity string Activity string
}{ }{
SiteName: c.SiteName, SiteName: c.SiteName,
FromEmail: c.FromEmail, FromEmail: c.FromEmail,
@ -340,9 +337,9 @@ func sendAlertEmail(username, recipient, message string) bool {
log.Printf("scsusers.sendAlertEmail: Alert template failed to execute: %v returned %s\n", data, err.Error()) log.Printf("scsusers.sendAlertEmail: Alert template failed to execute: %v returned %s\n", data, err.Error())
return false return false
} }
subject := fmt.Sprintf("New activity on %s", c.SiteName) subject := fmt.Sprintf("New activity on %s", c.SiteName)
err = SendMail(c.SMTPServer, c.FromEmail, subject, body.String(), recipient) err = SendMail(c.SMTPServer, c.FromEmail, subject, body.String(), recipient)
if err != nil { if err != nil {
log.Printf("scsusers.sendAlertEmail: Error sending mail to %s: %s\n", recipient, err.Error()) log.Printf("scsusers.sendAlertEmail: Error sending mail to %s: %s\n", recipient, err.Error())
return false return false
@ -352,15 +349,15 @@ func sendAlertEmail(username, recipient, message string) bool {
func sendRecoveryEmail(recipient, username, code string) bool { func sendRecoveryEmail(recipient, username, code string) bool {
data := struct { data := struct {
SiteName string SiteName string
FromEmail string FromEmail string
UserName string UserName string
RecoveryCode string RecoveryCode string
}{ }{
SiteName: c.SiteName, SiteName: c.SiteName,
FromEmail: c.FromEmail, FromEmail: c.FromEmail,
UserName: username, UserName: username,
RecoveryCode: code, RecoveryCode: code,
} }
var body bytes.Buffer var body bytes.Buffer
err := c.Templates.Registration.Execute(&body, data) err := c.Templates.Registration.Execute(&body, data)
@ -377,7 +374,6 @@ func sendRecoveryEmail(recipient, username, code string) bool {
return true return true
} }
func SetRegistrationTemplate(t string) bool { func SetRegistrationTemplate(t string) bool {
if len(t) != 0 { if len(t) != 0 {
r, err := template.New("reg").Parse(t) r, err := template.New("reg").Parse(t)
@ -414,7 +410,6 @@ func SetAlertTemplate(t string) bool {
return false return false
} }
func SetRecoveryTemplate(t string) bool { func SetRecoveryTemplate(t string) bool {
if len(t) != 0 { if len(t) != 0 {
r, err := template.New("recovery").Parse(t) r, err := template.New("recovery").Parse(t)