Adds EntraID auth support to all Redis Components (#3470)
Signed-off-by: Bernd Verst <github@bernd.dev>
This commit is contained in:
parent
f09c2c2941
commit
b656b0d5d5
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
4
Makefile
4
Makefile
|
@ -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
|
||||
|
|
|
@ -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"
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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"
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
4
go.mod
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue