forked from scaleway/scaleway-cli
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add login command (scaleway#4043)
- Loading branch information
Showing
9 changed files
with
373 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
🎲🎲🎲 EXIT CODE: 0 🎲🎲🎲 | ||
🟥🟥🟥 STDERR️️ 🟥🟥🟥️ | ||
Start an interactive connection to scaleway to initialize the active profile of the config | ||
A webpage will open while the CLI will wait for a response. | ||
Once you connected to Scaleway, the profile should be configured. | ||
|
||
USAGE: | ||
scw login [arg=value ...] | ||
|
||
ARGS: | ||
[port] The port number used to wait for browser's response | ||
|
||
FLAGS: | ||
-h, --help help for login | ||
|
||
GLOBAL FLAGS: | ||
-c, --config string The path to the config file | ||
-D, --debug Enable debug mode | ||
-o, --output string Output format: json or human, see 'scw help output' for more info (default "human") | ||
-p, --profile string The config profile to use | ||
|
||
SEE ALSO: | ||
# Init profile manually | ||
scw init |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<!-- DO NOT EDIT: this file is automatically generated using scw-doc-gen --> | ||
# Documentation for `scw login` | ||
Start an interactive connection to scaleway to initialize the active profile of the config | ||
A webpage will open while the CLI will wait for a response. | ||
Once you connected to Scaleway, the profile should be configured. | ||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package login | ||
|
||
import ( | ||
"context" | ||
"encoding/base64" | ||
"encoding/json" | ||
"fmt" | ||
"reflect" | ||
"time" | ||
|
||
"github.com/scaleway/scaleway-cli/v2/internal/core" | ||
initCommand "github.com/scaleway/scaleway-cli/v2/internal/namespaces/init" | ||
"github.com/scaleway/scaleway-cli/v2/internal/namespaces/login/webcallback" | ||
iam "github.com/scaleway/scaleway-sdk-go/api/iam/v1alpha1" | ||
"github.com/scaleway/scaleway-sdk-go/logger" | ||
"github.com/scaleway/scaleway-sdk-go/scw" | ||
"github.com/skratchdot/open-golang/open" | ||
) | ||
|
||
func GetCommands() *core.Commands { | ||
return core.NewCommands(loginCommand()) | ||
} | ||
|
||
type loginArgs struct { | ||
Port int `json:"port"` | ||
// PrintURL will print the account url instead of trying to open it with a browser | ||
PrintURL bool `json:"print_url"` | ||
} | ||
|
||
func loginCommand() *core.Command { | ||
return &core.Command{ | ||
Groups: []string{"config"}, | ||
Short: `Login to scaleway`, | ||
Long: `Start an interactive connection to scaleway to initialize the active profile of the config | ||
A webpage will open while the CLI will wait for a response. | ||
Once you connected to Scaleway, the profile should be configured. | ||
`, | ||
Namespace: "login", | ||
AllowAnonymousClient: true, | ||
ArgsType: reflect.TypeOf(loginArgs{}), | ||
ArgSpecs: core.ArgSpecs{ | ||
{ | ||
Name: "port", | ||
Short: "The port number used to wait for browser's response", | ||
}, | ||
}, | ||
SeeAlsos: []*core.SeeAlso{ | ||
{ | ||
Short: "Init profile manually", | ||
Command: "scw init", | ||
}, | ||
}, | ||
Run: func(ctx context.Context, argsI interface{}) (interface{}, error) { | ||
args := argsI.(*loginArgs) | ||
|
||
opts := []webcallback.Options(nil) | ||
if args.Port > 0 { | ||
opts = append(opts, webcallback.WithPort(args.Port)) | ||
} | ||
|
||
wb := webcallback.New(opts...) | ||
err := wb.Start() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
callbackURL := fmt.Sprintf("http://localhost:%d/callback", wb.Port()) | ||
|
||
accountURL := "https://account.scaleway.com/authenticate?redirectToUrl=" + callbackURL | ||
|
||
logger.Debugf("Web server started, waiting for callback on %s\n", callbackURL) | ||
|
||
if args.PrintURL { | ||
fmt.Println(accountURL) | ||
} else { | ||
err = open.Start(accountURL) | ||
if err != nil { | ||
logger.Warningf("Failed to open web url, you may not have a default browser configured") | ||
logger.Warningf("You can open it: " + accountURL) | ||
} | ||
} | ||
|
||
fmt.Println("waiting for callback from browser...") | ||
token, err := wb.Wait(ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
rawToken, err := base64.StdEncoding.DecodeString(token) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
tt := Token{} | ||
|
||
err = json.Unmarshal(rawToken, &tt) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
client, err := scw.NewClient(scw.WithJWT(tt.Token)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
api := iam.NewAPI(client) | ||
apiKey, err := api.CreateAPIKey(&iam.CreateAPIKeyRequest{ | ||
UserID: &tt.Jwt.AudienceID, | ||
Description: "Generated by the Scaleway CLI", | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
resp, err := initCommand.Command().Run(ctx, &initCommand.Args{ | ||
AccessKey: apiKey.AccessKey, | ||
SecretKey: *apiKey.SecretKey, | ||
ProjectID: apiKey.DefaultProjectID, | ||
OrganizationID: apiKey.DefaultProjectID, | ||
Region: scw.RegionFrPar, | ||
Zone: scw.ZoneFrPar1, | ||
}) | ||
if err != nil { | ||
// Cleanup API Key if init failed | ||
logger.Warningf("Init failed, cleaning API key.\n") | ||
cleanErr := api.DeleteAPIKey(&iam.DeleteAPIKeyRequest{ | ||
AccessKey: apiKey.AccessKey, | ||
}) | ||
if cleanErr != nil { | ||
logger.Warningf("Failed to clean API key: %s\n", err.Error()) | ||
} | ||
return nil, err | ||
} | ||
|
||
return resp, nil | ||
}, | ||
} | ||
} | ||
|
||
type Token struct { | ||
Jwt struct { | ||
AudienceID string `json:"audienceId"` | ||
CreatedAt time.Time `json:"createdAt"` | ||
ExpiresAt time.Time `json:"expiresAt"` | ||
IP string `json:"ip"` | ||
IssuerID string `json:"issuerId"` | ||
Jti string `json:"jti"` | ||
UpdatedAt time.Time `json:"updatedAt"` | ||
UserAgent string `json:"userAgent"` | ||
} `json:"jwt"` | ||
RenewToken string `json:"renewToken"` | ||
Token string `json:"token"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package webcallback | ||
|
||
type Options func(*WebCallback) | ||
|
||
func WithPort(port int) Options { | ||
return func(callback *WebCallback) { | ||
callback.port = port | ||
} | ||
} |
Oops, something went wrong.