diff --git a/.mockery.yaml b/.mockery.yaml index 2d6924e93..d1d2d7cc6 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -13,4 +13,7 @@ packages: sigs.k8s.io/controller-runtime/pkg/client: interfaces: Client: - SubResourceWriter: \ No newline at end of file + SubResourceWriter: + github.com/dexidp/dex/storage: + interfaces: + Storage: \ No newline at end of file diff --git a/cmd/greenhouse/controllers.go b/cmd/greenhouse/controllers.go index 9803b9ee7..f887ae42a 100644 --- a/cmd/greenhouse/controllers.go +++ b/cmd/greenhouse/controllers.go @@ -76,8 +76,10 @@ func startOrganizationReconciler(name string, mgr ctrl.Manager) error { if v, ok := os.LookupEnv("POD_NAMESPACE"); ok { namespace = v } + return (&organizationcontrollers.OrganizationReconciler{ Namespace: namespace, + NetworkDB: postgresDB, }).SetupWithManager(name, mgr) } diff --git a/cmd/greenhouse/main.go b/cmd/greenhouse/main.go index 4b0994e38..ebfe322ae 100644 --- a/cmd/greenhouse/main.go +++ b/cmd/greenhouse/main.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "github.com/dexidp/dex/storage/sql" flag "github.com/spf13/pflag" "go.uber.org/zap/zapcore" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -58,6 +59,8 @@ var ( remoteClusterBearerTokenValidity, renewRemoteClusterBearerTokenAfter time.Duration kubeClientOpts clientutil.RuntimeOptions + // DB connection parameters + postgresDB sql.NetworkDB ) func init() { @@ -91,6 +94,12 @@ func main() { flag.StringVar(&common.DNSDomain, "dns-domain", "", "The DNS domain to use for the Greenhouse central cluster") + flag.StringVar(&postgresDB.Database, "database", clientutil.GetEnvOrDefault("DEX_POSTGRES_DATABASE", "dex"), "Database name") + flag.StringVar(&postgresDB.Host, "dbHost", clientutil.GetEnvOrDefault("DEX_POSTGRES_HOST", "localhost"), "Database host") + flag.Uint16Var(&postgresDB.Port, "dbPort", 5432, "Database port") + flag.StringVar(&postgresDB.User, "dbUser", clientutil.GetEnvOrDefault("DEX_POSTGRES_USER", "dex"), "Database user") + flag.StringVar(&postgresDB.Password, "dbPassword", clientutil.GetEnvOrDefault("DEX_POSTGRES_PASSWORD", "dex"), "Database password") + opts := zap.Options{ Development: true, TimeEncoder: zapcore.RFC3339TimeEncoder, diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index bb22b9df0..bc9b2286c 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -16,6 +16,7 @@ import ( "time" "github.com/dexidp/dex/server" + "github.com/dexidp/dex/storage/sql" "github.com/go-logr/logr" "github.com/oklog/run" "github.com/prometheus/client_golang/prometheus" @@ -39,6 +40,9 @@ func main() { var idTokenValidity time.Duration var listenAddr, metricsAddr string var allowedOrigins []string + // DB connection parameters + var postgresDB sql.NetworkDB + logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) // set default logger to be used by log slog.SetDefault(logger) @@ -48,6 +52,11 @@ func main() { flag.StringVar(&kubeconfig, "kubeconfig", os.Getenv("KUBECONFIG"), "Use kubeconfig for authentication") flag.StringVar(&kubecontext, "kubecontext", os.Getenv("KUBECONTEXT"), "Use context from kubeconfig") flag.StringVar(&kubenamespace, "kubenamespace", os.Getenv("KUBENAMESPACE"), "Use namespace") + flag.StringVar(&postgresDB.Database, "database", os.Getenv("DB_NAME"), "Database name") + flag.StringVar(&postgresDB.Host, "dbHost", os.Getenv("DB_HOST"), "Database host") + flag.Uint16Var(&postgresDB.Port, "dbPort", 5432, "Database port") + flag.StringVar(&postgresDB.User, "dbUser", os.Getenv("DB_USER"), "Database user") + flag.StringVar(&postgresDB.Password, "dbPassword", os.Getenv("DB_PASSWORD"), "Database password") flag.StringVar(&issuer, "issuer", "", "Issuer URL") flag.StringVar(&listenAddr, "listen-addr", ":8080", "oidc listen address") flag.StringVar(&metricsAddr, "metrics-addr", ":6543", "bind address for metrics") @@ -58,7 +67,13 @@ func main() { if issuer == "" { log.Fatal("No --issuer given") } + /* + sqlDexStorage, err := idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, postgresDB, logger.With("component", "storage")) + if err != nil { + log.Fatalf("Failed to initialize postgres storage: %s", err) + } + */ dexStorage, err := idproxy.NewKubernetesStorage(kubeconfig, kubecontext, kubenamespace, logger.With("component", "storage")) if err != nil { log.Fatalf("Failed to initialize kubernetes storage: %s", err) @@ -78,6 +93,7 @@ func main() { SkipApprovalScreen: true, Logger: logger.With("component", "server"), Storage: dexStorage, + // Storage: sqlDexStorage, AllowedOrigins: allowedOrigins, IDTokensValidFor: idTokenValidity, RefreshTokenPolicy: refreshPolicy, diff --git a/go.mod b/go.mod index b5165487e..b4df83c13 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,8 @@ require ( github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.10.0 + github.com/testcontainers/testcontainers-go v0.35.0 + github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 github.com/vladimirvivien/gexe v0.4.1 github.com/wI2L/jsondiff v0.6.1 go.uber.org/zap v1.27.0 @@ -55,35 +57,55 @@ require ( cloud.google.com/go/auth v0.9.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect dario.cat/mergo v1.0.1 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.6 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/containerd/errdefs v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/creack/pty v1.1.23 // indirect github.com/distribution/reference v0.6.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/miekg/dns v1.1.58 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.3.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/otiai10/mint v1.6.3 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/sergi/go-diff v1.3.1 // indirect + github.com/shirou/gopsutil/v3 v3.23.12 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect - gotest.tools/v3 v3.5.1 // indirect ) require ( diff --git a/go.sum b/go.sum index c97417af5..ba53ad96c 100644 --- a/go.sum +++ b/go.sum @@ -83,6 +83,8 @@ github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/ github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -118,6 +120,8 @@ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= @@ -167,6 +171,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-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -210,6 +216,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -262,6 +269,14 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8= +github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= @@ -313,6 +328,10 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU= @@ -327,6 +346,8 @@ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mdelapenya/tlscert v0.1.0 h1:YTpF579PYUX475eOL+6zyEO3ngLTOUWck78NBuJVXaM= +github.com/mdelapenya/tlscert v0.1.0/go.mod h1:wrbyM/DwbFCeCeqdPX/8c6hNOqQgbf0rUDErE1uD+64= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -335,12 +356,20 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -354,6 +383,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -385,6 +416,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -427,6 +460,12 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -453,8 +492,13 @@ 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/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= +github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= +github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 h1:eEGx9kYzZb2cNhRbBrNOCL/YPOM7+RMJiy3bB+ie0/I= +github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0/go.mod h1:hfH71Mia/WWLBgMD2YctYcMlfsbnT0hflweL1dy8Q4s= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -465,6 +509,10 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/vladimirvivien/gexe v0.4.1 h1:W9gWkp8vSPjDoXDu04Yp4KljpVMaSt8IQuHswLDd5LY= github.com/vladimirvivien/gexe v0.4.1/go.mod h1:3gjgTqE2c0VyHnU5UOIwk7gyNzZDGulPb/DJPgcw64E= github.com/wI2L/jsondiff v0.6.1 h1:ISZb9oNWbP64LHnu4AUhsMF5W0FIj5Ok3Krip9Shqpw= @@ -483,6 +531,8 @@ github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/exporters/autoexport v0.46.1 h1:ysCfPZB9AjUlMa1UHYup3c9dAOCMQX/6sxSfPBUoxHw= @@ -589,8 +639,10 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h 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= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -600,6 +652,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index b73c7c13d..e9db885a7 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/dexidp/dex/connector/oidc" + "github.com/dexidp/dex/storage" "github.com/pkg/errors" "golang.org/x/text/cases" "golang.org/x/text/language" @@ -88,6 +89,31 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org log.FromContext(ctx).Info("updated dex connector", "namespace", dexConnector.Namespace, "name", dexConnector.GetName()) r.recorder.Eventf(org, corev1.EventTypeNormal, "UpdatedDexConnector", "Updated dex connector %s/%s", dexConnector.Namespace, dexConnector.GetName()) } + + // Create the connectors also in SQL storage + var oidcConnector storage.Connector + if oidcConnector, err = r.dexStorage.GetConnector(org.Name); err != nil { + if err = r.dexStorage.CreateConnector(ctx, storage.Connector{ + ID: org.Name, + Type: dexConnectorTypeGreenhouse, + Name: cases.Title(language.English).String(org.Name), + Config: configByte, + }); err != nil { + return err + } + log.FromContext(ctx).Info("created dex connector in SQL storage", "name", org.Name) + } + if err = r.dexStorage.UpdateConnector(oidcConnector.ID, func(c storage.Connector) (storage.Connector, error) { + c.ID = org.Name + c.Type = dexConnectorTypeGreenhouse + c.Name = cases.Title(language.English).String(org.Name) + c.Config = configByte + return c, nil + }); err != nil { + return err + } + log.FromContext(ctx).Info("updated dex connector in SQL storage", "name", org.Name) + return nil } @@ -118,6 +144,40 @@ func (r *OrganizationReconciler) discoverOIDCRedirectURL(ctx context.Context, or } func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { + var oAuthClient storage.Client + var err error + if oAuthClient, err = r.dexStorage.GetClient(org.Name); err != nil { + if err = r.dexStorage.CreateClient(ctx, storage.Client{ + Public: true, + ID: org.Name, + Name: org.Name, + RedirectURIs: []string{ + "https://dashboard." + common.DNSDomain, + fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), + }, + }); err != nil { + return err + } + log.FromContext(ctx).Info("created oauth2client", "name", org.Name) + return nil + } + + if err = r.dexStorage.UpdateClient(oAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { + authClient.Public = true + authClient.ID = org.Name + authClient.Name = org.Name + for _, requiredRedirectURL := range []string{ + "https://dashboard." + common.DNSDomain, + fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), + } { + authClient.RedirectURIs = util.AppendStringToSliceIfNotContains(requiredRedirectURL, authClient.RedirectURIs) + } + return authClient, nil + }); err != nil { + return err + } + log.FromContext(ctx).Info("updated oauth2client", "name", org.Name) + var oAuth2Client = new(dexapi.OAuth2Client) oAuth2Client.ObjectMeta.Name = encodedOAuth2ClientName(org.Name) oAuth2Client.ObjectMeta.Namespace = r.Namespace diff --git a/pkg/controllers/organization/organization_controller.go b/pkg/controllers/organization/organization_controller.go index fa3f90734..6e5bc63a4 100644 --- a/pkg/controllers/organization/organization_controller.go +++ b/pkg/controllers/organization/organization_controller.go @@ -5,7 +5,11 @@ package organization import ( "context" + "log/slog" + "os" + "github.com/dexidp/dex/storage" + "github.com/dexidp/dex/storage/sql" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -21,6 +25,7 @@ import ( greenhousesapv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/cloudoperators/greenhouse/pkg/clientutil" dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" + "github.com/cloudoperators/greenhouse/pkg/idproxy" "github.com/cloudoperators/greenhouse/pkg/lifecycle" "github.com/cloudoperators/greenhouse/pkg/scim" ) @@ -45,6 +50,9 @@ type OrganizationReconciler struct { client.Client recorder record.EventRecorder Namespace string + // Database related configuration + NetworkDB sql.NetworkDB + dexStorage storage.Storage } //+kubebuilder:rbac:groups=greenhouse.sap,resources=organizations,verbs=get;list;watch;create;update;patch;delete @@ -64,6 +72,15 @@ type OrganizationReconciler struct { func (r *OrganizationReconciler) SetupWithManager(name string, mgr ctrl.Manager) error { r.Client = mgr.GetClient() r.recorder = mgr.GetEventRecorderFor(name) + + var err error + logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) + if r.dexStorage, err = idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, r.NetworkDB, logger.With("component", "storage")); err != nil { + return errors.Wrap(err, "failed to initialize postgres storage") + } + + defer r.dexStorage.Close() + return ctrl.NewControllerManagedBy(mgr). Named(name). For(&greenhousesapv1alpha1.Organization{}). diff --git a/pkg/controllers/organization/suite_test.go b/pkg/controllers/organization/suite_test.go index 2e4b55efa..d7856ad9a 100644 --- a/pkg/controllers/organization/suite_test.go +++ b/pkg/controllers/organization/suite_test.go @@ -4,9 +4,14 @@ package organization_test import ( + "context" "net/http/httptest" "testing" + "github.com/dexidp/dex/storage/sql" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -14,10 +19,19 @@ import ( organizationpkg "github.com/cloudoperators/greenhouse/pkg/controllers/organization" "github.com/cloudoperators/greenhouse/pkg/scim" "github.com/cloudoperators/greenhouse/pkg/test" + + "github.com/testcontainers/testcontainers-go/modules/postgres" +) + +const ( + mockDB = "mock" + mockUSR = "mock" + mockPWD = "mock_pwd" ) var ( groupsServer *httptest.Server + mockPgTc *postgres.PostgresContainer ) func TestOrganization(t *testing.T) { @@ -26,10 +40,29 @@ func TestOrganization(t *testing.T) { } var _ = BeforeSuite(func() { + var err error + ctx := context.Background() By("mocking SCIM server") groupsServer = scim.ReturnDefaultGroupResponseMockServer() - test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default"}).SetupWithManager) + mockPgTc, err = startPgTC(ctx) + Expect(err).NotTo(HaveOccurred()) + + host, err := mockPgTc.Host(ctx) + Expect(err).NotTo(HaveOccurred()) + + port, err := mockPgTc.MappedPort(ctx, "5432/tcp") + Expect(err).NotTo(HaveOccurred()) + + netDB := sql.NetworkDB{ + Host: host, + Port: uint16(port.Int()), + User: mockUSR, + Password: mockPWD, + Database: mockDB, + } + + test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default", NetworkDB: netDB}).SetupWithManager) test.RegisterWebhook("orgWebhook", admission.SetupOrganizationWebhookWithManager) test.RegisterWebhook("teamWebhook", admission.SetupTeamWebhookWithManager) test.RegisterWebhook("pluginDefinitionWebhook", admission.SetupPluginDefinitionWebhookWithManager) @@ -41,6 +74,24 @@ var _ = BeforeSuite(func() { var _ = AfterSuite(func() { By("tearing down the test environment") groupsServer.Close() + err := testcontainers.TerminateContainer(mockPgTc) + Expect(err).NotTo(HaveOccurred()) test.TestAfterSuite() }) + +func startPgTC(ctx context.Context) (*postgres.PostgresContainer, error) { + return postgres.Run(ctx, "postgres:16-alpine", + postgres.WithDatabase(mockDB), + postgres.WithUsername(mockUSR), + postgres.WithPassword(mockPWD), + testcontainers.WithWaitStrategy( + // First, we wait for the container to log readiness twice. + // This is because it will restart itself after the first startup. + wait.ForLog("database system is ready to accept connections").WithOccurrence(2), + // Then, we wait for docker to actually serve the port on localhost. + // For non-linux OSes like Mac and Windows, Docker or Rancher Desktop will have to start a separate proxy. + // Without this, the tests will be flaky on those OSes! + wait.ForListeningPort("5432/tcp"), + )) +} diff --git a/pkg/idproxy/storage.go b/pkg/idproxy/storage.go index a14e366e1..7bb4c66f3 100644 --- a/pkg/idproxy/storage.go +++ b/pkg/idproxy/storage.go @@ -11,9 +11,35 @@ import ( "github.com/dexidp/dex/storage" "github.com/dexidp/dex/storage/kubernetes" "github.com/dexidp/dex/storage/kubernetes/k8sapi" + "github.com/dexidp/dex/storage/sql" "github.com/ghodss/yaml" ) +// NewPostgresStorage creates a new Postgres storage. +func NewPostgresStorage(sslConfig sql.SSL, dbConfig sql.NetworkDB, logger *slog.Logger) (storage.Storage, error) { + sqlStorage, err := postgresStorage(sslConfig, sql.NetworkDB{ + Host: dbConfig.Host, + Port: dbConfig.Port, + User: dbConfig.User, + Password: dbConfig.Password, + Database: dbConfig.Database, + }, logger.With("component", "storage")) + if err != nil { + return nil, err + } + return sqlStorage, nil +} + +// postgresStorage creates a new Postgres storage. +func postgresStorage(sslConfig sql.SSL, dbConfig sql.NetworkDB, logger *slog.Logger) (storage.Storage, error) { + var storageConfig = sql.Postgres{ + SSL: sslConfig, + NetworkDB: dbConfig, + } + return storageConfig.Open(logger) +} + +// NewKubernetesStorage creates a new Kubernetes storage. func NewKubernetesStorage(kubeconfig, kubecontext, namespace string, logger *slog.Logger) (storage.Storage, error) { var storageConfig = kubernetes.Config{InCluster: true} if kubeconfig != "" { diff --git a/pkg/mocks/mock_Storage.go b/pkg/mocks/mock_Storage.go new file mode 100644 index 000000000..ad7edf9de --- /dev/null +++ b/pkg/mocks/mock_Storage.go @@ -0,0 +1,912 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by mockery v2.51.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + storage "github.com/dexidp/dex/storage" + mock "github.com/stretchr/testify/mock" + + time "time" +) + +// MockStorage is an autogenerated mock type for the Storage type +type MockStorage struct { + mock.Mock +} + +// Close provides a mock function with no fields +func (_m *MockStorage) Close() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateAuthCode provides a mock function with given fields: ctx, c +func (_m *MockStorage) CreateAuthCode(ctx context.Context, c storage.AuthCode) error { + ret := _m.Called(ctx, c) + + if len(ret) == 0 { + panic("no return value specified for CreateAuthCode") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.AuthCode) error); ok { + r0 = rf(ctx, c) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateAuthRequest provides a mock function with given fields: ctx, a +func (_m *MockStorage) CreateAuthRequest(ctx context.Context, a storage.AuthRequest) error { + ret := _m.Called(ctx, a) + + if len(ret) == 0 { + panic("no return value specified for CreateAuthRequest") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.AuthRequest) error); ok { + r0 = rf(ctx, a) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateClient provides a mock function with given fields: ctx, c +func (_m *MockStorage) CreateClient(ctx context.Context, c storage.Client) error { + ret := _m.Called(ctx, c) + + if len(ret) == 0 { + panic("no return value specified for CreateClient") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.Client) error); ok { + r0 = rf(ctx, c) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateConnector provides a mock function with given fields: ctx, c +func (_m *MockStorage) CreateConnector(ctx context.Context, c storage.Connector) error { + ret := _m.Called(ctx, c) + + if len(ret) == 0 { + panic("no return value specified for CreateConnector") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.Connector) error); ok { + r0 = rf(ctx, c) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateDeviceRequest provides a mock function with given fields: ctx, d +func (_m *MockStorage) CreateDeviceRequest(ctx context.Context, d storage.DeviceRequest) error { + ret := _m.Called(ctx, d) + + if len(ret) == 0 { + panic("no return value specified for CreateDeviceRequest") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.DeviceRequest) error); ok { + r0 = rf(ctx, d) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateDeviceToken provides a mock function with given fields: ctx, d +func (_m *MockStorage) CreateDeviceToken(ctx context.Context, d storage.DeviceToken) error { + ret := _m.Called(ctx, d) + + if len(ret) == 0 { + panic("no return value specified for CreateDeviceToken") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.DeviceToken) error); ok { + r0 = rf(ctx, d) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateOfflineSessions provides a mock function with given fields: ctx, s +func (_m *MockStorage) CreateOfflineSessions(ctx context.Context, s storage.OfflineSessions) error { + ret := _m.Called(ctx, s) + + if len(ret) == 0 { + panic("no return value specified for CreateOfflineSessions") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.OfflineSessions) error); ok { + r0 = rf(ctx, s) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreatePassword provides a mock function with given fields: ctx, p +func (_m *MockStorage) CreatePassword(ctx context.Context, p storage.Password) error { + ret := _m.Called(ctx, p) + + if len(ret) == 0 { + panic("no return value specified for CreatePassword") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.Password) error); ok { + r0 = rf(ctx, p) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateRefresh provides a mock function with given fields: ctx, r +func (_m *MockStorage) CreateRefresh(ctx context.Context, r storage.RefreshToken) error { + ret := _m.Called(ctx, r) + + if len(ret) == 0 { + panic("no return value specified for CreateRefresh") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.RefreshToken) error); ok { + r0 = rf(ctx, r) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteAuthCode provides a mock function with given fields: code +func (_m *MockStorage) DeleteAuthCode(code string) error { + ret := _m.Called(code) + + if len(ret) == 0 { + panic("no return value specified for DeleteAuthCode") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(code) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteAuthRequest provides a mock function with given fields: id +func (_m *MockStorage) DeleteAuthRequest(id string) error { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for DeleteAuthRequest") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteClient provides a mock function with given fields: id +func (_m *MockStorage) DeleteClient(id string) error { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for DeleteClient") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteConnector provides a mock function with given fields: id +func (_m *MockStorage) DeleteConnector(id string) error { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for DeleteConnector") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteOfflineSessions provides a mock function with given fields: userID, connID +func (_m *MockStorage) DeleteOfflineSessions(userID string, connID string) error { + ret := _m.Called(userID, connID) + + if len(ret) == 0 { + panic("no return value specified for DeleteOfflineSessions") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, string) error); ok { + r0 = rf(userID, connID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeletePassword provides a mock function with given fields: email +func (_m *MockStorage) DeletePassword(email string) error { + ret := _m.Called(email) + + if len(ret) == 0 { + panic("no return value specified for DeletePassword") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(email) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteRefresh provides a mock function with given fields: id +func (_m *MockStorage) DeleteRefresh(id string) error { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for DeleteRefresh") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GarbageCollect provides a mock function with given fields: now +func (_m *MockStorage) GarbageCollect(now time.Time) (storage.GCResult, error) { + ret := _m.Called(now) + + if len(ret) == 0 { + panic("no return value specified for GarbageCollect") + } + + var r0 storage.GCResult + var r1 error + if rf, ok := ret.Get(0).(func(time.Time) (storage.GCResult, error)); ok { + return rf(now) + } + if rf, ok := ret.Get(0).(func(time.Time) storage.GCResult); ok { + r0 = rf(now) + } else { + r0 = ret.Get(0).(storage.GCResult) + } + + if rf, ok := ret.Get(1).(func(time.Time) error); ok { + r1 = rf(now) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAuthCode provides a mock function with given fields: id +func (_m *MockStorage) GetAuthCode(id string) (storage.AuthCode, error) { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for GetAuthCode") + } + + var r0 storage.AuthCode + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.AuthCode, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(string) storage.AuthCode); ok { + r0 = rf(id) + } else { + r0 = ret.Get(0).(storage.AuthCode) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAuthRequest provides a mock function with given fields: id +func (_m *MockStorage) GetAuthRequest(id string) (storage.AuthRequest, error) { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for GetAuthRequest") + } + + var r0 storage.AuthRequest + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.AuthRequest, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(string) storage.AuthRequest); ok { + r0 = rf(id) + } else { + r0 = ret.Get(0).(storage.AuthRequest) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetClient provides a mock function with given fields: id +func (_m *MockStorage) GetClient(id string) (storage.Client, error) { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for GetClient") + } + + var r0 storage.Client + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.Client, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(string) storage.Client); ok { + r0 = rf(id) + } else { + r0 = ret.Get(0).(storage.Client) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetConnector provides a mock function with given fields: id +func (_m *MockStorage) GetConnector(id string) (storage.Connector, error) { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for GetConnector") + } + + var r0 storage.Connector + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.Connector, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(string) storage.Connector); ok { + r0 = rf(id) + } else { + r0 = ret.Get(0).(storage.Connector) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetDeviceRequest provides a mock function with given fields: userCode +func (_m *MockStorage) GetDeviceRequest(userCode string) (storage.DeviceRequest, error) { + ret := _m.Called(userCode) + + if len(ret) == 0 { + panic("no return value specified for GetDeviceRequest") + } + + var r0 storage.DeviceRequest + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.DeviceRequest, error)); ok { + return rf(userCode) + } + if rf, ok := ret.Get(0).(func(string) storage.DeviceRequest); ok { + r0 = rf(userCode) + } else { + r0 = ret.Get(0).(storage.DeviceRequest) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(userCode) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetDeviceToken provides a mock function with given fields: deviceCode +func (_m *MockStorage) GetDeviceToken(deviceCode string) (storage.DeviceToken, error) { + ret := _m.Called(deviceCode) + + if len(ret) == 0 { + panic("no return value specified for GetDeviceToken") + } + + var r0 storage.DeviceToken + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.DeviceToken, error)); ok { + return rf(deviceCode) + } + if rf, ok := ret.Get(0).(func(string) storage.DeviceToken); ok { + r0 = rf(deviceCode) + } else { + r0 = ret.Get(0).(storage.DeviceToken) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(deviceCode) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetKeys provides a mock function with no fields +func (_m *MockStorage) GetKeys() (storage.Keys, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetKeys") + } + + var r0 storage.Keys + var r1 error + if rf, ok := ret.Get(0).(func() (storage.Keys, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() storage.Keys); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(storage.Keys) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetOfflineSessions provides a mock function with given fields: userID, connID +func (_m *MockStorage) GetOfflineSessions(userID string, connID string) (storage.OfflineSessions, error) { + ret := _m.Called(userID, connID) + + if len(ret) == 0 { + panic("no return value specified for GetOfflineSessions") + } + + var r0 storage.OfflineSessions + var r1 error + if rf, ok := ret.Get(0).(func(string, string) (storage.OfflineSessions, error)); ok { + return rf(userID, connID) + } + if rf, ok := ret.Get(0).(func(string, string) storage.OfflineSessions); ok { + r0 = rf(userID, connID) + } else { + r0 = ret.Get(0).(storage.OfflineSessions) + } + + if rf, ok := ret.Get(1).(func(string, string) error); ok { + r1 = rf(userID, connID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetPassword provides a mock function with given fields: email +func (_m *MockStorage) GetPassword(email string) (storage.Password, error) { + ret := _m.Called(email) + + if len(ret) == 0 { + panic("no return value specified for GetPassword") + } + + var r0 storage.Password + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.Password, error)); ok { + return rf(email) + } + if rf, ok := ret.Get(0).(func(string) storage.Password); ok { + r0 = rf(email) + } else { + r0 = ret.Get(0).(storage.Password) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(email) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetRefresh provides a mock function with given fields: id +func (_m *MockStorage) GetRefresh(id string) (storage.RefreshToken, error) { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for GetRefresh") + } + + var r0 storage.RefreshToken + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.RefreshToken, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(string) storage.RefreshToken); ok { + r0 = rf(id) + } else { + r0 = ret.Get(0).(storage.RefreshToken) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListClients provides a mock function with no fields +func (_m *MockStorage) ListClients() ([]storage.Client, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ListClients") + } + + var r0 []storage.Client + var r1 error + if rf, ok := ret.Get(0).(func() ([]storage.Client, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []storage.Client); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]storage.Client) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListConnectors provides a mock function with no fields +func (_m *MockStorage) ListConnectors() ([]storage.Connector, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ListConnectors") + } + + var r0 []storage.Connector + var r1 error + if rf, ok := ret.Get(0).(func() ([]storage.Connector, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []storage.Connector); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]storage.Connector) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListPasswords provides a mock function with no fields +func (_m *MockStorage) ListPasswords() ([]storage.Password, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ListPasswords") + } + + var r0 []storage.Password + var r1 error + if rf, ok := ret.Get(0).(func() ([]storage.Password, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []storage.Password); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]storage.Password) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListRefreshTokens provides a mock function with no fields +func (_m *MockStorage) ListRefreshTokens() ([]storage.RefreshToken, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ListRefreshTokens") + } + + var r0 []storage.RefreshToken + var r1 error + if rf, ok := ret.Get(0).(func() ([]storage.RefreshToken, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []storage.RefreshToken); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]storage.RefreshToken) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UpdateAuthRequest provides a mock function with given fields: id, updater +func (_m *MockStorage) UpdateAuthRequest(id string, updater func(storage.AuthRequest) (storage.AuthRequest, error)) error { + ret := _m.Called(id, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateAuthRequest") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, func(storage.AuthRequest) (storage.AuthRequest, error)) error); ok { + r0 = rf(id, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateClient provides a mock function with given fields: id, updater +func (_m *MockStorage) UpdateClient(id string, updater func(storage.Client) (storage.Client, error)) error { + ret := _m.Called(id, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateClient") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, func(storage.Client) (storage.Client, error)) error); ok { + r0 = rf(id, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateConnector provides a mock function with given fields: id, updater +func (_m *MockStorage) UpdateConnector(id string, updater func(storage.Connector) (storage.Connector, error)) error { + ret := _m.Called(id, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateConnector") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, func(storage.Connector) (storage.Connector, error)) error); ok { + r0 = rf(id, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateDeviceToken provides a mock function with given fields: deviceCode, updater +func (_m *MockStorage) UpdateDeviceToken(deviceCode string, updater func(storage.DeviceToken) (storage.DeviceToken, error)) error { + ret := _m.Called(deviceCode, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateDeviceToken") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, func(storage.DeviceToken) (storage.DeviceToken, error)) error); ok { + r0 = rf(deviceCode, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateKeys provides a mock function with given fields: updater +func (_m *MockStorage) UpdateKeys(updater func(storage.Keys) (storage.Keys, error)) error { + ret := _m.Called(updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateKeys") + } + + var r0 error + if rf, ok := ret.Get(0).(func(func(storage.Keys) (storage.Keys, error)) error); ok { + r0 = rf(updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateOfflineSessions provides a mock function with given fields: userID, connID, updater +func (_m *MockStorage) UpdateOfflineSessions(userID string, connID string, updater func(storage.OfflineSessions) (storage.OfflineSessions, error)) error { + ret := _m.Called(userID, connID, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateOfflineSessions") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, string, func(storage.OfflineSessions) (storage.OfflineSessions, error)) error); ok { + r0 = rf(userID, connID, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdatePassword provides a mock function with given fields: email, updater +func (_m *MockStorage) UpdatePassword(email string, updater func(storage.Password) (storage.Password, error)) error { + ret := _m.Called(email, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdatePassword") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, func(storage.Password) (storage.Password, error)) error); ok { + r0 = rf(email, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateRefreshToken provides a mock function with given fields: id, updater +func (_m *MockStorage) UpdateRefreshToken(id string, updater func(storage.RefreshToken) (storage.RefreshToken, error)) error { + ret := _m.Called(id, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateRefreshToken") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, func(storage.RefreshToken) (storage.RefreshToken, error)) error); ok { + r0 = rf(id, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewMockStorage creates a new instance of MockStorage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockStorage(t interface { + mock.TestingT + Cleanup(func()) +}) *MockStorage { + mock := &MockStorage{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +}