Skip to content

fix #59 #132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 25, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 78 additions & 28 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ import (
"github.com/scaleway/scaleway-cli/vendor/github.com/moul/anonuuid"
)

// Default values
var (
ComputeAPI string = "https://api.scaleway.com/"
AccountAPI string = "https://account.scaleway.com/"
)

// ScalewayAPI is the interface used to communicate with the Scaleway API
type ScalewayAPI struct {
// ComputeAPI is the endpoint to the Scaleway API
Expand Down Expand Up @@ -499,35 +505,72 @@ type ScalewayImageDefinition struct {
Arch string `json:"arch"`
}

// ScalewayTokenUserIDRoleDefinition represents a Scaleway Token UserId Role
type ScalewayTokenUserIDRoleDefinition struct {
Organization string `json:"organization,omitempty"`
Role string `json:"role,omitempty"`
// ScalewayRoleDefinition represents a Scaleway Token UserId Role
type ScalewayRoleDefinition struct {
Organization ScalewayOrganizationDefinition `json:"organization,omitempty"`
Role string `json:"role,omitempty"`
}

// ScalewayTokenDefinition represents a Scaleway Token
type ScalewayTokenDefinition struct {
UserID string `json:"user_id"`
Description string `json:"description,omitempty"`
Roles ScalewayTokenUserIDRoleDefinition `json:"roles"`
Expires string `json:"expires"`
InheritsUsersPerms bool `json:"inherits_user_perms"`
ID string `json:"id"`
UserID string `json:"user_id"`
Description string `json:"description,omitempty"`
Roles ScalewayRoleDefinition `json:"roles"`
Expires string `json:"expires"`
InheritsUsersPerms bool `json:"inherits_user_perms"`
ID string `json:"id"`
}

// ScalewayTokensDefinition represents a Scaleway Tokens
type ScalewayTokensDefinition struct {
Tokens []ScalewayTokenDefinition `json:"tokens"`
}

// ScalewayUserPatchKeyDefinition represents a key
type ScalewayUserPatchKeyDefinition struct {
// ScalewayConnectResponse represents the answer from POST /tokens
type ScalewayConnectResponse struct {
Token ScalewayTokenDefinition `json:"token"`
}

// ScalewayConnect represents the data to connect
type ScalewayConnect struct {
Email string `json:"email"`
Password string `json:"password"`
Description string `json:"description"`
Expires bool `json:"expires"`
}

// ScalewayOrganizationDefinition represents a Scaleway Organization
type ScalewayOrganizationDefinition struct {
ID string `json:"id"`
Name string `json:"name"`
Users []ScalewayUserDefinition `json:"users"`
}

// ScalewayOrganizationsDefinition represents a Scaleway Organizations
type ScalewayOrganizationsDefinition struct {
Organizations []ScalewayOrganizationDefinition `json:"organizations"`
}

// ScalewayUserDefinition represents a Scaleway User
type ScalewayUserDefinition struct {
Email string `json:"email"`
Firstname string `json:"firstname"`
Fullname string `json:"fullname"`
ID string `json:"id"`
Lastname string `json:"lastname"`
Organizations []ScalewayOrganizationDefinition `json:"organizations"`
Roles []ScalewayRoleDefinition `json:"roles"`
SSHPublicKeys []ScalewayKeyDefinition `json:"ssh_public_keys"`
}

// ScalewayKeyDefinition represents a key
type ScalewayKeyDefinition struct {
Key string `json:"key"`
}

// ScalewayUserPatchDefinition represents a User Patch
type ScalewayUserPatchDefinition struct {
SSHPublicKeys []ScalewayUserPatchKeyDefinition `json:"ssh_public_keys"`
// ScalewayUserPatchSSHKeyDefinition represents a User Patch
type ScalewayUserPatchSSHKeyDefinition struct {
SSHPublicKeys []ScalewayKeyDefinition `json:"ssh_public_keys"`
}

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

// PatchUser updates a user
func (s *ScalewayAPI) PatchUser(UserID string, definition ScalewayUserPatchDefinition) error {
s.enableAccountAPI()
defer s.disableAccountAPI()
// PatchUserSSHKey updates a user
func (s *ScalewayAPI) PatchUserSSHKey(UserID string, definition ScalewayUserPatchSSHKeyDefinition) error {
s.EnableAccountAPI()
defer s.DisableAccountAPI()
resp, err := s.PatchResponse(fmt.Sprintf("users/%s", UserID), definition)
if err != nil {
return err
Expand Down Expand Up @@ -1267,8 +1310,8 @@ func (s *ScalewayAPI) GetTasks() (*[]ScalewayTask, error) {

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

// GetUserID returns the UserID
func (s *ScalewayAPI) GetUserID() (string, error) {
s.enableAccountAPI()
defer s.disableAccountAPI()
s.EnableAccountAPI()
defer s.DisableAccountAPI()
resp, err := s.GetResponse("tokens")
if err != nil {
return "", err
Expand All @@ -1300,7 +1343,7 @@ func (s *ScalewayAPI) GetUserID() (string, error) {
return "", err
}
if len(tokens.Tokens) == 0 {
return "", fmt.Errorf("Unable to get tokens")
return "", fmt.Errorf("unable to get tokens")
}
return tokens.Tokens[0].UserID, nil
}
Expand Down Expand Up @@ -1409,15 +1452,22 @@ func (s *ScalewayAPI) GetBootscriptID(needle string) string {

// HideAPICredentials removes API credentials from a string
func (s *ScalewayAPI) HideAPICredentials(input string) string {
output := strings.Replace(input, s.Token, s.anonuuid.FakeUUID(s.Token), -1)
output = strings.Replace(output, s.Organization, s.anonuuid.FakeUUID(s.Organization), -1)
output := input
if s.Token != "" {
output = strings.Replace(output, s.Token, s.anonuuid.FakeUUID(s.Token), -1)
}
if s.Organization != "" {
output = strings.Replace(output, s.Organization, s.anonuuid.FakeUUID(s.Organization), -1)
}
return output
}

func (s *ScalewayAPI) enableAccountAPI() {
// EnableAccountAPI enable accountAPI
func (s *ScalewayAPI) EnableAccountAPI() {
s.APIUrl = s.AccountAPI
}

func (s *ScalewayAPI) disableAccountAPI() {
// DisableAccountAPI disable accountAPI
func (s *ScalewayAPI) DisableAccountAPI() {
s.APIUrl = s.ComputeAPI
}
10 changes: 8 additions & 2 deletions pkg/cli/cmd_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@

package cli

import "github.com/scaleway/scaleway-cli/pkg/commands"
import (
"fmt"

"github.com/scaleway/scaleway-cli/pkg/commands"
)

var cmdLogin = &Command{
Exec: runLogin,
Expand Down Expand Up @@ -36,7 +40,9 @@ func runLogin(cmd *Command, rawArgs []string) error {
if len(rawArgs) != 0 {
return cmd.PrintShortUsage()
}

if (organization != "" || token != "") && (organization == "" || token == "") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't this be simplified to:

if (organization == "" || token == "")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No because if I check only (organization == "" || token == ""), I can't run scw login
This condition is used If the user run scw login -o="1111-..."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ack

return fmt.Errorf("you must define organization AND token")
}
args := commands.LoginArgs{
Organization: organization,
Token: token,
Expand Down
123 changes: 112 additions & 11 deletions pkg/commands/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package commands

import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"os"
Expand Down Expand Up @@ -78,19 +79,119 @@ func selectKey(args *LoginArgs) error {
return nil
}

func getToken(connect api.ScalewayConnect) (string, error) {
FakeConnection, err := api.NewScalewayAPI(api.ComputeAPI, api.AccountAPI, "", "")
if err != nil {
return "", fmt.Errorf("Unable to create a fake ScalewayAPI: %s", err)
}
FakeConnection.EnableAccountAPI()

resp, err := FakeConnection.PostResponse("tokens", connect)
if err != nil {
return "", fmt.Errorf("unable to connect %v", err)
}
// Succeed POST code
if resp.StatusCode != 201 {
return "", fmt.Errorf("[%d] maybe your email or your password is not valid", resp.StatusCode)
}
var data api.ScalewayConnectResponse

defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&data)
if err != nil {
return "", err
}
return data.Token.ID, nil
}

func getOrga(token string, email string) (string, error) {
FakeConnection, err := api.NewScalewayAPI(api.ComputeAPI, api.AccountAPI, "", token)
if err != nil {
return "", fmt.Errorf("Unable to create a fake ScalewayAPI: %s", err)
}
FakeConnection.EnableAccountAPI()

resp, err := FakeConnection.GetResponse("organizations")
if err != nil {
return "", err
}
if resp.StatusCode != 200 {
return "", fmt.Errorf("[%d] unable to GET", resp.StatusCode)
}

var data api.ScalewayOrganizationsDefinition

defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)
err = decoder.Decode(&data)
if err != nil {
return "", err
}
orgaID := ""

for _, orga := range data.Organizations {
for _, user := range orga.Users {
if user.Email == email {
for i := range user.Organizations {
if user.Organizations[i].Name != "OCS" {
orgaID = user.Organizations[i].ID
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes 👍

goto exit
}
}
}
}
}
if orgaID == "" {
return "", fmt.Errorf("Unable to find your organization")
}
exit:
return orgaID, nil
}

func connectAPI() (string, string, error) {
email := ""
password := ""
orga := ""
token := ""
hostname, err := os.Hostname()
if err != nil {
return "", "", fmt.Errorf("unable to get your Hostname %v", err)
}
promptUser("Login (cloud.scaleway.com): ", &email, true)
promptUser("Password: ", &password, false)

connect := api.ScalewayConnect{
Email: strings.Trim(email, "\n"),
Password: strings.Trim(password, "\n"),
Expires: false,
Description: strings.Join([]string{"scw", hostname}, "-"),
}
token, err = getToken(connect)
if err != nil {
return "", "", err
}
orga, err = getOrga(token, connect.Email)
if err != nil {
return "", "", err
}
return orga, token, nil
}

// RunLogin is the handler for 'scw login'
func RunLogin(ctx CommandContext, args LoginArgs) error {
if args.Organization == "" {
fmt.Println("You can get your credentials on https://cloud.scaleway.com/#/credentials")
promptUser("Organization (access key): ", &args.Organization, true)
}
if args.Token == "" {
promptUser("Token: ", &args.Token, false)
if args.Organization == "" || args.Token == "" {
var err error

args.Organization, args.Token, err = connectAPI()
if err != nil {
return err
}
}

cfg := &config.Config{
ComputeAPI: "https://api.scaleway.com/",
AccountAPI: "https://account.scaleway.com/",
ComputeAPI: api.ComputeAPI,
AccountAPI: api.AccountAPI,
Organization: strings.Trim(args.Organization, "\n"),
Token: strings.Trim(args.Token, "\n"),
}
Expand All @@ -112,13 +213,13 @@ func RunLogin(ctx CommandContext, args LoginArgs) error {
logrus.Errorf("Unable to contact ScalewayAPI: %s", err)
} else {

SSHKey := api.ScalewayUserPatchDefinition{
SSHPublicKeys: []api.ScalewayUserPatchKeyDefinition{{
SSHKey := api.ScalewayUserPatchSSHKeyDefinition{
SSHPublicKeys: []api.ScalewayKeyDefinition{{
Key: strings.Trim(args.SSHKey, "\n"),
}},
}

if err = apiConnection.PatchUser(userID, SSHKey); err != nil {
if err = apiConnection.PatchUserSSHKey(userID, SSHKey); err != nil {
logrus.Errorf("Unable to patch SSHkey: %v", err)
}
}
Expand Down