Compare commits

..

No commits in common. "main" and "0.3.0" have entirely different histories.
main ... 0.3.0

28 changed files with 402 additions and 571 deletions

View file

@ -8,7 +8,7 @@ linters-settings:
const: const:
ORGANIZATION: CAcert Inc. ORGANIZATION: CAcert Inc.
template: |- template: |-
Copyright {{ ORGANIZATION }} Copyright {{ YEAR-RANGE }} {{ ORGANIZATION }}
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -47,7 +47,7 @@ linters:
- gofmt - gofmt
- goheader - goheader
- goimports - goimports
- mnd - gomnd
- gosec - gosec
- lll - lll
- makezero - makezero

View file

@ -66,26 +66,26 @@ internationalization (i18n) support.
The translation workflow needs the `go18n` binary which can be installed via The translation workflow needs the `go18n` binary which can be installed via
```shell ```
go install github.com/nicksnyder/go-i18n/v2/goi18n go install github.com/nicksnyder/go-i18n/v2/goi18n
``` ```
To extract new messages from the code run To extract new messages from the code run
```shell ```
goi18n extract -outdir translations . goi18n extract .
``` ```
Then use Then use
```shell ```
goi18n merge -outdir translations translations/active.*.toml goi18n merge active.*.toml
``` ```
to create TOML files for translation as `translate.<locale>.toml`. After to create TOML files for translation as `translate.<locale>.toml`. After
translating the messages run translating the messages run
```shell ```
goi18n merge active.*.toml translate.*.toml goi18n merge active.*.toml translate.*.toml
``` ```

View file

@ -5,16 +5,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### [0.4.0] - 2024-05-20
- HTML for client logo image is only rendered if a client application has a logo URL
- link to client information is only rendered if a client application has a client URL
- use Consent and Deny buttons instead of a checkbox when asking for consent
- update dependencies
- switch from logrus to slog
### Fixed
- fix subject handling for login requests
## [0.3.0] - 2023-08-07 ## [0.3.0] - 2023-08-07
### Changed ### Changed
- use a session to transport data from the login to the consent screens - use a session to transport data from the login to the consent screens

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2020-2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -24,7 +24,6 @@ import (
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@ -37,6 +36,7 @@ import (
"github.com/knadh/koanf/parsers/toml" "github.com/knadh/koanf/parsers/toml"
"github.com/knadh/koanf/providers/confmap" "github.com/knadh/koanf/providers/confmap"
hydra "github.com/ory/hydra-client-go/v2" 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/handlers"
"code.cacert.org/cacert/oidc-idp/internal/services" "code.cacert.org/cacert/oidc-idp/internal/services"
@ -55,8 +55,6 @@ const (
sessionKeyLength = 32 sessionKeyLength = 32
sessionAuthKeyLength = 64 sessionAuthKeyLength = 64
minCSRFKeyLength = 32
) )
var ( var (
@ -66,61 +64,42 @@ var (
) )
func main() { func main() {
var ( logger := log.New()
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) config, err := services.ConfigureApplication(logger, "IDP", services.DefaultConfig)
if err != nil { if err != nil {
logger.Error("error loading configuration", "err", err) logger.WithError(err).Fatal("error loading configuration")
os.Exit(1)
} }
if level := config.Bytes("log.level"); level != nil { if level := config.String("log.level"); level != "" {
err := logLevel.UnmarshalText(level) logLevel, err := log.ParseLevel(level)
if err != nil { if err != nil {
logger.Error("could not parse log level", "error", err) logger.WithError(err).Fatal("could not parse log level")
os.Exit(1)
} }
slog.SetLogLoggerLevel(logLevel.Level()) logger.SetLevel(logLevel)
} }
if config.Bool("log.json") { if config.Bool("log.json") {
logHandler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: logLevel}) logger.SetFormatter(&log.JSONFormatter{})
logger = slog.New(logHandler)
slog.SetDefault(logger)
} }
logger.Info("Starting CAcert OpenID Connect Identity Provider", logger.WithFields(log.Fields{
"version", version, "commit", commit, "date", date, "version": version, "commit": commit, "date": date,
) }).Info("Starting CAcert OpenID Connect Identity Provider")
logger.Info("Server is starting") logger.Infoln("Server is starting")
i18nService := services.InitI18n(logger, config.Strings("i18n.languages")) i18nService := services.InitI18n(logger, config.Strings("i18n.languages"))
if err = i18nService.AddMessages(); err != nil { if err = i18nService.AddMessages(); err != nil {
logger.Error("could not add messages for i18n", "error", err) logger.WithError(err).Fatal("could not add messages for i18n")
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) services.InitSessionStore(sessionAuthKey, sessionEncKey)
clientTransport, err := configureAdminClient(config) clientTransport, err := configureAdminClient(config)
if err != nil { if err != nil {
logger.Error("could not configure Hydra admin client", "error", err) logger.WithError(err).Fatal("could not configure Hydra admin client")
os.Exit(1)
} }
tc := handlers.PopulateTemplateCache() tc := handlers.PopulateTemplateCache()
@ -128,12 +107,12 @@ func main() {
needsAuth := handlers.NewAuthMiddleware(logger, tc, i18nService).NeedsAuth needsAuth := handlers.NewAuthMiddleware(logger, tc, i18nService).NeedsAuth
indexHandler := handlers.NewIndex(logger, tc, i18nService) indexHandler := handlers.NewIndex(logger, tc, i18nService)
manageConsentHandler := handlers.NewManageConsent(logger, tc, i18nService, clientTransport.OAuth2API) manageConsentHandler := handlers.NewManageConsent(logger, tc, i18nService, clientTransport.OAuth2Api)
revokeConsentHandler := handlers.NewRevokeConsent(logger, tc, i18nService, clientTransport.OAuth2API) revokeConsentHandler := handlers.NewRevokeConsent(logger, tc, i18nService, clientTransport.OAuth2Api)
loginHandler := handlers.NewLoginHandler(logger, tc, i18nService, clientTransport.OAuth2API) loginHandler := handlers.NewLoginHandler(logger, tc, i18nService, clientTransport.OAuth2Api)
consentHandler := handlers.NewConsentHandler(logger, tc, i18nService, clientTransport.OAuth2API) consentHandler := handlers.NewConsentHandler(logger, tc, i18nService, clientTransport.OAuth2Api)
logoutHandler := handlers.NewLogout(logger, clientTransport.OAuth2API) logoutHandler := handlers.NewLogout(logger, clientTransport.OAuth2Api)
logoutSuccessHandler := handlers.NewLogoutSuccess(logger, tc, i18nService) logoutSuccessHandler := handlers.NewLogoutSuccess(logger, tc, i18nService)
errorHandler := handlers.NewErrorHandler(logger, i18nService) errorHandler := handlers.NewErrorHandler(logger, i18nService)
@ -153,6 +132,11 @@ func main() {
router.Handle("/css/", staticFiles) router.Handle("/css/", staticFiles)
router.Handle("/js/", 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 { nextRequestID := func() string {
return fmt.Sprintf("%d", time.Now().UnixNano()) return fmt.Sprintf("%d", time.Now().UnixNano())
} }
@ -168,17 +152,12 @@ func main() {
errorMiddleware, err := handlers.ErrorHandling(logger, tc, i18nService) errorMiddleware, err := handlers.ErrorHandling(logger, tc, i18nService)
if err != nil { if err != nil {
logger.Error("could not initialize request error handling", "error", err) logger.WithError(err).Fatal("could not initialize request error handling")
os.Exit(1)
} }
handlerChain := tracing(logging(hsts(errorMiddleware(csrfProtect(router))))) handlerChain := tracing(logging(hsts(errorMiddleware(csrfProtect(router)))))
err = startServer(logger, config, handlerChain) 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) { func configureAdminClient(config *koanf.Koanf) (*hydra.APIClient, error) {
@ -218,7 +197,7 @@ func configureAdminClient(config *koanf.Koanf) (*hydra.APIClient, error) {
return c, nil return c, nil
} }
func startServer(logger *slog.Logger, config *koanf.Koanf, handlerChain http.Handler) error { func startServer(logger *log.Logger, config *koanf.Koanf, handlerChain http.Handler) {
clientCertificateCAFile := config.MustString("security.client.ca-file") clientCertificateCAFile := config.MustString("security.client.ca-file")
serverBindAddress := config.String("server.bind_address") serverBindAddress := config.String("server.bind_address")
serverName := config.String("server.name") serverName := config.String("server.name")
@ -228,7 +207,7 @@ func startServer(logger *slog.Logger, config *koanf.Koanf, handlerChain http.Han
pemBytes, err := os.ReadFile(clientCertificateCAFile) pemBytes, err := os.ReadFile(clientCertificateCAFile)
if err != nil { if err != nil {
return fmt.Errorf("could not load client CA certificates: %w", err) logger.WithError(err).Fatal("could not load client CA certificates")
} }
clientCertPool.AppendCertsFromPEM(pemBytes) clientCertPool.AppendCertsFromPEM(pemBytes)
@ -254,7 +233,7 @@ func startServer(logger *slog.Logger, config *koanf.Koanf, handlerChain http.Han
go func() { go func() {
<-quit <-quit
logger.Info("Server is shutting down...") logger.Infoln("Server is shutting down...")
atomic.StoreInt32(&handlers.Healthy, 0) atomic.StoreInt32(&handlers.Healthy, 0)
ctx, cancel := context.WithTimeout(context.Background(), ShutdownTimeout) ctx, cancel := context.WithTimeout(context.Background(), ShutdownTimeout)
@ -263,30 +242,27 @@ func startServer(logger *slog.Logger, config *koanf.Koanf, handlerChain http.Han
server.SetKeepAlivesEnabled(false) server.SetKeepAlivesEnabled(false)
if err := server.Shutdown(ctx); err != nil { if err := server.Shutdown(ctx); err != nil {
logger.Error("could not shutdown server gracefully", "error", err) logger.WithError(err).Fatal("Could not gracefully shutdown the server")
} }
close(done) close(done)
}() }()
logger.Info( logger.WithFields(log.Fields{
"Server is ready to handle requests", "address": server.Addr, "url": publicAddress(serverName, serverPort),
"address", server.Addr, "url", publicAddress(serverName, serverPort), }).Info("Server is ready to handle requests")
)
atomic.StoreInt32(&handlers.Healthy, 1) atomic.StoreInt32(&handlers.Healthy, 1)
if err := server.ListenAndServeTLS( if err := server.ListenAndServeTLS(
config.String("server.certificate"), config.String("server.key"), config.String("server.certificate"), config.String("server.key"),
); err != nil && !errors.Is(err, http.ErrServerClosed) { ); err != nil && !errors.Is(err, http.ErrServerClosed) {
logger.Error("could not listen on configured server address", "server_addr", server.Addr) logger.WithError(err).WithField(
"server_addr", server.Addr,
return fmt.Errorf("could not create listener: %w", err) ).Fatal("Could not listen on configured server address")
} }
<-done <-done
logger.Info("Server stopped") logger.Infoln("Server stopped")
return nil
} }
func publicAddress(serverName string, serverPort int) string { func publicAddress(serverName string, serverPort int) string {
@ -297,66 +273,42 @@ func publicAddress(serverName string, serverPort int) string {
return fmt.Sprintf("https://%s/", serverName) return fmt.Sprintf("https://%s/", serverName)
} }
func configureSessionParameters(config *koanf.Koanf) ([]byte, []byte, []byte, error) { //nolint:cyclop func configureSessionParameters(config *koanf.Koanf) ([]byte, []byte) {
sessionAuthKey, err := base64.StdEncoding.DecodeString(config.String("session.auth-key")) sessionAuthKey, err := base64.StdEncoding.DecodeString(config.String("session.auth-key"))
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("could not decode session auth key: %w", err) log.WithError(err).Fatal("could not decode session auth key")
} }
sessionEncKey, err := base64.StdEncoding.DecodeString(config.String("session.enc-key")) sessionEncKey, err := base64.StdEncoding.DecodeString(config.String("session.enc-key"))
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("could not decode session encryption key: %w", err) log.WithError(err).Fatal("could not decode session encryption key")
}
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 generated := false
if len(sessionAuthKey) != sessionAuthKeyLength { if len(sessionAuthKey) != sessionAuthKeyLength {
sessionAuthKey, err = services.GenerateKey(sessionAuthKeyLength) sessionAuthKey = services.GenerateKey(sessionAuthKeyLength)
if err != nil {
return nil, nil, nil, fmt.Errorf("could not generate session authentication key: %w", err)
}
generated = true generated = true
} }
if len(sessionEncKey) != sessionKeyLength { if len(sessionEncKey) != sessionKeyLength {
sessionEncKey, err = services.GenerateKey(sessionKeyLength) sessionEncKey = 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 generated = true
} }
if generated { if generated {
_ = config.Load(confmap.Provider(map[string]interface{}{ _ = config.Load(confmap.Provider(map[string]interface{}{
"session.auth-key": base64.StdEncoding.EncodeToString(sessionAuthKey), "session.auth-key": sessionAuthKey,
"session.enc-key": base64.StdEncoding.EncodeToString(sessionEncKey), "session.enc-key": sessionEncKey,
"security.csrf.key": base64.StdEncoding.EncodeToString(csrfKey),
}, "."), nil) }, "."), nil)
tomlData, err := config.Marshal(toml.Parser()) tomlData, err := config.Marshal(toml.Parser())
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("could not encode session config: %w", err) log.WithError(err).Fatal("could not encode session config")
} }
slog.Info("generated configuration values, put the following into your idp.toml") log.Infof("put the following in your resource_app.toml:\n%s", string(tomlData))
fmt.Printf("------\n%s------\n", string(tomlData)) //nolint:forbidigo
} }
return sessionAuthKey, sessionEncKey, csrfKey, nil return sessionAuthKey, sessionEncKey
} }

37
go.mod
View file

@ -1,29 +1,31 @@
module code.cacert.org/cacert/oidc-idp module code.cacert.org/cacert/oidc-idp
go 1.22 go 1.19
require ( require (
github.com/BurntSushi/toml v1.3.2 github.com/BurntSushi/toml v1.3.2
github.com/dustin/go-humanize v1.0.1 github.com/dustin/go-humanize v1.0.0
github.com/go-playground/form/v4 v4.2.1 github.com/go-playground/form/v4 v4.2.1
github.com/gorilla/csrf v1.7.2 github.com/gorilla/csrf v1.7.1
github.com/gorilla/sessions v1.2.2 github.com/gorilla/sessions v1.2.1
github.com/knadh/koanf v1.5.0 github.com/knadh/koanf v1.5.0
github.com/lestrrat-go/jwx v1.2.29 github.com/lestrrat-go/jwx v1.2.26
github.com/nicksnyder/go-i18n/v2 v2.4.0 github.com/nicksnyder/go-i18n/v2 v2.2.1
github.com/ory/hydra-client-go/v2 v2.2.0 github.com/ory/hydra-client-go/v2 v2.1.1
github.com/sirupsen/logrus v1.9.3
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/yuin/goldmark v1.7.1 github.com/yuin/goldmark v1.5.5
golang.org/x/text v0.15.0 golang.org/x/text v0.11.0
) )
require ( require (
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.2 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/blackmagic v1.0.1 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect github.com/lestrrat-go/option v1.0.1 // indirect
@ -32,7 +34,10 @@ require (
github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
golang.org/x/crypto v0.23.0 // indirect golang.org/x/crypto v0.11.0 // indirect
golang.org/x/oauth2 v0.20.0 // indirect golang.org/x/net v0.13.0 // indirect
golang.org/x/sys v0.20.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect
golang.org/x/sys v0.10.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
) )

89
go.sum
View file

@ -1,6 +1,7 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -38,12 +39,10 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@ -53,8 +52,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -90,6 +89,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -102,17 +103,14 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI= github.com/gorilla/csrf v1.7.1 h1:Ir3o2c1/Uzj6FBxMlAUB6SivgVMy1ONXwYgXn+/aHPE=
github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk= github.com/gorilla/csrf v1.7.1/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ=
@ -174,14 +172,14 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A= github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= github.com/lestrrat-go/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/jwx v1.2.29 h1:QT0utmUJ4/12rmsVQrJ3u55bycPkKqGYuGT4tyRhxSQ= github.com/lestrrat-go/jwx v1.2.26 h1:4iFo8FPRZGDYe1t19mQP0zTRqA7n8HnJ5lkIiDvJcB0=
github.com/lestrrat-go/jwx v1.2.29/go.mod h1:hU8k2l6WF0ncx20uQdOmik/Gjg6E3/wIRtXSNFeZuB8= github.com/lestrrat-go/jwx v1.2.26/go.mod h1:MaiCdGbn3/cckbOFSCluJlJMmp9dmZm5hDuIkx8ftpQ=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
@ -218,12 +216,12 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM= github.com/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYojM/PaA=
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4= github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0=
github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/ory/hydra-client-go/v2 v2.2.0 h1:g8hw0YQD5Us1aAgZj7OyBmBGSDwlnY9/2Pb/pQQq8YE= github.com/ory/hydra-client-go/v2 v2.1.1 h1:3JatU9uFbw5XhF3lgPCas1l1Kok2v5Mq1p26zZwGHNg=
github.com/ory/hydra-client-go/v2 v2.2.0/go.mod h1:h0DSI2kQA3S2fN7HyD8DNWcvbgDmYRSxfhwu/mSBhH8= github.com/ory/hydra-client-go/v2 v2.1.1/go.mod h1:IiIwChp/9wRvPoyFQblqPvg78uVishCCrV9+/M7Pl34=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
@ -262,13 +260,14 @@ 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.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 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.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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@ -277,15 +276,14 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U= github.com/yuin/goldmark v1.5.5 h1:IJznPe8wOzfIKETmMkd06F8nXkmlhaHqFRM9l1hAGsU=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.5.5/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
@ -298,10 +296,9 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -320,6 +317,7 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -333,12 +331,13 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY=
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -382,19 +381,17 @@ 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-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-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-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.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/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= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@ -402,11 +399,11 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -427,6 +424,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
@ -452,6 +451,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -22,12 +22,12 @@ import (
"context" "context"
"fmt" "fmt"
"html/template" "html/template"
"log/slog"
"net/http" "net/http"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/gorilla/sessions" "github.com/gorilla/sessions"
"github.com/nicksnyder/go-i18n/v2/i18n" "github.com/nicksnyder/go-i18n/v2/i18n"
log "github.com/sirupsen/logrus"
"code.cacert.org/cacert/oidc-idp/ui" "code.cacert.org/cacert/oidc-idp/ui"
@ -46,7 +46,7 @@ func GetSession(r *http.Request) (*sessions.Session, error) {
} }
type AuthMiddleware struct { type AuthMiddleware struct {
logger *slog.Logger logger *log.Logger
trans *services.I18NService trans *services.I18NService
templates TemplateCache templates TemplateCache
} }
@ -83,7 +83,7 @@ func GetAuthenticatedAddresses(r *http.Request) []string {
return nil return nil
} }
func NewAuthMiddleware(logger *slog.Logger, tc TemplateCache, trans *services.I18NService) *AuthMiddleware { func NewAuthMiddleware(logger *log.Logger, tc TemplateCache, trans *services.I18NService) *AuthMiddleware {
return &AuthMiddleware{logger: logger, trans: trans, templates: tc} return &AuthMiddleware{logger: logger, trans: trans, templates: tc}
} }
@ -104,13 +104,13 @@ const (
type TemplateCache map[templateName]*template.Template type TemplateCache map[templateName]*template.Template
func (c TemplateCache) render( func (c TemplateCache) render(
logger *slog.Logger, w http.ResponseWriter, name templateName, params map[string]interface{}, logger *log.Logger, w http.ResponseWriter, name templateName, params map[string]interface{},
) { ) {
rendered := bytes.NewBuffer(make([]byte, 0)) rendered := bytes.NewBuffer(make([]byte, 0))
err := c[name].Lookup("base").Execute(rendered, params) err := c[name].Lookup("base").Execute(rendered, params)
if err != nil { if err != nil {
logger.Error("template rendering failed", "error", err) logger.WithError(err).Error("template rendering failed")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
@ -166,7 +166,7 @@ func PopulateTemplateCache() TemplateCache {
} }
func renderNoEmailsInClientCertificate( func renderNoEmailsInClientCertificate(
logger *slog.Logger, logger *log.Logger,
templates TemplateCache, templates TemplateCache,
trans *services.I18NService, trans *services.I18NService,
w http.ResponseWriter, w http.ResponseWriter,
@ -179,7 +179,7 @@ func renderNoEmailsInClientCertificate(
"Explanation": msg("NoEmailsInClientCertificateExplanation", nil, localizer), "Explanation": msg("NoEmailsInClientCertificateExplanation", nil, localizer),
}) })
if err != nil { if err != nil {
logger.Error("template rendering failed", "error", err) logger.WithError(err).Error("template rendering failed")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2020-2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -22,7 +22,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"html/template" "html/template"
"log/slog"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@ -34,22 +33,23 @@ import (
"github.com/lestrrat-go/jwx/jwt/openid" "github.com/lestrrat-go/jwx/jwt/openid"
"github.com/nicksnyder/go-i18n/v2/i18n" "github.com/nicksnyder/go-i18n/v2/i18n"
client "github.com/ory/hydra-client-go/v2" 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/models"
"code.cacert.org/cacert/oidc-idp/internal/services" "code.cacert.org/cacert/oidc-idp/internal/services"
) )
type ConsentHandler struct { type ConsentHandler struct {
logger *slog.Logger logger *log.Logger
trans *services.I18NService trans *services.I18NService
adminClient client.OAuth2API adminClient client.OAuth2Api
templates TemplateCache templates TemplateCache
} }
type ConsentInformation struct { type ConsentInformation struct {
GrantedScopes []string `form:"scope"` GrantedScopes []string `form:"scope"`
SelectedClaims []string `form:"claims"` SelectedClaims []string `form:"claims"`
ConsentAction string `form:"consent"` ConsentChecked bool `form:"consent"`
} }
type UserInfo struct { type UserInfo struct {
@ -108,7 +108,7 @@ func (i *UserInfo) GetFullName() string {
func (h *ConsentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *ConsentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
challenge := r.URL.Query().Get("consent_challenge") challenge := r.URL.Query().Get("consent_challenge")
h.logger.Debug("received consent challenge", "consent_challenge", challenge) h.logger.WithField("consent_challenge", challenge).Debug("received consent challenge")
localizer := getLocalizer(h.trans, r) localizer := getLocalizer(h.trans, r)
@ -121,7 +121,7 @@ func (h *ConsentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
session, err := GetSession(r) session, err := GetSession(r)
if err != nil { if err != nil {
h.logger.Error("could get session for request", "error", err) h.logger.WithError(err).Error("could get session for request")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -149,7 +149,7 @@ func (h *ConsentHandler) handleGet(
if consentData.GetSkip() { if consentData.GetSkip() {
consentRequest, err := h.handleExistingConsent(consentData, requestedClaims, session) consentRequest, err := h.handleExistingConsent(consentData, requestedClaims, session)
if err != nil { if err != nil {
h.logger.Error("could not handle existing consent", "error", err) h.logger.WithError(err).Error("could not handle existing consent")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -158,7 +158,7 @@ func (h *ConsentHandler) handleGet(
err = h.acceptConsent(w, r, challenge, consentRequest) err = h.acceptConsent(w, r, challenge, consentRequest)
if err != nil { if err != nil {
h.logger.Error("could not accept consent", "error", err) h.logger.WithError(err).Error("could not accept consent")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
} }
@ -182,23 +182,23 @@ func (h *ConsentHandler) handlePost(
// validate input // validate input
decoder := form.NewDecoder() decoder := form.NewDecoder()
if err := decoder.Decode(&consentInfo, r.Form); err != nil { if err := decoder.Decode(&consentInfo, r.Form); err != nil {
h.logger.Error("could not decode consent form", "error", err) h.logger.WithError(err).Error("could not decode consent form")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
} }
if consentInfo.ConsentAction == "consent" { if consentInfo.ConsentChecked {
consentRequest, err := h.rememberNewConsent(consentData, consentInfo, requestedClaims, session) consentRequest, err := h.rememberNewConsent(consentData, consentInfo, requestedClaims, session)
if err != nil { if err != nil {
h.logger.Error("could not accept consent", "error", err) h.logger.WithError(err).Error("could not accept consent")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
} }
err = h.acceptConsent(w, r, challenge, consentRequest) err = h.acceptConsent(w, r, challenge, consentRequest)
if err != nil { if err != nil {
h.logger.Error("could not accept consent", "error", err) h.logger.WithError(err).Error("could not accept consent")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
} }
@ -210,7 +210,7 @@ func (h *ConsentHandler) handlePost(
r.Context(), r.Context(),
).ConsentChallenge(challenge).Execute() ).ConsentChallenge(challenge).Execute()
if err != nil { if err != nil {
h.logger.Error("reject consent request failed", "error", err) h.logger.WithError(err).Error("reject consent request failed")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
@ -218,10 +218,9 @@ func (h *ConsentHandler) handlePost(
defer func() { _ = response.Body.Close() }() defer func() { _ = response.Body.Close() }()
h.logger.Debug( h.logger.WithFields(
"received response for RejectOAuth2ConsentRequest", log.Fields{"response": response.Status, "reject_consent_request": consentRequest},
"response", response.Status, "reject_consent_request", consentRequest, ).Debug("received response for RejectOAuth2ConsentRequest")
)
w.Header().Add("Location", consentRequest.GetRedirectTo()) w.Header().Add("Location", consentRequest.GetRedirectTo())
w.WriteHeader(http.StatusFound) w.WriteHeader(http.StatusFound)
@ -267,10 +266,9 @@ func (h *ConsentHandler) acceptConsent(
defer func() { _ = response.Body.Close() }() defer func() { _ = response.Body.Close() }()
h.logger.Debug( h.logger.WithFields(
"received response for AcceptOAuth2ConsentRequest", log.Fields{"response": response.Status, "redirect_to": oAuth2RedirectTo},
"response", response.Status, "redirect_to", oAuth2RedirectTo, ).Debug("received response for AcceptOAuth2ConsentRequest")
)
w.Header().Add("Location", oAuth2RedirectTo.GetRedirectTo()) w.Header().Add("Location", oAuth2RedirectTo.GetRedirectTo())
w.WriteHeader(http.StatusFound) w.WriteHeader(http.StatusFound)
@ -307,7 +305,7 @@ func (h *ConsentHandler) getRequestedConsentInformation(challenge string, r *htt
r.Context(), r.Context(),
).ConsentChallenge(challenge).Execute() ).ConsentChallenge(challenge).Execute()
if err != nil { if err != nil {
h.logger.Error("error getting consent information", "error", err) h.logger.WithError(err).Error("error getting consent information")
if errorBucket := GetErrorBucket(r); errorBucket != nil { if errorBucket := GetErrorBucket(r); errorBucket != nil {
errorDetails := &ErrorDetails{ errorDetails := &ErrorDetails{
@ -323,10 +321,9 @@ func (h *ConsentHandler) getRequestedConsentInformation(challenge string, r *htt
defer func() { _ = response.Body.Close() }() defer func() { _ = response.Body.Close() }()
h.logger.Debug( h.logger.WithFields(
"response for GetOAuth2ConsentRequest", log.Fields{"response": response.Status, "consent_request": consentRequest},
"response", response.Status, "consent_request", consentRequest, ).Debug("response for GetOAuth2ConsentRequest")
)
var requestedClaims models.OIDCClaimsRequest var requestedClaims models.OIDCClaimsRequest
@ -334,21 +331,18 @@ func (h *ConsentHandler) getRequestedConsentInformation(challenge string, r *htt
requestURL, err := url.Parse(requestURLStr) requestURL, err := url.Parse(requestURLStr)
if err != nil { if err != nil {
h.logger.Warn( h.logger.WithError(err).WithField(
"could not parse original request URL", "request_url", requestURLStr,
"error", err, "request_url", requestURLStr, ).Warn("could not parse original request URL")
)
} else { } else {
claimsParameter := requestURL.Query().Get("claims") claimsParameter := requestURL.Query().Get("claims")
if claimsParameter != "" { if claimsParameter != "" {
decoder := json.NewDecoder(strings.NewReader(claimsParameter)) decoder := json.NewDecoder(strings.NewReader(claimsParameter))
err := decoder.Decode(&requestedClaims) err := decoder.Decode(&requestedClaims)
if err != nil { if err != nil {
h.logger.Warn( h.logger.WithError(err).WithField(
"ignoring claims request parameter that could not be decoded", "claims_parameter", claimsParameter,
"error", err, "claims_parameter", claimsParameter, ).Warn("ignoring claims request parameter that could not be decoded")
)
} }
} }
} }
@ -363,42 +357,36 @@ func (h *ConsentHandler) renderConsentForm(
claims *models.OIDCClaimsRequest, claims *models.OIDCClaimsRequest,
localizer *i18n.Localizer, localizer *i18n.Localizer,
) { ) {
trans := h.trans.LookupMessage trans := func(id string, values ...map[string]interface{}) string {
transMarkdown := func(id string, params map[string]interface{}, localizer *i18n.Localizer) template.HTML { if len(values) > 0 {
return template.HTML( //nolint:gosec return h.trans.LookupMessage(id, values[0], localizer)
h.trans.LookupMarkdownMessage(id, params, localizer), }
)
return h.trans.LookupMessage(id, nil, localizer)
} }
// render consent form // render consent form
oAuth2Client := consentRequest.Client oAuth2Client := consentRequest.Client
clientLogoURI := oAuth2Client.GetLogoUri()
clientName := template.HTMLEscaper(oAuth2Client.GetClientName())
clientURI := oAuth2Client.GetClientUri()
h.templates.render(h.logger, w, Consent, map[string]interface{}{ h.templates.render(h.logger, w, Consent, map[string]interface{}{
"Title": trans("TitleRequestConsent", nil, localizer), "Title": trans("TitleRequestConsent"),
csrf.TemplateTag: csrf.TemplateField(r), csrf.TemplateTag: csrf.TemplateField(r),
"errors": map[string]string{}, "errors": map[string]string{},
"LogoURI": clientLogoURI, "client": oAuth2Client,
"ClientName": clientName,
"requestedScope": h.mapRequestedScope(consentRequest.RequestedScope, localizer), "requestedScope": h.mapRequestedScope(consentRequest.RequestedScope, localizer),
"requestedClaims": h.mapRequestedClaims(claims, localizer), "requestedClaims": h.mapRequestedClaims(claims, localizer),
"ButtonTitleConsent": trans("ButtonTitleConsent", nil, localizer), "LabelSubmit": trans("LabelSubmit"),
"ButtonTitleDeny": trans("ButtonTitleDeny", nil, localizer), "LabelConsent": trans("LabelConsent"),
"HasMoreInformation": clientURI != "", "IntroMoreInformation": template.HTML( //nolint:gosec
"IntroMoreInformation": transMarkdown( trans("IntroConsentMoreInformation", map[string]interface{}{
"IntroConsentMoreInformation", map[string]interface{}{ "client": oAuth2Client.GetClientName(),
"client": clientName, "clientLink": oAuth2Client.GetClientUri(),
"clientLink": clientURI, })),
}, localizer), "ClaimsInformation": template.HTML( //nolint:gosec
"LabelConsent": transMarkdown("LabelConsent", nil, localizer), trans("ClaimsInformation", nil)),
"ClaimsInformation": transMarkdown( "IntroConsentRequested": template.HTML( //nolint:gosec
"ClaimsInformation", nil, localizer), trans("IntroConsentRequested", map[string]interface{}{
"IntroConsentRequested": transMarkdown( "client": oAuth2Client.GetClientName(),
"IntroConsentRequested", map[string]interface{}{ })),
"client": clientName,
}, localizer),
}) })
} }
@ -415,7 +403,7 @@ func (h *ConsentHandler) mapRequestedScope(
for _, scopeName := range scope { for _, scopeName := range scope {
if _, ok := supportedScopes[scopeName]; !ok { if _, ok := supportedScopes[scopeName]; !ok {
h.logger.Warn("ignoring unsupported scope", "scope", scopeName) h.logger.WithField("scope", scopeName).Warn("ignoring unsupported scope")
continue continue
} }
@ -424,7 +412,7 @@ func (h *ConsentHandler) mapRequestedScope(
DefaultMessage: supportedScopes[scopeName], DefaultMessage: supportedScopes[scopeName],
}) })
if err != nil { if err != nil {
h.logger.Warn("could not localize scope label", "error", err, "scope", scopeName) h.logger.WithError(err).WithField("scope", scopeName).Warn("could not localize scope label")
label = scopeName label = scopeName
} }
@ -450,14 +438,8 @@ func (h *ConsentHandler) mapRequestedClaims(
for _, claimElement := range []*models.ClaimElement{claims.GetUserInfo(), claims.GetIDToken()} { for _, claimElement := range []*models.ClaimElement{claims.GetUserInfo(), claims.GetIDToken()} {
if claimElement != nil { if claimElement != nil {
for k, v := range *claimElement { for k, v := range *claimElement {
if v == nil {
h.logger.Warn("claim element is nil", "key", k)
continue
}
if _, ok := supportedClaims[k]; !ok { if _, ok := supportedClaims[k]; !ok {
h.logger.Warn("ignoring unsupported claim", "claim", k) h.logger.WithField("claim", k).Warn("ignoring unsupported claim")
continue continue
} }
@ -466,7 +448,7 @@ func (h *ConsentHandler) mapRequestedClaims(
DefaultMessage: supportedClaims[k], DefaultMessage: supportedClaims[k],
}) })
if err != nil { if err != nil {
h.logger.Warn("could not localize claim label", "error", err, "claim", k) h.logger.WithError(err).WithField("claim", k).Warn("could not localize claim label")
label = k label = k
} }
@ -596,9 +578,9 @@ func (h *ConsentHandler) parseUserInfoClaims(
} }
if claim.IsEssential() { if claim.IsEssential() {
h.logger.Warn("handling for essential claim not implemented", "claim", claimName) h.logger.WithField("claim", claimName).Warn("handling for essential claim not implemented")
} else { } else {
h.logger.Warn("handling for claim not implemented", "claim", claimName) h.logger.WithField("claim", claimName).Warn("handling for claim not implemented")
} }
} }
@ -606,7 +588,7 @@ func (h *ConsentHandler) parseUserInfoClaims(
} }
func NewConsentHandler( func NewConsentHandler(
logger *slog.Logger, templateCache TemplateCache, trans *services.I18NService, adminClient client.OAuth2API, logger *log.Logger, templateCache TemplateCache, trans *services.I18NService, adminClient client.OAuth2Api,
) *ConsentHandler { ) *ConsentHandler {
return &ConsentHandler{ return &ConsentHandler{
logger: logger, logger: logger,

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2020-2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2020-2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -22,9 +22,10 @@ import (
"context" "context"
"fmt" "fmt"
"html/template" "html/template"
"log/slog"
"net/http" "net/http"
log "github.com/sirupsen/logrus"
"code.cacert.org/cacert/oidc-idp/internal/services" "code.cacert.org/cacert/oidc-idp/internal/services"
"code.cacert.org/cacert/oidc-idp/ui" "code.cacert.org/cacert/oidc-idp/ui"
) )
@ -43,7 +44,7 @@ type ErrorDetails struct {
} }
type ErrorBucket struct { type ErrorBucket struct {
logger *slog.Logger logger *log.Logger
trans *services.I18NService trans *services.I18NService
errorDetails *ErrorDetails errorDetails *ErrorDetails
templates TemplateCache templates TemplateCache
@ -117,7 +118,9 @@ func (w *errorResponseWriter) Write(content []byte) (int, error) {
} }
func ErrorHandling( func ErrorHandling(
logger *slog.Logger, templateCache TemplateCache, trans *services.I18NService, logger *log.Logger,
templateCache TemplateCache,
trans *services.I18NService,
) (func(http.Handler) http.Handler, error) { ) (func(http.Handler) http.Handler, error) {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -136,7 +139,7 @@ func ErrorHandling(
} }
type ErrorHandler struct { type ErrorHandler struct {
logger *slog.Logger logger *log.Logger
trans *services.I18NService trans *services.I18NService
template *template.Template template *template.Template
} }
@ -153,10 +156,10 @@ func (h *ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
errorName := r.URL.Query().Get("error") errorName := r.URL.Query().Get("error")
errorDescription := r.URL.Query().Get("error_description") errorDescription := r.URL.Query().Get("error_description")
h.logger.Debug( h.logger.WithFields(log.Fields{
"error from Hydra", "error_name": errorName,
"error_name", errorName, "error_description", errorDescription, "error_description": errorDescription,
) }).Debug("error from Hydra")
rendered := bytes.NewBuffer(make([]byte, 0)) rendered := bytes.NewBuffer(make([]byte, 0))
@ -171,7 +174,7 @@ func (h *ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
"ErrorMessage": errorDescription, "ErrorMessage": errorDescription,
}) })
if err != nil { if err != nil {
h.logger.Error("template rendering failed", "error", err) h.logger.WithError(err).Error("template rendering failed")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
@ -183,7 +186,7 @@ func (h *ErrorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write(rendered.Bytes()) _, _ = w.Write(rendered.Bytes())
} }
func NewErrorHandler(logger *slog.Logger, trans *services.I18NService) *ErrorHandler { func NewErrorHandler(logger *log.Logger, trans *services.I18NService) *ErrorHandler {
return &ErrorHandler{ return &ErrorHandler{
logger: logger, logger: logger,
trans: trans, trans: trans,

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2020-2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -23,12 +23,12 @@ import (
"fmt" "fmt"
"html/template" "html/template"
"io" "io"
"log/slog"
"net/http" "net/http"
"github.com/gorilla/csrf" "github.com/gorilla/csrf"
"github.com/nicksnyder/go-i18n/v2/i18n" "github.com/nicksnyder/go-i18n/v2/i18n"
client "github.com/ory/hydra-client-go/v2" client "github.com/ory/hydra-client-go/v2"
log "github.com/sirupsen/logrus"
"code.cacert.org/cacert/oidc-idp/internal/services" "code.cacert.org/cacert/oidc-idp/internal/services"
) )
@ -49,9 +49,9 @@ const (
) )
type LoginHandler struct { type LoginHandler struct {
logger *slog.Logger logger *log.Logger
trans *services.I18NService trans *services.I18NService
adminClient client.OAuth2API adminClient client.OAuth2Api
templates TemplateCache templates TemplateCache
} }
@ -72,7 +72,7 @@ func (h *LoginHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
h.logger.Debug("received login challenge", "challenge", challenge) h.logger.WithField("challenge", challenge).Debug("received login challenge")
certFullName, certEmails := getDataFromClientCert(h.logger, r) certFullName, certEmails := getDataFromClientCert(h.logger, r)
@ -100,48 +100,22 @@ func (h *LoginHandler) handleGet(
r.Context(), r.Context(),
).LoginChallenge(challenge).Execute() ).LoginChallenge(challenge).Execute()
if err != nil { if err != nil {
h.logger.Warn( h.logger.WithError(err).WithField(
"could not get login request for challenge", "challenge", challenge,
"error", err, "challenge", challenge, ).Warn("could not get login request for challenge")
)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
} }
usableEmails := certEmails
defer func() { _ = response.Body.Close() }() defer func() { _ = response.Body.Close() }()
h.logger.Debug( h.logger.WithFields(
"got response for GetOAuth2LoginRequest", log.Fields{"response": response.Status, "login_request": oAuth2LoginRequest},
"response", response.Status, "login_request", oAuth2LoginRequest, ).Debug("got response for GetOAuth2LoginRequest")
)
if subject, ok := oAuth2LoginRequest.GetSubjectOk(); ok && *subject != "" { h.renderRequestForClientCert(w, r, certEmails, localizer, oAuth2LoginRequest)
h.logger.Info("oauth2LoginRequest expects subject", "subject", *subject)
subjectInCert := false
for _, email := range certEmails {
if *subject == email {
subjectInCert = true
break
}
}
if !subjectInCert {
h.rejectLoginMissingSubject(w, r, challenge, localizer, *subject)
return
}
usableEmails = []string{*subject}
}
h.renderRequestForClientCert(w, r, usableEmails, localizer, oAuth2LoginRequest)
} }
type FlashMessage struct { type FlashMessage struct {
@ -177,14 +151,14 @@ func (h *LoginHandler) handlePost(
} }
// perform certificate auth // perform certificate auth
h.logger.Info( h.logger.WithFields(log.Fields{
"will perform certificate authentication", "emails": certEmails,
"emails", certEmails, "full_name", certFullName, "full_name": certFullName,
) }).Info("will perform certificate authentication")
userID, err := h.performCertificateLogin(certEmails, r) userID, err := h.performCertificateLogin(certEmails, r)
if err != nil { if err != nil {
h.logger.Error("could not perform certificate login", "error", err) h.logger.WithError(err).Error("could not perform certificate login")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
@ -192,7 +166,7 @@ func (h *LoginHandler) handlePost(
session, err := GetSession(r) session, err := GetSession(r)
if err != nil { if err != nil {
h.logger.Error("could not perform certificate login", "error", err) h.logger.WithError(err).Error("could not perform certificate login")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
@ -204,7 +178,7 @@ func (h *LoginHandler) handlePost(
session.Options.Secure = true session.Options.Secure = true
if err = session.Save(r, w); err != nil { if err = session.Save(r, w); err != nil {
h.logger.Error("could not save session", "error", err) h.logger.WithError(err).Error("could not save session")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
@ -220,8 +194,9 @@ func (h *LoginHandler) handlePost(
r.Context(), r.Context(),
).LoginChallenge(challenge).AcceptOAuth2LoginRequest(*acceptRequest).Execute() ).LoginChallenge(challenge).AcceptOAuth2LoginRequest(*acceptRequest).Execute()
if err != nil { if err != nil {
h.logger.Error("error getting login request", "error", err) h.logger.WithError(err).Error("error getting login request")
// h.fillAcceptLoginRequestErrorBucket(r, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
@ -229,13 +204,13 @@ func (h *LoginHandler) handlePost(
defer func() { _ = response.Body.Close() }() defer func() { _ = response.Body.Close() }()
h.logger.Debug("got response for AcceptOAuth2LoginRequest", h.logger.WithFields(
"response", response.Status, "accept_login_request", loginRequest, log.Fields{"response": response.Status, "accept_login_request": loginRequest},
) ).Debug("got response for AcceptOAuth2LoginRequest")
if h.logger.Enabled(r.Context(), slog.LevelDebug) { if h.logger.IsLevelEnabled(log.TraceLevel) {
if rb, err := io.ReadAll(response.Body); err == nil { if rb, err := io.ReadAll(response.Body); err == nil {
h.logger.Debug("response body from Hydra", "response_body", rb) h.logger.WithField("response_body", rb).Trace("response body from Hydra")
} }
} }
@ -243,6 +218,36 @@ func (h *LoginHandler) handlePost(
w.WriteHeader(http.StatusFound) w.WriteHeader(http.StatusFound)
} }
/*
func (h *LoginHandler) fillAcceptLoginRequestErrorBucket(r *http.Request, err error) {
if errorBucket := GetErrorBucket(r); errorBucket != nil {
var (
errorDetails *ErrorDetails
acceptLoginRequestNotFound *client.AcceptLoginRequestNotFound
)
if errors.As(err, &acceptLoginRequestNotFound) {
payload := acceptLoginRequestNotFound.GetPayload()
errorDetails = &ErrorDetails{
ErrorMessage: payload.Error,
ErrorDetails: []string{payload.ErrorDescription},
}
if acceptLoginRequestNotFound.Payload.StatusCode != 0 {
errorDetails.ErrorCode = strconv.Itoa(int(payload.StatusCode))
}
} else {
errorDetails = &ErrorDetails{
ErrorMessage: "could not accept login",
ErrorDetails: []string{err.Error()},
}
}
errorBucket.AddError(errorDetails)
}
}
*/
func (h *LoginHandler) rejectLogin( func (h *LoginHandler) rejectLogin(
w http.ResponseWriter, w http.ResponseWriter,
r *http.Request, r *http.Request,
@ -258,7 +263,7 @@ func (h *LoginHandler) rejectLogin(
r.Context(), r.Context(),
).LoginChallenge(challenge).RejectOAuth2Request(*rejectRequest).Execute() ).LoginChallenge(challenge).RejectOAuth2Request(*rejectRequest).Execute()
if err != nil { if err != nil {
h.logger.Error("error sending reject login request", "error", err) h.logger.WithError(err).Error("error getting reject login request")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
@ -266,44 +271,9 @@ func (h *LoginHandler) rejectLogin(
defer func() { _ = response.Body.Close() }() defer func() { _ = response.Body.Close() }()
h.logger.DebugContext( h.logger.WithFields(
r.Context(), log.Fields{"response": response.Status, "reject_login_request": rejectLoginRequest},
"got response for RejectOAuth2LoginRequest", ).Debug("go response for RejectOAuth2LoginRequest")
"response", response.Status, "reject_login_request", rejectLoginRequest,
)
w.Header().Set("Location", rejectLoginRequest.GetRedirectTo())
w.WriteHeader(http.StatusFound)
}
func (h *LoginHandler) rejectLoginMissingSubject(
w http.ResponseWriter, r *http.Request, challenge string, localizer *i18n.Localizer, subject string,
) {
rejectRequest := client.NewRejectOAuth2RequestWithDefaults()
rejectRequest.SetErrorDescription(h.trans.LookupMessage(
"LoginDeniedSubjectMissing", map[string]interface{}{"Subject": subject}, localizer),
)
rejectRequest.SetErrorHint(h.trans.LookupMessage("HintChooseDifferentClientCertificate", nil, localizer))
rejectRequest.SetStatusCode(http.StatusForbidden)
rejectLoginRequest, response, err := h.adminClient.RejectOAuth2LoginRequest(
r.Context()).LoginChallenge(challenge).RejectOAuth2Request(
*rejectRequest,
).Execute()
if err != nil {
h.logger.Error("error sending reject login request", "error", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
defer func() { _ = response.Body.Close() }()
h.logger.DebugContext(
r.Context(),
"got response for RejectOAuth2LoginRequest",
"response", response.Status, "reject_login_request", rejectLoginRequest,
)
w.Header().Set("Location", rejectLoginRequest.GetRedirectTo()) w.Header().Set("Location", rejectLoginRequest.GetRedirectTo())
w.WriteHeader(http.StatusFound) w.WriteHeader(http.StatusFound)
@ -338,7 +308,7 @@ func (h *LoginHandler) renderRequestForClientCert(
"FlashMessage": r.Context().Value(ctxKeyMessage), "FlashMessage": r.Context().Value(ctxKeyMessage),
}) })
if err != nil { if err != nil {
h.logger.Error("template rendering failed", "error", err) h.logger.WithError(err).Error("template rendering failed")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
@ -370,7 +340,7 @@ func (h *LoginHandler) renderNoChallengeInRequest(w http.ResponseWriter, localiz
)), )),
}) })
if err != nil { if err != nil {
h.logger.Error("template rendering failed", "error", err) h.logger.WithError(err).Error("template rendering failed")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
@ -378,7 +348,7 @@ func (h *LoginHandler) renderNoChallengeInRequest(w http.ResponseWriter, localiz
} }
func NewLoginHandler( func NewLoginHandler(
logger *slog.Logger, tc TemplateCache, trans *services.I18NService, adminClient client.OAuth2API, logger *log.Logger, tc TemplateCache, trans *services.I18NService, adminClient client.OAuth2Api,
) *LoginHandler { ) *LoginHandler {
return &LoginHandler{ return &LoginHandler{
logger: logger, logger: logger,

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2020-2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -19,28 +19,28 @@ package handlers
import ( import (
"html/template" "html/template"
"log/slog"
"net/http" "net/http"
client "github.com/ory/hydra-client-go/v2" client "github.com/ory/hydra-client-go/v2"
log "github.com/sirupsen/logrus"
"code.cacert.org/cacert/oidc-idp/internal/services" "code.cacert.org/cacert/oidc-idp/internal/services"
) )
type LogoutHandler struct { type LogoutHandler struct {
adminClient client.OAuth2API adminClient client.OAuth2Api
logger *slog.Logger logger *log.Logger
} }
func (h *LogoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *LogoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
challenge := r.URL.Query().Get("logout_challenge") challenge := r.URL.Query().Get("logout_challenge")
h.logger.Debug("received logout challenge", "challenge", challenge) h.logger.WithField("challenge", challenge).Debug("received logout challenge")
logoutRequest, response, err := h.adminClient.GetOAuth2LogoutRequest( logoutRequest, response, err := h.adminClient.GetOAuth2LogoutRequest(
r.Context(), r.Context(),
).LogoutChallenge(challenge).Execute() ).LogoutChallenge(challenge).Execute()
if err != nil { if err != nil {
h.logger.Error("error getting logout requests", "error", err) h.logger.WithError(err).Error("error getting logout requests")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
@ -48,31 +48,29 @@ func (h *LogoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
defer func() { _ = response.Body.Close() }() defer func() { _ = response.Body.Close() }()
h.logger.Debug( h.logger.WithFields(
"got response for GetOAuth2LogoutRequest", log.Fields{"response": response.Status, "logout_request": logoutRequest},
"response", response.Status, "logout_request", logoutRequest, ).Debug("got response for GetOAuth2LogoutRequest")
)
acceptLogoutRequest, response, err := h.adminClient.AcceptOAuth2LogoutRequest( acceptLogoutRequest, response, err := h.adminClient.AcceptOAuth2LogoutRequest(
r.Context(), r.Context(),
).LogoutChallenge(challenge).Execute() ).LogoutChallenge(challenge).Execute()
if err != nil { if err != nil {
h.logger.Error("accept logout request failed", "error", err) h.logger.WithError(err).Error("accept logout request failed")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
} }
defer func() { _ = response.Body.Close() }() defer func() { _ = response.Body.Close() }()
h.logger.Debug( h.logger.WithFields(
"got response for AcceptOAuth2LogoutRequest", log.Fields{"response": response.Status, "accept_logout_request": acceptLogoutRequest},
"response", response.Status, "accept_logout_request", acceptLogoutRequest, ).Debug("got response for AcceptOAuth2LogoutRequest")
)
w.Header().Set("Location", acceptLogoutRequest.GetRedirectTo()) w.Header().Set("Location", acceptLogoutRequest.GetRedirectTo())
w.WriteHeader(http.StatusFound) w.WriteHeader(http.StatusFound)
} }
func NewLogout(logger *slog.Logger, adminClient client.OAuth2API) *LogoutHandler { func NewLogout(logger *log.Logger, adminClient client.OAuth2Api) *LogoutHandler {
return &LogoutHandler{ return &LogoutHandler{
logger: logger, logger: logger,
adminClient: adminClient, adminClient: adminClient,
@ -80,7 +78,7 @@ func NewLogout(logger *slog.Logger, adminClient client.OAuth2API) *LogoutHandler
} }
type LogoutSuccessHandler struct { type LogoutSuccessHandler struct {
logger *slog.Logger logger *log.Logger
trans *services.I18NService trans *services.I18NService
templates TemplateCache templates TemplateCache
} }
@ -105,7 +103,9 @@ func (h *LogoutSuccessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
} }
func NewLogoutSuccess( func NewLogoutSuccess(
logger *slog.Logger, templateCache TemplateCache, trans *services.I18NService, logger *log.Logger,
templateCache TemplateCache,
trans *services.I18NService,
) *LogoutSuccessHandler { ) *LogoutSuccessHandler {
return &LogoutSuccessHandler{logger: logger, trans: trans, templates: templateCache} return &LogoutSuccessHandler{logger: logger, trans: trans, templates: templateCache}
} }

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -22,7 +22,6 @@ import (
"fmt" "fmt"
"html/template" "html/template"
"io" "io"
"log/slog"
"net/http" "net/http"
"sort" "sort"
"strings" "strings"
@ -31,12 +30,13 @@ import (
"github.com/gorilla/csrf" "github.com/gorilla/csrf"
"github.com/nicksnyder/go-i18n/v2/i18n" "github.com/nicksnyder/go-i18n/v2/i18n"
client "github.com/ory/hydra-client-go/v2" client "github.com/ory/hydra-client-go/v2"
log "github.com/sirupsen/logrus"
"code.cacert.org/cacert/oidc-idp/internal/services" "code.cacert.org/cacert/oidc-idp/internal/services"
) )
type IndexHandler struct { type IndexHandler struct {
logger *slog.Logger logger *log.Logger
trans *services.I18NService trans *services.I18NService
templates TemplateCache templates TemplateCache
} }
@ -66,14 +66,14 @@ func (h *IndexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}) })
} }
func NewIndex(logger *slog.Logger, templateCache TemplateCache, trans *services.I18NService) *IndexHandler { func NewIndex(logger *log.Logger, templateCache TemplateCache, trans *services.I18NService) *IndexHandler {
return &IndexHandler{logger: logger, trans: trans, templates: templateCache} return &IndexHandler{logger: logger, trans: trans, templates: templateCache}
} }
type ManageConsentHandler struct { type ManageConsentHandler struct {
logger *slog.Logger logger *log.Logger
trans *services.I18NService trans *services.I18NService
adminAPI client.OAuth2API adminAPI client.OAuth2Api
templates TemplateCache templates TemplateCache
} }
@ -107,15 +107,13 @@ func (h *ManageConsentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
requestSession, err := GetSession(r) requestSession, err := GetSession(r)
if err != nil { if err != nil {
h.logger.Error("could not get session for request", "error", err) h.logger.WithError(err).Error("could not get session for request")
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
} }
h.logger.Debug("got session for request", "new_session", requestSession.IsNew)
h.templates.render(h.logger, w, ManageConsent, map[string]interface{}{ h.templates.render(h.logger, w, ManageConsent, map[string]interface{}{
"Title": h.trans.LookupMessage("ManageConsentTitle", nil, localizer), "Title": h.trans.LookupMessage("ManageConsentTitle", nil, localizer),
"Description": template.HTML(h.trans.LookupMarkdownMessage( //nolint:gosec "Description": template.HTML(h.trans.LookupMarkdownMessage( //nolint:gosec
@ -204,7 +202,7 @@ func (h *ManageConsentHandler) getConsentSessions(
) ([]client.OAuth2ConsentSession, bool) { ) ([]client.OAuth2ConsentSession, bool) {
sessions, response, err := h.adminAPI.ListOAuth2ConsentSessions(r.Context()).Subject(subject).Execute() sessions, response, err := h.adminAPI.ListOAuth2ConsentSessions(r.Context()).Subject(subject).Execute()
if err != nil { if err != nil {
h.logger.Error("error getting consent session list", "error", err) h.logger.WithError(err).Error("error getting consent session list")
// h.fillAcceptLoginRequestErrorBucket(r, err) // h.fillAcceptLoginRequestErrorBucket(r, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -214,14 +212,13 @@ func (h *ManageConsentHandler) getConsentSessions(
defer func(response *http.Response) { _ = response.Body.Close() }(response) defer func(response *http.Response) { _ = response.Body.Close() }(response)
h.logger.Debug( h.logger.WithFields(
"got response for AcceptOAuth2LoginRequest", log.Fields{"response": response.Status, "consent_sessions": sessions},
"response", response.Status, "consent_sessions", sessions, ).Debug("got response for AcceptOAuth2LoginRequest")
)
if h.logger.Enabled(r.Context(), slog.LevelDebug) { if h.logger.IsLevelEnabled(log.TraceLevel) {
if rb, err := io.ReadAll(response.Body); err == nil { if rb, err := io.ReadAll(response.Body); err == nil {
h.logger.Debug("response body from Hydra", "response_body", rb) h.logger.WithField("response_body", rb).Trace("response body from Hydra")
} }
} }
@ -229,15 +226,15 @@ func (h *ManageConsentHandler) getConsentSessions(
} }
func NewManageConsent( func NewManageConsent(
logger *slog.Logger, tc TemplateCache, trans *services.I18NService, adminAPI client.OAuth2API, logger *log.Logger, tc TemplateCache, trans *services.I18NService, adminAPI client.OAuth2Api,
) *ManageConsentHandler { ) *ManageConsentHandler {
return &ManageConsentHandler{logger: logger, trans: trans, adminAPI: adminAPI, templates: tc} return &ManageConsentHandler{logger: logger, trans: trans, adminAPI: adminAPI, templates: tc}
} }
type RevokeConsentHandler struct { type RevokeConsentHandler struct {
logger *slog.Logger logger *log.Logger
trans *services.I18NService trans *services.I18NService
adminAPI client.OAuth2API adminAPI client.OAuth2Api
templates TemplateCache templates TemplateCache
} }
@ -275,7 +272,7 @@ func (h *RevokeConsentHandler) handleGet(
) { ) {
clientApp, found, err := h.getClient(r.Context(), clientID) clientApp, found, err := h.getClient(r.Context(), clientID)
if err != nil { if err != nil {
h.logger.Error("could not get client", "error", err) h.logger.WithError(err).Error("could not get client")
http.Error( http.Error(
w, h.trans.LookupHTTPErrorMessage(http.StatusInternalServerError, localizer), w, h.trans.LookupHTTPErrorMessage(http.StatusInternalServerError, localizer),
@ -311,7 +308,7 @@ func (h *RevokeConsentHandler) handlePost(
) { ) {
err := h.revokeConsent(r.Context(), clientID, subject) err := h.revokeConsent(r.Context(), clientID, subject)
if err != nil { if err != nil {
h.logger.Error("could not revoke consent", "error", err) h.logger.WithError(err).Error("could not revoke consent")
http.Error( http.Error(
w, h.trans.LookupHTTPErrorMessage(http.StatusInternalServerError, localizer), w, h.trans.LookupHTTPErrorMessage(http.StatusInternalServerError, localizer),
@ -332,14 +329,13 @@ func (h *RevokeConsentHandler) getClient(ctx context.Context, clientID string) (
defer func(response *http.Response) { _ = response.Body.Close() }(response) defer func(response *http.Response) { _ = response.Body.Close() }(response)
h.logger.Debug( h.logger.WithFields(
"got response for GetOAuth2Client", log.Fields{"response": response.Status, "client_app": clientApp},
"response", response.Status, "client_app", clientApp, ).Debug("got response for GetOAuth2Client")
)
if h.logger.Enabled(ctx, slog.LevelDebug) { if h.logger.IsLevelEnabled(log.TraceLevel) {
if rb, err := io.ReadAll(response.Body); err == nil { if rb, err := io.ReadAll(response.Body); err == nil {
h.logger.Debug("response body from Hydra", "response_body", rb) h.logger.WithField("response_body", rb).Trace("response body from Hydra")
} }
} }
@ -354,11 +350,13 @@ func (h *RevokeConsentHandler) revokeConsent(ctx context.Context, clientID, subj
defer func(response *http.Response) { _ = response.Body.Close() }(response) defer func(response *http.Response) { _ = response.Body.Close() }(response)
h.logger.Debug("got response for RevokeOAuth2ConsentSessions", "response", response.Status) h.logger.WithFields(
log.Fields{"response": response.Status},
).Debug("got response for RevokeOAuth2ConsentSessions")
if h.logger.Enabled(ctx, slog.LevelDebug) { if h.logger.IsLevelEnabled(log.TraceLevel) {
if rb, err := io.ReadAll(response.Body); err == nil { if rb, err := io.ReadAll(response.Body); err == nil {
h.logger.Debug("response body from Hydra", "response_body", rb) h.logger.WithField("response_body", rb).Trace("response body from Hydra")
} }
} }
@ -366,7 +364,7 @@ func (h *RevokeConsentHandler) revokeConsent(ctx context.Context, clientID, subj
} }
func NewRevokeConsent( func NewRevokeConsent(
logger *slog.Logger, tc TemplateCache, trans *services.I18NService, adminAPI client.OAuth2API, logger *log.Logger, tc TemplateCache, trans *services.I18NService, adminAPI client.OAuth2Api,
) *RevokeConsentHandler { ) *RevokeConsentHandler {
return &RevokeConsentHandler{logger: logger, trans: trans, adminAPI: adminAPI, templates: tc} return &RevokeConsentHandler{logger: logger, trans: trans, adminAPI: adminAPI, templates: tc}
} }

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2020-2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -20,9 +20,10 @@ package handlers
import ( import (
"context" "context"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"sync/atomic" "sync/atomic"
log "github.com/sirupsen/logrus"
) )
type key int type key int
@ -53,7 +54,7 @@ func (sci *statusCodeInterceptor) Write(content []byte) (int, error) {
return count, nil return count, nil
} }
func Logging(logger *slog.Logger) func(http.Handler) http.Handler { func Logging(logger *log.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
interceptor := &statusCodeInterceptor{w, http.StatusOK, 0} interceptor := &statusCodeInterceptor{w, http.StatusOK, 0}
@ -62,9 +63,7 @@ func Logging(logger *slog.Logger) func(http.Handler) http.Handler {
if !ok { if !ok {
requestID = "unknown" requestID = "unknown"
} }
logger.Infof(
logger.Info(
fmt.Sprintf(
"[%s] %s \"%s %s\" %d %d \"%s\"", "[%s] %s \"%s %s\" %d %d \"%s\"",
requestID, requestID,
r.RemoteAddr, r.RemoteAddr,
@ -73,7 +72,6 @@ func Logging(logger *slog.Logger) func(http.Handler) http.Handler {
interceptor.code, interceptor.code,
interceptor.count, interceptor.count,
r.UserAgent(), r.UserAgent(),
),
) )
}() }()
next.ServeHTTP(interceptor, r) next.ServeHTTP(interceptor, r)
@ -88,9 +86,7 @@ func Tracing(nextRequestID func() string) func(http.Handler) http.Handler {
if requestID == "" { if requestID == "" {
requestID = nextRequestID() requestID = nextRequestID()
} }
ctx := context.WithValue(r.Context(), requestIDKey, requestID) ctx := context.WithValue(r.Context(), requestIDKey, requestID)
w.Header().Set("X-Request-Id", requestID) w.Header().Set("X-Request-Id", requestID)
next.ServeHTTP(w, r.WithContext(ctx)) next.ServeHTTP(w, r.WithContext(ctx))
}) })
@ -100,7 +96,7 @@ func Tracing(nextRequestID func() string) func(http.Handler) http.Handler {
var Healthy int32 var Healthy int32
func NewHealthHandler() http.Handler { func NewHealthHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if atomic.LoadInt32(&Healthy) == 1 { if atomic.LoadInt32(&Healthy) == 1 {
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2020-2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -20,23 +20,23 @@ package handlers
import ( import (
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
"time" "time"
log "github.com/sirupsen/logrus"
) )
func EnableHSTS() func(http.Handler) http.Handler { func EnableHSTS() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
const Days180 = 180 const Days180 = 180
w.Header().Set("Strict-Transport-Security", fmt.Sprintf("max-age=%d", int((time.Hour*24*Days180).Seconds()))) w.Header().Set("Strict-Transport-Security", fmt.Sprintf("max-age=%d", int((time.Hour*24*Days180).Seconds())))
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
}) })
} }
} }
func getDataFromClientCert(logger *slog.Logger, r *http.Request) (string, []string) { func getDataFromClientCert(logger *log.Logger, r *http.Request) (string, []string) {
if r.TLS != nil && r.TLS.PeerCertificates != nil && len(r.TLS.PeerCertificates) > 0 { if r.TLS != nil && r.TLS.PeerCertificates != nil && len(r.TLS.PeerCertificates) > 0 {
firstCert := r.TLS.PeerCertificates[0] firstCert := r.TLS.PeerCertificates[0]
@ -45,7 +45,9 @@ func getDataFromClientCert(logger *slog.Logger, r *http.Request) (string, []stri
} }
for _, email := range firstCert.EmailAddresses { for _, email := range firstCert.EmailAddresses {
logger.Info("authenticated with a client certificate for email address", "email", email) logger.WithField(
"email", email,
).Info("authenticated with a client certificate for email address")
} }
return firstCert.Subject.CommonName, firstCert.EmailAddresses return firstCert.Subject.CommonName, firstCert.EmailAddresses

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2020-2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2020-2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -19,7 +19,6 @@ package services
import ( import (
"fmt" "fmt"
"log/slog"
"os" "os"
"strings" "strings"
@ -29,6 +28,7 @@ import (
"github.com/knadh/koanf/providers/env" "github.com/knadh/koanf/providers/env"
"github.com/knadh/koanf/providers/file" "github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/providers/posflag" "github.com/knadh/koanf/providers/posflag"
"github.com/sirupsen/logrus"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
@ -51,7 +51,7 @@ var DefaultConfig = map[string]interface{}{
} }
func ConfigureApplication( func ConfigureApplication(
logger *slog.Logger, logger *logrus.Logger,
appName string, appName string,
defaultConfig map[string]interface{}, defaultConfig map[string]interface{},
) (*koanf.Koanf, error) { ) (*koanf.Koanf, error) {
@ -70,7 +70,7 @@ func ConfigureApplication(
var err error var err error
if err = f.Parse(os.Args[1:]); err != nil { if err = f.Parse(os.Args[1:]); err != nil {
return nil, fmt.Errorf("could not parse command line arguments: %w", err) logger.WithError(err).Fatal("could not parse command line arguments")
} }
config := koanf.New(".") config := koanf.New(".")
@ -78,22 +78,18 @@ func ConfigureApplication(
_ = config.Load(confmap.Provider(defaultConfig, "."), nil) _ = config.Load(confmap.Provider(defaultConfig, "."), nil)
if err = config.Load(file.Provider(defaultFile), toml.Parser()); err != nil && !os.IsNotExist(err) { if err = config.Load(file.Provider(defaultFile), toml.Parser()); err != nil && !os.IsNotExist(err) {
logger.Error("could not load configuration from file", "file", defaultFile) logrus.WithError(err).WithField("file", defaultFile).Fatal("error loading configuration from file")
return nil, fmt.Errorf("error loading configuration: %w", err)
} }
cFiles, _ := f.GetStringSlice("conf") cFiles, _ := f.GetStringSlice("conf")
for _, c := range cFiles { for _, c := range cFiles {
if err = config.Load(file.Provider(c), toml.Parser()); err != nil { if err = config.Load(file.Provider(c), toml.Parser()); err != nil {
logger.Error("could not load configuration from file", "file", c) logger.WithError(err).WithField("file", c).Fatal("error loading configuration from file")
return nil, fmt.Errorf("error loading configuration: %w", err)
} }
} }
if err = config.Load(posflag.Provider(f, ".", config), nil); err != nil { if err = config.Load(posflag.Provider(f, ".", config), nil); err != nil {
return nil, fmt.Errorf("error loading configuration from command line: %w", err) logger.WithError(err).Fatal("error loading configuration from command line")
} }
prefix := fmt.Sprintf("%s_", strings.ToUpper(appName)) prefix := fmt.Sprintf("%s_", strings.ToUpper(appName))
@ -101,7 +97,7 @@ func ConfigureApplication(
if err = config.Load(env.Provider(prefix, ".", func(s string) string { if err = config.Load(env.Provider(prefix, ".", func(s string) string {
return strings.ReplaceAll(strings.ToLower(strings.TrimPrefix(s, prefix)), "_", ".") return strings.ReplaceAll(strings.ToLower(strings.TrimPrefix(s, prefix)), "_", ".")
}), nil); err != nil { }), nil); err != nil {
return nil, fmt.Errorf("error loading configuration from environment: %w", err) logrus.WithError(err).Fatal("error loading configuration from environment")
} }
return config, nil return config, nil

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2020-2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -21,9 +21,9 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"log/slog"
"net/http" "net/http"
log "github.com/sirupsen/logrus"
"github.com/yuin/goldmark" "github.com/yuin/goldmark"
"code.cacert.org/cacert/oidc-idp/translations" "code.cacert.org/cacert/oidc-idp/translations"
@ -35,7 +35,7 @@ import (
type MessageCatalog struct { type MessageCatalog struct {
messages map[string]*i18n.Message messages map[string]*i18n.Message
logger *slog.Logger logger *log.Logger
} }
func (m *MessageCatalog) AddMessages(messages map[string]*i18n.Message) { func (m *MessageCatalog) AddMessages(messages map[string]*i18n.Message) {
@ -61,7 +61,7 @@ func (s *I18NService) LookupMessage(
return translation return translation
} }
s.logger.Warn("no translation found for id", "id", id) s.logger.WithField("id", id).Warn("no translation found for id")
return id return id
} }
@ -88,7 +88,7 @@ func (s *I18NService) LookupMarkdownMessage(
TemplateData: templateData, TemplateData: templateData,
}) })
if err != nil { if err != nil {
s.logger.Warn("message localization failed", "error", err, "message", message) s.logger.WithError(err).Warn(err)
} }
if translation == "" { if translation == "" {
@ -105,7 +105,7 @@ func (s *I18NService) LookupMarkdownMessage(
return buf.String() return buf.String()
} }
s.logger.Warn("no translation found for id", "id", id) s.logger.WithField("id", id).Warn("no translation found for id")
return id return id
} }
@ -129,7 +129,7 @@ func (s *I18NService) LookupMessagePlural(
return translation return translation
} }
s.logger.Warn("no translation found for id", "id", id) s.logger.WithField("id", id).Warn("no translation found for id")
return id return id
} }
@ -138,20 +138,20 @@ func (m *MessageCatalog) handleLocalizeError(id string, translation string, err
var messageNotFound *i18n.MessageNotFoundErr var messageNotFound *i18n.MessageNotFoundErr
if errors.As(err, &messageNotFound) { if errors.As(err, &messageNotFound) {
m.logger.Warn("message not found", "error", err, "message", id) m.logger.WithError(err).WithField("message", id).Warn("message not found")
if translation != "" { if translation != "" {
return translation return translation
} }
} else { } else {
m.logger.Error("translation error", "error", err, "message", id) m.logger.WithError(err).WithField("message", id).Error("translation error")
} }
return id return id
} }
type I18NService struct { type I18NService struct {
logger *slog.Logger logger *log.Logger
bundle *i18n.Bundle bundle *i18n.Bundle
catalog *MessageCatalog catalog *MessageCatalog
} }
@ -181,16 +181,6 @@ func (s *I18NService) AddMessages() error {
Description: "Title for a button to cancel an action", Description: "Title for a button to cancel an action",
Other: "Cancel", Other: "Cancel",
} }
messages["ButtonTitleConsent"] = &i18n.Message{
ID: "ButtonTitleConsent",
Description: "Title for a button to give consent",
Other: "Consent",
}
messages["ButtonTitleDeny"] = &i18n.Message{
ID: "ButtonTitleDeny",
Description: "Title for a button to deny consent",
Other: "Deny",
}
messages["ButtonTitleRevoke"] = &i18n.Message{ messages["ButtonTitleRevoke"] = &i18n.Message{
ID: "ButtonTitleRevoke", ID: "ButtonTitleRevoke",
Description: "Title for a button to revoke consent", Description: "Title for a button to revoke consent",
@ -257,11 +247,13 @@ func (s *I18NService) AddMessages() error {
} }
messages["IntroConsentRequested"] = &i18n.Message{ messages["IntroConsentRequested"] = &i18n.Message{
ID: "IntroConsentRequested", ID: "IntroConsentRequested",
Other: "The **{{ .client }}** application requested your consent for the following set of permissions:", Other: "The <strong>{{ .client }}</strong> application requested your consent for the following set of " +
"permissions:",
} }
messages["IntroConsentMoreInformation"] = &i18n.Message{ messages["IntroConsentMoreInformation"] = &i18n.Message{
ID: "IntroConsentMoreInformation", ID: "IntroConsentMoreInformation",
Other: "You can find more information about **{{ .client }}** at [its description page]({{ .clientLink }}).", Other: "You can find more information about <strong>{{ .client }}</strong> at " +
"<a href=\"{{ .clientLink }}\">its description page</a>.",
} }
messages["ClaimsInformation"] = &i18n.Message{ messages["ClaimsInformation"] = &i18n.Message{
ID: "ClaimsInformation", ID: "ClaimsInformation",
@ -273,7 +265,7 @@ func (s *I18NService) AddMessages() error {
} }
messages["CertLoginIntroText"] = &i18n.Message{ messages["CertLoginIntroText"] = &i18n.Message{
ID: "CertLoginIntroText", ID: "CertLoginIntroText",
Other: "The application **{{ .ClientName }}** requests a login.", Other: "The application <strong>{{ .ClientName }}</strong> requests a login.",
} }
messages["EmailChoiceText"] = &i18n.Message{ messages["EmailChoiceText"] = &i18n.Message{
ID: "EmailChoiceText", ID: "EmailChoiceText",
@ -312,10 +304,6 @@ func (s *I18NService) AddMessages() error {
ID: "LoginDeniedByUser", ID: "LoginDeniedByUser",
Other: "Login has been denied by the user.", Other: "Login has been denied by the user.",
} }
messages["LoginDeniedSubjectMissing"] = &i18n.Message{
ID: "LoginDeniedSubjectMissing",
Other: "Login has been denied because the requested subject {{ .Subject }} could not be authenticated.",
}
messages["LogoutSuccessfulTitle"] = &i18n.Message{ messages["LogoutSuccessfulTitle"] = &i18n.Message{
ID: "LogoutSuccessfulTitle", ID: "LogoutSuccessfulTitle",
Other: "Logout successful", Other: "Logout successful",
@ -336,10 +324,6 @@ func (s *I18NService) AddMessages() error {
ID: "HintChooseAnIdentityForAuthentication", ID: "HintChooseAnIdentityForAuthentication",
Other: "Choose an identity for authentication.", Other: "Choose an identity for authentication.",
} }
messages["HintChooseDifferentClientCertificate"] = &i18n.Message{
ID: "HintChooseDifferentClientCertificate",
Other: "Choose a different client certificate for authentication.",
}
messages["NoConsentGiven"] = &i18n.Message{ messages["NoConsentGiven"] = &i18n.Message{
ID: "NoConsentGiven", ID: "NoConsentGiven",
Other: "You have not given consent to use your data to any application yet.", Other: "You have not given consent to use your data to any application yet.",
@ -377,7 +361,7 @@ func (s *I18NService) Localizer(languages string) *i18n.Localizer {
return i18n.NewLocalizer(s.bundle, languages) return i18n.NewLocalizer(s.bundle, languages)
} }
func InitI18n(logger *slog.Logger, languages []string) *I18NService { func InitI18n(logger *log.Logger, languages []string) *I18NService {
bundle := i18n.NewBundle(language.English) bundle := i18n.NewBundle(language.English)
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal) bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
@ -386,7 +370,7 @@ func InitI18n(logger *slog.Logger, languages []string) *I18NService {
bundleBytes, err := translations.Bundles.ReadFile(bundleName) bundleBytes, err := translations.Bundles.ReadFile(bundleName)
if err != nil { if err != nil {
logger.Warn("message bundle not found", "bundle", bundleName) logger.WithField("bundle", bundleName).Warn("message bundle not found")
continue continue
} }
@ -399,7 +383,7 @@ func InitI18n(logger *slog.Logger, languages []string) *I18NService {
return &I18NService{logger: logger, bundle: bundle, catalog: catalog} return &I18NService{logger: logger, bundle: bundle, catalog: catalog}
} }
func initMessageCatalog(logger *slog.Logger) *MessageCatalog { func initMessageCatalog(logger *log.Logger) *MessageCatalog {
messages := make(map[string]*i18n.Message) messages := make(map[string]*i18n.Message)
messages["ErrorTitle"] = &i18n.Message{ messages["ErrorTitle"] = &i18n.Message{
ID: "ErrorTitle", ID: "ErrorTitle",

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@ -19,25 +19,21 @@ package services
import ( import (
"crypto/rand" "crypto/rand"
"errors"
"log/slog" log "github.com/sirupsen/logrus"
) )
var errCouldNotGenerateKey = errors.New("could not generate key") func GenerateKey(length int) []byte {
func GenerateKey(length int) ([]byte, error) {
key := make([]byte, length) key := make([]byte, length)
read, err := rand.Read(key) read, err := rand.Read(key)
if err != nil { if err != nil {
return nil, errCouldNotGenerateKey log.WithError(err).Fatal("could not generate key")
} }
if read != length { if read != length {
slog.Error("read unexpected number of bytes", "read", read, "expected", length) log.WithFields(log.Fields{"read": read, "expected": length}).Fatal("read unexpected number of bytes")
return nil, errCouldNotGenerateKey
} }
return key, nil return key
} }

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -16,24 +16,14 @@ description = "Title for a button to confirm consent revocation"
hash = "sha1-bb25839982a1fe044fc2ac39552903c65402ad48" hash = "sha1-bb25839982a1fe044fc2ac39552903c65402ad48"
other = "Ja, Widerrufen!" other = "Ja, Widerrufen!"
[ButtonTitleConsent]
description = "Title for a button to give consent"
hash = "sha1-7f5c5d105e7a9600c7a8d3d092c7e812cf344e95"
other = "Zustimmen"
[ButtonTitleDeny]
description = "Title for a button to deny consent"
hash = "sha1-d5245e4b19ed6f0af002aee7ab488b8f822d53c7"
other = "Ablehnen"
[ButtonTitleRevoke] [ButtonTitleRevoke]
description = "Title for a button to revoke consent" description = "Title for a button to revoke consent"
hash = "sha1-b4524373ff63f37e062bd5f496e8ba04ee5c678d" hash = "sha1-b4524373ff63f37e062bd5f496e8ba04ee5c678d"
other = "Widerrufen" other = "Widerrufen"
[CertLoginIntroText] [CertLoginIntroText]
hash = "sha1-02871569c8d36e522cdb0716081ef62b7c7a71ec" hash = "sha1-e9f7c0522e49ffacc49e3fc35c6ffd31e495baf6"
other = "Die Anwendung **{{ .ClientName }}** fragt nach einer Anmeldung." other = "Die Anwendung <strong>{{ .ClientName }}</strong> fragt nach einer Anmeldung."
[CertLoginRequestText] [CertLoginRequestText]
hash = "sha1-1b20eea0f6fbb4ff139ecfe6b7a93c98cb14b8d7" hash = "sha1-1b20eea0f6fbb4ff139ecfe6b7a93c98cb14b8d7"
@ -93,10 +83,6 @@ other = "Unbekannter Fehler"
hash = "sha1-7ee5b067009bbedc061274ee50a3027b50a06163" hash = "sha1-7ee5b067009bbedc061274ee50a3027b50a06163"
other = "Wähle eine Identität für die Anmeldung." other = "Wähle eine Identität für die Anmeldung."
[HintChooseDifferentClientCertificate]
hash = "sha1-22002121759ae58afccfccb88383dbe54f94f0a9"
other = "Wähle ein anderes Client-Zertifikat für die Anmeldung."
[IndexTitle] [IndexTitle]
hash = "sha1-c763022b69a8ad58ab42d8ea708192abd85fd8f6" hash = "sha1-c763022b69a8ad58ab42d8ea708192abd85fd8f6"
other = "Willkommen bei deinem Identitätsprovider" other = "Willkommen bei deinem Identitätsprovider"
@ -106,12 +92,12 @@ hash = "sha1-00632c6562df53c62861c33e468e729887816419"
other = "Besuche [die Freigabeverwaltung]({{ .ManageConsentHRef }}), um deine Freigaben für Applikationen einzusehen oder zu widerrufen." other = "Besuche [die Freigabeverwaltung]({{ .ManageConsentHRef }}), um deine Freigaben für Applikationen einzusehen oder zu widerrufen."
[IntroConsentMoreInformation] [IntroConsentMoreInformation]
hash = "sha1-836b2931b417e98db4102731604443ee2700123c" hash = "sha1-f58b8378238bd433deef3c3e6b0b70d0fd0dd59e"
other = "Auf der [Beschreibungsseite]({{ .clientLink }}) findest du mehr Informationen zu **{{ .client }}**." other = "Auf der <a href=\"{{ .clientLink }}\">Beschreibungsseite</a> findest du mehr Informationen zu <strong>{{ .client }}</strong>."
[IntroConsentRequested] [IntroConsentRequested]
hash = "sha1-db5e67a16c181c4d27baf5d0e3bf255224b0fffc" hash = "sha1-3ac6a3583d40b5e8930c57531f0be9706f1e0194"
other = "Die Anwendung **{{ .client }}** hat deine Zustimmung für die Erteilung der folgenden Berechtigungen angefragt:" other = "Die Anwendung <strong>{{ .client }}</strong> hat deine Zustimmung für die Erteilung der folgenden Berechtigungen angefragt:"
[LabelAcceptCertLogin] [LabelAcceptCertLogin]
description = "Label for a button to accept certificate login" description = "Label for a button to accept certificate login"
@ -143,10 +129,6 @@ other = "Unbekannt"
hash = "sha1-bbad650536bfb091ad55d576262bbe4358277c73" hash = "sha1-bbad650536bfb091ad55d576262bbe4358277c73"
other = "Die Anmeldung wurde durch den Nutzer abgelehnt." other = "Die Anmeldung wurde durch den Nutzer abgelehnt."
[LoginDeniedSubjectMissing]
hash = "sha1-c787e750515612fd695d6a6beeb3a07ec381f3e7"
other = "Die Anmeldung wurde abgelehnt, weil die angeforderte Identität {{ .Subject }} nicht bestätigt werden konnte."
[LoginTitle] [LoginTitle]
hash = "sha1-9a24c8b64e047edc13f3c41ef7785bb2044a6d69" hash = "sha1-9a24c8b64e047edc13f3c41ef7785bb2044a6d69"
other = "Anmelden mit einem Client-Zertifikat" other = "Anmelden mit einem Client-Zertifikat"

View file

@ -1,6 +1,6 @@
AuthServerErrorExplanation = "A request that your browser sent to the authorization server caused an error. The authorization server returned details about the error that are printed below." AuthServerErrorExplanation = "A request that your browser sent to the authorization server caused an error. The authorization server returned details about the error that are printed below."
AuthServerErrorTitle = "Authorization server returned an error" AuthServerErrorTitle = "Authorization server returned an error"
CertLoginIntroText = "The application **{{ .ClientName }}** requests a login." CertLoginIntroText = "The application <strong>{{ .ClientName }}</strong> requests a login."
CertLoginRequestText = "Do you want to use the chosen identity from the certificate for authentication?" CertLoginRequestText = "Do you want to use the chosen identity from the certificate for authentication?"
ClaimsInformation = "In addition the application wants access to the following information:" ClaimsInformation = "In addition the application wants access to the following information:"
ConfirmRevokeExplanation = "Do you want to revoke your consent to allow **{{ .Application }}** access to identity data for **{{ .Subject }}**?" ConfirmRevokeExplanation = "Do you want to revoke your consent to allow **{{ .Application }}** access to identity data for **{{ .Subject }}**?"
@ -8,17 +8,15 @@ ConfirmRevokeTitle = "Revoke consent"
ErrorTitle = "An error has occurred" ErrorTitle = "An error has occurred"
ErrorUnknown = "Unknown error" ErrorUnknown = "Unknown error"
HintChooseAnIdentityForAuthentication = "Choose an identity for authentication." HintChooseAnIdentityForAuthentication = "Choose an identity for authentication."
HintChooseDifferentClientCertificate = "Choose a different client certificate for authentication."
IndexTitle = "Welcome to your identity provider" IndexTitle = "Welcome to your identity provider"
IndexWelcomeMessage = "Go to [manage consent]({{ .ManageConsentHRef }}) to show or revoke consent you have given to client applications." IndexWelcomeMessage = "Go to [manage consent]({{ .ManageConsentHRef }}) to show or revoke consent you have given to client applications."
IntroConsentMoreInformation = "You can find more information about **{{ .client }}** at [its description page]({{ .clientLink }})." IntroConsentMoreInformation = "You can find more information about <strong>{{ .client }}</strong> at <a href=\"{{ .clientLink }}\">its description page</a>."
IntroConsentRequested = "The **{{ .client }}** application requested your consent for the following set of permissions:" IntroConsentRequested = "The <strong>{{ .client }}</strong> application requested your consent for the following set of permissions:"
LabelConsent = "I hereby agree that the application may get the requested permissions." LabelConsent = "I hereby agree that the application may get the requested permissions."
LabelNever = "Never" LabelNever = "Never"
LabelSubmit = "Submit" LabelSubmit = "Submit"
LabelUnknown = "Unknown" LabelUnknown = "Unknown"
LoginDeniedByUser = "Login has been denied by the user." LoginDeniedByUser = "Login has been denied by the user."
LoginDeniedSubjectMissing = "Login has been denied because the requested subject {{ .Subject }} could not be authenticated."
LoginTitle = "Authenticate with a client certificate" LoginTitle = "Authenticate with a client certificate"
LogoutSuccessfulText = "You have been logged out successfully." LogoutSuccessfulText = "You have been logged out successfully."
LogoutSuccessfulTitle = "Logout successful" LogoutSuccessfulTitle = "Logout successful"
@ -45,14 +43,6 @@ other = "Cancel"
description = "Title for a button to confirm consent revocation" description = "Title for a button to confirm consent revocation"
other = "Yes, Revoke!" other = "Yes, Revoke!"
[ButtonTitleConsent]
description = "Title for a button to give consent"
other = "Consent"
[ButtonTitleDeny]
description = "Title for a button to deny consent"
other = "Deny"
[ButtonTitleRevoke] [ButtonTitleRevoke]
description = "Title for a button to revoke consent" description = "Title for a button to revoke consent"
other = "Revoke" other = "Revoke"

View file

@ -1,5 +1,5 @@
/* /*
Copyright CAcert Inc. Copyright 2020-2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0 SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");

View file

@ -33,7 +33,7 @@
{{ template "content" . }} {{ template "content" . }}
<footer class="footer mt-auto py-3"> <footer class="footer mt-auto py-3">
<div class="container"> <div class="container">
<span class="text-muted small">© <a href="https://www.cacert.org/">CAcert</a></span> <span class="text-muted small">© 2020-2023 <a href="https://www.cacert.org/">CAcert</a></span>
</div> </div>
</footer> </footer>
<script type="text/javascript" src="/js/cacert.bundle.js"></script> <script type="text/javascript" src="/js/cacert.bundle.js"></script>

View file

@ -1,7 +1,7 @@
{{ define "content" }} {{ define "content" }}
<main role="main" class="container"> <main role="main" class="container">
<h1>{{ .Title }}</h1> <h1>{{ .Title }}</h1>
{{ .IntroText }} <p>{{ .IntroText }}</p>
{{ with .FlashMessage }} {{ with .FlashMessage }}
<div class="alert alert-{{ .Type }}" role="alert"> <div class="alert alert-{{ .Type }}" role="alert">
{{ .Message }} {{ .Message }}

View file

@ -1,12 +1,12 @@
{{ define "content" }} {{ define "content" }}
<main role="main" class="container"> <main role="main" class="container">
<h1 class="h3 mb-3">{{ .Title }}</h1> <h1 class="h3 mb-3">{{ .Title }}</h1>
{{ if .LogoURI }} {{ if .client.LogoUri }}
<p class="text-center"> <p>
<img src="{{ .LogoURI }}" alt="{{ .ClientName }}" width="240" height="240" /> <img src="{{ .client.LogoUri }}" alt="{{ .client.ClientName }}"/>
</p> </p>
{{ end }} {{ end }}
{{ .IntroConsentRequested }} <p class="text-left">{{ .IntroConsentRequested }}</p>
<form method="post"> <form method="post">
<ul class="list-group text-left small mb-3"> <ul class="list-group text-left small mb-3">
{{ range $i, $scope := .requestedScope }} {{ range $i, $scope := .requestedScope }}
@ -26,16 +26,16 @@
{{ end}} {{ end}}
</ul> </ul>
{{ end }} {{ end }}
{{ if .HasMoreInformation }} <p class="text-left">{{ .IntroMoreInformation }}</p>
{{ .IntroMoreInformation }}
{{ end }}
{{ .csrfField }} {{ .csrfField }}
{{ .LabelConsent }} <div class="checkbox mb-3">
<label>
<input type="checkbox" name="consent" id="consent" value="true"/>
{{ .LabelConsent }}</label>
</div>
<button class="btn btn-primary" type="submit" name="consent" <button class="btn btn-lg btn-primary btn-block" type="submit">{{ .LabelSubmit }}</button>
value="consent">{{ .ButtonTitleConsent }}</button>
<button class="btn btn-outline-secondary" type="submit" name="consent" value="deny">{{ .ButtonTitleDeny }}</button>
</form> </form>
</main> </main>
{{ end }} {{ end }}

View file

@ -1,9 +1,10 @@
{{ define "content" }} {{ define "content" }}
<main class="container"> <div class="container">
<img src="/images/CAcert-logo.svg" width="300" height="68" alt="CAcert" class="mb-4">
<h1>{{ .Title }}</h1> <h1>{{ .Title }}</h1>
{{ .Explanation }} {{ .Explanation }}
{{ if .ErrorMessage }} {{ if .ErrorMessage }}
<div class="alert alert-danger">{{ .ErrorMessage }}</div> <div class="alert alert-danger">{{ .ErrorMessage }}</div>
{{ end }} {{ end }}
</main> </div>
{{ end }} {{ end }}

View file

@ -1,20 +1,3 @@
/*
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package ui package ui
import "embed" import "embed"