From d5aa640df08dddea7d9d38f3861403dd9e4acd72 Mon Sep 17 00:00:00 2001 From: Melora Hugues Date: Fri, 16 Aug 2024 11:02:44 +0200 Subject: [PATCH] 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 07c0a9c..804b56d 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 +);