You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ju...@apache.org on 2020/07/12 04:55:22 UTC
[incubator-apisix-dashboard] branch master updated: add transaction
for ssl and consumer (#308)
This is an automated email from the ASF dual-hosted git repository.
juzhiyuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-apisix-dashboard.git
The following commit(s) were added to refs/heads/master by this push:
new 08e65f4 add transaction for ssl and consumer (#308)
08e65f4 is described below
commit 08e65f48983608dbab91947775e88ad668eeff90
Author: nic-chen <33...@users.noreply.github.com>
AuthorDate: Sun Jul 12 12:55:11 2020 +0800
add transaction for ssl and consumer (#308)
---
api/errno/error.go | 113 ++++++++++++----------
api/script/db/schema.sql | 4 +-
api/service/consumer.go | 130 ++++++++++++++++++++-----
api/service/ssl.go | 245 +++++++++++++++++++++++++++++++++++++++++------
api/service/ssl_test.go | 86 ++++++++++++++++-
5 files changed, 463 insertions(+), 115 deletions(-)
diff --git a/api/errno/error.go b/api/errno/error.go
index 24bb5a3..08b5adb 100644
--- a/api/errno/error.go
+++ b/api/errno/error.go
@@ -22,65 +22,74 @@ import (
)
type Message struct {
- Code string
- Msg string
+ Code string
+ Msg string
+ Status int `json:"-"`
}
var (
//AA 01 api-manager-api
- SystemSuccess = Message{"010000", "success"}
- SystemError = Message{"010001", "system error"}
- BadRequestError = Message{Code: "010002", Msg: "Request format error"}
- NotFoundError = Message{Code: "010003", Msg: "No resources found"}
- InvalidParam = Message{"010004", "Request parameter error"}
- DBWriteError = Message{"010005", "Database save failed"}
- DBReadError = Message{"010006", "Database query failed"}
- DBDeleteError = Message{"010007", "Database delete failed"}
- RecordNotExist = Message{"010009", "Record does not exist"}
-
- //BB 01 config module
- ConfEnvError = Message{"010101", "Environment variable not found: %s"}
- ConfFilePathError = Message{"010102", "Error loading configuration file: %s"}
-
- // BB 02 route module
- RouteRequestError = Message{"010201", "Route request parameters are abnormal: %s"}
- ApisixRouteCreateError = Message{"010202", "Failed to create APISIX route: %s"}
- DBRouteCreateError = Message{"010203", "Route storage failure: %s"}
- ApisixRouteUpdateError = Message{"010204", "Update APISIX routing failed: %s"}
- ApisixRouteDeleteError = Message{"010205", "Failed to remove APISIX route: %s"}
- DBRouteUpdateError = Message{"010206", "Route update failed: %s"}
- DBRouteDeleteError = Message{"010207", "Route remove failed: %s"}
- DBRouteReduplicateError = Message{"010208", "Route name is reduplicate : %s"}
-
- // 03 plugin module
- ApisixPluginListError = Message{"010301", "List APISIX plugins failed: %s"}
- ApisixPluginSchemaError = Message{"010301", "Find APISIX plugin schema failed: %s"}
-
- // 04 ssl模块
- SslParseError = Message{"010401", "Certificate resolution failed: %s"}
- ApisixSslCreateError = Message{"010402", "Create APISIX SSL failed"}
- ApisixSslUpdateError = Message{"010403", "Update APISIX SSL failed"}
- ApisixSslDeleteError = Message{"010404", "Delete APISIX SSL failed"}
+ //BB 00 system
+ SystemSuccess = Message{"010000", "success", 200}
+ SystemError = Message{"010001", "system error", 500}
+ BadRequestError = Message{"010002", "Request format error", 400}
+ NotFoundError = Message{"010003", "No resources found", 404}
+ InvalidParam = Message{"010004", "Request parameter error", 400}
+ DBWriteError = Message{"010005", "Database save failed", 500}
+ DBReadError = Message{"010006", "Database query failed", 500}
+ DBDeleteError = Message{"010007", "Database delete failed", 500}
+ RecordNotExist = Message{"010009", "Record does not exist", 404}
+ InvalidParamDetail = Message{"010010", "Invalid request parameter: %s", 400}
+ AdminApiSaveError = Message{"010011", "Data save failed", 500}
+ SchemaCheckFailed = Message{"010012", "%s", 400}
+
+ //BB 01 configuration
+ ConfEnvError = Message{"010101", "Environment variable not found: %s", 500}
+ ConfFilePathError = Message{"010102", "Error loading configuration file: %s", 500}
+
+ // BB 02 route
+ RouteRequestError = Message{"010201", "Route request parameters are abnormal: %s", 400}
+ ApisixRouteCreateError = Message{"010202", "Failed to create APISIX route: %s", 500}
+ DBRouteCreateError = Message{"010203", "Route storage failure: %s", 500}
+ ApisixRouteUpdateError = Message{"010204", "Update APISIX routing failed: %s", 500}
+ ApisixRouteDeleteError = Message{"010205", "Failed to delete APISIX route: %s", 500}
+ DBRouteUpdateError = Message{"010206", "Route update failed: %s", 500}
+ DBRouteDeleteError = Message{"010207", "Route deletion failed: %s", 500}
+ DBRouteReduplicateError = Message{"010208", "Route name is reduplicate : %s", 400}
+
+ // 03 plugins
+ ApisixPluginListError = Message{"010301", "find APISIX plugin list failed: %s", 500}
+ ApisixPluginSchemaError = Message{"010301", "find APISIX plugin schema failed: %s", 500}
+
+ // 04 ssl
+ SslParseError = Message{"010401", "Certificate resolution failed: %s", 400}
+ ApisixSslCreateError = Message{"010402", "Failed to create APISIX SSL", 500}
+ ApisixSslUpdateError = Message{"010403", "Failed to update APISIX SSL", 500}
+ ApisixSslDeleteError = Message{"010404", "Failed to delete APISIX SSL", 500}
+ SslForSniNotExists = Message{"010407", "Ssl for sni not exists:%s", 400}
+ DuplicateSslCert = Message{"010408", "Duplicate ssl cert", 400}
// 06 upstream
- UpstreamRequestError = Message{"010601", "upstream request parameters are abnormal: %s"}
- UpstreamTransError = Message{"010602", "upstream parameter conversion is abnormal: %s"}
- DBUpstreamError = Message{"010603", "upstream storage failure: %s"}
- ApisixUpstreamCreateError = Message{"010604", "apisix upstream create failure: %s"}
- ApisixUpstreamUpdateError = Message{"010605", "apisix upstream update failure: %s"}
- ApisixUpstreamDeleteError = Message{"010606", "apisix upstream delete failure: %s"}
- DBUpstreamDeleteError = Message{"010607", "upstream delete failure: %s"}
- DBUpstreamReduplicateError = Message{"010608", "Upstream name is reduplicate : %s"}
-
- ApisixConsumerCreateError = Message{"010702", "Create APISIX Consumer failed"}
- ApisixConsumerUpdateError = Message{"010703", "Update APISIX Consumer failed"}
- ApisixConsumerDeleteError = Message{"010704", "Delete APISIX Consumer failed"}
- DuplicateUserName = Message{"010705", "Duplicate username"}
+ UpstreamRequestError = Message{"010601", "upstream request parameters exception: %s", 400}
+ UpstreamTransError = Message{"010602", "Abnormal upstream parameter conversion: %s", 400}
+ DBUpstreamError = Message{"010603", "upstream storage failure: %s", 500}
+ ApisixUpstreamCreateError = Message{"010604", "apisix upstream create failed: %s", 500}
+ ApisixUpstreamUpdateError = Message{"010605", "apisix upstream update failed: %s", 500}
+ ApisixUpstreamDeleteError = Message{"010606", "apisix upstream delete failed: %s", 500}
+ DBUpstreamDeleteError = Message{"010607", "upstream storage delete failed: %s", 500}
+ DBUpstreamReduplicateError = Message{"010608", "Upstream name is reduplicate : %s", 500}
+
+ // 07 consumer
+ ApisixConsumerCreateError = Message{"010702", "APISIX Consumer create failed", 500}
+ ApisixConsumerUpdateError = Message{"010703", "APISIX Consumer update failed", 500}
+ ApisixConsumerDeleteError = Message{"010704", "APISIX Consumer delete failed", 500}
+ DuplicateUserName = Message{"010705", "Duplicate consumer username", 400}
)
type ManagerError struct {
TraceId string
Code string
+ Status int
Msg string
Data interface{}
Detail string
@@ -96,11 +105,11 @@ func (e *ManagerError) ErrorDetail() string {
}
func FromMessage(m Message, args ...interface{}) *ManagerError {
- return &ManagerError{TraceId: "", Code: m.Code, Msg: fmt.Sprintf(m.Msg, args...)}
+ return &ManagerError{TraceId: "", Code: m.Code, Status: m.Status, Msg: fmt.Sprintf(m.Msg, args...)}
}
func New(m Message, args ...interface{}) *ManagerError {
- return &ManagerError{TraceId: "", Code: m.Code, Msg: m.Msg, Detail: fmt.Sprintf("%s", args...)}
+ return &ManagerError{TraceId: "", Code: m.Code, Msg: m.Msg, Status: m.Status, Detail: fmt.Sprintf("%s", args...)}
}
func (e *ManagerError) Response() map[string]interface{} {
@@ -139,9 +148,9 @@ func Succeed() map[string]interface{} {
type HttpError struct {
Code int
- Msg string
+ Msg Message
}
func (e *HttpError) Error() string {
- return e.Msg
+ return e.Msg.Msg
}
diff --git a/api/script/db/schema.sql b/api/script/db/schema.sql
index 83c4bdd..95d4e60 100644
--- a/api/script/db/schema.sql
+++ b/api/script/db/schema.sql
@@ -28,7 +28,9 @@ CREATE TABLE `ssls` (
`status` tinyint(1) unsigned NOT NULL DEFAULT '1',
`create_time` bigint(20) unsigned NOT NULL,
`update_time` bigint(20) unsigned NOT NULL,
- PRIMARY KEY (`id`)
+ `public_key_hash` varchar(64) NOT NULL DEFAULT '',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uni_public_key_hash` (`public_key_hash`)
) DEFAULT CHARSET=utf8;
-- upstream
diff --git a/api/service/consumer.go b/api/service/consumer.go
index a29dde6..98dee45 100644
--- a/api/service/consumer.go
+++ b/api/service/consumer.go
@@ -102,8 +102,8 @@ func ConsumerList(page, size int, search string) (int, []ConsumerDto, error) {
db := conf.DB().Table("consumers")
if search != "" {
- db = db.Where("name like ? ", "%"+search+"%").
- Or("description like ? ", "%"+search+"%")
+ db = db.Where("username like ? ", "%"+search+"%").
+ Or("`desc` like ? ", "%"+search+"%")
}
if err := db.Order("create_time desc").Offset((page - 1) * size).Limit(size).Find(&consumerList).Error; err != nil {
@@ -128,6 +128,10 @@ func ConsumerList(page, size int, search string) (int, []ConsumerDto, error) {
}
func ConsumerItem(id string) (*ConsumerDto, error) {
+ if id == "" {
+ return nil, errno.New(errno.InvalidParam)
+ }
+
consumer := &Consumer{}
if err := conf.DB().Table("consumers").Where("id = ?", id).First(consumer).Error; err != nil {
e := errno.New(errno.DBReadError, err.Error())
@@ -144,6 +148,14 @@ func ConsumerCreate(param interface{}, id string) error {
req := &ConsumerDto{}
req.Parse(param)
+ if req.Username == "" {
+ return errno.New(errno.InvalidParamDetail, "username is required")
+ }
+
+ if len(req.Desc) > 200 {
+ return errno.New(errno.InvalidParamDetail, "description is too long")
+ }
+
exists := Consumer{}
conf.DB().Table("consumers").Where("username = ?", req.Username).First(&exists)
if exists != (Consumer{}) {
@@ -151,10 +163,29 @@ func ConsumerCreate(param interface{}, id string) error {
return e
}
+ // trans
+ tx := conf.DB().Begin()
+ defer func() {
+ if r := recover(); r != nil {
+ tx.Rollback()
+ }
+ }()
+
+ consumer := &Consumer{}
+ consumer.Transfer(req)
+
+ // update mysql
+ consumer.ID = uuid.FromStringOrNil(id)
+ if err := tx.Create(consumer).Error; err != nil {
+ tx.Rollback()
+ return errno.New(errno.DBWriteError, err.Error())
+ }
+
apisixConsumer := &ApisixConsumer{}
apisixConsumer.Transfer(req)
if _, err := apisixConsumer.PutConsumerToApisix(req.Username); err != nil {
+ tx.Rollback()
if _, ok := err.(*errno.HttpError); ok {
return err
}
@@ -162,33 +193,56 @@ func ConsumerCreate(param interface{}, id string) error {
return e
}
- consumer := &Consumer{}
- consumer.Transfer(req)
-
- // update mysql
- consumer.ID = uuid.FromStringOrNil(id)
- if err := conf.DB().Create(consumer).Error; err != nil {
- return errno.New(errno.DBWriteError, err.Error())
- }
+ tx.Commit()
return nil
}
func ConsumerUpdate(param interface{}, id string) error {
+ if id == "" {
+ return errno.New(errno.InvalidParam)
+ }
+
req := &ConsumerDto{}
req.Parse(param)
+ if req == nil {
+ return errno.New(errno.InvalidParam)
+ }
- exists := Consumer{}
- conf.DB().Table("consumers").Where("username = ?", req.Username).First(&exists)
- if exists != (Consumer{}) && exists.ID != req.ID {
- e := errno.New(errno.DuplicateUserName)
- return e
+ req.ID = uuid.FromStringOrNil(id)
+ if req.Username != "" {
+ exists := Consumer{}
+ conf.DB().Table("consumers").Where("username = ?", req.Username).First(&exists)
+ if exists != (Consumer{}) && exists.ID != req.ID {
+ e := errno.New(errno.DuplicateUserName)
+ return e
+ }
+ }
+ // trans
+ tx := conf.DB().Begin()
+ defer func() {
+ if r := recover(); r != nil {
+ tx.Rollback()
+ }
+ }()
+
+ // update mysql
+ consumer := &Consumer{}
+ consumer.Transfer(req)
+ data := Consumer{Desc: consumer.Desc, Plugins: consumer.Plugins}
+ if req.Username != "" {
+ data.Username = req.Username
+ }
+ if err := tx.Model(&consumer).Updates(data).Error; err != nil {
+ tx.Rollback()
+ return errno.New(errno.DBWriteError, err.Error())
}
apisixConsumer := &ApisixConsumer{}
apisixConsumer.Transfer(req)
if _, err := apisixConsumer.PutConsumerToApisix(req.Username); err != nil {
+ tx.Rollback()
if _, ok := err.(*errno.HttpError); ok {
return err
}
@@ -196,18 +250,15 @@ func ConsumerUpdate(param interface{}, id string) error {
return e
}
- // update mysql
- consumer := &Consumer{}
- consumer.Transfer(req)
- consumer.ID = uuid.FromStringOrNil(id)
- if err := conf.DB().Model(&consumer).Updates(consumer).Error; err != nil {
- return errno.New(errno.DBWriteError, err.Error())
- }
+ tx.Commit()
return nil
}
func ConsumerDelete(id string) error {
+ if id == "" {
+ return errno.New(errno.InvalidParam)
+ }
//
consumer := &Consumer{}
if err := conf.DB().Table("consumers").Where("id = ?", id).First(consumer).Error; err != nil {
@@ -215,17 +266,31 @@ func ConsumerDelete(id string) error {
return e
}
+ // trans
+ tx := conf.DB().Begin()
+ defer func() {
+ if r := recover(); r != nil {
+ tx.Rollback()
+ }
+ }()
+
+ // delete from mysql
+ if err := conf.DB().Delete(consumer).Error; err != nil {
+ tx.Rollback()
+ return errno.New(errno.DBDeleteError, err.Error())
+ }
+
+ //delete from apisix
if _, err := consumer.DeleteConsumerFromApisix(); err != nil {
+ tx.Rollback()
if _, ok := err.(*errno.HttpError); ok {
return err
}
e := errno.New(errno.ApisixConsumerDeleteError, err.Error())
return e
}
- // delete from mysql
- if err := conf.DB().Delete(consumer).Error; err != nil {
- return errno.New(errno.DBDeleteError, err.Error())
- }
+
+ tx.Commit()
return nil
}
@@ -269,3 +334,16 @@ func (req *Consumer) DeleteConsumerFromApisix() (*ApisixConsumerResponse, error)
}
}
}
+
+func GetConsumerByUserName(name string) (*Consumer, error) {
+ if name == "" {
+ return nil, errno.New(errno.InvalidParam)
+ }
+ consumer := &Consumer{}
+ if err := conf.DB().Table("consumers").Where("username = ?", name).First(consumer).Error; err != nil {
+ e := errno.New(errno.NotFoundError, err.Error())
+ return nil, e
+ }
+
+ return consumer, nil
+}
diff --git a/api/service/ssl.go b/api/service/ssl.go
index 08dea43..2fbfbfd 100644
--- a/api/service/ssl.go
+++ b/api/service/ssl.go
@@ -17,12 +17,15 @@
package service
import (
+ "crypto/md5"
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
+ "regexp"
+ "strings"
"github.com/apisix/manager-api/conf"
"github.com/apisix/manager-api/errno"
@@ -37,6 +40,7 @@ type Ssl struct {
Snis string `json:"snis"`
Status uint64 `json:"status"`
PublicKey string `json:"public_key,omitempty"`
+ PublicKeyHash string `json:"public_key_hash,omitempty"`
}
type SslDto struct {
@@ -69,8 +73,9 @@ type SslNode struct {
func (req *SslRequest) Parse(body interface{}) {
if err := json.Unmarshal(body.([]byte), req); err != nil {
+ logger.Info("req:")
+ logger.Info(req)
req = nil
- logger.Error(errno.FromMessage(errno.RouteRequestError, err.Error()).Msg)
}
}
@@ -91,7 +96,7 @@ func (sslDto *SslDto) Parse(ssl *Ssl) error {
return nil
}
-func SslList(page, size, status, expireStart, expireEnd int, sni string) (int, []SslDto, error) {
+func SslList(page, size, status, expireStart, expireEnd int, sni, sortType string) (int, []SslDto, error) {
var count int
sslList := []Ssl{}
db := conf.DB().Table("ssls")
@@ -109,7 +114,12 @@ func SslList(page, size, status, expireStart, expireEnd int, sni string) (int, [
db = db.Where("validity_end <= ? ", expireEnd)
}
- if err := db.Order("validity_end desc").Offset((page - 1) * size).Limit(size).Find(&sslList).Error; err != nil {
+ sortType = strings.ToLower(sortType)
+ if sortType != "desc" {
+ sortType = "asc"
+ }
+
+ if err := db.Order("validity_end " + sortType).Offset((page - 1) * size).Limit(size).Find(&sslList).Error; err != nil {
e := errno.New(errno.DBReadError, err.Error())
return 0, nil, e
}
@@ -131,7 +141,11 @@ func SslList(page, size, status, expireStart, expireEnd int, sni string) (int, [
}
func SslItem(id string) (*SslDto, error) {
+ if id == "" {
+ return nil, errno.New(errno.InvalidParam)
+ }
ssl := &Ssl{}
+
if err := conf.DB().Table("ssls").Where("id = ?", id).First(ssl).Error; err != nil {
e := errno.New(errno.DBReadError, err.Error())
return nil, e
@@ -166,53 +180,126 @@ func SslCreate(param interface{}, id string) error {
sslReq := &SslRequest{}
sslReq.Parse(param)
- ssl, err := ParseCert(sslReq.PublicKey, sslReq.PrivateKey)
+ if sslReq.PrivateKey == "" {
+ return errno.New(errno.InvalidParamDetail, "Key is required")
+ }
+ if sslReq.PublicKey == "" {
+ return errno.New(errno.InvalidParamDetail, "Cert is required")
+ }
+ sslReq.PublicKey = strings.TrimSpace(sslReq.PublicKey)
+ sslReq.PrivateKey = strings.TrimSpace(sslReq.PrivateKey)
+
+ ssl, err := ParseCert(sslReq.PublicKey, sslReq.PrivateKey)
if err != nil {
e := errno.FromMessage(errno.SslParseError, err.Error())
return e
}
- // first admin api
+ ssl.ID = uuid.FromStringOrNil(id)
+ ssl.Status = 1
+ data := []byte(ssl.PublicKey)
+ hash := md5.Sum(data)
+ ssl.PublicKeyHash = fmt.Sprintf("%x", hash)
+
+ //check hash
+ exists := Ssl{}
+ conf.DB().Table("ssls").Where("public_key_hash = ?", ssl.PublicKeyHash).First(&exists)
+ if exists != (Ssl{}) {
+ e := errno.New(errno.DuplicateSslCert)
+ return e
+ }
+
+ //check sni
var snis []string
_ = json.Unmarshal([]byte(ssl.Snis), &snis)
sslReq.Snis = snis
+ // trans
+ tx := conf.DB().Begin()
+ defer func() {
+ if r := recover(); r != nil {
+ tx.Rollback()
+ }
+ }()
+
+ // update mysql
+ if err := tx.Create(ssl).Error; err != nil {
+ tx.Rollback()
+ return errno.New(errno.DBWriteError, err.Error())
+ }
+
+ //admin api
+
if _, err := sslReq.PutToApisix(id); err != nil {
+ tx.Rollback()
if _, ok := err.(*errno.HttpError); ok {
return err
}
e := errno.New(errno.ApisixSslCreateError, err.Error())
return e
}
- // then mysql
- ssl.ID = uuid.FromStringOrNil(id)
- ssl.Status = 1
- if err := conf.DB().Create(ssl).Error; err != nil {
- return errno.New(errno.DBWriteError, err.Error())
- }
+ tx.Commit()
return nil
}
func SslUpdate(param interface{}, id string) error {
+ if id == "" {
+ return errno.New(errno.InvalidParam)
+ }
+
sslReq := &SslRequest{}
sslReq.Parse(param)
- ssl, err := ParseCert(sslReq.PublicKey, sslReq.PrivateKey)
+ if sslReq.PrivateKey == "" {
+ return errno.New(errno.InvalidParamDetail, "Key is required")
+ }
+ if sslReq.PublicKey == "" {
+ return errno.New(errno.InvalidParamDetail, "Cert is required")
+ }
+ ssl, err := ParseCert(sslReq.PublicKey, sslReq.PrivateKey)
if err != nil {
- e := errno.FromMessage(errno.SslParseError, err.Error())
+ return errno.FromMessage(errno.SslParseError, err.Error())
+ }
+
+ hash := md5.Sum([]byte(ssl.PublicKey))
+ ssl.ID = uuid.FromStringOrNil(id)
+ ssl.PublicKeyHash = fmt.Sprintf("%x", hash)
+
+ //check hash
+ exists := Ssl{}
+ conf.DB().Table("ssls").Where("public_key_hash = ?", ssl.PublicKeyHash).First(&exists)
+ if exists != (Ssl{}) && exists.ID != ssl.ID {
+ e := errno.New(errno.DuplicateSslCert)
return e
}
- // first admin api
+ // trans
+ tx := conf.DB().Begin()
+ defer func() {
+ if r := recover(); r != nil {
+ tx.Rollback()
+ }
+ }()
+
+ //sni check
var snis []string
_ = json.Unmarshal([]byte(ssl.Snis), &snis)
sslReq.Snis = snis
+ // update mysql
+ data := Ssl{PublicKey: ssl.PublicKey, Snis: ssl.Snis, ValidityStart: ssl.ValidityStart, ValidityEnd: ssl.ValidityEnd}
+ if err := tx.Model(&ssl).Updates(data).Error; err != nil {
+ tx.Rollback()
+ return errno.New(errno.DBWriteError, err.Error())
+ }
+
+ //admin api
if _, err := sslReq.PutToApisix(id); err != nil {
+ tx.Rollback()
if _, ok := err.(*errno.HttpError); ok {
return err
}
@@ -220,21 +307,37 @@ func SslUpdate(param interface{}, id string) error {
return e
}
- // then mysql
- ssl.ID = uuid.FromStringOrNil(id)
- data := Ssl{PublicKey: ssl.PublicKey, Snis: ssl.Snis, ValidityStart: ssl.ValidityStart, ValidityEnd: ssl.ValidityEnd}
- if err := conf.DB().Model(&ssl).Updates(data).Error; err != nil {
- return errno.New(errno.DBWriteError, err.Error())
- }
+ tx.Commit()
return nil
}
func SslPatch(param interface{}, id string) error {
+ if id == "" {
+ return errno.New(errno.InvalidParam)
+ }
+
sslReq := &SslRequest{}
sslReq.Parse(param)
+ // trans
+ tx := conf.DB().Begin()
+ defer func() {
+ if r := recover(); r != nil {
+ tx.Rollback()
+ }
+ }()
+
+ // update mysql
+ ssl := Ssl{}
+ ssl.ID = uuid.FromStringOrNil(id)
+ if err := tx.Model(&ssl).Update("status", sslReq.Status).Error; err != nil {
+ tx.Rollback()
+ return errno.New(errno.DBWriteError, err.Error())
+ }
+
if _, err := sslReq.PatchToApisix(id); err != nil {
+ tx.Rollback()
if _, ok := err.(*errno.HttpError); ok {
return err
}
@@ -242,32 +345,45 @@ func SslPatch(param interface{}, id string) error {
return e
}
- ssl := Ssl{}
- ssl.ID = uuid.FromStringOrNil(id)
- if err := conf.DB().Model(&ssl).Update("status", sslReq.Status).Error; err != nil {
- return errno.New(errno.DBWriteError, err.Error())
- }
+ tx.Commit()
return nil
}
func SslDelete(id string) error {
+ if id == "" {
+ return errno.New(errno.InvalidParam)
+ }
+
+ // trans
+ tx := conf.DB().Begin()
+ defer func() {
+ if r := recover(); r != nil {
+ tx.Rollback()
+ }
+ }()
+
+ // delete from mysql
+ ssl := &Ssl{}
+ ssl.ID = uuid.FromStringOrNil(id)
+ if err := conf.DB().Delete(ssl).Error; err != nil {
+ tx.Rollback()
+ return errno.New(errno.DBDeleteError, err.Error())
+ }
+
// delete from apisix
request := &SslRequest{}
request.ID = id
if _, err := request.DeleteFromApisix(); err != nil {
+ tx.Rollback()
if _, ok := err.(*errno.HttpError); ok {
return err
}
e := errno.New(errno.ApisixSslDeleteError, err.Error())
return e
}
- // delete from mysql
- ssl := &Ssl{}
- ssl.ID = uuid.FromStringOrNil(id)
- if err := conf.DB().Delete(ssl).Error; err != nil {
- return errno.New(errno.DBDeleteError, err.Error())
- }
+
+ tx.Commit()
return nil
}
@@ -404,3 +520,70 @@ func ParseCert(crt, key string) (*Ssl, error) {
return &ssl, nil
}
}
+
+func CheckSniExists(param interface{}) error {
+ var hosts []string
+ if err := json.Unmarshal(param.([]byte), &hosts); err != nil {
+ return errno.FromMessage(errno.InvalidParam)
+ }
+
+ sslList := []Ssl{}
+ db := conf.DB().Table("ssls")
+ db = db.Where("`status` = ? ", 1)
+
+ condition := ""
+ args := []interface{}{}
+ first := true
+ for _, host := range hosts {
+ idx := strings.Index(host, "*")
+ keyword := strings.Replace(host, "*.", "", -1)
+ if idx == -1 {
+ if j := strings.Index(host, "."); j != -1 {
+ keyword = host[j:]
+ //just one `.`
+ if j := strings.Index(host[(j+1):], "."); j == -1 {
+ keyword = host
+ }
+ }
+ }
+ if first {
+ condition = condition + "`snis` like ?"
+ } else {
+ condition = condition + " or `snis` like ?"
+ }
+ first = false
+ args = append(args, "%"+keyword+"%")
+ }
+ db = db.Where(condition, args...)
+
+ if err := db.Find(&sslList).Error; err != nil {
+ return errno.FromMessage(errno.SslForSniNotExists, hosts[0])
+ }
+
+hre:
+ for _, host := range hosts {
+ for _, ssl := range sslList {
+ sslDto := SslDto{}
+ sslDto.Parse(&ssl)
+ for _, sni := range sslDto.Snis {
+ if sni == host {
+ continue hre
+ }
+ regx := strings.Replace(sni, ".", `\.`, -1)
+ regx = strings.Replace(regx, "*", `([^\.]+)`, -1)
+ regx = "^" + regx + "$"
+ if isOk, _ := regexp.MatchString(regx, host); isOk {
+ continue hre
+ }
+ }
+ }
+ return errno.FromMessage(errno.SslForSniNotExists, host)
+ }
+
+ return nil
+}
+
+func DeleteTestSslData() {
+ db := conf.DB().Table("ssls")
+ db.Where("snis LIKE ? OR (snis LIKE ? AND snis LIKE ? )", "%*.route.com%", "%r.com%", "%s.com%").Delete(Ssl{})
+}
diff --git a/api/service/ssl_test.go b/api/service/ssl_test.go
index 46607e8..46ecbb9 100644
--- a/api/service/ssl_test.go
+++ b/api/service/ssl_test.go
@@ -218,6 +218,16 @@ func TestSslCurd(t *testing.T) {
assert.Equal(true, strings.Contains(dm, "test3.com"))
}
+ //test3.com duplicate
+ param = []byte(`{
+ "cert": "-----BEGIN CERTIFICATE-----\nMIIEWjCCAsKgAwIBAgIRAMLLNCKEvgEQL22Hpox6E1kwDQYJKoZIhvcNAQELBQAw\nfzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSowKAYDVQQLDCFqdW54\ndWNoZW5AanVueHVkZUFpciAoanVueHUgY2hlbikxMTAvBgNVBAMMKG1rY2VydCBq\ndW54dWNoZW5AanVueHVkZUFpciAoanVueHUgY2hlbikwHhcNMTkwNjAxMDAwMDAw\nWhcNMzAwNjA5MTA0MjA1WjBVMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1lbnQg\nY2VydGlmaWNhdGUxKjAoBgNVBAsMIWp1bnh1Y2hlbkBqdW54dWRlQWlyIChqdW54\ndSBjaGVuKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL2l [...]
+ "key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC9pYPr8x5ESArP\nDx7+lFcVxW6y+sJbHbqziApFuXWilCcs1GUGZddQivUljpPLwHO496UOzM9ea4X0\nAeefFh8yP1122dTa2a06S2i9H1luZ+ISBPBG2H9x8v8TPIEVF7FjBHsatlgv0T30\nwkCGfOb1EgDUu9hOax3W2t2OSDK/G5/zVINbJti8WUJmVerQTqWJ4m8o+218cPJM\na+MPgf+YwwT5sC+fW3aGrRLNRY3R88/n21tSuDXR2WRKLWKpEIQktPSZ8BrMHQK4\nsviLOOJLf4LcO51Vprlf3kGsTxB1ROAcL3Zr9dSLD7I+oYn9t4F41yyB8eugmP0u\nCPz9GpZZAgMBAAECggEAZ2+8SVgb/QASLSdBL3d3HB/IJgSRNyM67qrXd [...]
+ }`)
+
+ err = SslCreate(param, u2.String())
+ assert.NotNil(err)
+ assert.Equal(errno.DuplicateSslCert.Code, err.(*errno.ManagerError).Code)
+
//a.com b.com fail
param = []byte(`{
"cert": "-----BEGIN CERTIFICATE-----\nMIICcTCCAdoCCQDQoPEll/bQizANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJD\nTjEOMAwGA1UECAwFbXlrZXkxDjAMBgNVBAcMBW15a2V5MQ4wDAYDVQQKDAVteWtl\neTEOMAwGA1UECwwFbXlrZXkxDjAMBgNVBAMMBWEuY29tMQ4wDAYDVQQDDAViLmNv\nbTEOMAwGA1UEAwwFYy5jb20wHhcNMjAwNjE3MDk1MDA0WhcNMzAwNjE1MDk1MDA0\nWjB9MQswCQYDVQQGEwJDTjEOMAwGA1UECAwFbXlrZXkxDjAMBgNVBAcMBW15a2V5\nMQ4wDAYDVQQKDAVteWtleTEOMAwGA1UECwwFbXlrZXkxDjAMBgNVBAMMBWEuY29t\nMQ4wDAYDVQQDDAViLmNvbTEOMAwGA1UEAwwFYy5jb20wgZ8wDQYJKoZI [...]
@@ -238,10 +248,29 @@ func TestSslCurd(t *testing.T) {
assert.Nil(err)
//list
- count, list, err := SslList(2, 1, -1, 0, 0, "")
+ count, list, err := SslList(2, 1, -1, 0, 0, "", "asc")
assert.Equal(true, count >= 2)
assert.Equal(1, len(list))
+ // check sni ssl exist
+ param = []byte(`[
+ "test3.com",
+ "www.test3.com",
+ "a.com"
+ ]`)
+
+ err = CheckSniExists(param)
+ assert.Nil(err)
+
+ // check sni ssl exist
+ param = []byte(`[
+ "test3.com",
+ "a.test3.com",
+ "b.com"
+ ]`)
+ err = CheckSniExists(param)
+ assert.NotNil(err)
+
// patch
param = []byte(`{
"status": 0
@@ -252,23 +281,70 @@ func TestSslCurd(t *testing.T) {
ssl, err = SslItem(u1.String())
assert.Equal(uint64(0), ssl.Status)
+ // check sni ssl exist --- disable test3
+ param = []byte(`[
+ "test3.com",
+ "www.test3.com",
+ "a.com"
+ ]`)
+
+ err = CheckSniExists(param)
+ assert.NotNil(err)
+
+ param = []byte(`[
+ "a.com"
+ ]`)
+ err = CheckSniExists(param)
+ assert.Nil(err)
+
//update
param = []byte(`{
- "cert": "-----BEGIN CERTIFICATE-----\nMIICcTCCAdoCCQDQoPEll/bQizANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJD\nTjEOMAwGA1UECAwFbXlrZXkxDjAMBgNVBAcMBW15a2V5MQ4wDAYDVQQKDAVteWtl\neTEOMAwGA1UECwwFbXlrZXkxDjAMBgNVBAMMBWEuY29tMQ4wDAYDVQQDDAViLmNv\nbTEOMAwGA1UEAwwFYy5jb20wHhcNMjAwNjE3MDk1MDA0WhcNMzAwNjE1MDk1MDA0\nWjB9MQswCQYDVQQGEwJDTjEOMAwGA1UECAwFbXlrZXkxDjAMBgNVBAcMBW15a2V5\nMQ4wDAYDVQQKDAVteWtleTEOMAwGA1UECwwFbXlrZXkxDjAMBgNVBAMMBWEuY29t\nMQ4wDAYDVQQDDAViLmNvbTEOMAwGA1UEAwwFYy5jb20wgZ8wDQYJKoZI [...]
- "key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQDRzKypXxcyW8mLg9GL3itpjjl0fc14vrwuZVukWqMio8eQC8iF\n0Fl1ERlZ8cNfsW5sVjdTF2cgD+3iWBT2tDyCb1tJG8h5Cbf06wsgS1TqqU6rxJAw\nCHXvRcfN+eerFw+Nzqvd5cga2l5kfn17qC8SUEC4QC1tbToNV91a8SmFzQIDAQAB\nAoGBAJIL/y4wqf8+ckES1G6fjG0AuvJjGQQzEuDhYjg5eFMG3EdkTIUKkxuxeYpp\niG43H/1+zyiipAFn1Vu5oW5T7cJEgC1YA39dERT605S5BrNWWHoZsgH+qmLoq7X+\njXMlmCagwlgwhUWMU2M1/LUbAl42384dK9u3EwcCgS//sFuBAkEA6mK52/Z03PB3\n0sS14eN7xFl96yc/NcneJ7Vy5APT0KGLo0j2S8gpOVW9EYrrzDzWg [...]
+ "cert": "-----BEGIN CERTIFICATE-----\nMIIEWzCCAsOgAwIBAgIQDYoN+el2w074sSGlyKVZFTANBgkqhkiG9w0BAQsFADB/\nMR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExKjAoBgNVBAsMIWp1bnh1\nY2hlbkBqdW54dWRlQWlyIChqdW54dSBjaGVuKTExMC8GA1UEAwwobWtjZXJ0IGp1\nbnh1Y2hlbkBqdW54dWRlQWlyIChqdW54dSBjaGVuKTAeFw0xOTA2MDEwMDAwMDBa\nFw0zMDA3MDQwNjA0MzNaMFUxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBj\nZXJ0aWZpY2F0ZTEqMCgGA1UECwwhanVueHVjaGVuQGp1bnh1ZGVBaXIgKGp1bnh1\nIGNoZW4pMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy21L [...]
+ "key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDLbUtgWZBileYy\nVSPJvBsnffybAqbFj7BMQksGHDu0Fxq71oWOPI7hpGmHiul0xCAKfiXBMwTaVsV1\nGEfNTn5LL0mwjWT738nyxlJpMiP2lo5fY9icQ6gWMjH3CklGl4s0GPLcgJLccLhL\nNNsNUCT0tkgsu//zW+3UqlDUeg+kT45nXBf4IvT/pGc2Z1qqCua20+/Sy6X3Ih4r\nPX0evbqnHuDpSegrSjytLAKpWaOhVMn55jbOabNKGoU9MsWC81mYx4vd0+aDqcZ2\nOOxuRdLnS2HJMNv+H5gi8Sxl+sNWBtXaDo/8qLJ8oLJ68xDF3Unn9QjgeW0jcxeC\numhqIOJXAgMBAAECggEARdPea9RSm4SY3+4ZusW3DHdSnmLqnCYWfhbDa [...]
}`)
err = SslUpdate(param, u1.String())
assert.Nil(err)
ssl, _ = SslItem(u1.String())
- assert.Equal(3, len(ssl.Snis))
+ assert.Equal(2, len(ssl.Snis))
+
+ // check sni ssl exist
+ param = []byte(`[
+ "example.com",
+ "www.example.com",
+ "a.example.com",
+ "a.com",
+ "b.com"
+ ]`)
+
+ err = CheckSniExists(param)
+ assert.NotNil(err)
+ assert.Equal(errno.SslForSniNotExists.Code, err.(*errno.ManagerError).Code)
+
+ param = []byte(`{
+ "status": 1
+ }`)
+ err = SslPatch(param, u1.String())
+ assert.Nil(err)
+
+ // check sni ssl exist
+ param = []byte(`[
+ "example.com",
+ "www.example.com",
+ "a.example.com",
+ "a.com",
+ "b.com"
+ ]`)
+
+ err = CheckSniExists(param)
+ assert.Nil(err)
//delete
err = SslDelete(u1.String())
assert.Nil(err)
- count2, _, err := SslList(2, 1, -1, 0, 0, "")
+ count2, _, err := SslList(2, 1, -1, 0, 0, "", "desc")
assert.Equal(count2, count-1)
err = SslDelete(u2.String())