use slog for structured logging (#3502)

Signed-off-by: Sean Liao <sean+git@liao.dev>
This commit is contained in:
Sean Liao
2024-06-01 20:15:58 +04:00
committed by GitHub
parent 8e59b6d3b3
commit 0b6a78397e
66 changed files with 471 additions and 604 deletions
+3 -3
View File
@@ -4,6 +4,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"log/slog"
"net/http"
"os"
"strings"
@@ -11,7 +12,6 @@ import (
"golang.org/x/crypto/bcrypt"
"github.com/dexidp/dex/pkg/featureflags"
"github.com/dexidp/dex/pkg/log"
"github.com/dexidp/dex/server"
"github.com/dexidp/dex/storage"
"github.com/dexidp/dex/storage/ent"
@@ -236,7 +236,7 @@ type Storage struct {
// StorageConfig is a configuration that can create a storage.
type StorageConfig interface {
Open(logger log.Logger) (storage.Storage, error)
Open(logger *slog.Logger) (storage.Storage, error)
}
var (
@@ -386,7 +386,7 @@ type Expiry struct {
// Logger holds configuration required to customize logging for dex.
type Logger struct {
// Level sets logging level severity.
Level string `json:"level"`
Level slog.Level `json:"level"`
// Format specifies the format to be used for logging.
Format string `json:"format"`
+3 -2
View File
@@ -1,6 +1,7 @@
package main
import (
"log/slog"
"os"
"testing"
@@ -219,7 +220,7 @@ logger:
DeviceRequests: "10m",
},
Logger: Logger{
Level: "debug",
Level: slog.LevelDebug,
Format: "json",
},
}
@@ -426,7 +427,7 @@ logger:
AuthRequests: "25h",
},
Logger: Logger{
Level: "debug",
Level: slog.LevelDebug,
Format: "json",
},
}
+52 -75
View File
@@ -6,6 +6,7 @@ import (
"crypto/x509"
"errors"
"fmt"
"log/slog"
"net"
"net/http"
"net/http/pprof"
@@ -28,14 +29,12 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/reflection"
"github.com/dexidp/dex/api/v2"
"github.com/dexidp/dex/pkg/log"
"github.com/dexidp/dex/server"
"github.com/dexidp/dex/storage"
)
@@ -98,22 +97,24 @@ func runServe(options serveOptions) error {
return fmt.Errorf("invalid config: %v", err)
}
logger.Infof(
"Dex Version: %s, Go Version: %s, Go OS/ARCH: %s %s",
version,
runtime.Version(),
runtime.GOOS,
runtime.GOARCH,
logger.Info(
"Version info",
"dex_version", version,
slog.Group("go",
"version", runtime.Version(),
"os", runtime.GOOS,
"arch", runtime.GOARCH,
),
)
if c.Logger.Level != "" {
logger.Infof("config using log level: %s", c.Logger.Level)
if c.Logger.Level != slog.LevelInfo {
logger.Info("config using log level", "level", c.Logger.Level)
}
if err := c.Validate(); err != nil {
return err
}
logger.Infof("config issuer: %s", c.Issuer)
logger.Info("config issuer", "issuer", c.Issuer)
prometheusRegistry := prometheus.NewRegistry()
err = prometheusRegistry.Register(collectors.NewGoCollector())
@@ -188,7 +189,7 @@ func runServe(options serveOptions) error {
}
defer s.Close()
logger.Infof("config storage: %s", c.Storage.Type)
logger.Info("config storage", "storage_type", c.Storage.Type)
if len(c.StaticClients) > 0 {
for i, client := range c.StaticClients {
@@ -213,7 +214,7 @@ func runServe(options serveOptions) error {
}
c.StaticClients[i].Secret = os.Getenv(client.SecretEnv)
}
logger.Infof("config static client: %s", client.Name)
logger.Info("config static client", "client_name", client.Name)
}
s = storage.WithStaticClients(s, c.StaticClients)
}
@@ -233,7 +234,7 @@ func runServe(options serveOptions) error {
if c.Config == nil {
return fmt.Errorf("invalid config: no config field for connector %q", c.ID)
}
logger.Infof("config connector: %s", c.ID)
logger.Info("config connector", "connector_id", c.ID)
// convert to a storage connector object
conn, err := ToStorageConnector(c)
@@ -249,22 +250,22 @@ func runServe(options serveOptions) error {
Name: "Email",
Type: server.LocalConnector,
})
logger.Infof("config connector: local passwords enabled")
logger.Info("config connector: local passwords enabled")
}
s = storage.WithStaticConnectors(s, storageConnectors)
if len(c.OAuth2.ResponseTypes) > 0 {
logger.Infof("config response types accepted: %s", c.OAuth2.ResponseTypes)
logger.Info("config response types accepted", "response_types", c.OAuth2.ResponseTypes)
}
if c.OAuth2.SkipApprovalScreen {
logger.Infof("config skipping approval screen")
logger.Info("config skipping approval screen")
}
if c.OAuth2.PasswordConnector != "" {
logger.Infof("config using password grant connector: %s", c.OAuth2.PasswordConnector)
logger.Info("config using password grant connector", "password_connector", c.OAuth2.PasswordConnector)
}
if len(c.Web.AllowedOrigins) > 0 {
logger.Infof("config allowed origins: %s", c.Web.AllowedOrigins)
logger.Info("config allowed origins", "origins", c.Web.AllowedOrigins)
}
// explicitly convert to UTC.
@@ -294,7 +295,7 @@ func runServe(options serveOptions) error {
if err != nil {
return fmt.Errorf("invalid config value %q for signing keys expiry: %v", c.Expiry.SigningKeys, err)
}
logger.Infof("config signing keys expire after: %v", signingKeys)
logger.Info("config signing keys", "expire_after", signingKeys)
serverConfig.RotateKeysAfter = signingKeys
}
if c.Expiry.IDTokens != "" {
@@ -302,7 +303,7 @@ func runServe(options serveOptions) error {
if err != nil {
return fmt.Errorf("invalid config value %q for id token expiry: %v", c.Expiry.IDTokens, err)
}
logger.Infof("config id tokens valid for: %v", idTokens)
logger.Info("config id tokens", "valid_for", idTokens)
serverConfig.IDTokensValidFor = idTokens
}
if c.Expiry.AuthRequests != "" {
@@ -310,7 +311,7 @@ func runServe(options serveOptions) error {
if err != nil {
return fmt.Errorf("invalid config value %q for auth request expiry: %v", c.Expiry.AuthRequests, err)
}
logger.Infof("config auth requests valid for: %v", authRequests)
logger.Info("config auth requests", "valid_for", authRequests)
serverConfig.AuthRequestsValidFor = authRequests
}
if c.Expiry.DeviceRequests != "" {
@@ -318,7 +319,7 @@ func runServe(options serveOptions) error {
if err != nil {
return fmt.Errorf("invalid config value %q for device request expiry: %v", c.Expiry.AuthRequests, err)
}
logger.Infof("config device requests valid for: %v", deviceRequests)
logger.Info("config device requests", "valid_for", deviceRequests)
serverConfig.DeviceRequestsValidFor = deviceRequests
}
refreshTokenPolicy, err := server.NewRefreshTokenPolicy(
@@ -368,7 +369,7 @@ func runServe(options serveOptions) error {
if c.Telemetry.HTTP != "" {
const name = "telemetry"
logger.Infof("listening (%s) on %s", name, c.Telemetry.HTTP)
logger.Info("listening on", "server", name, "address", c.Telemetry.HTTP)
l, err := net.Listen("tcp", c.Telemetry.HTTP)
if err != nil {
@@ -390,9 +391,9 @@ func runServe(options serveOptions) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
logger.Debugf("starting graceful shutdown (%s)", name)
logger.Debug("starting graceful shutdown", "server", name)
if err := server.Shutdown(ctx); err != nil {
logger.Errorf("graceful shutdown (%s): %v", name, err)
logger.Error("graceful shutdown", "server", name, "err", err)
}
})
}
@@ -401,7 +402,7 @@ func runServe(options serveOptions) error {
if c.Web.HTTP != "" {
const name = "http"
logger.Infof("listening (%s) on %s", name, c.Web.HTTP)
logger.Info("listening on", "server", name, "address", c.Web.HTTP)
l, err := net.Listen("tcp", c.Web.HTTP)
if err != nil {
@@ -419,9 +420,9 @@ func runServe(options serveOptions) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
logger.Debugf("starting graceful shutdown (%s)", name)
logger.Debug("starting graceful shutdown", "server", name)
if err := server.Shutdown(ctx); err != nil {
logger.Errorf("graceful shutdown (%s): %v", name, err)
logger.Error("graceful shutdown", "server", name, "err", err)
}
})
}
@@ -430,7 +431,7 @@ func runServe(options serveOptions) error {
if c.Web.HTTPS != "" {
const name = "https"
logger.Infof("listening (%s) on %s", name, c.Web.HTTPS)
logger.Info("listening on", "server", name, "address", c.Web.HTTPS)
l, err := net.Listen("tcp", c.Web.HTTPS)
if err != nil {
@@ -470,16 +471,16 @@ func runServe(options serveOptions) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
logger.Debugf("starting graceful shutdown (%s)", name)
logger.Debug("starting graceful shutdown", "server", name)
if err := server.Shutdown(ctx); err != nil {
logger.Errorf("graceful shutdown (%s): %v", name, err)
logger.Error("graceful shutdown", "server", name, "err", err)
}
})
}
// Set up grpc server
if c.GRPC.Addr != "" {
logger.Infof("listening (grpc) on %s", c.GRPC.Addr)
logger.Info("listening on", "server", "grpc", "address", c.GRPC.Addr)
grpcListener, err := net.Listen("tcp", c.GRPC.Addr)
if err != nil {
@@ -498,7 +499,7 @@ func runServe(options serveOptions) error {
group.Add(func() error {
return grpcSrv.Serve(grpcListener)
}, func(err error) {
logger.Debugf("starting graceful shutdown (grpc)")
logger.Debug("starting graceful shutdown", "server", "grpc")
grpcSrv.GracefulStop()
})
}
@@ -508,53 +509,29 @@ func runServe(options serveOptions) error {
if _, ok := err.(run.SignalError); !ok {
return fmt.Errorf("run groups: %w", err)
}
logger.Infof("%v, shutdown now", err)
logger.Info("shutdown now", "err", err)
}
return nil
}
var (
logLevels = []string{"debug", "info", "error"}
logFormats = []string{"json", "text"}
)
var logFormats = []string{"json", "text"}
type utcFormatter struct {
f logrus.Formatter
}
func (f *utcFormatter) Format(e *logrus.Entry) ([]byte, error) {
e.Time = e.Time.UTC()
return f.f.Format(e)
}
func newLogger(level string, format string) (log.Logger, error) {
var logLevel logrus.Level
switch strings.ToLower(level) {
case "debug":
logLevel = logrus.DebugLevel
case "", "info":
logLevel = logrus.InfoLevel
case "error":
logLevel = logrus.ErrorLevel
default:
return nil, fmt.Errorf("log level is not one of the supported values (%s): %s", strings.Join(logLevels, ", "), level)
}
var formatter utcFormatter
func newLogger(level slog.Level, format string) (*slog.Logger, error) {
var handler slog.Handler
switch strings.ToLower(format) {
case "", "text":
formatter.f = &logrus.TextFormatter{DisableColors: true}
slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
Level: level,
})
case "json":
formatter.f = &logrus.JSONFormatter{}
slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
Level: level,
})
default:
return nil, fmt.Errorf("log format is not one of the supported values (%s): %s", strings.Join(logFormats, ", "), format)
}
return &logrus.Logger{
Out: os.Stderr,
Formatter: &formatter,
Level: logLevel,
}, nil
return slog.New(handler), nil
}
func applyConfigOverrides(options serveOptions, config *Config) {
@@ -600,7 +577,7 @@ func pprofHandler(router *http.ServeMux) {
// newTLSReloader returns a [tls.Config] with GetCertificate or GetConfigForClient set
// to reload certificates from the given paths on SIGHUP or on file creates (atomic update via rename).
func newTLSReloader(logger log.Logger, certFile, keyFile, caFile string, baseConfig *tls.Config) (*tls.Config, error) {
func newTLSReloader(logger *slog.Logger, certFile, keyFile, caFile string, baseConfig *tls.Config) (*tls.Config, error) {
// trigger reload on channel
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGHUP)
@@ -631,7 +608,7 @@ func newTLSReloader(logger log.Logger, certFile, keyFile, caFile string, baseCon
// recommended by fsnotify: watch the dir to handle renames
// https://pkg.go.dev/github.com/fsnotify/fsnotify#hdr-Watching_files
for dir := range watchDirs {
logger.Debugf("watching dir: %v", dir)
logger.Debug("watching dir", "dir", dir)
err := watcher.Add(dir)
if err != nil {
return nil, fmt.Errorf("watch dir for TLS reloader: %v", err)
@@ -654,19 +631,19 @@ func newTLSReloader(logger log.Logger, certFile, keyFile, caFile string, baseCon
for {
select {
case sig := <-sigc:
logger.Debug("reloading cert from signal: %v", sig)
logger.Debug("reloading cert from signal", "signal", sig)
case evt := <-watcher.Events:
if _, ok := watchFiles[evt.Name]; !ok || !evt.Has(fsnotify.Create) {
continue loop
}
logger.Debug("reloading cert from fsnotify: %v %v", evt.Name, evt.Op.String())
logger.Debug("reloading cert from fsnotify", "event", evt.Name, "operation", evt.Op.String())
case err := <-watcher.Errors:
logger.Errorf("TLS reloader watch: %v", err)
logger.Error("TLS reloader watch", "err", err)
}
loaded, err := loadTLSConfig(certFile, keyFile, caFile, baseConfig)
if err != nil {
logger.Errorf("reload TLS config: %v", err)
logger.Error("reload TLS config", "err", err)
}
ptr.Store(loaded)
}
+7 -7
View File
@@ -7,6 +7,7 @@ import (
"encoding/json"
"fmt"
"io"
"log/slog"
"net"
"net/http"
"strings"
@@ -14,7 +15,6 @@ import (
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/groups"
"github.com/dexidp/dex/pkg/log"
)
// Config holds configuration options for Atlassian Crowd connector.
@@ -80,16 +80,16 @@ type crowdAuthenticationError struct {
}
// Open returns a strategy for logging in through Atlassian Crowd
func (c *Config) Open(_ string, logger log.Logger) (connector.Connector, error) {
func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
if c.BaseURL == "" {
return nil, fmt.Errorf("crowd: no baseURL provided for crowd connector")
}
return &crowdConnector{Config: *c, logger: logger}, nil
return &crowdConnector{Config: *c, logger: logger.With(slog.Group("connector", "type", "atlassiancrowd", "id", id))}, nil
}
type crowdConnector struct {
Config
logger log.Logger
logger *slog.Logger
}
var (
@@ -375,7 +375,7 @@ func (c *crowdConnector) identityFromCrowdUser(user crowdUser) connector.Identit
identity.PreferredUsername = user.Email
default:
if c.PreferredUsernameField != "" {
c.logger.Warnf("preferred_username left empty. Invalid crowd field mapped to preferred_username: %s", c.PreferredUsernameField)
c.logger.Warn("preferred_username left empty. Invalid crowd field mapped to preferred_username", "field", c.PreferredUsernameField)
}
}
@@ -436,12 +436,12 @@ func (c *crowdConnector) validateCrowdResponse(resp *http.Response) ([]byte, err
}
if resp.StatusCode == http.StatusForbidden && strings.Contains(string(body), "The server understood the request but refuses to authorize it.") {
c.logger.Debugf("crowd response validation failed: %s", string(body))
c.logger.Debug("crowd response validation failed", "response", string(body))
return nil, fmt.Errorf("dex is forbidden from making requests to the Atlassian Crowd application by URL %q", c.BaseURL)
}
if resp.StatusCode == http.StatusUnauthorized && string(body) == "Application failed to authenticate" {
c.logger.Debugf("crowd response validation failed: %s", string(body))
c.logger.Debug("crowd response validation failed", "response", string(body))
return nil, fmt.Errorf("dex failed to authenticate Crowd Application with ID %q", c.ClientID)
}
return body, nil
@@ -7,12 +7,11 @@ import (
"encoding/json"
"fmt"
"io"
"log/slog"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"github.com/sirupsen/logrus"
)
func TestUserGroups(t *testing.T) {
@@ -151,11 +150,7 @@ type TestServerResponse struct {
func newTestCrowdConnector(baseURL string) crowdConnector {
connector := crowdConnector{}
connector.BaseURL = baseURL
connector.logger = &logrus.Logger{
Out: io.Discard,
Level: logrus.DebugLevel,
Formatter: &logrus.TextFormatter{DisableColors: true},
}
connector.logger = slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{}))
return connector
}
+4 -4
View File
@@ -5,12 +5,12 @@ package authproxy
import (
"fmt"
"log/slog"
"net/http"
"net/url"
"strings"
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/log"
)
// Config holds the configuration parameters for a connector which returns an
@@ -27,7 +27,7 @@ type Config struct {
}
// Open returns an authentication strategy which requires no user interaction.
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
userIDHeader := c.UserIDHeader
if userIDHeader == "" {
userIDHeader = "X-Remote-User-Id"
@@ -51,7 +51,7 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
emailHeader: emailHeader,
groupHeader: groupHeader,
groups: c.Groups,
logger: logger,
logger: logger.With(slog.Group("connector", "type", "authproxy", "id", id)),
pathSuffix: "/" + id,
}, nil
}
@@ -64,7 +64,7 @@ type callback struct {
emailHeader string
groupHeader string
groups []string
logger log.Logger
logger *slog.Logger
pathSuffix string
}
+2 -3
View File
@@ -2,12 +2,11 @@ package authproxy
import (
"io"
"log/slog"
"net/http"
"reflect"
"testing"
"github.com/sirupsen/logrus"
"github.com/dexidp/dex/connector"
)
@@ -23,7 +22,7 @@ const (
testUserID = "1234567890"
)
var logger = &logrus.Logger{Out: io.Discard, Formatter: &logrus.TextFormatter{}}
var logger = slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{}))
func TestUser(t *testing.T) {
config := Config{
+4 -4
View File
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"net/http"
"sync"
"time"
@@ -16,7 +17,6 @@ import (
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/groups"
"github.com/dexidp/dex/pkg/log"
)
const (
@@ -42,7 +42,7 @@ type Config struct {
}
// Open returns a strategy for logging in through Bitbucket.
func (c *Config) Open(_ string, logger log.Logger) (connector.Connector, error) {
func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
b := bitbucketConnector{
redirectURI: c.RedirectURI,
teams: c.Teams,
@@ -51,7 +51,7 @@ func (c *Config) Open(_ string, logger log.Logger) (connector.Connector, error)
includeTeamGroups: c.IncludeTeamGroups,
apiURL: apiURL,
legacyAPIURL: legacyAPIURL,
logger: logger,
logger: logger.With(slog.Group("connector", "type", "bitbucketcloud", "id", id)),
}
return &b, nil
@@ -73,7 +73,7 @@ type bitbucketConnector struct {
teams []string
clientID string
clientSecret string
logger log.Logger
logger *slog.Logger
apiURL string
legacyAPIURL string
+4 -4
View File
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"net/http"
"strconv"
"sync"
@@ -15,7 +16,6 @@ import (
"golang.org/x/oauth2"
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/log"
)
// Config holds configuration options for gitea logins.
@@ -51,7 +51,7 @@ type giteaUser struct {
}
// Open returns a strategy for logging in through Gitea
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
if c.BaseURL == "" {
c.BaseURL = "https://gitea.com"
}
@@ -61,7 +61,7 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
orgs: c.Orgs,
clientID: c.ClientID,
clientSecret: c.ClientSecret,
logger: logger,
logger: logger.With(slog.Group("connector", "type", "gitea", "id", id)),
loadAllGroups: c.LoadAllGroups,
useLoginAsID: c.UseLoginAsID,
}, nil
@@ -84,7 +84,7 @@ type giteaConnector struct {
orgs []Org
clientID string
clientSecret string
logger log.Logger
logger *slog.Logger
httpClient *http.Client
// if set to true and no orgs are configured then connector loads all user claims (all orgs and team)
loadAllGroups bool
+6 -6
View File
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"net/http"
"regexp"
"strconv"
@@ -18,7 +19,6 @@ import (
"github.com/dexidp/dex/connector"
groups_pkg "github.com/dexidp/dex/pkg/groups"
"github.com/dexidp/dex/pkg/httpclient"
"github.com/dexidp/dex/pkg/log"
)
const (
@@ -66,7 +66,7 @@ type Org struct {
}
// Open returns a strategy for logging in through GitHub.
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
if c.Org != "" {
// Return error if both 'org' and 'orgs' fields are used.
if len(c.Orgs) > 0 {
@@ -82,7 +82,7 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
clientID: c.ClientID,
clientSecret: c.ClientSecret,
apiURL: apiURL,
logger: logger,
logger: logger.With(slog.Group("connector", "type", "github", "id", id)),
useLoginAsID: c.UseLoginAsID,
preferredEmailDomain: c.PreferredEmailDomain,
}
@@ -142,7 +142,7 @@ type githubConnector struct {
orgs []Org
clientID string
clientSecret string
logger log.Logger
logger *slog.Logger
// apiURL defaults to "https://api.github.com"
apiURL string
// hostName of the GitHub enterprise account.
@@ -362,7 +362,7 @@ func (c *githubConnector) groupsForOrgs(ctx context.Context, client *http.Client
if len(org.Teams) == 0 {
inOrgNoTeams = true
} else if teams = groups_pkg.Filter(teams, org.Teams); len(teams) == 0 {
c.logger.Infof("github: user %q in org %q but no teams", userName, org.Name)
c.logger.Info("user in org but no teams", "user", userName, "org", org.Name)
}
for _, teamName := range teams {
@@ -667,7 +667,7 @@ func (c *githubConnector) userInOrg(ctx context.Context, client *http.Client, us
switch resp.StatusCode {
case http.StatusNoContent:
case http.StatusFound, http.StatusNotFound:
c.logger.Infof("github: user %q not in org %q or application not authorized to read org data", userName, orgName)
c.logger.Info("user not in org or application not authorized to read org data", "user", userName, "org", orgName)
default:
err = fmt.Errorf("github: unexpected return status: %q", resp.Status)
}
+4 -1
View File
@@ -6,6 +6,8 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"net/http"
"net/http/httptest"
"net/url"
@@ -449,6 +451,7 @@ func Test_isPreferredEmailDomain(t *testing.T) {
}
func Test_Open_PreferredDomainConfig(t *testing.T) {
log := slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{}))
tests := []struct {
preferredEmailDomain string
email string
@@ -476,7 +479,7 @@ func Test_Open_PreferredDomainConfig(t *testing.T) {
c := Config{
PreferredEmailDomain: test.preferredEmailDomain,
}
_, err := c.Open("id", nil)
_, err := c.Open("id", log)
expectEquals(t, err, test.expected)
})
+4 -4
View File
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"net/http"
"strconv"
"time"
@@ -15,7 +16,6 @@ import (
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/groups"
"github.com/dexidp/dex/pkg/log"
)
const (
@@ -46,7 +46,7 @@ type gitlabUser struct {
}
// Open returns a strategy for logging in through GitLab.
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
if c.BaseURL == "" {
c.BaseURL = "https://gitlab.com"
}
@@ -55,7 +55,7 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
redirectURI: c.RedirectURI,
clientID: c.ClientID,
clientSecret: c.ClientSecret,
logger: logger,
logger: logger.With(slog.Group("connector", "type", "gitlab", "id", id)),
groups: c.Groups,
useLoginAsID: c.UseLoginAsID,
}, nil
@@ -78,7 +78,7 @@ type gitlabConnector struct {
groups []string
clientID string
clientSecret string
logger log.Logger
logger *slog.Logger
httpClient *http.Client
// if set to true will use the user's handle rather than their numeric id as the ID
useLoginAsID bool
+10 -9
View File
@@ -5,6 +5,7 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"net/http"
"os"
"strings"
@@ -21,7 +22,6 @@ import (
"github.com/dexidp/dex/connector"
pkg_groups "github.com/dexidp/dex/pkg/groups"
"github.com/dexidp/dex/pkg/log"
)
const (
@@ -67,9 +67,10 @@ type Config struct {
}
// Open returns a connector which can be used to login users through Google.
func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, err error) {
func (c *Config) Open(id string, logger *slog.Logger) (conn connector.Connector, err error) {
logger = logger.With(slog.Group("connector", "type", "google", "id", id))
if c.AdminEmail != "" {
log.Deprecated(logger, `google: use "domainToAdminEmail.*: %s" option instead of "adminEmail: %s".`, c.AdminEmail, c.AdminEmail)
logger.Warn(`use "domainToAdminEmail.*" option instead of "adminEmail"`, "deprecated", true)
if c.DomainToAdminEmail == nil {
c.DomainToAdminEmail = make(map[string]string)
}
@@ -152,7 +153,7 @@ type googleConnector struct {
oauth2Config *oauth2.Config
verifier *oidc.IDTokenVerifier
cancel context.CancelFunc
logger log.Logger
logger *slog.Logger
hostedDomains []string
groups []string
serviceAccountFilePath string
@@ -340,7 +341,7 @@ func (c *googleConnector) findAdminService(domain string) (*admin.Service, error
adminSrv, ok := c.adminSrv[domain]
if !ok {
adminSrv, ok = c.adminSrv[wildcardDomainToAdminEmail]
c.logger.Debugf("using wildcard (%s) admin email to fetch groups", c.domainToAdminEmail[wildcardDomainToAdminEmail])
c.logger.Debug("using wildcard admin email to fetch groups", "admin_email", c.domainToAdminEmail[wildcardDomainToAdminEmail])
}
if !ok {
@@ -377,7 +378,7 @@ func getCredentialsFromFilePath(serviceAccountFilePath string) ([]byte, error) {
// If the default credential is empty, it attempts to create a new service with metadata credentials.
// If successful, it returns the service and nil error.
// If unsuccessful, it returns the error and a nil service.
func getCredentialsFromDefault(ctx context.Context, email string, logger log.Logger) ([]byte, *admin.Service, error) {
func getCredentialsFromDefault(ctx context.Context, email string, logger *slog.Logger) ([]byte, *admin.Service, error) {
credential, err := google.FindDefaultCredentials(ctx)
if err != nil {
return nil, nil, fmt.Errorf("failed to fetch application default credentials: %w", err)
@@ -397,9 +398,9 @@ func getCredentialsFromDefault(ctx context.Context, email string, logger log.Log
// createServiceWithMetadataServer creates a new service using metadata server.
// If an error occurs during the process, it is returned along with a nil service.
func createServiceWithMetadataServer(ctx context.Context, adminEmail string, logger log.Logger) (*admin.Service, error) {
func createServiceWithMetadataServer(ctx context.Context, adminEmail string, logger *slog.Logger) (*admin.Service, error) {
serviceAccountEmail, err := metadata.Email("default")
logger.Infof("discovered serviceAccountEmail: %s", serviceAccountEmail)
logger.Info("discovered serviceAccountEmail", "email", serviceAccountEmail)
if err != nil {
return nil, fmt.Errorf("unable to get service account email from metadata server: %v", err)
@@ -423,7 +424,7 @@ func createServiceWithMetadataServer(ctx context.Context, adminEmail string, log
// createDirectoryService sets up super user impersonation and creates an admin client for calling
// the google admin api. If no serviceAccountFilePath is defined, the application default credential
// is used.
func createDirectoryService(serviceAccountFilePath, email string, logger log.Logger) (service *admin.Service, err error) {
func createDirectoryService(serviceAccountFilePath, email string, logger *slog.Logger) (service *admin.Service, err error) {
var jsonCredentials []byte
ctx := context.Background()
+3 -2
View File
@@ -4,6 +4,8 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"log/slog"
"net/http"
"net/http/httptest"
"net/url"
@@ -11,7 +13,6 @@ import (
"strings"
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
admin "google.golang.org/api/admin/directory/v1"
"google.golang.org/api/option"
@@ -51,7 +52,7 @@ func testSetup() *httptest.Server {
}
func newConnector(config *Config) (*googleConnector, error) {
log := logrus.New()
log := slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{}))
conn, err := config.Open("id", log)
if err != nil {
return nil, err
+5 -5
View File
@@ -7,10 +7,10 @@ import (
"encoding/json"
"fmt"
"io"
"log/slog"
"net/http"
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/log"
)
type conn struct {
@@ -19,7 +19,7 @@ type conn struct {
AdminUsername string
AdminPassword string
client *http.Client
Logger log.Logger
Logger *slog.Logger
}
type userKeystone struct {
@@ -111,13 +111,13 @@ var (
)
// Open returns an authentication strategy using Keystone.
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
return &conn{
Domain: c.Domain,
Host: c.Host,
AdminUsername: c.AdminUsername,
AdminPassword: c.AdminPassword,
Logger: logger,
Logger: logger.With(slog.Group("connector", "type", "keystone", "id", id)),
client: http.DefaultClient,
}, nil
}
@@ -287,7 +287,7 @@ func (p *conn) getUserGroups(ctx context.Context, userID string, token string) (
req = req.WithContext(ctx)
resp, err := p.client.Do(req)
if err != nil {
p.Logger.Errorf("keystone: error while fetching user %q groups\n", userID)
p.Logger.Error("error while fetching user groups", "user_id", userID, "err", err)
return nil, err
}
+19 -18
View File
@@ -7,6 +7,7 @@ import (
"crypto/x509"
"encoding/json"
"fmt"
"log/slog"
"net"
"os"
"strings"
@@ -14,7 +15,6 @@ import (
"github.com/go-ldap/ldap/v3"
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/log"
)
// Config holds the configuration parameters for the LDAP connector. The LDAP
@@ -188,12 +188,12 @@ func parseScope(s string) (int, bool) {
// Function exists here to allow backward compatibility between old and new
// group to user matching implementations.
// See "Config.GroupSearch.UserMatchers" comments for the details
func userMatchers(c *Config, logger log.Logger) []UserMatcher {
func userMatchers(c *Config, logger *slog.Logger) []UserMatcher {
if len(c.GroupSearch.UserMatchers) > 0 && c.GroupSearch.UserMatchers[0].UserAttr != "" {
return c.GroupSearch.UserMatchers
}
log.Deprecated(logger, `LDAP: use groupSearch.userMatchers option instead of "userAttr/groupAttr" fields.`)
logger.Warn(`use "groupSearch.userMatchers" option instead of "userAttr/groupAttr" fields`, "deprecated", true)
return []UserMatcher{
{
UserAttr: c.GroupSearch.UserAttr,
@@ -203,7 +203,8 @@ func userMatchers(c *Config, logger log.Logger) []UserMatcher {
}
// Open returns an authentication strategy using LDAP.
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
logger = logger.With(slog.Group("connector", "type", "ldap", "id", id))
conn, err := c.OpenConnector(logger)
if err != nil {
return nil, err
@@ -217,7 +218,7 @@ type refreshData struct {
}
// OpenConnector is the same as Open but returns a type with all implemented connector interfaces.
func (c *Config) OpenConnector(logger log.Logger) (interface {
func (c *Config) OpenConnector(logger *slog.Logger) (interface {
connector.Connector
connector.PasswordConnector
connector.RefreshConnector
@@ -226,7 +227,7 @@ func (c *Config) OpenConnector(logger log.Logger) (interface {
return c.openConnector(logger)
}
func (c *Config) openConnector(logger log.Logger) (*ldapConnector, error) {
func (c *Config) openConnector(logger *slog.Logger) (*ldapConnector, error) {
requiredFields := []struct {
name string
val string
@@ -300,7 +301,7 @@ type ldapConnector struct {
tlsConfig *tls.Config
logger log.Logger
logger *slog.Logger
}
var (
@@ -359,7 +360,7 @@ func (c *ldapConnector) getAttrs(e ldap.Entry, name string) []string {
return []string{e.DN}
}
c.logger.Debugf("%q attribute is not fround in entry", name)
c.logger.Debug("attribute is not fround in entry", "attribute", name)
return nil
}
@@ -438,8 +439,8 @@ func (c *ldapConnector) userEntry(conn *ldap.Conn, username string) (user ldap.E
req.Attributes = append(req.Attributes, c.UserSearch.PreferredUsernameAttrAttr)
}
c.logger.Infof("performing ldap search %s %s %s",
req.BaseDN, scopeString(req.Scope), req.Filter)
c.logger.Info("performing ldap search",
"base_dn", req.BaseDN, "scope", scopeString(req.Scope), "filter", req.Filter)
resp, err := conn.Search(req)
if err != nil {
return ldap.Entry{}, false, fmt.Errorf("ldap: search with filter %q failed: %v", req.Filter, err)
@@ -447,11 +448,11 @@ func (c *ldapConnector) userEntry(conn *ldap.Conn, username string) (user ldap.E
switch n := len(resp.Entries); n {
case 0:
c.logger.Errorf("ldap: no results returned for filter: %q", filter)
c.logger.Error("no results returned for filter", "filter", filter)
return ldap.Entry{}, false, nil
case 1:
user = *resp.Entries[0]
c.logger.Infof("username %q mapped to entry %s", username, user.DN)
c.logger.Info("username mapped to entry", "username", username, "user_dn", user.DN)
return user, true, nil
default:
return ldap.Entry{}, false, fmt.Errorf("ldap: filter returned multiple (%d) results: %q", n, filter)
@@ -491,11 +492,11 @@ func (c *ldapConnector) Login(ctx context.Context, s connector.Scopes, username,
if ldapErr, ok := err.(*ldap.Error); ok {
switch ldapErr.ResultCode {
case ldap.LDAPResultInvalidCredentials:
c.logger.Errorf("ldap: invalid password for user %q", user.DN)
c.logger.Error("invalid password for user", "user_dn", user.DN)
incorrectPass = true
return nil
case ldap.LDAPResultConstraintViolation:
c.logger.Errorf("ldap: constraint violation for user %q: %s", user.DN, ldapErr.Error())
c.logger.Error("constraint violation for user", "user_dn", user.DN, "err", ldapErr.Error())
incorrectPass = true
return nil
}
@@ -581,7 +582,7 @@ func (c *ldapConnector) Refresh(ctx context.Context, s connector.Scopes, ident c
func (c *ldapConnector) groups(ctx context.Context, user ldap.Entry) ([]string, error) {
if c.GroupSearch.BaseDN == "" {
c.logger.Debugf("No groups returned for %q because no groups baseDN has been configured.", c.getAttr(user, c.UserSearch.NameAttr))
c.logger.Debug("No groups returned because no groups baseDN has been configured.", "base_dn", c.getAttr(user, c.UserSearch.NameAttr))
return nil, nil
}
@@ -602,8 +603,8 @@ func (c *ldapConnector) groups(ctx context.Context, user ldap.Entry) ([]string,
gotGroups := false
if err := c.do(ctx, func(conn *ldap.Conn) error {
c.logger.Infof("performing ldap search %s %s %s",
req.BaseDN, scopeString(req.Scope), req.Filter)
c.logger.Info("performing ldap search",
"base_dn", req.BaseDN, "scope", scopeString(req.Scope), "filter", req.Filter)
resp, err := conn.Search(req)
if err != nil {
return fmt.Errorf("ldap: search failed: %v", err)
@@ -616,7 +617,7 @@ func (c *ldapConnector) groups(ctx context.Context, user ldap.Entry) ([]string,
}
if !gotGroups {
// TODO(ericchiang): Is this going to spam the logs?
c.logger.Errorf("ldap: groups search with filter %q returned no groups", filter)
c.logger.Error("groups search returned no groups", "filter", filter)
}
}
}
+2 -2
View File
@@ -4,11 +4,11 @@ import (
"context"
"fmt"
"io"
"log/slog"
"os"
"testing"
"github.com/kylelemons/godebug/pretty"
"github.com/sirupsen/logrus"
"github.com/dexidp/dex/connector"
)
@@ -567,7 +567,7 @@ func runTests(t *testing.T, connMethod connectionMethod, config *Config, tests [
c.BindDN = "cn=admin,dc=example,dc=org"
c.BindPW = "admin"
l := &logrus.Logger{Out: io.Discard, Formatter: &logrus.TextFormatter{}}
l := slog.New(slog.NewTextHandler(io.Discard, &slog.HandlerOptions{}))
conn, err := c.openConnector(l)
if err != nil {
+4 -4
View File
@@ -6,13 +6,13 @@ import (
"encoding/json"
"fmt"
"io"
"log/slog"
"net/http"
"strings"
"golang.org/x/oauth2"
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/log"
)
const (
@@ -29,7 +29,7 @@ type Config struct {
}
// Open returns a strategy for logging in through LinkedIn
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
return &linkedInConnector{
oauth2Config: &oauth2.Config{
ClientID: c.ClientID,
@@ -41,7 +41,7 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
Scopes: []string{"r_liteprofile", "r_emailaddress"},
RedirectURL: c.RedirectURI,
},
logger: logger,
logger: logger.With(slog.Group("connector", "type", "linkedin", "id", id)),
}, nil
}
@@ -51,7 +51,7 @@ type connectorData struct {
type linkedInConnector struct {
oauth2Config *oauth2.Config
logger log.Logger
logger *slog.Logger
}
// LinkedIn doesn't provide refresh tokens, so refresh tokens issued by Dex
+4 -4
View File
@@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"io"
"log/slog"
"net/http"
"strings"
"sync"
@@ -17,7 +18,6 @@ import (
"github.com/dexidp/dex/connector"
groups_pkg "github.com/dexidp/dex/pkg/groups"
"github.com/dexidp/dex/pkg/log"
)
// GroupNameFormat represents the format of the group identifier
@@ -66,7 +66,7 @@ type Config struct {
}
// Open returns a strategy for logging in through Microsoft.
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
m := microsoftConnector{
apiURL: strings.TrimSuffix(c.APIURL, "/"),
graphURL: strings.TrimSuffix(c.GraphURL, "/"),
@@ -78,7 +78,7 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
groups: c.Groups,
groupNameFormat: c.GroupNameFormat,
useGroupsAsWhitelist: c.UseGroupsAsWhitelist,
logger: logger,
logger: logger.With(slog.Group("connector", "type", "microsoft", "id", id)),
emailToLowercase: c.EmailToLowercase,
promptType: c.PromptType,
domainHint: c.DomainHint,
@@ -133,7 +133,7 @@ type microsoftConnector struct {
groupNameFormat GroupNameFormat
groups []string
useGroupsAsWhitelist bool
logger log.Logger
logger *slog.Logger
emailToLowercase bool
promptType string
domainHint string
+7 -6
View File
@@ -5,16 +5,16 @@ import (
"context"
"errors"
"fmt"
"log/slog"
"net/http"
"net/url"
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/log"
)
// NewCallbackConnector returns a mock connector which requires no user interaction. It always returns
// the same (fake) identity.
func NewCallbackConnector(logger log.Logger) connector.Connector {
func NewCallbackConnector(logger *slog.Logger) connector.Connector {
return &Callback{
Identity: connector.Identity{
UserID: "0-385-28089-0",
@@ -39,7 +39,7 @@ var (
type Callback struct {
// The returned identity.
Identity connector.Identity
Logger log.Logger
Logger *slog.Logger
}
// LoginURL returns the URL to redirect the user to login with.
@@ -74,7 +74,8 @@ func (m *Callback) TokenIdentity(ctx context.Context, subjectTokenType, subjectT
type CallbackConfig struct{}
// Open returns an authentication strategy which requires no user interaction.
func (c *CallbackConfig) Open(id string, logger log.Logger) (connector.Connector, error) {
func (c *CallbackConfig) Open(id string, logger *slog.Logger) (connector.Connector, error) {
logger = logger.With(slog.Group("connector", "type", "callback", "id", id))
return NewCallbackConnector(logger), nil
}
@@ -86,7 +87,7 @@ type PasswordConfig struct {
}
// Open returns an authentication strategy which prompts for a predefined username and password.
func (c *PasswordConfig) Open(id string, logger log.Logger) (connector.Connector, error) {
func (c *PasswordConfig) Open(id string, logger *slog.Logger) (connector.Connector, error) {
if c.Username == "" {
return nil, errors.New("no username supplied")
}
@@ -99,7 +100,7 @@ func (c *PasswordConfig) Open(id string, logger log.Logger) (connector.Connector
type passwordConnector struct {
username string
password string
logger log.Logger
logger *slog.Logger
}
func (p passwordConnector) Close() error { return nil }

Some files were not shown because too many files have changed in this diff Show More