2022-07-18 19:16:11 -04:00
// Copyright (c) HashiCorp, Inc.
2022-09-16 15:00:04 -07:00
// SPDX-License-Identifier: BUSL-1.1
2023-02-02 17:15:17 -08:00
2022-05-18 20:20:45 -07:00
package credentials
import (
"context"
"encoding/base64"
2022-09-16 15:00:04 -07:00
"encoding/json"
2022-05-18 20:20:45 -07:00
"fmt"
2023-11-29 16:29:55 -08:00
"slices"
2022-05-18 20:20:45 -07:00
"strings"
"testing"
"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"
2023-11-29 16:29:55 -08:00
"github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/authtoken"
2022-07-18 19:16:11 -04:00
"github.com/hashicorp/boundary/internal/credential"
2022-05-18 20:20:45 -07:00
"github.com/hashicorp/boundary/internal/credential/static"
2023-11-29 16:29:55 -08:00
"github.com/hashicorp/boundary/internal/credential/static/store"
2022-05-18 20:20:45 -07:00
"github.com/hashicorp/boundary/internal/credential/vault"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
2023-11-29 16:29:55 -08:00
authpb "github.com/hashicorp/boundary/internal/gen/controller/auth"
2022-05-18 20:20:45 -07:00
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/libs/crypto"
2023-11-29 16:29:55 -08:00
"github.com/hashicorp/boundary/internal/requests"
2022-05-18 20:20:45 -07:00
"github.com/hashicorp/boundary/internal/scheduler"
2023-11-29 16:29:55 -08:00
"github.com/hashicorp/boundary/internal/server"
2022-05-18 20:20:45 -07:00
"github.com/hashicorp/boundary/internal/types/scope"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/credentials"
scopepb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2022-08-08 15:15:03 -04:00
"golang.org/x/crypto/ssh/testdata"
2022-05-18 20:20:45 -07:00
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/fieldmaskpb"
2022-09-16 15:00:04 -07:00
"google.golang.org/protobuf/types/known/structpb"
2022-05-18 20:20:45 -07:00
"google.golang.org/protobuf/types/known/timestamppb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
var testAuthorizedActions = [] string { "no-op" , "read" , "update" , "delete" }
2023-11-29 16:29:55 -08:00
func staticJsonCredentialToProto ( cred * static . JsonCredential , prj * iam . Scope , hmac string ) * pb . Credential {
return & pb . Credential {
Id : cred . GetPublicId (),
CredentialStoreId : cred . GetStoreId (),
Scope : & scopepb . ScopeInfo { Id : prj . GetPublicId (), Type : scope . Project . String (), ParentScopeId : prj . GetParentId ()},
CreatedTime : cred . GetCreateTime (). GetTimestamp (),
UpdatedTime : cred . GetUpdateTime (). GetTimestamp (),
Version : cred . GetVersion (),
Type : credential . JsonSubtype . String (),
AuthorizedActions : testAuthorizedActions ,
Attrs : & pb . Credential_JsonAttributes {
JsonAttributes : & pb . JsonAttributes {
ObjectHmac : base64 . RawURLEncoding . EncodeToString ([] byte ( hmac )),
},
},
}
}
func staticPasswordCredentialToProto ( cred * static . UsernamePasswordCredential , prj * iam . Scope , hmac string ) * pb . Credential {
return & pb . Credential {
Id : cred . GetPublicId (),
CredentialStoreId : cred . GetStoreId (),
Scope : & scopepb . ScopeInfo { Id : prj . GetPublicId (), Type : scope . Project . String (), ParentScopeId : prj . GetParentId ()},
CreatedTime : cred . GetCreateTime (). GetTimestamp (),
UpdatedTime : cred . GetUpdateTime (). GetTimestamp (),
Version : cred . GetVersion (),
Type : credential . UsernamePasswordSubtype . String (),
AuthorizedActions : testAuthorizedActions ,
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Username : wrapperspb . String ( cred . GetUsername ()),
PasswordHmac : base64 . RawURLEncoding . EncodeToString ([] byte ( hmac )),
},
},
}
}
func staticSshCredentialToProto ( cred * static . SshPrivateKeyCredential , prj * iam . Scope , hmac string ) * pb . Credential {
return & pb . Credential {
Id : cred . GetPublicId (),
CredentialStoreId : cred . GetStoreId (),
Scope : & scopepb . ScopeInfo { Id : prj . GetPublicId (), Type : scope . Project . String (), ParentScopeId : prj . GetParentId ()},
CreatedTime : cred . GetCreateTime (). GetTimestamp (),
UpdatedTime : cred . GetUpdateTime (). GetTimestamp (),
Version : cred . GetVersion (),
Type : credential . SshPrivateKeySubtype . String (),
AuthorizedActions : testAuthorizedActions ,
Attrs : & pb . Credential_SshPrivateKeyAttributes {
SshPrivateKeyAttributes : & pb . SshPrivateKeyAttributes {
Username : wrapperspb . String ( cred . GetUsername ()),
PrivateKeyHmac : base64 . RawURLEncoding . EncodeToString ([] byte ( hmac )),
},
},
}
}
2022-05-18 20:20:45 -07:00
func TestList ( t * testing . T ) {
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2022-05-18 20:20:45 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
wrapper := db . TestWrapper ( t )
kkms := kms . TestKms ( t , conn , wrapper )
rw := db . New ( conn )
iamRepo := iam . TestRepo ( t , conn , wrapper )
iamRepoFn := func () ( * iam . Repository , error ) {
return iamRepo , nil
}
staticRepoFn := func () ( * static . Repository , error ) {
2023-06-06 17:09:08 -04:00
return static . NewRepository ( ctx , rw , rw , kkms )
2022-05-18 20:20:45 -07:00
}
_ , prj := iam . TestScopes ( t , iamRepo )
store := static . TestCredentialStore ( t , conn , wrapper , prj . GetPublicId ())
storeNoCreds := static . TestCredentialStore ( t , conn , wrapper , prj . GetPublicId ())
2023-06-06 17:09:08 -04:00
databaseWrapper , err := kkms . GetWrapper ( ctx , prj . GetPublicId (), kms . KeyPurposeDatabase )
2022-07-18 19:16:11 -04:00
require . NoError ( t , err )
2022-05-18 20:20:45 -07:00
var wantCreds [] * pb . Credential
for i := 0 ; i < 10 ; i ++ {
user := fmt . Sprintf ( "user-%d" , i )
pass := fmt . Sprintf ( "pass-%d" , i )
c := static . TestUsernamePasswordCredential ( t , conn , wrapper , user , pass , store . GetPublicId (), prj . GetPublicId ())
2023-06-06 17:09:08 -04:00
hm , err := crypto . HmacSha256 ( ctx , [] byte ( pass ), databaseWrapper , [] byte ( store . GetPublicId ()), nil , crypto . WithEd25519 ())
2022-05-18 20:20:45 -07:00
require . NoError ( t , err )
2023-11-29 16:29:55 -08:00
wantCreds = append ( wantCreds , staticPasswordCredentialToProto ( c , prj , hm ))
2022-07-18 19:16:11 -04:00
spk := static . TestSshPrivateKeyCredential ( t , conn , wrapper , user , static . TestSshPrivateKeyPem , store . GetPublicId (), prj . GetPublicId ())
2023-06-06 17:09:08 -04:00
hm , err = crypto . HmacSha256 ( ctx , [] byte ( static . TestSshPrivateKeyPem ), databaseWrapper , [] byte ( store . GetPublicId ()), nil )
2022-07-18 19:16:11 -04:00
require . NoError ( t , err )
2023-11-29 16:29:55 -08:00
wantCreds = append ( wantCreds , staticSshCredentialToProto ( spk , prj , hm ))
2022-09-16 15:00:04 -07:00
2023-10-30 11:03:04 -07:00
obj , objBytes := static . TestJsonObject ( t )
2022-09-16 15:00:04 -07:00
credJson := static . TestJsonCredential ( t , conn , wrapper , store . GetPublicId (), prj . GetPublicId (), obj )
2023-06-06 17:09:08 -04:00
hm , err = crypto . HmacSha256 ( ctx , objBytes , databaseWrapper , [] byte ( store . GetPublicId ()), nil )
2022-09-16 15:00:04 -07:00
require . NoError ( t , err )
2023-11-29 16:29:55 -08:00
wantCreds = append ( wantCreds , staticJsonCredentialToProto ( credJson , prj , hm ))
2022-05-18 20:20:45 -07:00
}
cases := [] struct {
name string
req * pbs . ListCredentialsRequest
res * pbs . ListCredentialsResponse
anonRes * pbs . ListCredentialsResponse
err error
}{
{
2023-11-29 16:29:55 -08:00
name : "List many credentials" ,
req : & pbs . ListCredentialsRequest { CredentialStoreId : store . GetPublicId ()},
res : & pbs . ListCredentialsResponse {
Items : wantCreds ,
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 30 ,
},
anonRes : & pbs . ListCredentialsResponse {
Items : wantCreds ,
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 30 ,
},
2022-05-18 20:20:45 -07:00
},
{
2023-11-29 16:29:55 -08:00
name : "List no credentials" ,
req : & pbs . ListCredentialsRequest { CredentialStoreId : storeNoCreds . GetPublicId ()},
res : & pbs . ListCredentialsResponse {
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
},
anonRes : & pbs . ListCredentialsResponse {
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
},
2022-05-18 20:20:45 -07:00
},
{
2023-11-29 16:29:55 -08:00
name : "Filter to one credential" ,
req : & pbs . ListCredentialsRequest { CredentialStoreId : store . GetPublicId (), Filter : fmt . Sprintf ( `"/item/id"==%q` , wantCreds [ 1 ]. GetId ())},
res : & pbs . ListCredentialsResponse {
Items : wantCreds [ 1 : 2 ],
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 1 ,
},
anonRes : & pbs . ListCredentialsResponse {
Items : wantCreds [ 1 : 2 ],
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 1 ,
},
2022-05-18 20:20:45 -07:00
},
{
2023-11-29 16:29:55 -08:00
name : "Filter on Attribute" ,
req : & pbs . ListCredentialsRequest { CredentialStoreId : store . GetPublicId (), Filter : fmt . Sprintf ( `"/item/attributes/username"==%q` , wantCreds [ 3 ]. GetUsernamePasswordAttributes (). GetUsername (). Value )},
res : & pbs . ListCredentialsResponse {
Items : wantCreds [ 3 : 5 ],
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 2 ,
},
anonRes : & pbs . ListCredentialsResponse {
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
}, // anonymous user does not have access to attributes
2022-05-18 20:20:45 -07:00
},
{
2023-11-29 16:29:55 -08:00
name : "Filter to no credential" ,
req : & pbs . ListCredentialsRequest { CredentialStoreId : store . GetPublicId (), Filter : `"/item/id"=="doesnt match"` },
res : & pbs . ListCredentialsResponse {
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
},
anonRes : & pbs . ListCredentialsResponse {
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
},
2022-05-18 20:20:45 -07:00
},
{
name : "Filter Bad Format" ,
req : & pbs . ListCredentialsRequest { CredentialStoreId : store . GetPublicId (), Filter : `"//id/"=="bad"` },
err : handlers . InvalidArgumentErrorf ( "bad format" , nil ),
},
}
for _ , tc := range cases {
t . Run ( tc . name , func ( t * testing . T ) {
2023-11-29 16:29:55 -08:00
s , err := NewService ( ctx , iamRepoFn , staticRepoFn , 1000 )
2022-05-18 20:20:45 -07:00
require . NoError ( t , err , "Couldn't create new host set service." )
// Test non-anonymous listing
got , gErr := s . ListCredentials ( auth . DisabledAuthTestContext ( iamRepoFn , prj . GetPublicId ()), tc . req )
if tc . err != nil {
require . Error ( t , gErr )
assert . True ( t , errors . Is ( gErr , tc . err ), "ListCredentialStore(%q) got error %v, wanted %v" , tc . req , gErr , tc . err )
return
}
require . NoError ( t , gErr )
2023-11-29 16:29:55 -08:00
assert . Empty (
t ,
cmp . Diff (
got ,
tc . res ,
protocmp . Transform (),
protocmp . SortRepeated ( func ( x , y * pb . Credential ) bool {
return x . Id < y . Id
}),
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
protocmp . IgnoreFields ( & pbs . ListCredentialsResponse {}, "list_token" ),
),
)
2022-05-18 20:20:45 -07:00
// Test anonymous listing
2022-10-28 15:52:06 -04:00
got , gErr = s . ListCredentials ( auth . DisabledAuthTestContext ( iamRepoFn , prj . GetPublicId (), auth . WithUserId ( globals . AnonymousUserId )), tc . req )
2022-05-18 20:20:45 -07:00
require . NoError ( t , gErr )
assert . Len ( t , got . Items , len ( tc . anonRes . Items ))
for _ , item := range got . GetItems () {
require . Nil ( t , item . CreatedTime )
require . Nil ( t , item . UpdatedTime )
require . Zero ( t , item . Version )
}
})
}
}
func TestGet ( t * testing . T ) {
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2022-05-18 20:20:45 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
wrapper := db . TestWrapper ( t )
kkms := kms . TestKms ( t , conn , wrapper )
rw := db . New ( conn )
iamRepo := iam . TestRepo ( t , conn , wrapper )
iamRepoFn := func () ( * iam . Repository , error ) {
return iamRepo , nil
}
staticRepoFn := func () ( * static . Repository , error ) {
return static . NewRepository ( context . Background (), rw , rw , kkms )
}
_ , prj := iam . TestScopes ( t , iamRepo )
2022-07-18 19:16:11 -04:00
databaseWrapper , err := kkms . GetWrapper ( context . Background (), prj . GetPublicId (), kms . KeyPurposeDatabase )
require . NoError ( t , err )
2022-05-18 20:20:45 -07:00
store := static . TestCredentialStore ( t , conn , wrapper , prj . GetPublicId ())
2023-11-29 16:29:55 -08:00
s , err := NewService ( ctx , iamRepoFn , staticRepoFn , 1000 )
2022-05-18 20:20:45 -07:00
require . NoError ( t , err )
2022-07-18 19:16:11 -04:00
upCred := static . TestUsernamePasswordCredential ( t , conn , wrapper , "user" , "pass" , store . GetPublicId (), prj . GetPublicId ())
2023-03-10 15:06:39 -05:00
upCredPrev := static . TestUsernamePasswordCredential ( t , conn , wrapper , "user" , "pass" , store . GetPublicId (), prj . GetPublicId (), static . WithPublicId ( fmt . Sprintf ( "%s_1234567890" , globals . UsernamePasswordCredentialPreviousPrefix )))
2022-07-18 19:16:11 -04:00
upHm , err := crypto . HmacSha256 ( context . Background (), [] byte ( "pass" ), databaseWrapper , [] byte ( store . GetPublicId ()), nil , crypto . WithEd25519 ())
2022-05-18 20:20:45 -07:00
require . NoError ( t , err )
2022-07-18 19:16:11 -04:00
spkCred := static . TestSshPrivateKeyCredential ( t , conn , wrapper , "user" , static . TestSshPrivateKeyPem , store . GetPublicId (), prj . GetPublicId ())
spkHm , err := crypto . HmacSha256 ( context . Background (), [] byte ( static . TestSshPrivateKeyPem ), databaseWrapper , [] byte ( store . GetPublicId ()), nil )
2022-05-18 20:20:45 -07:00
require . NoError ( t , err )
2022-08-08 15:15:03 -04:00
spkCredWithPass := static . TestSshPrivateKeyCredential ( t , conn , wrapper ,
"user" , string ( testdata . PEMEncryptedKeys [ 0 ]. PEMBytes ),
store . GetPublicId (), prj . GetPublicId (),
static . WithPrivateKeyPassphrase ([] byte ( testdata . PEMEncryptedKeys [ 0 ]. EncryptionKey )))
spkWithPassHm , err := crypto . HmacSha256 ( context . Background (), [] byte ( testdata . PEMEncryptedKeys [ 0 ]. PEMBytes ), databaseWrapper , [] byte ( store . GetPublicId ()), nil )
require . NoError ( t , err )
passHm , err := crypto . HmacSha256 ( context . Background (), [] byte ( testdata . PEMEncryptedKeys [ 0 ]. EncryptionKey ), databaseWrapper , [] byte ( store . GetPublicId ()), nil )
2023-11-29 16:29:55 -08:00
require . NoError ( t , err )
2022-09-16 15:00:04 -07:00
2023-10-30 11:03:04 -07:00
obj , objBytes := static . TestJsonObject ( t )
2022-09-16 15:00:04 -07:00
jsonCred := static . TestJsonCredential ( t , conn , wrapper , store . GetPublicId (), prj . GetPublicId (), obj )
objectHmac , err := crypto . HmacSha256 ( context . Background (), objBytes , databaseWrapper , [] byte ( store . GetPublicId ()), nil )
2022-08-08 15:15:03 -04:00
require . NoError ( t , err )
2022-05-18 20:20:45 -07:00
cases := [] struct {
name string
id string
res * pbs . GetCredentialResponse
err error
}{
{
2022-07-18 19:16:11 -04:00
name : "success-up" ,
id : upCred . GetPublicId (),
2022-05-18 20:20:45 -07:00
res : & pbs . GetCredentialResponse {
Item : & pb . Credential {
2022-07-18 19:16:11 -04:00
Id : upCred . GetPublicId (),
CredentialStoreId : upCred . GetStoreId (),
2022-07-20 16:24:46 -07:00
Scope : & scopepb . ScopeInfo { Id : store . GetProjectId (), Type : scope . Project . String (), ParentScopeId : prj . GetParentId ()},
2023-11-02 16:03:47 -04:00
Type : credential . UsernamePasswordSubtype . String (),
2022-05-18 20:20:45 -07:00
AuthorizedActions : testAuthorizedActions ,
2022-07-18 19:16:11 -04:00
CreatedTime : upCred . CreateTime . GetTimestamp (),
UpdatedTime : upCred . UpdateTime . GetTimestamp (),
2022-05-18 20:20:45 -07:00
Version : 1 ,
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Username : wrapperspb . String ( "user" ),
2022-07-18 19:16:11 -04:00
PasswordHmac : base64 . RawURLEncoding . EncodeToString ([] byte ( upHm )),
2022-05-18 20:20:45 -07:00
},
},
},
},
},
2022-07-15 13:09:55 -04:00
{
2022-07-18 19:16:11 -04:00
name : "success-up-prev-prefix" ,
id : upCredPrev . GetPublicId (),
2022-07-15 13:09:55 -04:00
res : & pbs . GetCredentialResponse {
Item : & pb . Credential {
2022-07-18 19:16:11 -04:00
Id : upCredPrev . GetPublicId (),
CredentialStoreId : upCred . GetStoreId (),
2022-07-20 16:24:46 -07:00
Scope : & scopepb . ScopeInfo { Id : store . GetProjectId (), Type : scope . Project . String (), ParentScopeId : prj . GetParentId ()},
2023-11-02 16:03:47 -04:00
Type : credential . UsernamePasswordSubtype . String (),
2022-07-15 13:09:55 -04:00
AuthorizedActions : testAuthorizedActions ,
2022-07-18 19:16:11 -04:00
CreatedTime : upCredPrev . CreateTime . GetTimestamp (),
UpdatedTime : upCredPrev . UpdateTime . GetTimestamp (),
2022-07-15 13:09:55 -04:00
Version : 1 ,
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Username : wrapperspb . String ( "user" ),
2022-07-18 19:16:11 -04:00
PasswordHmac : base64 . RawURLEncoding . EncodeToString ([] byte ( upHm )),
},
},
},
},
},
{
name : "success-spk" ,
id : spkCred . GetPublicId (),
res : & pbs . GetCredentialResponse {
Item : & pb . Credential {
Id : spkCred . GetPublicId (),
CredentialStoreId : spkCred . GetStoreId (),
2022-07-20 16:24:46 -07:00
Scope : & scopepb . ScopeInfo { Id : store . GetProjectId (), Type : scope . Project . String (), ParentScopeId : prj . GetParentId ()},
2023-11-02 16:03:47 -04:00
Type : credential . SshPrivateKeySubtype . String (),
2022-07-18 19:16:11 -04:00
AuthorizedActions : testAuthorizedActions ,
CreatedTime : spkCred . CreateTime . GetTimestamp (),
UpdatedTime : spkCred . UpdateTime . GetTimestamp (),
Version : 1 ,
Attrs : & pb . Credential_SshPrivateKeyAttributes {
SshPrivateKeyAttributes : & pb . SshPrivateKeyAttributes {
Username : wrapperspb . String ( "user" ),
PrivateKeyHmac : base64 . RawURLEncoding . EncodeToString ([] byte ( spkHm )),
2022-07-15 13:09:55 -04:00
},
},
},
},
},
2022-08-08 15:15:03 -04:00
{
name : "success-spk-with-pass" ,
id : spkCredWithPass . GetPublicId (),
res : & pbs . GetCredentialResponse {
Item : & pb . Credential {
Id : spkCredWithPass . GetPublicId (),
CredentialStoreId : spkCredWithPass . GetStoreId (),
2022-07-20 16:24:46 -07:00
Scope : & scopepb . ScopeInfo { Id : store . GetProjectId (), Type : scope . Project . String (), ParentScopeId : prj . GetParentId ()},
2023-11-02 16:03:47 -04:00
Type : credential . SshPrivateKeySubtype . String (),
2022-08-08 15:15:03 -04:00
AuthorizedActions : testAuthorizedActions ,
CreatedTime : spkCredWithPass . CreateTime . GetTimestamp (),
UpdatedTime : spkCredWithPass . UpdateTime . GetTimestamp (),
Version : 1 ,
Attrs : & pb . Credential_SshPrivateKeyAttributes {
SshPrivateKeyAttributes : & pb . SshPrivateKeyAttributes {
Username : wrapperspb . String ( "user" ),
PrivateKeyHmac : base64 . RawURLEncoding . EncodeToString ([] byte ( spkWithPassHm )),
PrivateKeyPassphraseHmac : base64 . RawURLEncoding . EncodeToString ([] byte ( passHm )),
},
},
},
},
},
2022-09-16 15:00:04 -07:00
{
name : "success-json" ,
id : jsonCred . GetPublicId (),
res : & pbs . GetCredentialResponse {
Item : & pb . Credential {
Id : jsonCred . GetPublicId (),
CredentialStoreId : jsonCred . GetStoreId (),
Scope : & scopepb . ScopeInfo { Id : store . GetProjectId (), Type : scope . Project . String (), ParentScopeId : prj . GetParentId ()},
2023-11-02 16:03:47 -04:00
Type : credential . JsonSubtype . String (),
2022-09-16 15:00:04 -07:00
AuthorizedActions : testAuthorizedActions ,
CreatedTime : jsonCred . CreateTime . GetTimestamp (),
UpdatedTime : jsonCred . UpdateTime . GetTimestamp (),
Version : 1 ,
Attrs : & pb . Credential_JsonAttributes {
JsonAttributes : & pb . JsonAttributes {
ObjectHmac : base64 . RawURLEncoding . EncodeToString ([] byte ( objectHmac )),
},
},
},
},
},
2022-05-18 20:20:45 -07:00
{
name : "not found error" ,
2023-03-10 15:06:39 -05:00
id : fmt . Sprintf ( "%s_1234567890" , globals . UsernamePasswordCredentialPrefix ),
2022-05-18 20:20:45 -07:00
err : handlers . NotFoundError (),
},
{
name : "bad prefix" ,
2023-03-10 15:06:39 -05:00
id : fmt . Sprintf ( "%s_1234567890" , globals . StaticCredentialStorePrefix ),
2022-05-18 20:20:45 -07:00
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
}
for _ , tc := range cases {
t . Run ( tc . name , func ( t * testing . T ) {
req := & pbs . GetCredentialRequest { Id : tc . id }
// Test non-anonymous get
got , gErr := s . GetCredential ( auth . DisabledAuthTestContext ( iamRepoFn , prj . GetPublicId ()), req )
if tc . err != nil {
require . Error ( t , gErr )
assert . True ( t , errors . Is ( gErr , tc . err ))
return
}
require . NoError ( t , gErr )
2023-10-31 13:44:17 +00:00
assert . Empty ( t , cmp . Diff (
got ,
tc . res ,
protocmp . Transform (),
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
))
2022-05-18 20:20:45 -07:00
// Test anonymous get
2022-10-28 15:52:06 -04:00
got , gErr = s . GetCredential ( auth . DisabledAuthTestContext ( iamRepoFn , prj . GetPublicId (), auth . WithUserId ( globals . AnonymousUserId )), req )
2022-05-18 20:20:45 -07:00
require . NoError ( t , gErr )
require . Nil ( t , got . Item . CreatedTime )
require . Nil ( t , got . Item . UpdatedTime )
require . Zero ( t , got . Item . Version )
})
}
}
func TestDelete ( t * testing . T ) {
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2022-05-18 20:20:45 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
wrapper := db . TestWrapper ( t )
kms := kms . TestKms ( t , conn , wrapper )
sche := scheduler . TestScheduler ( t , conn , wrapper )
rw := db . New ( conn )
err := vault . RegisterJobs ( context . Background (), sche , rw , rw , kms )
require . NoError ( t , err )
iamRepo := iam . TestRepo ( t , conn , wrapper )
iamRepoFn := func () ( * iam . Repository , error ) {
return iamRepo , nil
}
staticRepoFn := func () ( * static . Repository , error ) {
return static . NewRepository ( context . Background (), rw , rw , kms )
}
_ , prj := iam . TestScopes ( t , iamRepo )
store := static . TestCredentialStore ( t , conn , wrapper , prj . GetPublicId ())
2023-11-29 16:29:55 -08:00
s , err := NewService ( ctx , iamRepoFn , staticRepoFn , 1000 )
2022-05-18 20:20:45 -07:00
require . NoError ( t , err )
2022-07-18 19:16:11 -04:00
upCred := static . TestUsernamePasswordCredential ( t , conn , wrapper , "user" , "pass" , store . GetPublicId (), prj . GetPublicId ())
spkCred := static . TestSshPrivateKeyCredential ( t , conn , wrapper , "user" , static . TestSshPrivateKeyPem , store . GetPublicId (), prj . GetPublicId ())
2023-10-30 11:03:04 -07:00
obj , _ := static . TestJsonObject ( t )
2022-09-16 15:00:04 -07:00
jsonCred := static . TestJsonCredential ( t , conn , wrapper , store . GetPublicId (), prj . GetPublicId (), obj )
2022-05-18 20:20:45 -07:00
cases := [] struct {
name string
id string
err error
res * pbs . DeleteCredentialResponse
}{
{
2022-07-18 19:16:11 -04:00
name : "success-up" ,
id : upCred . GetPublicId (),
},
{
name : "success-spk" ,
id : spkCred . GetPublicId (),
2022-05-18 20:20:45 -07:00
},
2022-09-16 15:00:04 -07:00
{
name : "success-json" ,
id : jsonCred . GetPublicId (),
},
2022-05-18 20:20:45 -07:00
{
name : "not found error" ,
2023-03-10 15:06:39 -05:00
id : fmt . Sprintf ( "%s_1234567890" , globals . UsernamePasswordCredentialPrefix ),
2022-05-18 20:20:45 -07:00
err : handlers . NotFoundError (),
},
{
name : "bad prefix" ,
2023-03-10 15:06:39 -05:00
id : fmt . Sprintf ( "%s_1234567890" , globals . StaticCredentialStorePrefix ),
2022-05-18 20:20:45 -07:00
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
}
for _ , tc := range cases {
t . Run ( tc . name , func ( t * testing . T ) {
got , gErr := s . DeleteCredential ( auth . DisabledAuthTestContext ( iamRepoFn , prj . GetPublicId ()), & pbs . DeleteCredentialRequest { Id : tc . id })
assert . EqualValuesf ( t , tc . res , got , "DeleteCredentialStore got response %q, wanted %q" , got , tc . res )
if tc . err != nil {
require . Error ( t , gErr )
assert . True ( t , errors . Is ( gErr , tc . err ))
return
}
require . NoError ( t , gErr )
g , err := s . GetCredential ( auth . DisabledAuthTestContext ( iamRepoFn , prj . GetPublicId ()), & pbs . GetCredentialRequest { Id : tc . id })
assert . Nil ( t , g )
assert . True ( t , errors . Is ( err , handlers . NotFoundError ()))
})
}
}
func TestCreate ( t * testing . T ) {
2023-06-06 17:09:08 -04:00
ctx := context . Background ()
2022-05-18 20:20:45 -07:00
conn , _ := db . TestSetup ( t , "postgres" )
wrapper := db . TestWrapper ( t )
kkms := kms . TestKms ( t , conn , wrapper )
rw := db . New ( conn )
iamRepo := iam . TestRepo ( t , conn , wrapper )
iamRepoFn := func () ( * iam . Repository , error ) {
return iamRepo , nil
}
repoFn := func () ( * static . Repository , error ) {
return static . NewRepository ( context . Background (), rw , rw , kkms )
}
_ , prj := iam . TestScopes ( t , iamRepo )
store := static . TestCredentialStore ( t , conn , wrapper , prj . GetPublicId ())
2023-10-30 11:03:04 -07:00
obj , objBytes := static . TestJsonObject ( t )
2022-09-16 15:00:04 -07:00
2022-05-18 20:20:45 -07:00
cases := [] struct {
name string
req * pbs . CreateCredentialRequest
res * pbs . CreateCredentialResponse
idPrefix string
err error
wantErr bool
}{
{
name : "Can't specify Id" ,
req : & pbs . CreateCredentialRequest { Item : & pb . Credential {
CredentialStoreId : store . GetPublicId (),
2023-03-10 15:06:39 -05:00
Id : globals . UsernamePasswordCredentialPrefix + "_notallowed" ,
2023-11-02 16:03:47 -04:00
Type : credential . UsernamePasswordSubtype . String (),
2022-05-18 20:20:45 -07:00
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Username : wrapperspb . String ( "username" ),
Password : wrapperspb . String ( "password" ),
},
},
}},
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
2022-06-24 09:16:11 -07:00
{
name : "Invalid Credential Store Id" ,
req : & pbs . CreateCredentialRequest { Item : & pb . Credential {
CredentialStoreId : "p_invalidid" ,
2023-11-02 16:03:47 -04:00
Type : credential . UsernamePasswordSubtype . String (),
2022-06-24 09:16:11 -07:00
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Username : wrapperspb . String ( "username" ),
Password : wrapperspb . String ( "password" ),
},
},
}},
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
2022-05-18 20:20:45 -07:00
{
name : "Can't specify Created Time" ,
req : & pbs . CreateCredentialRequest { Item : & pb . Credential {
CredentialStoreId : store . GetPublicId (),
CreatedTime : timestamppb . Now (),
2023-11-02 16:03:47 -04:00
Type : credential . UsernamePasswordSubtype . String (),
2022-05-18 20:20:45 -07:00
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Username : wrapperspb . String ( "username" ),
},
},
}},
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "Can't specify Updated Time" ,
req : & pbs . CreateCredentialRequest { Item : & pb . Credential {
CredentialStoreId : store . GetPublicId (),
UpdatedTime : timestamppb . Now (),
2023-11-02 16:03:47 -04:00
Type : credential . UsernamePasswordSubtype . String (),
2022-05-18 20:20:45 -07:00
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Username : wrapperspb . String ( "username" ),
Password : wrapperspb . String ( "password" ),
},
},
}},
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "Must provide type" ,
req : & pbs . CreateCredentialRequest { Item : & pb . Credential {
CredentialStoreId : store . GetPublicId (),
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Username : wrapperspb . String ( "username" ),
Password : wrapperspb . String ( "password" ),
},
},
}},
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
name : "Must provide username" ,
req : & pbs . CreateCredentialRequest { Item : & pb . Credential {
CredentialStoreId : store . GetPublicId (),
2023-11-02 16:03:47 -04:00
Type : credential . UsernamePasswordSubtype . String (),
2022-05-18 20:20:45 -07:00
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Password : wrapperspb . String ( "password" ),
},
},
}},
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
2022-07-18 19:16:11 -04:00
name : "Must provide password" ,
2022-05-18 20:20:45 -07:00
req : & pbs . CreateCredentialRequest { Item : & pb . Credential {
CredentialStoreId : store . GetPublicId (),
2023-11-02 16:03:47 -04:00
Type : credential . UsernamePasswordSubtype . String (),
2022-05-18 20:20:45 -07:00
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Username : wrapperspb . String ( "username" ),
},
},
}},
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
{
2022-07-18 19:16:11 -04:00
name : "Must provide private key" ,
req : & pbs . CreateCredentialRequest { Item : & pb . Credential {
CredentialStoreId : store . GetPublicId (),
2023-11-02 16:03:47 -04:00
Type : credential . SshPrivateKeySubtype . String (),
2022-07-18 19:16:11 -04:00
Attrs : & pb . Credential_SshPrivateKeyAttributes {
SshPrivateKeyAttributes : & pb . SshPrivateKeyAttributes {
Username : wrapperspb . String ( "username" ),
},
},
}},
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
2022-09-16 15:00:04 -07:00
{
name : "Must provide json secret" ,
req : & pbs . CreateCredentialRequest { Item : & pb . Credential {
CredentialStoreId : store . GetPublicId (),
2023-11-02 16:03:47 -04:00
Type : credential . JsonSubtype . String (),
2022-09-16 15:00:04 -07:00
Attrs : & pb . Credential_JsonAttributes {
JsonAttributes : & pb . JsonAttributes {},
},
}},
res : nil ,
err : handlers . ApiErrorWithCode ( codes . InvalidArgument ),
},
2022-07-18 19:16:11 -04:00
{
name : "valid-up" ,
2022-05-18 20:20:45 -07:00
req : & pbs . CreateCredentialRequest { Item : & pb . Credential {
CredentialStoreId : store . GetPublicId (),
2023-11-02 16:03:47 -04:00
Type : credential . UsernamePasswordSubtype . String (),
2022-05-18 20:20:45 -07:00
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Username : wrapperspb . String ( "username" ),
Password : wrapperspb . String ( "password" ),
},
},
}},
2023-03-10 15:06:39 -05:00
idPrefix : globals . UsernamePasswordCredentialPrefix + "_" ,
2022-05-18 20:20:45 -07:00
res : & pbs . CreateCredentialResponse {
2023-03-10 15:06:39 -05:00
Uri : fmt . Sprintf ( "credentials/%s_" , globals . UsernamePasswordCredentialPrefix ),
2022-05-18 20:20:45 -07:00
Item : & pb . Credential {
Id : store . GetPublicId (),
CredentialStoreId : store . GetPublicId (),
CreatedTime : store . GetCreateTime (). GetTimestamp (),
UpdatedTime : store . GetUpdateTime (). GetTimestamp (),
Scope : & scopepb . ScopeInfo { Id : prj . GetPublicId (), Type : prj . GetType (), ParentScopeId : prj . GetParentId ()},
Version : 1 ,
2023-11-02 16:03:47 -04:00
Type : credential . UsernamePasswordSubtype . String (),
2022-07-18 19:16:11 -04:00
AuthorizedActions : testAuthorizedActions ,
},
},
},
{
name : "valid-spk" ,
req : & pbs . CreateCredentialRequest { Item : & pb . Credential {
CredentialStoreId : store . GetPublicId (),
2023-11-02 16:03:47 -04:00
Type : credential . SshPrivateKeySubtype . String (),
2022-07-18 19:16:11 -04:00
Attrs : & pb . Credential_SshPrivateKeyAttributes {
SshPrivateKeyAttributes : & pb . SshPrivateKeyAttributes {
Username : wrapperspb . String ( "username" ),
PrivateKey : wrapperspb . String ( static . TestSshPrivateKeyPem ),
},
},
}},
2023-03-10 15:06:39 -05:00
idPrefix : globals . SshPrivateKeyCredentialPrefix + "_" ,
2022-07-18 19:16:11 -04:00
res : & pbs . CreateCredentialResponse {
2023-03-10 15:06:39 -05:00
Uri : fmt . Sprintf ( "credentials/%s_" , globals . SshPrivateKeyCredentialPrefix ),
2022-07-18 19:16:11 -04:00
Item : & pb . Credential {
Id : store . GetPublicId (),
CredentialStoreId : store . GetPublicId (),
CreatedTime : store . GetCreateTime (). GetTimestamp (),
UpdatedTime : store . GetUpdateTime (). GetTimestamp (),
Scope : & scopepb . ScopeInfo { Id : prj . GetPublicId (), Type : prj . GetType (), ParentScopeId : prj . GetParentId ()},
Version : 1 ,
2023-11-02 16:03:47 -04:00
Type : credential . SshPrivateKeySubtype . String (),
2022-05-18 20:20:45 -07:00
AuthorizedActions : testAuthorizedActions ,
},
},
},
2022-08-08 15:15:03 -04:00
{
name : "valid-spk-with-passphrase" ,
req : & pbs . CreateCredentialRequest { Item : & pb . Credential {
CredentialStoreId : store . GetPublicId (),
2023-11-02 16:03:47 -04:00
Type : credential . SshPrivateKeySubtype . String (),
2022-08-08 15:15:03 -04:00
Attrs : & pb . Credential_SshPrivateKeyAttributes {
SshPrivateKeyAttributes : & pb . SshPrivateKeyAttributes {
Username : wrapperspb . String ( "username" ),
PrivateKey : wrapperspb . String ( string ( testdata . PEMEncryptedKeys [ 0 ]. PEMBytes )),
PrivateKeyPassphrase : wrapperspb . String ( testdata . PEMEncryptedKeys [ 0 ]. EncryptionKey ),
},
},
}},
2023-03-10 15:06:39 -05:00
idPrefix : globals . SshPrivateKeyCredentialPrefix + "_" ,
2022-08-08 15:15:03 -04:00
res : & pbs . CreateCredentialResponse {
2023-03-10 15:06:39 -05:00
Uri : fmt . Sprintf ( "credentials/%s_" , globals . SshPrivateKeyCredentialPrefix ),
2022-08-08 15:15:03 -04:00
Item : & pb . Credential {
Id : store . GetPublicId (),
CredentialStoreId : store . GetPublicId (),
CreatedTime : store . GetCreateTime (). GetTimestamp (),
UpdatedTime : store . GetUpdateTime (). GetTimestamp (),
Scope : & scopepb . ScopeInfo { Id : prj . GetPublicId (), Type : prj . GetType (), ParentScopeId : prj . GetParentId ()},
Version : 1 ,
2023-11-02 16:03:47 -04:00
Type : credential . SshPrivateKeySubtype . String (),
2022-08-08 15:15:03 -04:00
AuthorizedActions : testAuthorizedActions ,
},
},
},
2022-09-16 15:00:04 -07:00
{
name : "valid-json" ,
req : & pbs . CreateCredentialRequest { Item : & pb . Credential {
CredentialStoreId : store . GetPublicId (),
2023-11-02 16:03:47 -04:00
Type : credential . JsonSubtype . String (),
2022-09-16 15:00:04 -07:00
Attrs : & pb . Credential_JsonAttributes {
JsonAttributes : & pb . JsonAttributes {
2023-09-19 09:17:42 -07:00
Object : obj . Struct ,
2022-09-16 15:00:04 -07:00
},
},
}},
2023-03-10 15:06:39 -05:00
idPrefix : globals . JsonCredentialPrefix + "_" ,
2022-09-16 15:00:04 -07:00
res : & pbs . CreateCredentialResponse {
2023-03-10 15:06:39 -05:00
Uri : fmt . Sprintf ( "credentials/%s_" , globals . JsonCredentialPrefix ),
2022-09-16 15:00:04 -07:00
Item : & pb . Credential {
Id : store . GetPublicId (),
CredentialStoreId : store . GetPublicId (),
CreatedTime : store . GetCreateTime (). GetTimestamp (),
UpdatedTime : store . GetUpdateTime (). GetTimestamp (),
Scope : & scopepb . ScopeInfo { Id : prj . GetPublicId (), Type : prj . GetType (), ParentScopeId : prj . GetParentId ()},
Version : 1 ,
2023-11-02 16:03:47 -04:00
Type : credential . JsonSubtype . String (),
2022-09-16 15:00:04 -07:00
AuthorizedActions : testAuthorizedActions ,
},
},
},
2022-05-18 20:20:45 -07:00
}
for _ , tc := range cases {
t . Run ( tc . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2023-11-29 16:29:55 -08:00
s , err := NewService ( ctx , iamRepoFn , repoFn , 1000 )
2022-05-18 20:20:45 -07:00
require . NoError ( err , "Error when getting new credential store service." )
got , gErr := s . CreateCredential ( auth . DisabledAuthTestContext ( iamRepoFn , prj . GetPublicId ()), tc . req )
if tc . wantErr || tc . err != nil {
require . Error ( gErr )
if tc . err != nil {
assert . True ( errors . Is ( gErr , tc . err ), "CreateCredential(...) got error %v, wanted %v" , gErr , tc . err )
}
return
}
require . NoError ( gErr )
if tc . res == nil {
require . Nil ( got )
}
if got != nil {
assert . Contains ( got . GetUri (), tc . res . Uri )
assert . True ( strings . HasPrefix ( got . GetItem (). GetId (), tc . idPrefix ))
gotCreateTime := got . GetItem (). GetCreatedTime ()
gotUpdateTime := got . GetItem (). GetUpdatedTime ()
// Verify it is a credential store created after the test setup's default credential store
assert . True ( gotCreateTime . AsTime (). After ( store . CreateTime . AsTime ()), "New credential should have been created after default credential store. Was created %v, which is after %v" , gotCreateTime , store . CreateTime . AsTime ())
assert . True ( gotUpdateTime . AsTime (). After ( store . CreateTime . AsTime ()), "New credential should have been updated after default credential store. Was updated %v, which is after %v" , gotUpdateTime , store . CreateTime . AsTime ())
// Calculate hmac
2023-06-06 17:09:08 -04:00
databaseWrapper , err := kkms . GetWrapper ( ctx , prj . PublicId , kms . KeyPurposeDatabase )
2022-05-18 20:20:45 -07:00
require . NoError ( err )
2022-07-18 19:16:11 -04:00
switch tc . req . Item . Type {
2023-11-02 16:03:47 -04:00
case credential . UsernamePasswordSubtype . String ():
2022-07-18 19:16:11 -04:00
password := tc . req . GetItem (). GetUsernamePasswordAttributes (). GetPassword (). GetValue ()
2023-06-06 17:09:08 -04:00
hm , err := crypto . HmacSha256 ( ctx , [] byte ( password ), databaseWrapper , [] byte ( store . GetPublicId ()), nil , crypto . WithEd25519 ())
2022-07-18 19:16:11 -04:00
require . NoError ( err )
// Validate attributes equal
assert . Equal ( tc . req . GetItem (). GetUsernamePasswordAttributes (). GetUsername (). GetValue (),
got . GetItem (). GetUsernamePasswordAttributes (). GetUsername (). GetValue ())
assert . Equal ( base64 . RawURLEncoding . EncodeToString ([] byte ( hm )), got . GetItem (). GetUsernamePasswordAttributes (). GetPasswordHmac ())
assert . Empty ( got . GetItem (). GetUsernamePasswordAttributes (). GetPassword ())
2023-11-02 16:03:47 -04:00
case credential . SshPrivateKeySubtype . String ():
2022-08-08 15:15:03 -04:00
pk := tc . req . GetItem (). GetSshPrivateKeyAttributes (). GetPrivateKey (). GetValue ()
2023-06-06 17:09:08 -04:00
hm , err := crypto . HmacSha256 ( ctx , [] byte ( pk ), databaseWrapper , [] byte ( store . GetPublicId ()), nil )
2022-07-18 19:16:11 -04:00
require . NoError ( err )
// Validate attributes equal
assert . Equal ( tc . req . GetItem (). GetSshPrivateKeyAttributes (). GetUsername (). GetValue (),
got . GetItem (). GetSshPrivateKeyAttributes (). GetUsername (). GetValue ())
assert . Equal ( base64 . RawURLEncoding . EncodeToString ([] byte ( hm )), got . GetItem (). GetSshPrivateKeyAttributes (). GetPrivateKeyHmac ())
assert . Empty ( got . GetItem (). GetSshPrivateKeyAttributes (). GetPrivateKey ())
2022-08-08 15:15:03 -04:00
if pass := tc . req . GetItem (). GetSshPrivateKeyAttributes (). GetPrivateKeyPassphrase (). GetValue (); pass != "" {
2023-06-06 17:09:08 -04:00
hm , err := crypto . HmacSha256 ( ctx , [] byte ( pass ), databaseWrapper , [] byte ( store . GetPublicId ()), nil )
2022-08-08 15:15:03 -04:00
require . NoError ( err )
assert . Equal ( base64 . RawURLEncoding . EncodeToString ([] byte ( hm )), got . GetItem (). GetSshPrivateKeyAttributes (). GetPrivateKeyPassphraseHmac ())
assert . Empty ( got . GetItem (). GetSshPrivateKeyAttributes (). GetPrivateKeyPassphrase ())
}
2023-11-02 16:03:47 -04:00
case credential . JsonSubtype . String ():
2023-06-06 17:09:08 -04:00
hm , err := crypto . HmacSha256 ( ctx , objBytes , databaseWrapper , [] byte ( store . GetPublicId ()), nil )
2022-09-16 15:00:04 -07:00
require . NoError ( err )
// Validate attributes equal
assert . Equal ( base64 . RawURLEncoding . EncodeToString ([] byte ( hm )), got . GetItem (). GetJsonAttributes (). GetObjectHmac ())
assert . Empty ( got . GetItem (). GetJsonAttributes (). GetObject ())
2022-07-18 19:16:11 -04:00
default :
require . Fail ( "unknown type" )
}
2022-05-18 20:20:45 -07:00
// Clear attributes for compare below
got . Item . Attrs = nil
// Clear all values which are hard to compare against.
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 (
got ,
tc . res ,
protocmp . Transform (),
protocmp . SortRepeatedFields ( got ),
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
), "CreateCredential(%q) got response %q, wanted %q" , tc . req , got , tc . res )
2022-05-18 20:20:45 -07:00
})
}
}
2022-07-18 19:16:11 -04:00
const TestSecondarySshPrivateKeyPem = `
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDxfwhEAZKrnsbQxOjVA3PFiB3bW3tSpNKx8TdMiCqlzQAAAJDmpbfr5qW3
6wAAAAtzc2gtZWQyNTUxOQAAACDxfwhEAZKrnsbQxOjVA3PFiB3bW3tSpNKx8TdMiCqlzQ
AAAEBvvkQkH06ad2GpX1VVARzu9NkHA6gzamAaQ/hkn5FuZvF/CEQBkquextDE6NUDc8WI
Hdtbe1Kk0rHxN0yIKqXNAAAACWplZmZAYXJjaAECAwQ=
-----END OPENSSH PRIVATE KEY-----
`
2022-05-18 20:20:45 -07:00
func TestUpdate ( t * testing . T ) {
conn , _ := db . TestSetup ( t , "postgres" )
wrapper := db . TestWrapper ( t )
kkms := kms . TestKms ( t , conn , wrapper )
rw := db . New ( conn )
iamRepo := iam . TestRepo ( t , conn , wrapper )
iamRepoFn := func () ( * iam . Repository , error ) {
return iamRepo , nil
}
staticRepoFn := func () ( * static . Repository , error ) {
return static . NewRepository ( context . Background (), rw , rw , kkms )
}
_ , prj := iam . TestScopes ( t , iamRepo )
ctx := auth . DisabledAuthTestContext ( iamRepoFn , prj . GetPublicId ())
2023-11-29 16:29:55 -08:00
s , err := NewService ( ctx , iamRepoFn , staticRepoFn , 1000 )
2022-05-18 20:20:45 -07:00
require . NoError ( t , err )
fieldmask := func ( paths ... string ) * fieldmaskpb . FieldMask {
return & fieldmaskpb . FieldMask { Paths : paths }
}
store := static . TestCredentialStore ( t , conn , wrapper , prj . GetPublicId ())
2022-07-18 19:16:11 -04:00
freshCredUp := func ( user , pass string ) ( * static . UsernamePasswordCredential , func ()) {
2022-05-18 20:20:45 -07:00
t . Helper ()
cred := static . TestUsernamePasswordCredential ( t , conn , wrapper , user , pass , store . GetPublicId (), prj . GetPublicId ())
clean := func () {
_ , err := s . DeleteCredential ( ctx , & pbs . DeleteCredentialRequest { Id : cred . GetPublicId ()})
require . NoError ( t , err )
}
return cred , clean
}
2022-07-18 19:16:11 -04:00
freshCredSpk := func ( user string ) ( * static . SshPrivateKeyCredential , func ()) {
t . Helper ()
cred := static . TestSshPrivateKeyCredential ( t , conn , wrapper , user , static . TestSshPrivateKeyPem , store . GetPublicId (), prj . GetPublicId ())
clean := func () {
_ , err := s . DeleteCredential ( ctx , & pbs . DeleteCredentialRequest { Id : cred . GetPublicId ()})
require . NoError ( t , err )
}
return cred , clean
}
2022-09-16 15:00:04 -07:00
freshCredJson := func () ( * static . JsonCredential , func ()) {
t . Helper ()
2023-10-30 11:03:04 -07:00
obj , _ := static . TestJsonObject ( t )
2022-09-16 15:00:04 -07:00
cred := static . TestJsonCredential ( t , conn , wrapper , store . GetPublicId (), prj . GetPublicId (), obj )
clean := func () {
_ , err := s . DeleteCredential ( ctx , & pbs . DeleteCredentialRequest { Id : cred . GetPublicId ()})
require . NoError ( t , err )
}
return cred , clean
}
secondSecret := & structpb . Struct {
Fields : map [ string ] * structpb . Value {
"username" : structpb . NewStringValue ( "new-user" ),
"password" : structpb . NewStringValue ( "new-password" ),
"hash" : structpb . NewStringValue ( "0123456789" ),
},
}
secondSecretBytes , err := json . Marshal ( secondSecret )
require . NoError ( t , err )
2022-05-18 20:20:45 -07:00
databaseWrapper , err := kkms . GetWrapper ( context . Background (), prj . GetPublicId (), kms . KeyPurposeDatabase )
require . NoError ( t , err )
2022-08-08 15:15:03 -04:00
successFailCases := [] struct {
name string
req * pbs . UpdateCredentialRequest
res func ( cred * pb . Credential ) * pb . Credential
expErrorContains string
2022-05-18 20:20:45 -07:00
}{
{
name : "name" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "name" ),
Item : & pb . Credential {
Name : wrapperspb . String ( "new-name" ),
},
},
res : func ( in * pb . Credential ) * pb . Credential {
out := proto . Clone ( in ).( * pb . Credential )
out . Name = wrapperspb . String ( "new-name" )
return out
},
},
{
name : "description" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "description" ),
Item : & pb . Credential {
Description : wrapperspb . String ( "new-description" ),
},
},
res : func ( in * pb . Credential ) * pb . Credential {
out := proto . Clone ( in ).( * pb . Credential )
out . Description = wrapperspb . String ( "new-description" )
return out
},
},
{
name : "name-and-description" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "name" , "description" ),
Item : & pb . Credential {
Name : wrapperspb . String ( "new-name" ),
Description : wrapperspb . String ( "new-description" ),
},
},
res : func ( in * pb . Credential ) * pb . Credential {
out := proto . Clone ( in ).( * pb . Credential )
out . Name = wrapperspb . String ( "new-name" )
out . Description = wrapperspb . String ( "new-description" )
return out
},
},
{
2022-07-18 19:16:11 -04:00
name : "name-and-description-spk" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "name" , "description" ),
Item : & pb . Credential {
Name : wrapperspb . String ( "new-name" ),
Description : wrapperspb . String ( "new-description" ),
},
},
res : func ( in * pb . Credential ) * pb . Credential {
out := proto . Clone ( in ).( * pb . Credential )
out . Name = wrapperspb . String ( "new-name" )
out . Description = wrapperspb . String ( "new-description" )
return out
},
},
{
name : "update-username-up" ,
2022-05-18 20:20:45 -07:00
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "attributes.username" ),
Item : & pb . Credential {
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Username : wrapperspb . String ( "new-user-name" ),
},
},
},
},
res : func ( in * pb . Credential ) * pb . Credential {
out := proto . Clone ( in ).( * pb . Credential )
out . GetUsernamePasswordAttributes (). Username = wrapperspb . String ( "new-user-name" )
return out
},
},
2022-07-18 19:16:11 -04:00
{
name : "update-username-spk" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "attributes.username" ),
Item : & pb . Credential {
Attrs : & pb . Credential_SshPrivateKeyAttributes {
SshPrivateKeyAttributes : & pb . SshPrivateKeyAttributes {
Username : wrapperspb . String ( "new-user-name" ),
},
},
},
},
res : func ( in * pb . Credential ) * pb . Credential {
out := proto . Clone ( in ).( * pb . Credential )
out . GetSshPrivateKeyAttributes (). Username = wrapperspb . String ( "new-user-name" )
return out
},
},
2022-05-18 20:20:45 -07:00
{
name : "update-password" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "attributes.password" ),
Item : & pb . Credential {
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Password : wrapperspb . String ( "new-password" ),
},
},
},
},
res : func ( in * pb . Credential ) * pb . Credential {
out := proto . Clone ( in ).( * pb . Credential )
hm , err := crypto . HmacSha256 ( context . Background (), [] byte ( "new-password" ), databaseWrapper , [] byte ( store . GetPublicId ()), nil , crypto . WithEd25519 ())
require . NoError ( t , err )
out . GetUsernamePasswordAttributes (). PasswordHmac = base64 . RawURLEncoding . EncodeToString ([] byte ( hm ))
return out
},
},
2022-07-18 19:16:11 -04:00
{
name : "update-spk" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "attributes.private_key" ),
Item : & pb . Credential {
Attrs : & pb . Credential_SshPrivateKeyAttributes {
SshPrivateKeyAttributes : & pb . SshPrivateKeyAttributes {
PrivateKey : wrapperspb . String ( TestSecondarySshPrivateKeyPem ),
},
},
},
},
res : func ( in * pb . Credential ) * pb . Credential {
out := proto . Clone ( in ).( * pb . Credential )
hm , err := crypto . HmacSha256 ( context . Background (), [] byte ( TestSecondarySshPrivateKeyPem ), databaseWrapper , [] byte ( store . GetPublicId ()), nil )
require . NoError ( t , err )
out . GetSshPrivateKeyAttributes (). PrivateKeyHmac = base64 . RawURLEncoding . EncodeToString ([] byte ( hm ))
return out
},
},
2022-05-18 20:20:45 -07:00
{
name : "update-username-and-password" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "attributes.username" , "attributes.password" ),
Item : & pb . Credential {
Attrs : & pb . Credential_UsernamePasswordAttributes {
UsernamePasswordAttributes : & pb . UsernamePasswordAttributes {
Username : wrapperspb . String ( "new-username" ),
Password : wrapperspb . String ( "new-password" ),
},
},
},
},
res : func ( in * pb . Credential ) * pb . Credential {
out := proto . Clone ( in ).( * pb . Credential )
out . GetUsernamePasswordAttributes (). Username = wrapperspb . String ( "new-username" )
hm , err := crypto . HmacSha256 ( context . Background (), [] byte ( "new-password" ), databaseWrapper , [] byte ( store . GetPublicId ()), nil , crypto . WithEd25519 ())
require . NoError ( t , err )
out . GetUsernamePasswordAttributes (). PasswordHmac = base64 . RawURLEncoding . EncodeToString ([] byte ( hm ))
return out
},
},
2022-07-18 19:16:11 -04:00
{
name : "update-username-and-spk" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "attributes.username" , "attributes.private_key" ),
Item : & pb . Credential {
Attrs : & pb . Credential_SshPrivateKeyAttributes {
SshPrivateKeyAttributes : & pb . SshPrivateKeyAttributes {
Username : wrapperspb . String ( "new-username" ),
PrivateKey : wrapperspb . String ( TestSecondarySshPrivateKeyPem ),
},
},
},
},
res : func ( in * pb . Credential ) * pb . Credential {
out := proto . Clone ( in ).( * pb . Credential )
out . GetSshPrivateKeyAttributes (). Username = wrapperspb . String ( "new-username" )
hm , err := crypto . HmacSha256 ( context . Background (), [] byte ( TestSecondarySshPrivateKeyPem ), databaseWrapper , [] byte ( store . GetPublicId ()), nil )
require . NoError ( t , err )
out . GetSshPrivateKeyAttributes (). PrivateKeyHmac = base64 . RawURLEncoding . EncodeToString ([] byte ( hm ))
return out
},
},
2022-08-08 15:15:03 -04:00
{
name : "update-spk-with-passphrase" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "attributes.private_key" , "attributes.private_key_passphrase" ),
Item : & pb . Credential {
Attrs : & pb . Credential_SshPrivateKeyAttributes {
SshPrivateKeyAttributes : & pb . SshPrivateKeyAttributes {
PrivateKey : wrapperspb . String ( string ( testdata . PEMEncryptedKeys [ 0 ]. PEMBytes )),
PrivateKeyPassphrase : wrapperspb . String ( testdata . PEMEncryptedKeys [ 0 ]. EncryptionKey ),
},
},
},
},
res : func ( in * pb . Credential ) * pb . Credential {
out := proto . Clone ( in ).( * pb . Credential )
hm , err := crypto . HmacSha256 ( context . Background (), testdata . PEMEncryptedKeys [ 0 ]. PEMBytes , databaseWrapper , [] byte ( store . GetPublicId ()), nil )
require . NoError ( t , err )
out . GetSshPrivateKeyAttributes (). PrivateKeyHmac = base64 . RawURLEncoding . EncodeToString ([] byte ( hm ))
hm , err = crypto . HmacSha256 ( context . Background (), [] byte ( testdata . PEMEncryptedKeys [ 0 ]. EncryptionKey ), databaseWrapper , [] byte ( store . GetPublicId ()), nil )
require . NoError ( t , err )
out . GetSshPrivateKeyAttributes (). PrivateKeyPassphraseHmac = base64 . RawURLEncoding . EncodeToString ([] byte ( hm ))
return out
},
},
2022-09-16 15:00:04 -07:00
{
name : "update-json" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "attributes.object.username" , "attributes.object.password" , "attributes.object.hash" ),
Item : & pb . Credential {
Attrs : & pb . Credential_JsonAttributes {
JsonAttributes : & pb . JsonAttributes {
Object : secondSecret ,
},
},
},
},
res : func ( in * pb . Credential ) * pb . Credential {
out := proto . Clone ( in ).( * pb . Credential )
hm , err := crypto . HmacSha256 ( context . Background (), secondSecretBytes , databaseWrapper , [] byte ( store . GetPublicId ()), nil )
require . NoError ( t , err )
out . GetJsonAttributes (). ObjectHmac = base64 . RawURLEncoding . EncodeToString ([] byte ( hm ))
return out
},
},
2023-02-06 08:20:12 -08:00
{
name : "update-empty-object-json" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "attributes.object.password" ),
Item : & pb . Credential {
Attrs : & pb . Credential_JsonAttributes {
JsonAttributes : & pb . JsonAttributes {
Object : & structpb . Struct {},
},
},
},
},
expErrorContains : "This is a required field and cannot be set to empty" ,
},
{
2023-04-19 10:17:07 -07:00
name : "update-nil-object-json" ,
2023-02-06 08:20:12 -08:00
req : & pbs . UpdateCredentialRequest {
2023-04-19 10:17:07 -07:00
UpdateMask : fieldmask ( "attributes.object.password" ),
2023-02-06 08:20:12 -08:00
Item : & pb . Credential {
Attrs : & pb . Credential_JsonAttributes {
2023-04-19 10:17:07 -07:00
JsonAttributes : & pb . JsonAttributes {},
2023-02-06 08:20:12 -08:00
},
},
},
expErrorContains : "This is a required field and cannot be set to empty" ,
},
2022-08-08 15:15:03 -04:00
{
name : "update-spk-with-bad-passphrase" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "attributes.private_key" , "attributes.private_key_passphrase" ),
Item : & pb . Credential {
Attrs : & pb . Credential_SshPrivateKeyAttributes {
SshPrivateKeyAttributes : & pb . SshPrivateKeyAttributes {
PrivateKey : wrapperspb . String ( string ( testdata . PEMEncryptedKeys [ 0 ]. PEMBytes )),
PrivateKeyPassphrase : wrapperspb . String ( strings . ToLower ( testdata . PEMEncryptedKeys [ 0 ]. EncryptionKey )),
},
},
},
},
expErrorContains : "Incorrect private key passphrase" ,
},
{
name : "update-non-passphrase-spk-with-passphrase" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "attributes.private_key" ),
Item : & pb . Credential {
Attrs : & pb . Credential_SshPrivateKeyAttributes {
SshPrivateKeyAttributes : & pb . SshPrivateKeyAttributes {
PrivateKey : wrapperspb . String ( static . TestSshPrivateKeyPem ),
PrivateKeyPassphrase : wrapperspb . String ( testdata . PEMEncryptedKeys [ 0 ]. EncryptionKey ),
},
},
},
},
expErrorContains : "Passphrase supplied for unencrypted key" ,
},
{
name : "update-non-passphrase-spk-with-no-passphrase" ,
req : & pbs . UpdateCredentialRequest {
UpdateMask : fieldmask ( "attributes.private_key" ),
Item : & pb . Credential {
Attrs : & pb . Credential_SshPrivateKeyAttributes {
SshPrivateKeyAttributes : & pb . SshPrivateKeyAttributes {
PrivateKey : wrapperspb . String ( static . TestSshPrivateKeyPem ),
},
},
},
},
res : func ( in * pb . Credential ) * pb . Credential {
out := proto . Clone ( in ).( * pb . Credential )
hm , err := crypto . HmacSha256 ( context . Background (), [] byte ( static . TestSshPrivateKeyPem ), databaseWrapper , [] byte ( store . GetPublicId ()), nil )
require . NoError ( t , err )
out . GetSshPrivateKeyAttributes (). PrivateKeyHmac = base64 . RawURLEncoding . EncodeToString ([] byte ( hm ))
out . GetSshPrivateKeyAttributes (). PrivateKeyPassphraseHmac = ""
2022-09-16 15:00:04 -07:00
2022-08-08 15:15:03 -04:00
return out
},
},
2022-05-18 20:20:45 -07:00
}
2022-08-08 15:15:03 -04:00
for _ , tc := range successFailCases {
2022-05-18 20:20:45 -07:00
t . Run ( tc . name , func ( t * testing . T ) {
assert , require := assert . New ( t ), require . New ( t )
2022-07-18 19:16:11 -04:00
var cred credential . Static
var cleanup func ()
2022-09-16 15:00:04 -07:00
if strings . Contains ( tc . name , "spk" ) {
2022-07-18 19:16:11 -04:00
cred , cleanup = freshCredSpk ( "user" )
2022-09-16 15:00:04 -07:00
} else if strings . Contains ( tc . name , "json" ) {
cred , cleanup = freshCredJson ()
} else {
2022-07-18 19:16:11 -04:00
cred , cleanup = freshCredUp ( "user" , "pass" )
}
2022-05-18 20:20:45 -07:00
defer cleanup ()
if tc . req . Item . GetVersion () == 0 {
tc . req . Item . Version = 1
}
if tc . req . GetId () == "" {
tc . req . Id = cred . GetPublicId ()
}
resToChange , err := s . GetCredential ( ctx , & pbs . GetCredentialRequest { Id : cred . GetPublicId ()})
require . NoError ( err )
got , gErr := s . UpdateCredential ( ctx , tc . req )
2022-08-08 15:15:03 -04:00
if tc . expErrorContains != "" {
require . NotNil ( gErr )
assert . Contains ( gErr . Error (), tc . expErrorContains )
return
}
2022-05-18 20:20:45 -07:00
require . NoError ( gErr )
require . NotNil ( got )
2022-08-08 15:15:03 -04:00
want := & pbs . UpdateCredentialResponse { Item : tc . res ( resToChange . GetItem ())}
2022-05-18 20:20:45 -07:00
gotUpdateTime := got . GetItem (). GetUpdatedTime ()
created := cred . GetCreateTime (). GetTimestamp ()
assert . True ( gotUpdateTime . AsTime (). After ( created . AsTime ()), "Should have been updated after it's creation. Was updated %v, which is after %v" , gotUpdateTime , created )
want . Item . UpdatedTime = got . Item . UpdatedTime
assert . EqualValues ( 2 , got . Item . Version )
want . Item . Version = 2
2023-10-31 13:44:17 +00:00
assert . Empty ( cmp . Diff (
got ,
want ,
protocmp . Transform (),
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
))
2022-05-18 20:20:45 -07:00
})
}
2022-07-28 11:58:45 -07:00
// cant update read only fields
2022-09-16 15:00:04 -07:00
credJson , cleanupJson := freshCredJson ()
defer cleanupJson ()
// cant update read only fields
credUp , cleanUp := freshCredUp ( "user" , "pass" )
defer cleanUp ()
2022-07-28 11:58:45 -07:00
newStore := static . TestCredentialStore ( t , conn , wrapper , prj . GetPublicId ())
roCases := [] struct {
path string
item * pb . Credential
matcher func ( t * testing . T , e error ) // When not set defaults to checking against InvalidArgument Error
}{
{
path : "type" ,
item : & pb . Credential { Type : "something" },
},
{
path : "store_id" ,
item : & pb . Credential { CredentialStoreId : newStore . GetPublicId ()},
},
{
path : "updated_time" ,
item : & pb . Credential { UpdatedTime : timestamppb . Now ()},
},
{
path : "created_time" ,
item : & pb . Credential { UpdatedTime : timestamppb . Now ()},
},
{
path : "authorized_actions" ,
item : & pb . Credential { AuthorizedActions : append ( testAuthorizedActions , "another" )},
},
}
for _ , tc := range roCases {
t . Run ( fmt . Sprintf ( "ReadOnlyField/%s" , tc . path ), func ( t * testing . T ) {
req := & pbs . UpdateCredentialRequest {
2022-09-16 15:00:04 -07:00
Id : credUp . GetPublicId (),
2022-07-28 11:58:45 -07:00
Item : tc . item ,
UpdateMask : & fieldmaskpb . FieldMask { Paths : [] string { tc . path }},
}
2022-09-16 15:00:04 -07:00
req . Item . Version = credUp . Version
2022-07-28 11:58:45 -07:00
got , gErr := s . UpdateCredential ( ctx , req )
assert . Error ( t , gErr )
matcher := tc . matcher
if matcher == nil {
matcher = func ( t * testing . T , e error ) {
assert . Truef ( t , errors . Is ( gErr , handlers . ApiErrorWithCode ( codes . InvalidArgument )), "got error %v, wanted invalid argument" , gErr )
2022-07-18 19:16:11 -04:00
}
2022-07-28 11:58:45 -07:00
}
matcher ( t , gErr )
assert . Nil ( t , got )
2022-09-16 15:00:04 -07:00
req = & pbs . UpdateCredentialRequest {
Id : credJson . GetPublicId (),
Item : tc . item ,
UpdateMask : & fieldmaskpb . FieldMask { Paths : [] string { tc . path }},
}
req . Item . Version = credJson . Version
got , gErr = s . UpdateCredential ( ctx , req )
assert . Error ( t , gErr )
matcher = tc . matcher
if matcher == nil {
matcher = func ( t * testing . T , e error ) {
assert . Truef ( t , errors . Is ( gErr , handlers . ApiErrorWithCode ( codes . InvalidArgument )), "got error %v, wanted invalid argument" , gErr )
}
}
matcher ( t , gErr )
assert . Nil ( t , got )
2022-07-28 11:58:45 -07:00
})
}
2022-05-18 20:20:45 -07:00
}
2023-11-29 16:29:55 -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
})
assert , require := assert . New ( t ), require . New ( t )
ctx := context . Background ()
conn , _ := db . TestSetup ( t , "postgres" )
sqlDB , err := conn . SqlDB ( ctx )
require . NoError ( err )
wrapper := db . TestWrapper ( t )
kmsRepo := kms . TestKms ( t , conn , wrapper )
rw := db . New ( conn )
iamRepo := iam . TestRepo ( t , conn , wrapper )
iamRepoFn := func () ( * iam . Repository , error ) {
return iamRepo , nil
}
staticRepoFn := func () ( * static . Repository , error ) {
return static . NewRepository ( ctx , rw , rw , kmsRepo )
}
tokenRepoFn := func () ( * authtoken . Repository , error ) {
return authtoken . NewRepository ( ctx , rw , rw , kmsRepo )
}
serversRepoFn := func () ( * server . Repository , error ) {
return server . NewRepository ( ctx , rw , rw , kmsRepo )
}
repo , err := staticRepoFn ()
require . NoError ( err )
tokenRepo , err := tokenRepoFn ()
require . NoError ( err )
_ , prjNoStores := iam . TestScopes ( t , iamRepo )
o , prj := iam . TestScopes ( t , iamRepo )
credStore := static . TestCredentialStore ( t , conn , wrapper , prj . GetPublicId ())
emptyStore := static . TestCredentialStore ( t , conn , wrapper , prj . GetPublicId ())
databaseWrapper , err := kmsRepo . GetWrapper ( ctx , prj . GetPublicId (), kms . KeyPurposeDatabase )
require . NoError ( err )
var allCredentials [] * pb . Credential
testObj , testObjBytes := static . TestJsonObject ( t )
for _ , l := range static . TestJsonCredentials ( t , conn , wrapper , credStore . GetPublicId (), prj . PublicId , testObj , 5 ) {
hm , err := crypto . HmacSha256 ( ctx , [] byte ( testObjBytes ), databaseWrapper , [] byte ( credStore . GetPublicId ()), nil )
require . NoError ( err )
allCredentials = append ( allCredentials , staticJsonCredentialToProto ( l , prj , hm ))
}
for _ , l := range static . TestSshPrivateKeyCredentials ( t , conn , wrapper , "username" , static . TestSshPrivateKeyPem , credStore . GetPublicId (), prj . PublicId , 5 ) {
hm , err := crypto . HmacSha256 ( ctx , [] byte ( static . TestSshPrivateKeyPem ), databaseWrapper , [] byte ( credStore . GetPublicId ()), nil )
require . NoError ( err )
allCredentials = append ( allCredentials , staticSshCredentialToProto ( l , prj , hm ))
}
for _ , l := range static . TestUsernamePasswordCredentials ( t , conn , wrapper , "username" , "password" , credStore . GetPublicId (), prj . PublicId , 5 ) {
hm , err := crypto . HmacSha256 ( ctx , [] byte ( "password" ), databaseWrapper , [] byte ( credStore . GetPublicId ()), nil , crypto . WithEd25519 ())
require . NoError ( err )
allCredentials = append ( allCredentials , staticPasswordCredentialToProto ( l , prj , hm ))
}
// Reverse slices since response is ordered by created_time descending (newest first)
slices . Reverse ( allCredentials )
// Run analyze to update postgres meta tables
_ , err = sqlDB . ExecContext ( ctx , "analyze" )
require . NoError ( err )
authMethod := password . TestAuthMethods ( t , conn , o . GetPublicId (), 1 )[ 0 ]
// auth account is only used to join auth method to user.
// We don't do anything else with the auth account in the test setup.
acct := password . TestAccount ( t , conn , authMethod . GetPublicId (), "test_user" )
u := iam . TestUser ( t , iamRepo , o . GetPublicId (), iam . WithAccountIds ( acct . PublicId ))
role1 := iam . TestRole ( t , conn , prj . GetPublicId ())
2024-01-17 23:54:49 -08:00
iam . TestRoleGrant ( t , conn , role1 . GetPublicId (), "ids=*;type=*;actions=*" )
2023-11-29 16:29:55 -08:00
iam . TestUserRole ( t , conn , role1 . GetPublicId (), u . GetPublicId ())
role2 := iam . TestRole ( t , conn , prjNoStores . GetPublicId ())
2024-01-17 23:54:49 -08:00
iam . TestRoleGrant ( t , conn , role2 . GetPublicId (), "ids=*;type=*;actions=*" )
2023-11-29 16:29:55 -08:00
iam . TestUserRole ( t , conn , role2 . GetPublicId (), u . GetPublicId ())
at , err := tokenRepo . CreateAuthToken ( ctx , u , acct . GetPublicId ())
require . NoError ( err )
// Test without anon user
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 , kmsRepo , & requestInfo )
s , err := NewService ( ctx , iamRepoFn , staticRepoFn , 1000 )
require . NoError ( err )
// Start paginating, recursively
req := & pbs . ListCredentialsRequest {
CredentialStoreId : credStore . PublicId ,
Filter : "" ,
ListToken : "" ,
PageSize : 2 ,
}
got , err := s . ListCredentials ( ctx , req )
require . NoError ( err )
require . Len ( got . GetItems (), 2 )
2023-12-13 19:49:51 -08:00
// Compare without comparing the list token
2023-11-29 16:29:55 -08:00
assert . Empty (
cmp . Diff (
got ,
& pbs . ListCredentialsResponse {
Items : allCredentials [ 0 : 2 ],
ResponseType : "delta" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 15 ,
},
2023-12-13 13:59:34 -08:00
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
2023-11-29 16:29:55 -08:00
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListCredentialsResponse {}, "list_token" ),
),
)
// Request second page
req . ListToken = got . ListToken
got , err = s . ListCredentials ( ctx , req )
require . NoError ( err )
require . Len ( got . GetItems (), 2 )
2023-12-13 19:49:51 -08:00
// Compare without comparing the list token
2023-11-29 16:29:55 -08:00
assert . Empty (
cmp . Diff (
got ,
& pbs . ListCredentialsResponse {
Items : allCredentials [ 2 : 4 ],
ResponseType : "delta" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 15 ,
},
2023-12-13 13:59:34 -08:00
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
2023-11-29 16:29:55 -08:00
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListCredentialsResponse {}, "list_token" ),
),
)
// Request rest of results
req . ListToken = got . ListToken
req . PageSize = 15
got , err = s . ListCredentials ( ctx , req )
require . NoError ( err )
require . Len ( got . GetItems (), 11 )
2023-12-13 19:49:51 -08:00
// Compare without comparing the list token
2023-11-29 16:29:55 -08:00
assert . Empty (
cmp . Diff (
got ,
& pbs . ListCredentialsResponse {
Items : allCredentials [ 4 :],
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 15 ,
},
2023-12-13 13:59:34 -08:00
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
2023-11-29 16:29:55 -08:00
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListCredentialsResponse {}, "list_token" ),
),
)
// Create another credential
newCred := static . TestJsonCredential ( t , conn , wrapper , credStore . GetPublicId (), prj . GetPublicId (), testObj )
hm , err := crypto . HmacSha256 ( ctx , [] byte ( testObjBytes ), databaseWrapper , [] byte ( credStore . GetPublicId ()), nil )
require . NoError ( err )
pbNewCred := staticJsonCredentialToProto ( newCred , prj , hm )
// Add to the front since it's most recently updated
allCredentials = append ([] * pb . Credential { pbNewCred }, allCredentials ... )
// Delete one of the other credentials
_ , err = repo . DeleteCredential ( ctx , prj . GetPublicId (), allCredentials [ len ( allCredentials ) - 1 ]. GetId ())
require . NoError ( err )
deletedCred := allCredentials [ len ( allCredentials ) - 1 ]
allCredentials = allCredentials [: len ( allCredentials ) - 1 ]
// Update one of the other credentials
allCredentials [ 1 ]. Name = wrapperspb . String ( "new-name" )
allCredentials [ 1 ]. Version = 2
updatedCredential := & static . UsernamePasswordCredential {
UsernamePasswordCredential : & store . UsernamePasswordCredential {
PublicId : allCredentials [ 1 ]. GetId (),
Name : allCredentials [ 1 ]. GetName (). GetValue (),
StoreId : allCredentials [ 1 ]. GetCredentialStoreId (),
},
}
cred , _ , err := repo . UpdateUsernamePasswordCredential ( ctx , prj . GetPublicId (), updatedCredential , 1 , [] string { "name" })
require . NoError ( err )
allCredentials [ 1 ]. UpdatedTime = cred . UpdateTime . GetTimestamp ()
allCredentials [ 1 ]. Version = cred . GetVersion ()
// Add to the front since it's most recently updated
allCredentials = append (
[] * pb . Credential { allCredentials [ 1 ]},
append (
[] * pb . Credential { allCredentials [ 0 ]},
allCredentials [ 2 :] ... ,
) ... ,
)
// Run analyze to update postgres meta tables
_ , err = sqlDB . ExecContext ( ctx , "analyze" )
require . NoError ( err )
// Request updated results
req . ListToken = got . ListToken
req . PageSize = 1
got , err = s . ListCredentials ( ctx , req )
require . NoError ( err )
assert . Len ( got . GetItems (), 1 )
2023-12-13 19:49:51 -08:00
// Compare without comparing the list token
2023-11-29 16:29:55 -08:00
assert . Empty (
cmp . Diff (
got ,
& pbs . ListCredentialsResponse {
Items : [] * pb . Credential { allCredentials [ 0 ]},
ResponseType : "delta" ,
ListToken : "" ,
SortBy : "updated_time" ,
SortDir : "desc" ,
// Should contain the deleted credential
RemovedIds : [] string { deletedCred . Id },
EstItemCount : 15 ,
},
2023-12-13 13:59:34 -08:00
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
2023-11-29 16:29:55 -08:00
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListCredentialsResponse {}, "list_token" ),
),
)
// Get next page
req . ListToken = got . ListToken
got , err = s . ListCredentials ( ctx , req )
require . NoError ( err )
require . Len ( got . GetItems (), 1 )
// Compare without comparing the list token
assert . Empty (
cmp . Diff (
got ,
& pbs . ListCredentialsResponse {
Items : [] * pb . Credential { allCredentials [ 1 ]},
ResponseType : "complete" ,
SortBy : "updated_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 15 ,
},
2023-12-13 13:59:34 -08:00
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
2023-11-29 16:29:55 -08:00
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListCredentialsResponse {}, "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` , allCredentials [ len ( allCredentials ) - 2 ]. Id , allCredentials [ len ( allCredentials ) - 1 ]. Id )
got , err = s . ListCredentials ( ctx , req )
require . NoError ( err )
require . Len ( got . GetItems (), 1 )
assert . Empty (
cmp . Diff (
got ,
& pbs . ListCredentialsResponse {
Items : [] * pb . Credential { allCredentials [ len ( allCredentials ) - 2 ]},
ResponseType : "delta" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
// Should be empty again
RemovedIds : nil ,
EstItemCount : 15 ,
},
2023-12-13 13:59:34 -08:00
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
2023-11-29 16:29:55 -08:00
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListCredentialsResponse {}, "list_token" ),
),
)
req . ListToken = got . ListToken
// Get the second page
got , err = s . ListCredentials ( ctx , req )
require . NoError ( err )
require . Len ( got . GetItems (), 1 )
assert . Empty (
cmp . Diff (
got ,
& pbs . ListCredentialsResponse {
Items : [] * pb . Credential { allCredentials [ len ( allCredentials ) - 1 ]},
ResponseType : "complete" ,
ListToken : "" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
EstItemCount : 15 ,
},
2023-12-13 13:59:34 -08:00
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
2023-11-29 16:29:55 -08:00
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListCredentialsResponse {}, "list_token" ),
),
)
req . ListToken = got . ListToken
// List items in the empty store
req = & pbs . ListCredentialsRequest {
CredentialStoreId : emptyStore . PublicId ,
Filter : "" ,
ListToken : "" ,
PageSize : 2 ,
}
got , err = s . ListCredentials ( ctx , req )
require . NoError ( err )
require . Len ( got . GetItems (), 0 )
2023-12-13 19:49:51 -08:00
// Compare without comparing the list token
2023-11-29 16:29:55 -08:00
assert . Empty (
cmp . Diff (
got ,
& pbs . ListCredentialsResponse {
Items : nil ,
ResponseType : "complete" ,
SortBy : "created_time" ,
SortDir : "desc" ,
RemovedIds : nil ,
},
2023-12-13 13:59:34 -08:00
cmpopts . SortSlices ( func ( a , b string ) bool {
return a < b
}),
2023-11-29 16:29:55 -08:00
protocmp . Transform (),
protocmp . IgnoreFields ( & pbs . ListCredentialsResponse {}, "list_token" ),
),
)
2024-02-15 09:26:29 -08:00
// Create unauthenticated user
unauthAt := authtoken . TestAuthToken ( t , conn , kmsRepo , o . GetPublicId ())
unauthR := iam . TestRole ( t , conn , prj . GetPublicId ())
_ = iam . TestUserRole ( t , conn , unauthR . GetPublicId (), unauthAt . GetIamUserId ())
// Make a request with the unauthenticated user,
// ensure the response is 403 forbidden.
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 , kmsRepo , & requestInfo )
_ , err = s . ListCredentials ( ctx , & pbs . ListCredentialsRequest {
CredentialStoreId : credStore . PublicId ,
})
require . Error ( err )
assert . Equal ( handlers . ForbiddenError (), err )
2023-11-29 16:29:55 -08:00
}