package authz import ( "testing" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/model" "github.com/glebarez/sqlite" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/gorm" ) func newAuthzTestDB(t *testing.T) *gorm.DB { t.Helper() wasMaster := common.IsMasterNode common.IsMasterNode = true t.Cleanup(func() { common.IsMasterNode = wasMaster }) db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) sqlDB, err := db.DB() require.NoError(t, err) sqlDB.SetMaxOpenConns(1) require.NoError(t, db.AutoMigrate(&model.CasbinRule{}, &model.AuthzRole{})) return db } func TestInitSeedsBuiltInRolesAndPoliciesOnce(t *testing.T) { db := newAuthzTestDB(t) require.NoError(t, Init(db)) require.NoError(t, Init(db)) // root is a superuser role and is granted everything implicitly, so only the // admin baseline is written as explicit policy rows. var count int64 require.NoError(t, db.Model(&model.CasbinRule{}).Count(&count).Error) assert.Equal(t, int64(len(PermissionsForRole(BuiltInRoleAdmin))), count) var roles []model.AuthzRole require.NoError(t, db.Order("sort asc").Find(&roles).Error) require.Len(t, roles, 2) assert.Equal(t, BuiltInRoleRoot, roles[0].Key) assert.Equal(t, BuiltInRoleAdmin, roles[1].Key) assert.True(t, Can(1, common.RoleRootUser, ChannelSensitiveWrite)) assert.True(t, Can(2, common.RoleAdminUser, ChannelRead)) assert.True(t, Can(2, common.RoleAdminUser, ChannelOperate)) assert.True(t, Can(2, common.RoleAdminUser, ChannelWrite)) assert.False(t, Can(2, common.RoleAdminUser, ChannelSensitiveWrite)) assert.False(t, Can(3, common.RoleCommonUser, ChannelRead)) } func TestInitOnSlaveOnlyLoadsPolicies(t *testing.T) { wasMaster := common.IsMasterNode common.IsMasterNode = false t.Cleanup(func() { common.IsMasterNode = wasMaster }) db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) require.NoError(t, err) sqlDB, err := db.DB() require.NoError(t, err) sqlDB.SetMaxOpenConns(1) require.NoError(t, db.AutoMigrate(&model.CasbinRule{}, &model.AuthzRole{})) require.NoError(t, Init(db)) var roleCount int64 require.NoError(t, db.Model(&model.AuthzRole{}).Count(&roleCount).Error) assert.Equal(t, int64(0), roleCount) var policyCount int64 require.NoError(t, db.Model(&model.CasbinRule{}).Count(&policyCount).Error) assert.Equal(t, int64(0), policyCount) assert.False(t, Can(2, common.RoleAdminUser, ChannelRead)) } func TestSetUserPermissionsStoresOnlyOverrides(t *testing.T) { db := newAuthzTestDB(t) require.NoError(t, Init(db)) require.NoError(t, SetUserPermissions(42, PermissionsMap{ ResourceChannel: { ActionRead: true, ActionOperate: true, ActionWrite: false, ActionSensitiveWrite: true, ActionSecretView: false, "unknown": true, }, "unknown": { ActionRead: true, }, })) assert.True(t, Can(42, common.RoleAdminUser, ChannelSensitiveWrite)) assert.False(t, Can(42, common.RoleAdminUser, ChannelWrite)) assert.Equal(t, PermissionsMap{ ResourceChannel: { ActionRead: true, ActionOperate: true, ActionWrite: false, ActionSensitiveWrite: true, ActionSecretView: false, }, }, ExplicitUserPermissions(42)) assert.Equal(t, PermissionsMap{ ResourceChannel: { ActionSensitiveWrite: true, ActionWrite: false, }, }, ExplicitUserOverrides(42)) var userPolicyCount int64 require.NoError(t, db.Model(&model.CasbinRule{}).Where("v0 = ?", UserSubject(42)).Count(&userPolicyCount).Error) assert.Equal(t, int64(2), userPolicyCount) require.NoError(t, SetUserPermissions(42, PermissionsMap{ResourceChannel: { ActionRead: true, ActionOperate: true, ActionWrite: true, ActionSensitiveWrite: false, ActionSecretView: false, }})) assert.False(t, Can(42, common.RoleAdminUser, ChannelSensitiveWrite)) assert.Equal(t, PermissionsMap{ ResourceChannel: { ActionRead: true, ActionOperate: true, ActionWrite: true, ActionSensitiveWrite: false, ActionSecretView: false, }, }, ExplicitUserPermissions(42)) assert.Empty(t, ExplicitUserOverrides(42)) } func TestClearUserAuthorizationRemovesOverrides(t *testing.T) { db := newAuthzTestDB(t) require.NoError(t, Init(db)) require.NoError(t, SetUserPermissions(90, PermissionsMap{ResourceChannel: { ActionWrite: false, ActionSensitiveWrite: true, }})) assert.True(t, Can(90, common.RoleAdminUser, ChannelSensitiveWrite)) assert.False(t, Can(90, common.RoleAdminUser, ChannelWrite)) require.NoError(t, ClearUserAuthorization(90)) assert.Empty(t, ExplicitUserOverrides(90)) assert.True(t, Can(90, common.RoleAdminUser, ChannelRead)) assert.True(t, Can(90, common.RoleAdminUser, ChannelWrite)) assert.False(t, Can(90, common.RoleAdminUser, ChannelSensitiveWrite)) assert.False(t, Can(90, common.RoleCommonUser, ChannelRead)) } func TestSetUserPermissionsInTxDoesNotMutateEnforcerBeforeReload(t *testing.T) { db := newAuthzTestDB(t) require.NoError(t, Init(db)) require.NoError(t, db.Transaction(func(tx *gorm.DB) error { return SetUserPermissionsInTx(tx, 42, PermissionsMap{ResourceChannel: { ActionRead: true, ActionOperate: true, ActionWrite: true, ActionSensitiveWrite: true, ActionSecretView: false, }}) })) assert.False(t, Can(42, common.RoleAdminUser, ChannelSensitiveWrite)) require.NoError(t, ReloadPolicy()) assert.True(t, Can(42, common.RoleAdminUser, ChannelSensitiveWrite)) } func TestSetUserPermissionsInTxRollbackLeavesNoPolicy(t *testing.T) { db := newAuthzTestDB(t) require.NoError(t, Init(db)) tx := db.Begin() require.NoError(t, tx.Error) require.NoError(t, SetUserPermissionsInTx(tx, 43, PermissionsMap{ResourceChannel: { ActionSensitiveWrite: true, }})) require.NoError(t, tx.Rollback().Error) require.NoError(t, ReloadPolicy()) assert.False(t, Can(43, common.RoleAdminUser, ChannelSensitiveWrite)) var count int64 require.NoError(t, db.Model(&model.CasbinRule{}).Where("v0 = ?", UserSubject(43)).Count(&count).Error) assert.Equal(t, int64(0), count) } func TestAdapterAddPolicyIsIdempotent(t *testing.T) { db := newAuthzTestDB(t) adapter := newGormAdapter(db) rule := []string{UserSubject(55), ResourceChannel, ActionSensitiveWrite, EffectAllow} require.NoError(t, adapter.AddPolicy("p", "p", rule)) require.NoError(t, adapter.AddPolicy("p", "p", rule)) var count int64 require.NoError(t, db.Model(&model.CasbinRule{}).Where( "ptype = ? AND v0 = ? AND v1 = ? AND v2 = ? AND v3 = ?", "p", UserSubject(55), ResourceChannel, ActionSensitiveWrite, EffectAllow, ).Count(&count).Error) assert.Equal(t, int64(1), count) } func TestCapabilitiesUseCatalogShape(t *testing.T) { db := newAuthzTestDB(t) require.NoError(t, Init(db)) capabilities := Capabilities(7, common.RoleAdminUser) assert.True(t, capabilities[ResourceChannel][ActionRead]) assert.True(t, capabilities[ResourceChannel][ActionOperate]) assert.True(t, capabilities[ResourceChannel][ActionWrite]) assert.False(t, capabilities[ResourceChannel][ActionSensitiveWrite]) assert.False(t, capabilities[ResourceChannel][ActionSecretView]) }