[CI] Add gocritic linter (#977)

* [CI] Add gocritic linter

* Implement gocritic recommendations
The outstanding recommendations are due to be addressed in #959 and #971 respectively.

* Fix implementation tests

* Fix remaining linting issues.

* Fix tests.

Co-authored-by: Clément Michaud <clement.michaud34@gmail.com>
pull/982/head^2
Amir Zarrinkafsh 2020-05-06 10:52:06 +10:00 committed by GitHub
parent 50f12bc4a4
commit cc06ab6c18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 84 additions and 57 deletions

View File

@ -16,6 +16,7 @@ linters:
enable: enable:
- asciicheck - asciicheck
- goconst - goconst
- gocritic
- gocyclo - gocyclo
- godot - godot
- gofmt - gofmt

View File

@ -202,7 +202,8 @@ func deployManifest(docker *Docker, tag string, amd64tag string, arm32v7tag stri
func publishDockerImage(arch string) { func publishDockerImage(arch string) {
docker := &Docker{} docker := &Docker{}
if ciTag != "" { switch {
case ciTag != "":
if len(tags) == 4 { if len(tags) == 4 {
log.Infof("Detected tags: '%s' | '%s' | '%s'", tags[1], tags[2], tags[3]) log.Infof("Detected tags: '%s' | '%s' | '%s'", tags[1], tags[2], tags[3])
login(docker) login(docker)
@ -216,16 +217,16 @@ func publishDockerImage(arch string) {
} else { } else {
log.Fatal("Docker image will not be published, the specified tag does not conform to the standard") log.Fatal("Docker image will not be published, the specified tag does not conform to the standard")
} }
} else if ciBranch != masterTag && !publicRepo.MatchString(ciBranch) { case ciBranch != masterTag && !publicRepo.MatchString(ciBranch):
login(docker) login(docker)
deploy(docker, ciBranch+"-"+arch) deploy(docker, ciBranch+"-"+arch)
} else if ciBranch != masterTag && publicRepo.MatchString(ciBranch) { case ciBranch != masterTag && publicRepo.MatchString(ciBranch):
login(docker) login(docker)
deploy(docker, "PR"+ciPullRequest+"-"+arch) deploy(docker, "PR"+ciPullRequest+"-"+arch)
} else if ciBranch == masterTag && ciPullRequest == stringFalse { case ciBranch == masterTag && ciPullRequest == stringFalse:
login(docker) login(docker)
deploy(docker, "master-"+arch) deploy(docker, "master-"+arch)
} else { default:
log.Info("Docker image will not be published") log.Info("Docker image will not be published")
} }
} }
@ -233,7 +234,8 @@ func publishDockerImage(arch string) {
func publishDockerManifest() { func publishDockerManifest() {
docker := &Docker{} docker := &Docker{}
if ciTag != "" { switch {
case ciTag != "":
if len(tags) == 4 { if len(tags) == 4 {
log.Infof("Detected tags: '%s' | '%s' | '%s'", tags[1], tags[2], tags[3]) log.Infof("Detected tags: '%s' | '%s' | '%s'", tags[1], tags[2], tags[3])
login(docker) login(docker)
@ -250,17 +252,17 @@ func publishDockerManifest() {
} else { } else {
log.Fatal("Docker manifest will not be published, the specified tag does not conform to the standard") log.Fatal("Docker manifest will not be published, the specified tag does not conform to the standard")
} }
} else if ciBranch != masterTag && !publicRepo.MatchString(ciBranch) { case ciBranch != masterTag && !publicRepo.MatchString(ciBranch):
login(docker) login(docker)
deployManifest(docker, ciBranch, ciBranch+"-amd64", ciBranch+"-arm32v7", ciBranch+"-arm64v8") deployManifest(docker, ciBranch, ciBranch+"-amd64", ciBranch+"-arm32v7", ciBranch+"-arm64v8")
} else if ciBranch != masterTag && publicRepo.MatchString(ciBranch) { case ciBranch != masterTag && publicRepo.MatchString(ciBranch):
login(docker) login(docker)
deployManifest(docker, "PR"+ciPullRequest, "PR"+ciPullRequest+"-amd64", "PR"+ciPullRequest+"-arm32v7", "PR"+ciPullRequest+"-arm64v8") deployManifest(docker, "PR"+ciPullRequest, "PR"+ciPullRequest+"-amd64", "PR"+ciPullRequest+"-arm32v7", "PR"+ciPullRequest+"-arm64v8")
} else if ciBranch == masterTag && ciPullRequest == stringFalse { case ciBranch == masterTag && ciPullRequest == stringFalse:
login(docker) login(docker)
deployManifest(docker, "master", "master-amd64", "master-arm32v7", "master-arm64v8") deployManifest(docker, "master", "master-amd64", "master-arm32v7", "master-arm64v8")
publishDockerReadme(docker) publishDockerReadme(docker)
} else { default:
log.Info("Docker manifest will not be published") log.Info("Docker manifest will not be published")
} }
} }

View File

@ -63,31 +63,36 @@ func startServer() {
var userProvider authentication.UserProvider var userProvider authentication.UserProvider
if config.AuthenticationBackend.File != nil { switch {
case config.AuthenticationBackend.File != nil:
userProvider = authentication.NewFileUserProvider(config.AuthenticationBackend.File) userProvider = authentication.NewFileUserProvider(config.AuthenticationBackend.File)
} else if config.AuthenticationBackend.Ldap != nil { case config.AuthenticationBackend.Ldap != nil:
userProvider = authentication.NewLDAPUserProvider(*config.AuthenticationBackend.Ldap) userProvider = authentication.NewLDAPUserProvider(*config.AuthenticationBackend.Ldap)
} else { default:
log.Fatalf("Unrecognized authentication backend") log.Fatalf("Unrecognized authentication backend")
} }
var storageProvider storage.Provider var storageProvider storage.Provider
if config.Storage.PostgreSQL != nil {
switch {
case config.Storage.PostgreSQL != nil:
storageProvider = storage.NewPostgreSQLProvider(*config.Storage.PostgreSQL) storageProvider = storage.NewPostgreSQLProvider(*config.Storage.PostgreSQL)
} else if config.Storage.MySQL != nil { case config.Storage.MySQL != nil:
storageProvider = storage.NewMySQLProvider(*config.Storage.MySQL) storageProvider = storage.NewMySQLProvider(*config.Storage.MySQL)
} else if config.Storage.Local != nil { case config.Storage.Local != nil:
storageProvider = storage.NewSQLiteProvider(config.Storage.Local.Path) storageProvider = storage.NewSQLiteProvider(config.Storage.Local.Path)
} else { default:
log.Fatalf("Unrecognized storage backend") log.Fatalf("Unrecognized storage backend")
} }
var notifier notification.Notifier var notifier notification.Notifier
if config.Notifier.SMTP != nil {
switch {
case config.Notifier.SMTP != nil:
notifier = notification.NewSMTPNotifier(*config.Notifier.SMTP) notifier = notification.NewSMTPNotifier(*config.Notifier.SMTP)
} else if config.Notifier.FileSystem != nil { case config.Notifier.FileSystem != nil:
notifier = notification.NewFileNotifier(*config.Notifier.FileSystem) notifier = notification.NewFileNotifier(*config.Notifier.FileSystem)
} else { default:
log.Fatalf("Unrecognized notifier") log.Fatalf("Unrecognized notifier")
} }

View File

@ -33,7 +33,7 @@ type CryptAlgo string
const ( const (
// HashingAlgorithmArgon2id Argon2id hash identifier. // HashingAlgorithmArgon2id Argon2id hash identifier.
HashingAlgorithmArgon2id CryptAlgo = "argon2id" HashingAlgorithmArgon2id CryptAlgo = argon2id
// HashingAlgorithmSHA512 SHA512 hash identifier. // HashingAlgorithmSHA512 SHA512 hash identifier.
HashingAlgorithmSHA512 CryptAlgo = "6" HashingAlgorithmSHA512 CryptAlgo = "6"
) )
@ -54,6 +54,7 @@ var HashingPossibleSaltCharacters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJ
// ErrUserNotFound indicates the user wasn't found in the authentication backend. // ErrUserNotFound indicates the user wasn't found in the authentication backend.
var ErrUserNotFound = errors.New("user not found") var ErrUserNotFound = errors.New("user not found")
const argon2id = "argon2id"
const sha512 = "sha512" const sha512 = "sha512"
const testPassword = "my;secure*password" const testPassword = "my;secure*password"

View File

@ -149,11 +149,13 @@ func (p *FileUserProvider) UpdatePassword(username string, newPassword string) e
} }
var algorithm CryptAlgo var algorithm CryptAlgo
if p.configuration.Password.Algorithm == "argon2id" {
switch p.configuration.Password.Algorithm {
case argon2id:
algorithm = HashingAlgorithmArgon2id algorithm = HashingAlgorithmArgon2id
} else if p.configuration.Password.Algorithm == sha512 { case sha512:
algorithm = HashingAlgorithmSHA512 algorithm = HashingAlgorithmSHA512
} else { default:
return errors.New("Invalid algorithm in configuration. It should be `argon2id` or `sha512`") return errors.New("Invalid algorithm in configuration. It should be `argon2id` or `sha512`")
} }

View File

@ -48,23 +48,26 @@ func ParseHash(hash string) (passwordHash *PasswordHash, err error) {
return nil, errors.New("Salt contains invalid base64 characters") return nil, errors.New("Salt contains invalid base64 characters")
} }
if code == HashingAlgorithmSHA512 { switch code {
case HashingAlgorithmSHA512:
h.Iterations = parameters.GetInt("rounds", HashingDefaultSHA512Iterations) h.Iterations = parameters.GetInt("rounds", HashingDefaultSHA512Iterations)
h.Algorithm = HashingAlgorithmSHA512 h.Algorithm = HashingAlgorithmSHA512
if parameters["rounds"] != "" && parameters["rounds"] != strconv.Itoa(h.Iterations) { if parameters["rounds"] != "" && parameters["rounds"] != strconv.Itoa(h.Iterations) {
return nil, fmt.Errorf("SHA512 iterations is not numeric (%s)", parameters["rounds"]) return nil, fmt.Errorf("SHA512 iterations is not numeric (%s)", parameters["rounds"])
} }
} else if code == HashingAlgorithmArgon2id { case HashingAlgorithmArgon2id:
version := parameters.GetInt("v", 0) version := parameters.GetInt("v", 0)
if version < 19 { if version < 19 {
if version == 0 { if version == 0 {
return nil, fmt.Errorf("Argon2id version parameter not found (%s)", hash) return nil, fmt.Errorf("Argon2id version parameter not found (%s)", hash)
} }
return nil, fmt.Errorf("Argon2id versions less than v19 are not supported (hash is version %d)", version) return nil, fmt.Errorf("Argon2id versions less than v19 are not supported (hash is version %d)", version)
} else if version > 19 { } else if version > 19 {
return nil, fmt.Errorf("Argon2id versions greater than v19 are not supported (hash is version %d)", version) return nil, fmt.Errorf("Argon2id versions greater than v19 are not supported (hash is version %d)", version)
} }
h.Algorithm = HashingAlgorithmArgon2id h.Algorithm = HashingAlgorithmArgon2id
h.Memory = parameters.GetInt("m", HashingDefaultArgon2idMemory) h.Memory = parameters.GetInt("m", HashingDefaultArgon2idMemory)
h.Iterations = parameters.GetInt("t", HashingDefaultArgon2idTime) h.Iterations = parameters.GetInt("t", HashingDefaultArgon2idTime)
@ -72,13 +75,15 @@ func ParseHash(hash string) (passwordHash *PasswordHash, err error) {
h.KeyLength = parameters.GetInt("k", HashingDefaultArgon2idKeyLength) h.KeyLength = parameters.GetInt("k", HashingDefaultArgon2idKeyLength)
decodedKey, err := crypt.Base64Encoding.DecodeString(h.Key) decodedKey, err := crypt.Base64Encoding.DecodeString(h.Key)
if err != nil { if err != nil {
return nil, errors.New("Hash key contains invalid base64 characters") return nil, errors.New("Hash key contains invalid base64 characters")
} }
if len(decodedKey) != h.KeyLength { if len(decodedKey) != h.KeyLength {
return nil, fmt.Errorf("Argon2id key length parameter (%d) does not match the actual key length (%d)", h.KeyLength, len(decodedKey)) return nil, fmt.Errorf("Argon2id key length parameter (%d) does not match the actual key length (%d)", h.KeyLength, len(decodedKey))
} }
} else { default:
return nil, fmt.Errorf("Authelia only supports salted SHA512 hashing ($6$) and salted argon2id ($argon2id$), not $%s$", code) return nil, fmt.Errorf("Authelia only supports salted SHA512 hashing ($6$) and salted argon2id ($argon2id$), not $%s$", code)
} }
@ -159,11 +164,12 @@ func CheckPassword(password, hash string) (ok bool, err error) {
} }
func getCryptSettings(salt string, algorithm CryptAlgo, iterations, memory, parallelism, keyLength int) (settings string) { func getCryptSettings(salt string, algorithm CryptAlgo, iterations, memory, parallelism, keyLength int) (settings string) {
if algorithm == HashingAlgorithmArgon2id { switch algorithm {
case HashingAlgorithmArgon2id:
settings, _ = crypt.Argon2idSettings(memory, iterations, parallelism, keyLength, salt) settings, _ = crypt.Argon2idSettings(memory, iterations, parallelism, keyLength, salt)
} else if algorithm == HashingAlgorithmSHA512 { case HashingAlgorithmSHA512:
settings = fmt.Sprintf("$6$rounds=%d$%s", iterations, salt) settings = fmt.Sprintf("$6$rounds=%d$%s", iterations, salt)
} else { default:
panic("invalid password hashing algorithm provided") panic("invalid password hashing algorithm provided")
} }

View File

@ -36,7 +36,7 @@ func TestShouldHashArgon2idPassword(t *testing.T) {
code, parameters, salt, key, err := crypt.DecodeSettings(hash) code, parameters, salt, key, err := crypt.DecodeSettings(hash)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "argon2id", code) assert.Equal(t, argon2id, code)
assert.Equal(t, "BpLnfgDsc2WD8F2q", salt) assert.Equal(t, "BpLnfgDsc2WD8F2q", salt)
assert.Equal(t, "O126GHPeZ5fwj7OLSs7PndXsTbje76R+QW9/EGfhkJg", key) assert.Equal(t, "O126GHPeZ5fwj7OLSs7PndXsTbje76R+QW9/EGfhkJg", key)
assert.Equal(t, schema.DefaultCIPasswordConfiguration.Iterations, parameters.GetInt("t", HashingDefaultArgon2idTime)) assert.Equal(t, schema.DefaultCIPasswordConfiguration.Iterations, parameters.GetInt("t", HashingDefaultArgon2idTime))

View File

@ -47,13 +47,14 @@ func selectMatchingSubjectRules(rules []schema.ACLRule, subject Subject) []schem
selectedRules := []schema.ACLRule{} selectedRules := []schema.ACLRule{}
for _, rule := range rules { for _, rule := range rules {
if len(rule.Subjects) > 0 { switch {
case len(rule.Subjects) > 0:
for _, subjectRule := range rule.Subjects { for _, subjectRule := range rule.Subjects {
if isSubjectMatching(subject, subjectRule) && isIPMatching(subject.IP, rule.Networks) { if isSubjectMatching(subject, subjectRule) && isIPMatching(subject.IP, rule.Networks) {
selectedRules = append(selectedRules, rule) selectedRules = append(selectedRules, rule)
} }
} }
} else { default:
if isIPMatching(subject.IP, rule.Networks) { if isIPMatching(subject.IP, rule.Networks) {
selectedRules = append(selectedRules, rule) selectedRules = append(selectedRules, rule)
} }

View File

@ -45,7 +45,7 @@ var DefaultPasswordConfiguration = PasswordConfiguration{
Iterations: 1, Iterations: 1,
KeyLength: 32, KeyLength: 32,
SaltLength: 16, SaltLength: 16,
Algorithm: "argon2id", Algorithm: argon2id,
Memory: 1024, Memory: 1024,
Parallelism: 8, Parallelism: 8,
} }
@ -55,7 +55,7 @@ var DefaultCIPasswordConfiguration = PasswordConfiguration{
Iterations: 1, Iterations: 1,
KeyLength: 32, KeyLength: 32,
SaltLength: 16, SaltLength: 16,
Algorithm: "argon2id", Algorithm: argon2id,
Memory: 128, Memory: 128,
Parallelism: 8, Parallelism: 8,
} }

View File

@ -6,6 +6,8 @@ import (
const denyPolicy = "deny" const denyPolicy = "deny"
const argon2id = "argon2id"
// ProfileRefreshDisabled represents a value for refresh_interval that disables the check entirely. // ProfileRefreshDisabled represents a value for refresh_interval that disables the check entirely.
const ProfileRefreshDisabled = "disable" const ProfileRefreshDisabled = "disable"

View File

@ -40,11 +40,12 @@ func validateFileAuthenticationBackend(configuration *schema.FileAuthenticationB
} }
//Salt Length //Salt Length
if configuration.Password.SaltLength == 0 { switch {
case configuration.Password.SaltLength == 0:
configuration.Password.SaltLength = schema.DefaultPasswordConfiguration.SaltLength configuration.Password.SaltLength = schema.DefaultPasswordConfiguration.SaltLength
} else if configuration.Password.SaltLength < 2 { case configuration.Password.SaltLength < 2:
validator.Push(fmt.Errorf("The salt length must be 2 or more, you configured %d", configuration.Password.SaltLength)) validator.Push(fmt.Errorf("The salt length must be 2 or more, you configured %d", configuration.Password.SaltLength))
} else if configuration.Password.SaltLength > 16 { case configuration.Password.SaltLength > 16:
validator.Push(fmt.Errorf("The salt length must be 16 or less, you configured %d", configuration.Password.SaltLength)) validator.Push(fmt.Errorf("The salt length must be 16 or less, you configured %d", configuration.Password.SaltLength))
} }
@ -137,10 +138,8 @@ func validateLdapAuthenticationBackend(configuration *schema.LDAPAuthenticationB
if configuration.GroupsFilter == "" { if configuration.GroupsFilter == "" {
validator.Push(errors.New("Please provide a groups filter with `groups_filter` attribute")) validator.Push(errors.New("Please provide a groups filter with `groups_filter` attribute"))
} else { } else if !strings.HasPrefix(configuration.GroupsFilter, "(") || !strings.HasSuffix(configuration.GroupsFilter, ")") {
if !strings.HasPrefix(configuration.GroupsFilter, "(") || !strings.HasSuffix(configuration.GroupsFilter, ")") { validator.Push(errors.New("The groups filter should contain enclosing parenthesis. For instance cn={input} should be (cn={input})"))
validator.Push(errors.New("The groups filter should contain enclosing parenthesis. For instance cn={input} should be (cn={input})"))
}
} }
if configuration.UsernameAttribute == "" { if configuration.UsernameAttribute == "" {

View File

@ -12,11 +12,12 @@ func ValidateStorage(configuration schema.StorageConfiguration, validator *schem
validator.Push(errors.New("A storage configuration must be provided. It could be 'local', 'mysql' or 'postgres'")) validator.Push(errors.New("A storage configuration must be provided. It could be 'local', 'mysql' or 'postgres'"))
} }
if configuration.MySQL != nil { switch {
case configuration.MySQL != nil:
validateSQLConfiguration(&configuration.MySQL.SQLStorageConfiguration, validator) validateSQLConfiguration(&configuration.MySQL.SQLStorageConfiguration, validator)
} else if configuration.PostgreSQL != nil { case configuration.PostgreSQL != nil:
validatePostgreSQLConfiguration(configuration.PostgreSQL, validator) validatePostgreSQLConfiguration(configuration.PostgreSQL, validator)
} else if configuration.Local != nil { case configuration.Local != nil:
validateLocalStorageConfiguration(configuration.Local, validator) validateLocalStorageConfiguration(configuration.Local, validator)
} }
} }

View File

@ -130,14 +130,15 @@ func IdentityVerificationFinish(args IdentityVerificationFinishArgs, next func(c
if err != nil { if err != nil {
if ve, ok := err.(*jwt.ValidationError); ok { if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 { switch {
case ve.Errors&jwt.ValidationErrorMalformed != 0:
ctx.Error(fmt.Errorf("Cannot parse token"), operationFailedMessage) ctx.Error(fmt.Errorf("Cannot parse token"), operationFailedMessage)
return return
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 { case ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0:
// Token is either expired or not active yet // Token is either expired or not active yet
ctx.Error(fmt.Errorf("Token expired"), identityVerificationTokenHasExpiredMessage) ctx.Error(fmt.Errorf("Token expired"), identityVerificationTokenHasExpiredMessage)
return return
} else { default:
ctx.Error(fmt.Errorf("Cannot handle this token: %s", ve), operationFailedMessage) ctx.Error(fmt.Errorf("Cannot handle this token: %s", ve), operationFailedMessage)
return return
} }

View File

@ -101,8 +101,8 @@ func (n *SMTPNotifier) startTLS() error {
return nil return nil
} }
ok, _ := n.client.Extension("STARTTLS") switch ok, _ := n.client.Extension("STARTTLS"); ok {
if ok { case true:
log.Debugf("Notifier SMTP server supports STARTTLS (disableVerifyCert: %t, ServerName: %s), attempting", n.tlsConfig.InsecureSkipVerify, n.tlsConfig.ServerName) log.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 { if err := n.client.StartTLS(n.tlsConfig); err != nil {
@ -110,10 +110,13 @@ func (n *SMTPNotifier) startTLS() error {
} }
log.Debug("Notifier SMTP STARTTLS completed without error") log.Debug("Notifier SMTP STARTTLS completed without error")
} else if n.disableRequireTLS { default:
log.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)") switch n.disableRequireTLS {
} else { case true:
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)") log.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)")
}
} }
return nil return nil

View File

@ -14,7 +14,9 @@ func ParseDurationString(input string) (time.Duration, error) {
var duration time.Duration var duration time.Duration
matches := parseDurationRegexp.FindStringSubmatch(input) matches := parseDurationRegexp.FindStringSubmatch(input)
if len(matches) == 3 && matches[2] != "" {
switch {
case len(matches) == 3 && matches[2] != "":
d, _ := strconv.Atoi(matches[1]) d, _ := strconv.Atoi(matches[1])
switch matches[2] { switch matches[2] {
@ -33,13 +35,14 @@ func ParseDurationString(input string) (time.Duration, error) {
case "s": case "s":
duration = time.Duration(d) * time.Second duration = time.Duration(d) * time.Second
} }
} else if input == "0" || len(matches) == 3 { case input == "0" || len(matches) == 3:
seconds, err := strconv.Atoi(input) seconds, err := strconv.Atoi(input)
if err != nil { if err != nil {
return 0, fmt.Errorf("Could not convert the input string of %s into a duration: %s", input, err) return 0, fmt.Errorf("Could not convert the input string of %s into a duration: %s", input, err)
} }
duration = time.Duration(seconds) * time.Second duration = time.Duration(seconds) * time.Second
} else if input != "" { case input != "":
// Throw this error if input is anything other than a blank string, blank string will default to a duration of nothing // Throw this error if input is anything other than a blank string, blank string will default to a duration of nothing
return 0, fmt.Errorf("Could not convert the input string of %s into a duration", input) return 0, fmt.Errorf("Could not convert the input string of %s into a duration", input)
} }