943 lines
25 KiB
Go
943 lines
25 KiB
Go
package tenant_test
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/influxdata/influxdb/v2"
|
|
"github.com/influxdata/influxdb/v2/kit/platform"
|
|
"github.com/influxdata/influxdb/v2/mock"
|
|
"github.com/influxdata/influxdb/v2/tenant"
|
|
itesting "github.com/influxdata/influxdb/v2/testing"
|
|
)
|
|
|
|
type tenantFields struct {
|
|
OrgIDGenerator platform.IDGenerator
|
|
BucketIDGenerator platform.IDGenerator
|
|
Users []*influxdb.User
|
|
Passwords []string // passwords are indexed against the Users field
|
|
UserResourceMappings []*influxdb.UserResourceMapping
|
|
Organizations []*influxdb.Organization
|
|
Buckets []*influxdb.Bucket
|
|
}
|
|
|
|
// TestBoltTenantService tests the tenant service functions.
|
|
// These tests stress the relation between the services embedded by the TenantService.
|
|
// The individual functionality of services is tested elsewhere.
|
|
func TestBoltTenantService(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
fn func(t *testing.T, init func(*testing.T, tenantFields) (*tenant.Service, func()))
|
|
}{
|
|
{
|
|
name: "Create",
|
|
fn: Create,
|
|
},
|
|
{
|
|
name: "Delete",
|
|
fn: Delete,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tt.fn(t, initBoltTenantService)
|
|
})
|
|
}
|
|
}
|
|
|
|
type bucketsByName []*influxdb.Bucket
|
|
|
|
func (b bucketsByName) Len() int {
|
|
return len(b)
|
|
}
|
|
|
|
func (b bucketsByName) Less(i, j int) bool {
|
|
return strings.Compare(b[i].Name, b[j].Name) < 0
|
|
}
|
|
|
|
func (b bucketsByName) Swap(i, j int) {
|
|
b[i], b[j] = b[j], b[i]
|
|
}
|
|
|
|
type urmByResourceID []*influxdb.UserResourceMapping
|
|
|
|
func (u urmByResourceID) Len() int {
|
|
return len(u)
|
|
}
|
|
|
|
func (u urmByResourceID) Less(i, j int) bool {
|
|
return u[i].ResourceID < u[j].ResourceID
|
|
}
|
|
|
|
func (u urmByResourceID) Swap(i, j int) {
|
|
u[i], u[j] = u[j], u[i]
|
|
}
|
|
|
|
type urmByUserID []*influxdb.UserResourceMapping
|
|
|
|
func (u urmByUserID) Len() int {
|
|
return len(u)
|
|
}
|
|
|
|
func (u urmByUserID) Less(i, j int) bool {
|
|
return u[i].UserID < u[j].UserID
|
|
}
|
|
|
|
func (u urmByUserID) Swap(i, j int) {
|
|
u[i], u[j] = u[j], u[i]
|
|
}
|
|
|
|
// Create tests various cases of creation for the services in the TenantService.
|
|
// For example, when you create a user, do you create system buckets? How are URMs organized?
|
|
func Create(t *testing.T, init func(*testing.T, tenantFields) (*tenant.Service, func())) {
|
|
t.Helper()
|
|
|
|
// Blank fields, we are testing creation.
|
|
fields := func() tenantFields {
|
|
return tenantFields{
|
|
OrgIDGenerator: mock.NewIncrementingIDGenerator(1),
|
|
BucketIDGenerator: mock.NewIncrementingIDGenerator(1),
|
|
}
|
|
}
|
|
|
|
// NOTE(affo)(*kv.Service): tests that contain s.CreateOrganization() generate error in logs:
|
|
// Failed to make user owner of organization: {"error": "could not find authorizer on context when adding user to resource type orgs"}.
|
|
// This happens because kv requires an authorization to be in context.
|
|
// This is a bad dependency pattern (store -> auth) and should not be there.
|
|
// Anyways this does not prevent the org to be created. If you add the urm manually you'll obtain the same result.
|
|
|
|
// NOTE(affo)(*kv.Service): it also creates urms for the non existing user found in context.
|
|
t.Run("creating an org creates system buckets", func(t *testing.T) {
|
|
data := fields()
|
|
s, done := init(t, data)
|
|
defer done()
|
|
ctx := context.Background()
|
|
|
|
o := &influxdb.Organization{
|
|
// ID(1)
|
|
Name: "org1",
|
|
}
|
|
if err := s.CreateOrganization(ctx, o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Check existence
|
|
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if norgs != 1 {
|
|
t.Errorf("expected 1 org, got: %v", orgs)
|
|
}
|
|
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nusrs > 0 {
|
|
t.Errorf("expected no user, got: %v", usrs)
|
|
}
|
|
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nurms > 0 {
|
|
t.Errorf("expected no urm, got: %+v", urms)
|
|
}
|
|
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nbs != 2 {
|
|
t.Errorf("expected 2 buckets, got: %v", bs)
|
|
}
|
|
sort.Sort(bucketsByName(bs))
|
|
if name := bs[0].Name; name != "_monitoring" {
|
|
t.Errorf("unexpected nam for bucket: %s", name)
|
|
}
|
|
if name := bs[1].Name; name != "_tasks" {
|
|
t.Errorf("unexpected nam for bucket: %s", name)
|
|
}
|
|
})
|
|
|
|
// NOTE(affo)(*kv.Service): nope, it does create system buckets with invalid OrgIDs.
|
|
t.Run("creating user creates only the user", func(t *testing.T) {
|
|
data := fields()
|
|
s, done := init(t, data)
|
|
defer done()
|
|
ctx := context.Background()
|
|
|
|
// Number of buckets prior to user creation.
|
|
// This is because, for now, system buckets always get returned for compatibility with the old system.
|
|
_, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
u := &influxdb.User{
|
|
ID: 1,
|
|
Name: "user1",
|
|
}
|
|
if err := s.CreateUser(ctx, u); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Check existence
|
|
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if norgs != 0 {
|
|
t.Errorf("expected no org, got: %v", orgs)
|
|
}
|
|
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nusrs != 1 {
|
|
t.Errorf("expected 1 user, got: %v", usrs)
|
|
}
|
|
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nurms > 0 {
|
|
t.Errorf("expected no urm, got: %v", urms)
|
|
}
|
|
bs, nnbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Compare new number of buckets with the one prior to user creation.
|
|
if nnbs != nbs {
|
|
t.Errorf("expected no bucket created, got: %+v", bs)
|
|
}
|
|
})
|
|
|
|
// NOTE(affo)(*kv.Service): nope, it does create a useless URM, no existence check.
|
|
// Apparently, system buckets are created too :thinking.
|
|
t.Run("creating urm pointing to non existing user fails", func(t *testing.T) {
|
|
data := fields()
|
|
s, done := init(t, data)
|
|
defer done()
|
|
ctx := context.Background()
|
|
|
|
// First create an org and a user.
|
|
u := &influxdb.User{
|
|
ID: 1,
|
|
Name: "user1",
|
|
}
|
|
if err := s.CreateUser(ctx, u); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
o := &influxdb.Organization{
|
|
// ID(1)
|
|
Name: "org1",
|
|
}
|
|
if err := s.CreateOrganization(ctx, o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
checkInvariance := func(nurms int) {
|
|
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if norgs != 1 {
|
|
t.Errorf("expected 1 org, got: %v", orgs)
|
|
}
|
|
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nusrs != 1 {
|
|
t.Errorf("expected 1 user, got: %v", usrs)
|
|
}
|
|
urms, nnurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nnurms != nurms {
|
|
t.Errorf("expected %d urms got %d: %+v", nurms, nnurms, urms)
|
|
}
|
|
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nbs != 2 {
|
|
t.Errorf("expected 2 buckets, got: %v", bs)
|
|
}
|
|
}
|
|
|
|
checkInvariance(0)
|
|
|
|
// Wrong userID.
|
|
urm := &influxdb.UserResourceMapping{
|
|
UserID: 2,
|
|
UserType: influxdb.Owner,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.OrgsResourceType,
|
|
ResourceID: 1,
|
|
}
|
|
if err := s.CreateUserResourceMapping(ctx, urm); err == nil {
|
|
t.Errorf("expected error got none")
|
|
}
|
|
|
|
checkInvariance(0)
|
|
|
|
// Wrong orgID. The URM gets created successfully.
|
|
urm = &influxdb.UserResourceMapping{
|
|
UserID: 1,
|
|
UserType: influxdb.Owner,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.OrgsResourceType,
|
|
ResourceID: 2,
|
|
}
|
|
if err := s.CreateUserResourceMapping(ctx, urm); err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
|
|
checkInvariance(1)
|
|
})
|
|
|
|
// NOTE(affo)(*kv.Service): errors on bucket creation.
|
|
// But, apparently, system buckets are created too :thinking.
|
|
t.Run("should not be possible to create bucket without org", func(t *testing.T) {
|
|
data := fields()
|
|
s, done := init(t, data)
|
|
defer done()
|
|
ctx := context.Background()
|
|
|
|
// Number of buckets prior to bucket creation.
|
|
// This is because, for now, system buckets always get returned for compatibility with the old system.
|
|
_, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
b := &influxdb.Bucket{
|
|
// ID(1)
|
|
OrgID: 1,
|
|
Name: "bucket1",
|
|
}
|
|
if err := s.CreateBucket(ctx, b); err == nil {
|
|
t.Errorf("expected error got none")
|
|
}
|
|
|
|
// Check existence
|
|
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if norgs != 0 {
|
|
t.Errorf("expected no org, got: %v", orgs)
|
|
}
|
|
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nusrs != 0 {
|
|
t.Errorf("expected no user, got: %v", usrs)
|
|
}
|
|
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nurms > 0 {
|
|
t.Errorf("expected no urm, got: %v", urms)
|
|
}
|
|
bs, nnbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Compare new number of buckets with the one prior to bucket creation.
|
|
if nnbs != nbs {
|
|
t.Errorf("expected bucket created, got: %+v", bs)
|
|
}
|
|
})
|
|
|
|
t.Run("making user part of org creates mapping to org only", func(t *testing.T) {
|
|
for _, userType := range []influxdb.UserType{influxdb.Owner, influxdb.Member} {
|
|
t.Run(string(userType), func(t *testing.T) {
|
|
data := fields()
|
|
s, done := init(t, data)
|
|
defer done()
|
|
ctx := context.Background()
|
|
|
|
u := &influxdb.User{
|
|
ID: 1,
|
|
Name: "user1",
|
|
}
|
|
if err := s.CreateUser(ctx, u); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
o := &influxdb.Organization{
|
|
// ID(1)
|
|
Name: "org1",
|
|
}
|
|
if err := s.CreateOrganization(ctx, o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
urm := &influxdb.UserResourceMapping{
|
|
UserID: u.ID,
|
|
UserType: userType,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.OrgsResourceType,
|
|
ResourceID: o.ID,
|
|
}
|
|
if err := s.CreateUserResourceMapping(ctx, urm); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Check existence
|
|
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if norgs != 1 {
|
|
t.Errorf("expected 1 org, got: %v", orgs)
|
|
}
|
|
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nusrs != 1 {
|
|
t.Errorf("expected 1 user, got: %v", usrs)
|
|
}
|
|
|
|
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nbs != 2 {
|
|
t.Errorf("expected 2 buckets, got: %v", bs)
|
|
}
|
|
sort.Sort(bucketsByName(bs))
|
|
if name := bs[0].Name; name != "_monitoring" {
|
|
t.Errorf("unexpected name for bucket: %s", name)
|
|
}
|
|
if name := bs[1].Name; name != "_tasks" {
|
|
t.Errorf("unexpected name for bucket: %v", name)
|
|
}
|
|
|
|
urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := []*influxdb.UserResourceMapping{
|
|
{
|
|
UserID: u.ID,
|
|
UserType: userType,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.OrgsResourceType,
|
|
ResourceID: o.ID,
|
|
},
|
|
}
|
|
sort.Sort(urmByResourceID(want))
|
|
sort.Sort(urmByResourceID(urms))
|
|
if diff := cmp.Diff(want, urms); diff != "" {
|
|
t.Errorf("unexpected urms -want/+got:\n\t%s", diff)
|
|
}
|
|
|
|
// Now add a new bucket and check the URMs.
|
|
b := &influxdb.Bucket{
|
|
// ID(1)
|
|
OrgID: o.ID,
|
|
Name: "bucket1",
|
|
}
|
|
if err := s.CreateBucket(ctx, b); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
urms, _, err = s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sort.Sort(urmByResourceID(urms))
|
|
if diff := cmp.Diff(want, urms); diff != "" {
|
|
t.Errorf("unexpected urms -want/+got:\n\t%s", diff)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
// Delete tests various cases of deletion for the services in the TenantService.
|
|
// An example: if you delete a bucket the corresponding user resource mapping is not present.
|
|
func Delete(t *testing.T, init func(*testing.T, tenantFields) (*tenant.Service, func())) {
|
|
t.Helper()
|
|
|
|
fields := func() tenantFields {
|
|
return tenantFields{
|
|
OrgIDGenerator: mock.NewIncrementingIDGenerator(1),
|
|
// URM are userID + resourceID (they do not include resource type)
|
|
// so same IDs across different resources leads to collisions
|
|
// therefore, we need to start bucket IDs at higher offset for
|
|
// test.
|
|
BucketIDGenerator: mock.NewIncrementingIDGenerator(10),
|
|
Users: []*influxdb.User{
|
|
{
|
|
ID: 1,
|
|
Name: "user1",
|
|
},
|
|
{
|
|
ID: 2,
|
|
Name: "user2",
|
|
},
|
|
},
|
|
Passwords: []string{"password1", "password2"},
|
|
Organizations: []*influxdb.Organization{
|
|
{
|
|
// ID(1)
|
|
Name: "org1",
|
|
},
|
|
{
|
|
// ID(2)
|
|
Name: "org2",
|
|
},
|
|
},
|
|
// 2 organizations create 2 system buckets each
|
|
// so start at 14
|
|
Buckets: []*influxdb.Bucket{
|
|
{
|
|
// ID(14)
|
|
OrgID: 1,
|
|
Name: "bucket1",
|
|
},
|
|
{
|
|
// ID(15)
|
|
OrgID: 2,
|
|
Name: "bucket2",
|
|
},
|
|
},
|
|
UserResourceMappings: []*influxdb.UserResourceMapping{
|
|
// NOTE(affo): bucket URMs should not be here, create them only for deletion purposes.
|
|
// user 1 owns org1 (and so bucket1)
|
|
{
|
|
UserID: 1,
|
|
UserType: influxdb.Owner,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.OrgsResourceType,
|
|
ResourceID: 1,
|
|
},
|
|
{
|
|
UserID: 1,
|
|
UserType: influxdb.Owner,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
ResourceID: 14,
|
|
},
|
|
// user 1 is member of org2 (and so bucket2)
|
|
{
|
|
UserID: 1,
|
|
UserType: influxdb.Member,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.OrgsResourceType,
|
|
ResourceID: 2,
|
|
},
|
|
{
|
|
UserID: 1,
|
|
UserType: influxdb.Member,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
ResourceID: 15,
|
|
},
|
|
// user 2 owns org2 (and so bucket2)
|
|
{
|
|
UserID: 2,
|
|
UserType: influxdb.Owner,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.OrgsResourceType,
|
|
ResourceID: 2,
|
|
},
|
|
{
|
|
UserID: 2,
|
|
UserType: influxdb.Owner,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
ResourceID: 15,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
t.Run("deleting bucket deletes urm", func(t *testing.T) {
|
|
data := fields()
|
|
s, done := init(t, data)
|
|
defer done()
|
|
ctx := context.Background()
|
|
|
|
f := influxdb.UserResourceMappingFilter{
|
|
ResourceID: data.Buckets[0].ID,
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
}
|
|
urms, n, err := s.FindUserResourceMappings(ctx, f)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n != 1 {
|
|
t.Fatalf("expected 1 urm, got: %v", urms)
|
|
}
|
|
if err := s.DeleteBucket(ctx, data.Buckets[0].ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
f = influxdb.UserResourceMappingFilter{
|
|
ResourceID: data.Buckets[0].ID,
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
}
|
|
urms, n, err = s.FindUserResourceMappings(ctx, f)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n > 0 {
|
|
t.Fatalf("expected no urm, got: %v", urms)
|
|
}
|
|
})
|
|
|
|
// NOTE(affo): those resources could not be dangling (URM could be inferred from an user being in the owner org).
|
|
// We do not want to automatically propagate this kind of delete because an resource will always have an owner org.
|
|
t.Run("deleting bucket urm does create dangling bucket", func(t *testing.T) {
|
|
data := fields()
|
|
s, done := init(t, data)
|
|
defer done()
|
|
ctx := context.Background()
|
|
|
|
// Pre-check the current situation.
|
|
// bucket1 is owned by user1.
|
|
// Check it.
|
|
urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
ResourceID: data.Buckets[0].ID,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := []*influxdb.UserResourceMapping{
|
|
{
|
|
UserID: data.Users[0].ID,
|
|
UserType: influxdb.Owner,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
ResourceID: data.Buckets[0].ID,
|
|
},
|
|
}
|
|
sort.Sort(urmByUserID(want))
|
|
sort.Sort(urmByUserID(urms))
|
|
if diff := cmp.Diff(want, urms); diff != "" {
|
|
t.Fatalf("unexpected urms -want/+got:\n\t%s", diff)
|
|
}
|
|
// bucket2 is owned by user2.
|
|
// bucket2 is readable by user2.
|
|
// Check it.
|
|
urms, _, err = s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
ResourceID: data.Buckets[1].ID,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want = []*influxdb.UserResourceMapping{
|
|
{
|
|
UserID: data.Users[1].ID,
|
|
UserType: influxdb.Owner,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
ResourceID: data.Buckets[1].ID,
|
|
},
|
|
{
|
|
UserID: data.Users[0].ID,
|
|
UserType: influxdb.Member,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
ResourceID: data.Buckets[1].ID,
|
|
},
|
|
}
|
|
sort.Sort(urmByUserID(want))
|
|
sort.Sort(urmByUserID(urms))
|
|
if diff := cmp.Diff(want, urms); diff != "" {
|
|
t.Fatalf("unexpected urms -want/+got:\n\t%s", diff)
|
|
}
|
|
|
|
// Now delete user2 -> bucket2.
|
|
// Still expect bucket2 to exist (user1 still points to it).
|
|
if err := s.DeleteUserResourceMapping(ctx, data.Buckets[1].ID, data.Users[1].ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{ID: &data.Buckets[1].ID})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nbs != 1 {
|
|
t.Errorf("expected 1 buckets, got: %v", bs)
|
|
}
|
|
// Now delete user1 -> bucket2.
|
|
// Still expect bucket2 to exist (nobody points to it).
|
|
if err := s.DeleteUserResourceMapping(ctx, data.Buckets[1].ID, data.Users[0].ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bs, nbs, err = s.FindBuckets(ctx, influxdb.BucketFilter{ID: &data.Buckets[1].ID})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nbs != 1 {
|
|
t.Errorf("expected 1 buckets, got: %v", bs)
|
|
}
|
|
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
ResourceID: data.Buckets[1].ID,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nurms != 0 {
|
|
t.Errorf("expected bucket2, to be dangling, got: %+v", urms)
|
|
}
|
|
// Now delete user1 -> bucket1.
|
|
// Still expect bucket1 to exist (nobody points to it).
|
|
if err := s.DeleteUserResourceMapping(ctx, data.Buckets[0].ID, data.Users[0].ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bs, nbs, err = s.FindBuckets(ctx, influxdb.BucketFilter{ID: &data.Buckets[0].ID})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nbs != 1 {
|
|
t.Errorf("expected 1 buckets, got: %v", bs)
|
|
}
|
|
urms, nurms, err = s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
ResourceID: data.Buckets[0].ID,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nurms != 0 {
|
|
t.Errorf("expected bucket1, to be dangling, got: %+v", urms)
|
|
}
|
|
})
|
|
|
|
t.Run("deleting a user deletes every related urm and nothing else", func(t *testing.T) {
|
|
data := fields()
|
|
s, done := init(t, data)
|
|
defer done()
|
|
ctx := context.Background()
|
|
|
|
// bucket1 is owned by user1.
|
|
// Check it.
|
|
urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
ResourceID: data.Buckets[0].ID,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := []*influxdb.UserResourceMapping{
|
|
{
|
|
UserID: data.Users[0].ID,
|
|
UserType: influxdb.Owner,
|
|
MappingType: influxdb.UserMappingType,
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
ResourceID: data.Buckets[0].ID,
|
|
},
|
|
}
|
|
sort.Sort(urmByUserID(want))
|
|
sort.Sort(urmByUserID(urms))
|
|
if diff := cmp.Diff(want, urms); diff != "" {
|
|
t.Fatalf("unexpected urms -want/+got:\n\t%s", diff)
|
|
}
|
|
|
|
// Delete user1.
|
|
// We expect his urms deleted but not bucket1.
|
|
if err := s.DeleteUser(ctx, data.Users[0].ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
|
|
UserID: data.Users[0].ID,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nurms > 0 {
|
|
t.Errorf("expected that user deletion would remove dangling urms, got: %+v", urms)
|
|
}
|
|
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{ID: &data.Buckets[0].ID})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nbs != 1 {
|
|
t.Errorf("expected 1 buckets, got: %v", bs)
|
|
}
|
|
})
|
|
|
|
t.Run("deleting a bucket deletes every related urm", func(t *testing.T) {
|
|
data := fields()
|
|
s, done := init(t, data)
|
|
defer done()
|
|
ctx := context.Background()
|
|
|
|
// Delete bucket2.
|
|
// We expect its urms deleted.
|
|
if err := s.DeleteBucket(ctx, data.Buckets[1].ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
ResourceID: data.Buckets[1].ID,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nurms > 0 {
|
|
t.Errorf("expected that bucket deletion would remove dangling urms, got: %+v", urms)
|
|
}
|
|
})
|
|
|
|
// NOTE(affo)(*kv.Service): buckets, users, and urms survive.
|
|
t.Run("deleting an organization should delete everything that depends on it", func(t *testing.T) {
|
|
data := fields()
|
|
s, done := init(t, data)
|
|
defer done()
|
|
ctx := context.Background()
|
|
|
|
// Delete org1.
|
|
// We expect its buckets to be deleted.
|
|
// We expect urms to those buckets to be deleted too.
|
|
// No user should be deleted.
|
|
preDeletionBuckets, _, err := s.FindBuckets(ctx, influxdb.BucketFilter{OrganizationID: &data.Organizations[0].ID})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.DeleteOrganization(ctx, data.Organizations[0].ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bs, nbs, err := s.FindBuckets(ctx, influxdb.BucketFilter{OrganizationID: &data.Organizations[0].ID})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nbs != 0 {
|
|
t.Errorf("expected org buckets to be deleted, got: %+v", bs)
|
|
}
|
|
|
|
urms, _, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
|
|
UserID: data.Users[0].ID,
|
|
ResourceType: influxdb.BucketsResourceType,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, urm := range urms {
|
|
for _, b := range preDeletionBuckets {
|
|
if urm.ResourceID == b.ID {
|
|
t.Errorf("expected this urm to be deleted, got %+v instead", urm)
|
|
}
|
|
}
|
|
}
|
|
if _, err := s.FindUser(ctx, influxdb.UserFilter{ID: &data.Users[0].ID}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Delete org2.
|
|
// Everything should disappear.
|
|
if err := s.DeleteOrganization(ctx, data.Organizations[1].ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
orgs, norgs, err := s.FindOrganizations(ctx, influxdb.OrganizationFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if norgs != 0 {
|
|
t.Errorf("expected no org, got: %v", orgs)
|
|
}
|
|
usrs, nusrs, err := s.FindUsers(ctx, influxdb.UserFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nusrs != 2 {
|
|
t.Errorf("expected 2 users, got: %v", usrs)
|
|
}
|
|
urms, nurms, err := s.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nurms > 0 {
|
|
t.Errorf("expected no urm, got: %v", urms)
|
|
}
|
|
bs, nbs, err = s.FindBuckets(ctx, influxdb.BucketFilter{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if nbs != 0 {
|
|
t.Errorf("expected buckets to be deleted, got: %+v", bs)
|
|
}
|
|
})
|
|
}
|
|
|
|
func initBoltTenantService(t *testing.T, f tenantFields) (*tenant.Service, func()) {
|
|
s, closeBolt := itesting.NewTestBoltStore(t)
|
|
store := tenant.NewStore(s)
|
|
|
|
if f.OrgIDGenerator != nil {
|
|
store.OrgIDGen = f.OrgIDGenerator
|
|
}
|
|
|
|
if f.BucketIDGenerator != nil {
|
|
store.BucketIDGen = f.BucketIDGenerator
|
|
}
|
|
|
|
svc := tenant.NewService(store)
|
|
|
|
for _, u := range f.Users {
|
|
if err := svc.CreateUser(context.Background(), u); err != nil {
|
|
t.Fatalf("error populating users: %v", err)
|
|
}
|
|
}
|
|
|
|
for i := range f.Passwords {
|
|
if err := svc.SetPassword(context.Background(), f.Users[i].ID, f.Passwords[i]); err != nil {
|
|
t.Fatalf("error setting passsword user, %s %s: %v", f.Users[i].Name, f.Passwords[i], err)
|
|
}
|
|
}
|
|
|
|
for _, o := range f.Organizations {
|
|
if err := svc.CreateOrganization(context.Background(), o); err != nil {
|
|
t.Fatalf("failed to populate organizations: %s", err)
|
|
}
|
|
}
|
|
|
|
for _, b := range f.Buckets {
|
|
if err := svc.CreateBucket(context.Background(), b); err != nil {
|
|
t.Fatalf("failed to populate buckets: %s", err)
|
|
}
|
|
}
|
|
|
|
for _, m := range f.UserResourceMappings {
|
|
if err := svc.CreateUserResourceMapping(context.Background(), m); err != nil {
|
|
t.Fatalf("failed to populate mappings: %v", err)
|
|
}
|
|
}
|
|
|
|
return svc, func() {
|
|
for _, u := range f.Users {
|
|
if err := svc.DeleteUser(context.Background(), u.ID); err != nil {
|
|
t.Logf("error removing users: %v", err)
|
|
}
|
|
}
|
|
|
|
for _, m := range f.UserResourceMappings {
|
|
if err := svc.DeleteUserResourceMapping(context.Background(), m.ResourceID, m.UserID); err != nil {
|
|
t.Logf("failed to remove user resource mapping: %v", err)
|
|
}
|
|
}
|
|
|
|
for _, o := range f.Organizations {
|
|
if err := svc.DeleteOrganization(context.Background(), o.ID); err != nil {
|
|
t.Logf("failed to remove organization: %v", err)
|
|
}
|
|
}
|
|
|
|
closeBolt()
|
|
}
|
|
}
|