diff --git a/cmd/authelia/main.go b/cmd/authelia/main.go index 63e8f845c..1bb57dc35 100644 --- a/cmd/authelia/main.go +++ b/cmd/authelia/main.go @@ -25,11 +25,12 @@ var configPathFlag string //nolint:gocyclo // TODO: Consider refactoring/simplifying, time permitting. func startServer() { + logger := logging.Logger() config, errs := configuration.Read(configPathFlag) if len(errs) > 0 { for _, err := range errs { - logging.Logger().Error(err) + logger.Error(err) } os.Exit(1) @@ -38,7 +39,7 @@ func startServer() { autheliaCertPool, errs, nonFatalErrs := utils.NewX509CertPool(config.CertificatesDirectory, config) if len(errs) > 0 { for _, err := range errs { - logging.Logger().Error(err) + logger.Error(err) } os.Exit(2) @@ -46,28 +47,28 @@ func startServer() { if len(nonFatalErrs) > 0 { for _, err := range nonFatalErrs { - logging.Logger().Warn(err) + logger.Warn(err) } } if err := logging.InitializeLogger(config.LogFormat, config.LogFilePath); err != nil { - logging.Logger().Fatalf("Cannot initialize logger: %v", err) + logger.Fatalf("Cannot initialize logger: %v", err) } switch config.LogLevel { case "info": - logging.Logger().Info("Logging severity set to info") + logger.Info("Logging severity set to info") logging.SetLevel(logrus.InfoLevel) case "debug": - logging.Logger().Info("Logging severity set to debug") + logger.Info("Logging severity set to debug") logging.SetLevel(logrus.DebugLevel) case "trace": - logging.Logger().Info("Logging severity set to trace") + logger.Info("Logging severity set to trace") logging.SetLevel(logrus.TraceLevel) } if os.Getenv("ENVIRONMENT") == "dev" { - logging.Logger().Info("===> Authelia is running in development mode. <===") + logger.Info("===> Authelia is running in development mode. <===") } var storageProvider storage.Provider @@ -80,7 +81,7 @@ func startServer() { case config.Storage.Local != nil: storageProvider = storage.NewSQLiteProvider(config.Storage.Local.Path) default: - logging.Logger().Fatalf("Unrecognized storage backend") + logger.Fatalf("Unrecognized storage backend") } var userProvider authentication.UserProvider @@ -91,7 +92,7 @@ func startServer() { case config.AuthenticationBackend.Ldap != nil: userProvider = authentication.NewLDAPUserProvider(*config.AuthenticationBackend.Ldap, autheliaCertPool) default: - logging.Logger().Fatalf("Unrecognized authentication backend") + logger.Fatalf("Unrecognized authentication backend") } var notifier notification.Notifier @@ -102,13 +103,13 @@ func startServer() { case config.Notifier.FileSystem != nil: notifier = notification.NewFileNotifier(*config.Notifier.FileSystem) default: - logging.Logger().Fatalf("Unrecognized notifier") + logger.Fatalf("Unrecognized notifier") } if !config.Notifier.DisableStartupCheck { _, err := notifier.StartupCheck() if err != nil { - logging.Logger().Fatalf("Error during notifier startup check: %s", err) + logger.Fatalf("Error during notifier startup check: %s", err) } } @@ -129,6 +130,7 @@ func startServer() { } func main() { + logger := logging.Logger() rootCmd := &cobra.Command{ Use: "authelia", Run: func(cmd *cobra.Command, args []string) { @@ -150,6 +152,6 @@ func main() { commands.ValidateConfigCmd, commands.CertificatesCmd) if err := rootCmd.Execute(); err != nil { - logging.Logger().Fatal(err) + logger.Fatal(err) } } diff --git a/internal/authentication/file_user_provider.go b/internal/authentication/file_user_provider.go index 130a534ca..3a42290c0 100644 --- a/internal/authentication/file_user_provider.go +++ b/internal/authentication/file_user_provider.go @@ -36,10 +36,12 @@ type DatabaseModel struct { // NewFileUserProvider creates a new instance of FileUserProvider. func NewFileUserProvider(configuration *schema.FileAuthenticationBackendConfiguration) *FileUserProvider { + logger := logging.Logger() + errs := checkDatabase(configuration.Path) if errs != nil { for _, err := range errs { - logging.Logger().Error(err) + logger.Error(err) } os.Exit(1) diff --git a/internal/authentication/ldap_user_provider.go b/internal/authentication/ldap_user_provider.go index 27af621cd..85c9fef5c 100644 --- a/internal/authentication/ldap_user_provider.go +++ b/internal/authentication/ldap_user_provider.go @@ -166,8 +166,9 @@ func (p *LDAPUserProvider) resolveUsersFilter(userFilter string, inputUsername s } func (p *LDAPUserProvider) getUserProfile(conn LDAPConnection, inputUsername string) (*ldapUserProfile, error) { + logger := logging.Logger() userFilter := p.resolveUsersFilter(p.configuration.UsersFilter, inputUsername) - logging.Logger().Tracef("Computed user filter is %s", userFilter) + logger.Tracef("Computed user filter is %s", userFilter) attributes := []string{"dn", p.configuration.DisplayNameAttribute, @@ -239,6 +240,8 @@ func (p *LDAPUserProvider) resolveGroupsFilter(inputUsername string, profile *ld // GetDetails retrieve the groups a user belongs to. func (p *LDAPUserProvider) GetDetails(inputUsername string) (*UserDetails, error) { + logger := logging.Logger() + conn, err := p.connect(p.configuration.User, p.configuration.Password) if err != nil { return nil, err @@ -255,7 +258,7 @@ func (p *LDAPUserProvider) GetDetails(inputUsername string) (*UserDetails, error return nil, fmt.Errorf("Unable to create group filter for user %s. Cause: %s", inputUsername, err) } - logging.Logger().Tracef("Computed groups filter is %s", groupsFilter) + logger.Tracef("Computed groups filter is %s", groupsFilter) // Search for the given username. searchGroupRequest := ldap.NewSearchRequest( @@ -273,7 +276,7 @@ func (p *LDAPUserProvider) GetDetails(inputUsername string) (*UserDetails, error for _, res := range sr.Entries { if len(res.Attributes) == 0 { - logging.Logger().Warningf("No groups retrieved from LDAP for user %s", inputUsername) + logger.Warningf("No groups retrieved from LDAP for user %s", inputUsername) break } // Append all values of the document. Normally there should be only one per document. diff --git a/internal/authorization/authorizer.go b/internal/authorization/authorizer.go index 4ae7ee87f..af0ee0dc5 100644 --- a/internal/authorization/authorizer.go +++ b/internal/authorization/authorizer.go @@ -114,8 +114,8 @@ func (p *Authorizer) IsSecondFactorEnabled() bool { // GetRequiredLevel retrieve the required level of authorization to access the object. func (p *Authorizer) GetRequiredLevel(subject Subject, requestURL url.URL) Level { - logging.Logger().Tracef("Check authorization of subject %s and url %s.", - subject.String(), requestURL.String()) + logger := logging.Logger() + logger.Tracef("Check authorization of subject %s and url %s.", subject.String(), requestURL.String()) matchingRules := selectMatchingRules(p.configuration.Rules, p.configuration.Networks, subject, Object{ Domain: requestURL.Hostname(), @@ -126,8 +126,7 @@ func (p *Authorizer) GetRequiredLevel(subject Subject, requestURL url.URL) Level return PolicyToLevel(matchingRules[0].Policy) } - logging.Logger().Tracef("No matching rule for subject %s and url %s... Applying default policy.", - subject.String(), requestURL.String()) + logger.Tracef("No matching rule for subject %s and url %s... Applying default policy.", subject.String(), requestURL.String()) return PolicyToLevel(p.configuration.DefaultPolicy) } diff --git a/internal/configuration/reader.go b/internal/configuration/reader.go index b64047abc..abe2d5d39 100644 --- a/internal/configuration/reader.go +++ b/internal/configuration/reader.go @@ -18,6 +18,8 @@ import ( // Read a YAML configuration and create a Configuration object out of it. //go:generate broccoli -src ../../config.template.yml -var=cfg -o configuration func Read(configPath string) (*schema.Configuration, []error) { + logger := logging.Logger() + if configPath == "" { return nil, []error{errors.New("No config file path provided")} } @@ -81,7 +83,7 @@ func Read(configPath string) (*schema.Configuration, []error) { if val.HasWarnings() { for _, warn := range val.Warnings() { - logging.Logger().Warnf(warn.Error()) + logger.Warnf(warn.Error()) } } diff --git a/internal/notification/smtp_notifier.go b/internal/notification/smtp_notifier.go index db9bcc2c1..60e35e65d 100644 --- a/internal/notification/smtp_notifier.go +++ b/internal/notification/smtp_notifier.go @@ -51,25 +51,26 @@ func NewSMTPNotifier(configuration schema.SMTPNotifierConfiguration, certPool *x // Do startTLS if available (some servers only provide the auth extension after, and encryption is preferred). func (n *SMTPNotifier) startTLS() error { + logger := logging.Logger() // Only start if not already encrypted if _, ok := n.client.TLSConnectionState(); ok { - logging.Logger().Debugf("Notifier SMTP connection is already encrypted, skipping STARTTLS") + logger.Debugf("Notifier SMTP connection is already encrypted, skipping STARTTLS") return nil } switch ok, _ := n.client.Extension("STARTTLS"); ok { case true: - logging.Logger().Debugf("Notifier SMTP server supports STARTTLS (disableVerifyCert: %t, ServerName: %s), attempting", n.tlsConfig.InsecureSkipVerify, n.tlsConfig.ServerName) + logger.Debugf("Notifier SMTP server supports STARTTLS (disableVerifyCert: %t, ServerName: %s), attempting", n.tlsConfig.InsecureSkipVerify, n.tlsConfig.ServerName) if err := n.client.StartTLS(n.tlsConfig); err != nil { return err } - logging.Logger().Debug("Notifier SMTP STARTTLS completed without error") + logger.Debug("Notifier SMTP STARTTLS completed without error") default: switch n.disableRequireTLS { case true: - logging.Logger().Warn("Notifier SMTP server does not support STARTTLS and SMTP configuration is set to disable the TLS requirement (only useful for unauthenticated emails over plain text)") + logger.Warn("Notifier SMTP server does not support STARTTLS and SMTP configuration is set to disable the TLS requirement (only useful for unauthenticated emails over plain text)") default: return errors.New("Notifier SMTP server does not support TLS and it is required by default (see documentation if you want to disable this highly recommended requirement)") } @@ -80,6 +81,7 @@ func (n *SMTPNotifier) startTLS() error { // Attempt Authentication. func (n *SMTPNotifier) auth() error { + logger := logging.Logger() // Attempt AUTH if password is specified only. if n.password != "" { _, ok := n.client.TLSConnectionState() @@ -92,18 +94,18 @@ func (n *SMTPNotifier) auth() error { if ok { var auth smtp.Auth - logging.Logger().Debugf("Notifier SMTP server supports authentication with the following mechanisms: %s", m) + logger.Debugf("Notifier SMTP server supports authentication with the following mechanisms: %s", m) mechanisms := strings.Split(m, " ") // Adaptively select the AUTH mechanism to use based on what the server advertised. if utils.IsStringInSlice("PLAIN", mechanisms) { auth = smtp.PlainAuth("", n.username, n.password, n.host) - logging.Logger().Debug("Notifier SMTP client attempting AUTH PLAIN with server") + logger.Debug("Notifier SMTP client attempting AUTH PLAIN with server") } else if utils.IsStringInSlice("LOGIN", mechanisms) { auth = newLoginAuth(n.username, n.password, n.host) - logging.Logger().Debug("Notifier SMTP client attempting AUTH LOGIN with server") + logger.Debug("Notifier SMTP client attempting AUTH LOGIN with server") } // Throw error since AUTH extension is not supported. @@ -116,7 +118,7 @@ func (n *SMTPNotifier) auth() error { return err } - logging.Logger().Debug("Notifier SMTP client authenticated successfully with the server") + logger.Debug("Notifier SMTP client authenticated successfully with the server") return nil } @@ -124,13 +126,14 @@ func (n *SMTPNotifier) auth() error { return errors.New("Notifier SMTP server does not advertise the AUTH extension but config requires AUTH (password specified), either disable AUTH, or use an SMTP host that supports AUTH PLAIN or AUTH LOGIN") } - logging.Logger().Debug("Notifier SMTP config has no password specified so authentication is being skipped") + logger.Debug("Notifier SMTP config has no password specified so authentication is being skipped") return nil } func (n *SMTPNotifier) compose(recipient, subject, body, htmlBody string) error { - logging.Logger().Debugf("Notifier SMTP client attempting to send email body to %s", recipient) + logger := logging.Logger() + logger.Debugf("Notifier SMTP client attempting to send email body to %s", recipient) if !n.disableRequireTLS { _, ok := n.client.TLSConnectionState() @@ -141,7 +144,7 @@ func (n *SMTPNotifier) compose(recipient, subject, body, htmlBody string) error wc, err := n.client.Data() if err != nil { - logging.Logger().Debugf("Notifier SMTP client error while obtaining WriteCloser: %s", err) + logger.Debugf("Notifier SMTP client error while obtaining WriteCloser: %s", err) return err } @@ -171,13 +174,13 @@ func (n *SMTPNotifier) compose(recipient, subject, body, htmlBody string) error _, err = fmt.Fprint(wc, msg) if err != nil { - logging.Logger().Debugf("Notifier SMTP client error while sending email body over WriteCloser: %s", err) + logger.Debugf("Notifier SMTP client error while sending email body over WriteCloser: %s", err) return err } err = wc.Close() if err != nil { - logging.Logger().Debugf("Notifier SMTP client error while closing the WriteCloser: %s", err) + logger.Debugf("Notifier SMTP client error while closing the WriteCloser: %s", err) return err } @@ -186,10 +189,11 @@ func (n *SMTPNotifier) compose(recipient, subject, body, htmlBody string) error // Dial the SMTP server with the SMTPNotifier config. func (n *SMTPNotifier) dial() error { - logging.Logger().Debugf("Notifier SMTP client attempting connection to %s", n.address) + logger := logging.Logger() + logger.Debugf("Notifier SMTP client attempting connection to %s", n.address) if n.port == 465 { - logging.Logger().Warnf("Notifier SMTP client configured to connect to a SMTPS server. It's highly recommended you use a non SMTPS port and STARTTLS instead of SMTPS, as the protocol is long deprecated.") + logger.Warnf("Notifier SMTP client configured to connect to a SMTPS server. It's highly recommended you use a non SMTPS port and STARTTLS instead of SMTPS, as the protocol is long deprecated.") conn, err := tls.Dial("tcp", n.address, n.tlsConfig) if err != nil { @@ -211,16 +215,18 @@ func (n *SMTPNotifier) dial() error { n.client = client } - logging.Logger().Debug("Notifier SMTP client connected successfully") + logger.Debug("Notifier SMTP client connected successfully") return nil } // Closes the connection properly. func (n *SMTPNotifier) cleanup() { + logger := logging.Logger() + err := n.client.Quit() if err != nil { - logging.Logger().Warnf("Notifier SMTP client encountered error during cleanup: %s", err) + logger.Warnf("Notifier SMTP client encountered error during cleanup: %s", err) } } @@ -261,6 +267,7 @@ func (n *SMTPNotifier) StartupCheck() (bool, error) { // Send is used to send an email to a recipient. func (n *SMTPNotifier) Send(recipient, title, body, htmlBody string) error { + logger := logging.Logger() subject := strings.ReplaceAll(n.subject, "{title}", title) if err := n.dial(); err != nil { @@ -285,12 +292,12 @@ func (n *SMTPNotifier) Send(recipient, title, body, htmlBody string) error { // Set the sender and recipient first. if err := n.client.Mail(n.sender); err != nil { - logging.Logger().Debugf("Notifier SMTP failed while sending MAIL FROM (using sender) with error: %s", err) + logger.Debugf("Notifier SMTP failed while sending MAIL FROM (using sender) with error: %s", err) return err } if err := n.client.Rcpt(recipient); err != nil { - logging.Logger().Debugf("Notifier SMTP failed while sending RCPT TO (using recipient) with error: %s", err) + logger.Debugf("Notifier SMTP failed while sending RCPT TO (using recipient) with error: %s", err) return err } @@ -299,7 +306,7 @@ func (n *SMTPNotifier) Send(recipient, title, body, htmlBody string) error { return err } - logging.Logger().Debug("Notifier SMTP client successfully sent email") + logger.Debug("Notifier SMTP client successfully sent email") return nil } diff --git a/internal/server/error_handler.go b/internal/server/error_handler.go index bc1254f30..0d7c2c18a 100644 --- a/internal/server/error_handler.go +++ b/internal/server/error_handler.go @@ -10,17 +10,19 @@ import ( // Replacement for the default error handler in fasthttp. func autheliaErrorHandler(ctx *fasthttp.RequestCtx, err error) { + logger := logging.Logger() + if _, ok := err.(*fasthttp.ErrSmallBuffer); ok { // Note: Getting X-Forwarded-For or Request URI is impossible for ths error. - logging.Logger().Tracef("Request was too large to handle from client %s. Response Code %d.", ctx.RemoteIP().String(), fasthttp.StatusRequestHeaderFieldsTooLarge) + logger.Tracef("Request was too large to handle from client %s. Response Code %d.", ctx.RemoteIP().String(), fasthttp.StatusRequestHeaderFieldsTooLarge) ctx.Error("Request header too large", fasthttp.StatusRequestHeaderFieldsTooLarge) } else if netErr, ok := err.(*net.OpError); ok && netErr.Timeout() { // TODO: Add X-Forwarded-For Check here. - logging.Logger().Tracef("Request timeout occurred while handling from client %s: %s. Response Code %d.", ctx.RemoteIP().String(), ctx.RequestURI(), fasthttp.StatusRequestTimeout) + logger.Tracef("Request timeout occurred while handling from client %s: %s. Response Code %d.", ctx.RemoteIP().String(), ctx.RequestURI(), fasthttp.StatusRequestTimeout) ctx.Error("Request timeout", fasthttp.StatusRequestTimeout) } else { // TODO: Add X-Forwarded-For Check here. - logging.Logger().Tracef("An unknown error occurred while handling a request from client %s: %s. Response Code %d.", ctx.RemoteIP().String(), ctx.RequestURI(), fasthttp.StatusBadRequest) + logger.Tracef("An unknown error occurred while handling a request from client %s: %s. Response Code %d.", ctx.RemoteIP().String(), ctx.RequestURI(), fasthttp.StatusBadRequest) ctx.Error("Error when parsing request", fasthttp.StatusBadRequest) } } diff --git a/internal/server/server.go b/internal/server/server.go index 4168acadd..05b0af3a4 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -24,6 +24,7 @@ import ( // StartServer start Authelia server with the given configuration and providers. func StartServer(configuration schema.Configuration, providers middlewares.Providers) { + logger := logging.Logger() autheliaMiddleware := middlewares.AutheliaMiddleware(configuration, providers) embeddedAssets := "/public_html/" swaggerAssets := embeddedAssets + "api/" @@ -147,30 +148,30 @@ func StartServer(configuration schema.Configuration, providers middlewares.Provi listener, err := net.Listen("tcp", addrPattern) if err != nil { - logging.Logger().Fatalf("Error initializing listener: %s", err) + logger.Fatalf("Error initializing listener: %s", err) } if configuration.AuthenticationBackend.File != nil && configuration.AuthenticationBackend.File.Password.Algorithm == "argon2id" && runtime.GOOS == "linux" { f, err := ioutil.ReadFile("/sys/fs/cgroup/memory/memory.limit_in_bytes") if err != nil { - logging.Logger().Warnf("Error reading hosts memory limit: %s", err) + logger.Warnf("Error reading hosts memory limit: %s", err) } else { m, _ := strconv.Atoi(strings.TrimSuffix(string(f), "\n")) hostMem := float64(m) / 1024 / 1024 / 1024 argonMem := float64(configuration.AuthenticationBackend.File.Password.Memory) / 1024 if hostMem/argonMem <= 2 { - logging.Logger().Warnf("Authelia's password hashing memory parameter is set to: %gGB this is %g%% of the available memory: %gGB", argonMem, argonMem/hostMem*100, hostMem) - logging.Logger().Warn("Please read https://www.authelia.com/docs/configuration/authentication/file.html#memory and tune your deployment") + logger.Warnf("Authelia's password hashing memory parameter is set to: %gGB this is %g%% of the available memory: %gGB", argonMem, argonMem/hostMem*100, hostMem) + logger.Warn("Please read https://www.authelia.com/docs/configuration/authentication/file.html#memory and tune your deployment") } } } if configuration.TLSCert != "" && configuration.TLSKey != "" { - logging.Logger().Infof("Authelia is listening for TLS connections on %s%s", addrPattern, configuration.Server.Path) - logging.Logger().Fatal(server.ServeTLS(listener, configuration.TLSCert, configuration.TLSKey)) + logger.Infof("Authelia is listening for TLS connections on %s%s", addrPattern, configuration.Server.Path) + logger.Fatal(server.ServeTLS(listener, configuration.TLSCert, configuration.TLSKey)) } else { - logging.Logger().Infof("Authelia is listening for non-TLS connections on %s%s", addrPattern, configuration.Server.Path) - logging.Logger().Fatal(server.Serve(listener)) + logger.Infof("Authelia is listening for non-TLS connections on %s%s", addrPattern, configuration.Server.Path) + logger.Fatal(server.Serve(listener)) } } diff --git a/internal/server/template.go b/internal/server/template.go index 209727b53..6717ebcc2 100644 --- a/internal/server/template.go +++ b/internal/server/template.go @@ -20,19 +20,21 @@ var alphaNumericRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV // and generate a nonce to support a restrictive CSP while using material-ui. //go:generate broccoli -src ../../public_html -o public_html func ServeTemplatedFile(publicDir, file, base, session, rememberMe, resetPassword string) fasthttp.RequestHandler { + logger := logging.Logger() + f, err := br.Open(publicDir + file) if err != nil { - logging.Logger().Fatalf("Unable to open %s: %s", file, err) + logger.Fatalf("Unable to open %s: %s", file, err) } b, err := ioutil.ReadAll(f) if err != nil { - logging.Logger().Fatalf("Unable to read %s: %s", file, err) + logger.Fatalf("Unable to read %s: %s", file, err) } tmpl, err := template.New("file").Parse(string(b)) if err != nil { - logging.Logger().Fatalf("Unable to parse %s template: %s", file, err) + logger.Fatalf("Unable to parse %s template: %s", file, err) } return func(ctx *fasthttp.RequestCtx) { @@ -46,10 +48,10 @@ func ServeTemplatedFile(publicDir, file, base, session, rememberMe, resetPasswor } switch { - case os.Getenv("ENVIRONMENT") == dev: - ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf("default-src 'self' 'unsafe-eval'; object-src 'none'; style-src 'self' 'nonce-%s'", nonce)) case publicDir == "/public_html/api/": ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf("base-uri 'self' ; default-src 'self' ; img-src 'self' https://validator.swagger.io data: ; object-src 'none' ; script-src 'self' 'unsafe-inline' 'nonce-%s' ; style-src 'self' 'nonce-%s'", nonce, nonce)) + case os.Getenv("ENVIRONMENT") == dev: + ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf("default-src 'self' 'unsafe-eval'; object-src 'none'; style-src 'self' 'nonce-%s'", nonce)) default: ctx.Response.Header.Add("Content-Security-Policy", fmt.Sprintf("default-src 'self' ; object-src 'none'; style-src 'self' 'nonce-%s'", nonce)) } @@ -57,7 +59,7 @@ func ServeTemplatedFile(publicDir, file, base, session, rememberMe, resetPasswor err := tmpl.Execute(ctx.Response.BodyWriter(), struct{ Base, CSPNonce, Session, RememberMe, ResetPassword string }{Base: base, CSPNonce: nonce, Session: session, RememberMe: rememberMe, ResetPassword: resetPassword}) if err != nil { ctx.Error("An error occurred", 503) - logging.Logger().Errorf("Unable to execute template: %v", err) + logger.Errorf("Unable to execute template: %v", err) return } diff --git a/internal/suites/suite_cli_test.go b/internal/suites/suite_cli_test.go index 543f69fe5..8af92c827 100644 --- a/internal/suites/suite_cli_test.go +++ b/internal/suites/suite_cli_test.go @@ -37,6 +37,12 @@ func (s *CLISuite) SetupTest() { s.coverageArg = coverageArg } +func (s *CLISuite) TestShouldPrintVersion() { + output, err := s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "version"}) + s.Assert().Nil(err) + s.Assert().Contains(output, "Authelia version") +} + func (s *CLISuite) TestShouldValidateConfig() { output, err := s.Exec("authelia-backend", []string{"authelia", s.testArg, s.coverageArg, "validate-config", "/config/configuration.yml"}) s.Assert().Nil(err)