2020-10-12 16:01:00 +00:00
|
|
|
package upgrade
|
|
|
|
|
|
|
|
import (
|
2020-10-30 16:54:12 +00:00
|
|
|
"context"
|
2020-10-12 16:01:00 +00:00
|
|
|
"errors"
|
2022-06-13 19:52:28 +00:00
|
|
|
"fmt"
|
2020-10-12 16:01:00 +00:00
|
|
|
"reflect"
|
2020-10-30 16:54:12 +00:00
|
|
|
"sort"
|
2020-10-12 16:01:00 +00:00
|
|
|
"testing"
|
|
|
|
"unsafe"
|
|
|
|
|
|
|
|
"github.com/google/go-cmp/cmp"
|
2020-10-30 16:54:12 +00:00
|
|
|
"github.com/influxdata/influxdb/v2"
|
refactor(kv): delete deprecated kv service code
This includes removal of a lot of kv.Service responsibilities. However,
it does not finish the re-wiring. It removes documents, telegrafs,
notification rules + endpoints, checks, orgs, users, buckets, passwords,
urms, labels and authorizations. There are some oustanding pieces that
are needed to get kv service compiling (dashboard service urm
dependency). Then all the call sites for kv service need updating and
the new implementations of telegraf and notification rules + endpoints
needed installing (along with any necessary migrations).
2020-10-20 13:25:36 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/authorization"
|
2020-10-30 16:54:12 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/inmem"
|
2021-09-13 19:12:35 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/kit/platform"
|
2020-10-30 16:54:12 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/kv/migration"
|
|
|
|
"github.com/influxdata/influxdb/v2/kv/migration/all"
|
2021-01-29 16:50:57 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/pkg/testing/assert"
|
2020-10-30 16:54:12 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/tenant"
|
|
|
|
authv1 "github.com/influxdata/influxdb/v2/v1/authorization"
|
2020-10-12 16:01:00 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/v1/services/meta"
|
|
|
|
"github.com/influxdata/influxql"
|
|
|
|
"github.com/stretchr/testify/require"
|
2020-10-30 16:54:12 +00:00
|
|
|
"go.uber.org/zap"
|
2020-10-12 16:01:00 +00:00
|
|
|
"go.uber.org/zap/zaptest"
|
2020-10-30 16:54:12 +00:00
|
|
|
"golang.org/x/crypto/bcrypt"
|
2020-10-12 16:01:00 +00:00
|
|
|
)
|
|
|
|
|
2020-10-30 16:54:12 +00:00
|
|
|
func TestUpgradeSecurity(t *testing.T) {
|
2020-10-12 16:01:00 +00:00
|
|
|
|
|
|
|
type testCase struct {
|
|
|
|
name string
|
|
|
|
users []meta.UserInfo
|
2021-03-30 18:10:02 +00:00
|
|
|
db2ids map[string][]platform.ID
|
2020-10-12 16:01:00 +00:00
|
|
|
wantErr error
|
2020-10-30 16:54:12 +00:00
|
|
|
want []*influxdb.Authorization
|
|
|
|
}
|
|
|
|
|
|
|
|
hash := func(password string) string {
|
|
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return string(hash)
|
2020-10-12 16:01:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var testCases = []testCase{
|
|
|
|
{
|
|
|
|
name: "ordinary",
|
|
|
|
users: []meta.UserInfo{
|
|
|
|
{ // not upgraded because admin
|
|
|
|
Name: "superman",
|
|
|
|
Admin: true,
|
2020-10-30 16:54:12 +00:00
|
|
|
Hash: hash("superman@123"),
|
2020-10-12 16:01:00 +00:00
|
|
|
},
|
|
|
|
{ // not upgraded because no privileges
|
|
|
|
Name: "loser",
|
|
|
|
Admin: false,
|
2020-10-30 16:54:12 +00:00
|
|
|
Hash: hash("loser@123"),
|
2020-10-12 16:01:00 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "weatherman",
|
|
|
|
Admin: false,
|
2020-10-30 16:54:12 +00:00
|
|
|
Hash: hash("weatherman@123"),
|
2020-10-12 16:01:00 +00:00
|
|
|
Privileges: map[string]influxql.Privilege{
|
|
|
|
"water": influxql.AllPrivileges,
|
|
|
|
"air": influxql.AllPrivileges,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "hitgirl",
|
|
|
|
Admin: false,
|
2020-10-30 16:54:12 +00:00
|
|
|
Hash: hash("hitgirl@123"),
|
2020-10-12 16:01:00 +00:00
|
|
|
Privileges: map[string]influxql.Privilege{
|
|
|
|
"hits": influxql.WritePrivilege,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "boss@hits.org", // special name
|
|
|
|
Admin: false,
|
2020-10-30 16:54:12 +00:00
|
|
|
Hash: hash("boss@123"),
|
2020-10-12 16:01:00 +00:00
|
|
|
Privileges: map[string]influxql.Privilege{
|
|
|
|
"hits": influxql.AllPrivileges,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "viewer",
|
|
|
|
Admin: false,
|
2020-10-30 16:54:12 +00:00
|
|
|
Hash: hash("viewer@123"),
|
2020-10-12 16:01:00 +00:00
|
|
|
Privileges: map[string]influxql.Privilege{
|
|
|
|
"water": influxql.ReadPrivilege,
|
|
|
|
"air": influxql.ReadPrivilege,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2021-03-30 18:10:02 +00:00
|
|
|
db2ids: map[string][]platform.ID{
|
2020-10-30 16:54:12 +00:00
|
|
|
"water": {0x33f9d67bc9cbc5b7, 0x33f9d67bc9cbc5b8, 0x33f9d67bc9cbc5b9},
|
|
|
|
"air": {0x43f9d67bc9cbc5b7, 0x43f9d67bc9cbc5b8, 0x43f9d67bc9cbc5b9},
|
|
|
|
"hits": {0x53f9d67bc9cbc5b7},
|
|
|
|
},
|
|
|
|
want: []*influxdb.Authorization{
|
|
|
|
{
|
|
|
|
Token: "boss@hits.org",
|
|
|
|
Status: "active",
|
2020-11-09 23:10:46 +00:00
|
|
|
Description: "boss@hits.org's Legacy Token",
|
2020-10-30 16:54:12 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Token: "hitgirl",
|
|
|
|
Status: "active",
|
2020-11-09 23:10:46 +00:00
|
|
|
Description: "hitgirl's Legacy Token",
|
2020-10-30 16:54:12 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Token: "viewer",
|
|
|
|
Status: "active",
|
2020-11-09 23:10:46 +00:00
|
|
|
Description: "viewer's Legacy Token",
|
2020-10-30 16:54:12 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Token: "weatherman",
|
|
|
|
Status: "active",
|
2020-11-09 23:10:46 +00:00
|
|
|
Description: "weatherman's Legacy Token",
|
2020-10-30 16:54:12 +00:00
|
|
|
},
|
2020-10-12 16:01:00 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "missing buckets",
|
|
|
|
users: []meta.UserInfo{
|
|
|
|
{
|
|
|
|
Name: "weatherman",
|
|
|
|
Admin: false,
|
2020-10-30 16:54:12 +00:00
|
|
|
Hash: hash("weatherman@123"),
|
2020-10-12 16:01:00 +00:00
|
|
|
Privileges: map[string]influxql.Privilege{
|
|
|
|
"water": influxql.AllPrivileges,
|
|
|
|
"air": influxql.AllPrivileges,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
db2ids: nil,
|
|
|
|
wantErr: errors.New("upgrade: there were errors/warnings, please fix them and run the command again"),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "no users",
|
|
|
|
users: []meta.UserInfo{},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
|
|
|
tc := tc
|
|
|
|
t.Run(tc.name, func(t *testing.T) { // better do not run in parallel
|
2020-10-30 16:54:12 +00:00
|
|
|
ctx := context.Background()
|
|
|
|
log := zaptest.NewLogger(t)
|
|
|
|
|
2020-10-12 16:01:00 +00:00
|
|
|
// mock v1 meta
|
|
|
|
v1 := &influxDBv1{
|
|
|
|
meta: &meta.Client{},
|
|
|
|
}
|
|
|
|
data := &meta.Data{
|
|
|
|
Users: tc.users,
|
|
|
|
}
|
|
|
|
f := reflect.ValueOf(v1.meta).Elem().Field(4)
|
|
|
|
f = reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem()
|
|
|
|
f.Set(reflect.ValueOf(data))
|
|
|
|
|
2020-10-30 16:54:12 +00:00
|
|
|
// mock v2 meta
|
|
|
|
kvStore := inmem.NewKVStore()
|
|
|
|
migrator, err := migration.NewMigrator(zap.NewNop(), kvStore, all.Migrations[:]...)
|
|
|
|
require.NoError(t, err)
|
|
|
|
err = migrator.Up(ctx)
|
2020-10-12 16:01:00 +00:00
|
|
|
require.NoError(t, err)
|
refactor(kv): delete deprecated kv service code
This includes removal of a lot of kv.Service responsibilities. However,
it does not finish the re-wiring. It removes documents, telegrafs,
notification rules + endpoints, checks, orgs, users, buckets, passwords,
urms, labels and authorizations. There are some oustanding pieces that
are needed to get kv service compiling (dashboard service urm
dependency). Then all the call sites for kv service need updating and
the new implementations of telegraf and notification rules + endpoints
needed installing (along with any necessary migrations).
2020-10-20 13:25:36 +00:00
|
|
|
|
|
|
|
authStoreV1, err := authv1.NewStore(kvStore)
|
2020-10-30 16:54:12 +00:00
|
|
|
require.NoError(t, err)
|
refactor(kv): delete deprecated kv service code
This includes removal of a lot of kv.Service responsibilities. However,
it does not finish the re-wiring. It removes documents, telegrafs,
notification rules + endpoints, checks, orgs, users, buckets, passwords,
urms, labels and authorizations. There are some oustanding pieces that
are needed to get kv service compiling (dashboard service urm
dependency). Then all the call sites for kv service need updating and
the new implementations of telegraf and notification rules + endpoints
needed installing (along with any necessary migrations).
2020-10-20 13:25:36 +00:00
|
|
|
|
2020-10-30 16:54:12 +00:00
|
|
|
tenantStore := tenant.NewStore(kvStore)
|
|
|
|
tenantSvc := tenant.NewService(tenantStore)
|
refactor(kv): delete deprecated kv service code
This includes removal of a lot of kv.Service responsibilities. However,
it does not finish the re-wiring. It removes documents, telegrafs,
notification rules + endpoints, checks, orgs, users, buckets, passwords,
urms, labels and authorizations. There are some oustanding pieces that
are needed to get kv service compiling (dashboard service urm
dependency). Then all the call sites for kv service need updating and
the new implementations of telegraf and notification rules + endpoints
needed installing (along with any necessary migrations).
2020-10-20 13:25:36 +00:00
|
|
|
|
|
|
|
authStoreV2, err := authorization.NewStore(kvStore)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-10-30 16:54:12 +00:00
|
|
|
v2 := &influxDBv2{
|
refactor(kv): delete deprecated kv service code
This includes removal of a lot of kv.Service responsibilities. However,
it does not finish the re-wiring. It removes documents, telegrafs,
notification rules + endpoints, checks, orgs, users, buckets, passwords,
urms, labels and authorizations. There are some oustanding pieces that
are needed to get kv service compiling (dashboard service urm
dependency). Then all the call sites for kv service need updating and
the new implementations of telegraf and notification rules + endpoints
needed installing (along with any necessary migrations).
2020-10-20 13:25:36 +00:00
|
|
|
authSvc: authv1.NewService(authStoreV1, tenantSvc),
|
|
|
|
onboardSvc: tenant.NewOnboardService(
|
|
|
|
tenantSvc,
|
|
|
|
authorization.NewService(authStoreV2, tenantSvc),
|
|
|
|
),
|
2020-10-30 16:54:12 +00:00
|
|
|
}
|
2020-10-12 16:01:00 +00:00
|
|
|
|
2020-10-30 16:54:12 +00:00
|
|
|
// onboard admin
|
|
|
|
oReq := &influxdb.OnboardingRequest{
|
2021-03-01 14:55:39 +00:00
|
|
|
User: "admin",
|
|
|
|
Password: "12345678",
|
|
|
|
Org: "testers",
|
|
|
|
Bucket: "def",
|
|
|
|
RetentionPeriodSeconds: influxdb.InfiniteRetention,
|
2020-10-30 16:54:12 +00:00
|
|
|
}
|
|
|
|
oResp, err := setupAdmin(ctx, v2, oReq)
|
2020-10-13 18:07:09 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-10-30 16:54:12 +00:00
|
|
|
// target options
|
2020-10-12 16:01:00 +00:00
|
|
|
targetOptions := optionsV2{
|
2020-10-30 16:54:12 +00:00
|
|
|
userName: oReq.User,
|
|
|
|
orgName: oReq.Org,
|
|
|
|
token: oResp.Auth.Token,
|
|
|
|
orgID: oResp.Auth.OrgID,
|
|
|
|
userID: oResp.Auth.UserID,
|
2020-10-12 16:01:00 +00:00
|
|
|
}
|
|
|
|
|
2022-06-13 19:52:28 +00:00
|
|
|
for k, v := range tc.db2ids {
|
|
|
|
for i, id := range v {
|
|
|
|
b := &influxdb.Bucket{
|
|
|
|
ID: id,
|
|
|
|
Name: fmt.Sprintf("%s_%d", k, id),
|
|
|
|
OrgID: targetOptions.orgID,
|
|
|
|
}
|
|
|
|
err := tenantSvc.CreateBucket(context.Background(), b)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tc.db2ids[k][i] = b.ID
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-30 16:54:12 +00:00
|
|
|
// fill in expected permissions now that we know IDs
|
|
|
|
for _, want := range tc.want {
|
|
|
|
for _, user := range tc.users {
|
|
|
|
if want.Token == user.Name { // v1 username is v2 token
|
|
|
|
var permissions []influxdb.Permission
|
|
|
|
for db, privilege := range user.Privileges {
|
|
|
|
ids, ok := tc.db2ids[db]
|
|
|
|
require.True(t, ok)
|
|
|
|
for _, id := range ids {
|
|
|
|
id := id
|
|
|
|
resource := influxdb.Resource{
|
|
|
|
Type: influxdb.BucketsResourceType,
|
|
|
|
OrgID: &targetOptions.orgID,
|
|
|
|
ID: &id,
|
|
|
|
}
|
|
|
|
switch privilege {
|
|
|
|
case influxql.ReadPrivilege:
|
|
|
|
permissions = append(permissions, influxdb.Permission{
|
|
|
|
Action: influxdb.ReadAction,
|
|
|
|
Resource: resource,
|
|
|
|
})
|
|
|
|
case influxql.WritePrivilege:
|
|
|
|
permissions = append(permissions, influxdb.Permission{
|
|
|
|
Action: influxdb.WriteAction,
|
|
|
|
Resource: resource,
|
|
|
|
})
|
|
|
|
case influxql.AllPrivileges:
|
|
|
|
permissions = append(permissions, influxdb.Permission{
|
|
|
|
Action: influxdb.ReadAction,
|
|
|
|
Resource: resource,
|
|
|
|
})
|
|
|
|
permissions = append(permissions, influxdb.Permission{
|
|
|
|
Action: influxdb.WriteAction,
|
|
|
|
Resource: resource,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
want.Permissions = permissions
|
|
|
|
}
|
|
|
|
}
|
2020-10-12 16:01:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// command execution
|
2020-11-16 14:05:40 +00:00
|
|
|
n, err := upgradeUsers(ctx, v1, v2, &targetOptions, tc.db2ids, log)
|
|
|
|
assert.Equal(t, len(tc.want), n, "Upgraded count must match")
|
2020-10-12 16:01:00 +00:00
|
|
|
if err != nil {
|
|
|
|
if tc.wantErr != nil {
|
|
|
|
if diff := cmp.Diff(tc.wantErr.Error(), err.Error()); diff != "" {
|
|
|
|
t.Fatal(diff)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
} else if tc.wantErr != nil {
|
2020-10-13 18:07:09 +00:00
|
|
|
t.Fatalf("should have failed with %v", tc.wantErr)
|
2020-10-12 16:01:00 +00:00
|
|
|
}
|
2020-10-30 16:54:12 +00:00
|
|
|
for _, want := range tc.want {
|
|
|
|
actual, err := v2.authSvc.FindAuthorizationByToken(ctx, want.Token)
|
|
|
|
require.NoError(t, err)
|
|
|
|
if diff := cmp.Diff(targetOptions.orgID, actual.OrgID); diff != "" {
|
|
|
|
t.Fatal(diff)
|
2020-10-12 16:01:00 +00:00
|
|
|
}
|
2020-10-30 16:54:12 +00:00
|
|
|
if diff := cmp.Diff(targetOptions.userID, actual.UserID); diff != "" {
|
|
|
|
t.Fatal(diff)
|
|
|
|
}
|
|
|
|
if diff := cmp.Diff(want.Token, actual.Token); diff != "" {
|
|
|
|
t.Fatal(diff)
|
|
|
|
}
|
|
|
|
if diff := cmp.Diff(want.Description, actual.Description); diff != "" {
|
|
|
|
t.Fatal(diff)
|
|
|
|
}
|
|
|
|
if diff := cmp.Diff(want.Status, actual.Status); diff != "" {
|
|
|
|
t.Fatal(diff)
|
|
|
|
}
|
|
|
|
sort.Slice(want.Permissions, func(i, j int) bool {
|
|
|
|
return *(want.Permissions[i].Resource.ID) < *(want.Permissions[j].Resource.ID)
|
|
|
|
})
|
|
|
|
sort.Slice(actual.Permissions, func(i, j int) bool {
|
|
|
|
return *(actual.Permissions[i].Resource.ID) < *(actual.Permissions[j].Resource.ID)
|
|
|
|
})
|
|
|
|
if diff := cmp.Diff(want.Permissions, actual.Permissions); diff != "" {
|
|
|
|
t.Logf("permissions mismatch for user %s", want.Token)
|
|
|
|
t.Fatal(diff)
|
2020-10-12 16:01:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|