Skip to content

Commit 2d841b3

Browse files
committed
broken ecs server stuff. Change the REST API
1 parent 193aa7d commit 2d841b3

File tree

8 files changed

+441
-206
lines changed

8 files changed

+441
-206
lines changed

CHANGELOG.md

+13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# AWS SSO CLI Changelog
22

3+
## [v1.13.1] - 2023-08-23
4+
5+
### Bugs
6+
7+
* Fix ECS Server to be more RESTful and document the API
8+
* Fix bugs in ECS Server
9+
10+
### Changes
11+
12+
* Default profile `AWS_CONTAINER_CREDENTIALS_FULL_URI` is now `http://localhost:4144/`
13+
* Slotted profile `AWS_CONTAINER_CREDENTIALS_FULL_URI` is now `http://localhost:4144/slot/<profile>`
14+
* `aws-sso ecs list` and `aws-sso ecs profile` now return the same output format
15+
316
## [v1.13.0] - 2023-08-21
417

518
### Bugs

cmd/aws-sso/ecs_cmd.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,15 @@ func (cc *EcsProfileCmd) Run(ctx *RunContext) error {
9595
if err != nil {
9696
return err
9797
}
98-
fmt.Printf("%s\n", profile)
99-
return nil
98+
99+
if profile.ProfileName == "" {
100+
return fmt.Errorf("No profile loaded in ECS Server.")
101+
}
102+
103+
profiles := []server.ListProfilesResponse{
104+
profile,
105+
}
106+
return listProfiles(profiles)
100107
}
101108

102109
func (cc *EcsUnloadCmd) Run(ctx *RunContext) error {
@@ -146,6 +153,10 @@ func (cc *EcsListCmd) Run(ctx *RunContext) error {
146153
return nil
147154
}
148155

156+
return listProfiles(profiles)
157+
}
158+
159+
func listProfiles(profiles []server.ListProfilesResponse) error {
149160
// sort our results
150161
sort.Slice(profiles, func(i, j int) bool {
151162
return strings.Compare(profiles[i].ProfileName, profiles[j].ProfileName) < 0
@@ -157,7 +168,7 @@ func (cc *EcsListCmd) Run(ctx *RunContext) error {
157168
}
158169

159170
fields := []string{"ProfileName", "AccountIdPad", "RoleName", "Expires"}
160-
err = gotable.GenerateTable(tr, fields)
171+
err := gotable.GenerateTable(tr, fields)
161172
if err != nil {
162173
fmt.Printf("\n")
163174
}

docs/ecs-server.md

+20-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
* [Errors](#errors)
1313
* [Authentication](#authentication)
1414
* [HTTPS Transport](#https-transport)
15+
* [REST API](#rest-api)
1516

1617
## Overview
1718

@@ -41,7 +42,7 @@ port via the `--port` flag or setting the `AWS_SSO_ECS_PORT` environment variabl
4142

4243
AWS clients and `aws-sso` should use:
4344

44-
`AWS_CONTAINER_CREDENTIALS_FULL_URI=http://localhost:4144/creds`
45+
`AWS_CONTAINER_CREDENTIALS_FULL_URI=http://localhost:4144/`
4546

4647
### AWS\_CONTAINER\_CREDENTIALS\_RELATIVE\_URI
4748

@@ -111,7 +112,7 @@ To see a list of profiles loaded in named slots use `aws-sso ecs list`.
111112

112113
Accessing the individual credentials is done via the `profile` query parameter:
113114

114-
`export AWS_CONTAINER_CREDENTIALS_FULL_URI=http://localhost:4144/creds?profile=ExampleProfileName`
115+
`export AWS_CONTAINER_CREDENTIALS_FULL_URI=http://localhost:4144/slot/ExampleProfileName`
115116

116117
Would utilize the `ExampleProfileName` role. Note that the `profile` parameter
117118
value must be URL Escaped.
@@ -144,3 +145,20 @@ this feature if you want it!
144145

145146
Support for using [HTTPS](https://github.com/synfinatic/aws-sso-cli/issues/518)
146147
is TBD. Please vote for this feature if you want it!
148+
149+
## REST API
150+
151+
### Default credentials
152+
153+
* `GET /` -- Fetch default credentials
154+
* `GET /profile` -- Fetch profile name of the default credentials
155+
* `PUT /` -- Upload default credentials
156+
* `DELETE /` -- Delete default credentials
157+
158+
### Slotted credentials
159+
160+
* `GET /creds` -- Fetch list of default credentials
161+
* `GET /creds/<profile>` -- Fetch credentials of the named profile
162+
* `PUT /creds/<profile>` -- Upload credentials of the named profile
163+
* `DELETE /creds/<profile>` -- Delete credentials of the named profile
164+
* `DELETE /creds` -- Delete all named credentials

internal/server/client.go

+56-44
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,19 @@ func NewClient(port int) *Client {
4444

4545
func (c *Client) LoadUrl(profile string) string {
4646
if profile == "" {
47-
return fmt.Sprintf("http://localhost:%d%s", c.port, CREDS_ROUTE)
47+
return fmt.Sprintf("http://localhost:%d/", c.port)
4848
}
49-
return fmt.Sprintf("http://localhost:%d%s?profile=%s", c.port, CREDS_ROUTE, url.QueryEscape(profile))
49+
return fmt.Sprintf("http://localhost:%d%s/%s", c.port, SLOT_ROUTE, url.QueryEscape(profile))
5050
}
5151

5252
func (c *Client) ProfileUrl() string {
5353
return fmt.Sprintf("http://localhost:%d%s", c.port, PROFILE_ROUTE)
5454
}
5555

56+
func (c *Client) ListUrl() string {
57+
return fmt.Sprintf("http://localhost:%d%s", c.port, SLOT_ROUTE)
58+
}
59+
5660
type ClientRequest struct {
5761
Creds *storage.RoleCredentials `json:"Creds"`
5862
ProfileName string `json:"ProfileName"`
@@ -78,48 +82,41 @@ func (c *Client) SubmitCreds(creds *storage.RoleCredentials, profile string, slo
7882
}
7983
req.Header.Set("Content-Type", CHARSET_JSON)
8084
client := &http.Client{}
81-
_, err = client.Do(req)
82-
return err
83-
}
84-
85-
func (c *Client) GetProfile() (string, error) {
86-
req, err := http.NewRequest(http.MethodGet, c.ProfileUrl(), bytes.NewBuffer([]byte("")))
85+
resp, err := client.Do(req)
8786
if err != nil {
88-
return "", err
87+
return err
8988
}
89+
return CheckDoResponse(resp)
90+
}
91+
92+
type ListProfilesResponse struct {
93+
ProfileName string `json:"ProfileName" header:"ProfileName"`
94+
AccountIdPad string `json:"AccountId" header:"AccountIdPad"`
95+
RoleName string `json:"RoleName" header:"RoleName"`
96+
Expiration int64 `json:"Expiration" header:"Expiration"`
97+
Expires string `json:"Expires" header:"Expires"`
98+
}
9099

100+
func (c *Client) GetProfile() (ListProfilesResponse, error) {
101+
lpr := ListProfilesResponse{}
91102
client := &http.Client{}
92-
req.Header.Set("Content-Type", CHARSET_JSON)
103+
resp, err := client.Get(c.ProfileUrl())
93104
if err != nil {
94-
return "", err
95-
}
96-
resp, err := client.Do(req)
97-
if err != nil {
98-
return "", err
105+
return lpr, err
99106
}
100107
defer resp.Body.Close()
101108

102109
body, err := io.ReadAll(resp.Body)
103110
if err != nil {
104-
return "", err
111+
return lpr, err
105112
}
106113

107-
m := map[string]string{}
108-
err = json.Unmarshal(body, &m)
109-
if err != nil {
110-
return "", err
114+
if err = json.Unmarshal(body, &lpr); err != nil {
115+
return lpr, err
111116
}
112-
log.Debugf("resp: %s", spew.Sdump(m))
113-
114-
return m["profile"], nil
115-
}
117+
log.Debugf("resp: %s", spew.Sdump(lpr))
116118

117-
type ListProfilesResponse struct {
118-
ProfileName string `json:"ProfileName" header:"ProfileName"`
119-
AccountIdPad string `json:"AccountId" header:"AccountIdPad"`
120-
RoleName string `json:"RoleName" header:"RoleName"`
121-
Expiration int64 `json:"Expiration" header:"Expiration"`
122-
Expires string `json:"Expires" header:"Expires"`
119+
return lpr, nil
123120
}
124121

125122
// GetHeader is required for GenerateTable()
@@ -131,17 +128,8 @@ func (lpr ListProfilesResponse) GetHeader(fieldName string) (string, error) {
131128
// ListProfiles returns a list of profiles that are loaded into slots
132129
func (c *Client) ListProfiles() ([]ListProfilesResponse, error) {
133130
lpr := []ListProfilesResponse{}
134-
req, err := http.NewRequest(http.MethodGet, c.LoadUrl(""), bytes.NewBuffer([]byte("")))
135-
if err != nil {
136-
return lpr, err
137-
}
138-
139131
client := &http.Client{}
140-
req.Header.Set("Content-Type", CHARSET_JSON)
141-
if err != nil {
142-
return lpr, err
143-
}
144-
resp, err := client.Do(req)
132+
resp, err := client.Get(c.ListUrl())
145133
if err != nil {
146134
return lpr, err
147135
}
@@ -152,8 +140,7 @@ func (c *Client) ListProfiles() ([]ListProfilesResponse, error) {
152140
return lpr, err
153141
}
154142

155-
err = json.Unmarshal(body, &lpr)
156-
if err != nil {
143+
if err = json.Unmarshal(body, &lpr); err != nil {
157144
return lpr, err
158145
}
159146
log.Debugf("resp: %s", spew.Sdump(lpr))
@@ -172,6 +159,31 @@ func (c *Client) Delete(profile string) error {
172159
if err != nil {
173160
return err
174161
}
175-
_, err = client.Do(req)
176-
return err
162+
resp, err := client.Do(req)
163+
if err != nil {
164+
return err
165+
}
166+
return CheckDoResponse(resp)
167+
}
168+
169+
// ReadClientRequest unmarshals the client's request into our ClientRequest struct
170+
// used to load new credentials into the server
171+
func ReadClientRequest(r *http.Request) (*ClientRequest, error) {
172+
defer r.Body.Close()
173+
body, err := io.ReadAll(r.Body)
174+
if err != nil {
175+
return &ClientRequest{}, fmt.Errorf("reading body: %s", err.Error())
176+
}
177+
req := &ClientRequest{}
178+
if err = json.Unmarshal(body, req); err != nil {
179+
return &ClientRequest{}, fmt.Errorf("parsing json: %s", err.Error())
180+
}
181+
return req, nil
182+
}
183+
184+
func CheckDoResponse(resp *http.Response) error {
185+
if resp.StatusCode < 200 || resp.StatusCode > 200 {
186+
return fmt.Errorf("HTTP Error %d", resp.StatusCode)
187+
}
188+
return nil
177189
}

0 commit comments

Comments
 (0)