package miniauth import ( "errors" "net/mail" "regexp" "git.keks.cloud/kekskurse/miniauth/pkg/smtpclient" "git.keks.cloud/kekskurse/miniauth/pkg/userstore" "git.keks.cloud/kekskurse/miniauth/pkg/utils" "github.com/go-passwd/validator" "github.com/rs/zerolog" "github.com/rs/zerolog/log" ) type MiniauthConfig struct { LoginWithNotApprovedMail bool `env:"LOGIN_WITH_NOT_APPROVED_MAIL"` } type Miniauth struct { store userstore.Store log zerolog.Logger smtp smtpclient.SMTPClient config MiniauthConfig } type User struct { userstore.User } func NewMiniauth(conf MiniauthConfig, us userstore.Store, sm smtpclient.SMTPClient) Miniauth { m := Miniauth{} m.store = us m.smtp = sm m.config = conf m.log = log.With().Str("pkg", "miniauth").Logger() return m } func (m Miniauth) RegisterUser(username string, email string, password string) error { log := m.log.With().Str("func", "RegisterUser").Str("username", username).Str("mail", email).Logger() err := m.checkUsernameForRegistration(username) if err != nil { return utils.WrapError(ErrUserHasInvalideChars, err, log) } err = m.checkPasswordForRegistration(password) if err != nil { return utils.WrapError(ErrPasswordNotComplexEnough, err, log) } _, err = mail.ParseAddress(email) if err != nil { return utils.WrapError(ErrMailAddressInvalide, err, log) } used, err := m.store.MailCheckExists(email) if err != nil { return utils.WrapError(ErrMailAddressInvalide, err, log) } if used { return utils.WrapError(ErrMailAddressInvalide, errors.New("email alreadys used"), log) } id, err := m.store.UserWrite(username, password) if err != nil { return utils.WrapError(ErrCantCreateUser, err, log) } log = log.With().Int64("userid", id).Logger() err = m.store.MailAdd(email, id, true) if err != nil { return utils.WrapError(ErrCantCreateUser, err, log) } err = m.smtp.SendMail("register", email, map[string]any{"hash": "abc"}) if err != nil { m.store.MailRemove(email) m.store.UserDelete(username) return utils.WrapError(ErrCantSendMail, err, log) } return nil } func (m Miniauth) UserLogin(username, password string) (User, error) { log := m.log.With().Str("func", "RegisterUser").Str("username", username).Logger() userdata, err := m.store.UserCheckPassword(username, password) if err != nil { return User{}, utils.WrapError(ErrLoginFailed, err, log) } if !m.config.LoginWithNotApprovedMail { mail, err := m.store.MailGetPrimaryForUsername(username) if err != nil { return User{}, utils.WrapError(ErrLoginFailed, err, log) } if !mail.IsValidated { return User{}, utils.WrapError(ErrLoginFailed, errors.New("mail is not validated"), log) } } u := User{} u.User = userdata return u, nil } func (m Miniauth) checkPasswordForRegistration(password string) error { passwordValidator := validator.New( validator.MinLength(15, nil), validator.ContainsAtLeast("abcdefghijklmnopqrstuvwxyz", 5, nil), validator.ContainsAtLeast("0123456789", 3, nil), validator.ContainsAtLeast(",;.:-_#+*?=}])[({/&%$ยง!<>|", 3, nil)) err := passwordValidator.Validate(password) return err } func (m Miniauth) checkUsernameForRegistration(username string) error { reg := regexp.MustCompile("^[a-z0-9]{4,25}$") if !reg.MatchString(username) { return ErrUserHasInvalideChars } exists, err := m.store.UserExists(username) if err != nil { return err } if exists { return ErrUsernameIsTaken } return nil }