From 86a814e2fadb677526aa5874ef26c75d1118cf58 Mon Sep 17 00:00:00 2001 From: Aaron Turner Date: Fri, 5 Jul 2024 13:31:04 -0700 Subject: [PATCH] Add --server flag for ecs client commands Add support for connecting to a random server Fixes: #937 --- CHANGELOG.md | 1 + cmd/aws-sso/ecs_client_cmd.go | 22 ++++++------ internal/ecs/client/client.go | 32 +++++++++++------ internal/ecs/client/client_test.go | 56 +++++++++++++++++++----------- 4 files changed, 70 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5b9ce7b..6d1ef337 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Add support for HTTP Auth/`AWS_CONTAINER_AUTHORIZATION_TOKEN` env variable #516 * Add support for HTTPS #518 * Add Docker container support #569 + * Replace `--port` with `--server` flag for the `aws-sso ecs [list|load|unload|profile]` commands #937 ## [v1.16.1] - 2024-06-13 diff --git a/cmd/aws-sso/ecs_client_cmd.go b/cmd/aws-sso/ecs_client_cmd.go index 1f004a01..974c16ac 100644 --- a/cmd/aws-sso/ecs_client_cmd.go +++ b/cmd/aws-sso/ecs_client_cmd.go @@ -33,7 +33,7 @@ import ( ) type EcsListCmd struct { - Port int `kong:"help='TCP port of aws-sso ECS Server',env='AWS_SSO_ECS_PORT',default=4144"` // SEE ECS_PORT in ecs_cmd.go + Server string `kong:"help='Endpoint of aws-sso ECS Server',env='AWS_SSO_ECS_SERVER',default='localhost:4144'"` } type EcsLoadCmd struct { @@ -44,17 +44,17 @@ type EcsLoadCmd struct { Profile string `kong:"short='p',help='Name of AWS Profile to assume',predictor='profile',xor='account,role'"` // Other params - Port int `kong:"help='TCP port of aws-sso ECS Server',env='AWS_SSO_ECS_PORT',default=4144"` // SEE ECS_PORT in ecs_cmd.go - Slotted bool `kong:"short='s',help='Load credentials in a unique slot using the ProfileName as the key'"` + Server string `kong:"help='Endpoint of aws-sso ECS Server',env='AWS_SSO_ECS_SERVER',default='localhost:4144'"` + Slotted bool `kong:"short='s',help='Load credentials in a unique slot using the ProfileName as the key'"` } type EcsProfileCmd struct { - Port int `kong:"help='TCP port of aws-sso ECS Server',env='AWS_SSO_ECS_PORT',default=4144"` + Server string `kong:"help='URL endpoint of aws-sso ECS Server',env='AWS_SSO_ECS_SERVER',default='localhost:4144'"` } type EcsUnloadCmd struct { - Port int `kong:"help='TCP port of aws-sso ECS Server',env='AWS_SSO_ECS_PORT',default=4144"` Profile string `kong:"short='p',help='Name of AWS Profile to unload',predictor='profile'"` + Server string `kong:"help='Endpoint of aws-sso ECS Server',env='AWS_SSO_ECS_SERVER',default='localhost:4144'"` } func (cc *EcsLoadCmd) Run(ctx *RunContext) error { @@ -71,7 +71,7 @@ func (cc *EcsLoadCmd) Run(ctx *RunContext) error { } func (cc *EcsProfileCmd) Run(ctx *RunContext) error { - c := newClient(ctx.Cli.Ecs.Profile.Port, ctx) + c := newClient(ctx.Cli.Ecs.Profile.Server, ctx) profile, err := c.GetProfile() if err != nil { @@ -89,14 +89,14 @@ func (cc *EcsProfileCmd) Run(ctx *RunContext) error { } func (cc *EcsUnloadCmd) Run(ctx *RunContext) error { - c := newClient(ctx.Cli.Ecs.Unload.Port, ctx) + c := newClient(ctx.Cli.Ecs.Unload.Server, ctx) return c.Delete(ctx.Cli.Ecs.Unload.Profile) } // Loads our AWS API creds into the ECS Server func ecsLoadCmd(ctx *RunContext, awssso *sso.AWSSSO, accountId int64, role string) error { - c := newClient(ctx.Cli.Ecs.Load.Port, ctx) + c := newClient(ctx.Cli.Ecs.Load.Server, ctx) creds := GetRoleCredentials(ctx, awssso, accountId, role) @@ -123,7 +123,7 @@ func ecsLoadCmd(ctx *RunContext, awssso *sso.AWSSSO, accountId int64, role strin } func (cc *EcsListCmd) Run(ctx *RunContext) error { - c := newClient(ctx.Cli.Ecs.Profile.Port, ctx) + c := newClient(ctx.Cli.Ecs.Profile.Server, ctx) profiles, err := c.ListProfiles() if err != nil { @@ -157,7 +157,7 @@ func listProfiles(profiles []ecs.ListProfilesResponse) error { return err } -func newClient(port int, ctx *RunContext) *client.ECSClient { +func newClient(server string, ctx *RunContext) *client.ECSClient { certChain, err := ctx.Store.GetEcsSslCert() if err != nil { log.Fatalf("Unable to get ECS SSL cert: %s", err) @@ -166,5 +166,5 @@ func newClient(port int, ctx *RunContext) *client.ECSClient { if err != nil { log.Fatalf("Unable to get ECS bearer token: %s", err) } - return client.NewECSClient(port, bearerToken, certChain) + return client.NewECSClient(server, bearerToken, certChain) } diff --git a/internal/ecs/client/client.go b/internal/ecs/client/client.go index fb39b55f..d3d8731c 100644 --- a/internal/ecs/client/client.go +++ b/internal/ecs/client/client.go @@ -27,6 +27,8 @@ import ( "io" "net/http" "net/url" + "strconv" + "strings" "github.com/davecgh/go-spew/spew" "github.com/synfinatic/aws-sso-cli/internal/ecs" @@ -34,7 +36,7 @@ import ( ) type ECSClient struct { - port int + server string authToken string loadUrl string loadSlotUrl string @@ -43,7 +45,7 @@ type ECSClient struct { client *http.Client } -func NewECSClient(port int, authToken string, certChain string) *ECSClient { +func NewECSClient(server, authToken, certChain string) *ECSClient { var client *http.Client = &http.Client{} var proto string = "http" var err error @@ -52,25 +54,35 @@ func NewECSClient(port int, authToken string, certChain string) *ECSClient { proto = "https" client, err = NewHTTPClient(certChain) if err != nil { - panic(fmt.Sprintf("unable to load SSL certificate: %s", err)) + panic(fmt.Sprintf("unable to load SSL certificate: %s", err.Error())) } } if authToken == "" { - log.Warnf("No auth token provided, ECS server communication will be unauthenticated") + log.Warnf("no auth token provided, ECS server communication will be unauthenticated") } if certChain == "" { - log.Warnf("No SSL cert provided, ECS server communication will be unencrypted") + log.Warnf("no SSL cert provided, ECS server communication will be unencrypted") + } + + hostPort := strings.Split(server, ":") + if len(hostPort) != 2 || hostPort[0] == "" || hostPort[1] == "" { + panic(fmt.Sprintf("invalid --server address: %s", server)) + } + + port, err := strconv.Atoi(hostPort[1]) + if err != nil || port < 1 || port > 65535 { + panic(fmt.Sprintf("invalid --server port: %s", hostPort[1])) } return &ECSClient{ + server: server, client: client, - port: port, authToken: authToken, - loadUrl: fmt.Sprintf("%s://localhost:%d/", proto, port), - loadSlotUrl: fmt.Sprintf("%s://localhost:%d%s", proto, port, ecs.SLOT_ROUTE), - profileUrl: fmt.Sprintf("%s://localhost:%d%s", proto, port, ecs.PROFILE_ROUTE), - listUrl: fmt.Sprintf("%s://localhost:%d%s", proto, port, ecs.SLOT_ROUTE), + loadUrl: fmt.Sprintf("%s://%s/", proto, server), + loadSlotUrl: fmt.Sprintf("%s://%s%s", proto, server, ecs.SLOT_ROUTE), + profileUrl: fmt.Sprintf("%s://%s%s", proto, server, ecs.PROFILE_ROUTE), + listUrl: fmt.Sprintf("%s://%s%s", proto, server, ecs.SLOT_ROUTE), } } diff --git a/internal/ecs/client/client_test.go b/internal/ecs/client/client_test.go index afefde1e..73ee3d91 100644 --- a/internal/ecs/client/client_test.go +++ b/internal/ecs/client/client_test.go @@ -31,7 +31,7 @@ import ( ) func TestCheckDoResponse(t *testing.T) { - t.Parallel() + // t.Parallel() resp := http.Response{ StatusCode: http.StatusOK, @@ -45,11 +45,11 @@ func TestCheckDoResponse(t *testing.T) { } func TestNewECSClient(t *testing.T) { - t.Parallel() + // t.Parallel() - c := NewECSClient(4144, "token", "") + c := NewECSClient("localhost:4144", "token", "") assert.NotNil(t, c) - assert.Equal(t, 4144, c.port) + assert.Equal(t, "localhost:4144", c.server) assert.Equal(t, "token", c.authToken) assert.NotEmpty(t, c.loadUrl) assert.NotEmpty(t, c.loadSlotUrl) @@ -58,16 +58,32 @@ func TestNewECSClient(t *testing.T) { certChain, err := os.ReadFile("../server/testdata/localhost.crt") assert.NoError(t, err) - c = NewECSClient(4144, "token", string(certChain)) + c = NewECSClient("localhost:4144", "token", string(certChain)) assert.NotNil(t, c) - assert.Panics(t, func() { NewECSClient(4144, "token", "foobar") }) +} + +func TestNewEcsClientFail(t *testing.T) { + // t.Parallel() + // assert.Panics(t, func() { NewECSClient("localhost:4144", "token", "foobar") }) + + assert.Panics(t, func() { NewECSClient("localhost", "token", "") }) + + assert.Panics(t, func() { NewECSClient("localhost:", "token", "") }) + + assert.Panics(t, func() { NewECSClient(":4144", "token", "") }) + + assert.Panics(t, func() { NewECSClient("localhost:foo", "token", "") }) + + assert.Panics(t, func() { NewECSClient("localhost:0", "token", "") }) + + assert.Panics(t, func() { NewECSClient("localhost:65536", "token", "") }) } func TestECSClientLoadUrl(t *testing.T) { - t.Parallel() + // t.Parallel() - c := NewECSClient(4144, "token", "") + c := NewECSClient("localhost:4144", "token", "") assert.NotNil(t, c) assert.Equal(t, "http://localhost:4144/", c.LoadUrl("")) @@ -76,17 +92,17 @@ func TestECSClientLoadUrl(t *testing.T) { } func TestECSClientProfileUrl(t *testing.T) { - t.Parallel() + // t.Parallel() - c := NewECSClient(4144, "token", "") + c := NewECSClient("localhost:4144", "token", "") assert.NotNil(t, c) assert.Equal(t, "http://localhost:4144/profile", c.ProfileUrl()) } func TestECSClientListUrl(t *testing.T) { - t.Parallel() + // t.Parallel() - c := NewECSClient(4144, "token", "") + c := NewECSClient("localhost:4144", "token", "") assert.NotNil(t, c) assert.Equal(t, "http://localhost:4144/slot", c.ListUrl()) } @@ -94,7 +110,7 @@ func TestECSClientListUrl(t *testing.T) { func TestECSClientNewRequest(t *testing.T) { t.Parallel() - c := NewECSClient(4144, "Bearer token", "") + c := NewECSClient("localhost:4144", "Bearer token", "") assert.NotNil(t, c) req, err := c.newRequest(http.MethodGet, "http://localhost:4144", nil) @@ -104,7 +120,7 @@ func TestECSClientNewRequest(t *testing.T) { assert.Equal(t, "Bearer token", req.Header.Get("Authorization")) assert.Equal(t, "application/json; charset=utf-8", req.Header.Get("Content-Type")) - c = NewECSClient(4144, "", "") + c = NewECSClient("localhost:4144", "", "") req, err = c.newRequest(http.MethodGet, "http://localhost:4144", nil) assert.NoError(t, err) assert.NotNil(t, req) @@ -130,7 +146,7 @@ func TestECSClientSubmitCredsPass(t *testing.T) { ) defer ts.Close() - c := NewECSClient(4144, "", "") + c := NewECSClient("localhost:4144", "", "") c.loadUrl = ts.URL c.loadSlotUrl = ts.URL assert.NotNil(t, c) @@ -175,7 +191,7 @@ func TestECSClientSubmitCredsFail(t *testing.T) { ) defer ts.Close() - c := NewECSClient(4144, "token", "") + c := NewECSClient("localhost:4144", "token", "") c.loadUrl = ts.URL assert.NotNil(t, c) @@ -213,7 +229,7 @@ func TestECSGetProfile(t *testing.T) { ) defer ts.Close() - c := NewECSClient(4144, "token", "") + c := NewECSClient("localhost:4144", "token", "") assert.NotNil(t, c) c.profileUrl = ts.URL @@ -252,7 +268,7 @@ func TestECSAuthFailures(t *testing.T) { ) defer ts.Close() - c := NewECSClient(4144, "token", "") + c := NewECSClient("localhost:4144", "token", "") assert.NotNil(t, c) c.profileUrl = ts.URL @@ -293,7 +309,7 @@ func TestECSListProfiles(t *testing.T) { ) defer ts.Close() - c := NewECSClient(4144, "token", "") + c := NewECSClient("localhost:4144", "token", "") c.listUrl = ts.URL assert.NotNil(t, c) @@ -332,7 +348,7 @@ func TestECSDelete(t *testing.T) { ) defer ts.Close() - c := NewECSClient(4144, "token", "") + c := NewECSClient("localhost:4144", "token", "") c.loadUrl = ts.URL c.loadSlotUrl = ts.URL assert.NotNil(t, c)