mirror of https://github.com/hashicorp/boundary
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
14 KiB
387 lines
14 KiB
package credentials_test
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/boundary/api"
|
|
"github.com/hashicorp/boundary/api/credentials"
|
|
"github.com/hashicorp/boundary/api/credentialstores"
|
|
"github.com/hashicorp/boundary/internal/credential"
|
|
"github.com/hashicorp/boundary/internal/daemon/controller"
|
|
"github.com/hashicorp/boundary/internal/iam"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/crypto/ssh/testdata"
|
|
)
|
|
|
|
func TestList(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
tc := controller.NewTestController(t, nil)
|
|
defer tc.Shutdown()
|
|
|
|
client := tc.Client()
|
|
token := tc.Token()
|
|
client.SetToken(token.Token)
|
|
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId))
|
|
|
|
cs, err := credentialstores.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId())
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
|
|
credClient := credentials.NewClient(client)
|
|
|
|
ul, err := credClient.List(tc.Context(), cs.Item.Id)
|
|
require.NoError(err)
|
|
assert.Empty(ul.Items)
|
|
|
|
cred, err := credClient.Create(tc.Context(), credential.UsernamePasswordSubtype.String(), cs.Item.Id,
|
|
credentials.WithName("0"),
|
|
credentials.WithDescription("description"),
|
|
credentials.WithUsernamePasswordCredentialUsername("user"),
|
|
credentials.WithUsernamePasswordCredentialPassword("pass"))
|
|
require.NoError(err)
|
|
|
|
expected := make([]*credentials.Credential, 10)
|
|
expected[0] = cred.Item
|
|
|
|
ul, err = credClient.List(tc.Context(), cs.Item.Id)
|
|
require.NoError(err)
|
|
assert.ElementsMatch(comparableCatalogSlice(expected[:1]), comparableCatalogSlice(ul.Items))
|
|
|
|
for i := 1; i < 10; i++ {
|
|
cred, err := credClient.Create(tc.Context(), credential.UsernamePasswordSubtype.String(), cs.Item.Id,
|
|
credentials.WithName(fmt.Sprintf("%d", i)),
|
|
credentials.WithDescription("description"),
|
|
credentials.WithUsernamePasswordCredentialUsername("user"),
|
|
credentials.WithUsernamePasswordCredentialPassword("pass"))
|
|
require.NoError(err)
|
|
expected[i] = cred.Item
|
|
}
|
|
ul, err = credClient.List(tc.Context(), cs.Item.Id)
|
|
require.NoError(err)
|
|
assert.ElementsMatch(comparableCatalogSlice(expected), comparableCatalogSlice(ul.Items))
|
|
|
|
filterItem := ul.Items[3]
|
|
ul, err = credClient.List(tc.Context(), cs.Item.Id,
|
|
credentials.WithFilter(fmt.Sprintf(`"/item/id"==%q`, filterItem.Id)))
|
|
require.NoError(err)
|
|
assert.Len(ul.Items, 1)
|
|
assert.Equal(filterItem.Id, ul.Items[0].Id)
|
|
}
|
|
|
|
func comparableCatalogSlice(in []*credentials.Credential) []credentials.Credential {
|
|
var filtered []credentials.Credential
|
|
for _, i := range in {
|
|
p := credentials.Credential{
|
|
Id: i.Id,
|
|
Name: i.Name,
|
|
Description: i.Description,
|
|
CreatedTime: i.CreatedTime,
|
|
UpdatedTime: i.UpdatedTime,
|
|
Attributes: i.Attributes,
|
|
}
|
|
filtered = append(filtered, p)
|
|
}
|
|
return filtered
|
|
}
|
|
|
|
func TestCrud(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
tc := controller.NewTestController(t, nil)
|
|
defer tc.Shutdown()
|
|
|
|
client := tc.Client()
|
|
token := tc.Token()
|
|
client.SetToken(token.Token)
|
|
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId))
|
|
|
|
cs, err := credentialstores.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId())
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
|
|
checkResource := func(step string, c *credentials.Credential, wantedName, wantedUser string, wantVersion uint32) {
|
|
assert.NotNil(c, "returned no resource", step)
|
|
assert.Equal(wantedName, c.Name, step)
|
|
gotUser, ok := c.Attributes["username"]
|
|
require.True(ok)
|
|
assert.Equal(wantedUser, gotUser, step)
|
|
assert.Equal(wantVersion, c.Version)
|
|
}
|
|
credClient := credentials.NewClient(client)
|
|
|
|
cred, err := credClient.Create(tc.Context(), credential.UsernamePasswordSubtype.String(), cs.Item.Id, credentials.WithName("foo"),
|
|
credentials.WithUsernamePasswordCredentialUsername("user"), credentials.WithUsernamePasswordCredentialPassword("pass"))
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("create", cred.Item, "foo", "user", 1)
|
|
|
|
cred, err = credClient.Read(tc.Context(), cred.Item.Id)
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("read", cred.Item, "foo", "user", 1)
|
|
|
|
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.WithName("bar"))
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("update", cred.Item, "bar", "user", 2)
|
|
|
|
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.WithUsernamePasswordCredentialUsername("newuser"))
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("update", cred.Item, "bar", "newuser", 3)
|
|
|
|
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.DefaultName())
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("update", cred.Item, "", "newuser", 4)
|
|
|
|
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version,
|
|
credentials.WithName("newuser"), credentials.WithUsernamePasswordCredentialUsername("neweruser"))
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("update", cred.Item, "newuser", "neweruser", 5)
|
|
|
|
_, err = credClient.Delete(tc.Context(), cred.Item.Id)
|
|
assert.NoError(err)
|
|
|
|
_, err = credClient.Delete(tc.Context(), cred.Item.Id)
|
|
require.Error(err)
|
|
apiErr := api.AsServerError(err)
|
|
assert.NotNil(apiErr)
|
|
assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode())
|
|
}
|
|
|
|
func TestCrudSpk(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
tc := controller.NewTestController(t, nil)
|
|
defer tc.Shutdown()
|
|
|
|
client := tc.Client()
|
|
token := tc.Token()
|
|
client.SetToken(token.Token)
|
|
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId))
|
|
|
|
cs, err := credentialstores.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId())
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
|
|
checkResource := func(step string, c *credentials.Credential, wantedName, wantedUser string, wantVersion uint32) {
|
|
assert.NotNil(c, "returned no resource", step)
|
|
assert.Equal(wantedName, c.Name, step)
|
|
gotUser, ok := c.Attributes["username"]
|
|
require.True(ok)
|
|
assert.Equal(wantedUser, gotUser, step)
|
|
assert.Equal(wantVersion, c.Version)
|
|
}
|
|
credClient := credentials.NewClient(client)
|
|
|
|
spk := string(testdata.PEMBytes["rsa"])
|
|
spkWithPass := string(testdata.PEMEncryptedKeys[0].PEMBytes)
|
|
pass := testdata.PEMEncryptedKeys[0].EncryptionKey
|
|
|
|
cred, err := credClient.Create(tc.Context(), credential.SshPrivateKeySubtype.String(), cs.Item.Id, credentials.WithName("foo"),
|
|
credentials.WithSshPrivateKeyCredentialUsername("user"),
|
|
credentials.WithSshPrivateKeyCredentialPrivateKey(spkWithPass),
|
|
credentials.WithSshPrivateKeyCredentialPrivateKeyPassphrase(pass))
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("create", cred.Item, "foo", "user", 1)
|
|
|
|
// Validate passphrase hmac was set and passpharse is not set
|
|
passHmac, ok := cred.GetItem().Attributes["private_key_passphrase_hmac"].(string)
|
|
require.True(ok)
|
|
require.NotNil(passHmac)
|
|
|
|
cred, err = credClient.Read(tc.Context(), cred.Item.Id)
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("read", cred.Item, "foo", "user", 1)
|
|
|
|
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.WithName("bar"))
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("update", cred.Item, "bar", "user", 2)
|
|
|
|
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.WithSshPrivateKeyCredentialUsername("newuser"))
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("update", cred.Item, "bar", "newuser", 3)
|
|
|
|
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.DefaultName())
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("update", cred.Item, "", "newuser", 4)
|
|
|
|
// Update to non-encrypted key
|
|
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.WithSshPrivateKeyCredentialPrivateKey(spk))
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("update", cred.Item, "", "newuser", 5)
|
|
|
|
// Validate passphrase hmac is no longer set
|
|
_, ok = cred.GetItem().Attributes["private_key_passphrase_hmac"].(string)
|
|
require.False(ok)
|
|
|
|
_, err = credClient.Delete(tc.Context(), cred.Item.Id)
|
|
assert.NoError(err)
|
|
|
|
_, err = credClient.Delete(tc.Context(), cred.Item.Id)
|
|
require.Error(err)
|
|
apiErr := api.AsServerError(err)
|
|
assert.NotNil(apiErr)
|
|
assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode())
|
|
}
|
|
|
|
func TestCrudJson(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
tc := controller.NewTestController(t, nil)
|
|
defer tc.Shutdown()
|
|
|
|
client := tc.Client()
|
|
token := tc.Token()
|
|
client.SetToken(token.Token)
|
|
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId))
|
|
|
|
cs, err := credentialstores.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId())
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
|
|
checkResource := func(step string, c *credentials.Credential, wantedName string, wantVersion uint32) {
|
|
assert.NotNil(c, "returned no resource", step)
|
|
assert.Equal(wantedName, c.Name, step)
|
|
assert.Equal(wantVersion, c.Version)
|
|
}
|
|
credClient := credentials.NewClient(client)
|
|
|
|
obj := map[string]interface{}{
|
|
"username": "admin",
|
|
"password": "pass",
|
|
}
|
|
cred, err := credClient.Create(tc.Context(), credential.JsonSubtype.String(), cs.Item.Id, credentials.WithName("foo"), credentials.WithJsonCredentialObject(obj))
|
|
require.NoError(err)
|
|
require.NotNil(cred)
|
|
checkResource("create", cred.Item, "foo", 1)
|
|
|
|
jsonAttributes, err := cred.GetItem().GetJsonAttributes()
|
|
require.NoError(err)
|
|
require.Nil(jsonAttributes.Object)
|
|
require.NotEmpty(jsonAttributes.ObjectHmac)
|
|
|
|
sshAttributes, err := cred.GetItem().GetSshPrivateKeyAttributes()
|
|
require.Error(err)
|
|
require.Nil(sshAttributes)
|
|
|
|
upAttributes, err := cred.GetItem().GetUsernamePasswordAttributes()
|
|
require.Error(err)
|
|
require.Nil(upAttributes)
|
|
|
|
// Validate object hmac was set and object is not set
|
|
originalObjectHmac, ok := cred.GetItem().Attributes["object_hmac"].(string)
|
|
require.True(ok)
|
|
require.NotNil(originalObjectHmac)
|
|
object, ok := cred.GetItem().Attributes["object"].(string)
|
|
require.False(ok)
|
|
require.Empty(object)
|
|
|
|
cred, err = credClient.Read(tc.Context(), cred.Item.Id)
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("read", cred.Item, "foo", 1)
|
|
|
|
// Validate object hmac was set and object is not set
|
|
objectHmac, ok := cred.GetItem().Attributes["object_hmac"].(string)
|
|
require.True(ok)
|
|
require.NotNil(objectHmac)
|
|
object, ok = cred.GetItem().Attributes["object"].(string)
|
|
require.False(ok)
|
|
require.Empty(object)
|
|
|
|
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.WithName("bar"))
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("update", cred.Item, "bar", 2)
|
|
|
|
cred, err = credClient.Update(tc.Context(), cred.Item.Id, cred.Item.Version, credentials.WithJsonCredentialObject(map[string]interface{}{
|
|
"username": "not_admin",
|
|
"password": "not_password",
|
|
}))
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
checkResource("update", cred.Item, "bar", 3)
|
|
|
|
// Validate secret hmac was set & is not the same as the original value & secret is not set
|
|
objectHmac, ok = cred.GetItem().Attributes["object_hmac"].(string)
|
|
require.True(ok)
|
|
require.NotNil(objectHmac)
|
|
require.NotEqual(originalObjectHmac, objectHmac)
|
|
object, ok = cred.GetItem().Attributes["secrets"].(string)
|
|
require.False(ok)
|
|
require.Empty(object)
|
|
|
|
_, err = credClient.Delete(tc.Context(), cred.Item.Id)
|
|
assert.NoError(err)
|
|
|
|
_, err = credClient.Delete(tc.Context(), cred.Item.Id)
|
|
require.Error(err)
|
|
apiErr := api.AsServerError(err)
|
|
assert.NotNil(apiErr)
|
|
assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode())
|
|
}
|
|
|
|
func TestErrors(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
tc := controller.NewTestController(t, nil)
|
|
defer tc.Shutdown()
|
|
|
|
client := tc.Client()
|
|
token := tc.Token()
|
|
client.SetToken(token.Token)
|
|
_, proj := iam.TestScopes(t, tc.IamRepo(), iam.WithUserId(token.UserId))
|
|
cs, err := credentialstores.NewClient(client).Create(tc.Context(), "static", proj.GetPublicId())
|
|
require.NoError(err)
|
|
require.NotNil(cs)
|
|
|
|
c := credentials.NewClient(client)
|
|
|
|
cred, err := c.Create(tc.Context(), credential.UsernamePasswordSubtype.String(), cs.Item.Id, credentials.WithName("foo"),
|
|
credentials.WithUsernamePasswordCredentialUsername("user"), credentials.WithUsernamePasswordCredentialPassword("pass"))
|
|
require.NoError(err)
|
|
require.NotNil(cred)
|
|
|
|
// A malformed id is processed as the id and not a different path to the api.
|
|
_, err = c.Read(tc.Context(), fmt.Sprintf("%s/../", cred.Item.Id))
|
|
require.Error(err)
|
|
apiErr := api.AsServerError(err)
|
|
require.NotNil(apiErr)
|
|
assert.EqualValues(http.StatusBadRequest, apiErr.Response().StatusCode())
|
|
require.Len(apiErr.Details.RequestFields, 1)
|
|
assert.Equal(apiErr.Details.RequestFields[0].Name, "id")
|
|
|
|
// Updating the wrong version should fail.
|
|
_, err = c.Update(tc.Context(), cred.Item.Id, 73, credentials.WithName("anything"))
|
|
require.Error(err)
|
|
apiErr = api.AsServerError(err)
|
|
assert.NotNil(apiErr)
|
|
assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode())
|
|
|
|
// Same name
|
|
_, err = c.Create(tc.Context(), credential.UsernamePasswordSubtype.String(), proj.GetPublicId(), credentials.WithName("foo"))
|
|
require.Error(err)
|
|
apiErr = api.AsServerError(err)
|
|
assert.NotNil(apiErr)
|
|
|
|
_, err = c.Read(tc.Context(), credential.UsernamePasswordCredentialPrefix+"_doesntexis")
|
|
require.Error(err)
|
|
apiErr = api.AsServerError(err)
|
|
assert.NotNil(apiErr)
|
|
assert.EqualValues(http.StatusNotFound, apiErr.Response().StatusCode())
|
|
|
|
_, err = c.Read(tc.Context(), "invalid id")
|
|
require.Error(err)
|
|
apiErr = api.AsServerError(err)
|
|
assert.NotNil(apiErr)
|
|
assert.EqualValues(http.StatusBadRequest, apiErr.Response().StatusCode())
|
|
}
|