Skip to content

Commit

Permalink
workspace management and auth
Browse files Browse the repository at this point in the history
  • Loading branch information
rusq committed Oct 27, 2022
1 parent 8e1e7a8 commit 350ab4c
Show file tree
Hide file tree
Showing 23 changed files with 375 additions and 34 deletions.
1 change: 1 addition & 0 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ func Save(w io.Writer, p Provider) error {
return nil
}

// IsClientToken returns true if the tok is a web-client token.
func IsClientToken(tok string) bool {
return strings.HasPrefix(tok, "xoxc-")
}
22 changes: 22 additions & 0 deletions auth/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package auth

import (
"context"
"errors"
)

type ctxKey int

const providerKey ctxKey = 0

func FromContext(ctx context.Context) (Provider, error) {
prov, ok := ctx.Value(providerKey).(Provider)
if !ok {
return nil, errors.New("internal error: no provider in context")
}
return prov, nil
}

func WithContext(pctx context.Context, p Provider) context.Context {
return context.WithValue(pctx, providerKey, p)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package app
package cfg

import (
"os"
Expand All @@ -22,5 +22,8 @@ func ucd(ucdFn func() (string, error)) string {
}

func CacheDir() string {
if cacheDir == "" {

}
return ucd(os.UserCacheDir)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package app
package cfg

import (
"errors"
Expand Down
20 changes: 17 additions & 3 deletions cmd/slackdump/internal/cfg/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,24 @@ import (
"os"

"github.com/rusq/osenv/v2"

"github.com/rusq/slackdump/v2"
)

var (
TraceFile string
LogFile string
Verbose bool

ConfigFile string
SlackToken string
SlackCookie string
ConfigFile string
BaseLoc string // base location - directory or a zip file.
cacheDir string // cache directory
Workspace string

SlackToken string
SlackCookie string
SlackOptions = slackdump.DefOptions

DownloadFiles bool
)

Expand All @@ -26,6 +34,7 @@ const (
OmitAuthFlags FlagMask = 1 << iota
OmitDownloadFlag
OmitConfigFlag
OmitBaseLoc
)

// SetBaseFlags sets base flags
Expand All @@ -45,4 +54,9 @@ func SetBaseFlags(fs *flag.FlagSet, mask FlagMask) {
if mask&OmitConfigFlag == 0 {
fs.StringVar(&ConfigFile, "config", "", "configuration `file` with API limits overrides")
}
if mask&OmitBaseLoc == 0 {
fs.StringVar(&BaseLoc, "base", os.Getenv("BASE_LOC"), "a `location` (directory or a ZIP file) on a local disk where the files will be saved.")
}
fs.StringVar(&cacheDir, "cache-dir", osenv.Value("CACHE_DIR", CacheDir()), "cache `directory` location")
fs.StringVar(&Workspace, "workspace", osenv.Value("SLACK_WORKSPACE", ""), "Slack workspace to use") // TODO: load from configuration.
}
53 changes: 53 additions & 0 deletions cmd/slackdump/internal/emoji/emoji.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package emoji

import (
"context"

"github.com/rusq/dlog"

"github.com/rusq/slackdump/v2"
"github.com/rusq/slackdump/v2/auth"
"github.com/rusq/slackdump/v2/cmd/slackdump/internal/cfg"
"github.com/rusq/slackdump/v2/cmd/slackdump/internal/golang/base"
"github.com/rusq/slackdump/v2/internal/app/emoji"
)

var CmdEmoji = &base.Command{
Run: runEmoji,
Wizard: func(context.Context, *base.Command, []string) error { panic("not implemented") },
UsageLine: "slackdump emoji [flags]",
Short: "download workspace emojis",
Long: "",
FlagMask: cfg.OmitDownloadFlag | cfg.OmitConfigFlag,
RequireAuth: true,
PrintFlags: true,
}

// emoji specific flags
var (
ignoreErrors bool
)

func init() {
CmdEmoji.Flag.BoolVar(&ignoreErrors, "ignore-errors", true, "ignore download errors (skip failed emojis)")
}

func runEmoji(ctx context.Context, cmd *base.Command, args []string) {
prov, err := auth.FromContext(ctx)
if err != nil {
base.SetExitStatus(base.SAuthError)
dlog.Printf("auth error: %s", err)
return
}
sess, err := slackdump.NewWithOptions(ctx, prov, cfg.SlackOptions)
if err != nil {
base.SetExitStatus(base.SApplicationError)
dlog.Printf("application error: %s", err)
return
}
if err := emoji.Dl(ctx, sess, cfg.BaseLoc, ignoreErrors); err != nil {
base.SetExitStatus(base.SApplicationError)
dlog.Printf("application error: %s", err)
return
}
}
6 changes: 5 additions & 1 deletion cmd/slackdump/internal/golang/base/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ type Command struct {
// It only matters for the commands that have no subcommands.
PrintFlags bool

// RequireAuth is a flag that indicates that the command requires
// authentication.
RequireAuth bool

// Commands lists the available commands and help topics.
// The order here is the order in which they are printed by 'go help'.
// Note that subcommands are in general best avoided.
Expand All @@ -64,7 +68,7 @@ var Slackdump = &Command{
// Commands initialised in main.
}

var exitStatus = 0
var exitStatus = SNoError
var exitMu sync.Mutex

func SetExitStatus(n int) {
Expand Down
13 changes: 13 additions & 0 deletions cmd/slackdump/internal/golang/base/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package base

// Some status codes returned by the main executable.
const (
SNoError = iota
SGenericError
SInvalidParameters
SHelpRequested
SAuthError
SApplicationError
SWorkspaceError
SCacheError
)
12 changes: 7 additions & 5 deletions cmd/slackdump/internal/v1/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import (
"github.com/slack-go/slack"

"github.com/rusq/slackdump/v2"
"github.com/rusq/slackdump/v2/cmd/slackdump/internal/cfg"
"github.com/rusq/slackdump/v2/cmd/slackdump/internal/golang/base"
"github.com/rusq/slackdump/v2/export"
"github.com/rusq/slackdump/v2/internal/app"
"github.com/rusq/slackdump/v2/internal/app/appauth"
"github.com/rusq/slackdump/v2/internal/app/config"
"github.com/rusq/slackdump/v2/internal/structures"
"github.com/rusq/slackdump/v2/logger"
Expand Down Expand Up @@ -73,7 +75,7 @@ var secrets = []string{".env", ".env.txt", "secrets.txt"}
// params is the command line parameters
type params struct {
appCfg config.Params
creds app.SlackCreds
creds appauth.SlackCreds
authReset bool

traceFile string // trace file
Expand Down Expand Up @@ -109,7 +111,7 @@ func runV1(ctx context.Context, cmd *base.Command, args []string) {
return
}
if params.authReset {
if err := app.AuthReset(params.appCfg.Options.CacheDir); err != nil {
if err := appauth.AuthReset(params.appCfg.Options.CacheDir); err != nil {
if !os.IsNotExist(err) {
dlog.Printf("auth reset error: %s", err)
}
Expand Down Expand Up @@ -145,11 +147,11 @@ func run(ctx context.Context, p params) error {
ctx, task := trace.NewTask(ctx, "main.run")
defer task.End()

provider, err := app.InitProvider(ctx, p.appCfg.Options.CacheDir, "", p.creds)
provider, err := appauth.InitProvider(ctx, p.appCfg.Options.CacheDir, "", p.creds)
if err != nil {
return err
} else {
p.creds = app.SlackCreds{}
p.creds = appauth.SlackCreds{}
}

// trace startup parameters for debugging
Expand Down Expand Up @@ -337,7 +339,7 @@ func parseCmdLine(args []string) (params, error) {
fs.IntVar(&p.appCfg.Options.RepliesPerReq, "rpr", slackdump.DefOptions.RepliesPerReq, "number of `replies` per request.")

// - cache controls
fs.StringVar(&p.appCfg.Options.CacheDir, "cache-dir", app.CacheDir(), "slackdump cache directory")
fs.StringVar(&p.appCfg.Options.CacheDir, "cache-dir", cfg.CacheDir(), "slackdump cache directory")
fs.StringVar(&p.appCfg.Options.UserCacheFilename, "user-cache-file", slackdump.DefOptions.UserCacheFilename, "user cache file`name`.")
fs.DurationVar(&p.appCfg.Options.MaxUserCacheAge, "user-cache-age", slackdump.DefOptions.MaxUserCacheAge, "user cache lifetime `duration`. Set this to 0 to disable cache.")
fs.BoolVar(&p.appCfg.Options.NoUserCache, "no-user-cache", slackdump.DefOptions.NoUserCache, "skip fetching users")
Expand Down
9 changes: 5 additions & 4 deletions cmd/slackdump/internal/v1/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import (
"github.com/stretchr/testify/assert"

"github.com/rusq/slackdump/v2"
"github.com/rusq/slackdump/v2/internal/app"
"github.com/rusq/slackdump/v2/cmd/slackdump/internal/cfg"
"github.com/rusq/slackdump/v2/internal/app/appauth"
"github.com/rusq/slackdump/v2/internal/app/config"
"github.com/rusq/slackdump/v2/internal/structures"
)
Expand Down Expand Up @@ -43,7 +44,7 @@ func Test_output_validFormat(t *testing.T) {

func Test_checkParameters(t *testing.T) {
// setup
slackdump.DefOptions.CacheDir = app.CacheDir()
slackdump.DefOptions.CacheDir = cfg.CacheDir()

// test
type args struct {
Expand All @@ -59,7 +60,7 @@ func Test_checkParameters(t *testing.T) {
"channels",
args{[]string{"-c", "-t", "x", "-cookie", "d"}},
params{
creds: app.SlackCreds{
creds: appauth.SlackCreds{
Token: "x",
Cookie: "d",
},
Expand All @@ -79,7 +80,7 @@ func Test_checkParameters(t *testing.T) {
"users",
args{[]string{"-u", "-t", "x", "-cookie", "d"}},
params{
creds: app.SlackCreds{
creds: appauth.SlackCreds{
Token: "x",
Cookie: "d",
},
Expand Down
7 changes: 4 additions & 3 deletions cmd/slackdump/internal/wizard/wizard.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import (

"github.com/AlecAivazis/survey/v2"
"github.com/rusq/dlog"
"github.com/rusq/slackdump/v2/cmd/slackdump/internal/golang/base"
"golang.org/x/text/cases"
"golang.org/x/text/language"

"github.com/rusq/slackdump/v2/cmd/slackdump/internal/golang/base"
)

var CmdWizard = &base.Command{
Expand All @@ -19,6 +20,7 @@ var CmdWizard = &base.Command{
Long: `
Slackdump Wizard guides through the dumping process.
`,
RequireAuth: true,
}

var titlecase = cases.Title(language.English)
Expand Down Expand Up @@ -53,8 +55,7 @@ func runWizard(ctx context.Context, cmd *base.Command, args []string) {

menu := makeMenu(baseCommands, "", "What would you like to do?")
if err := show(menu, func(cmd *base.Command) error {
cmd.Run(ctx, cmd, args)
return nil
return cmd.Wizard(ctx, cmd, args)
}); err != nil {
dlog.Println("error running wizard: %s", err)
base.SetExitStatus(1)
Expand Down
1 change: 1 addition & 0 deletions cmd/slackdump/internal/workspace/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package workspace
76 changes: 76 additions & 0 deletions cmd/slackdump/internal/workspace/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package workspace

import (
"context"
"fmt"
"os"
"path/filepath"

"github.com/rusq/dlog"

"github.com/rusq/slackdump/v2/cmd/slackdump/internal/cfg"
"github.com/rusq/slackdump/v2/cmd/slackdump/internal/golang/base"
)

var CmdListWsp = &base.Command{
Run: runList,
Wizard: nil,
UsageLine: "slackdump workspace list [flags]",
Short: "list saved workspaces",
Long: `
List allows to list Slack Workspaces, that you have previously authenticated in.
`,
FlagMask: flagmask,
PrintFlags: true,
}

func runList(ctx context.Context, cmd *base.Command, args []string) {
entries, err := filepath.Glob(filepath.Join(cfg.CacheDir(), "*.bin"))
if err != nil {
dlog.Printf("error trying to find existing workspaces: %s", err)
base.SetExitStatus(base.SCacheError)
return
}
if len(entries) == 0 {
fmt.Println("No workspaces exist on this device.")
fmt.Println("Run: slackdump workspaces auth to authenticate.")
// TODO: do we want to ask user to authenticate?
return
}
fmt.Printf("Workspaces in %q:\n\n", cfg.CacheDir())
current, err := Current()
if err != nil {
dlog.Printf("error getting the current workspace: %s", err)
base.SetExitStatus(base.SWorkspaceError)
}
for _, direntry := range entries {
fmt.Println("\t" + formatWsp(direntry, current))
}
fmt.Println("\nCurrent workspace is marked with ' => '.")
}

func wspName(filename string) string {
name := filepath.Base(filename)
if name == defaultWspFilename {
name = "default"
} else {
ext := filepath.Ext(name)
name = name[:len(name)-len(ext)]
}
return name
}

func formatWsp(filename string, current string) string {
timestamp := "unknown"
if fi, err := os.Stat(filename); err == nil {
timestamp = fi.ModTime().Format("2006-01-02 15:04:05")
}
name := wspName(filename)
if filepath.Base(filename) == current {
name = "=> " + name
} else {
name = " " + name
}

return fmt.Sprintf("%s (last modified: %s)", name, timestamp)
}
3 changes: 3 additions & 0 deletions cmd/slackdump/internal/workspace/reset.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package workspace

// reset deletes all workspaces.
1 change: 1 addition & 0 deletions cmd/slackdump/internal/workspace/select.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package workspace
Loading

0 comments on commit 350ab4c

Please sign in to comment.