|
@@ -19,6 +19,9 @@ package api
|
|
|
import (
|
|
import (
|
|
|
"archive/tar"
|
|
"archive/tar"
|
|
|
"context"
|
|
"context"
|
|
|
|
|
+ "crypto/ecdsa"
|
|
|
|
|
+ "encoding/hex"
|
|
|
|
|
+ "errors"
|
|
|
"fmt"
|
|
"fmt"
|
|
|
"io"
|
|
"io"
|
|
|
"math/big"
|
|
"math/big"
|
|
@@ -43,6 +46,10 @@ import (
|
|
|
opentracing "github.com/opentracing/opentracing-go"
|
|
opentracing "github.com/opentracing/opentracing-go"
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
+var (
|
|
|
|
|
+ ErrNotFound = errors.New("not found")
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
var (
|
|
var (
|
|
|
apiResolveCount = metrics.NewRegisteredCounter("api.resolve.count", nil)
|
|
apiResolveCount = metrics.NewRegisteredCounter("api.resolve.count", nil)
|
|
|
apiResolveFail = metrics.NewRegisteredCounter("api.resolve.fail", nil)
|
|
apiResolveFail = metrics.NewRegisteredCounter("api.resolve.fail", nil)
|
|
@@ -227,14 +234,18 @@ type API struct {
|
|
|
resource *mru.Handler
|
|
resource *mru.Handler
|
|
|
fileStore *storage.FileStore
|
|
fileStore *storage.FileStore
|
|
|
dns Resolver
|
|
dns Resolver
|
|
|
|
|
+ Decryptor func(context.Context, string) DecryptFunc
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// NewAPI the api constructor initialises a new API instance.
|
|
// NewAPI the api constructor initialises a new API instance.
|
|
|
-func NewAPI(fileStore *storage.FileStore, dns Resolver, resourceHandler *mru.Handler) (self *API) {
|
|
|
|
|
|
|
+func NewAPI(fileStore *storage.FileStore, dns Resolver, resourceHandler *mru.Handler, pk *ecdsa.PrivateKey) (self *API) {
|
|
|
self = &API{
|
|
self = &API{
|
|
|
fileStore: fileStore,
|
|
fileStore: fileStore,
|
|
|
dns: dns,
|
|
dns: dns,
|
|
|
resource: resourceHandler,
|
|
resource: resourceHandler,
|
|
|
|
|
+ Decryptor: func(ctx context.Context, credentials string) DecryptFunc {
|
|
|
|
|
+ return self.doDecrypt(ctx, credentials, pk)
|
|
|
|
|
+ },
|
|
|
}
|
|
}
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
@@ -260,8 +271,30 @@ func (a *API) Store(ctx context.Context, data io.Reader, size int64, toEncrypt b
|
|
|
// ErrResolve is returned when an URI cannot be resolved from ENS.
|
|
// ErrResolve is returned when an URI cannot be resolved from ENS.
|
|
|
type ErrResolve error
|
|
type ErrResolve error
|
|
|
|
|
|
|
|
|
|
+// Resolve a name into a content-addressed hash
|
|
|
|
|
+// where address could be an ENS name, or a content addressed hash
|
|
|
|
|
+func (a *API) Resolve(ctx context.Context, address string) (storage.Address, error) {
|
|
|
|
|
+ // if DNS is not configured, return an error
|
|
|
|
|
+ if a.dns == nil {
|
|
|
|
|
+ if hashMatcher.MatchString(address) {
|
|
|
|
|
+ return common.Hex2Bytes(address), nil
|
|
|
|
|
+ }
|
|
|
|
|
+ apiResolveFail.Inc(1)
|
|
|
|
|
+ return nil, fmt.Errorf("no DNS to resolve name: %q", address)
|
|
|
|
|
+ }
|
|
|
|
|
+ // try and resolve the address
|
|
|
|
|
+ resolved, err := a.dns.Resolve(address)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ if hashMatcher.MatchString(address) {
|
|
|
|
|
+ return common.Hex2Bytes(address), nil
|
|
|
|
|
+ }
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+ return resolved[:], nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// Resolve resolves a URI to an Address using the MultiResolver.
|
|
// Resolve resolves a URI to an Address using the MultiResolver.
|
|
|
-func (a *API) Resolve(ctx context.Context, uri *URI) (storage.Address, error) {
|
|
|
|
|
|
|
+func (a *API) ResolveURI(ctx context.Context, uri *URI, credentials string) (storage.Address, error) {
|
|
|
apiResolveCount.Inc(1)
|
|
apiResolveCount.Inc(1)
|
|
|
log.Trace("resolving", "uri", uri.Addr)
|
|
log.Trace("resolving", "uri", uri.Addr)
|
|
|
|
|
|
|
@@ -280,28 +313,44 @@ func (a *API) Resolve(ctx context.Context, uri *URI) (storage.Address, error) {
|
|
|
return key, nil
|
|
return key, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // if DNS is not configured, check if the address is a hash
|
|
|
|
|
- if a.dns == nil {
|
|
|
|
|
- key := uri.Address()
|
|
|
|
|
- if key == nil {
|
|
|
|
|
- apiResolveFail.Inc(1)
|
|
|
|
|
- return nil, fmt.Errorf("no DNS to resolve name: %q", uri.Addr)
|
|
|
|
|
- }
|
|
|
|
|
- return key, nil
|
|
|
|
|
|
|
+ addr, err := a.Resolve(ctx, uri.Addr)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // try and resolve the address
|
|
|
|
|
- resolved, err := a.dns.Resolve(uri.Addr)
|
|
|
|
|
- if err == nil {
|
|
|
|
|
- return resolved[:], nil
|
|
|
|
|
|
|
+ if uri.Path == "" {
|
|
|
|
|
+ return addr, nil
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- key := uri.Address()
|
|
|
|
|
- if key == nil {
|
|
|
|
|
- apiResolveFail.Inc(1)
|
|
|
|
|
|
|
+ walker, err := a.NewManifestWalker(ctx, addr, a.Decryptor(ctx, credentials), nil)
|
|
|
|
|
+ if err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
- return key, nil
|
|
|
|
|
|
|
+ var entry *ManifestEntry
|
|
|
|
|
+ walker.Walk(func(e *ManifestEntry) error {
|
|
|
|
|
+ // if the entry matches the path, set entry and stop
|
|
|
|
|
+ // the walk
|
|
|
|
|
+ if e.Path == uri.Path {
|
|
|
|
|
+ entry = e
|
|
|
|
|
+ // return an error to cancel the walk
|
|
|
|
|
+ return errors.New("found")
|
|
|
|
|
+ }
|
|
|
|
|
+ // ignore non-manifest files
|
|
|
|
|
+ if e.ContentType != ManifestType {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ // if the manifest's path is a prefix of the
|
|
|
|
|
+ // requested path, recurse into it by returning
|
|
|
|
|
+ // nil and continuing the walk
|
|
|
|
|
+ if strings.HasPrefix(uri.Path, e.Path) {
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ return ErrSkipManifest
|
|
|
|
|
+ })
|
|
|
|
|
+ if entry == nil {
|
|
|
|
|
+ return nil, errors.New("not found")
|
|
|
|
|
+ }
|
|
|
|
|
+ addr = storage.Address(common.Hex2Bytes(entry.Hash))
|
|
|
|
|
+ return addr, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Put provides singleton manifest creation on top of FileStore store
|
|
// Put provides singleton manifest creation on top of FileStore store
|
|
@@ -332,10 +381,10 @@ func (a *API) Put(ctx context.Context, content string, contentType string, toEnc
|
|
|
// Get uses iterative manifest retrieval and prefix matching
|
|
// Get uses iterative manifest retrieval and prefix matching
|
|
|
// to resolve basePath to content using FileStore retrieve
|
|
// to resolve basePath to content using FileStore retrieve
|
|
|
// it returns a section reader, mimeType, status, the key of the actual content and an error
|
|
// it returns a section reader, mimeType, status, the key of the actual content and an error
|
|
|
-func (a *API) Get(ctx context.Context, manifestAddr storage.Address, path string) (reader storage.LazySectionReader, mimeType string, status int, contentAddr storage.Address, err error) {
|
|
|
|
|
|
|
+func (a *API) Get(ctx context.Context, decrypt DecryptFunc, manifestAddr storage.Address, path string) (reader storage.LazySectionReader, mimeType string, status int, contentAddr storage.Address, err error) {
|
|
|
log.Debug("api.get", "key", manifestAddr, "path", path)
|
|
log.Debug("api.get", "key", manifestAddr, "path", path)
|
|
|
apiGetCount.Inc(1)
|
|
apiGetCount.Inc(1)
|
|
|
- trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil)
|
|
|
|
|
|
|
+ trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil, decrypt)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
apiGetNotFound.Inc(1)
|
|
apiGetNotFound.Inc(1)
|
|
|
status = http.StatusNotFound
|
|
status = http.StatusNotFound
|
|
@@ -347,6 +396,16 @@ func (a *API) Get(ctx context.Context, manifestAddr storage.Address, path string
|
|
|
|
|
|
|
|
if entry != nil {
|
|
if entry != nil {
|
|
|
log.Debug("trie got entry", "key", manifestAddr, "path", path, "entry.Hash", entry.Hash)
|
|
log.Debug("trie got entry", "key", manifestAddr, "path", path, "entry.Hash", entry.Hash)
|
|
|
|
|
+
|
|
|
|
|
+ if entry.ContentType == ManifestType {
|
|
|
|
|
+ log.Debug("entry is manifest", "key", manifestAddr, "new key", entry.Hash)
|
|
|
|
|
+ adr, err := hex.DecodeString(entry.Hash)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, "", 0, nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+ return a.Get(ctx, decrypt, adr, entry.Path)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// we need to do some extra work if this is a mutable resource manifest
|
|
// we need to do some extra work if this is a mutable resource manifest
|
|
|
if entry.ContentType == ResourceContentType {
|
|
if entry.ContentType == ResourceContentType {
|
|
|
|
|
|
|
@@ -398,7 +457,7 @@ func (a *API) Get(ctx context.Context, manifestAddr storage.Address, path string
|
|
|
log.Trace("resource is multihash", "key", manifestAddr)
|
|
log.Trace("resource is multihash", "key", manifestAddr)
|
|
|
|
|
|
|
|
// get the manifest the multihash digest points to
|
|
// get the manifest the multihash digest points to
|
|
|
- trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil)
|
|
|
|
|
|
|
+ trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil, decrypt)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
apiGetNotFound.Inc(1)
|
|
apiGetNotFound.Inc(1)
|
|
|
status = http.StatusNotFound
|
|
status = http.StatusNotFound
|
|
@@ -451,7 +510,7 @@ func (a *API) Delete(ctx context.Context, addr string, path string) (storage.Add
|
|
|
apiDeleteFail.Inc(1)
|
|
apiDeleteFail.Inc(1)
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
- key, err := a.Resolve(ctx, uri)
|
|
|
|
|
|
|
+ key, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS)
|
|
|
|
|
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
@@ -470,13 +529,13 @@ func (a *API) Delete(ctx context.Context, addr string, path string) (storage.Add
|
|
|
|
|
|
|
|
// GetDirectoryTar fetches a requested directory as a tarstream
|
|
// GetDirectoryTar fetches a requested directory as a tarstream
|
|
|
// it returns an io.Reader and an error. Do not forget to Close() the returned ReadCloser
|
|
// it returns an io.Reader and an error. Do not forget to Close() the returned ReadCloser
|
|
|
-func (a *API) GetDirectoryTar(ctx context.Context, uri *URI) (io.ReadCloser, error) {
|
|
|
|
|
|
|
+func (a *API) GetDirectoryTar(ctx context.Context, decrypt DecryptFunc, uri *URI) (io.ReadCloser, error) {
|
|
|
apiGetTarCount.Inc(1)
|
|
apiGetTarCount.Inc(1)
|
|
|
- addr, err := a.Resolve(ctx, uri)
|
|
|
|
|
|
|
+ addr, err := a.Resolve(ctx, uri.Addr)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
- walker, err := a.NewManifestWalker(ctx, addr, nil)
|
|
|
|
|
|
|
+ walker, err := a.NewManifestWalker(ctx, addr, decrypt, nil)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
apiGetTarFail.Inc(1)
|
|
apiGetTarFail.Inc(1)
|
|
|
return nil, err
|
|
return nil, err
|
|
@@ -542,9 +601,9 @@ func (a *API) GetDirectoryTar(ctx context.Context, uri *URI) (io.ReadCloser, err
|
|
|
|
|
|
|
|
// GetManifestList lists the manifest entries for the specified address and prefix
|
|
// GetManifestList lists the manifest entries for the specified address and prefix
|
|
|
// and returns it as a ManifestList
|
|
// and returns it as a ManifestList
|
|
|
-func (a *API) GetManifestList(ctx context.Context, addr storage.Address, prefix string) (list ManifestList, err error) {
|
|
|
|
|
|
|
+func (a *API) GetManifestList(ctx context.Context, decryptor DecryptFunc, addr storage.Address, prefix string) (list ManifestList, err error) {
|
|
|
apiManifestListCount.Inc(1)
|
|
apiManifestListCount.Inc(1)
|
|
|
- walker, err := a.NewManifestWalker(ctx, addr, nil)
|
|
|
|
|
|
|
+ walker, err := a.NewManifestWalker(ctx, addr, decryptor, nil)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
apiManifestListFail.Inc(1)
|
|
apiManifestListFail.Inc(1)
|
|
|
return ManifestList{}, err
|
|
return ManifestList{}, err
|
|
@@ -631,7 +690,7 @@ func (a *API) UpdateManifest(ctx context.Context, addr storage.Address, update f
|
|
|
func (a *API) Modify(ctx context.Context, addr storage.Address, path, contentHash, contentType string) (storage.Address, error) {
|
|
func (a *API) Modify(ctx context.Context, addr storage.Address, path, contentHash, contentType string) (storage.Address, error) {
|
|
|
apiModifyCount.Inc(1)
|
|
apiModifyCount.Inc(1)
|
|
|
quitC := make(chan bool)
|
|
quitC := make(chan bool)
|
|
|
- trie, err := loadManifest(ctx, a.fileStore, addr, quitC)
|
|
|
|
|
|
|
+ trie, err := loadManifest(ctx, a.fileStore, addr, quitC, NOOPDecrypt)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
apiModifyFail.Inc(1)
|
|
apiModifyFail.Inc(1)
|
|
|
return nil, err
|
|
return nil, err
|
|
@@ -663,7 +722,7 @@ func (a *API) AddFile(ctx context.Context, mhash, path, fname string, content []
|
|
|
apiAddFileFail.Inc(1)
|
|
apiAddFileFail.Inc(1)
|
|
|
return nil, "", err
|
|
return nil, "", err
|
|
|
}
|
|
}
|
|
|
- mkey, err := a.Resolve(ctx, uri)
|
|
|
|
|
|
|
+ mkey, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
apiAddFileFail.Inc(1)
|
|
apiAddFileFail.Inc(1)
|
|
|
return nil, "", err
|
|
return nil, "", err
|
|
@@ -770,7 +829,7 @@ func (a *API) RemoveFile(ctx context.Context, mhash string, path string, fname s
|
|
|
apiRmFileFail.Inc(1)
|
|
apiRmFileFail.Inc(1)
|
|
|
return "", err
|
|
return "", err
|
|
|
}
|
|
}
|
|
|
- mkey, err := a.Resolve(ctx, uri)
|
|
|
|
|
|
|
+ mkey, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
apiRmFileFail.Inc(1)
|
|
apiRmFileFail.Inc(1)
|
|
|
return "", err
|
|
return "", err
|
|
@@ -837,7 +896,7 @@ func (a *API) AppendFile(ctx context.Context, mhash, path, fname string, existin
|
|
|
apiAppendFileFail.Inc(1)
|
|
apiAppendFileFail.Inc(1)
|
|
|
return nil, "", err
|
|
return nil, "", err
|
|
|
}
|
|
}
|
|
|
- mkey, err := a.Resolve(ctx, uri)
|
|
|
|
|
|
|
+ mkey, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
apiAppendFileFail.Inc(1)
|
|
apiAppendFileFail.Inc(1)
|
|
|
return nil, "", err
|
|
return nil, "", err
|
|
@@ -891,13 +950,13 @@ func (a *API) BuildDirectoryTree(ctx context.Context, mhash string, nameresolver
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, nil, err
|
|
return nil, nil, err
|
|
|
}
|
|
}
|
|
|
- addr, err = a.Resolve(ctx, uri)
|
|
|
|
|
|
|
+ addr, err = a.Resolve(ctx, uri.Addr)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, nil, err
|
|
return nil, nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
quitC := make(chan bool)
|
|
quitC := make(chan bool)
|
|
|
- rootTrie, err := loadManifest(ctx, a.fileStore, addr, quitC)
|
|
|
|
|
|
|
+ rootTrie, err := loadManifest(ctx, a.fileStore, addr, quitC, NOOPDecrypt)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, nil, fmt.Errorf("can't load manifest %v: %v", addr.String(), err)
|
|
return nil, nil, fmt.Errorf("can't load manifest %v: %v", addr.String(), err)
|
|
|
}
|
|
}
|
|
@@ -955,7 +1014,7 @@ func (a *API) ResourceHashSize() int {
|
|
|
|
|
|
|
|
// ResolveResourceManifest retrieves the Mutable Resource manifest for the given address, and returns the address of the metadata chunk.
|
|
// ResolveResourceManifest retrieves the Mutable Resource manifest for the given address, and returns the address of the metadata chunk.
|
|
|
func (a *API) ResolveResourceManifest(ctx context.Context, addr storage.Address) (storage.Address, error) {
|
|
func (a *API) ResolveResourceManifest(ctx context.Context, addr storage.Address) (storage.Address, error) {
|
|
|
- trie, err := loadManifest(ctx, a.fileStore, addr, nil)
|
|
|
|
|
|
|
+ trie, err := loadManifest(ctx, a.fileStore, addr, nil, NOOPDecrypt)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, fmt.Errorf("cannot load resource manifest: %v", err)
|
|
return nil, fmt.Errorf("cannot load resource manifest: %v", err)
|
|
|
}
|
|
}
|