-
Notifications
You must be signed in to change notification settings - Fork 4
/
broker.go
140 lines (121 loc) · 3.27 KB
/
broker.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
package keys
import (
"context"
"crypto/rsa"
"fmt"
"log"
"math/big"
"time"
"github.com/dgrijalva/jwt-go"
)
// Renewer represents behaviour for marking a broker for renewal
type Renewer interface {
Renew()
}
// RSAPublicKeyCopier represents behaviour for distributing copies of public keys
type RSAPublicKeyCopier interface {
Copy() rsa.PublicKey
}
// RSAPublicKeyCopierRenewer represents the combination of a Copier and Renewer interface
type RSAPublicKeyCopierRenewer interface {
RSAPublicKeyCopier
Renewer
}
var (
defaultRSA = &rsa.PublicKey{E: 0, N: big.NewInt(0)}
)
// BrokerRSAPublicKey will broker a public key from a source on an interval
func BrokerRSAPublicKey(ctx context.Context, source Source, tick time.Duration) (RSAPublicKeyCopierRenewer, func()) {
broker := &RSAPublicKeyBroker{
source: source,
ticker: time.NewTicker(tick),
key: defaultRSA,
renew: make(chan struct{}, 1),
cancelled: make(chan struct{}, 1),
}
// Make sure the broker is marked for renewal immediately
broker.Renew()
// Begin the key renewal
go broker.run(ctx)
// Return the broker together with a separate cancel function
// We do this to ensure cancellation is handeled correctly
return broker, broker.cancel
}
// RSAPublicKeyBroker defines the implementation for brokering an RSA public key
type RSAPublicKeyBroker struct {
source Source
ticker *time.Ticker
key *rsa.PublicKey
renew chan struct{}
cancelled chan struct{}
}
// Copy returns a shallow copy o the RSA public key
func (b *RSAPublicKeyBroker) Copy() rsa.PublicKey {
return *b.key
}
// Renew will inform the broker to force renewal of the key
func (b *RSAPublicKeyBroker) Renew() {
select {
// Return early if the cancelled channel is already closed
case <-b.cancelled:
return
case b.renew <- struct{}{}:
// Exit if we can't send or receive on any channels
default:
}
}
// Close stops the ticker and releases resources
func (b *RSAPublicKeyBroker) cancel() {
// Close the cancelled channel first to stop all select switches
close(b.cancelled)
b.ticker.Stop()
close(b.renew)
}
// Run will periodically try and the public key
func (b *RSAPublicKeyBroker) run(ctx context.Context) {
for {
select {
case <-b.cancelled:
return
case <-b.ticker.C:
select {
case <-b.renew:
if err := b.get(ctx); err != nil {
log.Printf("RSA broker interval error: %v\n", err)
}
default:
}
case <-ctx.Done():
return
}
}
}
func (b *RSAPublicKeyBroker) get(ctx context.Context) error {
bts, err := b.source.Get(ctx)
if err != nil {
return fmt.Errorf("cannot get key: %v", err)
}
b.key, err = jwt.ParseRSAPublicKeyFromPEM(bts)
if err != nil {
return fmt.Errorf("cannot parse key: %v", err)
}
return nil
}
// MockRSAPublicKey resolves any source and returns a mocked RSAPublicKeyCopier and Renewer
func MockRSAPublicKey(key rsa.PublicKey) RSAPublicKeyCopierRenewer {
return &RSAPublicKeyBrokerMock{
key: &key,
}
}
// RSAPublicKeyBrokerMock defines the implementation for brokering an RSA public key during testing
type RSAPublicKeyBrokerMock struct {
key *rsa.PublicKey
}
// Copy returns a shallow copy o the RSA public key
func (b *RSAPublicKeyBrokerMock) Copy() rsa.PublicKey {
return *b.key
}
// Renew is a no-op
func (b *RSAPublicKeyBrokerMock) Renew() {
// no-op
}