mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-19 10:46:58 +08:00
remove obsolete codes from apigateway
This commit is contained in:
@@ -15,207 +15,15 @@
|
||||
package clientman
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang-plus/uuid"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apigateway/options"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
"yunion.io/x/onecloud/pkg/util/seclib2"
|
||||
)
|
||||
|
||||
func authVersion() string {
|
||||
pos := strings.LastIndexByte(options.Options.AuthURL, '/')
|
||||
if pos > 0 {
|
||||
return options.Options.AuthURL[pos+1:]
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
mapTokenManagerV2 在mapTokenManager的基础上增加了 token持久化.后端为sqlite
|
||||
token存储了两份:分别位于缓存和后端sqlite.
|
||||
save同时会从sqlite中清理过期的缓存
|
||||
*/
|
||||
type ITokenManagerV2 interface {
|
||||
mcclient.TokenManager
|
||||
ITotpManager
|
||||
}
|
||||
|
||||
type credential struct {
|
||||
Token mcclient.TokenCredential
|
||||
Totp ITotp // 双因子认证。只有options totp enable的情况下该字段才有意义
|
||||
}
|
||||
|
||||
type SMapTokenManagerV2 struct {
|
||||
table map[string]*credential
|
||||
}
|
||||
|
||||
func UnmarshalV2Token(rbody jsonutils.JSONObject) (cred mcclient.TokenCredential, err error) {
|
||||
access, err := rbody.Get("access")
|
||||
if err == nil {
|
||||
cred = &mcclient.TokenCredentialV2{}
|
||||
err = access.Unmarshal(cred)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Invalid response when unmarshal V2 Token: %s", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
err = fmt.Errorf("Invalid response: no access object")
|
||||
return
|
||||
}
|
||||
|
||||
func UnmarshalV3Token(rbody jsonutils.JSONObject, tokenId string) (cred mcclient.TokenCredential, err error) {
|
||||
cred = &mcclient.TokenCredentialV3{Id: tokenId}
|
||||
err = rbody.Unmarshal(cred)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Invalid response when unmarshal V3 Token: %v", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (this *SMapTokenManagerV2) save(token mcclient.TokenCredential) string {
|
||||
key, e := uuid.NewV4()
|
||||
if e != nil {
|
||||
log.Fatalf("uuid.NewV4 returns error!")
|
||||
}
|
||||
|
||||
var totp ITotp
|
||||
kkey := key.String()
|
||||
totp = &STotp{}
|
||||
this.table[kkey] = &credential{Token: token, Totp: totp}
|
||||
// 存储到后端服务
|
||||
go func() {
|
||||
t := jsonutils.Marshal(token).String()
|
||||
t2 := jsonutils.Marshal(totp).String()
|
||||
tr := TokenRecord{
|
||||
TokenID: kkey,
|
||||
Token: t,
|
||||
TokenStr: token.GetTokenString(),
|
||||
Totp: t2,
|
||||
Version: authVersion(),
|
||||
ExpireAt: token.GetExpires(),
|
||||
}
|
||||
if DB.Create(&tr); DB.Error != nil {
|
||||
log.Errorf("%v", DB.Error)
|
||||
} else {
|
||||
// 删除已经过期的token避免无效token堆积
|
||||
now := time.Now()
|
||||
if DB.Delete(TokenRecord{}, "expire_at < ?", now); DB.Error != nil {
|
||||
log.Errorf("%v", DB.Error)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return kkey
|
||||
}
|
||||
|
||||
func (this *SMapTokenManagerV2) get(tid string) mcclient.TokenCredential {
|
||||
if cred := this.table[tid]; cred != nil && cred.Token != nil {
|
||||
return cred.Token
|
||||
}
|
||||
|
||||
// load from db
|
||||
err := this.loadFromDB(tid)
|
||||
if err != nil {
|
||||
log.Errorf("loadFromDB by tid %s: %v", tid, err)
|
||||
return nil
|
||||
} else {
|
||||
return this.table[tid].Token
|
||||
}
|
||||
}
|
||||
|
||||
func (this *SMapTokenManagerV2) remove(tid string) {
|
||||
delete(this.table, tid)
|
||||
if DB.Delete(TokenRecord{}, "token_id = ?", tid); DB.Error != nil {
|
||||
log.Errorf("%v", DB.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *SMapTokenManagerV2) getTotp(tid string) ITotp {
|
||||
if cred := this.table[tid]; cred != nil && cred.Totp != nil {
|
||||
return cred.Totp
|
||||
}
|
||||
|
||||
// load from db
|
||||
err := this.loadFromDB(tid)
|
||||
if err != nil {
|
||||
log.Errorf(err.Error())
|
||||
return nil
|
||||
} else {
|
||||
return this.table[tid].Totp
|
||||
}
|
||||
}
|
||||
|
||||
func (this *SMapTokenManagerV2) saveTotp(tid string) {
|
||||
totp := this.table[tid].Totp
|
||||
if totp == nil {
|
||||
log.Errorf("%s totp is nil", tid)
|
||||
return
|
||||
}
|
||||
|
||||
// 存储到后端服务
|
||||
go func() {
|
||||
tr := TokenRecord{}
|
||||
if DB.Where("token_id = ?", tid).First(&tr); DB.Error != nil {
|
||||
log.Errorf("%v", DB.Error)
|
||||
} else {
|
||||
DB.Model(&tr).Update("Totp", jsonutils.Marshal(totp).String())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (this *SMapTokenManagerV2) loadFromDB(tid string) error {
|
||||
// load from db
|
||||
tr := TokenRecord{}
|
||||
if DB.Where("token_id = ?", tid).First(&tr); DB.Error != nil {
|
||||
return fmt.Errorf("%v", DB.Error)
|
||||
}
|
||||
|
||||
tjson, err := jsonutils.ParseString(tr.Token)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "parse token to json: %s", tr.Token)
|
||||
}
|
||||
|
||||
var token mcclient.TokenCredential
|
||||
if authVersion() == "v3" {
|
||||
if token, err = UnmarshalV3Token(tjson, tr.TokenStr); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if token, err = UnmarshalV2Token(tjson); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var totp STotp
|
||||
totpJson, err := jsonutils.ParseString(tr.Totp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := totpJson.Unmarshal(&totp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
this.table[tid] = &credential{Token: token, Totp: &totp}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*func NewMapTokenManagerV2() ITokenManagerV2 {
|
||||
return &SMapTokenManagerV2{table: make(map[string]*credential)}
|
||||
}*/
|
||||
|
||||
func InitClient() error {
|
||||
info := auth.NewAuthInfo(options.Options.AuthURL,
|
||||
options.Options.AdminDomain,
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// 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 clientman
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"yunion.io/x/log"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
type TokenRecord struct {
|
||||
ID uint `gorm:"primary_key"`
|
||||
TokenID string `gorm:"not null;unique"`
|
||||
Token string `gorm:"not null;unique"`
|
||||
TokenStr string `gorm:"not null;DEFAULT:''"`
|
||||
Totp string `gorm:"not null;DEFAULT:''"`
|
||||
Version string `gorm:"not null;DEFAULT:'v2'"`
|
||||
ExpireAt time.Time
|
||||
}
|
||||
|
||||
func initDB(dialect string, args ...interface{}) error {
|
||||
if DB == nil {
|
||||
db, err := gorm.Open(dialect, args...)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Init DB by args: %v", args)
|
||||
}
|
||||
// Migrate the schema
|
||||
db.AutoMigrate(&TokenRecord{})
|
||||
DB = db
|
||||
return nil
|
||||
}
|
||||
log.Warningf("DB: %s , Conn: %v already connected...", dialect, args)
|
||||
return nil
|
||||
}
|
||||
@@ -16,10 +16,6 @@ package clientman
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pquerna/otp/totp"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules"
|
||||
@@ -27,69 +23,6 @@ import (
|
||||
|
||||
const MAX_OTP_RETRY = 5 // totp验证最大重试次数
|
||||
|
||||
type ITotp interface {
|
||||
UpdateRetryCount()
|
||||
IsVerified() bool
|
||||
MarkVerified()
|
||||
VerifyTotpPasscode(s *mcclient.ClientSession, uid, passcode string) error
|
||||
}
|
||||
|
||||
type ITotpManager interface {
|
||||
GetTotp(tid string) ITotp
|
||||
SaveTotp(tid string)
|
||||
}
|
||||
|
||||
type STotp struct {
|
||||
RetryCount int `json:"retry_count"` // 重试计数器
|
||||
LockExpireTime time.Time `json:"lock_expire_time"` // 锁定时间
|
||||
Verified bool `json:"verified"` // 验证状态
|
||||
}
|
||||
|
||||
func (self *STotp) UpdateRetryCount() {
|
||||
if self.RetryCount < MAX_OTP_RETRY {
|
||||
self.RetryCount += 1
|
||||
|
||||
// 锁定
|
||||
if self.RetryCount >= MAX_OTP_RETRY {
|
||||
self.LockExpireTime = time.Now().Add(30 * time.Second)
|
||||
}
|
||||
} else {
|
||||
// 清零计数器,解除锁定
|
||||
if self.LockExpireTime.Before(time.Now()) {
|
||||
self.LockExpireTime = time.Now()
|
||||
self.RetryCount = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *STotp) IsVerified() bool {
|
||||
return self.Verified
|
||||
}
|
||||
|
||||
func (self *STotp) MarkVerified() {
|
||||
self.Verified = true
|
||||
}
|
||||
|
||||
func (self *STotp) VerifyTotpPasscode(s *mcclient.ClientSession, uid, passcode string) error {
|
||||
secret, err := fetchUserTotpCredSecret(s, uid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetch totp secrets error: %s", err.Error())
|
||||
}
|
||||
|
||||
releaseSeconds := self.LockExpireTime.Unix() - time.Now().Unix()
|
||||
if releaseSeconds > 0 {
|
||||
return fmt.Errorf("locked, retry after %d seconds", releaseSeconds)
|
||||
}
|
||||
|
||||
if totp.Validate(passcode, secret) {
|
||||
self.MarkVerified()
|
||||
return nil
|
||||
}
|
||||
|
||||
self.UpdateRetryCount()
|
||||
return fmt.Errorf("invalid passcode")
|
||||
}
|
||||
|
||||
// 获取用户TOTP credential 密码.
|
||||
func fetchUserTotpCredSecret(s *mcclient.ClientSession, uid string) (string, error) {
|
||||
secret, err := modules.Credentials.GetTotpSecret(s, uid)
|
||||
|
||||
@@ -28,8 +28,6 @@ type GatewayOptions struct {
|
||||
|
||||
EnableTotp bool `help:"Enable two-factor authentication" default:"true"`
|
||||
|
||||
// SqlitePath string `help:"sqlite db path" default:"/etc/yunion/data/yunionapi.db"`
|
||||
|
||||
common_options.CommonOptions `"request_worker_count->default":"32"`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user