Skip to content

Commit d7b60dd

Browse files
committed
Credentials Provider - Core Interface
1 parent c846eb0 commit d7b60dd

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed

credentials_provider.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package entraid
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
7+
"github.com/redis-developer/go-redis-entraid/manager"
8+
"github.com/redis-developer/go-redis-entraid/token"
9+
"github.com/redis/go-redis/v9/auth"
10+
)
11+
12+
// Ensure entraidCredentialsProvider implements the auth.StreamingCredentialsProvider interface.
13+
var _ auth.StreamingCredentialsProvider = (*entraidCredentialsProvider)(nil)
14+
15+
// entraidCredentialsProvider is a struct that implements the StreamingCredentialsProvider interface.
16+
type entraidCredentialsProvider struct {
17+
options CredentialsProviderOptions
18+
19+
tokenManager manager.TokenManager
20+
cancelTokenManager manager.CancelFunc
21+
22+
// listeners is a slice of listeners that are notified when the token manager receives a new token.
23+
listeners []auth.CredentialsListener
24+
25+
// rwLock is a mutex that is used to synchronize access to the listeners slice.
26+
rwLock sync.RWMutex
27+
}
28+
29+
// onTokenNext is a method that is called when the token manager receives a new token.
30+
func (e *entraidCredentialsProvider) onTokenNext(t *token.Token) {
31+
e.rwLock.RLock()
32+
defer e.rwLock.RUnlock()
33+
// Notify all listeners with the new token.
34+
for _, listener := range e.listeners {
35+
listener.OnNext(t)
36+
}
37+
}
38+
39+
// onTokenError is a method that is called when the token manager encounters an error.
40+
// It notifies all listeners with the error.
41+
func (e *entraidCredentialsProvider) onTokenError(err error) {
42+
e.rwLock.RLock()
43+
defer e.rwLock.RUnlock()
44+
45+
// Notify all listeners with the error
46+
for _, listener := range e.listeners {
47+
listener.OnError(err)
48+
}
49+
}
50+
51+
// Subscribe subscribes to the credentials provider and returns a channel that will receive updates.
52+
// The first response is blocking, then data will notify the listener.
53+
// The listener will be notified with the credentials when they are available.
54+
// The listener will be notified with an error if there is an error obtaining the credentials.
55+
// The caller can cancel the subscription by calling the cancel function which is the second return value.
56+
func (e *entraidCredentialsProvider) Subscribe(listener auth.CredentialsListener) (auth.Credentials, auth.CancelProviderFunc, error) {
57+
e.rwLock.Lock()
58+
// Check if the listener is already in the list of listeners.
59+
alreadySubscribed := false
60+
for _, l := range e.listeners {
61+
if l == listener {
62+
alreadySubscribed = true
63+
break
64+
}
65+
}
66+
67+
if !alreadySubscribed {
68+
// add new listener
69+
e.listeners = append(e.listeners, listener)
70+
}
71+
e.rwLock.Unlock()
72+
73+
token, err := e.tokenManager.GetToken(false)
74+
if err != nil {
75+
go listener.OnError(err)
76+
return nil, nil, err
77+
}
78+
79+
// Notify the listener with the credentials.
80+
go listener.OnNext(token)
81+
82+
cancel := func() error {
83+
// Remove the listener from the list of listeners.
84+
e.rwLock.Lock()
85+
defer e.rwLock.Unlock()
86+
for i, l := range e.listeners {
87+
if l == listener {
88+
e.listeners = append(e.listeners[:i], e.listeners[i+1:]...)
89+
break
90+
}
91+
}
92+
if len(e.listeners) == 0 {
93+
if e.cancelTokenManager != nil {
94+
defer func() {
95+
e.cancelTokenManager = nil
96+
e.listeners = nil
97+
}()
98+
return e.cancelTokenManager()
99+
}
100+
}
101+
return nil
102+
}
103+
104+
return token, cancel, nil
105+
}
106+
107+
// NewCredentialsProvider creates a new credentials provider.
108+
// It takes a TokenManager and CredentialProviderOptions as arguments and returns a StreamingCredentialsProvider interface.
109+
// The TokenManager is used to obtain the token, and the CredentialProviderOptions contains options for the credentials provider.
110+
// The credentials provider is responsible for managing the credentials and refreshing them when necessary.
111+
// It returns an error if the token manager cannot be started.
112+
//
113+
// This function is typically used when you need to create a custom credentials provider with a specific token manager.
114+
// For most use cases, it's recommended to use the type-specific constructors:
115+
// - NewManagedIdentityCredentialsProvider for managed identity authentication
116+
// - NewConfidentialCredentialsProvider for client secret or certificate authentication
117+
// - NewDefaultAzureCredentialsProvider for default Azure identity authentication
118+
func NewCredentialsProvider(tokenManager manager.TokenManager, options CredentialsProviderOptions) (auth.StreamingCredentialsProvider, error) {
119+
cp := &entraidCredentialsProvider{
120+
tokenManager: tokenManager,
121+
options: options,
122+
}
123+
cancelTokenManager, err := cp.tokenManager.Start(tokenListenerFromCP(cp))
124+
if err != nil {
125+
return nil, fmt.Errorf("couldn't start token manager: %w", err)
126+
}
127+
cp.cancelTokenManager = cancelTokenManager
128+
return cp, nil
129+
}

0 commit comments

Comments
 (0)