cmd/uplink: use arguments in share command as allowed path prefixes

Fixes Least Authority Issue F:
https://storjlabs.atlassian.net/browse/V3-3409

If the --allowed-path-prefix flag is not set to the `share` command, any
command arguments will be used as allowed path prefixes.

This patch also improves the output of the `share` command to print the
state of all restrictions, so users can confirm they match their
intention.

Change-Id: Id1b4df20b182d3fe04cb2196feea090975fce8b4
This commit is contained in:
Kaloyan Raev 2019-12-26 13:30:11 +02:00
parent 7d1e28ea30
commit 7df3c9efc3

View File

@ -7,9 +7,9 @@ import (
"fmt"
"io/ioutil"
"path/filepath"
"strings"
"time"
"github.com/gogo/protobuf/proto"
"github.com/spf13/cobra"
"github.com/zeebo/errs"
@ -30,7 +30,7 @@ var shareCfg struct {
Writeonly bool `default:"false" help:"implies disallow_reads and disallow_lists"`
NotBefore string `help:"disallow access before this time"`
NotAfter string `help:"disallow access after this time"`
AllowedPathPrefix []string `help:"whitelist of bucket path prefixes to require"`
AllowedPathPrefix []string `help:"whitelist of path prefixes to require, overrides the [allowed-path-prefix] arguments"`
ExportTo string `default:"" help:"path to export the shared scope to"`
// Share requires information about the current scope
@ -42,8 +42,8 @@ func init() {
// above, and addCmd adds a whole lot more than we want.
shareCmd := &cobra.Command{
Use: "share",
Short: "Creates a possibly restricted api key",
Use: "share [allowed-path-prefix]...",
Short: "Shares restricted access to objects.",
RunE: shareMain,
}
RootCmd.AddCommand(shareCmd)
@ -86,6 +86,14 @@ func shareMain(cmd *cobra.Command, args []string) (err error) {
return err
}
if len(shareCfg.AllowedPathPrefix) == 0 {
// if the --allowed-path-prefix flag is not set,
// use any arguments as allowed path prefixes
for _, arg := range args {
shareCfg.AllowedPathPrefix = append(shareCfg.AllowedPathPrefix, strings.Split(arg, ",")...)
}
}
var restrictions []libuplink.EncryptionRestriction
for _, path := range shareCfg.AllowedPathPrefix {
p, err := fpath.New(path)
@ -127,23 +135,6 @@ func shareMain(cmd *cobra.Command, args []string) (err error) {
caveat.NotBefore = notBefore
caveat.NotAfter = notAfter
{
// Times don't marshal very well with MarshalTextString, and the nonce doesn't
// matter to humans, so handle those explicitly and then dispatch to the generic
// routine to avoid having to print all the things individually.
caveatCopy := proto.Clone(&caveat).(*macaroon.Caveat)
caveatCopy.Nonce = nil
if caveatCopy.NotBefore != nil {
fmt.Println("not before:", caveatCopy.NotBefore.Truncate(0).Format(shareISO8601))
caveatCopy.NotBefore = nil
}
if caveatCopy.NotAfter != nil {
fmt.Println("not after:", caveatCopy.NotAfter.Truncate(0).Format(shareISO8601))
caveatCopy.NotAfter = nil
}
fmt.Print(proto.MarshalTextString(caveatCopy))
}
key, err = key.Restrict(caveat)
if err != nil {
return err
@ -165,9 +156,20 @@ func shareMain(cmd *cobra.Command, args []string) (err error) {
return err
}
fmt.Println("api key:", key.Serialize())
fmt.Println("enc ctx:", accessData)
fmt.Println("scope :", scopeData)
fmt.Println("=========== INTERNAL SCOPE INFO =========================================================")
fmt.Println("Satellite :", scope.SatelliteAddr)
fmt.Println("API Key :", key.Serialize())
fmt.Println("Enc Access:", accessData)
fmt.Println("=========== SHARE RESTRICTIONS ==========================================================")
fmt.Println("Reads :", formatPermission(!caveat.GetDisallowReads()))
fmt.Println("Writes :", formatPermission(!caveat.GetDisallowWrites()))
fmt.Println("Lists :", formatPermission(!caveat.GetDisallowLists()))
fmt.Println("Deletes :", formatPermission(!caveat.GetDisallowDeletes()))
fmt.Println("Not Before:", formatTimeRestriction(caveat.NotBefore))
fmt.Println("Not After :", formatTimeRestriction(caveat.NotAfter))
fmt.Println("Paths :", formatPaths(restrictions))
fmt.Println("=========== SERIALIZED SCOPE WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========")
fmt.Println("Scope :", scopeData)
if shareCfg.ExportTo != "" {
// convert to an absolute path, mostly for output purposes.
@ -178,7 +180,40 @@ func shareMain(cmd *cobra.Command, args []string) (err error) {
if err := ioutil.WriteFile(exportTo, []byte(scopeData+"\n"), 0600); err != nil {
return Error.Wrap(err)
}
fmt.Printf("exported to %s\n", exportTo)
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 == nil {
return "No restriction"
}
return formatTime(*t)
}
func formatPaths(restrictions []libuplink.EncryptionRestriction) string {
if len(restrictions) == 0 {
return "WARNING! The entire project is shared!"
}
var paths []string
for _, restriction := range restrictions {
path := "sj://" + restriction.Bucket
if len(restriction.PathPrefix) == 0 {
path += " (entire bucket)"
} else {
path += "/" + restriction.PathPrefix
}
paths = append(paths, path)
}
return strings.Join(paths, "\n ")
}