package scsusers import ( "database/sql" "encoding/json" "fmt" "html/template" "log" "strings" "github.com/jmoiron/sqlx" "golang.org/x/crypto/bcrypt" ) type templates struct { Registration *template.Template Alert *template.Template Recovery *template.Template TwoFA *template.Template } type config struct { SiteName string FromEmail string Templates templates SMTPServer string db *sqlx.DB testing bool TablePrefix string } type UserData struct { UserID int64 `db:"id"` Username string `db:"username"` Password string `db:"password"` Meta map[string]metadata } type metadata struct { Key string `db:"meta_key"` Value string `db:"meta_value"` ID int64 `db:"id"` } var c config func Init(dbin *sqlx.DB, tp, sitename, fromaddr, smtpserver string) { c.db = dbin c.TablePrefix = tp c.SiteName = sitename c.FromEmail = fromaddr c.SMTPServer = smtpserver SetRegistrationTemplate("") SetAlertTemplate("") SetRecoveryTemplate("") Set2faTemplate("") } func UsernameAvailable(username string) bool { if len(username) == 0 { return false } var tmp string username = strings.ToLower(username) q := fmt.Sprintf("select username from %s_auth where username = ?", c.TablePrefix) err := c.db.Get(&tmp, q, username) if err == sql.ErrNoRows { return true } if err != nil { log.Printf("UsernameAvailable returned error: " + err.Error() + " Query was " + q) return false } return false } /* Check for username availability, add to database, send email */ func Register(username, email, ip string) bool { if !UsernameAvailable(username) { return false } username = strings.ToLower(username) pass := randBytes(16) crypt, err := bcrypt.GenerateFromPassword(pass, 10) if err != nil { log.Printf("scsusers.Register: Bcrypt GenerateFromPassword failed? Pass is %s and error is %s\n", pass, err.Error()) return false } _, err = c.db.Query(fmt.Sprintf("insert into %s_auth (username, password) VALUES (?,?)", c.TablePrefix), username, crypt) if err != nil { log.Printf("scsusers.Register: insert failed: %s\n", err.Error()) return false } if c.testing { return true } if SendRegistrationEmail(email, username, string(pass)) { log.Printf("scsusers.Register: New user registration: %s from %s\n", username, ip) return true } log.Printf("scsusers.Register: Failed to send registration email, deleting user %s\n", username) q := fmt.Sprintf("delete from %s_auth where username = ? AND password=?", c.TablePrefix) _, err = c.db.Exec(q, username, string(crypt)) if err != nil { log.Printf("scsusers.Register: Failed to delete new user %s: %s\n", username, err.Error()) } return false } func NewUser() *UserData { var u UserData u.Meta = make(map[string]metadata) return &u } func Get(username string) (*UserData, bool) { u := NewUser() q := fmt.Sprintf("select username, password, id from %s_auth where username=?", c.TablePrefix) err := c.db.Get(u, q, username) if err != nil { if err == sql.ErrNoRows { return nil, false } log.Printf("scsusers.Get: %s", err.Error()) return nil, false } q = fmt.Sprintf("select meta_key, meta_value, id from %s_meta where user=?", c.TablePrefix) rows, err := c.db.Query(q, u.UserID) if err != nil && err != sql.ErrNoRows { log.Printf("scsuser.Get: select: %s", err.Error()) return u, false } for rows.Next() { var m metadata rows.Scan(m.Key, m.Value, m.ID) u.Meta[m.Key] = m } return u, true } func Login(username, password string) bool { u, ok := Get(username) if !ok { return false } if bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)) != nil { log.Printf("scsusers.Login: Failed password for " + username) return false } log.Printf("User %s logged in\n", username) return true } func (u *UserData) ChangePassword(oldpass, newpass string) bool { if bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(oldpass)) != nil { log.Printf("scsusers.ChangePassword: Failed password for %s\n", u.Username) return false } newcrypt, err := bcrypt.GenerateFromPassword([]byte(newpass), 10) if err != nil { log.Printf("scsusers.ChangePassword: generate: %s", err.Error()) return false } q := fmt.Sprintf("update %s_auth set password=? where userid=?", c.TablePrefix) _, err = c.db.Exec(q, newcrypt, u.UserID) if err != nil { log.Printf("scsusers.ChangePassword: update failed for %s: %s\n", u.Username, err.Error()) return false } return true } func GetUserid(username string) int64 { var i int64 username = strings.ToLower(username) q := fmt.Sprintf("select userid from %s_auth where username = ?", c.TablePrefix) err := c.db.Get(&i, q, username) if err != nil { log.Printf("scsusers.getUserId: Error loading user: %s : %s\n", username, err.Error()) return 0 } return i } func (u *UserData) Get(key string) (string, bool) { tmp, ok := u.Meta[key] return tmp.Value, ok } func (u *UserData) Set(key, value string) error { tmp, ok := u.Meta[key] if ok { _, err := c.db.Query(fmt.Sprintf("delete from %s_meta where id=?", c.TablePrefix), tmp.ID) if err != nil { log.Printf("scsauth: set: delete: %s", err.Error()) return err } } var insertid int64 err := c.db.Get(&insertid, fmt.Sprintf("insert into %s_meta (userid, meta_key, meta_value) VALUES (?,?,?) returning id", c.TablePrefix), u.UserID, key, value) if err != nil { log.Printf("scsauth: set: insert: %s", err.Error()) return err } var m metadata m.Key = key m.Value = value m.ID = insertid u.Meta[key] = m return nil } func SaveUser(username string, d UserData) bool { username = strings.ToLower(username) q := fmt.Sprintf("update %s_userdata set data=? where username = ?", c.TablePrefix) j, err := json.Marshal(d) if err != nil { log.Printf("scsusers.SaveUser: json.Marshal failed for username %s : %s\n", username, err.Error()) return false } _, err = c.db.Exec(q, username, j) if err != nil { log.Printf("scsusers.SaveUser: db.Exec failed for username %s : %s\n", username, err.Error()) return false } return true } type Metadata struct { MetaKey string `db:"meta_key"` MetaValue string `db:"meta_value"` } func GetAllMeta(username string) map[string]string { meta := make(map[string]string) username = strings.ToLower(username) q := fmt.Sprintf(`select meta_key, meta_value from %s_user_metadata where user_id=(select userid from %s_auth where username = ?)`, c.TablePrefix, c.TablePrefix) rows, err := c.db.Queryx(q, username) if err != nil && err != sql.ErrNoRows { log.Printf("scsusers.GetAllMeta: %s: %s\n", username, err.Error()) return meta } var m Metadata for rows.Next() { err = rows.StructScan(&m) if err != nil { log.Printf("scsusers.GetAllMeta: StructScan: %s: %s\n", username, err.Error()) return meta } meta[m.MetaKey] = m.MetaValue } return meta } func GetMeta(username string, metakey string) string { var v string username = strings.ToLower(username) q := fmt.Sprintf(`select meta_value from %s_user_metadata where user_id=(select userid from %s_auth where username = ?) AND meta_key=?`, c.TablePrefix, c.TablePrefix) err := c.db.Get(&v, q, username, metakey) if err != nil && err != sql.ErrNoRows { log.Printf("scsusers.GetMeta: %s - %s - %s\n", username, metakey, err.Error()) } if v == "" { // get default user err := c.db.Get(&v, q, "//default//", metakey) if err != nil && err != sql.ErrNoRows { log.Printf("scsusers.GetMeta: %s - %s - %s\n", username, metakey, err.Error()) } } return v } func SetMeta(username string, metakey string, metavalue string) { var err error username = strings.ToLower(username) if metavalue == "" { q := fmt.Sprintf(`delete from %s_user_metadata where user_id=(select userid from %s_auth where username = ?) AND meta_key=?`, c.TablePrefix, c.TablePrefix) _, err = c.db.Exec(q, username, metakey) } else { q := fmt.Sprintf(`insert into %s_user_metadata (user_id, meta_key, meta_value) VALUES ((select userid from %s_auth where username = ?), ?, ?)`, c.TablePrefix, c.TablePrefix) _, err = c.db.Exec(q, username, metakey, metavalue) } if err != nil { log.Printf("scsusers.SetMeta: %s %s %s %s\n", username, metakey, metavalue, err.Error()) } }