uplink docs (#2372)

* uplink docs

Change-Id: I67414c9e91b158ba1c120188aeb41b2907282431

* review

Change-Id: I34b2185e261e3425beb5099f0a161f988d920966
This commit is contained in:
JT Olio 2019-06-28 07:40:37 -06:00 committed by GitHub
parent 827fb92b47
commit 53e550f7d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 239 additions and 185 deletions

View File

@ -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

View File

@ -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)
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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!")
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}