Skip to content

Commit

Permalink
feat: support authz
Browse files Browse the repository at this point in the history
Signed-off-by: yangk <[email protected]>
  • Loading branch information
yangkaa committed Oct 17, 2024
1 parent 3e62106 commit c0e86f5
Show file tree
Hide file tree
Showing 8 changed files with 283 additions and 127 deletions.
5 changes: 3 additions & 2 deletions api/api_routers/license/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ import (
"github.com/go-chi/chi"
)

//License license struct
// License license struct
type License struct{}

//Routes routes
// Routes routes
func Routes() chi.Router {
r := chi.NewRouter()
r.Get("/", controller.GetLicenseManager().Getlicense)
r.Get("/features", controller.GetLicenseManager().GetlicenseFeature)
return r
}
12 changes: 6 additions & 6 deletions api/api_routers/version2/v2Plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
"github.com/go-chi/chi"
)

//PluginRouter plugin router
// PluginRouter plugin router
func (v2 *V2) pluginRouter() chi.Router {
r := chi.NewRouter()
//初始化应用信息
Expand All @@ -47,13 +47,13 @@ func (v2 *V2) serviceRelatePluginRouter() chi.Router {
r := chi.NewRouter()
//service relate plugin
// v2/tenant/tenant_name/services/service_alias/plugin/xxx
r.Post("/", middleware.WrapEL(controller.GetManager().PluginSet, dbmodel.TargetTypeService, "create-service-plugin", dbmodel.SYNEVENTTYPE))
r.Put("/", middleware.WrapEL(controller.GetManager().PluginSet, dbmodel.TargetTypeService, "update-service-plugin", dbmodel.SYNEVENTTYPE))
r.Post("/", middleware.WrapEL(controller.GetManager().PluginSet, dbmodel.TargetTypeService, "create-service-plugin", dbmodel.SYNEVENTTYPE, false))
r.Put("/", middleware.WrapEL(controller.GetManager().PluginSet, dbmodel.TargetTypeService, "update-service-plugin", dbmodel.SYNEVENTTYPE, false))
r.Get("/", controller.GetManager().PluginSet)
r.Delete("/{plugin_id}", middleware.WrapEL(controller.GetManager().DeletePluginRelation, dbmodel.TargetTypeService, "delete-service-plugin", dbmodel.SYNEVENTTYPE))
r.Delete("/{plugin_id}", middleware.WrapEL(controller.GetManager().DeletePluginRelation, dbmodel.TargetTypeService, "delete-service-plugin", dbmodel.SYNEVENTTYPE, false))
// app plugin config supdate
r.Post("/{plugin_id}/setenv", middleware.WrapEL(controller.GetManager().UpdateVersionEnv, dbmodel.TargetTypeService, "update-service-plugin-config", dbmodel.SYNEVENTTYPE))
r.Put("/{plugin_id}/upenv", middleware.WrapEL(controller.GetManager().UpdateVersionEnv, dbmodel.TargetTypeService, "update-service-plugin-config", dbmodel.SYNEVENTTYPE))
r.Post("/{plugin_id}/setenv", middleware.WrapEL(controller.GetManager().UpdateVersionEnv, dbmodel.TargetTypeService, "update-service-plugin-config", dbmodel.SYNEVENTTYPE, false))
r.Put("/{plugin_id}/upenv", middleware.WrapEL(controller.GetManager().UpdateVersionEnv, dbmodel.TargetTypeService, "update-service-plugin-config", dbmodel.SYNEVENTTYPE, false))
//deprecated
r.Get("/{plugin_id}/envs", controller.GetManager().GePluginEnvWhichCanBeSet)
return r
Expand Down
124 changes: 62 additions & 62 deletions api/api_routers/version2/v2Routers.go

Large diffs are not rendered by default.

27 changes: 26 additions & 1 deletion api/controller/batch_operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package controller
import (
"context"
"fmt"
"github.com/goodrain/rainbond/api/middleware"
"github.com/goodrain/rainbond/db"
"net/http"

Expand Down Expand Up @@ -48,6 +49,11 @@ func BatchOperation(w http.ResponseWriter, r *http.Request) {
var f func(ctx context.Context, tenant *dbmodel.Tenants, operator string, batchOpReqs model.BatchOpRequesters) (model.BatchOpResult, error)
switch build.Body.Operation {
case "build":
err := middleware.LicenseVerification(r, true)
if err != nil {
err.Handle(r, w)
return
}
for _, build := range build.Body.Builds {
build.TenantName = tenant.Name
batchOpReqs = append(batchOpReqs, build)
Expand All @@ -59,6 +65,11 @@ func BatchOperation(w http.ResponseWriter, r *http.Request) {
}
f = handler.GetBatchOperationHandler().Build
case "start":
err := middleware.LicenseVerification(r, true)
if err != nil {
err.Handle(r, w)
return
}
for _, start := range build.Body.Starts {
batchOpReqs = append(batchOpReqs, start)
}
Expand All @@ -69,18 +80,32 @@ func BatchOperation(w http.ResponseWriter, r *http.Request) {
}
f = handler.GetBatchOperationHandler().Start
case "stop":

err := middleware.LicenseVerification(r, true)
if err != nil {
err.Handle(r, w)
return
}
for _, stop := range build.Body.Stops {
batchOpReqs = append(batchOpReqs, stop)
}
f = handler.GetBatchOperationHandler().Stop
case "upgrade":
err := middleware.LicenseVerification(r, true)
if err != nil {
err.Handle(r, w)
return
}
for _, upgrade := range build.Body.Upgrades {
batchOpReqs = append(batchOpReqs, upgrade)
}
b := handler.GetBatchOperationHandler()
f = b.Upgrade
case "export":
err := middleware.LicenseVerification(r, true)
if err != nil {
err.Handle(r, w)
return
}
for _, build := range build.Body.Builds {
build.TenantName = tenant.Name
batchOpReqs = append(batchOpReqs, build)
Expand Down
22 changes: 15 additions & 7 deletions api/controller/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@
package controller

import (
"net/http"

"github.com/goodrain/rainbond/api/middleware"
"github.com/goodrain/rainbond/api/util/license"
httputil "github.com/goodrain/rainbond/util/http"
"net/http"
)

//LicenseManager license manager
// LicenseManager license manager
type LicenseManager struct{}

var licenseManager *LicenseManager

//GetLicenseManager get license Manager
// GetLicenseManager get license Manager
func GetLicenseManager() *LicenseManager {
if licenseManager != nil {
return licenseManager
Expand All @@ -39,14 +39,22 @@ func GetLicenseManager() *LicenseManager {
return licenseManager
}

// Deprecated
func (l *LicenseManager) GetlicenseFeature(w http.ResponseWriter, r *http.Request) {
//The following features are designed for license control.
// GPU Support
// Windows Container Support
// Gateway security control
features := []license.Feature{}
if lic := license.ReadLicense(); lic != nil {
features = lic.Features
}
httputil.ReturnSuccess(r, w, features)
}

// Getlicense -
func (l *LicenseManager) Getlicense(w http.ResponseWriter, r *http.Request) {
err := middleware.LicenseVerification(r, true)
if err != nil {
httputil.ReturnError(r, w, 400, err.Error())
return
}
httputil.ReturnSuccess(r, w, middleware.LicenseCache.Data)
}
5 changes: 0 additions & 5 deletions api/handler/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ import (
apimodel "github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/api/util"
"github.com/goodrain/rainbond/api/util/bcode"
"github.com/goodrain/rainbond/api/util/license"
"github.com/goodrain/rainbond/builder/parser"
"github.com/goodrain/rainbond/db"
dberr "github.com/goodrain/rainbond/db/errors"
Expand Down Expand Up @@ -435,10 +434,6 @@ func (s *ServiceAction) ServiceVertical(ctx context.Context, vs *model.VerticalS
if vs.ContainerGPU != nil {
service.ContainerGPU = *vs.ContainerGPU
}
licenseInfo := license.ReadLicense()
if licenseInfo == nil || !licenseInfo.HaveFeature("GPU") {
service.ContainerGPU = 0
}
if service.ContainerMemory == oldMemory && service.ContainerCPU == oldCPU && service.ContainerGPU == oldGPU {
db.GetManager().ServiceEventDao().SetEventStatus(ctx, dbmodel.EventStatusSuccess)
return nil
Expand Down
110 changes: 107 additions & 3 deletions api/middleware/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,27 @@ import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/go-chi/chi"
"github.com/goodrain/rainbond/api/handler"
"github.com/goodrain/rainbond/api/util"
ctxutil "github.com/goodrain/rainbond/api/util/ctx"
"github.com/goodrain/rainbond/api/util/license"
"github.com/goodrain/rainbond/db"
dbmodel "github.com/goodrain/rainbond/db/model"
"github.com/goodrain/rainbond/event"
"github.com/goodrain/rainbond/pkg/component/k8s"
httputil "github.com/goodrain/rainbond/util/http"
"github.com/jinzhu/gorm"
"github.com/sirupsen/logrus"
"io"
"io/ioutil"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"net/http"
"strconv"
"strings"
"sync"
"time"
)

var pool []string
Expand Down Expand Up @@ -238,8 +246,13 @@ func (w *resWriter) WriteHeader(statusCode int) {
}

// WrapEL wrap eventlog, handle event log before and after process
func WrapEL(f http.HandlerFunc, target, optType string, synType int) http.HandlerFunc {
func WrapEL(f http.HandlerFunc, target, optType string, synType int, resourceValidation bool) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
err := LicenseVerification(r, resourceValidation)
if err != nil {
err.Handle(r, w)
return
}
var (
serviceKind string
)
Expand All @@ -250,7 +263,7 @@ func WrapEL(f http.HandlerFunc, target, optType string, synType int) http.Handle
}

if r.Method != "GET" {
body, err := ioutil.ReadAll(r.Body)
body, err := io.ReadAll(r.Body)
if err != nil {
logrus.Warningf("error reading request body: %v", err)
} else {
Expand Down Expand Up @@ -319,6 +332,97 @@ func debugRequestBody(r *http.Request) {
logrus.Debugf("method: %s; uri: %s; body: %s", r.Method, r.RequestURI, string(body))

// set a new body, which will simulate the same data we read
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
r.Body = io.NopCloser(bytes.NewBuffer(body))
}
}

// LicenseCache for caching information
var LicenseCache struct {
sync.RWMutex
Data *license.LicenseResp
InfoBody string
ExpireAt time.Time
}

// LicenseVerification verification Information
func LicenseVerification(r *http.Request, resourceValidation bool) *util.APIHandleError {
if !resourceValidation {
return nil
}
_, err := k8s.Default().RainbondClient.RainbondV1alpha1().RBDPlugins(metav1.NamespaceNone).Get(context.TODO(), "rainbond-enterprise-base", metav1.GetOptions{})
if err != nil {
// If the plugin does not exist, it is considered an open source version and returned directly without License verification.
return nil
}
enterpriseID, infoBody, actualCluster, actualNode, actualMemory, err := ParseLicenseParam(r)
if err != nil {
return util.CreateAPIHandleError(412, fmt.Errorf("authorize_cluster_lack_of_license"))
}

now := time.Now()
// check whether the cache is valid
LicenseCache.RLock()
if LicenseCache.Data != nil && LicenseCache.ExpireAt.After(now) && LicenseCache.InfoBody == infoBody {
logrus.Debug("Using cached license data")
LicenseCache.RUnlock()
return validateLicense(*LicenseCache.Data, actualCluster, actualNode, actualMemory, now)
}
LicenseCache.RUnlock()

logrus.Debug("Fetching new license data")
lic := license.ReadLicense(enterpriseID, infoBody)
if lic == nil {
return util.CreateAPIHandleError(400, fmt.Errorf("invaild license"))
}

// Update the cache for 10 minute
resp := lic.SetResp(actualCluster, actualNode, actualMemory)
LicenseCache.Lock()
LicenseCache.Data = resp
LicenseCache.ExpireAt = now.Add(10 * time.Minute)
LicenseCache.InfoBody = infoBody
LicenseCache.Unlock()

return validateLicense(*resp, actualCluster, actualNode, actualMemory, now)
}

// validateLicense - 执行 License 验证逻辑
func validateLicense(licenseResp license.LicenseResp, actualCluster, actualNode, actualMemory int64, now time.Time) *util.APIHandleError {
if licenseResp.ExpectCluster != -1 && actualCluster > licenseResp.ExpectCluster {
return util.CreateAPIHandleError(412, fmt.Errorf("authorize_cluster_lack_of_license"))
}
if licenseResp.ExpectNode != -1 && actualNode > licenseResp.ExpectNode {
return util.CreateAPIHandleError(412, fmt.Errorf("authorize_cluster_lack_of_node"))
}
if licenseResp.ExpectMemory != -1 && actualMemory > licenseResp.ExpectMemory {
return util.CreateAPIHandleError(412, fmt.Errorf("authorize_cluster_lack_of_memory"))
}
if licenseResp.EndTime != "" {
endTime, err := time.Parse("2006-01-02 15:04:05", licenseResp.EndTime)
if err == nil && endTime.Before(now) {
return util.CreateAPIHandleError(412, fmt.Errorf("authorize_expiration_of_authorization"))
}
}
return nil
}

func ParseLicenseParam(r *http.Request) (enterpriseID, infoBody string, actualCluster, actualNode, actualMemory int64, err error) {
enterpriseID = r.Header.Get("enterprise_id")
infoBody = r.Header.Get("info_body")
actualClusterStr := r.Header.Get("actual_cluster")
actualNodeStr := r.Header.Get("actual_node")
actualMemoryStr := r.Header.Get("actual_memory")
actualCluster, err = strconv.ParseInt(actualClusterStr, 10, 64)
if err != nil {
return
}
actualNode, err = strconv.ParseInt(actualNodeStr, 10, 64)
if err != nil {
return
}
actualMemory, err = strconv.ParseInt(actualMemoryStr, 10, 64)
if err != nil {
return
}
return
}
Loading

0 comments on commit c0e86f5

Please sign in to comment.