feat(share): owner-private note on shares (#3570)
Share owners can now attach a free-text note/alias to a share for identifying it in My Shares. The note lives in the existing ShareProps JSON column (no schema migration), is settable on create and edit via the share dialog, renders as a secondary line on share cards, and is only serialized for the share owner - BuildShare omits it for visitors and anonymous viewers, covered by a new redaction test. Authored By: TDvorak <info@tdvorak.dev> Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>pull/3582/head
parent
67871b8e89
commit
9bf2e14b5f
@ -0,0 +1,68 @@
|
||||
package explorer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/cloudreve/Cloudreve/v4/application/dependency"
|
||||
"github.com/cloudreve/Cloudreve/v4/ent"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory/types"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/hashid"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/setting"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// stubSettingProvider satisfies the SettingProvider dependency; only
|
||||
// ExposeUserEmail is exercised by BuildUserRedacted.
|
||||
type stubSettingProvider struct {
|
||||
setting.Provider
|
||||
}
|
||||
|
||||
func (stubSettingProvider) ExposeUserEmail(context.Context) bool { return false }
|
||||
|
||||
// TestBuildShareNoteVisibility ensures the owner-defined share note is only
|
||||
// exposed to the share owner, never to visitors (upstream #3570).
|
||||
func TestBuildShareNoteVisibility(t *testing.T) {
|
||||
dep := dependency.NewDependency(dependency.WithSettingProvider(stubSettingProvider{}))
|
||||
ctx := context.WithValue(context.Background(), dependency.DepCtx{}, dep)
|
||||
|
||||
hasher, err := hashid.New("test-salt")
|
||||
require.NoError(t, err)
|
||||
base := &url.URL{Scheme: "https", Host: "example.com"}
|
||||
|
||||
owner := &ent.User{ID: 1, Email: "owner@example.com", Settings: &types.UserSetting{}}
|
||||
visitor := &ent.User{ID: 2, Email: "visitor@example.com", Settings: &types.UserSetting{}}
|
||||
anonymous := &ent.User{ID: 0, Settings: &types.UserSetting{}}
|
||||
|
||||
newShare := func() *ent.Share {
|
||||
return &ent.Share{
|
||||
ID: 1,
|
||||
Props: &types.ShareProps{Note: "tax receipts Q3"},
|
||||
}
|
||||
}
|
||||
|
||||
t.Run("owner sees note", func(t *testing.T) {
|
||||
res := BuildShare(ctx, newShare(), base, hasher, owner, owner,
|
||||
"receipts.zip", types.FileTypeFile, true, false)
|
||||
require.Equal(t, "tax receipts Q3", res.Note)
|
||||
})
|
||||
|
||||
t.Run("unlocked visitor does not see note", func(t *testing.T) {
|
||||
res := BuildShare(ctx, newShare(), base, hasher, visitor, owner,
|
||||
"receipts.zip", types.FileTypeFile, true, false)
|
||||
require.Empty(t, res.Note)
|
||||
})
|
||||
|
||||
t.Run("anonymous visitor does not see note", func(t *testing.T) {
|
||||
res := BuildShare(ctx, newShare(), base, hasher, anonymous, owner,
|
||||
"receipts.zip", types.FileTypeFile, true, false)
|
||||
require.Empty(t, res.Note)
|
||||
})
|
||||
|
||||
t.Run("locked share does not leak note to visitor", func(t *testing.T) {
|
||||
res := BuildShare(ctx, newShare(), base, hasher, visitor, owner,
|
||||
"receipts.zip", types.FileTypeFile, false, false)
|
||||
require.Empty(t, res.Note)
|
||||
})
|
||||
}
|
||||
Loading…
Reference in new issue