From f3060bee3b78b49c1db7bd9d48a8f35022479c99 Mon Sep 17 00:00:00 2001 From: Melora Hugues Date: Thu, 15 Aug 2024 18:25:15 +0200 Subject: [PATCH 1/7] feat: start replacing dex with zitadel (#48) Start the process of replacing dex with zitadel, this commit is absolutely not prod-ready, basically we just added zitatel, and the necessary elements to make it work to at least getting a client from the DB - replace logrus with zap - start our own storage for the users - instanciate zitaled on start - allow getting client using the ID from the DB --- polyculeconnect/cmd/serve/serve.go | 58 ++++---- polyculeconnect/config/config.go | 29 ++-- polyculeconnect/controller/ui/static.go | 6 +- polyculeconnect/go.mod | 45 +++++-- polyculeconnect/go.sum | 103 +++++++++++---- polyculeconnect/helpers/helpers.go | 4 +- polyculeconnect/internal/db/base.go | 5 + polyculeconnect/internal/db/client.go | 69 ++++++++++ .../{ => internal}/middlewares/logger.go | 4 +- .../internal/middlewares/middlewarechain.go | 14 ++ polyculeconnect/internal/model/client.go | 88 ++++++++++++ polyculeconnect/internal/storage/local.go | 4 + polyculeconnect/internal/storage/storage.go | 125 ++++++++++++++++++ polyculeconnect/logger/logger.go | 28 +++- .../middlewares/middlewarechain.go | 14 -- polyculeconnect/polyculeconnect.db | Bin 0 -> 98304 bytes polyculeconnect/server/server.go | 12 +- 17 files changed, 497 insertions(+), 111 deletions(-) create mode 100644 polyculeconnect/internal/db/base.go create mode 100644 polyculeconnect/internal/db/client.go rename polyculeconnect/{ => internal}/middlewares/logger.go (96%) create mode 100644 polyculeconnect/internal/middlewares/middlewarechain.go create mode 100644 polyculeconnect/internal/model/client.go create mode 100644 polyculeconnect/internal/storage/local.go create mode 100644 polyculeconnect/internal/storage/storage.go delete mode 100644 polyculeconnect/middlewares/middlewarechain.go create mode 100644 polyculeconnect/polyculeconnect.db diff --git a/polyculeconnect/cmd/serve/serve.go b/polyculeconnect/cmd/serve/serve.go index 1c4a98f..01b814b 100644 --- a/polyculeconnect/cmd/serve/serve.go +++ b/polyculeconnect/cmd/serve/serve.go @@ -2,19 +2,21 @@ package serve import ( "context" + "log/slog" "os" "os/signal" "time" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/utils" - "git.faercol.me/faercol/polyculeconnect/polyculeconnect/connector" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/storage" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/logger" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/server" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/services" - dex_server "github.com/dexidp/dex/server" - "github.com/prometheus/client_golang/prometheus" "github.com/spf13/cobra" + "github.com/zitadel/oidc/v3/pkg/op" + "go.uber.org/zap/exp/zapslog" ) var configPath string @@ -41,43 +43,43 @@ func serve() { storageType := utils.InitStorage(conf) logger.L.Infof("Initialized storage backend %q", conf.StorageType) - dexConf := dex_server.Config{ - Web: dex_server.WebConfig{ - Dir: conf.StaticDir, - Theme: "default", - }, - Storage: storageType, - Issuer: conf.Issuer, - SupportedResponseTypes: []string{"code"}, - SkipApprovalScreen: false, - AllowedOrigins: []string{"*"}, - Logger: logger.L, - PrometheusRegistry: prometheus.NewRegistry(), - } logger.L.Info("Initializing authentication backends") - dex_server.ConnectorsConfig[connector.TypeRefuseAll] = func() dex_server.ConnectorConfig { return new(connector.RefuseAllConfig) } - connectors, err := dexConf.Storage.ListConnectors() + // dex_server.ConnectorsConfig[connector.TypeRefuseAll] = func() dex_server.ConnectorConfig { return new(connector.RefuseAllConfig) } + // connectors, err := dexConf.Storage.ListConnectors() + // if err != nil { + // logger.L.Fatalf("Failed to get existing connectors: %s", err.Error()) + // } + // var connectorIDs []string + // for _, conn := range connectors { + // connectorIDs = append(connectorIDs, conn.ID) + // } + + userDB, err := db.New(*conf) if err != nil { - logger.L.Fatalf("Failed to get existing connectors: %s", err.Error()) + utils.Failf("failed to init user DB: %s", err.Error()) } - var connectorIDs []string - for _, conn := range connectors { - connectorIDs = append(connectorIDs, conn.ID) + + st := storage.Storage{LocalStorage: userDB} + opConf := op.Config{} + slogger := slog.New(zapslog.NewHandler(logger.L.Desugar().Core(), nil)) + // slogger := + options := []op.Option{ + op.WithAllowInsecure(), + op.WithLogger(slogger), + } + provider, err := op.NewProvider(&opConf, &st, op.StaticIssuer(conf.Issuer), options...) + if err != nil { + utils.Failf("failed to init OIDC provider: %s", err.Error()) } if err := services.AddDefaultBackend(storageType); err != nil { logger.L.Errorf("Failed to add connector for backend RefuseAll to stage: %s", err.Error()) } - dexSrv, err := dex_server.NewServer(mainCtx, dexConf) - if err != nil { - logger.L.Fatalf("Failed to init dex server: %s", err.Error()) - } - logger.L.Info("Initializing server") - s, err := server.New(conf, dexSrv, logger.L) + s, err := server.New(conf, provider, logger.L) if err != nil { logger.L.Fatalf("Failed to initialize server: %s", err.Error()) } diff --git a/polyculeconnect/config/config.go b/polyculeconnect/config/config.go index 2db5424..4ba8be6 100644 --- a/polyculeconnect/config/config.go +++ b/polyculeconnect/config/config.go @@ -9,7 +9,7 @@ import ( "github.com/dexidp/dex/connector/oidc" "github.com/kelseyhightower/envconfig" - "github.com/sirupsen/logrus" + "go.uber.org/zap" ) const ( @@ -32,7 +32,7 @@ const ( ) const ( - defaultLogLevel = logrus.InfoLevel + defaultLogLevel = zap.InfoLevel defaultServerMode = ModeNet defaultServerHost = "0.0.0.0" @@ -126,12 +126,12 @@ func (c *jsonConf) initValues(ac *AppConfig) { } type AppConfig struct { - LogLevel logrus.Level `envconfig:"LOG_LEVEL"` - ServerMode ListeningMode `envconfig:"SERVER_MODE"` - Host string `envconfig:"SERVER_HOST"` - Port int `envconfig:"SERVER_PORT"` - SockPath string `envconfig:"SERVER_SOCK"` - StorageType string `envconfig:"STORAGE_TYPE"` + LogLevel zap.AtomicLevel `envconfig:"LOG_LEVEL"` + ServerMode ListeningMode `envconfig:"SERVER_MODE"` + Host string `envconfig:"SERVER_HOST"` + Port int `envconfig:"SERVER_PORT"` + SockPath string `envconfig:"SERVER_SOCK"` + StorageType string `envconfig:"STORAGE_TYPE"` StorageConfig *StorageConfig Issuer string StaticDir string @@ -139,7 +139,7 @@ type AppConfig struct { func defaultConfig() AppConfig { return AppConfig{ - LogLevel: defaultLogLevel, + LogLevel: zap.NewAtomicLevelAt(defaultLogLevel), ServerMode: defaultServerMode, Host: defaultServerHost, Port: defaultServerPort, @@ -162,13 +162,12 @@ func defaultConfig() AppConfig { } } -func parseLevel(lvlStr string) logrus.Level { - for _, lvl := range logrus.AllLevels { - if lvl.String() == lvlStr { - return lvl - } +func parseLevel(lvlStr string) zap.AtomicLevel { + var res zap.AtomicLevel + if err := res.UnmarshalText([]byte(lvlStr)); err != nil { + return zap.NewAtomicLevelAt(zap.InfoLevel) } - return logrus.InfoLevel + return res } func (ac *AppConfig) UnmarshalJSON(data []byte) error { diff --git a/polyculeconnect/controller/ui/static.go b/polyculeconnect/controller/ui/static.go index 1145194..e98224a 100644 --- a/polyculeconnect/controller/ui/static.go +++ b/polyculeconnect/controller/ui/static.go @@ -9,7 +9,7 @@ import ( "path/filepath" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/helpers" - "github.com/sirupsen/logrus" + "go.uber.org/zap" ) const StaticRoute = "/static/" @@ -30,12 +30,12 @@ func (sc *StaticController) ServeHTTP(w http.ResponseWriter, r *http.Request) { } type IndexController struct { - l *logrus.Logger + l *zap.SugaredLogger downstreamConstroller http.Handler baseDir string } -func NewIndexController(l *logrus.Logger, downstream http.Handler, baseDir string) *IndexController { +func NewIndexController(l *zap.SugaredLogger, downstream http.Handler, baseDir string) *IndexController { return &IndexController{ l: l, downstreamConstroller: downstream, diff --git a/polyculeconnect/go.mod b/polyculeconnect/go.mod index 4214398..0e65fd2 100644 --- a/polyculeconnect/go.mod +++ b/polyculeconnect/go.mod @@ -1,19 +1,24 @@ module git.faercol.me/faercol/polyculeconnect/polyculeconnect -go 1.20 +go 1.21 + +toolchain go1.22.6 require ( github.com/dexidp/dex v0.0.0-20231014000322-089f374d4f3e + github.com/go-jose/go-jose/v4 v4.0.4 github.com/kelseyhightower/envconfig v1.4.0 - github.com/prometheus/client_golang v1.17.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 + github.com/zitadel/oidc v1.13.5 + github.com/zitadel/oidc/v3 v3.27.0 + go.uber.org/zap v1.24.0 + go.uber.org/zap/exp v0.2.0 ) require ( - cloud.google.com/go/compute v1.23.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/compute/metadata v0.3.0 // indirect github.com/AppsFlyer/go-sundheit v0.5.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/Masterminds/goutils v1.1.1 // indirect @@ -21,23 +26,29 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/beevik/etree v1.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/coreos/go-oidc/v3 v3.6.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dexidp/dex/api/v2 v2.1.1-0.20231014000322-089f374d4f3e // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect + github.com/go-chi/chi/v5 v5.1.0 // indirect github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/go-ldap/ldap/v3 v3.4.6 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/s2a-go v0.1.7 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/schema v1.2.0 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.11 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -48,24 +59,34 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect + github.com/muhlemmer/gu v0.3.1 // indirect + github.com/muhlemmer/httpforwarded v0.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.1 // indirect + github.com/rs/cors v1.11.0 // indirect github.com/russellhaering/goxmldsig v1.4.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/pflag v1.0.5 // indirect + github.com/zitadel/logging v0.6.0 // indirect + github.com/zitadel/schema v1.3.0 // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/crypto v0.14.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.10.0 // indirect + golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20221004215720-b9f4876ce741 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/oauth2 v0.13.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/oauth2 v0.22.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect google.golang.org/api v0.147.0 // indirect - google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect google.golang.org/grpc v1.58.3 // indirect google.golang.org/protobuf v1.31.0 // indirect diff --git a/polyculeconnect/go.sum b/polyculeconnect/go.sum index c60f175..3a0d6b9 100644 --- a/polyculeconnect/go.sum +++ b/polyculeconnect/go.sum @@ -1,8 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= github.com/AppsFlyer/go-sundheit v0.5.0 h1:/VxpyigCfJrq1r97mn9HPiAB2qrhcTFHwNIIDr15CZM= github.com/AppsFlyer/go-sundheit v0.5.0/go.mod h1:2ZM0BnfqT/mljBQO224VbL5XH06TgWuQ6Cn+cTtCpTY= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= @@ -19,8 +17,12 @@ github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1L github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beevik/etree v1.2.0 h1:l7WETslUG/T+xOPs47dtd6jov2Ii/8/OjCldk5fYfQw= github.com/beevik/etree v1.2.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= +github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -48,10 +50,19 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= +github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -59,8 +70,9 @@ github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -79,13 +91,17 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= @@ -94,6 +110,10 @@ github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= +github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= +github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= @@ -108,11 +128,13 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 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.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU= @@ -125,6 +147,10 @@ github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMK github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM= +github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM= +github.com/muhlemmer/httpforwarded v0.1.0 h1:x4DLrzXdliq8mprgUMR0olDvHGkou5BJsK/vWUetyzY= +github.com/muhlemmer/httpforwarded v0.1.0/go.mod h1:yo9czKedo2pdZhoXe+yDkGVbU0TJ0q9oQ90BVoDEtw0= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -143,6 +169,9 @@ github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= +github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russellhaering/goxmldsig v1.4.0 h1:8UcDh/xGyQiyrW+Fq5t8f+l2DLB1+zlhYzkPUJ7Qhys= github.com/russellhaering/goxmldsig v1.4.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -160,8 +189,9 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -170,19 +200,43 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -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.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zitadel/logging v0.6.0 h1:t5Nnt//r+m2ZhhoTmoPX+c96pbMarqJvW1Vq6xFTank= +github.com/zitadel/logging v0.6.0/go.mod h1:Y4CyAXHpl3Mig6JOszcV5Rqqsojj+3n7y2F591Mp/ow= +github.com/zitadel/oidc v1.13.5 h1:7jhh68NGZitLqwLiVU9Dtwa4IraJPFF1vS+4UupO93U= +github.com/zitadel/oidc v1.13.5/go.mod h1:rHs1DhU3Sv3tnI6bQRVlFa3u0lCwtR7S21WHY+yXgPA= +github.com/zitadel/oidc/v3 v3.27.0 h1:zeYpyRH0UcgdCjVHUYzSsqf1jbSwVMPVxYKOnRXstgU= +github.com/zitadel/oidc/v3 v3.27.0/go.mod h1:ZwBEqSviCpJVZiYashzo53bEGRGXi7amE5Q8PpQg9IM= +github.com/zitadel/schema v1.3.0 h1:kQ9W9tvIwZICCKWcMvCEweXET1OcOyGEuFbHs4o5kg0= +github.com/zitadel/schema v1.3.0/go.mod h1:NptN6mkBDFvERUCvZHlvWmmME+gmZ44xzwRXwhzsbtc= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs= +go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/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.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20221004215720-b9f4876ce741 h1:fGZugkZk2UgYBxtpKmvub51Yno1LJDeEsRp2xGD+0gY= golang.org/x/exp v0.0.0-20221004215720-b9f4876ce741/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= @@ -196,7 +250,6 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= @@ -204,18 +257,19 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.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= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -229,8 +283,8 @@ golang.org/x/sys v0.2.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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -238,14 +292,14 @@ 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.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/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.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 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-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -260,13 +314,13 @@ google.golang.org/api v0.147.0 h1:Can3FaQo9LlVqxJCodNmeZW/ib3/qKAY3rFeXiHo5gc= google.golang.org/api v0.147.0/go.mod h1:pQ/9j83DcmPd/5C9e2nFOdjjNkDZ1G+zkbK2uvdkJMs= 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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU= +google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c h1:jHkCUWkseRf+W+edG5hMzr/Uh1xkDREY4caybAq4dpY= google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -299,6 +353,7 @@ gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/polyculeconnect/helpers/helpers.go b/polyculeconnect/helpers/helpers.go index 5a6e659..fce58ab 100644 --- a/polyculeconnect/helpers/helpers.go +++ b/polyculeconnect/helpers/helpers.go @@ -4,7 +4,7 @@ import ( "context" "net/http" - "github.com/sirupsen/logrus" + "go.uber.org/zap" ) type ContextKey string @@ -16,7 +16,7 @@ type ResponseInfo struct { ContentLength int } -func HandleResponse(w http.ResponseWriter, r *http.Request, returncode int, content []byte, l *logrus.Logger) { +func HandleResponse(w http.ResponseWriter, r *http.Request, returncode int, content []byte, l *zap.SugaredLogger) { w.WriteHeader(returncode) n, err := w.Write(content) if err != nil { diff --git a/polyculeconnect/internal/db/base.go b/polyculeconnect/internal/db/base.go new file mode 100644 index 0000000..4010d49 --- /dev/null +++ b/polyculeconnect/internal/db/base.go @@ -0,0 +1,5 @@ +package db + +import "errors" + +var ErrNotFound = errors.New("not found") diff --git a/polyculeconnect/internal/db/client.go b/polyculeconnect/internal/db/client.go new file mode 100644 index 0000000..b466340 --- /dev/null +++ b/polyculeconnect/internal/db/client.go @@ -0,0 +1,69 @@ +package db + +import ( + "context" + "database/sql" + "errors" + "fmt" + "strings" + + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/config" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model" + _ "github.com/mattn/go-sqlite3" +) + +const clientRows = `"client"."id", "client"."secret", "client"."redirect_uris", "client"."trusted_peers", "client"."name"` + +type ClientDB interface { + GetClientByID(ctx context.Context, id string) (*model.Client, error) +} + +type sqlClientDB struct { + db *sql.DB +} + +func strArrayToSlice(rawVal string) []string { // TODO this won't work if there's more than one element + res := []string{} + insideStr, ok := strings.CutPrefix(rawVal, `["`) + if !ok { + return res + } + insideStr, ok = strings.CutSuffix(insideStr, `"]`) + if !ok { + return res + } + + return []string{insideStr} +} + +func clientFromRow(row *sql.Row) (*model.Client, error) { + var res model.Client + redirectURIsStr := "" + trustedPeersStr := "" + + if err := row.Scan(&res.ID, &res.Secret, &redirectURIsStr, &trustedPeersStr, &res.Name); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrNotFound + } + return nil, fmt.Errorf("invalid format for client: %w", err) + } + + res.ClientConfig.RedirectURIs = strArrayToSlice(redirectURIsStr) + res.ClientConfig.TrustedPeers = strArrayToSlice(trustedPeersStr) + + return &res, nil +} + +func (db *sqlClientDB) GetClientByID(ctx context.Context, id string) (*model.Client, error) { + query := fmt.Sprintf(`SELECT %s FROM "client" WHERE "id" = ?`, clientRows) + row := db.db.QueryRowContext(ctx, query, id) + return clientFromRow(row) +} + +func New(conf config.AppConfig) (*sqlClientDB, error) { + db, err := sql.Open("sqlite3", conf.StorageConfig.File) + if err != nil { + return nil, fmt.Errorf("failed to open DB: %w", err) + } + return &sqlClientDB{db: db}, nil +} diff --git a/polyculeconnect/middlewares/logger.go b/polyculeconnect/internal/middlewares/logger.go similarity index 96% rename from polyculeconnect/middlewares/logger.go rename to polyculeconnect/internal/middlewares/logger.go index 0808010..43c1d10 100644 --- a/polyculeconnect/middlewares/logger.go +++ b/polyculeconnect/internal/middlewares/logger.go @@ -5,7 +5,7 @@ import ( "net/http" "time" - "github.com/sirupsen/logrus" + "go.uber.org/zap" ) type loggedResponseWriter struct { @@ -33,7 +33,7 @@ func (lr *loggedResponseWriter) WriteHeader(statusCode int) { } type LoggerMiddleware struct { - l *logrus.Logger + l *zap.SugaredLogger h http.Handler } diff --git a/polyculeconnect/internal/middlewares/middlewarechain.go b/polyculeconnect/internal/middlewares/middlewarechain.go new file mode 100644 index 0000000..ba89469 --- /dev/null +++ b/polyculeconnect/internal/middlewares/middlewarechain.go @@ -0,0 +1,14 @@ +package middlewares + +import ( + "net/http" + + "go.uber.org/zap" +) + +func WithLogger(handler http.Handler, l *zap.SugaredLogger) http.Handler { + return &LoggerMiddleware{ + l: l, + h: handler, + } +} diff --git a/polyculeconnect/internal/model/client.go b/polyculeconnect/internal/model/client.go new file mode 100644 index 0000000..179bdaa --- /dev/null +++ b/polyculeconnect/internal/model/client.go @@ -0,0 +1,88 @@ +package model + +import ( + "time" + + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" +) + +type ClientConfig struct { + ID string + Secret string + RedirectURIs []string + TrustedPeers []string + Name string +} + +type Client struct { + ClientConfig +} + +func (c Client) GetID() string { + return c.ClientConfig.ID +} + +func (c Client) RedirectURIs() []string { + return c.ClientConfig.RedirectURIs +} + +func (c Client) PostLogoutRedirectURIs() []string { + return nil +} + +func (c Client) ApplicationType() op.ApplicationType { + return op.ApplicationTypeWeb // TODO: should we support more? +} + +func (c Client) AuthMethod() oidc.AuthMethod { + return oidc.AuthMethodNone +} + +func (c Client) ResponseTypes() []oidc.ResponseType { + return []oidc.ResponseType{oidc.ResponseTypeCode} +} + +func (c Client) GrantTypes() []oidc.GrantType { + return []oidc.GrantType{oidc.GrantTypeCode} +} + +func (c Client) LoginURL(id string) string { + return id +} + +func (c Client) AccessTokenType() op.AccessTokenType { + return op.AccessTokenTypeJWT +} + +func (c Client) IDTokenLifetime() time.Duration { + return 1 * time.Hour +} + +func (c Client) DevMode() bool { + return true +} + +func (c Client) RestrictAdditionalIdTokenScopes() func(scopes []string) []string { + return func(scopes []string) []string { + return scopes + } +} + +func (c Client) RestrictAdditionalAccessTokenScopes() func(scopes []string) []string { + return func(scopes []string) []string { + return scopes + } +} + +func (c Client) IsScopeAllowed(scope string) bool { + return true +} + +func (c Client) IDTokenUserinfoClaimsAssertion() bool { + return true +} + +func (c Client) ClockSkew() time.Duration { + return 0 +} diff --git a/polyculeconnect/internal/storage/local.go b/polyculeconnect/internal/storage/local.go new file mode 100644 index 0000000..c964712 --- /dev/null +++ b/polyculeconnect/internal/storage/local.go @@ -0,0 +1,4 @@ +package storage + +type LocalStorage struct { +} diff --git a/polyculeconnect/internal/storage/storage.go b/polyculeconnect/internal/storage/storage.go new file mode 100644 index 0000000..aef9b56 --- /dev/null +++ b/polyculeconnect/internal/storage/storage.go @@ -0,0 +1,125 @@ +package storage + +import ( + "context" + "fmt" + "time" + + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db" + "github.com/go-jose/go-jose/v4" + "github.com/zitadel/oidc/v3/pkg/oidc" + "github.com/zitadel/oidc/v3/pkg/op" +) + +func ErrNotImplemented(name string) error { + return fmt.Errorf("%s is not implemented", name) +} + +// Storage implements the Storage interface from zitadel/oidc/op +type Storage struct { + LocalStorage db.ClientDB +} + +/* +Auth storage interface +*/ +func (s *Storage) CreateAuthRequest(ctx context.Context, req *oidc.AuthRequest, userID string) (op.AuthRequest, error) { + return nil, ErrNotImplemented("CreateAuthRequest") +} + +func (s *Storage) AuthRequestByID(ctx context.Context, requestID string) (op.AuthRequest, error) { + return nil, ErrNotImplemented("AuthRequestByID") +} + +func (s *Storage) AuthRequestByCode(ctx context.Context, requestCode string) (op.AuthRequest, error) { + return nil, ErrNotImplemented("AuthRequestByCode") +} + +func (s *Storage) SaveAuthCode(ctx context.Context, id string, code string) error { + return ErrNotImplemented("SaveAuthCode") +} + +func (s *Storage) DeleteAuthRequest(ctx context.Context, id string) error { + return ErrNotImplemented("DeleteAuthRequest") +} + +func (s *Storage) CreateAccessToken(ctx context.Context, req op.TokenRequest) (accessTokenID string, expiration time.Time, err error) { + return "", time.Time{}, ErrNotImplemented("CreateAccessToken") +} + +func (s *Storage) CreateAccessAndRefreshTokens(ctx context.Context, request op.TokenRequest, currentRefreshToken string) (accessTokenID string, newRefreshTokenID string, expiration time.Time, err error) { + return "", "", time.Time{}, ErrNotImplemented("CreateAccessAndRefreshTokens") +} + +func (s *Storage) TokenRequestByRefreshToken(ctx context.Context, refreshTokenID string) (op.RefreshTokenRequest, error) { + return nil, ErrNotImplemented("TokenRequestByRefreshToken") +} + +func (s *Storage) TerminateSession(ctx context.Context, userID string, clientID string) error { + return ErrNotImplemented("TerminateSession") +} + +func (s *Storage) RevokeToken(ctx context.Context, tokenOrTokenID string, userID string, clientID string) *oidc.Error { + return nil +} + +func (s *Storage) GetRefreshTokenInfo(ctx context.Context, clientID string, stoken string) (string, string, error) { + return "", "", ErrNotImplemented("GetRefreshTokenInfo") +} + +func (s *Storage) SigningKey(ctx context.Context) (op.SigningKey, error) { + return nil, ErrNotImplemented("SigningKey") +} + +func (s *Storage) SignatureAlgorithms(ctx context.Context) ([]jose.SignatureAlgorithm, error) { + return nil, ErrNotImplemented("SignatureAlgorithms") +} + +func (s *Storage) KeySet(ctx context.Context) ([]op.Key, error) { + return nil, ErrNotImplemented("KeySet") +} + +/* + OP storage +*/ + +func (s *Storage) GetClientByClientID(ctx context.Context, clientID string) (op.Client, error) { + clt, err := s.LocalStorage.GetClientByID(ctx, clientID) + if err != nil { + return nil, fmt.Errorf("failed to get client from local storage: %w", err) + } + + return clt, nil +} + +func (s *Storage) AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string) error { + return ErrNotImplemented("AuthorizeClientIDSecret") +} + +func (s *Storage) SetUserinfoFromScopes(ctx context.Context, userinfo *oidc.UserInfo, userID, clientID string, scopes []string) error { + return ErrNotImplemented("SetUserinfoFromScopes") +} + +func (s *Storage) SetUserinfoFromToken(ctx context.Context, userinfo *oidc.UserInfo, tokenID, subject, origin string) error { + return ErrNotImplemented("SetUserinfoFromToken") +} + +func (s *Storage) SetIntrospectionFromToken(ctx context.Context, userinfo *oidc.IntrospectionResponse, tokenID, subject, clientID string) error { + return ErrNotImplemented("SetIntrospectionFromToken") +} + +func (s *Storage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clientID string, scopes []string) (map[string]interface{}, error) { + return nil, ErrNotImplemented("GetPrivateClaimsFromScopes") +} + +func (s *Storage) GetKeyByIDAndClientID(ctx context.Context, keyID, clientID string) (*jose.JSONWebKey, error) { + return nil, ErrNotImplemented("GetKeyByIDAndClientID") +} + +func (s *Storage) ValidateJWTProfileScopes(ctx context.Context, userID string, scopes []string) ([]string, error) { + return nil, ErrNotImplemented("ValidateJWTProfileScopes") +} + +func (s *Storage) Health(ctx context.Context) error { + return ErrNotImplemented("Health") +} diff --git a/polyculeconnect/logger/logger.go b/polyculeconnect/logger/logger.go index 31b8a0e..da777cf 100644 --- a/polyculeconnect/logger/logger.go +++ b/polyculeconnect/logger/logger.go @@ -1,10 +1,28 @@ package logger -import "github.com/sirupsen/logrus" +import ( + "fmt" -var L *logrus.Logger + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +var L *zap.SugaredLogger + +func Init(level zap.AtomicLevel) { + conf := zap.Config{ + Level: level, + Encoding: "console", + OutputPaths: []string{"stdout"}, + ErrorOutputPaths: []string{"stderr"}, + EncoderConfig: zap.NewDevelopmentEncoderConfig(), + } + conf.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + + if l, err := conf.Build(); err != nil { + panic(fmt.Errorf("failed to init logger: %w", err)) + } else { + L = l.Sugar() + } -func Init(level logrus.Level) { - L = logrus.New() - L.SetLevel(level) } diff --git a/polyculeconnect/middlewares/middlewarechain.go b/polyculeconnect/middlewares/middlewarechain.go deleted file mode 100644 index f1eb3ea..0000000 --- a/polyculeconnect/middlewares/middlewarechain.go +++ /dev/null @@ -1,14 +0,0 @@ -package middlewares - -import ( - "net/http" - - "github.com/sirupsen/logrus" -) - -func WithLogger(handler http.Handler, l *logrus.Logger) http.Handler { - return &LoggerMiddleware{ - l: l, - h: handler, - } -} diff --git a/polyculeconnect/polyculeconnect.db b/polyculeconnect/polyculeconnect.db new file mode 100644 index 0000000000000000000000000000000000000000..2708fa48bd056ecba49f0434efc9f22189d9f006 GIT binary patch literal 98304 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 literal 0 HcmV?d00001 diff --git a/polyculeconnect/server/server.go b/polyculeconnect/server/server.go index 84a8b5e..0785610 100644 --- a/polyculeconnect/server/server.go +++ b/polyculeconnect/server/server.go @@ -10,9 +10,9 @@ import ( "git.faercol.me/faercol/polyculeconnect/polyculeconnect/config" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/controller/ui" - "git.faercol.me/faercol/polyculeconnect/polyculeconnect/middlewares" - dex_server "github.com/dexidp/dex/server" - "github.com/sirupsen/logrus" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/middlewares" + "github.com/zitadel/oidc/v3/pkg/op" + "go.uber.org/zap" ) type Server struct { @@ -24,7 +24,7 @@ type Server struct { address string handler *http.ServeMux controllers map[string]http.Handler - l *logrus.Logger + l *zap.SugaredLogger } func newUnixListener(sockPath string) (net.Listener, error) { @@ -42,7 +42,7 @@ func newUnixListener(sockPath string) (net.Listener, error) { return sock, nil } -func New(appConf *config.AppConfig, dexSrv *dex_server.Server, logger *logrus.Logger) (*Server, error) { +func New(appConf *config.AppConfig, oidcHandler *op.Provider, logger *zap.SugaredLogger) (*Server, error) { var listener net.Listener var addr string var err error @@ -65,7 +65,7 @@ func New(appConf *config.AppConfig, dexSrv *dex_server.Server, logger *logrus.Lo controllers := map[string]http.Handler{ ui.StaticRoute: middlewares.WithLogger(ui.NewStaticController(appConf.StaticDir), logger), - "/": middlewares.WithLogger(ui.NewIndexController(logger, dexSrv, appConf.StaticDir), logger), + "/": middlewares.WithLogger(ui.NewIndexController(logger, oidcHandler, appConf.StaticDir), logger), } m := http.NewServeMux() -- 2.45.2 From 64e48a56892080fab26dd3a41ab5f34376acd9b8 Mon Sep 17 00:00:00 2001 From: Melora Hugues Date: Fri, 16 Aug 2024 11:02:44 +0200 Subject: [PATCH 2/7] Add basic way to get backend from query (#48) Because polyculeconnect is a OIDC proxy, we need to know which auth backend to use. This is provided using a query param or a form, so we need to get it from our own middleware. This commit adds the following elements: - basic DB storage for the backends - support for DB migrations and a first test migration (not definitive) - middleware to get the backend from the request and put it in the context - test that the backend exists in the auth flow --- polyculeconnect/cmd/db/migrate.go | 60 +++++++++++++++++++ polyculeconnect/cmd/serve/serve.go | 2 + polyculeconnect/go.mod | 21 +++---- polyculeconnect/go.sum | 43 ++++++------- .../internal/db/backend/backend.go | 46 ++++++++++++++ polyculeconnect/internal/db/base.go | 39 +++++++++++- .../internal/db/{ => client}/client.go | 31 ++++------ polyculeconnect/internal/middlewares/test.go | 40 +++++++++++++ polyculeconnect/internal/model/backend.go | 10 ++++ polyculeconnect/internal/storage/storage.go | 25 +++++++- .../0_create_backend_table.down.sql | 1 + .../migrations/0_create_backend_table.up.sql | 4 ++ 12 files changed, 268 insertions(+), 54 deletions(-) create mode 100644 polyculeconnect/cmd/db/migrate.go create mode 100644 polyculeconnect/internal/db/backend/backend.go rename polyculeconnect/internal/db/{ => client}/client.go (66%) create mode 100644 polyculeconnect/internal/middlewares/test.go create mode 100644 polyculeconnect/internal/model/backend.go create mode 100644 polyculeconnect/migrations/0_create_backend_table.down.sql create mode 100644 polyculeconnect/migrations/0_create_backend_table.up.sql diff --git a/polyculeconnect/cmd/db/migrate.go b/polyculeconnect/cmd/db/migrate.go new file mode 100644 index 0000000..92ca2de --- /dev/null +++ b/polyculeconnect/cmd/db/migrate.go @@ -0,0 +1,60 @@ +package db + +import ( + "fmt" + + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/utils" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/config" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db" + "github.com/golang-migrate/migrate/v4" + "github.com/golang-migrate/migrate/v4/database/sqlite3" + _ "github.com/golang-migrate/migrate/v4/source/file" + "github.com/spf13/cobra" +) + +// migrateCmd represents the db migrate command +var migrateCmd = &cobra.Command{ + Use: "migrate", + Short: "Run the database migrations", + Long: `Run the database migrations.`, + Run: func(cmd *cobra.Command, args []string) { + conf := utils.InitConfig("") + if err := runMigrations(conf); err != nil { + utils.Failf("Failed to run migrations: %s", err.Error()) + } + }, +} + +func runMigrations(conf *config.AppConfig) error { + storage, err := db.New(*conf) + if err != nil { + return fmt.Errorf("failed to connect to db: %w", err) + } + driver, err := sqlite3.WithInstance(storage.DB(), &sqlite3.Config{}) + if err != nil { + return fmt.Errorf("failed to open sqlite3 driver: %w", err) + } + + m, err := migrate.NewWithDatabaseInstance("file://migrations", "", driver) + if err != nil { + return fmt.Errorf("failed to init migrator: %w", err) + } + if err := m.Up(); err != nil { + return fmt.Errorf("failed to run migrations: %w", err) + } + return nil +} + +func init() { + dbCmd.AddCommand(migrateCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // dbCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // dbCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/polyculeconnect/cmd/serve/serve.go b/polyculeconnect/cmd/serve/serve.go index 01b814b..1fd4080 100644 --- a/polyculeconnect/cmd/serve/serve.go +++ b/polyculeconnect/cmd/serve/serve.go @@ -10,6 +10,7 @@ import ( "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/utils" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/middlewares" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/storage" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/logger" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/server" @@ -68,6 +69,7 @@ func serve() { options := []op.Option{ op.WithAllowInsecure(), op.WithLogger(slogger), + op.WithHttpInterceptors(middlewares.WithBackendFromRequestMiddleware), } provider, err := op.NewProvider(&opConf, &st, op.StaticIssuer(conf.Issuer), options...) if err != nil { diff --git a/polyculeconnect/go.mod b/polyculeconnect/go.mod index 0e65fd2..2730293 100644 --- a/polyculeconnect/go.mod +++ b/polyculeconnect/go.mod @@ -7,11 +7,13 @@ toolchain go1.22.6 require ( github.com/dexidp/dex v0.0.0-20231014000322-089f374d4f3e github.com/go-jose/go-jose/v4 v4.0.4 + github.com/golang-migrate/migrate/v4 v4.17.1 + github.com/google/uuid v1.6.0 github.com/kelseyhightower/envconfig v1.4.0 + github.com/mattn/go-sqlite3 v1.14.17 github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.7.0 github.com/stretchr/testify v1.9.0 - github.com/zitadel/oidc v1.13.5 github.com/zitadel/oidc/v3 v3.27.0 go.uber.org/zap v1.24.0 go.uber.org/zap/exp v0.2.0 @@ -42,20 +44,19 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/s2a-go v0.1.7 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/mux v1.8.0 // indirect - github.com/gorilla/schema v1.2.0 // indirect github.com/gorilla/securecookie v1.1.2 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.11 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect github.com/lib/pq v1.10.9 // indirect github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect - github.com/mattn/go-sqlite3 v1.14.17 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect @@ -81,15 +82,15 @@ require ( go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.10.0 // indirect golang.org/x/crypto v0.25.0 // indirect - golang.org/x/exp v0.0.0-20221004215720-b9f4876ce741 // indirect + golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 // indirect golang.org/x/net v0.26.0 // indirect golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect - google.golang.org/api v0.147.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect - google.golang.org/grpc v1.58.3 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/api v0.150.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect + google.golang.org/grpc v1.59.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/polyculeconnect/go.sum b/polyculeconnect/go.sum index 3a0d6b9..6758873 100644 --- a/polyculeconnect/go.sum +++ b/polyculeconnect/go.sum @@ -65,6 +65,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4= +github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -102,18 +104,21 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= -github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= -github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= @@ -205,8 +210,6 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zitadel/logging v0.6.0 h1:t5Nnt//r+m2ZhhoTmoPX+c96pbMarqJvW1Vq6xFTank= github.com/zitadel/logging v0.6.0/go.mod h1:Y4CyAXHpl3Mig6JOszcV5Rqqsojj+3n7y2F591Mp/ow= -github.com/zitadel/oidc v1.13.5 h1:7jhh68NGZitLqwLiVU9Dtwa4IraJPFF1vS+4UupO93U= -github.com/zitadel/oidc v1.13.5/go.mod h1:rHs1DhU3Sv3tnI6bQRVlFa3u0lCwtR7S21WHY+yXgPA= github.com/zitadel/oidc/v3 v3.27.0 h1:zeYpyRH0UcgdCjVHUYzSsqf1jbSwVMPVxYKOnRXstgU= github.com/zitadel/oidc/v3 v3.27.0/go.mod h1:ZwBEqSviCpJVZiYashzo53bEGRGXi7amE5Q8PpQg9IM= github.com/zitadel/schema v1.3.0 h1:kQ9W9tvIwZICCKWcMvCEweXET1OcOyGEuFbHs4o5kg0= @@ -238,8 +241,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20221004215720-b9f4876ce741 h1:fGZugkZk2UgYBxtpKmvub51Yno1LJDeEsRp2xGD+0gY= -golang.org/x/exp v0.0.0-20221004215720-b9f4876ce741/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0 h1:pVgRXcIictcr+lBQIFeiwuwtDIs4eL21OuM9nyAADmo= +golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 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-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -310,26 +313,26 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.147.0 h1:Can3FaQo9LlVqxJCodNmeZW/ib3/qKAY3rFeXiHo5gc= -google.golang.org/api v0.147.0/go.mod h1:pQ/9j83DcmPd/5C9e2nFOdjjNkDZ1G+zkbK2uvdkJMs= +google.golang.org/api v0.150.0 h1:Z9k22qD289SZ8gCJrk4DrWXkNjtfvKAUo/l1ma8eBYE= +google.golang.org/api v0.150.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg= 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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= -google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU= -google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c h1:jHkCUWkseRf+W+edG5hMzr/Uh1xkDREY4caybAq4dpY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= +google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= +google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b h1:CIC2YMXmIhYw6evmhPxBKJ4fmLbOFtXQN/GV3XOZR8k= +google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:IBQ646DjkDkvUIsVq/cc03FUFQ9wbZu7yE396YcL870= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= -google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -341,8 +344,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.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= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/polyculeconnect/internal/db/backend/backend.go b/polyculeconnect/internal/db/backend/backend.go new file mode 100644 index 0000000..8a7928f --- /dev/null +++ b/polyculeconnect/internal/db/backend/backend.go @@ -0,0 +1,46 @@ +package backend + +import ( + "context" + "database/sql" + "errors" + "fmt" + + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model" +) + +var ErrNotFound = errors.New("backend not found") + +// const backendRows = `"id", "name", "oidc_id", "oidc_secret"` +const backendRows = `"id", "name"` + +type BackendDB interface { + GetBackendByName(ctx context.Context, name string) (*model.Backend, error) +} + +type sqlBackendDB struct { + db *sql.DB +} + +func backendFromRow(row sql.Row) (*model.Backend, error) { + var res model.Backend + + if err := row.Scan(&res.ID, &res.Name); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrNotFound + } + return nil, fmt.Errorf("invalid format for backend: %w", err) + } + return &res, nil +} + +func (db *sqlBackendDB) GetBackendByName(ctx context.Context, name string) (*model.Backend, error) { + query := fmt.Sprintf(`SELECT %s FROM "backend" WHERE "name" = ?`, backendRows) + fmt.Println(query, name) + row := db.db.QueryRowContext(ctx, query, name) + return backendFromRow(*row) +} + +func New(db *sql.DB) *sqlBackendDB { + return &sqlBackendDB{db: db} +} diff --git a/polyculeconnect/internal/db/base.go b/polyculeconnect/internal/db/base.go index 4010d49..dabfded 100644 --- a/polyculeconnect/internal/db/base.go +++ b/polyculeconnect/internal/db/base.go @@ -1,5 +1,40 @@ package db -import "errors" +import ( + "database/sql" + "fmt" -var ErrNotFound = errors.New("not found") + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/config" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db/backend" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db/client" +) + +type Storage interface { + DB() *sql.DB + ClientStorage() client.ClientDB + BackendStorage() backend.BackendDB +} + +type sqlStorage struct { + db *sql.DB +} + +func (s *sqlStorage) DB() *sql.DB { + return s.db +} + +func (s *sqlStorage) ClientStorage() client.ClientDB { + return client.New(s.db) +} + +func (s *sqlStorage) BackendStorage() backend.BackendDB { + return backend.New(s.db) +} + +func New(conf config.AppConfig) (Storage, error) { + db, err := sql.Open("sqlite3", conf.StorageConfig.File) + if err != nil { + return nil, fmt.Errorf("failed to open DB: %w", err) + } + return &sqlStorage{db: db}, nil +} diff --git a/polyculeconnect/internal/db/client.go b/polyculeconnect/internal/db/client/client.go similarity index 66% rename from polyculeconnect/internal/db/client.go rename to polyculeconnect/internal/db/client/client.go index b466340..0b90045 100644 --- a/polyculeconnect/internal/db/client.go +++ b/polyculeconnect/internal/db/client/client.go @@ -1,17 +1,18 @@ -package db +package client import ( "context" "database/sql" + "encoding/json" "errors" "fmt" - "strings" - "git.faercol.me/faercol/polyculeconnect/polyculeconnect/config" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model" _ "github.com/mattn/go-sqlite3" ) +var ErrNotFound = errors.New("not found") + const clientRows = `"client"."id", "client"."secret", "client"."redirect_uris", "client"."trusted_peers", "client"."name"` type ClientDB interface { @@ -22,18 +23,12 @@ type sqlClientDB struct { db *sql.DB } -func strArrayToSlice(rawVal string) []string { // TODO this won't work if there's more than one element - res := []string{} - insideStr, ok := strings.CutPrefix(rawVal, `["`) - if !ok { - return res +func strArrayToSlice(rawVal string) []string { + var res []string + if err := json.Unmarshal([]byte(rawVal), &res); err != nil { + return nil } - insideStr, ok = strings.CutSuffix(insideStr, `"]`) - if !ok { - return res - } - - return []string{insideStr} + return res } func clientFromRow(row *sql.Row) (*model.Client, error) { @@ -60,10 +55,6 @@ func (db *sqlClientDB) GetClientByID(ctx context.Context, id string) (*model.Cli return clientFromRow(row) } -func New(conf config.AppConfig) (*sqlClientDB, error) { - db, err := sql.Open("sqlite3", conf.StorageConfig.File) - if err != nil { - return nil, fmt.Errorf("failed to open DB: %w", err) - } - return &sqlClientDB{db: db}, nil +func New(db *sql.DB) *sqlClientDB { + return &sqlClientDB{db: db} } diff --git a/polyculeconnect/internal/middlewares/test.go b/polyculeconnect/internal/middlewares/test.go new file mode 100644 index 0000000..548714b --- /dev/null +++ b/polyculeconnect/internal/middlewares/test.go @@ -0,0 +1,40 @@ +package middlewares + +import ( + "context" + "net/http" +) + +const ( + backendNameQueryParam = "connector_id" + backendCtxKeyName = "backendName" +) + +type BackendFromRequestMiddleware struct { + h http.Handler +} + +func (m *BackendFromRequestMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + // TODO: handle this better + w.WriteHeader(http.StatusBadRequest) + return + } + + backendName := r.Form.Get(backendNameQueryParam) + // TODO this should be explicitly handled + if backendName == "" { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte("no backend id provided")) + return + } + + // TODO we should test that this backend actually exists here + + ctx := context.WithValue(r.Context(), backendCtxKeyName, backendName) + m.h.ServeHTTP(w, r.WithContext(ctx)) +} + +func WithBackendFromRequestMiddleware(input http.Handler) http.Handler { + return &BackendFromRequestMiddleware{h: input} +} diff --git a/polyculeconnect/internal/model/backend.go b/polyculeconnect/internal/model/backend.go new file mode 100644 index 0000000..a832d33 --- /dev/null +++ b/polyculeconnect/internal/model/backend.go @@ -0,0 +1,10 @@ +package model + +import "github.com/google/uuid" + +type Backend struct { + ID uuid.UUID + Name string + OIDCID string + OIDCSecret string +} diff --git a/polyculeconnect/internal/storage/storage.go b/polyculeconnect/internal/storage/storage.go index aef9b56..22aa245 100644 --- a/polyculeconnect/internal/storage/storage.go +++ b/polyculeconnect/internal/storage/storage.go @@ -2,6 +2,7 @@ package storage import ( "context" + "errors" "fmt" "time" @@ -17,13 +18,23 @@ func ErrNotImplemented(name string) error { // Storage implements the Storage interface from zitadel/oidc/op type Storage struct { - LocalStorage db.ClientDB + LocalStorage db.Storage } /* Auth storage interface */ func (s *Storage) CreateAuthRequest(ctx context.Context, req *oidc.AuthRequest, userID string) (op.AuthRequest, error) { + // validate that the connector is correct + backendName, ok := stringFromCtx(ctx, "backendName") + if !ok { + return nil, errors.New("no backend name provided") + } + _, err := s.LocalStorage.BackendStorage().GetBackendByName(ctx, backendName) + if err != nil { + return nil, fmt.Errorf("failed to get backend: %w", err) + } + return nil, ErrNotImplemented("CreateAuthRequest") } @@ -84,7 +95,7 @@ func (s *Storage) KeySet(ctx context.Context) ([]op.Key, error) { */ func (s *Storage) GetClientByClientID(ctx context.Context, clientID string) (op.Client, error) { - clt, err := s.LocalStorage.GetClientByID(ctx, clientID) + clt, err := s.LocalStorage.ClientStorage().GetClientByID(ctx, clientID) if err != nil { return nil, fmt.Errorf("failed to get client from local storage: %w", err) } @@ -123,3 +134,13 @@ func (s *Storage) ValidateJWTProfileScopes(ctx context.Context, userID string, s func (s *Storage) Health(ctx context.Context) error { return ErrNotImplemented("Health") } + +func stringFromCtx(ctx context.Context, key string) (string, bool) { + rawVal := ctx.Value(key) + if rawVal == nil { + return "", false + } + + val, ok := rawVal.(string) + return val, ok +} diff --git a/polyculeconnect/migrations/0_create_backend_table.down.sql b/polyculeconnect/migrations/0_create_backend_table.down.sql new file mode 100644 index 0000000..898fe6b --- /dev/null +++ b/polyculeconnect/migrations/0_create_backend_table.down.sql @@ -0,0 +1 @@ +DROP TABLE "backend"; \ No newline at end of file diff --git a/polyculeconnect/migrations/0_create_backend_table.up.sql b/polyculeconnect/migrations/0_create_backend_table.up.sql new file mode 100644 index 0000000..44c8d03 --- /dev/null +++ b/polyculeconnect/migrations/0_create_backend_table.up.sql @@ -0,0 +1,4 @@ +CREATE TABLE "backend" ( + id TEXT NOT NULL PRIMARY KEY, + name TEXT NOT NULL UNIQUE +); -- 2.45.2 From 9ba477174d2b4552b9d8ec763854fb4340300e5c Mon Sep 17 00:00:00 2001 From: Melora Hugues Date: Fri, 16 Aug 2024 11:28:32 +0200 Subject: [PATCH 3/7] Allow migrating up and down --- polyculeconnect/cmd/db/migrate.go | 46 +++++++++++++++++++++++++---- polyculeconnect/polyculeconnect.db | Bin 98304 -> 118784 bytes 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/polyculeconnect/cmd/db/migrate.go b/polyculeconnect/cmd/db/migrate.go index 92ca2de..bf5749f 100644 --- a/polyculeconnect/cmd/db/migrate.go +++ b/polyculeconnect/cmd/db/migrate.go @@ -2,6 +2,7 @@ package db import ( "fmt" + "strconv" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/utils" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/config" @@ -19,13 +20,40 @@ var migrateCmd = &cobra.Command{ Long: `Run the database migrations.`, Run: func(cmd *cobra.Command, args []string) { conf := utils.InitConfig("") - if err := runMigrations(conf); err != nil { + up, nbSteps := parseArgs(args) + if err := runMigrations(conf, up, nbSteps); err != nil { utils.Failf("Failed to run migrations: %s", err.Error()) } }, } -func runMigrations(conf *config.AppConfig) error { +func parseArgs(args []string) (bool, int) { + if len(args) == 0 { + return true, 0 + } + var actionUp bool + switch args[0] { + case "up": + actionUp = true + case "down": + actionUp = false + default: + actionUp = true + } + + nbSteps := 0 + if len(args) > 1 { + var err error + nbSteps, err = strconv.Atoi(args[1]) + if err != nil { + return actionUp, 0 + } + } + + return actionUp, nbSteps +} + +func runMigrations(conf *config.AppConfig, up bool, nbSteps int) error { storage, err := db.New(*conf) if err != nil { return fmt.Errorf("failed to connect to db: %w", err) @@ -39,10 +67,18 @@ func runMigrations(conf *config.AppConfig) error { if err != nil { return fmt.Errorf("failed to init migrator: %w", err) } - if err := m.Up(); err != nil { - return fmt.Errorf("failed to run migrations: %w", err) + + if nbSteps > 0 { + multiplier := 1 + if !up { + multiplier = -1 + } + return m.Steps(multiplier * nbSteps) } - return nil + if up { + return m.Up() + } + return m.Down() } func init() { diff --git a/polyculeconnect/polyculeconnect.db b/polyculeconnect/polyculeconnect.db index 2708fa48bd056ecba49f0434efc9f22189d9f006..870928863414f696e7ae1b0d6937a641a3e79dbd 100644 GIT binary patch delta 576 zcmZo@U~5>wK0#Vgnt_2q7KmX$exi;sqjY1!)&#~S{u)AT&lvcR@{933<-N?iisu5) zMD8=(Gr4NG>^Y`#{^QVM-_7=ny@x$^v!cK*w)#>Dc5!KG#zx1I#H5_mq{QUx)VvfZ zCG8yK>KNjx5aQ_M%2=erG42o3nc8$~l ztH?{tO+`}^>gO34>dK|5E5#=6D9y+)*_cmE9BOd95tJdTD+!XFY{(~350f*5GGuba z*~K;W85{XC^HNeP%2JDpGxPJ}OY<@dOH+%JGg5OC<8w39ixNwK!o^ZhA3&^C@bq(W zjZi?^Y`#{@bi5P|UG?Y6N4pIsmz96kPxS -- 2.45.2 From b0e0a19c97a26413a5eed7288a44815e680e9bce Mon Sep 17 00:00:00 2001 From: Melora Hugues Date: Fri, 16 Aug 2024 13:49:59 +0200 Subject: [PATCH 4/7] Replace previous backend service with new storage (#48) --- polyculeconnect/cmd/backend/add.go | 31 ++- polyculeconnect/cmd/backend/remove.go | 18 +- polyculeconnect/cmd/backend/show.go | 37 +-- .../internal/db/backend/backend.go | 79 ++++++- polyculeconnect/internal/model/backend.go | 10 +- .../migrations/0_create_backend_table.up.sql | 6 +- polyculeconnect/polyculeconnect.db | Bin 118784 -> 118784 bytes polyculeconnect/services/backend/backend.go | 123 ---------- .../services/backend/backend_test.go | 215 ------------------ 9 files changed, 142 insertions(+), 377 deletions(-) delete mode 100644 polyculeconnect/services/backend/backend.go delete mode 100644 polyculeconnect/services/backend/backend_test.go diff --git a/polyculeconnect/cmd/backend/add.go b/polyculeconnect/cmd/backend/add.go index ba70a18..7d21a34 100644 --- a/polyculeconnect/cmd/backend/add.go +++ b/polyculeconnect/cmd/backend/add.go @@ -1,11 +1,14 @@ package cmd import ( + "context" "fmt" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/utils" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/logger" - "git.faercol.me/faercol/polyculeconnect/polyculeconnect/services/backend" + "github.com/google/uuid" "github.com/spf13/cobra" ) @@ -38,7 +41,11 @@ Parameters to provide: func addNewBackend() { c := utils.InitConfig("") logger.Init(c.LogLevel) - s := utils.InitStorage(c) + + s, err := db.New(*c) + if err != nil { + utils.Failf("failed to init storage: %s", err.Error()) + } if backendClientID == "" { utils.Fail("Empty client ID") @@ -47,15 +54,19 @@ func addNewBackend() { utils.Fail("Empty client secret") } - backendConf := backend.BackendConfig{ - Issuer: backendIssuer, - ClientID: backendClientID, - ClientSecret: backendClientSecret, - RedirectURI: c.RedirectURI(), - ID: backendID, - Name: backendName, + backendIDUUID := uuid.New() + + backendConf := model.Backend{ + ID: backendIDUUID, + Name: backendName, + OIDCConfig: model.BackendOIDCConfig{ + ClientID: backendClientID, + ClientSecret: backendClientSecret, + Issuer: backendIssuer, + RedirectURI: c.RedirectURI(), + }, } - if err := backend.New(s).AddBackend(backendConf); err != nil { + if err := s.BackendStorage().AddBackend(context.Background(), &backendConf); err != nil { utils.Failf("Failed to add new backend to storage: %s", err.Error()) } diff --git a/polyculeconnect/cmd/backend/remove.go b/polyculeconnect/cmd/backend/remove.go index de52874..a74de87 100644 --- a/polyculeconnect/cmd/backend/remove.go +++ b/polyculeconnect/cmd/backend/remove.go @@ -1,12 +1,14 @@ package cmd import ( + "context" "errors" "fmt" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/utils" - "git.faercol.me/faercol/polyculeconnect/polyculeconnect/services/backend" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db" "github.com/dexidp/dex/storage" + "github.com/google/uuid" "github.com/spf13/cobra" ) @@ -20,10 +22,18 @@ var backendRemoveCmd = &cobra.Command{ }, } -func removeBackend(backendID string) { - s := utils.InitStorage(utils.InitConfig("")) +func removeBackend(backendIDStr string) { + backendID, err := uuid.Parse(backendIDStr) + if err != nil { + utils.Failf("Invalid UUID format: %s", err.Error()) + } - if err := backend.New(s).RemoveBackend(backendID); err != nil { + s, err := db.New(*utils.InitConfig("")) + if err != nil { + utils.Failf("Failed to init storage: %s", err.Error()) + } + + if err := s.BackendStorage().DeleteBackend(context.Background(), backendID); err != nil { if !errors.Is(err, storage.ErrNotFound) { utils.Failf("Failed to remove backend: %s", err.Error()) } diff --git a/polyculeconnect/cmd/backend/show.go b/polyculeconnect/cmd/backend/show.go index 1fc8253..6999e5c 100644 --- a/polyculeconnect/cmd/backend/show.go +++ b/polyculeconnect/cmd/backend/show.go @@ -1,11 +1,13 @@ package cmd import ( + "context" "errors" "fmt" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/utils" - "git.faercol.me/faercol/polyculeconnect/polyculeconnect/services/backend" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db/backend" "github.com/dexidp/dex/storage" "github.com/spf13/cobra" ) @@ -19,36 +21,39 @@ Optional parameters: - app-id: id of the backend to display. If empty, display the list of available backends instead`, Args: cobra.MaximumNArgs(1), Run: func(cmd *cobra.Command, args []string) { - s := utils.InitStorage(utils.InitConfig("")) + s, err := db.New(*utils.InitConfig("")) + if err != nil { + utils.Failf("Failed to init storage: %s", err.Error()) + } if len(args) > 0 { - showBackend(args[0], backend.New(s)) + showBackend(args[0], s.BackendStorage()) } else { - listBackends(backend.New(s)) + listBackends(s.BackendStorage()) } }, } -func showBackend(backendId string, backendService backend.Service) { - backendConfig, err := backendService.GetBackend(backendId) +func showBackend(backendName string, backendService backend.BackendDB) { + backendConfig, err := backendService.GetBackendByName(context.Background(), backendName) if err != nil { if errors.Is(err, storage.ErrNotFound) { - utils.Failf("Backend with ID %s does not exist\n", backendId) + utils.Failf("Backend with name %s does not exist\n", backendName) } - utils.Failf("Failed to get config for backend %s: %q\n", backendId, err.Error()) + utils.Failf("Failed to get config for backend %s: %q\n", backendName, err.Error()) } fmt.Println("Backend config:") - printProperty("ID", backendConfig.ID) + printProperty("ID", backendConfig.ID.String()) printProperty("Name", backendConfig.Name) - printProperty("Issuer", backendConfig.Issuer) - printProperty("Client ID", backendConfig.ClientID) - printProperty("Client secret", backendConfig.ClientSecret) - printProperty("Redirect URI", backendConfig.RedirectURI) + printProperty("Issuer", backendConfig.OIDCConfig.Issuer) + printProperty("Client ID", backendConfig.OIDCConfig.ClientID) + printProperty("Client secret", backendConfig.OIDCConfig.ClientSecret) + printProperty("Redirect URI", backendConfig.OIDCConfig.RedirectURI) } -func listBackends(backendService backend.Service) { - backends, err := backendService.ListBackends() +func listBackends(backendStorage backend.BackendDB) { + backends, err := backendStorage.GetAllBackends(context.Background()) if err != nil { utils.Failf("Failed to list backends: %q\n", err.Error()) } @@ -58,7 +63,7 @@ func listBackends(backendService backend.Service) { return } for _, b := range backends { - fmt.Printf("\t - %s: (%s) - %s\n", b.ID, b.Name, b.Issuer) + fmt.Printf("\t - %s: (%s) - %s\n", b.ID, b.Name, b.OIDCConfig.Issuer) } } diff --git a/polyculeconnect/internal/db/backend/backend.go b/polyculeconnect/internal/db/backend/backend.go index 8a7928f..3708442 100644 --- a/polyculeconnect/internal/db/backend/backend.go +++ b/polyculeconnect/internal/db/backend/backend.go @@ -7,25 +7,35 @@ import ( "fmt" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model" + "github.com/google/uuid" ) var ErrNotFound = errors.New("backend not found") -// const backendRows = `"id", "name", "oidc_id", "oidc_secret"` -const backendRows = `"id", "name"` +const backendRows = `"id", "name", "oidc_issuer", "oidc_client_id", "oidc_client_secret", "oidc_redirect_uri"` + +type scannable interface { + Scan(dest ...any) error +} type BackendDB interface { + GetAllBackends(ctx context.Context) ([]*model.Backend, error) + GetBackendByName(ctx context.Context, name string) (*model.Backend, error) + + AddBackend(ctx context.Context, newBackend *model.Backend) error + + DeleteBackend(ctx context.Context, id uuid.UUID) error } type sqlBackendDB struct { db *sql.DB } -func backendFromRow(row sql.Row) (*model.Backend, error) { +func backendFromRow(row scannable) (*model.Backend, error) { var res model.Backend - if err := row.Scan(&res.ID, &res.Name); err != nil { + if err := row.Scan(&res.ID, &res.Name, &res.OIDCConfig.Issuer, &res.OIDCConfig.ClientID, &res.OIDCConfig.ClientSecret, &res.OIDCConfig.RedirectURI); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } @@ -36,9 +46,66 @@ func backendFromRow(row sql.Row) (*model.Backend, error) { func (db *sqlBackendDB) GetBackendByName(ctx context.Context, name string) (*model.Backend, error) { query := fmt.Sprintf(`SELECT %s FROM "backend" WHERE "name" = ?`, backendRows) - fmt.Println(query, name) row := db.db.QueryRowContext(ctx, query, name) - return backendFromRow(*row) + return backendFromRow(row) +} + +func (db *sqlBackendDB) GetAllBackends(ctx context.Context) ([]*model.Backend, error) { + rows, err := db.db.QueryContext(ctx, fmt.Sprintf(`SELECT %s FROM "backend"`, backendRows)) + if err != nil { + return nil, err + } + + var res []*model.Backend + for rows.Next() { + b, err := backendFromRow(rows) + if err != nil { + return nil, err + } + res = append(res, b) + } + return res, nil +} + +func (db *sqlBackendDB) AddBackend(ctx context.Context, newBackend *model.Backend) error { + tx, err := db.db.BeginTx(ctx, nil) + if err != nil { + return fmt.Errorf("failed to start transaction: %w", err) + } + defer func() { _ = tx.Rollback() }() + + query := fmt.Sprintf(`INSERT INTO "backend" (%s) VALUES ($1, $2, $3, $4, $5, $6)`, backendRows) + _, err = tx.ExecContext( + ctx, query, + newBackend.ID, newBackend.Name, + newBackend.OIDCConfig.Issuer, newBackend.OIDCConfig.ClientID, + newBackend.OIDCConfig.ClientSecret, newBackend.OIDCConfig.RedirectURI, + ) + if err != nil { + return fmt.Errorf("failed to insert in DB: %w", err) + } + + if err := tx.Commit(); err != nil { + return fmt.Errorf("failed to commit transaction: %w", err) + } + return nil +} + +func (db *sqlBackendDB) DeleteBackend(ctx context.Context, id uuid.UUID) error { + tx, err := db.db.BeginTx(ctx, nil) + if err != nil { + return fmt.Errorf("failed to start transaction: %w", err) + } + defer func() { _ = tx.Rollback() }() + + if _, err := tx.ExecContext(ctx, `DELETE FROM "backend" WHERE id = $1`, id.String()); err != nil { + return fmt.Errorf("failed to run query: %w", err) + } + + if err := tx.Commit(); err != nil { + return fmt.Errorf("failed to commit transaction: %w", err) + } + return nil } func New(db *sql.DB) *sqlBackendDB { diff --git a/polyculeconnect/internal/model/backend.go b/polyculeconnect/internal/model/backend.go index a832d33..1453733 100644 --- a/polyculeconnect/internal/model/backend.go +++ b/polyculeconnect/internal/model/backend.go @@ -2,9 +2,15 @@ package model import "github.com/google/uuid" +type BackendOIDCConfig struct { + Issuer string + ClientID string + ClientSecret string + RedirectURI string +} + type Backend struct { ID uuid.UUID Name string - OIDCID string - OIDCSecret string + OIDCConfig BackendOIDCConfig } diff --git a/polyculeconnect/migrations/0_create_backend_table.up.sql b/polyculeconnect/migrations/0_create_backend_table.up.sql index 44c8d03..498fef0 100644 --- a/polyculeconnect/migrations/0_create_backend_table.up.sql +++ b/polyculeconnect/migrations/0_create_backend_table.up.sql @@ -1,4 +1,8 @@ CREATE TABLE "backend" ( id TEXT NOT NULL PRIMARY KEY, - name TEXT NOT NULL UNIQUE + name TEXT NOT NULL UNIQUE, + oidc_issuer TEXT NOT NULL, + oidc_client_id TEXT NOT NULL, + oidc_client_secret TEXT NOT NULL, + oidc_redirect_uri TEXT NOT NULL ); diff --git a/polyculeconnect/polyculeconnect.db b/polyculeconnect/polyculeconnect.db index 870928863414f696e7ae1b0d6937a641a3e79dbd..8cfa771ce82dd4511fe555c0dd4188c2d49c0265 100644 GIT binary patch delta 432 zcmZozz}~QceS);0G6MsHED*zh;zS)|M&-tYtqF|F{23#svj;FLutu`?u*Ysz6wqbk zYGRaN7nhc1Y>u4%K8!J^UWZEo2=X&ilH)Uri%V0B6hd4hLKOV`fjHF12T3|PCo?s# zBtA2Rkc#5eQEb~D9nQGcgNc9YWLn4HZ83O@k`2L2ncfMj6UENyUv-$I=^ dl#>|y;U@fGkO7(@y;;!V1wRKnvnVIXWdOp{amD}u delta 105 zcmZozz}~QceS);0Gy?;JED*zh{6rmNM(M_ctqF|F{28B3XAfXhV135k!yda?QD7Gv zXQ>3cxU@85qvQ1CFvgti(cz41JvMVV{E(j%z|ATHG(~!|pu-D(4t8cy&XW9+d;t3n B8Os0w diff --git a/polyculeconnect/services/backend/backend.go b/polyculeconnect/services/backend/backend.go deleted file mode 100644 index 9f9d6f5..0000000 --- a/polyculeconnect/services/backend/backend.go +++ /dev/null @@ -1,123 +0,0 @@ -package backend - -import ( - "encoding/json" - "errors" - "fmt" - - "git.faercol.me/faercol/polyculeconnect/polyculeconnect/connector" - "github.com/dexidp/dex/connector/oidc" - "github.com/dexidp/dex/storage" -) - -var ErrUnsupportedType = errors.New("unsupported connector type") - -type BackendConfig struct { - ID string - Name string - Issuer string - ClientID string - ClientSecret string - RedirectURI string -} - -func (bc *BackendConfig) OIDC() oidc.Config { - return oidc.Config{ - Issuer: bc.Issuer, - ClientID: bc.ClientID, - ClientSecret: bc.ClientSecret, - RedirectURI: bc.RedirectURI, - } -} - -func (bc *BackendConfig) Storage() (storage.Connector, error) { - oidcJSON, err := json.Marshal(bc.OIDC()) - if err != nil { - return storage.Connector{}, fmt.Errorf("failed to serialize oidc config: %w", err) - } - - return storage.Connector{ - ID: bc.ID, - Type: "oidc", - Name: bc.Name, - Config: oidcJSON, - }, nil -} - -func (bc *BackendConfig) FromConnector(connector storage.Connector) error { - var oidc oidc.Config - if connector.Type != "oidc" { - return ErrUnsupportedType - } - if err := json.Unmarshal(connector.Config, &oidc); err != nil { - return fmt.Errorf("invalid OIDC config: %w", err) - } - bc.ID = connector.ID - bc.Name = connector.Name - bc.ClientID = oidc.ClientID - bc.ClientSecret = oidc.ClientSecret - bc.Issuer = oidc.Issuer - bc.RedirectURI = oidc.RedirectURI - return nil -} - -type Service interface { - ListBackends() ([]BackendConfig, error) - GetBackend(id string) (BackendConfig, error) - AddBackend(config BackendConfig) error - RemoveBackend(id string) error -} - -type concreteBackendService struct { - s storage.Storage -} - -func (cbs *concreteBackendService) ListBackends() ([]BackendConfig, error) { - connectors, err := cbs.s.ListConnectors() - if err != nil { - return nil, fmt.Errorf("failed to get connectors from storage: %w", err) - } - var res []BackendConfig - for _, c := range connectors { - - // We know that this type is special, we don't want to use it at all here - if c.Type == connector.TypeRefuseAll { - continue - } - - var b BackendConfig - if err := b.FromConnector(c); err != nil { - return res, err - } - res = append(res, b) - } - return res, nil -} - -func (cbs *concreteBackendService) GetBackend(connectorID string) (BackendConfig, error) { - c, err := cbs.s.GetConnector(connectorID) - if err != nil { - return BackendConfig{}, fmt.Errorf("failed to get connector from storage: %w", err) - } - var res BackendConfig - if err := res.FromConnector(c); err != nil { - return BackendConfig{}, err - } - return res, nil -} - -func (cbs *concreteBackendService) AddBackend(config BackendConfig) error { - storageConf, err := config.Storage() - if err != nil { - return fmt.Errorf("failed to create storage configuration: %w", err) - } - return cbs.s.CreateConnector(storageConf) -} - -func (cbs *concreteBackendService) RemoveBackend(connectorID string) error { - return cbs.s.DeleteConnector(connectorID) -} - -func New(s storage.Storage) Service { - return &concreteBackendService{s} -} diff --git a/polyculeconnect/services/backend/backend_test.go b/polyculeconnect/services/backend/backend_test.go deleted file mode 100644 index 4fde675..0000000 --- a/polyculeconnect/services/backend/backend_test.go +++ /dev/null @@ -1,215 +0,0 @@ -package backend_test - -import ( - "encoding/json" - "fmt" - "testing" - - "git.faercol.me/faercol/polyculeconnect/polyculeconnect/connector" - "git.faercol.me/faercol/polyculeconnect/polyculeconnect/services/backend" - "github.com/dexidp/dex/storage" - "github.com/dexidp/dex/storage/memory" - logt "github.com/sirupsen/logrus/hooks/test" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -const ( - testDomain string = "https://test.domain.com" - testClientID string = "this_is_an_id" - testClientSecret string = "this_is_a_secret" - testRedirectURI string = "http://127.0.0.1:5000/callback" - testConnectorName string = "Test connector" -) - -func generateConnector(id string) storage.Connector { - confJson := fmt.Sprintf(`{ - "issuer": "%s", - "clientID": "%s", - "clientSecret": "%s", - "redirectURI": "%s" -}`, testDomain, testClientID, testClientSecret, testRedirectURI) - storageConfig := storage.Connector{ - ID: id, - Name: testConnectorName, - Type: "oidc", - Config: []byte(confJson), - } - return storageConfig -} - -func generateConfig(id string) backend.BackendConfig { - return backend.BackendConfig{ - ID: id, - Name: testConnectorName, - Issuer: testDomain, - ClientID: testClientID, - ClientSecret: testClientSecret, - RedirectURI: testRedirectURI, - } -} - -func checkStrInMap(t *testing.T, vals map[string]interface{}, key, expected string) { - rawVal, ok := vals[key] - require.Truef(t, ok, "missing key %s", key) - strVal, ok := rawVal.(string) - require.Truef(t, ok, "invalid string format %v", rawVal) - assert.Equal(t, expected, strVal, "unexpected value") -} - -func initStorage(t *testing.T) storage.Storage { - logger, _ := logt.NewNullLogger() - s := memory.New(logger) - - require.NoError(t, s.CreateConnector(connector.RefuseAllConnectorConfig)) - require.NoError(t, s.CreateConnector(generateConnector("test0"))) - require.NoError(t, s.CreateConnector(generateConnector("test1"))) - - return s -} - -func TestBackendConfigFromConnector(t *testing.T) { - connector := generateConnector("test") - var bc backend.BackendConfig - require.NoError(t, bc.FromConnector(connector)) - - assert.Equal(t, testDomain, bc.Issuer) - assert.Equal(t, testClientID, bc.ClientID) - assert.Equal(t, testClientSecret, bc.ClientSecret) - assert.Equal(t, testRedirectURI, bc.RedirectURI) - assert.Equal(t, testConnectorName, bc.Name) - assert.Equal(t, "test", bc.ID) -} - -func TestBackendConfigInvalidType(t *testing.T) { - connector := generateConnector("test") - connector.Type = "test" - var bc backend.BackendConfig - assert.ErrorIs(t, bc.FromConnector(connector), backend.ErrUnsupportedType) -} - -func TestBackendConfigInvalidOIDCConfig(t *testing.T) { - connector := generateConnector("test") - connector.Config = []byte("toto") - var bc backend.BackendConfig - assert.ErrorContains(t, bc.FromConnector(connector), "invalid OIDC config") -} - -func TestOIDCConfigFromBackendConfig(t *testing.T) { - conf := generateConfig("test") - oidcConf := conf.OIDC() - - assert.Equal(t, testDomain, oidcConf.Issuer) - assert.Equal(t, testClientID, oidcConf.ClientID) - assert.Equal(t, testClientSecret, oidcConf.ClientSecret) - assert.Equal(t, testRedirectURI, oidcConf.RedirectURI) -} - -func TestConnectorConfigFromBackendConfig(t *testing.T) { - conf := generateConfig("test") - con, err := conf.Storage() - require.NoError(t, err) - - // The OIDC config is stored as JSON data, we just want the raw keys here - var oidcConf map[string]interface{} - require.NoError(t, json.Unmarshal(con.Config, &oidcConf)) - - assert.Equal(t, "oidc", con.Type) - assert.Equal(t, "test", con.ID) - assert.Equal(t, testConnectorName, con.Name) - checkStrInMap(t, oidcConf, "issuer", testDomain) - checkStrInMap(t, oidcConf, "clientID", testClientID) - checkStrInMap(t, oidcConf, "clientSecret", testClientSecret) - checkStrInMap(t, oidcConf, "redirectURI", testRedirectURI) -} - -func TestListBackendsEmpty(t *testing.T) { - logger, _ := logt.NewNullLogger() - s := memory.New(logger) - - // add the default refuse all connector, it should not be visible in the list - require.NoError(t, s.CreateConnector(connector.RefuseAllConnectorConfig)) - srv := backend.New(s) - - backends, err := srv.ListBackends() // empty list, and no error - require.NoError(t, err) - require.Len(t, backends, 0) -} - -func TestListBackendsNotEmpty(t *testing.T) { - s := initStorage(t) - srv := backend.New(s) - - backends, err := srv.ListBackends() // empty list, and no error - expectedIds := []string{"test0", "test1"} - require.NoError(t, err) - assert.Len(t, backends, 2) - for _, c := range backends { - assert.Contains(t, expectedIds, c.ID) - } -} - -func TestGetBackend(t *testing.T) { - s := initStorage(t) - srv := backend.New(s) - - t.Run("OK", func(t *testing.T) { - conf, err := srv.GetBackend("test0") - require.NoError(t, err) - assert.Equal(t, testDomain, conf.Issuer) - assert.Equal(t, testClientID, conf.ClientID) - assert.Equal(t, testClientSecret, conf.ClientSecret) - assert.Equal(t, testRedirectURI, conf.RedirectURI) - assert.Equal(t, testConnectorName, conf.Name) - assert.Equal(t, "test0", conf.ID) - }) - - t.Run("Not exist", func(t *testing.T) { - _, err := srv.GetBackend("toto") - assert.ErrorIs(t, err, storage.ErrNotFound) - }) - - t.Run("Invalid type", func(t *testing.T) { - _, err := srv.GetBackend("null") // null has a RefuseAll type, which is unsupported here - assert.ErrorIs(t, err, backend.ErrUnsupportedType) - }) -} - -func TestAddBackend(t *testing.T) { - s := initStorage(t) - srv := backend.New(s) - - t.Run("OK", func(t *testing.T) { - conf := generateConfig("test_add") - require.NoError(t, srv.AddBackend(conf)) - - var parsedConf backend.BackendConfig - storageConf, err := s.GetConnector("test_add") - require.NoError(t, err) - require.NoError(t, parsedConf.FromConnector(storageConf)) - assert.Equal(t, conf, parsedConf) - }) - - t.Run("Already exists", func(t *testing.T) { - require.ErrorIs(t, srv.AddBackend(generateConfig("test0")), storage.ErrAlreadyExists) - }) -} - -func TestRemoveBackend(t *testing.T) { - s := initStorage(t) - srv := backend.New(s) - - t.Run("OK", func(t *testing.T) { - require.NoError(t, srv.AddBackend(generateConfig("to_remove"))) - _, err := s.GetConnector("to_remove") - require.NoError(t, err) // no error means it's present - - require.NoError(t, srv.RemoveBackend("to_remove")) - _, err = s.GetConnector("to_remove") - assert.ErrorIs(t, err, storage.ErrNotFound) // means it's been deleted - }) - - t.Run("No present", func(t *testing.T) { - require.ErrorIs(t, srv.RemoveBackend("toto"), storage.ErrNotFound) - }) -} -- 2.45.2 From 13f65707e7c7ec7318c83475e811ae70b264952e Mon Sep 17 00:00:00 2001 From: Melora Hugues Date: Sat, 17 Aug 2024 14:23:06 +0200 Subject: [PATCH 5/7] Init OIDC client on start with config from DB (#48) --- polyculeconnect/cmd/backend/backend.go | 3 -- polyculeconnect/cmd/serve/serve.go | 37 +++++++++++++++------- polyculeconnect/internal/client/client.go | 23 ++++++++++++++ polyculeconnect/polyculeconnect.db | Bin 118784 -> 118784 bytes 4 files changed, 48 insertions(+), 15 deletions(-) create mode 100644 polyculeconnect/internal/client/client.go diff --git a/polyculeconnect/cmd/backend/backend.go b/polyculeconnect/cmd/backend/backend.go index b971f35..b5b01c5 100644 --- a/polyculeconnect/cmd/backend/backend.go +++ b/polyculeconnect/cmd/backend/backend.go @@ -11,9 +11,6 @@ var backendCmd = &cobra.Command{ Use: "backend", Short: "Handle authentication backends", Long: `Add, Remove or Show currently installed authentication backends`, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("backend called") - }, } func printProperty(key, value string) { diff --git a/polyculeconnect/cmd/serve/serve.go b/polyculeconnect/cmd/serve/serve.go index 1fd4080..63c7415 100644 --- a/polyculeconnect/cmd/serve/serve.go +++ b/polyculeconnect/cmd/serve/serve.go @@ -9,6 +9,7 @@ import ( "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/utils" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/client" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/middlewares" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/storage" @@ -45,18 +46,6 @@ func serve() { storageType := utils.InitStorage(conf) logger.L.Infof("Initialized storage backend %q", conf.StorageType) - logger.L.Info("Initializing authentication backends") - - // dex_server.ConnectorsConfig[connector.TypeRefuseAll] = func() dex_server.ConnectorConfig { return new(connector.RefuseAllConfig) } - // connectors, err := dexConf.Storage.ListConnectors() - // if err != nil { - // logger.L.Fatalf("Failed to get existing connectors: %s", err.Error()) - // } - // var connectorIDs []string - // for _, conn := range connectors { - // connectorIDs = append(connectorIDs, conn.ID) - // } - userDB, err := db.New(*conf) if err != nil { utils.Failf("failed to init user DB: %s", err.Error()) @@ -71,6 +60,30 @@ func serve() { op.WithLogger(slogger), op.WithHttpInterceptors(middlewares.WithBackendFromRequestMiddleware), } + + logger.L.Info("Initializing authentication backends") + backends := []*client.OIDCClient{} + backendConfs, err := userDB.BackendStorage().GetAllBackends(context.Background()) + if err != nil { + utils.Failf("failed to get backend configs from the DB: %s", err.Error()) + } + + // TODO: check if we need to do it this way or + // - do a try-loop? + // - only init when using them in a request? + for _, c := range backendConfs { + b, err := client.New(context.Background(), c) + if err != nil { + utils.Failf("failed to init backend client: %s", err.Error()) + } + backends = append(backends, b) + } + if len(backends) == 0 { + logger.L.Warn("No auth backend loaded") + } else { + logger.L.Infof("Initialized %d auth backends", len(backends)) + } + provider, err := op.NewProvider(&opConf, &st, op.StaticIssuer(conf.Issuer), options...) if err != nil { utils.Failf("failed to init OIDC provider: %s", err.Error()) diff --git a/polyculeconnect/internal/client/client.go b/polyculeconnect/internal/client/client.go new file mode 100644 index 0000000..0cccef7 --- /dev/null +++ b/polyculeconnect/internal/client/client.go @@ -0,0 +1,23 @@ +package client + +import ( + "context" + "fmt" + + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model" + "github.com/zitadel/oidc/v3/pkg/client/rp" +) + +type OIDCClient struct { + Conf *model.Backend + provider rp.RelyingParty +} + +func New(ctx context.Context, conf *model.Backend) (*OIDCClient, error) { + pr, err := rp.NewRelyingPartyOIDC(ctx, conf.OIDCConfig.Issuer, conf.OIDCConfig.ClientID, conf.OIDCConfig.ClientSecret, conf.OIDCConfig.RedirectURI, []string{}) + if err != nil { + return nil, fmt.Errorf("failed to init relying party provider: %w", err) + } + + return &OIDCClient{Conf: conf, provider: pr}, nil +} diff --git a/polyculeconnect/polyculeconnect.db b/polyculeconnect/polyculeconnect.db index 8cfa771ce82dd4511fe555c0dd4188c2d49c0265..3b23f7a54c5201a4115148b2f9117b487113a872 100644 GIT binary patch delta 266 zcmZozz}~QceS$Qj#zYxsMvcaVtqF`v=Cd*K$20K9Zx&Pt<*#p)V`LB24m8#EG&f5% zu`sqU)U`0RG}1LOOtaKYN=Z)DO*1!2HZnFcNJ%kBDag;MOfJnyP0r8DOHD4xC@Co@ zw$j%}6V^*hOf5>z&(X_GElDjd(M6Lk$;d2@2cpFI;#8L8qSTVsJ`Gtj;wK>u31OS1nQoaBH delta 187 zcmZozz}~QceS$Qj@-7IL(&2Ny&$Q~+cE-LJ4YM5w} zXk=`vYiw#}p=)AhZlG(Knv$ZMWSnT8n4FSmXkwOBl3G0d;d(}4E;DlrLnC7oQ#14F xkJd9PK}@^>H1Qn2g*tO6CozU`F#rL`B|jKsfGp|Ff(|eEIoO#+If2%c000YSF8Kfe -- 2.45.2 From 741e638c78e696126fa095d2bb329c0466805fdc Mon Sep 17 00:00:00 2001 From: Melora Hugues Date: Sat, 17 Aug 2024 15:22:37 +0200 Subject: [PATCH 6/7] Add basic support to store auth_requests (#48) --- polyculeconnect/internal/middlewares/test.go | 6 + polyculeconnect/internal/model/authrequest.go | 116 ++++++++++++++++++ polyculeconnect/internal/model/client.go | 10 +- polyculeconnect/internal/storage/storage.go | 8 +- 4 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 polyculeconnect/internal/model/authrequest.go diff --git a/polyculeconnect/internal/middlewares/test.go b/polyculeconnect/internal/middlewares/test.go index 548714b..1f83086 100644 --- a/polyculeconnect/internal/middlewares/test.go +++ b/polyculeconnect/internal/middlewares/test.go @@ -3,6 +3,7 @@ package middlewares import ( "context" "net/http" + "strings" ) const ( @@ -15,6 +16,11 @@ type BackendFromRequestMiddleware struct { } func (m *BackendFromRequestMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if !strings.HasPrefix(r.RequestURI, "/authorize") { + m.h.ServeHTTP(w, r) + return + } + if err := r.ParseForm(); err != nil { // TODO: handle this better w.WriteHeader(http.StatusBadRequest) diff --git a/polyculeconnect/internal/model/authrequest.go b/polyculeconnect/internal/model/authrequest.go new file mode 100644 index 0000000..71c5f8e --- /dev/null +++ b/polyculeconnect/internal/model/authrequest.go @@ -0,0 +1,116 @@ +package model + +import ( + "fmt" + "strings" + "time" + + "github.com/google/uuid" + "github.com/zitadel/oidc/v3/pkg/oidc" +) + +// AuthRequest also implements the op.AuthRequest interface +type AuthRequest struct { + ID uuid.UUID + ClientID string + Scopes []string + RedirectURI string + State string + Nonce string + + ResponseType string + + CreationDate time.Time + AuthTime time.Time + + // TODO mapping to claims to be added I guess + + CodeChallenge string + CodeChallengeMethod string + + BackendID uuid.UUID + UserID uuid.UUID + done bool +} + +func (a AuthRequest) GetID() string { + return a.ID.String() +} + +func (a AuthRequest) GetACR() string { + return "" // TODO: the hell is ACR??? +} + +func (a AuthRequest) GetAMR() []string { + return []string{} // TODO: the hell is this??? +} + +func (a AuthRequest) GetAudience() []string { + return []string{a.ID.String()} // TODO: check if we need to return something else +} + +func (a AuthRequest) GetAuthTime() time.Time { + return a.AuthTime +} + +func (a AuthRequest) GetClientID() string { + return a.ClientID +} + +func (a AuthRequest) GetCodeChallenge() *oidc.CodeChallenge { + return &oidc.CodeChallenge{ + Challenge: a.CodeChallenge, + Method: oidc.CodeChallengeMethod(a.CodeChallengeMethod), + } +} + +func (a AuthRequest) GetNonce() string { + return a.Nonce +} + +func (a AuthRequest) GetRedirectURI() string { + return a.RedirectURI +} + +func (a AuthRequest) GetResponseType() oidc.ResponseType { + return oidc.ResponseType(a.ResponseType) +} + +func (a AuthRequest) GetResponseMode() oidc.ResponseMode { + return oidc.ResponseModeQuery // TODO: check if this is good +} + +func (a AuthRequest) GetScopes() []string { + return a.Scopes +} + +func (a AuthRequest) GetState() string { + return a.State +} + +func (a AuthRequest) GetSubject() string { + return a.UserID.String() +} + +func (a AuthRequest) Done() bool { + return a.done +} + +func (a *AuthRequest) FromOIDCAuthRequest(req *oidc.AuthRequest, backendID uuid.UUID) { + fmt.Println(req) + + a.ID = uuid.New() + a.ClientID = req.ClientID + a.Scopes = strings.Split(req.Scopes.String(), " ") + a.RedirectURI = req.RedirectURI + a.State = req.State + a.Nonce = req.Nonce + a.ResponseType = string(req.ResponseType) + a.CreationDate = time.Now().UTC() + a.CodeChallenge = req.CodeChallenge + a.CodeChallengeMethod = string(req.CodeChallengeMethod) + a.BackendID = backendID + + fmt.Println(a) + +} diff --git a/polyculeconnect/internal/model/client.go b/polyculeconnect/internal/model/client.go index 179bdaa..661aefb 100644 --- a/polyculeconnect/internal/model/client.go +++ b/polyculeconnect/internal/model/client.go @@ -13,6 +13,7 @@ type ClientConfig struct { RedirectURIs []string TrustedPeers []string Name string + AuthRequest *AuthRequest } type Client struct { @@ -47,8 +48,13 @@ func (c Client) GrantTypes() []oidc.GrantType { return []oidc.GrantType{oidc.GrantTypeCode} } -func (c Client) LoginURL(id string) string { - return id +func (c Client) LoginURL(authRequestID string) string { + // here we have the requestID, meaning we should: + // - get the request from its ID + // - get the associated backend + // - build the correct URI to use as a redirection, which is from the backend + // - afterwards would should basically handle it as a OIDC client + return authRequestID } func (c Client) AccessTokenType() op.AccessTokenType { diff --git a/polyculeconnect/internal/storage/storage.go b/polyculeconnect/internal/storage/storage.go index 22aa245..f0c9c84 100644 --- a/polyculeconnect/internal/storage/storage.go +++ b/polyculeconnect/internal/storage/storage.go @@ -7,6 +7,7 @@ import ( "time" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model" "github.com/go-jose/go-jose/v4" "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/oidc/v3/pkg/op" @@ -30,12 +31,15 @@ func (s *Storage) CreateAuthRequest(ctx context.Context, req *oidc.AuthRequest, if !ok { return nil, errors.New("no backend name provided") } - _, err := s.LocalStorage.BackendStorage().GetBackendByName(ctx, backendName) + selectedBackend, err := s.LocalStorage.BackendStorage().GetBackendByName(ctx, backendName) if err != nil { return nil, fmt.Errorf("failed to get backend: %w", err) } - return nil, ErrNotImplemented("CreateAuthRequest") + var opReq model.AuthRequest + opReq.FromOIDCAuthRequest(req, selectedBackend.ID) + + return opReq, nil } func (s *Storage) AuthRequestByID(ctx context.Context, requestID string) (op.AuthRequest, error) { -- 2.45.2 From 9206c8e41e6bb732a6fe4b84dbbb8366f1574972 Mon Sep 17 00:00:00 2001 From: Melora Hugues Date: Sun, 22 Sep 2024 10:26:27 +0200 Subject: [PATCH 7/7] Add start of auth request storage --- polyculeconnect.db | Bin 0 -> 98304 bytes polyculeconnect/cmd/serve/serve.go | 1 + polyculeconnect/go.sum | 2 + polyculeconnect/internal/client/client.go | 24 +++++- .../internal/db/authrequest/authrequest.go | 77 ++++++++++++++++++ .../internal/db/backend/backend.go | 7 ++ polyculeconnect/internal/db/base.go | 6 ++ polyculeconnect/internal/model/authrequest.go | 14 ++-- polyculeconnect/internal/model/client.go | 11 ++- polyculeconnect/internal/storage/storage.go | 43 +++++++++- .../migrations/1_create_auth_request.down.sql | 1 + .../migrations/1_create_auth_request.up.sql | 11 +++ polyculeconnect/polyculeconnect.db | Bin 118784 -> 126976 bytes 13 files changed, 174 insertions(+), 23 deletions(-) create mode 100644 polyculeconnect.db create mode 100644 polyculeconnect/internal/db/authrequest/authrequest.go create mode 100644 polyculeconnect/migrations/1_create_auth_request.down.sql create mode 100644 polyculeconnect/migrations/1_create_auth_request.up.sql diff --git a/polyculeconnect.db b/polyculeconnect.db new file mode 100644 index 0000000000000000000000000000000000000000..5b1a7bd14883b4779373e7767555331a9ff12e27 GIT binary patch literal 98304 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
  • @O?cagAM%qlnM(qPzHhO1Ti-M5=AwMnWbh=JS7XjKnigC7>#nhM7lT$~3x4+Q6SkZbKSyIT z>-g%Ts&>{vLg!fQL4ZbC2g2vc3H_~zsW%H*Wpg2o&e2`>nN90jwX-#SyqcGlg$3!v zpNMz%cisyT%Ff<^GhwBq|8}#d_pKlcfU){RFl|cXk5P$##AOHafKmY;|fB*!} GOW?m)Mc4rV literal 0 HcmV?d00001 diff --git a/polyculeconnect/cmd/serve/serve.go b/polyculeconnect/cmd/serve/serve.go index 63c7415..32a48dd 100644 --- a/polyculeconnect/cmd/serve/serve.go +++ b/polyculeconnect/cmd/serve/serve.go @@ -72,6 +72,7 @@ func serve() { // - do a try-loop? // - only init when using them in a request? for _, c := range backendConfs { + logger.L.Debugf("Initializing backend %s", c.Name) b, err := client.New(context.Background(), c) if err != nil { utils.Failf("failed to init backend client: %s", err.Error()) diff --git a/polyculeconnect/go.sum b/polyculeconnect/go.sum index 6758873..99d4fb1 100644 --- a/polyculeconnect/go.sum +++ b/polyculeconnect/go.sum @@ -125,6 +125,8 @@ github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jeremija/gosubmit v0.2.7 h1:At0OhGCFGPXyjPYAsCchoBUhE099pcBXmsb4iZqROIc= +github.com/jeremija/gosubmit v0.2.7/go.mod h1:Ui+HS073lCFREXBbdfrJzMB57OI/bdxTiLtrDHHhFPI= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= diff --git a/polyculeconnect/internal/client/client.go b/polyculeconnect/internal/client/client.go index 0cccef7..6a933ba 100644 --- a/polyculeconnect/internal/client/client.go +++ b/polyculeconnect/internal/client/client.go @@ -3,21 +3,37 @@ package client import ( "context" "fmt" + "log/slog" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/logger" "github.com/zitadel/oidc/v3/pkg/client/rp" + "go.uber.org/zap" + "go.uber.org/zap/exp/zapslog" ) type OIDCClient struct { - Conf *model.Backend + conf *model.Backend + provider rp.RelyingParty + ctx context.Context + st db.Storage + l *zap.SugaredLogger } -func New(ctx context.Context, conf *model.Backend) (*OIDCClient, error) { - pr, err := rp.NewRelyingPartyOIDC(ctx, conf.OIDCConfig.Issuer, conf.OIDCConfig.ClientID, conf.OIDCConfig.ClientSecret, conf.OIDCConfig.RedirectURI, []string{}) +func New(ctx context.Context, conf *model.Backend, l *zap.SugaredLogger) (*OIDCClient, error) { + options := []rp.Option{ + rp.WithLogger(slog.New(zapslog.NewHandler(logger.L.Desugar().Core(), nil))), + } + pr, err := rp.NewRelyingPartyOIDC(ctx, conf.OIDCConfig.Issuer, conf.OIDCConfig.ClientID, conf.OIDCConfig.ClientSecret, conf.OIDCConfig.RedirectURI, []string{}, options...) if err != nil { return nil, fmt.Errorf("failed to init relying party provider: %w", err) } - return &OIDCClient{Conf: conf, provider: pr}, nil + return &OIDCClient{ctx: ctx, conf: conf, provider: pr, l: l}, nil +} + +func (c *OIDCClient) toto() { + c.provider.GetDeviceAuthorizationEndpoint() } diff --git a/polyculeconnect/internal/db/authrequest/authrequest.go b/polyculeconnect/internal/db/authrequest/authrequest.go new file mode 100644 index 0000000..3506dee --- /dev/null +++ b/polyculeconnect/internal/db/authrequest/authrequest.go @@ -0,0 +1,77 @@ +package authrequest + +import ( + "context" + "database/sql" + "encoding/json" + "errors" + "fmt" + + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model" + "github.com/google/uuid" +) + +var ErrNotFound = errors.New("backend not found") + +const authRequestRows = `"id", "client_id", "backend_id", "scopes", "redirect_uri", "state", "nonce", "response_type", "creation_time"` + +type AuthRequestDB interface { + GetAuthRequestByID(ctx context.Context, id uuid.UUID) (*model.AuthRequest, error) + CreateAuthRequest(ctx context.Context, req model.AuthRequest) error +} + +type sqlAuthRequestDB struct { + db *sql.DB +} + +func (db *sqlAuthRequestDB) GetAuthRequestByID(ctx context.Context, id uuid.UUID) (*model.AuthRequest, error) { + query := fmt.Sprintf(`SELECT %s FROM "auth_request_2" WHERE "id" = ?`, authRequestRows) + row := db.db.QueryRowContext(ctx, query, id) + + var res model.AuthRequest + var scopesStr []byte + + if err := row.Scan(&res.ID, &res.ClientID, &res.BackendID, &scopesStr, &res.RedirectURI, &res.State, &res.Nonce, &res.ResponseType, &res.CreationDate); err != nil { + return nil, fmt.Errorf("failed to get auth request from DB: %w", err) + } + if err := json.Unmarshal(scopesStr, &res.Scopes); err != nil { + return nil, fmt.Errorf("invalid format for scopes: %w", err) + } + + fmt.Println(res) + + return &res, nil +} + +func (db *sqlAuthRequestDB) CreateAuthRequest(ctx context.Context, req model.AuthRequest) error { + tx, err := db.db.BeginTx(ctx, nil) + if err != nil { + return fmt.Errorf("failed to start transaction: %w", err) + } + defer func() { _ = tx.Rollback() }() + + scopesStr, err := json.Marshal(req.Scopes) + if err != nil { + return fmt.Errorf("failed to serialize scopes: %w", err) + } + + query := fmt.Sprintf(`INSERT INTO "auth_request_2" (%s) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, authRequestRows) + _, err = tx.ExecContext(ctx, query, + req.ID, req.ClientID, req.BackendID, + scopesStr, req.RedirectURI, req.State, + req.Nonce, req.ResponseType, req.CreationDate, req.AuthTime, + ) + if err != nil { + return fmt.Errorf("failed to insert in DB: %w", err) + } + + if err := tx.Commit(); err != nil { + return fmt.Errorf("failed to commit transaction: %w", err) + } + + return nil +} + +func New(db *sql.DB) *sqlAuthRequestDB { + return &sqlAuthRequestDB{db: db} +} diff --git a/polyculeconnect/internal/db/backend/backend.go b/polyculeconnect/internal/db/backend/backend.go index 3708442..b68f3ef 100644 --- a/polyculeconnect/internal/db/backend/backend.go +++ b/polyculeconnect/internal/db/backend/backend.go @@ -21,6 +21,7 @@ type scannable interface { type BackendDB interface { GetAllBackends(ctx context.Context) ([]*model.Backend, error) + GetBackendByID(ctx context.Context, id uuid.UUID) (*model.Backend, error) GetBackendByName(ctx context.Context, name string) (*model.Backend, error) AddBackend(ctx context.Context, newBackend *model.Backend) error @@ -50,6 +51,12 @@ func (db *sqlBackendDB) GetBackendByName(ctx context.Context, name string) (*mod return backendFromRow(row) } +func (db *sqlBackendDB) GetBackendByID(ctx context.Context, id uuid.UUID) (*model.Backend, error) { + query := fmt.Sprintf(`SELECT %s FROM "backend" WHERE "id" = ?`, backendRows) + row := db.db.QueryRowContext(ctx, query, id) + return backendFromRow(row) +} + func (db *sqlBackendDB) GetAllBackends(ctx context.Context) ([]*model.Backend, error) { rows, err := db.db.QueryContext(ctx, fmt.Sprintf(`SELECT %s FROM "backend"`, backendRows)) if err != nil { diff --git a/polyculeconnect/internal/db/base.go b/polyculeconnect/internal/db/base.go index dabfded..207a520 100644 --- a/polyculeconnect/internal/db/base.go +++ b/polyculeconnect/internal/db/base.go @@ -5,6 +5,7 @@ import ( "fmt" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/config" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db/authrequest" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db/backend" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db/client" ) @@ -13,6 +14,7 @@ type Storage interface { DB() *sql.DB ClientStorage() client.ClientDB BackendStorage() backend.BackendDB + AuthRequestStorage() authrequest.AuthRequestDB } type sqlStorage struct { @@ -31,6 +33,10 @@ func (s *sqlStorage) BackendStorage() backend.BackendDB { return backend.New(s.db) } +func (s *sqlStorage) AuthRequestStorage() authrequest.AuthRequestDB { + return authrequest.New(s.db) +} + func New(conf config.AppConfig) (Storage, error) { db, err := sql.Open("sqlite3", conf.StorageConfig.File) if err != nil { diff --git a/polyculeconnect/internal/model/authrequest.go b/polyculeconnect/internal/model/authrequest.go index 71c5f8e..07324a8 100644 --- a/polyculeconnect/internal/model/authrequest.go +++ b/polyculeconnect/internal/model/authrequest.go @@ -1,7 +1,6 @@ package model import ( - "fmt" "strings" "time" @@ -29,8 +28,10 @@ type AuthRequest struct { CodeChallengeMethod string BackendID uuid.UUID - UserID uuid.UUID - done bool + Backend *Backend + + UserID uuid.UUID + done bool } func (a AuthRequest) GetID() string { @@ -54,7 +55,7 @@ func (a AuthRequest) GetAuthTime() time.Time { } func (a AuthRequest) GetClientID() string { - return a.ClientID + return a.ID.String() // small hack since we actually need the AuthRequestID here } func (a AuthRequest) GetCodeChallenge() *oidc.CodeChallenge { @@ -97,8 +98,6 @@ func (a AuthRequest) Done() bool { } func (a *AuthRequest) FromOIDCAuthRequest(req *oidc.AuthRequest, backendID uuid.UUID) { - fmt.Println(req) - a.ID = uuid.New() a.ClientID = req.ClientID a.Scopes = strings.Split(req.Scopes.String(), " ") @@ -110,7 +109,4 @@ func (a *AuthRequest) FromOIDCAuthRequest(req *oidc.AuthRequest, backendID uuid. a.CodeChallenge = req.CodeChallenge a.CodeChallengeMethod = string(req.CodeChallengeMethod) a.BackendID = backendID - - fmt.Println(a) - } diff --git a/polyculeconnect/internal/model/client.go b/polyculeconnect/internal/model/client.go index 661aefb..f2cdda7 100644 --- a/polyculeconnect/internal/model/client.go +++ b/polyculeconnect/internal/model/client.go @@ -49,12 +49,11 @@ func (c Client) GrantTypes() []oidc.GrantType { } func (c Client) LoginURL(authRequestID string) string { - // here we have the requestID, meaning we should: - // - get the request from its ID - // - get the associated backend - // - build the correct URI to use as a redirection, which is from the backend - // - afterwards would should basically handle it as a OIDC client - return authRequestID + if c.AuthRequest == nil { + return "" // we don't have a request, let's return nothing + } + + return c.AuthRequest.Backend.OIDCConfig.Issuer } func (c Client) AccessTokenType() op.AccessTokenType { diff --git a/polyculeconnect/internal/storage/storage.go b/polyculeconnect/internal/storage/storage.go index f0c9c84..902c8a1 100644 --- a/polyculeconnect/internal/storage/storage.go +++ b/polyculeconnect/internal/storage/storage.go @@ -9,6 +9,7 @@ import ( "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/db" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/internal/model" "github.com/go-jose/go-jose/v4" + "github.com/google/uuid" "github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/oidc/v3/pkg/op" ) @@ -39,6 +40,10 @@ func (s *Storage) CreateAuthRequest(ctx context.Context, req *oidc.AuthRequest, var opReq model.AuthRequest opReq.FromOIDCAuthRequest(req, selectedBackend.ID) + if err := s.LocalStorage.AuthRequestStorage().CreateAuthRequest(ctx, opReq); err != nil { + return nil, fmt.Errorf("failed to save auth request: %w", err) + } + return opReq, nil } @@ -98,13 +103,43 @@ func (s *Storage) KeySet(ctx context.Context) ([]op.Key, error) { OP storage */ -func (s *Storage) GetClientByClientID(ctx context.Context, clientID string) (op.Client, error) { - clt, err := s.LocalStorage.ClientStorage().GetClientByID(ctx, clientID) +func (s *Storage) getClientWithDetails(ctx context.Context, authRequestID uuid.UUID) (op.Client, error) { + authRequest, err := s.LocalStorage.AuthRequestStorage().GetAuthRequestByID(ctx, authRequestID) if err != nil { - return nil, fmt.Errorf("failed to get client from local storage: %w", err) + return nil, fmt.Errorf("failed to get authRequest from local storage: %w", err) + } + backend, err := s.LocalStorage.BackendStorage().GetBackendByID(ctx, authRequest.BackendID) + if err != nil { + return nil, fmt.Errorf("failed to get associated backend from local storage: %w", err) + } + client, err := s.LocalStorage.ClientStorage().GetClientByID(ctx, authRequest.ClientID) + if err != nil { + return nil, fmt.Errorf("failed to get associated client from local storage: %w", err) } - return clt, nil + authRequest.Backend = backend + client.AuthRequest = authRequest + + return client, nil +} + +// We're cheating a bit here since we're using the authrequest to get its associated client +// but a request is always associated to a backend, and we really need both, so we have no +// choice here. I'll maybe need to have a more elegant solution later, but not choice for now +func (s *Storage) GetClientByClientID(ctx context.Context, id string) (op.Client, error) { + + authRequestID, err := uuid.Parse(id) + if err != nil { + // it's not a UUID, it means this was called using client_id, we just return the client without details + client, err := s.LocalStorage.ClientStorage().GetClientByID(ctx, id) + if err != nil { + return nil, fmt.Errorf("failed to get client %s from local storage: %w", id, err) + } + return client, nil + } + + // we have a UUID, it means we got a requestID, so we can get all details here + return s.getClientWithDetails(ctx, authRequestID) } func (s *Storage) AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string) error { diff --git a/polyculeconnect/migrations/1_create_auth_request.down.sql b/polyculeconnect/migrations/1_create_auth_request.down.sql new file mode 100644 index 0000000..998bfa6 --- /dev/null +++ b/polyculeconnect/migrations/1_create_auth_request.down.sql @@ -0,0 +1 @@ +DROP TABLE "auth_request_2"; \ No newline at end of file diff --git a/polyculeconnect/migrations/1_create_auth_request.up.sql b/polyculeconnect/migrations/1_create_auth_request.up.sql new file mode 100644 index 0000000..534cadc --- /dev/null +++ b/polyculeconnect/migrations/1_create_auth_request.up.sql @@ -0,0 +1,11 @@ +CREATE TABLE "auth_request_2" ( + id TEXT NOT NULL PRIMARY KEY, + client_id TEXT NOT NULL, + backend_id TEXT NOT NULL, + scopes blob NOT NULL, -- list of strings, json-encoded + redirect_uri TEXT NOT NULL, + state TEXT NOT NULL, + nonce TEXT NOT NULL, + response_type TEXT NOT NULL, + CREATION_TIME timestamp NOT NULL +); \ No newline at end of file diff --git a/polyculeconnect/polyculeconnect.db b/polyculeconnect/polyculeconnect.db index 3b23f7a54c5201a4115148b2f9117b487113a872..0cc1857cd47957be263f1156fd7837ffb2342048 100644 GIT binary patch delta 1553 zcmc(ePj3`A7{=G^ZdUoT3`MD^NDA(fs-(n=$M$$U3pu1!q(%w}l`v~W9xyxoAkIJ7@6yV+a|J?_FFXl%J`&sEW_`Fcq9~l4lvvYCbn_qq&D_xkJ z1ixNS{Z?D~8|iAZtJXJEk~Ycd)1|M!ot;^lg-bJE&CkNh>C5c*2KX|2(Ey*8TE6^A zML~AZ02gi~GVZ7Zw%TzEE-Ws=h5G#b6hsKN!z6{VhDq8DqaTtf_+t`BNJTOZR4@e1 z$Y$<=i`nxA7$_#GpQ`Lx1B@1oW~bKtux(-+dlC{%7{US(2ijiErZsF=iC`2n&aYuy!+3YSaKe6< z2D;@c;UZsq6fqYoGN;UJ1s{FrjacO zN6gb2wS?5j7Y+#omDFMQkbd9;NN?AeTlFx(m~-z$`!lcUxl&5_!bTXo#y4EsNN7rt zl0?|VcQDiJgVEy}rB&DSJVCkV9cvF>(^IBe*ccxW7Ry;rtlz8^>mQ4mUanZ%SMznyPYV}&h5htP;jpj| UYvy}}xB9WcnJ@D7!G08f30ywRGynhq delta 74 zcmZp8z}~QceS);01_J|wED*zh;zS)|MvcaVtqF`v{F#N=BB!$lFsg1=6wqbc9vQ~C U)RK{7`~QB%|NJ0&J|m+609juWg#Z8m -- 2.45.2