This commit is contained in:
		
							
								
								
									
										144
									
								
								service/reg_token_service.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								service/reg_token_service.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2025. Gardel <sunxinao@hotmail.com> and contributors
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package service
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	lru "github.com/hashicorp/golang-lru"
 | 
			
		||||
	"github.com/wneessen/go-mail"
 | 
			
		||||
	"text/template"
 | 
			
		||||
	"yggdrasil-go/model"
 | 
			
		||||
	"yggdrasil-go/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type RegTokenService interface {
 | 
			
		||||
	SendTokenEmail(tokenType RegTokenType, email string) error
 | 
			
		||||
	VerifyToken(accessToken string) (string, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type RegTokenType uint
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	RegisterToken RegTokenType = iota
 | 
			
		||||
	ResetPasswordToken
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SmtpConfig struct {
 | 
			
		||||
	SmtpServer            string
 | 
			
		||||
	SmtpPort              int
 | 
			
		||||
	SmtpSsl               bool
 | 
			
		||||
	EmailFrom             string
 | 
			
		||||
	SmtpUser              string
 | 
			
		||||
	SmtpPassword          string
 | 
			
		||||
	TitlePrefix           string
 | 
			
		||||
	RegisterTemplate      string
 | 
			
		||||
	ResetPasswordTemplate string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type regTokenServiceImpl struct {
 | 
			
		||||
	tokenCache            *lru.Cache
 | 
			
		||||
	smtpServer            string
 | 
			
		||||
	smtpPort              int
 | 
			
		||||
	smtpSsl               bool
 | 
			
		||||
	smtpUser              string
 | 
			
		||||
	smtpPassword          string
 | 
			
		||||
	emailFrom             string
 | 
			
		||||
	titlePrefix           string
 | 
			
		||||
	registerTemplate      *template.Template
 | 
			
		||||
	resetPasswordTemplate *template.Template
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewRegTokenService(smtpCfg *SmtpConfig) RegTokenService {
 | 
			
		||||
	cache, _ := lru.New(10000000)
 | 
			
		||||
	impl := ®TokenServiceImpl{
 | 
			
		||||
		tokenCache:            cache,
 | 
			
		||||
		smtpServer:            smtpCfg.SmtpServer,
 | 
			
		||||
		smtpPort:              smtpCfg.SmtpPort,
 | 
			
		||||
		smtpSsl:               smtpCfg.SmtpSsl,
 | 
			
		||||
		smtpUser:              smtpCfg.SmtpUser,
 | 
			
		||||
		smtpPassword:          smtpCfg.SmtpPassword,
 | 
			
		||||
		emailFrom:             smtpCfg.EmailFrom,
 | 
			
		||||
		titlePrefix:           smtpCfg.TitlePrefix,
 | 
			
		||||
		registerTemplate:      template.Must(template.New("register").Parse(smtpCfg.RegisterTemplate)),
 | 
			
		||||
		resetPasswordTemplate: template.Must(template.New("resetPassword").Parse(smtpCfg.ResetPasswordTemplate)),
 | 
			
		||||
	}
 | 
			
		||||
	return impl
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *regTokenServiceImpl) SendTokenEmail(tokenType RegTokenType, email string) error {
 | 
			
		||||
	token := model.NewRegToken(email)
 | 
			
		||||
	r.tokenCache.Add(token.AccessToken, token)
 | 
			
		||||
 | 
			
		||||
	var subject, body string
 | 
			
		||||
	buf := bytes.Buffer{}
 | 
			
		||||
	switch tokenType {
 | 
			
		||||
	case RegisterToken:
 | 
			
		||||
		subject = fmt.Sprintf("%s 注册验证码", r.titlePrefix)
 | 
			
		||||
		if err := r.registerTemplate.Execute(&buf, token); err != nil {
 | 
			
		||||
			return fmt.Errorf("execute registerTemplate error: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		body = buf.String()
 | 
			
		||||
	case ResetPasswordToken:
 | 
			
		||||
		subject = fmt.Sprintf("%s 重置密码验证码", r.titlePrefix)
 | 
			
		||||
		if err := r.resetPasswordTemplate.Execute(&buf, token); err != nil {
 | 
			
		||||
			return fmt.Errorf("execute resetPasswordTemplate error: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		body = buf.String()
 | 
			
		||||
	default:
 | 
			
		||||
		return fmt.Errorf("unknown token type")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	message := mail.NewMsg()
 | 
			
		||||
	if err := message.From(r.emailFrom); err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to set From address: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if err := message.To(email); err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to set To address: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	message.Subject(subject)
 | 
			
		||||
	message.SetBodyString(mail.TypeTextHTML, body)
 | 
			
		||||
	client, err := mail.NewClient(r.smtpServer, mail.WithPort(r.smtpPort), mail.WithSMTPAuth(mail.SMTPAuthPlain),
 | 
			
		||||
		mail.WithUsername(r.smtpUser), mail.WithPassword(r.smtpPassword))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to create mail client: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if r.smtpSsl {
 | 
			
		||||
		client.SetSSL(true)
 | 
			
		||||
	}
 | 
			
		||||
	if err := client.DialAndSend(message); err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to send mail: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *regTokenServiceImpl) VerifyToken(accessToken string) (string, error) {
 | 
			
		||||
	token, ok := r.tokenCache.Get(accessToken)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return "", util.NewIllegalArgumentError(util.MessageInvalidToken)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if regToken, ok := token.(model.RegToken); ok {
 | 
			
		||||
		if regToken.IsValid() {
 | 
			
		||||
			r.tokenCache.Remove(accessToken)
 | 
			
		||||
			return regToken.Email, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", util.NewIllegalArgumentError("wrong access token or email")
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (C) 2022. Gardel <sunxinao@hotmail.com> and contributors
 | 
			
		||||
 * Copyright (C) 2022-2025. Gardel <sunxinao@hotmail.com> and contributors
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
@@ -39,7 +39,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type UserService interface {
 | 
			
		||||
	Register(username string, password string, profileName string) (*model.UserResponse, error)
 | 
			
		||||
	Register(username, password, profileName, ip string) (*model.UserResponse, error)
 | 
			
		||||
	Login(username string, password string, clientToken *string, requestUser bool) (*LoginResponse, error)
 | 
			
		||||
	ChangeProfile(accessToken string, clientToken *string, changeTo string) error
 | 
			
		||||
	Refresh(accessToken string, clientToken *string, requestUser bool, selectedProfile *model.ProfileResponse) (*LoginResponse, error)
 | 
			
		||||
@@ -47,9 +47,13 @@ type UserService interface {
 | 
			
		||||
	Invalidate(accessToken string) error
 | 
			
		||||
	Signout(username string, password string) error
 | 
			
		||||
	UsernameToUUID(username string) (*model.ProfileResponse, error)
 | 
			
		||||
	UUIDToUUID(profileId uuid.UUID) (*model.ProfileResponse, error)
 | 
			
		||||
	QueryUUIDs(usernames []string) ([]model.ProfileResponse, error)
 | 
			
		||||
	QueryProfile(profileId uuid.UUID, unsigned bool, textureBaseUrl string) (map[string]interface{}, error)
 | 
			
		||||
	ProfileKey(accessToken string) (*ProfileKeyResponse, error)
 | 
			
		||||
	SendEmail(email string, tokenType RegTokenType, ip string) error
 | 
			
		||||
	VerifyEmail(accessToken string) error
 | 
			
		||||
	ResetPassword(email string, password string, accessToken string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type LoginResponse struct {
 | 
			
		||||
@@ -75,18 +79,20 @@ type ProfileKeyPair struct {
 | 
			
		||||
 | 
			
		||||
type userServiceImpl struct {
 | 
			
		||||
	tokenService    TokenService
 | 
			
		||||
	regTokenService RegTokenService
 | 
			
		||||
	db              *gorm.DB
 | 
			
		||||
	limitLruCache   *lru.Cache
 | 
			
		||||
	profileKeyCache *lru.Cache
 | 
			
		||||
	keyPairCh       chan ProfileKeyPair
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewUserService(tokenService TokenService, db *gorm.DB) UserService {
 | 
			
		||||
func NewUserService(tokenService TokenService, regTokenService RegTokenService, db *gorm.DB) UserService {
 | 
			
		||||
	cache0, _ := lru.New(10000)
 | 
			
		||||
	cache1, _ := lru.New(10000)
 | 
			
		||||
	ch := make(chan ProfileKeyPair, 100)
 | 
			
		||||
	userService := userServiceImpl{
 | 
			
		||||
		tokenService:    tokenService,
 | 
			
		||||
		regTokenService: regTokenService,
 | 
			
		||||
		db:              db,
 | 
			
		||||
		limitLruCache:   cache0,
 | 
			
		||||
		profileKeyCache: cache1,
 | 
			
		||||
@@ -96,7 +102,7 @@ func NewUserService(tokenService TokenService, db *gorm.DB) UserService {
 | 
			
		||||
	return &userService
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *userServiceImpl) Register(username string, password string, profileName string) (*model.UserResponse, error) {
 | 
			
		||||
func (u *userServiceImpl) Register(username, password, profileName, ip string) (*model.UserResponse, error) {
 | 
			
		||||
	var count int64
 | 
			
		||||
	if err := u.db.Table("users").Where("email = ?", username).Count(&count).Error; err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -134,6 +140,7 @@ func (u *userServiceImpl) Register(username string, password string, profileName
 | 
			
		||||
	if err := u.db.Create(&user).Error; err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	_ = u.SendEmail(user.Email, RegisterToken, ip)
 | 
			
		||||
	response := user.ToResponse()
 | 
			
		||||
	return &response, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -155,6 +162,9 @@ func (u *userServiceImpl) Login(username string, password string, clientToken *s
 | 
			
		||||
	user := model.User{}
 | 
			
		||||
	if err := u.db.Where("email = ?", username).First(&user).Error; err == nil {
 | 
			
		||||
		if bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) == nil {
 | 
			
		||||
			if !user.EmailVerified {
 | 
			
		||||
				return nil, util.NewForbiddenOperationError("Email not verified")
 | 
			
		||||
			}
 | 
			
		||||
			var useClientToken string
 | 
			
		||||
			if clientToken == nil || *clientToken == "" {
 | 
			
		||||
				useClientToken = util.RandomUUID()
 | 
			
		||||
@@ -252,56 +262,24 @@ func (u *userServiceImpl) Refresh(accessToken string, clientToken *string, reque
 | 
			
		||||
		}
 | 
			
		||||
		return &response, nil
 | 
			
		||||
	} else {
 | 
			
		||||
		data := map[string]interface{}{
 | 
			
		||||
			"accessToken":     accessToken,
 | 
			
		||||
			"clientToken":     clientToken,
 | 
			
		||||
			"requestUser":     requestUser,
 | 
			
		||||
			"selectedProfile": selectedProfile,
 | 
			
		||||
		}
 | 
			
		||||
		loginResponse := LoginResponse{}
 | 
			
		||||
		err := util.PostObject("https://authserver.mojang.com/refresh", data, &loginResponse)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		} else {
 | 
			
		||||
			return &loginResponse, nil
 | 
			
		||||
		}
 | 
			
		||||
		return nil, util.NewForbiddenOperationError(util.MessageInvalidToken)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *userServiceImpl) Validate(accessToken string, clientToken *string) error {
 | 
			
		||||
	if len(accessToken) <= 36 {
 | 
			
		||||
		if u.tokenService.VerifyToken(accessToken, clientToken) != model.Valid {
 | 
			
		||||
			return util.NewForbiddenOperationError(util.MessageInvalidToken)
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	if len(accessToken) <= 36 && u.tokenService.VerifyToken(accessToken, clientToken) == model.Valid {
 | 
			
		||||
		return nil
 | 
			
		||||
	} else {
 | 
			
		||||
		data := map[string]interface{}{
 | 
			
		||||
			"accessToken": accessToken,
 | 
			
		||||
			"clientToken": clientToken,
 | 
			
		||||
		}
 | 
			
		||||
		err := util.PostObjectForError("https://authserver.mojang.com/validate", data)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		return util.NewForbiddenOperationError(util.MessageInvalidToken)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *userServiceImpl) Invalidate(accessToken string) error {
 | 
			
		||||
	if len(accessToken) <= 36 {
 | 
			
		||||
		u.tokenService.RemoveAccessToken(accessToken)
 | 
			
		||||
	} else {
 | 
			
		||||
		data := map[string]interface{}{
 | 
			
		||||
			"accessToken": accessToken,
 | 
			
		||||
		}
 | 
			
		||||
		err := util.PostObjectForError("https://authserver.mojang.com/invalidate", data)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return util.NewForbiddenOperationError(util.MessageInvalidToken)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *userServiceImpl) Signout(username string, password string) error {
 | 
			
		||||
@@ -317,21 +295,9 @@ func (u *userServiceImpl) Signout(username string, password string) error {
 | 
			
		||||
		if bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) == nil {
 | 
			
		||||
			u.tokenService.RemoveAll(user.ID)
 | 
			
		||||
			return nil
 | 
			
		||||
		} else {
 | 
			
		||||
			return util.NewForbiddenOperationError(util.MessageInvalidCredentials)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		data := map[string]interface{}{
 | 
			
		||||
			"username": username,
 | 
			
		||||
			"password": password,
 | 
			
		||||
		}
 | 
			
		||||
		err := util.PostObjectForError("https://authserver.mojang.com/signout", data)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return util.NewForbiddenOperationError(util.MessageInvalidCredentials)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *userServiceImpl) UsernameToUUID(username string) (*model.ProfileResponse, error) {
 | 
			
		||||
@@ -351,6 +317,23 @@ func (u *userServiceImpl) UsernameToUUID(username string) (*model.ProfileRespons
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *userServiceImpl) UUIDToUUID(profileId uuid.UUID) (*model.ProfileResponse, error) {
 | 
			
		||||
	user := model.User{}
 | 
			
		||||
	if result := u.db.First(&user, profileId); result.Error == nil {
 | 
			
		||||
		return &model.ProfileResponse{
 | 
			
		||||
			Name: user.ProfileName,
 | 
			
		||||
			Id:   util.UnsignedString(user.ID),
 | 
			
		||||
		}, nil
 | 
			
		||||
	} else {
 | 
			
		||||
		response, err := mojangUUIDToUUID(util.UnsignedString(profileId))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil
 | 
			
		||||
		} else {
 | 
			
		||||
			return &response, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *userServiceImpl) QueryUUIDs(usernames []string) ([]model.ProfileResponse, error) {
 | 
			
		||||
	var users []model.User
 | 
			
		||||
	var names []string
 | 
			
		||||
@@ -359,13 +342,27 @@ func (u *userServiceImpl) QueryUUIDs(usernames []string) ([]model.ProfileRespons
 | 
			
		||||
	} else {
 | 
			
		||||
		names = usernames
 | 
			
		||||
	}
 | 
			
		||||
	var responses = make([]model.ProfileResponse, 0)
 | 
			
		||||
	responses := make([]model.ProfileResponse, 0)
 | 
			
		||||
	notFoundUsers := make([]string, 0)
 | 
			
		||||
	foundUsernames := make(map[string]bool)
 | 
			
		||||
	if err := u.db.Table("users").Where("profile_name in ?", names).Find(&users).Error; err == nil {
 | 
			
		||||
		for _, user := range users {
 | 
			
		||||
			responses = append(responses, model.ProfileResponse{
 | 
			
		||||
				Name: user.ProfileName,
 | 
			
		||||
				Id:   util.UnsignedString(user.ID),
 | 
			
		||||
			})
 | 
			
		||||
			foundUsernames[user.ProfileName] = true
 | 
			
		||||
		}
 | 
			
		||||
		for _, name := range names {
 | 
			
		||||
			if !foundUsernames[name] {
 | 
			
		||||
				notFoundUsers = append(notFoundUsers, name)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(notFoundUsers) > 0 {
 | 
			
		||||
		mojangResponses, _ := mojangUsernamesToUUIDs(notFoundUsers)
 | 
			
		||||
		for _, resp := range mojangResponses {
 | 
			
		||||
			responses = append(responses, resp)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return responses, nil
 | 
			
		||||
@@ -423,6 +420,66 @@ func (u *userServiceImpl) ProfileKey(accessToken string) (resp *ProfileKeyRespon
 | 
			
		||||
	return resp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *userServiceImpl) SendEmail(email string, tokenType RegTokenType, ip string) error {
 | 
			
		||||
	if !u.allowEmail("ip:"+ip) || !u.allowEmail("email:"+email) {
 | 
			
		||||
		return util.YggdrasilError{
 | 
			
		||||
			Status:       http.StatusTooManyRequests,
 | 
			
		||||
			ErrorCode:    "ForbiddenOperationException",
 | 
			
		||||
			ErrorMessage: "Forbidden",
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	var count int64
 | 
			
		||||
	if err := u.db.Table("users").Where("email = ?", email).Count(&count).Error; err != nil {
 | 
			
		||||
		return util.NewIllegalArgumentError(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	if count == 0 {
 | 
			
		||||
		return util.NewForbiddenOperationError("user not found")
 | 
			
		||||
	}
 | 
			
		||||
	return u.regTokenService.SendTokenEmail(tokenType, email)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *userServiceImpl) VerifyEmail(accessToken string) error {
 | 
			
		||||
	email, err := u.regTokenService.VerifyToken(accessToken)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	user := model.User{}
 | 
			
		||||
	err = u.db.Where("email = ?", email).First(&user).Error
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return util.NewIllegalArgumentError("user not found")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	user.EmailVerified = true
 | 
			
		||||
	return u.db.Model(&user).Update("email_verified", user.EmailVerified).Error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *userServiceImpl) ResetPassword(email string, password string, accessToken string) error {
 | 
			
		||||
	user := model.User{}
 | 
			
		||||
	err := u.db.Where("email = ?", email).First(&user).Error
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return util.NewIllegalArgumentError("user not found")
 | 
			
		||||
	}
 | 
			
		||||
	tokenEmail, err := u.regTokenService.VerifyToken(accessToken)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if tokenEmail != email {
 | 
			
		||||
		return util.NewIllegalArgumentError("email invalid")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(password) < 6 {
 | 
			
		||||
		return util.NewIllegalArgumentError("bad format(password longer than 5)")
 | 
			
		||||
	}
 | 
			
		||||
	hashedPass, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
 | 
			
		||||
	user.Password = string(hashedPass)
 | 
			
		||||
	user.EmailVerified = true
 | 
			
		||||
	return u.db.Model(&user).Updates(model.User{
 | 
			
		||||
		EmailVerified: user.EmailVerified,
 | 
			
		||||
		Password:      user.Password,
 | 
			
		||||
	}).Error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *userServiceImpl) allowUser(username string) bool {
 | 
			
		||||
	if value, ok := u.limitLruCache.Get(username); ok {
 | 
			
		||||
		if limiter, ok := value.(*rate.Limiter); ok {
 | 
			
		||||
@@ -437,6 +494,20 @@ func (u *userServiceImpl) allowUser(username string) bool {
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *userServiceImpl) allowEmail(key string) bool {
 | 
			
		||||
	if value, ok := u.limitLruCache.Get(key); ok {
 | 
			
		||||
		if limiter, ok := value.(*rate.Limiter); ok {
 | 
			
		||||
			return limiter.Allow()
 | 
			
		||||
		} else {
 | 
			
		||||
			u.limitLruCache.Remove(key)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		limiter := rate.NewLimiter(0.02, 1)
 | 
			
		||||
		u.limitLruCache.Add(key, limiter)
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u *userServiceImpl) getProfileKey(profileId uuid.UUID) (*ProfileKeyPair, error) {
 | 
			
		||||
	if value, ok := u.profileKeyCache.Get(profileId); ok {
 | 
			
		||||
		if keyPair, ok := value.(*ProfileKeyPair); ok {
 | 
			
		||||
@@ -483,7 +554,7 @@ func (u *userServiceImpl) genKeyPair() {
 | 
			
		||||
 | 
			
		||||
func mojangUsernameToUUID(username string) (model.ProfileResponse, error) {
 | 
			
		||||
	response := model.ProfileResponse{}
 | 
			
		||||
	reqUrl := fmt.Sprintf("https://api.mojang.com/users/profiles/minecraft/%s", url.PathEscape(username))
 | 
			
		||||
	reqUrl := fmt.Sprintf("https://api.minecraftservices.com/minecraft/profile/lookup/name/%s", url.PathEscape(username))
 | 
			
		||||
	err := util.GetObject(reqUrl, &response)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return response, err
 | 
			
		||||
@@ -491,3 +562,24 @@ func mojangUsernameToUUID(username string) (model.ProfileResponse, error) {
 | 
			
		||||
		return response, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func mojangUUIDToUUID(uid string) (model.ProfileResponse, error) {
 | 
			
		||||
	response := model.ProfileResponse{}
 | 
			
		||||
	reqUrl := fmt.Sprintf("https://api.minecraftservices.com/minecraft/profile/lookup/%s", uid)
 | 
			
		||||
	err := util.GetObject(reqUrl, &response)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return response, err
 | 
			
		||||
	} else {
 | 
			
		||||
		return response, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func mojangUsernamesToUUIDs(username []string) ([]model.ProfileResponse, error) {
 | 
			
		||||
	response := make([]model.ProfileResponse, 0)
 | 
			
		||||
	err := util.PostObject("https://api.minecraftservices.com/minecraft/profile/lookup/bulk/byname", username, &response)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return response, err
 | 
			
		||||
	} else {
 | 
			
		||||
		return response, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user