blob: 8f75d56d890d09eb24d22b958a9f3ea140e475bf [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package pmhttp
import (
"encoding/json"
"fmt"
"log"
"net/http"
"regexp"
"strings"
tuf_data "github.com/theupdateframework/go-tuf/data"
"go.fuchsia.dev/fuchsia/src/sys/pkg/lib/repo"
)
type ConfigServerV2 struct {
rootKeyFetcher func() []byte
storageType repo.RepositoryStorageType
}
func NewConfigServerV2(rootKeyFetcher func() []byte, persist bool) *ConfigServerV2 {
cfg := &ConfigServerV2{rootKeyFetcher: rootKeyFetcher, storageType: repo.Unset}
if persist {
cfg.storageType = repo.Persistent
}
return cfg
}
func (c *ConfigServerV2) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var scheme = "http://"
if r.TLS != nil {
scheme = "https://"
}
repoUrl := fmt.Sprintf("%s%s", scheme, r.Host)
cfg, err := c.parseConfig(repoUrl)
if err != nil {
log.Printf("%s", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(cfg)
}
func (c *ConfigServerV2) parseConfig(repoUrl string) (repo.Config, error) {
cfg := repo.Config{
URL: buildRepoUrl(repoUrl),
Mirrors: []repo.MirrorConfig{
{
URL: repoUrl,
Subscribe: true,
},
},
StorageType: c.storageType,
}
root, err := func() (tuf_data.Root, error) {
var signed tuf_data.Signed
var root tuf_data.Root
if err := json.Unmarshal(c.rootKeyFetcher(), &signed); err != nil {
return root, err
}
if err := json.Unmarshal(signed.Signed, &root); err != nil {
return root, err
}
return root, nil
}()
if err != nil {
return repo.Config{}, fmt.Errorf("root.json parsing error: %w", err)
}
cfg.RootKeys, err = repo.GetRootKeys(&root)
if err != nil {
return repo.Config{}, fmt.Errorf("could not get root keys from root.json: %w", err)
}
cfg.RootVersion, err = intToUint32(root.Version)
if err != nil {
return repo.Config{}, fmt.Errorf("error parsing root version: %w", err)
}
if rootRole, ok := root.Roles["root"]; ok {
cfg.RootThreshold, err = intToUint32(rootRole.Threshold)
if err != nil {
return repo.Config{}, fmt.Errorf("error parsing root threshold: %w", err)
}
}
return cfg, nil
}
// invalidHostnameCharsPattern contains all characters not allowed by the spec in
// https://fuchsia.dev/fuchsia-src/concepts/packages/package_url
var invalidHostnameCharsPattern = regexp.MustCompile("[^a-z0-9-.]")
// buildRepoUrl ensures the repoUrl string complies with the hostname spec
// in https://fuchsia.dev/fuchsia-src/concepts/packages/package_url
func buildRepoUrl(repoUrl string) string {
ps := strings.Index(repoUrl, "://")
if ps >= 0 && len(repoUrl) > ps+3 {
repoUrl = repoUrl[ps+3:]
}
repoUrl = invalidHostnameCharsPattern.ReplaceAllString(strings.ToLower(repoUrl), "-")
return "fuchsia-pkg://" + repoUrl
}