107 lines
2.7 KiB
Go
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, ":")
|
|
}
|