todo
parent
38854d5461
commit
77b65feb29
|
@ -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"})
|
||||
}))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue