Skip to content

Commit 13c58b6

Browse files
Support accessing non-tenanted/instanced tables with tenanted/instanced roles
1 parent 941bdef commit 13c58b6

File tree

7 files changed

+57
-42
lines changed

7 files changed

+57
-42
lines changed

pkg/authorizer/authorizer_test.go

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func testGettingOrgFromContext(t *testing.T, authorizer Authorizer) {
4747

4848
ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs(METADATA_KEY_ORGID, PEPSI))
4949
orgId, err := authorizer.GetOrgFromContext(ctx)
50-
assert.Nil(err)
50+
assert.NoError(err)
5151
assert.Equal(PEPSI, orgId)
5252
}
5353

@@ -63,41 +63,51 @@ func TestGettingMatchingDbRoleWithMetadataBasedAuthorizer(t *testing.T) {
6363

6464
gCtx := authorizer.GetDefaultOrgAdminContext()
6565
dbRole, err := authorizer.GetMatchingDbRole(gCtx, "appUser", "app")
66-
assert.Nil(err)
66+
assert.NoError(err)
6767
assert.Equal(dbrole.TENANT_WRITER, dbRole)
6868

6969
aCtx := metadata.NewIncomingContext(context.Background(), metadata.Pairs(METADATA_KEY_ORGID, PEPSI, METADATA_KEY_ROLE, METADATA_ROLE_SERVICE_ADMIN))
7070
dbRole, err = authorizer.GetMatchingDbRole(aCtx, "appUser", "app")
71-
assert.Nil(err)
71+
assert.NoError(err)
7272
assert.Equal(dbrole.WRITER, dbRole)
7373

7474
uCtx := metadata.NewIncomingContext(context.Background(), metadata.Pairs(METADATA_KEY_ORGID, PEPSI, METADATA_KEY_ROLE, METADATA_ROLE_SERVICE_AUDITOR))
7575
dbRole, err = authorizer.GetMatchingDbRole(uCtx, "appUser", "app")
76-
assert.Nil(err)
76+
assert.NoError(err)
7777
assert.Equal(dbrole.READER, dbRole)
7878

7979
wCtx := metadata.NewIncomingContext(context.Background(), metadata.Pairs(METADATA_KEY_ORGID, PEPSI, METADATA_KEY_ROLE, METADATA_ROLE_ADMIN))
8080
dbRole, err = authorizer.GetMatchingDbRole(wCtx, "appUser", "app")
81-
assert.Nil(err)
81+
assert.NoError(err)
8282
assert.Equal(dbrole.TENANT_WRITER, dbRole)
8383

8484
rCtx := metadata.NewIncomingContext(context.Background(), metadata.Pairs(METADATA_KEY_ORGID, PEPSI, METADATA_KEY_ROLE, METADATA_ROLE_AUDITOR))
8585
dbRole, err = authorizer.GetMatchingDbRole(rCtx, "appUser", "app")
86-
assert.Nil(err)
86+
assert.NoError(err)
8787
assert.Equal(dbrole.TENANT_READER, dbRole)
8888
}
8989

9090
/*
9191
Checks if updating role mapping in MetadataBasedAuthorizer works.
9292
*/
9393
func TestConfiguringMetadataBasedAuthorizer(t *testing.T) {
94-
authorizer := &MetadataBasedAuthorizer{}
94+
assert := assert.New(t)
9595

96+
authorizer := &MetadataBasedAuthorizer{}
9697
newRoleMapping := map[string]map[string]dbrole.DbRole{
9798
"app": {
98-
"SERVICE_ADMIN": dbrole.WRITER,
99+
"SERVICE_ADMIN": dbrole.READER,
99100
},
100101
}
101-
102102
authorizer.Configure("app", newRoleMapping["app"])
103+
104+
ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs(METADATA_KEY_ROLE, "SERVICE_ADMIN"))
105+
dbRole, err := authorizer.GetMatchingDbRole(ctx, "app")
106+
assert.NoError(err)
107+
assert.Equal(dbrole.READER, dbRole)
108+
109+
ctx = metadata.NewIncomingContext(context.Background(), metadata.Pairs(METADATA_KEY_ROLE, "SERVICE_OP"))
110+
dbRole, err = authorizer.GetMatchingDbRole(ctx, "app")
111+
assert.NoError(err)
112+
assert.Equal(dbrole.TENANT_READER, dbRole)
103113
}

pkg/datastore/database.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -622,15 +622,15 @@ func (db *relationalDb) RegisterHelper(_ context.Context, roleMapping map[string
622622
// This function considers multi-tenancy and multi-instance configurations to determine
623623
// the users relevant to the given table.
624624
func (db *relationalDb) GetDbUsers(record Record, tableName string) []dbUserSpec {
625-
users := getDbUsers(tableName, false, false)
626-
if IsMultiTenanted(record, tableName) {
627-
users = append(users, getDbUsers(tableName, true, false)...)
628-
if IsMultiInstanced(record, tableName, db.instancer != nil) {
629-
users = append(users, getDbUsers(tableName, true, true)...)
630-
}
631-
} else if IsMultiInstanced(record, tableName, db.instancer != nil) {
632-
users = append(users, getDbUsers(tableName, false, true)...)
633-
}
625+
tableTenanted := IsMultiTenanted(record, tableName)
626+
tableInstanced := IsMultiInstanced(record, tableName, db.instancer != nil)
627+
// Create all users but with conditions set based on whether the role and the table configuration
628+
// Tenant role without tenant column would not set the condition for tenant
629+
// Insntacer role without instance column would not set the condition for instancer
630+
users := getDbUsers(tableName, false, false, false, false)
631+
users = append(users, getDbUsers(tableName, true, false, tableTenanted, false)...)
632+
users = append(users, getDbUsers(tableName, false, true, false, tableInstanced)...)
633+
users = append(users, getDbUsers(tableName, true, true, tableTenanted, tableInstanced)...)
634634
return users
635635
}
636636

pkg/datastore/datastore_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,6 @@ func TestTransactions(t *testing.T) {
729729
assert.NoError(err)
730730

731731
testSingleTableTransactions(t, ds, ps)
732-
// testMultiTableTransactions(t, ds, ps)
733-
// testMultiProtoTransactions(t, ds, ps)
732+
testMultiTableTransactions(t, ds, ps)
733+
testMultiProtoTransactions(t, ds, ps)
734734
}

pkg/datastore/db_user.go

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func (spec dbUserSpec) String() string {
7373
}
7474

7575
func getDbUser(dbRole dbrole.DbRole) dbUserSpec {
76-
for _, spec := range getAllDbUsers("ANY") {
76+
for _, spec := range getAllDbUsers() {
7777
if spec.username == dbRole {
7878
return spec
7979
}
@@ -86,35 +86,40 @@ Generates specifications of 2 DB users:
8686
- user with read-only access (to specific org/instance)
8787
- user with read-write access (to specific org/instance)
8888
All the users have additional conditions to restrict access to records
89-
belonging to specific instance, if withInstanceIdCheck is set.
89+
belonging to specific instance, if instancerRole is set.
9090
*/
91-
func getDbUsers(tableName string, withTenantIdCheck, withInstanceIdCheck bool) []dbUserSpec {
91+
func getDbUsers(tableName string, tenantRole, instancerRole bool, isTableTenanted, isTableInstanced bool) []dbUserSpec {
9292
readerCommands := []string{"SELECT"}
9393
writerCommands := []string{"SELECT", "INSERT", "UPDATE", "DELETE"}
9494

9595
tenantCond := COLUMN_ORGID + " = current_setting('" + DbConfigOrgId + "')"
9696
instanceCond := COLUMN_INSTANCEID + " = current_setting('" + DbConfigInstanceId + "')"
9797
tenantInstanceCond := tenantCond + " AND " + instanceCond
9898

99-
cond := "true"
10099
rwUser := dbrole.WRITER
101100
rUser := dbrole.READER
102-
103101
switch {
104-
case withInstanceIdCheck && withTenantIdCheck:
105-
cond = tenantInstanceCond
102+
case tenantRole && instancerRole:
106103
rwUser = dbrole.TENANT_INSTANCE_WRITER
107104
rUser = dbrole.TENANT_INSTANCE_READER
108-
case withTenantIdCheck:
109-
cond = tenantCond
105+
case tenantRole:
110106
rwUser = dbrole.TENANT_WRITER
111107
rUser = dbrole.TENANT_READER
112-
case withInstanceIdCheck:
113-
cond = instanceCond
108+
case instancerRole:
114109
rwUser = dbrole.INSTANCE_WRITER
115110
rUser = dbrole.INSTANCE_READER
116111
}
117112

113+
cond := "true"
114+
switch {
115+
case isTableInstanced && isTableTenanted:
116+
cond = tenantInstanceCond
117+
case isTableTenanted:
118+
cond = tenantCond
119+
case isTableInstanced:
120+
cond = instanceCond
121+
}
122+
118123
writer := dbUserSpec{
119124
username: rwUser,
120125
commands: writerCommands,
@@ -134,8 +139,8 @@ func getDbUsers(tableName string, withTenantIdCheck, withInstanceIdCheck bool) [
134139
dbUsers[i].password = getPassword(string(dbUsers[i].username))
135140
dbUsers[i].policyName = getRlsPolicyName(string(dbUsers[i].username), tableName)
136141
}
137-
TRACE("Returning DB user specs for table %q:\n\twithTenantIdCheck - %t\n\twithInstanceIdCheck - %t\n\tdbUsers - %+v\n",
138-
tableName, withTenantIdCheck, withInstanceIdCheck, dbUsers)
142+
TRACE("Returning DB user specs for table %q:\n\t[roleT=%s, roleI=%s, tableT=%s, tableI=%s]\n\tdbUsers - %+v\n",
143+
tableName, tenantRole, instancerRole, isTableTenanted, isTableInstanced, dbUsers)
139144
return dbUsers
140145
}
141146

@@ -147,11 +152,12 @@ func getPassword(username string) string {
147152
return strconv.FormatInt(int64(h.Sum32()), 16)
148153
}
149154

150-
func getAllDbUsers(tableName string) []dbUserSpec {
155+
func getAllDbUsers() []dbUserSpec {
156+
tableName := "ANY" // The returned user specs are for creating users only not policies
151157
allDbUsers := make([]dbUserSpec, 0)
152-
allDbUsers = append(allDbUsers, getDbUsers(tableName, false, false)...)
153-
allDbUsers = append(allDbUsers, getDbUsers(tableName, false, true)...)
154-
allDbUsers = append(allDbUsers, getDbUsers(tableName, true, false)...)
155-
allDbUsers = append(allDbUsers, getDbUsers(tableName, true, true)...)
158+
allDbUsers = append(allDbUsers, getDbUsers(tableName, false, false, true, true)...)
159+
allDbUsers = append(allDbUsers, getDbUsers(tableName, false, true, false, true)...)
160+
allDbUsers = append(allDbUsers, getDbUsers(tableName, true, false, true, false)...)
161+
allDbUsers = append(allDbUsers, getDbUsers(tableName, true, true, true, true)...)
156162
return allDbUsers
157163
}

pkg/datastore/helper.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ func FromConfig(l *logrus.Entry, a authorizer.Authorizer, instancer authorizer.I
134134
}
135135

136136
// Create Users when the MAIN connection to DB is established
137-
for _, dbUserSpec := range getAllDbUsers("ANY") {
137+
for _, dbUserSpec := range getAllDbUsers() {
138138
stmt := getCreateUserStmt(string(dbUserSpec.username), dbUserSpec.password)
139139
if tx := db.gormDBMap[dbrole.MAIN].Exec(stmt); tx.Error != nil {
140140
err = ErrExecutingSqlStmt.Wrap(tx.Error).WithValue(SQL_STMT, stmt).WithValue(DB_NAME, db.dbName)

pkg/datastore/sql_struct.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ func GetTableName(x interface{}) (tableName string) {
252252

253253
// Generates RLS-policy name based on database role/user and table name.
254254
func getRlsPolicyName(username string, tableName string) string {
255-
policyName := strings.ToLower(username + "_" + tableName + "_v2")
255+
policyName := strings.ToLower(username + "_" + tableName + "_policy_v2")
256256
policyName = strings.ReplaceAll(policyName, "\"", "")
257257
return policyName
258258
}

pkg/datastore/transaction_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/datastore"
2929
"github.com/vmware-labs/multi-tenant-persistence-for-saas/pkg/protostore"
3030
. "github.com/vmware-labs/multi-tenant-persistence-for-saas/test"
31+
"github.com/vmware-labs/multi-tenant-persistence-for-saas/test/pb"
3132
)
3233

3334
func testSingleTableTransactions(t *testing.T, ds datastore.DataStore, ps protostore.ProtoStore) {
@@ -118,7 +119,6 @@ func testSingleTableTransactions(t *testing.T, ds datastore.DataStore, ps protos
118119
assert.NoError(tx.Error)
119120
}
120121

121-
/* FIXME(miriyalak): Enable test with non conflicting role bindings
122122
func testMultiTableTransactions(t *testing.T, ds datastore.DataStore, ps protostore.ProtoStore) {
123123
t.Helper()
124124
assert := assert.New(t)
@@ -329,4 +329,3 @@ func testMultiProtoTransactions(t *testing.T, ds datastore.DataStore, ps protost
329329
assert.NoError(err)
330330
t.Log("Purging pb.Disk after soft delete succeeded")
331331
}
332-
*/

0 commit comments

Comments
 (0)