1
15 package auth
16
17 import (
18 "context"
19 "strings"
20 "sync"
21
22 errdef "oras.land/oras-go/pkg/content"
23 "oras.land/oras-go/pkg/registry/remote/internal/syncutil"
24 )
25
26
27 var DefaultCache Cache = NewCache()
28
29
30
31
32
33 type Cache interface {
34
35
36
37
38
39 GetScheme(ctx context.Context, registry string) (Scheme, error)
40
41
42
43
44
45 GetToken(ctx context.Context, registry string, scheme Scheme, key string) (string, error)
46
47
48
49
50
51
52 Set(ctx context.Context, registry string, scheme Scheme, key string, fetch func(context.Context) (string, error)) (string, error)
53 }
54
55
56 type cacheEntry struct {
57 scheme Scheme
58 tokens sync.Map
59 }
60
61
62 type concurrentCache struct {
63 status sync.Map
64 cache sync.Map
65 }
66
67
68 func NewCache() Cache {
69 return &concurrentCache{}
70 }
71
72
73 func (cc *concurrentCache) GetScheme(ctx context.Context, registry string) (Scheme, error) {
74 entry, ok := cc.cache.Load(registry)
75 if !ok {
76 return SchemeUnknown, errdef.ErrNotFound
77 }
78 return entry.(*cacheEntry).scheme, nil
79 }
80
81
82
83 func (cc *concurrentCache) GetToken(ctx context.Context, registry string, scheme Scheme, key string) (string, error) {
84 entryValue, ok := cc.cache.Load(registry)
85 if !ok {
86 return "", errdef.ErrNotFound
87 }
88 entry := entryValue.(*cacheEntry)
89 if entry.scheme != scheme {
90 return "", errdef.ErrNotFound
91 }
92 if token, ok := entry.tokens.Load(key); ok {
93 return token.(string), nil
94 }
95 return "", errdef.ErrNotFound
96 }
97
98
99
100
101
102 func (cc *concurrentCache) Set(ctx context.Context, registry string, scheme Scheme, key string, fetch func(context.Context) (string, error)) (string, error) {
103
104 statusKey := strings.Join([]string{
105 registry,
106 scheme.String(),
107 key,
108 }, " ")
109 statusValue, _ := cc.status.LoadOrStore(statusKey, syncutil.NewOnce())
110 fetchOnce := statusValue.(*syncutil.Once)
111 fetchedFirst, result, err := fetchOnce.Do(ctx, func() (interface{}, error) {
112 return fetch(ctx)
113 })
114 if fetchedFirst {
115 cc.status.Delete(statusKey)
116 }
117 if err != nil {
118 return "", err
119 }
120 token := result.(string)
121 if !fetchedFirst {
122 return token, nil
123 }
124
125
126 newEntry := &cacheEntry{
127 scheme: scheme,
128 }
129 entryValue, exists := cc.cache.LoadOrStore(registry, newEntry)
130 entry := entryValue.(*cacheEntry)
131 if exists && entry.scheme != scheme {
132
133
134 entry = newEntry
135 cc.cache.Store(registry, entry)
136 }
137 entry.tokens.Store(key, token)
138
139 return token, nil
140 }
141
142
143 type noCache struct{}
144
145
146 func (noCache) GetScheme(ctx context.Context, registry string) (Scheme, error) {
147 return SchemeUnknown, errdef.ErrNotFound
148 }
149
150
151 func (noCache) GetToken(ctx context.Context, registry string, scheme Scheme, key string) (string, error) {
152 return "", errdef.ErrNotFound
153 }
154
155
156 func (noCache) Set(ctx context.Context, registry string, scheme Scheme, key string, fetch func(context.Context) (string, error)) (string, error) {
157 return fetch(ctx)
158 }
159
View as plain text