2020-08-13 17:39:48 -04:00
// Copyright (c) HashiCorp, Inc.
2023-01-06 17:27:19 -06:00
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2020-07-22 00:14:28 -04:00
package scopes_test
import (
"context"
2020-08-13 17:39:48 -04:00
"fmt"
2024-01-02 17:19:29 -08:00
"slices"
2020-07-22 00:14:28 -04:00
"strings"
"testing"
2023-05-25 15:43:52 +01:00
"unicode"
2020-07-22 00:14:28 -04:00
"github.com/golang/protobuf/ptypes/wrappers"
2020-08-05 10:19:55 -07:00
"github.com/google/go-cmp/cmp"
2023-10-31 13:44:17 +00:00
"github.com/google/go-cmp/cmp/cmpopts"
2022-10-28 15:52:06 -04:00
"github.com/hashicorp/boundary/globals"
2024-01-02 17:19:29 -08:00
"github.com/hashicorp/boundary/internal/auth/ldap"
2022-11-16 17:17:28 -06:00
"github.com/hashicorp/boundary/internal/auth/oidc"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller"
2022-05-09 12:45:10 -07:00
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/scopes"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/db"
2024-01-02 17:19:29 -08:00
"github.com/hashicorp/boundary/internal/errors"
2020-08-10 17:15:02 -04:00
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
2022-11-16 17:17:28 -06:00
authpb "github.com/hashicorp/boundary/internal/gen/controller/auth"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/iam"
2022-11-16 17:17:28 -06:00
"github.com/hashicorp/boundary/internal/kms"
2021-05-04 19:57:35 -05:00
"github.com/hashicorp/boundary/internal/perms"
2024-01-02 17:19:29 -08:00
"github.com/hashicorp/boundary/internal/requests"
2022-11-16 17:17:28 -06:00
"github.com/hashicorp/boundary/internal/server"
2020-08-10 17:15:02 -04:00
"github.com/hashicorp/boundary/internal/types/scope"
2021-08-26 10:44:41 -04:00
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
2022-11-16 17:17:28 -06:00
wrappingKms "github.com/hashicorp/go-kms-wrapping/extras/kms/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2020-07-22 00:14:28 -04:00
"google.golang.org/genproto/protobuf/field_mask"
"google.golang.org/grpc/codes"
2024-01-03 17:54:00 -08:00
"google.golang.org/grpc/status"
2020-07-22 00:14:28 -04:00
"google.golang.org/protobuf/proto"
2020-08-05 10:19:55 -07:00
"google.golang.org/protobuf/testing/protocmp"
2021-01-20 10:53:01 -05:00
"google.golang.org/protobuf/types/known/structpb"
2021-04-14 16:19:02 -05:00
"google.golang.org/protobuf/types/known/timestamppb"
2020-07-22 00:14:28 -04:00
"google.golang.org/protobuf/types/known/wrapperspb"
)
2024-01-18 17:29:53 -08:00
var (
testAuthorizedOrgActions = [] string { "no-op" , "read" , "update" , "delete" , "attach-storage-policy" , "detach-storage-policy" }
testAuthorizedPrjActions = [] string { "no-op" , "read" , "update" , "delete" }
testAuthorizedGlobalActions = [] string { "no-op" , "read" , "update" , "attach-storage-policy" , "detach-storage-policy" }
)
2021-04-23 09:13:37 -05:00
2022-11-16 17:17:28 -06:00
func createDefaultScopesRepoAndKms ( t * testing . T ) ( * iam . Scope , * iam . Scope , func () ( * iam . Repository , error ), * kms . Kms ) {
2020-07-22 00:14:28 -04:00
t . Helper ()
conn , _ := db . TestSetup ( t , "postgres" )
wrap := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
iamRepo := iam . TestRepo ( t , conn , wrap )
2020-07-22 00:14:28 -04:00
repoFn := func () ( * iam . Repository , error ) {
2020-08-19 16:47:18 -04:00
return iamRepo , nil
2020-07-22 00:14:28 -04:00
}
2022-11-16 17:17:28 -06:00
kms := kms . TestKms ( t , conn , wrap )
2020-07-22 00:14:28 -04:00
2020-08-19 16:47:18 -04:00
oRes , pRes := iam . TestScopes ( t , iamRepo )
2020-07-22 00:14:28 -04:00
2021-03-25 03:15:43 -04:00
oRes . Name = "defaultOrg"
oRes . Description = "defaultOrg"
2020-07-22 00:14:28 -04:00
repo , err := repoFn ()
require . NoError ( t , err )
2020-08-07 12:55:02 -04:00
oRes , _ , err = repo . UpdateScope ( context . Background (), oRes , 1 , [] string { "Name" , "Description" })
2020-07-22 00:14:28 -04:00
require . NoError ( t , err )
pRes . Name = "defaultProj"
pRes . Description = "defaultProj"
repo , err = repoFn ()
require . NoError ( t , err )
2020-08-07 12:55:02 -04:00
pRes , _ , err = repo . UpdateScope ( context . Background (), pRes , 1 , [] string { "Name" , "Description" })
2020-07-22 00:14:28 -04:00
require . NoError ( t , err )
2022-11-16 17:17:28 -06:00
return oRes , pRes , repoFn , kms
2020-07-22 00:14:28 -04:00
}
2021-04-08 20:54:32 -04:00
var globalAuthorizedCollectionActions = map [ string ] * structpb . ListValue {
2024-01-31 09:54:12 -08:00
"aliases" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
2021-04-08 20:54:32 -04:00
"auth-methods" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
"auth-tokens" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "list" ),
},
},
"groups" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
2023-11-22 18:02:26 +00:00
"policies" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
2021-04-08 20:54:32 -04:00
"roles" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
"scopes" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
2023-10-31 13:44:17 +00:00
structpb . NewStringValue ( "destroy-key-version" ),
2021-04-08 20:54:32 -04:00
structpb . NewStringValue ( "list" ),
2023-10-31 13:44:17 +00:00
structpb . NewStringValue ( "list-key-version-destruction-jobs" ),
2022-11-16 17:17:28 -06:00
structpb . NewStringValue ( "list-keys" ),
structpb . NewStringValue ( "rotate-keys" ),
2021-04-08 20:54:32 -04:00
},
},
2023-05-12 08:02:18 -07:00
"session-recordings" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "list" ),
},
},
2023-05-03 11:12:41 -07:00
"storage-buckets" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
2021-04-08 20:54:32 -04:00
"users" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
2022-06-05 16:43:02 -04:00
"workers" : {
Values : [] * structpb . Value {
2022-09-02 14:32:41 -04:00
structpb . NewStringValue ( "create:controller-led" ),
2022-06-07 11:50:30 -04:00
structpb . NewStringValue ( "create:worker-led" ),
2022-06-05 16:43:02 -04:00
structpb . NewStringValue ( "list" ),
2022-09-08 13:19:21 -04:00
structpb . NewStringValue ( "read-certificate-authority" ),
structpb . NewStringValue ( "reinitialize-certificate-authority" ),
2022-06-05 16:43:02 -04:00
},
},
2021-04-08 20:54:32 -04:00
}
2021-01-20 10:53:01 -05:00
var orgAuthorizedCollectionActions = map [ string ] * structpb . ListValue {
"auth-methods" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
"auth-tokens" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "list" ),
},
},
"groups" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
2023-11-22 18:02:26 +00:00
"policies" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
2021-01-20 10:53:01 -05:00
"roles" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
"scopes" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
2023-10-31 13:44:17 +00:00
structpb . NewStringValue ( "destroy-key-version" ),
2021-01-20 10:53:01 -05:00
structpb . NewStringValue ( "list" ),
2023-10-31 13:44:17 +00:00
structpb . NewStringValue ( "list-key-version-destruction-jobs" ),
2022-11-16 17:17:28 -06:00
structpb . NewStringValue ( "list-keys" ),
structpb . NewStringValue ( "rotate-keys" ),
2021-01-20 10:53:01 -05:00
},
},
2023-05-12 08:02:18 -07:00
"session-recordings" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "list" ),
},
},
2023-05-03 11:12:41 -07:00
"storage-buckets" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
2021-01-20 10:53:01 -05:00
"users" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
}
var projectAuthorizedCollectionActions = map [ string ] * structpb . ListValue {
2021-09-13 13:56:44 -04:00
"credential-stores" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
2021-01-20 10:53:01 -05:00
"groups" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
"host-catalogs" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
"roles" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
2021-09-13 20:26:42 -04:00
"sessions" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "list" ),
},
},
2022-11-16 17:17:28 -06:00
"scopes" : {
Values : [] * structpb . Value {
2023-10-31 13:44:17 +00:00
structpb . NewStringValue ( "destroy-key-version" ),
structpb . NewStringValue ( "list-key-version-destruction-jobs" ),
2022-11-16 17:17:28 -06:00
structpb . NewStringValue ( "list-keys" ),
structpb . NewStringValue ( "rotate-keys" ),
},
},
2021-01-20 10:53:01 -05:00
"targets" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "create" ),
structpb . NewStringValue ( "list" ),
},
},
}
2020-07-22 00:14:28 -04:00
func TestGet ( t * testing . T ) {
2022-11-16 17:17:28 -06:00
org , proj , repoFn , kms := createDefaultScopesRepoAndKms ( t )
2020-07-22 00:14:28 -04:00
toMerge := & pbs . GetScopeRequest {
Id : proj . GetPublicId (),
}
oScope := & pb . Scope {
2021-01-20 10:53:01 -05:00
Id : org . GetPublicId (),
ScopeId : org . GetParentId (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : "global" , Type : scope . Global . String (), Name : scope . Global . String (), Description : "Global Scope" },
2021-01-20 10:53:01 -05:00
Name : & wrapperspb . StringValue { Value : org . GetName ()},
Description : & wrapperspb . StringValue { Value : org . GetDescription ()},
CreatedTime : org . CreateTime . GetTimestamp (),
UpdatedTime : org . UpdateTime . GetTimestamp (),
Version : 2 ,
Type : scope . Org . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedOrgActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : orgAuthorizedCollectionActions ,
2020-07-22 00:14:28 -04:00
}
pScope := & pb . Scope {
2021-01-20 10:53:01 -05:00
Id : proj . GetPublicId (),
ScopeId : proj . GetParentId (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : oScope . Id , Type : scope . Org . String (), ParentScopeId : scope . Global . String (), Name : "defaultOrg" , Description : "defaultOrg" },
2021-01-20 10:53:01 -05:00
Name : & wrapperspb . StringValue { Value : proj . GetName ()},
Description : & wrapperspb . StringValue { Value : proj . GetDescription ()},
CreatedTime : proj . CreateTime . GetTimestamp (),
UpdatedTime : proj . UpdateTime . GetTimestamp (),
Version : 2 ,
Type : scope . Project . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedPrjActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : projectAuthorizedCollectionActions ,
2020-07-22 00:14:28 -04:00
}
cases := [] struct {
name string
scopeId string
req * pbs . GetScopeRequest
res * pbs . GetScopeResponse
2020-09-23 08:10:27 -07:00
err error
2020-07-22 00:14:28 -04:00
}{
{
name : "Get an existing org" ,
scopeId : "global" ,
req : & pbs . GetScopeRequest { Id : org . GetPublicId ()},
res : & pbs . GetScopeResponse { Item : oScope },
},
{
name : "Get a non existing org" ,
scopeId : "global" ,
req : & pbs . GetScopeRequest { Id : "o_DoesntExis" },
res : nil ,
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . NotFound ),
2020-07-22 00:14:28 -04:00
},
{
name : "Get an existing project" ,
scopeId : org . GetPublicId (),
req : & pbs . GetScopeRequest { Id : proj . GetPublicId ()},
res : & pbs . GetScopeResponse { Item : pScope },
},
{
name : "Get a non existing project" ,
scopeId : org . GetPublicId (),
2022-11-16 17:17:28 -06:00
req : & pbs . GetScopeRequest { Id : "p_DoesntExist" },
2020-07-22 00:14:28 -04:00
res : nil ,
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . NotFound ),
2020-07-22 00:14:28 -04:00
},
{
name : "Wrong id prefix" ,
scopeId : org . GetPublicId (),
req : & pbs . GetScopeRequest { Id : "j_1234567890" },
res : nil ,
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-07-22 00:14:28 -04:00
},
{
name : "space in id" ,
scopeId : org . GetPublicId (),
req : & pbs . GetScopeRequest { Id : "p_1 23456789" },
res : nil ,
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-07-22 00:14:28 -04:00
},
}
for _ , tc := range cases {
t . Run ( tc . name , func ( t * testing . T ) {
2020-09-23 08:10:27 -07:00
assert , require := assert . New ( t ), require . New ( t )
2020-07-22 00:14:28 -04:00
req := proto . Clone ( toMerge ).( * pbs . GetScopeRequest )
proto . Merge ( req , tc . req )
2024-01-03 17:54:00 -08:00
s , err := scopes . NewServiceFn ( context . Background (), repoFn , kms , 1000 )
2020-07-22 00:14:28 -04:00
require . NoError ( err , "Couldn't create new project service." )
2021-03-25 03:15:43 -04:00
got , gErr := s . GetScope ( auth . DisabledAuthTestContext ( repoFn , tc . scopeId ), req )
2020-09-23 08:10:27 -07:00
if tc . err != nil {
require . Error ( gErr )
assert . True ( errors . Is ( gErr , tc . err ), "GetScope(%+v) got error\n%v, wanted\n%v" , req , gErr , tc . err )
}
2023-10-31 13:44:17 +00:00
assert . Empty ( cmp . Diff (
tc . res ,
got ,
protocmp . Transform (),
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
cmpopts . SortSlices ( func ( a , b protocmp . Message ) bool {
return a . String () < b . String ()
}),
), "GetScope(%q) got response\n%q, wanted\n%q" , req , got , tc . res )
2020-07-22 00:14:28 -04:00
})
}
}
func TestList ( t * testing . T ) {
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2020-07-22 00:14:28 -04:00
conn , _ := db . TestSetup ( t , "postgres" )
wrap := db . TestWrapper ( t )
2020-08-19 16:47:18 -04:00
iamRepo := iam . TestRepo ( t , conn , wrap )
2020-07-22 00:14:28 -04:00
repoFn := func () ( * iam . Repository , error ) {
2020-08-19 16:47:18 -04:00
return iamRepo , nil
2020-07-22 00:14:28 -04:00
}
repo , err := repoFn ()
require . NoError ( t , err )
2022-11-16 17:17:28 -06:00
kms := kms . TestKms ( t , conn , wrap )
2020-07-22 00:14:28 -04:00
2020-08-19 16:47:18 -04:00
oNoProjects , p1 := iam . TestScopes ( t , repo )
2020-07-22 00:14:28 -04:00
_ , err = repo . DeleteScope ( context . Background (), p1 . GetPublicId ())
require . NoError ( t , err )
2020-08-19 16:47:18 -04:00
oWithProjects , p2 := iam . TestScopes ( t , repo )
2020-07-22 00:14:28 -04:00
_ , err = repo . DeleteScope ( context . Background (), p2 . GetPublicId ())
require . NoError ( t , err )
2023-01-06 17:27:19 -06:00
outputFields := new ( perms . OutputFields ). SelfOrDefaults ( globals . AnyAuthenticatedUserId )
2020-07-22 00:14:28 -04:00
var initialOrgs [] * pb . Scope
2021-03-25 03:15:43 -04:00
globalScope := & pb . ScopeInfo { Id : "global" , Type : scope . Global . String (), Name : scope . Global . String (), Description : "Global Scope" }
2023-01-06 17:27:19 -06:00
oNoProjectsProto , err := scopes . ToProto ( context . Background (), oNoProjects , handlers . WithOutputFields ( outputFields ))
2021-05-04 19:57:35 -05:00
require . NoError ( t , err )
2020-07-22 00:14:28 -04:00
oNoProjectsProto . Scope = globalScope
2024-01-18 17:29:53 -08:00
oNoProjectsProto . AuthorizedActions = testAuthorizedOrgActions
2021-01-20 10:53:01 -05:00
oNoProjectsProto . AuthorizedCollectionActions = orgAuthorizedCollectionActions
2023-01-06 17:27:19 -06:00
oWithProjectsProto , err := scopes . ToProto ( context . Background (), oWithProjects , handlers . WithOutputFields ( outputFields ))
2021-05-04 19:57:35 -05:00
require . NoError ( t , err )
2020-07-22 00:14:28 -04:00
oWithProjectsProto . Scope = globalScope
2024-01-18 17:29:53 -08:00
oWithProjectsProto . AuthorizedActions = testAuthorizedOrgActions
2021-01-20 10:53:01 -05:00
oWithProjectsProto . AuthorizedCollectionActions = orgAuthorizedCollectionActions
2020-07-22 00:14:28 -04:00
initialOrgs = append ( initialOrgs , oNoProjectsProto , oWithProjectsProto )
2024-01-02 17:19:29 -08:00
// Reverse slice since we order by create time (newest first)
slices . Reverse ( initialOrgs )
2020-07-22 00:14:28 -04:00
cases := [] struct {
name string
scopeId string
req * pbs . ListScopesRequest
res * pbs . ListScopesResponse
2020-09-23 08:10:27 -07:00
err error
2020-07-22 00:14:28 -04:00
}{
{
2020-07-22 15:10:44 -04:00
name : "List initial orgs" ,
2020-07-22 00:14:28 -04:00
scopeId : scope . Global . String (),
2020-07-22 15:10:44 -04:00
req : & pbs . ListScopesRequest { ScopeId : "global" },
2024-01-02 17:19:29 -08:00
res : & pbs . ListScopesResponse {
Items : initialOrgs ,
EstItemCount : 2 ,
ResponseType : "complete" ,
SortBy : "created_time" ,
SortDir : "desc" ,
},
2020-07-22 00:14:28 -04:00
},
{
name : "List No Projects" ,
scopeId : oNoProjects . GetPublicId (),
2020-07-22 15:10:44 -04:00
req : & pbs . ListScopesRequest { ScopeId : oNoProjects . GetPublicId ()},
2024-01-02 17:19:29 -08:00
res : & pbs . ListScopesResponse {
ResponseType : "complete" ,
SortBy : "created_time" ,
SortDir : "desc" ,
},
2020-07-22 00:14:28 -04:00
},
2020-10-07 17:43:12 -07:00
{
name : "Cant List Project Scopes" ,
scopeId : p1 . GetPublicId (),
req : & pbs . ListScopesRequest { ScopeId : p1 . GetPublicId ()},
2020-10-13 13:56:21 -04:00
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-10-07 17:43:12 -07:00
},
2021-02-22 10:09:25 -07:00
{
name : "Filter To Single Org" ,
scopeId : scope . Global . String (),
req : & pbs . ListScopesRequest { ScopeId : "global" , Filter : fmt . Sprintf ( `"/item/id"==%q` , initialOrgs [ 1 ]. GetId ())},
2024-01-02 17:19:29 -08:00
res : & pbs . ListScopesResponse {
Items : initialOrgs [ 1 : 2 ],
EstItemCount : 1 ,
ResponseType : "complete" ,
SortBy : "created_time" ,
SortDir : "desc" ,
},
2021-02-22 10:09:25 -07:00
},
2020-07-22 00:14:28 -04:00
}
for _ , tc := range cases {
t . Run ( tc . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2024-01-03 17:54:00 -08:00
s , err := scopes . NewServiceFn ( context . Background (), repoFn , kms , 1000 )
2020-07-22 00:14:28 -04:00
require . NoError ( err , "Couldn't create new role service." )
2021-04-14 16:19:02 -05:00
// Test with non-anonymous listing first
2021-05-04 19:57:35 -05:00
got , gErr := s . ListScopes ( auth . DisabledAuthTestContext ( repoFn , tc . scopeId ), tc . req )
2020-09-23 08:10:27 -07:00
if tc . err != nil {
require . Error ( gErr )
assert . True ( errors . Is ( gErr , tc . err ), "ListScopes(%+v) got error\n%v, wanted\n%v" , tc . req , gErr , tc . err )
2021-04-14 16:19:02 -05:00
return
2020-09-23 08:10:27 -07:00
}
2024-01-02 17:19:29 -08:00
assert . Empty (
cmp . Diff (
got ,
tc . res ,
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
cmpopts . SortSlices ( func ( a , b protocmp . Message ) bool {
return a . String () < b . String ()
}),
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListScopesResponse {}, "list_token" ),
),
)
2021-04-14 16:19:02 -05:00
// Now test with anonymous listing
2022-10-28 15:52:06 -04:00
got , gErr = s . ListScopes ( auth . DisabledAuthTestContext ( repoFn , tc . scopeId , auth . WithUserId ( globals . AnonymousUserId )), tc . req )
2021-04-14 16:19:02 -05:00
require . NoError ( gErr )
2021-05-04 19:57:35 -05:00
assert . Len ( got . Items , len ( tc . res . Items ))
2021-04-14 16:19:02 -05:00
for _ , item := range got . GetItems () {
assert . Nil ( item . CreatedTime )
assert . Nil ( item . UpdatedTime )
assert . Empty ( item . Version )
}
2020-07-22 00:14:28 -04:00
})
}
var wantOrgs [] * pb . Scope
for i := 0 ; i < 10 ; i ++ {
2023-06-06 17:09:08 -04:00
newO , err := iam . NewOrg ( ctx )
2020-07-22 00:14:28 -04:00
require . NoError ( t , err )
2023-06-06 17:09:08 -04:00
o , err := repo . CreateScope ( ctx , newO , "" )
2020-07-22 00:14:28 -04:00
require . NoError ( t , err )
wantOrgs = append ( wantOrgs , & pb . Scope {
2021-01-20 10:53:01 -05:00
Id : o . GetPublicId (),
ScopeId : globalScope . GetId (),
Scope : globalScope ,
CreatedTime : o . GetCreateTime (). GetTimestamp (),
UpdatedTime : o . GetUpdateTime (). GetTimestamp (),
Version : 1 ,
Type : scope . Org . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedOrgActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : orgAuthorizedCollectionActions ,
2020-07-22 00:14:28 -04:00
})
}
2024-01-02 17:19:29 -08:00
// Reverse slice since we order by create time (newest first)
slices . Reverse ( wantOrgs )
2020-07-22 00:14:28 -04:00
wantOrgs = append ( wantOrgs , initialOrgs ... )
var wantProjects [] * pb . Scope
for i := 0 ; i < 10 ; i ++ {
2023-06-06 17:09:08 -04:00
newP , err := iam . NewProject ( ctx , oWithProjects . GetPublicId ())
2020-07-22 00:14:28 -04:00
require . NoError ( t , err )
2023-06-06 17:09:08 -04:00
p , err := repo . CreateScope ( ctx , newP , "" )
2020-07-22 00:14:28 -04:00
require . NoError ( t , err )
wantProjects = append ( wantProjects , & pb . Scope {
2021-01-20 10:53:01 -05:00
Id : p . GetPublicId (),
ScopeId : oWithProjects . GetPublicId (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : oWithProjects . GetPublicId (), Type : scope . Org . String (), ParentScopeId : scope . Global . String ()},
2021-01-20 10:53:01 -05:00
CreatedTime : p . GetCreateTime (). GetTimestamp (),
UpdatedTime : p . GetUpdateTime (). GetTimestamp (),
Version : 1 ,
Type : scope . Project . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedPrjActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : projectAuthorizedCollectionActions ,
2020-07-22 00:14:28 -04:00
})
}
2024-01-02 17:19:29 -08:00
// Reverse slice since we order by create time (newest first)
slices . Reverse ( wantProjects )
2020-07-22 00:14:28 -04:00
2021-01-25 19:37:40 -05:00
totalScopes := append ( wantOrgs , wantProjects ... )
2020-07-22 00:14:28 -04:00
cases = [] struct {
name string
scopeId string
req * pbs . ListScopesRequest
res * pbs . ListScopesResponse
2020-09-23 08:10:27 -07:00
err error
2020-07-22 00:14:28 -04:00
}{
{
name : "List Many Orgs" ,
scopeId : scope . Global . String (),
2020-07-22 15:10:44 -04:00
req : & pbs . ListScopesRequest { ScopeId : "global" },
2024-01-02 17:19:29 -08:00
res : & pbs . ListScopesResponse {
Items : wantOrgs ,
EstItemCount : 12 ,
ResponseType : "complete" ,
SortBy : "created_time" ,
SortDir : "desc" ,
},
2020-07-22 00:14:28 -04:00
},
{
name : "List Many Projects" ,
scopeId : oWithProjects . GetPublicId (),
2020-07-22 15:10:44 -04:00
req : & pbs . ListScopesRequest { ScopeId : oWithProjects . GetPublicId ()},
2024-01-02 17:19:29 -08:00
res : & pbs . ListScopesResponse {
Items : wantProjects ,
EstItemCount : 10 ,
ResponseType : "complete" ,
SortBy : "created_time" ,
SortDir : "desc" ,
},
2020-07-22 00:14:28 -04:00
},
2021-01-25 19:37:40 -05:00
{
name : "List Global Recursively" ,
scopeId : scope . Global . String (),
req : & pbs . ListScopesRequest { ScopeId : scope . Global . String (), Recursive : true },
2024-01-02 17:19:29 -08:00
res : & pbs . ListScopesResponse {
Items : totalScopes ,
EstItemCount : 22 ,
ResponseType : "complete" ,
SortBy : "created_time" ,
SortDir : "desc" ,
},
2021-01-25 19:37:40 -05:00
},
2021-02-22 10:09:25 -07:00
{
name : "Filter To Orgs" ,
scopeId : scope . Global . String (),
req : & pbs . ListScopesRequest { ScopeId : scope . Global . String (), Recursive : true , Filter : fmt . Sprintf ( `"/item/scope/type"==%q` , scope . Global . String ())},
2024-01-02 17:19:29 -08:00
res : & pbs . ListScopesResponse {
Items : wantOrgs ,
EstItemCount : 12 ,
ResponseType : "complete" ,
SortBy : "created_time" ,
SortDir : "desc" ,
},
2021-02-22 10:09:25 -07:00
},
{
name : "Filter To Projects" ,
scopeId : scope . Global . String (),
req : & pbs . ListScopesRequest { ScopeId : scope . Global . String (), Recursive : true , Filter : fmt . Sprintf ( `"/item/scope/type"==%q` , scope . Org . String ())},
2024-01-02 17:19:29 -08:00
res : & pbs . ListScopesResponse {
Items : wantProjects ,
EstItemCount : 10 ,
ResponseType : "complete" ,
SortBy : "created_time" ,
SortDir : "desc" ,
},
2021-02-22 10:09:25 -07:00
},
{
name : "Filter Bad Format" ,
req : & pbs . ListScopesRequest { ScopeId : scope . Global . String (), Filter : `"//id/"=="bad"` },
err : handlers . InvalidArgumentErrorf ( "bad format" , nil ),
},
2020-07-22 00:14:28 -04:00
}
for _ , tc := range cases {
t . Run ( tc . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2024-01-03 17:54:00 -08:00
s , err := scopes . NewServiceFn ( context . Background (), repoFn , kms , 1000 )
2024-01-02 17:19:29 -08:00
require . NoError ( err , "Couldn't create new scope service." )
2020-07-22 00:14:28 -04:00
2021-04-14 16:19:02 -05:00
// Test with non-anonymous listing first
2021-05-04 19:57:35 -05:00
got , gErr := s . ListScopes ( auth . DisabledAuthTestContext ( repoFn , tc . scopeId ), tc . req )
2020-09-23 08:10:27 -07:00
if tc . err != nil {
require . Error ( gErr )
assert . True ( errors . Is ( gErr , tc . err ), "ListScopes(%+v) got error\n%v, wanted\n%v" , tc . req , gErr , tc . err )
2021-04-14 16:19:02 -05:00
return
2020-09-23 08:10:27 -07:00
}
2021-04-14 16:19:02 -05:00
require . NoError ( gErr )
2024-01-02 17:19:29 -08:00
assert . Empty (
cmp . Diff (
got ,
tc . res ,
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
cmpopts . SortSlices ( func ( a , b protocmp . Message ) bool {
return a . String () < b . String ()
}),
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListScopesResponse {}, "list_token" ),
),
)
2021-04-14 16:19:02 -05:00
// Now test with anonymous listing
2022-10-28 15:52:06 -04:00
got , gErr = s . ListScopes ( auth . DisabledAuthTestContext ( repoFn , tc . scopeId , auth . WithUserId ( globals . AnonymousUserId )), tc . req )
2021-04-14 16:19:02 -05:00
require . NoError ( gErr )
2021-05-04 19:57:35 -05:00
assert . Len ( got . Items , len ( tc . res . Items ))
2021-04-14 16:19:02 -05:00
for _ , item := range got . GetItems () {
assert . Nil ( item . CreatedTime )
assert . Nil ( item . UpdatedTime )
assert . Empty ( item . Version )
}
2020-07-22 00:14:28 -04:00
})
}
}
2024-01-02 17:19:29 -08:00
func TestListPagination ( t * testing . T ) {
// Set database read timeout to avoid duplicates in response
oldReadTimeout := globals . RefreshReadLookbackDuration
globals . RefreshReadLookbackDuration = 0
t . Cleanup ( func () {
globals . RefreshReadLookbackDuration = oldReadTimeout
})
ctx := context . Background ()
conn , _ := db . TestSetup ( t , "postgres" )
sqlDb , err := conn . SqlDB ( ctx )
require . NoError ( t , err )
wrapper := db . TestWrapper ( t )
kms := kms . TestKms ( t , conn , wrapper )
rw := db . New ( conn )
repoFn := func () ( * iam . Repository , error ) {
return iam . TestRepo ( t , conn , wrapper ), nil
}
repo , err := repoFn ()
require . NoError ( t , err )
iamRepoFn := func () ( * iam . Repository , error ) {
return repo , nil
}
iamRepo , err := iamRepoFn ()
require . NoError ( t , err )
tokenRepoFn := func () ( * authtoken . Repository , error ) {
return authtoken . NewRepository ( ctx , rw , rw , kms )
}
tokenRepo , err := tokenRepoFn ()
require . NoError ( t , err )
serversRepoFn := func () ( * server . Repository , error ) {
return server . NewRepository ( ctx , rw , rw , kms )
}
2024-02-15 09:26:29 -08:00
oWithProjects , p2 := iam . TestScopes ( t , repo , iam . WithSkipDefaultRoleCreation ( true ))
2024-01-02 17:19:29 -08:00
_ , err = repo . DeleteScope ( context . Background (), p2 . GetPublicId ())
require . NoError ( t , err )
paginationAuthorizedCollectionActions := map [ string ] * structpb . ListValue {
"sessions" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "list" ),
},
},
"targets" : {
Values : [] * structpb . Value {
structpb . NewStringValue ( "list" ),
},
},
}
var wantProjects [] * pb . Scope
for i := 0 ; i < 10 ; i ++ {
newP , err := iam . NewProject ( ctx , oWithProjects . GetPublicId ())
require . NoError ( t , err )
p , err := repo . CreateScope ( ctx , newP , "" )
require . NoError ( t , err )
wantProjects = append ( wantProjects , & pb . Scope {
Id : p . GetPublicId (),
ScopeId : oWithProjects . GetPublicId (),
Scope : & pb . ScopeInfo { Id : oWithProjects . GetPublicId (), Type : scope . Org . String (), ParentScopeId : scope . Global . String ()},
CreatedTime : p . GetCreateTime (). GetTimestamp (),
UpdatedTime : p . GetUpdateTime (). GetTimestamp (),
Version : 1 ,
Type : scope . Project . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedPrjActions ,
2024-01-02 17:19:29 -08:00
AuthorizedCollectionActions : paginationAuthorizedCollectionActions ,
})
}
// Reverse slice since we order by create time (newest first)
slices . Reverse ( wantProjects )
// Run analyze to update scope estimate
_ , err = sqlDb . ExecContext ( ctx , "analyze" )
require . NoError ( t , err )
authMethod := ldap . TestAuthMethod ( t , conn , wrapper , oWithProjects . PublicId , [] string { "ldaps://no-managed-groups" })
acct := ldap . TestAccount ( t , conn , authMethod , "test-login-last" )
u := iam . TestUser ( t , iamRepo , oWithProjects . GetPublicId (), iam . WithAccountIds ( acct . PublicId ))
// privProjRole := iam.TestRole(t, conn, pwt.GetPublicId())
2024-01-17 23:54:49 -08:00
// iam.TestRoleGrant(t, conn, privProjRole.GetPublicId(), "ids=*;type=*;actions=*")
2024-01-02 17:19:29 -08:00
// iam.TestUserRole(t, conn, privProjRole.GetPublicId(), u.GetPublicId())
privOrgRole := iam . TestRole ( t , conn , oWithProjects . GetPublicId ())
2024-01-17 23:54:49 -08:00
iam . TestRoleGrant ( t , conn , privOrgRole . GetPublicId (), "ids=*;type=*;actions=*" )
2024-01-02 17:19:29 -08:00
iam . TestUserRole ( t , conn , privOrgRole . GetPublicId (), u . GetPublicId ())
at , _ := tokenRepo . CreateAuthToken ( ctx , u , acct . GetPublicId ())
requestInfo := authpb . RequestInfo {
TokenFormat : uint32 ( auth . AuthTokenTypeBearer ),
PublicId : at . GetPublicId (),
Token : at . GetToken (),
}
requestContext := context . WithValue ( context . Background (), requests . ContextRequestInformationKey , & requests . RequestContext {})
ctx = auth . NewVerifierContext ( requestContext , iamRepoFn , tokenRepoFn , serversRepoFn , kms , & requestInfo )
req := & pbs . ListScopesRequest {
ScopeId : oWithProjects . GetPublicId (),
Filter : "" ,
ListToken : "" ,
PageSize : 2 ,
}
2024-01-03 17:54:00 -08:00
s , err := scopes . NewServiceFn ( ctx , repoFn , kms , 1000 )
2024-01-02 17:19:29 -08:00
require . NoError ( t , err )
got , err := s . ListScopes ( ctx , req )
require . NoError ( t , err )
require . Len ( t , got . GetItems (), 2 )
// all comparisons will be done without refresh token
assert . Empty ( t ,
cmp . Diff (
got ,
& pbs . ListScopesResponse {
Items : wantProjects [ 0 : 2 ],
ResponseType : "delta" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 12 ,
},
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
cmpopts . SortSlices ( func ( a , b protocmp . Message ) bool {
return a . String () < b . String ()
}),
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListScopesResponse {}, "list_token" ),
),
)
// second page
req . ListToken = got . ListToken
got , err = s . ListScopes ( ctx , req )
require . NoError ( t , err )
require . Len ( t , got . GetItems (), 2 )
assert . Empty ( t ,
cmp . Diff (
got ,
& pbs . ListScopesResponse {
Items : wantProjects [ 2 : 4 ],
ResponseType : "delta" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 12 ,
},
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
cmpopts . SortSlices ( func ( a , b protocmp . Message ) bool {
return a . String () < b . String ()
}),
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListScopesResponse {}, "list_token" ),
),
)
// remainder of results
req . ListToken = got . ListToken
req . PageSize = 6
got , err = s . ListScopes ( ctx , req )
require . NoError ( t , err )
require . Len ( t , got . GetItems (), 6 )
assert . Empty ( t ,
cmp . Diff (
got ,
& pbs . ListScopesResponse {
Items : wantProjects [ 4 :],
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 12 ,
},
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
cmpopts . SortSlices ( func ( a , b protocmp . Message ) bool {
return a . String () < b . String ()
}),
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListScopesResponse {}, "list_token" ),
),
)
// create another scope
newP , err := iam . NewProject ( ctx , oWithProjects . GetPublicId ())
require . NoError ( t , err )
p , err := repo . CreateScope ( ctx , newP , "" )
require . NoError ( t , err )
newScope := & pb . Scope {
Id : p . GetPublicId (),
ScopeId : oWithProjects . GetPublicId (),
Scope : & pb . ScopeInfo { Id : oWithProjects . GetPublicId (), Type : scope . Org . String (), ParentScopeId : scope . Global . String ()},
CreatedTime : p . GetCreateTime (). GetTimestamp (),
UpdatedTime : p . GetUpdateTime (). GetTimestamp (),
Version : 1 ,
Type : scope . Project . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedPrjActions ,
2024-01-02 17:19:29 -08:00
AuthorizedCollectionActions : paginationAuthorizedCollectionActions ,
}
// Add to the front of the slice since it's the most recently updated
wantProjects = append ([] * pb . Scope { newScope }, wantProjects ... )
// delete different scope
_ , err = repo . DeleteScope ( ctx , wantProjects [ len ( wantProjects ) - 1 ]. Id )
wantProjects = wantProjects [: len ( wantProjects ) - 1 ]
require . NoError ( t , err )
// Run analyze to update postgres estimates
_ , err = sqlDb . ExecContext ( ctx , "analyze" )
require . NoError ( t , err )
// request updated results
// since both creating and deleting scopes will affect the grantsHash
// we expect this to error
req . ListToken = got . ListToken
_ , err = s . ListScopes ( ctx , req )
require . Error ( t , err )
require . True ( t , errors . Match ( errors . T ( errors . InvalidListToken ), err ))
// clear the refresh token
req . ListToken = ""
req . PageSize = 2
got , err = s . ListScopes ( ctx , req )
require . NoError ( t , err )
require . Len ( t , got . GetItems (), 2 )
assert . Empty ( t ,
cmp . Diff (
got ,
& pbs . ListScopesResponse {
Items : wantProjects [ 0 : 2 ],
ResponseType : "delta" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 12 ,
},
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
cmpopts . SortSlices ( func ( a , b protocmp . Message ) bool {
return a . String () < b . String ()
}),
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListScopesResponse {}, "list_token" ),
),
)
// Request new page with filter requiring looping
// to fill the page.
req . ListToken = ""
req . PageSize = 1
req . Filter = fmt . Sprintf ( `"/item/id"==%q or "/item/id"==%q` , wantProjects [ len ( wantProjects ) - 2 ]. Id , wantProjects [ len ( wantProjects ) - 1 ]. Id )
got , err = s . ListScopes ( ctx , req )
require . NoError ( t , err )
require . Len ( t , got . GetItems (), 1 )
assert . Empty ( t ,
cmp . Diff (
got ,
& pbs . ListScopesResponse {
Items : [] * pb . Scope { wantProjects [ len ( wantProjects ) - 2 ]},
ResponseType : "delta" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
// Should be empty again
RemovedIds : nil ,
EstItemCount : 12 ,
},
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
cmpopts . SortSlices ( func ( a , b protocmp . Message ) bool {
return a . String () < b . String ()
}),
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListScopesResponse {}, "list_token" ),
),
)
req . ListToken = got . ListToken
// Get the second page
got , err = s . ListScopes ( ctx , req )
require . NoError ( t , err )
require . Len ( t , got . GetItems (), 1 )
assert . Empty ( t ,
cmp . Diff (
got ,
& pbs . ListScopesResponse {
Items : [] * pb . Scope { wantProjects [ len ( wantProjects ) - 1 ]},
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 12 ,
},
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
cmpopts . SortSlices ( func ( a , b protocmp . Message ) bool {
return a . String () < b . String ()
}),
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListScopesResponse {}, "list_token" ),
),
)
2024-02-15 09:26:29 -08:00
// Create unauthenticated user
unauthAt := authtoken . TestAuthToken ( t , conn , kms , oWithProjects . GetPublicId ())
unauthR := iam . TestRole ( t , conn , p . GetPublicId ())
_ = iam . TestUserRole ( t , conn , unauthR . GetPublicId (), unauthAt . GetIamUserId ())
// Make a request with the unauthenticated user,
// ensure the response contains the pagination parameters.
requestInfo = authpb . RequestInfo {
TokenFormat : uint32 ( auth . AuthTokenTypeBearer ),
PublicId : unauthAt . GetPublicId (),
Token : unauthAt . GetToken (),
}
requestContext = context . WithValue ( context . Background (), requests . ContextRequestInformationKey , & requests . RequestContext {})
ctx = auth . NewVerifierContext ( requestContext , iamRepoFn , tokenRepoFn , serversRepoFn , kms , & requestInfo )
_ , err = s . ListScopes ( ctx , & pbs . ListScopesRequest {
ScopeId : "global" ,
Recursive : true ,
})
require . Error ( t , err )
assert . ErrorIs ( t , handlers . ForbiddenError (), err )
2024-01-02 17:19:29 -08:00
}
2020-07-22 00:14:28 -04:00
func TestDelete ( t * testing . T ) {
2022-11-16 17:17:28 -06:00
org , proj , repoFn , kms := createDefaultScopesRepoAndKms ( t )
2020-07-22 00:14:28 -04:00
2024-01-03 17:54:00 -08:00
s , err := scopes . NewServiceFn ( context . Background (), repoFn , kms , 1000 )
2020-09-23 08:10:27 -07:00
require . NoError ( t , err , "Error when getting new project service." )
2020-07-22 00:14:28 -04:00
cases := [] struct {
name string
scopeId string
req * pbs . DeleteScopeRequest
res * pbs . DeleteScopeResponse
2020-09-23 08:10:27 -07:00
err error
2020-07-22 00:14:28 -04:00
}{
{
name : "Delete an Existing Project" ,
scopeId : org . GetPublicId (),
req : & pbs . DeleteScopeRequest {
Id : proj . GetPublicId (),
},
},
{
name : "Delete bad project id Project" ,
scopeId : org . GetPublicId (),
req : & pbs . DeleteScopeRequest {
2022-11-16 17:17:28 -06:00
Id : "p_DoesntExist" ,
2020-07-22 00:14:28 -04:00
},
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . NotFound ),
2020-07-22 00:14:28 -04:00
},
{
name : "Bad Project Id formatting" ,
scopeId : org . GetPublicId (),
req : & pbs . DeleteScopeRequest {
Id : "bad_format" ,
},
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-07-22 00:14:28 -04:00
},
{
name : "Delete an Existing Org" ,
scopeId : scope . Global . String (),
req : & pbs . DeleteScopeRequest {
Id : org . GetPublicId (),
},
},
{
name : "Delete bad org id Org" ,
scopeId : scope . Global . String (),
req : & pbs . DeleteScopeRequest {
2022-11-16 17:17:28 -06:00
Id : "p_DoesntExist" ,
2020-07-22 00:14:28 -04:00
},
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . NotFound ),
2020-07-22 00:14:28 -04:00
},
{
name : "Bad Org Id formatting" ,
scopeId : scope . Global . String (),
req : & pbs . DeleteScopeRequest {
Id : "bad_format" ,
},
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-07-22 00:14:28 -04:00
},
}
for _ , tc := range cases {
t . Run ( tc . name , func ( t * testing . T ) {
2020-09-23 08:10:27 -07:00
assert , require := assert . New ( t ), require . New ( t )
2021-03-25 03:15:43 -04:00
got , gErr := s . DeleteScope ( auth . DisabledAuthTestContext ( repoFn , tc . scopeId ), tc . req )
2020-09-23 08:10:27 -07:00
if tc . err != nil {
require . Error ( gErr )
assert . True ( errors . Is ( gErr , tc . err ), "DeleteScope(%+v) got error %v, wanted %v" , tc . req , gErr , tc . err )
}
2020-08-05 10:19:55 -07:00
assert . EqualValuesf ( tc . res , got , "DeleteScope(%q) got response %q, wanted %q" , tc . req , got , tc . res )
2020-07-22 00:14:28 -04:00
})
}
}
func TestDelete_twice ( t * testing . T ) {
2020-09-23 08:10:27 -07:00
assert , require := assert . New ( t ), require . New ( t )
2022-11-16 17:17:28 -06:00
org , proj , repoFn , kms := createDefaultScopesRepoAndKms ( t )
2020-07-22 00:14:28 -04:00
2024-01-03 17:54:00 -08:00
s , err := scopes . NewServiceFn ( context . Background (), repoFn , kms , 1000 )
2020-07-22 00:14:28 -04:00
require . NoError ( err , "Error when getting new scopes service" )
2021-03-25 03:15:43 -04:00
ctx := auth . DisabledAuthTestContext ( repoFn , org . GetPublicId ())
2020-07-22 00:14:28 -04:00
req := & pbs . DeleteScopeRequest {
Id : proj . GetPublicId (),
}
2020-09-08 23:57:26 -04:00
_ , gErr := s . DeleteScope ( ctx , req )
2020-07-22 00:14:28 -04:00
assert . NoError ( gErr , "First attempt" )
2020-09-08 23:57:26 -04:00
_ , gErr = s . DeleteScope ( ctx , req )
2020-09-04 14:48:11 -07:00
assert . Error ( gErr , "Second attempt" )
2020-09-23 08:10:27 -07:00
assert . True ( errors . Is ( gErr , handlers . ApiErrorWithCode ( codes . NotFound )), "Expected not found for the second delete." )
2020-07-22 00:14:28 -04:00
2021-03-25 03:15:43 -04:00
ctx = auth . DisabledAuthTestContext ( repoFn , scope . Global . String ())
2020-07-22 00:14:28 -04:00
req = & pbs . DeleteScopeRequest {
Id : org . GetPublicId (),
}
2020-09-08 23:57:26 -04:00
_ , gErr = s . DeleteScope ( ctx , req )
2020-07-22 00:14:28 -04:00
assert . NoError ( gErr , "First attempt" )
2020-09-08 23:57:26 -04:00
_ , gErr = s . DeleteScope ( ctx , req )
2020-09-04 14:48:11 -07:00
assert . Error ( gErr , "Second attempt" )
2020-09-23 08:10:27 -07:00
assert . True ( errors . Is ( gErr , handlers . ApiErrorWithCode ( codes . NotFound )), "Expected not found for the second delete." )
2020-07-22 00:14:28 -04:00
}
func TestCreate ( t * testing . T ) {
2020-08-13 17:39:48 -04:00
ctx := context . Background ()
2022-11-16 17:17:28 -06:00
defaultOrg , defaultProj , repoFn , kms := createDefaultScopesRepoAndKms ( t )
2021-04-14 16:19:02 -05:00
defaultProjCreated := defaultProj . GetCreateTime (). GetTimestamp (). AsTime ()
2020-07-22 00:14:28 -04:00
toMerge := & pbs . CreateScopeRequest {}
2020-08-13 17:39:48 -04:00
repo , err := repoFn ()
2020-09-08 16:04:19 -07:00
require . NoError ( t , err )
2023-06-06 17:09:08 -04:00
globalUser , err := iam . NewUser ( ctx , scope . Global . String ())
2020-09-08 16:04:19 -07:00
require . NoError ( t , err )
2020-08-13 17:39:48 -04:00
globalUser , err = repo . CreateUser ( ctx , globalUser )
2020-09-08 16:04:19 -07:00
require . NoError ( t , err )
2023-06-06 17:09:08 -04:00
orgUser , err := iam . NewUser ( ctx , defaultOrg . GetPublicId ())
2020-09-08 16:04:19 -07:00
require . NoError ( t , err )
2020-08-13 17:39:48 -04:00
orgUser , err = repo . CreateUser ( ctx , orgUser )
2020-09-08 16:04:19 -07:00
require . NoError ( t , err )
2020-08-13 17:39:48 -04:00
2020-07-22 00:14:28 -04:00
cases := [] struct {
name string
scopeId string
req * pbs . CreateScopeRequest
res * pbs . CreateScopeResponse
2020-09-23 08:10:27 -07:00
err error
2020-07-22 00:14:28 -04:00
}{
{
name : "Create a valid Project" ,
scopeId : defaultOrg . GetPublicId (),
2020-07-22 15:10:44 -04:00
req : & pbs . CreateScopeRequest {
Item : & pb . Scope {
2020-09-02 19:43:41 -07:00
ScopeId : defaultOrg . GetPublicId (),
2020-07-22 15:10:44 -04:00
Name : & wrapperspb . StringValue { Value : "name" },
Description : & wrapperspb . StringValue { Value : "desc" },
},
},
2020-07-22 00:14:28 -04:00
res : & pbs . CreateScopeResponse {
Uri : "scopes/p_" ,
Item : & pb . Scope {
2021-01-20 10:53:01 -05:00
ScopeId : defaultOrg . GetPublicId (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : defaultOrg . GetPublicId (), Type : scope . Org . String (), ParentScopeId : scope . Global . String (), Name : "defaultOrg" , Description : "defaultOrg" },
2021-01-20 10:53:01 -05:00
Name : & wrapperspb . StringValue { Value : "name" },
Description : & wrapperspb . StringValue { Value : "desc" },
Version : 1 ,
Type : scope . Project . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedPrjActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : projectAuthorizedCollectionActions ,
2020-07-22 00:14:28 -04:00
},
},
},
{
name : "Create a valid Org" ,
scopeId : scope . Global . String (),
2020-07-22 15:10:44 -04:00
req : & pbs . CreateScopeRequest {
Item : & pb . Scope {
2020-09-02 19:43:41 -07:00
ScopeId : scope . Global . String (),
2020-07-22 15:10:44 -04:00
Name : & wrapperspb . StringValue { Value : "name" },
Description : & wrapperspb . StringValue { Value : "desc" },
},
},
2020-07-22 00:14:28 -04:00
res : & pbs . CreateScopeResponse {
Uri : "scopes/o_" ,
Item : & pb . Scope {
2021-01-20 10:53:01 -05:00
ScopeId : scope . Global . String (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : scope . Global . String (), Type : scope . Global . String (), Name : scope . Global . String (), Description : "Global Scope" },
2021-01-20 10:53:01 -05:00
Name : & wrapperspb . StringValue { Value : "name" },
Description : & wrapperspb . StringValue { Value : "desc" },
Version : 1 ,
Type : scope . Org . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedOrgActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : orgAuthorizedCollectionActions ,
2020-09-08 16:04:19 -07:00
},
},
},
{
name : "Create a valid Project with type specified" ,
scopeId : defaultOrg . GetPublicId (),
req : & pbs . CreateScopeRequest {
Item : & pb . Scope {
ScopeId : defaultOrg . GetPublicId (),
Description : & wrapperspb . StringValue { Value : "desc" },
Type : scope . Project . String (),
},
},
res : & pbs . CreateScopeResponse {
Uri : "scopes/p_" ,
Item : & pb . Scope {
2021-01-20 10:53:01 -05:00
ScopeId : defaultOrg . GetPublicId (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : defaultOrg . GetPublicId (), Type : scope . Org . String (), ParentScopeId : scope . Global . String (), Name : "defaultOrg" , Description : "defaultOrg" },
2021-01-20 10:53:01 -05:00
Description : & wrapperspb . StringValue { Value : "desc" },
Version : 1 ,
Type : scope . Project . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedPrjActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : projectAuthorizedCollectionActions ,
2020-09-08 16:04:19 -07:00
},
},
},
{
name : "Create a valid Org with type specified" ,
scopeId : scope . Global . String (),
req : & pbs . CreateScopeRequest {
Item : & pb . Scope {
ScopeId : scope . Global . String (),
Description : & wrapperspb . StringValue { Value : "desc" },
Type : scope . Org . String (),
},
},
res : & pbs . CreateScopeResponse {
Uri : "scopes/o_" ,
Item : & pb . Scope {
2021-01-20 10:53:01 -05:00
ScopeId : scope . Global . String (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : scope . Global . String (), Type : scope . Global . String (), Name : scope . Global . String (), Description : "Global Scope" },
2021-01-20 10:53:01 -05:00
Description : & wrapperspb . StringValue { Value : "desc" },
Version : 1 ,
Type : scope . Org . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedOrgActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : orgAuthorizedCollectionActions ,
2020-07-22 00:14:28 -04:00
},
},
},
2023-05-25 15:43:52 +01:00
{
name : "Create a valid org with leading and trailing whitespace on name and description" ,
scopeId : scope . Global . String (),
req : & pbs . CreateScopeRequest {
Item : & pb . Scope {
ScopeId : scope . Global . String (),
Description : & wrapperspb . StringValue { Value : " test description with whitespace " },
Type : scope . Org . String (),
Name : & wrapperspb . StringValue { Value : " test org name with whitespace " },
},
},
res : & pbs . CreateScopeResponse {
Uri : "scopes/o_" ,
Item : & pb . Scope {
ScopeId : scope . Global . String (),
Scope : & pb . ScopeInfo { Id : scope . Global . String (), Type : scope . Global . String (), Name : scope . Global . String (), Description : "Global Scope" },
Description : & wrapperspb . StringValue { Value : "test description with whitespace" }, // assert the whitespace is trimmed
Name : & wrapperspb . StringValue { Value : "test org name with whitespace" }, // assert the whitespace is trimmed
Version : 1 ,
Type : scope . Org . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedOrgActions ,
2023-05-25 15:43:52 +01:00
AuthorizedCollectionActions : orgAuthorizedCollectionActions ,
},
},
},
{
name : "Create a global type scope" ,
scopeId : scope . Global . String (),
req : & pbs . CreateScopeRequest {
Item : & pb . Scope {
ScopeId : scope . Global . String (),
Description : & wrapperspb . StringValue { Value : "desc" },
Type : scope . Global . String (),
},
},
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "Create with a custom version number" ,
scopeId : scope . Global . String (),
req : & pbs . CreateScopeRequest {
Item : & pb . Scope {
ScopeId : scope . Global . String (),
Description : & wrapperspb . StringValue { Value : "desc" },
Type : scope . Org . String (),
Version : 5 ,
},
},
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
2020-09-08 16:04:19 -07:00
{
name : "Project with bad type specified" ,
scopeId : defaultOrg . GetPublicId (),
req : & pbs . CreateScopeRequest {
Item : & pb . Scope {
ScopeId : defaultOrg . GetPublicId (),
Description : & wrapperspb . StringValue { Value : "desc" },
Type : scope . Org . String (),
},
},
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-09-08 16:04:19 -07:00
},
{
name : "Org with bad type specified" ,
scopeId : scope . Global . String (),
req : & pbs . CreateScopeRequest {
Item : & pb . Scope {
ScopeId : scope . Global . String (),
Description : & wrapperspb . StringValue { Value : "desc" },
Type : scope . Project . String (),
},
},
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-09-08 16:04:19 -07:00
},
2020-07-22 00:14:28 -04:00
{
name : "Can't specify Id" ,
scopeId : defaultOrg . GetPublicId (),
req : & pbs . CreateScopeRequest { Item : & pb . Scope {
Id : "not allowed to be set" ,
}},
2020-09-23 08:10:27 -07:00
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-07-22 00:14:28 -04:00
},
{
name : "Can't specify Created Time" ,
scopeId : defaultOrg . GetPublicId (),
req : & pbs . CreateScopeRequest { Item : & pb . Scope {
2021-04-14 16:19:02 -05:00
CreatedTime : timestamppb . Now (),
2020-07-22 00:14:28 -04:00
}},
2020-09-23 08:10:27 -07:00
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-07-22 00:14:28 -04:00
},
{
name : "Can't specify Update Time" ,
scopeId : defaultOrg . GetPublicId (),
req : & pbs . CreateScopeRequest { Item : & pb . Scope {
2021-04-14 16:19:02 -05:00
UpdatedTime : timestamppb . Now (),
2020-07-22 00:14:28 -04:00
}},
2020-09-23 08:10:27 -07:00
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-07-22 00:14:28 -04:00
},
}
for _ , tc := range cases {
2020-08-13 17:39:48 -04:00
for _ , withUserId := range [] bool { false , true } {
t . Run ( fmt . Sprintf ( "%s-userid-%t" , tc . name , withUserId ), func ( t * testing . T ) {
2020-09-08 16:04:19 -07:00
assert , require := assert . New ( t ), require . New ( t )
2020-08-13 17:39:48 -04:00
var name string
if tc . req != nil && tc . req . GetItem () != nil && tc . req . GetItem (). GetName () != nil {
name = tc . req . GetItem (). GetName (). GetValue ()
localName := name
defer func () {
tc . res . GetItem (). GetName (). Value = localName
}()
}
req := proto . Clone ( toMerge ).( * pbs . CreateScopeRequest )
proto . Merge ( req , tc . req )
2020-07-22 00:14:28 -04:00
2024-01-03 17:54:00 -08:00
s , err := scopes . NewServiceFn ( context . Background (), repoFn , kms , 1000 )
2020-08-13 17:39:48 -04:00
require . NoError ( err , "Error when getting new project service." )
if name != "" {
2023-05-25 15:43:52 +01:00
// Test cases can specify names with leading and trailing
// whitespace to test for Boundary's whitespace removal.
// Since we're adding to the name before we send the request
// off, we need to make sure we keep it as we found it.
// Find where leading whitespace ends, add withUserId there.
idx := 0
for i , r := range name {
if ! unicode . IsSpace ( r ) {
idx = i
break
}
}
if idx == 0 {
name = fmt . Sprintf ( "%t-%s" , withUserId , name )
} else {
name = fmt . Sprintf ( "%s%t-%s" , name [: idx ], withUserId , name [ idx :])
}
2020-08-13 17:39:48 -04:00
req . GetItem (). GetName (). Value = name
2020-07-22 00:14:28 -04:00
}
2020-08-13 17:39:48 -04:00
var userId string
if withUserId {
if tc . scopeId == scope . Global . String () {
userId = globalUser . GetPublicId ()
} else {
userId = orgUser . GetPublicId ()
}
assert . NotEmpty ( userId )
}
2021-03-25 03:15:43 -04:00
got , gErr := s . CreateScope ( auth . DisabledAuthTestContext ( repoFn , tc . scopeId , auth . WithUserId ( userId )), req )
2020-09-23 08:10:27 -07:00
if tc . err != nil {
require . Error ( gErr )
assert . True ( errors . Is ( gErr , tc . err ), "CreateScope(%+v) got error %v, wanted %v" , req , gErr , tc . err )
}
2020-08-13 17:39:48 -04:00
if got != nil {
assert . Contains ( got . GetUri (), tc . res . Uri )
switch tc . scopeId {
case "global" :
assert . True ( strings . HasPrefix ( got . GetItem (). GetId (), "o_" ))
default :
assert . True ( strings . HasPrefix ( got . GetItem (). GetId (), "p_" ))
}
2021-04-14 16:19:02 -05:00
gotCreateTime := got . GetItem (). GetCreatedTime (). AsTime ()
gotUpdateTime := got . GetItem (). GetUpdatedTime (). AsTime ()
2020-08-13 17:39:48 -04:00
// Verify it is a project created after the test setup's default project
assert . True ( gotCreateTime . After ( defaultProjCreated ), "New scope should have been created after default project. Was created %v, which is after %v" , gotCreateTime , defaultProjCreated )
assert . True ( gotUpdateTime . After ( defaultProjCreated ), "New scope should have been updated after default project. Was updated %v, which is after %v" , gotUpdateTime , defaultProjCreated )
if withUserId {
repo , err := repoFn ()
require . NoError ( err )
2023-12-15 11:49:59 -08:00
noopFilter := func ( ctx context . Context , item * iam . Role ) ( bool , error ) {
return true , nil
}
roles , err := iam . ListRoles ( ctx , [] byte ( "test" ), globals . DefaultMaxPageSize , noopFilter , repo , [] string { got . GetItem (). GetId ()})
2020-08-13 17:39:48 -04:00
require . NoError ( err )
2020-10-13 13:56:21 -04:00
switch tc . scopeId {
case defaultOrg . PublicId :
2023-12-15 11:49:59 -08:00
require . Len ( roles . Items , 2 )
2020-10-13 13:56:21 -04:00
case "global" :
2023-12-15 11:49:59 -08:00
require . Len ( roles . Items , 2 )
2020-10-13 13:56:21 -04:00
}
2023-12-15 11:49:59 -08:00
for _ , role := range roles . Items {
2020-09-29 20:38:16 -04:00
switch role . GetName () {
case "Administration" :
assert . Equal ( fmt . Sprintf ( "Role created for administration of scope %s by user %s at its creation time" , got . GetItem (). GetId (), userId ), role . GetDescription ())
2020-10-01 10:51:54 -04:00
case "Login and Default Grants" :
2021-02-11 13:14:18 -05:00
assert . Equal ( fmt . Sprintf ( "Role created for login capability, account self-management, and other default grants for users of scope %s at its creation time" , got . GetItem (). GetId ()), role . GetDescription ())
case "Default Grants" :
assert . Equal ( fmt . Sprintf ( "Role created to provide default grants to users of scope %s at its creation time" , got . GetItem (). GetId ()), role . GetDescription ())
2020-09-29 20:38:16 -04:00
default :
t . Fatal ( "unexpected role name" , role . GetName ())
}
}
2020-08-13 17:39:48 -04:00
}
// Clear all values which are hard to compare against.
2023-05-25 15:43:52 +01:00
assert . Equal ( strings . TrimSpace ( name ), got . GetItem (). GetName (). GetValue ())
2020-08-13 17:39:48 -04:00
got . Item . Name = tc . res . GetItem (). GetName ()
got . Uri , tc . res . Uri = "" , ""
got . Item . Id , tc . res . Item . Id = "" , ""
got . Item . CreatedTime , got . Item . UpdatedTime , tc . res . Item . CreatedTime , tc . res . Item . UpdatedTime = nil , nil , nil , nil
}
2023-10-31 13:44:17 +00:00
assert . Empty ( cmp . Diff (
tc . res ,
got ,
protocmp . Transform (),
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
cmpopts . SortSlices ( func ( a , b protocmp . Message ) bool {
return a . String () < b . String ()
}),
), "CreateScope(%q) got response %q, wanted %q" , req , got , tc . res )
2020-08-13 17:39:48 -04:00
})
}
2020-07-22 00:14:28 -04:00
}
}
func TestUpdate ( t * testing . T ) {
2022-11-16 17:17:28 -06:00
org , proj , repoFn , kms := createDefaultScopesRepoAndKms ( t )
2024-01-03 17:54:00 -08:00
tested , err := scopes . NewServiceFn ( context . Background (), repoFn , kms , 1000 )
2020-09-08 16:04:19 -07:00
require . NoError ( t , err , "Error when getting new project service." )
2020-07-22 00:14:28 -04:00
2021-04-08 20:54:32 -04:00
iamRepo , err := repoFn ()
require . NoError ( t , err )
global , err := iamRepo . LookupScope ( context . Background (), "global" )
require . NoError ( t , err )
2020-08-07 12:55:02 -04:00
var orgVersion uint32 = 2
var projVersion uint32 = 2
2024-02-13 10:54:48 -05:00
globalVersion := global . Version
2020-08-07 12:55:02 -04:00
2020-07-22 00:14:28 -04:00
resetOrg := func () {
2020-08-07 12:55:02 -04:00
orgVersion ++
2020-07-22 00:14:28 -04:00
repo , err := repoFn ()
2020-09-08 16:04:19 -07:00
require . NoError ( t , err , "Couldn't get a new repo" )
2020-08-07 12:55:02 -04:00
org , _ , err = repo . UpdateScope ( context . Background (), org , orgVersion , [] string { "Name" , "Description" })
2020-09-08 16:04:19 -07:00
require . NoError ( t , err , "Failed to reset the org" )
2020-08-07 12:55:02 -04:00
orgVersion ++
2020-07-22 00:14:28 -04:00
}
resetProject := func () {
2020-08-07 12:55:02 -04:00
projVersion ++
2020-07-22 00:14:28 -04:00
repo , err := repoFn ()
2020-09-08 16:04:19 -07:00
require . NoError ( t , err , "Couldn't get a new repo" )
2020-08-07 12:55:02 -04:00
proj , _ , err = repo . UpdateScope ( context . Background (), proj , projVersion , [] string { "Name" , "Description" })
2020-09-08 16:04:19 -07:00
require . NoError ( t , err , "Failed to reset the project" )
2020-08-07 12:55:02 -04:00
projVersion ++
2020-07-22 00:14:28 -04:00
}
2021-04-08 20:54:32 -04:00
resetGlobal := func () {
repo , err := repoFn ()
require . NoError ( t , err , "Couldn't get a new repo" )
globalScope := iam . AllocScope ()
globalScope . PublicId = "global"
global , _ , err = repo . UpdateScope ( context . Background (), & globalScope , globalVersion , [] string { "Name" , "Description" })
require . NoError ( t , err , "Failed to reset the global scope" )
globalVersion = global . Version
}
2021-04-14 16:19:02 -05:00
projCreated := proj . GetCreateTime (). GetTimestamp (). AsTime ()
2020-07-22 00:14:28 -04:00
projToMerge := & pbs . UpdateScopeRequest {
Id : proj . GetPublicId (),
}
orgToMerge := & pbs . UpdateScopeRequest {
Id : org . GetPublicId (),
}
2021-04-08 20:54:32 -04:00
globalToMerge := & pbs . UpdateScopeRequest {
Id : "global" ,
}
2020-07-22 00:14:28 -04:00
cases := [] struct {
name string
scopeId string
req * pbs . UpdateScopeRequest
res * pbs . UpdateScopeResponse
2020-09-23 08:10:27 -07:00
err error
2020-07-22 00:14:28 -04:00
}{
{
name : "Update an Existing Project" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "name" , "description" },
},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "new" },
Description : & wrapperspb . StringValue { Value : "desc" },
2020-09-08 16:04:19 -07:00
Type : scope . Project . String (),
2020-07-22 00:14:28 -04:00
},
},
res : & pbs . UpdateScopeResponse {
Item : & pb . Scope {
2021-01-20 10:53:01 -05:00
Id : proj . GetPublicId (),
ScopeId : org . GetPublicId (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : org . GetPublicId (), Type : scope . Org . String (), ParentScopeId : scope . Global . String (), Name : "defaultOrg" , Description : "defaultOrg" },
2021-01-20 10:53:01 -05:00
Name : & wrapperspb . StringValue { Value : "new" },
Description : & wrapperspb . StringValue { Value : "desc" },
CreatedTime : proj . GetCreateTime (). GetTimestamp (),
Type : scope . Project . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedPrjActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : projectAuthorizedCollectionActions ,
2020-07-22 00:14:28 -04:00
},
},
},
{
name : "Update an Existing Org" ,
scopeId : scope . Global . String (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "name" , "description" },
},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "new" },
Description : & wrapperspb . StringValue { Value : "desc" },
2020-09-08 16:04:19 -07:00
Type : scope . Org . String (),
2020-07-22 00:14:28 -04:00
},
},
res : & pbs . UpdateScopeResponse {
Item : & pb . Scope {
2021-01-20 10:53:01 -05:00
Id : org . GetPublicId (),
ScopeId : scope . Global . String (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : scope . Global . String (), Type : scope . Global . String (), Name : scope . Global . String (), Description : "Global Scope" },
2021-01-20 10:53:01 -05:00
Name : & wrapperspb . StringValue { Value : "new" },
Description : & wrapperspb . StringValue { Value : "desc" },
CreatedTime : org . GetCreateTime (). GetTimestamp (),
Type : scope . Org . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedOrgActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : orgAuthorizedCollectionActions ,
2020-07-22 00:14:28 -04:00
},
},
},
2023-05-25 15:43:52 +01:00
{
name : "Update org name and description with whitespace" ,
scopeId : scope . Global . String (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "name" , "description" },
},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : " new name " },
Description : & wrapperspb . StringValue { Value : " new desc " },
Type : scope . Org . String (),
},
},
res : & pbs . UpdateScopeResponse {
Item : & pb . Scope {
Id : org . GetPublicId (),
ScopeId : scope . Global . String (),
Scope : & pb . ScopeInfo { Id : scope . Global . String (), Type : scope . Global . String (), Name : scope . Global . String (), Description : "Global Scope" },
Name : & wrapperspb . StringValue { Value : "new name" },
Description : & wrapperspb . StringValue { Value : "new desc" },
CreatedTime : org . GetCreateTime (). GetTimestamp (),
Type : scope . Org . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedOrgActions ,
2023-05-25 15:43:52 +01:00
AuthorizedCollectionActions : orgAuthorizedCollectionActions ,
},
},
},
2021-04-08 20:54:32 -04:00
{
name : "Update global" ,
scopeId : scope . Global . String (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "name" , "description" },
},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "new" },
Description : & wrapperspb . StringValue { Value : "desc" },
Type : scope . Global . String (),
},
},
res : & pbs . UpdateScopeResponse {
Item : & pb . Scope {
Id : scope . Global . String (),
Scope : & pb . ScopeInfo { Id : scope . Global . String (), Type : scope . Global . String (), Name : scope . Global . String (), Description : "Global Scope" },
Name : & wrapperspb . StringValue { Value : "new" },
Description : & wrapperspb . StringValue { Value : "desc" },
CreatedTime : global . GetCreateTime (). GetTimestamp (),
Type : scope . Global . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedGlobalActions ,
2021-04-08 20:54:32 -04:00
AuthorizedCollectionActions : globalAuthorizedCollectionActions ,
},
},
},
2020-07-22 00:14:28 -04:00
{
name : "Multiple Paths in single string" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "name,description" },
},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "new" },
Description : & wrapperspb . StringValue { Value : "desc" },
},
},
res : & pbs . UpdateScopeResponse {
Item : & pb . Scope {
2021-01-20 10:53:01 -05:00
Id : proj . GetPublicId (),
ScopeId : org . GetPublicId (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : org . GetPublicId (), Type : scope . Org . String (), ParentScopeId : scope . Global . String (), Name : "defaultOrg" , Description : "defaultOrg" },
2021-01-20 10:53:01 -05:00
Name : & wrapperspb . StringValue { Value : "new" },
Description : & wrapperspb . StringValue { Value : "desc" },
CreatedTime : proj . GetCreateTime (). GetTimestamp (),
Type : scope . Project . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedPrjActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : projectAuthorizedCollectionActions ,
2020-07-22 00:14:28 -04:00
},
},
},
{
name : "No Update Mask" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "updated name" },
Description : & wrapperspb . StringValue { Value : "updated desc" },
},
},
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-07-22 00:14:28 -04:00
},
2020-09-08 16:04:19 -07:00
{
name : "Cant modify type" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "name" , "type" },
},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "updated name" },
Type : scope . Org . String (),
},
},
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-09-08 16:04:19 -07:00
},
2020-07-22 00:14:28 -04:00
{
name : "No Paths in Mask" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask { Paths : [] string {}},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "updated name" },
Description : & wrapperspb . StringValue { Value : "updated desc" },
},
},
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-07-22 00:14:28 -04:00
},
2023-05-25 15:43:52 +01:00
{
name : "Invalidly formatted scope id - org scope" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
Id : "o_!@£$*" ,
UpdateMask : & field_mask . FieldMask { Paths : [] string {}},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "updated name" },
Description : & wrapperspb . StringValue { Value : "updated desc" },
},
},
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "Invalidly formatted scope id - proj scope" ,
scopeId : proj . GetPublicId (),
req : & pbs . UpdateScopeRequest {
Id : "p_!@£$*" ,
UpdateMask : & field_mask . FieldMask { Paths : [] string {}},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "updated name" },
Description : & wrapperspb . StringValue { Value : "updated desc" },
},
},
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "Invalidly formatted scope id - unknown scope" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
Id : "bla_123" ,
UpdateMask : & field_mask . FieldMask { Paths : [] string {}},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "updated name" },
Description : & wrapperspb . StringValue { Value : "updated desc" },
},
},
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "Unknown scope type - org scope" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
Id : org . GetPublicId (),
UpdateMask : & field_mask . FieldMask { Paths : [] string {}},
Item : & pb . Scope {
Type : "test" ,
Name : & wrapperspb . StringValue { Value : "updated name" },
Description : & wrapperspb . StringValue { Value : "updated desc" },
},
},
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "Unknown scope type - proj scope" ,
scopeId : proj . GetPublicId (),
req : & pbs . UpdateScopeRequest {
Id : proj . GetPublicId (),
UpdateMask : & field_mask . FieldMask { Paths : [] string {}},
Item : & pb . Scope {
Type : "test" ,
Name : & wrapperspb . StringValue { Value : "updated name" },
Description : & wrapperspb . StringValue { Value : "updated desc" },
},
},
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "Unknown primary auth method id" ,
scopeId : proj . GetPublicId (),
req : & pbs . UpdateScopeRequest {
Id : proj . GetPublicId (),
UpdateMask : & field_mask . FieldMask { Paths : [] string {}},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "updated name" },
Description : & wrapperspb . StringValue { Value : "updated desc" },
PrimaryAuthMethodId : & wrapperspb . StringValue { Value : "test_123" },
},
},
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "Update org name and description with whitespace only" ,
scopeId : scope . Global . String (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "name" , "description" },
},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : " " },
Description : & wrapperspb . StringValue { Value : " " },
Type : scope . Org . String (),
},
},
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "Update org name and description with non-printable characters" ,
scopeId : scope . Global . String (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "name" , "description" },
},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : " na\u200Bme " },
Description : & wrapperspb . StringValue { Value : " desc\u200Bription " },
Type : scope . Org . String (),
},
},
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
2020-07-22 00:14:28 -04:00
{
2024-02-16 08:30:16 -05:00
name : "Only non-existent paths in Mask" ,
2020-07-22 00:14:28 -04:00
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
2024-02-16 08:30:16 -05:00
UpdateMask : & field_mask . FieldMask { Paths : [] string { "nonexistent_field" }},
2020-07-22 00:14:28 -04:00
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "updated name" },
Description : & wrapperspb . StringValue { Value : "updated desc" },
},
},
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-07-22 00:14:28 -04:00
},
{
name : "Unset Name" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "name" },
},
Item : & pb . Scope {
Description : & wrapperspb . StringValue { Value : "ignored" },
},
},
res : & pbs . UpdateScopeResponse {
Item : & pb . Scope {
2021-01-20 10:53:01 -05:00
Id : proj . GetPublicId (),
ScopeId : org . GetPublicId (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : org . GetPublicId (), Type : scope . Org . String (), ParentScopeId : scope . Global . String (), Name : "defaultOrg" , Description : "defaultOrg" },
2021-01-20 10:53:01 -05:00
Description : & wrapperspb . StringValue { Value : "defaultProj" },
CreatedTime : proj . GetCreateTime (). GetTimestamp (),
Type : scope . Project . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedPrjActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : projectAuthorizedCollectionActions ,
2020-07-22 00:14:28 -04:00
},
},
},
{
name : "Unset Description" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "description" },
},
Item : & pb . Scope {
Name : & wrappers . StringValue { Value : "ignored" },
},
},
res : & pbs . UpdateScopeResponse {
Item : & pb . Scope {
2021-01-20 10:53:01 -05:00
Id : proj . GetPublicId (),
ScopeId : org . GetPublicId (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : org . GetPublicId (), Type : scope . Org . String (), ParentScopeId : scope . Global . String (), Name : "defaultOrg" , Description : "defaultOrg" },
2021-01-20 10:53:01 -05:00
Name : & wrappers . StringValue { Value : "defaultProj" },
CreatedTime : proj . GetCreateTime (). GetTimestamp (),
Type : scope . Project . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedPrjActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : projectAuthorizedCollectionActions ,
2020-07-22 00:14:28 -04:00
},
},
},
{
name : "Update Only Name" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "name" },
},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "updated" },
Description : & wrapperspb . StringValue { Value : "ignored" },
},
},
res : & pbs . UpdateScopeResponse {
Item : & pb . Scope {
2021-01-20 10:53:01 -05:00
Id : proj . GetPublicId (),
ScopeId : org . GetPublicId (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : org . GetPublicId (), Type : scope . Org . String (), ParentScopeId : scope . Global . String (), Name : "defaultOrg" , Description : "defaultOrg" },
2021-01-20 10:53:01 -05:00
Name : & wrapperspb . StringValue { Value : "updated" },
Description : & wrapperspb . StringValue { Value : "defaultProj" },
CreatedTime : proj . GetCreateTime (). GetTimestamp (),
Type : scope . Project . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedPrjActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : projectAuthorizedCollectionActions ,
2020-07-22 00:14:28 -04:00
},
},
},
{
name : "Update Only Description" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "description" },
},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "ignored" },
Description : & wrapperspb . StringValue { Value : "notignored" },
},
},
res : & pbs . UpdateScopeResponse {
Item : & pb . Scope {
2021-01-20 10:53:01 -05:00
Id : proj . GetPublicId (),
ScopeId : org . GetPublicId (),
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : org . GetPublicId (), Type : scope . Org . String (), ParentScopeId : scope . Global . String (), Name : "defaultOrg" , Description : "defaultOrg" },
2021-01-20 10:53:01 -05:00
Name : & wrapperspb . StringValue { Value : "defaultProj" },
Description : & wrapperspb . StringValue { Value : "notignored" },
CreatedTime : proj . GetCreateTime (). GetTimestamp (),
Type : scope . Project . String (),
2024-01-18 17:29:53 -08:00
AuthorizedActions : testAuthorizedPrjActions ,
2021-01-20 10:53:01 -05:00
AuthorizedCollectionActions : projectAuthorizedCollectionActions ,
2020-07-22 00:14:28 -04:00
},
},
},
{
name : "Update a Non Existing Project" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
2022-11-16 17:17:28 -06:00
Id : "p_DoesntExist" ,
2020-07-22 00:14:28 -04:00
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "description" },
},
Item : & pb . Scope {
Name : & wrapperspb . StringValue { Value : "new" },
Description : & wrapperspb . StringValue { Value : "desc" },
},
},
2020-09-23 08:10:27 -07:00
err : handlers . ApiErrorWithCode ( codes . NotFound ),
2020-07-22 00:14:28 -04:00
},
{
name : "Cant change Id" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
Id : proj . GetPublicId (),
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "id" },
},
Item : & pb . Scope {
Id : "p_somethinge" ,
2021-03-25 03:15:43 -04:00
Scope : & pb . ScopeInfo { Id : org . GetPublicId (), Type : scope . Org . String (), ParentScopeId : scope . Global . String (), Name : "defaultOrg" , Description : "defaultOrg" },
2020-07-22 00:14:28 -04:00
Name : & wrapperspb . StringValue { Value : "new" },
Description : & wrapperspb . StringValue { Value : "new desc" },
2021-01-07 10:02:32 -05:00
},
},
2020-09-23 08:10:27 -07:00
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-07-22 00:14:28 -04:00
},
{
name : "Cant specify Created Time" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "created_time" },
},
Item : & pb . Scope {
2021-04-14 16:19:02 -05:00
CreatedTime : timestamppb . Now (),
2020-07-22 00:14:28 -04:00
},
},
2020-09-23 08:10:27 -07:00
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-07-22 00:14:28 -04:00
},
{
name : "Cant specify Updated Time" ,
scopeId : org . GetPublicId (),
req : & pbs . UpdateScopeRequest {
UpdateMask : & field_mask . FieldMask {
Paths : [] string { "updated_time" },
},
Item : & pb . Scope {
2021-04-14 16:19:02 -05:00
UpdatedTime : timestamppb . Now (),
2020-07-22 00:14:28 -04:00
},
},
2020-09-23 08:10:27 -07:00
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
2020-07-22 00:14:28 -04:00
},
}
for _ , tc := range cases {
t . Run ( tc . name , func ( t * testing . T ) {
2020-08-07 12:55:02 -04:00
ver := orgVersion
if tc . req . Id == proj . PublicId {
ver = projVersion
}
2020-08-18 17:38:38 -07:00
tc . req . Item . Version = ver
2020-08-07 12:55:02 -04:00
2020-09-08 16:04:19 -07:00
assert , require := assert . New ( t ), require . New ( t )
2020-07-22 00:14:28 -04:00
var req * pbs . UpdateScopeRequest
2021-04-08 20:54:32 -04:00
switch {
case tc . scopeId == scope . Global . String () && tc . req . Item . GetType () == scope . Global . String ():
tc . req . Item . Version = globalVersion
ver = globalVersion
req = proto . Clone ( globalToMerge ).( * pbs . UpdateScopeRequest )
if tc . err == nil {
defer resetGlobal ()
}
case tc . scopeId == scope . Global . String ():
2020-07-22 00:14:28 -04:00
req = proto . Clone ( orgToMerge ).( * pbs . UpdateScopeRequest )
2020-09-23 08:10:27 -07:00
if tc . err == nil {
2020-08-07 12:55:02 -04:00
defer resetOrg ()
}
2020-07-22 00:14:28 -04:00
default :
2020-08-07 12:55:02 -04:00
ver = projVersion
2020-08-18 17:38:38 -07:00
tc . req . Item . Version = ver
2020-07-22 00:14:28 -04:00
req = proto . Clone ( projToMerge ).( * pbs . UpdateScopeRequest )
2020-09-23 08:10:27 -07:00
if tc . err == nil {
2020-08-07 12:55:02 -04:00
defer resetProject ()
}
2020-07-22 00:14:28 -04:00
}
proto . Merge ( req , tc . req )
2021-03-25 03:15:43 -04:00
got , gErr := tested . UpdateScope ( auth . DisabledAuthTestContext ( repoFn , tc . scopeId ), req )
2020-09-23 08:10:27 -07:00
if tc . err != nil {
require . Error ( gErr )
assert . True ( errors . Is ( gErr , tc . err ), "UpdateScope(%+v) got error\n%v, wanted\n%v" , req , gErr , tc . err )
}
2020-07-22 00:14:28 -04:00
if got != nil {
assert . NotNilf ( tc . res , "Expected UpdateScope response to be nil, but was %v" , got )
2021-04-14 16:19:02 -05:00
gotUpdateTime := got . GetItem (). GetUpdatedTime (). AsTime ()
2020-07-22 00:14:28 -04:00
// Verify it is a project updated after it was created
assert . True ( gotUpdateTime . After ( projCreated ), "Updated project should have been updated after it's creation. Was updated %v, which is after %v" , gotUpdateTime , projCreated )
// Clear all values which are hard to compare against.
got . Item . UpdatedTime , tc . res . Item . UpdatedTime = nil , nil
2020-08-07 12:55:02 -04:00
assert . Equal ( ver + 1 , got . GetItem (). GetVersion ())
tc . res . Item . Version = ver + 1
2020-07-22 00:14:28 -04:00
}
2023-10-31 13:44:17 +00:00
assert . Empty ( cmp . Diff (
got ,
tc . res ,
protocmp . Transform (),
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
cmpopts . SortSlices ( func ( a , b protocmp . Message ) bool {
return a . String () < b . String ()
}),
), "UpdateScope(%q) got response\n%q, wanted\n%q" , req , got , tc . res )
2020-07-22 00:14:28 -04:00
})
}
}
2022-11-16 17:17:28 -06:00
func TestListKeys ( t * testing . T ) {
tc := controller . NewTestController ( t , nil )
aToken := tc . Token ()
uToken := tc . UnprivilegedToken ()
iamRepoFn := func () ( * iam . Repository , error ) {
return tc . IamRepo (), nil
}
serversRepoFn := func () ( * server . Repository , error ) {
return tc . ServersRepo (), nil
}
authTokenRepoFn := func () ( * authtoken . Repository , error ) {
return tc . AuthTokenRepo (), nil
}
privCtx := auth . NewVerifierContext (
context . Background (),
iamRepoFn ,
authTokenRepoFn ,
serversRepoFn ,
tc . Kms (),
& authpb . RequestInfo {
PublicId : aToken . Id ,
EncryptedToken : strings . Split ( aToken . Token , "_" )[ 2 ],
TokenFormat : uint32 ( auth . AuthTokenTypeBearer ),
})
unprivCtx := auth . NewVerifierContext (
context . Background (),
iamRepoFn ,
authTokenRepoFn ,
serversRepoFn ,
tc . Kms (),
& authpb . RequestInfo {
PublicId : uToken . Id ,
EncryptedToken : strings . Split ( uToken . Token , "_" )[ 2 ],
TokenFormat : uint32 ( auth . AuthTokenTypeBearer ),
})
org , proj := iam . TestScopes ( t , tc . IamRepo ())
org . Name = "defaultOrg"
org . Description = "defaultOrg"
org , _ , err := tc . IamRepo (). UpdateScope ( context . Background (), org , 1 , [] string { "Name" , "Description" })
require . NoError ( t , err )
// Add new role for listing keys in org
listKeysRole := iam . TestRole ( t , tc . DbConn (), org . PublicId )
2024-01-17 23:54:49 -08:00
_ , err = tc . IamRepo (). AddRoleGrants ( context . Background (), listKeysRole . PublicId , 1 , [] string { "ids=*;type=*;actions=list-keys" })
2022-11-16 17:17:28 -06:00
require . NoError ( t , err )
_ , err = tc . IamRepo (). AddPrincipalRoles ( context . Background (), listKeysRole . PublicId , 2 , [] string { aToken . UserId })
require . NoError ( t , err )
proj . Name = "defaultProj"
proj . Description = "defaultProj"
proj , _ , err = tc . IamRepo (). UpdateScope ( context . Background (), proj , 1 , [] string { "Name" , "Description" })
require . NoError ( t , err )
// Add new role for listing keys in project
listKeysRole = iam . TestRole ( t , tc . DbConn (), proj . PublicId )
2024-01-17 23:54:49 -08:00
_ , err = tc . IamRepo (). AddRoleGrants ( context . Background (), listKeysRole . PublicId , 1 , [] string { "ids=*;type=*;actions=list-keys" })
2022-11-16 17:17:28 -06:00
require . NoError ( t , err )
_ , err = tc . IamRepo (). AddPrincipalRoles ( context . Background (), listKeysRole . PublicId , 2 , [] string { aToken . UserId })
require . NoError ( t , err )
cases := [] struct {
name string
req * pbs . ListKeysRequest
res * pbs . ListKeysResponse
authCtx context . Context
err error
}{
{
name : "List keys in the global scope" ,
req : & pbs . ListKeysRequest { Id : "global" },
res : & pbs . ListKeysResponse {
Items : [] * pb . Key {
{
Scope : & pb . ScopeInfo {
Id : "global" ,
Type : "global" ,
Name : "global" ,
Description : "Global Scope" ,
},
Purpose : "tokens" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : "global" ,
Type : "global" ,
Name : "global" ,
Description : "Global Scope" ,
},
Purpose : "oplog" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : "global" ,
Type : "global" ,
Name : "global" ,
Description : "Global Scope" ,
},
Purpose : "database" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : "global" ,
Type : "global" ,
Name : "global" ,
Description : "Global Scope" ,
},
Purpose : "audit" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : "global" ,
Type : "global" ,
Name : "global" ,
Description : "Global Scope" ,
},
Purpose : "oidc" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : "global" ,
Type : "global" ,
Name : "global" ,
Description : "Global Scope" ,
},
Purpose : "sessions" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : "global" ,
Type : "global" ,
Name : "global" ,
Description : "Global Scope" ,
},
Purpose : "rootKey" ,
Type : "kek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
},
},
authCtx : privCtx ,
},
{
name : "List keys in an existing org" ,
req : & pbs . ListKeysRequest { Id : org . GetPublicId ()},
res : & pbs . ListKeysResponse {
Items : [] * pb . Key {
{
Scope : & pb . ScopeInfo {
Id : org . PublicId ,
ParentScopeId : "global" ,
Type : "org" ,
Name : org . Name ,
Description : org . Description ,
},
Purpose : "tokens" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : org . PublicId ,
ParentScopeId : "global" ,
Type : "org" ,
Name : org . Name ,
Description : org . Description ,
},
Purpose : "oplog" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : org . PublicId ,
ParentScopeId : "global" ,
Type : "org" ,
Name : org . Name ,
Description : org . Description ,
},
Purpose : "database" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : org . PublicId ,
ParentScopeId : "global" ,
Type : "org" ,
Name : org . Name ,
Description : org . Description ,
},
Purpose : "audit" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : org . PublicId ,
ParentScopeId : "global" ,
Type : "org" ,
Name : org . Name ,
Description : org . Description ,
},
Purpose : "oidc" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : org . PublicId ,
ParentScopeId : "global" ,
Type : "org" ,
Name : org . Name ,
Description : org . Description ,
},
Purpose : "sessions" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : org . PublicId ,
ParentScopeId : "global" ,
Type : "org" ,
Name : org . Name ,
Description : org . Description ,
},
Purpose : "rootKey" ,
Type : "kek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
},
},
authCtx : privCtx ,
},
{
name : "List keys in a non existing org" ,
req : & pbs . ListKeysRequest { Id : "o_DoesntExis" },
authCtx : privCtx ,
err : handlers . ApiErrorWithCode ( codes . NotFound ),
},
{
name : "List keys in an existing project" ,
req : & pbs . ListKeysRequest { Id : proj . GetPublicId ()},
res : & pbs . ListKeysResponse {
Items : [] * pb . Key {
{
Scope : & pb . ScopeInfo {
Id : proj . PublicId ,
ParentScopeId : org . PublicId ,
Type : "project" ,
Name : proj . Name ,
Description : proj . Description ,
},
Purpose : "tokens" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : proj . PublicId ,
ParentScopeId : org . PublicId ,
Type : "project" ,
Name : proj . Name ,
Description : proj . Description ,
},
Purpose : "oplog" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : proj . PublicId ,
ParentScopeId : org . PublicId ,
Type : "project" ,
Name : proj . Name ,
Description : proj . Description ,
},
Purpose : "database" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : proj . PublicId ,
ParentScopeId : org . PublicId ,
Type : "project" ,
Name : proj . Name ,
Description : proj . Description ,
},
Purpose : "audit" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : proj . PublicId ,
ParentScopeId : org . PublicId ,
Type : "project" ,
Name : proj . Name ,
Description : proj . Description ,
},
Purpose : "oidc" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : proj . PublicId ,
ParentScopeId : org . PublicId ,
Type : "project" ,
Name : proj . Name ,
Description : proj . Description ,
},
Purpose : "sessions" ,
Type : "dek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
{
Scope : & pb . ScopeInfo {
Id : proj . PublicId ,
ParentScopeId : org . PublicId ,
Type : "project" ,
Name : proj . Name ,
Description : proj . Description ,
},
Purpose : "rootKey" ,
Type : "kek" ,
Versions : [] * pb . KeyVersion {
{
Version : 1 ,
},
},
},
},
},
authCtx : privCtx ,
},
{
name : "List keys in a non existing project" ,
req : & pbs . ListKeysRequest { Id : "p_DoesntExist" },
err : handlers . ApiErrorWithCode ( codes . NotFound ),
authCtx : privCtx ,
},
{
name : "Wrong id prefix" ,
req : & pbs . ListKeysRequest { Id : "j_1234567890" },
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
authCtx : privCtx ,
},
{
name : "space in id" ,
req : & pbs . ListKeysRequest { Id : "p_1 23456789" },
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
authCtx : privCtx ,
},
{
name : "unauthorized" ,
req : & pbs . ListKeysRequest { Id : "global" },
err : handlers . ApiErrorWithCode ( codes . PermissionDenied ),
authCtx : unprivCtx ,
},
}
for _ , tt := range cases {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2024-01-03 17:54:00 -08:00
s , err := scopes . NewServiceFn ( context . Background (), iamRepoFn , tc . Kms (), 1000 )
2022-11-16 17:17:28 -06:00
require . NoError ( err , "Couldn't create new project service." )
got , gErr := s . ListKeys ( tt . authCtx , tt . req )
if tt . err != nil {
require . Error ( gErr )
assert . True ( errors . Is ( gErr , tt . err ), "ListKeys(%+v) got error\n%v, wanted\n%v" , tt . req , gErr , tt . err )
} else {
require . NoError ( gErr )
}
assert . Empty (
cmp . Diff (
tt . res ,
got ,
protocmp . Transform (),
// Sort by purpose for comparison since it is the only unique and predictable field
protocmp . SortRepeated ( func ( i , j * pb . Key ) bool { return i . GetPurpose () < j . GetPurpose () }),
protocmp . IgnoreFields ( & pb . Key {}, "id" , "created_time" ),
protocmp . IgnoreFields ( & pb . KeyVersion {}, "id" , "created_time" ),
2023-10-31 13:44:17 +00:00
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
2022-11-16 17:17:28 -06:00
),
"ListKeys(%q) got response\n%q, wanted\n%q" , tt . req , got , tt . res ,
)
})
}
}
func TestRotateKeys ( t * testing . T ) {
tc := controller . NewTestController ( t , nil )
aToken := tc . Token ()
uToken := tc . UnprivilegedToken ()
iamRepoFn := func () ( * iam . Repository , error ) {
return tc . IamRepo (), nil
}
serversRepoFn := func () ( * server . Repository , error ) {
return tc . ServersRepo (), nil
}
authTokenRepoFn := func () ( * authtoken . Repository , error ) {
return tc . AuthTokenRepo (), nil
}
privCtx := auth . NewVerifierContext (
context . Background (),
iamRepoFn ,
authTokenRepoFn ,
serversRepoFn ,
tc . Kms (),
& authpb . RequestInfo {
PublicId : aToken . Id ,
EncryptedToken : strings . Split ( aToken . Token , "_" )[ 2 ],
TokenFormat : uint32 ( auth . AuthTokenTypeBearer ),
})
unprivCtx := auth . NewVerifierContext (
context . Background (),
iamRepoFn ,
authTokenRepoFn ,
serversRepoFn ,
tc . Kms (),
& authpb . RequestInfo {
PublicId : uToken . Id ,
EncryptedToken : strings . Split ( uToken . Token , "_" )[ 2 ],
TokenFormat : uint32 ( auth . AuthTokenTypeBearer ),
})
org , proj := iam . TestScopes ( t , tc . IamRepo (), iam . WithUserId ( aToken . UserId ))
// Add new role for listing+rotating keys in org
rotateKeysRole := iam . TestRole ( t , tc . DbConn (), org . PublicId )
2024-01-17 23:54:49 -08:00
iam . TestRoleGrant ( t , tc . DbConn (), rotateKeysRole . PublicId , "ids=*;type=*;actions=rotate-keys,list-keys" )
2022-11-16 17:17:28 -06:00
_ = iam . TestUserRole ( t , tc . DbConn (), rotateKeysRole . PublicId , aToken . UserId )
// Add new role for listing+rotating keys in project
rotateKeysRole = iam . TestRole ( t , tc . DbConn (), proj . PublicId )
2024-01-17 23:54:49 -08:00
iam . TestRoleGrant ( t , tc . DbConn (), rotateKeysRole . PublicId , "ids=*;type=*;actions=rotate-keys,list-keys" )
2022-11-16 17:17:28 -06:00
_ = iam . TestUserRole ( t , tc . DbConn (), rotateKeysRole . PublicId , aToken . UserId )
cases := [] struct {
name string
req * pbs . RotateKeysRequest
res * pbs . RotateKeysResponse
authCtx context . Context
err error
}{
{
name : "unauthorized" ,
req : & pbs . RotateKeysRequest { ScopeId : "global" , Rewrap : false },
err : handlers . ApiErrorWithCode ( codes . PermissionDenied ),
authCtx : unprivCtx ,
},
{
name : "Rotate keys in a non existing org" ,
req : & pbs . RotateKeysRequest { ScopeId : "o_DoesntExis" , Rewrap : false },
authCtx : privCtx ,
err : handlers . ApiErrorWithCode ( codes . NotFound ),
},
{
name : "successfully Rotate keys in org" ,
req : & pbs . RotateKeysRequest { ScopeId : org . GetPublicId (), Rewrap : false },
authCtx : privCtx ,
},
{
name : "successfully Rotate and rewrap keys in org" ,
req : & pbs . RotateKeysRequest { ScopeId : org . GetPublicId (), Rewrap : true },
authCtx : privCtx ,
},
{
name : "successfully Rotate keys in project" ,
req : & pbs . RotateKeysRequest { ScopeId : proj . GetPublicId (), Rewrap : false },
authCtx : privCtx ,
},
{
name : "successfully Rotate and rewrap keys in project" ,
req : & pbs . RotateKeysRequest { ScopeId : proj . GetPublicId (), Rewrap : true },
authCtx : privCtx ,
},
}
for _ , tt := range cases {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2024-01-03 17:54:00 -08:00
s , err := scopes . NewServiceFn ( context . Background (), iamRepoFn , tc . Kms (), 1000 )
2022-11-16 17:17:28 -06:00
require . NoError ( err , "Couldn't create new project service." )
prevKeyVersions := map [ uint32 ] int {}
var prevLatest uint32 = 0
// checking key versions before rotation
if tt . err == nil {
keys , gErr := s . ListKeys ( privCtx , & pbs . ListKeysRequest { Id : tt . req . ScopeId })
require . NoError ( gErr )
for _ , key := range keys . Items {
for _ , keyVersion := range key . Versions {
if keyVersion . Version > prevLatest {
prevLatest = keyVersion . Version
}
_ , ok := prevKeyVersions [ keyVersion . Version ]
if ! ok {
prevKeyVersions [ keyVersion . Version ] = 0
}
prevKeyVersions [ keyVersion . Version ] ++
}
}
}
// RotateKeys returns nocontent response
_ , kErr := s . RotateKeys ( tt . authCtx , tt . req )
if tt . err != nil {
require . Error ( kErr )
assert . True ( errors . Is ( kErr , tt . err ), "RotateKeys(%+v) got error\n%v, wanted\n%v" , tt . req , kErr , tt . err )
} else {
require . NoError ( kErr )
keys , gErr := s . ListKeys ( privCtx , & pbs . ListKeysRequest { Id : tt . req . ScopeId })
require . NoError ( gErr )
keyVersions := map [ uint32 ] int {}
var latest uint32 = 0
for _ , key := range keys . Items {
for _ , keyVersion := range key . Versions {
if keyVersion . Version > latest {
latest = keyVersion . Version
}
_ , ok := keyVersions [ keyVersion . Version ]
if ! ok {
keyVersions [ keyVersion . Version ] = 0
}
keyVersions [ keyVersion . Version ] ++
}
}
// there should only be one new key version
assert . Equal ( len ( prevKeyVersions ) + 1 , len ( keyVersions ))
// since we just rotated them, there should be the same number of version 1 and version 2
assert . Equal ( prevKeyVersions [ prevLatest ], keyVersions [ latest ])
}
})
}
}
func TestListKeyVersionDestructionJobs ( t * testing . T ) {
tc := controller . NewTestController ( t , nil )
aToken := tc . Token ()
uToken := tc . UnprivilegedToken ()
iamRepoFn := func () ( * iam . Repository , error ) {
return tc . IamRepo (), nil
}
serversRepoFn := func () ( * server . Repository , error ) {
return tc . ServersRepo (), nil
}
authTokenRepoFn := func () ( * authtoken . Repository , error ) {
return tc . AuthTokenRepo (), nil
}
privCtx := auth . NewVerifierContext (
tc . Context (),
iamRepoFn ,
authTokenRepoFn ,
serversRepoFn ,
tc . Kms (),
& authpb . RequestInfo {
PublicId : aToken . Id ,
EncryptedToken : strings . Split ( aToken . Token , "_" )[ 2 ],
TokenFormat : uint32 ( auth . AuthTokenTypeBearer ),
},
)
unprivCtx := auth . NewVerifierContext (
tc . Context (),
iamRepoFn ,
authTokenRepoFn ,
serversRepoFn ,
tc . Kms (),
& authpb . RequestInfo {
PublicId : uToken . Id ,
EncryptedToken : strings . Split ( uToken . Token , "_" )[ 2 ],
TokenFormat : uint32 ( auth . AuthTokenTypeBearer ),
},
)
sqldb , err := tc . DbConn (). SqlDB ( tc . Context ())
require . NoError ( t , err )
org , proj := iam . TestScopes ( t , tc . IamRepo ())
org . Name = "defaultOrg"
org . Description = "defaultOrg"
org , _ , err = tc . IamRepo (). UpdateScope ( tc . Context (), org , 1 , [] string { "Name" , "Description" })
require . NoError ( t , err )
// Add new role for listing key version destructions in org
listKeysRole := iam . TestRole ( t , tc . DbConn (), org . PublicId )
2024-01-17 23:54:49 -08:00
_ = iam . TestRoleGrant ( t , tc . DbConn (), listKeysRole . PublicId , "ids=*;type=*;actions=list-key-version-destruction-jobs" )
2022-11-16 17:17:28 -06:00
_ = iam . TestUserRole ( t , tc . DbConn (), listKeysRole . PublicId , aToken . UserId )
// Create a oidc auth method to create an encrypted value in this scope
databaseWrapper , err := tc . Kms (). GetWrapper ( tc . Context (), org . PublicId , kms . KeyPurposeDatabase )
require . NoError ( t , err )
_ = oidc . TestAuthMethod ( t , tc . DbConn (), databaseWrapper , org . PublicId , oidc . InactiveState , "noAccounts" , "fido" )
proj . Name = "defaultProj"
proj . Description = "defaultProj"
proj , _ , err = tc . IamRepo (). UpdateScope ( tc . Context (), proj , 1 , [] string { "Name" , "Description" })
require . NoError ( t , err )
// Add new role for listing key version destructions in project
listKeysRole = iam . TestRole ( t , tc . DbConn (), proj . PublicId )
2024-01-17 23:54:49 -08:00
_ = iam . TestRoleGrant ( t , tc . DbConn (), listKeysRole . PublicId , "ids=*;type=*;actions=list-key-version-destruction-jobs" )
2022-11-16 17:17:28 -06:00
_ = iam . TestUserRole ( t , tc . DbConn (), listKeysRole . PublicId , aToken . UserId )
// Create a oidc auth method to create an encrypted value in this scope
databaseWrapper , err = tc . Kms (). GetWrapper ( tc . Context (), proj . PublicId , kms . KeyPurposeDatabase )
require . NoError ( t , err )
_ = oidc . TestAuthMethod ( t , tc . DbConn (), databaseWrapper , proj . PublicId , oidc . InactiveState , "noAccounts" , "fido" )
for _ , scope := range [] string { "global" , org . PublicId , proj . PublicId } {
err = tc . Kms (). RotateKeys ( tc . Context (), scope )
require . NoError ( t , err )
keys , err := tc . Kms (). ListKeys ( tc . Context (), scope )
require . NoError ( t , err )
var kvToDestroy wrappingKms . KeyVersion
for _ , key := range keys {
if key . Purpose == wrappingKms . KeyPurpose ( kms . KeyPurposeDatabase . String ()) {
kvToDestroy = key . Versions [ 0 ]
}
}
destroyed , err := tc . Kms (). DestroyKeyVersion ( tc . Context (), scope , kvToDestroy . Id )
require . NoError ( t , err )
assert . False ( t , destroyed )
t . Cleanup ( func () {
_ , err = sqldb . ExecContext ( tc . Context (), "delete from kms_data_key_version_destruction_job where key_id=$1" , kvToDestroy . Id )
require . NoError ( t , err )
})
}
cases := [] struct {
name string
req * pbs . ListKeyVersionDestructionJobsRequest
res * pbs . ListKeyVersionDestructionJobsResponse
authCtx context . Context
err error
}{
{
name : "List key version destruction jobs in the global scope" ,
req : & pbs . ListKeyVersionDestructionJobsRequest { ScopeId : "global" },
res : & pbs . ListKeyVersionDestructionJobsResponse {
Items : [] * pb . KeyVersionDestructionJob {
{
Scope : & pb . ScopeInfo {
Id : "global" ,
Type : "global" ,
Name : "global" ,
Description : "Global Scope" ,
},
Status : "pending" ,
CompletedCount : 0 ,
TotalCount : 7 ,
},
},
},
authCtx : privCtx ,
},
{
name : "List key version destruction jobs in an existing org" ,
req : & pbs . ListKeyVersionDestructionJobsRequest { ScopeId : org . GetPublicId ()},
res : & pbs . ListKeyVersionDestructionJobsResponse {
Items : [] * pb . KeyVersionDestructionJob {
{
Scope : & pb . ScopeInfo {
Id : org . GetPublicId (),
ParentScopeId : "global" ,
Type : "org" ,
Name : org . GetName (),
Description : org . GetDescription (),
},
Status : "pending" ,
CompletedCount : 0 ,
TotalCount : 1 ,
},
},
},
authCtx : privCtx ,
},
{
name : "List key version destruction jobs in a non existing org" ,
req : & pbs . ListKeyVersionDestructionJobsRequest { ScopeId : "o_DoesntExis" },
authCtx : privCtx ,
err : handlers . ApiErrorWithCode ( codes . NotFound ),
},
{
name : "List key version destruction jobs in an existing project" ,
req : & pbs . ListKeyVersionDestructionJobsRequest { ScopeId : proj . GetPublicId ()},
res : & pbs . ListKeyVersionDestructionJobsResponse {
Items : [] * pb . KeyVersionDestructionJob {
{
Scope : & pb . ScopeInfo {
Id : proj . GetPublicId (),
ParentScopeId : org . GetPublicId (),
Type : "project" ,
Name : proj . GetName (),
Description : proj . GetDescription (),
},
Status : "pending" ,
CompletedCount : 0 ,
TotalCount : 1 ,
},
},
},
authCtx : privCtx ,
},
{
name : "List key version destruction jobs in a non existing project" ,
req : & pbs . ListKeyVersionDestructionJobsRequest { ScopeId : "p_DoesntExist" },
authCtx : privCtx ,
err : handlers . ApiErrorWithCode ( codes . NotFound ),
},
{
name : "Wrong id prefix" ,
req : & pbs . ListKeyVersionDestructionJobsRequest { ScopeId : "j_1234567890" },
authCtx : privCtx ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "space in id" ,
req : & pbs . ListKeyVersionDestructionJobsRequest { ScopeId : "p_1 23456789" },
authCtx : privCtx ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "unauthorized" ,
req : & pbs . ListKeyVersionDestructionJobsRequest { ScopeId : "global" },
authCtx : unprivCtx ,
err : handlers . ApiErrorWithCode ( codes . PermissionDenied ),
},
}
for _ , tt := range cases {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2024-01-03 17:54:00 -08:00
s , err := scopes . NewServiceFn ( context . Background (), iamRepoFn , tc . Kms (), 1000 )
2022-11-16 17:17:28 -06:00
require . NoError ( err , "Couldn't create new project service." )
got , gErr := s . ListKeyVersionDestructionJobs ( tt . authCtx , tt . req )
if tt . err != nil {
require . Error ( gErr )
assert . True ( errors . Is ( gErr , tt . err ), "ListKeyVersionDestructionJobs(%+v) got error\n%v, wanted\n%v" , tt . req , gErr , tt . err )
} else {
require . NoError ( gErr )
}
assert . Empty (
cmp . Diff (
tt . res ,
got ,
protocmp . Transform (),
protocmp . SortRepeated ( func ( i , j * pb . KeyVersionDestructionJob ) bool { return i . GetTotalCount () < j . GetTotalCount () }),
protocmp . IgnoreFields ( & pb . KeyVersionDestructionJob {}, "key_version_id" , "created_time" ),
2023-10-31 13:44:17 +00:00
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
cmpopts . SortSlices ( func ( a , b protocmp . Message ) bool {
return a . String () < b . String ()
}),
2022-11-16 17:17:28 -06:00
),
"ListKeyVersionDestructionJobs(%q) got response\n%q, wanted\n%q" , tt . req , got , tt . res ,
)
})
}
}
func TestDestroyKeyVersion ( t * testing . T ) {
tc := controller . NewTestController ( t , nil )
aToken := tc . Token ()
uToken := tc . UnprivilegedToken ()
iamRepoFn := func () ( * iam . Repository , error ) {
return tc . IamRepo (), nil
}
serversRepoFn := func () ( * server . Repository , error ) {
return tc . ServersRepo (), nil
}
authTokenRepoFn := func () ( * authtoken . Repository , error ) {
return tc . AuthTokenRepo (), nil
}
privCtx := auth . NewVerifierContext (
tc . Context (),
iamRepoFn ,
authTokenRepoFn ,
serversRepoFn ,
tc . Kms (),
& authpb . RequestInfo {
PublicId : aToken . Id ,
EncryptedToken : strings . Split ( aToken . Token , "_" )[ 2 ],
TokenFormat : uint32 ( auth . AuthTokenTypeBearer ),
},
)
unprivCtx := auth . NewVerifierContext (
tc . Context (),
iamRepoFn ,
authTokenRepoFn ,
serversRepoFn ,
tc . Kms (),
& authpb . RequestInfo {
PublicId : uToken . Id ,
EncryptedToken : strings . Split ( uToken . Token , "_" )[ 2 ],
TokenFormat : uint32 ( auth . AuthTokenTypeBearer ),
},
)
org , proj := iam . TestScopes ( t , tc . IamRepo ())
org . Name = "defaultOrg"
org . Description = "defaultOrg"
org , _ , err := tc . IamRepo (). UpdateScope ( tc . Context (), org , 1 , [] string { "Name" , "Description" })
require . NoError ( t , err )
// Add new role for destroying key versions in org
listKeysRole := iam . TestRole ( t , tc . DbConn (), org . PublicId )
2024-01-17 23:54:49 -08:00
_ = iam . TestRoleGrant ( t , tc . DbConn (), listKeysRole . PublicId , "ids=*;type=*;actions=destroy-key-version" )
2022-11-16 17:17:28 -06:00
_ = iam . TestUserRole ( t , tc . DbConn (), listKeysRole . PublicId , aToken . UserId )
// Create a oidc auth method to create an encrypted value in this scope
databaseWrapper , err := tc . Kms (). GetWrapper ( tc . Context (), org . PublicId , kms . KeyPurposeDatabase )
require . NoError ( t , err )
_ = oidc . TestAuthMethod ( t , tc . DbConn (), databaseWrapper , org . PublicId , oidc . InactiveState , "noAccounts" , "fido" )
proj . Name = "defaultProj"
proj . Description = "defaultProj"
proj , _ , err = tc . IamRepo (). UpdateScope ( tc . Context (), proj , 1 , [] string { "Name" , "Description" })
require . NoError ( t , err )
// Add new role for destroying key versions in project
listKeysRole = iam . TestRole ( t , tc . DbConn (), proj . PublicId )
2024-01-17 23:54:49 -08:00
_ = iam . TestRoleGrant ( t , tc . DbConn (), listKeysRole . PublicId , "ids=*;type=*;actions=destroy-key-version" )
2022-11-16 17:17:28 -06:00
_ = iam . TestUserRole ( t , tc . DbConn (), listKeysRole . PublicId , aToken . UserId )
// Create a oidc auth method to create an encrypted value in this scope
databaseWrapper , err = tc . Kms (). GetWrapper ( tc . Context (), proj . PublicId , kms . KeyPurposeDatabase )
require . NoError ( t , err )
_ = oidc . TestAuthMethod ( t , tc . DbConn (), databaseWrapper , proj . PublicId , oidc . InactiveState , "noAccounts" , "fido" )
scopeToImmediatelyDestroyedKeyVersionId := map [ string ] string {}
scopeToPendingDestructionKeyVersionId := map [ string ] string {}
for _ , scope := range [] string { "global" , org . PublicId , proj . PublicId } {
err = tc . Kms (). RotateKeys ( tc . Context (), scope )
require . NoError ( t , err )
keys , err := tc . Kms (). ListKeys ( tc . Context (), scope )
require . NoError ( t , err )
for _ , key := range keys {
switch key . Purpose {
case wrappingKms . KeyPurpose ( kms . KeyPurposeDatabase . String ()):
scopeToPendingDestructionKeyVersionId [ scope ] = key . Versions [ 0 ]. Id
case wrappingKms . KeyPurpose ( kms . KeyPurposeRootKey . String ()):
scopeToImmediatelyDestroyedKeyVersionId [ scope ] = key . Versions [ 0 ]. Id
}
}
}
cases := [] struct {
name string
req * pbs . DestroyKeyVersionRequest
res * pbs . DestroyKeyVersionResponse
authCtx context . Context
err error
}{
{
name : "Errors when specifying a non existing key version Id" ,
req : & pbs . DestroyKeyVersionRequest {
ScopeId : "global" ,
KeyVersionId : "krkv_DoesntExist" ,
},
authCtx : privCtx ,
err : handlers . ApiErrorWithCode ( codes . NotFound ),
},
{
name : "Creates a key version destruction job in the global scope" ,
req : & pbs . DestroyKeyVersionRequest {
ScopeId : "global" ,
KeyVersionId : scopeToPendingDestructionKeyVersionId [ "global" ],
},
res : & pbs . DestroyKeyVersionResponse {
State : "pending" ,
},
authCtx : privCtx ,
},
{
name : "Immediately destroys a root key version in the global scope" ,
req : & pbs . DestroyKeyVersionRequest {
ScopeId : "global" ,
KeyVersionId : scopeToImmediatelyDestroyedKeyVersionId [ "global" ],
},
res : & pbs . DestroyKeyVersionResponse {
State : "completed" ,
},
authCtx : privCtx ,
},
{
name : "Creates a key version destruction job in an existing org" ,
req : & pbs . DestroyKeyVersionRequest {
ScopeId : org . GetPublicId (),
KeyVersionId : scopeToPendingDestructionKeyVersionId [ org . GetPublicId ()],
},
res : & pbs . DestroyKeyVersionResponse {
State : "pending" ,
},
authCtx : privCtx ,
},
{
name : "Immediately destroys a root key version in an existing org" ,
req : & pbs . DestroyKeyVersionRequest {
ScopeId : org . GetPublicId (),
KeyVersionId : scopeToImmediatelyDestroyedKeyVersionId [ org . GetPublicId ()],
},
res : & pbs . DestroyKeyVersionResponse {
State : "completed" ,
},
authCtx : privCtx ,
},
{
name : "Creates a key version destruction job in an existing project" ,
req : & pbs . DestroyKeyVersionRequest {
ScopeId : proj . GetPublicId (),
KeyVersionId : scopeToPendingDestructionKeyVersionId [ proj . GetPublicId ()],
},
res : & pbs . DestroyKeyVersionResponse {
State : "pending" ,
},
authCtx : privCtx ,
},
{
name : "Immediately destroys a root key version in an existing project" ,
req : & pbs . DestroyKeyVersionRequest {
ScopeId : proj . GetPublicId (),
KeyVersionId : scopeToImmediatelyDestroyedKeyVersionId [ proj . GetPublicId ()],
},
res : & pbs . DestroyKeyVersionResponse {
State : "completed" ,
},
authCtx : privCtx ,
},
{
name : "Errors when specifying a key version that doesn't exist in the scope" ,
req : & pbs . DestroyKeyVersionRequest {
ScopeId : org . GetPublicId (),
KeyVersionId : scopeToPendingDestructionKeyVersionId [ proj . GetPublicId ()],
},
authCtx : privCtx ,
err : handlers . ApiErrorWithCode ( codes . NotFound ),
},
{
name : "Errors when specifying a non existing org" ,
req : & pbs . DestroyKeyVersionRequest {
ScopeId : "o_DoesntExist" ,
KeyVersionId : scopeToImmediatelyDestroyedKeyVersionId [ org . GetPublicId ()],
},
authCtx : privCtx ,
err : handlers . ApiErrorWithCode ( codes . NotFound ),
},
{
name : "Errors when specifying a non existing project" ,
req : & pbs . DestroyKeyVersionRequest {
ScopeId : "p_DoesntExist" ,
KeyVersionId : scopeToImmediatelyDestroyedKeyVersionId [ proj . GetPublicId ()],
},
authCtx : privCtx ,
err : handlers . ApiErrorWithCode ( codes . NotFound ),
},
{
name : "Wrong id prefix" ,
req : & pbs . DestroyKeyVersionRequest {
ScopeId : "j_1234567890" ,
KeyVersionId : scopeToImmediatelyDestroyedKeyVersionId [ "global" ],
},
authCtx : privCtx ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "space in id" ,
req : & pbs . DestroyKeyVersionRequest {
ScopeId : "p_1 23456789" ,
KeyVersionId : scopeToImmediatelyDestroyedKeyVersionId [ "global" ],
},
authCtx : privCtx ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "unauthorized" ,
req : & pbs . DestroyKeyVersionRequest {
ScopeId : "global" ,
KeyVersionId : scopeToImmediatelyDestroyedKeyVersionId [ "global" ],
},
authCtx : unprivCtx ,
err : handlers . ApiErrorWithCode ( codes . PermissionDenied ),
},
}
for _ , tt := range cases {
t . Run ( tt . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2024-01-03 17:54:00 -08:00
s , err := scopes . NewServiceFn ( context . Background (), iamRepoFn , tc . Kms (), 1000 )
2022-11-16 17:17:28 -06:00
require . NoError ( err , "Couldn't create new project service." )
got , gErr := s . DestroyKeyVersion ( tt . authCtx , tt . req )
if tt . err != nil {
require . Error ( gErr )
assert . True ( errors . Is ( gErr , tt . err ), "DestroyKeyVersion(%+v) got error\n%v, wanted\n%v" , tt . req , gErr , tt . err )
} else {
require . NoError ( gErr )
}
2023-10-31 13:44:17 +00:00
assert . Empty ( cmp . Diff (
tt . res ,
got ,
protocmp . Transform (),
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
cmpopts . SortSlices ( func ( a , b protocmp . Message ) bool {
return a . String () < b . String ()
}),
), "DestroyKeyVersion(%q) got response\n%q, wanted\n%q" , tt . req , got , tt . res )
2022-11-16 17:17:28 -06:00
})
}
}
2024-01-03 17:54:00 -08:00
func TestAttachStoragePolicy ( t * testing . T ) {
t . Run ( "unimplemented" , func ( t * testing . T ) {
service := & scopes . Service {}
_ , err := service . AttachStoragePolicy ( context . Background (), & pbs . AttachStoragePolicyRequest {})
require . Error ( t , err )
gotStatus , ok := status . FromError ( err )
require . True ( t , ok )
assert . Equal ( t , gotStatus . Code (), codes . Unimplemented )
assert . Equal ( t , gotStatus . Message (), "Policies are an Enterprise-only feature" )
})
}
func TestDetachStoragePolicy ( t * testing . T ) {
t . Run ( "unimplemented" , func ( t * testing . T ) {
service := & scopes . Service {}
_ , err := service . DetachStoragePolicy ( context . Background (), & pbs . DetachStoragePolicyRequest {})
require . Error ( t , err )
gotStatus , ok := status . FromError ( err )
require . True ( t , ok )
assert . Equal ( t , gotStatus . Code (), codes . Unimplemented )
assert . Equal ( t , gotStatus . Message (), "Policies are an Enterprise-only feature" )
})
}