From 86614832d7a376a89d4b2dd754a5531e8f014fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Thu, 25 Apr 2024 13:13:46 +0200 Subject: [PATCH 1/7] implement user confirmation for re-authenticating --- internal/cmd/auth/login/login.go | 2 +- internal/pkg/auth/auth.go | 5 ++--- internal/pkg/auth/auth_test.go | 2 +- internal/pkg/auth/user_login.go | 11 ++++++++++- internal/pkg/auth/user_token_flow.go | 5 ++--- internal/pkg/auth/user_token_flow_test.go | 2 +- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/internal/cmd/auth/login/login.go b/internal/cmd/auth/login/login.go index 55f3663b0..3ba46f3d5 100644 --- a/internal/cmd/auth/login/login.go +++ b/internal/cmd/auth/login/login.go @@ -23,7 +23,7 @@ func NewCmd(p *print.Printer) *cobra.Command { "$ stackit auth login"), ), RunE: func(cmd *cobra.Command, args []string) error { - err := auth.AuthorizeUser() + err := auth.AuthorizeUser(p, false) if err != nil { return fmt.Errorf("authorization failed: %w", err) } diff --git a/internal/pkg/auth/auth.go b/internal/pkg/auth/auth.go index c7372599c..15f280b15 100644 --- a/internal/pkg/auth/auth.go +++ b/internal/pkg/auth/auth.go @@ -22,7 +22,7 @@ type tokenClaims struct { // It returns the configuration option that can be used to create an authenticated SDK client. // // If the user was logged in and the user session expired, reauthorizeUserRoutine is called to reauthenticate the user again. -func AuthenticationConfig(p *print.Printer, reauthorizeUserRoutine func() error) (authCfgOption sdkConfig.ConfigurationOption, err error) { +func AuthenticationConfig(p *print.Printer, reauthorizeUserRoutine func(p *print.Printer, isReauthentication bool) error) (authCfgOption sdkConfig.ConfigurationOption, err error) { flow, err := GetAuthFlow() if err != nil { return nil, fmt.Errorf("get authentication flow: %w", err) @@ -57,8 +57,7 @@ func AuthenticationConfig(p *print.Printer, reauthorizeUserRoutine func() error) authCfgOption = sdkConfig.WithCustomAuth(keyFlow) case AUTH_FLOW_USER_TOKEN: if userSessionExpired { - p.Warn("Session expired, logging in again...\n") - err = reauthorizeUserRoutine() + err = reauthorizeUserRoutine(p, true) if err != nil { return nil, fmt.Errorf("user login: %w", err) } diff --git a/internal/pkg/auth/auth_test.go b/internal/pkg/auth/auth_test.go index 18e61b158..9d4ee8778 100644 --- a/internal/pkg/auth/auth_test.go +++ b/internal/pkg/auth/auth_test.go @@ -192,7 +192,7 @@ func TestAuthenticationConfig(t *testing.T) { } reauthorizeUserCalled := false - reauthenticateUser := func() error { + reauthenticateUser := func(p *print.Printer, isReauthentication bool) error { if reauthorizeUserCalled { t.Errorf("user reauthorized more than once") } diff --git a/internal/pkg/auth/user_login.go b/internal/pkg/auth/user_login.go index e3c0d3740..f0d7b9d7c 100644 --- a/internal/pkg/auth/user_login.go +++ b/internal/pkg/auth/user_login.go @@ -17,6 +17,8 @@ import ( "time" "golang.org/x/oauth2" + + "github.com/stackitcloud/stackit-cli/internal/pkg/print" ) const ( @@ -36,7 +38,14 @@ type User struct { } // AuthorizeUser implements the PKCE OAuth2 flow. -func AuthorizeUser() error { +func AuthorizeUser(p *print.Printer, isReauthentication bool) error { + if isReauthentication { + err := p.PromptForConfirmation("Your session has expired! Do you want to login again? ") + if err != nil { + return err + } + } + listener, err := net.Listen("tcp", ":0") if err != nil { return fmt.Errorf("bind port for login redirect: %w", err) diff --git a/internal/pkg/auth/user_token_flow.go b/internal/pkg/auth/user_token_flow.go index ab450d6f0..8abba51d0 100644 --- a/internal/pkg/auth/user_token_flow.go +++ b/internal/pkg/auth/user_token_flow.go @@ -14,7 +14,7 @@ import ( type userTokenFlow struct { printer *print.Printer - reauthorizeUserRoutine func() error // Called if the user needs to login again + reauthorizeUserRoutine func(p *print.Printer, isReauthentication bool) error // Called if the user needs to login again client *http.Client authFlow AuthFlow accessToken string @@ -59,7 +59,6 @@ func (utf *userTokenFlow) RoundTrip(req *http.Request) (*http.Response, error) { } if !accessTokenValid { - utf.printer.Warn("Session expired, logging in again...") err = reauthenticateUser(utf) if err != nil { return nil, fmt.Errorf("reauthenticate user: %w", err) @@ -91,7 +90,7 @@ func loadVarsFromStorage(utf *userTokenFlow) error { } func reauthenticateUser(utf *userTokenFlow) error { - err := utf.reauthorizeUserRoutine() + err := utf.reauthorizeUserRoutine(utf.printer, true) if err != nil { return fmt.Errorf("authenticate user: %w", err) } diff --git a/internal/pkg/auth/user_token_flow_test.go b/internal/pkg/auth/user_token_flow_test.go index 66831292f..b89f29bb8 100644 --- a/internal/pkg/auth/user_token_flow_test.go +++ b/internal/pkg/auth/user_token_flow_test.go @@ -271,7 +271,7 @@ func TestRoundTrip(t *testing.T) { authorizeUserCalled: &authorizeUserCalled, tokensRefreshed: &tokensRefreshed, } - authorizeUserRoutine := func() error { + authorizeUserRoutine := func(p *print.Printer, isReauthentication bool) error { return reauthorizeUser(authorizeUserContext) } From 9d9010ccad533fca28e87ffe701989af6aca99d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Fri, 26 Apr 2024 12:16:11 +0200 Subject: [PATCH 2/7] implement PromtForEnter --- internal/pkg/auth/user_login.go | 2 +- internal/pkg/print/print.go | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/internal/pkg/auth/user_login.go b/internal/pkg/auth/user_login.go index f0d7b9d7c..49dec2177 100644 --- a/internal/pkg/auth/user_login.go +++ b/internal/pkg/auth/user_login.go @@ -40,7 +40,7 @@ type User struct { // AuthorizeUser implements the PKCE OAuth2 flow. func AuthorizeUser(p *print.Printer, isReauthentication bool) error { if isReauthentication { - err := p.PromptForConfirmation("Your session has expired! Do you want to login again? ") + err := p.PromptForEnter("Your session has expired, press Enter to login again...") if err != nil { return err } diff --git a/internal/pkg/print/print.go b/internal/pkg/print/print.go index aadb64bed..ae7e5eedb 100644 --- a/internal/pkg/print/print.go +++ b/internal/pkg/print/print.go @@ -130,6 +130,25 @@ func (p *Printer) PromptForConfirmation(prompt string) error { return fmt.Errorf("max number of wrong inputs") } +// Prompts the user for confirmation by pressing Enter. +// +// Returns nil only if the user (explicitly) answers positive. +// Returns ErrAborted if the user answers negative. +func (p *Printer) PromptForEnter(prompt string) error { + reader := bufio.NewReaderSize(p.Cmd.InOrStdin(), 1) + + p.Cmd.PrintErr(prompt) + answer, err := reader.ReadByte() + p.Cmd.Printf("Answer: %v\n", answer) + if err != nil { + return fmt.Errorf("run less command: %w", err) + } + if answer == 10 { + return nil + } + return errAborted +} + // Shows the content in the command's stdout using the "less" command // If output format is set to none, it does nothing func (p *Printer) PagerDisplay(content string) error { From 9d9aa08226f76f73af44d7e4184f8a9a683ddf95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Fri, 26 Apr 2024 13:11:22 +0200 Subject: [PATCH 3/7] update PromptForEnter --- internal/pkg/print/print.go | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/internal/pkg/print/print.go b/internal/pkg/print/print.go index ae7e5eedb..6fd768639 100644 --- a/internal/pkg/print/print.go +++ b/internal/pkg/print/print.go @@ -4,11 +4,13 @@ import ( "bufio" "errors" "fmt" + "log/slog" "os" "os/exec" "strings" + "github.com/mattn/go-tty" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/stackitcloud/stackit-cli/internal/pkg/config" @@ -132,20 +134,31 @@ func (p *Printer) PromptForConfirmation(prompt string) error { // Prompts the user for confirmation by pressing Enter. // -// Returns nil only if the user (explicitly) answers positive. -// Returns ErrAborted if the user answers negative. +// Returns nil only if the user (explicitly) press directly enter. +// Returns ErrAborted if the user press anything else. func (p *Printer) PromptForEnter(prompt string) error { - reader := bufio.NewReaderSize(p.Cmd.InOrStdin(), 1) + question := fmt.Sprintf("%s \n", prompt) - p.Cmd.PrintErr(prompt) - answer, err := reader.ReadByte() - p.Cmd.Printf("Answer: %v\n", answer) + tty_, err := tty.Open() if err != nil { - return fmt.Errorf("run less command: %w", err) + return fmt.Errorf("open tty: %w", err) + } + + p.Cmd.PrintErr(question) + r, err := tty_.ReadRune() + if err != nil { + return fmt.Errorf("read rune: %w", err) } - if answer == 10 { + + if r == 13 { return nil } + + err = tty_.Close() + if err != nil { + return fmt.Errorf("close tty: %w", err) + } + return errAborted } From 74dd0b7a1478ba5a84458ff21e0f3899576f61f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Fri, 26 Apr 2024 13:15:19 +0200 Subject: [PATCH 4/7] add dependencies --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index 86c2321c6..f7a5221eb 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 + github.com/mattn/go-tty v0.0.5 github.com/stackitcloud/stackit-sdk-go/core v0.12.0 github.com/stackitcloud/stackit-sdk-go/services/authorization v0.2.0 github.com/stackitcloud/stackit-sdk-go/services/dns v0.9.0 diff --git a/go.sum b/go.sum index 361137000..4ed05326d 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-tty v0.0.5 h1:s09uXI7yDbXzzTTfw3zonKFzwGkyYlgU3OMjqA0ddz4= +github.com/mattn/go-tty v0.0.5/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3pxse28= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= From 5773d7770ff75ead2385fa97278388cc86ed9a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Fri, 26 Apr 2024 15:06:03 +0200 Subject: [PATCH 5/7] rollback PromptForEnter function --- go.mod | 1 - go.sum | 2 -- internal/pkg/print/print.go | 25 ++++++------------------- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index f7a5221eb..86c2321c6 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,6 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 - github.com/mattn/go-tty v0.0.5 github.com/stackitcloud/stackit-sdk-go/core v0.12.0 github.com/stackitcloud/stackit-sdk-go/services/authorization v0.2.0 github.com/stackitcloud/stackit-sdk-go/services/dns v0.9.0 diff --git a/go.sum b/go.sum index 4ed05326d..361137000 100644 --- a/go.sum +++ b/go.sum @@ -36,8 +36,6 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-tty v0.0.5 h1:s09uXI7yDbXzzTTfw3zonKFzwGkyYlgU3OMjqA0ddz4= -github.com/mattn/go-tty v0.0.5/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3pxse28= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= diff --git a/internal/pkg/print/print.go b/internal/pkg/print/print.go index 6fd768639..8879166ce 100644 --- a/internal/pkg/print/print.go +++ b/internal/pkg/print/print.go @@ -10,7 +10,6 @@ import ( "os/exec" "strings" - "github.com/mattn/go-tty" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/stackitcloud/stackit-cli/internal/pkg/config" @@ -135,30 +134,18 @@ func (p *Printer) PromptForConfirmation(prompt string) error { // Prompts the user for confirmation by pressing Enter. // // Returns nil only if the user (explicitly) press directly enter. -// Returns ErrAborted if the user press anything else. +// Returns ErrAborted if the user press anything else before pressing enter. func (p *Printer) PromptForEnter(prompt string) error { - question := fmt.Sprintf("%s \n", prompt) + reader := bufio.NewReaderSize(p.Cmd.InOrStdin(), 1) - tty_, err := tty.Open() + p.Cmd.PrintErr(prompt) + answer, err := reader.ReadByte() if err != nil { - return fmt.Errorf("open tty: %w", err) + return fmt.Errorf("read user response: %w", err) } - - p.Cmd.PrintErr(question) - r, err := tty_.ReadRune() - if err != nil { - return fmt.Errorf("read rune: %w", err) - } - - if r == 13 { + if answer == 10 { return nil } - - err = tty_.Close() - if err != nil { - return fmt.Errorf("close tty: %w", err) - } - return errAborted } From 0137ae22dd8b6078fc093b726df9d78edbca40d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Fri, 26 Apr 2024 15:29:41 +0200 Subject: [PATCH 6/7] add comment to condition --- internal/pkg/print/print.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/pkg/print/print.go b/internal/pkg/print/print.go index 8879166ce..19852b20b 100644 --- a/internal/pkg/print/print.go +++ b/internal/pkg/print/print.go @@ -143,6 +143,9 @@ func (p *Printer) PromptForEnter(prompt string) error { if err != nil { return fmt.Errorf("read user response: %w", err) } + + // Normally the ASCII code for enter is 13. But the returned answer from ReadByte method when enter is pressed is 10 + // That is why the condition is checking with using 10 if answer == 10 { return nil } From 926f0e5974f66c17e47c4498be0d85a2c2eb520f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Go=CC=88kc=CC=A7e=20Go=CC=88k=20Klingel?= Date: Fri, 26 Apr 2024 15:46:49 +0200 Subject: [PATCH 7/7] update comment --- internal/pkg/print/print.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/pkg/print/print.go b/internal/pkg/print/print.go index 19852b20b..3115a8d26 100644 --- a/internal/pkg/print/print.go +++ b/internal/pkg/print/print.go @@ -144,8 +144,7 @@ func (p *Printer) PromptForEnter(prompt string) error { return fmt.Errorf("read user response: %w", err) } - // Normally the ASCII code for enter is 13. But the returned answer from ReadByte method when enter is pressed is 10 - // That is why the condition is checking with using 10 + // ASCII code for Enter (newline) is 10. if answer == 10 { return nil }