AuthZ: Test List (#101721)
* Authz: Test List * Anonymous case * Cover rendering
This commit is contained in:
@@ -897,6 +897,299 @@ func TestService_Check(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_List(t *testing.T) {
|
||||
callingService := authn.NewAccessTokenAuthInfo(authn.Claims[authn.AccessTokenClaims]{
|
||||
Claims: jwt.Claims{
|
||||
Subject: types.NewTypeID(types.TypeAccessPolicy, "some-service"),
|
||||
Audience: []string{"authzservice"},
|
||||
},
|
||||
Rest: authn.AccessTokenClaims{Namespace: "org-12"},
|
||||
})
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
req *authzv1.ListRequest
|
||||
permissions []accesscontrol.Permission
|
||||
expected *authzv1.ListResponse
|
||||
expectErr bool
|
||||
}
|
||||
|
||||
t.Run("Require auth info", func(t *testing.T) {
|
||||
s := setupService()
|
||||
ctx := context.Background()
|
||||
_, err := s.List(ctx, &authzv1.ListRequest{
|
||||
Namespace: "org-12",
|
||||
Subject: "user:test-uid",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Verb: "get",
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "could not get auth info")
|
||||
})
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "should error if no namespace is provided",
|
||||
req: &authzv1.ListRequest{
|
||||
Namespace: "",
|
||||
Subject: "user:test-uid",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Verb: "get",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "should error if caller namespace does not match request namespace",
|
||||
req: &authzv1.ListRequest{
|
||||
Namespace: "org-13",
|
||||
Subject: "user:test-uid",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Verb: "get",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "should error if no subject is provided",
|
||||
req: &authzv1.ListRequest{
|
||||
Namespace: "org-12",
|
||||
Subject: "",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Verb: "get",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "should error if an unsupported subject type is provided",
|
||||
req: &authzv1.ListRequest{
|
||||
Namespace: "org-12",
|
||||
Subject: "api-key:12",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Verb: "get",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "should error if an invalid subject is provided",
|
||||
req: &authzv1.ListRequest{
|
||||
Namespace: "org-12",
|
||||
Subject: "invalid:12",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Verb: "get",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "should error if an unknown group is provided",
|
||||
req: &authzv1.ListRequest{
|
||||
Namespace: "org-12",
|
||||
Subject: "user:test-uid",
|
||||
Group: "unknown.grafana.app",
|
||||
Resource: "unknown",
|
||||
Verb: "get",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
name: "should error if an unknown verb is provided",
|
||||
req: &authzv1.ListRequest{
|
||||
Namespace: "org-12",
|
||||
Subject: "user:test-uid",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Verb: "unknown",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
t.Run("Request validation", func(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s := setupService()
|
||||
ctx := types.WithAuthInfo(context.Background(), callingService)
|
||||
userID := &store.UserIdentifiers{UID: "test-uid", ID: 1}
|
||||
store := &fakeStore{
|
||||
userID: userID,
|
||||
userPermissions: tc.permissions,
|
||||
}
|
||||
s.store = store
|
||||
s.permissionStore = store
|
||||
s.identityStore = &fakeIdentityStore{teams: []int64{1, 2}}
|
||||
|
||||
_, err := s.List(ctx, tc.req)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
testCases = []testCase{
|
||||
{
|
||||
name: "should list permissions for user with permission",
|
||||
req: &authzv1.ListRequest{
|
||||
Namespace: "org-12",
|
||||
Subject: "user:test-uid",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Verb: "get",
|
||||
},
|
||||
permissions: []accesscontrol.Permission{
|
||||
{Action: "dashboards:read", Scope: "dashboards:uid:dash1"},
|
||||
{Action: "dashboards:read", Scope: "dashboards:uid:dash2"},
|
||||
{Action: "dashboards:read", Scope: "folders:uid:fold1"},
|
||||
},
|
||||
expected: &authzv1.ListResponse{
|
||||
Items: []string{"dash1", "dash2"},
|
||||
Folders: []string{"fold1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return empty list for user without permission",
|
||||
req: &authzv1.ListRequest{
|
||||
Namespace: "org-12",
|
||||
Subject: "user:test-uid",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Verb: "get",
|
||||
},
|
||||
permissions: []accesscontrol.Permission{},
|
||||
expected: &authzv1.ListResponse{},
|
||||
},
|
||||
}
|
||||
t.Run("User permission list", func(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s := setupService()
|
||||
ctx := types.WithAuthInfo(context.Background(), callingService)
|
||||
userID := &store.UserIdentifiers{UID: "test-uid", ID: 1}
|
||||
store := &fakeStore{
|
||||
userID: userID,
|
||||
userPermissions: tc.permissions,
|
||||
}
|
||||
s.store = store
|
||||
s.permissionStore = store
|
||||
s.identityStore = &fakeIdentityStore{teams: []int64{1, 2}}
|
||||
|
||||
resp, err := s.List(ctx, tc.req)
|
||||
require.NoError(t, err)
|
||||
require.ElementsMatch(t, resp.Items, tc.expected.Items)
|
||||
require.ElementsMatch(t, resp.Folders, tc.expected.Folders)
|
||||
|
||||
// Check cache
|
||||
id, ok := s.idCache.Get(ctx, userIdentifierCacheKey("org-12", "test-uid"))
|
||||
require.True(t, ok)
|
||||
require.Equal(t, id.UID, "test-uid")
|
||||
perms, ok := s.permCache.Get(ctx, userPermCacheKey("org-12", "test-uid", "dashboards:read"))
|
||||
require.True(t, ok)
|
||||
require.Len(t, perms, len(tc.expected.Items)+len(tc.expected.Folders))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
testCases = []testCase{
|
||||
{
|
||||
name: "should list permissions for anonymous with permission",
|
||||
req: &authzv1.ListRequest{
|
||||
Namespace: "org-12",
|
||||
Subject: "anonymous:0",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Verb: "get",
|
||||
},
|
||||
permissions: []accesscontrol.Permission{
|
||||
{Action: "dashboards:read", Scope: "dashboards:uid:dash1"},
|
||||
{Action: "dashboards:read", Scope: "dashboards:uid:dash2"},
|
||||
{Action: "dashboards:read", Scope: "folders:uid:fold1"},
|
||||
},
|
||||
expected: &authzv1.ListResponse{
|
||||
Items: []string{"dash1", "dash2"},
|
||||
Folders: []string{"fold1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return empty list for anonymous without permission",
|
||||
req: &authzv1.ListRequest{
|
||||
Namespace: "org-12",
|
||||
Subject: "anonymous:0",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Verb: "get",
|
||||
},
|
||||
permissions: []accesscontrol.Permission{},
|
||||
expected: &authzv1.ListResponse{},
|
||||
},
|
||||
}
|
||||
t.Run("Anonymous permission list", func(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s := setupService()
|
||||
ctx := types.WithAuthInfo(context.Background(), callingService)
|
||||
store := &fakeStore{userPermissions: tc.permissions}
|
||||
s.store = store
|
||||
s.permissionStore = store
|
||||
s.identityStore = &fakeIdentityStore{teams: []int64{1, 2}}
|
||||
|
||||
resp, err := s.List(ctx, tc.req)
|
||||
require.NoError(t, err)
|
||||
require.ElementsMatch(t, resp.Items, tc.expected.Items)
|
||||
require.ElementsMatch(t, resp.Folders, tc.expected.Folders)
|
||||
|
||||
// Check cache
|
||||
perms, ok := s.permCache.Get(ctx, anonymousPermCacheKey("org-12", "dashboards:read"))
|
||||
require.True(t, ok)
|
||||
require.Len(t, perms, len(tc.expected.Items)+len(tc.expected.Folders))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
testCases = []testCase{
|
||||
{
|
||||
name: "should list permissions for rendering",
|
||||
req: &authzv1.ListRequest{
|
||||
Namespace: "org-12",
|
||||
Subject: "render:0",
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Verb: "get",
|
||||
},
|
||||
expected: &authzv1.ListResponse{
|
||||
All: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should deny rendering access to another app resources",
|
||||
req: &authzv1.ListRequest{
|
||||
Namespace: "org-12",
|
||||
Subject: "render:0",
|
||||
Group: "another.grafana.app",
|
||||
Resource: "dashboards",
|
||||
Verb: "get",
|
||||
},
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
t.Run("Rendering permission list", func(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
s := setupService()
|
||||
ctx := types.WithAuthInfo(context.Background(), callingService)
|
||||
|
||||
resp, err := s.List(ctx, tc.req)
|
||||
if tc.expectErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.expected.All, resp.All)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func setupService() *Service {
|
||||
cache := cache.NewLocalCache(cache.Config{Expiry: 5 * time.Minute, CleanupInterval: 5 * time.Minute})
|
||||
logger := log.New("authz-rbac-service")
|
||||
|
||||
Reference in New Issue
Block a user