-
Notifications
You must be signed in to change notification settings - Fork 282
/
keystore.go
135 lines (110 loc) · 2.54 KB
/
keystore.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
// Copyright 2016 Yahoo Inc.
// Licensed under the terms of the Apache version 2.0 license. See LICENSE file for terms.
package zmssvctoken
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"sync"
"time"
)
type keySource struct {
domain string
name string
keyVersion string
}
type validatorMeta struct {
pubKey []byte
validator TokenValidator
expiry time.Time
}
func (k keySource) String() string {
return fmt.Sprintf("[Domain: '%s', Name: '%s', Key version: '%s']", k.domain, k.name, k.keyVersion)
}
type keyStore struct {
sync.RWMutex
cache map[keySource]*validatorMeta
config *ValidationConfig
}
func newKeyStore(cfg *ValidationConfig) *keyStore {
return &keyStore{
config: cfg,
cache: make(map[keySource]*validatorMeta),
}
}
func (k *keyStore) loadKey(src keySource) ([]byte, error) {
client := &http.Client{
Timeout: k.config.PublicKeyFetchTimeout,
}
url := fmt.Sprintf("%s/domain/%s/service/%s/publickey/%s", k.config.ZTSBaseUrl, src.domain, src.name, src.keyVersion)
res, err := client.Get(url)
if err != nil {
return nil, err
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("ZTS returned status %d", res.StatusCode)
}
b, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
return nil, err
}
var data struct {
Key string
}
err = json.Unmarshal(b, &data)
if err != nil {
return nil, err
}
s, err := new(YBase64).DecodeString(data.Key)
if err != nil {
return nil, err
}
return s, nil
}
func (k *keyStore) getValidator(src keySource) (TokenValidator, error) {
var (
oldKey []byte // caches the previous seen key to avoid reloading
oldValidator TokenValidator // caches the previous seen validator, ditto
)
k.RLock()
meta, ok := k.cache[src]
if ok {
oldKey = meta.pubKey
oldValidator = meta.validator
if meta.expiry.Before(time.Now()) { // dead
meta = nil
}
}
k.RUnlock()
// return from cache if valid entry
if meta != nil {
return meta.validator, nil
}
// get remote key, if not
key, err := k.loadKey(src)
if err != nil {
return nil, fmt.Errorf("Unable to get public key from ZTS for %v, err: %v", src, err)
}
var v TokenValidator
if oldKey != nil && bytes.Equal(key, oldKey) { // no changes to key, use old validator
v = oldValidator
} else {
v, err = NewPubKeyTokenValidator(key)
if err != nil {
return nil, err
}
}
meta = &validatorMeta{
pubKey: key,
validator: v,
expiry: time.Now().Add(k.config.CacheTTL),
}
// add to cache
k.Lock()
k.cache[src] = meta
k.Unlock()
return v, nil
}