From b1a26b12e4c30cbe00e074c3f97e58e9f970380a Mon Sep 17 00:00:00 2001 From: Jigar Joshi Date: Wed, 7 Jun 2023 20:46:06 -0700 Subject: [PATCH] fix: enforce project app key mapping (#1198) feat: Associate role with appkeys fix: Do not allow higher role app key created fix: Fixed test to follow project level authz fix: rebase fix: rebase fix: rebase fix: Fix the merge fix: User cannot invite user with higher role --- server/middleware/authz.go | 38 +- server/middleware/authz_test.go | 483 ++++++++++++------------ server/services/v1/auth/gotrue.go | 133 +++++-- server/services/v1/auth/gotrue_test.go | 2 +- server/services/v1/auth/user_manager.go | 9 + test/v1/server/auth_test.go | 189 +++++----- 6 files changed, 488 insertions(+), 366 deletions(-) diff --git a/server/middleware/authz.go b/server/middleware/authz.go index 505c3b6a..acc8df4c 100644 --- a/server/middleware/authz.go +++ b/server/middleware/authz.go @@ -23,6 +23,8 @@ import ( "github.com/tigrisdata/tigris/lib/container" "github.com/tigrisdata/tigris/server/config" "github.com/tigrisdata/tigris/server/request" + "github.com/tigrisdata/tigris/server/services/v1/auth" + "github.com/tigrisdata/tigris/server/types" "google.golang.org/grpc" ) @@ -32,12 +34,6 @@ const ( ) var ( - // role names. - readOnlyRoleName = "ro" - editorRoleName = "e" - ownerRoleName = "o" - ClusterAdminRoleName = "cluster_admin" - adminNamespaces = container.NewHashSet(config.DefaultConfig.Auth.AdminNamespaces...) readonlyMethods = container.NewHashSet( // db @@ -434,11 +430,12 @@ func authorize(ctx context.Context) (err error) { Msg("Empty role allowed for transition purpose") return nil } - // if !isAuthorizedProject(reqMetadata, accessToken) { - // authorizationErr = errors.PermissionDenied("You are not allowed to perform operation: %s", reqMetadata.GetFullMethod()) - //} var authorizationErr error - if !isAuthorizedOperation(reqMetadata.GetFullMethod(), role) { + if !isAuthorizedProject(reqMetadata, accessToken) { + authorizationErr = errors.PermissionDenied("You are not allowed to perform operation on this project: %s", reqMetadata.GetFullMethod()) + } + + if authorizationErr == nil && !isAuthorizedOperation(reqMetadata.GetFullMethod(), role) { authorizationErr = errors.PermissionDenied("You are not allowed to perform operation: %s", reqMetadata.GetFullMethod()) } @@ -457,6 +454,17 @@ func authorize(ctx context.Context) (err error) { return nil } +func isAuthorizedProject(reqMetadata *request.Metadata, accessToken *types.AccessToken) bool { + if reqMetadata.GetProject() != "" && accessToken.Project != "" && reqMetadata.GetProject() != accessToken.Project { + log.Error(). + Str("accessible_project", accessToken.Project). + Str("requested_project", reqMetadata.GetProject()). + Msg("Project mismatch") + return false + } + return true +} + func isAuthorizedOperation(method string, role string) bool { if methods := getMethodsForRole(role); methods != nil { return methods.Contains(method) @@ -466,13 +474,13 @@ func isAuthorizedOperation(method string, role string) bool { func getMethodsForRole(role string) *container.HashSet { switch role { - case ClusterAdminRoleName: + case auth.ClusterAdminRoleName: return &clusterAdminMethods - case ownerRoleName: + case auth.OwnerRoleName: return &ownerMethods - case editorRoleName: + case auth.EditorRoleName: return &editorMethods - case readOnlyRoleName: + case auth.ReadOnlyRoleName: return &readonlyMethods } return nil @@ -480,7 +488,7 @@ func getMethodsForRole(role string) *container.HashSet { func getRole(reqMetadata *request.Metadata) string { if isAdminNamespace(reqMetadata.GetNamespace()) { - return ClusterAdminRoleName + return auth.ClusterAdminRoleName } // empty role check for transition purpose diff --git a/server/middleware/authz_test.go b/server/middleware/authz_test.go index 497a2d8e..abe29b22 100644 --- a/server/middleware/authz_test.go +++ b/server/middleware/authz_test.go @@ -19,308 +19,309 @@ import ( "github.com/stretchr/testify/require" api "github.com/tigrisdata/tigris/api/server/v1" + "github.com/tigrisdata/tigris/server/services/v1/auth" ) func TestAuthzOwnerRole(t *testing.T) { - require.True(t, isAuthorizedOperation(api.BeginTransactionMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.CommitTransactionMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.RollbackTransactionMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.InsertMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ReplaceMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.UpdateMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ReadMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.CountMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.BuildCollectionIndexMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ExplainMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.SearchMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ImportMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.CreateOrUpdateCollectionMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.CreateOrUpdateCollectionsMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.DropCollectionMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ListProjectsMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ListCollectionsMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.CreateProjectMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteProjectMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.DescribeDatabaseMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.DescribeCollectionMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.CreateBranchMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteBranchMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ListBranchesMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.CreateAppKeyMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.UpdateAppKeyMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteAppKeyMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ListAppKeysMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.RotateAppKeySecretMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.CreateGlobalAppKeyMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.UpdateGlobalAppKeyMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteGlobalAppKeyMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ListGlobalAppKeysMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.RotateGlobalAppKeySecretMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.IndexCollection, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.SearchIndexCollectionMethodName, ownerRoleName)) + require.True(t, isAuthorizedOperation(api.BeginTransactionMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.CommitTransactionMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.RollbackTransactionMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.InsertMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ReplaceMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.UpdateMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ReadMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.CountMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.BuildCollectionIndexMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ExplainMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.SearchMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ImportMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.CreateOrUpdateCollectionMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.CreateOrUpdateCollectionsMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.DropCollectionMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ListProjectsMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ListCollectionsMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.CreateProjectMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteProjectMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.DescribeDatabaseMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.DescribeCollectionMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.CreateBranchMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteBranchMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ListBranchesMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.CreateAppKeyMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.UpdateAppKeyMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteAppKeyMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ListAppKeysMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.RotateAppKeySecretMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.CreateGlobalAppKeyMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.UpdateGlobalAppKeyMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteGlobalAppKeyMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ListGlobalAppKeysMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.RotateGlobalAppKeySecretMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.IndexCollection, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.SearchIndexCollectionMethodName, auth.OwnerRoleName)) // auth - require.True(t, isAuthorizedOperation(api.GetAccessTokenMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.CreateInvitationsMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteInvitationsMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ListInvitationsMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ListUsersMethodName, ownerRoleName)) + require.True(t, isAuthorizedOperation(api.GetAccessTokenMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.CreateInvitationsMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteInvitationsMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ListInvitationsMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ListUsersMethodName, auth.OwnerRoleName)) // billing - require.True(t, isAuthorizedOperation(api.ListInvoicesMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.BillingGetUsageMethodName, ownerRoleName)) + require.True(t, isAuthorizedOperation(api.ListInvoicesMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.BillingGetUsageMethodName, auth.OwnerRoleName)) // cache - require.True(t, isAuthorizedOperation(api.CreateCacheMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ListCachesMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteCacheMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.SetMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.GetSetMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.GetMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.DelMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.KeysMethodName, ownerRoleName)) + require.True(t, isAuthorizedOperation(api.CreateCacheMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ListCachesMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteCacheMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.SetMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.GetSetMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.GetMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.DelMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.KeysMethodName, auth.OwnerRoleName)) // health - require.True(t, isAuthorizedOperation(api.HealthMethodName, ownerRoleName)) + require.True(t, isAuthorizedOperation(api.HealthMethodName, auth.OwnerRoleName)) // management - require.True(t, isAuthorizedOperation(api.InsertUserMetadataMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.GetUserMetadataMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.UpdateUserMetadataMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.InsertNamespaceMetadataMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.GetNamespaceMetadataMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.UpdateNamespaceMetadataMethodName, ownerRoleName)) + require.True(t, isAuthorizedOperation(api.InsertUserMetadataMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.GetUserMetadataMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.UpdateUserMetadataMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.InsertNamespaceMetadataMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.GetNamespaceMetadataMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.UpdateNamespaceMetadataMethodName, auth.OwnerRoleName)) // observability - require.True(t, isAuthorizedOperation(api.QueryTimeSeriesMetricsMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.QuotaLimitsMetricsMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.QuotaUsageMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.GetInfoMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.WhoAmIMethodName, ownerRoleName)) + require.True(t, isAuthorizedOperation(api.QueryTimeSeriesMetricsMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.QuotaLimitsMetricsMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.QuotaUsageMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.GetInfoMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.WhoAmIMethodName, auth.OwnerRoleName)) // realtime - require.True(t, isAuthorizedOperation(api.PresenceMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.GetRTChannelMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.GetRTChannelsMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ReadMessagesMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.MessagesMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ListSubscriptionsMethodName, ownerRoleName)) + require.True(t, isAuthorizedOperation(api.PresenceMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.GetRTChannelMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.GetRTChannelsMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ReadMessagesMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.MessagesMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ListSubscriptionsMethodName, auth.OwnerRoleName)) // search - require.True(t, isAuthorizedOperation(api.CreateOrUpdateIndexMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.GetIndexMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteIndexMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.ListIndexesMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.SearchGetMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.SearchCreateById, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.SearchCreate, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.SearchCreateOrReplace, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.SearchUpdate, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.SearchDeleteByQuery, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.SearchSearch, ownerRoleName)) + require.True(t, isAuthorizedOperation(api.CreateOrUpdateIndexMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.GetIndexMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteIndexMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.ListIndexesMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.SearchGetMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.SearchCreateById, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.SearchCreate, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.SearchCreateOrReplace, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.SearchUpdate, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.SearchDeleteByQuery, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.SearchSearch, auth.OwnerRoleName)) // negative - require.False(t, isAuthorizedOperation(api.VerifyInvitationMethodName, ownerRoleName)) - require.False(t, isAuthorizedOperation(api.CreateNamespaceMethodName, ownerRoleName)) - require.False(t, isAuthorizedOperation(api.ListNamespacesMethodName, ownerRoleName)) - require.False(t, isAuthorizedOperation(api.DeleteNamespaceMethodName, ownerRoleName)) + require.False(t, isAuthorizedOperation(api.VerifyInvitationMethodName, auth.OwnerRoleName)) + require.False(t, isAuthorizedOperation(api.CreateNamespaceMethodName, auth.OwnerRoleName)) + require.False(t, isAuthorizedOperation(api.ListNamespacesMethodName, auth.OwnerRoleName)) + require.False(t, isAuthorizedOperation(api.DeleteNamespaceMethodName, auth.OwnerRoleName)) } func TestAuthzEditorRole(t *testing.T) { // db - require.True(t, isAuthorizedOperation(api.BeginTransactionMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.CommitTransactionMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.RollbackTransactionMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.InsertMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.ReplaceMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.UpdateMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.ReadMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.CountMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.BuildCollectionIndexMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.ExplainMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.SearchMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.ImportMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.CreateOrUpdateCollectionMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.CreateOrUpdateCollectionsMethodName, ownerRoleName)) - require.True(t, isAuthorizedOperation(api.DropCollectionMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.ListProjectsMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.ListCollectionsMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.CreateProjectMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteProjectMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.DescribeDatabaseMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.DescribeCollectionMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.CreateBranchMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteBranchMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.ListBranchesMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.CreateAppKeyMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.UpdateAppKeyMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteAppKeyMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.ListAppKeysMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.RotateAppKeySecretMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.IndexCollection, editorRoleName)) - require.True(t, isAuthorizedOperation(api.SearchIndexCollectionMethodName, editorRoleName)) + require.True(t, isAuthorizedOperation(api.BeginTransactionMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.CommitTransactionMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.RollbackTransactionMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.InsertMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.ReplaceMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.UpdateMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.ReadMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.CountMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.BuildCollectionIndexMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.ExplainMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.SearchMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.ImportMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.CreateOrUpdateCollectionMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.CreateOrUpdateCollectionsMethodName, auth.OwnerRoleName)) + require.True(t, isAuthorizedOperation(api.DropCollectionMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.ListProjectsMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.ListCollectionsMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.CreateProjectMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteProjectMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.DescribeDatabaseMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.DescribeCollectionMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.CreateBranchMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteBranchMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.ListBranchesMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.CreateAppKeyMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.UpdateAppKeyMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteAppKeyMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.ListAppKeysMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.RotateAppKeySecretMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.IndexCollection, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.SearchIndexCollectionMethodName, auth.EditorRoleName)) // auth - require.True(t, isAuthorizedOperation(api.GetAccessTokenMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.CreateInvitationsMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteInvitationsMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.ListInvitationsMethodName, editorRoleName)) + require.True(t, isAuthorizedOperation(api.GetAccessTokenMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.CreateInvitationsMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteInvitationsMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.ListInvitationsMethodName, auth.EditorRoleName)) // cache - require.True(t, isAuthorizedOperation(api.CreateCacheMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.ListCachesMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteCacheMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.SetMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.GetSetMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.GetMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.DelMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.KeysMethodName, editorRoleName)) + require.True(t, isAuthorizedOperation(api.CreateCacheMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.ListCachesMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteCacheMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.SetMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.GetSetMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.GetMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.DelMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.KeysMethodName, auth.EditorRoleName)) // health - require.True(t, isAuthorizedOperation(api.HealthMethodName, editorRoleName)) + require.True(t, isAuthorizedOperation(api.HealthMethodName, auth.EditorRoleName)) // management - require.True(t, isAuthorizedOperation(api.InsertUserMetadataMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.GetUserMetadataMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.UpdateUserMetadataMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.InsertNamespaceMetadataMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.GetNamespaceMetadataMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.UpdateNamespaceMetadataMethodName, editorRoleName)) + require.True(t, isAuthorizedOperation(api.InsertUserMetadataMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.GetUserMetadataMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.UpdateUserMetadataMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.InsertNamespaceMetadataMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.GetNamespaceMetadataMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.UpdateNamespaceMetadataMethodName, auth.EditorRoleName)) // observability - require.True(t, isAuthorizedOperation(api.QueryTimeSeriesMetricsMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.QuotaLimitsMetricsMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.QuotaUsageMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.GetInfoMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.WhoAmIMethodName, editorRoleName)) + require.True(t, isAuthorizedOperation(api.QueryTimeSeriesMetricsMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.QuotaLimitsMetricsMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.QuotaUsageMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.GetInfoMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.WhoAmIMethodName, auth.EditorRoleName)) // realtime - require.True(t, isAuthorizedOperation(api.PresenceMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.GetRTChannelMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.GetRTChannelsMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.ReadMessagesMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.MessagesMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.ListSubscriptionsMethodName, editorRoleName)) + require.True(t, isAuthorizedOperation(api.PresenceMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.GetRTChannelMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.GetRTChannelsMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.ReadMessagesMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.MessagesMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.ListSubscriptionsMethodName, auth.EditorRoleName)) // search - require.True(t, isAuthorizedOperation(api.CreateOrUpdateIndexMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.GetIndexMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.DeleteIndexMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.ListIndexesMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.SearchGetMethodName, editorRoleName)) - require.True(t, isAuthorizedOperation(api.SearchCreateById, editorRoleName)) - require.True(t, isAuthorizedOperation(api.SearchCreate, editorRoleName)) - require.True(t, isAuthorizedOperation(api.SearchCreateOrReplace, editorRoleName)) - require.True(t, isAuthorizedOperation(api.SearchUpdate, editorRoleName)) - require.True(t, isAuthorizedOperation(api.SearchDeleteByQuery, editorRoleName)) - require.True(t, isAuthorizedOperation(api.SearchSearch, editorRoleName)) + require.True(t, isAuthorizedOperation(api.CreateOrUpdateIndexMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.GetIndexMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.DeleteIndexMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.ListIndexesMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.SearchGetMethodName, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.SearchCreateById, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.SearchCreate, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.SearchCreateOrReplace, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.SearchUpdate, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.SearchDeleteByQuery, auth.EditorRoleName)) + require.True(t, isAuthorizedOperation(api.SearchSearch, auth.EditorRoleName)) // negative - require.False(t, isAuthorizedOperation(api.ListUsersMethodName, editorRoleName)) - require.False(t, isAuthorizedOperation(api.VerifyInvitationMethodName, editorRoleName)) - require.False(t, isAuthorizedOperation(api.CreateNamespaceMethodName, editorRoleName)) - require.False(t, isAuthorizedOperation(api.ListNamespacesMethodName, editorRoleName)) - require.False(t, isAuthorizedOperation(api.DeleteNamespaceMethodName, editorRoleName)) - - require.False(t, isAuthorizedOperation(api.CreateGlobalAppKeyMethodName, editorRoleName)) - require.False(t, isAuthorizedOperation(api.UpdateGlobalAppKeyMethodName, editorRoleName)) - require.False(t, isAuthorizedOperation(api.DeleteGlobalAppKeyMethodName, editorRoleName)) - require.False(t, isAuthorizedOperation(api.ListGlobalAppKeysMethodName, editorRoleName)) - require.False(t, isAuthorizedOperation(api.RotateGlobalAppKeySecretMethodName, editorRoleName)) - require.False(t, isAuthorizedOperation(api.BillingGetUsageMethodName, editorRoleName)) + require.False(t, isAuthorizedOperation(api.ListUsersMethodName, auth.EditorRoleName)) + require.False(t, isAuthorizedOperation(api.VerifyInvitationMethodName, auth.EditorRoleName)) + require.False(t, isAuthorizedOperation(api.CreateNamespaceMethodName, auth.EditorRoleName)) + require.False(t, isAuthorizedOperation(api.ListNamespacesMethodName, auth.EditorRoleName)) + require.False(t, isAuthorizedOperation(api.DeleteNamespaceMethodName, auth.EditorRoleName)) + + require.False(t, isAuthorizedOperation(api.CreateGlobalAppKeyMethodName, auth.EditorRoleName)) + require.False(t, isAuthorizedOperation(api.UpdateGlobalAppKeyMethodName, auth.EditorRoleName)) + require.False(t, isAuthorizedOperation(api.DeleteGlobalAppKeyMethodName, auth.EditorRoleName)) + require.False(t, isAuthorizedOperation(api.ListGlobalAppKeysMethodName, auth.EditorRoleName)) + require.False(t, isAuthorizedOperation(api.RotateGlobalAppKeySecretMethodName, auth.EditorRoleName)) + require.False(t, isAuthorizedOperation(api.BillingGetUsageMethodName, auth.EditorRoleName)) } func TestAuthzReadOnlyRole(t *testing.T) { // db - require.True(t, isAuthorizedOperation(api.ReadMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.CountMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.ExplainMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.SearchMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.ListProjectsMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.DescribeDatabaseMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.DescribeCollectionMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.ListBranchesMethodName, readOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.ReadMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.CountMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.ExplainMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.SearchMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.ListProjectsMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.DescribeDatabaseMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.DescribeCollectionMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.ListBranchesMethodName, auth.ReadOnlyRoleName)) // auth - require.True(t, isAuthorizedOperation(api.ListAppKeysMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.GetAccessTokenMethodName, readOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.ListAppKeysMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.GetAccessTokenMethodName, auth.ReadOnlyRoleName)) // cache - require.True(t, isAuthorizedOperation(api.ListCachesMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.GetMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.KeysMethodName, readOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.ListCachesMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.GetMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.KeysMethodName, auth.ReadOnlyRoleName)) // health - require.True(t, isAuthorizedOperation(api.HealthMethodName, readOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.HealthMethodName, auth.ReadOnlyRoleName)) // management - require.True(t, isAuthorizedOperation(api.GetUserMetadataMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.InsertUserMetadataMethodName, readOnlyRoleName)) // exception for user preferences - require.True(t, isAuthorizedOperation(api.UpdateUserMetadataMethodName, readOnlyRoleName)) // exception for user preferences + require.True(t, isAuthorizedOperation(api.GetUserMetadataMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.InsertUserMetadataMethodName, auth.ReadOnlyRoleName)) // exception for user preferences + require.True(t, isAuthorizedOperation(api.UpdateUserMetadataMethodName, auth.ReadOnlyRoleName)) // exception for user preferences // observability - require.True(t, isAuthorizedOperation(api.QueryTimeSeriesMetricsMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.QuotaLimitsMetricsMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.QuotaUsageMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.GetInfoMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.WhoAmIMethodName, readOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.QueryTimeSeriesMetricsMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.QuotaLimitsMetricsMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.QuotaUsageMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.GetInfoMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.WhoAmIMethodName, auth.ReadOnlyRoleName)) // realtime - require.True(t, isAuthorizedOperation(api.ReadMessagesMethodName, readOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.ReadMessagesMethodName, auth.ReadOnlyRoleName)) // search - require.True(t, isAuthorizedOperation(api.GetIndexMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.ListIndexesMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.SearchGetMethodName, readOnlyRoleName)) - require.True(t, isAuthorizedOperation(api.SearchSearch, readOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.GetIndexMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.ListIndexesMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.SearchGetMethodName, auth.ReadOnlyRoleName)) + require.True(t, isAuthorizedOperation(api.SearchSearch, auth.ReadOnlyRoleName)) // negative - require.False(t, isAuthorizedOperation(api.BeginTransactionMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.CommitTransactionMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.RollbackTransactionMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.InsertMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.UpdateMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.DeleteMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.CreateProjectMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.CreateOrUpdateCollectionMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.CreateOrUpdateCollectionsMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.DeleteProjectMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.DropCollectionMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.CreateCacheMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.SetMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.GetSetMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.DelMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.CreateBranchMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.DeleteBranchMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.CreateAppKeyMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.UpdateAppKeyMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.DeleteAppKeyMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.RotateAppKeySecretMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.CreateInvitationsMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.DeleteInvitationsMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.IndexCollection, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.SearchIndexCollectionMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.ListUsersMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.VerifyInvitationMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.CreateNamespaceMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.ListNamespacesMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.DeleteNamespaceMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.CreateGlobalAppKeyMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.UpdateGlobalAppKeyMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.DeleteGlobalAppKeyMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.ListGlobalAppKeysMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.RotateGlobalAppKeySecretMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.BillingGetUsageMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.ListInvoicesMethodName, readOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.BeginTransactionMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.CommitTransactionMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.RollbackTransactionMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.InsertMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.UpdateMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.DeleteMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.CreateProjectMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.CreateOrUpdateCollectionMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.CreateOrUpdateCollectionsMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.DeleteProjectMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.DropCollectionMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.CreateCacheMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.SetMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.GetSetMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.DelMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.CreateBranchMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.DeleteBranchMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.CreateAppKeyMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.UpdateAppKeyMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.DeleteAppKeyMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.RotateAppKeySecretMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.CreateInvitationsMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.DeleteInvitationsMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.IndexCollection, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.SearchIndexCollectionMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.ListUsersMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.VerifyInvitationMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.CreateNamespaceMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.ListNamespacesMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.DeleteNamespaceMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.CreateGlobalAppKeyMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.UpdateGlobalAppKeyMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.DeleteGlobalAppKeyMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.ListGlobalAppKeysMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.RotateGlobalAppKeySecretMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.BillingGetUsageMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.ListInvoicesMethodName, auth.ReadOnlyRoleName)) // search - require.False(t, isAuthorizedOperation(api.CreateOrUpdateIndexMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.DeleteIndexMethodName, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.SearchCreateById, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.SearchCreate, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.SearchCreateOrReplace, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.SearchUpdate, readOnlyRoleName)) - require.False(t, isAuthorizedOperation(api.SearchDeleteByQuery, readOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.CreateOrUpdateIndexMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.DeleteIndexMethodName, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.SearchCreateById, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.SearchCreate, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.SearchCreateOrReplace, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.SearchUpdate, auth.ReadOnlyRoleName)) + require.False(t, isAuthorizedOperation(api.SearchDeleteByQuery, auth.ReadOnlyRoleName)) } diff --git a/server/services/v1/auth/gotrue.go b/server/services/v1/auth/gotrue.go index f5616f80..40cae455 100644 --- a/server/services/v1/auth/gotrue.go +++ b/server/services/v1/auth/gotrue.go @@ -55,6 +55,11 @@ const ( AppKeyTypeCredentials = "credentials" AppKeyTypeApiKey = "api_key" + + ClusterAdminRoleName = "cluster_admin" + ReadOnlyRoleName = "ro" + EditorRoleName = "e" + OwnerRoleName = "o" ) type gotrue struct { @@ -101,7 +106,9 @@ type UserAppData struct { Name string `json:"name"` Description string `json:"description"` Project string `json:"tigris_project"` - KeyType string `json:"key_type"` + // Gotrue has multiple roles in user. We use it as an array but app key has single role (first and only element of array) + Roles []string `json:"roles"` + KeyType string `json:"key_type"` } type GetUserResp struct { @@ -115,8 +122,15 @@ type GetUserResp struct { AppMetaData *UserAppData `db:"app_metadata" json:"app_metadata"` } -// returns currentSub, creationTime, error. -func _createAppKey(ctx context.Context, clientId string, clientSecret string, g *gotrue, keyName string, keyDescription string, project string, keyType string) (string, int64, error) { +type keyInfo struct { + name string + description string + project string + role string + keyType string +} + +func _createAppKey(ctx context.Context, clientId string, clientSecret string, g *gotrue, info keyInfo) (string, int64, error) { currentSub, err := GetCurrentSub(ctx) if err != nil { log.Err(err).Msg("Failed to create application: reason - unable to extract current sub") @@ -130,8 +144,8 @@ func _createAppKey(ctx context.Context, clientId string, clientSecret string, g } // make gotrue call - email := _getEmail(clientId, keyType, g) creationTime := time.Now().UnixMilli() + email := _getEmail(clientId, info.keyType, g) payloadBytes, err := jsoniter.Marshal(CreateUserPayload{ Email: email, Password: clientSecret, @@ -139,10 +153,11 @@ func _createAppKey(ctx context.Context, clientId string, clientSecret string, g CreatedAt: creationTime, CreatedBy: currentSub, TigrisNamespace: currentNamespace, - Name: keyName, - Description: keyDescription, - Project: project, - KeyType: keyType, + Name: info.name, + Description: info.description, + Project: info.project, + Roles: []string{info.role}, + KeyType: info.keyType, }, }) if err != nil { @@ -157,12 +172,73 @@ func _createAppKey(ctx context.Context, clientId string, clientSecret string, g Str("namespace", currentNamespace). Str("sub", currentSub). Str("client_id", clientId). - Str("key_type", keyType). Str(Component, AppKey). + Str("key_type", info.keyType). Msg("appkey created") return currentSub, creationTime, nil } +func _getEmail(clientId string, appKeyType string, g *gotrue) string { + suffix := g.AuthConfig.Gotrue.UsernameSuffix + if appKeyType == AppKeyTypeApiKey { + suffix = g.AuthConfig.ApiKeys.EmailSuffix + } + return fmt.Sprintf("%s%s", clientId, suffix) +} + +func validateRole(ctx context.Context, role string) error { + if role == "" { + return errors.InvalidArgument("Role must be specified") + } + + if !(role == OwnerRoleName || role == EditorRoleName || role == ReadOnlyRoleName) { + log.Error().Str("input_role", role).Msg("Unsupported role received as input") + return errors.InvalidArgument("Invalid role input. Supported roles are [owner, editor, readonly]") + } + + reqMetadata, err := request.GetRequestMetadataFromContext(ctx) + if err != nil { + log.Err(err).Msg("Failed to retrieve role of current user") + return errors.Internal("Failed to validate role") + } + + currentRole := reqMetadata.GetRole() + if currentRole == "" { + // for migration purpose + // TODO: clean up post authz enablement + currentRole = OwnerRoleName + } + currentRoleWeight := getRoleWeightage(currentRole) + if currentRoleWeight == -1 { + return errors.Internal("Failed to current validate role") + } + + inputRoleWeight := getRoleWeightage(role) + if inputRoleWeight == -1 { + return errors.InvalidArgument("Failed to input validate role") + } + + if inputRoleWeight > currentRoleWeight { + log.Error().Str("input_role", role). + Str("current_role", currentRole). + Msg("Requested higher role than own role") + return errors.PermissionDenied("Requested higher role than user's own role") + } + return nil +} + +func getRoleWeightage(role string) int { + switch role { + case OwnerRoleName: + return 300 + case EditorRoleName: + return 200 + case ReadOnlyRoleName: + return 100 + } + return -1 +} + func (g *gotrue) CreateAppKey(ctx context.Context, req *api.CreateAppKeyRequest) (*api.CreateAppKeyResponse, error) { if req.GetProject() == "" { return nil, errors.InvalidArgument("Project must be specified") @@ -170,7 +246,10 @@ func (g *gotrue) CreateAppKey(ctx context.Context, req *api.CreateAppKeyRequest) if req.GetKeyType() != "" && !(req.GetKeyType() == AppKeyTypeCredentials || req.GetKeyType() == AppKeyTypeApiKey) { return nil, errors.InvalidArgument("app key supported types are [credentials, api_key]") } - + err := validateRole(ctx, req.GetRole()) + if err != nil { + return nil, err + } appKeyType := AppKeyTypeCredentials if req.GetKeyType() != "" { appKeyType = req.GetKeyType() @@ -183,7 +262,13 @@ func (g *gotrue) CreateAppKey(ctx context.Context, req *api.CreateAppKeyRequest) clientId = generateClientId(ApiKeyPrefix, config.DefaultConfig.Auth.ApiKeys.Length) clientSecret = config.DefaultConfig.Auth.ApiKeys.UserPassword } - currentSub, creationTime, err := _createAppKey(ctx, clientId, clientSecret, g, req.GetName(), req.GetDescription(), req.GetProject(), appKeyType) + currentSub, creationTime, err := _createAppKey(ctx, clientId, clientSecret, g, keyInfo{ + name: req.GetName(), + description: req.GetDescription(), + project: req.GetProject(), + role: req.GetRole(), + keyType: appKeyType, + }) if err != nil { return nil, err } @@ -191,6 +276,7 @@ func (g *gotrue) CreateAppKey(ctx context.Context, req *api.CreateAppKeyRequest) if req.GetKeyType() == AppKeyTypeApiKey { clientSecret = "" // hide the secret } + return &api.CreateAppKeyResponse{ CreatedAppKey: &api.AppKey{ Id: clientId, @@ -200,6 +286,7 @@ func (g *gotrue) CreateAppKey(ctx context.Context, req *api.CreateAppKeyRequest) CreatedAt: creationTime, CreatedBy: currentSub, Project: req.GetProject(), + Role: req.GetRole(), }, }, nil } @@ -208,7 +295,13 @@ func (g *gotrue) CreateGlobalAppKey(ctx context.Context, req *api.CreateGlobalAp clientId := generateClientId(GlobalClientIdPrefix, config.DefaultConfig.Auth.Gotrue.ClientIdLength) clientSecret := generateClientSecret(g, GlobalClientSecretPrefix) - currentSub, creationTime, err := _createAppKey(ctx, clientId, clientSecret, g, req.GetName(), req.GetDescription(), "", AppKeyTypeCredentials) + currentSub, creationTime, err := _createAppKey(ctx, clientId, clientSecret, g, keyInfo{ + name: req.GetName(), + description: req.GetDescription(), + project: "", + role: OwnerRoleName, + keyType: AppKeyTypeCredentials, + }) if err != nil { return nil, err } @@ -225,16 +318,8 @@ func (g *gotrue) CreateGlobalAppKey(ctx context.Context, req *api.CreateGlobalAp }, nil } -func _getEmail(clientId string, appKeyType string, g *gotrue) string { - suffix := g.AuthConfig.Gotrue.UsernameSuffix - if appKeyType == AppKeyTypeApiKey { - suffix = g.AuthConfig.ApiKeys.EmailSuffix - } - return fmt.Sprintf("%s%s", clientId, suffix) -} - -func _updateAppKey(ctx context.Context, g *gotrue, id string, name string, description string, appKeyType string) error { - email := _getEmail(id, appKeyType, g) +func _updateAppKey(ctx context.Context, g *gotrue, id string, name string, description string, keyType string) error { + email := _getEmail(id, keyType, g) updateAppKeyUrl := fmt.Sprintf("%s/admin/users/%s", g.AuthConfig.Gotrue.URL, email) currentSub, err := GetCurrentSub(ctx) @@ -512,6 +597,7 @@ type appKeyInternal struct { CreatedBy string CreatedAt int64 Project string + Role string KeyType string } @@ -615,6 +701,7 @@ func _listAppKeys(ctx context.Context, g *gotrue, project string, keyType string // parse string time to millis using rfc3339 format createdAtMillis = readDate(createdAtStr) } + appKey := appKeyInternal{ Id: clientId, Name: appMetadata.Name, @@ -623,6 +710,7 @@ func _listAppKeys(ctx context.Context, g *gotrue, project string, keyType string CreatedBy: appMetadata.CreatedBy, CreatedAt: createdAtMillis, Project: appMetadata.Project, + Role: appMetadata.Roles[0], } appKey.KeyType = AppKeyTypeCredentials if appMetadata.KeyType != "" { @@ -647,6 +735,7 @@ func (g *gotrue) ListAppKeys(ctx context.Context, req *api.ListAppKeysRequest) ( CreatedAt: internalAppKey.CreatedAt, CreatedBy: internalAppKey.CreatedBy, Project: internalAppKey.Project, + Role: internalAppKey.Role, KeyType: internalAppKey.KeyType, } // expose secret in case of credentials, for backward compatibility defaults to credentials diff --git a/server/services/v1/auth/gotrue_test.go b/server/services/v1/auth/gotrue_test.go index ead86e2b..75d957f6 100644 --- a/server/services/v1/auth/gotrue_test.go +++ b/server/services/v1/auth/gotrue_test.go @@ -44,7 +44,7 @@ func TestClientCredentialsCharacters(t *testing.T) { // generate 100 random creds and inspect them for i := 0; i < 100; i++ { - id := generateClientId(ClientIdPrefix, config.DefaultConfig.Auth.Gotrue.ClientIdLength) + id := generateClientId(ClientIdPrefix, g.AuthConfig.Gotrue.ClientIdLength) secret := generateClientSecret(g, ClientSecretPrefix) require.Truef(t, containsFromTheseChars(id, idCharSet), "Invalid character in id found, id=%s", id) require.Truef(t, containsFromTheseChars(secret, secretCharSet), "Invalid character in secret found, id=%s", secret) diff --git a/server/services/v1/auth/user_manager.go b/server/services/v1/auth/user_manager.go index 9851ac65..1752c5fb 100644 --- a/server/services/v1/auth/user_manager.go +++ b/server/services/v1/auth/user_manager.go @@ -91,6 +91,15 @@ func createInvitation(ctx context.Context, email string, role string, invitation return errors.InvalidArgument("Role must be specified") } + if !(role == OwnerRoleName || role == EditorRoleName || role == ReadOnlyRoleName) { + return errors.InvalidArgument("Supported roles are [editor(e), readonly(ro), owner(o)]") + } + + err := validateRole(ctx, role) + if err != nil { + return err + } + namespace, err := request.GetNamespace(ctx) if err != nil { log.Err(err).Msg("Failed to get namespace while creating invitation") diff --git a/test/v1/server/auth_test.go b/test/v1/server/auth_test.go index ef63b92b..15f82bd4 100644 --- a/test/v1/server/auth_test.go +++ b/test/v1/server/auth_test.go @@ -155,7 +155,7 @@ func TestGoTrueAuthProvider(t *testing.T) { createProject2(t, testProject, token).Status(http.StatusOK) // create app key - createdAppKey := createAppKey(e2, token, "test_key", "auth_test", "") + createdAppKey := createAppKey(e2, token, "test_key", "auth_test", auth.OwnerRoleName, auth.AppKeyTypeCredentials) require.NotNil(t, createdAppKey) id := createdAppKey.Object().Value("id").String() secret := createdAppKey.Object().Value("secret").String() @@ -236,47 +236,12 @@ func TestGoTrueAuthProvider(t *testing.T) { require.True(t, deletedResponse.Raw()) } -func cleanupAppKeys(e *httpexpect.Expect, token string, project string) { - globalAppKeys := e.GET(globalAppKeysOperation("get")). - WithHeader(Authorization, Bearer+token). - Expect(). - Status(http.StatusOK). - JSON(). - Object() - - if len(globalAppKeys.Keys().Raw()) > 0 { - arr := globalAppKeys.Value("app_keys").Array() - for i := 0; i < int(arr.Length().Raw()); i++ { - k := arr.Element(i) - deleteGlobalAppKey(e, token, k.Object().Value("id").String().Raw()) - } - } - - if project != "" { - localAppKeys := e.GET(appKeysOperation(project, "get")). - WithHeader(Authorization, Bearer+token). - Expect(). - Status(http.StatusOK). - JSON(). - Object() - - if len(localAppKeys.Keys().Raw()) > 0 { - arr := localAppKeys.Value("app_keys").Array() - for _, lKey := range arr.Iter() { - deleteAppKey(e, token, lKey.Object().Value("id").String().Raw(), project) - } - } - } -} - func TestGlobalAppKeys(t *testing.T) { e := expectAuthLow(t) token := readToken(t, RSATokenFilePath) createTestNamespace(t, token) - cleanupAppKeys(e, token, "") - // create app key createdAppKey := createGlobalAppKey(e, token, "test_key") require.NotNil(t, createdAppKey) @@ -319,7 +284,6 @@ func TestGlobalAppKeys(t *testing.T) { require.Equal(t, id.Raw(), rotatedKey.Object().Value("id").Raw()) require.NotEqual(t, secret.Raw(), rotatedKey.Object().Value("secret").Raw()) require.Equal(t, 50+len(auth.GlobalClientSecretPrefix), len(rotatedKey.Object().Value("secret").String().Raw())) - // list globalAppKeys := e.GET(globalAppKeysOperation("get")). WithHeader(Authorization, Bearer+token). @@ -370,9 +334,9 @@ func TestGlobalAndLocalAppKeys(t *testing.T) { _ = createGlobalAppKey(e, token, "g2") // create three local app keys - _ = createAppKey(e, token, "l1", proj, "") - _ = createAppKey(e, token, "l2", proj, "") - _ = createAppKey(e, token, "l3", proj, "") + _ = createAppKey(e, token, "l1", proj, auth.OwnerRoleName, auth.AppKeyTypeCredentials) + _ = createAppKey(e, token, "l2", proj, auth.OwnerRoleName, auth.AppKeyTypeCredentials) + _ = createAppKey(e, token, "l3", proj, auth.OwnerRoleName, auth.AppKeyTypeCredentials) // list globalAppKeys := e.GET(globalAppKeysOperation("get")). @@ -405,41 +369,24 @@ func TestGlobalAndLocalAppKeys(t *testing.T) { require.Equal(t, int32(1), localKeysMap["l3"]) } -func deleteAppKey(e *httpexpect.Expect, token string, id string, project string) *httpexpect.Response { - deleteGlobalAppKeyPayload := Map{ - "id": id, - } - return e.DELETE(appKeysOperation(project, "delete")). - WithHeader(Authorization, Bearer+token).WithJSON(deleteGlobalAppKeyPayload). - Expect() -} - -func deleteGlobalAppKey(e *httpexpect.Expect, token string, id string) *httpexpect.Response { - deleteGlobalAppKeyPayload := Map{ - "id": id, - } - return e.DELETE(globalAppKeysOperation("delete")). - WithHeader(Authorization, Bearer+token).WithJSON(deleteGlobalAppKeyPayload). - Expect() -} - -func createAppKey(e *httpexpect.Expect, token string, name string, project string, keyType string) *httpexpect.Value { +func createAppKey2(e *httpexpect.Expect, token string, name string, project string, role string, keyType string) *httpexpect.Response { createAppKeyPayload := Map{ "name": name, "description": "This key is used for integration test purpose.", "project": project, - } - if keyType != "" { - createAppKeyPayload["key_type"] = keyType + "role": role, + "key_type": keyType, } return e.POST(appKeysOperation(project, "create")). WithHeader(Authorization, Bearer+token).WithJSON(createAppKeyPayload). - Expect(). + Expect() +} +func createAppKey(e *httpexpect.Expect, token string, name string, project string, role string, keyType string) *httpexpect.Value { + return createAppKey2(e, token, name, project, role, keyType). Status(http.StatusOK). JSON(). Object().Value("created_app_key") } - func createGlobalAppKey(e *httpexpect.Expect, token string, name string) *httpexpect.Value { createGlobalAppKeyPayload := Map{ "name": name, @@ -453,6 +400,24 @@ func createGlobalAppKey(e *httpexpect.Expect, token string, name string) *httpex Object().Value("created_app_key") } +func deleteAppKey(e *httpexpect.Expect, token string, id string, project string) *httpexpect.Response { + deleteAppKeyPayload := Map{ + "id": id, + } + return e.DELETE(appKeysOperation(project, "delete")). + WithHeader(Authorization, Bearer+token).WithJSON(deleteAppKeyPayload). + Expect() +} + +func deleteGlobalAppKey(e *httpexpect.Expect, token string, id string) *httpexpect.Response { + deleteGlobalAppKeyPayload := Map{ + "id": id, + } + return e.DELETE(globalAppKeysOperation("delete")). + WithHeader(Authorization, Bearer+token).WithJSON(deleteGlobalAppKeyPayload). + Expect() +} + func TestMultipleAppsCreation(t *testing.T) { testStartTime := time.Now() @@ -464,7 +429,7 @@ func TestMultipleAppsCreation(t *testing.T) { createProject2(t, testProject, token).Status(http.StatusOK) for i := 0; i < 5; i++ { - createdAppKey := createAppKey(e2, token, fmt.Sprintf("test_key_%d", i), testProject, "") + createdAppKey := createAppKey(e2, token, fmt.Sprintf("test_key_%d", i), testProject, auth.OwnerRoleName, auth.AppKeyTypeCredentials) require.NotNil(t, createdAppKey) generatedClientId := createdAppKey.Object().Value("id").String().Raw() generatedClientSecret := createdAppKey.Object().Value("secret").String().Raw() @@ -486,7 +451,38 @@ func TestMultipleAppsCreation(t *testing.T) { require.True(t, createdAt >= testStartTime.UnixMilli()) } } +func cleanupAppKeys(e *httpexpect.Expect, token string, project string) { + globalAppKeys := e.GET(globalAppKeysOperation("get")). + WithHeader(Authorization, Bearer+token). + Expect(). + Status(http.StatusOK). + JSON(). + Object() + + if len(globalAppKeys.Keys().Raw()) > 0 { + arr := globalAppKeys.Value("app_keys").Array() + for i := 0; i < int(arr.Length().Raw()); i++ { + k := arr.Element(i) + deleteGlobalAppKey(e, token, k.Object().Value("id").String().Raw()) + } + } + + if project != "" { + localAppKeys := e.GET(appKeysOperation(project, "get")). + WithHeader(Authorization, Bearer+token). + Expect(). + Status(http.StatusOK). + JSON(). + Object() + if len(localAppKeys.Keys().Raw()) > 0 { + arr := localAppKeys.Value("app_keys").Array() + for _, lKey := range arr.Iter() { + deleteAppKey(e, token, lKey.Object().Value("id").String().Raw(), project) + } + } + } +} func TestListAppKeys(t *testing.T) { e2 := expectAuthLow(t) testProject := "auth_test" @@ -500,7 +496,7 @@ func TestListAppKeys(t *testing.T) { for i := 0; i < 5; i++ { projectForThisKey := fmt.Sprintf("%s%d", testProject, i%2) - createdAppKey := createAppKey(e2, token, fmt.Sprintf("test_key_%d", i), projectForThisKey, "") + createdAppKey := createAppKey(e2, token, fmt.Sprintf("test_key_%d", i), projectForThisKey, auth.OwnerRoleName, auth.AppKeyTypeCredentials) require.NotNil(t, createdAppKey) } @@ -548,7 +544,7 @@ func TestApiKeyUsage(t *testing.T) { e := expectAuthLow(t) testProject := "TestApiKey" - createdApiKey := createAppKey(e, token, "test_api_key", testProject, auth.AppKeyTypeApiKey) + createdApiKey := createAppKey(e, token, "test_api_key", testProject, auth.OwnerRoleName, auth.AppKeyTypeApiKey) require.NotNil(t, createdApiKey) key := createdApiKey.Object().Value("id").String().Raw() require.Equal(t, 125, len(key)) // 120 fixed + 5 prefix @@ -565,8 +561,8 @@ func TestApiKeyCrud(t *testing.T) { testProject := "TestApiKeyCrud" // create - _ = createAppKey(e, token, "test_api_key_1", testProject, auth.AppKeyTypeApiKey) - createdApiKey := createAppKey(e, token, "test_api_key_2", testProject, auth.AppKeyTypeApiKey) + _ = createAppKey(e, token, "test_api_key_1", testProject, auth.OwnerRoleName, auth.AppKeyTypeApiKey) + createdApiKey := createAppKey(e, token, "test_api_key_2", testProject, auth.OwnerRoleName, auth.AppKeyTypeApiKey) require.NotNil(t, createdApiKey) key := createdApiKey.Object().Value("id").String().Raw() require.Equal(t, 125, len(key)) // 120 fixed + 5 prefix @@ -662,7 +658,7 @@ func TestCreateAccessToken(t *testing.T) { testProject := "auth_test" token := readToken(t, RSATokenFilePath) createTestNamespace(t, token) - createdAppKey := createAppKey(e2, token, "test_key", testProject, "") + createdAppKey := createAppKey(e2, token, "test_key", testProject, auth.OwnerRoleName, auth.AppKeyTypeCredentials) require.NotNil(t, createdAppKey) id := createdAppKey.Object().Value("id").String() @@ -681,11 +677,11 @@ func TestCreateAccessToken(t *testing.T) { // use access token deleteProject2(t, "new-project-1", token) - createProject2(t, "new-project-1", accessToken).Status(http.StatusOK) + listCollections(t, testProject, accessToken).Status(http.StatusOK) deleteProject2(t, "new-project-2", token) // use access token bypassing auth caches - _ = e2.POST(getProjectURL("new-project-2", "create")). + _ = e2.GET(listCollectionsUrl(testProject)). WithHeader(Authorization, Bearer+accessToken). WithHeader(api.HeaderBypassAuthCache, "true"). Expect(). @@ -693,7 +689,6 @@ func TestCreateAccessToken(t *testing.T) { deleteProject2(t, "new-project-3", token) // use access token with cache - createProject2(t, "new-project-3", accessToken).Status(http.StatusOK) } func TestCreateGlobalAccessToken(t *testing.T) { @@ -732,9 +727,7 @@ func TestCreateGlobalAccessToken(t *testing.T) { createProject2(t, "TestCreateGlobalAccessToken-project-1", accessToken).Status(http.StatusOK) deleteProject2(t, "TestCreateGlobalAccessToken-project-1", token) - deleteProject2(t, "TestCreateGlobalAccessToken-project-2", token) //cleanup - // use access token bypassing auth caches _ = e2.POST(getProjectURL("TestCreateGlobalAccessToken-project-2", "create")). WithHeader(Authorization, Bearer+accessToken). @@ -780,42 +773,40 @@ func TestAuthFailure(t *testing.T) { func TestUserInvitations(t *testing.T) { token := readToken(t, RSATokenFilePath) createTestNamespace(t, token) - deleteUserInvitations(t, "a@hello.com", "PENDING", token) deleteUserInvitations(t, "b@hello.com", "PENDING", token) deleteUserInvitations(t, "c@hello.com", "PENDING", token) deleteUserInvitations(t, "d@hello.com", "PENDING", token) deleteUserInvitations(t, "b@hello.com", "ACCEPTED", token) - - createUserInvitation(t, "a@hello.com", "editor_a", "TestUserInvitations", token). + createUserInvitation(t, "a@hello.com", "e", "TestUserInvitations", token). Status(http.StatusOK). JSON(). Object(). Value("status"). String(). Equal(auth.CreatedStatus) - createUserInvitation(t, "b@hello.com", "editor_b", "TestUserInvitations", token). + createUserInvitation(t, "b@hello.com", "e", "TestUserInvitations", token). Status(http.StatusOK). JSON(). Object(). Value("status"). String(). Equal(auth.CreatedStatus) - createUserInvitation(t, "c@hello.com", "editor_c", "TestUserInvitations", token). + createUserInvitation(t, "c@hello.com", "e", "TestUserInvitations", token). Status(http.StatusOK). JSON(). Object(). Value("status"). String(). Equal(auth.CreatedStatus) - createUserInvitation(t, "d@hello.com", "editor_c", "TestUserInvitations", token). + createUserInvitation(t, "d@hello.com", "e", "TestUserInvitations", token). Status(http.StatusOK). JSON(). Object(). Value("status"). String(). Equal(auth.CreatedStatus) - createUserInvitation(t, "a@hello.com", "editor_a", "TestUserInvitations", token). + createUserInvitation(t, "a@hello.com", "e", "TestUserInvitations", token). Status(http.StatusOK). JSON(). Object(). @@ -826,7 +817,7 @@ func TestUserInvitations(t *testing.T) { listUserInvitations1 := listUserInvitations(t, token) listUserInvitations1.Status(http.StatusOK) invitations1 := listUserInvitations1.JSON().Object().Value("invitations").Array() - //require.Equal(t, float64(4), invitations1.Length().Raw()) + require.Equal(t, float64(4), invitations1.Length().Raw()) emailCountMap1 := make(map[string]int) for _, value := range invitations1.Iter() { @@ -894,7 +885,6 @@ func TestUserInvitations(t *testing.T) { func TestAuthzOwner(t *testing.T) { token := readToken(t, OwnerTokenFilePath) createTestNamespace(t, token) - deleteProject2(t, "TestAuthzOwner", token) // create project should be allowed @@ -908,9 +898,7 @@ func TestAuthzOwner(t *testing.T) { func TestAuthzEditor(t *testing.T) { token := readToken(t, EditorTokenFilePath) createTestNamespace(t, token) - deleteProject2(t, "TestAuthzEditor", token) - // create project should be allowed createProject2(t, "TestAuthzEditor", token). Status(http.StatusOK) @@ -936,6 +924,24 @@ func TestAuthzReadonly(t *testing.T) { resp) } +func TestAppKeyCreationWithHigherRole(t *testing.T) { + token := readToken(t, EditorTokenFilePath) + proj := "TestAppKeyCreationWithHigherRole" + createTestNamespace(t, token) + deleteProject2(t, proj, token) + // create project should be allowed + createProject2(t, proj, token). + Status(http.StatusOK) + + // creating appkey with owner role shuoldn't be allowed + e := expectAuthLow(t) + + createOwnerAppKeyRes := createAppKey2(e, token, "test_key_name", proj, auth.OwnerRoleName, auth.AppKeyTypeCredentials) + createOwnerAppKeyRes.Status(http.StatusForbidden) + require.Equal(t, "PERMISSION_DENIED", createOwnerAppKeyRes.JSON().Object().Value("error").Object().Value("code").String().Raw()) + require.Equal(t, "Requested higher role than user's own role", createOwnerAppKeyRes.JSON().Object().Value("error").Object().Value("message").String().Raw()) +} + func createProject2(t *testing.T, projectName string, token string) *httpexpect.Response { e2 := expectAuthLow(t) @@ -998,7 +1004,12 @@ func listUserInvitations(t *testing.T, token string) *httpexpect.Response { func listProjects(t *testing.T, token string) *httpexpect.Response { e2 := expectAuthLow(t) - return e2.GET(listProjectsUrl()). + return e2.GET(listProjectsUrl()).WithHeader(Authorization, Bearer+token).Expect() +} + +func listCollections(t *testing.T, project string, token string) *httpexpect.Response { + e2 := expectAuthLow(t) + return e2.GET(listCollectionsUrl(project)). WithHeader(Authorization, Bearer+token). Expect() } @@ -1033,3 +1044,7 @@ func invitationUrl(operation string) string { func listProjectsUrl() string { return "/v1/projects" } + +func listCollectionsUrl(project string) string { + return fmt.Sprintf("/v1/projects/%s/database/collections", project) +}