Files
2026-05-07 02:06:54 +02:00

107 lines
2.7 KiB
Go

package handlers
import (
"crypto/md5"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/go-chi/chi/v5"
"golang.org/x/crypto/ssh"
"xorm.io/xorm"
"github.com/forgeo/forgebucket/internal/api/middleware"
"github.com/forgeo/forgebucket/internal/models"
)
type SSHKeyHandler struct {
db *xorm.Engine
}
func NewSSHKeyHandler(db *xorm.Engine) *SSHKeyHandler {
return &SSHKeyHandler{db: db}
}
func (h *SSHKeyHandler) List(w http.ResponseWriter, r *http.Request) {
userID, _ := middleware.UserIDFromContext(r.Context())
var keys []models.SSHKey
if err := h.db.Where("user_id = ?", userID).Find(&keys); err != nil {
jsonError(w, "could not list SSH keys", http.StatusInternalServerError)
return
}
if keys == nil {
keys = []models.SSHKey{}
}
jsonOK(w, keys)
}
func (h *SSHKeyHandler) Add(w http.ResponseWriter, r *http.Request) {
userID, _ := middleware.UserIDFromContext(r.Context())
var body struct {
Title string `json:"title"`
PublicKey string `json:"publicKey"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
jsonError(w, "invalid request body", http.StatusBadRequest)
return
}
if body.Title == "" || body.PublicKey == "" {
jsonError(w, "title and publicKey are required", http.StatusBadRequest)
return
}
// Parse and validate the public key
pubKeyBytes := []byte(strings.TrimSpace(body.PublicKey))
pub, _, _, _, err := ssh.ParseAuthorizedKey(pubKeyBytes)
if err != nil {
jsonError(w, "invalid SSH public key format", http.StatusBadRequest)
return
}
// Compute MD5 fingerprint (standard display format)
fingerprint := fingerprintMD5(pub)
key := &models.SSHKey{
UserID: userID,
Title: body.Title,
Fingerprint: fingerprint,
PublicKey: strings.TrimSpace(body.PublicKey),
}
if _, err := h.db.Insert(key); err != nil {
jsonError(w, "key already exists or could not be saved", http.StatusConflict)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(key)
}
func (h *SSHKeyHandler) Delete(w http.ResponseWriter, r *http.Request) {
userID, _ := middleware.UserIDFromContext(r.Context())
keyID, err := strconv.ParseInt(chi.URLParam(r, "keyID"), 10, 64)
if err != nil {
jsonError(w, "invalid key ID", http.StatusBadRequest)
return
}
n, err := h.db.Where("id = ? AND user_id = ?", keyID, userID).Delete(&models.SSHKey{})
if err != nil || n == 0 {
jsonError(w, "key not found", http.StatusNotFound)
return
}
w.WriteHeader(http.StatusNoContent)
}
func fingerprintMD5(pub ssh.PublicKey) string {
hash := md5.Sum(pub.Marshal())
parts := make([]string, len(hash))
for i, b := range hash {
parts[i] = fmt.Sprintf("%02x", b)
}
return strings.Join(parts, ":")
}