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
}
type Opts struct {
type options struct {
ConfigPath string
IO iostreams
Log func(string)
@ -53,7 +53,7 @@ func main() {
Err: os.Stderr,
Out: os.Stdout,
}
opts := &Opts{
opts := &options{
ConfigPath: *configFlag,
Reset: *resetFlag,
IO: io,
@ -75,7 +75,7 @@ func main() {
type Teardown func()
func setupDB(opts *Opts) (Teardown, error) {
func setupDB(opts *options) (Teardown, error) {
db, err := sql.Open("sqlite3", opts.Config.DBPath)
fmt.Printf("DBG %#v\n", db)
@ -84,7 +84,7 @@ func setupDB(opts *Opts) (Teardown, error) {
return func() { db.Close() }, err
}
func _main(opts *Opts) error {
func _main(opts *options) error {
cfg, err := parseConfig(opts.ConfigPath)
if err != nil {
fmt.Fprintf(os.Stderr, "could not read config file '%s'", opts.ConfigPath)
@ -115,7 +115,7 @@ func _main(opts *Opts) error {
return nil
}
func ensureSchema(opts Opts) error {
func ensureSchema(opts options) error {
db := opts.DB
if opts.Reset {
@ -150,7 +150,7 @@ func ensureSchema(opts Opts) error {
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
return func(w http.ResponseWriter, req *http.Request) {
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
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
type AuthInfo struct {
Username string
Hash 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) {
opts.Logf(err.Error())
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) {
if req.Method != "POST" {
badMethod(w)
@ -236,7 +283,7 @@ func setupAPI(opts Opts) {
type AuthArgs struct {
Username string `json:"user_name"`
AuthHash string `json:"auth_hash"`
Hash string `json:"auth_hash"`
}
var args AuthArgs
@ -245,44 +292,34 @@ func setupAPI(opts Opts) {
return
}
if args.AuthHash == "" || args.Username == "" {
if args.Hash == "" || args.Username == "" {
invalidArgs(w)
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)
var authHash string
err = stmt.QueryRow(args.Username).Scan(&authHash)
if err == nil {
if err := checkAuth(opts, AuthInfo{args.Username, args.Hash}); err == nil {
opts.Logf("found %s", args.Username)
// code 4 apparently
writeErrorResponse(w, 403, BBJResponse{
Error: true,
Data: "user already exists",
})
return
} else if err != nil && !strings.Contains(err.Error(), "no rows in result") {
} else if err.Error() != "no such user" {
serverErr(w, err)
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()
if err != nil {
serverErr(w, err)
return
}
_, err = stmt.Exec(id, args.Username, args.AuthHash, time.Now())
_, err = stmt.Exec(id, args.Username, args.Hash, time.Now())
if err != nil {
serverErr(w, err)
}
@ -375,7 +412,29 @@ func setupAPI(opts Opts) {
}))
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
writeResponse(w, BBJResponse{Data: "TODO"})
}))
}