mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-10-28 12:45:47 +00:00
refactor: use a boostrap service to bootstrap the app
This commit is contained in:
@@ -5,7 +5,7 @@ SECRET_FILE=app_secret_file
|
|||||||
APP_URL=http://localhost:3000
|
APP_URL=http://localhost:3000
|
||||||
USERS=your_user_password_hash
|
USERS=your_user_password_hash
|
||||||
USERS_FILE=users_file
|
USERS_FILE=users_file
|
||||||
COOKIE_SECURE=false
|
SECURE_COOKIE=false
|
||||||
GITHUB_CLIENT_ID=github_client_id
|
GITHUB_CLIENT_ID=github_client_id
|
||||||
GITHUB_CLIENT_SECRET=github_client_secret
|
GITHUB_CLIENT_SECRET=github_client_secret
|
||||||
GITHUB_CLIENT_SECRET_FILE=github_client_secret_file
|
GITHUB_CLIENT_SECRET_FILE=github_client_secret_file
|
||||||
@@ -25,7 +25,7 @@ GENERIC_NAME=My OAuth
|
|||||||
SESSION_EXPIRY=7200
|
SESSION_EXPIRY=7200
|
||||||
LOGIN_TIMEOUT=300
|
LOGIN_TIMEOUT=300
|
||||||
LOGIN_MAX_RETRIES=5
|
LOGIN_MAX_RETRIES=5
|
||||||
LOG_LEVEL=0
|
LOG_LEVEL=debug
|
||||||
APP_TITLE=Tinyauth SSO
|
APP_TITLE=Tinyauth SSO
|
||||||
FORGOT_PASSWORD_MESSAGE=Some message about resetting the password
|
FORGOT_PASSWORD_MESSAGE=Some message about resetting the password
|
||||||
OAUTH_AUTO_REDIRECT=none
|
OAUTH_AUTO_REDIRECT=none
|
||||||
|
|||||||
346
cmd/root.go
346
cmd/root.go
@@ -1,22 +1,13 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
totpCmd "tinyauth/cmd/totp"
|
totpCmd "tinyauth/cmd/totp"
|
||||||
userCmd "tinyauth/cmd/user"
|
userCmd "tinyauth/cmd/user"
|
||||||
"tinyauth/internal/auth"
|
"tinyauth/internal/bootstrap"
|
||||||
"tinyauth/internal/constants"
|
"tinyauth/internal/config"
|
||||||
"tinyauth/internal/controller"
|
|
||||||
"tinyauth/internal/docker"
|
|
||||||
"tinyauth/internal/ldap"
|
|
||||||
"tinyauth/internal/middleware"
|
|
||||||
"tinyauth/internal/providers"
|
|
||||||
"tinyauth/internal/types"
|
|
||||||
"tinyauth/internal/utils"
|
"tinyauth/internal/utils"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/go-playground/validator"
|
"github.com/go-playground/validator"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@@ -24,197 +15,51 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Middleware interface {
|
|
||||||
Middleware() gin.HandlerFunc
|
|
||||||
Init() error
|
|
||||||
Name() string
|
|
||||||
}
|
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "tinyauth",
|
Use: "tinyauth",
|
||||||
Short: "The simplest way to protect your apps with a login screen.",
|
Short: "The simplest way to protect your apps with a login screen.",
|
||||||
Long: `Tinyauth is a simple authentication middleware that adds simple username/password login or OAuth with Google, Github and any generic OAuth provider to all of your docker apps.`,
|
Long: `Tinyauth is a simple authentication middleware that adds simple username/password login or OAuth with Google, Github and any generic OAuth provider to all of your docker apps.`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
var config types.Config
|
var conf config.Config
|
||||||
err := viper.Unmarshal(&config)
|
|
||||||
HandleError(err, "Failed to parse config")
|
err := viper.Unmarshal(&conf)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Failed to parse config")
|
||||||
|
}
|
||||||
|
|
||||||
// Check if secrets have a file associated with them
|
// Check if secrets have a file associated with them
|
||||||
config.Secret = utils.GetSecret(config.Secret, config.SecretFile)
|
conf.Secret = utils.GetSecret(conf.Secret, conf.SecretFile)
|
||||||
config.GithubClientSecret = utils.GetSecret(config.GithubClientSecret, config.GithubClientSecretFile)
|
conf.GithubClientSecret = utils.GetSecret(conf.GithubClientSecret, conf.GithubClientSecretFile)
|
||||||
config.GoogleClientSecret = utils.GetSecret(config.GoogleClientSecret, config.GoogleClientSecretFile)
|
conf.GoogleClientSecret = utils.GetSecret(conf.GoogleClientSecret, conf.GoogleClientSecretFile)
|
||||||
config.GenericClientSecret = utils.GetSecret(config.GenericClientSecret, config.GenericClientSecretFile)
|
conf.GenericClientSecret = utils.GetSecret(conf.GenericClientSecret, conf.GenericClientSecretFile)
|
||||||
|
|
||||||
validator := validator.New()
|
validator := validator.New()
|
||||||
err = validator.Struct(config)
|
|
||||||
HandleError(err, "Failed to validate config")
|
|
||||||
|
|
||||||
log.Logger = log.Level(zerolog.Level(config.LogLevel))
|
err = validator.Struct(conf)
|
||||||
log.Info().Str("version", strings.TrimSpace(constants.Version)).Msg("Starting tinyauth")
|
if err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Invalid config")
|
||||||
log.Info().Msg("Parsing users")
|
|
||||||
users, err := utils.GetUsers(config.Users, config.UsersFile)
|
|
||||||
HandleError(err, "Failed to parse users")
|
|
||||||
|
|
||||||
log.Debug().Msg("Getting domain")
|
|
||||||
domain, err := utils.GetUpperDomain(config.AppURL)
|
|
||||||
HandleError(err, "Failed to get upper domain")
|
|
||||||
log.Info().Str("domain", domain).Msg("Using domain for cookie store")
|
|
||||||
|
|
||||||
cookieId := utils.GenerateIdentifier(strings.Split(domain, ".")[0])
|
|
||||||
sessionCookieName := fmt.Sprintf("%s-%s", constants.SessionCookieName, cookieId)
|
|
||||||
csrfCookieName := fmt.Sprintf("%s-%s", constants.CsrfCookieName, cookieId)
|
|
||||||
redirectCookieName := fmt.Sprintf("%s-%s", constants.RedirectCookieName, cookieId)
|
|
||||||
|
|
||||||
log.Debug().Msg("Deriving HMAC and encryption secrets")
|
|
||||||
|
|
||||||
hmacSecret, err := utils.DeriveKey(config.Secret, "hmac")
|
|
||||||
HandleError(err, "Failed to derive HMAC secret")
|
|
||||||
|
|
||||||
encryptionSecret, err := utils.DeriveKey(config.Secret, "encryption")
|
|
||||||
HandleError(err, "Failed to derive encryption secret")
|
|
||||||
|
|
||||||
// Split the config into service-specific sub-configs
|
|
||||||
oauthConfig := types.OAuthConfig{
|
|
||||||
GithubClientId: config.GithubClientId,
|
|
||||||
GithubClientSecret: config.GithubClientSecret,
|
|
||||||
GoogleClientId: config.GoogleClientId,
|
|
||||||
GoogleClientSecret: config.GoogleClientSecret,
|
|
||||||
GenericClientId: config.GenericClientId,
|
|
||||||
GenericClientSecret: config.GenericClientSecret,
|
|
||||||
GenericScopes: strings.Split(config.GenericScopes, ","),
|
|
||||||
GenericAuthURL: config.GenericAuthURL,
|
|
||||||
GenericTokenURL: config.GenericTokenURL,
|
|
||||||
GenericUserURL: config.GenericUserURL,
|
|
||||||
GenericSkipSSL: config.GenericSkipSSL,
|
|
||||||
AppURL: config.AppURL,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
authConfig := types.AuthConfig{
|
log.Logger = log.Level(zerolog.Level(utils.GetLogLevel(conf.LogLevel)))
|
||||||
Users: users,
|
log.Info().Str("version", strings.TrimSpace(config.Version)).Msg("Starting tinyauth")
|
||||||
OauthWhitelist: config.OAuthWhitelist,
|
|
||||||
CookieSecure: config.CookieSecure,
|
// Create bootstrap app
|
||||||
SessionExpiry: config.SessionExpiry,
|
app := bootstrap.NewBootstrapApp(conf)
|
||||||
Domain: domain,
|
|
||||||
LoginTimeout: config.LoginTimeout,
|
// Run
|
||||||
LoginMaxRetries: config.LoginMaxRetries,
|
err = app.Setup()
|
||||||
SessionCookieName: sessionCookieName,
|
|
||||||
HMACSecret: hmacSecret,
|
if err != nil {
|
||||||
EncryptionSecret: encryptionSecret,
|
log.Fatal().Err(err).Msg("Failed to setup app")
|
||||||
}
|
}
|
||||||
|
|
||||||
var ldapService *ldap.LDAP
|
|
||||||
|
|
||||||
if config.LdapAddress != "" {
|
|
||||||
log.Info().Msg("Using LDAP for authentication")
|
|
||||||
ldapConfig := types.LdapConfig{
|
|
||||||
Address: config.LdapAddress,
|
|
||||||
BindDN: config.LdapBindDN,
|
|
||||||
BindPassword: config.LdapBindPassword,
|
|
||||||
BaseDN: config.LdapBaseDN,
|
|
||||||
Insecure: config.LdapInsecure,
|
|
||||||
SearchFilter: config.LdapSearchFilter,
|
|
||||||
}
|
|
||||||
ldapService, err = ldap.NewLDAP(ldapConfig)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("Failed to initialize LDAP service, disabling LDAP authentication")
|
|
||||||
ldapService = nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Info().Msg("LDAP not configured, using local users or OAuth")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have a source of users
|
|
||||||
if len(users) == 0 && !utils.OAuthConfigured(config) && ldapService == nil {
|
|
||||||
HandleError(errors.New("err no users"), "Unable to find a source of users")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup the services
|
|
||||||
docker, err := docker.NewDocker()
|
|
||||||
HandleError(err, "Failed to initialize docker")
|
|
||||||
auth := auth.NewAuth(authConfig, docker, ldapService)
|
|
||||||
providers := providers.NewProviders(oauthConfig)
|
|
||||||
|
|
||||||
// Create the engine
|
|
||||||
engine := gin.New()
|
|
||||||
|
|
||||||
// Create the group
|
|
||||||
router := engine.Group("/api")
|
|
||||||
|
|
||||||
// Setup the middlewares
|
|
||||||
var middlewares []Middleware
|
|
||||||
|
|
||||||
contextMiddleware := middleware.NewContextMiddleware(middleware.ContextMiddlewareConfig{
|
|
||||||
Domain: domain,
|
|
||||||
}, auth, providers)
|
|
||||||
uiMiddleware := middleware.NewUIMiddleware()
|
|
||||||
zerologMiddleware := middleware.NewZerologMiddleware()
|
|
||||||
|
|
||||||
middlewares = append(middlewares, contextMiddleware, uiMiddleware, zerologMiddleware)
|
|
||||||
|
|
||||||
for _, middleware := range middlewares {
|
|
||||||
log.Debug().Str("middleware", middleware.Name()).Msg("Initializing middleware")
|
|
||||||
err := middleware.Init()
|
|
||||||
HandleError(err, fmt.Sprintf("Failed to initialize middleware %s", middleware.Name()))
|
|
||||||
router.Use(middleware.Middleware())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create configured providers
|
|
||||||
var configuredProviders []string
|
|
||||||
|
|
||||||
configuredProviders = append(configuredProviders, providers.GetConfiguredProviders()...)
|
|
||||||
|
|
||||||
if auth.UserAuthConfigured() {
|
|
||||||
configuredProviders = append(configuredProviders, "username")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create controllers
|
|
||||||
contextController := controller.NewContextController(controller.ContextControllerConfig{
|
|
||||||
ConfiguredProviders: configuredProviders,
|
|
||||||
DisableContinue: config.DisableContinue,
|
|
||||||
Title: config.Title,
|
|
||||||
GenericName: config.GenericName,
|
|
||||||
Domain: domain,
|
|
||||||
ForgotPasswordMessage: config.FogotPasswordMessage,
|
|
||||||
BackgroundImage: config.BackgroundImage,
|
|
||||||
OAuthAutoRedirect: config.OAuthAutoRedirect,
|
|
||||||
}, router)
|
|
||||||
contextController.SetupRoutes()
|
|
||||||
|
|
||||||
healthController := controller.NewHealthController(router)
|
|
||||||
healthController.SetupRoutes()
|
|
||||||
|
|
||||||
oauthController := controller.NewOAuthController(controller.OAuthControllerConfig{
|
|
||||||
AppURL: config.AppURL,
|
|
||||||
SecureCookie: config.CookieSecure,
|
|
||||||
CSRFCookieName: csrfCookieName,
|
|
||||||
RedirectCookieName: redirectCookieName,
|
|
||||||
}, router, auth, providers)
|
|
||||||
oauthController.SetupRoutes()
|
|
||||||
|
|
||||||
proxyController := controller.NewProxyController(controller.ProxyControllerConfig{
|
|
||||||
AppURL: config.AppURL,
|
|
||||||
}, router, docker, auth)
|
|
||||||
proxyController.SetupRoutes()
|
|
||||||
|
|
||||||
userController := controller.NewUserController(controller.UserControllerConfig{
|
|
||||||
Domain: domain,
|
|
||||||
}, router, auth)
|
|
||||||
userController.SetupRoutes()
|
|
||||||
|
|
||||||
// Run server
|
|
||||||
engine.Run(fmt.Sprintf("%s:%d", config.Address, config.Port))
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func Execute() {
|
func Execute() {
|
||||||
err := rootCmd.Execute()
|
err := rootCmd.Execute()
|
||||||
HandleError(err, "Failed to execute root command")
|
|
||||||
}
|
|
||||||
|
|
||||||
func HandleError(err error, msg string) {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg(msg)
|
log.Fatal().Err(err).Msg("Failed to execute command")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,85 +69,66 @@ func init() {
|
|||||||
|
|
||||||
viper.AutomaticEnv()
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
rootCmd.Flags().Int("port", 3000, "Port to run the server on.")
|
configOptions := []struct {
|
||||||
rootCmd.Flags().String("address", "0.0.0.0", "Address to bind the server to.")
|
name string
|
||||||
rootCmd.Flags().String("secret", "", "Secret to use for the cookie.")
|
defaultVal any
|
||||||
rootCmd.Flags().String("secret-file", "", "Path to a file containing the secret.")
|
description string
|
||||||
rootCmd.Flags().String("app-url", "", "The tinyauth URL.")
|
}{
|
||||||
rootCmd.Flags().String("users", "", "Comma separated list of users in the format username:hash.")
|
{"port", 3000, "Port to run the server on."},
|
||||||
rootCmd.Flags().String("users-file", "", "Path to a file containing users in the format username:hash.")
|
{"address", "0.0.0.0", "Address to bind the server to."},
|
||||||
rootCmd.Flags().Bool("cookie-secure", false, "Send cookie over secure connection only.")
|
{"secret", "", "Secret to use for the cookie."},
|
||||||
rootCmd.Flags().String("github-client-id", "", "Github OAuth client ID.")
|
{"secret-file", "", "Path to a file containing the secret."},
|
||||||
rootCmd.Flags().String("github-client-secret", "", "Github OAuth client secret.")
|
{"app-url", "", "The Tinyauth URL."},
|
||||||
rootCmd.Flags().String("github-client-secret-file", "", "Github OAuth client secret file.")
|
{"users", "", "Comma separated list of users in the format username:hash."},
|
||||||
rootCmd.Flags().String("google-client-id", "", "Google OAuth client ID.")
|
{"users-file", "", "Path to a file containing users in the format username:hash."},
|
||||||
rootCmd.Flags().String("google-client-secret", "", "Google OAuth client secret.")
|
{"cookie-secure", false, "Send cookie over secure connection only."},
|
||||||
rootCmd.Flags().String("google-client-secret-file", "", "Google OAuth client secret file.")
|
{"github-client-id", "", "Github OAuth client ID."},
|
||||||
rootCmd.Flags().String("generic-client-id", "", "Generic OAuth client ID.")
|
{"github-client-secret", "", "Github OAuth client secret."},
|
||||||
rootCmd.Flags().String("generic-client-secret", "", "Generic OAuth client secret.")
|
{"github-client-secret-file", "", "Github OAuth client secret file."},
|
||||||
rootCmd.Flags().String("generic-client-secret-file", "", "Generic OAuth client secret file.")
|
{"google-client-id", "", "Google OAuth client ID."},
|
||||||
rootCmd.Flags().String("generic-scopes", "", "Generic OAuth scopes.")
|
{"google-client-secret", "", "Google OAuth client secret."},
|
||||||
rootCmd.Flags().String("generic-auth-url", "", "Generic OAuth auth URL.")
|
{"google-client-secret-file", "", "Google OAuth client secret file."},
|
||||||
rootCmd.Flags().String("generic-token-url", "", "Generic OAuth token URL.")
|
{"generic-client-id", "", "Generic OAuth client ID."},
|
||||||
rootCmd.Flags().String("generic-user-url", "", "Generic OAuth user info URL.")
|
{"generic-client-secret", "", "Generic OAuth client secret."},
|
||||||
rootCmd.Flags().String("generic-name", "Generic", "Generic OAuth provider name.")
|
{"generic-client-secret-file", "", "Generic OAuth client secret file."},
|
||||||
rootCmd.Flags().Bool("generic-skip-ssl", false, "Skip SSL verification for the generic OAuth provider.")
|
{"generic-scopes", "", "Generic OAuth scopes."},
|
||||||
rootCmd.Flags().Bool("disable-continue", false, "Disable continue screen and redirect to app directly.")
|
{"generic-auth-url", "", "Generic OAuth auth URL."},
|
||||||
rootCmd.Flags().String("oauth-whitelist", "", "Comma separated list of email addresses to whitelist when using OAuth.")
|
{"generic-token-url", "", "Generic OAuth token URL."},
|
||||||
rootCmd.Flags().String("oauth-auto-redirect", "none", "Auto redirect to the specified OAuth provider if configured. (available providers: github, google, generic)")
|
{"generic-user-url", "", "Generic OAuth user info URL."},
|
||||||
rootCmd.Flags().Int("session-expiry", 86400, "Session (cookie) expiration time in seconds.")
|
{"generic-name", "Generic", "Generic OAuth provider name."},
|
||||||
rootCmd.Flags().Int("login-timeout", 300, "Login timeout in seconds after max retries reached (0 to disable).")
|
{"generic-skip-ssl", false, "Skip SSL verification for the generic OAuth provider."},
|
||||||
rootCmd.Flags().Int("login-max-retries", 5, "Maximum login attempts before timeout (0 to disable).")
|
{"disable-continue", false, "Disable continue screen and redirect to app directly."},
|
||||||
rootCmd.Flags().Int("log-level", 1, "Log level.")
|
{"oauth-whitelist", "", "Comma separated list of email addresses to whitelist when using OAuth."},
|
||||||
rootCmd.Flags().String("app-title", "Tinyauth", "Title of the app.")
|
{"oauth-auto-redirect", "none", "Auto redirect to the specified OAuth provider if configured. (available providers: github, google, generic)"},
|
||||||
rootCmd.Flags().String("forgot-password-message", "", "Message to show on the forgot password page.")
|
{"session-expiry", 86400, "Session (cookie) expiration time in seconds."},
|
||||||
rootCmd.Flags().String("background-image", "/background.jpg", "Background image URL for the login page.")
|
{"login-timeout", 300, "Login timeout in seconds after max retries reached (0 to disable)."},
|
||||||
rootCmd.Flags().String("ldap-address", "", "LDAP server address (e.g. ldap://localhost:389).")
|
{"login-max-retries", 5, "Maximum login attempts before timeout (0 to disable)."},
|
||||||
rootCmd.Flags().String("ldap-bind-dn", "", "LDAP bind DN (e.g. uid=user,dc=example,dc=com).")
|
{"log-level", "info", "Log level."},
|
||||||
rootCmd.Flags().String("ldap-bind-password", "", "LDAP bind password.")
|
{"app-title", "Tinyauth", "Title of the app."},
|
||||||
rootCmd.Flags().String("ldap-base-dn", "", "LDAP base DN (e.g. dc=example,dc=com).")
|
{"forgot-password-message", "", "Message to show on the forgot password page."},
|
||||||
rootCmd.Flags().Bool("ldap-insecure", false, "Skip certificate verification for the LDAP server.")
|
{"background-image", "/background.jpg", "Background image URL for the login page."},
|
||||||
rootCmd.Flags().String("ldap-search-filter", "(uid=%s)", "LDAP search filter for user lookup.")
|
{"ldap-address", "", "LDAP server address (e.g. ldap://localhost:389)."},
|
||||||
|
{"ldap-bind-dn", "", "LDAP bind DN (e.g. uid=user,dc=example,dc=com)."},
|
||||||
|
{"ldap-bind-password", "", "LDAP bind password."},
|
||||||
|
{"ldap-base-dn", "", "LDAP base DN (e.g. dc=example,dc=com)."},
|
||||||
|
{"ldap-insecure", false, "Skip certificate verification for the LDAP server."},
|
||||||
|
{"ldap-search-filter", "(uid=%s)", "LDAP search filter for user lookup."},
|
||||||
|
}
|
||||||
|
|
||||||
viper.BindEnv("port", "PORT")
|
for _, opt := range configOptions {
|
||||||
viper.BindEnv("address", "ADDRESS")
|
switch v := opt.defaultVal.(type) {
|
||||||
viper.BindEnv("secret", "SECRET")
|
case bool:
|
||||||
viper.BindEnv("secret-file", "SECRET_FILE")
|
rootCmd.Flags().Bool(opt.name, v, opt.description)
|
||||||
viper.BindEnv("app-url", "APP_URL")
|
case int:
|
||||||
viper.BindEnv("users", "USERS")
|
rootCmd.Flags().Int(opt.name, v, opt.description)
|
||||||
viper.BindEnv("users-file", "USERS_FILE")
|
case string:
|
||||||
viper.BindEnv("cookie-secure", "COOKIE_SECURE")
|
rootCmd.Flags().String(opt.name, v, opt.description)
|
||||||
viper.BindEnv("github-client-id", "GITHUB_CLIENT_ID")
|
}
|
||||||
viper.BindEnv("github-client-secret", "GITHUB_CLIENT_SECRET")
|
|
||||||
viper.BindEnv("github-client-secret-file", "GITHUB_CLIENT_SECRET_FILE")
|
// Create uppercase env var name
|
||||||
viper.BindEnv("google-client-id", "GOOGLE_CLIENT_ID")
|
envVar := strings.ReplaceAll(strings.ToUpper(opt.name), "-", "_")
|
||||||
viper.BindEnv("google-client-secret", "GOOGLE_CLIENT_SECRET")
|
viper.BindEnv(opt.name, envVar)
|
||||||
viper.BindEnv("google-client-secret-file", "GOOGLE_CLIENT_SECRET_FILE")
|
}
|
||||||
viper.BindEnv("generic-client-id", "GENERIC_CLIENT_ID")
|
|
||||||
viper.BindEnv("generic-client-secret", "GENERIC_CLIENT_SECRET")
|
|
||||||
viper.BindEnv("generic-client-secret-file", "GENERIC_CLIENT_SECRET_FILE")
|
|
||||||
viper.BindEnv("generic-scopes", "GENERIC_SCOPES")
|
|
||||||
viper.BindEnv("generic-auth-url", "GENERIC_AUTH_URL")
|
|
||||||
viper.BindEnv("generic-token-url", "GENERIC_TOKEN_URL")
|
|
||||||
viper.BindEnv("generic-user-url", "GENERIC_USER_URL")
|
|
||||||
viper.BindEnv("generic-name", "GENERIC_NAME")
|
|
||||||
viper.BindEnv("generic-skip-ssl", "GENERIC_SKIP_SSL")
|
|
||||||
viper.BindEnv("disable-continue", "DISABLE_CONTINUE")
|
|
||||||
viper.BindEnv("oauth-whitelist", "OAUTH_WHITELIST")
|
|
||||||
viper.BindEnv("oauth-auto-redirect", "OAUTH_AUTO_REDIRECT")
|
|
||||||
viper.BindEnv("session-expiry", "SESSION_EXPIRY")
|
|
||||||
viper.BindEnv("log-level", "LOG_LEVEL")
|
|
||||||
viper.BindEnv("app-title", "APP_TITLE")
|
|
||||||
viper.BindEnv("login-timeout", "LOGIN_TIMEOUT")
|
|
||||||
viper.BindEnv("login-max-retries", "LOGIN_MAX_RETRIES")
|
|
||||||
viper.BindEnv("forgot-password-message", "FORGOT_PASSWORD_MESSAGE")
|
|
||||||
viper.BindEnv("background-image", "BACKGROUND_IMAGE")
|
|
||||||
viper.BindEnv("ldap-address", "LDAP_ADDRESS")
|
|
||||||
viper.BindEnv("ldap-bind-dn", "LDAP_BIND_DN")
|
|
||||||
viper.BindEnv("ldap-bind-password", "LDAP_BIND_PASSWORD")
|
|
||||||
viper.BindEnv("ldap-base-dn", "LDAP_BASE_DN")
|
|
||||||
viper.BindEnv("ldap-insecure", "LDAP_INSECURE")
|
|
||||||
viper.BindEnv("ldap-search-filter", "LDAP_SEARCH_FILTER")
|
|
||||||
|
|
||||||
viper.BindPFlags(rootCmd.Flags())
|
viper.BindPFlags(rootCmd.Flags())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"tinyauth/internal/constants"
|
"tinyauth/internal/config"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -12,9 +12,9 @@ var versionCmd = &cobra.Command{
|
|||||||
Short: "Print the version number of Tinyauth",
|
Short: "Print the version number of Tinyauth",
|
||||||
Long: `All software has versions. This is Tinyauth's`,
|
Long: `All software has versions. This is Tinyauth's`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
fmt.Printf("Version: %s\n", constants.Version)
|
fmt.Printf("Version: %s\n", config.Version)
|
||||||
fmt.Printf("Commit Hash: %s\n", constants.CommitHash)
|
fmt.Printf("Commit Hash: %s\n", config.CommitHash)
|
||||||
fmt.Printf("Build Timestamp: %s\n", constants.BuildTimestamp)
|
fmt.Printf("Build Timestamp: %s\n", config.BuildTimestamp)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
246
internal/bootstrap/bootstrap_app.go
Normal file
246
internal/bootstrap/bootstrap_app.go
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
package bootstrap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"tinyauth/internal/config"
|
||||||
|
"tinyauth/internal/controller"
|
||||||
|
"tinyauth/internal/middleware"
|
||||||
|
"tinyauth/internal/service"
|
||||||
|
"tinyauth/internal/utils"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Controller interface {
|
||||||
|
SetupRoutes()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Middleware interface {
|
||||||
|
Middleware() gin.HandlerFunc
|
||||||
|
Init() error
|
||||||
|
Name() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Service interface {
|
||||||
|
Init() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type BootstrapApp struct {
|
||||||
|
Config config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBootstrapApp(config config.Config) *BootstrapApp {
|
||||||
|
return &BootstrapApp{
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *BootstrapApp) Setup() error {
|
||||||
|
// Parse users
|
||||||
|
users, err := utils.GetUsers(app.Config.Users, app.Config.UsersFile)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get domain
|
||||||
|
domain, err := utils.GetUpperDomain(app.Config.AppURL)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cookie names
|
||||||
|
cookieId := utils.GenerateIdentifier(strings.Split(domain, ".")[0])
|
||||||
|
sessionCookieName := fmt.Sprintf("%s-%s", config.SessionCookieName, cookieId)
|
||||||
|
csrfCookieName := fmt.Sprintf("%s-%s", config.CSRFCookieName, cookieId)
|
||||||
|
redirectCookieName := fmt.Sprintf("%s-%s", config.RedirectCookieName, cookieId)
|
||||||
|
|
||||||
|
// Secrets
|
||||||
|
encryptionSecret, err := utils.DeriveKey(app.Config.Secret, "encryption")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
hmacSecret, err := utils.DeriveKey(app.Config.Secret, "hmac")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create configs
|
||||||
|
authConfig := service.AuthServiceConfig{
|
||||||
|
Users: users,
|
||||||
|
OauthWhitelist: app.Config.OAuthWhitelist,
|
||||||
|
SessionExpiry: app.Config.SessionExpiry,
|
||||||
|
SecureCookie: app.Config.SecureCookie,
|
||||||
|
Domain: domain,
|
||||||
|
LoginTimeout: app.Config.LoginTimeout,
|
||||||
|
LoginMaxRetries: app.Config.LoginMaxRetries,
|
||||||
|
SessionCookieName: sessionCookieName,
|
||||||
|
HMACSecret: hmacSecret,
|
||||||
|
EncryptionSecret: encryptionSecret,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup services
|
||||||
|
var ldapService *service.LdapService
|
||||||
|
|
||||||
|
if app.Config.LdapAddress != "" {
|
||||||
|
ldapConfig := service.LdapServiceConfig{
|
||||||
|
Address: app.Config.LdapAddress,
|
||||||
|
BindDN: app.Config.LdapBindDN,
|
||||||
|
BindPassword: app.Config.LdapBindPassword,
|
||||||
|
BaseDN: app.Config.LdapBaseDN,
|
||||||
|
Insecure: app.Config.LdapInsecure,
|
||||||
|
SearchFilter: app.Config.LdapSearchFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
ldapService = service.NewLdapService(ldapConfig)
|
||||||
|
|
||||||
|
err := ldapService.Init()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ldapService = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dockerService := service.NewDockerService()
|
||||||
|
authService := service.NewAuthService(authConfig, dockerService, ldapService)
|
||||||
|
oauthBrokerService := service.NewOAuthBrokerService(app.getOAuthBrokerConfig())
|
||||||
|
|
||||||
|
// Initialize services
|
||||||
|
services := []Service{
|
||||||
|
dockerService,
|
||||||
|
authService,
|
||||||
|
oauthBrokerService,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, svc := range services {
|
||||||
|
if svc != nil {
|
||||||
|
err := svc.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configured providers
|
||||||
|
var configuredProviders []string
|
||||||
|
|
||||||
|
if authService.UserAuthConfigured() || ldapService != nil {
|
||||||
|
configuredProviders = append(configuredProviders, "username")
|
||||||
|
}
|
||||||
|
|
||||||
|
configuredProviders = append(configuredProviders, oauthBrokerService.GetConfiguredServices()...)
|
||||||
|
|
||||||
|
if len(configuredProviders) == 0 {
|
||||||
|
return fmt.Errorf("no authentication providers configured")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create engine
|
||||||
|
engine := gin.New()
|
||||||
|
router := engine.Group("/api")
|
||||||
|
|
||||||
|
// Create middlewares
|
||||||
|
var middlewares []Middleware
|
||||||
|
|
||||||
|
contextMiddleware := middleware.NewContextMiddleware(middleware.ContextMiddlewareConfig{
|
||||||
|
Domain: domain,
|
||||||
|
}, authService, oauthBrokerService)
|
||||||
|
|
||||||
|
uiMiddleware := middleware.NewUIMiddleware()
|
||||||
|
zerologMiddleware := middleware.NewZerologMiddleware()
|
||||||
|
|
||||||
|
middlewares = append(middlewares, contextMiddleware, uiMiddleware, zerologMiddleware)
|
||||||
|
|
||||||
|
for _, middleware := range middlewares {
|
||||||
|
log.Debug().Str("middleware", middleware.Name()).Msg("Initializing middleware")
|
||||||
|
err := middleware.Init()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to initialize %s middleware: %w", middleware.Name(), err)
|
||||||
|
}
|
||||||
|
router.Use(middleware.Middleware())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create controllers
|
||||||
|
contextController := controller.NewContextController(controller.ContextControllerConfig{
|
||||||
|
ConfiguredProviders: configuredProviders,
|
||||||
|
DisableContinue: app.Config.DisableContinue,
|
||||||
|
Title: app.Config.Title,
|
||||||
|
GenericName: app.Config.GenericName,
|
||||||
|
Domain: domain,
|
||||||
|
ForgotPasswordMessage: app.Config.FogotPasswordMessage,
|
||||||
|
BackgroundImage: app.Config.BackgroundImage,
|
||||||
|
OAuthAutoRedirect: app.Config.OAuthAutoRedirect,
|
||||||
|
}, router)
|
||||||
|
|
||||||
|
oauthController := controller.NewOAuthController(controller.OAuthControllerConfig{
|
||||||
|
AppURL: app.Config.AppURL,
|
||||||
|
SecureCookie: app.Config.SecureCookie,
|
||||||
|
CSRFCookieName: csrfCookieName,
|
||||||
|
RedirectCookieName: redirectCookieName,
|
||||||
|
}, router, authService, oauthBrokerService)
|
||||||
|
|
||||||
|
proxyController := controller.NewProxyController(controller.ProxyControllerConfig{
|
||||||
|
AppURL: app.Config.AppURL,
|
||||||
|
}, router, dockerService, authService)
|
||||||
|
|
||||||
|
userController := controller.NewUserController(controller.UserControllerConfig{
|
||||||
|
Domain: domain,
|
||||||
|
}, router, authService)
|
||||||
|
|
||||||
|
healthController := controller.NewHealthController(router)
|
||||||
|
|
||||||
|
// Setup routes
|
||||||
|
controller := []Controller{
|
||||||
|
contextController,
|
||||||
|
oauthController,
|
||||||
|
proxyController,
|
||||||
|
userController,
|
||||||
|
healthController,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ctrl := range controller {
|
||||||
|
log.Debug().Msgf("Setting up %T routes", ctrl)
|
||||||
|
ctrl.SetupRoutes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start server
|
||||||
|
address := fmt.Sprintf("%s:%d", app.Config.Address, app.Config.Port)
|
||||||
|
log.Info().Msgf("Starting server on %s", address)
|
||||||
|
if err := engine.Run(address); err != nil {
|
||||||
|
log.Fatal().Err(err).Msg("Failed to start server")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary
|
||||||
|
func (app *BootstrapApp) getOAuthBrokerConfig() map[string]config.OAuthServiceConfig {
|
||||||
|
return map[string]config.OAuthServiceConfig{
|
||||||
|
"google": {
|
||||||
|
ClientID: app.Config.GoogleClientId,
|
||||||
|
ClientSecret: app.Config.GoogleClientSecret,
|
||||||
|
RedirectURL: fmt.Sprintf("%s/api/oauth/callback/google", app.Config.AppURL),
|
||||||
|
},
|
||||||
|
"github": {
|
||||||
|
ClientID: app.Config.GithubClientId,
|
||||||
|
ClientSecret: app.Config.GithubClientSecret,
|
||||||
|
RedirectURL: fmt.Sprintf("%s/api/oauth/callback/github", app.Config.AppURL),
|
||||||
|
},
|
||||||
|
"generic": {
|
||||||
|
ClientID: app.Config.GenericClientId,
|
||||||
|
ClientSecret: app.Config.GenericClientSecret,
|
||||||
|
RedirectURL: fmt.Sprintf("%s/api/oauth/callback/generic", app.Config.AppURL),
|
||||||
|
Scopes: strings.Split(app.Config.GenericScopes, ","),
|
||||||
|
AuthURL: app.Config.GenericAuthURL,
|
||||||
|
TokenURL: app.Config.GenericTokenURL,
|
||||||
|
UserinfoURL: app.Config.GenericUserURL,
|
||||||
|
InsecureSkipVerify: app.Config.GenericSkipSSL,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ var CommitHash = "n/a"
|
|||||||
var BuildTimestamp = "n/a"
|
var BuildTimestamp = "n/a"
|
||||||
|
|
||||||
var SessionCookieName = "tinyauth-session"
|
var SessionCookieName = "tinyauth-session"
|
||||||
var CsrfCookieName = "tinyauth-csrf"
|
var CSRFCookieName = "tinyauth-csrf"
|
||||||
var RedirectCookieName = "tinyauth-redirect"
|
var RedirectCookieName = "tinyauth-redirect"
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -23,7 +23,7 @@ type Config struct {
|
|||||||
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"`
|
SecureCookie bool `mapstructure:"secure-cookie"`
|
||||||
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"`
|
||||||
@@ -43,9 +43,8 @@ type Config struct {
|
|||||||
OAuthWhitelist string `mapstructure:"oauth-whitelist"`
|
OAuthWhitelist string `mapstructure:"oauth-whitelist"`
|
||||||
OAuthAutoRedirect string `mapstructure:"oauth-auto-redirect" validate:"oneof=none github google generic"`
|
OAuthAutoRedirect string `mapstructure:"oauth-auto-redirect" validate:"oneof=none github google generic"`
|
||||||
SessionExpiry int `mapstructure:"session-expiry"`
|
SessionExpiry int `mapstructure:"session-expiry"`
|
||||||
LogLevel int8 `mapstructure:"log-level" validate:"min=-1,max=5"`
|
LogLevel string `mapstructure:"log-level" validate:"oneof=trace debug info warn error fatal panic"`
|
||||||
Title string `mapstructure:"app-title"`
|
Title string `mapstructure:"app-title"`
|
||||||
EnvFile string `mapstructure:"env-file"`
|
|
||||||
LoginTimeout int `mapstructure:"login-timeout"`
|
LoginTimeout int `mapstructure:"login-timeout"`
|
||||||
LoginMaxRetries int `mapstructure:"login-max-retries"`
|
LoginMaxRetries int `mapstructure:"login-max-retries"`
|
||||||
FogotPasswordMessage string `mapstructure:"forgot-password-message"`
|
FogotPasswordMessage string `mapstructure:"forgot-password-message"`
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ type AuthServiceConfig struct {
|
|||||||
Users []config.User
|
Users []config.User
|
||||||
OauthWhitelist string
|
OauthWhitelist string
|
||||||
SessionExpiry int
|
SessionExpiry int
|
||||||
CookieSecure bool
|
SecureCookie bool
|
||||||
Domain string
|
Domain string
|
||||||
LoginTimeout int
|
LoginTimeout int
|
||||||
LoginMaxRetries int
|
LoginMaxRetries int
|
||||||
@@ -57,10 +57,11 @@ func (auth *AuthService) Init() error {
|
|||||||
store.Options = &sessions.Options{
|
store.Options = &sessions.Options{
|
||||||
Path: "/",
|
Path: "/",
|
||||||
MaxAge: auth.Config.SessionExpiry,
|
MaxAge: auth.Config.SessionExpiry,
|
||||||
Secure: auth.Config.CookieSecure,
|
Secure: auth.Config.SecureCookie,
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
Domain: fmt.Sprintf(".%s", auth.Config.Domain),
|
Domain: fmt.Sprintf(".%s", auth.Config.Domain),
|
||||||
}
|
}
|
||||||
|
auth.Store = store
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ func (auth *AuthService) GetSession(c *gin.Context) (*sessions.Session, error) {
|
|||||||
// If there was an error getting the session, it might be invalid so let's clear it and retry
|
// If there was an error getting the session, it might be invalid so let's clear it and retry
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Invalid session, clearing cookie and retrying")
|
log.Error().Err(err).Msg("Invalid session, clearing cookie and retrying")
|
||||||
c.SetCookie(auth.Config.SessionCookieName, "", -1, "/", fmt.Sprintf(".%s", auth.Config.Domain), auth.Config.CookieSecure, true)
|
c.SetCookie(auth.Config.SessionCookieName, "", -1, "/", fmt.Sprintf(".%s", auth.Config.Domain), auth.Config.SecureCookie, true)
|
||||||
session, err = auth.Store.Get(c.Request, auth.Config.SessionCookieName)
|
session, err = auth.Store.Get(c.Request, auth.Config.SessionCookieName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to get session")
|
log.Error().Err(err).Msg("Failed to get session")
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"tinyauth/internal/config"
|
"tinyauth/internal/config"
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/oauth2/endpoints"
|
||||||
)
|
)
|
||||||
|
|
||||||
var GithubOAuthScopes = []string{"user:email", "read:user"}
|
var GithubOAuthScopes = []string{"user:email", "read:user"}
|
||||||
@@ -39,6 +40,7 @@ func NewGithubOAuthService(config config.OAuthServiceConfig) *GithubOAuthService
|
|||||||
ClientSecret: config.ClientSecret,
|
ClientSecret: config.ClientSecret,
|
||||||
RedirectURL: config.RedirectURL,
|
RedirectURL: config.RedirectURL,
|
||||||
Scopes: GithubOAuthScopes,
|
Scopes: GithubOAuthScopes,
|
||||||
|
Endpoint: endpoints.GitHub,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"tinyauth/internal/config"
|
"tinyauth/internal/config"
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/oauth2/endpoints"
|
||||||
)
|
)
|
||||||
|
|
||||||
var GoogleOAuthScopes = []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"}
|
var GoogleOAuthScopes = []string{"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile"}
|
||||||
@@ -34,6 +35,7 @@ func NewGoogleOAuthService(config config.OAuthServiceConfig) *GoogleOAuthService
|
|||||||
ClientSecret: config.ClientSecret,
|
ClientSecret: config.ClientSecret,
|
||||||
RedirectURL: config.RedirectURL,
|
RedirectURL: config.RedirectURL,
|
||||||
Scopes: GoogleOAuthScopes,
|
Scopes: GoogleOAuthScopes,
|
||||||
|
Endpoint: endpoints.Google,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"golang.org/x/crypto/hkdf"
|
"golang.org/x/crypto/hkdf"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -360,3 +361,22 @@ func GetContext(c *gin.Context) (config.UserContext, error) {
|
|||||||
|
|
||||||
return *userContext, nil
|
return *userContext, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetLogLevel(level string) zerolog.Level {
|
||||||
|
switch strings.ToLower(level) {
|
||||||
|
case "debug":
|
||||||
|
return zerolog.DebugLevel
|
||||||
|
case "info":
|
||||||
|
return zerolog.InfoLevel
|
||||||
|
case "warn":
|
||||||
|
return zerolog.WarnLevel
|
||||||
|
case "error":
|
||||||
|
return zerolog.ErrorLevel
|
||||||
|
case "fatal":
|
||||||
|
return zerolog.FatalLevel
|
||||||
|
case "panic":
|
||||||
|
return zerolog.PanicLevel
|
||||||
|
default:
|
||||||
|
return zerolog.InfoLevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user