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:
parent
7d1e28ea30
commit
7df3c9efc3
@ -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 ")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user