mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-10-31 14:15:50 +00:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
			v2.0.2-bet
			...
			v2.1.1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 97830a309b | ||
|   | fe594d2755 | ||
|   | b3aac26644 | ||
|   | c37f66abb9 | ||
|   | 2c4f086008 | ||
|   | 6e5f882e0b | ||
|   | 99268f80c9 | ||
|   | dcd816b6c6 | ||
|   | 381f6ef76f | ||
|   | 8a8ba18ded | ||
|   | 29f0a94faf | ||
|   | 6602e8140b | ||
|   | 2385599c80 | 
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -13,3 +13,9 @@ users.txt | |||||||
| # secret test file | # secret test file | ||||||
| secret.txt | secret.txt | ||||||
| secret_oauth.txt | secret_oauth.txt | ||||||
|  |  | ||||||
|  | # vscode | ||||||
|  | .vscode | ||||||
|  |  | ||||||
|  | # apple stuff | ||||||
|  | .DS_Store | ||||||
| @@ -35,7 +35,7 @@ COPY ./cmd ./cmd | |||||||
| COPY ./internal ./internal | COPY ./internal ./internal | ||||||
| COPY --from=site-builder /site/dist ./internal/assets/dist | COPY --from=site-builder /site/dist ./internal/assets/dist | ||||||
|  |  | ||||||
| RUN CGO_ENABLED=0 go build | RUN CGO_ENABLED=0 go build -ldflags "-s -w" | ||||||
|  |  | ||||||
| # Runner | # Runner | ||||||
| FROM alpine:3.21 AS runner | FROM alpine:3.21 AS runner | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								cmd/root.go
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								cmd/root.go
									
									
									
									
									
								
							| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"tinyauth/internal/api" | 	"tinyauth/internal/api" | ||||||
| 	"tinyauth/internal/assets" | 	"tinyauth/internal/assets" | ||||||
| 	"tinyauth/internal/auth" | 	"tinyauth/internal/auth" | ||||||
|  | 	"tinyauth/internal/docker" | ||||||
| 	"tinyauth/internal/hooks" | 	"tinyauth/internal/hooks" | ||||||
| 	"tinyauth/internal/providers" | 	"tinyauth/internal/providers" | ||||||
| 	"tinyauth/internal/types" | 	"tinyauth/internal/types" | ||||||
| @@ -38,6 +39,7 @@ var rootCmd = &cobra.Command{ | |||||||
| 		config.GithubClientSecret = utils.GetSecret(config.GithubClientSecret, config.GithubClientSecretFile) | 		config.GithubClientSecret = utils.GetSecret(config.GithubClientSecret, config.GithubClientSecretFile) | ||||||
| 		config.GoogleClientSecret = utils.GetSecret(config.GoogleClientSecret, config.GoogleClientSecretFile) | 		config.GoogleClientSecret = utils.GetSecret(config.GoogleClientSecret, config.GoogleClientSecretFile) | ||||||
| 		config.GenericClientSecret = utils.GetSecret(config.GenericClientSecret, config.GenericClientSecretFile) | 		config.GenericClientSecret = utils.GetSecret(config.GenericClientSecret, config.GenericClientSecretFile) | ||||||
|  | 		config.TailscaleClientSecret = utils.GetSecret(config.TailscaleClientSecret, config.TailscaleClientSecretFile) | ||||||
|  |  | ||||||
| 		// Validate config | 		// Validate config | ||||||
| 		validator := validator.New() | 		validator := validator.New() | ||||||
| @@ -62,23 +64,32 @@ var rootCmd = &cobra.Command{ | |||||||
|  |  | ||||||
| 		// Create OAuth config | 		// Create OAuth config | ||||||
| 		oauthConfig := types.OAuthConfig{ | 		oauthConfig := types.OAuthConfig{ | ||||||
| 			GithubClientId:      config.GithubClientId, | 			GithubClientId:        config.GithubClientId, | ||||||
| 			GithubClientSecret:  config.GithubClientSecret, | 			GithubClientSecret:    config.GithubClientSecret, | ||||||
| 			GoogleClientId:      config.GoogleClientId, | 			GoogleClientId:        config.GoogleClientId, | ||||||
| 			GoogleClientSecret:  config.GoogleClientSecret, | 			GoogleClientSecret:    config.GoogleClientSecret, | ||||||
| 			GenericClientId:     config.GenericClientId, | 			TailscaleClientId:     config.TailscaleClientId, | ||||||
| 			GenericClientSecret: config.GenericClientSecret, | 			TailscaleClientSecret: config.TailscaleClientSecret, | ||||||
| 			GenericScopes:       strings.Split(config.GenericScopes, ","), | 			GenericClientId:       config.GenericClientId, | ||||||
| 			GenericAuthURL:      config.GenericAuthURL, | 			GenericClientSecret:   config.GenericClientSecret, | ||||||
| 			GenericTokenURL:     config.GenericTokenURL, | 			GenericScopes:         strings.Split(config.GenericScopes, ","), | ||||||
| 			GenericUserURL:      config.GenericUserURL, | 			GenericAuthURL:        config.GenericAuthURL, | ||||||
| 			AppURL:              config.AppURL, | 			GenericTokenURL:       config.GenericTokenURL, | ||||||
|  | 			GenericUserURL:        config.GenericUserURL, | ||||||
|  | 			AppURL:                config.AppURL, | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		log.Debug().Msg("Parsed OAuth config") | 		log.Debug().Msg("Parsed OAuth config") | ||||||
|  |  | ||||||
|  | 		// Create docker service | ||||||
|  | 		docker := docker.NewDocker() | ||||||
|  |  | ||||||
|  | 		// Initialize docker | ||||||
|  | 		dockerErr := docker.Init() | ||||||
|  | 		HandleError(dockerErr, "Failed to initialize docker") | ||||||
|  |  | ||||||
| 		// Create auth service | 		// Create auth service | ||||||
| 		auth := auth.NewAuth(users, oauthWhitelist) | 		auth := auth.NewAuth(docker, users, oauthWhitelist) | ||||||
|  |  | ||||||
| 		// Create OAuth providers service | 		// Create OAuth providers service | ||||||
| 		providers := providers.NewProviders(oauthConfig) | 		providers := providers.NewProviders(oauthConfig) | ||||||
| @@ -139,6 +150,9 @@ func init() { | |||||||
| 	rootCmd.Flags().String("google-client-id", "", "Google OAuth client ID.") | 	rootCmd.Flags().String("google-client-id", "", "Google OAuth client ID.") | ||||||
| 	rootCmd.Flags().String("google-client-secret", "", "Google OAuth client secret.") | 	rootCmd.Flags().String("google-client-secret", "", "Google OAuth client secret.") | ||||||
| 	rootCmd.Flags().String("google-client-secret-file", "", "Google OAuth client secret file.") | 	rootCmd.Flags().String("google-client-secret-file", "", "Google OAuth client secret file.") | ||||||
|  | 	rootCmd.Flags().String("tailscale-client-id", "", "Tailscale OAuth client ID.") | ||||||
|  | 	rootCmd.Flags().String("tailscale-client-secret", "", "Tailscale OAuth client secret.") | ||||||
|  | 	rootCmd.Flags().String("tailscale-client-secret-file", "", "Tailscale OAuth client secret file.") | ||||||
| 	rootCmd.Flags().String("generic-client-id", "", "Generic OAuth client ID.") | 	rootCmd.Flags().String("generic-client-id", "", "Generic OAuth client ID.") | ||||||
| 	rootCmd.Flags().String("generic-client-secret", "", "Generic OAuth client secret.") | 	rootCmd.Flags().String("generic-client-secret", "", "Generic OAuth client secret.") | ||||||
| 	rootCmd.Flags().String("generic-client-secret-file", "", "Generic OAuth client secret file.") | 	rootCmd.Flags().String("generic-client-secret-file", "", "Generic OAuth client secret file.") | ||||||
| @@ -164,6 +178,9 @@ func init() { | |||||||
| 	viper.BindEnv("google-client-id", "GOOGLE_CLIENT_ID") | 	viper.BindEnv("google-client-id", "GOOGLE_CLIENT_ID") | ||||||
| 	viper.BindEnv("google-client-secret", "GOOGLE_CLIENT_SECRET") | 	viper.BindEnv("google-client-secret", "GOOGLE_CLIENT_SECRET") | ||||||
| 	viper.BindEnv("google-client-secret-file", "GOOGLE_CLIENT_SECRET_FILE") | 	viper.BindEnv("google-client-secret-file", "GOOGLE_CLIENT_SECRET_FILE") | ||||||
|  | 	viper.BindEnv("tailscale-client-id", "TAILSCALE_CLIENT_ID") | ||||||
|  | 	viper.BindEnv("tailscale-client-secret", "TAILSCALE_CLIENT_SECRET") | ||||||
|  | 	viper.BindEnv("tailscale-client-secret-file", "TAILSCALE_CLIENT_SECRET_FILE") | ||||||
| 	viper.BindEnv("generic-client-id", "GENERIC_CLIENT_ID") | 	viper.BindEnv("generic-client-id", "GENERIC_CLIENT_ID") | ||||||
| 	viper.BindEnv("generic-client-secret", "GENERIC_CLIENT_SECRET") | 	viper.BindEnv("generic-client-secret", "GENERIC_CLIENT_SECRET") | ||||||
| 	viper.BindEnv("generic-client-secret-file", "GENERIC_CLIENT_SECRET_FILE") | 	viper.BindEnv("generic-client-secret-file", "GENERIC_CLIENT_SECRET_FILE") | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								go.mod
									
									
									
									
									
								
							| @@ -14,6 +14,7 @@ require ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
|  | 	github.com/Microsoft/go-winio v0.4.14 // indirect | ||||||
| 	github.com/atotto/clipboard v0.1.4 // indirect | 	github.com/atotto/clipboard v0.1.4 // indirect | ||||||
| 	github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect | 	github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect | ||||||
| 	github.com/bytedance/sonic v1.12.7 // indirect | 	github.com/bytedance/sonic v1.12.7 // indirect | ||||||
| @@ -27,14 +28,22 @@ require ( | |||||||
| 	github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect | 	github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect | ||||||
| 	github.com/charmbracelet/x/term v0.2.0 // indirect | 	github.com/charmbracelet/x/term v0.2.0 // indirect | ||||||
| 	github.com/cloudwego/base64x v0.1.4 // indirect | 	github.com/cloudwego/base64x v0.1.4 // indirect | ||||||
|  | 	github.com/distribution/reference v0.6.0 // indirect | ||||||
|  | 	github.com/docker/docker v27.5.1+incompatible // indirect | ||||||
|  | 	github.com/docker/go-connections v0.5.0 // indirect | ||||||
|  | 	github.com/docker/go-units v0.5.0 // indirect | ||||||
| 	github.com/dustin/go-humanize v1.0.1 // indirect | 	github.com/dustin/go-humanize v1.0.1 // indirect | ||||||
| 	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect | 	github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect | ||||||
|  | 	github.com/felixge/httpsnoop v1.0.4 // indirect | ||||||
| 	github.com/fsnotify/fsnotify v1.7.0 // indirect | 	github.com/fsnotify/fsnotify v1.7.0 // indirect | ||||||
| 	github.com/gabriel-vasile/mimetype v1.4.8 // indirect | 	github.com/gabriel-vasile/mimetype v1.4.8 // indirect | ||||||
| 	github.com/gin-contrib/sse v1.0.0 // indirect | 	github.com/gin-contrib/sse v1.0.0 // indirect | ||||||
|  | 	github.com/go-logr/logr v1.4.1 // indirect | ||||||
|  | 	github.com/go-logr/stdr v1.2.2 // indirect | ||||||
| 	github.com/go-playground/locales v0.14.1 // indirect | 	github.com/go-playground/locales v0.14.1 // indirect | ||||||
| 	github.com/go-playground/universal-translator v0.18.1 // indirect | 	github.com/go-playground/universal-translator v0.18.1 // indirect | ||||||
| 	github.com/goccy/go-json v0.10.4 // indirect | 	github.com/goccy/go-json v0.10.4 // indirect | ||||||
|  | 	github.com/gogo/protobuf v1.3.2 // indirect | ||||||
| 	github.com/gorilla/context v1.1.2 // indirect | 	github.com/gorilla/context v1.1.2 // indirect | ||||||
| 	github.com/gorilla/securecookie v1.1.2 // indirect | 	github.com/gorilla/securecookie v1.1.2 // indirect | ||||||
| 	github.com/gorilla/sessions v1.2.2 // indirect | 	github.com/gorilla/sessions v1.2.2 // indirect | ||||||
| @@ -51,12 +60,16 @@ require ( | |||||||
| 	github.com/mattn/go-runewidth v0.0.16 // indirect | 	github.com/mattn/go-runewidth v0.0.16 // indirect | ||||||
| 	github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect | 	github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect | ||||||
| 	github.com/mitchellh/mapstructure v1.5.0 // indirect | 	github.com/mitchellh/mapstructure v1.5.0 // indirect | ||||||
|  | 	github.com/moby/docker-image-spec v1.3.1 // indirect | ||||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||||
| 	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect | 	github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect | ||||||
| 	github.com/muesli/cancelreader v0.2.2 // indirect | 	github.com/muesli/cancelreader v0.2.2 // indirect | ||||||
| 	github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect | 	github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect | ||||||
|  | 	github.com/opencontainers/go-digest v1.0.0 // indirect | ||||||
|  | 	github.com/opencontainers/image-spec v1.1.0 // indirect | ||||||
| 	github.com/pelletier/go-toml/v2 v2.2.3 // indirect | 	github.com/pelletier/go-toml/v2 v2.2.3 // indirect | ||||||
|  | 	github.com/pkg/errors v0.9.1 // indirect | ||||||
| 	github.com/rivo/uniseg v0.4.7 // indirect | 	github.com/rivo/uniseg v0.4.7 // indirect | ||||||
| 	github.com/sagikazarmark/locafero v0.4.0 // indirect | 	github.com/sagikazarmark/locafero v0.4.0 // indirect | ||||||
| 	github.com/sagikazarmark/slog-shim v0.1.0 // indirect | 	github.com/sagikazarmark/slog-shim v0.1.0 // indirect | ||||||
| @@ -67,6 +80,10 @@ require ( | |||||||
| 	github.com/subosito/gotenv v1.6.0 // indirect | 	github.com/subosito/gotenv v1.6.0 // indirect | ||||||
| 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect | 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect | ||||||
| 	github.com/ugorji/go/codec v1.2.12 // indirect | 	github.com/ugorji/go/codec v1.2.12 // indirect | ||||||
|  | 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect | ||||||
|  | 	go.opentelemetry.io/otel v1.24.0 // indirect | ||||||
|  | 	go.opentelemetry.io/otel/metric v1.24.0 // indirect | ||||||
|  | 	go.opentelemetry.io/otel/trace v1.24.0 // indirect | ||||||
| 	go.uber.org/atomic v1.9.0 // indirect | 	go.uber.org/atomic v1.9.0 // indirect | ||||||
| 	go.uber.org/multierr v1.9.0 // indirect | 	go.uber.org/multierr v1.9.0 // indirect | ||||||
| 	golang.org/x/arch v0.13.0 // indirect | 	golang.org/x/arch v0.13.0 // indirect | ||||||
|   | |||||||
							
								
								
									
										69
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										69
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,3 +1,5 @@ | |||||||
|  | github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= | ||||||
|  | github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= | ||||||
| github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= | github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= | ||||||
| github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= | github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= | ||||||
| github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= | github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= | ||||||
| @@ -32,10 +34,20 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs | |||||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= | ||||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||||
|  | github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= | ||||||
|  | github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= | ||||||
|  | github.com/docker/docker v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8= | ||||||
|  | github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= | ||||||
|  | github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= | ||||||
|  | github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= | ||||||
|  | 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/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= | ||||||
| github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= | ||||||
| github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= | github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= | ||||||
| github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= | github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= | ||||||
|  | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= | ||||||
|  | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= | ||||||
| github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= | ||||||
| github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= | ||||||
| github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= | ||||||
| @@ -48,6 +60,11 @@ github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E | |||||||
| github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0= | github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0= | ||||||
| github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= | github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= | ||||||
| github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= | github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= | ||||||
|  | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= | ||||||
|  | github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= | ||||||
|  | github.com/go-logr/logr v1.4.1/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-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= | ||||||
| github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= | ||||||
| github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= | ||||||
| @@ -59,6 +76,8 @@ github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1 | |||||||
| github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= | github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= | ||||||
| github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= | github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= | ||||||
| github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= | ||||||
|  | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= | ||||||
|  | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= | ||||||
| github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||||
| github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | ||||||
| github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||||
| @@ -79,10 +98,13 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 | |||||||
| github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= | ||||||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | ||||||
| github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | ||||||
|  | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= | ||||||
|  | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | ||||||
| github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= | ||||||
| github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= | github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= | ||||||
| github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= | github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= | ||||||
| github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= | ||||||
|  | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= | ||||||
| github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= | ||||||
| github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= | ||||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||||
| @@ -108,6 +130,8 @@ github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4 | |||||||
| github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= | github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= | ||||||
| github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= | ||||||
| github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= | ||||||
|  | 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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= | ||||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||||
| @@ -119,8 +143,14 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU | |||||||
| github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= | github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= | ||||||
| github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg= | github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg= | ||||||
| github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= | github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= | ||||||
|  | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= | ||||||
|  | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= | ||||||
|  | github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= | ||||||
|  | github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= | ||||||
| github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= | github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= | ||||||
| github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= | github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= | ||||||
|  | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||||
|  | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | 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 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= | ||||||
| @@ -138,6 +168,7 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke | |||||||
| github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= | github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= | ||||||
| github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= | github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= | ||||||
| github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= | github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= | ||||||
|  | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= | ||||||
| github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= | github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= | ||||||
| github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= | github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= | ||||||
| github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= | github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= | ||||||
| @@ -151,9 +182,11 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An | |||||||
| github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= | github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= | ||||||
| github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= | github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= | ||||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||||
|  | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||||
| github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||||||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||||||
| github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= | github.com/stretchr/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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||||
| github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||||
| @@ -168,31 +201,67 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS | |||||||
| github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= | ||||||
| github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= | ||||||
| github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= | ||||||
|  | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||||
|  | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||||
|  | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= | ||||||
|  | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= | ||||||
|  | go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= | ||||||
|  | go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= | ||||||
|  | go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= | ||||||
|  | go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= | ||||||
|  | go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= | ||||||
|  | go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= | ||||||
| go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= | go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= | ||||||
| go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= | go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= | ||||||
| go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= | go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= | ||||||
| go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= | go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= | ||||||
| golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA= | golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA= | ||||||
| golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= | golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= | ||||||
|  | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||||
|  | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||||
|  | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||||
| golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= | golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= | ||||||
| golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= | golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= | ||||||
| golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= | ||||||
| golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= | golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= | ||||||
|  | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||||
|  | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||||
|  | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||||
|  | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||||
|  | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||||
|  | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||||
| golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= | golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= | ||||||
| golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= | golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= | ||||||
| golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= | golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= | ||||||
| golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= | golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= | ||||||
|  | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
|  | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
|  | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= | golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= | ||||||
| golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | ||||||
|  | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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= | ||||||
|  | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
|  | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||||
| golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= | ||||||
| golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||||
|  | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||||
|  | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||||
| golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= | ||||||
| golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= | ||||||
|  | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||||
|  | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
|  | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | ||||||
|  | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||||
|  | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
|  | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
|  | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
| google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= | google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= | ||||||
| google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= | google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= | ||||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ package api | |||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io/fs" | 	"io/fs" | ||||||
|  | 	"math/rand/v2" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" | 	"os" | ||||||
| 	"strings" | 	"strings" | ||||||
| @@ -98,8 +99,33 @@ func (api *API) SetupRoutes() { | |||||||
| 		log.Debug().Msg("Checking auth") | 		log.Debug().Msg("Checking auth") | ||||||
| 		userContext := api.Hooks.UseUserContext(c) | 		userContext := api.Hooks.UseUserContext(c) | ||||||
|  |  | ||||||
|  | 		uri := c.Request.Header.Get("X-Forwarded-Uri") | ||||||
|  | 		proto := c.Request.Header.Get("X-Forwarded-Proto") | ||||||
|  | 		host := c.Request.Header.Get("X-Forwarded-Host") | ||||||
|  |  | ||||||
| 		if userContext.IsLoggedIn { | 		if userContext.IsLoggedIn { | ||||||
| 			log.Debug().Msg("Authenticated") | 			log.Debug().Msg("Authenticated") | ||||||
|  |  | ||||||
|  | 			appAllowed, appAllowedErr := api.Auth.ResourceAllowed(userContext, host) | ||||||
|  |  | ||||||
|  | 			log.Debug().Bool("appAllowed", appAllowed).Msg("Checking if user is allowed") | ||||||
|  |  | ||||||
|  | 			if api.handleError(c, "Failed to check if resource is allowed", appAllowedErr) { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if !appAllowed { | ||||||
|  | 				log.Warn().Str("username", userContext.Username).Str("host", host).Msg("User not allowed") | ||||||
|  | 				queries, queryErr := query.Values(types.UnauthorizedQuery{ | ||||||
|  | 					Username: userContext.Username, | ||||||
|  | 					Resource: strings.Split(host, ".")[0], | ||||||
|  | 				}) | ||||||
|  | 				if api.handleError(c, "Failed to build query", queryErr) { | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 				c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/unauthorized?%s", api.Config.AppURL, queries.Encode())) | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			c.JSON(200, gin.H{ | 			c.JSON(200, gin.H{ | ||||||
| 				"status":  200, | 				"status":  200, | ||||||
| 				"message": "Authenticated", | 				"message": "Authenticated", | ||||||
| @@ -107,9 +133,6 @@ func (api *API) SetupRoutes() { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		uri := c.Request.Header.Get("X-Forwarded-Uri") |  | ||||||
| 		proto := c.Request.Header.Get("X-Forwarded-Proto") |  | ||||||
| 		host := c.Request.Header.Get("X-Forwarded-Host") |  | ||||||
| 		queries, queryErr := query.Values(types.LoginQuery{ | 		queries, queryErr := query.Values(types.LoginQuery{ | ||||||
| 			RedirectURI: fmt.Sprintf("%s://%s%s", proto, host, uri), | 			RedirectURI: fmt.Sprintf("%s://%s%s", proto, host, uri), | ||||||
| 		}) | 		}) | ||||||
| @@ -275,6 +298,21 @@ func (api *API) SetupRoutes() { | |||||||
| 			c.SetCookie("tinyauth_redirect_uri", redirectURI, 3600, "/", api.Domain, api.Config.CookieSecure, true) | 			c.SetCookie("tinyauth_redirect_uri", redirectURI, 3600, "/", api.Domain, api.Config.CookieSecure, true) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		if request.Provider == "tailscale" { | ||||||
|  | 			tailscaleQuery, tailscaleQueryErr := query.Values(types.TailscaleQuery{ | ||||||
|  | 				Code: (1000 + rand.IntN(9000)), // doesn't need to be secure, just there to avoid caching | ||||||
|  | 			}) | ||||||
|  | 			if api.handleError(c, "Failed to build query", tailscaleQueryErr) { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			c.JSON(200, gin.H{ | ||||||
|  | 				"status":  200, | ||||||
|  | 				"message": "Ok", | ||||||
|  | 				"url":     fmt.Sprintf("%s/api/oauth/callback/tailscale?%s", api.Config.AppURL, tailscaleQuery.Encode()), | ||||||
|  | 			}) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		c.JSON(200, gin.H{ | 		c.JSON(200, gin.H{ | ||||||
| 			"status":  200, | 			"status":  200, | ||||||
| 			"message": "Ok", | 			"message": "Ok", | ||||||
| @@ -287,7 +325,7 @@ func (api *API) SetupRoutes() { | |||||||
|  |  | ||||||
| 		bindErr := c.BindUri(&providerName) | 		bindErr := c.BindUri(&providerName) | ||||||
|  |  | ||||||
| 		if handleApiError(c, "Failed to bind URI", bindErr) { | 		if api.handleError(c, "Failed to bind URI", bindErr) { | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -316,7 +354,7 @@ func (api *API) SetupRoutes() { | |||||||
|  |  | ||||||
| 		log.Debug().Msg("Got token") | 		log.Debug().Msg("Got token") | ||||||
|  |  | ||||||
| 		if handleApiError(c, "Failed to exchange token", tokenErr) { | 		if api.handleError(c, "Failed to exchange token", tokenErr) { | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -324,7 +362,7 @@ func (api *API) SetupRoutes() { | |||||||
|  |  | ||||||
| 		log.Debug().Str("email", email).Msg("Got email") | 		log.Debug().Str("email", email).Msg("Got email") | ||||||
|  |  | ||||||
| 		if handleApiError(c, "Failed to get user", emailErr) { | 		if api.handleError(c, "Failed to get user", emailErr) { | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -333,7 +371,7 @@ func (api *API) SetupRoutes() { | |||||||
| 			unauthorizedQuery, unauthorizedQueryErr := query.Values(types.UnauthorizedQuery{ | 			unauthorizedQuery, unauthorizedQueryErr := query.Values(types.UnauthorizedQuery{ | ||||||
| 				Username: email, | 				Username: email, | ||||||
| 			}) | 			}) | ||||||
| 			if handleApiError(c, "Failed to build query", unauthorizedQueryErr) { | 			if api.handleError(c, "Failed to build query", unauthorizedQueryErr) { | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/unauthorized?%s", api.Config.AppURL, unauthorizedQuery.Encode())) | 			c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/unauthorized?%s", api.Config.AppURL, unauthorizedQuery.Encode())) | ||||||
| @@ -365,7 +403,7 @@ func (api *API) SetupRoutes() { | |||||||
|  |  | ||||||
| 		log.Debug().Msg("Got redirect query") | 		log.Debug().Msg("Got redirect query") | ||||||
|  |  | ||||||
| 		if handleApiError(c, "Failed to build query", redirectQueryErr) { | 		if api.handleError(c, "Failed to build query", redirectQueryErr) { | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -378,6 +416,15 @@ func (api *API) Run() { | |||||||
| 	api.Router.Run(fmt.Sprintf("%s:%d", api.Config.Address, api.Config.Port)) | 	api.Router.Run(fmt.Sprintf("%s:%d", api.Config.Address, api.Config.Port)) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (api *API) handleError(c *gin.Context, msg string, err error) bool { | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error().Err(err).Msg(msg) | ||||||
|  | 		c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", api.Config.AppURL)) | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
| func zerolog() gin.HandlerFunc { | func zerolog() gin.HandlerFunc { | ||||||
| 	return func(c *gin.Context) { | 	return func(c *gin.Context) { | ||||||
| 		tStart := time.Now() | 		tStart := time.Now() | ||||||
| @@ -401,12 +448,3 @@ func zerolog() gin.HandlerFunc { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func handleApiError(c *gin.Context, msg string, err error) bool { |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Error().Err(err).Msg(msg) |  | ||||||
| 		c.Redirect(http.StatusPermanentRedirect, "/error") |  | ||||||
| 		return true |  | ||||||
| 	} |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| v2.0.2 | v2.1.1 | ||||||
| @@ -1,7 +1,11 @@ | |||||||
| package auth | package auth | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"slices" | ||||||
|  | 	"strings" | ||||||
|  | 	"tinyauth/internal/docker" | ||||||
| 	"tinyauth/internal/types" | 	"tinyauth/internal/types" | ||||||
|  | 	"tinyauth/internal/utils" | ||||||
|  |  | ||||||
| 	"github.com/gin-contrib/sessions" | 	"github.com/gin-contrib/sessions" | ||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
| @@ -9,8 +13,9 @@ import ( | |||||||
| 	"golang.org/x/crypto/bcrypt" | 	"golang.org/x/crypto/bcrypt" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func NewAuth(userList types.Users, oauthWhitelist []string) *Auth { | func NewAuth(docker *docker.Docker, userList types.Users, oauthWhitelist []string) *Auth { | ||||||
| 	return &Auth{ | 	return &Auth{ | ||||||
|  | 		Docker:         docker, | ||||||
| 		Users:          userList, | 		Users:          userList, | ||||||
| 		OAuthWhitelist: oauthWhitelist, | 		OAuthWhitelist: oauthWhitelist, | ||||||
| 	} | 	} | ||||||
| @@ -18,6 +23,7 @@ func NewAuth(userList types.Users, oauthWhitelist []string) *Auth { | |||||||
|  |  | ||||||
| type Auth struct { | type Auth struct { | ||||||
| 	Users          types.Users | 	Users          types.Users | ||||||
|  | 	Docker         *docker.Docker | ||||||
| 	OAuthWhitelist []string | 	OAuthWhitelist []string | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -89,3 +95,60 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) (types.SessionCookie, error) | |||||||
| func (auth *Auth) UserAuthConfigured() bool { | func (auth *Auth) UserAuthConfigured() bool { | ||||||
| 	return len(auth.Users) > 0 | 	return len(auth.Users) > 0 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (auth *Auth) ResourceAllowed(context types.UserContext, host string) (bool, error) { | ||||||
|  | 	isConnected := auth.Docker.DockerConnected() | ||||||
|  |  | ||||||
|  | 	if !isConnected { | ||||||
|  | 		log.Debug().Msg("Docker not connected, allowing access") | ||||||
|  | 		return true, nil | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	appId := strings.Split(host, ".")[0] | ||||||
|  | 	containers, containersErr := auth.Docker.GetContainers() | ||||||
|  |  | ||||||
|  | 	if containersErr != nil { | ||||||
|  | 		return false, containersErr | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Debug().Msg("Got containers") | ||||||
|  |  | ||||||
|  | 	for _, container := range containers { | ||||||
|  | 		inspect, inspectErr := auth.Docker.InspectContainer(container.ID) | ||||||
|  |  | ||||||
|  | 		if inspectErr != nil { | ||||||
|  | 			return false, inspectErr | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		containerName := strings.Split(inspect.Name, "/")[1] | ||||||
|  |  | ||||||
|  | 		if containerName == appId { | ||||||
|  | 			log.Debug().Str("container", containerName).Msg("Found container") | ||||||
|  |  | ||||||
|  | 			labels := utils.GetTinyauthLabels(inspect.Config.Labels) | ||||||
|  |  | ||||||
|  | 			log.Debug().Msg("Got labels") | ||||||
|  |  | ||||||
|  | 			if context.OAuth && len(labels.OAuthWhitelist) != 0 { | ||||||
|  | 				log.Debug().Msg("Checking OAuth whitelist") | ||||||
|  | 				if slices.Contains(labels.OAuthWhitelist, context.Username) { | ||||||
|  | 					return true, nil | ||||||
|  | 				} | ||||||
|  | 				return false, nil | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if len(labels.Users) != 0 { | ||||||
|  | 				log.Debug().Msg("Checking users") | ||||||
|  | 				if slices.Contains(labels.Users, context.Username) { | ||||||
|  | 					return true, nil | ||||||
|  | 				} | ||||||
|  | 				return false, nil | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Debug().Msg("No matching container found, allowing access") | ||||||
|  |  | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								internal/constants/constants.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								internal/constants/constants.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | package constants | ||||||
|  |  | ||||||
|  | var TinyauthLabels = []string{ | ||||||
|  | 	"tinyauth.oauth.whitelist", | ||||||
|  | 	"tinyauth.users", | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								internal/docker/docker.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								internal/docker/docker.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | package docker | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  |  | ||||||
|  | 	"github.com/docker/docker/api/types" | ||||||
|  | 	"github.com/docker/docker/api/types/container" | ||||||
|  | 	"github.com/docker/docker/client" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func NewDocker() *Docker { | ||||||
|  | 	return &Docker{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Docker struct { | ||||||
|  | 	Client  *client.Client | ||||||
|  | 	Context context.Context | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (docker *Docker) Init() error { | ||||||
|  | 	apiClient, err := client.NewClientWithOpts(client.FromEnv) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	docker.Context = context.Background() | ||||||
|  | 	docker.Client = apiClient | ||||||
|  |  | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (docker *Docker) GetContainers() ([]types.Container, error) { | ||||||
|  | 	containers, err := docker.Client.ContainerList(docker.Context, container.ListOptions{}) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return containers, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (docker *Docker) InspectContainer(containerId string) (types.ContainerJSON, error) { | ||||||
|  | 	inspect, err := docker.Client.ContainerInspect(docker.Context, containerId) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return types.ContainerJSON{}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return inspect, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (docker *Docker) DockerConnected() bool { | ||||||
|  | 	_, err := docker.Client.Ping(docker.Context) | ||||||
|  | 	return err == nil | ||||||
|  | } | ||||||
| @@ -17,10 +17,11 @@ func NewProviders(config types.OAuthConfig) *Providers { | |||||||
| } | } | ||||||
|  |  | ||||||
| type Providers struct { | type Providers struct { | ||||||
| 	Config  types.OAuthConfig | 	Config    types.OAuthConfig | ||||||
| 	Github  *oauth.OAuth | 	Github    *oauth.OAuth | ||||||
| 	Google  *oauth.OAuth | 	Google    *oauth.OAuth | ||||||
| 	Generic *oauth.OAuth | 	Tailscale *oauth.OAuth | ||||||
|  | 	Generic   *oauth.OAuth | ||||||
| } | } | ||||||
|  |  | ||||||
| func (providers *Providers) Init() { | func (providers *Providers) Init() { | ||||||
| @@ -46,6 +47,17 @@ func (providers *Providers) Init() { | |||||||
| 		}) | 		}) | ||||||
| 		providers.Google.Init() | 		providers.Google.Init() | ||||||
| 	} | 	} | ||||||
|  | 	if providers.Config.TailscaleClientId != "" && providers.Config.TailscaleClientSecret != "" { | ||||||
|  | 		log.Info().Msg("Initializing Tailscale OAuth") | ||||||
|  | 		providers.Tailscale = oauth.NewOAuth(oauth2.Config{ | ||||||
|  | 			ClientID:     providers.Config.TailscaleClientId, | ||||||
|  | 			ClientSecret: providers.Config.TailscaleClientSecret, | ||||||
|  | 			RedirectURL:  fmt.Sprintf("%s/api/oauth/callback/tailscale", providers.Config.AppURL), | ||||||
|  | 			Scopes:       TailscaleScopes(), | ||||||
|  | 			Endpoint:     TailscaleEndpoint, | ||||||
|  | 		}) | ||||||
|  | 		providers.Tailscale.Init() | ||||||
|  | 	} | ||||||
| 	if providers.Config.GenericClientId != "" && providers.Config.GenericClientSecret != "" { | 	if providers.Config.GenericClientId != "" && providers.Config.GenericClientSecret != "" { | ||||||
| 		log.Info().Msg("Initializing Generic OAuth") | 		log.Info().Msg("Initializing Generic OAuth") | ||||||
| 		providers.Generic = oauth.NewOAuth(oauth2.Config{ | 		providers.Generic = oauth.NewOAuth(oauth2.Config{ | ||||||
| @@ -68,6 +80,8 @@ func (providers *Providers) GetProvider(provider string) *oauth.OAuth { | |||||||
| 		return providers.Github | 		return providers.Github | ||||||
| 	case "google": | 	case "google": | ||||||
| 		return providers.Google | 		return providers.Google | ||||||
|  | 	case "tailscale": | ||||||
|  | 		return providers.Tailscale | ||||||
| 	case "generic": | 	case "generic": | ||||||
| 		return providers.Generic | 		return providers.Generic | ||||||
| 	default: | 	default: | ||||||
| @@ -103,6 +117,19 @@ func (providers *Providers) GetUser(provider string) (string, error) { | |||||||
| 		} | 		} | ||||||
| 		log.Debug().Msg("Got email from google") | 		log.Debug().Msg("Got email from google") | ||||||
| 		return email, nil | 		return email, nil | ||||||
|  | 	case "tailscale": | ||||||
|  | 		if providers.Tailscale == nil { | ||||||
|  | 			log.Debug().Msg("Tailscale provider not configured") | ||||||
|  | 			return "", nil | ||||||
|  | 		} | ||||||
|  | 		client := providers.Tailscale.GetClient() | ||||||
|  | 		log.Debug().Msg("Got client from tailscale") | ||||||
|  | 		email, emailErr := GetTailscaleEmail(client) | ||||||
|  | 		if emailErr != nil { | ||||||
|  | 			return "", emailErr | ||||||
|  | 		} | ||||||
|  | 		log.Debug().Msg("Got email from tailscale") | ||||||
|  | 		return email, nil | ||||||
| 	case "generic": | 	case "generic": | ||||||
| 		if providers.Generic == nil { | 		if providers.Generic == nil { | ||||||
| 			log.Debug().Msg("Generic provider not configured") | 			log.Debug().Msg("Generic provider not configured") | ||||||
| @@ -129,6 +156,9 @@ func (provider *Providers) GetConfiguredProviders() []string { | |||||||
| 	if provider.Google != nil { | 	if provider.Google != nil { | ||||||
| 		providers = append(providers, "google") | 		providers = append(providers, "google") | ||||||
| 	} | 	} | ||||||
|  | 	if provider.Tailscale != nil { | ||||||
|  | 		providers = append(providers, "tailscale") | ||||||
|  | 	} | ||||||
| 	if provider.Generic != nil { | 	if provider.Generic != nil { | ||||||
| 		providers = append(providers, "generic") | 		providers = append(providers, "generic") | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										56
									
								
								internal/providers/tailscale.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								internal/providers/tailscale.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | |||||||
|  | package providers | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"io" | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"github.com/rs/zerolog/log" | ||||||
|  | 	"golang.org/x/oauth2" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type TailscaleUser struct { | ||||||
|  | 	LoginName string `json:"loginName"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type TailscaleUserInfoResponse struct { | ||||||
|  | 	Users []TailscaleUser `json:"users"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TailscaleScopes() []string { | ||||||
|  | 	return []string{"users:read"} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var TailscaleEndpoint = oauth2.Endpoint{ | ||||||
|  | 	TokenURL: "https://api.tailscale.com/api/v2/oauth/token", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetTailscaleEmail(client *http.Client) (string, error) { | ||||||
|  | 	res, resErr := client.Get("https://api.tailscale.com/api/v2/tailnet/-/users") | ||||||
|  |  | ||||||
|  | 	if resErr != nil { | ||||||
|  | 		return "", resErr | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Debug().Msg("Got response from tailscale") | ||||||
|  |  | ||||||
|  | 	body, bodyErr := io.ReadAll(res.Body) | ||||||
|  |  | ||||||
|  | 	if bodyErr != nil { | ||||||
|  | 		return "", bodyErr | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Debug().Msg("Read body from tailscale") | ||||||
|  |  | ||||||
|  | 	var users TailscaleUserInfoResponse | ||||||
|  |  | ||||||
|  | 	jsonErr := json.Unmarshal(body, &users) | ||||||
|  |  | ||||||
|  | 	if jsonErr != nil { | ||||||
|  | 		return "", jsonErr | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Debug().Msg("Parsed users from tailscale") | ||||||
|  |  | ||||||
|  | 	return users.Users[0].LoginName, nil | ||||||
|  | } | ||||||
| @@ -19,31 +19,34 @@ type User struct { | |||||||
| type Users []User | type Users []User | ||||||
|  |  | ||||||
| type Config struct { | type Config struct { | ||||||
| 	Port                    int    `mapstructure:"port" validate:"required"` | 	Port                      int    `mapstructure:"port" validate:"required"` | ||||||
| 	Address                 string `validate:"required,ip4_addr" mapstructure:"address"` | 	Address                   string `validate:"required,ip4_addr" mapstructure:"address"` | ||||||
| 	Secret                  string `validate:"required,len=32" mapstructure:"secret"` | 	Secret                    string `validate:"required,len=32" mapstructure:"secret"` | ||||||
| 	SecretFile              string `mapstructure:"secret-file"` | 	SecretFile                string `mapstructure:"secret-file"` | ||||||
| 	AppURL                  string `validate:"required,url" mapstructure:"app-url"` | 	AppURL                    string `validate:"required,url" mapstructure:"app-url"` | ||||||
| 	Users                   string `mapstructure:"users"` | 	Users                     string `mapstructure:"users"` | ||||||
| 	UsersFile               string `mapstructure:"users-file"` | 	UsersFile                 string `mapstructure:"users-file"` | ||||||
| 	CookieSecure            bool   `mapstructure:"cookie-secure"` | 	CookieSecure              bool   `mapstructure:"cookie-secure"` | ||||||
| 	GithubClientId          string `mapstructure:"github-client-id"` | 	GithubClientId            string `mapstructure:"github-client-id"` | ||||||
| 	GithubClientSecret      string `mapstructure:"github-client-secret"` | 	GithubClientSecret        string `mapstructure:"github-client-secret"` | ||||||
| 	GithubClientSecretFile  string `mapstructure:"github-client-secret-file"` | 	GithubClientSecretFile    string `mapstructure:"github-client-secret-file"` | ||||||
| 	GoogleClientId          string `mapstructure:"google-client-id"` | 	GoogleClientId            string `mapstructure:"google-client-id"` | ||||||
| 	GoogleClientSecret      string `mapstructure:"google-client-secret"` | 	GoogleClientSecret        string `mapstructure:"google-client-secret"` | ||||||
| 	GoogleClientSecretFile  string `mapstructure:"google-client-secret-file"` | 	GoogleClientSecretFile    string `mapstructure:"google-client-secret-file"` | ||||||
| 	GenericClientId         string `mapstructure:"generic-client-id"` | 	TailscaleClientId         string `mapstructure:"tailscale-client-id"` | ||||||
| 	GenericClientSecret     string `mapstructure:"generic-client-secret"` | 	TailscaleClientSecret     string `mapstructure:"tailscale-client-secret"` | ||||||
| 	GenericClientSecretFile string `mapstructure:"generic-client-secret-file"` | 	TailscaleClientSecretFile string `mapstructure:"tailscale-client-secret-file"` | ||||||
| 	GenericScopes           string `mapstructure:"generic-scopes"` | 	GenericClientId           string `mapstructure:"generic-client-id"` | ||||||
| 	GenericAuthURL          string `mapstructure:"generic-auth-url"` | 	GenericClientSecret       string `mapstructure:"generic-client-secret"` | ||||||
| 	GenericTokenURL         string `mapstructure:"generic-token-url"` | 	GenericClientSecretFile   string `mapstructure:"generic-client-secret-file"` | ||||||
| 	GenericUserURL          string `mapstructure:"generic-user-info-url"` | 	GenericScopes             string `mapstructure:"generic-scopes"` | ||||||
| 	DisableContinue         bool   `mapstructure:"disable-continue"` | 	GenericAuthURL            string `mapstructure:"generic-auth-url"` | ||||||
| 	OAuthWhitelist          string `mapstructure:"oauth-whitelist"` | 	GenericTokenURL           string `mapstructure:"generic-token-url"` | ||||||
| 	CookieExpiry            int    `mapstructure:"cookie-expiry"` | 	GenericUserURL            string `mapstructure:"generic-user-url"` | ||||||
| 	LogLevel                int8   `mapstructure:"log-level" validate:"min=-1,max=5"` | 	DisableContinue           bool   `mapstructure:"disable-continue"` | ||||||
|  | 	OAuthWhitelist            string `mapstructure:"oauth-whitelist"` | ||||||
|  | 	CookieExpiry              int    `mapstructure:"cookie-expiry"` | ||||||
|  | 	LogLevel                  int8   `mapstructure:"log-level" validate:"min=-1,max=5"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type UserContext struct { | type UserContext struct { | ||||||
| @@ -64,17 +67,19 @@ type APIConfig struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| type OAuthConfig struct { | type OAuthConfig struct { | ||||||
| 	GithubClientId      string | 	GithubClientId        string | ||||||
| 	GithubClientSecret  string | 	GithubClientSecret    string | ||||||
| 	GoogleClientId      string | 	GoogleClientId        string | ||||||
| 	GoogleClientSecret  string | 	GoogleClientSecret    string | ||||||
| 	GenericClientId     string | 	TailscaleClientId     string | ||||||
| 	GenericClientSecret string | 	TailscaleClientSecret string | ||||||
| 	GenericScopes       []string | 	GenericClientId       string | ||||||
| 	GenericAuthURL      string | 	GenericClientSecret   string | ||||||
| 	GenericTokenURL     string | 	GenericScopes         []string | ||||||
| 	GenericUserURL      string | 	GenericAuthURL        string | ||||||
| 	AppURL              string | 	GenericTokenURL       string | ||||||
|  | 	GenericUserURL        string | ||||||
|  | 	AppURL                string | ||||||
| } | } | ||||||
|  |  | ||||||
| type OAuthRequest struct { | type OAuthRequest struct { | ||||||
| @@ -89,9 +94,19 @@ type OAuthProviders struct { | |||||||
|  |  | ||||||
| type UnauthorizedQuery struct { | type UnauthorizedQuery struct { | ||||||
| 	Username string `url:"username"` | 	Username string `url:"username"` | ||||||
|  | 	Resource string `url:"resource"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type SessionCookie struct { | type SessionCookie struct { | ||||||
| 	Username string | 	Username string | ||||||
| 	Provider string | 	Provider string | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type TinyauthLabels struct { | ||||||
|  | 	OAuthWhitelist []string | ||||||
|  | 	Users          []string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type TailscaleQuery struct { | ||||||
|  | 	Code int `url:"code"` | ||||||
|  | } | ||||||
|   | |||||||
| @@ -4,7 +4,9 @@ import ( | |||||||
| 	"errors" | 	"errors" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"slices" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 	"tinyauth/internal/constants" | ||||||
| 	"tinyauth/internal/types" | 	"tinyauth/internal/types" | ||||||
|  |  | ||||||
| 	"github.com/rs/zerolog/log" | 	"github.com/rs/zerolog/log" | ||||||
| @@ -42,7 +44,7 @@ func GetRootURL(urlSrc string) (string, error) { | |||||||
| 		return "", parseErr | 		return "", parseErr | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	urlSplitted := strings.Split(urlParsed.Host, ".") | 	urlSplitted := strings.Split(urlParsed.Hostname(), ".") | ||||||
|  |  | ||||||
| 	urlFinal := strings.Join(urlSplitted[1:], ".") | 	urlFinal := strings.Join(urlSplitted[1:], ".") | ||||||
|  |  | ||||||
| @@ -128,3 +130,19 @@ func GetUsers(conf string, file string) (types.Users, error) { | |||||||
| func OAuthConfigured(config types.Config) bool { | func OAuthConfigured(config types.Config) bool { | ||||||
| 	return (config.GithubClientId != "" && config.GithubClientSecret != "") || (config.GoogleClientId != "" && config.GoogleClientSecret != "") || (config.GenericClientId != "" && config.GenericClientSecret != "") | 	return (config.GithubClientId != "" && config.GithubClientSecret != "") || (config.GoogleClientId != "" && config.GoogleClientSecret != "") || (config.GenericClientId != "" && config.GenericClientSecret != "") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func GetTinyauthLabels(labels map[string]string) types.TinyauthLabels { | ||||||
|  | 	var tinyauthLabels types.TinyauthLabels | ||||||
|  | 	for label, value := range labels { | ||||||
|  | 		if slices.Contains(constants.TinyauthLabels, label) { | ||||||
|  | 			log.Debug().Str("label", label).Msg("Found label") | ||||||
|  | 			switch label { | ||||||
|  | 			case "tinyauth.oauth.whitelist": | ||||||
|  | 				tinyauthLabels.OAuthWhitelist = strings.Split(value, ",") | ||||||
|  | 			case "tinyauth.users": | ||||||
|  | 				tinyauthLabels.Users = strings.Split(value, ",") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return tinyauthLabels | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										55
									
								
								site/src/icons/tailscale.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								site/src/icons/tailscale.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | |||||||
|  | import type { SVGProps } from "react"; | ||||||
|  |  | ||||||
|  | export function TailscaleIcon(props: SVGProps<SVGSVGElement>) { | ||||||
|  |   return ( | ||||||
|  |     <svg | ||||||
|  |       xmlns="http://www.w3.org/2000/svg" | ||||||
|  |       viewBox="0 0 512 512" | ||||||
|  |       width={24} | ||||||
|  |       height={24} | ||||||
|  |       {...props} | ||||||
|  |     > | ||||||
|  |       <style>{".st0{opacity:0.2;fill:#CCCAC9;}.st1{fill:#FFFFFF;}"}</style> | ||||||
|  |       <g> | ||||||
|  |         <g> | ||||||
|  |           <path | ||||||
|  |             className="st0" | ||||||
|  |             d="M65.6,127.7c35.3,0,63.9-28.6,63.9-63.9S100.9,0,65.6,0S1.8,28.6,1.8,63.9S30.4,127.7,65.6,127.7z" | ||||||
|  |           /> | ||||||
|  |           <path | ||||||
|  |             className="st1" | ||||||
|  |             d="M65.6,318.1c35.3,0,63.9-28.6,63.9-63.9s-28.6-63.9-63.9-63.9S1.8,219,1.8,254.2S30.4,318.1,65.6,318.1z" | ||||||
|  |           /> | ||||||
|  |           <path | ||||||
|  |             className="st0" | ||||||
|  |             d="M65.6,512c35.3,0,63.9-28.6,63.9-63.9s-28.6-63.9-63.9-63.9S1.8,412.9,1.8,448.1S30.4,512,65.6,512z" | ||||||
|  |           /> | ||||||
|  |           <path | ||||||
|  |             className="st1" | ||||||
|  |             d="M257.2,318.1c35.3,0,63.9-28.6,63.9-63.9s-28.6-63.9-63.9-63.9s-63.9,28.6-63.9,63.9S221.9,318.1,257.2,318.1z" | ||||||
|  |           /> | ||||||
|  |           <path | ||||||
|  |             className="st1" | ||||||
|  |             d="M257.2,512c35.3,0,63.9-28.6,63.9-63.9s-28.6-63.9-63.9-63.9s-63.9,28.6-63.9,63.9S221.9,512,257.2,512z" | ||||||
|  |           /> | ||||||
|  |           <path | ||||||
|  |             className="st0" | ||||||
|  |             d="M257.2,127.7c35.3,0,63.9-28.6,63.9-63.9S292.5,0,257.2,0s-63.9,28.6-63.9,63.9S221.9,127.7,257.2,127.7z" | ||||||
|  |           /> | ||||||
|  |           <path | ||||||
|  |             className="st0" | ||||||
|  |             d="M446.4,127.7c35.3,0,63.9-28.6,63.9-63.9S481.6,0,446.4,0c-35.3,0-63.9,28.6-63.9,63.9S411.1,127.7,446.4,127.7z" | ||||||
|  |           /> | ||||||
|  |           <path | ||||||
|  |             className="st1" | ||||||
|  |             d="M446.4,318.1c35.3,0,63.9-28.6,63.9-63.9s-28.6-63.9-63.9-63.9s-63.9,28.6-63.9,63.9S411.1,318.1,446.4,318.1z" | ||||||
|  |           /> | ||||||
|  |           <path | ||||||
|  |             className="st0" | ||||||
|  |             d="M446.4,512c35.3,0,63.9-28.6,63.9-63.9s-28.6-63.9-63.9-63.9s-63.9,28.6-63.9,63.9S411.1,512,446.4,512z" | ||||||
|  |           /> | ||||||
|  |         </g> | ||||||
|  |       </g> | ||||||
|  |     </svg> | ||||||
|  |   ); | ||||||
|  | } | ||||||
| @@ -19,6 +19,7 @@ import { Layout } from "../components/layouts/layout"; | |||||||
| import { GoogleIcon } from "../icons/google"; | import { GoogleIcon } from "../icons/google"; | ||||||
| import { GithubIcon } from "../icons/github"; | import { GithubIcon } from "../icons/github"; | ||||||
| import { OAuthIcon } from "../icons/oauth"; | import { OAuthIcon } from "../icons/oauth"; | ||||||
|  | import { TailscaleIcon } from "../icons/tailscale"; | ||||||
|  |  | ||||||
| export const LoginPage = () => { | export const LoginPage = () => { | ||||||
|   const queryString = window.location.search; |   const queryString = window.location.search; | ||||||
| @@ -26,6 +27,9 @@ export const LoginPage = () => { | |||||||
|   const redirectUri = params.get("redirect_uri"); |   const redirectUri = params.get("redirect_uri"); | ||||||
|  |  | ||||||
|   const { isLoggedIn, configuredProviders } = useUserContext(); |   const { isLoggedIn, configuredProviders } = useUserContext(); | ||||||
|  |   const oauthProviders = configuredProviders.filter( | ||||||
|  |     (value) => value !== "username", | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   if (isLoggedIn) { |   if (isLoggedIn) { | ||||||
|     return <Navigate to="/logout" />; |     return <Navigate to="/logout" />; | ||||||
| @@ -107,18 +111,13 @@ export const LoginPage = () => { | |||||||
|     <Layout> |     <Layout> | ||||||
|       <Title ta="center">Tinyauth</Title> |       <Title ta="center">Tinyauth</Title> | ||||||
|       <Paper shadow="md" p="xl" mt={30} radius="md" withBorder> |       <Paper shadow="md" p="xl" mt={30} radius="md" withBorder> | ||||||
|         {configuredProviders.length === 0 && ( |         {oauthProviders.length > 0 && ( | ||||||
|           <Text size="lg" mb="md" fw={500} ta="center"> |  | ||||||
|             Welcome back, please login |  | ||||||
|           </Text> |  | ||||||
|         )} |  | ||||||
|         {configuredProviders.length > 0 && ( |  | ||||||
|           <> |           <> | ||||||
|             <Text size="lg" fw={500} ta="center"> |             <Text size="lg" fw={500} ta="center"> | ||||||
|               Welcome back, login with |               Welcome back, login with | ||||||
|             </Text> |             </Text> | ||||||
|             <Grid mb="md" mt="md" align="center" justify="center"> |             <Grid mb="md" mt="md" align="center" justify="center"> | ||||||
|               {configuredProviders.includes("google") && ( |               {oauthProviders.includes("google") && ( | ||||||
|                 <Grid.Col span="content"> |                 <Grid.Col span="content"> | ||||||
|                   <Button |                   <Button | ||||||
|                     radius="xl" |                     radius="xl" | ||||||
| @@ -133,7 +132,7 @@ export const LoginPage = () => { | |||||||
|                   </Button> |                   </Button> | ||||||
|                 </Grid.Col> |                 </Grid.Col> | ||||||
|               )} |               )} | ||||||
|               {configuredProviders.includes("github") && ( |               {oauthProviders.includes("github") && ( | ||||||
|                 <Grid.Col span="content"> |                 <Grid.Col span="content"> | ||||||
|                   <Button |                   <Button | ||||||
|                     radius="xl" |                     radius="xl" | ||||||
| @@ -148,7 +147,22 @@ export const LoginPage = () => { | |||||||
|                   </Button> |                   </Button> | ||||||
|                 </Grid.Col> |                 </Grid.Col> | ||||||
|               )} |               )} | ||||||
|               {configuredProviders.includes("generic") && ( |               {oauthProviders.includes("tailscale") && ( | ||||||
|  |                 <Grid.Col span="content"> | ||||||
|  |                   <Button | ||||||
|  |                     radius="xl" | ||||||
|  |                     leftSection={ | ||||||
|  |                       <TailscaleIcon style={{ width: 14, height: 14 }} /> | ||||||
|  |                     } | ||||||
|  |                     variant="default" | ||||||
|  |                     onClick={() => loginOAuthMutation.mutate("tailscale")} | ||||||
|  |                     loading={loginOAuthMutation.isLoading} | ||||||
|  |                   > | ||||||
|  |                     Tailscale | ||||||
|  |                   </Button> | ||||||
|  |                 </Grid.Col> | ||||||
|  |               )} | ||||||
|  |               {oauthProviders.includes("generic") && ( | ||||||
|                 <Grid.Col span="content"> |                 <Grid.Col span="content"> | ||||||
|                   <Button |                   <Button | ||||||
|                     radius="xl" |                     radius="xl" | ||||||
|   | |||||||
| @@ -45,8 +45,8 @@ export const LogoutPage = () => { | |||||||
|         </Text> |         </Text> | ||||||
|         <Text> |         <Text> | ||||||
|           You are currently logged in as <Code>{username}</Code> |           You are currently logged in as <Code>{username}</Code> | ||||||
|           {oauth && ` using ${capitalize(provider)}`}. Click the button below to |           {oauth && ` using ${capitalize(provider)} OAuth`}. Click the button | ||||||
|           log out. |           below to log out. | ||||||
|         </Text> |         </Text> | ||||||
|         <Button |         <Button | ||||||
|           fullWidth |           fullWidth | ||||||
|   | |||||||
| @@ -1,18 +1,12 @@ | |||||||
| import { Button, Code, Paper, Text } from "@mantine/core"; | import { Button, Code, Paper, Text } from "@mantine/core"; | ||||||
| import { Layout } from "../components/layouts/layout"; | import { Layout } from "../components/layouts/layout"; | ||||||
| import { useUserContext } from "../context/user-context"; |  | ||||||
| import { Navigate } from "react-router"; | import { Navigate } from "react-router"; | ||||||
|  |  | ||||||
| export const UnauthorizedPage = () => { | export const UnauthorizedPage = () => { | ||||||
|   const queryString = window.location.search; |   const queryString = window.location.search; | ||||||
|   const params = new URLSearchParams(queryString); |   const params = new URLSearchParams(queryString); | ||||||
|   const username = params.get("username"); |   const username = params.get("username"); | ||||||
|  |   const resource = params.get("resource"); | ||||||
|   const { isLoggedIn } = useUserContext(); |  | ||||||
|  |  | ||||||
|   if (isLoggedIn) { |  | ||||||
|     return <Navigate to="/" />; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   if (username === "null") { |   if (username === "null") { | ||||||
|     return <Navigate to="/" />; |     return <Navigate to="/" />; | ||||||
| @@ -25,8 +19,14 @@ export const UnauthorizedPage = () => { | |||||||
|           Unauthorized |           Unauthorized | ||||||
|         </Text> |         </Text> | ||||||
|         <Text> |         <Text> | ||||||
|           The user with username <Code>{username}</Code> is not authorized to |           The user with username <Code>{username}</Code> is not authorized to{" "} | ||||||
|           login. |           {resource !== "null" ? ( | ||||||
|  |             <span> | ||||||
|  |               access the <Code>{resource}</Code> resource. | ||||||
|  |             </span> | ||||||
|  |           ) : ( | ||||||
|  |             "login." | ||||||
|  |           )} | ||||||
|         </Text> |         </Text> | ||||||
|         <Button |         <Button | ||||||
|           fullWidth |           fullWidth | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user