uplink docs (#2372)
* uplink docs Change-Id: I67414c9e91b158ba1c120188aeb41b2907282431 * review Change-Id: I34b2185e261e3425beb5099f0a161f988d920966
This commit is contained in:
parent
827fb92b47
commit
53e550f7d8
@ -2,7 +2,119 @@
|
||||
// See LICENSE for copying information.
|
||||
|
||||
/*
|
||||
Package uplink provides variety of functions to access the objects using storj's
|
||||
uplink library
|
||||
Package uplink is the main entrypoint to interacting with Storj Labs' decentralized
|
||||
storage network.
|
||||
|
||||
Projects
|
||||
|
||||
An (*Uplink) reference lets you open a *Project, which should have already been created via
|
||||
the web interface of one of the Storj Labs or Tardigrade network Satellites. You may be able
|
||||
to create or access your us-central-1 account here: https://us-central-1.tardigrade.io/
|
||||
|
||||
Opening a *Project requires a specific Satellite address (e.g. "us-central-1.tardigrade.io:7777")
|
||||
and an API key. The API key will grant specific access to certain operations and resources within
|
||||
a project. Projects allow you to manage and open Buckets.
|
||||
|
||||
Example:
|
||||
|
||||
ul, err := uplink.NewUplink(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ul.Close()
|
||||
|
||||
p, err := ul.OpenProject(ctx, "us-central-1.tardigrade.io:7777", apiKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer p.Close()
|
||||
|
||||
API Keys
|
||||
|
||||
An API key is a "macaroon" (see https://ai.google/research/pubs/pub41892). As such, API keys
|
||||
can be restricted such that users of the restricted API key only have access to a subset of
|
||||
what the parent API key allowed. It is possible to restrict a macarron to specific operations,
|
||||
buckets, paths, path prefixes, or time windows.
|
||||
|
||||
If you need a valid API key, please visit your chosen Satellite's web interface.
|
||||
|
||||
Example:
|
||||
|
||||
adminKey, err := uplink.ParseAPIKey("13YqeJ3Xk4KHocypZMdQZZqfC1goMvxbYSCWWEjSmew6rVvJp3GCK")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
readOnlyKey, err := adminKey.Restrict(macaroon.Caveat{
|
||||
DisallowWrites: true,
|
||||
DisallowLists: true,
|
||||
DisallowDeletes: true,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// return a new restricted key that is read only
|
||||
return readOnlyKey.Serialize()
|
||||
|
||||
Restricting an API key to a path prefix is most easily accomplished using an
|
||||
EncryptionAccess, so see EncryptionAccess for more.
|
||||
|
||||
Buckets
|
||||
|
||||
A bucket represents a collection of objects. You can upload, download, list, and delete objects of
|
||||
any size or shape. Objects within buckets are represented by keys, where keys can optionally be
|
||||
listed using the "/" delimiter. Objects are always end-to-end encrypted.
|
||||
|
||||
b, err := p.OpenBucket(ctx, "staging", access)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer b.Close()
|
||||
|
||||
EncryptionAccess
|
||||
|
||||
Where an APIKey controls what resources and operations a Satellite will allow a user to access
|
||||
and perform, an EncryptionAccess controls what buckets, path prefixes, and objects a user has the
|
||||
ability to decrypt. An EncryptionAccess is a serializable collection of hierarchically-determined
|
||||
encryption keys, where by default the key starts at the root.
|
||||
|
||||
As an example, the following code creates an encryption access context (and API key) that is
|
||||
restricted to objects with the prefix "/logs/" inside the staging bucket.
|
||||
|
||||
access := uplink.NewEncryptionAccessWithDefaultKey(defaultKey)
|
||||
logServerKey, logServerAccess, err := access.Restrict(
|
||||
readOnlyKey, uplink.EncryptionRestriction{
|
||||
Bucket: "staging",
|
||||
Path: "/logs/",
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return logServerAccess.Serialize()
|
||||
|
||||
The keys to decrypt data in other buckets or in other path prefixes are not contained in this
|
||||
new serialized encryption access context. This new encryption access context only provides the
|
||||
information for just what is necessary.
|
||||
|
||||
Objects
|
||||
|
||||
Objects support a couple kilobytes of arbitrary key/value metadata, an arbitrary-size primary
|
||||
data streams, with seeking. If you want to access only a small subrange of the data you
|
||||
uploaded, you can download only the range of the data you need in a fast and performant way.
|
||||
This allows you to stream video straight out of the network with little overhead.
|
||||
|
||||
obj, err := b.OpenObject(ctx, "/logs/webserver.log")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer obj.Close()
|
||||
|
||||
reader, err := obj.DownloadRange(ctx, 0, -1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
*/
|
||||
package uplink
|
||||
|
@ -19,38 +19,42 @@ const (
|
||||
defaultCipher = storj.EncAESGCM
|
||||
)
|
||||
|
||||
// EncryptionAccess represents an encryption context. It holds information about
|
||||
// how various buckets and objects should be encrypted and decrypted.
|
||||
// EncryptionAccess represents an encryption access context. It holds
|
||||
// information about how various buckets and objects should be
|
||||
// encrypted and decrypted.
|
||||
type EncryptionAccess struct {
|
||||
store *encryption.Store
|
||||
}
|
||||
|
||||
// NewEncryptionAccess creates an encryption ctx
|
||||
// NewEncryptionAccess creates an encryption access context
|
||||
func NewEncryptionAccess() *EncryptionAccess {
|
||||
return &EncryptionAccess{
|
||||
store: encryption.NewStore(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewEncryptionAccessWithDefaultKey creates an encryption ctx with a default key set
|
||||
// NewEncryptionAccessWithDefaultKey creates an encryption access context with
|
||||
// a default key set.
|
||||
// Use (*Project).SaltedKeyFromPassphrase to generate a default key
|
||||
func NewEncryptionAccessWithDefaultKey(defaultKey storj.Key) *EncryptionAccess {
|
||||
ec := NewEncryptionAccess()
|
||||
ec.SetDefaultKey(defaultKey)
|
||||
return ec
|
||||
}
|
||||
|
||||
// Store returns the underlying encryption store for the context.
|
||||
// Store returns the underlying encryption store for the access context.
|
||||
func (s *EncryptionAccess) Store() *encryption.Store {
|
||||
return s.store
|
||||
}
|
||||
|
||||
// SetDefaultKey sets the default key for the encryption context.
|
||||
// SetDefaultKey sets the default key for the encryption access context.
|
||||
// Use (*Project).SaltedKeyFromPassphrase to generate a default key
|
||||
func (s *EncryptionAccess) SetDefaultKey(defaultKey storj.Key) {
|
||||
s.store.SetDefaultKey(&defaultKey)
|
||||
}
|
||||
|
||||
// Import merges the other encryption context into this one. In cases
|
||||
// of conflicting path decryption settings (including if both contexts have
|
||||
// Import merges the other encryption access context into this one. In cases
|
||||
// of conflicting path decryption settings (including if both accesses have
|
||||
// a default key), the new settings are kept.
|
||||
func (s *EncryptionAccess) Import(other *EncryptionAccess) error {
|
||||
if key := other.store.GetDefaultKey(); key != nil {
|
||||
@ -67,7 +71,7 @@ type EncryptionRestriction struct {
|
||||
}
|
||||
|
||||
// Restrict creates a new EncryptionAccess with no default key, where the key material
|
||||
// in the new context is just enough to allow someone to access all of the given
|
||||
// in the new access is just enough to allow someone to access all of the given
|
||||
// restrictions but no more.
|
||||
func (s *EncryptionAccess) Restrict(apiKey APIKey, restrictions ...EncryptionRestriction) (APIKey, *EncryptionAccess, error) {
|
||||
if len(restrictions) == 0 {
|
||||
@ -136,30 +140,30 @@ func (s *EncryptionAccess) Serialize() (string, error) {
|
||||
StoreEntries: storeEntries,
|
||||
})
|
||||
if err != nil {
|
||||
return "", errs.New("unable to marshal encryption ctx: %v", err)
|
||||
return "", errs.New("unable to marshal encryption access: %v", err)
|
||||
}
|
||||
|
||||
return base58.CheckEncode(data, 0), nil
|
||||
|
||||
}
|
||||
|
||||
// ParseEncryptionAccess parses a base58 serialized encryption context into a working one.
|
||||
func ParseEncryptionAccess(b58data string) (*EncryptionAccess, error) {
|
||||
data, version, err := base58.CheckDecode(b58data)
|
||||
// ParseEncryptionAccess parses a base58 serialized encryption access into a working one.
|
||||
func ParseEncryptionAccess(serialized string) (*EncryptionAccess, error) {
|
||||
data, version, err := base58.CheckDecode(serialized)
|
||||
if err != nil || version != 0 {
|
||||
return nil, errs.New("invalid encryption context format")
|
||||
return nil, errs.New("invalid encryption access format")
|
||||
}
|
||||
|
||||
p := new(pb.EncryptionAccess)
|
||||
if err := proto.Unmarshal(data, p); err != nil {
|
||||
return nil, errs.New("unable to unmarshal encryption context: %v", err)
|
||||
return nil, errs.New("unable to unmarshal encryption access: %v", err)
|
||||
}
|
||||
|
||||
encCtx := NewEncryptionAccess()
|
||||
|
||||
if len(p.DefaultKey) > 0 {
|
||||
if len(p.DefaultKey) != len(storj.Key{}) {
|
||||
return nil, errs.New("invalid default key in encryption context")
|
||||
return nil, errs.New("invalid default key in encryption access")
|
||||
}
|
||||
var defaultKey storj.Key
|
||||
copy(defaultKey[:], p.DefaultKey)
|
||||
@ -168,7 +172,7 @@ func ParseEncryptionAccess(b58data string) (*EncryptionAccess, error) {
|
||||
|
||||
for _, entry := range p.StoreEntries {
|
||||
if len(entry.Key) != len(storj.Key{}) {
|
||||
return nil, errs.New("invalid key in encryption context entry")
|
||||
return nil, errs.New("invalid key in encryption access entry")
|
||||
}
|
||||
var key storj.Key
|
||||
copy(key[:], entry.Key)
|
||||
@ -179,7 +183,7 @@ func ParseEncryptionAccess(b58data string) (*EncryptionAccess, error) {
|
||||
paths.NewEncrypted(string(entry.EncryptedPath)),
|
||||
key)
|
||||
if err != nil {
|
||||
return nil, errs.New("invalid encryption context entry: %v", err)
|
||||
return nil, errs.New("invalid encryption access entry: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,10 @@ import (
|
||||
"storj.io/storj/lib/uplink"
|
||||
)
|
||||
|
||||
func CreateBucketExample(ctx context.Context, satelliteAddress string, apiKey string, cfg *uplink.Config, out io.Writer) (err error) {
|
||||
func CreateBucketExample(ctx context.Context,
|
||||
satelliteAddress, apiKey string,
|
||||
cfg *uplink.Config, out io.Writer) (err error) {
|
||||
|
||||
errCatch := func(fn func() error) { err = errs.Combine(err, fn()) }
|
||||
|
||||
// First, create an Uplink handle.
|
||||
@ -24,14 +27,15 @@ func CreateBucketExample(ctx context.Context, satelliteAddress string, apiKey st
|
||||
}
|
||||
defer errCatch(ul.Close)
|
||||
|
||||
// Then, parse the API key. API keys are "macaroons" that allow you to create new, restricted
|
||||
// API keys.
|
||||
// Then, parse the API key. API keys are "macaroons" that allow you to create
|
||||
// new, restricted API keys.
|
||||
key, err := uplink.ParseAPIKey(apiKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Next, open the project in question. Projects are identified by a specific Satellite and API key
|
||||
// Next, open the project in question. Projects are identified by a specific
|
||||
// Satellite and API key
|
||||
p, err := ul.OpenProject(ctx, satelliteAddress, key)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -49,13 +53,15 @@ func CreateBucketExample(ctx context.Context, satelliteAddress string, apiKey st
|
||||
}
|
||||
|
||||
func Example_createBucket() {
|
||||
// The satellite address is the address of the satellite your API key is valid on
|
||||
// The satellite address is the address of the satellite your API key is
|
||||
// valid on
|
||||
satelliteAddress := "us-central-1.tardigrade.io:7777"
|
||||
|
||||
// The API key can be created in the web interface
|
||||
apiKey := "qPSUM3k0bZyOIyil2xrVWiSuc9HuB2yBP3qDrA2Gc"
|
||||
|
||||
err := CreateBucketExample(context.Background(), satelliteAddress, apiKey, &uplink.Config{}, os.Stdout)
|
||||
err := CreateBucketExample(context.Background(), satelliteAddress, apiKey,
|
||||
&uplink.Config{}, os.Stdout)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -14,7 +14,10 @@ import (
|
||||
"storj.io/storj/lib/uplink"
|
||||
)
|
||||
|
||||
func DeleteBucketExample(ctx context.Context, satelliteAddress string, apiKey string, cfg *uplink.Config, out io.Writer) (err error) {
|
||||
func DeleteBucketExample(ctx context.Context,
|
||||
satelliteAddress, apiKey string,
|
||||
cfg *uplink.Config, out io.Writer) (err error) {
|
||||
|
||||
errCatch := func(fn func() error) { err = errs.Combine(err, fn()) }
|
||||
|
||||
// First, create an Uplink handle.
|
||||
@ -24,14 +27,15 @@ func DeleteBucketExample(ctx context.Context, satelliteAddress string, apiKey st
|
||||
}
|
||||
defer errCatch(ul.Close)
|
||||
|
||||
// Then, parse the API key. API keys are "macaroons" that allow you to create new, restricted
|
||||
// API keys.
|
||||
// Then, parse the API key. API keys are "macaroons" that allow you to create
|
||||
// new, restricted API keys.
|
||||
key, err := uplink.ParseAPIKey(apiKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Next, open the project in question. Projects are identified by a specific Satellite and API key
|
||||
// Next, open the project in question. Projects are identified by a specific
|
||||
// Satellite and API key
|
||||
p, err := ul.OpenProject(ctx, satelliteAddress, key)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -49,13 +53,15 @@ func DeleteBucketExample(ctx context.Context, satelliteAddress string, apiKey st
|
||||
}
|
||||
|
||||
func Example_deleteBucket() {
|
||||
// The satellite address is the address of the satellite your API key is valid on
|
||||
// The satellite address is the address of the satellite your API key is
|
||||
// valid on
|
||||
satelliteAddress := "us-central-1.tardigrade.io:7777"
|
||||
|
||||
// The API key can be created in the web interface
|
||||
apiKey := "qPSUM3k0bZyOIyil2xrVWiSuc9HuB2yBP3qDrA2Gc"
|
||||
|
||||
err := DeleteBucketExample(context.Background(), satelliteAddress, apiKey, &uplink.Config{}, os.Stdout)
|
||||
err := DeleteBucketExample(context.Background(), satelliteAddress, apiKey,
|
||||
&uplink.Config{}, os.Stdout)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -1,116 +0,0 @@
|
||||
// Copyright (C) 2019 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package uplink_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
||||
"storj.io/storj/lib/uplink"
|
||||
"storj.io/storj/pkg/storj"
|
||||
)
|
||||
|
||||
func logClose(fn func() error) {
|
||||
err := fn()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
// WorkWithLibUplink uploads the specified data to the specified path in the
|
||||
// specified bucket, using the specified Satellite, encryption key, and API key.
|
||||
func WorkWithLibUplink(satelliteAddress string, encryptionKey *storj.Key, apiKey uplink.APIKey,
|
||||
bucketName, uploadPath string, dataToUpload []byte) error {
|
||||
ctx := context.Background()
|
||||
|
||||
// Create an Uplink object with a default config
|
||||
upl, err := uplink.NewUplink(ctx, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create new Uplink object: %v", err)
|
||||
}
|
||||
defer logClose(upl.Close)
|
||||
|
||||
// Open up the Project we will be working with
|
||||
proj, err := upl.OpenProject(ctx, satelliteAddress, apiKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open project: %v", err)
|
||||
}
|
||||
defer logClose(proj.Close)
|
||||
|
||||
// Create the desired Bucket within the Project
|
||||
_, err = proj.CreateBucket(ctx, bucketName, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create bucket: %v", err)
|
||||
}
|
||||
|
||||
// Open up the desired Bucket within the Project
|
||||
bucket, err := proj.OpenBucket(ctx, bucketName, uplink.NewEncryptionAccessWithDefaultKey(*encryptionKey))
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open bucket %q: %v", bucketName, err)
|
||||
}
|
||||
defer logClose(bucket.Close)
|
||||
|
||||
// Upload our Object to the specified path
|
||||
buf := bytes.NewBuffer(dataToUpload)
|
||||
err = bucket.UploadObject(ctx, uploadPath, buf, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not upload: %v", err)
|
||||
}
|
||||
|
||||
// Initiate a download of the same object again
|
||||
readBack, err := bucket.OpenObject(ctx, uploadPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open object at %q: %v", uploadPath, err)
|
||||
}
|
||||
defer logClose(readBack.Close)
|
||||
|
||||
// We want the whole thing, so range from 0 to -1
|
||||
strm, err := readBack.DownloadRange(ctx, 0, -1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not initiate download: %v", err)
|
||||
}
|
||||
defer logClose(strm.Close)
|
||||
|
||||
// Read everything from the stream
|
||||
receivedContents, err := ioutil.ReadAll(strm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read object: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(receivedContents, dataToUpload) {
|
||||
return fmt.Errorf("got different object back: %q != %q", dataToUpload, receivedContents)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Example_interface() {
|
||||
|
||||
const (
|
||||
myAPIKey = "change-me-to-the-api-key-created-in-satellite-gui"
|
||||
|
||||
satellite = "mars.tardigrade.io:7777"
|
||||
myBucket = "my-first-bucket"
|
||||
myUploadPath = "foo/bar/baz"
|
||||
myData = "one fish two fish red fish blue fish"
|
||||
myEncryptionKey = "you'll never guess this"
|
||||
)
|
||||
|
||||
var encryptionKey storj.Key
|
||||
copy(encryptionKey[:], []byte(myEncryptionKey))
|
||||
|
||||
apiKey, err := uplink.ParseAPIKey(myAPIKey)
|
||||
if err != nil {
|
||||
log.Fatal("could not parse api key:", err)
|
||||
}
|
||||
|
||||
err = WorkWithLibUplink(satellite, &encryptionKey, apiKey, myBucket, myUploadPath, []byte(myData))
|
||||
if err != nil {
|
||||
log.Fatal("error:", err)
|
||||
}
|
||||
|
||||
fmt.Println("success!")
|
||||
}
|
@ -15,7 +15,10 @@ import (
|
||||
"storj.io/storj/pkg/storj"
|
||||
)
|
||||
|
||||
func ListBucketsExample(ctx context.Context, satelliteAddress string, apiKey string, cfg *uplink.Config, out io.Writer) (err error) {
|
||||
func ListBucketsExample(ctx context.Context,
|
||||
satelliteAddress, apiKey string,
|
||||
cfg *uplink.Config, out io.Writer) (err error) {
|
||||
|
||||
errCatch := func(fn func() error) { err = errs.Combine(err, fn()) }
|
||||
|
||||
// First, create an Uplink handle.
|
||||
@ -25,21 +28,23 @@ func ListBucketsExample(ctx context.Context, satelliteAddress string, apiKey str
|
||||
}
|
||||
defer errCatch(ul.Close)
|
||||
|
||||
// Then, parse the API key. API keys are "macaroons" that allow you to create new, restricted
|
||||
// API keys.
|
||||
// Then, parse the API key. API keys are "macaroons" that allow you to create
|
||||
// new, restricted API keys.
|
||||
key, err := uplink.ParseAPIKey(apiKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Next, open the project in question. Projects are identified by a specific Satellite and API key
|
||||
// Next, open the project in question. Projects are identified by a specific
|
||||
// Satellite and API key
|
||||
p, err := ul.OpenProject(ctx, satelliteAddress, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer errCatch(p.Close)
|
||||
|
||||
// Last, list the buckets! Bucket listing is paginated, so you'll need to use pagination.
|
||||
// Last, list the buckets! Bucket listing is paginated, so you'll need to
|
||||
// use pagination.
|
||||
list := uplink.BucketListOptions{
|
||||
Direction: storj.Forward}
|
||||
for {
|
||||
@ -60,13 +65,15 @@ func ListBucketsExample(ctx context.Context, satelliteAddress string, apiKey str
|
||||
}
|
||||
|
||||
func Example_listBuckets() {
|
||||
// The satellite address is the address of the satellite your API key is valid on
|
||||
// The satellite address is the address of the satellite your API key is
|
||||
// valid on
|
||||
satelliteAddress := "us-central-1.tardigrade.io:7777"
|
||||
|
||||
// The API key can be created in the web interface
|
||||
apiKey := "qPSUM3k0bZyOIyil2xrVWiSuc9HuB2yBP3qDrA2Gc"
|
||||
|
||||
err := ListBucketsExample(context.Background(), satelliteAddress, apiKey, &uplink.Config{}, os.Stdout)
|
||||
err := ListBucketsExample(context.Background(), satelliteAddress, apiKey,
|
||||
&uplink.Config{}, os.Stdout)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -16,7 +16,10 @@ import (
|
||||
"storj.io/storj/lib/uplink"
|
||||
)
|
||||
|
||||
func CreateEncryptionKeyExampleByAdmin1(ctx context.Context, satelliteAddress, apiKey string, cfg *uplink.Config, out io.Writer) (serializedAccess string, err error) {
|
||||
func CreateEncryptionKeyExampleByAdmin1(ctx context.Context,
|
||||
satelliteAddress, apiKey string, cfg *uplink.Config, out io.Writer) (
|
||||
serializedAccess string, err error) {
|
||||
|
||||
errCatch := func(fn func() error) { err = errs.Combine(err, fn()) }
|
||||
|
||||
// First, create an Uplink handle.
|
||||
@ -26,13 +29,15 @@ func CreateEncryptionKeyExampleByAdmin1(ctx context.Context, satelliteAddress, a
|
||||
}
|
||||
defer errCatch(ul.Close)
|
||||
|
||||
// Parse the API key. API keys are "macaroons" that allow you to create new, restricted API keys.
|
||||
// Parse the API key. API keys are "macaroons" that allow you to create new,
|
||||
// restricted API keys.
|
||||
key, err := uplink.ParseAPIKey(apiKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Open the project in question. Projects are identified by a specific Satellite and API key
|
||||
// Open the project in question. Projects are identified by a specific
|
||||
// Satellite and API key
|
||||
p, err := ul.OpenProject(ctx, satelliteAddress, key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -67,7 +72,8 @@ func CreateEncryptionKeyExampleByAdmin1(ctx context.Context, satelliteAddress, a
|
||||
defer errCatch(bucket.Close)
|
||||
|
||||
// Upload a file
|
||||
err = bucket.UploadObject(ctx, "webserver/logs/log.txt", strings.NewReader("hello world"), nil)
|
||||
err = bucket.UploadObject(ctx, "webserver/logs/log.txt",
|
||||
strings.NewReader("hello world"), nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -76,7 +82,10 @@ func CreateEncryptionKeyExampleByAdmin1(ctx context.Context, satelliteAddress, a
|
||||
return serializedAccess, nil
|
||||
}
|
||||
|
||||
func CreateEncryptionKeyExampleByAdmin2(ctx context.Context, satelliteAddress, apiKey string, serializedAccess string, cfg *uplink.Config, out io.Writer) (err error) {
|
||||
func CreateEncryptionKeyExampleByAdmin2(ctx context.Context,
|
||||
satelliteAddress, apiKey string, serializedAccess string,
|
||||
cfg *uplink.Config, out io.Writer) (err error) {
|
||||
|
||||
errCatch := func(fn func() error) { err = errs.Combine(err, fn()) }
|
||||
|
||||
// First, create an Uplink handle.
|
||||
@ -86,13 +95,15 @@ func CreateEncryptionKeyExampleByAdmin2(ctx context.Context, satelliteAddress, a
|
||||
}
|
||||
defer errCatch(ul.Close)
|
||||
|
||||
// Parse the API key. API keys are "macaroons" that allow you to create new, restricted API keys.
|
||||
// Parse the API key. API keys are "macaroons" that allow you to create new,
|
||||
// restricted API keys.
|
||||
key, err := uplink.ParseAPIKey(apiKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Open the project in question. Projects are identified by a specific Satellite and API key
|
||||
// Open the project in question. Projects are identified by a specific
|
||||
// Satellite and API key
|
||||
p, err := ul.OpenProject(ctx, satelliteAddress, key)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -138,7 +149,8 @@ func CreateEncryptionKeyExampleByAdmin2(ctx context.Context, satelliteAddress, a
|
||||
}
|
||||
|
||||
func Example_createEncryptionKey() {
|
||||
// The satellite address is the address of the satellite your API key is valid on
|
||||
// The satellite address is the address of the satellite your API key is
|
||||
// valid on
|
||||
satelliteAddress := "us-central-1.tardigrade.io:7777"
|
||||
|
||||
// The API key can be created in the web interface
|
||||
@ -148,13 +160,16 @@ func Example_createEncryptionKey() {
|
||||
ctx := context.Background()
|
||||
|
||||
// Admin1 is going to create an encryption context and share it
|
||||
access, err := CreateEncryptionKeyExampleByAdmin1(ctx, satelliteAddress, admin1APIKey, &uplink.Config{}, os.Stdout)
|
||||
access, err := CreateEncryptionKeyExampleByAdmin1(ctx, satelliteAddress,
|
||||
admin1APIKey, &uplink.Config{}, os.Stdout)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Admin2 is going to use the provided encryption context to load the uploaded file
|
||||
err = CreateEncryptionKeyExampleByAdmin2(ctx, satelliteAddress, admin2APIKey, access, &uplink.Config{}, os.Stdout)
|
||||
// Admin2 is going to use the provided encryption context to load the
|
||||
// uploaded file
|
||||
err = CreateEncryptionKeyExampleByAdmin2(ctx, satelliteAddress,
|
||||
admin2APIKey, access, &uplink.Config{}, os.Stdout)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -16,15 +16,20 @@ import (
|
||||
"storj.io/storj/pkg/macaroon"
|
||||
)
|
||||
|
||||
func RestrictAccessExampleByAdmin(ctx context.Context, satelliteAddress, apiKey string, adminAccess string, cfg *uplink.Config, out io.Writer) (serializedUserAPIKey string, serializedAccess string, err error) {
|
||||
// Parse the API key. API keys are "macaroons" that allow you to create new, restricted API keys.
|
||||
func RestrictAccessExampleByAdmin(ctx context.Context,
|
||||
satelliteAddress, apiKey, adminAccess string,
|
||||
cfg *uplink.Config, out io.Writer) (
|
||||
serializedUserAPIKey string, serializedAccess string, err error) {
|
||||
|
||||
// Parse the API key. API keys are "macaroons" that allow you to create new,
|
||||
// restricted API keys.
|
||||
key, err := uplink.ParseAPIKey(apiKey)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Restrict the API key to be read only and to be for just the prod and staging buckets
|
||||
// for the path webserver/logs/
|
||||
// Restrict the API key to be read only and to be for just the prod and
|
||||
// staging buckets for the path webserver/logs/
|
||||
userAPIKey, err := key.Restrict(macaroon.Caveat{
|
||||
DisallowWrites: true,
|
||||
DisallowDeletes: true,
|
||||
@ -33,23 +38,29 @@ func RestrictAccessExampleByAdmin(ctx context.Context, satelliteAddress, apiKey
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Load the existing encryption context
|
||||
// Load the existing encryption access context
|
||||
access, err := uplink.ParseEncryptionAccess(adminAccess)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Restrict the encryption context to just the prod and staging buckets
|
||||
// for webserver/logs/
|
||||
// Restrict the encryption access context to just the prod and staging
|
||||
// buckets for webserver/logs/
|
||||
userAPIKey, userAccess, err := access.Restrict(userAPIKey,
|
||||
uplink.EncryptionRestriction{Bucket: "prod", PathPrefix: "webserver/logs"},
|
||||
uplink.EncryptionRestriction{Bucket: "staging", PathPrefix: "webserver/logs"},
|
||||
uplink.EncryptionRestriction{
|
||||
Bucket: "prod",
|
||||
PathPrefix: "webserver/logs",
|
||||
},
|
||||
uplink.EncryptionRestriction{
|
||||
Bucket: "staging",
|
||||
PathPrefix: "webserver/logs",
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Serialize the encryption context
|
||||
// Serialize the encryption access context
|
||||
serializedUserAccess, err := userAccess.Serialize()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
@ -59,7 +70,10 @@ func RestrictAccessExampleByAdmin(ctx context.Context, satelliteAddress, apiKey
|
||||
return userAPIKey.Serialize(), serializedUserAccess, nil
|
||||
}
|
||||
|
||||
func RestrictAccessExampleByUser(ctx context.Context, satelliteAddress, apiKey string, serializedAccess string, cfg *uplink.Config, out io.Writer) (err error) {
|
||||
func RestrictAccessExampleByUser(ctx context.Context,
|
||||
satelliteAddress, apiKey, serializedAccess string,
|
||||
cfg *uplink.Config, out io.Writer) (err error) {
|
||||
|
||||
errCatch := func(fn func() error) { err = errs.Combine(err, fn()) }
|
||||
|
||||
// First, create an Uplink handle.
|
||||
@ -75,14 +89,15 @@ func RestrictAccessExampleByUser(ctx context.Context, satelliteAddress, apiKey s
|
||||
return err
|
||||
}
|
||||
|
||||
// Open the project in question. Projects are identified by a specific Satellite and API key
|
||||
// Open the project in question. Projects are identified by a specific
|
||||
// Satellite and API key
|
||||
p, err := ul.OpenProject(ctx, satelliteAddress, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer errCatch(p.Close)
|
||||
|
||||
// Parse the encryption context
|
||||
// Parse the encryption access context
|
||||
access, err := uplink.ParseEncryptionAccess(serializedAccess)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -121,26 +136,31 @@ func RestrictAccessExampleByUser(ctx context.Context, satelliteAddress, apiKey s
|
||||
}
|
||||
|
||||
func Example_restrictAccess() {
|
||||
// The satellite address is the address of the satellite your API key is valid on
|
||||
// The satellite address is the address of the satellite your API key is
|
||||
// valid on
|
||||
satelliteAddress := "us-central-1.tardigrade.io:7777"
|
||||
|
||||
// The API key can be created in the web interface
|
||||
adminAPIKey := "qPSUM3k0bZyOIyil2xrVWiSuc9HuB2yBP3qDrA2Gc"
|
||||
|
||||
// The encryption context was created using NewEncryptionAccessWithDefaultKey and
|
||||
// The encryption access context was created using
|
||||
// NewEncryptionAccessWithDefaultKey and
|
||||
// (*Project).SaltedKeyFromPassphrase() earlier
|
||||
adminAccess := "HYGoqCEz43mCE40Hc5lQD3DtUYynx9Vo1GjOx75hQ"
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Admin1 is going to create an encryption context and share it
|
||||
userAPIKey, access, err := RestrictAccessExampleByAdmin(ctx, satelliteAddress, adminAPIKey, adminAccess, &uplink.Config{}, os.Stdout)
|
||||
// Admin1 is going to create an encryption access context and share it
|
||||
userAPIKey, access, err := RestrictAccessExampleByAdmin(ctx,
|
||||
satelliteAddress, adminAPIKey, adminAccess, &uplink.Config{}, os.Stdout)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Admin2 is going to use the provided encryption context to load the uploaded file
|
||||
err = RestrictAccessExampleByUser(ctx, satelliteAddress, userAPIKey, access, &uplink.Config{}, os.Stdout)
|
||||
// Admin2 is going to use the provided encryption access context to load
|
||||
// the uploaded file
|
||||
err = RestrictAccessExampleByUser(ctx, satelliteAddress, userAPIKey, access,
|
||||
&uplink.Config{}, os.Stdout)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user