Skip to content

Commit 747c82f

Browse files
authored
Merge pull request #439 from team-scaletech/fix/role-deletion
role deletion
2 parents b1bfaf6 + 40a0a2f commit 747c82f

File tree

4 files changed

+211
-11
lines changed

4 files changed

+211
-11
lines changed

server/resolvers/update_env.go

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"reflect"
99
"strings"
10+
"time"
1011

1112
log "github.com/sirupsen/logrus"
1213

@@ -93,6 +94,53 @@ func clearSessionIfRequired(currentData, updatedData map[string]interface{}) {
9394
}
9495
}
9596

97+
// updateRoles will update DB for user roles, if a role is deleted by admin
98+
// then this function will those roles from user roles if exists
99+
func updateRoles(ctx context.Context, deletedRoles []string) error {
100+
data, err := db.Provider.ListUsers(ctx, &model.Pagination{
101+
Limit: 1,
102+
Offset: 1,
103+
})
104+
if err != nil {
105+
return err
106+
}
107+
108+
allData, err := db.Provider.ListUsers(ctx, &model.Pagination{
109+
Limit: data.Pagination.Total,
110+
})
111+
if err != nil {
112+
return err
113+
}
114+
115+
chunkSize := 1000
116+
totalUsers := len(allData.Users)
117+
118+
for start := 0; start < totalUsers; start += chunkSize {
119+
end := start + chunkSize
120+
if end > totalUsers {
121+
end = totalUsers
122+
}
123+
124+
chunkUsers := allData.Users[start:end]
125+
126+
for i := range chunkUsers {
127+
roles := utils.DeleteFromArray(chunkUsers[i].Roles, deletedRoles)
128+
if len(chunkUsers[i].Roles) != len(roles) {
129+
updatedValues := map[string]interface{}{
130+
"roles": strings.Join(roles, ","),
131+
"updated_at": time.Now().Unix(),
132+
}
133+
id := []string{chunkUsers[i].ID}
134+
err = db.Provider.UpdateUsers(ctx, updatedValues, id)
135+
if err != nil {
136+
return err
137+
}
138+
}
139+
}
140+
}
141+
return nil
142+
}
143+
96144
// UpdateEnvResolver is a resolver for update config mutation
97145
// This is admin only mutation
98146
func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model.Response, error) {
@@ -291,28 +339,41 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
291339
}, nil)
292340
}
293341

342+
previousRoles := strings.Split(currentData[constants.EnvKeyRoles].(string), ",")
343+
previousProtectedRoles := strings.Split(currentData[constants.EnvKeyProtectedRoles].(string), ",")
344+
updatedRoles := strings.Split(updatedData[constants.EnvKeyRoles].(string), ",")
345+
updatedDefaultRoles := strings.Split(updatedData[constants.EnvKeyDefaultRoles].(string), ",")
346+
updatedProtectedRoles := strings.Split(updatedData[constants.EnvKeyProtectedRoles].(string), ",")
294347
// check the roles change
295-
if len(params.Roles) > 0 {
296-
if len(params.DefaultRoles) > 0 {
297-
// should be subset of roles
298-
for _, role := range params.DefaultRoles {
299-
if !utils.StringSliceContains(params.Roles, role) {
300-
log.Debug("Default roles should be subset of roles")
301-
return res, fmt.Errorf("default role %s is not in roles", role)
302-
}
348+
if len(updatedRoles) > 0 && len(updatedDefaultRoles) > 0 {
349+
// should be subset of roles
350+
for _, role := range updatedDefaultRoles {
351+
if !utils.StringSliceContains(updatedRoles, role) {
352+
log.Debug("Default roles should be subset of roles")
353+
return res, fmt.Errorf("default role %s is not in roles", role)
303354
}
304355
}
305356
}
306357

307-
if len(params.ProtectedRoles) > 0 {
308-
for _, role := range params.ProtectedRoles {
309-
if utils.StringSliceContains(params.Roles, role) || utils.StringSliceContains(params.DefaultRoles, role) {
358+
if len(updatedProtectedRoles) > 0 {
359+
for _, role := range updatedProtectedRoles {
360+
if utils.StringSliceContains(updatedRoles, role) || utils.StringSliceContains(updatedDefaultRoles, role) {
310361
log.Debug("Protected roles should not be in roles or default roles")
311362
return res, fmt.Errorf("protected role %s found roles or default roles", role)
312363
}
313364
}
314365
}
315366

367+
deletedRoles := utils.FindDeletedValues(previousRoles, updatedRoles)
368+
if len(deletedRoles) > 0 {
369+
go updateRoles(ctx, deletedRoles)
370+
}
371+
372+
deletedProtectedRoles := utils.FindDeletedValues(previousProtectedRoles, updatedProtectedRoles)
373+
if len(deletedProtectedRoles) > 0 {
374+
go updateRoles(ctx, deletedProtectedRoles)
375+
}
376+
316377
// Update local store
317378
memorystore.Provider.UpdateEnvStore(updatedData)
318379
jwk, err := crypto.GenerateJWKBasedOnEnv()

server/test/integration_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ func TestResolvers(t *testing.T) {
122122
updateEmailTemplateTest(t, s)
123123
emailTemplatesTest(t, s)
124124
deleteEmailTemplateTest(t, s)
125+
RoleDeletionTest(t, s)
125126

126127
// user resolvers tests
127128
loginTests(t, s)

server/test/role_deletion_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package test
2+
3+
import (
4+
"fmt"
5+
"github.com/authorizerdev/authorizer/server/crypto"
6+
"strings"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
11+
"github.com/authorizerdev/authorizer/server/constants"
12+
"github.com/authorizerdev/authorizer/server/graph/model"
13+
"github.com/authorizerdev/authorizer/server/memorystore"
14+
"github.com/authorizerdev/authorizer/server/refs"
15+
"github.com/authorizerdev/authorizer/server/resolvers"
16+
)
17+
18+
func RoleDeletionTest(t *testing.T, s TestSetup) {
19+
t.Helper()
20+
t.Run(`should complete role deletion`, func(t *testing.T) {
21+
// login as admin
22+
req, ctx := createContext(s)
23+
24+
_, err := resolvers.AdminLoginResolver(ctx, model.AdminLoginInput{
25+
AdminSecret: "admin_test",
26+
})
27+
assert.NotNil(t, err)
28+
29+
adminSecret, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret)
30+
assert.Nil(t, err)
31+
_, err = resolvers.AdminLoginResolver(ctx, model.AdminLoginInput{
32+
AdminSecret: adminSecret,
33+
})
34+
assert.Nil(t, err)
35+
36+
h, err := crypto.EncryptPassword(adminSecret)
37+
assert.Nil(t, err)
38+
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", constants.AdminCookieName, h))
39+
40+
// add new default role to get role, if not present in roles
41+
originalDefaultRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
42+
assert.Nil(t, err)
43+
originalDefaultRolesSlice := strings.Split(originalDefaultRoles, ",")
44+
45+
data := model.UpdateEnvInput{
46+
DefaultRoles: append(originalDefaultRolesSlice, "abc"),
47+
}
48+
_, err = resolvers.UpdateEnvResolver(ctx, data)
49+
assert.Error(t, err)
50+
51+
// add new role
52+
originalRoles, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyRoles)
53+
assert.Nil(t, err)
54+
originalRolesSlice := strings.Split(originalRoles, ",")
55+
roleToBeAdded := "abc"
56+
newRoles := append(originalRolesSlice, roleToBeAdded)
57+
data = model.UpdateEnvInput{
58+
Roles: newRoles,
59+
}
60+
_, err = resolvers.UpdateEnvResolver(ctx, data)
61+
assert.Nil(t, err)
62+
63+
// register a user with all roles
64+
email := "update_user." + s.TestInfo.Email
65+
_, err = resolvers.SignupResolver(ctx, model.SignUpInput{
66+
Email: refs.NewStringRef(email),
67+
Password: s.TestInfo.Password,
68+
ConfirmPassword: s.TestInfo.Password,
69+
Roles: newRoles,
70+
})
71+
assert.Nil(t, err)
72+
73+
regUserDetails, _ := resolvers.UserResolver(ctx, model.GetUserRequest{
74+
Email: refs.NewStringRef(email),
75+
})
76+
77+
// update env by removing role "abc"
78+
var newRolesAfterDeletion []string
79+
for _, value := range newRoles {
80+
if value != roleToBeAdded {
81+
newRolesAfterDeletion = append(newRolesAfterDeletion, value)
82+
}
83+
}
84+
data = model.UpdateEnvInput{
85+
Roles: newRolesAfterDeletion,
86+
}
87+
_, err = resolvers.UpdateEnvResolver(ctx, data)
88+
assert.Nil(t, err)
89+
90+
// check user if role still exist
91+
userDetails, err := resolvers.UserResolver(ctx, model.GetUserRequest{
92+
Email: refs.NewStringRef(email),
93+
})
94+
assert.Nil(t, err)
95+
assert.Equal(t, newRolesAfterDeletion, userDetails.Roles)
96+
assert.NotEqual(t, newRolesAfterDeletion, regUserDetails.Roles)
97+
})
98+
}

server/utils/common.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,43 @@ func GetInviteVerificationURL(verificationURL, token, redirectURI string) string
9595
func GetEmailVerificationURL(token, hostname, redirectURI string) string {
9696
return hostname + "/verify_email?token=" + token + "&redirect_uri=" + redirectURI
9797
}
98+
99+
// FindDeletedValues find deleted values between original and updated one
100+
func FindDeletedValues(original, updated []string) []string {
101+
deletedValues := make([]string, 0)
102+
103+
// Create a map to store elements of the updated array for faster lookups
104+
updatedMap := make(map[string]bool)
105+
for _, value := range updated {
106+
updatedMap[value] = true
107+
}
108+
109+
// Check for deleted values in the original array
110+
for _, value := range original {
111+
if _, found := updatedMap[value]; !found {
112+
deletedValues = append(deletedValues, value)
113+
}
114+
}
115+
116+
return deletedValues
117+
}
118+
119+
// DeleteFromArray will delete array from an array
120+
func DeleteFromArray(original, valuesToDelete []string) []string {
121+
result := make([]string, 0)
122+
123+
// Create a map to store values to delete for faster lookups
124+
valuesToDeleteMap := make(map[string]bool)
125+
for _, value := range valuesToDelete {
126+
valuesToDeleteMap[value] = true
127+
}
128+
129+
// Check if each element in the original array should be deleted
130+
for _, value := range original {
131+
if _, found := valuesToDeleteMap[value]; !found {
132+
result = append(result, value)
133+
}
134+
}
135+
136+
return result
137+
}

0 commit comments

Comments
 (0)