Compare commits

...

2 Commits

Author SHA1 Message Date
Joshua Herring e115492f0d basic API handler 3 weeks ago
Joshua Herring 5be9e89f52 add api handler 3 weeks ago

134
api.go

@ -0,0 +1,134 @@
package main
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"faculty_media_report/dbi"
"github.com/golang-jwt/jwt/v5"
)
type jsonRPCRequest struct {
Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"`
Params json.RawMessage `json:"params"`
ID *int `json:"id"`
}
type jsonRPCErrorBody struct {
Code int `json:"code"`
Message string `json:"message"`
}
type jsonRPCResponse struct {
Jsonrpc string `json:"jsonrpc"`
Result interface{} `json:"result,omitempty"`
Error *jsonRPCErrorBody `json:"error,omitempty"`
ID *int `json:"id"`
}
func writeJSONRPC(w http.ResponseWriter, resp jsonRPCResponse) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
}
func jsonRPCErr(w http.ResponseWriter, code int, message string, id *int) {
writeJSONRPC(w, jsonRPCResponse{
Jsonrpc: "2.0",
Error: &jsonRPCErrorBody{Code: code, Message: message},
ID: id,
})
}
func handleAPI(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if !strings.HasPrefix(authHeader, "Bearer ") {
http.Error(w, "missing or invalid Authorization header", http.StatusUnauthorized)
return
}
tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
db, err := dbi.GetDbConn()
if err != nil {
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
defer db.Close()
conn, err := db.Conn(context.Background())
if err != nil {
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
defer conn.Close()
var user dbi.User
token, err := jwt.Parse(tokenStr, func(t *jwt.Token) (interface{}, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
}
sub, err := t.Claims.GetSubject()
if err != nil {
return nil, fmt.Errorf("missing sub claim")
}
user, err = dbi.GetUser(conn, sub)
if err != nil {
return nil, fmt.Errorf("user not found")
}
return []byte(user.APIKey), nil
}, jwt.WithExpirationRequired())
if err != nil || !token.Valid {
if err != nil {
http.Error(w, "unauthorized: "+err.Error(), http.StatusUnauthorized)
} else {
http.Error(w, "unauthorized", http.StatusUnauthorized)
}
return
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
http.Error(w, "invalid token claims", http.StatusUnauthorized)
return
}
iat, err := claims.GetIssuedAt()
if err != nil || iat == nil {
http.Error(w, "missing iat claim", http.StatusUnauthorized)
return
}
if iat.After(time.Now()) {
http.Error(w, "iat is in the future", http.StatusUnauthorized)
return
}
var req jsonRPCRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
jsonRPCErr(w, -32700, "Parse error", nil)
return
}
if req.Jsonrpc != "2.0" {
jsonRPCErr(w, -32600, `Invalid Request: jsonrpc must be "2.0"`, req.ID)
return
}
if req.Method == "" {
jsonRPCErr(w, -32600, "Invalid Request: method is required", req.ID)
return
}
if req.Params == nil {
jsonRPCErr(w, -32600, "Invalid Request: params is required", req.ID)
return
}
if req.ID == nil {
jsonRPCErr(w, -32600, "Invalid Request: id is required", req.ID)
return
}
_ = user
jsonRPCErr(w, -32601, "Method not found", req.ID)
}

@ -72,5 +72,6 @@ func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /faculty/activity/login", handleLoginGet)
mux.HandleFunc("POST /faculty/activity/login", handleLoginPost)
mux.HandleFunc("POST /faculty/activity/api", handleAPI)
cgi.Serve(mux)
}

Loading…
Cancel
Save