trunk
vilmibm 2022-05-03 20:24:08 -05:00
parent 38854d5461
commit 77b65feb29
1 changed files with 96 additions and 37 deletions

View File

@ -35,7 +35,7 @@ type iostreams struct {
Out io.Writer Out io.Writer
} }
type Opts struct { type options struct {
ConfigPath string ConfigPath string
IO iostreams IO iostreams
Log func(string) Log func(string)
@ -53,7 +53,7 @@ func main() {
Err: os.Stderr, Err: os.Stderr,
Out: os.Stdout, Out: os.Stdout,
} }
opts := &Opts{ opts := &options{
ConfigPath: *configFlag, ConfigPath: *configFlag,
Reset: *resetFlag, Reset: *resetFlag,
IO: io, IO: io,
@ -75,7 +75,7 @@ func main() {
type Teardown func() type Teardown func()
func setupDB(opts *Opts) (Teardown, error) { func setupDB(opts *options) (Teardown, error) {
db, err := sql.Open("sqlite3", opts.Config.DBPath) db, err := sql.Open("sqlite3", opts.Config.DBPath)
fmt.Printf("DBG %#v\n", db) fmt.Printf("DBG %#v\n", db)
@ -84,7 +84,7 @@ func setupDB(opts *Opts) (Teardown, error) {
return func() { db.Close() }, err return func() { db.Close() }, err
} }
func _main(opts *Opts) error { func _main(opts *options) error {
cfg, err := parseConfig(opts.ConfigPath) cfg, err := parseConfig(opts.ConfigPath)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "could not read config file '%s'", opts.ConfigPath) fmt.Fprintf(os.Stderr, "could not read config file '%s'", opts.ConfigPath)
@ -115,7 +115,7 @@ func _main(opts *Opts) error {
return nil return nil
} }
func ensureSchema(opts Opts) error { func ensureSchema(opts options) error {
db := opts.DB db := opts.DB
if opts.Reset { if opts.Reset {
@ -150,7 +150,7 @@ func ensureSchema(opts Opts) error {
return nil return nil
} }
func handler(opts Opts, f http.HandlerFunc) http.HandlerFunc { func handler(opts options, f http.HandlerFunc) http.HandlerFunc {
// TODO make this more real // TODO make this more real
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
opts.Log(req.URL.Path) opts.Log(req.URL.Path)
@ -188,24 +188,56 @@ func writeErrorResponse(w http.ResponseWriter, code int, resp BBJResponse) {
// NB breaking: i'm not just returning 200 always but using http status codes // NB breaking: i'm not just returning 200 always but using http status codes
func setupAPI(opts Opts) { type AuthInfo struct {
Username string
http.HandleFunc("/instance_info", handler(opts, func(w http.ResponseWriter, req *http.Request) { Hash string
type instanceInfo struct {
InstanceName string `json:"instance_name"`
AllowAnon bool `json:"allow_anon"`
Admins []string
} }
writeResponse(w, BBJResponse{
Data: instanceInfo{
InstanceName: opts.Config.InstanceName,
AllowAnon: opts.Config.AllowAnon,
Admins: opts.Config.Admins,
},
})
}))
func getAuthInfo(opts options, req *http.Request) (ai AuthInfo, err error) {
ai.Username = req.Header.Get("User")
ai.Hash = req.Header.Get("Auth")
if ai.Username == "" {
err = errors.New("no User header set")
}
if ai.Username == "anon" {
if !opts.Config.AllowAnon {
err = errors.New("anonymous access disabled")
}
return
}
err = checkAuth(opts, ai)
return
}
func checkAuth(opts options, ai AuthInfo) error {
db := opts.DB
stmt, err := db.Prepare("select auth_hash from users where user_name = ?")
if err != nil {
return fmt.Errorf("db error: %w", err)
}
defer stmt.Close()
opts.Logf("querying for %s", ai.Username)
var authHash string
if err = stmt.QueryRow(ai.Username).Scan(&authHash); err != nil {
if strings.Contains(err.Error(), "no rows in result") {
return errors.New("no such user")
}
return fmt.Errorf("db error: %w", err)
}
if authHash != ai.Hash {
return errors.New("bad credentials")
}
return nil
}
func setupAPI(opts options) {
serverErr := func(w http.ResponseWriter, err error) { serverErr := func(w http.ResponseWriter, err error) {
opts.Logf(err.Error()) opts.Logf(err.Error())
writeErrorResponse(w, 500, BBJResponse{ writeErrorResponse(w, 500, BBJResponse{
@ -228,6 +260,21 @@ func setupAPI(opts Opts) {
}) })
} }
http.HandleFunc("/instance_info", handler(opts, func(w http.ResponseWriter, req *http.Request) {
type instanceInfo struct {
InstanceName string `json:"instance_name"`
AllowAnon bool `json:"allow_anon"`
Admins []string
}
writeResponse(w, BBJResponse{
Data: instanceInfo{
InstanceName: opts.Config.InstanceName,
AllowAnon: opts.Config.AllowAnon,
Admins: opts.Config.Admins,
},
})
}))
http.HandleFunc("/user_register", handler(opts, func(w http.ResponseWriter, req *http.Request) { http.HandleFunc("/user_register", handler(opts, func(w http.ResponseWriter, req *http.Request) {
if req.Method != "POST" { if req.Method != "POST" {
badMethod(w) badMethod(w)
@ -236,7 +283,7 @@ func setupAPI(opts Opts) {
type AuthArgs struct { type AuthArgs struct {
Username string `json:"user_name"` Username string `json:"user_name"`
AuthHash string `json:"auth_hash"` Hash string `json:"auth_hash"`
} }
var args AuthArgs var args AuthArgs
@ -245,44 +292,34 @@ func setupAPI(opts Opts) {
return return
} }
if args.AuthHash == "" || args.Username == "" { if args.Hash == "" || args.Username == "" {
invalidArgs(w) invalidArgs(w)
return return
} }
db := opts.DB
stmt, err := db.Prepare("select auth_hash from users where user_name = ?")
if err != nil {
serverErr(w, err)
return
}
defer stmt.Close()
opts.Logf("querying for %s", args.Username) opts.Logf("querying for %s", args.Username)
var authHash string if err := checkAuth(opts, AuthInfo{args.Username, args.Hash}); err == nil {
err = stmt.QueryRow(args.Username).Scan(&authHash)
if err == nil {
opts.Logf("found %s", args.Username) opts.Logf("found %s", args.Username)
// code 4 apparently // code 4 apparently
writeErrorResponse(w, 403, BBJResponse{ writeErrorResponse(w, 403, BBJResponse{
Error: true, Error: true,
Data: "user already exists", Data: "user already exists",
}) })
return } else if err.Error() != "no such user" {
} else if err != nil && !strings.Contains(err.Error(), "no rows in result") {
serverErr(w, err) serverErr(w, err)
return return
} }
stmt, err = db.Prepare(`INSERT INTO users VALUES (?, ?, ?, "", "", 0, 0, ?)`) db := opts.DB
stmt, err := db.Prepare(`INSERT INTO users VALUES (?, ?, ?, "", "", 0, 0, ?)`)
id, err := uuid.NewRandom() id, err := uuid.NewRandom()
if err != nil { if err != nil {
serverErr(w, err) serverErr(w, err)
return return
} }
_, err = stmt.Exec(id, args.Username, args.AuthHash, time.Now()) _, err = stmt.Exec(id, args.Username, args.Hash, time.Now())
if err != nil { if err != nil {
serverErr(w, err) serverErr(w, err)
} }
@ -375,7 +412,29 @@ func setupAPI(opts Opts) {
})) }))
http.HandleFunc("/thread_create", handler(opts, func(w http.ResponseWriter, req *http.Request) { http.HandleFunc("/thread_create", handler(opts, func(w http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
badMethod(w)
return
}
authInfo, err := getAuthInfo(opts, req)
if err != nil {
writeErrorResponse(w, 403, BBJResponse{
Error: true,
Data: err.Error(),
})
return
}
fmt.Printf("DBG %#v\n", authInfo)
type threadCreateArgs struct {
Title string
Body string
}
// TODO // TODO
writeResponse(w, BBJResponse{Data: "TODO"}) writeResponse(w, BBJResponse{Data: "TODO"})
})) }))
} }