Do not allow PKI-KMS workers to have names updated via API (#3143)
Do not allow PKI-KMS workers to have names updated via API This would cause their public IDs to change, which we do not want. Descriptions are also disallowed, since those come from the config file. This also adds logic to allow upgrading from old KMS method to new, with a test. Co-authored-by: Irena Rindos <irenarindos@users.noreply.github.com>
This commit is contained in:
1 parent
b4a8032649
commit
bdf7bf7702
13 files changed
+421
-141
No files matched your search
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -334,8 +335,25 @@ func (s Service) UpdateWorker(ctx context.Context, req *pbs.UpdateWorkerRequest)
|
||||
if authResults.Error != nil {
|
||||
return nil, authResults.Error
|
||||
}
|
||||
|
||||
w, err := s.updateInRepo(ctx, authResults.Scope.GetId(), req.GetId(), req.GetUpdateMask().GetPaths(), req.GetItem())
|
||||
if err != nil {
|
||||
switch {
|
||||
case err == nil:
|
||||
case stderrors.Is(err, server.ErrCannotUpdateKmsWorkerViaApi):
|
||||
// Treat this like a "bad field" error on name even though we couldn't
|
||||
// return it in validation without having to make an additional call and
|
||||
// a lot of additional logic
|
||||
return nil, handlers.ValidateUpdateRequest(req, req.GetItem(), func() map[string]string {
|
||||
badFields := make(map[string]string)
|
||||
if req.GetItem().GetName().GetValue() != "" {
|
||||
badFields[globals.NameField] = "KMS-registered workers cannot have their name updated via the API."
|
||||
}
|
||||
if req.GetItem().GetDescription().GetValue() != "" {
|
||||
badFields[globals.DescriptionField] = "KMS-registered workers cannot have their description updated via the API."
|
||||
}
|
||||
return badFields
|
||||
}, globals.WorkerPrefix)
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -487,32 +487,45 @@ func TestUpdate(t *testing.T) {
|
||||
return connectedDownstreams
|
||||
}
|
||||
|
||||
wkr := server.TestPkiWorker(t, conn, wrapper,
|
||||
pkiWkr := server.TestPkiWorker(t, conn, wrapper,
|
||||
server.WithName("default"),
|
||||
server.WithDescription("default"))
|
||||
|
||||
version := wkr.GetVersion()
|
||||
pkiKmsWkr := server.TestPkiWorker(t, conn, wrapper,
|
||||
server.WithName("default-kms"),
|
||||
server.WithDescription("default-kms"),
|
||||
server.WithNewIdFunc(func(ctx context.Context) (string, error) {
|
||||
return server.NewWorkerIdFromScopeAndName(ctx, scope.Global.String(), "default-kms")
|
||||
}),
|
||||
)
|
||||
|
||||
pkiVersion := pkiWkr.GetVersion()
|
||||
pkiKmsVersion := pkiKmsWkr.GetVersion()
|
||||
|
||||
resetWorker := func() {
|
||||
version++
|
||||
_, _, err = repo.UpdateWorker(context.Background(), wkr, version, []string{"Name", "Description"})
|
||||
require.NoError(t, err, "Failed to reset worker.")
|
||||
version++
|
||||
pkiVersion = pkiWkr.Version + 1
|
||||
pkiKmsVersion = pkiKmsWkr.Version + 1
|
||||
wkr, _, err := repo.UpdateWorker(context.Background(), pkiWkr, pkiVersion, []string{"Name", "Description"})
|
||||
require.NoError(t, err, "Failed to reset pki worker.")
|
||||
pkiVersion = wkr.Version
|
||||
}
|
||||
|
||||
wCreated := wkr.GetCreateTime().GetTimestamp().AsTime()
|
||||
toMerge := &pbs.UpdateWorkerRequest{
|
||||
Id: wkr.GetPublicId(),
|
||||
wCreated := pkiWkr.GetCreateTime().GetTimestamp().AsTime()
|
||||
toMerge := func(wkr *server.Worker) *pbs.UpdateWorkerRequest {
|
||||
return &pbs.UpdateWorkerRequest{
|
||||
Id: wkr.GetPublicId(),
|
||||
}
|
||||
}
|
||||
workerService, err := NewService(ctx, repoFn, iamRepoFn, workerAuthRepoFn, nil)
|
||||
require.NoError(t, err)
|
||||
expectedScope := &scopes.ScopeInfo{Id: scope.Global.String(), Type: scope.Global.String(), Name: scope.Global.String(), Description: "Global Scope"}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
req *pbs.UpdateWorkerRequest
|
||||
res *pbs.UpdateWorkerResponse
|
||||
err error
|
||||
name string
|
||||
reqIdFunc func(*server.Worker) string
|
||||
req *pbs.UpdateWorkerRequest
|
||||
res func(*server.Worker) *pbs.UpdateWorkerResponse
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "Update an Existing Worker",
|
||||
@@ -525,20 +538,22 @@ func TestUpdate(t *testing.T) {
|
||||
Description: wrapperspb.String("desc"),
|
||||
},
|
||||
},
|
||||
res: &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("name"),
|
||||
Description: wrapperspb.String("desc"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
DirectlyConnectedDownstreamWorkers: connectedDownstreams,
|
||||
},
|
||||
res: func(wkr *server.Worker) *pbs.UpdateWorkerResponse {
|
||||
return &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("name"),
|
||||
Description: wrapperspb.String("desc"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
DirectlyConnectedDownstreamWorkers: connectedDownstreams,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -549,23 +564,25 @@ func TestUpdate(t *testing.T) {
|
||||
},
|
||||
Item: &pb.Worker{
|
||||
Name: wrapperspb.String("name"),
|
||||
Description: wrapperspb.String("desc"),
|
||||
Description: wrapperspb.String("default"),
|
||||
},
|
||||
},
|
||||
res: &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("name"),
|
||||
Description: wrapperspb.String("desc"),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
DirectlyConnectedDownstreamWorkers: connectedDownstreams,
|
||||
},
|
||||
res: func(wkr *server.Worker) *pbs.UpdateWorkerResponse {
|
||||
return &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("name"),
|
||||
Description: wrapperspb.String("default"),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
DirectlyConnectedDownstreamWorkers: connectedDownstreams,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -622,19 +639,21 @@ func TestUpdate(t *testing.T) {
|
||||
Description: wrapperspb.String("ignored"),
|
||||
},
|
||||
},
|
||||
res: &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Description: wrapperspb.String("default"),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
DirectlyConnectedDownstreamWorkers: connectedDownstreams,
|
||||
},
|
||||
res: func(wkr *server.Worker) *pbs.UpdateWorkerResponse {
|
||||
return &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Description: wrapperspb.String(wkr.Description),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
DirectlyConnectedDownstreamWorkers: connectedDownstreams,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -647,19 +666,26 @@ func TestUpdate(t *testing.T) {
|
||||
Name: wrapperspb.String("ignored"),
|
||||
},
|
||||
},
|
||||
res: &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("default"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
DirectlyConnectedDownstreamWorkers: connectedDownstreams,
|
||||
},
|
||||
res: func(wkr *server.Worker) *pbs.UpdateWorkerResponse {
|
||||
ret := &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
DirectlyConnectedDownstreamWorkers: connectedDownstreams,
|
||||
},
|
||||
}
|
||||
// In the previous test, the name will now be blank if it's the
|
||||
// non-kms worker so must add it in to expected
|
||||
if wkr.PublicId == pkiKmsWkr.PublicId {
|
||||
ret.Item.Name = wrapperspb.String(wkr.Name)
|
||||
}
|
||||
return ret
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -673,20 +699,26 @@ func TestUpdate(t *testing.T) {
|
||||
Description: wrapperspb.String("ignored"),
|
||||
},
|
||||
},
|
||||
res: &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("updated"),
|
||||
Description: wrapperspb.String("default"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
DirectlyConnectedDownstreamWorkers: connectedDownstreams,
|
||||
},
|
||||
res: func(wkr *server.Worker) *pbs.UpdateWorkerResponse {
|
||||
ret := &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("updated"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
DirectlyConnectedDownstreamWorkers: connectedDownstreams,
|
||||
},
|
||||
}
|
||||
// The name will not be updated if it's the pki-kms worker
|
||||
if wkr.PublicId == pkiKmsWkr.PublicId {
|
||||
ret.Item.Name = wrapperspb.String(wkr.Name)
|
||||
}
|
||||
return ret
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -700,20 +732,27 @@ func TestUpdate(t *testing.T) {
|
||||
Description: wrapperspb.String("notignored"),
|
||||
},
|
||||
},
|
||||
res: &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("default"),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
Description: wrapperspb.String("notignored"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
DirectlyConnectedDownstreamWorkers: connectedDownstreams,
|
||||
},
|
||||
res: func(wkr *server.Worker) *pbs.UpdateWorkerResponse {
|
||||
ret := &pbs.UpdateWorkerResponse{
|
||||
Item: &pb.Worker{
|
||||
Id: wkr.GetPublicId(),
|
||||
ScopeId: wkr.GetScopeId(),
|
||||
Scope: expectedScope,
|
||||
Name: wrapperspb.String("updated"),
|
||||
ActiveConnectionCount: &wrapperspb.UInt32Value{Value: 0},
|
||||
Description: wrapperspb.String("notignored"),
|
||||
CreatedTime: wkr.GetCreateTime().GetTimestamp(),
|
||||
LastStatusTime: wkr.GetLastStatusTime().GetTimestamp(),
|
||||
AuthorizedActions: testAuthorizedActions,
|
||||
Type: PkiWorkerType,
|
||||
DirectlyConnectedDownstreamWorkers: connectedDownstreams,
|
||||
},
|
||||
}
|
||||
// The name will not have been updated previously if it's the pki-kms worker
|
||||
if wkr.PublicId == pkiKmsWkr.PublicId {
|
||||
ret.Item.Name = wrapperspb.String(wkr.Name)
|
||||
}
|
||||
return ret
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -730,9 +769,11 @@ func TestUpdate(t *testing.T) {
|
||||
err: handlers.ApiErrorWithCode(codes.NotFound),
|
||||
},
|
||||
{
|
||||
name: "Cant change Id",
|
||||
name: "Cant change Id pki worker",
|
||||
reqIdFunc: func(worker *server.Worker) string {
|
||||
return worker.GetPublicId()
|
||||
},
|
||||
req: &pbs.UpdateWorkerRequest{
|
||||
Id: wkr.GetPublicId(),
|
||||
UpdateMask: &field_mask.FieldMask{
|
||||
Paths: []string{"id"},
|
||||
},
|
||||
@@ -853,36 +894,77 @@ func TestUpdate(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tc.req.Item.Version = version
|
||||
for _, wkr := range []*server.Worker{pkiWkr, pkiKmsWkr} {
|
||||
t.Run(fmt.Sprintf("%s-%s", tc.name, wkr.Name), func(t *testing.T) {
|
||||
if wkr.PublicId == pkiWkr.PublicId {
|
||||
tc.req.Item.Version = pkiVersion
|
||||
} else {
|
||||
tc.req.Item.Version = pkiKmsVersion
|
||||
}
|
||||
|
||||
req := proto.Clone(toMerge).(*pbs.UpdateWorkerRequest)
|
||||
proto.Merge(req, tc.req)
|
||||
req := proto.Clone(toMerge(wkr)).(*pbs.UpdateWorkerRequest)
|
||||
proto.Merge(req, tc.req)
|
||||
if tc.reqIdFunc != nil {
|
||||
req.Id = tc.reqIdFunc(wkr)
|
||||
}
|
||||
|
||||
got, gErr := workerService.UpdateWorker(auth.DisabledAuthTestContext(iamRepoFn, scope.Global.String()), req)
|
||||
if tc.err != nil {
|
||||
require.Error(t, gErr)
|
||||
assert.True(t, errors.Is(gErr, tc.err), "UpdateWorker(%+v) got error %v, wanted %v", req, gErr, tc.err)
|
||||
}
|
||||
// Search through update masks to see if we're updating name
|
||||
var basicInfoChange bool
|
||||
if tc.req != nil && tc.req.UpdateMask != nil {
|
||||
for _, field := range tc.req.UpdateMask.Paths {
|
||||
if strings.ToLower(field) == "name" || strings.ToLower(field) == "description" {
|
||||
basicInfoChange = true
|
||||
}
|
||||
if strings.Contains(field, ",") {
|
||||
splitStr := strings.Split(field, ",")
|
||||
for _, field := range splitStr {
|
||||
if strings.ToLower(field) == "name" || strings.ToLower(field) == "description" {
|
||||
basicInfoChange = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if tc.err == nil {
|
||||
defer resetWorker()
|
||||
}
|
||||
got, gErr := workerService.UpdateWorker(auth.DisabledAuthTestContext(iamRepoFn, scope.Global.String()), req)
|
||||
// If it's a PKI-KMS worker and we wouldn't otherwise expect an
|
||||
// error but we tried a name change, ensure we see the expected
|
||||
// error here.
|
||||
if wkr.PublicId == pkiKmsWkr.PublicId && basicInfoChange && tc.err == nil {
|
||||
require.Error(t, gErr)
|
||||
assert.Contains(t, gErr.Error(), "KMS-registered workers cannot have their")
|
||||
assert.True(t, errors.Is(gErr, handlers.ApiErrorWithCode(codes.InvalidArgument)))
|
||||
return
|
||||
}
|
||||
if tc.err != nil {
|
||||
require.Error(t, gErr)
|
||||
assert.True(t, errors.Is(gErr, tc.err), "UpdateWorker(%+v) got error %v, wanted %v", req, gErr, tc.err)
|
||||
assert.Nil(t, got)
|
||||
return
|
||||
}
|
||||
|
||||
// Should not have nil got at this point
|
||||
require.NotNil(t, got)
|
||||
|
||||
if tc.err == nil {
|
||||
defer resetWorker()
|
||||
}
|
||||
|
||||
expRes := tc.res(wkr)
|
||||
|
||||
if got != nil {
|
||||
assert.NotNilf(t, tc.res, "Expected UpdateWorker response to be nil, but was %v", got)
|
||||
gotUpdateTime := got.GetItem().GetUpdatedTime().AsTime()
|
||||
// Verify updated set after it was created
|
||||
assert.True(t, gotUpdateTime.After(wCreated), "Updated resource should have been updated after its creation. Was updated %v, which is after %v", gotUpdateTime, wCreated)
|
||||
|
||||
// Clear all values which are hard to compare against.
|
||||
got.Item.UpdatedTime, tc.res.Item.UpdatedTime = nil, nil
|
||||
}
|
||||
if tc.res != nil {
|
||||
tc.res.Item.Version = version + 1
|
||||
}
|
||||
assert.Empty(t, cmp.Diff(got, tc.res, protocmp.Transform()), "UpdateWorker(%q) got response %q, wanted %q", req, got, tc.res)
|
||||
})
|
||||
got.Item.UpdatedTime, expRes.Item.UpdatedTime = nil, nil
|
||||
|
||||
expRes.Item.Version = tc.req.Item.Version + 1
|
||||
|
||||
assert.Empty(t, cmp.Diff(got, expRes, protocmp.Transform()), "UpdateWorker(%q) got response %q, wanted %q", req, got, expRes)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user