package handler
import (
"crypto/md5"
crand "crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"encoding/pem"
"fmt"
"github.com/goodrain/rainbond/config/configs"
"io/ioutil"
"math/big"
"time"
"github.com/jinzhu/gorm"
"github.com/sirupsen/logrus"
apimodel "github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/api/util"
"github.com/goodrain/rainbond/db"
dbmodel "github.com/goodrain/rainbond/db/model"
)
type CloudAction struct {
RegionTag string
APISSL bool
CAPath string
KeyPath string
}
func CreateCloudManager() *CloudAction {
config := configs.Default()
return &CloudAction{
APISSL: config.APIConfig.APISSL,
RegionTag: config.APIConfig.RegionTag,
CAPath: config.APIConfig.APICertFile,
KeyPath: config.APIConfig.APIKeyFile,
}
}
func (c *CloudAction) TokenDispatcher(gt *apimodel.GetUserToken) (*apimodel.TokenInfo, *util.APIHandleError) {
ti := &apimodel.TokenInfo{
EID: gt.Body.EID,
}
token := c.createToken(gt)
var oldToken string
tokenInfos, err := db.GetManager().RegionUserInfoDao().GetTokenByEid(gt.Body.EID)
if err != nil {
if err.Error() == gorm.ErrRecordNotFound.Error() {
goto CREATE
}
return nil, util.CreateAPIHandleErrorFromDBError("get user token info", err)
}
ti.CA = tokenInfos.CA
ti.Token = token
oldToken = tokenInfos.Token
tokenInfos.Token = token
tokenInfos.ValidityPeriod = gt.Body.ValidityPeriod
if err := db.GetManager().RegionUserInfoDao().UpdateModel(tokenInfos); err != nil {
return nil, util.CreateAPIHandleErrorFromDBError("recreate region user info", err)
}
tokenInfos.CA = ""
tokenInfos.Key = ""
GetTokenIdenHandler().DeleteTokenFromMap(oldToken, tokenInfos)
return ti, nil
CREATE:
ti.Token = token
logrus.Debugf("create token %v", token)
rui := &dbmodel.RegionUserInfo{
EID: gt.Body.EID,
RegionTag: c.RegionTag,
APIRange: gt.Body.Range,
ValidityPeriod: gt.Body.ValidityPeriod,
Token: token,
}
if c.APISSL {
ca, key, err := c.CertDispatcher(gt)
if err != nil {
return nil, util.CreateAPIHandleError(500, fmt.Errorf("create ca or key error"))
}
rui.CA = string(ca)
rui.Key = string(key)
ti.CA = string(ca)
}
if gt.Body.Range == "" {
rui.APIRange = dbmodel.SERVERSOURCE
}
GetTokenIdenHandler().AddTokenIntoMap(rui)
if err := db.GetManager().RegionUserInfoDao().AddModel(rui); err != nil {
return nil, util.CreateAPIHandleErrorFromDBError("create region user info", err)
}
return ti, nil
}
func (c *CloudAction) GetTokenInfo(eid string) (*dbmodel.RegionUserInfo, *util.APIHandleError) {
tokenInfos, err := db.GetManager().RegionUserInfoDao().GetTokenByEid(eid)
if err != nil {
return nil, util.CreateAPIHandleErrorFromDBError("get user token info", err)
}
return tokenInfos, nil
}
func (c *CloudAction) UpdateTokenTime(eid string, vd int) *util.APIHandleError {
tokenInfos, err := db.GetManager().RegionUserInfoDao().GetTokenByEid(eid)
if err != nil {
return util.CreateAPIHandleErrorFromDBError("get user token info", err)
}
tokenInfos.ValidityPeriod = vd
err = db.GetManager().RegionUserInfoDao().UpdateModel(tokenInfos)
if err != nil {
return util.CreateAPIHandleErrorFromDBError("update user token info", err)
}
return nil
}
func (c *CloudAction) CertDispatcher(gt *apimodel.GetUserToken) ([]byte, []byte, error) {
cert, err := analystCaKey(c.CAPath, "ca")
if err != nil {
return nil, nil, err
}
keyFile, err := analystCaKey(c.KeyPath, "key")
if err != nil {
return nil, nil, err
}
validHourTime := (gt.Body.ValidityPeriod - gt.Body.BeforeTime)
cer := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: fmt.Sprintf("%s@%d", gt.Body.EID, time.Now().Unix()),
Locality: []string{c.RegionTag},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Second * time.Duration(validHourTime)),
BasicConstraintsValid: true,
IsCA: false,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment,
}
priKey, err := rsa.GenerateKey(crand.Reader, 2048)
if err != nil {
return nil, nil, err
}
ca, err := x509.CreateCertificate(crand.Reader, cer, cert.(*x509.Certificate), &priKey.PublicKey, keyFile)
if err != nil {
return nil, nil, err
}
caPem := &pem.Block{
Type: "CERTIFICATE",
Bytes: ca,
}
ca = pem.EncodeToMemory(caPem)
buf := x509.MarshalPKCS1PrivateKey(priKey)
keyPem := &pem.Block{
Type: "PRIVATE KEY",
Bytes: buf,
}
key := pem.EncodeToMemory(keyPem)
return ca, key, nil
}
func analystCaKey(path, kind string) (interface{}, error) {
fileInfo, err := ioutil.ReadFile(path)
if err != nil {
return "", nil
}
fileBlock, _ := pem.Decode(fileInfo)
switch kind {
case "ca":
cert, err := x509.ParseCertificate(fileBlock.Bytes)
if err != nil {
return "", nil
}
return cert, nil
case "key":
praKey, err := x509.ParsePKCS1PrivateKey(fileBlock.Bytes)
if err != nil {
return "", nil
}
return praKey, nil
}
return "", nil
}
func (c *CloudAction) createToken(gt *apimodel.GetUserToken) string {
fullStr := fmt.Sprintf("%s-%s-%s-%d-%d", gt.Body.EID, c.RegionTag, gt.Body.Range, gt.Body.ValidityPeriod, int(time.Now().Unix()))
h := md5.New()
h.Write([]byte(fullStr))
return hex.EncodeToString(h.Sum(nil))
}