cmd/swarm, swarm: added access control functionality (#17404)
Co-authored-by: Janos Guljas <janos@resenje.org> Co-authored-by: Anton Evangelatov <anton.evangelatov@gmail.com> Co-authored-by: Balint Gabor <balint.g@gmail.com>
This commit is contained in:
135
swarm/api/api.go
135
swarm/api/api.go
@ -19,6 +19,9 @@ package api
|
||||
import (
|
||||
"archive/tar"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
@ -43,6 +46,10 @@ import (
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotFound = errors.New("not found")
|
||||
)
|
||||
|
||||
var (
|
||||
apiResolveCount = metrics.NewRegisteredCounter("api.resolve.count", nil)
|
||||
apiResolveFail = metrics.NewRegisteredCounter("api.resolve.fail", nil)
|
||||
@ -227,14 +234,18 @@ type API struct {
|
||||
resource *mru.Handler
|
||||
fileStore *storage.FileStore
|
||||
dns Resolver
|
||||
Decryptor func(context.Context, string) DecryptFunc
|
||||
}
|
||||
|
||||
// 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{
|
||||
fileStore: fileStore,
|
||||
dns: dns,
|
||||
resource: resourceHandler,
|
||||
Decryptor: func(ctx context.Context, credentials string) DecryptFunc {
|
||||
return self.doDecrypt(ctx, credentials, pk)
|
||||
},
|
||||
}
|
||||
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.
|
||||
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.
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// try and resolve the address
|
||||
resolved, err := a.dns.Resolve(uri.Addr)
|
||||
if err == nil {
|
||||
return resolved[:], nil
|
||||
}
|
||||
|
||||
key := uri.Address()
|
||||
if key == nil {
|
||||
apiResolveFail.Inc(1)
|
||||
addr, err := a.Resolve(ctx, uri.Addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
|
||||
if uri.Path == "" {
|
||||
return addr, nil
|
||||
}
|
||||
walker, err := a.NewManifestWalker(ctx, addr, a.Decryptor(ctx, credentials), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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
|
||||
@ -332,10 +381,10 @@ func (a *API) Put(ctx context.Context, content string, contentType string, toEnc
|
||||
// Get uses iterative manifest retrieval and prefix matching
|
||||
// to resolve basePath to content using FileStore retrieve
|
||||
// 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)
|
||||
apiGetCount.Inc(1)
|
||||
trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil)
|
||||
trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil, decrypt)
|
||||
if err != nil {
|
||||
apiGetNotFound.Inc(1)
|
||||
status = http.StatusNotFound
|
||||
@ -347,6 +396,16 @@ func (a *API) Get(ctx context.Context, manifestAddr storage.Address, path string
|
||||
|
||||
if entry != nil {
|
||||
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
|
||||
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)
|
||||
|
||||
// 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 {
|
||||
apiGetNotFound.Inc(1)
|
||||
status = http.StatusNotFound
|
||||
@ -451,7 +510,7 @@ func (a *API) Delete(ctx context.Context, addr string, path string) (storage.Add
|
||||
apiDeleteFail.Inc(1)
|
||||
return nil, err
|
||||
}
|
||||
key, err := a.Resolve(ctx, uri)
|
||||
key, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS)
|
||||
|
||||
if err != nil {
|
||||
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
|
||||
// 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)
|
||||
addr, err := a.Resolve(ctx, uri)
|
||||
addr, err := a.Resolve(ctx, uri.Addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
walker, err := a.NewManifestWalker(ctx, addr, nil)
|
||||
walker, err := a.NewManifestWalker(ctx, addr, decrypt, nil)
|
||||
if err != nil {
|
||||
apiGetTarFail.Inc(1)
|
||||
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
|
||||
// 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)
|
||||
walker, err := a.NewManifestWalker(ctx, addr, nil)
|
||||
walker, err := a.NewManifestWalker(ctx, addr, decryptor, nil)
|
||||
if err != nil {
|
||||
apiManifestListFail.Inc(1)
|
||||
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) {
|
||||
apiModifyCount.Inc(1)
|
||||
quitC := make(chan bool)
|
||||
trie, err := loadManifest(ctx, a.fileStore, addr, quitC)
|
||||
trie, err := loadManifest(ctx, a.fileStore, addr, quitC, NOOPDecrypt)
|
||||
if err != nil {
|
||||
apiModifyFail.Inc(1)
|
||||
return nil, err
|
||||
@ -663,7 +722,7 @@ func (a *API) AddFile(ctx context.Context, mhash, path, fname string, content []
|
||||
apiAddFileFail.Inc(1)
|
||||
return nil, "", err
|
||||
}
|
||||
mkey, err := a.Resolve(ctx, uri)
|
||||
mkey, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS)
|
||||
if err != nil {
|
||||
apiAddFileFail.Inc(1)
|
||||
return nil, "", err
|
||||
@ -770,7 +829,7 @@ func (a *API) RemoveFile(ctx context.Context, mhash string, path string, fname s
|
||||
apiRmFileFail.Inc(1)
|
||||
return "", err
|
||||
}
|
||||
mkey, err := a.Resolve(ctx, uri)
|
||||
mkey, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS)
|
||||
if err != nil {
|
||||
apiRmFileFail.Inc(1)
|
||||
return "", err
|
||||
@ -837,7 +896,7 @@ func (a *API) AppendFile(ctx context.Context, mhash, path, fname string, existin
|
||||
apiAppendFileFail.Inc(1)
|
||||
return nil, "", err
|
||||
}
|
||||
mkey, err := a.Resolve(ctx, uri)
|
||||
mkey, err := a.ResolveURI(ctx, uri, EMPTY_CREDENTIALS)
|
||||
if err != nil {
|
||||
apiAppendFileFail.Inc(1)
|
||||
return nil, "", err
|
||||
@ -891,13 +950,13 @@ func (a *API) BuildDirectoryTree(ctx context.Context, mhash string, nameresolver
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
addr, err = a.Resolve(ctx, uri)
|
||||
addr, err = a.Resolve(ctx, uri.Addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
quitC := make(chan bool)
|
||||
rootTrie, err := loadManifest(ctx, a.fileStore, addr, quitC)
|
||||
rootTrie, err := loadManifest(ctx, a.fileStore, addr, quitC, NOOPDecrypt)
|
||||
if err != nil {
|
||||
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.
|
||||
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 {
|
||||
return nil, fmt.Errorf("cannot load resource manifest: %v", err)
|
||||
}
|
||||
|
Reference in New Issue
Block a user