diff --git a/cmd/idp/main.go b/cmd/idp/main.go index 9d2edb6..f9217a5 100644 --- a/cmd/idp/main.go +++ b/cmd/idp/main.go @@ -24,6 +24,7 @@ import ( "encoding/base64" "errors" "fmt" + "log/slog" "net/http" "net/url" "os" @@ -36,7 +37,6 @@ import ( "github.com/knadh/koanf/parsers/toml" "github.com/knadh/koanf/providers/confmap" hydra "github.com/ory/hydra-client-go/v2" - log "github.com/sirupsen/logrus" "code.cacert.org/cacert/oidc-idp/internal/handlers" "code.cacert.org/cacert/oidc-idp/internal/services" @@ -55,6 +55,8 @@ const ( sessionKeyLength = 32 sessionAuthKeyLength = 64 + + minCSRFKeyLength = 64 ) var ( @@ -64,42 +66,61 @@ var ( ) func main() { - logger := log.New() + var ( + logLevel = new(slog.LevelVar) + logHandler slog.Handler + logger *slog.Logger + ) + + logHandler = slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: logLevel}) + logger = slog.New(logHandler) + slog.SetDefault(logger) config, err := services.ConfigureApplication(logger, "IDP", services.DefaultConfig) if err != nil { - logger.WithError(err).Fatal("error loading configuration") + logger.Error("error loading configuration", "err", err) + os.Exit(1) } - if level := config.String("log.level"); level != "" { - logLevel, err := log.ParseLevel(level) + if level := config.Bytes("log.level"); level != nil { + err := logLevel.UnmarshalText(level) if err != nil { - logger.WithError(err).Fatal("could not parse log level") + logger.Error("could not parse log level", "error", err) + os.Exit(1) } - logger.SetLevel(logLevel) + slog.SetLogLoggerLevel(logLevel.Level()) } if config.Bool("log.json") { - logger.SetFormatter(&log.JSONFormatter{}) + logHandler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: logLevel}) + logger = slog.New(logHandler) + slog.SetDefault(logger) } - logger.WithFields(log.Fields{ - "version": version, "commit": commit, "date": date, - }).Info("Starting CAcert OpenID Connect Identity Provider") - logger.Infoln("Server is starting") + logger.Info("Starting CAcert OpenID Connect Identity Provider", + "version", version, "commit", commit, "date", date, + ) + logger.Info("Server is starting") i18nService := services.InitI18n(logger, config.Strings("i18n.languages")) if err = i18nService.AddMessages(); err != nil { - logger.WithError(err).Fatal("could not add messages for i18n") + logger.Error("could not add messages for i18n", "error", err) + os.Exit(1) + } + + sessionAuthKey, sessionEncKey, csrfKey, err := configureSessionParameters(config) + if err != nil { + logger.Error("could not configure session parameters", "error", err) + os.Exit(1) } - sessionAuthKey, sessionEncKey := configureSessionParameters(config) services.InitSessionStore(sessionAuthKey, sessionEncKey) clientTransport, err := configureAdminClient(config) if err != nil { - logger.WithError(err).Fatal("could not configure Hydra admin client") + logger.Error("could not configure Hydra admin client", "error", err) + os.Exit(1) } tc := handlers.PopulateTemplateCache() @@ -132,11 +153,6 @@ func main() { router.Handle("/css/", staticFiles) router.Handle("/js/", staticFiles) - csrfKey, err := base64.StdEncoding.DecodeString(config.MustString("security.csrf.key")) - if err != nil { - logger.WithError(err).Fatal("could not parse CSRF key bytes") - } - nextRequestID := func() string { return fmt.Sprintf("%d", time.Now().UnixNano()) } @@ -152,12 +168,17 @@ func main() { errorMiddleware, err := handlers.ErrorHandling(logger, tc, i18nService) if err != nil { - logger.WithError(err).Fatal("could not initialize request error handling") + logger.Error("could not initialize request error handling", "error", err) + os.Exit(1) } handlerChain := tracing(logging(hsts(errorMiddleware(csrfProtect(router))))) - startServer(logger, config, handlerChain) + err = startServer(logger, config, handlerChain) + if err != nil { + logger.Error("server start failed", "error", err) + os.Exit(1) + } } func configureAdminClient(config *koanf.Koanf) (*hydra.APIClient, error) { @@ -197,7 +218,7 @@ func configureAdminClient(config *koanf.Koanf) (*hydra.APIClient, error) { return c, nil } -func startServer(logger *log.Logger, config *koanf.Koanf, handlerChain http.Handler) { +func startServer(logger *slog.Logger, config *koanf.Koanf, handlerChain http.Handler) error { clientCertificateCAFile := config.MustString("security.client.ca-file") serverBindAddress := config.String("server.bind_address") serverName := config.String("server.name") @@ -207,7 +228,7 @@ func startServer(logger *log.Logger, config *koanf.Koanf, handlerChain http.Hand pemBytes, err := os.ReadFile(clientCertificateCAFile) if err != nil { - logger.WithError(err).Fatal("could not load client CA certificates") + return fmt.Errorf("could not load client CA certificates: %w", err) } clientCertPool.AppendCertsFromPEM(pemBytes) @@ -233,7 +254,7 @@ func startServer(logger *log.Logger, config *koanf.Koanf, handlerChain http.Hand go func() { <-quit - logger.Infoln("Server is shutting down...") + logger.Info("Server is shutting down...") atomic.StoreInt32(&handlers.Healthy, 0) ctx, cancel := context.WithTimeout(context.Background(), ShutdownTimeout) @@ -242,27 +263,30 @@ func startServer(logger *log.Logger, config *koanf.Koanf, handlerChain http.Hand server.SetKeepAlivesEnabled(false) if err := server.Shutdown(ctx); err != nil { - logger.WithError(err).Fatal("Could not gracefully shutdown the server") + logger.Error("could not shutdown server gracefully", "error", err) } close(done) }() - logger.WithFields(log.Fields{ - "address": server.Addr, "url": publicAddress(serverName, serverPort), - }).Info("Server is ready to handle requests") + logger.Info( + "Server is ready to handle requests", + "address", server.Addr, "url", publicAddress(serverName, serverPort), + ) atomic.StoreInt32(&handlers.Healthy, 1) if err := server.ListenAndServeTLS( config.String("server.certificate"), config.String("server.key"), ); err != nil && !errors.Is(err, http.ErrServerClosed) { - logger.WithError(err).WithField( - "server_addr", server.Addr, - ).Fatal("Could not listen on configured server address") + logger.Error("could not listen on configured server address", "server_addr", server.Addr) + + return fmt.Errorf("could not create listener: %w", err) } <-done - logger.Infoln("Server stopped") + logger.Info("Server stopped") + + return nil } func publicAddress(serverName string, serverPort int) string { @@ -273,42 +297,66 @@ func publicAddress(serverName string, serverPort int) string { return fmt.Sprintf("https://%s/", serverName) } -func configureSessionParameters(config *koanf.Koanf) ([]byte, []byte) { +func configureSessionParameters(config *koanf.Koanf) ([]byte, []byte, []byte, error) { //nolint:cyclop sessionAuthKey, err := base64.StdEncoding.DecodeString(config.String("session.auth-key")) if err != nil { - log.WithError(err).Fatal("could not decode session auth key") + return nil, nil, nil, fmt.Errorf("could not decode session auth key: %w", err) } sessionEncKey, err := base64.StdEncoding.DecodeString(config.String("session.enc-key")) if err != nil { - log.WithError(err).Fatal("could not decode session encryption key") + return nil, nil, nil, fmt.Errorf("could not decode session encryption key: %w", err) + } + + csrfKey, err := base64.StdEncoding.DecodeString(config.String("security.csrf.key")) + if err != nil { + return nil, nil, nil, fmt.Errorf("could not decode CSRF key bytes: %w", err) } generated := false if len(sessionAuthKey) != sessionAuthKeyLength { - sessionAuthKey = services.GenerateKey(sessionAuthKeyLength) + sessionAuthKey, err = services.GenerateKey(sessionAuthKeyLength) + if err != nil { + return nil, nil, nil, fmt.Errorf("could not generate session authentication key: %w", err) + } + generated = true } if len(sessionEncKey) != sessionKeyLength { - sessionEncKey = services.GenerateKey(sessionKeyLength) + sessionEncKey, err = services.GenerateKey(sessionKeyLength) + if err != nil { + return nil, nil, nil, fmt.Errorf("could not generate session encryption key: %w", err) + } + + generated = true + } + + if len(csrfKey) < minCSRFKeyLength { + csrfKey, err = services.GenerateKey(minCSRFKeyLength) + if err != nil { + return nil, nil, nil, fmt.Errorf("could not generate CSRF key: %w", err) + } + generated = true } if generated { _ = config.Load(confmap.Provider(map[string]interface{}{ - "session.auth-key": sessionAuthKey, - "session.enc-key": sessionEncKey, + "session.auth-key": base64.StdEncoding.EncodeToString(sessionAuthKey), + "session.enc-key": base64.StdEncoding.EncodeToString(sessionEncKey), + "security.csrf.key": base64.StdEncoding.EncodeToString(csrfKey), }, "."), nil) tomlData, err := config.Marshal(toml.Parser()) if err != nil { - log.WithError(err).Fatal("could not encode session config") + return nil, nil, nil, fmt.Errorf("could not encode session config: %w", err) } - log.Infof("put the following in your resource_app.toml:\n%s", string(tomlData)) + slog.Info("generated configuration values, put the following into your idp.toml") + fmt.Printf("------\n%s------\n", string(tomlData)) //nolint:forbidigo } - return sessionAuthKey, sessionEncKey + return sessionAuthKey, sessionEncKey, csrfKey, nil } diff --git a/go.mod b/go.mod index 5e45ab0..5effbc5 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,6 @@ require ( github.com/lestrrat-go/jwx v1.2.29 github.com/nicksnyder/go-i18n/v2 v2.4.0 github.com/ory/hydra-client-go/v2 v2.2.0 - github.com/sirupsen/logrus v1.9.3 github.com/spf13/pflag v1.0.5 github.com/yuin/goldmark v1.7.1 golang.org/x/text v0.15.0 diff --git a/go.sum b/go.sum index 30e2632..8288643 100644 --- a/go.sum +++ b/go.sum @@ -262,8 +262,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -384,7 +382,6 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/internal/handlers/common.go b/internal/handlers/common.go index ac9ef58..eed8817 100644 --- a/internal/handlers/common.go +++ b/internal/handlers/common.go @@ -22,12 +22,12 @@ import ( "context" "fmt" "html/template" + "log/slog" "net/http" "github.com/dustin/go-humanize" "github.com/gorilla/sessions" "github.com/nicksnyder/go-i18n/v2/i18n" - log "github.com/sirupsen/logrus" "code.cacert.org/cacert/oidc-idp/ui" @@ -46,7 +46,7 @@ func GetSession(r *http.Request) (*sessions.Session, error) { } type AuthMiddleware struct { - logger *log.Logger + logger *slog.Logger trans *services.I18NService templates TemplateCache } @@ -83,7 +83,7 @@ func GetAuthenticatedAddresses(r *http.Request) []string { return nil } -func NewAuthMiddleware(logger *log.Logger, tc TemplateCache, trans *services.I18NService) *AuthMiddleware { +func NewAuthMiddleware(logger *slog.Logger, tc TemplateCache, trans *services.I18NService) *AuthMiddleware { return &AuthMiddleware{logger: logger, trans: trans, templates: tc} } @@ -104,13 +104,13 @@ const ( type TemplateCache map[templateName]*template.Template func (c TemplateCache) render( - logger *log.Logger, w http.ResponseWriter, name templateName, params map[string]interface{}, + logger *slog.Logger, w http.ResponseWriter, name templateName, params map[string]interface{}, ) { rendered := bytes.NewBuffer(make([]byte, 0)) err := c[name].Lookup("base").Execute(rendered, params) if err != nil { - logger.WithError(err).Error("template rendering failed") + logger.Error("template rendering failed", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return @@ -166,7 +166,7 @@ func PopulateTemplateCache() TemplateCache { } func renderNoEmailsInClientCertificate( - logger *log.Logger, + logger *slog.Logger, templates TemplateCache, trans *services.I18NService, w http.ResponseWriter, @@ -179,7 +179,7 @@ func renderNoEmailsInClientCertificate( "Explanation": msg("NoEmailsInClientCertificateExplanation", nil, localizer), }) if err != nil { - logger.WithError(err).Error("template rendering failed") + logger.Error("template rendering failed", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return diff --git a/internal/handlers/consent.go b/internal/handlers/consent.go index 295db79..b9b37b0 100644 --- a/internal/handlers/consent.go +++ b/internal/handlers/consent.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "html/template" + "log/slog" "net/http" "net/url" "strings" @@ -33,14 +34,13 @@ import ( "github.com/lestrrat-go/jwx/jwt/openid" "github.com/nicksnyder/go-i18n/v2/i18n" client "github.com/ory/hydra-client-go/v2" - log "github.com/sirupsen/logrus" "code.cacert.org/cacert/oidc-idp/internal/models" "code.cacert.org/cacert/oidc-idp/internal/services" ) type ConsentHandler struct { - logger *log.Logger + logger *slog.Logger trans *services.I18NService adminClient client.OAuth2API templates TemplateCache @@ -108,7 +108,7 @@ func (i *UserInfo) GetFullName() string { func (h *ConsentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { challenge := r.URL.Query().Get("consent_challenge") - h.logger.WithField("consent_challenge", challenge).Debug("received consent challenge") + h.logger.Debug("received consent challenge", "consent_challenge", challenge) localizer := getLocalizer(h.trans, r) @@ -121,7 +121,7 @@ func (h *ConsentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { session, err := GetSession(r) if err != nil { - h.logger.WithError(err).Error("could get session for request") + h.logger.Error("could get session for request", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -149,7 +149,7 @@ func (h *ConsentHandler) handleGet( if consentData.GetSkip() { consentRequest, err := h.handleExistingConsent(consentData, requestedClaims, session) if err != nil { - h.logger.WithError(err).Error("could not handle existing consent") + h.logger.Error("could not handle existing consent", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -158,7 +158,7 @@ func (h *ConsentHandler) handleGet( err = h.acceptConsent(w, r, challenge, consentRequest) if err != nil { - h.logger.WithError(err).Error("could not accept consent") + h.logger.Error("could not accept consent", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } @@ -182,7 +182,7 @@ func (h *ConsentHandler) handlePost( // validate input decoder := form.NewDecoder() if err := decoder.Decode(&consentInfo, r.Form); err != nil { - h.logger.WithError(err).Error("could not decode consent form") + h.logger.Error("could not decode consent form", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return @@ -191,14 +191,14 @@ func (h *ConsentHandler) handlePost( if consentInfo.ConsentAction == "consent" { consentRequest, err := h.rememberNewConsent(consentData, consentInfo, requestedClaims, session) if err != nil { - h.logger.WithError(err).Error("could not accept consent") + h.logger.Error("could not accept consent", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } err = h.acceptConsent(w, r, challenge, consentRequest) if err != nil { - h.logger.WithError(err).Error("could not accept consent") + h.logger.Error("could not accept consent", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } @@ -210,7 +210,7 @@ func (h *ConsentHandler) handlePost( r.Context(), ).ConsentChallenge(challenge).Execute() if err != nil { - h.logger.WithError(err).Error("reject consent request failed") + h.logger.Error("reject consent request failed", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return @@ -218,9 +218,10 @@ func (h *ConsentHandler) handlePost( defer func() { _ = response.Body.Close() }() - h.logger.WithFields( - log.Fields{"response": response.Status, "reject_consent_request": consentRequest}, - ).Debug("received response for RejectOAuth2ConsentRequest") + h.logger.Debug( + "received response for RejectOAuth2ConsentRequest", + "response", response.Status, "reject_consent_request", consentRequest, + ) w.Header().Add("Location", consentRequest.GetRedirectTo()) w.WriteHeader(http.StatusFound) @@ -266,9 +267,10 @@ func (h *ConsentHandler) acceptConsent( defer func() { _ = response.Body.Close() }() - h.logger.WithFields( - log.Fields{"response": response.Status, "redirect_to": oAuth2RedirectTo}, - ).Debug("received response for AcceptOAuth2ConsentRequest") + h.logger.Debug( + "received response for AcceptOAuth2ConsentRequest", + "response", response.Status, "redirect_to", oAuth2RedirectTo, + ) w.Header().Add("Location", oAuth2RedirectTo.GetRedirectTo()) w.WriteHeader(http.StatusFound) @@ -305,7 +307,7 @@ func (h *ConsentHandler) getRequestedConsentInformation(challenge string, r *htt r.Context(), ).ConsentChallenge(challenge).Execute() if err != nil { - h.logger.WithError(err).Error("error getting consent information") + h.logger.Error("error getting consent information", "error", err) if errorBucket := GetErrorBucket(r); errorBucket != nil { errorDetails := &ErrorDetails{ @@ -321,9 +323,10 @@ func (h *ConsentHandler) getRequestedConsentInformation(challenge string, r *htt defer func() { _ = response.Body.Close() }() - h.logger.WithFields( - log.Fields{"response": response.Status, "consent_request": consentRequest}, - ).Debug("response for GetOAuth2ConsentRequest") + h.logger.Debug( + "response for GetOAuth2ConsentRequest", + "response", response.Status, "consent_request", consentRequest, + ) var requestedClaims models.OIDCClaimsRequest @@ -331,9 +334,10 @@ func (h *ConsentHandler) getRequestedConsentInformation(challenge string, r *htt requestURL, err := url.Parse(requestURLStr) if err != nil { - h.logger.WithError(err).WithField( - "request_url", requestURLStr, - ).Warn("could not parse original request URL") + h.logger.Warn( + "could not parse original request URL", + "error", err, "request_url", requestURLStr, + ) } else { claimsParameter := requestURL.Query().Get("claims") if claimsParameter != "" { @@ -341,9 +345,10 @@ func (h *ConsentHandler) getRequestedConsentInformation(challenge string, r *htt err := decoder.Decode(&requestedClaims) if err != nil { - h.logger.WithError(err).WithField( - "claims_parameter", claimsParameter, - ).Warn("ignoring claims request parameter that could not be decoded") + h.logger.Warn( + "ignoring claims request parameter that could not be decoded", + "error", err, "claims_parameter", claimsParameter, + ) } } } @@ -410,7 +415,7 @@ func (h *ConsentHandler) mapRequestedScope( for _, scopeName := range scope { if _, ok := supportedScopes[scopeName]; !ok { - h.logger.WithField("scope", scopeName).Warn("ignoring unsupported scope") + h.logger.Warn("ignoring unsupported scope", "scope", scopeName) continue } @@ -419,7 +424,7 @@ func (h *ConsentHandler) mapRequestedScope( DefaultMessage: supportedScopes[scopeName], }) if err != nil { - h.logger.WithError(err).WithField("scope", scopeName).Warn("could not localize scope label") + h.logger.Warn("could not localize scope label", "error", err, "scope", scopeName) label = scopeName } @@ -446,7 +451,7 @@ func (h *ConsentHandler) mapRequestedClaims( if claimElement != nil { for k, v := range *claimElement { if _, ok := supportedClaims[k]; !ok { - h.logger.WithField("claim", k).Warn("ignoring unsupported claim") + h.logger.Warn("ignoring unsupported claim", "claim", k) continue } @@ -455,7 +460,7 @@ func (h *ConsentHandler) mapRequestedClaims( DefaultMessage: supportedClaims[k], }) if err != nil { - h.logger.WithError(err).WithField("claim", k).Warn("could not localize claim label") + h.logger.Warn("could not localize claim label", "error", err, "claim", k) label = k } @@ -585,9 +590,9 @@ func (h *ConsentHandler) parseUserInfoClaims( } if claim.IsEssential() { - h.logger.WithField("claim", claimName).Warn("handling for essential claim not implemented") + h.logger.Warn("handling for essential claim not implemented", "claim", claimName) } else { - h.logger.WithField("claim", claimName).Warn("handling for claim not implemented") + h.logger.Warn("handling for claim not implemented", "claim", claimName) } } @@ -595,7 +600,7 @@ func (h *ConsentHandler) parseUserInfoClaims( } func NewConsentHandler( - logger *log.Logger, templateCache TemplateCache, trans *services.I18NService, adminClient client.OAuth2API, + logger *slog.Logger, templateCache TemplateCache, trans *services.I18NService, adminClient client.OAuth2API, ) *ConsentHandler { return &ConsentHandler{ logger: logger, diff --git a/internal/handlers/error.go b/internal/handlers/error.go index eb4e79e..b05ccd0 100644 --- a/internal/handlers/error.go +++ b/internal/handlers/error.go @@ -22,10 +22,9 @@ import ( "context" "fmt" "html/template" + "log/slog" "net/http" - log "github.com/sirupsen/logrus" - "code.cacert.org/cacert/oidc-idp/internal/services" "code.cacert.org/cacert/oidc-idp/ui" ) @@ -44,7 +43,7 @@ type ErrorDetails struct { } type ErrorBucket struct { - logger *log.Logger + logger *slog.Logger trans *services.I18NService errorDetails *ErrorDetails templates TemplateCache @@ -118,9 +117,7 @@ func (w *errorResponseWriter) Write(content []byte) (int, error) { } func ErrorHandling( - logger *log.Logger, - templateCache TemplateCache, - trans *services.I18NService, + logger *slog.Logger, templateCache TemplateCache, trans *services.I18NService, ) (func(http.Handler) http.Handler, error) { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -139,7 +136,7 @@ func ErrorHandling( } type ErrorHandler struct { - logger *log.Logger + logger *slog.Logger trans *services.I18NService template *template.Template } @@ -156,10 +153,10 @@ func (h *ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { errorName := r.URL.Query().Get("error") errorDescription := r.URL.Query().Get("error_description") - h.logger.WithFields(log.Fields{ - "error_name": errorName, - "error_description": errorDescription, - }).Debug("error from Hydra") + h.logger.Debug( + "error from Hydra", + "error_name", errorName, "error_description", errorDescription, + ) rendered := bytes.NewBuffer(make([]byte, 0)) @@ -174,7 +171,7 @@ func (h *ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { "ErrorMessage": errorDescription, }) if err != nil { - h.logger.WithError(err).Error("template rendering failed") + h.logger.Error("template rendering failed", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return @@ -186,7 +183,7 @@ func (h *ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { _, _ = w.Write(rendered.Bytes()) } -func NewErrorHandler(logger *log.Logger, trans *services.I18NService) *ErrorHandler { +func NewErrorHandler(logger *slog.Logger, trans *services.I18NService) *ErrorHandler { return &ErrorHandler{ logger: logger, trans: trans, diff --git a/internal/handlers/login.go b/internal/handlers/login.go index 1d8e73a..bbedd62 100644 --- a/internal/handlers/login.go +++ b/internal/handlers/login.go @@ -23,12 +23,12 @@ import ( "fmt" "html/template" "io" + "log/slog" "net/http" "github.com/gorilla/csrf" "github.com/nicksnyder/go-i18n/v2/i18n" client "github.com/ory/hydra-client-go/v2" - log "github.com/sirupsen/logrus" "code.cacert.org/cacert/oidc-idp/internal/services" ) @@ -49,7 +49,7 @@ const ( ) type LoginHandler struct { - logger *log.Logger + logger *slog.Logger trans *services.I18NService adminClient client.OAuth2API templates TemplateCache @@ -72,7 +72,7 @@ func (h *LoginHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - h.logger.WithField("challenge", challenge).Debug("received login challenge") + h.logger.Debug("received login challenge", "challenge", challenge) certFullName, certEmails := getDataFromClientCert(h.logger, r) @@ -100,9 +100,10 @@ func (h *LoginHandler) handleGet( r.Context(), ).LoginChallenge(challenge).Execute() if err != nil { - h.logger.WithError(err).WithField( - "challenge", challenge, - ).Warn("could not get login request for challenge") + h.logger.Warn( + "could not get login request for challenge", + "error", err, "challenge", challenge, + ) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -111,9 +112,10 @@ func (h *LoginHandler) handleGet( defer func() { _ = response.Body.Close() }() - h.logger.WithFields( - log.Fields{"response": response.Status, "login_request": oAuth2LoginRequest}, - ).Debug("got response for GetOAuth2LoginRequest") + h.logger.Debug( + "got response for GetOAuth2LoginRequest", + "response", response.Status, "login_request", oAuth2LoginRequest, + ) h.renderRequestForClientCert(w, r, certEmails, localizer, oAuth2LoginRequest) } @@ -151,14 +153,14 @@ func (h *LoginHandler) handlePost( } // perform certificate auth - h.logger.WithFields(log.Fields{ - "emails": certEmails, - "full_name": certFullName, - }).Info("will perform certificate authentication") + h.logger.Info( + "will perform certificate authentication", + "emails", certEmails, "full_name", certFullName, + ) userID, err := h.performCertificateLogin(certEmails, r) if err != nil { - h.logger.WithError(err).Error("could not perform certificate login") + h.logger.Error("could not perform certificate login", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return @@ -166,7 +168,7 @@ func (h *LoginHandler) handlePost( session, err := GetSession(r) if err != nil { - h.logger.WithError(err).Error("could not perform certificate login") + h.logger.Error("could not perform certificate login", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return @@ -178,7 +180,7 @@ func (h *LoginHandler) handlePost( session.Options.Secure = true if err = session.Save(r, w); err != nil { - h.logger.WithError(err).Error("could not save session") + h.logger.Error("could not save session", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return @@ -194,7 +196,7 @@ func (h *LoginHandler) handlePost( r.Context(), ).LoginChallenge(challenge).AcceptOAuth2LoginRequest(*acceptRequest).Execute() if err != nil { - h.logger.WithError(err).Error("error getting login request") + h.logger.Error("error getting login request", "error", err) // h.fillAcceptLoginRequestErrorBucket(r, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -204,13 +206,13 @@ func (h *LoginHandler) handlePost( defer func() { _ = response.Body.Close() }() - h.logger.WithFields( - log.Fields{"response": response.Status, "accept_login_request": loginRequest}, - ).Debug("got response for AcceptOAuth2LoginRequest") + h.logger.Debug("got response for AcceptOAuth2LoginRequest", + "response", response.Status, "accept_login_request", loginRequest, + ) - if h.logger.IsLevelEnabled(log.TraceLevel) { + if h.logger.Enabled(r.Context(), slog.LevelDebug) { if rb, err := io.ReadAll(response.Body); err == nil { - h.logger.WithField("response_body", rb).Trace("response body from Hydra") + h.logger.Debug("response body from Hydra", "response_body", rb) } } @@ -233,7 +235,7 @@ func (h *LoginHandler) rejectLogin( r.Context(), ).LoginChallenge(challenge).RejectOAuth2Request(*rejectRequest).Execute() if err != nil { - h.logger.WithError(err).Error("error getting reject login request") + h.logger.Error("error getting reject login request", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return @@ -241,9 +243,10 @@ func (h *LoginHandler) rejectLogin( defer func() { _ = response.Body.Close() }() - h.logger.WithFields( - log.Fields{"response": response.Status, "reject_login_request": rejectLoginRequest}, - ).Debug("go response for RejectOAuth2LoginRequest") + h.logger.Debug( + "go response for RejectOAuth2LoginRequest", + "response", response.Status, "reject_login_request", rejectLoginRequest, + ) w.Header().Set("Location", rejectLoginRequest.GetRedirectTo()) w.WriteHeader(http.StatusFound) @@ -278,7 +281,7 @@ func (h *LoginHandler) renderRequestForClientCert( "FlashMessage": r.Context().Value(ctxKeyMessage), }) if err != nil { - h.logger.WithError(err).Error("template rendering failed") + h.logger.Error("template rendering failed", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return @@ -310,7 +313,7 @@ func (h *LoginHandler) renderNoChallengeInRequest(w http.ResponseWriter, localiz )), }) if err != nil { - h.logger.WithError(err).Error("template rendering failed") + h.logger.Error("template rendering failed", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return @@ -318,7 +321,7 @@ func (h *LoginHandler) renderNoChallengeInRequest(w http.ResponseWriter, localiz } func NewLoginHandler( - logger *log.Logger, tc TemplateCache, trans *services.I18NService, adminClient client.OAuth2API, + logger *slog.Logger, tc TemplateCache, trans *services.I18NService, adminClient client.OAuth2API, ) *LoginHandler { return &LoginHandler{ logger: logger, diff --git a/internal/handlers/logout.go b/internal/handlers/logout.go index 730e211..9ea9623 100644 --- a/internal/handlers/logout.go +++ b/internal/handlers/logout.go @@ -19,28 +19,28 @@ package handlers import ( "html/template" + "log/slog" "net/http" client "github.com/ory/hydra-client-go/v2" - log "github.com/sirupsen/logrus" "code.cacert.org/cacert/oidc-idp/internal/services" ) type LogoutHandler struct { adminClient client.OAuth2API - logger *log.Logger + logger *slog.Logger } func (h *LogoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { challenge := r.URL.Query().Get("logout_challenge") - h.logger.WithField("challenge", challenge).Debug("received logout challenge") + h.logger.Debug("received logout challenge", "challenge", challenge) logoutRequest, response, err := h.adminClient.GetOAuth2LogoutRequest( r.Context(), ).LogoutChallenge(challenge).Execute() if err != nil { - h.logger.WithError(err).Error("error getting logout requests") + h.logger.Error("error getting logout requests", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return @@ -48,29 +48,31 @@ func (h *LogoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { defer func() { _ = response.Body.Close() }() - h.logger.WithFields( - log.Fields{"response": response.Status, "logout_request": logoutRequest}, - ).Debug("got response for GetOAuth2LogoutRequest") + h.logger.Debug( + "got response for GetOAuth2LogoutRequest", + "response", response.Status, "logout_request", logoutRequest, + ) acceptLogoutRequest, response, err := h.adminClient.AcceptOAuth2LogoutRequest( r.Context(), ).LogoutChallenge(challenge).Execute() if err != nil { - h.logger.WithError(err).Error("accept logout request failed") + h.logger.Error("accept logout request failed", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } defer func() { _ = response.Body.Close() }() - h.logger.WithFields( - log.Fields{"response": response.Status, "accept_logout_request": acceptLogoutRequest}, - ).Debug("got response for AcceptOAuth2LogoutRequest") + h.logger.Debug( + "got response for AcceptOAuth2LogoutRequest", + "response", response.Status, "accept_logout_request", acceptLogoutRequest, + ) w.Header().Set("Location", acceptLogoutRequest.GetRedirectTo()) w.WriteHeader(http.StatusFound) } -func NewLogout(logger *log.Logger, adminClient client.OAuth2API) *LogoutHandler { +func NewLogout(logger *slog.Logger, adminClient client.OAuth2API) *LogoutHandler { return &LogoutHandler{ logger: logger, adminClient: adminClient, @@ -78,7 +80,7 @@ func NewLogout(logger *log.Logger, adminClient client.OAuth2API) *LogoutHandler } type LogoutSuccessHandler struct { - logger *log.Logger + logger *slog.Logger trans *services.I18NService templates TemplateCache } @@ -103,9 +105,7 @@ func (h *LogoutSuccessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) } func NewLogoutSuccess( - logger *log.Logger, - templateCache TemplateCache, - trans *services.I18NService, + logger *slog.Logger, templateCache TemplateCache, trans *services.I18NService, ) *LogoutSuccessHandler { return &LogoutSuccessHandler{logger: logger, trans: trans, templates: templateCache} } diff --git a/internal/handlers/manage.go b/internal/handlers/manage.go index 414f1cc..8bdf646 100644 --- a/internal/handlers/manage.go +++ b/internal/handlers/manage.go @@ -22,6 +22,7 @@ import ( "fmt" "html/template" "io" + "log/slog" "net/http" "sort" "strings" @@ -30,13 +31,12 @@ import ( "github.com/gorilla/csrf" "github.com/nicksnyder/go-i18n/v2/i18n" client "github.com/ory/hydra-client-go/v2" - log "github.com/sirupsen/logrus" "code.cacert.org/cacert/oidc-idp/internal/services" ) type IndexHandler struct { - logger *log.Logger + logger *slog.Logger trans *services.I18NService templates TemplateCache } @@ -66,12 +66,12 @@ func (h *IndexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { }) } -func NewIndex(logger *log.Logger, templateCache TemplateCache, trans *services.I18NService) *IndexHandler { +func NewIndex(logger *slog.Logger, templateCache TemplateCache, trans *services.I18NService) *IndexHandler { return &IndexHandler{logger: logger, trans: trans, templates: templateCache} } type ManageConsentHandler struct { - logger *log.Logger + logger *slog.Logger trans *services.I18NService adminAPI client.OAuth2API templates TemplateCache @@ -107,7 +107,7 @@ func (h *ManageConsentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) requestSession, err := GetSession(r) if err != nil { - h.logger.WithError(err).Error("could not get session for request") + h.logger.Error("could not get session for request", "error", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -202,7 +202,7 @@ func (h *ManageConsentHandler) getConsentSessions( ) ([]client.OAuth2ConsentSession, bool) { sessions, response, err := h.adminAPI.ListOAuth2ConsentSessions(r.Context()).Subject(subject).Execute() if err != nil { - h.logger.WithError(err).Error("error getting consent session list") + h.logger.Error("error getting consent session list", "error", err) // h.fillAcceptLoginRequestErrorBucket(r, err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) @@ -212,13 +212,14 @@ func (h *ManageConsentHandler) getConsentSessions( defer func(response *http.Response) { _ = response.Body.Close() }(response) - h.logger.WithFields( - log.Fields{"response": response.Status, "consent_sessions": sessions}, - ).Debug("got response for AcceptOAuth2LoginRequest") + h.logger.Debug( + "got response for AcceptOAuth2LoginRequest", + "response", response.Status, "consent_sessions", sessions, + ) - if h.logger.IsLevelEnabled(log.TraceLevel) { + if h.logger.Enabled(r.Context(), slog.LevelDebug) { if rb, err := io.ReadAll(response.Body); err == nil { - h.logger.WithField("response_body", rb).Trace("response body from Hydra") + h.logger.Debug("response body from Hydra", "response_body", rb) } } @@ -226,13 +227,13 @@ func (h *ManageConsentHandler) getConsentSessions( } func NewManageConsent( - logger *log.Logger, tc TemplateCache, trans *services.I18NService, adminAPI client.OAuth2API, + logger *slog.Logger, tc TemplateCache, trans *services.I18NService, adminAPI client.OAuth2API, ) *ManageConsentHandler { return &ManageConsentHandler{logger: logger, trans: trans, adminAPI: adminAPI, templates: tc} } type RevokeConsentHandler struct { - logger *log.Logger + logger *slog.Logger trans *services.I18NService adminAPI client.OAuth2API templates TemplateCache @@ -272,7 +273,7 @@ func (h *RevokeConsentHandler) handleGet( ) { clientApp, found, err := h.getClient(r.Context(), clientID) if err != nil { - h.logger.WithError(err).Error("could not get client") + h.logger.Error("could not get client", "error", err) http.Error( w, h.trans.LookupHTTPErrorMessage(http.StatusInternalServerError, localizer), @@ -308,7 +309,7 @@ func (h *RevokeConsentHandler) handlePost( ) { err := h.revokeConsent(r.Context(), clientID, subject) if err != nil { - h.logger.WithError(err).Error("could not revoke consent") + h.logger.Error("could not revoke consent", "error", err) http.Error( w, h.trans.LookupHTTPErrorMessage(http.StatusInternalServerError, localizer), @@ -329,13 +330,14 @@ func (h *RevokeConsentHandler) getClient(ctx context.Context, clientID string) ( defer func(response *http.Response) { _ = response.Body.Close() }(response) - h.logger.WithFields( - log.Fields{"response": response.Status, "client_app": clientApp}, - ).Debug("got response for GetOAuth2Client") + h.logger.Debug( + "got response for GetOAuth2Client", + "response", response.Status, "client_app", clientApp, + ) - if h.logger.IsLevelEnabled(log.TraceLevel) { + if h.logger.Enabled(ctx, slog.LevelDebug) { if rb, err := io.ReadAll(response.Body); err == nil { - h.logger.WithField("response_body", rb).Trace("response body from Hydra") + h.logger.Debug("response body from Hydra", "response_body", rb) } } @@ -350,13 +352,11 @@ func (h *RevokeConsentHandler) revokeConsent(ctx context.Context, clientID, subj defer func(response *http.Response) { _ = response.Body.Close() }(response) - h.logger.WithFields( - log.Fields{"response": response.Status}, - ).Debug("got response for RevokeOAuth2ConsentSessions") + h.logger.Debug("got response for RevokeOAuth2ConsentSessions", "response", response.Status) - if h.logger.IsLevelEnabled(log.TraceLevel) { + if h.logger.Enabled(ctx, slog.LevelDebug) { if rb, err := io.ReadAll(response.Body); err == nil { - h.logger.WithField("response_body", rb).Trace("response body from Hydra") + h.logger.Debug("response body from Hydra", "response_body", rb) } } @@ -364,7 +364,7 @@ func (h *RevokeConsentHandler) revokeConsent(ctx context.Context, clientID, subj } func NewRevokeConsent( - logger *log.Logger, tc TemplateCache, trans *services.I18NService, adminAPI client.OAuth2API, + logger *slog.Logger, tc TemplateCache, trans *services.I18NService, adminAPI client.OAuth2API, ) *RevokeConsentHandler { return &RevokeConsentHandler{logger: logger, trans: trans, adminAPI: adminAPI, templates: tc} } diff --git a/internal/handlers/observability.go b/internal/handlers/observability.go index 0923945..4d2245c 100644 --- a/internal/handlers/observability.go +++ b/internal/handlers/observability.go @@ -20,10 +20,9 @@ package handlers import ( "context" "fmt" + "log/slog" "net/http" "sync/atomic" - - log "github.com/sirupsen/logrus" ) type key int @@ -54,7 +53,7 @@ func (sci *statusCodeInterceptor) Write(content []byte) (int, error) { return count, nil } -func Logging(logger *log.Logger) func(http.Handler) http.Handler { +func Logging(logger *slog.Logger) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { interceptor := &statusCodeInterceptor{w, http.StatusOK, 0} @@ -64,15 +63,17 @@ func Logging(logger *log.Logger) func(http.Handler) http.Handler { requestID = "unknown" } - logger.Infof( - "[%s] %s \"%s %s\" %d %d \"%s\"", - requestID, - r.RemoteAddr, - r.Method, - r.URL.Path, - interceptor.code, - interceptor.count, - r.UserAgent(), + logger.Info( + fmt.Sprintf( + "[%s] %s \"%s %s\" %d %d \"%s\"", + requestID, + r.RemoteAddr, + r.Method, + r.URL.Path, + interceptor.code, + interceptor.count, + r.UserAgent(), + ), ) }() next.ServeHTTP(interceptor, r) diff --git a/internal/handlers/security.go b/internal/handlers/security.go index 17dbe59..3fc933e 100644 --- a/internal/handlers/security.go +++ b/internal/handlers/security.go @@ -20,10 +20,9 @@ package handlers import ( "crypto/x509" "fmt" + "log/slog" "net/http" "time" - - log "github.com/sirupsen/logrus" ) func EnableHSTS() func(http.Handler) http.Handler { @@ -37,7 +36,7 @@ func EnableHSTS() func(http.Handler) http.Handler { } } -func getDataFromClientCert(logger *log.Logger, r *http.Request) (string, []string) { +func getDataFromClientCert(logger *slog.Logger, r *http.Request) (string, []string) { if r.TLS != nil && r.TLS.PeerCertificates != nil && len(r.TLS.PeerCertificates) > 0 { firstCert := r.TLS.PeerCertificates[0] @@ -46,9 +45,7 @@ func getDataFromClientCert(logger *log.Logger, r *http.Request) (string, []strin } for _, email := range firstCert.EmailAddresses { - logger.WithField( - "email", email, - ).Info("authenticated with a client certificate for email address") + logger.Info("authenticated with a client certificate for email address", "email", email) } return firstCert.Subject.CommonName, firstCert.EmailAddresses diff --git a/internal/services/configuration.go b/internal/services/configuration.go index a415579..9f19aba 100644 --- a/internal/services/configuration.go +++ b/internal/services/configuration.go @@ -19,6 +19,7 @@ package services import ( "fmt" + "log/slog" "os" "strings" @@ -28,7 +29,6 @@ import ( "github.com/knadh/koanf/providers/env" "github.com/knadh/koanf/providers/file" "github.com/knadh/koanf/providers/posflag" - "github.com/sirupsen/logrus" "github.com/spf13/pflag" ) @@ -51,7 +51,7 @@ var DefaultConfig = map[string]interface{}{ } func ConfigureApplication( - logger *logrus.Logger, + logger *slog.Logger, appName string, defaultConfig map[string]interface{}, ) (*koanf.Koanf, error) { @@ -70,7 +70,7 @@ func ConfigureApplication( var err error if err = f.Parse(os.Args[1:]); err != nil { - logger.WithError(err).Fatal("could not parse command line arguments") + return nil, fmt.Errorf("could not parse command line arguments: %w", err) } config := koanf.New(".") @@ -78,18 +78,22 @@ func ConfigureApplication( _ = config.Load(confmap.Provider(defaultConfig, "."), nil) if err = config.Load(file.Provider(defaultFile), toml.Parser()); err != nil && !os.IsNotExist(err) { - logrus.WithError(err).WithField("file", defaultFile).Fatal("error loading configuration from file") + logger.Error("could not load configuration from file", "file", defaultFile) + + return nil, fmt.Errorf("error loading configuration: %w", err) } cFiles, _ := f.GetStringSlice("conf") for _, c := range cFiles { if err = config.Load(file.Provider(c), toml.Parser()); err != nil { - logger.WithError(err).WithField("file", c).Fatal("error loading configuration from file") + logger.Error("could not load configuration from file", "file", c) + + return nil, fmt.Errorf("error loading configuration: %w", err) } } if err = config.Load(posflag.Provider(f, ".", config), nil); err != nil { - logger.WithError(err).Fatal("error loading configuration from command line") + return nil, fmt.Errorf("error loading configuration from command line: %w", err) } prefix := fmt.Sprintf("%s_", strings.ToUpper(appName)) @@ -97,7 +101,7 @@ func ConfigureApplication( if err = config.Load(env.Provider(prefix, ".", func(s string) string { return strings.ReplaceAll(strings.ToLower(strings.TrimPrefix(s, prefix)), "_", ".") }), nil); err != nil { - logrus.WithError(err).Fatal("error loading configuration from environment") + return nil, fmt.Errorf("error loading configuration from environment: %w", err) } return config, nil diff --git a/internal/services/i18n.go b/internal/services/i18n.go index d320d72..51f6238 100644 --- a/internal/services/i18n.go +++ b/internal/services/i18n.go @@ -21,9 +21,9 @@ import ( "bytes" "errors" "fmt" + "log/slog" "net/http" - log "github.com/sirupsen/logrus" "github.com/yuin/goldmark" "code.cacert.org/cacert/oidc-idp/translations" @@ -35,7 +35,7 @@ import ( type MessageCatalog struct { messages map[string]*i18n.Message - logger *log.Logger + logger *slog.Logger } func (m *MessageCatalog) AddMessages(messages map[string]*i18n.Message) { @@ -61,7 +61,7 @@ func (s *I18NService) LookupMessage( return translation } - s.logger.WithField("id", id).Warn("no translation found for id") + s.logger.Warn("no translation found for id", "id", id) return id } @@ -88,7 +88,7 @@ func (s *I18NService) LookupMarkdownMessage( TemplateData: templateData, }) if err != nil { - s.logger.WithError(err).Warn(err) + s.logger.Warn("message localization failed", "error", err, "message", message) } if translation == "" { @@ -105,7 +105,7 @@ func (s *I18NService) LookupMarkdownMessage( return buf.String() } - s.logger.WithField("id", id).Warn("no translation found for id") + s.logger.Warn("no translation found for id", "id", id) return id } @@ -129,7 +129,7 @@ func (s *I18NService) LookupMessagePlural( return translation } - s.logger.WithField("id", id).Warn("no translation found for id") + s.logger.Warn("no translation found for id", "id", id) return id } @@ -138,20 +138,20 @@ func (m *MessageCatalog) handleLocalizeError(id string, translation string, err var messageNotFound *i18n.MessageNotFoundErr if errors.As(err, &messageNotFound) { - m.logger.WithError(err).WithField("message", id).Warn("message not found") + m.logger.Warn("message not found", "error", err, "message", id) if translation != "" { return translation } } else { - m.logger.WithError(err).WithField("message", id).Error("translation error") + m.logger.Error("translation error", "error", err, "message", id) } return id } type I18NService struct { - logger *log.Logger + logger *slog.Logger bundle *i18n.Bundle catalog *MessageCatalog } @@ -369,7 +369,7 @@ func (s *I18NService) Localizer(languages string) *i18n.Localizer { return i18n.NewLocalizer(s.bundle, languages) } -func InitI18n(logger *log.Logger, languages []string) *I18NService { +func InitI18n(logger *slog.Logger, languages []string) *I18NService { bundle := i18n.NewBundle(language.English) bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) @@ -378,7 +378,7 @@ func InitI18n(logger *log.Logger, languages []string) *I18NService { bundleBytes, err := translations.Bundles.ReadFile(bundleName) if err != nil { - logger.WithField("bundle", bundleName).Warn("message bundle not found") + logger.Warn("message bundle not found", "bundle", bundleName) continue } @@ -391,7 +391,7 @@ func InitI18n(logger *log.Logger, languages []string) *I18NService { return &I18NService{logger: logger, bundle: bundle, catalog: catalog} } -func initMessageCatalog(logger *log.Logger) *MessageCatalog { +func initMessageCatalog(logger *slog.Logger) *MessageCatalog { messages := make(map[string]*i18n.Message) messages["ErrorTitle"] = &i18n.Message{ ID: "ErrorTitle", diff --git a/internal/services/security.go b/internal/services/security.go index ad0fd6d..e99d3d8 100644 --- a/internal/services/security.go +++ b/internal/services/security.go @@ -19,21 +19,25 @@ package services import ( "crypto/rand" - - log "github.com/sirupsen/logrus" + "errors" + "log/slog" ) -func GenerateKey(length int) []byte { +var errCouldNotGenerateKey = errors.New("could not generate key") + +func GenerateKey(length int) ([]byte, error) { key := make([]byte, length) read, err := rand.Read(key) if err != nil { - log.WithError(err).Fatal("could not generate key") + return nil, errCouldNotGenerateKey } if read != length { - log.WithFields(log.Fields{"read": read, "expected": length}).Fatal("read unexpected number of bytes") + slog.Error("read unexpected number of bytes", "read", read, "expected", length) + + return nil, errCouldNotGenerateKey } - return key + return key, nil }