cmd/uplinkng: share command added
In uplink we have command uplink share and we need to port it to uplinkng to have command with same functionality. Command could be executed with parameter or without. without any flags we share as readonly. Change-Id: I973b11d00da237358834acf5a863ebab37e684cc
This commit is contained in:
parent
73730b23e7
commit
b5194762f2
@ -63,7 +63,7 @@ func init() {
|
||||
func shareMain(cmd *cobra.Command, args []string) (err error) {
|
||||
ctx, _ := withTelemetry(cmd)
|
||||
|
||||
isPublic := (shareCfg.Public || shareCfg.URL || shareCfg.DNS != "")
|
||||
isPublic := shareCfg.Public || shareCfg.URL || shareCfg.DNS != ""
|
||||
|
||||
if isPublic {
|
||||
if shareCfg.NotAfter == "" {
|
||||
|
@ -32,24 +32,13 @@ type accessPermissions struct {
|
||||
}
|
||||
|
||||
func (ap *accessPermissions) Setup(params clingy.Parameters) {
|
||||
transformSharePrefix := func(loc ulloc.Location) (uplink.SharePrefix, error) {
|
||||
bucket, key, ok := loc.RemoteParts()
|
||||
if !ok {
|
||||
return uplink.SharePrefix{}, errs.New("invalid prefix: must be remote: %q", loc)
|
||||
}
|
||||
return uplink.SharePrefix{
|
||||
Bucket: bucket,
|
||||
Prefix: key,
|
||||
}, nil
|
||||
}
|
||||
|
||||
ap.prefixes = params.Flag("prefix", "Key prefix access will be restricted to", []ulloc.Location{},
|
||||
clingy.Transform(ulloc.Parse),
|
||||
clingy.Transform(transformSharePrefix),
|
||||
clingy.Repeated,
|
||||
).([]uplink.SharePrefix)
|
||||
|
||||
ap.readonly = params.Flag("readonly", "Implies --disallow-writes and --disallow-deletes", false,
|
||||
ap.readonly = params.Flag("readonly", "Implies --disallow-writes and --disallow-deletes", true,
|
||||
clingy.Transform(strconv.ParseBool)).(bool)
|
||||
ap.writeonly = params.Flag("writeonly", "Implies --disallow-reads and --disallow-lists", false,
|
||||
clingy.Transform(strconv.ParseBool)).(bool)
|
||||
@ -66,6 +55,8 @@ func (ap *accessPermissions) Setup(params clingy.Parameters) {
|
||||
now := time.Now()
|
||||
transformHumanDate := clingy.Transform(func(date string) (time.Time, error) {
|
||||
switch {
|
||||
case date == "none":
|
||||
return time.Time{}, nil
|
||||
case date == "":
|
||||
return time.Time{}, nil
|
||||
case date == "now":
|
||||
@ -87,12 +78,37 @@ func (ap *accessPermissions) Setup(params clingy.Parameters) {
|
||||
time.Time{}, transformHumanDate, clingy.Type("relative_date")).(time.Time)
|
||||
}
|
||||
|
||||
func (ap *accessPermissions) SetupWithPrefixArg(params clingy.Parameters) {
|
||||
ap.Setup(params)
|
||||
|
||||
argPrefixes := params.Arg("prefix", "Key prefix access will be restricted to",
|
||||
clingy.Transform(ulloc.Parse),
|
||||
clingy.Transform(transformSharePrefix),
|
||||
clingy.Repeated,
|
||||
).([]uplink.SharePrefix)
|
||||
|
||||
if argPrefixes != nil {
|
||||
ap.prefixes = argPrefixes
|
||||
}
|
||||
}
|
||||
|
||||
func transformSharePrefix(loc ulloc.Location) (uplink.SharePrefix, error) {
|
||||
bucket, key, ok := loc.RemoteParts()
|
||||
if !ok {
|
||||
return uplink.SharePrefix{}, errs.New("invalid prefix: must be remote: %q", loc)
|
||||
}
|
||||
return uplink.SharePrefix{
|
||||
Bucket: bucket,
|
||||
Prefix: key,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (ap *accessPermissions) Apply(access *uplink.Access) (*uplink.Access, error) {
|
||||
permission := uplink.Permission{
|
||||
AllowDelete: !ap.disallowDeletes && !ap.readonly,
|
||||
AllowList: !ap.disallowLists && !ap.writeonly,
|
||||
AllowDownload: !ap.disallowReads && !ap.writeonly,
|
||||
AllowUpload: !ap.disallowWrites && !ap.readonly,
|
||||
AllowDelete: ap.AllowDelete(),
|
||||
AllowList: ap.AllowList(),
|
||||
AllowDownload: ap.AllowDownload(),
|
||||
AllowUpload: ap.AllowUpload(),
|
||||
NotBefore: ap.notBefore,
|
||||
NotAfter: ap.notAfter,
|
||||
}
|
||||
@ -114,3 +130,19 @@ func (ap *accessPermissions) Apply(access *uplink.Access) (*uplink.Access, error
|
||||
|
||||
return access, nil
|
||||
}
|
||||
|
||||
func (ap *accessPermissions) AllowDelete() bool {
|
||||
return !ap.disallowDeletes && !ap.readonly
|
||||
}
|
||||
|
||||
func (ap *accessPermissions) AllowList() bool {
|
||||
return !ap.disallowLists && !ap.writeonly
|
||||
}
|
||||
|
||||
func (ap *accessPermissions) AllowDownload() bool {
|
||||
return !ap.disallowReads && !ap.writeonly
|
||||
}
|
||||
|
||||
func (ap *accessPermissions) AllowUpload() bool {
|
||||
return !ap.disallowWrites && !ap.readonly
|
||||
}
|
||||
|
@ -4,15 +4,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/zeebo/clingy"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zeebo/clingy"
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/common/fpath"
|
||||
"storj.io/storj/cmd/uplinkng/ulext"
|
||||
"storj.io/uplink"
|
||||
)
|
||||
|
||||
const defaultAccessRegisterTimeout = 15 * time.Second
|
||||
|
||||
type cmdShare struct {
|
||||
ex ulext.External
|
||||
ap accessPermissions
|
||||
|
||||
access string
|
||||
access string
|
||||
exportTo string
|
||||
baseURL string
|
||||
register bool
|
||||
url bool
|
||||
dns string
|
||||
authService string
|
||||
public bool
|
||||
}
|
||||
|
||||
// sharePrefixExtension is a temporary struct type. We might want to add hasTrailingSlash bool to `uplink.SharePrefix` directly.
|
||||
type sharePrefixExtension struct {
|
||||
uplinkSharePrefix uplink.SharePrefix
|
||||
hasTrailingSlash bool
|
||||
}
|
||||
|
||||
func newCmdShare(ex ulext.External) *cmdShare {
|
||||
@ -20,9 +51,288 @@ func newCmdShare(ex ulext.External) *cmdShare {
|
||||
}
|
||||
|
||||
func (c *cmdShare) Setup(params clingy.Parameters) {
|
||||
c.access = params.Flag("access", "Access name or value to use", "").(string)
|
||||
c.access = params.Flag("access", "Access name or value to share", "").(string)
|
||||
c.exportTo = params.Flag("export-to", "Path to export the shared access to", "").(string)
|
||||
c.baseURL = params.Flag("base-url", "The base url for link sharing", "https://link.us1.storjshare.io").(string)
|
||||
c.register = params.Flag("register", "If true, creates and registers access grant", false, clingy.Transform(strconv.ParseBool)).(bool)
|
||||
c.url = params.Flag("url", "If true, returns a url for the shared path. implies --register and --public", false,
|
||||
clingy.Transform(strconv.ParseBool)).(bool)
|
||||
c.dns = params.Flag("dns", "Specify your custom hostname. if set, returns dns settings for web hosting. implies --register and --public", "").(string)
|
||||
c.authService = params.Flag("auth-service", "URL for shared auth service", "https://auth.us1.storjshare.io").(string)
|
||||
c.public = params.Flag("public", "If true, the access will be public. --dns and --url override this", false,
|
||||
clingy.Transform(strconv.ParseBool)).(bool)
|
||||
|
||||
c.ap.SetupWithPrefixArg(params)
|
||||
}
|
||||
|
||||
func (c *cmdShare) Execute(ctx clingy.Context) error {
|
||||
access, err := c.ex.OpenAccess(c.access)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
access, err = c.ap.Apply(access)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
isPublic := c.public || c.url || c.dns != ""
|
||||
|
||||
if isPublic {
|
||||
if c.ap.notAfter.String() == "" {
|
||||
fmt.Fprintf(ctx, "It's not recommended to create a shared Access without an expiration date.")
|
||||
fmt.Fprintf(ctx, "If you wish to do so anyway, please run this command with --not-after=none.")
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.ap.notAfter.String() == "none" {
|
||||
c.ap.notAfter = time.Time{}
|
||||
}
|
||||
}
|
||||
|
||||
newAccessData, err := access.Serialize()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var sharePrefixes []sharePrefixExtension
|
||||
for _, prefix := range c.ap.prefixes {
|
||||
sharePrefixes = append(sharePrefixes, sharePrefixExtension{
|
||||
uplinkSharePrefix: prefix,
|
||||
hasTrailingSlash: strings.HasSuffix(prefix.Prefix, "/"),
|
||||
})
|
||||
}
|
||||
|
||||
fmt.Fprintf(ctx, "Sharing access to satellite %s\n", access.SatelliteAddress())
|
||||
fmt.Fprintf(ctx, "=========== ACCESS RESTRICTIONS ==========================================================\n")
|
||||
fmt.Fprintf(ctx, "Download : %s\n", formatPermission(c.ap.AllowDownload()))
|
||||
fmt.Fprintf(ctx, "Upload : %s\n", formatPermission(c.ap.AllowUpload()))
|
||||
fmt.Fprintf(ctx, "Lists : %s\n", formatPermission(c.ap.AllowList()))
|
||||
fmt.Fprintf(ctx, "Deletes : %s\n", formatPermission(c.ap.AllowDelete()))
|
||||
fmt.Fprintf(ctx, "NotBefore : %s\n", formatTimeRestriction(c.ap.notBefore))
|
||||
fmt.Fprintf(ctx, "NotAfter : %s\n", formatTimeRestriction(c.ap.notAfter))
|
||||
fmt.Fprintf(ctx, "Paths : %s\n", formatPaths(c.ap.prefixes))
|
||||
fmt.Fprintf(ctx, "=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========\n")
|
||||
fmt.Fprintf(ctx, "Access : %s\n", newAccessData)
|
||||
|
||||
if c.register || c.url || c.dns != "" {
|
||||
accessKey, secretKey, endpoint, err := RegisterAccess(ctx, access, c.authService, isPublic, defaultAccessRegisterTimeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = DisplayGatewayCredentials(ctx, accessKey, secretKey, endpoint, "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fmt.Println("Public Access: ", isPublic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(c.ap.prefixes) == 1 && !c.ap.AllowUpload() && !c.ap.disallowDeletes {
|
||||
if c.url {
|
||||
if err = createURL(ctx, accessKey, c.ap.prefixes[0].Prefix, c.baseURL, sharePrefixes); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.dns != "" {
|
||||
if err = createDNS(ctx, accessKey, c.ap.prefixes[0].Prefix, c.baseURL, c.dns); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.exportTo != "" {
|
||||
// convert to an absolute path, mostly for output purposes.
|
||||
exportTo, err := filepath.Abs(c.exportTo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(exportTo, []byte(newAccessData+"\n"), 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Exported to:", exportTo)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatPermission(allowed bool) string {
|
||||
if allowed {
|
||||
return "Allowed"
|
||||
}
|
||||
return "Disallowed"
|
||||
}
|
||||
|
||||
func formatTimeRestriction(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return "No restriction"
|
||||
}
|
||||
return formatTime(true, t)
|
||||
}
|
||||
|
||||
func formatPaths(sharePrefixes []uplink.SharePrefix) string {
|
||||
if len(sharePrefixes) == 0 {
|
||||
return "WARNING! The entire project is shared!"
|
||||
}
|
||||
|
||||
var paths []string
|
||||
for _, prefix := range sharePrefixes {
|
||||
path := "sj://" + prefix.Bucket
|
||||
if len(prefix.Prefix) == 0 {
|
||||
path += "/ (entire bucket)"
|
||||
} else {
|
||||
path += "/" + prefix.Prefix
|
||||
}
|
||||
|
||||
paths = append(paths, path)
|
||||
}
|
||||
|
||||
return strings.Join(paths, "\n ")
|
||||
}
|
||||
|
||||
// RegisterAccess registers an access grant with a Gateway Authorization Service.
|
||||
func RegisterAccess(ctx context.Context, access *uplink.Access, authService string, public bool, timeout time.Duration) (accessKey, secretKey, endpoint string, err error) {
|
||||
if authService == "" {
|
||||
return "", "", "", errs.New("no auth service address provided")
|
||||
}
|
||||
accessSerialized, err := access.Serialize()
|
||||
if err != nil {
|
||||
return "", "", "", errs.Wrap(err)
|
||||
}
|
||||
postData, err := json.Marshal(map[string]interface{}{
|
||||
"access_grant": accessSerialized,
|
||||
"public": public,
|
||||
})
|
||||
if err != nil {
|
||||
return accessKey, "", "", errs.Wrap(err)
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, fmt.Sprintf("%s/v1/access", authService), bytes.NewReader(postData))
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
defer func() { err = errs.Combine(err, resp.Body.Close()) }()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
respBody := make(map[string]string)
|
||||
if err := json.Unmarshal(body, &respBody); err != nil {
|
||||
return "", "", "", errs.New("unexpected response from auth service: %s", string(body))
|
||||
}
|
||||
|
||||
accessKey, ok := respBody["access_key_id"]
|
||||
if !ok {
|
||||
return "", "", "", errs.New("access_key_id missing in response")
|
||||
}
|
||||
secretKey, ok = respBody["secret_key"]
|
||||
if !ok {
|
||||
return "", "", "", errs.New("secret_key missing in response")
|
||||
}
|
||||
return accessKey, secretKey, respBody["endpoint"], nil
|
||||
}
|
||||
|
||||
// Creates linksharing url for allowed path prefixes.
|
||||
func createURL(ctx clingy.Context, newAccessData, prefix, baseURL string, sharePrefixes []sharePrefixExtension) (err error) {
|
||||
p, err := fpath.New(prefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(ctx, "=========== BROWSER URL ==================================================================\n")
|
||||
fmt.Fprintf(ctx, "REMINDER : Object key must end in '/' when trying to share recursively\n")
|
||||
|
||||
path := p.Path()
|
||||
// If we're not sharing the entire bucket (the path is empty)
|
||||
// and the requested share prefix has a trailing slash, then
|
||||
// make sure to append a trailing slash to the URL.
|
||||
if path != "" && sharePrefixes[0].hasTrailingSlash {
|
||||
path += "/"
|
||||
}
|
||||
fmt.Fprintf(ctx, "URL : %s/s/%s/%s/%s\n", baseURL, url.PathEscape(newAccessData), p.Bucket(), path)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates dns record info for allowed path prefixes.
|
||||
func createDNS(ctx clingy.Context, accessKey, pathPrefix, baseURL, dns string) (err error) {
|
||||
p, err := fpath.New(pathPrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
CNAME, err := url.Parse(baseURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var printStorjRoot string
|
||||
if p.Path() == "" {
|
||||
printStorjRoot = fmt.Sprintf("txt-%s\tIN\tTXT \tstorj-root:%s", dns, p.Bucket())
|
||||
} else {
|
||||
printStorjRoot = fmt.Sprintf("txt-%s\tIN\tTXT \tstorj-root:%s/%s", dns, p.Bucket(), p.Path())
|
||||
}
|
||||
|
||||
fmt.Fprintf(ctx, "=========== DNS INFO =====================================================================\n")
|
||||
fmt.Fprintf(ctx, "Remember to update the $ORIGIN with your domain name. You may also change the $TTL.\n")
|
||||
fmt.Fprintf(ctx, "$ORIGIN example.com.\n")
|
||||
fmt.Fprintf(ctx, "$TTL 3600\n")
|
||||
fmt.Fprintf(ctx, "%s \tIN\tCNAME\t%s.\n", dns, CNAME.Host)
|
||||
fmt.Fprintln(ctx, printStorjRoot)
|
||||
fmt.Fprintf(ctx, "txt-%s\tIN\tTXT \tstorj-access:%s\n", dns, accessKey)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisplayGatewayCredentials formats and writes credentials to stdout.
|
||||
func DisplayGatewayCredentials(ctx clingy.Context, accessKey, secretKey, endpoint, format, awsProfile string) (err error) {
|
||||
switch format {
|
||||
case "env": // export / set compatible format
|
||||
// note that AWS_ENDPOINT configuration is not natively utilized by the AWS CLI
|
||||
_, err = fmt.Fprintf(ctx, "AWS_ACCESS_KEY_ID=%s\n"+
|
||||
"AWS_SECRET_ACCESS_KEY=%s\n"+
|
||||
"AWS_ENDPOINT=%s\n",
|
||||
accessKey, secretKey, endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "aws": // aws configuration commands
|
||||
profile := ""
|
||||
if awsProfile != "" {
|
||||
profile = " --profile " + awsProfile
|
||||
_, err = fmt.Fprintf(ctx, "aws configure %s\n", profile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// note that the endpoint_url configuration is not natively utilized by the AWS CLI
|
||||
_, err = fmt.Fprintf(ctx, "aws configure %s set aws_access_key_id %s\n"+
|
||||
"aws configure %s set aws_secret_access_key %s\n"+
|
||||
"aws configure %s set s3.endpoint_url %s\n",
|
||||
profile, accessKey, profile, secretKey, profile, endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default: // plain text
|
||||
_, err = fmt.Fprintf(ctx, "========== CREDENTIALS ===================================================================\n"+
|
||||
"Access Key ID: %s\n"+
|
||||
"Secret Key : %s\n"+
|
||||
"Endpoint : %s\n",
|
||||
accessKey, secretKey, endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
208
cmd/uplinkng/cmd_share_test.go
Normal file
208
cmd/uplinkng/cmd_share_test.go
Normal file
@ -0,0 +1,208 @@
|
||||
// Copyright (C) 2021 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"storj.io/storj/cmd/uplinkng/ultest"
|
||||
"storj.io/uplink"
|
||||
)
|
||||
|
||||
func TestShare(t *testing.T) {
|
||||
access := "12edqrJX1V243n5fWtUrwpMQXL8gKdY2wbyqRPSG3rsA1tzmZiQjtCyF896egifN2C2qdY6g5S1t6e8iDhMUon9Pb7HdecBFheAcvmN8652mqu8hRx5zcTUaRTWfFCKS2S6DHmTeqPUHJLEp6cJGXNHcdqegcKfeahVZGP4rTagHvFGEraXjYRJ3knAcWDGW6BxACqogEWez6r274JiUBfs4yRSbRNRqUEURd28CwDXMSHLRKKA7TEDKEdQ"
|
||||
|
||||
t.Run("share default access", func(t *testing.T) {
|
||||
state := ultest.Setup(commands)
|
||||
|
||||
acc, err := uplink.ParseAccess(access)
|
||||
assert.NoError(t, err)
|
||||
|
||||
result := state.Succeed(t, "share", "--access", access)
|
||||
|
||||
// TODO we need to find nicer way to compare results
|
||||
accessIndex := strings.Index(result.Stdout, "Access :")
|
||||
result.Stdout = result.Stdout[:accessIndex]
|
||||
|
||||
result.RequireStdout(t, `
|
||||
Sharing access to satellite `+acc.SatelliteAddress()+`
|
||||
=========== ACCESS RESTRICTIONS ==========================================================
|
||||
Download : Allowed
|
||||
Upload : Disallowed
|
||||
Lists : Allowed
|
||||
Deletes : Disallowed
|
||||
NotBefore : No restriction
|
||||
NotAfter : No restriction
|
||||
Paths : WARNING! The entire project is shared!
|
||||
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
|
||||
`)
|
||||
})
|
||||
|
||||
t.Run("share access with --readonly", func(t *testing.T) {
|
||||
state := ultest.Setup(commands)
|
||||
|
||||
acc, err := uplink.ParseAccess(access)
|
||||
assert.NoError(t, err)
|
||||
|
||||
result := state.Succeed(t, "share", "--access", access, "--readonly")
|
||||
|
||||
// TODO we need to find nicer way to compare results
|
||||
accessIndex := strings.Index(result.Stdout, "Access :")
|
||||
result.Stdout = result.Stdout[:accessIndex]
|
||||
|
||||
result.RequireStdout(t, `
|
||||
Sharing access to satellite `+acc.SatelliteAddress()+`
|
||||
=========== ACCESS RESTRICTIONS ==========================================================
|
||||
Download : Allowed
|
||||
Upload : Disallowed
|
||||
Lists : Allowed
|
||||
Deletes : Disallowed
|
||||
NotBefore : No restriction
|
||||
NotAfter : No restriction
|
||||
Paths : WARNING! The entire project is shared!
|
||||
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
|
||||
`)
|
||||
})
|
||||
|
||||
t.Run("share access with --disallow-lists", func(t *testing.T) {
|
||||
state := ultest.Setup(commands)
|
||||
|
||||
acc, err := uplink.ParseAccess(access)
|
||||
assert.NoError(t, err)
|
||||
|
||||
result := state.Succeed(t, "share", "--access", access, "--disallow-lists")
|
||||
|
||||
// TODO we need to find nicer way to compare results
|
||||
accessIndex := strings.Index(result.Stdout, "Access :")
|
||||
result.Stdout = result.Stdout[:accessIndex]
|
||||
|
||||
result.RequireStdout(t, `
|
||||
Sharing access to satellite `+acc.SatelliteAddress()+`
|
||||
=========== ACCESS RESTRICTIONS ==========================================================
|
||||
Download : Allowed
|
||||
Upload : Disallowed
|
||||
Lists : Disallowed
|
||||
Deletes : Disallowed
|
||||
NotBefore : No restriction
|
||||
NotAfter : No restriction
|
||||
Paths : WARNING! The entire project is shared!
|
||||
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
|
||||
`)
|
||||
})
|
||||
|
||||
t.Run("share access with --disallow-reads", func(t *testing.T) {
|
||||
state := ultest.Setup(commands)
|
||||
|
||||
acc, err := uplink.ParseAccess(access)
|
||||
assert.NoError(t, err)
|
||||
|
||||
result := state.Succeed(t, "share", "--access", access, "--disallow-reads")
|
||||
|
||||
// TODO we need to find nicer way to compare results
|
||||
accessIndex := strings.Index(result.Stdout, "Access :")
|
||||
result.Stdout = result.Stdout[:accessIndex]
|
||||
|
||||
result.RequireStdout(t, `
|
||||
Sharing access to satellite `+acc.SatelliteAddress()+`
|
||||
=========== ACCESS RESTRICTIONS ==========================================================
|
||||
Download : Disallowed
|
||||
Upload : Disallowed
|
||||
Lists : Allowed
|
||||
Deletes : Disallowed
|
||||
NotBefore : No restriction
|
||||
NotAfter : No restriction
|
||||
Paths : WARNING! The entire project is shared!
|
||||
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
|
||||
`)
|
||||
})
|
||||
|
||||
t.Run("share access with --writeonly", func(t *testing.T) {
|
||||
state := ultest.Setup(commands)
|
||||
|
||||
result := state.Fail(t, "share", "--access", access, "--writeonly")
|
||||
assert.Equal(t, "permission is empty", result.Err.Error())
|
||||
})
|
||||
|
||||
t.Run("share access with --public", func(t *testing.T) {
|
||||
state := ultest.Setup(commands)
|
||||
|
||||
acc, err := uplink.ParseAccess(access)
|
||||
assert.NoError(t, err)
|
||||
|
||||
result := state.Succeed(t, "share", "--public", "--not-after=none")
|
||||
|
||||
// TODO we need to find nicer way to compare results
|
||||
accessIndex := strings.Index(result.Stdout, "Access :")
|
||||
result.Stdout = result.Stdout[:accessIndex]
|
||||
|
||||
result.RequireStdout(t, `
|
||||
Sharing access to satellite `+acc.SatelliteAddress()+`
|
||||
=========== ACCESS RESTRICTIONS ==========================================================
|
||||
Download : Allowed
|
||||
Upload : Disallowed
|
||||
Lists : Allowed
|
||||
Deletes : Disallowed
|
||||
NotBefore : No restriction
|
||||
NotAfter : No restriction
|
||||
Paths : WARNING! The entire project is shared!
|
||||
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
|
||||
`)
|
||||
})
|
||||
|
||||
t.Run("share access with --not-after time restriction parameter", func(t *testing.T) {
|
||||
state := ultest.Setup(commands)
|
||||
|
||||
acc, err := uplink.ParseAccess(access)
|
||||
assert.NoError(t, err)
|
||||
|
||||
notAfterDate := "2022-01-01T15:01:01-01:00"
|
||||
result := state.Succeed(t, "share", "--access", access, "--not-after", notAfterDate)
|
||||
|
||||
// TODO we need to find nicer way to compare results
|
||||
accessIndex := strings.Index(result.Stdout, "Access :")
|
||||
result.Stdout = result.Stdout[:accessIndex]
|
||||
|
||||
result.RequireStdout(t, `
|
||||
Sharing access to satellite `+acc.SatelliteAddress()+`
|
||||
=========== ACCESS RESTRICTIONS ==========================================================
|
||||
Download : Allowed
|
||||
Upload : Disallowed
|
||||
Lists : Allowed
|
||||
Deletes : Disallowed
|
||||
NotBefore : No restriction
|
||||
NotAfter : 2022-01-01 16:01:01
|
||||
Paths : WARNING! The entire project is shared!
|
||||
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
|
||||
`)
|
||||
})
|
||||
|
||||
t.Run("share access with prefix", func(t *testing.T) {
|
||||
state := ultest.Setup(commands)
|
||||
|
||||
acc, err := uplink.ParseAccess(access)
|
||||
assert.NoError(t, err)
|
||||
|
||||
result := state.Succeed(t, "share", "--access", access, "sj://bucket/object-to-share")
|
||||
|
||||
// TODO we need to find nicer way to compare results
|
||||
accessIndex := strings.Index(result.Stdout, "Access :")
|
||||
result.Stdout = result.Stdout[:accessIndex]
|
||||
|
||||
result.RequireStdout(t, `
|
||||
Sharing access to satellite `+acc.SatelliteAddress()+`
|
||||
=========== ACCESS RESTRICTIONS ==========================================================
|
||||
Download : Allowed
|
||||
Upload : Disallowed
|
||||
Lists : Allowed
|
||||
Deletes : Disallowed
|
||||
NotBefore : No restriction
|
||||
NotAfter : No restriction
|
||||
Paths : sj://bucket/object-to-share
|
||||
=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========
|
||||
`)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user