Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 58 additions & 20 deletions internal/server/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
Expand Down Expand Up @@ -89,6 +90,8 @@ func (s *Server) handlePublish() http.HandlerFunc {
version := r.FormValue("version")
description := r.FormValue("description")
author := r.FormValue("author")
sourceRepository := r.FormValue("source_repository")
sourceIssues := r.FormValue("source_issues")
downloadURL := r.FormValue("download_url")
tagsStr := r.FormValue("tags")

Expand All @@ -97,6 +100,13 @@ func (s *Server) handlePublish() http.HandlerFunc {
return
}

if sourceIssues != "" {
if err := checkURL(sourceIssues); err != nil {
writeJSON(w, http.StatusUnprocessableEntity, map[string]string{"error": "invalid or unreachable issues URL: " + err.Error()})
return
}
}

var tags []string
if tagsStr != "" {
tags = strings.Split(tagsStr, ",")
Expand All @@ -106,12 +116,14 @@ func (s *Server) handlePublish() http.HandlerFunc {
}

pkg := store.Package{
Name: name,
Version: version,
Description: description,
Author: author,
DownloadURL: downloadURL,
Tags: tags,
Name: name,
Version: version,
Description: description,
Author: author,
SourceRepository: sourceRepository,
SourceIssues: sourceIssues,
DownloadURL: downloadURL,
Tags: tags,
}

file, _, err := r.FormFile("file")
Expand All @@ -136,13 +148,15 @@ func (s *Server) handlePublish() http.HandlerFunc {
}

var req struct {
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description"`
Author string `json:"author"`
DownloadURL string `json:"download_url"`
SHA256 string `json:"sha256"`
Tags []string `json:"tags"`
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description"`
Author string `json:"author"`
SourceRepository string `json:"source_repository"`
SourceIssues string `json:"source_issues"`
DownloadURL string `json:"download_url"`
SHA256 string `json:"sha256"`
Tags []string `json:"tags"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON: " + err.Error()})
Expand All @@ -154,14 +168,23 @@ func (s *Server) handlePublish() http.HandlerFunc {
return
}

if req.SourceIssues != "" {
if err := checkURL(req.SourceIssues); err != nil {
writeJSON(w, http.StatusUnprocessableEntity, map[string]string{"error": "invalid or unreachable issues URL: " + err.Error()})
return
}
}

pkg := store.Package{
Name: req.Name,
Version: req.Version,
Description: req.Description,
Author: req.Author,
DownloadURL: req.DownloadURL,
SHA256: req.SHA256,
Tags: req.Tags,
Name: req.Name,
Version: req.Version,
Description: req.Description,
Author: req.Author,
SourceRepository: req.SourceRepository,
SourceIssues: req.SourceIssues,
DownloadURL: req.DownloadURL,
SHA256: req.SHA256,
Tags: req.Tags,
}

if err := s.config.Store.Put(pkg); err != nil {
Expand Down Expand Up @@ -203,3 +226,18 @@ func writeJSON(w http.ResponseWriter, status int, v interface{}) {
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(v)
}

func checkURL(rawURL string) error {
if rawURL == "" {
return fmt.Errorf("empty URL")
}
u, err := http.Get(rawURL)
if err != nil {
return fmt.Errorf("unreachable: %w", err)
}
u.Body.Close()
if u.StatusCode != 200 {
return fmt.Errorf("HTTP %d", u.StatusCode)
}
return nil
}
14 changes: 14 additions & 0 deletions internal/server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"mime/multipart"
"net/http"
"net/http/httptest"
"os"
"testing"

"github.com/CognitiveOS-Project/registry-server/internal/auth"
Expand Down Expand Up @@ -223,6 +224,9 @@ func TestPublishWithAuth(t *testing.T) {
if pkg.SHA256 == "" {
t.Error("expected SHA-256 checksum to be computed")
}

// Notary mode: no file stored on disk
assertFileNotExist(t, "new-patch-0.1.0.cgp")
}

func TestPublishJSONWithDownloadURL(t *testing.T) {
Expand Down Expand Up @@ -258,6 +262,9 @@ func TestPublishJSONWithDownloadURL(t *testing.T) {
if pkg.SHA256 != "abcdef1234567890" {
t.Errorf("expected sha256, got %s", pkg.SHA256)
}

// Notary mode: no file stored on disk
assertFileNotExist(t, "json-patch-2.0.0.cgp")
}

func TestUnlock(t *testing.T) {
Expand Down Expand Up @@ -310,6 +317,13 @@ func TestUnlockMissingFields(t *testing.T) {
}
}

func assertFileNotExist(t *testing.T, name string) {
t.Helper()
if _, err := os.Stat(name); err == nil {
t.Errorf("expected file %s not to exist on disk (notary mode)", name)
}
}

func TestCORSHeaders(t *testing.T) {
srv, _ := setupTestServer(t)

Expand Down
24 changes: 13 additions & 11 deletions internal/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,19 @@ import (
)

type Package struct {
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description"`
Author string `json:"author"`
Size int64 `json:"size"`
SHA256 string `json:"sha256"`
DownloadURL string `json:"download_url,omitempty"`
Downloads int64 `json:"downloads"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
Tags []string `json:"tags"`
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description"`
Author string `json:"author"`
SourceRepository string `json:"source_repository,omitempty"`
SourceIssues string `json:"source_issues,omitempty"`
DownloadURL string `json:"download_url,omitempty"`
Size int64 `json:"size"`
SHA256 string `json:"sha256"`
Downloads int64 `json:"downloads"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
Tags []string `json:"tags"`
}

type Store interface {
Expand Down
Loading