casdoor/object/verification.go

242 lines
6.0 KiB
Go
Raw Normal View History

2022-02-13 23:39:27 +08:00
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"errors"
"fmt"
"math/rand"
"strings"
"time"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n"
2022-01-20 14:11:46 +08:00
"github.com/casdoor/casdoor/util"
feat: app session control and db migrate (#1539) * feat: integrate application session management into Casdoor's session management (#774) && standardized the database migration process (#1533) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) && standardized the database migration process * feat: integrate application session management into Casdoor's session management (#774) && standardized the database migration process * feat: integrate application session management into Casdoor's session management (#774) && standardized the database migration process --------- Co-authored-by: Zayn Xie <84443886+xiaoniuren99@users.noreply.github.com> * fix: migrate err * fix: migrate err * feat: app session control and db migrate * feat: app session control and db migrate * feat: app session control and db migrate --------- Co-authored-by: Zayn Xie <84443886+xiaoniuren99@users.noreply.github.com>
2023-02-12 09:33:24 +08:00
"github.com/xorm-io/core"
)
type VerifyResult struct {
Code int
Msg string
}
const (
VerificationSuccess = iota
wrongCodeError
noRecordError
timeoutError
)
const (
VerifyTypePhone = "phone"
VerifyTypeEmail = "email"
)
type VerificationRecord struct {
2021-05-24 01:27:03 +08:00
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
RemoteAddr string `xorm:"varchar(100)"`
Type string `xorm:"varchar(10)"`
2021-05-24 01:18:21 +08:00
User string `xorm:"varchar(100) notnull"`
2021-05-24 01:02:38 +08:00
Provider string `xorm:"varchar(100) notnull"`
Receiver string `xorm:"varchar(100) notnull"`
Code string `xorm:"varchar(10) notnull"`
Time int64 `xorm:"notnull"`
IsUsed bool
}
func IsAllowSend(user *User, remoteAddr, recordType string) error {
var record VerificationRecord
record.RemoteAddr = remoteAddr
record.Type = recordType
if user != nil {
record.User = user.GetId()
}
has, err := ormer.Engine.Desc("created_time").Get(&record)
if err != nil {
return err
}
now := time.Now().Unix()
if has && now-record.Time < 60 {
return errors.New("you can only send one code in 60s")
}
return nil
}
func SendVerificationCodeToEmail(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
2021-05-24 01:02:38 +08:00
if provider == nil {
2022-08-09 16:50:49 +08:00
return fmt.Errorf("please set an Email provider first")
2021-05-24 01:02:38 +08:00
}
2021-05-24 20:21:41 +08:00
sender := organization.DisplayName
title := provider.Title
2022-09-04 12:15:07 +08:00
code := getRandomCode(6)
2021-05-24 20:21:41 +08:00
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
content := fmt.Sprintf(provider.Content, code)
if err := IsAllowSend(user, remoteAddr, provider.Category); err != nil {
return err
}
if err := SendEmail(provider, title, content, dest, sender); err != nil {
return err
}
if err := AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code); err != nil {
return err
}
return nil
}
func SendVerificationCodeToPhone(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
2021-05-24 01:02:38 +08:00
if provider == nil {
2022-08-09 16:50:49 +08:00
return errors.New("please set a SMS provider first")
2021-05-24 01:02:38 +08:00
}
if err := IsAllowSend(user, remoteAddr, provider.Category); err != nil {
return err
}
2022-09-04 12:15:07 +08:00
code := getRandomCode(6)
if err := SendSms(provider, code, dest); err != nil {
return err
}
if err := AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code); err != nil {
return err
}
return nil
}
func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordType, dest, code string) error {
var record VerificationRecord
record.RemoteAddr = remoteAddr
record.Type = recordType
2021-05-24 01:18:21 +08:00
if user != nil {
record.User = user.GetId()
}
2021-05-24 01:27:03 +08:00
record.Owner = provider.Owner
record.Name = util.GenerateId()
record.CreatedTime = util.GetCurrentTime()
record.Provider = provider.Name
record.Receiver = dest
record.Code = code
record.Time = time.Now().Unix()
record.IsUsed = false
_, err := ormer.Engine.Insert(record)
if err != nil {
return err
}
return nil
}
func getVerificationRecord(dest string) (*VerificationRecord, error) {
var record VerificationRecord
record.Receiver = dest
has, err := ormer.Engine.Desc("time").Where("is_used = false").Get(&record)
if err != nil {
return nil, err
}
if !has {
return nil, nil
}
return &record, nil
}
func CheckVerificationCode(dest, code, lang string) *VerifyResult {
record, err := getVerificationRecord(dest)
if err != nil {
panic(err)
}
if record == nil {
return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:Code has not been sent yet!")}
}
timeout, err := conf.GetConfigInt64("verificationCodeTimeout")
if err != nil {
panic(err)
}
now := time.Now().Unix()
if now-record.Time > timeout*60 {
return &VerifyResult{timeoutError, fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeout)}
}
if record.Code != code {
return &VerifyResult{wrongCodeError, i18n.Translate(lang, "verification:Wrong verification code!")}
}
return &VerifyResult{VerificationSuccess, ""}
}
func DisableVerificationCode(dest string) (err error) {
record, err := getVerificationRecord(dest)
if record == nil || err != nil {
return
}
record.IsUsed = true
_, err = ormer.Engine.ID(core.PK{record.Owner, record.Name}).AllCols().Update(record)
return
}
func CheckSigninCode(user *User, dest, code, lang string) string {
// check the login error times
if msg := checkSigninErrorTimes(user, lang); msg != "" {
return msg
}
result := CheckVerificationCode(dest, code, lang)
switch result.Code {
case VerificationSuccess:
resetUserSigninErrorTimes(user)
return ""
case wrongCodeError:
return recordSigninErrorInfo(user, lang)
default:
return result.Msg
}
}
func GetVerifyType(username string) (verificationCodeType string) {
if strings.Contains(username, "@") {
return VerifyTypeEmail
} else {
return VerifyTypePhone
}
}
// From Casnode/object/validateCode.go line 116
var stdNums = []byte("0123456789")
func getRandomCode(length int) string {
var result []byte
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < length; i++ {
result = append(result, stdNums[r.Intn(len(stdNums))])
}
return string(result)
}