forked from tildetown/bbj2
		
	stuff
This commit is contained in:
		
							parent
							
								
									5498804032
								
							
						
					
					
						commit
						758acb3b4e
					
				| @ -161,8 +161,9 @@ func handler(opts options, f http.HandlerFunc) http.HandlerFunc { | ||||
| // transport encryption and then, on the server side, proper salted hashing. | ||||
| 
 | ||||
| type User struct { | ||||
| 	// TODO | ||||
| 	ID string | ||||
| 	ID       string | ||||
| 	Username string | ||||
| 	Hash     string | ||||
| } | ||||
| 
 | ||||
| type BBJResponse struct { | ||||
| @ -177,39 +178,55 @@ func writeResponse(w http.ResponseWriter, resp BBJResponse) { | ||||
| 	json.NewEncoder(w).Encode(resp) | ||||
| } | ||||
| 
 | ||||
| // NB breaking: i'm not just returning 200 always but using http status codes | ||||
| func writeErrorResponse(w http.ResponseWriter, code int, resp BBJResponse) { | ||||
| 	w.WriteHeader(code) | ||||
| 	w.Header().Set("Content-Type", "application/json") | ||||
| 	json.NewEncoder(w).Encode(resp) | ||||
| } | ||||
| 
 | ||||
| // NB breaking: i'm not just returning 200 always but using http status codes | ||||
| 
 | ||||
| type AuthInfo struct { | ||||
| 	Username string | ||||
| 	Hash     string | ||||
| } | ||||
| 
 | ||||
| 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 == "" { | ||||
| func getUserFromReq(opts options, req *http.Request) (u User, err error) { | ||||
| 	u.Username = req.Header.Get("User") | ||||
| 	u.Hash = req.Header.Get("Auth") | ||||
| 	if u.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) | ||||
| 	if u.Username == "anon" { | ||||
| 		if !opts.Config.AllowAnon { | ||||
| 			err = errors.New("anonymous access disabled") | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	db := opts.DB | ||||
| 	stmt, err := db.Prepare("select auth_hash, id from users where user_name = ?") | ||||
| 	if err != nil { | ||||
| 		err = fmt.Errorf("db error: %w", err) | ||||
| 		return | ||||
| 	} | ||||
| 	defer stmt.Close() | ||||
| 
 | ||||
| 	opts.Logf("querying for %s", u.Username) | ||||
| 
 | ||||
| 	var authHash string | ||||
| 	if err = stmt.QueryRow(u.Username).Scan(&authHash, u.ID); err != nil { | ||||
| 		if strings.Contains(err.Error(), "no rows in result") { | ||||
| 			err = errors.New("no such user") | ||||
| 		} else { | ||||
| 			err = fmt.Errorf("db error: %w", err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if authHash != u.Hash { | ||||
| 		err = errors.New("bad credentials") | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func checkAuth(opts options, ai AuthInfo) error { | ||||
| func checkAuth(opts options, username, hash string) error { | ||||
| 	db := opts.DB | ||||
| 	stmt, err := db.Prepare("select auth_hash from users where user_name = ?") | ||||
| 	if err != nil { | ||||
| @ -217,17 +234,17 @@ func checkAuth(opts options, ai AuthInfo) error { | ||||
| 	} | ||||
| 	defer stmt.Close() | ||||
| 
 | ||||
| 	opts.Logf("querying for %s", ai.Username) | ||||
| 	opts.Logf("querying for %s", username) | ||||
| 
 | ||||
| 	var authHash string | ||||
| 	if err = stmt.QueryRow(ai.Username).Scan(&authHash); err != nil { | ||||
| 	if err = stmt.QueryRow(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 { | ||||
| 	if authHash != hash { | ||||
| 		return errors.New("bad credentials") | ||||
| 	} | ||||
| 
 | ||||
| @ -296,7 +313,7 @@ func setupAPI(opts options) { | ||||
| 
 | ||||
| 		opts.Logf("querying for %s", args.Username) | ||||
| 
 | ||||
| 		if err := checkAuth(opts, AuthInfo{args.Username, args.Hash}); err == nil { | ||||
| 		if err := checkAuth(opts, args.Username, args.Hash); err == nil { | ||||
| 			opts.Logf("found %s", args.Username) | ||||
| 			// code 4 apparently | ||||
| 			writeErrorResponse(w, 403, BBJResponse{ | ||||
| @ -415,7 +432,7 @@ func setupAPI(opts options) { | ||||
| 		} | ||||
| 
 | ||||
| 		// TODO make this getUserInfoFromReq or similar so we can use the user ID later | ||||
| 		authInfo, err := getAuthInfo(opts, req) | ||||
| 		user, err := getUserFromReq(opts, req) | ||||
| 		if err != nil { | ||||
| 			writeErrorResponse(w, 403, BBJResponse{ | ||||
| 				Error: true, | ||||
| @ -455,8 +472,6 @@ func setupAPI(opts options) { | ||||
| 		} | ||||
| 		defer stmt.Close() | ||||
| 
 | ||||
| 		// TODO user id, not username | ||||
| 
 | ||||
| 		threadID, err := uuid.NewRandom() | ||||
| 		if err != nil { | ||||
| 			serverErr(w, err) | ||||
| @ -465,11 +480,11 @@ func setupAPI(opts options) { | ||||
| 		now := time.Now() | ||||
| 		if _, err = stmt.Exec( | ||||
| 			threadID, | ||||
| 			authInfo.Username, | ||||
| 			user.ID, | ||||
| 			args.Title, | ||||
| 			now, | ||||
| 			now, | ||||
| 			authInfo.Username, | ||||
| 			user.Username, | ||||
| 		); err != nil { | ||||
| 			serverErr(w, err) | ||||
| 			return | ||||
| @ -484,7 +499,7 @@ func setupAPI(opts options) { | ||||
| 
 | ||||
| 		if _, err = stmt.Exec( | ||||
| 			threadID, | ||||
| 			authInfo.Username, | ||||
| 			user.ID, | ||||
| 			now, | ||||
| 			args.Body, | ||||
| 			args.SendRaw, | ||||
| @ -498,8 +513,83 @@ func setupAPI(opts options) { | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		// TODO return the thread | ||||
| 		stmt, err = db.Prepare("select * from threads where thread_id = ? limit 1") | ||||
| 		if err != nil { | ||||
| 			serverErr(w, err) | ||||
| 			return | ||||
| 		} | ||||
| 		defer stmt.Close() | ||||
| 
 | ||||
| 		writeResponse(w, BBJResponse{Data: "TODO"}) | ||||
| 		t := &Thread{} | ||||
| 
 | ||||
| 		// TODO fill in rest of thread | ||||
| 		if err = stmt.QueryRow(threadID).Scan( | ||||
| 			t.ID, | ||||
| 			t.Author, | ||||
| 			t.Title, | ||||
| 			t.LastMod, | ||||
| 			t.Created, | ||||
| 			t.ReplyCount, | ||||
| 			t.Pinned, | ||||
| 			t.LastAuthor, | ||||
| 		); err != nil { | ||||
| 			serverErr(w, err) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		stmt, err = db.Prepare("select * from messages where thread_id = ?") | ||||
| 		if err != nil { | ||||
| 			serverErr(w, err) | ||||
| 			return | ||||
| 		} | ||||
| 		defer stmt.Close() | ||||
| 		rows, err := stmt.Query(threadID) | ||||
| 		if err != nil { | ||||
| 			serverErr(w, err) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		t.Messages = []Message{} | ||||
| 
 | ||||
| 		for rows.Next() { | ||||
| 			m := &Message{} | ||||
| 			if err := rows.Scan( | ||||
| 				m.ThreadID, | ||||
| 				m.PostID, | ||||
| 				m.Author, | ||||
| 				m.Created, | ||||
| 				m.Edited, | ||||
| 				m.Body, | ||||
| 				m.SendRaw, | ||||
| 			); err != nil { | ||||
| 				serverErr(w, err) | ||||
| 				return | ||||
| 			} | ||||
| 			t.Messages = append(t.Messages, *m) | ||||
| 		} | ||||
| 
 | ||||
| 		writeResponse(w, BBJResponse{Data: t}) | ||||
| 	})) | ||||
| } | ||||
| 
 | ||||
| type Thread struct { | ||||
| 	ID         string `json:"thread_id"` | ||||
| 	Author     string | ||||
| 	Title      string | ||||
| 	LastMod    time.Time `json:"last_mod"` | ||||
| 	Created    time.Time | ||||
| 	ReplyCount int    `json:"reply_count"` | ||||
| 	Pinned     int    // TODO bool | ||||
| 	LastAuthor string `json:"last_author"` | ||||
| 	Messages   []Message | ||||
| } | ||||
| 
 | ||||
| type Message struct { | ||||
| 	ThreadID string `json:"thread_id"` | ||||
| 	PostID   string `json:"post_id"` | ||||
| 	Author   string | ||||
| 	Created  time.Time | ||||
| 	Edited   int // TODO bool | ||||
| 	Body     string | ||||
| 	SendRaw  int `json:"send_raw"` // TODO bool | ||||
| } | ||||
|  | ||||
| @ -27,6 +27,7 @@ insert into users values ( | ||||
| ); | ||||
| 
 | ||||
| -- TODO unique constraint on user_name? | ||||
| -- TODO foreign keys | ||||
| 
 | ||||
| create table threads ( | ||||
|   thread_id text,   -- uuid string | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user