Skip to content

Commit

Permalink
legacy-browser flag
Browse files Browse the repository at this point in the history
  • Loading branch information
rusq committed Feb 6, 2024
1 parent 6a389bf commit df388f5
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 76 deletions.
7 changes: 5 additions & 2 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ import (
"io"
"net/http"
"os"
"regexp"
"runtime/trace"
"strings"

"github.com/rusq/chttp"
"github.com/slack-go/slack"
)

const SlackURL = "https://slack.com"

// tokenRE is the regexp that matches a valid Slack Client token.
var tokenRE = regexp.MustCompile(`xoxc-[0-9]+-[0-9]+-[0-9]+-[0-9a-z]{64}`)

// Provider is the Slack Authentication provider.
//
//go:generate mockgen -destination ../internal/mocks/mock_auth/mock_auth.go github.com/rusq/slackdump/v3/auth Provider
Expand Down Expand Up @@ -99,7 +102,7 @@ func Save(w io.Writer, p Provider) error {

// IsClientToken returns true if the tok is a web-client token.
func IsClientToken(tok string) bool {
return strings.HasPrefix(tok, "xoxc-")
return tokenRE.MatchString(tok)
}

// TestAuth attempts to authenticate with the given provider. It will return
Expand Down
18 changes: 4 additions & 14 deletions cmd/slackdump/internal/authcmd/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func argsWorkspace(args []string, defaultWsp string) string {

// Auth authenticates in the workspace wsp, and saves, or reuses the credentials
// in the dir.
func Auth(ctx context.Context, cacheDir string, wsp string) (auth.Provider, error) {
func Auth(ctx context.Context, cacheDir string, wsp string, usePlaywright bool) (auth.Provider, error) {
m, err := cache.NewManager(cacheDir)
if err != nil {
return nil, err
Expand All @@ -88,7 +88,7 @@ func Auth(ctx context.Context, cacheDir string, wsp string) (auth.Provider, erro
return nil, fmt.Errorf("workspace does not exist: %q", cfg.Workspace)
}

prov, err := m.Auth(ctx, wsp, cache.SlackCreds{Token: cfg.SlackToken, Cookie: cfg.SlackCookie})
prov, err := m.Auth(ctx, wsp, cache.AuthData{Token: cfg.SlackToken, Cookie: cfg.SlackCookie, UsePlaywright: usePlaywright})
if err != nil {
return nil, err
}
Expand All @@ -97,30 +97,20 @@ func Auth(ctx context.Context, cacheDir string, wsp string) (auth.Provider, erro

// AuthCurrent authenticates in the current workspace, or overrideWsp if it's
// provided.
func AuthCurrent(ctx context.Context, cacheDir string, overrideWsp string) (auth.Provider, error) {
func AuthCurrent(ctx context.Context, cacheDir string, overrideWsp string, usePlaywright bool) (auth.Provider, error) {
wsp, err := Current(cacheDir, overrideWsp)
if err != nil {
return nil, err
}
trace.Logf(ctx, "AuthCurrent", "current workspace=%s", wsp)

prov, err := Auth(ctx, cacheDir, wsp)
prov, err := Auth(ctx, cacheDir, wsp, usePlaywright)
if err != nil {
return nil, err
}
return prov, nil
}

// AuthCurrentCtx authenticates in the current or overriden workspace and
// returns the context with the auth.Provider.
func AuthCurrentCtx(pctx context.Context, cacheDir string, overrideWsp string) (context.Context, error) {
prov, err := AuthCurrent(pctx, cacheDir, overrideWsp)
if err != nil {
return nil, err
}
return auth.WithContext(pctx, prov), nil
}

// Current returns the current workspace in the directory dir, based on the
// configuration values. If cfg.Workspace is set, it checks if the workspace
// cfg.Workspace exists in the directory dir, and returns it.
Expand Down
2 changes: 1 addition & 1 deletion cmd/slackdump/internal/authcmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func printHeader(hi ...hdrItem) string {
}

func userInfo(ctx context.Context, m manager, name string) (*slack.AuthTestResponse, error) {
prov, err := m.Auth(ctx, name, cache.SlackCreds{})
prov, err := m.Auth(ctx, name, cache.AuthData{})
if err != nil {
return nil, err
}
Expand Down
24 changes: 13 additions & 11 deletions cmd/slackdump/internal/authcmd/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/cfg"
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/golang/base"
"github.com/rusq/slackdump/v3/internal/cache"
"github.com/rusq/slackdump/v3/logger"
)

var CmdWspNew = &base.Command{
Expand All @@ -20,21 +19,23 @@ var CmdWspNew = &base.Command{
**New** allows you to authenticate in an existing Slack Workspace.
`,
FlagMask: flagmask &^ cfg.OmitAuthFlags,
FlagMask: flagmask &^ cfg.OmitAuthFlags, // only auth flags.
PrintFlags: true,
}

var (
newConfirm = CmdWspNew.Flag.Bool("y", false, "answer yes to all questions")
)
var newParams = struct {
confirm bool
}{}

func init() {
CmdWspNew.Flag.BoolVar(&newParams.confirm, "y", false, "answer yes to all questions")

CmdWspNew.Run = runWspNew
}

// runWspNew authenticates in the new workspace.
func runWspNew(ctx context.Context, cmd *base.Command, args []string) error {
lg := logger.FromContext(ctx)
lg := cfg.Log
m, err := cache.NewManager(cfg.CacheDir(), cache.WithAuthOpts(auth.BrowserWithBrowser(cfg.Browser), auth.BrowserWithTimeout(cfg.LoginTimeout)))
if err != nil {
base.SetExitStatus(base.SCacheError)
Expand All @@ -44,7 +45,7 @@ func runWspNew(ctx context.Context, cmd *base.Command, args []string) error {
wsp := argsWorkspace(args, cfg.Workspace)

if m.Exists(realname(wsp)) {
if !*newConfirm && !base.YesNo(fmt.Sprintf("Workspace %q already exists. Overwrite", realname(wsp))) {
if !newParams.confirm && !base.YesNo(fmt.Sprintf("Workspace %q already exists. Overwrite", realname(wsp))) {
return ErrOpCancelled
}
if err := m.Delete(realname(wsp)); err != nil {
Expand All @@ -54,11 +55,12 @@ func runWspNew(ctx context.Context, cmd *base.Command, args []string) error {
}

lg.Debugln("requesting authentication...")
creds := cache.SlackCreds{
Token: cfg.SlackToken,
Cookie: cfg.SlackCookie,
ad := cache.AuthData{
Token: cfg.SlackToken,
Cookie: cfg.SlackCookie,
UsePlaywright: cfg.LegacyBrowser,
}
prov, err := m.Auth(ctx, wsp, creds)
prov, err := m.Auth(ctx, wsp, ad)
if err != nil {
base.SetExitStatus(base.SAuthError)
if errors.Is(err, auth.ErrCancelled) {
Expand Down
3 changes: 2 additions & 1 deletion cmd/slackdump/internal/cfg/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ const (
OmitWorkspaceFlag |
OmitAuthFlags |
OmitUserCacheFlag |
OmitTimeframeFlag
OmitTimeframeFlag |
OmitChunkCacheFlag
)

// SetBaseFlags sets base flags
Expand Down
75 changes: 52 additions & 23 deletions cmd/slackdump/internal/diag/eztest.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (

"github.com/playwright-community/playwright-go"

"github.com/rusq/slackdump/v3/auth/browser"
"github.com/rusq/slackdump/v3/auth"
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/golang/base"
"github.com/rusq/slackdump/v3/logger"
)
Expand Down Expand Up @@ -50,6 +50,7 @@ func runEzLoginTest(ctx context.Context, cmd *base.Command, args []string) error
lg := logger.FromContext(ctx)

wsp := cmd.Flag.String("w", "", "Slack `workspace` to login to.")
legacy := cmd.Flag.Bool("legacy-browser", false, "run with playwright")

if err := cmd.Flag.Parse(args); err != nil {
base.SetExitStatus(base.SInvalidParameters)
Expand All @@ -62,40 +63,68 @@ func runEzLoginTest(ctx context.Context, cmd *base.Command, args []string) error
return nil
}

if err := playwright.Install(&playwright.RunOptions{Browsers: []string{"firefox"}}); err != nil {
base.SetExitStatus(base.SApplicationError)
return fmt.Errorf("playwright installation error: %w", err)
}

b, err := browser.New(*wsp)
if err != nil {
base.SetExitStatus(base.SApplicationError)
return err
}
var (
res result
)

token, cookies, err := b.Authenticate(ctx)
r := result{
Engine: "playwright",
HasToken: len(token) > 0,
HasCookies: len(cookies) > 0,
}
if err != nil {
errStr := err.Error()
r.Err = &errStr
if *legacy {
res = tryPlaywrightAuth(ctx, *wsp)
} else {
res = tryRodAuth(ctx, *wsp)
}

enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
if err := enc.Encode(r); err != nil {
if err := enc.Encode(res); err != nil {
base.SetExitStatus(base.SApplicationError)
return err
}
if r.Err == nil {
if res.Err == nil {
lg.Println("OK")
} else {
lg.Println("ERROR")
base.SetExitStatus(base.SApplicationError)
return errors.New(*r.Err)
return errors.New(*res.Err)
}
return nil

}

func tryPlaywrightAuth(ctx context.Context, wsp string) result {
var res = result{Engine: "playwright"}

if err := playwright.Install(&playwright.RunOptions{Browsers: []string{"firefox"}}); err != nil {
res.Err = ptr(fmt.Sprintf("playwright installation error: %s", err))
return res
}

prov, err := auth.NewBrowserAuth(ctx, auth.BrowserWithWorkspace(wsp))
if err != nil {
res.Err = ptr(err.Error())
return res
}

res.HasToken = len(prov.SlackToken()) > 0
res.HasCookies = len(prov.Cookies()) > 0
if err != nil {
res.Err = ptr(err.Error())
return res
}
return res
}

func ptr[T any](t T) *T {
return &t
}

func tryRodAuth(ctx context.Context, wsp string) result {
ret := result{Engine: "rod"}
prov, err := auth.NewRODAuth(ctx, auth.BrowserWithWorkspace(wsp))
if err != nil {
ret.Err = ptr(err.Error())
return ret
}
ret.HasCookies = len(prov.Cookies()) > 0
ret.HasToken = len(prov.SlackToken()) > 0
return ret
}
2 changes: 1 addition & 1 deletion cmd/slackdump/internal/diag/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var CmdUninstall = &base.Command{
PrintFlags: true,
}

// reinstall parameters
// uninstallParams holds supported command line parameters
var uninstallParams = struct {
legacy bool // playwright
dry bool // dry run
Expand Down
6 changes: 3 additions & 3 deletions cmd/slackdump/internal/format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ func unmarshal[OUT any](rs io.ReadSeeker) (OUT, error) {

func getUsers(ctx context.Context, dmp *dump, isOnline bool) ([]slack.User, error) {
if isOnline {
return getUsersOnline(ctx, cfg.CacheDir(), cfg.Workspace)
return getUsersOnline(ctx, cfg.CacheDir(), cfg.Workspace, cfg.LegacyBrowser)
}
rgn := trace.StartRegion(ctx, "userIDs")
ids := dmp.userIDs()
Expand All @@ -239,8 +239,8 @@ func getUsers(ctx context.Context, dmp *dump, isOnline bool) ([]slack.User, erro
return uu, nil
}

func getUsersOnline(ctx context.Context, cacheDir, wsp string) ([]slack.User, error) {
prov, err := authcmd.AuthCurrent(ctx, cacheDir, wsp)
func getUsersOnline(ctx context.Context, cacheDir, wsp string, usePlaywright bool) ([]slack.User, error) {
prov, err := authcmd.AuthCurrent(ctx, cacheDir, wsp, usePlaywright)
if err != nil {
return nil, err
}
Expand Down
4 changes: 3 additions & 1 deletion cmd/slackdump/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/rusq/tracer"
"golang.org/x/term"

"github.com/rusq/slackdump/v3/auth"
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/apiconfig"
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/authcmd"
"github.com/rusq/slackdump/v3/cmd/slackdump/internal/cfg"
Expand Down Expand Up @@ -178,12 +179,13 @@ func invoke(cmd *base.Command, args []string) error {
if cmd.RequireAuth {
trace.Logf(ctx, "invoke", "command %s requires auth", cmd.Name())
var err error
ctx, err = authcmd.AuthCurrentCtx(ctx, cfg.CacheDir(), cfg.Workspace)
prov, err := authcmd.AuthCurrent(ctx, cfg.CacheDir(), cfg.Workspace, cfg.LegacyBrowser)
if err != nil {
trace.Logf(ctx, "invoke", "auth error: %s", err)
base.SetExitStatus(base.SAuthError)
return fmt.Errorf("auth error: %w", err)
}
ctx = auth.WithContext(ctx, prov)
}
trace.Log(ctx, "command", fmt.Sprint("Running ", cmd.Name(), " command"))
return cmd.Run(ctx, cmd, args)
Expand Down
29 changes: 17 additions & 12 deletions internal/cache/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ const ezLogin = "EZ-Login 3000"
// isWSL is true if we're running in the WSL environment
var isWSL = os.Getenv("WSL_DISTRO_NAME") != ""

// SlackCreds holds the Token and Cookie reference.
type SlackCreds struct {
Token string
Cookie string
// AuthData is the authentication data.
type AuthData struct {
Token string
Cookie string
UsePlaywright bool
}

var (
Expand All @@ -43,7 +44,7 @@ const (
ATValue
ATCookieFile
ATRod
ATBrowser
ATPlaywright
)

// Type returns the authentication type that should be used for the current
Expand All @@ -52,7 +53,11 @@ const (
// 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) (AuthType, error) {
func (c AuthData) Type(context.Context) (AuthType, error) {
ez := ATRod
if c.UsePlaywright {
ez = ATPlaywright
}
if !c.IsEmpty() {
if isExistingFile(c.Cookie) {
return ATCookieFile, nil
Expand All @@ -64,18 +69,18 @@ func (c SlackCreds) Type(ctx context.Context) (AuthType, error) {
return ATInvalid, ErrUnsupported
}
if !ezLoginTested() {
return ATRod, ErrNotTested
return ez, ErrNotTested
}
return ATRod, nil
return ez, nil
}

func (c SlackCreds) IsEmpty() bool {
func (c AuthData) IsEmpty() bool {
return c.Token == "" || (auth.IsClientToken(c.Token) && c.Cookie == "")
}

// AuthProvider returns the appropriate auth Provider depending on the values
// of the token and cookie.
func (c SlackCreds) AuthProvider(ctx context.Context, workspace string, opts ...auth.Option) (auth.Provider, error) {
func (c AuthData) AuthProvider(ctx context.Context, workspace string, opts ...auth.Option) (auth.Provider, error) {
authType, err := c.Type(ctx)
if err != nil {
return nil, err
Expand All @@ -85,14 +90,14 @@ func (c SlackCreds) AuthProvider(ctx context.Context, workspace string, opts ...
opts = append([]auth.Option{auth.BrowserWithWorkspace(workspace)}, opts...)

switch authType {
case ATBrowser:
return auth.NewBrowserAuth(ctx, opts...)
case ATCookieFile:
return auth.NewCookieFileAuth(c.Token, c.Cookie)
case ATValue:
return auth.NewValueAuth(c.Token, c.Cookie)
case ATRod:
return auth.NewRODAuth(ctx, opts...)
case ATPlaywright:
return auth.NewBrowserAuth(ctx, opts...)
}
return nil, errors.New("internal error: unsupported auth type")
}
Expand Down
Loading

0 comments on commit df388f5

Please sign in to comment.