2026-07-25 14:47:21 +00:00
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"errors"
|
|
|
|
|
"testing"
|
|
|
|
|
|
2026-08-28 06:54:32 +02:00
|
|
|
"github.com/J3vb/OwnCord/Server/db"
|
|
|
|
|
"github.com/J3vb/OwnCord/Server/permissions"
|
2026-07-25 14:47:21 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// PermissionService.RequireChannelAccess had no coverage. It is the single
|
|
|
|
|
// authorization gate that splits DM channels (membership check) from regular
|
|
|
|
|
// channels (role + override check); a wrong branch here either leaks a DM to a
|
|
|
|
|
// non-participant or locks legitimate users out of a channel.
|
|
|
|
|
|
|
|
|
|
func TestRequireChannelAccess_RegularChannel(t *testing.T) {
|
|
|
|
|
svc, database := newTestPermService(t)
|
|
|
|
|
seedChannel(t, database, &db.Channel{ID: 10, Name: "general", Type: "text"})
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
// The seeded Member role has SendMessages but not ManageChannels.
|
|
|
|
|
if err := svc.RequireChannelAccess(ctx, 1, "text", 10, permissions.SendMessages); err != nil {
|
|
|
|
|
t.Errorf("RequireChannelAccess with a granted permission = %v, want nil", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := svc.RequireChannelAccess(ctx, 1, "text", 10, permissions.ManageChannels)
|
|
|
|
|
if !errors.Is(err, permissions.ErrPermissionDenied) {
|
|
|
|
|
t.Errorf("RequireChannelAccess with a missing permission = %v, want ErrPermissionDenied", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestRequireChannelAccess_DMParticipant(t *testing.T) {
|
|
|
|
|
svc, database := newTestPermService(t)
|
|
|
|
|
seedChannel(t, database, &db.Channel{ID: 20, Name: "dm", Type: "dm"})
|
|
|
|
|
seedDMParticipant(t, database, 20, 1)
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
// For a DM the permission argument is irrelevant — membership decides.
|
|
|
|
|
// Pass a permission the Member role does not hold to prove that.
|
|
|
|
|
if err := svc.RequireChannelAccess(ctx, 1, "dm", 20, permissions.ManageChannels); err != nil {
|
|
|
|
|
t.Errorf("RequireChannelAccess for a DM participant = %v, want nil", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestRequireChannelAccess_DMNonParticipant(t *testing.T) {
|
|
|
|
|
svc, database := newTestPermService(t)
|
|
|
|
|
seedChannel(t, database, &db.Channel{ID: 20, Name: "dm", Type: "dm"})
|
|
|
|
|
seedUserRole(t, database, 2, permissions.MemberRoleID)
|
|
|
|
|
seedDMParticipant(t, database, 20, 1)
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
// User 2 is not in the DM. Role permissions must not open it.
|
|
|
|
|
err := svc.RequireChannelAccess(ctx, 2, "dm", 20, permissions.ReadMessages)
|
|
|
|
|
if !errors.Is(err, permissions.ErrNotDMParticipant) {
|
|
|
|
|
t.Errorf("RequireChannelAccess for a non-participant = %v, want ErrNotDMParticipant", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestRequireChannelAccess_DMWithNoParticipantsDenies(t *testing.T) {
|
|
|
|
|
svc, database := newTestPermService(t)
|
|
|
|
|
seedChannel(t, database, &db.Channel{ID: 21, Name: "empty-dm", Type: "dm"})
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
err := svc.RequireChannelAccess(ctx, 1, "dm", 21, permissions.ReadMessages)
|
|
|
|
|
if !errors.Is(err, permissions.ErrNotDMParticipant) {
|
|
|
|
|
t.Errorf("RequireChannelAccess on a DM with no participants = %v, want ErrNotDMParticipant", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestRequireChannelAccess_UnknownUserDenied(t *testing.T) {
|
|
|
|
|
svc, database := newTestPermService(t)
|
|
|
|
|
seedChannel(t, database, &db.Channel{ID: 10, Name: "general", Type: "text"})
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
// A user with no row has no role, so getOrPopulate yields nothing and the
|
|
|
|
|
// check must fail closed.
|
|
|
|
|
err := svc.RequireChannelAccess(ctx, 9999, "text", 10, permissions.ReadMessages)
|
|
|
|
|
if !errors.Is(err, permissions.ErrPermissionDenied) {
|
|
|
|
|
t.Errorf("RequireChannelAccess for an unknown user = %v, want ErrPermissionDenied", err)
|
|
|
|
|
}
|
|
|
|
|
}
|