-
-
Notifications
You must be signed in to change notification settings - Fork 72
/
auth.go
142 lines (121 loc) · 3.4 KB
/
auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package auth
import (
"context"
"encoding/json"
"errors"
"io"
"net/http"
"runtime/trace"
"strings"
"github.com/rusq/chttp"
"github.com/slack-go/slack"
)
const SlackURL = "https://slack.com"
// Type is the auth type.
//
//go:generate stringer -type Type -linecomment
type Type uint8
// All supported auth types.
const (
TypeInvalid Type = iota // Invalid
TypeValue // Value
TypeCookieFile // Cookie File
TypeBrowser // EZ-Login 3000
TypeRod // EZ-Login 3001
)
// Provider is the Slack Authentication provider.
//
//go:generate mockgen -destination ../internal/mocks/mock_auth/mock_auth.go github.com/rusq/slackdump/v2/auth Provider
type Provider interface {
// SlackToken should return the Slack Token value.
SlackToken() string
// Cookies should return a set of Slack Session cookies.
Cookies() []*http.Cookie
// Type returns the auth type.
Type() Type
// Validate should return error, in case the token or cookies cannot be
// retrieved.
Validate() error
// Test tests if credentials are valid.
Test(ctx context.Context) error
// Client returns an authenticated HTTP client
HTTPClient() (*http.Client, error)
}
var (
ErrNoToken = errors.New("no token")
ErrNoCookies = errors.New("no cookies")
ErrNotSupported = errors.New("not supported")
// ErrCancelled may be returned by auth providers, if the authentication
// process was cancelled.
ErrCancelled = errors.New("authentication cancelled")
)
type simpleProvider struct {
Token string
Cookie []*http.Cookie
}
func (c simpleProvider) Validate() error {
if c.Token == "" {
return ErrNoToken
}
if IsClientToken(c.Token) && len(c.Cookie) == 0 {
return ErrNoCookies
}
return nil
}
func (c simpleProvider) SlackToken() string {
return c.Token
}
func (c simpleProvider) Cookies() []*http.Cookie {
return c.Cookie
}
// Load deserialises JSON data from reader and returns a ValueAuth, that can
// be used to authenticate Slackdump. It will return ErrNoToken or
// ErrNoCookie if the authentication information is missing.
func Load(r io.Reader) (ValueAuth, error) {
dec := json.NewDecoder(r)
var s simpleProvider
if err := dec.Decode(&s); err != nil {
return ValueAuth{}, err
}
return ValueAuth{s}, s.Validate()
}
// Save serialises authentication information to writer. It will return
// ErrNoToken or ErrNoCookie if provider fails validation.
func Save(w io.Writer, p Provider) error {
if err := p.Validate(); err != nil {
return err
}
s := simpleProvider{
Token: p.SlackToken(),
Cookie: p.Cookies(),
}
enc := json.NewEncoder(w)
if err := enc.Encode(s); err != nil {
return err
}
return nil
}
// IsClientToken returns true if the tok is a web-client token.
func IsClientToken(tok string) bool {
return strings.HasPrefix(tok, "xoxc-")
}
// TestAuth attempts to authenticate with the given provider. It will return
// AuthError if failed.
func (s simpleProvider) Test(ctx context.Context) error {
ctx, task := trace.NewTask(ctx, "TestAuth")
defer task.End()
httpCl, err := s.HTTPClient()
if err != nil {
return &Error{Err: err}
}
cl := slack.New(s.Token, slack.OptionHTTPClient(httpCl))
region := trace.StartRegion(ctx, "simpleProvider.Test")
defer region.End()
if _, err := cl.AuthTestContext(ctx); err != nil {
return &Error{Err: err}
}
return nil
}
func (s simpleProvider) HTTPClient() (*http.Client, error) {
return chttp.New(SlackURL, s.Cookies())
}