This commit is contained in:
Your Name 2023-09-19 18:01:41 -04:00
parent 4db3ca30a9
commit d6eca5086f
5 changed files with 164 additions and 56 deletions

View File

@ -1,13 +1,14 @@
package main
import (
"gitto.work/shortcut/scsusers"
"github.com/jmoiron/sqlx"
"flag"
"os"
"fmt"
_ "github.com/mattn/go-sqlite3"
)
"flag"
"fmt"
"os"
"git.teamworkapps.com/shortcut/scsusers"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
)
func main() {
var email string
@ -27,14 +28,9 @@ func main() {
os.Exit(1)
}
schema:=`CREATE TABLE test_auth (
username text NOT NULL ,
password text NOT NULL,
email text NOT NULL unique,
recovery text NOT NULL DEFAULT '',
recoverytime timestamp null,
registration_date timestamp not null,
registration_ip text not null,
lastseen timestamp );`
id int primary key autoincrement,
username text NOT NULL unique key
password text NOT NULL);`
_ ,err=db.Exec(schema)
if err != nil {
fmt.Println("Schema creation failed: " + err.Error())

9
go.mod Normal file
View File

@ -0,0 +1,9 @@
module git.teamworkapps.com/shortcut/scsusers
go 1.20
require (
github.com/jmoiron/sqlx v1.3.5
github.com/mattn/go-sqlite3 v1.14.17
golang.org/x/crypto v0.12.0
)

11
go.sum Normal file
View File

@ -0,0 +1,11 @@
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=

122
main.go
View File

@ -29,17 +29,27 @@ type config struct {
Templates templates
SMTPServer string
db *sqlx.DB
testing bool
TablePrefix string
}
type UserData struct {
Username string `json:"username"`
UserPerms map[string]string `json:"perms"`
UserSettings map[string]string `json:"settings"`
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
@ -85,18 +95,20 @@ func Register(username, email, ip string) bool {
log.Printf("scsusers.Register: Bcrypt GenerateFromPassword failed? Pass is %s and error is %s\n", pass, err.Error())
return false
}
q := fmt.Sprintf("insert into %s_auth (username, displayname, email, password, registration_date, registration_ip) values ($1, $2, $3, $4, CURRENT_TIMESTAMP, $5)", c.TablePrefix)
_, err = c.db.Exec(q, username, username, email, crypt, ip)
_, err = c.db.Query(fmt.Sprintf("insert into %s_auth (username, password) VALUES ($1, $2)", 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 = $1 AND password=$2", c.TablePrefix)
q := fmt.Sprintf("delete from %s_auth where username = $1 AND password=$2", 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())
@ -104,17 +116,38 @@ func Register(username, email, ip string) bool {
return false
}
func Login(username, password string) bool {
username = strings.ToLower(username)
func NewUser() *UserData {
var u UserData
u.Meta = make(map[string]metadata)
return &u
}
q := fmt.Sprintf("select password from %s_auth where username = $1 AND status='active'", c.TablePrefix)
var crypt string
err := c.db.Get(&crypt, q, username)
func Get(username string) (*UserData, bool) {
u := NewUser()
q := fmt.Sprintf("select username, password, id from %s_auth where username=$1", c.TablePrefix)
err := c.db.Get(&u, q, username)
if err != nil {
log.Printf("scsusers.Login: Failed login attempt for unknown username: %s\n", username)
if err == sql.ErrNoRows {
return nil, false
}
log.Printf("scsusers.Get: %s", err.Error())
return nil, false
}
q = fmt.Sprintf("select key, value, id from %s_meta where user=%d", c.TablePrefix, u.UserID)
err = c.db.Select(&u.Meta, q)
if err != nil && err != sql.ErrNoRows {
log.Printf("scsuser.Get: select: %s", err.Error())
}
return u, true
}
func Login(username, password string) bool {
u, ok := Get(username)
if !ok {
return false
}
if bcrypt.CompareHashAndPassword([]byte(crypt), []byte(password)) != nil {
if bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password)) != nil {
log.Printf("scsusers.Login: Failed password for " + username)
return false
}
@ -122,25 +155,20 @@ func Login(username, password string) bool {
return true
}
func ChangePassword(username, oldpass, newpass string) bool {
username = strings.ToLower(username)
q := fmt.Sprintf("select password from %s_auth where username = $1 AND status='active'", c.TablePrefix)
var crypt string
err := c.db.Get(&crypt, q, username)
if err != nil {
log.Println("scsusers.ChangePassword: Failed change attempt for unknown username: " + username)
return false
}
if bcrypt.CompareHashAndPassword([]byte(crypt), []byte(oldpass)) != nil {
log.Printf("scsusers.ChangePassword: Failed password for %s\n", username)
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)
q = fmt.Sprintf("update %s_auth set password=$2 where username = $1", c.TablePrefix)
_, err = c.db.Exec(q, username, newcrypt)
if err != nil {
log.Printf("scsusers.ChangePassword: update failed for %s: %s\n", username, err.Error())
log.Printf("scsusers.ChangePassword: generate: %s", err.Error())
return false
}
q := fmt.Sprintf("update %s_auth set password=$2 where userid=$1", c.TablePrefix)
_, err = c.db.Exec(q, u.UserID, newcrypt)
if err != nil {
log.Printf("scsusers.ChangePassword: update failed for %s: %s\n", u.Username, err.Error())
return false
}
return true
@ -159,22 +187,34 @@ func GetUserid(username string) int64 {
}
return i
}
func LoadUser(username string) (UserData, error) {
var tmp UserData
username = strings.ToLower(username)
q := fmt.Sprintf("select data from %s_userdata where username = $1", c.TablePrefix)
var d string
err := c.db.Get(d, q, username)
if err != nil {
log.Printf("scsusers.LoadUser: Error loading user: %s : %s\n", username, err.Error())
return tmp, err
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=$1", c.TablePrefix), tmp.ID)
if err != nil {
log.Printf("scsauth: set: delete: %s", err.Error())
return err
}
}
err = json.Unmarshal([]byte(d), &tmp)
var insertid int64
err := c.db.Get(&insertid, fmt.Sprintf("insert into %s_meta (userid, meta_key, meta_value) VALUES ($1, $2, $3) returning id", c.TablePrefix), u.UserID, key, value)
if err != nil {
log.Printf("scsusers.LoadUser: Error decoding json on user %s. Unmarshal returned %s\n", username, err.Error())
log.Printf("scsauth: set: insert: %s", err.Error())
return err
}
return tmp, 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 {
@ -191,10 +231,10 @@ func SaveUser(username string, d UserData) bool {
return false
}
return true
}
func Bump(username string, ip string) {
username = strings.ToLower(username)
q := fmt.Sprintf("update %s_auth set lastseen=CURRENT_TIMESTAMP, lastseenip=$2 where username = $1 limit 1", c.TablePrefix)
_, err := c.db.Exec(q, username, ip)

52
main_test.go Normal file
View File

@ -0,0 +1,52 @@
package scsusers
import (
"flag"
"fmt"
"os"
"testing"
"github.com/jmoiron/sqlx"
_ "github.com/mattn/go-sqlite3"
)
func TestUsers(t *testing.T) {
var email string
c.testing=true
flag.StringVar(&email, "email", "", "Email address to use for registration test")
flag.Parse()
db, err := sqlx.Open("sqlite3", ":memory:")
if err != nil {
fmt.Println("Couldn't open sqlite3 in-memory db:" + err.Error())
os.Exit(1)
}
err = db.Ping()
if err != nil {
fmt.Println("Couldn't ping sqlite3 in-memory db:" + err.Error())
os.Exit(1)
}
schema := `CREATE TABLE test_auth (
id integer primary key autoincrement,
username text NOT NULL unique,
password text NOT NULL);`
_, err = db.Exec(schema)
if err != nil {
fmt.Println("Schema creation failed: " + err.Error())
os.Exit(1)
}
Init(db, "test", "Example Test", "nobody@nowhere.com", "localhost:25")
a := UsernameAvailable("testuser")
fmt.Printf("Initial test of username available: %v\n", a)
a = Register("testuser", email, "127.0.0.1")
fmt.Printf("Register returned %v\n", a)
fmt.Printf("Attempt to log in with invalid username returned %v\n", Login("baduser", "badpass"))
fmt.Printf("Enter code from email:")
var code string
fmt.Scan(&code)
a = Login("testuser", code)
fmt.Printf("Login returned %v\n", a)
}