-
Notifications
You must be signed in to change notification settings - Fork 280
/
provider.go
129 lines (119 loc) · 3.95 KB
/
provider.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
package oidc
import (
"context"
"fmt"
"strings"
"sync"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/cap/oidc"
)
var (
// cachedProviders provides a cache of oidc.Providers. This cache can't be
// done within the Repository, since a new Repository is created for every
// request.
cachedProviders *providers
initCachedProviders sync.Once
)
// providerCache returns the cache of providers
func providerCache() *providers {
initCachedProviders.Do(func() {
cachedProviders = newProviderCache()
})
return cachedProviders
}
// providers is a cache of oidc.Provider types used by the Repository to
// make requests to the IdP, verify ID tokens, etc. For more info on
// oidc.Provider capabilities see: https://github.com/hashicorp/cap
type providers struct {
cache map[string]*oidc.Provider
mu *sync.RWMutex
}
// newProviderCache make a new cache
func newProviderCache() *providers {
return &providers{
cache: map[string]*oidc.Provider{},
mu: &sync.RWMutex{},
}
}
// get determines if there's already a cached oidc.Provider for the
// current AuthMethod from the DB. The cached oidc.Provider is preferred since
// it maintains a cache of the JWKs required to verify ID Tokens from the IdP.
//
// Before returning a cached oidc.Provider, get ensures that the
// AuthMethod data used for the oidc.Provider's configuration hasn't changed
// since it was cached. This is necessary because another controller could
// update the AuthMethod in the DB, which would require changing the
// configuration of the cached provider.
//
// get will update the providerCache with the new AuthMethod, if it
// determines the provider's configuration has been updated in the DB.
func (c *providers) get(ctx context.Context, currentFromDb *AuthMethod) (*oidc.Provider, error) {
const op = "oidc.(providers).getProvider"
storedProvider, err := convertToProvider(ctx, currentFromDb)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
c.mu.RLock()
p, ok := c.cache[currentFromDb.PublicId]
c.mu.RUnlock()
if ok {
cachedHash, err := p.ConfigHash()
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to hash provider from cache"))
}
storedHash, err := storedProvider.ConfigHash()
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to hash provider from database"))
}
switch cachedHash == storedHash {
case true:
return p, nil
default:
c.delete(ctx, currentFromDb.PublicId)
}
}
c.set(ctx, currentFromDb.PublicId, storedProvider)
return storedProvider, nil
}
// set will set an entry in the cache.
func (c *providers) set(ctx context.Context, authMethodId string, p *oidc.Provider) {
c.mu.Lock()
defer c.mu.Unlock()
c.cache[authMethodId] = p
}
// delete will delete an entry in the cache.
func (c *providers) delete(ctx context.Context, authMethodId string) {
c.mu.Lock()
defer c.mu.Unlock()
delete(c.cache, authMethodId)
}
func convertToProvider(ctx context.Context, am *AuthMethod) (*oidc.Provider, error) {
const op = "oidc.convertToProvider"
if am == nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing auth method")
}
if err := am.isComplete(ctx); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
algs := make([]oidc.Alg, 0, len(am.SigningAlgs))
for _, a := range am.SigningAlgs {
algs = append(algs, oidc.Alg(a))
}
c, err := oidc.NewConfig(
am.Issuer,
am.ClientId,
oidc.ClientSecret(am.ClientSecret),
algs,
[]string{fmt.Sprintf(CallbackEndpoint, am.GetApiUrl())},
oidc.WithAudiences(am.AudClaims...),
oidc.WithProviderCA(strings.Join(am.Certificates, "\n")),
)
if err != nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "AuthMethod cannot be converted to a valid OIDC Provider Configuration", errors.WithWrap(err))
}
p, err := oidc.NewProvider(c)
if err != nil {
return nil, errors.New(ctx, errors.InvalidParameter, op, "AuthMethod cannot be converted to a valid OIDC Provider", errors.WithWrap(err))
}
return p, nil
}