2017-12-03 19:11:16 +08:00
|
|
|
|
// RAINBOND, Application Management Platform
|
|
|
|
|
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
|
|
|
|
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version. For any non-GPL usage of Rainbond,
|
|
|
|
|
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
|
|
|
|
// must be obtained first.
|
|
|
|
|
|
|
|
|
|
// 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 General Public License for more details.
|
|
|
|
|
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
package handler
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"crypto/md5"
|
|
|
|
|
crand "crypto/rand"
|
|
|
|
|
"crypto/rsa"
|
|
|
|
|
"crypto/x509"
|
|
|
|
|
"crypto/x509/pkix"
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"encoding/pem"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"math/big"
|
|
|
|
|
"time"
|
|
|
|
|
|
2017-12-07 10:42:32 +08:00
|
|
|
|
"github.com/Sirupsen/logrus"
|
2017-12-03 19:11:16 +08:00
|
|
|
|
"github.com/jinzhu/gorm"
|
|
|
|
|
|
|
|
|
|
"github.com/goodrain/rainbond/cmd/api/option"
|
|
|
|
|
api_model "github.com/goodrain/rainbond/pkg/api/model"
|
|
|
|
|
"github.com/goodrain/rainbond/pkg/api/util"
|
|
|
|
|
"github.com/goodrain/rainbond/pkg/db"
|
|
|
|
|
dbmodel "github.com/goodrain/rainbond/pkg/db/model"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
//CloudAction cloud action struct
|
|
|
|
|
type CloudAction struct {
|
|
|
|
|
RegionTag string
|
2017-12-07 09:56:08 +08:00
|
|
|
|
APISSL bool
|
2017-12-03 19:11:16 +08:00
|
|
|
|
CAPath string
|
|
|
|
|
KeyPath string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//CreateCloudManager get cloud manager
|
|
|
|
|
func CreateCloudManager(conf option.Config) (*CloudAction, error) {
|
|
|
|
|
return &CloudAction{
|
2017-12-07 09:56:08 +08:00
|
|
|
|
APISSL: conf.APISSL,
|
2017-12-03 19:11:16 +08:00
|
|
|
|
RegionTag: conf.RegionTag,
|
2017-12-07 09:56:08 +08:00
|
|
|
|
CAPath: conf.APICertFile,
|
|
|
|
|
KeyPath: conf.APIKeyFile,
|
2017-12-03 19:11:16 +08:00
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//TokenDispatcher token
|
|
|
|
|
func (c *CloudAction) TokenDispatcher(gt *api_model.GetUserToken) (*api_model.TokenInfo, *util.APIHandleError) {
|
|
|
|
|
//TODO: product token, 启动api时需要添加该参数
|
|
|
|
|
//token包含 eid,数据中心标识,可控范围,有效期
|
|
|
|
|
ti := &api_model.TokenInfo{
|
|
|
|
|
EID: gt.Body.EID,
|
|
|
|
|
}
|
|
|
|
|
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.Key = tokenInfos.Key
|
|
|
|
|
ti.Token = tokenInfos.Token
|
|
|
|
|
return ti, nil
|
|
|
|
|
CREATE:
|
|
|
|
|
token := c.createToken(gt)
|
2017-12-07 10:42:32 +08:00
|
|
|
|
ti.Token = token
|
|
|
|
|
logrus.Debugf("create token %v", token)
|
2017-12-03 19:11:16 +08:00
|
|
|
|
rui := &dbmodel.RegionUserInfo{
|
|
|
|
|
EID: gt.Body.EID,
|
|
|
|
|
RegionTag: c.RegionTag,
|
|
|
|
|
ValidityPeriod: gt.Body.ValidityPeriod,
|
|
|
|
|
Token: token,
|
|
|
|
|
}
|
2017-12-07 10:42:32 +08:00
|
|
|
|
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)
|
|
|
|
|
ti.Key = string(key)
|
|
|
|
|
}
|
2017-12-03 19:11:16 +08:00
|
|
|
|
if gt.Body.Range == "" {
|
2017-12-07 10:42:32 +08:00
|
|
|
|
rui.APIRange = dbmodel.SERVERSOURCE
|
2017-12-03 19:11:16 +08:00
|
|
|
|
}
|
|
|
|
|
GetTokenIdenHandler().AddTokenIntoMap(rui)
|
|
|
|
|
if err := db.GetManager().RegionUserInfoDao().AddModel(rui); err != nil {
|
|
|
|
|
return nil, util.CreateAPIHandleErrorFromDBError("create region user info", err)
|
|
|
|
|
}
|
|
|
|
|
return ti, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//CertDispatcher Cert
|
|
|
|
|
func (c *CloudAction) CertDispatcher(gt *api_model.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
|
|
|
|
|
}
|
2017-12-03 22:35:01 +08:00
|
|
|
|
//keyFile = keyFile.(rsa.PrivateKey)
|
2017-12-03 19:11:16 +08:00
|
|
|
|
|
|
|
|
|
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{fmt.Sprintf("%s", 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,
|
|
|
|
|
//EmailAddresses: []string{"region@test.com"},
|
|
|
|
|
//IPAddresses: []net.IP{net.ParseIP("192.168.1.59")},
|
|
|
|
|
}
|
|
|
|
|
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 *api_model.GetUserToken) string {
|
|
|
|
|
fullStr := fmt.Sprintf("%s-%s-%s-%d", gt.Body.EID, c.RegionTag, gt.Body.Range, gt.Body.ValidityPeriod)
|
|
|
|
|
h := md5.New()
|
|
|
|
|
h.Write([]byte(fullStr))
|
|
|
|
|
return hex.EncodeToString(h.Sum(nil))
|
|
|
|
|
}
|