Adds EntraID auth support to all Redis Components (#3470)

Signed-off-by: Bernd Verst <github@bernd.dev>
This commit is contained in:
Bernd Verst 2024-07-01 17:20:29 -07:00 committed by GitHub
parent f09c2c2941
commit b656b0d5d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 273 additions and 66 deletions

View File

@ -1,8 +1,6 @@
module github.com/dapr/components-contrib/build-tools
go 1.22.0
toolchain go1.22.2
go 1.22.4
require (
github.com/dapr/components-contrib v0.0.0

View File

@ -65,7 +65,7 @@ jobs:
GOOS: ${{ matrix.target_os }}
GOARCH: ${{ matrix.target_arch }}
GOPROXY: https://proxy.golang.org
GOLANGCI_LINT_VER: "v1.55.2"
GOLANGCI_LINT_VER: "v1.59.1"
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
@ -143,10 +143,11 @@ jobs:
run: make check-component-metadata-schema-diff
- name: Run golangci-lint
if: matrix.target_arch == 'amd64' && matrix.target_os == 'linux' && steps.skip_check.outputs.should_skip != 'true'
uses: golangci/golangci-lint-action@v3.2.0
uses: golangci/golangci-lint-action@v6.0.1
with:
version: ${{ env.GOLANGCI_LINT_VER }}
skip-cache: true
only-new-issues: true
args: --timeout 15m
- name: Run go mod tidy check diff
if: matrix.target_arch == 'amd64' && matrix.target_os == 'linux' && steps.skip_check.outputs.should_skip != 'true'

View File

@ -33,7 +33,7 @@ jobs:
GOOS: linux
GOARCH: amd64
GOPROXY: https://proxy.golang.org
GOLANGCI_LINT_VER: "v1.55.2"
GOLANGCI_LINT_VER: "v1.59.1"
steps:
- name: Check out code into the Go module directory
if: ${{ steps.skip_check.outputs.should_skip != 'true' }}
@ -62,10 +62,11 @@ jobs:
run: make check-component-metadata
- name: Run golangci-lint
if: steps.skip_check.outputs.should_skip != 'true'
uses: golangci/golangci-lint-action@v3.4.0
uses: golangci/golangci-lint-action@v6.0.1
with:
version: ${{ env.GOLANGCI_LINT_VER }}
skip-cache: true
only-new-issues: true
args: --timeout 15m
- name: Run go mod tidy check diff
if: steps.skip_check.outputs.should_skip != 'true'

View File

@ -23,7 +23,7 @@ run:
# default value is empty list, but next dirs are always skipped independently
# from this option's value:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
skip-dirs:
issues.exclude-dirs:
- ^vendor$
# which files to skip: they will be analyzed, but issues from them
@ -37,7 +37,7 @@ run:
# output configuration options
output:
# colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number"
format: tab
formats: tab
# print lines of code with issue, default is true
print-issued-lines: true
@ -71,9 +71,6 @@ linters-settings:
statements: 40
govet:
# report about shadowed variables
check-shadowing: true
# settings per analyzer
settings:
printf: # analyzer name, run `go tool vet help` to see all analyzers
@ -86,6 +83,7 @@ linters-settings:
# enable or disable analyzers by name
enable:
- atomicalign
- shadow
enable-all: false
disable:
- shadow
@ -106,9 +104,6 @@ linters-settings:
gocognit:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
dupl:
# tokens count to trigger issue, 150 by default
threshold: 100
@ -121,6 +116,10 @@ linters-settings:
rules:
main:
deny:
- pkg: "github.com/golang-jwt/jwt/v5"
desc: "must use github.com/lestrrat-go/jwx/v2/jwt"
- pkg: "github.com/golang-jwt/jwt/v4"
desc: "must use github.com/lestrrat-go/jwx/v2/jwt"
- pkg: "github.com/Sirupsen/logrus"
desc: "must use github.com/dapr/kit/logger"
- pkg: "github.com/agrea/ptr"
@ -277,28 +276,24 @@ linters:
- gocyclo
- gocognit
- godox
- interfacer
- lll
- maligned
- scopelint
- unparam
- wsl
- mnd
- gomnd
- testpackage
- goerr113
- err113
- nestif
- nlreturn
- exhaustive
- exhaustruct
- noctx
- gci
- golint
- tparallel
- paralleltest
- wrapcheck
- tagliatelle
- ireturn
- exhaustivestruct
- errchkjson
- contextcheck
- gomoddirectives
@ -307,7 +302,6 @@ linters:
- varnamelen
- errorlint
- forcetypeassert
- ifshort
- maintidx
- nilnil
- predeclared
@ -320,10 +314,6 @@ linters:
- asasalint
- rowserrcheck
- sqlclosecheck
- structcheck
- deadcode
- nosnakecase
- varcheck
- goconst
- tagalign
- inamedparam

View File

@ -65,7 +65,7 @@ export GH_LINT_VERSION := $(shell grep 'GOLANGCI_LINT_VER:' .github/workflows/co
ifeq (,$(LINTER_BINARY))
INSTALLED_LINT_VERSION := "v0.0.0"
else
INSTALLED_LINT_VERSION=v$(shell $(LINTER_BINARY) version | grep -Eo '([0-9]+\.)+[0-9]+' - || "")
INSTALLED_LINT_VERSION=v$(shell $(LINTER_BINARY) version | grep -Eo '([0-9]+\.)+[0-9]+' - | head -1 || "")
endif
# Build tools
@ -249,4 +249,4 @@ prettier-format:
################################################################################
.PHONY: conf-tests
conf-tests:
CGO_ENABLED=$(CGO) go test -v -tags=conftests -count=1 ./tests/conformance
CGO_ENABLED=$(CGO) go test -v -tags=conftests -count=1 ./tests/conformance

View File

@ -193,3 +193,18 @@ metadata:
"-1" disables idle timeout check.
default: "5m"
example: "10m"
builtinAuthenticationProfiles:
- name: "azuread"
metadata:
- name: useEntraID
required: false
default: "false"
example: "true"
type: bool
description: |
If set, enables authentication to Azure Cache for Redis using Microsoft EntraID. The Redis server must explicitly enable EntraID authentication. Note that
Azure Cache for Redis also requires the use of TLS, so `enableTLS` should be set. No username or password should be set.
- name: enableTLS
required: true
description: Must be set to true if using EntraID
example: "true"

View File

@ -44,7 +44,7 @@ func NewRedis(logger logger.Logger) bindings.OutputBinding {
// Init performs metadata parsing and connection creation.
func (r *Redis) Init(ctx context.Context, meta bindings.Metadata) (err error) {
r.client, r.clientSettings, err = rediscomponent.ParseClientFromProperties(meta.Properties, metadata.BindingType)
r.client, r.clientSettings, err = rediscomponent.ParseClientFromProperties(meta.Properties, metadata.BindingType, ctx, &r.logger)
if err != nil {
return err
}

View File

@ -143,7 +143,7 @@ func TestInvokeDelete(t *testing.T) {
rgetRep, err := c.DoRead(context.Background(), "GET", testKey)
assert.Equal(t, redis.Nil, err)
assert.Equal(t, nil, rgetRep)
assert.Nil(t, rgetRep)
}
func TestCreateExpire(t *testing.T) {

View File

@ -15,15 +15,23 @@ package redis
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"time"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/cenkalti/backoff/v4"
"github.com/lestrrat-go/jwx/v2/jwt"
"golang.org/x/mod/semver"
"github.com/dapr/components-contrib/common/authentication/azure"
"github.com/dapr/components-contrib/configuration"
"github.com/dapr/components-contrib/metadata"
kitlogger "github.com/dapr/kit/logger"
kitretry "github.com/dapr/kit/retry"
)
const (
@ -82,6 +90,7 @@ type RedisClient interface {
XClaimResult(ctx context.Context, stream string, group string, consumer string, minIdleTime time.Duration, messageIDs []string) ([]RedisXMessage, error)
TxPipeline() RedisPipeliner
TTLResult(ctx context.Context, key string) (time.Duration, error)
AuthACL(ctx context.Context, username, password string) error
}
type ConfigurationSubscribeArgs struct {
@ -94,8 +103,8 @@ type ConfigurationSubscribeArgs struct {
Stop chan struct{}
}
func ParseClientFromProperties(properties map[string]string, componentType metadata.ComponentType) (client RedisClient, settings *Settings, err error) {
settings = &Settings{}
func ParseClientFromProperties(properties map[string]string, componentType metadata.ComponentType, ctx context.Context, logger *kitlogger.Logger) (RedisClient, *Settings, error) {
settings := Settings{}
// upgrade legacy metadata properties and set defaults
switch componentType {
@ -112,9 +121,9 @@ func ParseClientFromProperties(properties map[string]string, componentType metad
if properties[redisMinRetryIntervalKey] == "" {
if properties[maxRetryBackoffKey] != "" {
// due to different duration formats, do not simply change the key name
parsedVal, parseErr := strconv.ParseInt(properties[maxRetryBackoffKey], 10, 0)
if parseErr != nil {
return nil, nil, fmt.Errorf("redis store error: can't parse maxRetryBackoff field: %s", parseErr)
parsedVal, err := strconv.ParseInt(properties[maxRetryBackoffKey], 10, 0)
if err != nil {
return nil, nil, fmt.Errorf("redis store error: can't parse maxRetryBackoff field: %s", err)
}
settings.RedisMinRetryInterval = Duration(time.Duration(parsedVal))
}
@ -132,7 +141,7 @@ func ParseClientFromProperties(properties map[string]string, componentType metad
settings.Concurrency = 10
}
err = settings.Decode(properties)
err := settings.Decode(properties)
if err != nil {
return nil, nil, fmt.Errorf("redis client configuration error: %w", err)
}
@ -157,6 +166,14 @@ func ParseClientFromProperties(properties map[string]string, componentType metad
// if there was an error we would try to interpret it as a duration string, which was already done in Decode()
}
}
var tokenExpires *time.Time
var tokenCredential *azcore.TokenCredential
if settings.UseEntraID {
tokenExpires, tokenCredential, err = settings.GetEntraIDCredentialAndSetInitialTokenAsPassword(ctx, &properties)
if err != nil {
return nil, nil, err
}
}
var c RedisClient
newClientFunc := newV8Client
@ -164,16 +181,19 @@ func ParseClientFromProperties(properties map[string]string, componentType metad
newClientFunc = newV8FailoverClient
}
c, err = newClientFunc(settings)
c, err = newClientFunc(&settings)
if err != nil {
return nil, nil, fmt.Errorf("redis client configuration error: %w", err)
}
version, versionErr := GetServerVersion(c)
c.Close() // close the client to avoid leaking connections
version, err := GetServerVersion(c)
closeErr := c.Close() // close the client to avoid leaking connections
if closeErr != nil {
return nil, nil, closeErr
}
useNewClient := false
if versionErr != nil {
if err != nil {
// we couldn't query the server version, so we will assume the v8 client is not supported
useNewClient = true
} else if semver.Compare("v"+version, "v7.0.0") > -1 {
@ -187,11 +207,133 @@ func ParseClientFromProperties(properties map[string]string, componentType metad
newClientFunc = newV9FailoverClient
}
}
c, err = newClientFunc(settings)
c, err = newClientFunc(&settings)
if err != nil {
return nil, nil, fmt.Errorf("redis client configuration error: %w", err)
}
return c, settings, nil
// start the token refresh goroutine
if settings.UseEntraID {
StartEntraIDTokenRefreshBackgroundRoutine(c, settings.Username, *tokenExpires, tokenCredential, ctx, logger)
}
return c, &settings, nil
}
func StartEntraIDTokenRefreshBackgroundRoutine(client RedisClient, username string, nextExpiration time.Time, cred *azcore.TokenCredential, parentCtx context.Context, logger *kitlogger.Logger) {
go func(cred *azcore.TokenCredential, username string, logger *kitlogger.Logger) {
ctx, cancel := context.WithCancel(parentCtx)
defer cancel()
backoffConfig := kitretry.DefaultConfig()
backoffConfig.MaxRetries = 3
backoffConfig.Policy = kitretry.PolicyExponential
var backoffManager backoff.BackOff
const refreshGracePeriod = 2 * time.Minute
tokenRefreshDuration := time.Until(nextExpiration.Add(-refreshGracePeriod))
(*logger).Debugf("redis client: starting entraID token refresh loop")
for {
(*logger).Debugf("redis client: next entraID token refresh: %v", tokenRefreshDuration)
select {
case <-ctx.Done():
(*logger).Infof("redis client: entraID token refresh stopped due to context cancellation")
return
case <-time.After(tokenRefreshDuration):
(*logger).Debug("redis client: refreshing entraID token")
// Get a new access token
backoffManager = backoffConfig.NewBackOffWithContext(ctx)
var token azcore.AccessToken
tokenErr := kitretry.NotifyRecover(
func() error {
var innerTokenErr error
token, innerTokenErr = (*cred).GetToken(ctx, policy.TokenRequestOptions{
Scopes: []string{"https://redis.azure.com/.default"},
})
return innerTokenErr
},
backoffManager,
func(err error, _ time.Duration) {
(*logger).Debugf("redis client: entraID token acquisition failed with error: %v. Retrying...", err)
},
func() {
(*logger).Debug("redis client: entraID token acquisition succeeded after error")
},
)
if tokenErr != nil {
_ = client.Close()
(*logger).Fatalf("redis client: entraID token acquisition failed: %v", tokenErr)
return
}
// Use the new access token via the Redis AUTH command
backoffManager = backoffConfig.NewBackOffWithContext(ctx)
authErr := kitretry.NotifyRecover(
func() error {
var innerAuthErr error
innerAuthErr = client.AuthACL(ctx, username, token.Token)
return innerAuthErr
},
backoffManager,
func(err error, _ time.Duration) {
(*logger).Debugf("redis client: entraID auth failed with error: %v. Retrying...", err)
},
func() {
(*logger).Debug("redis client: entraID auth succeeded after error")
},
)
if authErr != nil {
_ = client.Close()
(*logger).Fatalf("redis client: entraID auth failed: %v", authErr)
return
}
// Since the entraID auth succeeded we are setting the duration to wait for the next iteration of the refresh loop
(*logger).Debugf("redis client: entraID auth token successfully refreshed with the server")
tokenRefreshDuration = time.Until(token.ExpiresOn.Add(-refreshGracePeriod))
}
}
}(cred, username, logger)
}
func (s *Settings) GetEntraIDCredentialAndSetInitialTokenAsPassword(ctx context.Context, properties *map[string]string) (*time.Time, *azcore.TokenCredential, error) {
if len(s.Password) > 0 || len(s.Username) > 0 {
return nil, nil, errors.New(
"redis client configuration error: username or password must not be specified when using Entra ID authentication")
}
envSettings, err := azure.NewEnvironmentSettings(*properties)
if err != nil {
return nil, nil, fmt.Errorf("redis client configuration error: %w", err)
}
cred, err := envSettings.GetTokenCredential()
if err != nil {
return nil, nil, fmt.Errorf("redis client configuration error: %w", err)
}
token, err := cred.GetToken(ctx, policy.TokenRequestOptions{
Scopes: []string{"https://redis.azure.com/.default"},
})
if err != nil {
return nil, nil, fmt.Errorf("redis client configuration error: %w", err)
}
s.Password = token.Token
// This token has already been validated by EntraID. We use insecure parsing to get the object ID.
parsedToken, err := jwt.ParseString(token.Token, jwt.WithVerify(false), jwt.WithValidate(false))
if err != nil {
return nil, nil, fmt.Errorf("redis client configuration error: %w", err)
}
objectID, found := parsedToken.Get("oid")
if found {
s.Username = objectID.(string)
} else {
return nil, nil, errors.New("redis client configuration error: could not parse object ID from Auth token")
}
return &token.ExpiresOn, &cred, nil
}
func ClientHasJSONSupport(c RedisClient) bool {

View File

@ -105,6 +105,7 @@ func TestParseRedisMetadata(t *testing.T) {
assert.Equal(t, 1*time.Second, time.Duration(m.IdleCheckFrequency))
assert.True(t, m.Failover)
assert.Equal(t, "master", m.SentinelMasterName)
assert.False(t, m.UseEntraID)
})
// TODO: Refactor shared redis code to throw error for missing properties

View File

@ -101,6 +101,10 @@ type Settings struct {
// The max len of stream
MaxLenApprox int64 `mapstructure:"maxLenApprox" mdonly:"pubsub"`
// EntraID / AzureAD Authentication based on the shared code which essentially uses the DefaultAzureCredential
// from the official Azure Identity SDK for Go
UseEntraID bool `mapstructure:"useEntraID" mapstructurealiases:"useAzureAD"`
}
func (s *Settings) Decode(in interface{}) error {

View File

@ -316,6 +316,12 @@ func (c v8Client) TTLResult(ctx context.Context, key string) (time.Duration, err
return c.client.TTL(writeCtx, key).Result()
}
func (c v8Client) AuthACL(ctx context.Context, username, password string) error {
pipeline := c.client.Pipeline()
statusCmd := pipeline.AuthACL(ctx, username, password)
return statusCmd.Err()
}
func newV8FailoverClient(s *Settings) (RedisClient, error) {
if s == nil {
return nil, nil
@ -340,10 +346,9 @@ func newV8FailoverClient(s *Settings) (RedisClient, error) {
IdleTimeout: time.Duration(s.IdleTimeout),
}
/* #nosec */
if s.EnableTLS {
opts.TLSConfig = &tls.Config{
InsecureSkipVerify: s.EnableTLS,
InsecureSkipVerify: s.EnableTLS, //nolint:gosec
}
err := s.SetCertificate(func(cert *tls.Certificate) {
opts.TLSConfig.Certificates = []tls.Certificate{*cert}
@ -397,7 +402,7 @@ func newV8Client(s *Settings) (RedisClient, error) {
/* #nosec */
if s.EnableTLS {
options.TLSConfig = &tls.Config{
InsecureSkipVerify: s.EnableTLS,
InsecureSkipVerify: s.EnableTLS, //nolint:gosec
}
err := s.SetCertificate(func(cert *tls.Certificate) {
options.TLSConfig.Certificates = []tls.Certificate{*cert}
@ -437,7 +442,7 @@ func newV8Client(s *Settings) (RedisClient, error) {
/* #nosec */
if s.EnableTLS {
options.TLSConfig = &tls.Config{
InsecureSkipVerify: s.EnableTLS,
InsecureSkipVerify: s.EnableTLS, //nolint:gosec
}
err := s.SetCertificate(func(cert *tls.Certificate) {
options.TLSConfig.Certificates = []tls.Certificate{*cert}

View File

@ -317,6 +317,12 @@ func (c v9Client) TTLResult(ctx context.Context, key string) (time.Duration, err
return c.client.TTL(writeCtx, key).Result()
}
func (c v9Client) AuthACL(ctx context.Context, username, password string) error {
pipeline := c.client.Pipeline()
statusCmd := pipeline.AuthACL(ctx, username, password)
return statusCmd.Err()
}
func newV9FailoverClient(s *Settings) (RedisClient, error) {
if s == nil {
return nil, nil
@ -344,7 +350,7 @@ func newV9FailoverClient(s *Settings) (RedisClient, error) {
/* #nosec */
if s.EnableTLS {
opts.TLSConfig = &tls.Config{
InsecureSkipVerify: s.EnableTLS,
InsecureSkipVerify: s.EnableTLS, //nolint:gosec
}
err := s.SetCertificate(func(cert *tls.Certificate) {
opts.TLSConfig.Certificates = []tls.Certificate{*cert}
@ -400,7 +406,7 @@ func newV9Client(s *Settings) (RedisClient, error) {
if s.EnableTLS {
/* #nosec */
options.TLSConfig = &tls.Config{
InsecureSkipVerify: s.EnableTLS,
InsecureSkipVerify: s.EnableTLS, //nolint:gosec
}
err := s.SetCertificate(func(cert *tls.Certificate) {
options.TLSConfig.Certificates = []tls.Certificate{*cert}
@ -440,7 +446,7 @@ func newV9Client(s *Settings) (RedisClient, error) {
if s.EnableTLS {
/* #nosec */
options.TLSConfig = &tls.Config{
InsecureSkipVerify: s.EnableTLS,
InsecureSkipVerify: s.EnableTLS, //nolint:gosec
}
err := s.SetCertificate(func(cert *tls.Certificate) {
options.TLSConfig.Certificates = []tls.Certificate{*cert}

View File

@ -181,3 +181,18 @@ metadata:
"-1" disables idle timeout check.
default: "5m"
example: "10m"
builtinAuthenticationProfiles:
- name: "azuread"
metadata:
- name: useEntraID
required: false
default: "false"
example: "true"
type: bool
description: |
If set, enables authentication to Azure Cache for Redis using Microsoft EntraID. The Redis server must explicitly enable EntraID authentication. Note that
Azure Cache for Redis also requires the use of TLS, so `enableTLS` should be set. No username or password should be set.
- name: enableTLS
required: true
description: Must be set to true if using EntraID
example: "true"

View File

@ -64,7 +64,7 @@ func NewRedisConfigurationStore(logger logger.Logger) configuration.Store {
// Init does metadata and connection parsing.
func (r *ConfigurationStore) Init(ctx context.Context, metadata configuration.Metadata) error {
var err error
r.client, r.clientSettings, err = rediscomponent.ParseClientFromProperties(metadata.Properties, contribMetadata.ConfigurationStoreType)
r.client, r.clientSettings, err = rediscomponent.ParseClientFromProperties(metadata.Properties, contribMetadata.ConfigurationStoreType, ctx, &r.logger)
if err != nil {
return err
}

View File

@ -302,7 +302,9 @@ func Test_parseRedisMetadata(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, got, err := redisComponent.ParseClientFromProperties(tt.args.meta.Properties, contribMetadata.ConfigurationStoreType)
ctx := context.Background()
log := logger.NewLogger("dapr.components")
_, got, err := redisComponent.ParseClientFromProperties(tt.args.meta.Properties, contribMetadata.ConfigurationStoreType, ctx, &log)
if (err != nil) != tt.wantErr {
t.Errorf("edisComponent.ParseClientFromProperties error = %v, wantErr %v", err, tt.wantErr)
return
@ -321,6 +323,8 @@ func Test_parseRedisMetadata(t *testing.T) {
}
func setupMiniredis() (*miniredis.Miniredis, redisComponent.RedisClient) {
ctx := context.Background()
log := logger.NewLogger("dapr.components")
s, err := miniredis.Run()
if err != nil {
panic(err)
@ -329,7 +333,7 @@ func setupMiniredis() (*miniredis.Miniredis, redisComponent.RedisClient) {
"redisHost": s.Addr(),
"redisDB": "0",
}
redisClient, _, _ := redisComponent.ParseClientFromProperties(props, contribMetadata.ConfigurationStoreType)
redisClient, _, _ := redisComponent.ParseClientFromProperties(props, contribMetadata.ConfigurationStoreType, ctx, &log)
return s, redisClient
}

4
go.mod
View File

@ -1,8 +1,6 @@
module github.com/dapr/components-contrib
go 1.22.0
toolchain go1.22.2
go 1.22.4
require (
cloud.google.com/go/datastore v1.15.0

View File

@ -50,7 +50,7 @@ func NewStandaloneRedisLock(logger logger.Logger) lock.Store {
// Init StandaloneRedisLock.
func (r *StandaloneRedisLock) InitLockStore(ctx context.Context, metadata lock.Metadata) (err error) {
// Create the client
r.client, r.clientSettings, err = rediscomponent.ParseClientFromProperties(metadata.Properties, contribMetadata.LockStoreType)
r.client, r.clientSettings, err = rediscomponent.ParseClientFromProperties(metadata.Properties, contribMetadata.LockStoreType, ctx, &r.logger)
if err != nil {
return err
}

View File

@ -168,4 +168,19 @@ metadata:
required: false
description: Maximum number of items inside a stream.The old entries are automatically evicted when the specified length is reached, so that the stream is left at a constant size. Defaults to unlimited.
example: "10000"
type: number
type: number
builtinAuthenticationProfiles:
- name: "azuread"
metadata:
- name: useEntraID
required: false
default: "false"
example: "true"
type: bool
description: |
If set, enables authentication to Azure Cache for Redis using Microsoft EntraID. The Redis server must explicitly enable EntraID authentication. Note that
Azure Cache for Redis also requires the use of TLS, so `enableTLS` should be set. No username or password should be set.
- name: enableTLS
required: true
description: Must be set to true if using EntraID
example: "true"

View File

@ -76,7 +76,7 @@ func NewRedisStreams(logger logger.Logger) pubsub.PubSub {
func (r *redisStreams) Init(ctx context.Context, metadata pubsub.Metadata) error {
var err error
r.client, r.clientSettings, err = rediscomponent.ParseClientFromProperties(metadata.Properties, contribMetadata.PubSubType)
r.client, r.clientSettings, err = rediscomponent.ParseClientFromProperties(metadata.Properties, contribMetadata.PubSubType, ctx, &r.logger)
if err != nil {
return err
}

View File

@ -164,3 +164,18 @@ metadata:
description: Indexing schemas for querying JSON objects
example: "see Querying JSON objects"
type: string
builtinAuthenticationProfiles:
- name: "azuread"
metadata:
- name: useEntraID
required: false
default: "false"
example: "true"
type: bool
description: |
If set, enables authentication to Azure Cache for Redis using Microsoft EntraID. The Redis server must explicitly enable EntraID authentication. Note that
Azure Cache for Redis also requires the use of TLS, so `enableTLS` should be set. No username or password should be set.
- name: enableTLS
required: true
description: Must be set to true if using EntraID
example: "true"

View File

@ -131,7 +131,7 @@ func (r *StateStore) Ping(ctx context.Context) error {
// Init does metadata and connection parsing.
func (r *StateStore) Init(ctx context.Context, metadata state.Metadata) error {
var err error
r.client, r.clientSettings, err = rediscomponent.ParseClientFromProperties(metadata.Properties, daprmetadata.StateStoreType)
r.client, r.clientSettings, err = rediscomponent.ParseClientFromProperties(metadata.Properties, daprmetadata.StateStoreType, ctx, &r.logger)
if err != nil {
return err
}

View File

@ -1,8 +1,6 @@
module github.com/dapr/components-contrib/tests/certification
go 1.22.3
toolchain go1.22.4
go 1.22.4
require (
cloud.google.com/go/pubsub v1.36.1

View File

@ -1,8 +1,6 @@
module github.com/dapr/components-contrib/tests/e2e/pubsub/jetstream
go 1.22.0
toolchain go1.22.2
go 1.22.4
require (
github.com/dapr/components-contrib v1.10.6-0.20230403162214-9ee9d56cb7ea

View File

@ -48,7 +48,8 @@ func getRedisValuesFromItems(items map[string]*configuration.Item) []interface{}
func (r *ConfigUpdater) Init(props map[string]string) error {
var err error
r.Client, r.clientSettings, err = rediscomponent.ParseClientFromProperties(props, metadata.ConfigurationStoreType)
ctx := context.Background()
r.Client, r.clientSettings, err = rediscomponent.ParseClientFromProperties(props, metadata.ConfigurationStoreType, ctx, &r.logger)
if err != nil {
return err
}