package middleware
import (
"context"
"net/http"
"sync"
"time"
"github.com/goodrain/rainbond/api/handler"
rsalicense "github.com/goodrain/rainbond/api/util/license"
"github.com/goodrain/rainbond/pkg/component/k8s"
httputil "github.com/goodrain/rainbond/util/http"
"github.com/sirupsen/logrus"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type License struct{}
func NewLicense() *License {
handler.InvalidateLicenseCacheFunc = InvalidateRSALicenseCache
return &License{}
}
var rsaLicenseCache struct {
sync.RWMutex
token *rsalicense.LicenseToken
valid bool
reason string
expireAt time.Time
}
func InvalidateRSALicenseCache() {
rsaLicenseCache.Lock()
rsaLicenseCache.token = nil
rsaLicenseCache.valid = false
rsaLicenseCache.reason = ""
rsaLicenseCache.expireAt = time.Time{}
rsaLicenseCache.Unlock()
}
func (l *License) Verify(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := k8s.Default().RainbondClient.RainbondV1alpha1().RBDPlugins(metav1.NamespaceNone).Get(
context.TODO(), "rainbond-enterprise-base", metav1.GetOptions{})
if err != nil {
next.ServeHTTP(w, r)
return
}
if verifyRSALicense() {
next.ServeHTTP(w, r)
return
}
httputil.Return(r, w, 401, httputil.ResponseBody{
Bean: map[string]interface{}{
"msg": "invalid license",
"code": 10400,
},
})
})
}
func verifyRSALicense() bool {
now := time.Now()
rsaLicenseCache.RLock()
if rsaLicenseCache.token != nil && rsaLicenseCache.expireAt.After(now) {
valid := rsaLicenseCache.valid
rsaLicenseCache.RUnlock()
return valid
}
rsaLicenseCache.RUnlock()
ctx := context.TODO()
cm, err := k8s.Default().Clientset.CoreV1().ConfigMaps("rbd-system").Get(ctx, "rbd-license-info", metav1.GetOptions{})
if err != nil {
if k8serrors.IsNotFound(err) {
logrus.Debug("No rbd-license-info ConfigMap found")
} else {
logrus.Errorf("Failed to read license ConfigMap: %v", err)
}
return false
}
licenseData := cm.Data["license"]
if licenseData == "" {
return false
}
token, err := rsalicense.ParseLicenseJSON(licenseData)
if err != nil {
logrus.Errorf("Failed to decode RSA license: %v", err)
cacheRSAResult(nil, false, err.Error(), now)
return false
}
pubKey, err := rsalicense.GetEmbeddedPublicKey()
if err != nil {
logrus.Errorf("Failed to get embedded public key: %v", err)
return false
}
if err := rsalicense.VerifySignature(token, pubKey); err != nil {
logrus.Errorf("RSA license signature verification failed: %v", err)
cacheRSAResult(token, false, "invalid signature", now)
return false
}
unix := now.Unix()
if unix < token.StartAt {
cacheRSAResult(token, false, "license not yet valid", now)
return false
}
if unix > token.ExpireAt {
cacheRSAResult(token, false, "license expired", now)
return false
}
cacheRSAResult(token, true, "", now)
return true
}
func cacheRSAResult(token *rsalicense.LicenseToken, valid bool, reason string, now time.Time) {
rsaLicenseCache.Lock()
rsaLicenseCache.token = token
rsaLicenseCache.valid = valid
rsaLicenseCache.reason = reason
rsaLicenseCache.expireAt = now.Add(10 * time.Minute)
rsaLicenseCache.Unlock()
}