From c9bcadfb99e875a4a8ebaea8de1ae016ea6b4fb7 Mon Sep 17 00:00:00 2001 From: Rustam Gilyazov <16064414+rusq@users.noreply.github.com> Date: Fri, 29 Dec 2023 15:25:42 +1000 Subject: [PATCH 1/9] move sanitise and introduce exteded browser auth interface --- auth/auth.go | 18 ++++++++++++++++++ auth/auth_test.go | 30 ++++++++++++++++++++++++++++++ auth/browser.go | 25 +++++++------------------ auth/browser_test.go | 33 --------------------------------- 4 files changed, 55 insertions(+), 51 deletions(-) delete mode 100644 auth/browser_test.go diff --git a/auth/auth.go b/auth/auth.go index edff4b33..e834fb50 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -6,6 +6,7 @@ import ( "errors" "io" "net/http" + "net/url" "runtime/trace" "strings" @@ -26,6 +27,7 @@ const ( TypeValue // Value TypeCookieFile // Cookie File TypeBrowser // EZ-Login 3000 + TypeRod ) // Provider is the Slack Authentication provider. @@ -136,3 +138,19 @@ func (s simpleProvider) Test(ctx context.Context) error { func (s simpleProvider) HTTPClient() (*http.Client, error) { return chttp.New(SlackURL, s.Cookies()) } + +func sanitize(workspace string) (string, error) { + if !strings.Contains(workspace, ".slack.com") { + return workspace, nil + } + if strings.HasPrefix(workspace, "https://") { + uri, err := url.Parse(workspace) + if err != nil { + return "", err + } + workspace = uri.Host + } + // parse + parts := strings.Split(workspace, ".") + return parts[0], nil +} diff --git a/auth/auth_test.go b/auth/auth_test.go index b997edb0..071788e3 100644 --- a/auth/auth_test.go +++ b/auth/auth_test.go @@ -114,3 +114,33 @@ func TestSave(t *testing.T) { }) } } + +func Test_sanitize(t *testing.T) { + type args struct { + workspace string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + {"not a URL", args{"blahblah"}, "blahblah", false}, + {"url slash", args{"https://blahblah.slack.com/"}, "blahblah", false}, + {"url no slash", args{"https://blahblah.slack.com"}, "blahblah", false}, + {"url no schema slash", args{"blahblah.slack.com/"}, "blahblah", false}, + {"url no schema no slash", args{"blahblah.slack.com"}, "blahblah", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := sanitize(tt.args.workspace) + if (err != nil) != tt.wantErr { + t.Errorf("sanitize() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("sanitize() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/auth/browser.go b/auth/browser.go index d5766771..271df222 100644 --- a/auth/browser.go +++ b/auth/browser.go @@ -3,9 +3,7 @@ package auth import ( "context" "io" - "net/url" "os" - "strings" "time" "github.com/rusq/slackdump/v2/auth/auth_ui" @@ -33,6 +31,13 @@ type BrowserAuthUI interface { Stop() } +type BrowserAuthUIExt interface { + BrowserAuthUI + RequestEmail(w io.Writer) (string, error) + RequestPassword(w io.Writer) (string, error) + YesNo(w io.Writer, message string) (bool, error) +} + func NewBrowserAuth(ctx context.Context, opts ...Option) (BrowserAuth, error) { var br = BrowserAuth{ opts: browserOpts{ @@ -81,22 +86,6 @@ func (BrowserAuth) Type() Type { return TypeBrowser } -func sanitize(workspace string) (string, error) { - if !strings.Contains(workspace, ".slack.com") { - return workspace, nil - } - if strings.HasPrefix(workspace, "https://") { - uri, err := url.Parse(workspace) - if err != nil { - return "", err - } - workspace = uri.Host - } - // parse - parts := strings.Split(workspace, ".") - return parts[0], nil -} - func isDocker() bool { _, err := os.Stat("/.dockerenv") return err == nil diff --git a/auth/browser_test.go b/auth/browser_test.go deleted file mode 100644 index 07c91542..00000000 --- a/auth/browser_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package auth - -import "testing" - -func Test_sanitize(t *testing.T) { - type args struct { - workspace string - } - tests := []struct { - name string - args args - want string - wantErr bool - }{ - {"not a URL", args{"blahblah"}, "blahblah", false}, - {"url slash", args{"https://blahblah.slack.com/"}, "blahblah", false}, - {"url no slash", args{"https://blahblah.slack.com"}, "blahblah", false}, - {"url no schema slash", args{"blahblah.slack.com/"}, "blahblah", false}, - {"url no schema no slash", args{"blahblah.slack.com"}, "blahblah", false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := sanitize(tt.args.workspace) - if (err != nil) != tt.wantErr { - t.Errorf("sanitize() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("sanitize() got = %v, want %v", got, tt.want) - } - }) - } -} From dd0eae91c4bf52a0224b0534a09bc0a9d5de07dd Mon Sep 17 00:00:00 2001 From: Rustam Gilyazov <16064414+rusq@users.noreply.github.com> Date: Fri, 29 Dec 2023 15:50:44 +1000 Subject: [PATCH 2/9] implement rod auth --- auth/auth_ui/cli.go | 30 +++++++++++++++ auth/option.go | 1 + auth/rod.go | 87 ++++++++++++++++++++++++++++++++++++++++++++ go.mod | 9 ++++- go.sum | 18 +++++++++ internal/app/auth.go | 6 ++- 6 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 auth/rod.go diff --git a/auth/auth_ui/cli.go b/auth/auth_ui/cli.go index 6a478669..f56b0680 100644 --- a/auth/auth_ui/cli.go +++ b/auth/auth_ui/cli.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/fatih/color" + "golang.org/x/term" ) type CLI struct{} @@ -32,6 +33,35 @@ func (cl *CLI) RequestWorkspace(w io.Writer) (string, error) { return workspace, nil } +func (cl *CLI) RequestEmail(w io.Writer) (string, error) { + fmt.Fprint(w, "Enter Email: ") + username, err := readln(os.Stdin) + if err != nil { + return "", err + } + return username, nil +} + +func (cl *CLI) RequestPassword(w io.Writer) (string, error) { + fmt.Fprint(w, "Enter Password (won't be visible): ") + password, err := term.ReadPassword(int(os.Stdin.Fd())) + if err != nil { + return "", err + } + fmt.Fprintln(w) + return string(password), nil +} + +func (cl *CLI) YesNo(w io.Writer, message string) (bool, error) { + fmt.Fprintf(w, "%s [y/N]: ", message) + answer, err := readln(os.Stdin) + if err != nil { + return false, err + } + answer = strings.ToLower(answer) + return answer == "y" || answer == "yes", nil +} + func (*CLI) Stop() {} func readln(r io.Reader) (string, error) { diff --git a/auth/option.go b/auth/option.go index da63a3ea..5e135e69 100644 --- a/auth/option.go +++ b/auth/option.go @@ -9,6 +9,7 @@ import ( type options struct { *browserOpts + *rodOpts } type Option func(*options) diff --git a/auth/rod.go b/auth/rod.go new file mode 100644 index 00000000..ad23e31a --- /dev/null +++ b/auth/rod.go @@ -0,0 +1,87 @@ +package auth + +import ( + "context" + "fmt" + "os" + + "github.com/rusq/slackauth" + "github.com/rusq/slackdump/v2/auth/auth_ui" +) + +type RodAuth struct { + simpleProvider + opts rodOpts +} + +func (p RodAuth) Type() Type { + return TypeRod +} + +type rodOpts struct { + ui BrowserAuthUIExt + workspace string +} + +func RodWithWorkspace(name string) Option { + return func(o *options) { + o.rodOpts.workspace = name + } +} + +func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) { + r := RodAuth{ + opts: rodOpts{ + ui: &auth_ui.CLI{}, + }, + } + for _, opt := range opts { + opt(&options{ + rodOpts: &r.opts, + }) + } + if r.opts.workspace == "" { + var err error + r.opts.workspace, err = r.opts.ui.RequestWorkspace(os.Stdout) + if err != nil { + return r, err + } + } + if wsp, err := sanitize(r.opts.workspace); err != nil { + return r, err + } else { + r.opts.workspace = wsp + } + yes, err := r.opts.ui.YesNo(os.Stdout, "Does your Slack Workspace use Single Sign On (i.e. Google, Okta, Auth0 etc.)?\n\tIf unsure, say yes") + if err != nil { + return r, err + } + var sp simpleProvider + if yes { + var err error + sp.Token, sp.Cookie, err = slackauth.Browser(ctx, r.opts.workspace) + if err != nil { + return r, err + } + } else { + var err error + username, err := r.opts.ui.RequestEmail(os.Stdout) + if err != nil { + return r, err + } + password, err := r.opts.ui.RequestPassword(os.Stdout) + if err != nil { + return r, err + } + fmt.Fprintln(os.Stderr, "Please wait while slackdump authenticates with Slack...") + sp.Token, sp.Cookie, err = slackauth.Headless(ctx, r.opts.workspace, username, password) + if err != nil { + return r, err + } + fmt.Fprintln(os.Stderr, "authenticated.") + } + + return RodAuth{ + simpleProvider: sp, + }, nil +} diff --git a/go.mod b/go.mod index f86624ad..fbda8eb7 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/rusq/slackdump/v2 -go 1.18 +go 1.21.4 require ( github.com/AlecAivazis/survey/v2 v2.3.7 @@ -26,6 +26,7 @@ require ( github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-jose/go-jose/v3 v3.0.1 // indirect + github.com/go-rod/rod v0.114.5 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect @@ -37,6 +38,12 @@ require ( github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect + github.com/rusq/slackauth v0.0.1 // indirect + github.com/ysmood/fetchup v0.2.4 // indirect + github.com/ysmood/goob v0.4.0 // indirect + github.com/ysmood/got v0.38.2 // indirect + github.com/ysmood/gson v0.7.3 // indirect + github.com/ysmood/leakless v0.8.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.17.0 // indirect golang.org/x/net v0.17.0 // indirect diff --git a/go.sum b/go.sum index c14478e8..496a5351 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/go-rod/rod v0.114.5 h1:1x6oqnslwFVuXJbJifgxspJUd3O4ntaGhRLHt+4Er9c= +github.com/go-rod/rod v0.114.5/go.mod h1:aiedSEFg5DwG/fnNbUOTPMTTWX3MRj6vIs/a684Mthw= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= @@ -73,6 +75,8 @@ github.com/rusq/osenv/v2 v2.0.1 h1:1LtNt8VNV/W86wb38Hyu5W3Rwqt/F1JNRGE+8GRu09o= github.com/rusq/osenv/v2 v2.0.1/go.mod h1:+wJBSisjNZpfoD961JzqjaM+PtaqSusO3b4oVJi7TFY= github.com/rusq/secure v0.0.4 h1:svpiZHfHnx89eEDCCFI9OXG1Y8hL9kUWUG6fJbrWUOI= github.com/rusq/secure v0.0.4/go.mod h1:F1QilMKreuFRjov0UY7DZSIXn77/8RqMVGu2zV0RtqY= +github.com/rusq/slackauth v0.0.1 h1:UYW/lMr+FMnKeax40L55VpY40Ekmsoc0MhScdL4fopc= +github.com/rusq/slackauth v0.0.1/go.mod h1:zb1PJY2+8uEqn0RiWuRjnd+ZFwwfnvA5xrGoooVUgNY= github.com/rusq/tracer v1.0.1 h1:5u4PCV8NGO97VuAINQA4gOVRkPoqHimLE2jpezRVNMU= github.com/rusq/tracer v1.0.1/go.mod h1:Rqu48C3/K8bA5NPmF20Hft73v431MQIdM+Co+113pME= github.com/schollz/progressbar/v3 v3.13.0 h1:9TeeWRcjW2qd05I8Kf9knPkW4vLM/hYoa6z9ABvxje8= @@ -85,6 +89,20 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= +github.com/ysmood/fetchup v0.2.4 h1:2kfWr/UrdiHg4KYRrxL2Jcrqx4DZYD+OtWu7WPBZl5o= +github.com/ysmood/fetchup v0.2.4/go.mod h1:hbysoq65PXL0NQeNzUczNYIKpwpkwFL4LXMDEvIQq9A= +github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= +github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= +github.com/ysmood/gop v0.0.2/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk= +github.com/ysmood/got v0.34.1/go.mod h1:yddyjq/PmAf08RMLSwDjPyCvHvYed+WjHnQxpH851LM= +github.com/ysmood/got v0.38.2 h1:h2RYvAe5nIK+oBRMLzNIrkZaX5kjmkOBqfRMIsFzLyU= +github.com/ysmood/got v0.38.2/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg= +github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= +github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= +github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= +github.com/ysmood/leakless v0.8.0 h1:BzLrVoiwxikpgEQR0Lk8NyBN5Cit2b1z+u0mgL4ZJak= +github.com/ysmood/leakless v0.8.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= diff --git a/internal/app/auth.go b/internal/app/auth.go index 8ae644ae..c9285817 100644 --- a/internal/app/auth.go +++ b/internal/app/auth.go @@ -55,9 +55,9 @@ func (c SlackCreds) Type(ctx context.Context) (auth.Type, error) { return auth.TypeInvalid, ErrUnsupported } if !ezLoginTested() { - return auth.TypeBrowser, ErrNotTested + return auth.TypeRod, ErrNotTested } - return auth.TypeBrowser, nil + return auth.TypeRod, nil } @@ -73,6 +73,8 @@ func (c SlackCreds) AuthProvider(ctx context.Context, workspace string, browser return nil, err } switch authType { + case auth.TypeRod: + return auth.NewRODAuth(ctx, auth.RodWithWorkspace(workspace)) case auth.TypeBrowser: return auth.NewBrowserAuth(ctx, auth.BrowserWithWorkspace(workspace), auth.BrowserWithBrowser(browser)) case auth.TypeCookieFile: From 7d266c2f5ce28b1c46213a707695ebc24b0d1b24 Mon Sep 17 00:00:00 2001 From: Rustam Gilyazov <16064414+rusq@users.noreply.github.com> Date: Sat, 30 Dec 2023 11:09:15 +1000 Subject: [PATCH 3/9] switch to uber/gomock, disable survey auth UI --- auth/auth.go | 2 +- auth/auth_ui/survey.go | 2 + auth/browser.go | 2 +- auth/browser/extractors_test.go | 2 +- auth/browser/playwright_test.go | 14 ++- auth/rod.go | 6 +- auth/type_string.go | 5 +- channels_test.go | 2 +- clienter_mock_test.go | 28 +++-- cmd/slackdump/main.go | 4 +- downloader/downloader_test.go | 2 +- emoji_test.go | 2 +- export/export_test.go | 2 +- export/mock_dumper_test.go | 20 +-- go.mod | 5 +- go.sum | 9 ++ internal/app/auth.go | 20 +-- internal/app/auth_test.go | 16 +-- internal/app/emoji/emoji_mock_test.go | 10 +- internal/app/emoji/emoji_test.go | 2 +- internal/mocks/mock_app/mock_app.go | 20 +-- internal/mocks/mock_auth/mock_auth.go | 25 +++- internal/mocks/mock_dl/mock_exporter.go | 12 +- .../mocks/mock_downloader/mock_downloader.go | 10 +- internal/mocks/mock_fsadapter/mock_fs.go | 12 +- internal/mocks/mock_io/mock_io.go | 12 +- internal/mocks/mock_os/mock_os.go | 12 +- messages_test.go | 2 +- mocks/mock_io/mock_io.go | 115 ------------------ slackdump_test.go | 2 +- thread_test.go | 2 +- tools/rawoutput/main.go | 2 +- users_test.go | 2 +- 33 files changed, 173 insertions(+), 210 deletions(-) delete mode 100644 mocks/mock_io/mock_io.go diff --git a/auth/auth.go b/auth/auth.go index e834fb50..f55aa6d6 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -27,7 +27,7 @@ const ( TypeValue // Value TypeCookieFile // Cookie File TypeBrowser // EZ-Login 3000 - TypeRod + TypeRod // EZ-Login 3001 ) // Provider is the Slack Authentication provider. diff --git a/auth/auth_ui/survey.go b/auth/auth_ui/survey.go index e181a771..7616e080 100644 --- a/auth/auth_ui/survey.go +++ b/auth/auth_ui/survey.go @@ -1,3 +1,5 @@ +//go:build ignore + package auth_ui import ( diff --git a/auth/browser.go b/auth/browser.go index 271df222..a70f7363 100644 --- a/auth/browser.go +++ b/auth/browser.go @@ -11,7 +11,7 @@ import ( ) var _ Provider = BrowserAuth{} -var defaultFlow = &auth_ui.Survey{} +var defaultFlow = &auth_ui.CLI{} type BrowserAuth struct { simpleProvider diff --git a/auth/browser/extractors_test.go b/auth/browser/extractors_test.go index 8e221d66..5ac432e5 100644 --- a/auth/browser/extractors_test.go +++ b/auth/browser/extractors_test.go @@ -4,7 +4,7 @@ import ( "errors" "testing" - gomock "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" ) const testMultipart = "-----------------------------37168696061856579082739228613\r\nContent-Disposition: form-data; name=\"token\"\r\n\r\nxoxc-888888888888-888888888888-8888888888888-fffffffffffffffa915fe069d70a8ad81743b0ec4ee9c81540af43f5e143264b\r\n-----------------------------37168696061856579082739228613\r\nContent-Disposition: form-data; name=\"platform\"\r\n\r\nsonic\r\n-----------------------------37168696061856579082739228613\r\nContent-Disposition: form-data; name=\"_x_should_cache\"\r\n\r\nfalse\r\n-----------------------------37168696061856579082739228613\r\nContent-Disposition: form-data; name=\"_x_allow_cached\"\r\n\r\ntrue\r\n-----------------------------37168696061856579082739228613\r\nContent-Disposition: form-data; name=\"_x_team_id\"\r\n\r\nTFCSDNRL5\r\n-----------------------------37168696061856579082739228613\r\nContent-Disposition: form-data; name=\"_x_gantry\"\r\n\r\ntrue\r\n-----------------------------37168696061856579082739228613\r\nContent-Disposition: form-data; name=\"_x_sonic\"\r\n\r\ntrue\r\n-----------------------------37168696061856579082739228613--\r\n" diff --git a/auth/browser/playwright_test.go b/auth/browser/playwright_test.go index d07b602b..0ee662e4 100644 --- a/auth/browser/playwright_test.go +++ b/auth/browser/playwright_test.go @@ -1,14 +1,18 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/playwright-community/playwright-go (interfaces: Request) - +// +// Generated by this command: +// +// mockgen -package browser -destination playwright_test.go github.com/playwright-community/playwright-go Request +// // Package browser is a generated GoMock package. package browser import ( reflect "reflect" - gomock "github.com/golang/mock/gomock" playwright "github.com/playwright-community/playwright-go" + gomock "go.uber.org/mock/gomock" ) // MockRequest is a mock of Request interface. @@ -87,7 +91,7 @@ func (m *MockRequest) HeaderValue(arg0 string) (string, error) { } // HeaderValue indicates an expected call of HeaderValue. -func (mr *MockRequestMockRecorder) HeaderValue(arg0 interface{}) *gomock.Call { +func (mr *MockRequestMockRecorder) HeaderValue(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HeaderValue", reflect.TypeOf((*MockRequest)(nil).HeaderValue), arg0) } @@ -180,7 +184,7 @@ func (mr *MockRequestMockRecorder) PostDataBuffer() *gomock.Call { } // PostDataJSON mocks base method. -func (m *MockRequest) PostDataJSON(arg0 interface{}) error { +func (m *MockRequest) PostDataJSON(arg0 any) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PostDataJSON", arg0) ret0, _ := ret[0].(error) @@ -188,7 +192,7 @@ func (m *MockRequest) PostDataJSON(arg0 interface{}) error { } // PostDataJSON indicates an expected call of PostDataJSON. -func (mr *MockRequestMockRecorder) PostDataJSON(arg0 interface{}) *gomock.Call { +func (mr *MockRequestMockRecorder) PostDataJSON(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PostDataJSON", reflect.TypeOf((*MockRequest)(nil).PostDataJSON), arg0) } diff --git a/auth/rod.go b/auth/rod.go index ad23e31a..93530bb1 100644 --- a/auth/rod.go +++ b/auth/rod.go @@ -52,12 +52,12 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) { } else { r.opts.workspace = wsp } - yes, err := r.opts.ui.YesNo(os.Stdout, "Does your Slack Workspace use Single Sign On (i.e. Google, Okta, Auth0 etc.)?\n\tIf unsure, say yes") + usesEmail, err := r.opts.ui.YesNo(os.Stdout, "Do you login with your email/password into Slack (i.e. not using Google or SSO to login)?") if err != nil { return r, err } var sp simpleProvider - if yes { + if !usesEmail { var err error sp.Token, sp.Cookie, err = slackauth.Browser(ctx, r.opts.workspace) if err != nil { @@ -73,7 +73,7 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) { if err != nil { return r, err } - fmt.Fprintln(os.Stderr, "Please wait while slackdump authenticates with Slack...") + fmt.Fprintln(os.Stderr, "Please wait while Slackdump logs into Slack...") sp.Token, sp.Cookie, err = slackauth.Headless(ctx, r.opts.workspace, username, password) if err != nil { return r, err diff --git a/auth/type_string.go b/auth/type_string.go index fffecdf3..fdaf0045 100644 --- a/auth/type_string.go +++ b/auth/type_string.go @@ -12,11 +12,12 @@ func _() { _ = x[TypeValue-1] _ = x[TypeCookieFile-2] _ = x[TypeBrowser-3] + _ = x[TypeRod-4] } -const _Type_name = "InvalidValueCookie FileEZ-Login 3000" +const _Type_name = "InvalidValueCookie FileEZ-Login 3000EZ-Login 3001" -var _Type_index = [...]uint8{0, 7, 12, 23, 36} +var _Type_index = [...]uint8{0, 7, 12, 23, 36, 49} func (i Type) String() string { if i >= Type(len(_Type_index)-1) { diff --git a/channels_test.go b/channels_test.go index 846482be..6d503941 100644 --- a/channels_test.go +++ b/channels_test.go @@ -6,12 +6,12 @@ import ( "reflect" "testing" - "github.com/golang/mock/gomock" "github.com/rusq/slackdump/v2/fsadapter" "github.com/rusq/slackdump/v2/internal/structures" "github.com/rusq/slackdump/v2/types" "github.com/slack-go/slack" "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" ) func TestSession_getChannels(t *testing.T) { diff --git a/clienter_mock_test.go b/clienter_mock_test.go index 32da7a8f..15242138 100644 --- a/clienter_mock_test.go +++ b/clienter_mock_test.go @@ -1,6 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: slackdump.go - +// +// Generated by this command: +// +// mockgen -source slackdump.go -destination clienter_mock_test.go -package slackdump -mock_names clienter=mockClienter,Reporter=mockReporter +// // Package slackdump is a generated GoMock package. package slackdump @@ -9,8 +13,8 @@ import ( io "io" reflect "reflect" - gomock "github.com/golang/mock/gomock" slack "github.com/slack-go/slack" + gomock "go.uber.org/mock/gomock" ) // mockClienter is a mock of clienter interface. @@ -46,7 +50,7 @@ func (m *mockClienter) GetConversationHistoryContext(ctx context.Context, params } // GetConversationHistoryContext indicates an expected call of GetConversationHistoryContext. -func (mr *mockClienterMockRecorder) GetConversationHistoryContext(ctx, params interface{}) *gomock.Call { +func (mr *mockClienterMockRecorder) GetConversationHistoryContext(ctx, params any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConversationHistoryContext", reflect.TypeOf((*mockClienter)(nil).GetConversationHistoryContext), ctx, params) } @@ -61,7 +65,7 @@ func (m *mockClienter) GetConversationInfoContext(ctx context.Context, input *sl } // GetConversationInfoContext indicates an expected call of GetConversationInfoContext. -func (mr *mockClienterMockRecorder) GetConversationInfoContext(ctx, input interface{}) *gomock.Call { +func (mr *mockClienterMockRecorder) GetConversationInfoContext(ctx, input any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConversationInfoContext", reflect.TypeOf((*mockClienter)(nil).GetConversationInfoContext), ctx, input) } @@ -78,7 +82,7 @@ func (m *mockClienter) GetConversationRepliesContext(ctx context.Context, params } // GetConversationRepliesContext indicates an expected call of GetConversationRepliesContext. -func (mr *mockClienterMockRecorder) GetConversationRepliesContext(ctx, params interface{}) *gomock.Call { +func (mr *mockClienterMockRecorder) GetConversationRepliesContext(ctx, params any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConversationRepliesContext", reflect.TypeOf((*mockClienter)(nil).GetConversationRepliesContext), ctx, params) } @@ -94,7 +98,7 @@ func (m *mockClienter) GetConversationsContext(ctx context.Context, params *slac } // GetConversationsContext indicates an expected call of GetConversationsContext. -func (mr *mockClienterMockRecorder) GetConversationsContext(ctx, params interface{}) *gomock.Call { +func (mr *mockClienterMockRecorder) GetConversationsContext(ctx, params any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetConversationsContext", reflect.TypeOf((*mockClienter)(nil).GetConversationsContext), ctx, params) } @@ -109,7 +113,7 @@ func (m *mockClienter) GetEmojiContext(ctx context.Context) (map[string]string, } // GetEmojiContext indicates an expected call of GetEmojiContext. -func (mr *mockClienterMockRecorder) GetEmojiContext(ctx interface{}) *gomock.Call { +func (mr *mockClienterMockRecorder) GetEmojiContext(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEmojiContext", reflect.TypeOf((*mockClienter)(nil).GetEmojiContext), ctx) } @@ -123,7 +127,7 @@ func (m *mockClienter) GetFile(downloadURL string, writer io.Writer) error { } // GetFile indicates an expected call of GetFile. -func (mr *mockClienterMockRecorder) GetFile(downloadURL, writer interface{}) *gomock.Call { +func (mr *mockClienterMockRecorder) GetFile(downloadURL, writer any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFile", reflect.TypeOf((*mockClienter)(nil).GetFile), downloadURL, writer) } @@ -146,7 +150,7 @@ func (mr *mockClienterMockRecorder) GetTeamInfo() *gomock.Call { // GetUsersContext mocks base method. func (m *mockClienter) GetUsersContext(ctx context.Context, options ...slack.GetUsersOption) ([]slack.User, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx} + varargs := []any{ctx} for _, a := range options { varargs = append(varargs, a) } @@ -157,9 +161,9 @@ func (m *mockClienter) GetUsersContext(ctx context.Context, options ...slack.Get } // GetUsersContext indicates an expected call of GetUsersContext. -func (mr *mockClienterMockRecorder) GetUsersContext(ctx interface{}, options ...interface{}) *gomock.Call { +func (mr *mockClienterMockRecorder) GetUsersContext(ctx any, options ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx}, options...) + varargs := append([]any{ctx}, options...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUsersContext", reflect.TypeOf((*mockClienter)(nil).GetUsersContext), varargs...) } @@ -174,7 +178,7 @@ func (m *mockClienter) GetUsersInConversationContext(ctx context.Context, params } // GetUsersInConversationContext indicates an expected call of GetUsersInConversationContext. -func (mr *mockClienterMockRecorder) GetUsersInConversationContext(ctx, params interface{}) *gomock.Call { +func (mr *mockClienterMockRecorder) GetUsersInConversationContext(ctx, params any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUsersInConversationContext", reflect.TypeOf((*mockClienter)(nil).GetUsersInConversationContext), ctx, params) } diff --git a/cmd/slackdump/main.go b/cmd/slackdump/main.go index 9f6a51bc..a506285c 100644 --- a/cmd/slackdump/main.go +++ b/cmd/slackdump/main.go @@ -59,6 +59,7 @@ type params struct { browser browser.Browser browserTimeout time.Duration browserReinstall bool + legacyBrowser bool // TODO: remove once the new browser is tested traceFile string // trace file logFile string //log file, if not specified, outputs to stderr. @@ -142,7 +143,7 @@ func run(ctx context.Context, p params) error { } } - provider, err := app.InitProvider(ctx, p.appCfg.Options.CacheDir, p.workspace, p.creds, p.browser) + provider, err := app.InitProvider(ctx, p.appCfg.Options.CacheDir, p.workspace, p.creds, p.browser, p.legacyBrowser) if err != nil { return err } else { @@ -266,6 +267,7 @@ func parseCmdLine(args []string) (params, error) { fs.DurationVar(&p.browserTimeout, "browser-timeout", browser.DefLoginTimeout, "browser login timeout") fs.StringVar(&p.workspace, "w", "", "set the Slack `workspace` name. If not specifed, the slackdump will show an\ninteractive prompt.") fs.BoolVar(&p.browserReinstall, "browser-reinstall", false, "reinstall the playwright browser") + fs.BoolVar(&p.legacyBrowser, "legacy-browser", false, "use the legacy browser authentication method") // operation mode fs.BoolVar(&p.appCfg.ListFlags.Channels, "c", false, "same as -list-channels") diff --git a/downloader/downloader_test.go b/downloader/downloader_test.go index 6636b549..b459c7ec 100644 --- a/downloader/downloader_test.go +++ b/downloader/downloader_test.go @@ -10,10 +10,10 @@ import ( "errors" - gomock "github.com/golang/mock/gomock" "github.com/slack-go/slack" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" "golang.org/x/time/rate" "github.com/rusq/slackdump/v2/fsadapter" diff --git a/emoji_test.go b/emoji_test.go index 51531ebc..a62c148d 100644 --- a/emoji_test.go +++ b/emoji_test.go @@ -6,7 +6,7 @@ import ( "reflect" "testing" - "github.com/golang/mock/gomock" + "go.uber.org/mock/gomock" ) func TestSession_DumpEmojis(t *testing.T) { diff --git a/export/export_test.go b/export/export_test.go index e5808e32..e30d354f 100644 --- a/export/export_test.go +++ b/export/export_test.go @@ -13,7 +13,6 @@ import ( "testing" "time" - gomock "github.com/golang/mock/gomock" "github.com/rusq/slackdump/v2" "github.com/rusq/slackdump/v2/fsadapter" "github.com/rusq/slackdump/v2/internal/fixtures" @@ -24,6 +23,7 @@ import ( "github.com/rusq/slackdump/v2/types" "github.com/slack-go/slack" "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" ) func TestExport_saveChannel(t *testing.T) { diff --git a/export/mock_dumper_test.go b/export/mock_dumper_test.go index 2938ae77..0a54d9c3 100644 --- a/export/mock_dumper_test.go +++ b/export/mock_dumper_test.go @@ -1,6 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: future.go - +// +// Generated by this command: +// +// mockgen -destination=mock_dumper_test.go -source=future.go -package=export dumper +// // Package export is a generated GoMock package. package export @@ -9,10 +13,10 @@ import ( reflect "reflect" time "time" - gomock "github.com/golang/mock/gomock" slackdump "github.com/rusq/slackdump/v2" types "github.com/rusq/slackdump/v2/types" slack "github.com/slack-go/slack" + gomock "go.uber.org/mock/gomock" ) // Mockdumper is a mock of dumper interface. @@ -69,7 +73,7 @@ func (mr *MockdumperMockRecorder) CurrentUserID() *gomock.Call { // DumpRaw mocks base method. func (m *Mockdumper) DumpRaw(ctx context.Context, link string, oldest, latest time.Time, processFn ...slackdump.ProcessFunc) (*types.Conversation, error) { m.ctrl.T.Helper() - varargs := []interface{}{ctx, link, oldest, latest} + varargs := []any{ctx, link, oldest, latest} for _, a := range processFn { varargs = append(varargs, a) } @@ -80,9 +84,9 @@ func (m *Mockdumper) DumpRaw(ctx context.Context, link string, oldest, latest ti } // DumpRaw indicates an expected call of DumpRaw. -func (mr *MockdumperMockRecorder) DumpRaw(ctx, link, oldest, latest interface{}, processFn ...interface{}) *gomock.Call { +func (mr *MockdumperMockRecorder) DumpRaw(ctx, link, oldest, latest any, processFn ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{ctx, link, oldest, latest}, processFn...) + varargs := append([]any{ctx, link, oldest, latest}, processFn...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DumpRaw", reflect.TypeOf((*Mockdumper)(nil).DumpRaw), varargs...) } @@ -96,7 +100,7 @@ func (m *Mockdumper) GetChannelMembers(ctx context.Context, channelID string) ([ } // GetChannelMembers indicates an expected call of GetChannelMembers. -func (mr *MockdumperMockRecorder) GetChannelMembers(ctx, channelID interface{}) *gomock.Call { +func (mr *MockdumperMockRecorder) GetChannelMembers(ctx, channelID any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetChannelMembers", reflect.TypeOf((*Mockdumper)(nil).GetChannelMembers), ctx, channelID) } @@ -111,7 +115,7 @@ func (m *Mockdumper) GetUsers(ctx context.Context) (types.Users, error) { } // GetUsers indicates an expected call of GetUsers. -func (mr *MockdumperMockRecorder) GetUsers(ctx interface{}) *gomock.Call { +func (mr *MockdumperMockRecorder) GetUsers(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUsers", reflect.TypeOf((*Mockdumper)(nil).GetUsers), ctx) } @@ -125,7 +129,7 @@ func (m *Mockdumper) StreamChannels(ctx context.Context, chanTypes []string, cb } // StreamChannels indicates an expected call of StreamChannels. -func (mr *MockdumperMockRecorder) StreamChannels(ctx, chanTypes, cb interface{}) *gomock.Call { +func (mr *MockdumperMockRecorder) StreamChannels(ctx, chanTypes, cb any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamChannels", reflect.TypeOf((*Mockdumper)(nil).StreamChannels), ctx, chanTypes, cb) } diff --git a/go.mod b/go.mod index fbda8eb7..747afcb7 100644 --- a/go.mod +++ b/go.mod @@ -14,11 +14,14 @@ require ( github.com/rusq/dlog v1.4.0 github.com/rusq/osenv/v2 v2.0.1 github.com/rusq/secure v0.0.4 + github.com/rusq/slackauth v0.0.1 github.com/rusq/tracer v1.0.1 github.com/schollz/progressbar/v3 v3.13.0 github.com/slack-go/slack v0.12.1 github.com/stretchr/testify v1.8.4 + go.uber.org/mock v0.4.0 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 + golang.org/x/term v0.15.0 golang.org/x/time v0.3.0 ) @@ -38,7 +41,6 @@ require ( github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect - github.com/rusq/slackauth v0.0.1 // indirect github.com/ysmood/fetchup v0.2.4 // indirect github.com/ysmood/goob v0.4.0 // indirect github.com/ysmood/got v0.38.2 // indirect @@ -48,7 +50,6 @@ require ( golang.org/x/crypto v0.17.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 496a5351..8ce1f8ac 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,7 @@ github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -58,6 +59,7 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyex github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/playwright-community/playwright-go v0.4001.0 h1:2cBiTIjCvFu7zUrZ48C0YC2DIp90Tbudueq4brUGjHM= github.com/playwright-community/playwright-go v0.4001.0/go.mod h1:quEkYFrvvpQyGSxBjnYbGS52vrUDB2uaY1cOzkkSHCc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -67,6 +69,7 @@ github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rusq/chttp v1.0.2 h1:bc8FTKE/l318Kie3sb2KrGi7Fu5tSDQY+JiXMsq4fO8= github.com/rusq/chttp v1.0.2/go.mod h1:bmuoQMUFs9fmigUmT7xbp8s0rHyzUrf7+78yLklr1so= github.com/rusq/dlog v1.4.0 h1:64oHTSzHjzG6TXKvMbPKQzvqADCZRn6XgAWnp7ASr5k= @@ -95,9 +98,12 @@ github.com/ysmood/fetchup v0.2.4/go.mod h1:hbysoq65PXL0NQeNzUczNYIKpwpkwFL4LXMDE github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= github.com/ysmood/gop v0.0.2/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk= +github.com/ysmood/gop v0.2.0 h1:+tFrG0TWPxT6p9ZaZs+VY+opCvHU8/3Fk6BaNv6kqKg= +github.com/ysmood/gop v0.2.0/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk= github.com/ysmood/got v0.34.1/go.mod h1:yddyjq/PmAf08RMLSwDjPyCvHvYed+WjHnQxpH851LM= github.com/ysmood/got v0.38.2 h1:h2RYvAe5nIK+oBRMLzNIrkZaX5kjmkOBqfRMIsFzLyU= github.com/ysmood/got v0.38.2/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg= +github.com/ysmood/gotrace v0.6.0 h1:SyI1d4jclswLhg7SWTL6os3L1WOKeNn/ZtzVQF8QmdY= github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= @@ -105,6 +111,8 @@ github.com/ysmood/leakless v0.8.0 h1:BzLrVoiwxikpgEQR0Lk8NyBN5Cit2b1z+u0mgL4ZJak github.com/ysmood/leakless v0.8.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -163,6 +171,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/app/auth.go b/internal/app/auth.go index c9285817..7822b079 100644 --- a/internal/app/auth.go +++ b/internal/app/auth.go @@ -43,7 +43,11 @@ var ( // this unfortunate fact could be relayed to the end-user. If the type of the // authentication determined is not supported for the current system, it will // return ErrUnsupported. -func (c SlackCreds) Type(ctx context.Context) (auth.Type, error) { +func (c SlackCreds) Type(ctx context.Context, legacy bool) (auth.Type, error) { + var browserAuth = auth.TypeRod + if legacy { + browserAuth = auth.TypeBrowser + } if !c.IsEmpty() { if isExistingFile(c.Cookie) { return auth.TypeCookieFile, nil @@ -55,9 +59,9 @@ func (c SlackCreds) Type(ctx context.Context) (auth.Type, error) { return auth.TypeInvalid, ErrUnsupported } if !ezLoginTested() { - return auth.TypeRod, ErrNotTested + return browserAuth, ErrNotTested } - return auth.TypeRod, nil + return browserAuth, nil } @@ -67,8 +71,8 @@ func (c SlackCreds) IsEmpty() bool { // AuthProvider returns the appropriate auth Provider depending on the values // of the token and cookie. -func (c SlackCreds) AuthProvider(ctx context.Context, workspace string, browser browser.Browser) (auth.Provider, error) { - authType, err := c.Type(ctx) +func (c SlackCreds) AuthProvider(ctx context.Context, workspace string, browser browser.Browser, legacy bool) (auth.Provider, error) { + authType, err := c.Type(ctx, legacy) if err != nil { return nil, err } @@ -108,7 +112,7 @@ var filer createOpener = encryptedFile{} type Credentials interface { IsEmpty() bool - AuthProvider(ctx context.Context, workspace string, browser browser.Browser) (auth.Provider, error) + AuthProvider(ctx context.Context, workspace string, browser browser.Browser, legacy bool) (auth.Provider, error) } // InitProvider initialises the auth.Provider depending on provided slack @@ -126,7 +130,7 @@ type Credentials interface { // virtual), even another operating system on the same machine, unless it's a // clone of the source operating system on which the credentials storage was // created. -func InitProvider(ctx context.Context, cacheDir string, workspace string, creds Credentials, browser browser.Browser) (auth.Provider, error) { +func InitProvider(ctx context.Context, cacheDir string, workspace string, creds Credentials, browser browser.Browser, legacy bool) (auth.Provider, error) { ctx, task := trace.NewTask(ctx, "InitProvider") defer task.End() @@ -148,7 +152,7 @@ func InitProvider(ctx context.Context, cacheDir string, workspace string, creds // init the authentication provider trace.Log(ctx, "info", "getting credentals from file or browser") - provider, err := creds.AuthProvider(ctx, workspace, browser) + provider, err := creds.AuthProvider(ctx, workspace, browser, legacy) if err != nil { return nil, fmt.Errorf("failed to initialise the auth provider: %w", err) } diff --git a/internal/app/auth_test.go b/internal/app/auth_test.go index 426572b8..01b0a2cd 100644 --- a/internal/app/auth_test.go +++ b/internal/app/auth_test.go @@ -9,12 +9,12 @@ import ( "reflect" "testing" - "github.com/golang/mock/gomock" "github.com/rusq/slackdump/v2/auth" "github.com/rusq/slackdump/v2/auth/browser" "github.com/rusq/slackdump/v2/internal/mocks/mock_app" "github.com/rusq/slackdump/v2/internal/mocks/mock_io" "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" ) func Test_isExistingFile(t *testing.T) { @@ -76,7 +76,7 @@ func TestSlackCreds_Type(t *testing.T) { Token: tt.fields.Token, Cookie: tt.fields.Cookie, } - got, err := c.Type(tt.args.ctx) + got, err := c.Type(tt.args.ctx, true) if (err != nil) != tt.wantErr { t.Errorf("SlackCreds.Type() error = %v, wantErr %v", err, tt.wantErr) return @@ -149,7 +149,7 @@ func TestInitProvider(t *testing.T) { func(m *mock_app.MockCredentials) { m.EXPECT().IsEmpty().Return(false) m.EXPECT(). - AuthProvider(gomock.Any(), "wsp", browser.Bfirefox). + AuthProvider(gomock.Any(), "wsp", browser.Bfirefox, true). Return(storedProv, nil) }, nil, //not used in the test @@ -171,7 +171,7 @@ func TestInitProvider(t *testing.T) { args{context.Background(), testDir, "wsp"}, func(m *mock_app.MockCredentials) { m.EXPECT().IsEmpty().Return(true) - m.EXPECT().AuthProvider(gomock.Any(), "wsp", browser.Bfirefox).Return(returnedProv, nil) + m.EXPECT().AuthProvider(gomock.Any(), "wsp", browser.Bfirefox, true).Return(returnedProv, nil) }, errors.New("auth test fail"), // auth test fails returnedProv, @@ -182,7 +182,7 @@ func TestInitProvider(t *testing.T) { args{context.Background(), testDir, "wsp"}, func(m *mock_app.MockCredentials) { m.EXPECT().IsEmpty().Return(false) - m.EXPECT().AuthProvider(gomock.Any(), "wsp", browser.Bfirefox).Return(nil, errors.New("authProvider failed")) + m.EXPECT().AuthProvider(gomock.Any(), "wsp", browser.Bfirefox, true).Return(nil, errors.New("authProvider failed")) }, nil, nil, @@ -193,7 +193,7 @@ func TestInitProvider(t *testing.T) { args{context.Background(), testDir, "wsp"}, func(m *mock_app.MockCredentials) { m.EXPECT().IsEmpty().Return(false) - m.EXPECT().AuthProvider(gomock.Any(), "wsp", browser.Bfirefox).Return(returnedProv, nil) + m.EXPECT().AuthProvider(gomock.Any(), "wsp", browser.Bfirefox, true).Return(returnedProv, nil) }, nil, returnedProv, @@ -204,7 +204,7 @@ func TestInitProvider(t *testing.T) { args{context.Background(), t.TempDir() + "$", "wsp"}, func(m *mock_app.MockCredentials) { m.EXPECT().IsEmpty().Return(false) - m.EXPECT().AuthProvider(gomock.Any(), "wsp", browser.Bfirefox).Return(returnedProv, nil) + m.EXPECT().AuthProvider(gomock.Any(), "wsp", browser.Bfirefox, true).Return(returnedProv, nil) }, nil, returnedProv, @@ -230,7 +230,7 @@ func TestInitProvider(t *testing.T) { tt.expect(mc) // test - got, err := InitProvider(tt.args.ctx, tt.args.cacheDir, tt.args.workspace, mc, browser.Bfirefox) + got, err := InitProvider(tt.args.ctx, tt.args.cacheDir, tt.args.workspace, mc, browser.Bfirefox, true) if (err != nil) != tt.wantErr { t.Errorf("InitProvider() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/internal/app/emoji/emoji_mock_test.go b/internal/app/emoji/emoji_mock_test.go index 111a2afc..5fd39288 100644 --- a/internal/app/emoji/emoji_mock_test.go +++ b/internal/app/emoji/emoji_mock_test.go @@ -1,6 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: emoji.go - +// +// Generated by this command: +// +// mockgen -source emoji.go -destination emoji_mock_test.go -package emoji +// // Package emoji is a generated GoMock package. package emoji @@ -8,7 +12,7 @@ import ( context "context" reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // Mockemojidumper is a mock of emojidumper interface. @@ -44,7 +48,7 @@ func (m *Mockemojidumper) DumpEmojis(ctx context.Context) (map[string]string, er } // DumpEmojis indicates an expected call of DumpEmojis. -func (mr *MockemojidumperMockRecorder) DumpEmojis(ctx interface{}) *gomock.Call { +func (mr *MockemojidumperMockRecorder) DumpEmojis(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DumpEmojis", reflect.TypeOf((*Mockemojidumper)(nil).DumpEmojis), ctx) } diff --git a/internal/app/emoji/emoji_test.go b/internal/app/emoji/emoji_test.go index bba6e5bc..4478ad07 100644 --- a/internal/app/emoji/emoji_test.go +++ b/internal/app/emoji/emoji_test.go @@ -15,8 +15,8 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" "github.com/rusq/slackdump/v2/fsadapter" + "go.uber.org/mock/gomock" ) type fetchFunc func(ctx context.Context, fsa fsadapter.FS, dir string, name string, uri string) error diff --git a/internal/mocks/mock_app/mock_app.go b/internal/mocks/mock_app/mock_app.go index 49c9080c..7d73c1a7 100644 --- a/internal/mocks/mock_app/mock_app.go +++ b/internal/mocks/mock_app/mock_app.go @@ -1,6 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: auth.go - +// +// Generated by this command: +// +// mockgen -source=auth.go -destination=../mocks/mock_app/mock_app.go Credentials,createOpener +// // Package mock_app is a generated GoMock package. package mock_app @@ -9,9 +13,9 @@ import ( io "io" reflect "reflect" - gomock "github.com/golang/mock/gomock" auth "github.com/rusq/slackdump/v2/auth" browser "github.com/rusq/slackdump/v2/auth/browser" + gomock "go.uber.org/mock/gomock" ) // MockCredentials is a mock of Credentials interface. @@ -38,18 +42,18 @@ func (m *MockCredentials) EXPECT() *MockCredentialsMockRecorder { } // AuthProvider mocks base method. -func (m *MockCredentials) AuthProvider(ctx context.Context, workspace string, browser browser.Browser) (auth.Provider, error) { +func (m *MockCredentials) AuthProvider(ctx context.Context, workspace string, browser browser.Browser, legacy bool) (auth.Provider, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AuthProvider", ctx, workspace, browser) + ret := m.ctrl.Call(m, "AuthProvider", ctx, workspace, browser, legacy) ret0, _ := ret[0].(auth.Provider) ret1, _ := ret[1].(error) return ret0, ret1 } // AuthProvider indicates an expected call of AuthProvider. -func (mr *MockCredentialsMockRecorder) AuthProvider(ctx, workspace, browser interface{}) *gomock.Call { +func (mr *MockCredentialsMockRecorder) AuthProvider(ctx, workspace, browser, legacy any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthProvider", reflect.TypeOf((*MockCredentials)(nil).AuthProvider), ctx, workspace, browser) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthProvider", reflect.TypeOf((*MockCredentials)(nil).AuthProvider), ctx, workspace, browser, legacy) } // IsEmpty mocks base method. @@ -99,7 +103,7 @@ func (m *MockcreateOpener) Create(arg0 string) (io.WriteCloser, error) { } // Create indicates an expected call of Create. -func (mr *MockcreateOpenerMockRecorder) Create(arg0 interface{}) *gomock.Call { +func (mr *MockcreateOpenerMockRecorder) Create(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockcreateOpener)(nil).Create), arg0) } @@ -114,7 +118,7 @@ func (m *MockcreateOpener) Open(arg0 string) (io.ReadCloser, error) { } // Open indicates an expected call of Open. -func (mr *MockcreateOpenerMockRecorder) Open(arg0 interface{}) *gomock.Call { +func (mr *MockcreateOpenerMockRecorder) Open(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockcreateOpener)(nil).Open), arg0) } diff --git a/internal/mocks/mock_auth/mock_auth.go b/internal/mocks/mock_auth/mock_auth.go index 5ddd654c..17b35980 100644 --- a/internal/mocks/mock_auth/mock_auth.go +++ b/internal/mocks/mock_auth/mock_auth.go @@ -1,6 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/rusq/slackdump/v2/auth (interfaces: Provider) - +// +// Generated by this command: +// +// mockgen -destination ../internal/mocks/mock_auth/mock_auth.go github.com/rusq/slackdump/v2/auth Provider +// // Package mock_auth is a generated GoMock package. package mock_auth @@ -9,8 +13,8 @@ import ( http "net/http" reflect "reflect" - gomock "github.com/golang/mock/gomock" auth "github.com/rusq/slackdump/v2/auth" + gomock "go.uber.org/mock/gomock" ) // MockProvider is a mock of Provider interface. @@ -50,6 +54,21 @@ func (mr *MockProviderMockRecorder) Cookies() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cookies", reflect.TypeOf((*MockProvider)(nil).Cookies)) } +// HTTPClient mocks base method. +func (m *MockProvider) HTTPClient() (*http.Client, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HTTPClient") + ret0, _ := ret[0].(*http.Client) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// HTTPClient indicates an expected call of HTTPClient. +func (mr *MockProviderMockRecorder) HTTPClient() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HTTPClient", reflect.TypeOf((*MockProvider)(nil).HTTPClient)) +} + // SlackToken mocks base method. func (m *MockProvider) SlackToken() string { m.ctrl.T.Helper() @@ -73,7 +92,7 @@ func (m *MockProvider) Test(arg0 context.Context) error { } // Test indicates an expected call of Test. -func (mr *MockProviderMockRecorder) Test(arg0 interface{}) *gomock.Call { +func (mr *MockProviderMockRecorder) Test(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Test", reflect.TypeOf((*MockProvider)(nil).Test), arg0) } diff --git a/internal/mocks/mock_dl/mock_exporter.go b/internal/mocks/mock_dl/mock_exporter.go index 2988470e..ccbefe95 100644 --- a/internal/mocks/mock_dl/mock_exporter.go +++ b/internal/mocks/mock_dl/mock_exporter.go @@ -1,6 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/rusq/slackdump/v2/internal/structures/files/dl (interfaces: Exporter) - +// +// Generated by this command: +// +// mockgen -destination ../../../../internal/mocks/mock_dl/mock_exporter.go github.com/rusq/slackdump/v2/internal/structures/files/dl Exporter +// // Package mock_dl is a generated GoMock package. package mock_dl @@ -8,8 +12,8 @@ import ( context "context" reflect "reflect" - gomock "github.com/golang/mock/gomock" slackdump "github.com/rusq/slackdump/v2" + gomock "go.uber.org/mock/gomock" ) // MockExporter is a mock of Exporter interface. @@ -44,7 +48,7 @@ func (m *MockExporter) ProcessFunc(arg0 string) slackdump.ProcessFunc { } // ProcessFunc indicates an expected call of ProcessFunc. -func (mr *MockExporterMockRecorder) ProcessFunc(arg0 interface{}) *gomock.Call { +func (mr *MockExporterMockRecorder) ProcessFunc(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProcessFunc", reflect.TypeOf((*MockExporter)(nil).ProcessFunc), arg0) } @@ -56,7 +60,7 @@ func (m *MockExporter) Start(arg0 context.Context) { } // Start indicates an expected call of Start. -func (mr *MockExporterMockRecorder) Start(arg0 interface{}) *gomock.Call { +func (mr *MockExporterMockRecorder) Start(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockExporter)(nil).Start), arg0) } diff --git a/internal/mocks/mock_downloader/mock_downloader.go b/internal/mocks/mock_downloader/mock_downloader.go index 172171a1..16237a03 100644 --- a/internal/mocks/mock_downloader/mock_downloader.go +++ b/internal/mocks/mock_downloader/mock_downloader.go @@ -1,6 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/rusq/slackdump/v2/downloader (interfaces: Downloader) - +// +// Generated by this command: +// +// mockgen -destination internal/mocks/mock_downloader/mock_downloader.go github.com/rusq/slackdump/v2/downloader Downloader +// // Package mock_downloader is a generated GoMock package. package mock_downloader @@ -8,7 +12,7 @@ import ( io "io" reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockDownloader is a mock of Downloader interface. @@ -43,7 +47,7 @@ func (m *MockDownloader) GetFile(arg0 string, arg1 io.Writer) error { } // GetFile indicates an expected call of GetFile. -func (mr *MockDownloaderMockRecorder) GetFile(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockDownloaderMockRecorder) GetFile(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFile", reflect.TypeOf((*MockDownloader)(nil).GetFile), arg0, arg1) } diff --git a/internal/mocks/mock_fsadapter/mock_fs.go b/internal/mocks/mock_fsadapter/mock_fs.go index 1c5889c9..f6417014 100644 --- a/internal/mocks/mock_fsadapter/mock_fs.go +++ b/internal/mocks/mock_fsadapter/mock_fs.go @@ -1,6 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/rusq/slackdump/v2/fsadapter (interfaces: FS) - +// +// Generated by this command: +// +// mockgen -destination ../internal/mocks/mock_fsadapter/mock_fs.go github.com/rusq/slackdump/v2/fsadapter FS +// // Package mock_fsadapter is a generated GoMock package. package mock_fsadapter @@ -9,7 +13,7 @@ import ( fs "io/fs" reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockFS is a mock of FS interface. @@ -45,7 +49,7 @@ func (m *MockFS) Create(arg0 string) (io.WriteCloser, error) { } // Create indicates an expected call of Create. -func (mr *MockFSMockRecorder) Create(arg0 interface{}) *gomock.Call { +func (mr *MockFSMockRecorder) Create(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockFS)(nil).Create), arg0) } @@ -59,7 +63,7 @@ func (m *MockFS) WriteFile(arg0 string, arg1 []byte, arg2 fs.FileMode) error { } // WriteFile indicates an expected call of WriteFile. -func (mr *MockFSMockRecorder) WriteFile(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockFSMockRecorder) WriteFile(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteFile", reflect.TypeOf((*MockFS)(nil).WriteFile), arg0, arg1, arg2) } diff --git a/internal/mocks/mock_io/mock_io.go b/internal/mocks/mock_io/mock_io.go index 5065f7f1..f47480db 100644 --- a/internal/mocks/mock_io/mock_io.go +++ b/internal/mocks/mock_io/mock_io.go @@ -1,13 +1,17 @@ // Code generated by MockGen. DO NOT EDIT. // Source: io (interfaces: ReadCloser,WriteCloser) - +// +// Generated by this command: +// +// mockgen -destination=../mocks/mock_io/mock_io.go io ReadCloser,WriteCloser +// // Package mock_io is a generated GoMock package. package mock_io import ( reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockReadCloser is a mock of ReadCloser interface. @@ -57,7 +61,7 @@ func (m *MockReadCloser) Read(arg0 []byte) (int, error) { } // Read indicates an expected call of Read. -func (mr *MockReadCloserMockRecorder) Read(arg0 interface{}) *gomock.Call { +func (mr *MockReadCloserMockRecorder) Read(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockReadCloser)(nil).Read), arg0) } @@ -109,7 +113,7 @@ func (m *MockWriteCloser) Write(arg0 []byte) (int, error) { } // Write indicates an expected call of Write. -func (mr *MockWriteCloserMockRecorder) Write(arg0 interface{}) *gomock.Call { +func (mr *MockWriteCloserMockRecorder) Write(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockWriteCloser)(nil).Write), arg0) } diff --git a/internal/mocks/mock_os/mock_os.go b/internal/mocks/mock_os/mock_os.go index 1ecb422b..1176c8e6 100644 --- a/internal/mocks/mock_os/mock_os.go +++ b/internal/mocks/mock_os/mock_os.go @@ -1,6 +1,10 @@ // Code generated by MockGen. DO NOT EDIT. // Source: os (interfaces: FileInfo) - +// +// Generated by this command: +// +// mockgen -destination internal/mocks/mock_os/mock_os.go os FileInfo +// // Package mock_os is a generated GoMock package. package mock_os @@ -9,7 +13,7 @@ import ( reflect "reflect" time "time" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockFileInfo is a mock of FileInfo interface. @@ -106,10 +110,10 @@ func (mr *MockFileInfoMockRecorder) Size() *gomock.Call { } // Sys mocks base method. -func (m *MockFileInfo) Sys() interface{} { +func (m *MockFileInfo) Sys() any { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Sys") - ret0, _ := ret[0].(interface{}) + ret0, _ := ret[0].(any) return ret0 } diff --git a/messages_test.go b/messages_test.go index b0173c42..0e43c12c 100644 --- a/messages_test.go +++ b/messages_test.go @@ -6,9 +6,9 @@ import ( "reflect" "testing" - "github.com/golang/mock/gomock" "github.com/slack-go/slack" "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" "golang.org/x/time/rate" "github.com/rusq/slackdump/v2/internal/fixtures" diff --git a/mocks/mock_io/mock_io.go b/mocks/mock_io/mock_io.go deleted file mode 100644 index 5065f7f1..00000000 --- a/mocks/mock_io/mock_io.go +++ /dev/null @@ -1,115 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: io (interfaces: ReadCloser,WriteCloser) - -// Package mock_io is a generated GoMock package. -package mock_io - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" -) - -// MockReadCloser is a mock of ReadCloser interface. -type MockReadCloser struct { - ctrl *gomock.Controller - recorder *MockReadCloserMockRecorder -} - -// MockReadCloserMockRecorder is the mock recorder for MockReadCloser. -type MockReadCloserMockRecorder struct { - mock *MockReadCloser -} - -// NewMockReadCloser creates a new mock instance. -func NewMockReadCloser(ctrl *gomock.Controller) *MockReadCloser { - mock := &MockReadCloser{ctrl: ctrl} - mock.recorder = &MockReadCloserMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockReadCloser) EXPECT() *MockReadCloserMockRecorder { - return m.recorder -} - -// Close mocks base method. -func (m *MockReadCloser) Close() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Close") - ret0, _ := ret[0].(error) - return ret0 -} - -// Close indicates an expected call of Close. -func (mr *MockReadCloserMockRecorder) Close() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockReadCloser)(nil).Close)) -} - -// Read mocks base method. -func (m *MockReadCloser) Read(arg0 []byte) (int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Read", arg0) - ret0, _ := ret[0].(int) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Read indicates an expected call of Read. -func (mr *MockReadCloserMockRecorder) Read(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockReadCloser)(nil).Read), arg0) -} - -// MockWriteCloser is a mock of WriteCloser interface. -type MockWriteCloser struct { - ctrl *gomock.Controller - recorder *MockWriteCloserMockRecorder -} - -// MockWriteCloserMockRecorder is the mock recorder for MockWriteCloser. -type MockWriteCloserMockRecorder struct { - mock *MockWriteCloser -} - -// NewMockWriteCloser creates a new mock instance. -func NewMockWriteCloser(ctrl *gomock.Controller) *MockWriteCloser { - mock := &MockWriteCloser{ctrl: ctrl} - mock.recorder = &MockWriteCloserMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockWriteCloser) EXPECT() *MockWriteCloserMockRecorder { - return m.recorder -} - -// Close mocks base method. -func (m *MockWriteCloser) Close() error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Close") - ret0, _ := ret[0].(error) - return ret0 -} - -// Close indicates an expected call of Close. -func (mr *MockWriteCloserMockRecorder) Close() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockWriteCloser)(nil).Close)) -} - -// Write mocks base method. -func (m *MockWriteCloser) Write(arg0 []byte) (int, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Write", arg0) - ret0, _ := ret[0].(int) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Write indicates an expected call of Write. -func (mr *MockWriteCloserMockRecorder) Write(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockWriteCloser)(nil).Write), arg0) -} diff --git a/slackdump_test.go b/slackdump_test.go index c7da1513..bee89802 100644 --- a/slackdump_test.go +++ b/slackdump_test.go @@ -9,7 +9,6 @@ import ( "testing" "time" - "github.com/golang/mock/gomock" "github.com/rusq/dlog" "github.com/rusq/slackdump/v2/auth" "github.com/rusq/slackdump/v2/fsadapter" @@ -21,6 +20,7 @@ import ( "github.com/rusq/slackdump/v2/types" "github.com/slack-go/slack" "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" ) func Test_validateCache(t *testing.T) { diff --git a/thread_test.go b/thread_test.go index ea182118..131d80f5 100644 --- a/thread_test.go +++ b/thread_test.go @@ -7,9 +7,9 @@ import ( "errors" - "github.com/golang/mock/gomock" "github.com/slack-go/slack" "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" "golang.org/x/time/rate" "github.com/rusq/slackdump/v2/internal/network" diff --git a/tools/rawoutput/main.go b/tools/rawoutput/main.go index 90151400..3dddf001 100644 --- a/tools/rawoutput/main.go +++ b/tools/rawoutput/main.go @@ -64,7 +64,7 @@ const ( ) func run(ctx context.Context, p params) error { - prov, err := app.InitProvider(ctx, app.CacheDir(), p.workspace, p.creds, browser.Bfirefox) + prov, err := app.InitProvider(ctx, app.CacheDir(), p.workspace, p.creds, browser.Bfirefox, true) if err != nil { return err } diff --git a/users_test.go b/users_test.go index bcfd44ce..af86972e 100644 --- a/users_test.go +++ b/users_test.go @@ -10,9 +10,9 @@ import ( "errors" - "github.com/golang/mock/gomock" "github.com/slack-go/slack" "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" "github.com/rusq/slackdump/v2/internal/encio" "github.com/rusq/slackdump/v2/internal/fixtures" From 0fb5d3bdc855223f799974937f2e928b1fec3902 Mon Sep 17 00:00:00 2001 From: Rustam Gilyazov <16064414+rusq@users.noreply.github.com> Date: Sat, 30 Dec 2023 14:16:16 +1000 Subject: [PATCH 4/9] replacing survey with huh --- auth/auth.go | 17 ------- auth/auth_test.go | 2 +- auth/auth_ui/auth_ui.go | 27 +++++++++++ auth/auth_ui/auth_ui_test.go | 33 +++++++++++++ auth/auth_ui/cli.go | 41 ++++++++++++---- auth/auth_ui/huh.go | 59 ++++++++++++++++++++++ auth/auth_ui/survey.go | 31 ------------ auth/auth_ui/tview.go | 94 ------------------------------------ auth/auth_ui/validation.go | 61 +++++++++++++++++++++++ auth/browser.go | 15 ++---- auth/rod.go | 25 ++++++++-- go.mod | 29 +++++++++-- go.sum | 67 +++++++++++++++++++++++++ 13 files changed, 331 insertions(+), 170 deletions(-) create mode 100644 auth/auth_ui/auth_ui.go create mode 100644 auth/auth_ui/auth_ui_test.go create mode 100644 auth/auth_ui/huh.go delete mode 100644 auth/auth_ui/survey.go delete mode 100644 auth/auth_ui/tview.go create mode 100644 auth/auth_ui/validation.go diff --git a/auth/auth.go b/auth/auth.go index f55aa6d6..248cdda7 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -6,7 +6,6 @@ import ( "errors" "io" "net/http" - "net/url" "runtime/trace" "strings" @@ -138,19 +137,3 @@ func (s simpleProvider) Test(ctx context.Context) error { func (s simpleProvider) HTTPClient() (*http.Client, error) { return chttp.New(SlackURL, s.Cookies()) } - -func sanitize(workspace string) (string, error) { - if !strings.Contains(workspace, ".slack.com") { - return workspace, nil - } - if strings.HasPrefix(workspace, "https://") { - uri, err := url.Parse(workspace) - if err != nil { - return "", err - } - workspace = uri.Host - } - // parse - parts := strings.Split(workspace, ".") - return parts[0], nil -} diff --git a/auth/auth_test.go b/auth/auth_test.go index 071788e3..ccbb00b4 100644 --- a/auth/auth_test.go +++ b/auth/auth_test.go @@ -133,7 +133,7 @@ func Test_sanitize(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := sanitize(tt.args.workspace) + got, err := Sanitize(tt.args.workspace) if (err != nil) != tt.wantErr { t.Errorf("sanitize() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/auth/auth_ui/auth_ui.go b/auth/auth_ui/auth_ui.go new file mode 100644 index 00000000..91f8ceab --- /dev/null +++ b/auth/auth_ui/auth_ui.go @@ -0,0 +1,27 @@ +package auth_ui + +import ( + "net/url" + "strings" +) + +const ( + LoginEmail = 0 + LoginSSO = 1 +) + +func Sanitize(workspace string) (string, error) { + if !strings.Contains(workspace, ".slack.com") { + return workspace, nil + } + if strings.HasPrefix(workspace, "https://") { + uri, err := url.Parse(workspace) + if err != nil { + return "", err + } + workspace = uri.Host + } + // parse + parts := strings.Split(workspace, ".") + return parts[0], nil +} diff --git a/auth/auth_ui/auth_ui_test.go b/auth/auth_ui/auth_ui_test.go new file mode 100644 index 00000000..a2f23e8f --- /dev/null +++ b/auth/auth_ui/auth_ui_test.go @@ -0,0 +1,33 @@ +package auth_ui + +import "testing" + +func TestSanitize(t *testing.T) { + type args struct { + workspace string + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + {"not a URL", args{"blahblah"}, "blahblah", false}, + {"url slash", args{"https://blahblah.slack.com/"}, "blahblah", false}, + {"url no slash", args{"https://blahblah.slack.com"}, "blahblah", false}, + {"url no schema slash", args{"blahblah.slack.com/"}, "blahblah", false}, + {"url no schema no slash", args{"blahblah.slack.com"}, "blahblah", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Sanitize(tt.args.workspace) + if (err != nil) != tt.wantErr { + t.Errorf("sanitize() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("sanitize() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/auth/auth_ui/cli.go b/auth/auth_ui/cli.go index f56b0680..121d2dc6 100644 --- a/auth/auth_ui/cli.go +++ b/auth/auth_ui/cli.go @@ -30,7 +30,7 @@ func (cl *CLI) RequestWorkspace(w io.Writer) (string, error) { if err != nil { return "", err } - return workspace, nil + return Sanitize(workspace) } func (cl *CLI) RequestEmail(w io.Writer) (string, error) { @@ -42,8 +42,8 @@ func (cl *CLI) RequestEmail(w io.Writer) (string, error) { return username, nil } -func (cl *CLI) RequestPassword(w io.Writer) (string, error) { - fmt.Fprint(w, "Enter Password (won't be visible): ") +func (cl *CLI) RequestPassword(w io.Writer, account string) (string, error) { + fmt.Fprintf(w, "Enter Password for %s (won't be visible): ", account) password, err := term.ReadPassword(int(os.Stdin.Fd())) if err != nil { return "", err @@ -52,14 +52,35 @@ func (cl *CLI) RequestPassword(w io.Writer) (string, error) { return string(password), nil } -func (cl *CLI) YesNo(w io.Writer, message string) (bool, error) { - fmt.Fprintf(w, "%s [y/N]: ", message) - answer, err := readln(os.Stdin) - if err != nil { - return false, err +func (cl *CLI) RequestLoginType(w io.Writer) (int, error) { + var types = []struct { + name string + value int + }{ + {"Email", LoginEmail}, + {"Google", LoginSSO}, + {"Apple", LoginSSO}, + {"Login with Single-Sign-On (SSO)", LoginSSO}, + {"Other", LoginSSO}, + } + + var idx int + for idx < 1 || idx > len(types)+1 { + fmt.Fprintf(w, "Select login type:\n") + for i, t := range types { + fmt.Fprintf(w, "\t%d. %s\n", i+1, t.name) + } + fmt.Fprintf(w, "Enter number: ") + _, err := fmt.Fscanf(os.Stdin, "%d", &idx) + if err != nil { + fmt.Fprintln(w, err) + continue + } + if idx < 1 || idx > len(types)+1 { + fmt.Fprintln(w, "invalid login type") + } } - answer = strings.ToLower(answer) - return answer == "y" || answer == "yes", nil + return types[idx-1].value, nil } func (*CLI) Stop() {} diff --git a/auth/auth_ui/huh.go b/auth/auth_ui/huh.go new file mode 100644 index 00000000..7db21dd8 --- /dev/null +++ b/auth/auth_ui/huh.go @@ -0,0 +1,59 @@ +package auth_ui + +import ( + "io" + + "github.com/charmbracelet/huh" +) + +type Huh struct { + theme huh.Theme +} + +func (*Huh) RequestWorkspace(w io.Writer) (string, error) { + var workspace string + huh.NewInput(). + Title("Enter Slack workspace name"). + Value(&workspace). + Validate(valRequired). + Description("The workspace name is the part of the URL that comes before `.slack.com' in\nhttps://.slack.com/. Both name and URL are acceptable."). + Run() + return Sanitize(workspace) +} + +func (*Huh) Stop() {} + +func (*Huh) RequestEmail(w io.Writer) (string, error) { + var email string + huh.NewInput().Title("Enter Slack login email"). + Value(&email). + Validate(valAND(valEmail, valRequired)). + Run() + return email, nil +} + +func (*Huh) RequestPassword(w io.Writer, account string) (string, error) { + var password string + huh.NewInput().Title("Enter password for " + account). + Value(&password). + Password(true). + Validate(valRequired). + Run() + return password, nil +} + +func (*Huh) RequestLoginType(w io.Writer) (int, error) { + var loginType int + err := huh.NewSelect[int]().Title("Select login type"). + Options( + huh.NewOption("Email", LoginEmail), + huh.NewOption("Google", LoginSSO), + huh.NewOption("Apple", LoginSSO), + huh.NewOption("Login with Single-Sign-On (SSO)", LoginSSO), + huh.NewOption("Other", LoginSSO), + ). + Value(&loginType). + Description("If you are not sure, select 'Other'."). + Run() + return loginType, err +} diff --git a/auth/auth_ui/survey.go b/auth/auth_ui/survey.go deleted file mode 100644 index 7616e080..00000000 --- a/auth/auth_ui/survey.go +++ /dev/null @@ -1,31 +0,0 @@ -//go:build ignore - -package auth_ui - -import ( - "fmt" - "io" - - "github.com/AlecAivazis/survey/v2" - "github.com/rusq/slackdump/v2/internal/app/ui" -) - -type Survey struct{} - -func (*Survey) RequestWorkspace(w io.Writer) (string, error) { - workspace, err := ui.Input( - "Enter Slack Workspace Name: ", - "HELP:\n1. Enter the slack workspace name or paste the URL of your slack workspace.\n"+ - "2. Browser will open, login as usual.\n"+ - "3. Browser will close and slackdump will be authenticated.\n\n"+ - "This must be done only once. The credentials are saved in an encrypted\nfile, and can be used only on this device.", - survey.Required, - ) - if err != nil { - return "", err - } - fmt.Println("Please login in the browser...") - return workspace, err -} - -func (*Survey) Stop() {} diff --git a/auth/auth_ui/tview.go b/auth/auth_ui/tview.go deleted file mode 100644 index a150c024..00000000 --- a/auth/auth_ui/tview.go +++ /dev/null @@ -1,94 +0,0 @@ -//go:build ignore - -package auth_ui - -import ( - "errors" - "io" - - "github.com/gdamore/tcell/v2" - "github.com/rivo/tview" - "github.com/rusq/dlog" -) - -type TView struct { - app *tview.Application - - mustStop chan struct{} - inputReceived chan struct{} - done chan struct{} -} - -func (tv *TView) RequestWorkspace(w io.Writer) (string, error) { - tv.inputReceived = make(chan struct{}, 1) - tv.mustStop = make(chan struct{}, 1) - tv.done = make(chan struct{}, 1) - tv.app = tview.NewApplication() - - var workspace string - var exit bool - input := tview.NewInputField().SetLabel("Slack Workspace").SetFieldWidth(40) - form := tview.NewForm(). - AddFormItem(input). - AddButton("OK", func() { - workspace = input.GetText() - tv.wait() - }). - AddButton("Cancel", func() { - exit = true - tv.wait() - }) - - form.SetBorder(true). - SetTitle(" Slackdump EZ-Login 3000 "). - SetBackgroundColor(tcell.ColorDarkCyan). - SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { - if !input.HasFocus() { - return event - } - switch event.Key() { - default: - return event - case tcell.KeyCR: - workspace = input.GetText() - case tcell.KeyESC: - exit = true - - } - tv.wait() - return nil - }) - - go func() { - if err := tv.app.SetRoot(modal(form, 60, 7), true).EnableMouse(true).Run(); err != nil { - dlog.Println(err) - } - }() - - // waiting for the user to finish interaction - <-tv.inputReceived - if exit { - tv.app.Stop() - return "", errors.New("operation cancelled") - } - return workspace, nil -} - -func (tv *TView) wait() { - close(tv.inputReceived) - <-tv.mustStop - tv.app.Stop() - close(tv.done) -} - -func (tv *TView) Stop() { - close(tv.mustStop) - <-tv.done -} - -func modal(p tview.Primitive, width int, height int) tview.Primitive { - return tview.NewGrid(). - SetColumns(0, width, 0). - SetRows(0, height, 0). - AddItem(p, 1, 1, 1, 1, 0, 0, true) -} diff --git a/auth/auth_ui/validation.go b/auth/auth_ui/validation.go new file mode 100644 index 00000000..f5792c50 --- /dev/null +++ b/auth/auth_ui/validation.go @@ -0,0 +1,61 @@ +package auth_ui + +import ( + "errors" + "regexp" +) + +var ( + ErrNotURLSafe = errors.New("not a valid url safe string") + ErrRequired = errors.New("can not be empty") +) + +func valURLSafe(s string) error { + for _, c := range s { + if !isRuneURLSafe(c) { + return ErrNotURLSafe + } + } + return nil +} + +func isRuneURLSafe(r rune) bool { + switch { + case 'a' <= r && r <= 'z': + return true + case 'A' <= r && r <= 'Z': + return true + case '0' <= r && r <= '9': + return true + case r == '-' || r == '.' || r == '_' || r == '~': + return true + } + return false +} + +func valRequired(s string) error { + if s == "" { + return ErrRequired + } + return nil +} + +func valAND(fns ...func(string) error) func(string) error { + return func(s string) error { + for _, fn := range fns { + if err := fn(s); err != nil { + return err + } + } + return nil + } +} + +var dumbEmailRE = regexp.MustCompile(`^[^@]+@[^@]+$`) + +func valEmail(s string) error { + if !dumbEmailRE.MatchString(s) { + return errors.New("not a valid email") + } + return nil +} diff --git a/auth/browser.go b/auth/browser.go index a70f7363..1fd20f7f 100644 --- a/auth/browser.go +++ b/auth/browser.go @@ -11,7 +11,7 @@ import ( ) var _ Provider = BrowserAuth{} -var defaultFlow = &auth_ui.CLI{} +var defaultFlow = &auth_ui.Huh{} type BrowserAuth struct { simpleProvider @@ -27,17 +27,13 @@ type browserOpts struct { } type BrowserAuthUI interface { + // RequestWorkspace should request the workspace name from the user. RequestWorkspace(w io.Writer) (string, error) + // Stop indicates that the auth flow should cleanup and exit, if it is + // keeping the state. Stop() } -type BrowserAuthUIExt interface { - BrowserAuthUI - RequestEmail(w io.Writer) (string, error) - RequestPassword(w io.Writer) (string, error) - YesNo(w io.Writer, message string) (bool, error) -} - func NewBrowserAuth(ctx context.Context, opts ...Option) (BrowserAuth, error) { var br = BrowserAuth{ opts: browserOpts{ @@ -61,12 +57,11 @@ func NewBrowserAuth(ctx context.Context, opts ...Option) (BrowserAuth, error) { } defer br.opts.flow.Stop() } - if wsp, err := sanitize(br.opts.workspace); err != nil { + if wsp, err := auth_ui.Sanitize(br.opts.workspace); err != nil { return br, err } else { br.opts.workspace = wsp } - auther, err := browser.New(br.opts.workspace, browser.OptBrowser(br.opts.browser), browser.OptTimeout(br.opts.loginTimeout), browser.OptVerbose(br.opts.verbose)) if err != nil { return br, err diff --git a/auth/rod.go b/auth/rod.go index 93530bb1..03641704 100644 --- a/auth/rod.go +++ b/auth/rod.go @@ -3,6 +3,7 @@ package auth import ( "context" "fmt" + "io" "os" "github.com/rusq/slackauth" @@ -23,6 +24,13 @@ type rodOpts struct { workspace string } +type BrowserAuthUIExt interface { + BrowserAuthUI + RequestEmail(w io.Writer) (string, error) + RequestPassword(w io.Writer, account string) (string, error) + RequestLoginType(w io.Writer) (int, error) +} + func RodWithWorkspace(name string) Option { return func(o *options) { o.rodOpts.workspace = name @@ -46,18 +54,21 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) { if err != nil { return r, err } + if r.opts.workspace == "" { + return r, fmt.Errorf("workspace cannot be empty") + } } - if wsp, err := sanitize(r.opts.workspace); err != nil { + if wsp, err := auth_ui.Sanitize(r.opts.workspace); err != nil { return r, err } else { r.opts.workspace = wsp } - usesEmail, err := r.opts.ui.YesNo(os.Stdout, "Do you login with your email/password into Slack (i.e. not using Google or SSO to login)?") + resp, err := r.opts.ui.RequestLoginType(os.Stdout) if err != nil { return r, err } var sp simpleProvider - if !usesEmail { + if resp == auth_ui.LoginSSO { var err error sp.Token, sp.Cookie, err = slackauth.Browser(ctx, r.opts.workspace) if err != nil { @@ -69,10 +80,16 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) { if err != nil { return r, err } - password, err := r.opts.ui.RequestPassword(os.Stdout) + if username == "" { + return r, fmt.Errorf("email cannot be empty") + } + password, err := r.opts.ui.RequestPassword(os.Stdout, username) if err != nil { return r, err } + if password == "" { + return r, fmt.Errorf("password cannot be empty") + } fmt.Fprintln(os.Stderr, "Please wait while Slackdump logs into Slack...") sp.Token, sp.Cookie, err = slackauth.Headless(ctx, r.opts.workspace, username, password) if err != nil { diff --git a/go.mod b/go.mod index 747afcb7..2b83c75b 100644 --- a/go.mod +++ b/go.mod @@ -20,25 +20,46 @@ require ( github.com/slack-go/slack v0.12.1 github.com/stretchr/testify v1.8.4 go.uber.org/mock v0.4.0 - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 + golang.org/x/sync v0.4.0 golang.org/x/term v0.15.0 golang.org/x/time v0.3.0 ) require ( + github.com/alecthomas/chroma v0.10.0 // indirect + github.com/atotto/clipboard v0.1.4 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/aymerick/douceur v0.2.0 // indirect + github.com/catppuccin/go v0.2.0 // indirect + github.com/charmbracelet/bubbles v0.17.1 // indirect + github.com/charmbracelet/bubbletea v0.25.0 // indirect + github.com/charmbracelet/glamour v0.6.0 // indirect + github.com/charmbracelet/huh v0.2.3 // indirect + github.com/charmbracelet/lipgloss v0.9.1 // indirect + github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dlclark/regexp2 v1.10.0 // indirect github.com/go-jose/go-jose/v3 v3.0.1 // indirect github.com/go-rod/rod v0.114.5 // indirect github.com/go-stack/stack v1.8.1 // indirect + github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kr/text v0.2.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect + github.com/microcosm-cc/bluemonday v1.0.25 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/ysmood/fetchup v0.2.4 // indirect @@ -46,6 +67,8 @@ require ( github.com/ysmood/got v0.38.2 // indirect github.com/ysmood/gson v0.7.3 // indirect github.com/ysmood/leakless v0.8.0 // indirect + github.com/yuin/goldmark v1.6.0 // indirect + github.com/yuin/goldmark-emoji v1.0.2 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.17.0 // indirect golang.org/x/net v0.17.0 // indirect diff --git a/go.sum b/go.sum index 8ce1f8ac..bf7e319b 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,29 @@ github.com/MercuryEngineering/CookieMonster v0.0.0-20180304172713-1584578b3403 h github.com/MercuryEngineering/CookieMonster v0.0.0-20180304172713-1584578b3403/go.mod h1:mM6WvakkX2m+NgMiPCfFFjwfH4KzENC07zeGEqq9U7s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= +github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= +github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA= +github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= +github.com/charmbracelet/bubbles v0.17.1 h1:0SIyjOnkrsfDo88YvPgAWvZMwXe26TP6drRvmkjyUu4= +github.com/charmbracelet/bubbles v0.17.1/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o= +github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= +github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= +github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= +github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= +github.com/charmbracelet/huh v0.2.3 h1:fZaqnd/fiO7jlfcLqhP2iwpLt670IaHQfL/7Qu+fBm0= +github.com/charmbracelet/huh v0.2.3/go.mod h1:XmADLRnJs/Jqw7zIbi9BTss5gXbOkR6feyVoNAp19rA= +github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= +github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= +github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -14,6 +37,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ= github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= +github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= @@ -29,6 +55,8 @@ github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+Licev github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= @@ -43,6 +71,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -51,19 +81,42 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +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/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= +github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= +github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/playwright-community/playwright-go v0.4001.0 h1:2cBiTIjCvFu7zUrZ48C0YC2DIp90Tbudueq4brUGjHM= github.com/playwright-community/playwright-go v0.4001.0/go.mod h1:quEkYFrvvpQyGSxBjnYbGS52vrUDB2uaY1cOzkkSHCc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= @@ -90,6 +143,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= @@ -109,8 +163,16 @@ github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= github.com/ysmood/leakless v0.8.0 h1:BzLrVoiwxikpgEQR0Lk8NyBN5Cit2b1z+u0mgL4ZJak= github.com/ysmood/leakless v0.8.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= +github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= +github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s= +github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -128,12 +190,15 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -143,7 +208,9 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= From 98269d72f8a92101a3f420c10c7e1a395b42b702 Mon Sep 17 00:00:00 2001 From: Rustam Gilyazov <16064414+rusq@users.noreply.github.com> Date: Sat, 30 Dec 2023 16:27:18 +1000 Subject: [PATCH 5/9] tidy and improved login UX --- auth/auth_test.go | 30 ------------------------------ auth/auth_ui/huh.go | 4 +++- auth/rod.go | 45 +++++++++++++++++++++++++++++++++++++++++++-- go.mod | 5 ++--- go.sum | 20 ++------------------ 5 files changed, 50 insertions(+), 54 deletions(-) diff --git a/auth/auth_test.go b/auth/auth_test.go index ccbb00b4..b997edb0 100644 --- a/auth/auth_test.go +++ b/auth/auth_test.go @@ -114,33 +114,3 @@ func TestSave(t *testing.T) { }) } } - -func Test_sanitize(t *testing.T) { - type args struct { - workspace string - } - tests := []struct { - name string - args args - want string - wantErr bool - }{ - {"not a URL", args{"blahblah"}, "blahblah", false}, - {"url slash", args{"https://blahblah.slack.com/"}, "blahblah", false}, - {"url no slash", args{"https://blahblah.slack.com"}, "blahblah", false}, - {"url no schema slash", args{"blahblah.slack.com/"}, "blahblah", false}, - {"url no schema no slash", args{"blahblah.slack.com"}, "blahblah", false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := Sanitize(tt.args.workspace) - if (err != nil) != tt.wantErr { - t.Errorf("sanitize() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("sanitize() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/auth/auth_ui/huh.go b/auth/auth_ui/huh.go index 7db21dd8..806a2d13 100644 --- a/auth/auth_ui/huh.go +++ b/auth/auth_ui/huh.go @@ -16,7 +16,7 @@ func (*Huh) RequestWorkspace(w io.Writer) (string, error) { Title("Enter Slack workspace name"). Value(&workspace). Validate(valRequired). - Description("The workspace name is the part of the URL that comes before `.slack.com' in\nhttps://.slack.com/. Both name and URL are acceptable."). + Description("The workspace name is the part of the URL that comes before `.slack.com' in\nhttps://.slack.com/. Both workspace name or URL are acceptable."). Run() return Sanitize(workspace) } @@ -27,6 +27,7 @@ func (*Huh) RequestEmail(w io.Writer) (string, error) { var email string huh.NewInput().Title("Enter Slack login email"). Value(&email). + Description("The email that you use to login to Slack."). Validate(valAND(valEmail, valRequired)). Run() return email, nil @@ -37,6 +38,7 @@ func (*Huh) RequestPassword(w io.Writer, account string) (string, error) { huh.NewInput().Title("Enter password for " + account). Value(&password). Password(true). + Description("This is your Slack password, it will not be saved."). Validate(valRequired). Run() return password, nil diff --git a/auth/rod.go b/auth/rod.go index 03641704..4b1481f3 100644 --- a/auth/rod.go +++ b/auth/rod.go @@ -5,9 +5,11 @@ import ( "fmt" "io" "os" + "time" "github.com/rusq/slackauth" "github.com/rusq/slackdump/v2/auth/auth_ui" + "github.com/schollz/progressbar/v3" ) type RodAuth struct { @@ -37,10 +39,12 @@ func RodWithWorkspace(name string) Option { } } +const expectedLoginDuration = 16 * time.Second + func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) { r := RodAuth{ opts: rodOpts{ - ui: &auth_ui.CLI{}, + ui: &auth_ui.Huh{}, }, } for _, opt := range opts { @@ -90,8 +94,10 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) { if password == "" { return r, fmt.Errorf("password cannot be empty") } - fmt.Fprintln(os.Stderr, "Please wait while Slackdump logs into Slack...") + done, finished := fakeProgress("Logging in...", int(expectedLoginDuration.Seconds())*10, 100*time.Millisecond) sp.Token, sp.Cookie, err = slackauth.Headless(ctx, r.opts.workspace, username, password) + close(done) + <-finished if err != nil { return r, err } @@ -102,3 +108,38 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) { simpleProvider: sp, }, nil } + +// fakeProgress starts a fake spinner and returns a channel that must be closed +// once the operation completes. interval is interval between iterations. If not +// set, will default to 50ms. +func fakeProgress(title string, max int, interval time.Duration) (chan<- struct{}, <-chan struct{}) { + if interval == 0 { + interval = 50 * time.Millisecond + } + var ( + done = make(chan struct{}) + finished = make(chan struct{}) + ) + go func() { + bar := progressbar.NewOptions( + max, + progressbar.OptionSetDescription(title), + progressbar.OptionSpinnerType(9), + ) + t := time.NewTicker(interval) + defer t.Stop() + + for { + select { + case <-done: + bar.Finish() + fmt.Println() + close(finished) + return + case <-t.C: + bar.Add(1) + } + } + }() + return done, finished +} diff --git a/go.mod b/go.mod index 2b83c75b..38638232 100644 --- a/go.mod +++ b/go.mod @@ -5,16 +5,16 @@ go 1.21.4 require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/MercuryEngineering/CookieMonster v0.0.0-20180304172713-1584578b3403 + github.com/charmbracelet/huh v0.2.3 github.com/denisbrodbeck/machineid v1.0.1 github.com/fatih/color v1.15.0 - github.com/golang/mock v1.6.0 github.com/joho/godotenv v1.5.1 github.com/playwright-community/playwright-go v0.4001.0 github.com/rusq/chttp v1.0.2 github.com/rusq/dlog v1.4.0 github.com/rusq/osenv/v2 v2.0.1 github.com/rusq/secure v0.0.4 - github.com/rusq/slackauth v0.0.1 + github.com/rusq/slackauth v0.0.3 github.com/rusq/tracer v1.0.1 github.com/schollz/progressbar/v3 v3.13.0 github.com/slack-go/slack v0.12.1 @@ -34,7 +34,6 @@ require ( github.com/charmbracelet/bubbles v0.17.1 // indirect github.com/charmbracelet/bubbletea v0.25.0 // indirect github.com/charmbracelet/glamour v0.6.0 // indirect - github.com/charmbracelet/huh v0.2.3 // indirect github.com/charmbracelet/lipgloss v0.9.1 // indirect github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect diff --git a/go.sum b/go.sum index bf7e319b..70cd299f 100644 --- a/go.sum +++ b/go.sum @@ -50,8 +50,6 @@ github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho= github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= @@ -79,15 +77,12 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -131,8 +126,8 @@ github.com/rusq/osenv/v2 v2.0.1 h1:1LtNt8VNV/W86wb38Hyu5W3Rwqt/F1JNRGE+8GRu09o= github.com/rusq/osenv/v2 v2.0.1/go.mod h1:+wJBSisjNZpfoD961JzqjaM+PtaqSusO3b4oVJi7TFY= github.com/rusq/secure v0.0.4 h1:svpiZHfHnx89eEDCCFI9OXG1Y8hL9kUWUG6fJbrWUOI= github.com/rusq/secure v0.0.4/go.mod h1:F1QilMKreuFRjov0UY7DZSIXn77/8RqMVGu2zV0RtqY= -github.com/rusq/slackauth v0.0.1 h1:UYW/lMr+FMnKeax40L55VpY40Ekmsoc0MhScdL4fopc= -github.com/rusq/slackauth v0.0.1/go.mod h1:zb1PJY2+8uEqn0RiWuRjnd+ZFwwfnvA5xrGoooVUgNY= +github.com/rusq/slackauth v0.0.3 h1:cpq1xqDPm9Fkng/yrQJcugTLdO7f1GyJLn/mA7/Cx9Q= +github.com/rusq/slackauth v0.0.3/go.mod h1:zb1PJY2+8uEqn0RiWuRjnd+ZFwwfnvA5xrGoooVUgNY= github.com/rusq/tracer v1.0.1 h1:5u4PCV8NGO97VuAINQA4gOVRkPoqHimLE2jpezRVNMU= github.com/rusq/tracer v1.0.1/go.mod h1:Rqu48C3/K8bA5NPmF20Hft73v431MQIdM+Co+113pME= github.com/schollz/progressbar/v3 v3.13.0 h1:9TeeWRcjW2qd05I8Kf9knPkW4vLM/hYoa6z9ABvxje8= @@ -164,7 +159,6 @@ github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3R github.com/ysmood/leakless v0.8.0 h1:BzLrVoiwxikpgEQR0Lk8NyBN5Cit2b1z+u0mgL4ZJak= github.com/ysmood/leakless v0.8.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -179,23 +173,18 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= @@ -203,8 +192,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -230,12 +217,9 @@ golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From e35e2c13e3af075e5d8c2f195f9e7a64c7d29e2a Mon Sep 17 00:00:00 2001 From: Rustam Gilyazov <16064414+rusq@users.noreply.github.com> Date: Sat, 30 Dec 2023 16:33:12 +1000 Subject: [PATCH 6/9] update gha go version --- .github/workflows/go.yml | 2 +- auth_error.go | 21 ----------- auth_error_test.go | 79 ---------------------------------------- slackdump.go | 4 +- 4 files changed, 3 insertions(+), 103 deletions(-) delete mode 100644 auth_error.go delete mode 100644 auth_error_test.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index f125e2c6..a8a5ee76 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v2 with: - go-version: "1.20" + go-version: "1.21" - name: Build run: go build -v ./... diff --git a/auth_error.go b/auth_error.go deleted file mode 100644 index 2c00bdb4..00000000 --- a/auth_error.go +++ /dev/null @@ -1,21 +0,0 @@ -package slackdump - -import "fmt" - -// AuthError is the error returned by New, the underlying Err contains -// an API error returned by slack.AuthTest call. -type AuthError struct { - Err error -} - -func (ae *AuthError) Error() string { - return fmt.Sprintf("failed to authenticate: %s", ae.Err) -} - -func (ae *AuthError) Unwrap() error { - return ae.Err -} - -func (ae *AuthError) Is(target error) bool { - return target == ae.Err -} diff --git a/auth_error_test.go b/auth_error_test.go deleted file mode 100644 index 1fb4f23d..00000000 --- a/auth_error_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package slackdump - -import ( - "errors" - "fmt" - "testing" -) - -var errSample = errors.New("test error") - -func TestAuthError_Unwrap(t *testing.T) { - type fields struct { - Err error - } - tests := []struct { - name string - fields fields - wantErr error - }{ - { - "unwrap unwraps properly", - fields{Err: errSample}, - errSample, - }, - { - "multilevel wrap", - fields{Err: fmt.Errorf("blah: %w", errSample)}, - errSample, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ae := &AuthError{ - Err: tt.fields.Err, - } - if err := ae.Unwrap(); (err != nil) && !errors.Is(err, tt.wantErr) { - t.Errorf("AuthError.Unwrap() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func TestAuthError_Is(t *testing.T) { - type fields struct { - Err error - } - type args struct { - target error - } - tests := []struct { - name string - fields fields - args args - want bool - }{ - { - "is correctly compares underlying error", - fields{Err: errSample}, - args{errSample}, - true, - }, - { - "not matching error returns false", - fields{Err: errors.New("not me bro")}, - args{errSample}, - false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - ae := &AuthError{ - Err: tt.fields.Err, - } - if got := ae.Is(tt.args.target); got != tt.want { - t.Errorf("AuthError.Is() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/slackdump.go b/slackdump.go index 1251d174..eb7a23ab 100644 --- a/slackdump.go +++ b/slackdump.go @@ -97,7 +97,7 @@ func NewWithOptions(ctx context.Context, authProvider auth.Provider, opts Option authTestResp, err := cl.AuthTestContext(ctx) if err != nil { - return nil, &AuthError{Err: err} + return nil, &auth.Error{Err: err} } sd := &Session{ @@ -141,7 +141,7 @@ func TestAuth(ctx context.Context, provider auth.Provider) error { region := trace.StartRegion(ctx, "AuthTestContext") defer region.End() if _, err := cl.AuthTestContext(ctx); err != nil { - return &AuthError{Err: err} + return &auth.Error{Err: err} } return nil } From 6f14e1091790b16d9178079b4f3e2c6c57c6960e Mon Sep 17 00:00:00 2001 From: Rustam Gilyazov <16064414+rusq@users.noreply.github.com> Date: Sat, 30 Dec 2023 16:34:38 +1000 Subject: [PATCH 7/9] get fucked codeql --- .github/workflows/codeql-analysis.yml | 70 --------------------------- 1 file changed, 70 deletions(-) delete mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 8f1b0749..00000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,70 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ master ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ master ] - schedule: - - cron: '16 14 * * 3' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'go' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://git.io/codeql-language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 From e63eece8a82f80677511c2505bbae5bef158bf7d Mon Sep 17 00:00:00 2001 From: Rustam Gilyazov <16064414+rusq@users.noreply.github.com> Date: Sat, 30 Dec 2023 16:54:21 +1000 Subject: [PATCH 8/9] fix fallback cli auth --- auth/auth_ui/cli.go | 56 +++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/auth/auth_ui/cli.go b/auth/auth_ui/cli.go index 121d2dc6..f900f6b1 100644 --- a/auth/auth_ui/cli.go +++ b/auth/auth_ui/cli.go @@ -11,6 +11,7 @@ import ( "golang.org/x/term" ) +// CLI is the archaic fallback UI for auth. type CLI struct{} func (*CLI) instructions(w io.Writer) { @@ -25,8 +26,7 @@ func (*CLI) instructions(w io.Writer) { func (cl *CLI) RequestWorkspace(w io.Writer) (string, error) { cl.instructions(w) - fmt.Fprint(w, "Enter Slack Workspace Name: ") - workspace, err := readln(os.Stdin) + workspace, err := prompt(w, "Enter Slack Workspace Name or URL: ", readln) if err != nil { return "", err } @@ -34,22 +34,12 @@ func (cl *CLI) RequestWorkspace(w io.Writer) (string, error) { } func (cl *CLI) RequestEmail(w io.Writer) (string, error) { - fmt.Fprint(w, "Enter Email: ") - username, err := readln(os.Stdin) - if err != nil { - return "", err - } - return username, nil + return prompt(w, "Enter Email: ", readln) } func (cl *CLI) RequestPassword(w io.Writer, account string) (string, error) { - fmt.Fprintf(w, "Enter Password for %s (won't be visible): ", account) - password, err := term.ReadPassword(int(os.Stdin.Fd())) - if err != nil { - return "", err - } - fmt.Fprintln(w) - return string(password), nil + defer fmt.Fprintln(w) + return prompt(w, fmt.Sprintf("Enter Password for %s (won't be visible): ", account), readpwd) } func (cl *CLI) RequestLoginType(w io.Writer) (int, error) { @@ -64,31 +54,57 @@ func (cl *CLI) RequestLoginType(w io.Writer) (int, error) { {"Other", LoginSSO}, } - var idx int - for idx < 1 || idx > len(types)+1 { + var idx int = -1 + for idx < 0 || idx >= len(types) { fmt.Fprintf(w, "Select login type:\n") for i, t := range types { fmt.Fprintf(w, "\t%d. %s\n", i+1, t.name) } fmt.Fprintf(w, "Enter number: ") + _, err := fmt.Fscanf(os.Stdin, "%d", &idx) if err != nil { fmt.Fprintln(w, err) continue } - if idx < 1 || idx > len(types)+1 { + + idx -= 1 // adjusting for 0-index + + if idx < 0 || idx >= len(types) { fmt.Fprintln(w, "invalid login type") } } - return types[idx-1].value, nil + return types[idx].value, nil } func (*CLI) Stop() {} -func readln(r io.Reader) (string, error) { +func readln(r *os.File) (string, error) { line, err := bufio.NewReader(r).ReadString('\n') if err != nil { return "", err } return strings.TrimSpace(line), nil } + +func readpwd(f *os.File) (string, error) { + pwd, err := term.ReadPassword(int(f.Fd())) + if err != nil { + return "", err + } + return string(pwd), nil +} + +func prompt(w io.Writer, prompt string, readlnFn func(*os.File) (string, error)) (string, error) { + for { + fmt.Fprint(w, prompt) + v, err := readlnFn(os.Stdin) + if err != nil { + return "", err + } + if v != "" { + return v, nil + } + fmt.Fprintln(w, "input cannot be empty") + } +} From 607fa8e5101db0d31e8effe52a6cb7c404903092 Mon Sep 17 00:00:00 2001 From: Rustam Gilyazov <16064414+rusq@users.noreply.github.com> Date: Sat, 30 Dec 2023 16:55:31 +1000 Subject: [PATCH 9/9] why was this exported? --- auth/rod.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/auth/rod.go b/auth/rod.go index 4b1481f3..b6c295bb 100644 --- a/auth/rod.go +++ b/auth/rod.go @@ -22,11 +22,11 @@ func (p RodAuth) Type() Type { } type rodOpts struct { - ui BrowserAuthUIExt + ui browserAuthUIExt workspace string } -type BrowserAuthUIExt interface { +type browserAuthUIExt interface { BrowserAuthUI RequestEmail(w io.Writer) (string, error) RequestPassword(w io.Writer, account string) (string, error)