storj/cmd/uplink/access_permissions.go
Márton Elek 6737d427e4
cmd/uplink: better usability for date specification
I use `uplink share` command but I always fail to set the --not-before parameter.

 * Usually I try +2d when I see in the help that +2h is possible --> fail
 * When it fails, I try to set explicit date, like 2012-12-23 --> fail

This patch makes it possible to use:

 * day duration (like +3d)
 * shorter date definition (like `2023-12-12` or `2023-12-12T12:40`)

Change-Id: I2243b36f59c8929eb0473c4bb4fed19220890c71
2023-03-06 13:07:27 +01:00

129 lines
4.4 KiB
Go

// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"strconv"
"time"
"github.com/zeebo/clingy"
"github.com/zeebo/errs"
"storj.io/storj/cmd/uplink/ulloc"
"storj.io/uplink"
)
// accessPermissions holds flags and provides a Setup method for commands that
// have to modify permissions on access grants.
type accessPermissions struct {
prefixes []uplink.SharePrefix // prefixes is the set of path prefixes that the grant will be limited to
readonly bool
writeonly bool
disallowDeletes *bool
disallowLists *bool
disallowReads *bool
disallowWrites *bool
notBefore *time.Time
notAfter *time.Time
}
func (ap *accessPermissions) Setup(params clingy.Parameters, prefixFlags bool) {
if prefixFlags {
ap.prefixes = params.Flag("prefix", "Key prefix access will be restricted to", []uplink.SharePrefix{},
clingy.Transform(ulloc.Parse),
clingy.Transform(transformSharePrefix),
clingy.Repeated,
).([]uplink.SharePrefix)
}
ap.readonly = params.Flag("readonly", "Implies --disallow-writes and --disallow-deletes", true,
clingy.Transform(strconv.ParseBool), clingy.Boolean).(bool)
ap.writeonly = params.Flag("writeonly", "Implies --disallow-reads and --disallow-lists", false,
clingy.Transform(strconv.ParseBool), clingy.Boolean).(bool)
params.Break()
ap.disallowDeletes = params.Flag("disallow-deletes", "Disallow deletes with the access", nil,
clingy.Transform(strconv.ParseBool), clingy.Boolean, clingy.Optional).(*bool)
ap.disallowLists = params.Flag("disallow-lists", "Disallow lists with the access", nil,
clingy.Transform(strconv.ParseBool), clingy.Boolean, clingy.Optional).(*bool)
ap.disallowReads = params.Flag("disallow-reads", "Disallow reads with the access", nil,
clingy.Transform(strconv.ParseBool), clingy.Boolean, clingy.Optional).(*bool)
ap.disallowWrites = params.Flag("disallow-writes", "Disallow writes with the access", nil,
clingy.Transform(strconv.ParseBool), clingy.Boolean, clingy.Optional).(*bool)
params.Break()
ap.notBefore = params.Flag("not-before",
"Disallow access before this time (e.g. '+2h', 'now', '2020-01-02T15:04:05Z0700', 'none')",
nil, clingy.Transform(parseHumanDateNotBefore), clingy.Type("relative_date"), clingy.Optional).(*time.Time)
ap.notAfter = params.Flag("not-after",
"Disallow access after this time (e.g. '+2h', 'now', '2020-01-02T15:04:05Z0700', 'none')",
nil, clingy.Transform(parseHumanDateNotAfter), clingy.Type("relative_date"), clingy.Optional).(*time.Time)
if !prefixFlags {
ap.prefixes = params.Arg("prefix", "Key prefix access will be restricted to",
clingy.Transform(ulloc.Parse),
clingy.Transform(transformSharePrefix),
clingy.Repeated,
).([]uplink.SharePrefix)
}
}
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.AllowDelete(),
AllowList: ap.AllowList(),
AllowDownload: ap.AllowDownload(),
AllowUpload: ap.AllowUpload(),
NotBefore: ap.NotBefore(),
NotAfter: ap.NotAfter(),
}
// if we aren't actually restricting anything, then we don't need to Share.
if permission == (uplink.Permission{
AllowDelete: true,
AllowList: true,
AllowDownload: true,
AllowUpload: true,
}) && len(ap.prefixes) == 0 {
return access, nil
}
access, err := access.Share(permission, ap.prefixes...)
if err != nil {
return nil, errs.Wrap(err)
}
return access, nil
}
func defaulted[T any](val *T, def T) T {
if val != nil {
return *val
}
return def
}
func (ap *accessPermissions) NotBefore() time.Time { return defaulted(ap.notBefore, time.Time{}) }
func (ap *accessPermissions) NotAfter() time.Time { return defaulted(ap.notAfter, time.Time{}) }
func (ap *accessPermissions) AllowDelete() bool { return !defaulted(ap.disallowDeletes, ap.readonly) }
func (ap *accessPermissions) AllowList() bool { return !defaulted(ap.disallowLists, ap.writeonly) }
func (ap *accessPermissions) AllowDownload() bool { return !defaulted(ap.disallowReads, ap.writeonly) }
func (ap *accessPermissions) AllowUpload() bool { return !defaulted(ap.disallowWrites, ap.readonly) }