adapter_test.go 6.8 KB
package pgadapter

import (
	"os"
	"testing"

	"github.com/casbin/casbin/v2"
	"github.com/casbin/casbin/v2/util"
	"github.com/go-pg/pg/v9"
	"github.com/stretchr/testify/suite"
)

// AdapterTestSuite tests all functionalities of Adapter
type AdapterTestSuite struct {
	suite.Suite
	e *casbin.Enforcer
	a *Adapter
}

func (s *AdapterTestSuite) assertPolicy(expected, res [][]string) {
	s.T().Helper()
	s.Assert().True(util.Array2DEquals(expected, res), "Policy Got: %v, supposed to be %v", res, expected)
}

func (s *AdapterTestSuite) dropCasbinDB() {
	opts, err := pg.ParseURL(os.Getenv("PG_CONN"))
	s.Require().NoError(err)
	db := pg.Connect(opts)
	defer db.Close()
	db.Exec("DROP DATABASE casbin")
}

func (s *AdapterTestSuite) SetupTest() {
	s.dropCasbinDB()

	var err error
	s.a, err = NewAdapter(os.Getenv("PG_CONN"))
	s.Require().NoError(err)

	// This is a trick to save the current policy to the DB.
	// We can't call e.SavePolicy() because the adapter in the enforcer is still the file adapter.
	// The current policy means the policy in the Casbin enforcer (aka in memory).
	e, err := casbin.NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv")
	s.Require().NoError(err)
	err = s.a.SavePolicy(e.GetModel())
	s.Require().NoError(err)

	s.e, err = casbin.NewEnforcer("examples/rbac_model.conf", s.a)
	s.Require().NoError(err)
}

func (s *AdapterTestSuite) TearDownTest() {
	err := s.a.Close()
	s.Require().NoError(err)
}

func (s *AdapterTestSuite) TestSaveLoad() {
	s.Assert().False(s.e.IsFiltered())
	s.assertPolicy(
		[][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}},
		s.e.GetPolicy(),
	)
}

func (s *AdapterTestSuite) TestAutoSave() {
	// AutoSave is enabled by default.
	// Now we disable it.
	s.e.EnableAutoSave(false)

	// Because AutoSave is disabled, the policy change only affects the policy in Casbin enforcer,
	// it doesn't affect the policy in the storage.
	_, err := s.e.AddPolicy("alice", "data1", "write")
	s.Require().NoError(err)
	// Reload the policy from the storage to see the effect.
	err = s.e.LoadPolicy()
	s.Require().NoError(err)
	// This is still the original policy.
	s.assertPolicy(
		[][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}},
		s.e.GetPolicy(),
	)

	// Now we enable the AutoSave.
	s.e.EnableAutoSave(true)

	// Because AutoSave is enabled, the policy change not only affects the policy in Casbin enforcer,
	// but also affects the policy in the storage.
	_, err = s.e.AddPolicy("alice", "data1", "write")
	s.Require().NoError(err)
	// Reload the policy from the storage to see the effect.
	err = s.e.LoadPolicy()
	s.Require().NoError(err)
	// The policy has a new rule: {"alice", "data1", "write"}.
	s.assertPolicy(
		[][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}, {"alice", "data1", "write"}},
		s.e.GetPolicy(),
	)

	// Aditional AddPolicy have no effect
	_, err = s.e.AddPolicy("alice", "data1", "write")
	s.Require().NoError(err)
	err = s.e.LoadPolicy()
	s.Require().NoError(err)
	s.assertPolicy(
		[][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}, {"alice", "data1", "write"}},
		s.e.GetPolicy(),
	)
	s.Require().NoError(err)
}

func (s *AdapterTestSuite) TestConstructorOptions() {
	opts, err := pg.ParseURL(os.Getenv("PG_CONN"))
	s.Require().NoError(err)

	a, err := NewAdapter(opts)
	s.Require().NoError(err)
	defer a.Close()

	s.e, err = casbin.NewEnforcer("examples/rbac_model.conf", a)
	s.Require().NoError(err)

	s.assertPolicy(
		[][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}},
		s.e.GetPolicy(),
	)
}

func (s *AdapterTestSuite) TestRemovePolicy() {
	_, err := s.e.RemovePolicy("alice", "data1", "read")
	s.Require().NoError(err)

	s.assertPolicy(
		[][]string{{"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}},
		s.e.GetPolicy(),
	)

	err = s.e.LoadPolicy()
	s.Require().NoError(err)

	s.assertPolicy(
		[][]string{{"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}},
		s.e.GetPolicy(),
	)
}

func (s *AdapterTestSuite) TestRemoveFilteredPolicy() {
	_, err := s.e.RemoveFilteredPolicy(0, "", "data2")
	s.Require().NoError(err)

	s.assertPolicy(
		[][]string{{"alice", "data1", "read"}},
		s.e.GetPolicy(),
	)

	err = s.e.LoadPolicy()
	s.Require().NoError(err)

	s.assertPolicy(
		[][]string{{"alice", "data1", "read"}},
		s.e.GetPolicy(),
	)
}

func (s *AdapterTestSuite) TestLoadFilteredPolicy() {
	e, err := casbin.NewEnforcer("examples/rbac_model.conf", s.a)
	s.Require().NoError(err)

	err = e.LoadFilteredPolicy(&Filter{
		P: []string{"", "", "read"},
	})
	s.Require().NoError(err)
	s.Assert().True(e.IsFiltered())
	s.assertPolicy(
		[][]string{{"alice", "data1", "read"}, {"data2_admin", "data2", "read"}},
		e.GetPolicy(),
	)
}

func (s *AdapterTestSuite) TestLoadFilteredGroupingPolicy() {
	e, err := casbin.NewEnforcer("examples/rbac_model.conf", s.a)
	s.Require().NoError(err)

	err = e.LoadFilteredPolicy(&Filter{
		G: []string{"bob"},
	})
	s.Require().NoError(err)
	s.Assert().True(e.IsFiltered())
	s.assertPolicy([][]string{}, e.GetGroupingPolicy())

	e, err = casbin.NewEnforcer("examples/rbac_model.conf", s.a)
	s.Require().NoError(err)

	err = e.LoadFilteredPolicy(&Filter{
		G: []string{"alice"},
	})
	s.Require().NoError(err)
	s.Assert().True(e.IsFiltered())
	s.assertPolicy([][]string{{"alice", "data2_admin"}}, e.GetGroupingPolicy())
}

func (s *AdapterTestSuite) TestLoadFilteredPolicyNilFilter() {
	e, err := casbin.NewEnforcer("examples/rbac_model.conf", s.a)
	s.Require().NoError(err)

	err = e.LoadFilteredPolicy(nil)
	s.Require().NoError(err)

	s.Assert().False(e.IsFiltered())
	s.assertPolicy(
		[][]string{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"data2_admin", "data2", "read"}, {"data2_admin", "data2", "write"}},
		s.e.GetPolicy(),
	)
}

func (s *AdapterTestSuite) TestSavePolicyClearPreviousData() {
	s.e.EnableAutoSave(false)
	policies := s.e.GetPolicy()
	// clone slice to avoid shufling elements
	policies = append(policies[:0:0], policies...)
	for _, p := range policies {
		_, err := s.e.RemovePolicy(p)
		s.Require().NoError(err)
	}
	policies = s.e.GetGroupingPolicy()
	policies = append(policies[:0:0], policies...)
	for _, p := range policies {
		_, err := s.e.RemoveGroupingPolicy(p)
		s.Require().NoError(err)
	}
	s.assertPolicy(
		[][]string{},
		s.e.GetPolicy(),
	)

	err := s.e.SavePolicy()
	s.Require().NoError(err)

	err = s.e.LoadPolicy()
	s.Require().NoError(err)
	s.assertPolicy(
		[][]string{},
		s.e.GetPolicy(),
	)
}

func TestAdapterTestSuite(t *testing.T) {
	suite.Run(t, new(AdapterTestSuite))
}