Files
ttstd-infinite-canvas/backend/internal/auth/verification_policy.go

98 lines
3.0 KiB
Go

package auth
import (
"encoding/json"
"errors"
"gorm.io/gorm"
"yingce/backend/internal/kernel"
"yingce/backend/internal/model"
)
const authPolicyKey = "auth_verification_policy"
type VerificationPolicy struct {
SMSLogin bool `json:"smsLogin"`
EmailLogin bool `json:"emailLogin"`
SMSRegistration bool `json:"smsRegistration"`
EmailRegistration bool `json:"emailRegistration"`
SMSAndEmailRegistration bool `json:"smsAndEmailRegistration"`
}
func (s *Service) verificationPolicy() (VerificationPolicy, error) {
setting, err := s.repo.SystemSetting(authPolicyKey)
if errors.Is(err, gorm.ErrRecordNotFound) {
return VerificationPolicy{EmailRegistration: true}, nil
}
if err != nil {
return VerificationPolicy{}, err
}
var p VerificationPolicy
if json.Unmarshal([]byte(setting.ValueJSON), &p) != nil {
return p, kernel.NewAppError(503, "登录注册策略格式无效")
}
return p, nil
}
func (s *Service) AdminVerificationPolicy(actor *model.User) (*VerificationPolicy, error) {
if err := s.host.RequireAdmin(actor); err != nil {
return nil, err
}
p, err := s.verificationPolicy()
return &p, err
}
func (s *Service) UpdateVerificationPolicy(actor *model.User, p VerificationPolicy) (*VerificationPolicy, error) {
if err := s.host.RequireAdmin(actor); err != nil {
return nil, err
}
if p.SMSAndEmailRegistration && (p.SMSRegistration || p.EmailRegistration) {
return nil, kernel.BadAuthRequest("短信+邮箱同时验证不能与单一验证注册同时开启")
}
if p.EmailLogin || p.EmailRegistration || p.SMSAndEmailRegistration {
enabled, err := s.EmailEnabled()
if err != nil {
return nil, err
}
if !enabled {
return nil, kernel.BadAuthRequest("请先配置并开启邮件服务")
}
}
for scene, required := range map[string]bool{"login": p.SMSLogin, "register": p.SMSRegistration || p.SMSAndEmailRegistration} {
if !required {
continue
}
enabled, err := s.smsAvailable(scene)
if err != nil {
return nil, err
}
if !enabled {
return nil, kernel.BadAuthRequest("请先启用短信插件并配置对应场景的短信渠道:" + scene)
}
}
encoded, _ := json.Marshal(p)
if err := s.repo.SaveSystemSetting(&model.SystemSetting{Key: authPolicyKey, ValueJSON: string(encoded), UpdatedBy: actor.ID}); err != nil {
return nil, err
}
return &p, nil
}
func (s *Service) smsAvailable(scene string) (bool, error) {
if s.sms == nil {
return false, nil
}
return s.sms.Available(scene)
}
func (p VerificationPolicy) allows(purpose, method string) bool {
if purpose == "bind" {
return method == "sms" || method == "email"
}
if purpose == "login" {
return (method == "sms" && p.SMSLogin) || (method == "email" && p.EmailLogin)
}
if purpose == "register" {
if p.SMSAndEmailRegistration {
return method == "sms_email"
}
return (method == "sms" && p.SMSRegistration) || (method == "email" && p.EmailRegistration)
}
return false
}
func (p VerificationPolicy) hash() string { raw, _ := json.Marshal(p); return HashToken(string(raw)) }