Skip to content

Commit

Permalink
backport
Browse files Browse the repository at this point in the history
  • Loading branch information
rusq committed Nov 3, 2024
1 parent 564bcc2 commit 058b57a
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 52 deletions.
93 changes: 58 additions & 35 deletions auth/auth_ui/huh.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ func (h *Huh) RequestWorkspace(w io.Writer) (string, error) {
huh.NewInput().
Title("Enter Slack workspace name").
Value(&workspace).
Validate(valRequired).
Validate(valWorkspace).
Description("The workspace name is the part of the URL that comes before `.slack.com' in\nhttps://<workspace>.slack.com/. Both workspace name or URL are acceptable."),
)).Run()
if err != nil {
return "", err
}
return Sanitize(workspace)
return workspace, nil
}

func (*Huh) Stop() {}
Expand Down Expand Up @@ -61,7 +61,7 @@ func (m methodMenuItem) String() string {

var methods = []methodMenuItem{
{
"Manual",
"Interactive",
"Works with most authentication schemes, except Google.",
LInteractive,
},
Expand All @@ -71,18 +71,33 @@ var methods = []methodMenuItem{
LHeadless,
},
{
"User's Browser",
"User Browser",
"Loads your user profile, works with Google Auth",
LUserBrowser,
},
}

type LoginOpts struct {
Workspace string
Type LoginType
BrowserPath string
}

func (*Huh) RequestLoginType(w io.Writer) (LoginOpts, error) {
func valWorkspace(s string) error {
if err := valRequired(s); err != nil {
return err
}
_, err := Sanitize(s)
return err
}

func (*Huh) RequestLoginType(w io.Writer, workspace string) (LoginOpts, error) {
var ret = LoginOpts{
Workspace: workspace,
Type: LInteractive,
BrowserPath: "",
}

var opts = make([]huh.Option[LoginType], 0, len(methods))
for _, m := range methods {
opts = append(opts, huh.NewOption(m.String(), m.Type))
Expand All @@ -91,41 +106,49 @@ func (*Huh) RequestLoginType(w io.Writer) (LoginOpts, error) {
huh.NewOption("------", LoginType(-1)),
huh.NewOption("Cancel", LCancel),
)
var loginType LoginType
err := huh.NewForm(huh.NewGroup(
huh.NewSelect[LoginType]().Title("Select login type").
Options(opts...).
Value(&loginType).
Validate(valSepEaster()).
DescriptionFunc(func() string {
switch loginType {
case LInteractive:
return "Clean browser will open on a Slack Login page."
case LHeadless:
return "You will be prompted to enter your email and password, login is automated."
case LUserBrowser:
return "System browser will open on a Slack Login page."
case LCancel:
return "Cancel the login process."
default:
return ""
}
}, &loginType),
)).Run()
if err != nil {
return LoginOpts{Type: LCancel}, err
var fields []huh.Field
if workspace == "" {
fields = append(fields, huh.NewInput().
Title("Enter Slack workspace name").
Value(&ret.Workspace).
Validate(valWorkspace).
Description("The workspace name is the part of the URL that comes before `.slack.com' in\nhttps://<workspace>.slack.com/. Both workspace name or URL are acceptable."),
)
}

fields = append(fields, huh.NewSelect[LoginType]().
TitleFunc(func() string {
return fmt.Sprintf("Select login type for [%s]", ret.Workspace)
}, &ret.Workspace).
Options(opts...).
Value(&ret.Type).
Validate(valSepEaster()).
DescriptionFunc(func() string {
switch ret.Type {
case LInteractive:
return "Clean browser will open on a Slack Login page."
case LHeadless:
return "You will be prompted to enter your email and password, login is automated."
case LUserBrowser:
return "System browser will open on a Slack Login page."
case LCancel:
return "Cancel the login process."
default:
return ""
}
}, &ret.Type))
if err := huh.NewForm(huh.NewGroup(fields...)).Run(); err != nil {
return ret, err
}
if loginType == LUserBrowser {
if ret.Type == LUserBrowser {
path, err := chooseBrowser()
if err != nil {
return LoginOpts{Type: LCancel}, err
return ret, err
}
return LoginOpts{
Type: LUserBrowser,
BrowserPath: path,
}, err
ret.BrowserPath = path
return ret, err
}
return LoginOpts{Type: loginType}, nil
return ret, nil
}

func chooseBrowser() (string, error) {
Expand Down
33 changes: 16 additions & 17 deletions auth/rod.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"time"

"github.com/charmbracelet/huh/spinner"
"github.com/rusq/slackauth"

"github.com/rusq/slackdump/v2/auth/auth_ui"
Expand Down Expand Up @@ -60,11 +61,10 @@ func (ro rodOpts) slackauthOpts() []slackauth.Option {
}

type browserAuthUIExt interface {
BrowserAuthUI
// RequestLoginType should request the login type from the user and return
// one of the [auth_ui.LoginType] constants. The implementation should
// provide a way to cancel the login flow, returning [auth_ui.LoginCancel].
RequestLoginType(w io.Writer) (auth_ui.LoginOpts, error)
RequestLoginType(w io.Writer, workspace string) (auth_ui.LoginOpts, error)
// RequestCreds should request the user's email and password and return
// them.
RequestCreds(w io.Writer, workspace string) (email string, passwd string, err error)
Expand All @@ -89,23 +89,13 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) {
for _, opt := range opts {
opt(&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 r.opts.workspace == "" {
return r, fmt.Errorf("workspace cannot be empty")
}
}
if wsp, err := auth_ui.Sanitize(r.opts.workspace); err != nil {
return r, err
} else {
r.opts.workspace = wsp
}

resp, err := r.opts.ui.RequestLoginType(os.Stdout)
resp, err := r.opts.ui.RequestLoginType(os.Stdout, r.opts.workspace)
if err != nil {
return r, err
}
Expand All @@ -117,7 +107,7 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) {
}

cl, err := slackauth.New(
r.opts.workspace,
resp.Workspace,
sopts...,
)
if err != nil {
Expand All @@ -137,14 +127,13 @@ func NewRODAuth(ctx context.Context, opts ...Option) (RodAuth, error) {
return r, err
}
case auth_ui.LHeadless:
sp, err = headlessFlow(ctx, cl, r.opts.workspace, r.opts.ui)
sp, err = headlessFlow(ctx, cl, resp.Workspace, r.opts.ui)
if err != nil {
return r, err
}
case auth_ui.LCancel:
return r, ErrCancelled
}

lg.Printf("✅ authenticated (time taken: %s)", time.Since(t))

return RodAuth{
Expand All @@ -163,12 +152,22 @@ func headlessFlow(ctx context.Context, cl *slackauth.Client, workspace string, u
if password == "" {
return sp, fmt.Errorf("password cannot be empty")
}
logger.FromContext(ctx).Println("⏳ Logging in to Slack, depending on your connection speed, it will take 25-40 seconds...")

sctx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
_ = spinner.New().
Type(spinner.Dots).
Title("Logging in to Slack, it will take 25-40 seconds").
Context(sctx).
Run()
}()

var loginErr error
sp.Token, sp.Cookie, loginErr = cl.Headless(ctx, username, password)
if loginErr != nil {
return sp, loginErr
}

return
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.23
require (
github.com/MercuryEngineering/CookieMonster v0.0.0-20180304172713-1584578b3403
github.com/charmbracelet/huh v0.6.0
github.com/charmbracelet/huh/spinner v0.0.0-20241028115900-20a4d21717a8
github.com/denisbrodbeck/machineid v1.0.1
github.com/fatih/color v1.17.0
github.com/joho/godotenv v1.5.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqK
github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4=
github.com/charmbracelet/huh v0.6.0 h1:mZM8VvZGuE0hoDXq6XLxRtgfWyTI3b2jZNKh0xWmax8=
github.com/charmbracelet/huh v0.6.0/go.mod h1:GGNKeWCeNzKpEOh/OJD8WBwTQjV3prFAtQPpLv+AVwU=
github.com/charmbracelet/huh/spinner v0.0.0-20241028115900-20a4d21717a8 h1:g+Bz64hsMLTf3lAgUqI6Rj1YEAlm/HN39IuhyneCokc=
github.com/charmbracelet/huh/spinner v0.0.0-20241028115900-20a4d21717a8/go.mod h1:Cxhgl8N0sX9A+EQxedzzGZAalaF8fUVL+JP/pSOW8cI=
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY=
github.com/charmbracelet/x/ansi v0.3.2 h1:wsEwgAN+C9U06l9dCVMX0/L3x7ptvY1qmjMwyfE6USY=
Expand Down

0 comments on commit 058b57a

Please sign in to comment.