Skip to content

Commit b1145cf

Browse files
committed
Merge pull request #132 from QuentinPerez/change_login_behavior
fix #59
2 parents 1004506 + 8552997 commit b1145cf

File tree

3 files changed

+198
-41
lines changed

3 files changed

+198
-41
lines changed

pkg/api/api.go

+78-28
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ import (
2424
"github.com/scaleway/scaleway-cli/vendor/github.com/moul/anonuuid"
2525
)
2626

27+
// Default values
28+
var (
29+
ComputeAPI string = "https://api.scaleway.com/"
30+
AccountAPI string = "https://account.scaleway.com/"
31+
)
32+
2733
// ScalewayAPI is the interface used to communicate with the Scaleway API
2834
type ScalewayAPI struct {
2935
// ComputeAPI is the endpoint to the Scaleway API
@@ -499,35 +505,72 @@ type ScalewayImageDefinition struct {
499505
Arch string `json:"arch"`
500506
}
501507

502-
// ScalewayTokenUserIDRoleDefinition represents a Scaleway Token UserId Role
503-
type ScalewayTokenUserIDRoleDefinition struct {
504-
Organization string `json:"organization,omitempty"`
505-
Role string `json:"role,omitempty"`
508+
// ScalewayRoleDefinition represents a Scaleway Token UserId Role
509+
type ScalewayRoleDefinition struct {
510+
Organization ScalewayOrganizationDefinition `json:"organization,omitempty"`
511+
Role string `json:"role,omitempty"`
506512
}
507513

508514
// ScalewayTokenDefinition represents a Scaleway Token
509515
type ScalewayTokenDefinition struct {
510-
UserID string `json:"user_id"`
511-
Description string `json:"description,omitempty"`
512-
Roles ScalewayTokenUserIDRoleDefinition `json:"roles"`
513-
Expires string `json:"expires"`
514-
InheritsUsersPerms bool `json:"inherits_user_perms"`
515-
ID string `json:"id"`
516+
UserID string `json:"user_id"`
517+
Description string `json:"description,omitempty"`
518+
Roles ScalewayRoleDefinition `json:"roles"`
519+
Expires string `json:"expires"`
520+
InheritsUsersPerms bool `json:"inherits_user_perms"`
521+
ID string `json:"id"`
516522
}
517523

518524
// ScalewayTokensDefinition represents a Scaleway Tokens
519525
type ScalewayTokensDefinition struct {
520526
Tokens []ScalewayTokenDefinition `json:"tokens"`
521527
}
522528

523-
// ScalewayUserPatchKeyDefinition represents a key
524-
type ScalewayUserPatchKeyDefinition struct {
529+
// ScalewayConnectResponse represents the answer from POST /tokens
530+
type ScalewayConnectResponse struct {
531+
Token ScalewayTokenDefinition `json:"token"`
532+
}
533+
534+
// ScalewayConnect represents the data to connect
535+
type ScalewayConnect struct {
536+
Email string `json:"email"`
537+
Password string `json:"password"`
538+
Description string `json:"description"`
539+
Expires bool `json:"expires"`
540+
}
541+
542+
// ScalewayOrganizationDefinition represents a Scaleway Organization
543+
type ScalewayOrganizationDefinition struct {
544+
ID string `json:"id"`
545+
Name string `json:"name"`
546+
Users []ScalewayUserDefinition `json:"users"`
547+
}
548+
549+
// ScalewayOrganizationsDefinition represents a Scaleway Organizations
550+
type ScalewayOrganizationsDefinition struct {
551+
Organizations []ScalewayOrganizationDefinition `json:"organizations"`
552+
}
553+
554+
// ScalewayUserDefinition represents a Scaleway User
555+
type ScalewayUserDefinition struct {
556+
Email string `json:"email"`
557+
Firstname string `json:"firstname"`
558+
Fullname string `json:"fullname"`
559+
ID string `json:"id"`
560+
Lastname string `json:"lastname"`
561+
Organizations []ScalewayOrganizationDefinition `json:"organizations"`
562+
Roles []ScalewayRoleDefinition `json:"roles"`
563+
SSHPublicKeys []ScalewayKeyDefinition `json:"ssh_public_keys"`
564+
}
565+
566+
// ScalewayKeyDefinition represents a key
567+
type ScalewayKeyDefinition struct {
525568
Key string `json:"key"`
526569
}
527570

528-
// ScalewayUserPatchDefinition represents a User Patch
529-
type ScalewayUserPatchDefinition struct {
530-
SSHPublicKeys []ScalewayUserPatchKeyDefinition `json:"ssh_public_keys"`
571+
// ScalewayUserPatchSSHKeyDefinition represents a User Patch
572+
type ScalewayUserPatchSSHKeyDefinition struct {
573+
SSHPublicKeys []ScalewayKeyDefinition `json:"ssh_public_keys"`
531574
}
532575

533576
// FuncMap used for json inspection
@@ -821,10 +864,10 @@ func (s *ScalewayAPI) PostServer(definition ScalewayServerDefinition) (string, e
821864
return "", error
822865
}
823866

824-
// PatchUser updates a user
825-
func (s *ScalewayAPI) PatchUser(UserID string, definition ScalewayUserPatchDefinition) error {
826-
s.enableAccountAPI()
827-
defer s.disableAccountAPI()
867+
// PatchUserSSHKey updates a user
868+
func (s *ScalewayAPI) PatchUserSSHKey(UserID string, definition ScalewayUserPatchSSHKeyDefinition) error {
869+
s.EnableAccountAPI()
870+
defer s.DisableAccountAPI()
828871
resp, err := s.PatchResponse(fmt.Sprintf("users/%s", UserID), definition)
829872
if err != nil {
830873
return err
@@ -1267,8 +1310,8 @@ func (s *ScalewayAPI) GetTasks() (*[]ScalewayTask, error) {
12671310

12681311
// CheckCredentials performs a dummy check to ensure we can contact the API
12691312
func (s *ScalewayAPI) CheckCredentials() error {
1270-
s.enableAccountAPI()
1271-
defer s.disableAccountAPI()
1313+
s.EnableAccountAPI()
1314+
defer s.DisableAccountAPI()
12721315
query := url.Values{}
12731316
query.Set("token_id", s.Token)
12741317
resp, err := s.GetResponse("tokens?" + query.Encode())
@@ -1283,8 +1326,8 @@ func (s *ScalewayAPI) CheckCredentials() error {
12831326

12841327
// GetUserID returns the UserID
12851328
func (s *ScalewayAPI) GetUserID() (string, error) {
1286-
s.enableAccountAPI()
1287-
defer s.disableAccountAPI()
1329+
s.EnableAccountAPI()
1330+
defer s.DisableAccountAPI()
12881331
resp, err := s.GetResponse("tokens")
12891332
if err != nil {
12901333
return "", err
@@ -1300,7 +1343,7 @@ func (s *ScalewayAPI) GetUserID() (string, error) {
13001343
return "", err
13011344
}
13021345
if len(tokens.Tokens) == 0 {
1303-
return "", fmt.Errorf("Unable to get tokens")
1346+
return "", fmt.Errorf("unable to get tokens")
13041347
}
13051348
return tokens.Tokens[0].UserID, nil
13061349
}
@@ -1409,15 +1452,22 @@ func (s *ScalewayAPI) GetBootscriptID(needle string) string {
14091452

14101453
// HideAPICredentials removes API credentials from a string
14111454
func (s *ScalewayAPI) HideAPICredentials(input string) string {
1412-
output := strings.Replace(input, s.Token, s.anonuuid.FakeUUID(s.Token), -1)
1413-
output = strings.Replace(output, s.Organization, s.anonuuid.FakeUUID(s.Organization), -1)
1455+
output := input
1456+
if s.Token != "" {
1457+
output = strings.Replace(output, s.Token, s.anonuuid.FakeUUID(s.Token), -1)
1458+
}
1459+
if s.Organization != "" {
1460+
output = strings.Replace(output, s.Organization, s.anonuuid.FakeUUID(s.Organization), -1)
1461+
}
14141462
return output
14151463
}
14161464

1417-
func (s *ScalewayAPI) enableAccountAPI() {
1465+
// EnableAccountAPI enable accountAPI
1466+
func (s *ScalewayAPI) EnableAccountAPI() {
14181467
s.APIUrl = s.AccountAPI
14191468
}
14201469

1421-
func (s *ScalewayAPI) disableAccountAPI() {
1470+
// DisableAccountAPI disable accountAPI
1471+
func (s *ScalewayAPI) DisableAccountAPI() {
14221472
s.APIUrl = s.ComputeAPI
14231473
}

pkg/cli/cmd_login.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44

55
package cli
66

7-
import "github.com/scaleway/scaleway-cli/pkg/commands"
7+
import (
8+
"fmt"
9+
10+
"github.com/scaleway/scaleway-cli/pkg/commands"
11+
)
812

913
var cmdLogin = &Command{
1014
Exec: runLogin,
@@ -36,7 +40,9 @@ func runLogin(cmd *Command, rawArgs []string) error {
3640
if len(rawArgs) != 0 {
3741
return cmd.PrintShortUsage()
3842
}
39-
43+
if (organization != "" || token != "") && (organization == "" || token == "") {
44+
return fmt.Errorf("you must define organization AND token")
45+
}
4046
args := commands.LoginArgs{
4147
Organization: organization,
4248
Token: token,

pkg/commands/login.go

+112-11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package commands
66

77
import (
88
"bufio"
9+
"encoding/json"
910
"fmt"
1011
"io/ioutil"
1112
"os"
@@ -78,19 +79,119 @@ func selectKey(args *LoginArgs) error {
7879
return nil
7980
}
8081

82+
func getToken(connect api.ScalewayConnect) (string, error) {
83+
FakeConnection, err := api.NewScalewayAPI(api.ComputeAPI, api.AccountAPI, "", "")
84+
if err != nil {
85+
return "", fmt.Errorf("Unable to create a fake ScalewayAPI: %s", err)
86+
}
87+
FakeConnection.EnableAccountAPI()
88+
89+
resp, err := FakeConnection.PostResponse("tokens", connect)
90+
if err != nil {
91+
return "", fmt.Errorf("unable to connect %v", err)
92+
}
93+
// Succeed POST code
94+
if resp.StatusCode != 201 {
95+
return "", fmt.Errorf("[%d] maybe your email or your password is not valid", resp.StatusCode)
96+
}
97+
var data api.ScalewayConnectResponse
98+
99+
defer resp.Body.Close()
100+
decoder := json.NewDecoder(resp.Body)
101+
err = decoder.Decode(&data)
102+
if err != nil {
103+
return "", err
104+
}
105+
return data.Token.ID, nil
106+
}
107+
108+
func getOrga(token string, email string) (string, error) {
109+
FakeConnection, err := api.NewScalewayAPI(api.ComputeAPI, api.AccountAPI, "", token)
110+
if err != nil {
111+
return "", fmt.Errorf("Unable to create a fake ScalewayAPI: %s", err)
112+
}
113+
FakeConnection.EnableAccountAPI()
114+
115+
resp, err := FakeConnection.GetResponse("organizations")
116+
if err != nil {
117+
return "", err
118+
}
119+
if resp.StatusCode != 200 {
120+
return "", fmt.Errorf("[%d] unable to GET", resp.StatusCode)
121+
}
122+
123+
var data api.ScalewayOrganizationsDefinition
124+
125+
defer resp.Body.Close()
126+
decoder := json.NewDecoder(resp.Body)
127+
err = decoder.Decode(&data)
128+
if err != nil {
129+
return "", err
130+
}
131+
orgaID := ""
132+
133+
for _, orga := range data.Organizations {
134+
for _, user := range orga.Users {
135+
if user.Email == email {
136+
for i := range user.Organizations {
137+
if user.Organizations[i].Name != "OCS" {
138+
orgaID = user.Organizations[i].ID
139+
goto exit
140+
}
141+
}
142+
}
143+
}
144+
}
145+
if orgaID == "" {
146+
return "", fmt.Errorf("Unable to find your organization")
147+
}
148+
exit:
149+
return orgaID, nil
150+
}
151+
152+
func connectAPI() (string, string, error) {
153+
email := ""
154+
password := ""
155+
orga := ""
156+
token := ""
157+
hostname, err := os.Hostname()
158+
if err != nil {
159+
return "", "", fmt.Errorf("unable to get your Hostname %v", err)
160+
}
161+
promptUser("Login (cloud.scaleway.com): ", &email, true)
162+
promptUser("Password: ", &password, false)
163+
164+
connect := api.ScalewayConnect{
165+
Email: strings.Trim(email, "\n"),
166+
Password: strings.Trim(password, "\n"),
167+
Expires: false,
168+
Description: strings.Join([]string{"scw", hostname}, "-"),
169+
}
170+
token, err = getToken(connect)
171+
if err != nil {
172+
return "", "", err
173+
}
174+
orga, err = getOrga(token, connect.Email)
175+
if err != nil {
176+
return "", "", err
177+
}
178+
return orga, token, nil
179+
}
180+
81181
// RunLogin is the handler for 'scw login'
82182
func RunLogin(ctx CommandContext, args LoginArgs) error {
83-
if args.Organization == "" {
84-
fmt.Println("You can get your credentials on https://cloud.scaleway.com/#/credentials")
85-
promptUser("Organization (access key): ", &args.Organization, true)
86-
}
87-
if args.Token == "" {
88-
promptUser("Token: ", &args.Token, false)
183+
if args.Organization == "" || args.Token == "" {
184+
var err error
185+
186+
args.Organization, args.Token, err = connectAPI()
187+
if err != nil {
188+
return err
189+
}
89190
}
90191

91192
cfg := &config.Config{
92-
ComputeAPI: "https://api.scaleway.com/",
93-
AccountAPI: "https://account.scaleway.com/",
193+
ComputeAPI: api.ComputeAPI,
194+
AccountAPI: api.AccountAPI,
94195
Organization: strings.Trim(args.Organization, "\n"),
95196
Token: strings.Trim(args.Token, "\n"),
96197
}
@@ -112,13 +213,13 @@ func RunLogin(ctx CommandContext, args LoginArgs) error {
112213
logrus.Errorf("Unable to contact ScalewayAPI: %s", err)
113214
} else {
114215

115-
SSHKey := api.ScalewayUserPatchDefinition{
116-
SSHPublicKeys: []api.ScalewayUserPatchKeyDefinition{{
216+
SSHKey := api.ScalewayUserPatchSSHKeyDefinition{
217+
SSHPublicKeys: []api.ScalewayKeyDefinition{{
117218
Key: strings.Trim(args.SSHKey, "\n"),
118219
}},
119220
}
120221

121-
if err = apiConnection.PatchUser(userID, SSHKey); err != nil {
222+
if err = apiConnection.PatchUserSSHKey(userID, SSHKey); err != nil {
122223
logrus.Errorf("Unable to patch SSHkey: %v", err)
123224
}
124225
}

0 commit comments

Comments
 (0)