You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by vi...@apache.org on 2020/09/20 07:47:00 UTC
[apisix-dashboard] branch refactor updated: feat: add SSL
refactoring (#488)
This is an automated email from the ASF dual-hosted git repository.
vinci pushed a commit to branch refactor
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git
The following commit(s) were added to refs/heads/refactor by this push:
new 075c1c3 feat: add SSL refactoring (#488)
075c1c3 is described below
commit 075c1c3d26caf45874514cb067535d65a2073b15
Author: nic-chen <33...@users.noreply.github.com>
AuthorDate: Sun Sep 20 15:46:54 2020 +0800
feat: add SSL refactoring (#488)
Co-authored-by: Vinci Xu <27...@qq.com>
---
api/internal/core/entity/entity.go | 2 +-
api/internal/handler/ssl/ssl.go | 252 +++++++++++++++++++++++++++++++++++++
api/route/base.go | 2 +
3 files changed, 255 insertions(+), 1 deletion(-)
diff --git a/api/internal/core/entity/entity.go b/api/internal/core/entity/entity.go
index c81377b..f588feb 100644
--- a/api/internal/core/entity/entity.go
+++ b/api/internal/core/entity/entity.go
@@ -109,7 +109,7 @@ type Consumer struct {
Plugins interface{} `json:"plugins,omitempty"`
}
-type Ssl struct {
+type SSL struct {
ID string `json:"id"`
Cert string `json:"cert"`
Key string `json:"key"`
diff --git a/api/internal/handler/ssl/ssl.go b/api/internal/handler/ssl/ssl.go
new file mode 100644
index 0000000..a9a0882
--- /dev/null
+++ b/api/internal/handler/ssl/ssl.go
@@ -0,0 +1,252 @@
+package ssl
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "encoding/pem"
+ "errors"
+ "fmt"
+ "reflect"
+ "strings"
+
+ "github.com/api7/go-jsonpatch"
+ "github.com/gin-gonic/gin"
+ "github.com/shiningrush/droplet"
+ "github.com/shiningrush/droplet/data"
+ "github.com/shiningrush/droplet/wrapper"
+ wgin "github.com/shiningrush/droplet/wrapper/gin"
+
+ "github.com/apisix/manager-api/internal/core/entity"
+ "github.com/apisix/manager-api/internal/core/store"
+ "github.com/apisix/manager-api/internal/handler"
+ "github.com/apisix/manager-api/internal/utils"
+)
+
+type Handler struct {
+ sslStore *store.GenericStore
+}
+
+func NewHandler() (handler.RouteRegister, error) {
+ s, err := store.NewGenericStore(store.GenericStoreOption{
+ BasePath: "/apisix/ssl",
+ ObjType: reflect.TypeOf(entity.SSL{}),
+ KeyFunc: func(obj interface{}) string {
+ r := obj.(*entity.SSL)
+ return r.ID
+ },
+ })
+ if err != nil {
+ return nil, err
+ }
+ if err := s.Init(); err != nil {
+ return nil, err
+ }
+
+ utils.AppendToClosers(s.Close)
+ return &Handler{
+ sslStore: s,
+ }, nil
+}
+
+func (h *Handler) ApplyRoute(r *gin.Engine) {
+ r.GET("/apisix/admin/ssl/:id", wgin.Wraps(h.Get,
+ wrapper.InputType(reflect.TypeOf(GetInput{}))))
+ r.GET("/apisix/admin/ssl", wgin.Wraps(h.List,
+ wrapper.InputType(reflect.TypeOf(ListInput{}))))
+ r.POST("/apisix/admin/ssl", wgin.Wraps(h.Create,
+ wrapper.InputType(reflect.TypeOf(entity.SSL{}))))
+ r.POST("/apisix/admin/ssl/validate", wgin.Wraps(h.Validate,
+ wrapper.InputType(reflect.TypeOf(entity.SSL{}))))
+ r.PUT("/apisix/admin/ssl/:id", wgin.Wraps(h.Update,
+ wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
+ r.PATCH("/apisix/admin/ssl/:id", wgin.Wraps(h.Patch,
+ wrapper.InputType(reflect.TypeOf(UpdateInput{}))))
+ r.DELETE("/apisix/admin/ssl", wgin.Wraps(h.BatchDelete,
+ wrapper.InputType(reflect.TypeOf(BatchDelete{}))))
+}
+
+type GetInput struct {
+ ID string `auto_read:"id,path" validate:"required"`
+}
+
+func (h *Handler) Get(c droplet.Context) (interface{}, error) {
+ input := c.Input().(*GetInput)
+
+ r, err := h.sslStore.Get(input.ID)
+ if err != nil {
+ return nil, err
+ }
+ return r, nil
+}
+
+type ListInput struct {
+ ID string `auto_read:"id,query"`
+ data.Pager
+}
+
+func (h *Handler) List(c droplet.Context) (interface{}, error) {
+ input := c.Input().(*ListInput)
+
+ ret, err := h.sslStore.List(store.ListInput{
+ Predicate: func(obj interface{}) bool {
+ if input.ID != "" {
+ return strings.Index(obj.(*entity.SSL).ID, input.ID) > 0
+ }
+ return true
+ },
+ PageSize: input.PageSize,
+ PageNumber: input.PageNumber,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return ret, nil
+}
+
+func (h *Handler) Create(c droplet.Context) (interface{}, error) {
+ input := c.Input().(*entity.SSL)
+
+ if err := h.sslStore.Create(c.Context(), input); err != nil {
+ return nil, err
+ }
+
+ return nil, nil
+}
+
+type UpdateInput struct {
+ ID string `auto_read:"id,path"`
+ entity.SSL
+}
+
+func (h *Handler) Update(c droplet.Context) (interface{}, error) {
+ input := c.Input().(*UpdateInput)
+ input.SSL.ID = input.ID
+
+ if err := h.sslStore.Update(c.Context(), &input.SSL); err != nil {
+ return nil, err
+ }
+
+ return nil, nil
+}
+
+func (h *Handler) Patch(c droplet.Context) (interface{}, error) {
+ input := c.Input().(*UpdateInput)
+ arr := strings.Split(input.ID, "/")
+ var subPath string
+ if len(arr) > 1 {
+ input.ID = arr[0]
+ subPath = arr[1]
+ }
+
+ stored, err := h.sslStore.Get(input.ID)
+ if err != nil {
+ return nil, err
+ }
+
+ var patch jsonpatch.Patch
+ if subPath != "" {
+ patch = jsonpatch.Patch{
+ Operations: []jsonpatch.PatchOperation{
+ {Op: jsonpatch.Replace, Path: subPath, Value: c.Input()},
+ },
+ }
+ } else {
+ patch, err = jsonpatch.MakePatch(stored, input.SSL)
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ err = patch.Apply(&stored)
+ if err != nil {
+ panic(err)
+ }
+
+ if err := h.sslStore.Update(c.Context(), &stored); err != nil {
+ return nil, err
+ }
+
+ return nil, nil
+}
+
+type BatchDelete struct {
+ Ids string `auto_read:"ids,query"`
+}
+
+func (h *Handler) BatchDelete(c droplet.Context) (interface{}, error) {
+ input := c.Input().(*BatchDelete)
+
+ if err := h.sslStore.BatchDelete(c.Context(), strings.Split(input.Ids, ",")); err != nil {
+ return nil, err
+ }
+
+ return nil, nil
+}
+
+func ParseCert(crt, key string) (*entity.SSL, error) {
+ if crt == "" || key == "" {
+ return nil, errors.New("invalid certificate")
+ }
+
+ certDERBlock, _ := pem.Decode([]byte(crt))
+ if certDERBlock == nil {
+ return nil, errors.New("Certificate resolution failed")
+ }
+ // match
+ _, err := tls.X509KeyPair([]byte(crt), []byte(key))
+ if err != nil {
+ return nil, errors.New("key and cert don't match")
+ }
+
+ x509Cert, err := x509.ParseCertificate(certDERBlock.Bytes)
+
+ if err != nil {
+ return nil, errors.New("Certificate resolution failed")
+ } else {
+ ssl := entity.SSL{}
+ //domain
+ snis := []string{}
+ if x509Cert.DNSNames != nil && len(x509Cert.DNSNames) > 0 {
+ snis = x509Cert.DNSNames
+ } else if x509Cert.IPAddresses != nil && len(x509Cert.IPAddresses) > 0 {
+ for _, ip := range x509Cert.IPAddresses {
+ snis = append(snis, ip.String())
+ }
+ } else {
+ if x509Cert.Subject.Names != nil && len(x509Cert.Subject.Names) > 1 {
+ var attributeTypeNames = map[string]string{
+ "2.5.4.6": "C",
+ "2.5.4.10": "O",
+ "2.5.4.11": "OU",
+ "2.5.4.3": "CN",
+ "2.5.4.5": "SERIALNUMBER",
+ "2.5.4.7": "L",
+ "2.5.4.8": "ST",
+ "2.5.4.9": "STREET",
+ "2.5.4.17": "POSTALCODE",
+ }
+ for _, tv := range x509Cert.Subject.Names {
+ oidString := tv.Type.String()
+ typeName, ok := attributeTypeNames[oidString]
+ if ok && typeName == "CN" {
+ valueString := fmt.Sprint(tv.Value)
+ snis = append(snis, valueString)
+ }
+ }
+ }
+ }
+
+ return &ssl, nil
+ }
+}
+
+func (h *Handler) Validate(c droplet.Context) (interface{}, error) {
+ input := c.Input().(*entity.SSL)
+ ssl, err := ParseCert(input.Cert, input.Key)
+ if err != nil {
+ return nil, err
+ }
+
+ return ssl, nil
+}
diff --git a/api/route/base.go b/api/route/base.go
index b9fe141..2afd68e 100644
--- a/api/route/base.go
+++ b/api/route/base.go
@@ -27,6 +27,7 @@ import (
"github.com/apisix/manager-api/internal/handler"
"github.com/apisix/manager-api/internal/handler/consumer"
"github.com/apisix/manager-api/internal/handler/route"
+ "github.com/apisix/manager-api/internal/handler/ssl"
)
func SetUpRouter() *gin.Engine {
@@ -51,6 +52,7 @@ func SetUpRouter() *gin.Engine {
factories := []handler.RegisterFactory{
route.NewHandler,
+ ssl.NewHandler,
consumer.NewHandler,
}
for i := range factories {