146 lines
7 KiB
Go
146 lines
7 KiB
Go
package access
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/usememos/memos/store"
|
|
)
|
|
|
|
func TestCheckMemoReadMemoLocalAudiences(t *testing.T) {
|
|
owner := &store.User{ID: 1, RowStatus: store.Normal}
|
|
other := &store.User{ID: 2, RowStatus: store.Normal, Role: store.RoleAdmin}
|
|
public := &store.Memo{ID: 1, CreatorID: owner.ID, RowStatus: store.Normal, Visibility: store.Public}
|
|
protected := &store.Memo{ID: 2, CreatorID: owner.ID, RowStatus: store.Normal, Visibility: store.Protected}
|
|
private := &store.Memo{ID: 3, CreatorID: owner.ID, RowStatus: store.Normal, Visibility: store.Private}
|
|
|
|
require.Equal(t, MemoReadDecision{Class: MemoReadClassPublic}, CheckMemoReadContext(MemoReadContext{Memo: public, AllowAnonymous: true, CreatorValid: true, SpaceValid: true}))
|
|
require.Equal(t, MemoReadDenialUnauthenticated, CheckMemoReadContext(MemoReadContext{Memo: public, CreatorValid: true, SpaceValid: true}).Denial)
|
|
require.True(t, CheckMemoReadContext(MemoReadContext{Memo: protected, Viewer: other, CreatorValid: true, SpaceValid: true}).Allowed())
|
|
require.True(t, CheckMemoReadContext(MemoReadContext{Memo: private, Viewer: owner, CreatorValid: true, SpaceValid: true}).Allowed())
|
|
require.Equal(t, MemoReadDenialPermission, CheckMemoReadContext(MemoReadContext{Memo: private, Viewer: other, CreatorValid: true, SpaceValid: true}).Denial)
|
|
|
|
shareID := private.ID
|
|
require.True(t, CheckMemoReadContext(MemoReadContext{Memo: private, SharedMemoID: &shareID, CreatorValid: true, SpaceValid: true}).Allowed())
|
|
}
|
|
|
|
func TestCheckMemoReadCommentDoesNotInheritContext(t *testing.T) {
|
|
const ownerID int32 = 1
|
|
parentUID := "context-memo"
|
|
comment := &store.Memo{
|
|
ID: 2,
|
|
CreatorID: ownerID,
|
|
RowStatus: store.Normal,
|
|
Visibility: store.Public,
|
|
ParentUID: &parentUID,
|
|
}
|
|
|
|
require.Equal(t, MemoReadDecision{Class: MemoReadClassPublic}, CheckMemoReadContext(MemoReadContext{Memo: comment, AllowAnonymous: true, CreatorValid: true, SpaceValid: true}))
|
|
comment.Visibility = store.Private
|
|
require.Equal(t, MemoReadDenialUnauthenticated, CheckMemoReadContext(MemoReadContext{Memo: comment, AllowAnonymous: true, CreatorValid: true, SpaceValid: true}).Denial)
|
|
|
|
shareID := comment.ID
|
|
require.True(t, CheckMemoReadContext(MemoReadContext{Memo: comment, SharedMemoID: &shareID, CreatorValid: true, SpaceValid: true}).Allowed())
|
|
}
|
|
|
|
func TestCheckMemoReadSpaceAudience(t *testing.T) {
|
|
owner := &store.User{ID: 1, RowStatus: store.Normal}
|
|
member := &store.User{ID: 2, RowStatus: store.Normal}
|
|
appAdmin := &store.User{ID: 3, RowStatus: store.Normal, Role: store.RoleAdmin}
|
|
spaceID := int32(7)
|
|
memo := &store.Memo{ID: 10, CreatorID: owner.ID, RowStatus: store.Normal, Visibility: store.SpaceAudience, SpaceID: &spaceID}
|
|
|
|
base := MemoReadContext{Memo: memo, CreatorValid: true, SpaceValid: true}
|
|
require.Equal(t, MemoReadDenialUnauthenticated, CheckMemoReadContext(base).Denial)
|
|
|
|
base.Viewer = owner
|
|
require.Equal(t, MemoReadDenialPermission, CheckMemoReadContext(base).Denial)
|
|
|
|
base.ViewerSpaceMember = true
|
|
require.True(t, CheckMemoReadContext(base).Allowed())
|
|
|
|
base.Viewer = member
|
|
base.ViewerSpaceMember = true
|
|
require.True(t, CheckMemoReadContext(base).Allowed())
|
|
|
|
base.Viewer = appAdmin
|
|
base.ViewerSpaceMember = false
|
|
require.Equal(t, MemoReadDenialPermission, CheckMemoReadContext(base).Denial)
|
|
|
|
shareID := memo.ID
|
|
base.Viewer = nil
|
|
base.SharedMemoID = &shareID
|
|
require.Equal(t, MemoReadDenialUnauthenticated, CheckMemoReadContext(base).Denial)
|
|
|
|
memo.SpaceID = nil
|
|
base.Viewer = member
|
|
base.ViewerSpaceMember = true
|
|
require.Equal(t, MemoReadDenialNotFound, CheckMemoReadContext(base).Denial)
|
|
}
|
|
|
|
func TestCheckMemoReadInvalidStateFailsClosed(t *testing.T) {
|
|
owner := &store.User{ID: 1, RowStatus: store.Normal}
|
|
spaceID := int32(7)
|
|
memo := &store.Memo{ID: 10, CreatorID: owner.ID, RowStatus: store.Normal, Visibility: store.Public, SpaceID: &spaceID}
|
|
|
|
require.Equal(t, MemoReadDecision{Class: MemoReadClassPublic}, CheckMemoReadContext(MemoReadContext{
|
|
Memo: memo, Viewer: owner, AllowAnonymous: true, CreatorValid: true, SpaceValid: false,
|
|
}), "a dangling placement does not override a non-Space audience")
|
|
memo.Visibility = store.Protected
|
|
require.True(t, CheckMemoReadContext(MemoReadContext{
|
|
Memo: memo, Viewer: owner, CreatorValid: true, SpaceValid: false,
|
|
}).Allowed(), "PROTECTED remains readable through its own audience")
|
|
memo.Visibility = store.Private
|
|
require.True(t, CheckMemoReadContext(MemoReadContext{
|
|
Memo: memo, Viewer: owner, CreatorValid: true, SpaceValid: false,
|
|
}).Allowed(), "PRIVATE remains readable by its active author")
|
|
memo.Visibility = store.SpaceAudience
|
|
require.Equal(t, MemoReadDenialNotFound, CheckMemoReadContext(MemoReadContext{
|
|
Memo: memo, Viewer: owner, CreatorValid: true, SpaceValid: false, ViewerSpaceMember: true,
|
|
}).Denial, "SPACE depends on a valid assigned Space")
|
|
|
|
memo.SpaceID = nil
|
|
memo.Visibility = store.Visibility("FUTURE_AUDIENCE")
|
|
shareID := memo.ID
|
|
require.Equal(t, MemoReadDenialNotFound, CheckMemoReadContext(MemoReadContext{
|
|
Memo: memo, Viewer: owner, AllowAnonymous: true, SharedMemoID: &shareID, CreatorValid: true, SpaceValid: true,
|
|
}).Denial)
|
|
|
|
memo.Visibility = store.Public
|
|
memo.RowStatus = store.Archived
|
|
require.True(t, CheckMemoReadContext(MemoReadContext{Memo: memo, Viewer: owner, CreatorValid: true, SpaceValid: true}).Allowed())
|
|
require.Equal(t, MemoReadDenialNotFound, CheckMemoReadContext(MemoReadContext{Memo: memo, AllowAnonymous: true, CreatorValid: true, SpaceValid: true}).Denial)
|
|
memo.Visibility = store.SpaceAudience
|
|
memo.SpaceID = &spaceID
|
|
require.Equal(t, MemoReadDenialPermission, CheckMemoReadContext(MemoReadContext{
|
|
Memo: memo, Viewer: owner, CreatorValid: true, SpaceValid: true,
|
|
}).Denial, "an archived SPACE memo still requires active membership")
|
|
require.True(t, CheckMemoReadContext(MemoReadContext{
|
|
Memo: memo, Viewer: owner, CreatorValid: true, SpaceValid: true, ViewerSpaceMember: true,
|
|
}).Allowed())
|
|
|
|
memo.RowStatus = store.RowStatus("UNKNOWN")
|
|
require.Equal(t, MemoReadDenialNotFound, CheckMemoReadContext(MemoReadContext{Memo: memo, Viewer: owner, CreatorValid: true, SpaceValid: true}).Denial)
|
|
|
|
memo.RowStatus = store.Normal
|
|
memo.Visibility = store.Public
|
|
memo.SpaceID = nil
|
|
require.Equal(t, MemoReadDenialNotFound, CheckMemoReadContext(MemoReadContext{
|
|
Memo: memo, Viewer: owner, AllowAnonymous: true, SpaceValid: true,
|
|
}).Denial, "a missing or invalid creator fails closed before audience evaluation")
|
|
}
|
|
|
|
func TestCheckMemoReadAssignedPublicHasNoMembershipGate(t *testing.T) {
|
|
owner := &store.User{ID: 1}
|
|
other := &store.User{ID: 2, RowStatus: store.Normal}
|
|
spaceID := int32(7)
|
|
assigned := &store.Memo{ID: 10, CreatorID: owner.ID, RowStatus: store.Normal, Visibility: store.Public, SpaceID: &spaceID}
|
|
|
|
require.Equal(t, MemoReadDecision{Class: MemoReadClassPublic}, CheckMemoReadContext(MemoReadContext{
|
|
Memo: assigned, AllowAnonymous: true, CreatorValid: true, SpaceValid: true,
|
|
}))
|
|
require.True(t, CheckMemoReadContext(MemoReadContext{
|
|
Memo: assigned, Viewer: other, CreatorValid: true, SpaceValid: true,
|
|
}).Allowed(), "placement does not add a read gate to PUBLIC memos")
|
|
}
|