Compare commits

...

10 Commits
0.3.0 ... main

Author SHA1 Message Date
Jan Dittberner bdf37493d0 Debug logging for session 7 days ago
Jan Dittberner 407e9acfcc Remove comment 7 days ago
Jan Dittberner e576d981f9 Reduce minimum CSRF key length to 256 bits 7 days ago
Jan Dittberner 9e54bcabbe Remove copyright years from base template 7 days ago
Jan Dittberner 1e676e8cf1 Switch logging to slog
This commit replaces logrus with slog from the Go standard library.
1 week ago
Jan Dittberner f22f8ff902 Update golangci-lint, fix warnings
- remove copyright years
- mark unused parameter with _
- add missing empty lines before expressions
1 week ago
Jan Dittberner f3dc4d71d1 Update dependencies 1 week ago
Jan Dittberner 9aeca21faa Remove duplicate logo from hydra_error template 9 months ago
Jan Dittberner a5c583f1f6 Render client logo at specific size 10 months ago
Jan Dittberner 56ff01600f Improve consent handling
- hide client logo if there is no logo URI
- hide client information link if there is no client URI
- use buttons instead of a checkbox for consent
- use Markdown for messages
10 months ago

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

@ -5,6 +5,12 @@ 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/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Changed
- 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
## [0.3.0] - 2023-08-07
### Changed
- use a session to transport data from the login to the consent screens

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

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

@ -1,7 +1,6 @@
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=
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/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@ -39,10 +38,12 @@ 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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
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.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.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@ -52,8 +53,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/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.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
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.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -89,8 +90,6 @@ 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.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.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/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -103,14 +102,17 @@ 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.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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/gorilla/csrf v1.7.1 h1:Ir3o2c1/Uzj6FBxMlAUB6SivgVMy1ONXwYgXn+/aHPE=
github.com/gorilla/csrf v1.7.1/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI=
github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
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/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ=
@ -172,14 +174,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/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/blackmagic v1.0.1 h1:lS5Zts+5HIC/8og6cGHb0uCcNCa3OUt1ygh3Qz2Fe80=
github.com/lestrrat-go/blackmagic v1.0.1/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
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/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
github.com/lestrrat-go/jwx v1.2.26 h1:4iFo8FPRZGDYe1t19mQP0zTRqA7n8HnJ5lkIiDvJcB0=
github.com/lestrrat-go/jwx v1.2.26/go.mod h1:MaiCdGbn3/cckbOFSCluJlJMmp9dmZm5hDuIkx8ftpQ=
github.com/lestrrat-go/jwx v1.2.29 h1:QT0utmUJ4/12rmsVQrJ3u55bycPkKqGYuGT4tyRhxSQ=
github.com/lestrrat-go/jwx v1.2.29/go.mod h1:hU8k2l6WF0ncx20uQdOmik/Gjg6E3/wIRtXSNFeZuB8=
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/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
@ -216,12 +218,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/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/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYojM/PaA=
github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0=
github.com/nicksnyder/go-i18n/v2 v2.4.0 h1:3IcvPOAvnCKwNm0TB0dLDTuawWEj+ax/RERNC+diLMM=
github.com/nicksnyder/go-i18n/v2 v2.4.0/go.mod h1:nxYSZE9M0bf3Y70gPQjN9ha7XNHX7gMc814+6wVyEI4=
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/ory/hydra-client-go/v2 v2.1.1 h1:3JatU9uFbw5XhF3lgPCas1l1Kok2v5Mq1p26zZwGHNg=
github.com/ory/hydra-client-go/v2 v2.1.1/go.mod h1:IiIwChp/9wRvPoyFQblqPvg78uVishCCrV9+/M7Pl34=
github.com/ory/hydra-client-go/v2 v2.2.0 h1:g8hw0YQD5Us1aAgZj7OyBmBGSDwlnY9/2Pb/pQQq8YE=
github.com/ory/hydra-client-go/v2 v2.2.0/go.mod h1:h0DSI2kQA3S2fN7HyD8DNWcvbgDmYRSxfhwu/mSBhH8=
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/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
@ -260,14 +262,13 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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.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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@ -276,14 +277,15 @@ 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.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.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
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.5.5 h1:IJznPe8wOzfIKETmMkd06F8nXkmlhaHqFRM9l1hAGsU=
github.com/yuin/goldmark v1.5.5/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
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/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
@ -296,9 +298,10 @@ 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-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.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
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/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=
@ -317,7 +320,6 @@ 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-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-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-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=
@ -331,13 +333,12 @@ 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.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.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY=
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
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-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -381,17 +382,19 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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-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.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.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=
@ -399,11 +402,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.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.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.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
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/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=
@ -424,8 +427,6 @@ 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=
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.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-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
@ -451,8 +452,6 @@ 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.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.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/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=

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

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

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

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

@ -1,5 +1,5 @@
/*
Copyright 2020-2023 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -23,12 +23,12 @@ import (
"fmt"
"html/template"
"io"
"log/slog"
"net/http"
"github.com/gorilla/csrf"
"github.com/nicksnyder/go-i18n/v2/i18n"
client "github.com/ory/hydra-client-go/v2"
log "github.com/sirupsen/logrus"
"code.cacert.org/cacert/oidc-idp/internal/services"
)
@ -49,9 +49,9 @@ const (
)
type LoginHandler struct {
logger *log.Logger
logger *slog.Logger
trans *services.I18NService
adminClient client.OAuth2Api
adminClient client.OAuth2API
templates TemplateCache
}
@ -72,7 +72,7 @@ func (h *LoginHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
h.logger.WithField("challenge", challenge).Debug("received login challenge")
h.logger.Debug("received login challenge", "challenge", challenge)
certFullName, certEmails := getDataFromClientCert(h.logger, r)
@ -100,9 +100,10 @@ func (h *LoginHandler) handleGet(
r.Context(),
).LoginChallenge(challenge).Execute()
if err != nil {
h.logger.WithError(err).WithField(
"challenge", challenge,
).Warn("could not get login request for challenge")
h.logger.Warn(
"could not get login request for challenge",
"error", err, "challenge", challenge,
)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -111,9 +112,10 @@ func (h *LoginHandler) handleGet(
defer func() { _ = response.Body.Close() }()
h.logger.WithFields(
log.Fields{"response": response.Status, "login_request": oAuth2LoginRequest},
).Debug("got response for GetOAuth2LoginRequest")
h.logger.Debug(
"got response for GetOAuth2LoginRequest",
"response", response.Status, "login_request", oAuth2LoginRequest,
)
h.renderRequestForClientCert(w, r, certEmails, localizer, oAuth2LoginRequest)
}
@ -151,14 +153,14 @@ func (h *LoginHandler) handlePost(
}
// perform certificate auth
h.logger.WithFields(log.Fields{
"emails": certEmails,
"full_name": certFullName,
}).Info("will perform certificate authentication")
h.logger.Info(
"will perform certificate authentication",
"emails", certEmails, "full_name", certFullName,
)
userID, err := h.performCertificateLogin(certEmails, r)
if err != nil {
h.logger.WithError(err).Error("could not perform certificate login")
h.logger.Error("could not perform certificate login", "error", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
@ -166,7 +168,7 @@ func (h *LoginHandler) handlePost(
session, err := GetSession(r)
if err != nil {
h.logger.WithError(err).Error("could not perform certificate login")
h.logger.Error("could not perform certificate login", "error", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
@ -178,7 +180,7 @@ func (h *LoginHandler) handlePost(
session.Options.Secure = true
if err = session.Save(r, w); err != nil {
h.logger.WithError(err).Error("could not save session")
h.logger.Error("could not save session", "error", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
@ -194,9 +196,8 @@ func (h *LoginHandler) handlePost(
r.Context(),
).LoginChallenge(challenge).AcceptOAuth2LoginRequest(*acceptRequest).Execute()
if err != nil {
h.logger.WithError(err).Error("error getting login request")
h.logger.Error("error getting login request", "error", err)
// h.fillAcceptLoginRequestErrorBucket(r, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
@ -204,13 +205,13 @@ func (h *LoginHandler) handlePost(
defer func() { _ = response.Body.Close() }()
h.logger.WithFields(
log.Fields{"response": response.Status, "accept_login_request": loginRequest},
).Debug("got response for AcceptOAuth2LoginRequest")
h.logger.Debug("got response for AcceptOAuth2LoginRequest",
"response", response.Status, "accept_login_request", loginRequest,
)
if h.logger.IsLevelEnabled(log.TraceLevel) {
if h.logger.Enabled(r.Context(), slog.LevelDebug) {
if rb, err := io.ReadAll(response.Body); err == nil {
h.logger.WithField("response_body", rb).Trace("response body from Hydra")
h.logger.Debug("response body from Hydra", "response_body", rb)
}
}
@ -218,36 +219,6 @@ func (h *LoginHandler) handlePost(
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(
w http.ResponseWriter,
r *http.Request,
@ -263,7 +234,7 @@ func (h *LoginHandler) rejectLogin(
r.Context(),
).LoginChallenge(challenge).RejectOAuth2Request(*rejectRequest).Execute()
if err != nil {
h.logger.WithError(err).Error("error getting reject login request")
h.logger.Error("error getting reject login request", "error", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
@ -271,9 +242,10 @@ func (h *LoginHandler) rejectLogin(
defer func() { _ = response.Body.Close() }()
h.logger.WithFields(
log.Fields{"response": response.Status, "reject_login_request": rejectLoginRequest},
).Debug("go response for RejectOAuth2LoginRequest")
h.logger.Debug(
"go response for RejectOAuth2LoginRequest",
"response", response.Status, "reject_login_request", rejectLoginRequest,
)
w.Header().Set("Location", rejectLoginRequest.GetRedirectTo())
w.WriteHeader(http.StatusFound)
@ -308,7 +280,7 @@ func (h *LoginHandler) renderRequestForClientCert(
"FlashMessage": r.Context().Value(ctxKeyMessage),
})
if err != nil {
h.logger.WithError(err).Error("template rendering failed")
h.logger.Error("template rendering failed", "error", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
@ -340,7 +312,7 @@ func (h *LoginHandler) renderNoChallengeInRequest(w http.ResponseWriter, localiz
)),
})
if err != nil {
h.logger.WithError(err).Error("template rendering failed")
h.logger.Error("template rendering failed", "error", err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
@ -348,7 +320,7 @@ func (h *LoginHandler) renderNoChallengeInRequest(w http.ResponseWriter, localiz
}
func NewLoginHandler(
logger *log.Logger, tc TemplateCache, trans *services.I18NService, adminClient client.OAuth2Api,
logger *slog.Logger, tc TemplateCache, trans *services.I18NService, adminClient client.OAuth2API,
) *LoginHandler {
return &LoginHandler{
logger: logger,

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

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

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

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

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

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

@ -1,5 +1,5 @@
/*
Copyright 2020-2023 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -21,9 +21,9 @@ import (
"bytes"
"errors"
"fmt"
"log/slog"
"net/http"
log "github.com/sirupsen/logrus"
"github.com/yuin/goldmark"
"code.cacert.org/cacert/oidc-idp/translations"
@ -35,7 +35,7 @@ import (
type MessageCatalog struct {
messages map[string]*i18n.Message
logger *log.Logger
logger *slog.Logger
}
func (m *MessageCatalog) AddMessages(messages map[string]*i18n.Message) {
@ -61,7 +61,7 @@ func (s *I18NService) LookupMessage(
return translation
}
s.logger.WithField("id", id).Warn("no translation found for id")
s.logger.Warn("no translation found for id", "id", id)
return id
}
@ -88,7 +88,7 @@ func (s *I18NService) LookupMarkdownMessage(
TemplateData: templateData,
})
if err != nil {
s.logger.WithError(err).Warn(err)
s.logger.Warn("message localization failed", "error", err, "message", message)
}
if translation == "" {
@ -105,7 +105,7 @@ func (s *I18NService) LookupMarkdownMessage(
return buf.String()
}
s.logger.WithField("id", id).Warn("no translation found for id")
s.logger.Warn("no translation found for id", "id", id)
return id
}
@ -129,7 +129,7 @@ func (s *I18NService) LookupMessagePlural(
return translation
}
s.logger.WithField("id", id).Warn("no translation found for id")
s.logger.Warn("no translation found for id", "id", id)
return id
}
@ -138,20 +138,20 @@ func (m *MessageCatalog) handleLocalizeError(id string, translation string, err
var messageNotFound *i18n.MessageNotFoundErr
if errors.As(err, &messageNotFound) {
m.logger.WithError(err).WithField("message", id).Warn("message not found")
m.logger.Warn("message not found", "error", err, "message", id)
if translation != "" {
return translation
}
} else {
m.logger.WithError(err).WithField("message", id).Error("translation error")
m.logger.Error("translation error", "error", err, "message", id)
}
return id
}
type I18NService struct {
logger *log.Logger
logger *slog.Logger
bundle *i18n.Bundle
catalog *MessageCatalog
}
@ -181,6 +181,16 @@ func (s *I18NService) AddMessages() error {
Description: "Title for a button to cancel an action",
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{
ID: "ButtonTitleRevoke",
Description: "Title for a button to revoke consent",
@ -246,14 +256,12 @@ func (s *I18NService) AddMessages() error {
Other: "Unknown",
}
messages["IntroConsentRequested"] = &i18n.Message{
ID: "IntroConsentRequested",
Other: "The <strong>{{ .client }}</strong> application requested your consent for the following set of " +
"permissions:",
ID: "IntroConsentRequested",
Other: "The **{{ .client }}** application requested your consent for the following set of permissions:",
}
messages["IntroConsentMoreInformation"] = &i18n.Message{
ID: "IntroConsentMoreInformation",
Other: "You can find more information about <strong>{{ .client }}</strong> at " +
"<a href=\"{{ .clientLink }}\">its description page</a>.",
ID: "IntroConsentMoreInformation",
Other: "You can find more information about **{{ .client }}** at [its description page]({{ .clientLink }}).",
}
messages["ClaimsInformation"] = &i18n.Message{
ID: "ClaimsInformation",
@ -265,7 +273,7 @@ func (s *I18NService) AddMessages() error {
}
messages["CertLoginIntroText"] = &i18n.Message{
ID: "CertLoginIntroText",
Other: "The application <strong>{{ .ClientName }}</strong> requests a login.",
Other: "The application **{{ .ClientName }}** requests a login.",
}
messages["EmailChoiceText"] = &i18n.Message{
ID: "EmailChoiceText",
@ -361,7 +369,7 @@ func (s *I18NService) Localizer(languages string) *i18n.Localizer {
return i18n.NewLocalizer(s.bundle, languages)
}
func InitI18n(logger *log.Logger, languages []string) *I18NService {
func InitI18n(logger *slog.Logger, languages []string) *I18NService {
bundle := i18n.NewBundle(language.English)
bundle.RegisterUnmarshalFunc("toml", toml.Unmarshal)
@ -370,7 +378,7 @@ func InitI18n(logger *log.Logger, languages []string) *I18NService {
bundleBytes, err := translations.Bundles.ReadFile(bundleName)
if err != nil {
logger.WithField("bundle", bundleName).Warn("message bundle not found")
logger.Warn("message bundle not found", "bundle", bundleName)
continue
}
@ -383,7 +391,7 @@ func InitI18n(logger *log.Logger, languages []string) *I18NService {
return &I18NService{logger: logger, bundle: bundle, catalog: catalog}
}
func initMessageCatalog(logger *log.Logger) *MessageCatalog {
func initMessageCatalog(logger *slog.Logger) *MessageCatalog {
messages := make(map[string]*i18n.Message)
messages["ErrorTitle"] = &i18n.Message{
ID: "ErrorTitle",

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

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

@ -16,14 +16,24 @@ description = "Title for a button to confirm consent revocation"
hash = "sha1-bb25839982a1fe044fc2ac39552903c65402ad48"
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]
description = "Title for a button to revoke consent"
hash = "sha1-b4524373ff63f37e062bd5f496e8ba04ee5c678d"
other = "Widerrufen"
[CertLoginIntroText]
hash = "sha1-e9f7c0522e49ffacc49e3fc35c6ffd31e495baf6"
other = "Die Anwendung <strong>{{ .ClientName }}</strong> fragt nach einer Anmeldung."
hash = "sha1-02871569c8d36e522cdb0716081ef62b7c7a71ec"
other = "Die Anwendung **{{ .ClientName }}** fragt nach einer Anmeldung."
[CertLoginRequestText]
hash = "sha1-1b20eea0f6fbb4ff139ecfe6b7a93c98cb14b8d7"
@ -92,12 +102,12 @@ hash = "sha1-00632c6562df53c62861c33e468e729887816419"
other = "Besuche [die Freigabeverwaltung]({{ .ManageConsentHRef }}), um deine Freigaben für Applikationen einzusehen oder zu widerrufen."
[IntroConsentMoreInformation]
hash = "sha1-f58b8378238bd433deef3c3e6b0b70d0fd0dd59e"
other = "Auf der <a href=\"{{ .clientLink }}\">Beschreibungsseite</a> findest du mehr Informationen zu <strong>{{ .client }}</strong>."
hash = "sha1-836b2931b417e98db4102731604443ee2700123c"
other = "Auf der [Beschreibungsseite]({{ .clientLink }}) findest du mehr Informationen zu **{{ .client }}**."
[IntroConsentRequested]
hash = "sha1-3ac6a3583d40b5e8930c57531f0be9706f1e0194"
other = "Die Anwendung <strong>{{ .client }}</strong> hat deine Zustimmung für die Erteilung der folgenden Berechtigungen angefragt:"
hash = "sha1-db5e67a16c181c4d27baf5d0e3bf255224b0fffc"
other = "Die Anwendung **{{ .client }}** hat deine Zustimmung für die Erteilung der folgenden Berechtigungen angefragt:"
[LabelAcceptCertLogin]
description = "Label for a button to accept certificate login"

@ -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."
AuthServerErrorTitle = "Authorization server returned an error"
CertLoginIntroText = "The application <strong>{{ .ClientName }}</strong> requests a login."
CertLoginIntroText = "The application **{{ .ClientName }}** requests a login."
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:"
ConfirmRevokeExplanation = "Do you want to revoke your consent to allow **{{ .Application }}** access to identity data for **{{ .Subject }}**?"
@ -10,8 +10,8 @@ ErrorUnknown = "Unknown error"
HintChooseAnIdentityForAuthentication = "Choose an identity for authentication."
IndexTitle = "Welcome to your identity provider"
IndexWelcomeMessage = "Go to [manage consent]({{ .ManageConsentHRef }}) to show or revoke consent you have given to client applications."
IntroConsentMoreInformation = "You can find more information about <strong>{{ .client }}</strong> at <a href=\"{{ .clientLink }}\">its description page</a>."
IntroConsentRequested = "The <strong>{{ .client }}</strong> application requested your consent for the following set of permissions:"
IntroConsentMoreInformation = "You can find more information about **{{ .client }}** at [its description page]({{ .clientLink }})."
IntroConsentRequested = "The **{{ .client }}** application requested your consent for the following set of permissions:"
LabelConsent = "I hereby agree that the application may get the requested permissions."
LabelNever = "Never"
LabelSubmit = "Submit"
@ -43,6 +43,14 @@ other = "Cancel"
description = "Title for a button to confirm consent revocation"
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]
description = "Title for a button to revoke consent"
other = "Revoke"

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

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

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

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

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

@ -1,3 +1,20 @@
/*
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
import "embed"

Loading…
Cancel
Save