mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-03-12 00:12:37 +01:00
## Description This addresses Issue #5379. The email validation was extended. Additionally to checking whether the email domain is in the block list or in the allow list now we also check if the email domain is the servers own FQDN. Tests have been written for the correct function of the allow list and if the local FQDN is admitted as email domain. Edit: Clarifications, Typos ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. ### Documentation - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [x] I want the title to show in the release notes with a link to this pull request. Co-authored-by: Michael Jerger <michael.jerger@meissa-gmbh.de> Co-authored-by: patdyn <erik.seiert@meissa-gmbh.de> Co-authored-by: Mirco <mirco.zachmann@meissa.de> Co-authored-by: jerger <jerger@noreply.codeberg.org> Co-authored-by: zam <mirco.zachmann@meissa.de> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/5393 Reviewed-by: jerger <jerger@noreply.codeberg.org> Reviewed-by: Gusted <gusted@noreply.codeberg.org> Co-authored-by: patdyn <patdyn@noreply.codeberg.org> Co-committed-by: patdyn <patdyn@noreply.codeberg.org>
146 lines
3.5 KiB
Go
146 lines
3.5 KiB
Go
// Copyright 2016 The Gogs Authors. All rights reserved.
|
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
// Copyright 2024 The Forgejo Authors. All rights reserved
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package validation
|
|
|
|
import (
|
|
"fmt"
|
|
"net/mail"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"code.gitea.io/gitea/modules/setting"
|
|
"code.gitea.io/gitea/modules/util"
|
|
|
|
"github.com/gobwas/glob"
|
|
)
|
|
|
|
// ErrEmailNotActivated e-mail address has not been activated error
|
|
var ErrEmailNotActivated = util.NewInvalidArgumentErrorf("e-mail address has not been activated")
|
|
|
|
// ErrEmailCharIsNotSupported e-mail address contains unsupported character
|
|
type ErrEmailCharIsNotSupported struct {
|
|
Email string
|
|
}
|
|
|
|
// IsErrEmailCharIsNotSupported checks if an error is an ErrEmailCharIsNotSupported
|
|
func IsErrEmailCharIsNotSupported(err error) bool {
|
|
_, ok := err.(ErrEmailCharIsNotSupported)
|
|
return ok
|
|
}
|
|
|
|
func (err ErrEmailCharIsNotSupported) Error() string {
|
|
return fmt.Sprintf("e-mail address contains unsupported character [email: %s]", err.Email)
|
|
}
|
|
|
|
// ErrEmailInvalid represents an error where the email address does not comply with RFC 5322
|
|
// or has a leading '-' character
|
|
type ErrEmailInvalid struct {
|
|
Email string
|
|
}
|
|
|
|
// IsErrEmailInvalid checks if an error is an ErrEmailInvalid
|
|
func IsErrEmailInvalid(err error) bool {
|
|
_, ok := err.(ErrEmailInvalid)
|
|
return ok
|
|
}
|
|
|
|
func (err ErrEmailInvalid) Error() string {
|
|
return fmt.Sprintf("e-mail invalid [email: %s]", err.Email)
|
|
}
|
|
|
|
func (err ErrEmailInvalid) Unwrap() error {
|
|
return util.ErrInvalidArgument
|
|
}
|
|
|
|
var emailRegexp = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
|
|
|
|
// check if email is a valid address with allowed domain
|
|
func ValidateEmail(email string) error {
|
|
if err := validateEmailBasic(email); err != nil {
|
|
return err
|
|
}
|
|
return validateEmailDomain(email)
|
|
}
|
|
|
|
// check if email is a valid address when admins manually add or edit users
|
|
func ValidateEmailForAdmin(email string) error {
|
|
return validateEmailBasic(email)
|
|
// In this case we do not need to check the email domain
|
|
}
|
|
|
|
// validateEmailBasic checks whether the email complies with the rules
|
|
func validateEmailBasic(email string) error {
|
|
if len(email) == 0 {
|
|
return ErrEmailInvalid{email}
|
|
}
|
|
|
|
if !emailRegexp.MatchString(email) {
|
|
return ErrEmailCharIsNotSupported{email}
|
|
}
|
|
|
|
if email[0] == '-' {
|
|
return ErrEmailInvalid{email}
|
|
}
|
|
|
|
if _, err := mail.ParseAddress(email); err != nil {
|
|
return ErrEmailInvalid{email}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateEmailDomain(email string) error {
|
|
if !IsEmailDomainAllowed(email) {
|
|
return ErrEmailInvalid{email}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func IsEmailDomainAllowed(email string) bool {
|
|
return isEmailDomainAllowedInternal(
|
|
email,
|
|
setting.Service.EmailDomainAllowList,
|
|
setting.Service.EmailDomainBlockList)
|
|
}
|
|
|
|
func isEmailDomainAllowedInternal(
|
|
email string,
|
|
emailDomainAllowList []glob.Glob,
|
|
emailDomainBlockList []glob.Glob,
|
|
) bool {
|
|
var result bool
|
|
|
|
if len(emailDomainAllowList) == 0 {
|
|
result = !isEmailDomainListed(emailDomainBlockList, email)
|
|
} else {
|
|
result = isEmailDomainListed(emailDomainAllowList, email)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// isEmailDomainListed checks whether the domain of an email address
|
|
// matches a list of domains
|
|
func isEmailDomainListed(globs []glob.Glob, email string) bool {
|
|
if len(globs) == 0 {
|
|
return false
|
|
}
|
|
|
|
n := strings.LastIndex(email, "@")
|
|
if n <= 0 {
|
|
return false
|
|
}
|
|
|
|
domain := strings.ToLower(email[n+1:])
|
|
|
|
for _, g := range globs {
|
|
if g.Match(domain) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|