forked from toolshed/abra
chore: vendor
This commit is contained in:
22
vendor/github.com/theupdateframework/notary/storage/errors.go
generated
vendored
Normal file
22
vendor/github.com/theupdateframework/notary/storage/errors.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrPathOutsideStore indicates that the returned path would be
|
||||
// outside the store
|
||||
ErrPathOutsideStore = errors.New("path outside file store")
|
||||
)
|
||||
|
||||
// ErrMetaNotFound indicates we did not find a particular piece
|
||||
// of metadata in the store
|
||||
type ErrMetaNotFound struct {
|
||||
Resource string
|
||||
}
|
||||
|
||||
func (err ErrMetaNotFound) Error() string {
|
||||
return fmt.Sprintf("%s trust data unavailable. Has a notary repository been initialized?", err.Resource)
|
||||
}
|
278
vendor/github.com/theupdateframework/notary/storage/filestore.go
generated
vendored
Normal file
278
vendor/github.com/theupdateframework/notary/storage/filestore.go
generated
vendored
Normal file
@ -0,0 +1,278 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary"
|
||||
)
|
||||
|
||||
// NewFileStore creates a fully configurable file store
|
||||
func NewFileStore(baseDir, fileExt string) (*FilesystemStore, error) {
|
||||
baseDir = filepath.Clean(baseDir)
|
||||
if err := createDirectory(baseDir, notary.PrivExecPerms); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !strings.HasPrefix(fileExt, ".") {
|
||||
fileExt = "." + fileExt
|
||||
}
|
||||
|
||||
return &FilesystemStore{
|
||||
baseDir: baseDir,
|
||||
ext: fileExt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewPrivateKeyFileStorage initializes a new filestore for private keys, appending
|
||||
// the notary.PrivDir to the baseDir.
|
||||
func NewPrivateKeyFileStorage(baseDir, fileExt string) (*FilesystemStore, error) {
|
||||
baseDir = filepath.Join(baseDir, notary.PrivDir)
|
||||
myStore, err := NewFileStore(baseDir, fileExt)
|
||||
myStore.migrateTo0Dot4()
|
||||
return myStore, err
|
||||
}
|
||||
|
||||
// NewPrivateSimpleFileStore is a wrapper to create an owner readable/writeable
|
||||
// _only_ filestore
|
||||
func NewPrivateSimpleFileStore(baseDir, fileExt string) (*FilesystemStore, error) {
|
||||
return NewFileStore(baseDir, fileExt)
|
||||
}
|
||||
|
||||
// FilesystemStore is a store in a locally accessible directory
|
||||
type FilesystemStore struct {
|
||||
baseDir string
|
||||
ext string
|
||||
}
|
||||
|
||||
func (f *FilesystemStore) moveKeyTo0Dot4Location(file string) {
|
||||
keyID := filepath.Base(file)
|
||||
fileDir := filepath.Dir(file)
|
||||
d, _ := f.Get(file)
|
||||
block, _ := pem.Decode(d)
|
||||
if block == nil {
|
||||
logrus.Warn("Key data for", file, "could not be decoded as a valid PEM block. The key will not been migrated and may not be available")
|
||||
return
|
||||
}
|
||||
fileDir = strings.TrimPrefix(fileDir, notary.RootKeysSubdir)
|
||||
fileDir = strings.TrimPrefix(fileDir, notary.NonRootKeysSubdir)
|
||||
if fileDir != "" {
|
||||
block.Headers["gun"] = filepath.ToSlash(fileDir[1:])
|
||||
}
|
||||
if strings.Contains(keyID, "_") {
|
||||
role := strings.Split(keyID, "_")[1]
|
||||
keyID = strings.TrimSuffix(keyID, "_"+role)
|
||||
block.Headers["role"] = role
|
||||
}
|
||||
var keyPEM bytes.Buffer
|
||||
// since block came from decoding the PEM bytes in the first place, and all we're doing is adding some headers we ignore the possibility of an error while encoding the block
|
||||
pem.Encode(&keyPEM, block)
|
||||
f.Set(keyID, keyPEM.Bytes())
|
||||
}
|
||||
|
||||
func (f *FilesystemStore) migrateTo0Dot4() {
|
||||
rootKeysSubDir := filepath.Clean(filepath.Join(f.Location(), notary.RootKeysSubdir))
|
||||
nonRootKeysSubDir := filepath.Clean(filepath.Join(f.Location(), notary.NonRootKeysSubdir))
|
||||
if _, err := os.Stat(rootKeysSubDir); !os.IsNotExist(err) && f.Location() != rootKeysSubDir {
|
||||
if rootKeysSubDir == "" || rootKeysSubDir == "/" {
|
||||
// making sure we don't remove a user's homedir
|
||||
logrus.Warn("The directory for root keys is an unsafe value, we are not going to delete the directory. Please delete it manually")
|
||||
} else {
|
||||
// root_keys exists, migrate things from it
|
||||
listOnlyRootKeysDirStore, _ := NewFileStore(rootKeysSubDir, f.ext)
|
||||
for _, file := range listOnlyRootKeysDirStore.ListFiles() {
|
||||
f.moveKeyTo0Dot4Location(filepath.Join(notary.RootKeysSubdir, file))
|
||||
}
|
||||
// delete the old directory
|
||||
os.RemoveAll(rootKeysSubDir)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(nonRootKeysSubDir); !os.IsNotExist(err) && f.Location() != nonRootKeysSubDir {
|
||||
if nonRootKeysSubDir == "" || nonRootKeysSubDir == "/" {
|
||||
// making sure we don't remove a user's homedir
|
||||
logrus.Warn("The directory for non root keys is an unsafe value, we are not going to delete the directory. Please delete it manually")
|
||||
} else {
|
||||
// tuf_keys exists, migrate things from it
|
||||
listOnlyNonRootKeysDirStore, _ := NewFileStore(nonRootKeysSubDir, f.ext)
|
||||
for _, file := range listOnlyNonRootKeysDirStore.ListFiles() {
|
||||
f.moveKeyTo0Dot4Location(filepath.Join(notary.NonRootKeysSubdir, file))
|
||||
}
|
||||
// delete the old directory
|
||||
os.RemoveAll(nonRootKeysSubDir)
|
||||
}
|
||||
}
|
||||
|
||||
// if we have a trusted_certificates folder, let's delete for a complete migration since it is unused by new clients
|
||||
certsSubDir := filepath.Join(f.Location(), "trusted_certificates")
|
||||
if certsSubDir == "" || certsSubDir == "/" {
|
||||
logrus.Warn("The directory for trusted certificate is an unsafe value, we are not going to delete the directory. Please delete it manually")
|
||||
} else {
|
||||
os.RemoveAll(certsSubDir)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FilesystemStore) getPath(name string) (string, error) {
|
||||
fileName := fmt.Sprintf("%s%s", name, f.ext)
|
||||
fullPath := filepath.Join(f.baseDir, fileName)
|
||||
|
||||
if !strings.HasPrefix(fullPath, f.baseDir) {
|
||||
return "", ErrPathOutsideStore
|
||||
}
|
||||
return fullPath, nil
|
||||
}
|
||||
|
||||
// GetSized returns the meta for the given name (a role) up to size bytes
|
||||
// If size is "NoSizeLimit", this corresponds to "infinite," but we cut off at a
|
||||
// predefined threshold "notary.MaxDownloadSize". If the file is larger than size
|
||||
// we return ErrMaliciousServer for consistency with the HTTPStore
|
||||
func (f *FilesystemStore) GetSized(name string, size int64) ([]byte, error) {
|
||||
p, err := f.getPath(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
file, err := os.Open(p)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = ErrMetaNotFound{Resource: name}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
if size == NoSizeLimit {
|
||||
size = notary.MaxDownloadSize
|
||||
}
|
||||
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if stat.Size() > size {
|
||||
return nil, ErrMaliciousServer{}
|
||||
}
|
||||
|
||||
l := io.LimitReader(file, size)
|
||||
return ioutil.ReadAll(l)
|
||||
}
|
||||
|
||||
// Get returns the meta for the given name.
|
||||
func (f *FilesystemStore) Get(name string) ([]byte, error) {
|
||||
p, err := f.getPath(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
meta, err := ioutil.ReadFile(p)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = ErrMetaNotFound{Resource: name}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return meta, nil
|
||||
}
|
||||
|
||||
// SetMulti sets the metadata for multiple roles in one operation
|
||||
func (f *FilesystemStore) SetMulti(metas map[string][]byte) error {
|
||||
for role, blob := range metas {
|
||||
err := f.Set(role, blob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set sets the meta for a single role
|
||||
func (f *FilesystemStore) Set(name string, meta []byte) error {
|
||||
fp, err := f.getPath(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensures the parent directories of the file we are about to write exist
|
||||
err = os.MkdirAll(filepath.Dir(fp), notary.PrivExecPerms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if something already exists, just delete it and re-write it
|
||||
os.RemoveAll(fp)
|
||||
|
||||
// Write the file to disk
|
||||
return ioutil.WriteFile(fp, meta, notary.PrivNoExecPerms)
|
||||
}
|
||||
|
||||
// RemoveAll clears the existing filestore by removing its base directory
|
||||
func (f *FilesystemStore) RemoveAll() error {
|
||||
return os.RemoveAll(f.baseDir)
|
||||
}
|
||||
|
||||
// Remove removes the metadata for a single role - if the metadata doesn't
|
||||
// exist, no error is returned
|
||||
func (f *FilesystemStore) Remove(name string) error {
|
||||
p, err := f.getPath(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.RemoveAll(p) // RemoveAll succeeds if path doesn't exist
|
||||
}
|
||||
|
||||
// Location returns a human readable name for the storage location
|
||||
func (f FilesystemStore) Location() string {
|
||||
return f.baseDir
|
||||
}
|
||||
|
||||
// ListFiles returns a list of all the filenames that can be used with Get*
|
||||
// to retrieve content from this filestore
|
||||
func (f FilesystemStore) ListFiles() []string {
|
||||
files := make([]string, 0, 0)
|
||||
filepath.Walk(f.baseDir, func(fp string, fi os.FileInfo, err error) error {
|
||||
// If there are errors, ignore this particular file
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
// Ignore if it is a directory
|
||||
if fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If this is a symlink, ignore it
|
||||
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Only allow matches that end with our certificate extension (e.g. *.crt)
|
||||
matched, _ := filepath.Match("*"+f.ext, fi.Name())
|
||||
|
||||
if matched {
|
||||
// Find the relative path for this file relative to the base path.
|
||||
fp, err = filepath.Rel(f.baseDir, fp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trimmed := strings.TrimSuffix(fp, f.ext)
|
||||
files = append(files, trimmed)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return files
|
||||
}
|
||||
|
||||
// createDirectory receives a string of the path to a directory.
|
||||
// It does not support passing files, so the caller has to remove
|
||||
// the filename by doing filepath.Dir(full_path_to_file)
|
||||
func createDirectory(dir string, perms os.FileMode) error {
|
||||
// This prevents someone passing /path/to/dir and 'dir' not being created
|
||||
// If two '//' exist, MkdirAll deals it with correctly
|
||||
dir = dir + "/"
|
||||
return os.MkdirAll(dir, perms)
|
||||
}
|
379
vendor/github.com/theupdateframework/notary/storage/httpstore.go
generated
vendored
Normal file
379
vendor/github.com/theupdateframework/notary/storage/httpstore.go
generated
vendored
Normal file
@ -0,0 +1,379 @@
|
||||
// A Store that can fetch and set metadata on a remote server.
|
||||
// Some API constraints:
|
||||
// - Response bodies for error codes should be unmarshallable as:
|
||||
// {"errors": [{..., "detail": <serialized validation error>}]}
|
||||
// else validation error details, etc. will be unparsable. The errors
|
||||
// should have a github.com/theupdateframework/notary/tuf/validation/SerializableError
|
||||
// in the Details field.
|
||||
// If writing your own server, please have a look at
|
||||
// github.com/docker/distribution/registry/api/errcode
|
||||
|
||||
package storage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/theupdateframework/notary"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/theupdateframework/notary/tuf/validation"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxErrorResponseSize is the maximum size for an error message - 1KiB
|
||||
MaxErrorResponseSize int64 = 1 << 10
|
||||
// MaxKeySize is the maximum size for a stored TUF key - 256KiB
|
||||
MaxKeySize = 256 << 10
|
||||
)
|
||||
|
||||
// ErrServerUnavailable indicates an error from the server. code allows us to
|
||||
// populate the http error we received
|
||||
type ErrServerUnavailable struct {
|
||||
code int
|
||||
}
|
||||
|
||||
// NetworkError represents any kind of network error when attempting to make a request
|
||||
type NetworkError struct {
|
||||
Wrapped error
|
||||
}
|
||||
|
||||
func (n NetworkError) Error() string {
|
||||
if _, ok := n.Wrapped.(*url.Error); ok {
|
||||
// QueryUnescape does the inverse transformation of QueryEscape,
|
||||
// converting %AB into the byte 0xAB and '+' into ' ' (space).
|
||||
// It returns an error if any % is not followed by two hexadecimal digits.
|
||||
//
|
||||
// If this happens, we log out the QueryUnescape error and return the
|
||||
// original error to client.
|
||||
res, err := url.QueryUnescape(n.Wrapped.Error())
|
||||
if err != nil {
|
||||
logrus.Errorf("unescape network error message failed: %s", err)
|
||||
return n.Wrapped.Error()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
return n.Wrapped.Error()
|
||||
}
|
||||
|
||||
func (err ErrServerUnavailable) Error() string {
|
||||
if err.code == 401 {
|
||||
return fmt.Sprintf("you are not authorized to perform this operation: server returned 401.")
|
||||
}
|
||||
return fmt.Sprintf("unable to reach trust server at this time: %d.", err.code)
|
||||
}
|
||||
|
||||
// ErrMaliciousServer indicates the server returned a response that is highly suspected
|
||||
// of being malicious. i.e. it attempted to send us more data than the known size of a
|
||||
// particular role metadata.
|
||||
type ErrMaliciousServer struct{}
|
||||
|
||||
func (err ErrMaliciousServer) Error() string {
|
||||
return "trust server returned a bad response."
|
||||
}
|
||||
|
||||
// ErrInvalidOperation indicates that the server returned a 400 response and
|
||||
// propagate any body we received.
|
||||
type ErrInvalidOperation struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (err ErrInvalidOperation) Error() string {
|
||||
if err.msg != "" {
|
||||
return fmt.Sprintf("trust server rejected operation: %s", err.msg)
|
||||
}
|
||||
return "trust server rejected operation."
|
||||
}
|
||||
|
||||
// HTTPStore manages pulling and pushing metadata from and to a remote
|
||||
// service over HTTP. It assumes the URL structure of the remote service
|
||||
// maps identically to the structure of the TUF repo:
|
||||
// <baseURL>/<metaPrefix>/(root|targets|snapshot|timestamp).json
|
||||
// <baseURL>/<targetsPrefix>/foo.sh
|
||||
//
|
||||
// If consistent snapshots are disabled, it is advised that caching is not
|
||||
// enabled. Simple set a cachePath (and ensure it's writeable) to enable
|
||||
// caching.
|
||||
type HTTPStore struct {
|
||||
baseURL url.URL
|
||||
metaPrefix string
|
||||
metaExtension string
|
||||
keyExtension string
|
||||
roundTrip http.RoundTripper
|
||||
}
|
||||
|
||||
// NewNotaryServerStore returns a new HTTPStore against a URL which should represent a notary
|
||||
// server
|
||||
func NewNotaryServerStore(serverURL string, gun data.GUN, roundTrip http.RoundTripper) (RemoteStore, error) {
|
||||
return NewHTTPStore(
|
||||
serverURL+"/v2/"+gun.String()+"/_trust/tuf/",
|
||||
"",
|
||||
"json",
|
||||
"key",
|
||||
roundTrip,
|
||||
)
|
||||
}
|
||||
|
||||
// NewHTTPStore initializes a new store against a URL and a number of configuration options.
|
||||
//
|
||||
// In case of a nil `roundTrip`, a default offline store is used instead.
|
||||
func NewHTTPStore(baseURL, metaPrefix, metaExtension, keyExtension string, roundTrip http.RoundTripper) (RemoteStore, error) {
|
||||
base, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !base.IsAbs() {
|
||||
return nil, errors.New("HTTPStore requires an absolute baseURL")
|
||||
}
|
||||
if roundTrip == nil {
|
||||
return &OfflineStore{}, nil
|
||||
}
|
||||
return &HTTPStore{
|
||||
baseURL: *base,
|
||||
metaPrefix: metaPrefix,
|
||||
metaExtension: metaExtension,
|
||||
keyExtension: keyExtension,
|
||||
roundTrip: roundTrip,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func tryUnmarshalError(resp *http.Response, defaultError error) error {
|
||||
b := io.LimitReader(resp.Body, MaxErrorResponseSize)
|
||||
bodyBytes, err := ioutil.ReadAll(b)
|
||||
if err != nil {
|
||||
return defaultError
|
||||
}
|
||||
var parsedErrors struct {
|
||||
Errors []struct {
|
||||
Detail validation.SerializableError `json:"detail"`
|
||||
} `json:"errors"`
|
||||
}
|
||||
if err := json.Unmarshal(bodyBytes, &parsedErrors); err != nil {
|
||||
return defaultError
|
||||
}
|
||||
if len(parsedErrors.Errors) != 1 {
|
||||
return defaultError
|
||||
}
|
||||
err = parsedErrors.Errors[0].Detail.Error
|
||||
if err == nil {
|
||||
return defaultError
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func translateStatusToError(resp *http.Response, resource string) error {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return nil
|
||||
case http.StatusNotFound:
|
||||
return ErrMetaNotFound{Resource: resource}
|
||||
case http.StatusBadRequest:
|
||||
return tryUnmarshalError(resp, ErrInvalidOperation{})
|
||||
default:
|
||||
return ErrServerUnavailable{code: resp.StatusCode}
|
||||
}
|
||||
}
|
||||
|
||||
// GetSized downloads the named meta file with the given size. A short body
|
||||
// is acceptable because in the case of timestamp.json, the size is a cap,
|
||||
// not an exact length.
|
||||
// If size is "NoSizeLimit", this corresponds to "infinite," but we cut off at a
|
||||
// predefined threshold "notary.MaxDownloadSize".
|
||||
func (s HTTPStore) GetSized(name string, size int64) ([]byte, error) {
|
||||
url, err := s.buildMetaURL(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequest("GET", url.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := s.roundTrip.RoundTrip(req)
|
||||
if err != nil {
|
||||
return nil, NetworkError{Wrapped: err}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if err := translateStatusToError(resp, name); err != nil {
|
||||
logrus.Debugf("received HTTP status %d when requesting %s.", resp.StatusCode, name)
|
||||
return nil, err
|
||||
}
|
||||
if size == NoSizeLimit {
|
||||
size = notary.MaxDownloadSize
|
||||
}
|
||||
if resp.ContentLength > size {
|
||||
return nil, ErrMaliciousServer{}
|
||||
}
|
||||
logrus.Debugf("%d when retrieving metadata for %s", resp.StatusCode, name)
|
||||
b := io.LimitReader(resp.Body, size)
|
||||
body, err := ioutil.ReadAll(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// Set sends a single piece of metadata to the TUF server
|
||||
func (s HTTPStore) Set(name string, blob []byte) error {
|
||||
return s.SetMulti(map[string][]byte{name: blob})
|
||||
}
|
||||
|
||||
// Remove always fails, because we should never be able to delete metadata
|
||||
// remotely
|
||||
func (s HTTPStore) Remove(name string) error {
|
||||
return ErrInvalidOperation{msg: "cannot delete individual metadata files"}
|
||||
}
|
||||
|
||||
// NewMultiPartMetaRequest builds a request with the provided metadata updates
|
||||
// in multipart form
|
||||
func NewMultiPartMetaRequest(url string, metas map[string][]byte) (*http.Request, error) {
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
for role, blob := range metas {
|
||||
part, err := writer.CreateFormFile("files", role)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = io.Copy(part, bytes.NewBuffer(blob))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
err := writer.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequest("POST", url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// SetMulti does a single batch upload of multiple pieces of TUF metadata.
|
||||
// This should be preferred for updating a remote server as it enable the server
|
||||
// to remain consistent, either accepting or rejecting the complete update.
|
||||
func (s HTTPStore) SetMulti(metas map[string][]byte) error {
|
||||
url, err := s.buildMetaURL("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := NewMultiPartMetaRequest(url.String(), metas)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := s.roundTrip.RoundTrip(req)
|
||||
if err != nil {
|
||||
return NetworkError{Wrapped: err}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
// if this 404's something is pretty wrong
|
||||
return translateStatusToError(resp, "POST metadata endpoint")
|
||||
}
|
||||
|
||||
// RemoveAll will attempt to delete all TUF metadata for a GUN
|
||||
func (s HTTPStore) RemoveAll() error {
|
||||
url, err := s.buildMetaURL("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := http.NewRequest("DELETE", url.String(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := s.roundTrip.RoundTrip(req)
|
||||
if err != nil {
|
||||
return NetworkError{Wrapped: err}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return translateStatusToError(resp, "DELETE metadata for GUN endpoint")
|
||||
}
|
||||
|
||||
func (s HTTPStore) buildMetaURL(name string) (*url.URL, error) {
|
||||
var filename string
|
||||
if name != "" {
|
||||
filename = fmt.Sprintf("%s.%s", name, s.metaExtension)
|
||||
}
|
||||
uri := path.Join(s.metaPrefix, filename)
|
||||
return s.buildURL(uri)
|
||||
}
|
||||
|
||||
func (s HTTPStore) buildKeyURL(name data.RoleName) (*url.URL, error) {
|
||||
filename := fmt.Sprintf("%s.%s", name.String(), s.keyExtension)
|
||||
uri := path.Join(s.metaPrefix, filename)
|
||||
return s.buildURL(uri)
|
||||
}
|
||||
|
||||
func (s HTTPStore) buildURL(uri string) (*url.URL, error) {
|
||||
sub, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.baseURL.ResolveReference(sub), nil
|
||||
}
|
||||
|
||||
// GetKey retrieves a public key from the remote server
|
||||
func (s HTTPStore) GetKey(role data.RoleName) ([]byte, error) {
|
||||
url, err := s.buildKeyURL(role)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequest("GET", url.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := s.roundTrip.RoundTrip(req)
|
||||
if err != nil {
|
||||
return nil, NetworkError{Wrapped: err}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if err := translateStatusToError(resp, role.String()+" key"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := io.LimitReader(resp.Body, MaxKeySize)
|
||||
body, err := ioutil.ReadAll(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// RotateKey rotates a private key and returns the public component from the remote server
|
||||
func (s HTTPStore) RotateKey(role data.RoleName) ([]byte, error) {
|
||||
url, err := s.buildKeyURL(role)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequest("POST", url.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := s.roundTrip.RoundTrip(req)
|
||||
if err != nil {
|
||||
return nil, NetworkError{Wrapped: err}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if err := translateStatusToError(resp, role.String()+" key"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := io.LimitReader(resp.Body, MaxKeySize)
|
||||
body, err := ioutil.ReadAll(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// Location returns a human readable name for the storage location
|
||||
func (s HTTPStore) Location() string {
|
||||
return s.baseURL.Host
|
||||
}
|
39
vendor/github.com/theupdateframework/notary/storage/interfaces.go
generated
vendored
Normal file
39
vendor/github.com/theupdateframework/notary/storage/interfaces.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
// NoSizeLimit is represented as -1 for arguments to GetMeta
|
||||
const NoSizeLimit int64 = -1
|
||||
|
||||
// MetadataStore must be implemented by anything that intends to interact
|
||||
// with a store of TUF files
|
||||
type MetadataStore interface {
|
||||
GetSized(name string, size int64) ([]byte, error)
|
||||
Set(name string, blob []byte) error
|
||||
SetMulti(map[string][]byte) error
|
||||
RemoveAll() error
|
||||
Remove(name string) error
|
||||
Location() string
|
||||
}
|
||||
|
||||
// PublicKeyStore must be implemented by a key service
|
||||
type PublicKeyStore interface {
|
||||
GetKey(role data.RoleName) ([]byte, error)
|
||||
RotateKey(role data.RoleName) ([]byte, error)
|
||||
}
|
||||
|
||||
// RemoteStore is similar to LocalStore with the added expectation that it should
|
||||
// provide a way to download targets once located
|
||||
type RemoteStore interface {
|
||||
MetadataStore
|
||||
PublicKeyStore
|
||||
}
|
||||
|
||||
// Bootstrapper is a thing that can set itself up
|
||||
type Bootstrapper interface {
|
||||
// Bootstrap instructs a configured Bootstrapper to perform
|
||||
// its setup operations.
|
||||
Bootstrap() error
|
||||
}
|
137
vendor/github.com/theupdateframework/notary/storage/memorystore.go
generated
vendored
Normal file
137
vendor/github.com/theupdateframework/notary/storage/memorystore.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/theupdateframework/notary"
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
"github.com/theupdateframework/notary/tuf/utils"
|
||||
)
|
||||
|
||||
// NewMemoryStore returns a MetadataStore that operates entirely in memory.
|
||||
// Very useful for testing
|
||||
func NewMemoryStore(seed map[data.RoleName][]byte) *MemoryStore {
|
||||
var (
|
||||
consistent = make(map[string][]byte)
|
||||
initial = make(map[string][]byte)
|
||||
)
|
||||
// add all seed meta to consistent
|
||||
for name, d := range seed {
|
||||
checksum := sha256.Sum256(d)
|
||||
path := utils.ConsistentName(name.String(), checksum[:])
|
||||
initial[name.String()] = d
|
||||
consistent[path] = d
|
||||
}
|
||||
|
||||
return &MemoryStore{
|
||||
data: initial,
|
||||
consistent: consistent,
|
||||
}
|
||||
}
|
||||
|
||||
// MemoryStore implements a mock RemoteStore entirely in memory.
|
||||
// For testing purposes only.
|
||||
type MemoryStore struct {
|
||||
data map[string][]byte
|
||||
consistent map[string][]byte
|
||||
}
|
||||
|
||||
// GetSized returns up to size bytes of data references by name.
|
||||
// If size is "NoSizeLimit", this corresponds to "infinite," but we cut off at a
|
||||
// predefined threshold "notary.MaxDownloadSize", as we will always know the
|
||||
// size for everything but a timestamp and sometimes a root,
|
||||
// neither of which should be exceptionally large
|
||||
func (m MemoryStore) GetSized(name string, size int64) ([]byte, error) {
|
||||
d, ok := m.data[name]
|
||||
if ok {
|
||||
if size == NoSizeLimit {
|
||||
size = notary.MaxDownloadSize
|
||||
}
|
||||
if int64(len(d)) < size {
|
||||
return d, nil
|
||||
}
|
||||
return d[:size], nil
|
||||
}
|
||||
d, ok = m.consistent[name]
|
||||
if ok {
|
||||
if int64(len(d)) < size {
|
||||
return d, nil
|
||||
}
|
||||
return d[:size], nil
|
||||
}
|
||||
return nil, ErrMetaNotFound{Resource: name}
|
||||
}
|
||||
|
||||
// Get returns the data associated with name
|
||||
func (m MemoryStore) Get(name string) ([]byte, error) {
|
||||
if d, ok := m.data[name]; ok {
|
||||
return d, nil
|
||||
}
|
||||
if d, ok := m.consistent[name]; ok {
|
||||
return d, nil
|
||||
}
|
||||
return nil, ErrMetaNotFound{Resource: name}
|
||||
}
|
||||
|
||||
// Set sets the metadata value for the given name
|
||||
func (m *MemoryStore) Set(name string, meta []byte) error {
|
||||
m.data[name] = meta
|
||||
|
||||
parsedMeta := &data.SignedMeta{}
|
||||
err := json.Unmarshal(meta, parsedMeta)
|
||||
if err == nil {
|
||||
// no parse error means this is metadata and not a key, so store by version
|
||||
version := parsedMeta.Signed.Version
|
||||
versionedName := fmt.Sprintf("%d.%s", version, name)
|
||||
m.data[versionedName] = meta
|
||||
}
|
||||
|
||||
checksum := sha256.Sum256(meta)
|
||||
path := utils.ConsistentName(name, checksum[:])
|
||||
m.consistent[path] = meta
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetMulti sets multiple pieces of metadata for multiple names
|
||||
// in a single operation.
|
||||
func (m *MemoryStore) SetMulti(metas map[string][]byte) error {
|
||||
for role, blob := range metas {
|
||||
m.Set(role, blob)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes the metadata for a single role - if the metadata doesn't
|
||||
// exist, no error is returned
|
||||
func (m *MemoryStore) Remove(name string) error {
|
||||
if meta, ok := m.data[name]; ok {
|
||||
checksum := sha256.Sum256(meta)
|
||||
path := utils.ConsistentName(name, checksum[:])
|
||||
delete(m.data, name)
|
||||
delete(m.consistent, path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveAll clears the existing memory store by setting this store as new empty one
|
||||
func (m *MemoryStore) RemoveAll() error {
|
||||
*m = *NewMemoryStore(nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Location provides a human readable name for the storage location
|
||||
func (m MemoryStore) Location() string {
|
||||
return "memory"
|
||||
}
|
||||
|
||||
// ListFiles returns a list of all files. The names returned should be
|
||||
// usable with Get directly, with no modification.
|
||||
func (m *MemoryStore) ListFiles() []string {
|
||||
names := make([]string, 0, len(m.data))
|
||||
for n := range m.data {
|
||||
names = append(names, n)
|
||||
}
|
||||
return names
|
||||
}
|
58
vendor/github.com/theupdateframework/notary/storage/offlinestore.go
generated
vendored
Normal file
58
vendor/github.com/theupdateframework/notary/storage/offlinestore.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"github.com/theupdateframework/notary/tuf/data"
|
||||
)
|
||||
|
||||
// ErrOffline is used to indicate we are operating offline
|
||||
type ErrOffline struct{}
|
||||
|
||||
func (e ErrOffline) Error() string {
|
||||
return "client is offline"
|
||||
}
|
||||
|
||||
var err = ErrOffline{}
|
||||
|
||||
// OfflineStore is to be used as a placeholder for a nil store. It simply
|
||||
// returns ErrOffline for every operation
|
||||
type OfflineStore struct{}
|
||||
|
||||
// GetSized returns ErrOffline
|
||||
func (es OfflineStore) GetSized(name string, size int64) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set returns ErrOffline
|
||||
func (es OfflineStore) Set(name string, blob []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// SetMulti returns ErrOffline
|
||||
func (es OfflineStore) SetMulti(map[string][]byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove returns ErrOffline
|
||||
func (es OfflineStore) Remove(name string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// GetKey returns ErrOffline
|
||||
func (es OfflineStore) GetKey(role data.RoleName) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// RotateKey returns ErrOffline
|
||||
func (es OfflineStore) RotateKey(role data.RoleName) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// RemoveAll return ErrOffline
|
||||
func (es OfflineStore) RemoveAll() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Location returns a human readable name for the storage location
|
||||
func (es OfflineStore) Location() string {
|
||||
return "offline"
|
||||
}
|
Reference in New Issue
Block a user