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 <33000667+nic-c...@users.noreply.github.com>
AuthorDate: Sun Sep 20 15:46:54 2020 +0800

    feat: add SSL refactoring (#488)
    
    Co-authored-by: Vinci Xu <277040...@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 {

Reply via email to