Skip to content

Commit

Permalink
Add localauthority jwt show command to the server (#5412)
Browse files Browse the repository at this point in the history
* Add `localauthority jwt show command` to the server

Signed-off-by: Agustín Martínez Fayó <[email protected]>
  • Loading branch information
amartinezfayo authored Aug 22, 2024
1 parent 399ce63 commit 4f34e43
Show file tree
Hide file tree
Showing 10 changed files with 326 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package localauthority_test
package authoritycommon_test

import (
"bytes"
Expand All @@ -15,26 +15,26 @@ import (
)

var (
availableFormats = []string{"pretty", "json"}
AvailableFormats = []string{"pretty", "json"}
)

type localAuthorityTest struct {
stdin *bytes.Buffer
stdout *bytes.Buffer
stderr *bytes.Buffer
args []string
server *fakeLocalAuthorityServer
client cli.Command
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())
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 {
func SetupTest(t *testing.T, newClient func(*commoncli.Env) cli.Command) *localAuthorityTest {
server := &fakeLocalAuthorityServer{}

addr := spiretest.StartGRPCServer(t, func(s *grpc.Server) {
Expand All @@ -52,12 +52,12 @@ func setupTest(t *testing.T, newClient func(*commoncli.Env) cli.Command) *localA
})

test := &localAuthorityTest{
stdin: stdin,
stdout: stdout,
stderr: stderr,
args: []string{common.AddrArg, common.GetAddr(addr)},
server: server,
client: client,
Stdin: stdin,
Stdout: stdout,
Stderr: stderr,
Args: []string{common.AddrArg, common.GetAddr(addr)},
Server: server,
Client: client,
}

t.Cleanup(func() {
Expand All @@ -70,93 +70,93 @@ func setupTest(t *testing.T, newClient func(*commoncli.Env) cli.Command) *localA
type fakeLocalAuthorityServer struct {
localauthorityv1.UnsafeLocalAuthorityServer

activeJWT,
preparedJWT,
oldJWT,
activeX509,
preparedX509,
oldX509,
taintedX509,
revokedX509,
taintedJWT,
revokedJWT *localauthorityv1.AuthorityState
ActiveJWT,
PreparedJWT,
OldJWT,
ActiveX509,
PreparedX509,
OldX509,
TaintedX509,
RevokedX509,
TaintedJWT,
RevokedJWT *localauthorityv1.AuthorityState

err error
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
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
PreparedAuthority: s.PreparedJWT,
}, s.Err
}

func (s *fakeLocalAuthorityServer) ActivateJWTAuthority(context.Context, *localauthorityv1.ActivateJWTAuthorityRequest) (*localauthorityv1.ActivateJWTAuthorityResponse, error) {
return &localauthorityv1.ActivateJWTAuthorityResponse{
ActivatedAuthority: s.activeJWT,
}, s.err
ActivatedAuthority: s.ActiveJWT,
}, s.Err
}

func (s *fakeLocalAuthorityServer) TaintJWTAuthority(context.Context, *localauthorityv1.TaintJWTAuthorityRequest) (*localauthorityv1.TaintJWTAuthorityResponse, error) {
return &localauthorityv1.TaintJWTAuthorityResponse{
TaintedAuthority: s.taintedJWT,
}, s.err
TaintedAuthority: s.TaintedJWT,
}, s.Err
}

func (s *fakeLocalAuthorityServer) RevokeJWTAuthority(context.Context, *localauthorityv1.RevokeJWTAuthorityRequest) (*localauthorityv1.RevokeJWTAuthorityResponse, error) {
return &localauthorityv1.RevokeJWTAuthorityResponse{
RevokedAuthority: s.revokedJWT,
}, s.err
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
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
PreparedAuthority: s.PreparedX509,
}, s.Err
}

func (s *fakeLocalAuthorityServer) ActivateX509Authority(context.Context, *localauthorityv1.ActivateX509AuthorityRequest) (*localauthorityv1.ActivateX509AuthorityResponse, error) {
return &localauthorityv1.ActivateX509AuthorityResponse{
ActivatedAuthority: s.activeX509,
}, s.err
ActivatedAuthority: s.ActiveX509,
}, s.Err
}

func (s *fakeLocalAuthorityServer) TaintX509Authority(context.Context, *localauthorityv1.TaintX509AuthorityRequest) (*localauthorityv1.TaintX509AuthorityResponse, error) {
return &localauthorityv1.TaintX509AuthorityResponse{
TaintedAuthority: s.taintedX509,
}, s.err
TaintedAuthority: s.TaintedX509,
}, s.Err
}

func (s *fakeLocalAuthorityServer) TaintX509UpstreamAuthority(context.Context, *localauthorityv1.TaintX509UpstreamAuthorityRequest) (*localauthorityv1.TaintX509UpstreamAuthorityResponse, error) {
return &localauthorityv1.TaintX509UpstreamAuthorityResponse{}, s.err
return &localauthorityv1.TaintX509UpstreamAuthorityResponse{}, s.Err
}

func (s *fakeLocalAuthorityServer) RevokeX509Authority(context.Context, *localauthorityv1.RevokeX509AuthorityRequest) (*localauthorityv1.RevokeX509AuthorityResponse, error) {
return &localauthorityv1.RevokeX509AuthorityResponse{
RevokedAuthority: s.revokedX509,
}, s.err
RevokedAuthority: s.RevokedX509,
}, s.Err
}

func (s *fakeLocalAuthorityServer) RevokeX509UpstreamAuthority(context.Context, *localauthorityv1.RevokeX509UpstreamAuthorityRequest) (*localauthorityv1.RevokeX509UpstreamAuthorityResponse, error) {
return &localauthorityv1.RevokeX509UpstreamAuthorityResponse{}, s.err
return &localauthorityv1.RevokeX509UpstreamAuthorityResponse{}, s.Err
}

func requireOutputBasedOnFormat(t *testing.T, format, stdoutString string, expectedStdoutPretty, expectedStdoutJSON string) {
func RequireOutputBasedOnFormat(t *testing.T, format, stdoutString string, expectedStdoutPretty, expectedStdoutJSON string) {
switch format {
case "pretty":
require.Contains(t, stdoutString, expectedStdoutPretty)
Expand Down
8 changes: 6 additions & 2 deletions cmd/spire-server/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import (
"github.com/spiffe/spire/cmd/spire-server/cli/federation"
"github.com/spiffe/spire/cmd/spire-server/cli/healthcheck"
"github.com/spiffe/spire/cmd/spire-server/cli/jwt"
"github.com/spiffe/spire/cmd/spire-server/cli/localauthority"
localauthority_jwt "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/jwt"
localauthority_x509 "github.com/spiffe/spire/cmd/spire-server/cli/localauthority/x509"
"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"
Expand Down Expand Up @@ -163,7 +164,10 @@ func addCommandsEnabledByFFlags(commands map[string]cli.CommandFactory) {

if flagForcedRotationFound {
commands["localauthority x509 show"] = func() (cli.Command, error) {
return localauthority.NewX509ShowCommand(), nil
return localauthority_x509.NewX509ShowCommand(), nil
}
commands["localauthority jwt show"] = func() (cli.Command, error) {
return localauthority_jwt.NewJWTShowCommand(), nil
}
}
}
87 changes: 87 additions & 0 deletions cmd/spire-server/cli/localauthority/jwt/jwt_show.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package jwt

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/util"
commoncli "github.com/spiffe/spire/pkg/common/cli"
"github.com/spiffe/spire/pkg/common/cliprinter"
)

// NewJWTShowCommand creates a new "jwt show" subcommand for "localauthority" command.
func NewJWTShowCommand() cli.Command {
return NewJWTShowCommandWithEnv(commoncli.DefaultEnv)
}

// NewJWTShowCommandWithEnv creates a new "jwt show" subcommand for "localauthority" command
// using the environment specified
func NewJWTShowCommandWithEnv(env *commoncli.Env) cli.Command {
return util.AdaptCommand(env, &jwtShowCommand{env: env})
}

type jwtShowCommand struct {
printer cliprinter.Printer

env *commoncli.Env
}

func (c *jwtShowCommand) Name() string {
return "localauthority jwt show"
}

func (*jwtShowCommand) Synopsis() string {
return "Shows the local JWT authorities"
}

func (c *jwtShowCommand) AppendFlags(f *flag.FlagSet) {
cliprinter.AppendFlagWithCustomPretty(&c.printer, f, c.env, prettyPrintJWTShow)
}

// Run executes all logic associated with a single invocation of the
// `spire-server localauthority jwt show` CLI command
func (c *jwtShowCommand) Run(ctx context.Context, _ *commoncli.Env, serverClient util.ServerClient) error {
client := serverClient.NewLocalAuthorityClient()
resp, err := client.GetJWTAuthorityState(ctx, &localauthorityv1.GetJWTAuthorityStateRequest{})
if err != nil {
return err
}

return c.printer.PrintProto(resp)
}

func prettyPrintJWTShow(env *commoncli.Env, results ...any) error {
r, ok := results[0].(*localauthorityv1.GetJWTAuthorityStateResponse)
if !ok {
return errors.New("internal error: cli printer; please report this bug")
}

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())
} 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())
} 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())
} else {
env.Println(" No old JWT authority found")
}
return nil
}
12 changes: 12 additions & 0 deletions cmd/spire-server/cli/localauthority/jwt/jwt_show_posix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//go:build !windows

package jwt_test

var (
jwtShowUsage = `Usage of localauthority jwt show:
-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")
`
)
Loading

0 comments on commit 4f34e43

Please sign in to comment.