diff --git a/cmd/uplink/access_permissions.go b/cmd/uplink/access_permissions.go index b0abbde4d..aaa6d2b0e 100644 --- a/cmd/uplink/access_permissions.go +++ b/cmd/uplink/access_permissions.go @@ -29,6 +29,8 @@ type accessPermissions struct { notBefore *time.Time notAfter *time.Time + + maxObjectTTL *time.Duration } func (ap *accessPermissions) Setup(params clingy.Parameters, prefixFlags bool) { @@ -65,6 +67,12 @@ func (ap *accessPermissions) Setup(params clingy.Parameters, prefixFlags bool) { "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) + params.Break() + + ap.maxObjectTTL = params.Flag("max-object-ttl", + "The object is automatically deleted after this period. (e.g. '1h30m', '24h', '720h')", + nil, clingy.Transform(time.ParseDuration), clingy.Type("period"), clingy.Optional).(*time.Duration) + if !prefixFlags { ap.prefixes = params.Arg("prefix", "Key prefix access will be restricted to", clingy.Transform(ulloc.Parse), @@ -93,6 +101,7 @@ func (ap *accessPermissions) Apply(access *uplink.Access) (*uplink.Access, error AllowUpload: ap.AllowUpload(), NotBefore: ap.NotBefore(), NotAfter: ap.NotAfter(), + MaxObjectTTL: ap.MaxObjectTTL(), } // if we aren't actually restricting anything, then we don't need to Share. @@ -120,9 +129,10 @@ func defaulted[T any](val *T, def T) T { 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) } +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) } +func (ap *accessPermissions) MaxObjectTTL() *time.Duration { return ap.maxObjectTTL } diff --git a/cmd/uplink/cmd_share.go b/cmd/uplink/cmd_share.go index f93a7f7fe..35f6c4d48 100644 --- a/cmd/uplink/cmd_share.go +++ b/cmd/uplink/cmd_share.go @@ -104,15 +104,16 @@ func (c *cmdShare) Execute(ctx context.Context) error { fmt.Fprintf(clingy.Stdout(ctx), "Sharing access to satellite %s\n", access.SatelliteAddress()) fmt.Fprintf(clingy.Stdout(ctx), "=========== ACCESS RESTRICTIONS ==========================================================\n") - fmt.Fprintf(clingy.Stdout(ctx), "Download : %s\n", formatPermission(c.ap.AllowDownload())) - fmt.Fprintf(clingy.Stdout(ctx), "Upload : %s\n", formatPermission(c.ap.AllowUpload())) - fmt.Fprintf(clingy.Stdout(ctx), "Lists : %s\n", formatPermission(c.ap.AllowList())) - fmt.Fprintf(clingy.Stdout(ctx), "Deletes : %s\n", formatPermission(c.ap.AllowDelete())) - fmt.Fprintf(clingy.Stdout(ctx), "NotBefore : %s\n", formatTimeRestriction(c.ap.NotBefore())) - fmt.Fprintf(clingy.Stdout(ctx), "NotAfter : %s\n", formatTimeRestriction(c.ap.NotAfter())) - fmt.Fprintf(clingy.Stdout(ctx), "Paths : %s\n", formatPaths(c.ap.prefixes)) + fmt.Fprintf(clingy.Stdout(ctx), "Download : %s\n", formatPermission(c.ap.AllowDownload())) + fmt.Fprintf(clingy.Stdout(ctx), "Upload : %s\n", formatPermission(c.ap.AllowUpload())) + fmt.Fprintf(clingy.Stdout(ctx), "Lists : %s\n", formatPermission(c.ap.AllowList())) + fmt.Fprintf(clingy.Stdout(ctx), "Deletes : %s\n", formatPermission(c.ap.AllowDelete())) + fmt.Fprintf(clingy.Stdout(ctx), "NotBefore : %s\n", formatTimeRestriction(c.ap.NotBefore())) + fmt.Fprintf(clingy.Stdout(ctx), "NotAfter : %s\n", formatTimeRestriction(c.ap.NotAfter())) + fmt.Fprintf(clingy.Stdout(ctx), "MaxObjectTTL : %s\n", formatDuration(c.ap.maxObjectTTL)) + fmt.Fprintf(clingy.Stdout(ctx), "Paths : %s\n", formatPaths(c.ap.prefixes)) fmt.Fprintf(clingy.Stdout(ctx), "=========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS ===========\n") - fmt.Fprintf(clingy.Stdout(ctx), "Access : %s\n", newAccessData) + fmt.Fprintf(clingy.Stdout(ctx), "Access : %s\n", newAccessData) if c.register { credentials, err := RegisterAccess(ctx, access, c.authService, c.public, c.caCert) @@ -182,6 +183,13 @@ func formatTimeRestriction(t time.Time) string { return formatTime(true, t) } +func formatDuration(d *time.Duration) string { + if d == nil { + return "Not set" + } + return d.String() +} + func formatPaths(sharePrefixes []uplink.SharePrefix) string { if len(sharePrefixes) == 0 { return "WARNING! The entire project is shared!" diff --git a/cmd/uplink/cmd_share_test.go b/cmd/uplink/cmd_share_test.go index 3e22d9550..d9a6f51bb 100644 --- a/cmd/uplink/cmd_share_test.go +++ b/cmd/uplink/cmd_share_test.go @@ -33,15 +33,16 @@ func TestShare(t *testing.T) { state.Succeed(t, "share", "sj://some/prefix").RequireStdoutGlob(t, ` Sharing access to satellite * =========== ACCESS RESTRICTIONS ========================================================== - Download : Allowed - Upload : Disallowed - Lists : Allowed - Deletes : Disallowed - NotBefore : No restriction - NotAfter : No restriction - Paths : sj://some/prefix + Download : Allowed + Upload : Disallowed + Lists : Allowed + Deletes : Disallowed + NotBefore : No restriction + NotAfter : No restriction + MaxObjectTTL : Not set + Paths : sj://some/prefix =========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS =========== - Access : * + Access : * `) }) @@ -51,15 +52,16 @@ func TestShare(t *testing.T) { state.Succeed(t, "share", "--readonly", "sj://some/prefix").RequireStdoutGlob(t, ` Sharing access to satellite * =========== ACCESS RESTRICTIONS ========================================================== - Download : Allowed - Upload : Disallowed - Lists : Allowed - Deletes : Disallowed - NotBefore : No restriction - NotAfter : No restriction - Paths : sj://some/prefix + Download : Allowed + Upload : Disallowed + Lists : Allowed + Deletes : Disallowed + NotBefore : No restriction + NotAfter : No restriction + MaxObjectTTL : Not set + Paths : sj://some/prefix =========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS =========== - Access : * + Access : * `) }) @@ -69,15 +71,16 @@ func TestShare(t *testing.T) { state.Succeed(t, "share", "--disallow-lists", "sj://some/prefix").RequireStdoutGlob(t, ` Sharing access to satellite * =========== ACCESS RESTRICTIONS ========================================================== - Download : Allowed - Upload : Disallowed - Lists : Disallowed - Deletes : Disallowed - NotBefore : No restriction - NotAfter : No restriction - Paths : sj://some/prefix + Download : Allowed + Upload : Disallowed + Lists : Disallowed + Deletes : Disallowed + NotBefore : No restriction + NotAfter : No restriction + MaxObjectTTL : Not set + Paths : sj://some/prefix =========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS =========== - Access : * + Access : * `) }) @@ -87,15 +90,16 @@ func TestShare(t *testing.T) { state.Succeed(t, "share", "--disallow-reads", "sj://some/prefix").RequireStdoutGlob(t, ` Sharing access to satellite * =========== ACCESS RESTRICTIONS ========================================================== - Download : Disallowed - Upload : Disallowed - Lists : Allowed - Deletes : Disallowed - NotBefore : No restriction - NotAfter : No restriction - Paths : sj://some/prefix + Download : Disallowed + Upload : Disallowed + Lists : Allowed + Deletes : Disallowed + NotBefore : No restriction + NotAfter : No restriction + MaxObjectTTL : Not set + Paths : sj://some/prefix =========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS =========== - Access : * + Access : * `) }) @@ -116,33 +120,54 @@ func TestShare(t *testing.T) { state.Succeed(t, "share", "--public", "--not-after=none", "sj://some/prefix").RequireStdoutGlob(t, ` Sharing access to satellite * =========== ACCESS RESTRICTIONS ========================================================== - Download : Allowed - Upload : Disallowed - Lists : Allowed - Deletes : Disallowed - NotBefore : No restriction - NotAfter : No restriction - Paths : sj://some/prefix + Download : Allowed + Upload : Disallowed + Lists : Allowed + Deletes : Disallowed + NotBefore : No restriction + NotAfter : No restriction + MaxObjectTTL : Not set + Paths : sj://some/prefix =========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS =========== - Access : * + Access : * `) }) - t.Run("share access with --not-after time restriction parameter", func(t *testing.T) { + t.Run("share access with --not-after", func(t *testing.T) { state := ultest.Setup(commands) state.Succeed(t, "share", "--not-after", "2022-01-01T15:01:01-01:00", "sj://some/prefix").RequireStdoutGlob(t, ` Sharing access to satellite * =========== ACCESS RESTRICTIONS ========================================================== - Download : Allowed - Upload : Disallowed - Lists : Allowed - Deletes : Disallowed - NotBefore : No restriction - NotAfter : 2022-01-01 16:01:01 - Paths : sj://some/prefix + Download : Allowed + Upload : Disallowed + Lists : Allowed + Deletes : Disallowed + NotBefore : No restriction + NotAfter : 2022-01-01 16:01:01 + MaxObjectTTL : Not set + Paths : sj://some/prefix =========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS =========== - Access : * + Access : * + `) + }) + + t.Run("share access with --max-object-ttl", func(t *testing.T) { + state := ultest.Setup(commands) + + state.Succeed(t, "share", "--max-object-ttl", "720h", "--readonly=false", "sj://some/prefix").RequireStdoutGlob(t, ` + Sharing access to satellite * + =========== ACCESS RESTRICTIONS ========================================================== + Download : Allowed + Upload : Allowed + Lists : Allowed + Deletes : Allowed + NotBefore : No restriction + NotAfter : No restriction + MaxObjectTTL : 720h0m0s + Paths : sj://some/prefix + =========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS =========== + Access : * `) }) @@ -184,15 +209,16 @@ func TestShare(t *testing.T) { expected := ` Sharing access to satellite * =========== ACCESS RESTRICTIONS ========================================================== - Download : Allowed - Upload : Disallowed - Lists : Allowed - Deletes : Disallowed - NotBefore : No restriction - NotAfter : No restriction - Paths : sj://some/prefix + Download : Allowed + Upload : Disallowed + Lists : Allowed + Deletes : Disallowed + NotBefore : No restriction + NotAfter : No restriction + MaxObjectTTL : Not set + Paths : sj://some/prefix =========== SERIALIZED ACCESS WITH THE ABOVE RESTRICTIONS TO SHARE WITH OTHERS =========== - Access : * + Access : * ========== GATEWAY CREDENTIALS =========================================================== Access Key ID: accesskeyid Secret Key : secretkey diff --git a/go.mod b/go.mod index 8d51ae458..ca4dc67cd 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( storj.io/drpc v0.0.33 storj.io/monkit-jaeger v0.0.0-20220915074555-d100d7589f41 storj.io/private v0.0.0-20230824104110-1eac532af65a - storj.io/uplink v1.11.1-0.20230907104623-a2e90d4e0c18 + storj.io/uplink v1.11.1-0.20230907122241-39cbd8e765dc ) require ( diff --git a/go.sum b/go.sum index 1ce96af4f..0c4ac6e3a 100644 --- a/go.sum +++ b/go.sum @@ -1023,5 +1023,5 @@ storj.io/picobuf v0.0.2-0.20230906122608-c4ba17033c6c h1:or/DtG5uaZpzimL61ahlgAA storj.io/picobuf v0.0.2-0.20230906122608-c4ba17033c6c/go.mod h1:JCuc3C0gzCJHQ4J6SOx/Yjg+QTpX0D+Fvs5H46FETCk= storj.io/private v0.0.0-20230824104110-1eac532af65a h1:x0OnU7z801JmR0XwFrFxmEBqcq+FDDuSn5jMbFoyfBo= storj.io/private v0.0.0-20230824104110-1eac532af65a/go.mod h1:6+MGr4KUXEBIOsOstFz1efPkA+8wVVfzsO8RpuAhhB4= -storj.io/uplink v1.11.1-0.20230907104623-a2e90d4e0c18 h1:xAC4VhvSQpMbUesyvQGBEORTQ64ZBK3+RfJWWa9zVbY= -storj.io/uplink v1.11.1-0.20230907104623-a2e90d4e0c18/go.mod h1:JOUmWW3FrzCkDK4wgrTonKrKTWafK7jRQA8zBb8mkxs= +storj.io/uplink v1.11.1-0.20230907122241-39cbd8e765dc h1:B03iRQQ4N9zfDnBFPJK5jCLpcjU4XZJP8PPxyLkO7Zw= +storj.io/uplink v1.11.1-0.20230907122241-39cbd8e765dc/go.mod h1:xj3uTha+PlDcb1Jyh8uH0CVb6MjwlWDBpssiGweLM7M= diff --git a/testsuite/storjscan/go.mod b/testsuite/storjscan/go.mod index cf54a176f..193716a69 100644 --- a/testsuite/storjscan/go.mod +++ b/testsuite/storjscan/go.mod @@ -13,7 +13,7 @@ require ( storj.io/private v0.0.0-20230824104110-1eac532af65a storj.io/storj v1.63.1 storj.io/storjscan v0.0.0-20220926140643-1623c3b391b0 - storj.io/uplink v1.11.1-0.20230907104623-a2e90d4e0c18 + storj.io/uplink v1.11.1-0.20230907122241-39cbd8e765dc ) require ( diff --git a/testsuite/storjscan/go.sum b/testsuite/storjscan/go.sum index d800bae60..71da1bc86 100644 --- a/testsuite/storjscan/go.sum +++ b/testsuite/storjscan/go.sum @@ -1267,5 +1267,5 @@ storj.io/private v0.0.0-20230824104110-1eac532af65a h1:x0OnU7z801JmR0XwFrFxmEBqc storj.io/private v0.0.0-20230824104110-1eac532af65a/go.mod h1:6+MGr4KUXEBIOsOstFz1efPkA+8wVVfzsO8RpuAhhB4= storj.io/storjscan v0.0.0-20220926140643-1623c3b391b0 h1:pSfGf9E9OlUd17W7LSpL4tTONIyFji6dz8I2iTDd8BY= storj.io/storjscan v0.0.0-20220926140643-1623c3b391b0/go.mod h1:5nLgAOl1KTDVyqORAhvrp+167PtShEuS1L3pJgXPjwo= -storj.io/uplink v1.11.1-0.20230907104623-a2e90d4e0c18 h1:xAC4VhvSQpMbUesyvQGBEORTQ64ZBK3+RfJWWa9zVbY= -storj.io/uplink v1.11.1-0.20230907104623-a2e90d4e0c18/go.mod h1:JOUmWW3FrzCkDK4wgrTonKrKTWafK7jRQA8zBb8mkxs= +storj.io/uplink v1.11.1-0.20230907122241-39cbd8e765dc h1:B03iRQQ4N9zfDnBFPJK5jCLpcjU4XZJP8PPxyLkO7Zw= +storj.io/uplink v1.11.1-0.20230907122241-39cbd8e765dc/go.mod h1:xj3uTha+PlDcb1Jyh8uH0CVb6MjwlWDBpssiGweLM7M= diff --git a/testsuite/ui/go.mod b/testsuite/ui/go.mod index 6226ac5d0..e5ec780bb 100644 --- a/testsuite/ui/go.mod +++ b/testsuite/ui/go.mod @@ -233,5 +233,5 @@ require ( storj.io/minio v0.0.0-20230118205046-c025fcc9eef3 // indirect storj.io/monkit-jaeger v0.0.0-20220915074555-d100d7589f41 // indirect storj.io/picobuf v0.0.2-0.20230906122608-c4ba17033c6c // indirect - storj.io/uplink v1.11.1-0.20230907104623-a2e90d4e0c18 // indirect + storj.io/uplink v1.11.1-0.20230907122241-39cbd8e765dc // indirect ) diff --git a/testsuite/ui/go.sum b/testsuite/ui/go.sum index 5dcf1025d..023b977fd 100644 --- a/testsuite/ui/go.sum +++ b/testsuite/ui/go.sum @@ -1987,6 +1987,6 @@ storj.io/picobuf v0.0.2-0.20230906122608-c4ba17033c6c h1:or/DtG5uaZpzimL61ahlgAA storj.io/picobuf v0.0.2-0.20230906122608-c4ba17033c6c/go.mod h1:JCuc3C0gzCJHQ4J6SOx/Yjg+QTpX0D+Fvs5H46FETCk= storj.io/private v0.0.0-20230824104110-1eac532af65a h1:x0OnU7z801JmR0XwFrFxmEBqcq+FDDuSn5jMbFoyfBo= storj.io/private v0.0.0-20230824104110-1eac532af65a/go.mod h1:6+MGr4KUXEBIOsOstFz1efPkA+8wVVfzsO8RpuAhhB4= -storj.io/uplink v1.11.1-0.20230907104623-a2e90d4e0c18 h1:xAC4VhvSQpMbUesyvQGBEORTQ64ZBK3+RfJWWa9zVbY= -storj.io/uplink v1.11.1-0.20230907104623-a2e90d4e0c18/go.mod h1:JOUmWW3FrzCkDK4wgrTonKrKTWafK7jRQA8zBb8mkxs= +storj.io/uplink v1.11.1-0.20230907122241-39cbd8e765dc h1:B03iRQQ4N9zfDnBFPJK5jCLpcjU4XZJP8PPxyLkO7Zw= +storj.io/uplink v1.11.1-0.20230907122241-39cbd8e765dc/go.mod h1:xj3uTha+PlDcb1Jyh8uH0CVb6MjwlWDBpssiGweLM7M= storj.io/zipper v0.0.0-20220124122551-2ac2d53a46f6 h1:vJQmb+uAiYn8hVfkhMl6OqjnUyMWSCPnkzW8IsjF8vE=