Skip to content

Commit

Permalink
pkg/linksharing/sharing: hide parent dir option if parent is unlistable
Browse files Browse the repository at this point in the history
This change hides the option in the linksharing UI for navigating to
the parent directory if the access grant isn't permitted to do so.
Previously, this would show an Access Denied error page.

Resolves #386

Change-Id: I04566d92cf3408a4935d5bce21c665fd69ec80cb
  • Loading branch information
jewharton committed Mar 11, 2024
1 parent 41a7319 commit ecd5d8d
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 8 deletions.
33 changes: 27 additions & 6 deletions pkg/linksharing/sharing/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import (
"sort"
"strings"

"storj.io/common/grant"
"storj.io/common/memory"
"storj.io/common/paths"
"storj.io/edge/pkg/errdata"
"storj.io/uplink"
"storj.io/zipper"
Expand All @@ -33,23 +35,42 @@ type listObject struct {
func (handler *Handler) servePrefix(ctx context.Context, w http.ResponseWriter, project *uplink.Project, pr *parsedRequest, archivePath, cursor string) (err error) {
defer mon.Task()(&ctx)(&err)
var input struct {
Title string
Breadcrumbs []breadcrumb
Objects []listObject
NextCursor string
ShowBackButton bool
Title string
Breadcrumbs []breadcrumb
Objects []listObject
NextCursor string
ShowBackButton bool
IsParentListable bool
}
input.Title = pr.title
input.Breadcrumbs = append(input.Breadcrumbs, pr.root)
if pr.visibleKey != "" {
parts := strings.Split(strings.TrimRight(pr.visibleKey, "/"), "/")
trimmed := strings.TrimRight(pr.visibleKey, "/")
parts := strings.Split(trimmed, "/")
for i, prefix := range parts {
url := input.Breadcrumbs[i].URL + prefix
if archivePath == "" || i < len(parts)-1 {
url += "/"
}
input.Breadcrumbs = append(input.Breadcrumbs, breadcrumb{Prefix: prefix, URL: url})
}

serializedAccess, err := pr.access.Serialize()
if err != nil {
return errdata.WithAction(err, "serve prefix")
}
access, err := grant.ParseAccess(serializedAccess)
if err != nil {
return errdata.WithAction(err, "serve prefix")
}

var parent string
if index := strings.LastIndexByte(trimmed, '/'); index != -1 {
parent = pr.visibleKey[:index]
}
if _, _, base := access.EncAccess.Store.LookupUnencrypted(pr.bucket, paths.NewUnencrypted(parent)); base != nil {
input.IsParentListable = true
}
}

input.Objects = make([]listObject, 0)
Expand Down
4 changes: 2 additions & 2 deletions pkg/linksharing/web/prefix-listing.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ <h4 class="breadcrumbs">
{{end}}
{{end}}

{{if or (gt (len .Data.Breadcrumbs) 1) .Data.ShowBackButton .Data.NextCursor}}
{{if or .Data.IsParentListable .Data.ShowBackButton .Data.NextCursor}}
<div class="row">
{{if (gt (len .Data.Breadcrumbs) 1)}}
{{if .Data.IsParentListable}}
<a class="directory-link" href="{{if .ShowViewContents}}.{{else}}..{{end}}/">
<!-- template comment: . instead of .. is a special case for going back inside a zip -->
<div class="col">
Expand Down
25 changes: 25 additions & 0 deletions testsuite/linksharing/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,15 @@ func testHandlerRequests(t *testing.T, ctx *testcontext.Context, planet *testpla
serializedListOnlyAccess, err := listOnlyAccess.Serialize()
require.NoError(t, err)

prefixedAccess, err := access.Share(
uplink.Permission{AllowList: true},
uplink.SharePrefix{Bucket: "testbucket", Prefix: "test/"},
)
require.NoError(t, err)

serializedPrefixedAccess, err := prefixedAccess.Serialize()
require.NoError(t, err)

downloadOnlyAccess, err := access.Share(
uplink.Permission{AllowList: false, AllowDownload: true},
uplink.SharePrefix{Bucket: "testbucket"},
Expand All @@ -177,13 +186,15 @@ func testHandlerRequests(t *testing.T, ctx *testcontext.Context, planet *testpla
goodAccessName := randomAccessKey(t)
privateAccessName := randomAccessKey(t)
listOnlyAccessName := randomAccessKey(t)
prefixedAccessName := randomAccessKey(t)
downloadOnlyAccessName := randomAccessKey(t)

authToken := hex.EncodeToString(testrand.BytesInt(16))
validAuthServer := httptest.NewServer(makeAuthHandler(t, map[string]authHandlerEntry{
goodAccessName: {serializedAccess, true},
privateAccessName: {serializedAccess, false},
listOnlyAccessName: {serializedListOnlyAccess, true},
prefixedAccessName: {serializedPrefixedAccess, true},
downloadOnlyAccessName: {serializedDownloadOnlyAccess, true},
}, authToken))
defer validAuthServer.Close()
Expand Down Expand Up @@ -527,6 +538,20 @@ func testHandlerRequests(t *testing.T, ctx *testcontext.Context, planet *testpla
notContains: []string{"test0", "test2", "Next", sharing.FilePlaceholder},
expectedRPCCalls: []string{"/metainfo.Metainfo/GetObject", "/metainfo.Metainfo/GetObject", "/metainfo.Metainfo/ListObjects"},
},
{
name: "GET directory listing shows link to parent directory",
method: "GET",
path: path.Join("s", serializedAccess, "testbucket", "test") + "/",
status: http.StatusOK,
body: []string{"..."},
},
{
name: "GET directory listing hides link to unlistable parent directory",
method: "GET",
path: path.Join("s", serializedPrefixedAccess, "testbucket", "test") + "/",
status: http.StatusOK,
notContains: []string{"..."},
},
{
name: "GET prefix listing with cursor at last object fails",
method: "GET",
Expand Down

0 comments on commit ecd5d8d

Please sign in to comment.