diff --git a/cmd/spire-server/cli/authoritycommon/authoritycommon.go b/cmd/spire-server/cli/authoritycommon/authoritycommon.go index 83c16e85db..e59405addd 100644 --- a/cmd/spire-server/cli/authoritycommon/authoritycommon.go +++ b/cmd/spire-server/cli/authoritycommon/authoritycommon.go @@ -1,170 +1,31 @@ -package authoritycommon_test +package authoritycommon import ( - "bytes" - "context" - "testing" + "time" - "github.com/mitchellh/cli" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" - "github.com/spiffe/spire/cmd/spire-server/cli/common" commoncli "github.com/spiffe/spire/pkg/common/cli" - "github.com/spiffe/spire/test/spiretest" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" ) -var ( - AvailableFormats = []string{"pretty", "json"} -) - -type localAuthorityTest struct { - Stdin *bytes.Buffer - Stdout *bytes.Buffer - Stderr *bytes.Buffer - Args []string - Server *fakeLocalAuthorityServer - Client cli.Command +func PrettyPrintJWTAuthorityState(env *commoncli.Env, authorityState *localauthorityv1.AuthorityState) { + prettyPrintAuthorityState(env, authorityState, false) } -func (s *localAuthorityTest) afterTest(t *testing.T) { - t.Logf("TEST:%s", t.Name()) - t.Logf("STDOUT:\n%s", s.Stdout.String()) - t.Logf("STDIN:\n%s", s.Stdin.String()) - t.Logf("STDERR:\n%s", s.Stderr.String()) +func PrettyPrintX509AuthorityState(env *commoncli.Env, authorityState *localauthorityv1.AuthorityState) { + prettyPrintAuthorityState(env, authorityState, true) } -func SetupTest(t *testing.T, newClient func(*commoncli.Env) cli.Command) *localAuthorityTest { - server := &fakeLocalAuthorityServer{} - - addr := spiretest.StartGRPCServer(t, func(s *grpc.Server) { - localauthorityv1.RegisterLocalAuthorityServer(s, server) - }) - - stdin := new(bytes.Buffer) - stdout := new(bytes.Buffer) - stderr := new(bytes.Buffer) - - client := newClient(&commoncli.Env{ - Stdin: stdin, - Stdout: stdout, - Stderr: stderr, - }) - - test := &localAuthorityTest{ - Stdin: stdin, - Stdout: stdout, - Stderr: stderr, - Args: []string{common.AddrArg, common.GetAddr(addr)}, - Server: server, - Client: client, +func prettyPrintAuthorityState(env *commoncli.Env, authorityState *localauthorityv1.AuthorityState, includeUpstreamAuthority bool) { + env.Printf(" Authority ID: %s\n", authorityState.AuthorityId) + env.Printf(" Expires at: %s\n", time.Unix(authorityState.ExpiresAt, 0).UTC()) + if !includeUpstreamAuthority { + return } - t.Cleanup(func() { - test.afterTest(t) - }) - - return test -} - -type fakeLocalAuthorityServer struct { - localauthorityv1.UnsafeLocalAuthorityServer - - ActiveJWT, - PreparedJWT, - OldJWT, - ActiveX509, - PreparedX509, - OldX509, - TaintedX509, - RevokedX509, - TaintedJWT, - RevokedJWT *localauthorityv1.AuthorityState - - Err error -} - -func (s *fakeLocalAuthorityServer) GetJWTAuthorityState(context.Context, *localauthorityv1.GetJWTAuthorityStateRequest) (*localauthorityv1.GetJWTAuthorityStateResponse, error) { - return &localauthorityv1.GetJWTAuthorityStateResponse{ - Active: s.ActiveJWT, - Prepared: s.PreparedJWT, - Old: s.OldJWT, - }, s.Err -} - -func (s *fakeLocalAuthorityServer) PrepareJWTAuthority(context.Context, *localauthorityv1.PrepareJWTAuthorityRequest) (*localauthorityv1.PrepareJWTAuthorityResponse, error) { - return &localauthorityv1.PrepareJWTAuthorityResponse{ - PreparedAuthority: s.PreparedJWT, - }, s.Err -} - -func (s *fakeLocalAuthorityServer) ActivateJWTAuthority(context.Context, *localauthorityv1.ActivateJWTAuthorityRequest) (*localauthorityv1.ActivateJWTAuthorityResponse, error) { - return &localauthorityv1.ActivateJWTAuthorityResponse{ - ActivatedAuthority: s.ActiveJWT, - }, s.Err -} - -func (s *fakeLocalAuthorityServer) TaintJWTAuthority(context.Context, *localauthorityv1.TaintJWTAuthorityRequest) (*localauthorityv1.TaintJWTAuthorityResponse, error) { - return &localauthorityv1.TaintJWTAuthorityResponse{ - TaintedAuthority: s.TaintedJWT, - }, s.Err -} - -func (s *fakeLocalAuthorityServer) RevokeJWTAuthority(context.Context, *localauthorityv1.RevokeJWTAuthorityRequest) (*localauthorityv1.RevokeJWTAuthorityResponse, error) { - return &localauthorityv1.RevokeJWTAuthorityResponse{ - RevokedAuthority: s.RevokedJWT, - }, s.Err -} - -func (s *fakeLocalAuthorityServer) GetX509AuthorityState(context.Context, *localauthorityv1.GetX509AuthorityStateRequest) (*localauthorityv1.GetX509AuthorityStateResponse, error) { - return &localauthorityv1.GetX509AuthorityStateResponse{ - Active: s.ActiveX509, - Prepared: s.PreparedX509, - Old: s.OldX509, - }, s.Err -} - -func (s *fakeLocalAuthorityServer) PrepareX509Authority(context.Context, *localauthorityv1.PrepareX509AuthorityRequest) (*localauthorityv1.PrepareX509AuthorityResponse, error) { - return &localauthorityv1.PrepareX509AuthorityResponse{ - PreparedAuthority: s.PreparedX509, - }, s.Err -} - -func (s *fakeLocalAuthorityServer) ActivateX509Authority(context.Context, *localauthorityv1.ActivateX509AuthorityRequest) (*localauthorityv1.ActivateX509AuthorityResponse, error) { - return &localauthorityv1.ActivateX509AuthorityResponse{ - ActivatedAuthority: s.ActiveX509, - }, s.Err -} - -func (s *fakeLocalAuthorityServer) TaintX509Authority(context.Context, *localauthorityv1.TaintX509AuthorityRequest) (*localauthorityv1.TaintX509AuthorityResponse, error) { - return &localauthorityv1.TaintX509AuthorityResponse{ - TaintedAuthority: s.TaintedX509, - }, s.Err -} - -func (s *fakeLocalAuthorityServer) TaintX509UpstreamAuthority(context.Context, *localauthorityv1.TaintX509UpstreamAuthorityRequest) (*localauthorityv1.TaintX509UpstreamAuthorityResponse, error) { - return &localauthorityv1.TaintX509UpstreamAuthorityResponse{}, s.Err -} - -func (s *fakeLocalAuthorityServer) RevokeX509Authority(context.Context, *localauthorityv1.RevokeX509AuthorityRequest) (*localauthorityv1.RevokeX509AuthorityResponse, error) { - return &localauthorityv1.RevokeX509AuthorityResponse{ - RevokedAuthority: s.RevokedX509, - }, s.Err -} - -func (s *fakeLocalAuthorityServer) RevokeX509UpstreamAuthority(context.Context, *localauthorityv1.RevokeX509UpstreamAuthorityRequest) (*localauthorityv1.RevokeX509UpstreamAuthorityResponse, error) { - return &localauthorityv1.RevokeX509UpstreamAuthorityResponse{}, s.Err -} - -func RequireOutputBasedOnFormat(t *testing.T, format, stdoutString string, expectedStdoutPretty, expectedStdoutJSON string) { - switch format { - case "pretty": - require.Contains(t, stdoutString, expectedStdoutPretty) - case "json": - if expectedStdoutJSON != "" { - require.JSONEq(t, expectedStdoutJSON, stdoutString) - } else { - require.Empty(t, stdoutString) - } + if authorityState.UpstreamAuthoritySubjectKeyId != "" { + env.Printf(" Upstream authority Subject Key ID: %s\n", authorityState.UpstreamAuthoritySubjectKeyId) + return } + + env.Println(" Upstream authority ID: No upstream authority") } diff --git a/cmd/spire-server/cli/authoritycommon/test/authoritycommontest.go b/cmd/spire-server/cli/authoritycommon/test/authoritycommontest.go new file mode 100644 index 0000000000..2a78598f4e --- /dev/null +++ b/cmd/spire-server/cli/authoritycommon/test/authoritycommontest.go @@ -0,0 +1,176 @@ +package authoritycommontest + +import ( + "bytes" + "context" + "testing" + + "github.com/mitchellh/cli" + localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" + "github.com/spiffe/spire/cmd/spire-server/cli/common" + commoncli "github.com/spiffe/spire/pkg/common/cli" + "github.com/spiffe/spire/test/spiretest" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +var ( + AvailableFormats = []string{"pretty", "json"} +) + +type localAuthorityTest struct { + Stdin *bytes.Buffer + Stdout *bytes.Buffer + Stderr *bytes.Buffer + Args []string + Server *fakeLocalAuthorityServer + Client cli.Command +} + +func (s *localAuthorityTest) afterTest(t *testing.T) { + t.Logf("TEST:%s", t.Name()) + t.Logf("STDOUT:\n%s", s.Stdout.String()) + t.Logf("STDIN:\n%s", s.Stdin.String()) + t.Logf("STDERR:\n%s", s.Stderr.String()) +} + +func SetupTest(t *testing.T, newClient func(*commoncli.Env) cli.Command) *localAuthorityTest { + server := &fakeLocalAuthorityServer{} + + addr := spiretest.StartGRPCServer(t, func(s *grpc.Server) { + localauthorityv1.RegisterLocalAuthorityServer(s, server) + }) + + stdin := new(bytes.Buffer) + stdout := new(bytes.Buffer) + stderr := new(bytes.Buffer) + + client := newClient(&commoncli.Env{ + Stdin: stdin, + Stdout: stdout, + Stderr: stderr, + }) + + test := &localAuthorityTest{ + Stdin: stdin, + Stdout: stdout, + Stderr: stderr, + Args: []string{common.AddrArg, common.GetAddr(addr)}, + Server: server, + Client: client, + } + + t.Cleanup(func() { + test.afterTest(t) + }) + + return test +} + +type fakeLocalAuthorityServer struct { + localauthorityv1.UnsafeLocalAuthorityServer + + ActiveJWT, + PreparedJWT, + OldJWT, + ActiveX509, + PreparedX509, + OldX509, + TaintedX509, + RevokedX509, + TaintedJWT, + RevokedJWT *localauthorityv1.AuthorityState + + TaintedUpstreamAuthoritySubjectKeyId, + RevokedUpstreamAuthoritySubjectKeyId string + Err error +} + +func (s *fakeLocalAuthorityServer) GetJWTAuthorityState(context.Context, *localauthorityv1.GetJWTAuthorityStateRequest) (*localauthorityv1.GetJWTAuthorityStateResponse, error) { + return &localauthorityv1.GetJWTAuthorityStateResponse{ + Active: s.ActiveJWT, + Prepared: s.PreparedJWT, + Old: s.OldJWT, + }, s.Err +} + +func (s *fakeLocalAuthorityServer) PrepareJWTAuthority(context.Context, *localauthorityv1.PrepareJWTAuthorityRequest) (*localauthorityv1.PrepareJWTAuthorityResponse, error) { + return &localauthorityv1.PrepareJWTAuthorityResponse{ + PreparedAuthority: s.PreparedJWT, + }, s.Err +} + +func (s *fakeLocalAuthorityServer) ActivateJWTAuthority(context.Context, *localauthorityv1.ActivateJWTAuthorityRequest) (*localauthorityv1.ActivateJWTAuthorityResponse, error) { + return &localauthorityv1.ActivateJWTAuthorityResponse{ + ActivatedAuthority: s.ActiveJWT, + }, s.Err +} + +func (s *fakeLocalAuthorityServer) TaintJWTAuthority(context.Context, *localauthorityv1.TaintJWTAuthorityRequest) (*localauthorityv1.TaintJWTAuthorityResponse, error) { + return &localauthorityv1.TaintJWTAuthorityResponse{ + TaintedAuthority: s.TaintedJWT, + }, s.Err +} + +func (s *fakeLocalAuthorityServer) RevokeJWTAuthority(context.Context, *localauthorityv1.RevokeJWTAuthorityRequest) (*localauthorityv1.RevokeJWTAuthorityResponse, error) { + return &localauthorityv1.RevokeJWTAuthorityResponse{ + RevokedAuthority: s.RevokedJWT, + }, s.Err +} + +func (s *fakeLocalAuthorityServer) GetX509AuthorityState(context.Context, *localauthorityv1.GetX509AuthorityStateRequest) (*localauthorityv1.GetX509AuthorityStateResponse, error) { + return &localauthorityv1.GetX509AuthorityStateResponse{ + Active: s.ActiveX509, + Prepared: s.PreparedX509, + Old: s.OldX509, + }, s.Err +} + +func (s *fakeLocalAuthorityServer) PrepareX509Authority(context.Context, *localauthorityv1.PrepareX509AuthorityRequest) (*localauthorityv1.PrepareX509AuthorityResponse, error) { + return &localauthorityv1.PrepareX509AuthorityResponse{ + PreparedAuthority: s.PreparedX509, + }, s.Err +} + +func (s *fakeLocalAuthorityServer) ActivateX509Authority(context.Context, *localauthorityv1.ActivateX509AuthorityRequest) (*localauthorityv1.ActivateX509AuthorityResponse, error) { + return &localauthorityv1.ActivateX509AuthorityResponse{ + ActivatedAuthority: s.ActiveX509, + }, s.Err +} + +func (s *fakeLocalAuthorityServer) TaintX509Authority(context.Context, *localauthorityv1.TaintX509AuthorityRequest) (*localauthorityv1.TaintX509AuthorityResponse, error) { + return &localauthorityv1.TaintX509AuthorityResponse{ + TaintedAuthority: s.TaintedX509, + }, s.Err +} + +func (s *fakeLocalAuthorityServer) TaintX509UpstreamAuthority(context.Context, *localauthorityv1.TaintX509UpstreamAuthorityRequest) (*localauthorityv1.TaintX509UpstreamAuthorityResponse, error) { + return &localauthorityv1.TaintX509UpstreamAuthorityResponse{ + UpstreamAuthoritySubjectKeyId: s.TaintedUpstreamAuthoritySubjectKeyId, + }, s.Err +} + +func (s *fakeLocalAuthorityServer) RevokeX509Authority(context.Context, *localauthorityv1.RevokeX509AuthorityRequest) (*localauthorityv1.RevokeX509AuthorityResponse, error) { + return &localauthorityv1.RevokeX509AuthorityResponse{ + RevokedAuthority: s.RevokedX509, + }, s.Err +} + +func (s *fakeLocalAuthorityServer) RevokeX509UpstreamAuthority(context.Context, *localauthorityv1.RevokeX509UpstreamAuthorityRequest) (*localauthorityv1.RevokeX509UpstreamAuthorityResponse, error) { + return &localauthorityv1.RevokeX509UpstreamAuthorityResponse{ + UpstreamAuthoritySubjectKeyId: s.RevokedUpstreamAuthoritySubjectKeyId, + }, s.Err +} + +func RequireOutputBasedOnFormat(t *testing.T, format, stdoutString string, expectedStdoutPretty, expectedStdoutJSON string) { + switch format { + case "pretty": + require.Contains(t, stdoutString, expectedStdoutPretty) + case "json": + if expectedStdoutJSON != "" { + require.JSONEq(t, expectedStdoutJSON, stdoutString) + } else { + require.Empty(t, stdoutString) + } + } +} diff --git a/cmd/spire-server/cli/cli.go b/cmd/spire-server/cli/cli.go index 84ea882296..bccb08ca67 100644 --- a/cmd/spire-server/cli/cli.go +++ b/cmd/spire-server/cli/cli.go @@ -18,6 +18,7 @@ import ( "github.com/spiffe/spire/cmd/spire-server/cli/logger" "github.com/spiffe/spire/cmd/spire-server/cli/run" "github.com/spiffe/spire/cmd/spire-server/cli/token" + "github.com/spiffe/spire/cmd/spire-server/cli/upstreamauthority" "github.com/spiffe/spire/cmd/spire-server/cli/validate" "github.com/spiffe/spire/cmd/spire-server/cli/x509" "github.com/spiffe/spire/pkg/common/fflag" @@ -193,5 +194,11 @@ func addCommandsEnabledByFFlags(commands map[string]cli.CommandFactory) { commands["localauthority jwt revoke"] = func() (cli.Command, error) { return localauthority_jwt.NewJWTRevokeCommand(), nil } + commands["upstreamauthority taint"] = func() (cli.Command, error) { + return upstreamauthority.NewTaintCommand(), nil + } + commands["upstreamauthority revoke"] = func() (cli.Command, error) { + return upstreamauthority.NewRevokeCommand(), nil + } } } diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_activate.go b/cmd/spire-server/cli/localauthority/jwt/jwt_activate.go index 289f8f2cfd..4a13cc5c6e 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_activate.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_activate.go @@ -5,10 +5,10 @@ import ( "errors" "flag" "fmt" - "time" "github.com/mitchellh/cli" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" + "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" "github.com/spiffe/spire/cmd/spire-server/util" commoncli "github.com/spiffe/spire/pkg/common/cli" "github.com/spiffe/spire/pkg/common/cliprinter" @@ -80,9 +80,7 @@ func prettyPrintJWTActivate(env *commoncli.Env, results ...any) error { if r.ActivatedAuthority == nil { return errors.New("internal error: expected to have activated JWT authority information") } - - env.Printf(" Authority ID: %s\n", r.ActivatedAuthority.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.ActivatedAuthority.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintJWTAuthorityState(env, r.ActivatedAuthority) return nil } diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_activate_test.go b/cmd/spire-server/cli/localauthority/jwt/jwt_activate_test.go index 89998cfe7b..1fdbd67811 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_activate_test.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_activate_test.go @@ -6,7 +6,7 @@ import ( "github.com/gogo/status" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" - authority_common "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" + authoritycommon_test "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon/test" "github.com/spiffe/spire/cmd/spire-server/cli/common" "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/jwt" "github.com/stretchr/testify/require" @@ -14,14 +14,14 @@ import ( ) func TestJWTActivateHelp(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTActivateCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTActivateCommandWithEnv) test.Client.Help() require.Equal(t, jwtActivateUsage, test.Stderr.String()) } func TestJWTActivateSynopsys(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTActivateCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTActivateCommandWithEnv) require.Equal(t, "Activates a prepared JWT authority for use, which will cause it to be used for all JWT signing operations serviced by this server going forward", test.Client.Synopsis()) } @@ -49,7 +49,7 @@ func TestJWTActivate(t *testing.T) { ExpiresAt: 1002, }, expectStdoutPretty: "Activated JWT authority:\n Authority ID: active-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n", - expectStdoutJSON: `{"activated_authority":{"authority_id":"active-id","expires_at":"1001"}}`, + expectStdoutJSON: `{"activated_authority":{"authority_id":"active-id","expires_at":"1001","upstream_authority_subject_key_id":""}}`, }, { name: "no authority id", @@ -70,9 +70,9 @@ func TestJWTActivate(t *testing.T) { expectStderr: "Error: could not activate JWT authority: rpc error: code = Internal desc = internal server error\n", }, } { - for _, format := range authority_common.AvailableFormats { + for _, format := range authoritycommon_test.AvailableFormats { t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTActivateCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTActivateCommandWithEnv) test.Server.ActiveJWT = tt.active test.Server.Err = tt.serverErr args := tt.args @@ -80,7 +80,7 @@ func TestJWTActivate(t *testing.T) { returnCode := test.Client.Run(append(test.Args, args...)) - authority_common.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) + authoritycommon_test.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) require.Equal(t, tt.expectStderr, test.Stderr.String()) require.Equal(t, tt.expectReturnCode, returnCode) }) diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_prepare.go b/cmd/spire-server/cli/localauthority/jwt/jwt_prepare.go index b10a98233f..8dc8e55a67 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_prepare.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_prepare.go @@ -5,10 +5,10 @@ import ( "errors" "flag" "fmt" - "time" "github.com/mitchellh/cli" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" + "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" "github.com/spiffe/spire/cmd/spire-server/util" commoncli "github.com/spiffe/spire/pkg/common/cli" "github.com/spiffe/spire/pkg/common/cliprinter" @@ -64,9 +64,7 @@ func prettyPrintJWTPrepare(env *commoncli.Env, results ...any) error { if r.PreparedAuthority == nil { return errors.New("internal error: expected to have prepared JWT authority information") } - - env.Printf(" Authority ID: %s\n", r.PreparedAuthority.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.PreparedAuthority.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintJWTAuthorityState(env, r.PreparedAuthority) return nil } diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_prepare_test.go b/cmd/spire-server/cli/localauthority/jwt/jwt_prepare_test.go index 77d10299e6..b5b2dce04b 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_prepare_test.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_prepare_test.go @@ -6,7 +6,7 @@ import ( "github.com/gogo/status" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" - authority_common "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" + authoritycommon_test "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon/test" "github.com/spiffe/spire/cmd/spire-server/cli/common" "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/jwt" "github.com/stretchr/testify/require" @@ -14,14 +14,14 @@ import ( ) func TestJWTPrepareHelp(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTPrepareCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTPrepareCommandWithEnv) test.Client.Help() require.Equal(t, jwtPrepareUsage, test.Stderr.String()) } func TestJWTPrepareSynopsys(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTPrepareCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTPrepareCommandWithEnv) require.Equal(t, "Prepares a new JWT authority for use by generating a new key and injecting it into the bundle", test.Client.Synopsis()) } @@ -40,7 +40,7 @@ func TestJWTPrepare(t *testing.T) { name: "success", expectReturnCode: 0, expectStdoutPretty: "Prepared JWT authority:\n Authority ID: prepared-id\n Expires at: 1970-01-01 00:16:42 +0000 UTC\n", - expectStdoutJSON: `{"prepared_authority":{"authority_id":"prepared-id","expires_at":"1002"}}`, + expectStdoutJSON: `{"prepared_authority":{"authority_id":"prepared-id","expires_at":"1002","upstream_authority_subject_key_id":""}}`, prepared: &localauthorityv1.AuthorityState{ AuthorityId: "prepared-id", ExpiresAt: 1002, @@ -59,9 +59,9 @@ func TestJWTPrepare(t *testing.T) { expectStderr: "Error: could not prepare JWT authority: rpc error: code = Internal desc = internal server error\n", }, } { - for _, format := range authority_common.AvailableFormats { + for _, format := range authoritycommon_test.AvailableFormats { t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTPrepareCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTPrepareCommandWithEnv) test.Server.PreparedJWT = tt.prepared test.Server.Err = tt.serverErr args := tt.args @@ -69,7 +69,7 @@ func TestJWTPrepare(t *testing.T) { returnCode := test.Client.Run(append(test.Args, args...)) - authority_common.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) + authoritycommon_test.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) require.Equal(t, tt.expectStderr, test.Stderr.String()) require.Equal(t, tt.expectReturnCode, returnCode) }) diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_revoke.go b/cmd/spire-server/cli/localauthority/jwt/jwt_revoke.go index 2f5fe49886..e8e6c840d0 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_revoke.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_revoke.go @@ -5,10 +5,10 @@ import ( "errors" "flag" "fmt" - "time" "github.com/mitchellh/cli" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" + "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" "github.com/spiffe/spire/cmd/spire-server/util" commoncli "github.com/spiffe/spire/pkg/common/cli" "github.com/spiffe/spire/pkg/common/cliprinter" @@ -80,9 +80,7 @@ func prettyPrintJWTRevoke(env *commoncli.Env, results ...any) error { if r.RevokedAuthority == nil { return errors.New("internal error: expected to have revoked JWT authority information") } - - env.Printf(" Authority ID: %s\n", r.RevokedAuthority.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.RevokedAuthority.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintJWTAuthorityState(env, r.RevokedAuthority) return nil } diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_revoke_test.go b/cmd/spire-server/cli/localauthority/jwt/jwt_revoke_test.go index 9897a59dc1..66e3c821f9 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_revoke_test.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_revoke_test.go @@ -6,7 +6,7 @@ import ( "github.com/gogo/status" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" - authority_common "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" + authoritycommon_test "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon/test" "github.com/spiffe/spire/cmd/spire-server/cli/common" "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/jwt" "github.com/stretchr/testify/require" @@ -14,14 +14,14 @@ import ( ) func TestJWTRevokeHelp(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTRevokeCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTRevokeCommandWithEnv) test.Client.Help() require.Equal(t, jwtRevokeUsage, test.Stderr.String()) } func TestJWTRevokeSynopsys(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTRevokeCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTRevokeCommandWithEnv) require.Equal(t, "Revokes the previously active JWT authority by removing it from the bundle and propagating this update throughout the cluster", test.Client.Synopsis()) } @@ -45,7 +45,7 @@ func TestJWTRevoke(t *testing.T) { ExpiresAt: 1001, }, expectStdoutPretty: "Revoked JWT authority:\n Authority ID: revoked-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n", - expectStdoutJSON: `{"revoked_authority":{"authority_id":"revoked-id","expires_at":"1001"}}`, + expectStdoutJSON: `{"revoked_authority":{"authority_id":"revoked-id","expires_at":"1001","upstream_authority_subject_key_id":""}}`, }, { name: "no authority id", @@ -66,9 +66,9 @@ func TestJWTRevoke(t *testing.T) { expectStderr: "Error: could not revoke JWT authority: rpc error: code = Internal desc = internal server error\n", }, } { - for _, format := range authority_common.AvailableFormats { + for _, format := range authoritycommon_test.AvailableFormats { t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTRevokeCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTRevokeCommandWithEnv) test.Server.RevokedJWT = tt.revoked test.Server.Err = tt.serverErr args := tt.args @@ -76,7 +76,7 @@ func TestJWTRevoke(t *testing.T) { returnCode := test.Client.Run(append(test.Args, args...)) - authority_common.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) + authoritycommon_test.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) require.Equal(t, tt.expectStderr, test.Stderr.String()) require.Equal(t, tt.expectReturnCode, returnCode) }) diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_show.go b/cmd/spire-server/cli/localauthority/jwt/jwt_show.go index cee9cf37e2..31f8a258a4 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_show.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_show.go @@ -4,10 +4,10 @@ import ( "context" "errors" "flag" - "time" "github.com/mitchellh/cli" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" + "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" "github.com/spiffe/spire/cmd/spire-server/util" commoncli "github.com/spiffe/spire/pkg/common/cli" "github.com/spiffe/spire/pkg/common/cliprinter" @@ -62,24 +62,21 @@ func prettyPrintJWTShow(env *commoncli.Env, results ...any) error { env.Println("Active JWT authority:") if r.Active != nil { - env.Printf(" Authority ID: %s\n", r.Active.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.Active.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintJWTAuthorityState(env, r.Active) } else { env.Println(" No active JWT authority found") } env.Println() env.Println("Prepared JWT authority:") if r.Prepared != nil { - env.Printf(" Authority ID: %s\n", r.Prepared.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.Prepared.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintJWTAuthorityState(env, r.Prepared) } else { env.Println(" No prepared JWT authority found") } env.Println() env.Println("Old JWT authority:") if r.Old != nil { - env.Printf(" Authority ID: %s\n", r.Old.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.Old.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintJWTAuthorityState(env, r.Old) } else { env.Println(" No old JWT authority found") } diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_show_test.go b/cmd/spire-server/cli/localauthority/jwt/jwt_show_test.go index c6e3e5e114..0f68971a9e 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_show_test.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_show_test.go @@ -6,7 +6,7 @@ import ( "github.com/gogo/status" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" - authority_common "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" + authoritycommon_test "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon/test" "github.com/spiffe/spire/cmd/spire-server/cli/common" "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/jwt" "github.com/stretchr/testify/require" @@ -14,14 +14,14 @@ import ( ) func TestJWTShowHelp(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTShowCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTShowCommandWithEnv) test.Client.Help() require.Equal(t, jwtShowUsage, test.Stderr.String()) } func TestJWTShowSynopsys(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTShowCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTShowCommandWithEnv) require.Equal(t, "Shows the local JWT authorities", test.Client.Synopsis()) } @@ -55,7 +55,7 @@ func TestJWTShow(t *testing.T) { ExpiresAt: 1003, }, expectStdoutPretty: "Active JWT authority:\n Authority ID: active-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n\nPrepared JWT authority:\n Authority ID: prepared-id\n Expires at: 1970-01-01 00:16:42 +0000 UTC\n\nOld JWT authority:\n Authority ID: old-id\n Expires at: 1970-01-01 00:16:43 +0000 UTC\n", - expectStdoutJSON: `{"active":{"authority_id":"active-id","expires_at":"1001"},"prepared":{"authority_id":"prepared-id","expires_at":"1002"},"old":{"authority_id":"old-id","expires_at":"1003"}}`, + expectStdoutJSON: `{"active":{"authority_id":"active-id","expires_at":"1001","upstream_authority_subject_key_id":""},"prepared":{"authority_id":"prepared-id","expires_at":"1002","upstream_authority_subject_key_id":""},"old":{"authority_id":"old-id","expires_at":"1003","upstream_authority_subject_key_id":""}}`, }, { name: "success - no active", @@ -69,7 +69,7 @@ func TestJWTShow(t *testing.T) { ExpiresAt: 1003, }, expectStdoutPretty: "Active JWT authority:\n No active JWT authority found\n\nPrepared JWT authority:\n Authority ID: prepared-id\n Expires at: 1970-01-01 00:16:42 +0000 UTC\n\nOld JWT authority:\n Authority ID: old-id\n Expires at: 1970-01-01 00:16:43 +0000 UTC\n", - expectStdoutJSON: `{"prepared":{"authority_id":"prepared-id","expires_at":"1002"},"old":{"authority_id":"old-id","expires_at":"1003"}}`, + expectStdoutJSON: `{"prepared":{"authority_id":"prepared-id","expires_at":"1002","upstream_authority_subject_key_id":""},"old":{"authority_id":"old-id","expires_at":"1003","upstream_authority_subject_key_id":""}}`, }, { name: "success - no prepared", @@ -83,7 +83,7 @@ func TestJWTShow(t *testing.T) { ExpiresAt: 1003, }, expectStdoutPretty: "Active JWT authority:\n Authority ID: active-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n\nPrepared JWT authority:\n No prepared JWT authority found\n\nOld JWT authority:\n Authority ID: old-id\n Expires at: 1970-01-01 00:16:43 +0000 UTC\n", - expectStdoutJSON: `{"active":{"authority_id":"active-id","expires_at":"1001"},"old":{"authority_id":"old-id","expires_at":"1003"}}`, + expectStdoutJSON: `{"active":{"authority_id":"active-id","expires_at":"1001","upstream_authority_subject_key_id":""},"old":{"authority_id":"old-id","expires_at":"1003","upstream_authority_subject_key_id":""}}`, }, { name: "success - no old", @@ -97,7 +97,7 @@ func TestJWTShow(t *testing.T) { ExpiresAt: 1002, }, expectStdoutPretty: "Active JWT authority:\n Authority ID: active-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n\nPrepared JWT authority:\n Authority ID: prepared-id\n Expires at: 1970-01-01 00:16:42 +0000 UTC\n\nOld JWT authority:\n No old JWT authority found\n", - expectStdoutJSON: `{"active":{"authority_id":"active-id","expires_at":"1001"},"prepared":{"authority_id":"prepared-id","expires_at":"1002"}}`, + expectStdoutJSON: `{"active":{"authority_id":"active-id","expires_at":"1001","upstream_authority_subject_key_id":""},"prepared":{"authority_id":"prepared-id","expires_at":"1002","upstream_authority_subject_key_id":""}}`, }, { name: "wrong UDS path", @@ -112,9 +112,9 @@ func TestJWTShow(t *testing.T) { expectStderr: "Error: rpc error: code = Internal desc = internal server error\n", }, } { - for _, format := range authority_common.AvailableFormats { + for _, format := range authoritycommon_test.AvailableFormats { t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTShowCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTShowCommandWithEnv) test.Server.ActiveJWT = tt.active test.Server.PreparedJWT = tt.prepared test.Server.OldJWT = tt.old @@ -124,7 +124,7 @@ func TestJWTShow(t *testing.T) { returnCode := test.Client.Run(append(test.Args, args...)) - authority_common.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) + authoritycommon_test.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) require.Equal(t, tt.expectStderr, test.Stderr.String()) require.Equal(t, tt.expectReturnCode, returnCode) }) diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_taint.go b/cmd/spire-server/cli/localauthority/jwt/jwt_taint.go index c7e7ed0d43..ab8ec79831 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_taint.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_taint.go @@ -5,10 +5,10 @@ import ( "errors" "flag" "fmt" - "time" "github.com/mitchellh/cli" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" + "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" "github.com/spiffe/spire/cmd/spire-server/util" commoncli "github.com/spiffe/spire/pkg/common/cli" "github.com/spiffe/spire/pkg/common/cliprinter" @@ -76,9 +76,7 @@ func prettyPrintJWTTaint(env *commoncli.Env, results ...any) error { if r.TaintedAuthority == nil { return errors.New("internal error: expected to have tainted JWT authority information") } - - env.Printf(" Authority ID: %s\n", r.TaintedAuthority.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.TaintedAuthority.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintJWTAuthorityState(env, r.TaintedAuthority) return nil } diff --git a/cmd/spire-server/cli/localauthority/jwt/jwt_taint_test.go b/cmd/spire-server/cli/localauthority/jwt/jwt_taint_test.go index 6108270e05..ec765ea4fb 100644 --- a/cmd/spire-server/cli/localauthority/jwt/jwt_taint_test.go +++ b/cmd/spire-server/cli/localauthority/jwt/jwt_taint_test.go @@ -6,7 +6,7 @@ import ( "github.com/gogo/status" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" - authority_common "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" + authoritycommon_test "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon/test" "github.com/spiffe/spire/cmd/spire-server/cli/common" "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/jwt" "github.com/stretchr/testify/require" @@ -14,14 +14,14 @@ import ( ) func TestJWTTaintHelp(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTTaintCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTTaintCommandWithEnv) test.Client.Help() require.Equal(t, jwtTaintUsage, test.Stderr.String()) } func TestJWTTaintSynopsys(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTTaintCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTTaintCommandWithEnv) require.Equal(t, "Marks the previously active JWT authority as being tainted", test.Client.Synopsis()) } @@ -45,7 +45,7 @@ func TestJWTTaint(t *testing.T) { ExpiresAt: 1001, }, expectStdoutPretty: "Tainted JWT authority:\n Authority ID: tainted-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n", - expectStdoutJSON: `{"tainted_authority":{"authority_id":"tainted-id","expires_at":"1001"}}`, + expectStdoutJSON: `{"tainted_authority":{"authority_id":"tainted-id","expires_at":"1001","upstream_authority_subject_key_id":""}}`, }, { name: "no authority id", @@ -66,9 +66,9 @@ func TestJWTTaint(t *testing.T) { expectStderr: "Error: could not taint JWT authority: rpc error: code = Internal desc = internal server error\n", }, } { - for _, format := range authority_common.AvailableFormats { + for _, format := range authoritycommon_test.AvailableFormats { t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) { - test := authority_common.SetupTest(t, jwt.NewJWTTaintCommandWithEnv) + test := authoritycommon_test.SetupTest(t, jwt.NewJWTTaintCommandWithEnv) test.Server.TaintedJWT = tt.tainted test.Server.Err = tt.serverErr args := tt.args @@ -76,7 +76,7 @@ func TestJWTTaint(t *testing.T) { returnCode := test.Client.Run(append(test.Args, args...)) - authority_common.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) + authoritycommon_test.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) require.Equal(t, tt.expectStderr, test.Stderr.String()) require.Equal(t, tt.expectReturnCode, returnCode) }) diff --git a/cmd/spire-server/cli/localauthority/x509/x509_activate.go b/cmd/spire-server/cli/localauthority/x509/x509_activate.go index 118c2e409b..907642d236 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_activate.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_activate.go @@ -5,10 +5,10 @@ import ( "errors" "flag" "fmt" - "time" "github.com/mitchellh/cli" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" + "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" "github.com/spiffe/spire/cmd/spire-server/util" commoncli "github.com/spiffe/spire/pkg/common/cli" "github.com/spiffe/spire/pkg/common/cliprinter" @@ -81,8 +81,7 @@ func prettyPrintX509Activate(env *commoncli.Env, results ...any) error { return errors.New("internal error: expected to have activated X.509 authority information") } - env.Printf(" Authority ID: %s\n", r.ActivatedAuthority.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.ActivatedAuthority.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintX509AuthorityState(env, r.ActivatedAuthority) return nil } diff --git a/cmd/spire-server/cli/localauthority/x509/x509_activate_test.go b/cmd/spire-server/cli/localauthority/x509/x509_activate_test.go index 97e835fef7..25332acbfa 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_activate_test.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_activate_test.go @@ -6,7 +6,7 @@ import ( "github.com/gogo/status" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" - authority_common "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" + authoritycommon_test "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon/test" "github.com/spiffe/spire/cmd/spire-server/cli/common" "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/x509" "github.com/stretchr/testify/require" @@ -14,14 +14,14 @@ import ( ) func TestX509ActivateHelp(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509ActivateCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509ActivateCommandWithEnv) test.Client.Help() require.Equal(t, x509ActivateUsage, test.Stderr.String()) } func TestX509ActivateSynopsys(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509ActivateCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509ActivateCommandWithEnv) require.Equal(t, "Activates a prepared X.509 authority for use, which will cause it to be used for all X.509 signing operations serviced by this server going forward", test.Client.Synopsis()) } @@ -41,15 +41,17 @@ func TestX509Activate(t *testing.T) { expectReturnCode: 0, args: []string{"-authorityID", "prepared-id"}, active: &localauthorityv1.AuthorityState{ - AuthorityId: "active-id", - ExpiresAt: 1001, + AuthorityId: "active-id", + ExpiresAt: 1001, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, prepared: &localauthorityv1.AuthorityState{ - AuthorityId: "prepared-id", - ExpiresAt: 1002, + AuthorityId: "prepared-id", + ExpiresAt: 1002, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, - expectStdoutPretty: "Activated X.509 authority:\n Authority ID: active-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n", - expectStdoutJSON: `{"activated_authority":{"authority_id":"active-id","expires_at":"1001"}}`, + expectStdoutPretty: "Activated X.509 authority:\n Authority ID: active-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n Upstream authority Subject Key ID: some-subject-key-id", + expectStdoutJSON: `{"activated_authority":{"authority_id":"active-id","expires_at":"1001","upstream_authority_subject_key_id":"some-subject-key-id"}}`, }, { name: "no authority id", @@ -70,9 +72,9 @@ func TestX509Activate(t *testing.T) { expectStderr: "Error: could not activate X.509 authority: rpc error: code = Internal desc = internal server error\n", }, } { - for _, format := range authority_common.AvailableFormats { + for _, format := range authoritycommon_test.AvailableFormats { t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509ActivateCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509ActivateCommandWithEnv) test.Server.ActiveX509 = tt.active test.Server.Err = tt.serverErr args := tt.args @@ -80,7 +82,7 @@ func TestX509Activate(t *testing.T) { returnCode := test.Client.Run(append(test.Args, args...)) - authority_common.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) + authoritycommon_test.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) require.Equal(t, tt.expectStderr, test.Stderr.String()) require.Equal(t, tt.expectReturnCode, returnCode) }) diff --git a/cmd/spire-server/cli/localauthority/x509/x509_prepare.go b/cmd/spire-server/cli/localauthority/x509/x509_prepare.go index 7553c1f090..98c5349f11 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_prepare.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_prepare.go @@ -5,10 +5,10 @@ import ( "errors" "flag" "fmt" - "time" "github.com/mitchellh/cli" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" + "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" "github.com/spiffe/spire/cmd/spire-server/util" commoncli "github.com/spiffe/spire/pkg/common/cli" "github.com/spiffe/spire/pkg/common/cliprinter" @@ -65,8 +65,7 @@ func prettyPrintX509Prepare(env *commoncli.Env, results ...any) error { return errors.New("internal error: expected to have prepared X.509 authority information") } - env.Printf(" Authority ID: %s\n", r.PreparedAuthority.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.PreparedAuthority.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintX509AuthorityState(env, r.PreparedAuthority) return nil } diff --git a/cmd/spire-server/cli/localauthority/x509/x509_prepare_test.go b/cmd/spire-server/cli/localauthority/x509/x509_prepare_test.go index f1ccc6f773..0a6c65cf65 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_prepare_test.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_prepare_test.go @@ -6,7 +6,7 @@ import ( "github.com/gogo/status" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" - authority_common "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" + authoritycommon_test "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon/test" "github.com/spiffe/spire/cmd/spire-server/cli/common" "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/x509" "github.com/stretchr/testify/require" @@ -14,14 +14,14 @@ import ( ) func TestX509PrepareHelp(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509PrepareCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509PrepareCommandWithEnv) test.Client.Help() require.Equal(t, x509PrepareUsage, test.Stderr.String()) } func TestX509PrepareSynopsys(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509PrepareCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509PrepareCommandWithEnv) require.Equal(t, "Prepares a new X.509 authority for use by generating a new key and injecting the resulting CA certificate into the bundle", test.Client.Synopsis()) } @@ -39,11 +39,12 @@ func TestX509Prepare(t *testing.T) { { name: "success", expectReturnCode: 0, - expectStdoutPretty: "Prepared X.509 authority:\n Authority ID: prepared-id\n Expires at: 1970-01-01 00:16:42 +0000 UTC\n", - expectStdoutJSON: `{"prepared_authority":{"authority_id":"prepared-id","expires_at":"1002"}}`, + expectStdoutPretty: "Prepared X.509 authority:\n Authority ID: prepared-id\n Expires at: 1970-01-01 00:16:42 +0000 UTC\n Upstream authority Subject Key ID: some-subject-key-id", + expectStdoutJSON: `{"prepared_authority":{"authority_id":"prepared-id","expires_at":"1002","upstream_authority_subject_key_id":"some-subject-key-id"}}`, prepared: &localauthorityv1.AuthorityState{ - AuthorityId: "prepared-id", - ExpiresAt: 1002, + AuthorityId: "prepared-id", + ExpiresAt: 1002, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, }, { @@ -59,9 +60,9 @@ func TestX509Prepare(t *testing.T) { expectStderr: "Error: could not prepare X.509 authority: rpc error: code = Internal desc = internal server error\n", }, } { - for _, format := range authority_common.AvailableFormats { + for _, format := range authoritycommon_test.AvailableFormats { t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509PrepareCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509PrepareCommandWithEnv) test.Server.PreparedX509 = tt.prepared test.Server.Err = tt.serverErr args := tt.args @@ -69,7 +70,7 @@ func TestX509Prepare(t *testing.T) { returnCode := test.Client.Run(append(test.Args, args...)) - authority_common.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) + authoritycommon_test.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) require.Equal(t, tt.expectStderr, test.Stderr.String()) require.Equal(t, tt.expectReturnCode, returnCode) }) diff --git a/cmd/spire-server/cli/localauthority/x509/x509_revoke.go b/cmd/spire-server/cli/localauthority/x509/x509_revoke.go index ac42c35da8..edb59a623f 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_revoke.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_revoke.go @@ -5,10 +5,10 @@ import ( "errors" "flag" "fmt" - "time" "github.com/mitchellh/cli" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" + "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" "github.com/spiffe/spire/cmd/spire-server/util" commoncli "github.com/spiffe/spire/pkg/common/cli" "github.com/spiffe/spire/pkg/common/cliprinter" @@ -81,8 +81,7 @@ func prettyPrintX509Revoke(env *commoncli.Env, results ...any) error { return errors.New("internal error: expected to have revoked X.509 authority information") } - env.Printf(" Authority ID: %s\n", r.RevokedAuthority.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.RevokedAuthority.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintX509AuthorityState(env, r.RevokedAuthority) return nil } diff --git a/cmd/spire-server/cli/localauthority/x509/x509_revoke_test.go b/cmd/spire-server/cli/localauthority/x509/x509_revoke_test.go index 9459de5419..babe525b44 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_revoke_test.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_revoke_test.go @@ -6,7 +6,7 @@ import ( "github.com/gogo/status" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" - authority_common "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" + authoritycommon_test "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon/test" "github.com/spiffe/spire/cmd/spire-server/cli/common" "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/x509" "github.com/stretchr/testify/require" @@ -14,14 +14,14 @@ import ( ) func TestX509RevokeHelp(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509RevokeCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509RevokeCommandWithEnv) test.Client.Help() require.Equal(t, x509RevokeUsage, test.Stderr.String()) } func TestX509RevokeSynopsys(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509RevokeCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509RevokeCommandWithEnv) require.Equal(t, "Revokes the previously active X.509 authority by removing it from the bundle and propagating this update throughout the cluster", test.Client.Synopsis()) } @@ -41,11 +41,12 @@ func TestX509Revoke(t *testing.T) { expectReturnCode: 0, args: []string{"-authorityID", "prepared-id"}, revoked: &localauthorityv1.AuthorityState{ - AuthorityId: "revoked-id", - ExpiresAt: 1001, + AuthorityId: "revoked-id", + ExpiresAt: 1001, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, - expectStdoutPretty: "Revoked X.509 authority:\n Authority ID: revoked-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n", - expectStdoutJSON: `{"revoked_authority":{"authority_id":"revoked-id","expires_at":"1001"}}`, + expectStdoutPretty: "Revoked X.509 authority:\n Authority ID: revoked-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n Upstream authority Subject Key ID: some-subject-key-id", + expectStdoutJSON: `{"revoked_authority":{"authority_id":"revoked-id","expires_at":"1001","upstream_authority_subject_key_id":"some-subject-key-id"}}`, }, { name: "no authority id", @@ -66,9 +67,9 @@ func TestX509Revoke(t *testing.T) { expectStderr: "Error: could not revoke X.509 authority: rpc error: code = Internal desc = internal server error\n", }, } { - for _, format := range authority_common.AvailableFormats { + for _, format := range authoritycommon_test.AvailableFormats { t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509RevokeCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509RevokeCommandWithEnv) test.Server.RevokedX509 = tt.revoked test.Server.Err = tt.serverErr args := tt.args @@ -76,7 +77,7 @@ func TestX509Revoke(t *testing.T) { returnCode := test.Client.Run(append(test.Args, args...)) - authority_common.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) + authoritycommon_test.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) require.Equal(t, tt.expectStderr, test.Stderr.String()) require.Equal(t, tt.expectReturnCode, returnCode) }) diff --git a/cmd/spire-server/cli/localauthority/x509/x509_show.go b/cmd/spire-server/cli/localauthority/x509/x509_show.go index fbe6776401..ea202adc9d 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_show.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_show.go @@ -5,10 +5,10 @@ import ( "errors" "flag" "fmt" - "time" "github.com/mitchellh/cli" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" + "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" "github.com/spiffe/spire/cmd/spire-server/util" commoncli "github.com/spiffe/spire/pkg/common/cli" "github.com/spiffe/spire/pkg/common/cliprinter" @@ -63,24 +63,21 @@ func prettyPrintX509Show(env *commoncli.Env, results ...any) error { env.Println("Active X.509 authority:") if r.Active != nil { - env.Printf(" Authority ID: %s\n", r.Active.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.Active.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintX509AuthorityState(env, r.Active) } else { env.Println(" No active X.509 authority found") } env.Println() env.Println("Prepared X.509 authority:") if r.Prepared != nil { - env.Printf(" Authority ID: %s\n", r.Prepared.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.Prepared.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintX509AuthorityState(env, r.Prepared) } else { env.Println(" No prepared X.509 authority found") } env.Println() env.Println("Old X.509 authority:") if r.Old != nil { - env.Printf(" Authority ID: %s\n", r.Old.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.Old.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintX509AuthorityState(env, r.Old) } else { env.Println(" No old X.509 authority found") } diff --git a/cmd/spire-server/cli/localauthority/x509/x509_show_test.go b/cmd/spire-server/cli/localauthority/x509/x509_show_test.go index 8acfcbf8e3..738f052baf 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_show_test.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_show_test.go @@ -6,7 +6,7 @@ import ( "github.com/gogo/status" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" - authority_common "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" + authoritycommon_test "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon/test" "github.com/spiffe/spire/cmd/spire-server/cli/common" "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/x509" "github.com/stretchr/testify/require" @@ -14,14 +14,14 @@ import ( ) func TestX509ShowHelp(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509ShowCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509ShowCommandWithEnv) test.Client.Help() require.Equal(t, x509ShowUsage, test.Stderr.String()) } func TestX509ShowSynopsys(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509ShowCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509ShowCommandWithEnv) require.Equal(t, "Shows the local X.509 authorities", test.Client.Synopsis()) } @@ -43,61 +43,70 @@ func TestX509Show(t *testing.T) { name: "success", expectReturnCode: 0, active: &localauthorityv1.AuthorityState{ - AuthorityId: "active-id", - ExpiresAt: 1001, + AuthorityId: "active-id", + ExpiresAt: 1001, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, prepared: &localauthorityv1.AuthorityState{ - AuthorityId: "prepared-id", - ExpiresAt: 1002, + AuthorityId: "prepared-id", + ExpiresAt: 1002, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, old: &localauthorityv1.AuthorityState{ - AuthorityId: "old-id", - ExpiresAt: 1003, + AuthorityId: "old-id", + ExpiresAt: 1003, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, - expectStdoutPretty: "Active X.509 authority:\n Authority ID: active-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n\nPrepared X.509 authority:\n Authority ID: prepared-id\n Expires at: 1970-01-01 00:16:42 +0000 UTC\n\nOld X.509 authority:\n Authority ID: old-id\n Expires at: 1970-01-01 00:16:43 +0000 UTC\n", - expectStdoutJSON: `{"active":{"authority_id":"active-id","expires_at":"1001"},"prepared":{"authority_id":"prepared-id","expires_at":"1002"},"old":{"authority_id":"old-id","expires_at":"1003"}}`, + expectStdoutPretty: "Active X.509 authority:\n Authority ID: active-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n Upstream authority Subject Key ID: some-subject-key-id\n\nPrepared X.509 authority:\n Authority ID: prepared-id\n Expires at: 1970-01-01 00:16:42 +0000 UTC\n Upstream authority Subject Key ID: some-subject-key-id\n\nOld X.509 authority:\n Authority ID: old-id\n Expires at: 1970-01-01 00:16:43 +0000 UTC\n Upstream authority Subject Key ID: some-subject-key-id\n", + expectStdoutJSON: `{"active":{"authority_id":"active-id","expires_at":"1001","upstream_authority_subject_key_id":"some-subject-key-id"},"prepared":{"authority_id":"prepared-id","expires_at":"1002","upstream_authority_subject_key_id":"some-subject-key-id"},"old":{"authority_id":"old-id","expires_at":"1003","upstream_authority_subject_key_id":"some-subject-key-id"}}`, }, { name: "success - no active", expectReturnCode: 0, prepared: &localauthorityv1.AuthorityState{ - AuthorityId: "prepared-id", - ExpiresAt: 1002, + AuthorityId: "prepared-id", + ExpiresAt: 1002, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, old: &localauthorityv1.AuthorityState{ - AuthorityId: "old-id", - ExpiresAt: 1003, + AuthorityId: "old-id", + ExpiresAt: 1003, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, - expectStdoutPretty: "Active X.509 authority:\n No active X.509 authority found\n\nPrepared X.509 authority:\n Authority ID: prepared-id\n Expires at: 1970-01-01 00:16:42 +0000 UTC\n\nOld X.509 authority:\n Authority ID: old-id\n Expires at: 1970-01-01 00:16:43 +0000 UTC\n", - expectStdoutJSON: `{"prepared":{"authority_id":"prepared-id","expires_at":"1002"},"old":{"authority_id":"old-id","expires_at":"1003"}}`, + expectStdoutPretty: "Active X.509 authority:\n No active X.509 authority found\n\nPrepared X.509 authority:\n Authority ID: prepared-id\n Expires at: 1970-01-01 00:16:42 +0000 UTC\n Upstream authority Subject Key ID: some-subject-key-id\n\nOld X.509 authority:\n Authority ID: old-id\n Expires at: 1970-01-01 00:16:43 +0000 UTC\n Upstream authority Subject Key ID: some-subject-key-id\n", + expectStdoutJSON: `{"prepared":{"authority_id":"prepared-id","expires_at":"1002","upstream_authority_subject_key_id":"some-subject-key-id"},"old":{"authority_id":"old-id","expires_at":"1003","upstream_authority_subject_key_id":"some-subject-key-id"}}`, }, { name: "success - no prepared", expectReturnCode: 0, active: &localauthorityv1.AuthorityState{ - AuthorityId: "active-id", - ExpiresAt: 1001, + AuthorityId: "active-id", + ExpiresAt: 1001, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, old: &localauthorityv1.AuthorityState{ - AuthorityId: "old-id", - ExpiresAt: 1003, + AuthorityId: "old-id", + ExpiresAt: 1003, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, - expectStdoutPretty: "Active X.509 authority:\n Authority ID: active-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n\nPrepared X.509 authority:\n No prepared X.509 authority found\n\nOld X.509 authority:\n Authority ID: old-id\n Expires at: 1970-01-01 00:16:43 +0000 UTC\n", - expectStdoutJSON: `{"active":{"authority_id":"active-id","expires_at":"1001"},"old":{"authority_id":"old-id","expires_at":"1003"}}`, + expectStdoutPretty: "Active X.509 authority:\n Authority ID: active-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n Upstream authority Subject Key ID: some-subject-key-id\n\nPrepared X.509 authority:\n No prepared X.509 authority found\n\nOld X.509 authority:\n Authority ID: old-id\n Expires at: 1970-01-01 00:16:43 +0000 UTC\n Upstream authority Subject Key ID: some-subject-key-id\n", + expectStdoutJSON: `{"active":{"authority_id":"active-id","expires_at":"1001","upstream_authority_subject_key_id":"some-subject-key-id"},"old":{"authority_id":"old-id","expires_at":"1003","upstream_authority_subject_key_id":"some-subject-key-id"}}`, }, { name: "success - no old", expectReturnCode: 0, active: &localauthorityv1.AuthorityState{ - AuthorityId: "active-id", - ExpiresAt: 1001, + AuthorityId: "active-id", + ExpiresAt: 1001, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, prepared: &localauthorityv1.AuthorityState{ - AuthorityId: "prepared-id", - ExpiresAt: 1002, + AuthorityId: "prepared-id", + ExpiresAt: 1002, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, - expectStdoutPretty: "Active X.509 authority:\n Authority ID: active-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n\nPrepared X.509 authority:\n Authority ID: prepared-id\n Expires at: 1970-01-01 00:16:42 +0000 UTC\n\nOld X.509 authority:\n No old X.509 authority found\n", - expectStdoutJSON: `{"active":{"authority_id":"active-id","expires_at":"1001"},"prepared":{"authority_id":"prepared-id","expires_at":"1002"}}`, + expectStdoutPretty: "Active X.509 authority:\n Authority ID: active-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n Upstream authority Subject Key ID: some-subject-key-id\n\nPrepared X.509 authority:\n Authority ID: prepared-id\n Expires at: 1970-01-01 00:16:42 +0000 UTC\n Upstream authority Subject Key ID: some-subject-key-id\n\nOld X.509 authority:\n No old X.509 authority found\n", + expectStdoutJSON: `{"active":{"authority_id":"active-id","expires_at":"1001","upstream_authority_subject_key_id":"some-subject-key-id"},"prepared":{"authority_id":"prepared-id","expires_at":"1002","upstream_authority_subject_key_id":"some-subject-key-id"}}`, }, { name: "wrong UDS path", @@ -112,9 +121,9 @@ func TestX509Show(t *testing.T) { expectStderr: "Error: could not get X.509 authorities: rpc error: code = Internal desc = internal server error\n", }, } { - for _, format := range authority_common.AvailableFormats { + for _, format := range authoritycommon_test.AvailableFormats { t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509ShowCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509ShowCommandWithEnv) test.Server.ActiveX509 = tt.active test.Server.PreparedX509 = tt.prepared test.Server.OldX509 = tt.old @@ -124,7 +133,7 @@ func TestX509Show(t *testing.T) { returnCode := test.Client.Run(append(test.Args, args...)) - authority_common.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) + authoritycommon_test.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) require.Equal(t, tt.expectStderr, test.Stderr.String()) require.Equal(t, tt.expectReturnCode, returnCode) }) diff --git a/cmd/spire-server/cli/localauthority/x509/x509_taint.go b/cmd/spire-server/cli/localauthority/x509/x509_taint.go index f0c88cbde2..6b658ea26a 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_taint.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_taint.go @@ -5,10 +5,10 @@ import ( "errors" "flag" "fmt" - "time" "github.com/mitchellh/cli" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" + "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" "github.com/spiffe/spire/cmd/spire-server/util" commoncli "github.com/spiffe/spire/pkg/common/cli" "github.com/spiffe/spire/pkg/common/cliprinter" @@ -77,8 +77,7 @@ func prettyPrintX509Taint(env *commoncli.Env, results ...any) error { return errors.New("internal error: expected to have tainted X.509 authority information") } - env.Printf(" Authority ID: %s\n", r.TaintedAuthority.AuthorityId) - env.Printf(" Expires at: %s\n", time.Unix(r.TaintedAuthority.ExpiresAt, 0).UTC()) + authoritycommon.PrettyPrintX509AuthorityState(env, r.TaintedAuthority) return nil } diff --git a/cmd/spire-server/cli/localauthority/x509/x509_taint_test.go b/cmd/spire-server/cli/localauthority/x509/x509_taint_test.go index db3f0f13af..3582720c35 100644 --- a/cmd/spire-server/cli/localauthority/x509/x509_taint_test.go +++ b/cmd/spire-server/cli/localauthority/x509/x509_taint_test.go @@ -6,7 +6,7 @@ import ( "github.com/gogo/status" localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" - authority_common "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon" + authoritycommon_test "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon/test" "github.com/spiffe/spire/cmd/spire-server/cli/common" "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/x509" "github.com/stretchr/testify/require" @@ -14,14 +14,14 @@ import ( ) func TestX509TaintHelp(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509TaintCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509TaintCommandWithEnv) test.Client.Help() require.Equal(t, x509TaintUsage, test.Stderr.String()) } func TestX509TaintSynopsys(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509TaintCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509TaintCommandWithEnv) require.Equal(t, "Marks the previously active X.509 authority as being tainted", test.Client.Synopsis()) } @@ -41,11 +41,12 @@ func TestX509Taint(t *testing.T) { expectReturnCode: 0, args: []string{"-authorityID", "prepared-id"}, tainted: &localauthorityv1.AuthorityState{ - AuthorityId: "tainted-id", - ExpiresAt: 1001, + AuthorityId: "tainted-id", + ExpiresAt: 1001, + UpstreamAuthoritySubjectKeyId: "some-subject-key-id", }, expectStdoutPretty: "Tainted X.509 authority:\n Authority ID: tainted-id\n Expires at: 1970-01-01 00:16:41 +0000 UTC\n", - expectStdoutJSON: `{"tainted_authority":{"authority_id":"tainted-id","expires_at":"1001"}}`, + expectStdoutJSON: `{"tainted_authority":{"authority_id":"tainted-id","expires_at":"1001","upstream_authority_subject_key_id":"some-subject-key-id"}}`, }, { name: "no authority id", @@ -66,9 +67,9 @@ func TestX509Taint(t *testing.T) { expectStderr: "Error: could not taint X.509 authority: rpc error: code = Internal desc = internal server error\n", }, } { - for _, format := range authority_common.AvailableFormats { + for _, format := range authoritycommon_test.AvailableFormats { t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) { - test := authority_common.SetupTest(t, x509.NewX509TaintCommandWithEnv) + test := authoritycommon_test.SetupTest(t, x509.NewX509TaintCommandWithEnv) test.Server.TaintedX509 = tt.tainted test.Server.Err = tt.serverErr args := tt.args @@ -76,7 +77,7 @@ func TestX509Taint(t *testing.T) { returnCode := test.Client.Run(append(test.Args, args...)) - authority_common.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) + authoritycommon_test.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) require.Equal(t, tt.expectStderr, test.Stderr.String()) require.Equal(t, tt.expectReturnCode, returnCode) }) diff --git a/cmd/spire-server/cli/upstreamauthority/revoke.go b/cmd/spire-server/cli/upstreamauthority/revoke.go new file mode 100644 index 0000000000..a1389e5bce --- /dev/null +++ b/cmd/spire-server/cli/upstreamauthority/revoke.go @@ -0,0 +1,86 @@ +package upstreamauthority + +import ( + "context" + "errors" + "flag" + "fmt" + + "github.com/mitchellh/cli" + localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" + "github.com/spiffe/spire/cmd/spire-server/util" + commoncli "github.com/spiffe/spire/pkg/common/cli" + "github.com/spiffe/spire/pkg/common/cliprinter" +) + +// NewRevokeCommand creates a new "upstreamauthority revoke" subcommand for "upstreamauthority" command. +func NewRevokeCommand() cli.Command { + return newRevokeCommand(commoncli.DefaultEnv) +} + +// NewRevokeCommandWithEnv creates a new "upstreamauthority revoke" subcommand for "upstreamauthority" command +// using the environment specified +func NewRevokeCommandWithEnv(env *commoncli.Env) cli.Command { + return util.AdaptCommand(env, &upstreamauthorityRevokeCommand{env: env}) +} + +func newRevokeCommand(env *commoncli.Env) cli.Command { + return util.AdaptCommand(env, &upstreamauthorityRevokeCommand{env: env}) +} + +type upstreamauthorityRevokeCommand struct { + subjectKeyID string + printer cliprinter.Printer + env *commoncli.Env +} + +func (c *upstreamauthorityRevokeCommand) Name() string { + return "upstreamauthority revoke" +} + +func (*upstreamauthorityRevokeCommand) Synopsis() string { + return "Revokes the previously active X.509 upstream authority by removing it from the bundle and propagating this update throughout the cluster" +} + +func (c *upstreamauthorityRevokeCommand) AppendFlags(f *flag.FlagSet) { + f.StringVar(&c.subjectKeyID, "subjectKeyID", "", "The X.509 Subject Key Identifier (or SKID) of the authority's CA certificate of the X.509 upstream authority to revoke") + cliprinter.AppendFlagWithCustomPretty(&c.printer, f, c.env, prettyPrintRevoke) +} + +// Run executes all logic associated with a single invocation of the +// `spire-server upstreamauthority revoke` CLI command +func (c *upstreamauthorityRevokeCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient util.ServerClient) error { + if err := c.validate(); err != nil { + return err + } + + client := serverClient.NewLocalAuthorityClient() + resp, err := client.RevokeX509UpstreamAuthority(ctx, &localauthorityv1.RevokeX509UpstreamAuthorityRequest{ + SubjectKeyId: c.subjectKeyID, + }) + if err != nil { + return fmt.Errorf("could not revoke X.509 upstream authority: %w", err) + } + + return c.printer.PrintProto(resp) +} + +func prettyPrintRevoke(env *commoncli.Env, results ...any) error { + r, ok := results[0].(*localauthorityv1.RevokeX509UpstreamAuthorityResponse) + if !ok { + return errors.New("internal error: cli printer; please report this bug") + } + + env.Println("Revoked X.509 upstream authority:") + env.Printf(" Subject Key ID: %s\n", r.UpstreamAuthoritySubjectKeyId) + + return nil +} + +func (c *upstreamauthorityRevokeCommand) validate() error { + if c.subjectKeyID == "" { + return errors.New("the Subject Key ID of the X.509 upstream authority is required") + } + + return nil +} diff --git a/cmd/spire-server/cli/upstreamauthority/revoke_posix_test.go b/cmd/spire-server/cli/upstreamauthority/revoke_posix_test.go new file mode 100644 index 0000000000..e98f4dc476 --- /dev/null +++ b/cmd/spire-server/cli/upstreamauthority/revoke_posix_test.go @@ -0,0 +1,14 @@ +//go:build !windows + +package upstreamauthority_test + +var ( + revokeUsage = `Usage of upstreamauthority revoke: + -output value + Desired output format (pretty, json); default: pretty. + -socketPath string + Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock") + -subjectKeyID string + The X.509 Subject Key Identifier (or SKID) of the authority's CA certificate of the X.509 upstream authority to revoke +` +) diff --git a/cmd/spire-server/cli/upstreamauthority/revoke_test.go b/cmd/spire-server/cli/upstreamauthority/revoke_test.go new file mode 100644 index 0000000000..8692590816 --- /dev/null +++ b/cmd/spire-server/cli/upstreamauthority/revoke_test.go @@ -0,0 +1,81 @@ +package upstreamauthority_test + +import ( + "fmt" + "testing" + + "github.com/gogo/status" + authority_common_test "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon/test" + "github.com/spiffe/spire/cmd/spire-server/cli/common" + "github.com/spiffe/spire/cmd/spire-server/cli/upstreamauthority" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" +) + +func TestRevokeHelp(t *testing.T) { + test := authority_common_test.SetupTest(t, upstreamauthority.NewRevokeCommandWithEnv) + + test.Client.Help() + require.Equal(t, revokeUsage, test.Stderr.String()) +} + +func TestRevokeSynopsys(t *testing.T) { + test := authority_common_test.SetupTest(t, upstreamauthority.NewRevokeCommandWithEnv) + require.Equal(t, "Revokes the previously active X.509 upstream authority by removing it from the bundle and propagating this update throughout the cluster", test.Client.Synopsis()) +} + +func TestRevoke(t *testing.T) { + for _, tt := range []struct { + name string + args []string + expectReturnCode int + expectStdoutPretty string + expectStdoutJSON string + expectStderr string + serverErr error + upstreamAuthoritySubjectKeyId string + }{ + { + name: "success", + expectReturnCode: 0, + args: []string{"-subjectKeyID", "subject-key-id"}, + upstreamAuthoritySubjectKeyId: "subject-key-id", + expectStdoutPretty: "Revoked X.509 upstream authority:\n Subject Key ID: subject-key-id\n", + expectStdoutJSON: `{"upstream_authority_subject_key_id":"subject-key-id"}`, + }, + { + name: "no subject key id", + expectReturnCode: 1, + expectStderr: "Error: the Subject Key ID of the X.509 upstream authority is required\n", + }, + { + name: "wrong UDS path", + args: []string{common.AddrArg, common.AddrValue}, + expectReturnCode: 1, + expectStderr: common.AddrError, + }, + { + name: "server error", + args: []string{"-subjectKeyID", "some-id"}, + serverErr: status.Error(codes.Internal, "internal server error"), + expectReturnCode: 1, + expectStderr: "Error: could not revoke X.509 upstream authority: rpc error: code = Internal desc = internal server error\n", + }, + } { + for _, format := range authority_common_test.AvailableFormats { + t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) { + test := authority_common_test.SetupTest(t, upstreamauthority.NewRevokeCommandWithEnv) + test.Server.RevokedUpstreamAuthoritySubjectKeyId = tt.upstreamAuthoritySubjectKeyId + test.Server.Err = tt.serverErr + args := tt.args + args = append(args, "-output", format) + + returnCode := test.Client.Run(append(test.Args, args...)) + + authority_common_test.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) + require.Equal(t, tt.expectStderr, test.Stderr.String()) + require.Equal(t, tt.expectReturnCode, returnCode) + }) + } + } +} diff --git a/cmd/spire-server/cli/upstreamauthority/revoke_windows_test.go b/cmd/spire-server/cli/upstreamauthority/revoke_windows_test.go new file mode 100644 index 0000000000..51e079ec01 --- /dev/null +++ b/cmd/spire-server/cli/upstreamauthority/revoke_windows_test.go @@ -0,0 +1,14 @@ +//go:build windows + +package upstreamauthority_test + +var ( + revokeUsage = `Usage of upstreamauthority revoke: + -namedPipeName string + Pipe name of the SPIRE Server API named pipe (default "\\spire-server\\private\\api") + -output value + Desired output format (pretty, json); default: pretty. + -subjectKeyID string + The X.509 Subject Key Identifier (or SKID) of the authority's CA certificate of the X.509 upstream authority to revoke +` +) diff --git a/cmd/spire-server/cli/upstreamauthority/taint.go b/cmd/spire-server/cli/upstreamauthority/taint.go new file mode 100644 index 0000000000..5c5dfbda4a --- /dev/null +++ b/cmd/spire-server/cli/upstreamauthority/taint.go @@ -0,0 +1,85 @@ +package upstreamauthority + +import ( + "context" + "errors" + "flag" + "fmt" + + "github.com/mitchellh/cli" + localauthorityv1 "github.com/spiffe/spire-api-sdk/proto/spire/api/server/localauthority/v1" + "github.com/spiffe/spire/cmd/spire-server/util" + commoncli "github.com/spiffe/spire/pkg/common/cli" + "github.com/spiffe/spire/pkg/common/cliprinter" +) + +// NewTaintCommand creates a new "upstreamauthority taint" subcommand for "upstreamauthority" command. +func NewTaintCommand() cli.Command { + return newTaintCommand(commoncli.DefaultEnv) +} + +// NewUpstreamauthorityTaintCommandWithEnv creates a new "upstreamauthority taint" subcommand for "upstreamauthority" command +// using the environment specified +func NewTaintCommandWithEnv(env *commoncli.Env) cli.Command { + return util.AdaptCommand(env, &upstreamauthorityTaintCommand{env: env}) +} + +func newTaintCommand(env *commoncli.Env) cli.Command { + return util.AdaptCommand(env, &upstreamauthorityTaintCommand{env: env}) +} + +type upstreamauthorityTaintCommand struct { + subjectKeyID string + printer cliprinter.Printer + env *commoncli.Env +} + +func (c *upstreamauthorityTaintCommand) Name() string { + return "upstreamauthority taint" +} + +func (*upstreamauthorityTaintCommand) Synopsis() string { + return "Marks the provided X.509 upstream authority as being tainted" +} + +func (c *upstreamauthorityTaintCommand) AppendFlags(f *flag.FlagSet) { + f.StringVar(&c.subjectKeyID, "subjectKeyID", "", "The X.509 Subject Key Identifier (or SKID) of the authority's CA certificate of the upstream X.509 authority to taint") + cliprinter.AppendFlagWithCustomPretty(&c.printer, f, c.env, prettyPrintTaint) +} + +// Run executes all logic associated with a single invocation of the +// `spire-server upstreamauthority taint` CLI command +func (c *upstreamauthorityTaintCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient util.ServerClient) error { + if err := c.validate(); err != nil { + return err + } + + client := serverClient.NewLocalAuthorityClient() + resp, err := client.TaintX509UpstreamAuthority(ctx, &localauthorityv1.TaintX509UpstreamAuthorityRequest{ + SubjectKeyId: c.subjectKeyID, + }) + if err != nil { + return fmt.Errorf("could not taint X.509 upstream authority: %w", err) + } + + return c.printer.PrintProto(resp) +} + +func prettyPrintTaint(env *commoncli.Env, results ...any) error { + r, ok := results[0].(*localauthorityv1.TaintX509UpstreamAuthorityResponse) + if !ok { + return errors.New("internal error: cli printer; please report this bug") + } + + env.Println("Tainted X.509 upstream authority:") + env.Printf(" Subject Key ID: %s\n", r.UpstreamAuthoritySubjectKeyId) + return nil +} + +func (c *upstreamauthorityTaintCommand) validate() error { + if c.subjectKeyID == "" { + return errors.New("the Subject Key ID of the X.509 upstream authority is required") + } + + return nil +} diff --git a/cmd/spire-server/cli/upstreamauthority/taint_posix_test.go b/cmd/spire-server/cli/upstreamauthority/taint_posix_test.go new file mode 100644 index 0000000000..73aaa858fc --- /dev/null +++ b/cmd/spire-server/cli/upstreamauthority/taint_posix_test.go @@ -0,0 +1,14 @@ +//go:build !windows + +package upstreamauthority_test + +var ( + taintUsage = `Usage of upstreamauthority taint: + -output value + Desired output format (pretty, json); default: pretty. + -socketPath string + Path to the SPIRE Server API socket (default "/tmp/spire-server/private/api.sock") + -subjectKeyID string + The X.509 Subject Key Identifier (or SKID) of the authority's CA certificate of the upstream X.509 authority to taint +` +) diff --git a/cmd/spire-server/cli/upstreamauthority/taint_test.go b/cmd/spire-server/cli/upstreamauthority/taint_test.go new file mode 100644 index 0000000000..55337065ca --- /dev/null +++ b/cmd/spire-server/cli/upstreamauthority/taint_test.go @@ -0,0 +1,81 @@ +package upstreamauthority_test + +import ( + "fmt" + "testing" + + "github.com/gogo/status" + authority_common_test "github.com/spiffe/spire/cmd/spire-server/cli/authoritycommon/test" + "github.com/spiffe/spire/cmd/spire-server/cli/common" + "github.com/spiffe/spire/cmd/spire-server/cli/upstreamauthority" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" +) + +func TestTaintHelp(t *testing.T) { + test := authority_common_test.SetupTest(t, upstreamauthority.NewTaintCommandWithEnv) + + test.Client.Help() + require.Equal(t, taintUsage, test.Stderr.String()) +} + +func TestTaintSynopsys(t *testing.T) { + test := authority_common_test.SetupTest(t, upstreamauthority.NewTaintCommandWithEnv) + require.Equal(t, "Marks the provided X.509 upstream authority as being tainted", test.Client.Synopsis()) +} + +func TestTaint(t *testing.T) { + for _, tt := range []struct { + name string + args []string + expectReturnCode int + expectStdoutPretty string + expectStdoutJSON string + expectStderr string + serverErr error + upstreamAuthoritySubjectKeyId string + }{ + { + name: "success", + expectReturnCode: 0, + args: []string{"-subjectKeyID", "subject-key-id"}, + expectStdoutPretty: "Tainted X.509 upstream authority:\n Subject Key ID: subject-key-id\n", + expectStdoutJSON: `{"upstream_authority_subject_key_id":"subject-key-id"}`, + upstreamAuthoritySubjectKeyId: "subject-key-id", + }, + { + name: "no subject key id", + expectReturnCode: 1, + expectStderr: "Error: the Subject Key ID of the X.509 upstream authority is required\n", + }, + { + name: "wrong UDS path", + args: []string{common.AddrArg, common.AddrValue}, + expectReturnCode: 1, + expectStderr: common.AddrError, + }, + { + name: "server error", + args: []string{"-subjectKeyID", "subject-key-id"}, + serverErr: status.Error(codes.Internal, "internal server error"), + expectReturnCode: 1, + expectStderr: "Error: could not taint X.509 upstream authority: rpc error: code = Internal desc = internal server error\n", + }, + } { + for _, format := range authority_common_test.AvailableFormats { + t.Run(fmt.Sprintf("%s using %s format", tt.name, format), func(t *testing.T) { + test := authority_common_test.SetupTest(t, upstreamauthority.NewTaintCommandWithEnv) + test.Server.TaintedUpstreamAuthoritySubjectKeyId = tt.upstreamAuthoritySubjectKeyId + test.Server.Err = tt.serverErr + args := tt.args + args = append(args, "-output", format) + + returnCode := test.Client.Run(append(test.Args, args...)) + + authority_common_test.RequireOutputBasedOnFormat(t, format, test.Stdout.String(), tt.expectStdoutPretty, tt.expectStdoutJSON) + require.Equal(t, tt.expectStderr, test.Stderr.String()) + require.Equal(t, tt.expectReturnCode, returnCode) + }) + } + } +} diff --git a/cmd/spire-server/cli/upstreamauthority/taint_windows_test.go b/cmd/spire-server/cli/upstreamauthority/taint_windows_test.go new file mode 100644 index 0000000000..47886764ce --- /dev/null +++ b/cmd/spire-server/cli/upstreamauthority/taint_windows_test.go @@ -0,0 +1,14 @@ +//go:build windows + +package upstreamauthority_test + +var ( + taintUsage = `Usage of upstreamauthority taint: + -namedPipeName string + Pipe name of the SPIRE Server API named pipe (default "\\spire-server\\private\\api") + -output value + Desired output format (pretty, json); default: pretty. + -subjectKeyID string + The X.509 Subject Key Identifier (or SKID) of the authority's CA certificate of the upstream X.509 authority to taint +` +) diff --git a/go.mod b/go.mod index 466086793e..9ee6bcf09c 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/sigstore/sigstore v1.8.9 github.com/sirupsen/logrus v1.9.3 github.com/spiffe/go-spiffe/v2 v2.3.0 - github.com/spiffe/spire-api-sdk v1.2.5-0.20240807182354-18e423ce2c1c + github.com/spiffe/spire-api-sdk v1.2.5-0.20240916165922-16526993814a github.com/spiffe/spire-plugin-sdk v1.4.4-0.20240701180828-594312f4444d github.com/stretchr/testify v1.9.0 github.com/uber-go/tally/v4 v4.1.16 diff --git a/go.sum b/go.sum index a6f18a553f..2a2d7fb7c2 100644 --- a/go.sum +++ b/go.sum @@ -1391,8 +1391,8 @@ github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+ github.com/spiffe/go-spiffe/v2 v2.1.6/go.mod h1:eVDqm9xFvyqao6C+eQensb9ZPkyNEeaUbqbBpOhBnNk= github.com/spiffe/go-spiffe/v2 v2.3.0 h1:g2jYNb/PDMB8I7mBGL2Zuq/Ur6hUhoroxGQFyD6tTj8= github.com/spiffe/go-spiffe/v2 v2.3.0/go.mod h1:Oxsaio7DBgSNqhAO9i/9tLClaVlfRok7zvJnTV8ZyIY= -github.com/spiffe/spire-api-sdk v1.2.5-0.20240807182354-18e423ce2c1c h1:lK/B2paDUiqbngUGsLxDBmNX/BsG2yKxS8W/iGT+x2c= -github.com/spiffe/spire-api-sdk v1.2.5-0.20240807182354-18e423ce2c1c/go.mod h1:4uuhFlN6KBWjACRP3xXwrOTNnvaLp1zJs8Lribtr4fI= +github.com/spiffe/spire-api-sdk v1.2.5-0.20240916165922-16526993814a h1:z4A5TA8JKmXQirhOfSv45mjo1DEtmpWH/VJW+uidGQA= +github.com/spiffe/spire-api-sdk v1.2.5-0.20240916165922-16526993814a/go.mod h1:4uuhFlN6KBWjACRP3xXwrOTNnvaLp1zJs8Lribtr4fI= github.com/spiffe/spire-plugin-sdk v1.4.4-0.20240701180828-594312f4444d h1:Upcyq8u1aWFHTQSEskwxBE2PehobpY+M21LXXDS/mPw= github.com/spiffe/spire-plugin-sdk v1.4.4-0.20240701180828-594312f4444d/go.mod h1:GA6o2PVLwyJdevT6KKt5ZXCY/ziAPna13y/seGk49Ik= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/pkg/server/api/localauthority/v1/service.go b/pkg/server/api/localauthority/v1/service.go index e2d66302c1..0c69411f2f 100644 --- a/pkg/server/api/localauthority/v1/service.go +++ b/pkg/server/api/localauthority/v1/service.go @@ -285,8 +285,9 @@ func (s *Service) PrepareX509Authority(ctx context.Context, _ *localauthorityv1. return &localauthorityv1.PrepareX509AuthorityResponse{ PreparedAuthority: &localauthorityv1.AuthorityState{ - AuthorityId: slot.AuthorityID(), - ExpiresAt: slot.NotAfter().Unix(), + AuthorityId: slot.AuthorityID(), + ExpiresAt: slot.NotAfter().Unix(), + UpstreamAuthoritySubjectKeyId: slot.UpstreamAuthorityID(), }, }, nil } @@ -319,8 +320,9 @@ func (s *Service) ActivateX509Authority(ctx context.Context, req *localauthority current := s.ca.GetCurrentX509CASlot() state := &localauthorityv1.AuthorityState{ - AuthorityId: current.AuthorityID(), - ExpiresAt: current.NotAfter().Unix(), + AuthorityId: current.AuthorityID(), + ExpiresAt: current.NotAfter().Unix(), + UpstreamAuthoritySubjectKeyId: current.UpstreamAuthorityID(), } rpccontext.AuditRPC(ctx) @@ -365,7 +367,9 @@ func (s *Service) TaintX509Authority(ctx context.Context, req *localauthorityv1. } state := &localauthorityv1.AuthorityState{ - AuthorityId: nextSlot.AuthorityID(), + AuthorityId: nextSlot.AuthorityID(), + ExpiresAt: nextSlot.NotAfter().Unix(), + UpstreamAuthoritySubjectKeyId: nextSlot.UpstreamAuthorityID(), } if err := s.ca.NotifyTaintedX509Authority(ctx, nextSlot.AuthorityID()); err != nil { @@ -406,7 +410,9 @@ func (s *Service) TaintX509UpstreamAuthority(ctx context.Context, req *localauth rpccontext.AuditRPC(ctx) log.Info("X.509 upstream authority tainted successfully") - return &localauthorityv1.TaintX509UpstreamAuthorityResponse{}, nil + return &localauthorityv1.TaintX509UpstreamAuthorityResponse{ + UpstreamAuthoritySubjectKeyId: subjectKeyIDRequest, + }, nil } func (s *Service) RevokeX509Authority(ctx context.Context, req *localauthorityv1.RevokeX509AuthorityRequest) (*localauthorityv1.RevokeX509AuthorityResponse, error) { @@ -468,7 +474,9 @@ func (s *Service) RevokeX509UpstreamAuthority(ctx context.Context, req *localaut rpccontext.AuditRPC(ctx) log.Info("X.509 upstream authority successfully revoked") - return &localauthorityv1.RevokeX509UpstreamAuthorityResponse{}, nil + return &localauthorityv1.RevokeX509UpstreamAuthorityResponse{ + UpstreamAuthoritySubjectKeyId: subjectKeyIDRequest, + }, nil } // validateLocalAuthorityID validates provided authority ID, and return OLD associated public key @@ -562,7 +570,8 @@ func buildAuditUpstreamLogFields(authorityID string) logrus.Fields { func stateFromSlot(s manager.Slot) *localauthorityv1.AuthorityState { return &localauthorityv1.AuthorityState{ - AuthorityId: s.AuthorityID(), - ExpiresAt: s.NotAfter().Unix(), + AuthorityId: s.AuthorityID(), + ExpiresAt: s.NotAfter().Unix(), + UpstreamAuthoritySubjectKeyId: s.UpstreamAuthorityID(), } } diff --git a/pkg/server/api/localauthority/v1/service_test.go b/pkg/server/api/localauthority/v1/service_test.go index adf8ec12be..f09ba7556d 100644 --- a/pkg/server/api/localauthority/v1/service_test.go +++ b/pkg/server/api/localauthority/v1/service_test.go @@ -1340,6 +1340,7 @@ func TestTaintX509Authority(t *testing.T) { expectResp: &localauthorityv1.TaintX509AuthorityResponse{ TaintedAuthority: &localauthorityv1.AuthorityState{ AuthorityId: nextAuthorityID, + ExpiresAt: notAfterNext.Unix(), }, }, expectLogs: []spiretest.LogEntry{ @@ -1654,7 +1655,9 @@ func TestTaintX509UpstreamAuthority(t *testing.T) { currentSlot: createSlotWithUpstream(journal.Status_ACTIVE, currentIntermediateCA, notAfterCurrent), nextSlot: createSlotWithUpstream(journal.Status_OLD, oldIntermediateCA, notAfterNext), subjectKeyIDToTaint: deactivatedUpstreamAuthorityID, - expectResp: &localauthorityv1.TaintX509UpstreamAuthorityResponse{}, + expectResp: &localauthorityv1.TaintX509UpstreamAuthorityResponse{ + UpstreamAuthoritySubjectKeyId: deactivatedUpstreamAuthorityID, + }, expectLogs: []spiretest.LogEntry{ { Level: logrus.InfoLevel, @@ -2193,7 +2196,9 @@ func TestRevokeX509UpstreamAuthority(t *testing.T) { currentSlot: createSlotWithUpstream(journal.Status_ACTIVE, currentIntermediateCA, notAfterCurrent), nextSlot: createSlotWithUpstream(journal.Status_OLD, oldIntermediateCA, notAfterNext), subjectKeyIDToRevoke: deactivatedUpstreamAuthorityID, - expectResp: &localauthorityv1.RevokeX509UpstreamAuthorityResponse{}, + expectResp: &localauthorityv1.RevokeX509UpstreamAuthorityResponse{ + UpstreamAuthoritySubjectKeyId: deactivatedUpstreamAuthorityID, + }, expectLogs: []spiretest.LogEntry{ { Level: logrus.InfoLevel,