credentials.go raw
1 package credentials
2
3 import (
4 "errors"
5 "sync"
6 )
7
8 var (
9 ErrMissingIncomplete = errors.New("missing or incomplete API credentials")
10 )
11
12 type Value struct {
13 APIKey string
14 APISecret string
15 }
16
17 // IsSet returns true if the credentials Value has both APIKey and APISecret.
18 func (v Value) IsSet() bool {
19 return v.APIKey != "" && v.APISecret != ""
20 }
21
22 type Provider interface {
23 // Retrieve returns nil if it successfully retrieved the value.
24 // Error is returned if the value were not obtainable, or empty.
25 Retrieve() (Value, error)
26
27 // IsExpired returns if the credentials are no longer valid, and need
28 // to be retrieved.
29 IsExpired() bool
30 }
31
32 type Credentials struct {
33 credentials Value
34 provider Provider
35
36 sync.RWMutex
37 }
38
39 func NewCredentials(provider Provider) *Credentials {
40 creds := &Credentials{
41 provider: provider,
42 }
43
44 return creds
45 }
46
47 func (c *Credentials) Expire() {
48 c.Lock()
49 defer c.Unlock()
50
51 c.credentials = Value{}
52 }
53
54 func (c *Credentials) Get() (Value, error) {
55 if c.IsExpired() {
56 if err := c.retrieve(); err != nil {
57 return Value{}, err
58 }
59 }
60 c.RLock()
61 defer c.RUnlock()
62
63 if !c.credentials.IsSet() {
64 return Value{}, ErrMissingIncomplete
65 }
66
67 return c.credentials, nil
68 }
69
70 func (c *Credentials) IsExpired() bool {
71 c.RLock()
72 defer c.RUnlock()
73
74 return (!c.credentials.IsSet() || c.provider.IsExpired())
75 }
76
77 func (c *Credentials) retrieve() error {
78 c.Lock()
79 defer c.Unlock()
80
81 v, err := c.provider.Retrieve()
82 if err != nil {
83 return err
84 }
85
86 c.credentials = v
87
88 return nil
89 }
90