mirror of
https://github.com/niusmallnan/steve.git
synced 2025-09-10 01:20:11 +00:00
Fix update/patch and add webhook auth
This commit is contained in:
2
vendor/github.com/rancher/norman/pkg/api/server.go
generated
vendored
2
vendor/github.com/rancher/norman/pkg/api/server.go
generated
vendored
@@ -195,6 +195,8 @@ func determineVerb(apiOp *types.APIRequest) Verb {
|
||||
return Get
|
||||
case http.MethodPost:
|
||||
return Create
|
||||
case http.MethodPatch:
|
||||
return Update
|
||||
case http.MethodPut:
|
||||
return Update
|
||||
case http.MethodDelete:
|
||||
|
93
vendor/github.com/rancher/norman/pkg/auth/filter.go
generated
vendored
Normal file
93
vendor/github.com/rancher/norman/pkg/auth/filter.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
v1 "k8s.io/api/authentication/v1"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
|
||||
)
|
||||
|
||||
type Authenticator interface {
|
||||
Authenticate(req *http.Request) (user.Info, bool, error)
|
||||
}
|
||||
|
||||
func NewWebhookAuthenticator(kubeConfigFile string) (Authenticator, error) {
|
||||
wh, err := webhook.New(kubeConfigFile, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &webhookAuth{
|
||||
auth: wh,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewWebhookMiddleware(kubeConfigFile string) (func(http.ResponseWriter, *http.Request, http.Handler), error) {
|
||||
auth, err := NewWebhookAuthenticator(kubeConfigFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ToMiddleware(auth), nil
|
||||
}
|
||||
|
||||
type webhookAuth struct {
|
||||
auth authenticator.Token
|
||||
}
|
||||
|
||||
func (w *webhookAuth) Authenticate(req *http.Request) (user.Info, bool, error) {
|
||||
token := req.Header.Get("Authorization")
|
||||
if strings.HasPrefix(token, "Bearer ") {
|
||||
token = strings.TrimPrefix(token, "Bearer ")
|
||||
} else {
|
||||
token = ""
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
cookie, err := req.Cookie("R_SESS")
|
||||
if err != nil && err != http.ErrNoCookie {
|
||||
return nil, false, err
|
||||
} else if err != http.ErrNoCookie && len(cookie.Value) > 0 {
|
||||
token = "cookie://" + cookie.Value
|
||||
}
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
resp, ok, err := w.auth.AuthenticateToken(req.Context(), token)
|
||||
if resp == nil {
|
||||
return nil, ok, err
|
||||
}
|
||||
return resp.User, ok, err
|
||||
}
|
||||
|
||||
func ToMiddleware(auth Authenticator) func(rw http.ResponseWriter, req *http.Request, next http.Handler) {
|
||||
return func(rw http.ResponseWriter, req *http.Request, next http.Handler) {
|
||||
info, ok, err := auth.Authenticate(req)
|
||||
if err != nil {
|
||||
rw.WriteHeader(http.StatusServiceUnavailable)
|
||||
rw.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if !ok {
|
||||
rw.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := request.WithUser(req.Context(), info)
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
req.Header.Set(v1.ImpersonateUserHeader, info.GetName())
|
||||
for _, group := range info.GetGroups() {
|
||||
req.Header.Set(v1.ImpersonateGroupHeader, group)
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, req)
|
||||
}
|
||||
}
|
2
vendor/github.com/rancher/norman/pkg/data/data.go
generated
vendored
2
vendor/github.com/rancher/norman/pkg/data/data.go
generated
vendored
@@ -22,7 +22,7 @@ func (o Object) Map(names ...string) Object {
|
||||
func (o Object) Slice(names ...string) (result []Object) {
|
||||
v := values.GetValueN(o, names...)
|
||||
for _, item := range convert.ToInterfaceSlice(v) {
|
||||
result = append(result, Object(convert.ToMapInterface(item)))
|
||||
result = append(result, convert.ToMapInterface(item))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
40
vendor/github.com/rancher/norman/pkg/store/proxy/proxy_store.go
generated
vendored
40
vendor/github.com/rancher/norman/pkg/store/proxy/proxy_store.go
generated
vendored
@@ -1,17 +1,22 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
types2 "k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/rancher/norman/pkg/types/convert"
|
||||
|
||||
errors2 "github.com/pkg/errors"
|
||||
|
||||
"github.com/rancher/norman/pkg/types"
|
||||
"github.com/rancher/norman/pkg/types/convert/merge"
|
||||
"github.com/rancher/norman/pkg/types/values"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
@@ -260,31 +265,32 @@ func (s *Store) Update(apiOp *types.APIRequest, schema *types.Schema, params typ
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
resp, err := k8sClient.Get(id, metav1.GetOptions{})
|
||||
if apiOp.Method == http.MethodPatch {
|
||||
bytes, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
|
||||
resourceVersion, existing := resp.GetResourceVersion(), resp.Object
|
||||
existing = merge.APIUpdateMerge(schema.InternalSchema, apiOp.Schemas, existing, data, apiOp.Option("replace") == "true")
|
||||
|
||||
values.PutValue(existing, resourceVersion, "metadata", "resourceVersion")
|
||||
if len(apiOp.Namespaces) > 0 {
|
||||
values.PutValue(existing, apiOp.Namespaces[0], "metadata", "namespace")
|
||||
}
|
||||
values.PutValue(existing, id, "metadata", "name")
|
||||
|
||||
resp, err = k8sClient.Update(&unstructured.Unstructured{Object: existing}, metav1.UpdateOptions{})
|
||||
if errors.IsConflict(err) {
|
||||
continue
|
||||
} else if err != nil {
|
||||
resp, err := k8sClient.Patch(id, types2.StrategicMergePatchType, bytes, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
|
||||
_, result, err = s.singleResult(apiOp, schema, resp)
|
||||
return types.ToAPI(result), err
|
||||
}
|
||||
|
||||
resourceVersion := convert.ToString(values.GetValueN(data, "metadata", "resourceVersion"))
|
||||
if resourceVersion == "" {
|
||||
return types.APIObject{}, fmt.Errorf("metadata.resourceVersion is required for update")
|
||||
}
|
||||
|
||||
resp, err := k8sClient.Update(&unstructured.Unstructured{Object: data}, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return types.APIObject{}, err
|
||||
}
|
||||
|
||||
_, result, err = s.singleResult(apiOp, schema, resp)
|
||||
return types.ToAPI(result), err
|
||||
}
|
||||
|
||||
|
156
vendor/github.com/rancher/norman/pkg/types/convert/merge/merge.go
generated
vendored
156
vendor/github.com/rancher/norman/pkg/types/convert/merge/merge.go
generated
vendored
@@ -1,156 +0,0 @@
|
||||
package merge
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/rancher/norman/pkg/types"
|
||||
convert2 "github.com/rancher/norman/pkg/types/convert"
|
||||
definition2 "github.com/rancher/norman/pkg/types/definition"
|
||||
)
|
||||
|
||||
func APIUpdateMerge(schema *types.Schema, schemas *types.Schemas, dest, src map[string]interface{}, replace bool) map[string]interface{} {
|
||||
result := UpdateMerge(schema, schemas, dest, src, replace)
|
||||
if s, ok := dest["status"]; ok {
|
||||
result["status"] = s
|
||||
}
|
||||
if m, ok := dest["metadata"]; ok {
|
||||
result["metadata"] = mergeMetadata(convert2.ToMapInterface(m), convert2.ToMapInterface(src["metadata"]))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func UpdateMerge(schema *types.Schema, schemas *types.Schemas, dest, src map[string]interface{}, replace bool) map[string]interface{} {
|
||||
return mergeMaps("", nil, schema, schemas, replace, dest, src)
|
||||
}
|
||||
|
||||
func isProtected(k string) bool {
|
||||
if !strings.Contains(k, "cattle.io/") || (isField(k) && k != "field.cattle.io/creatorId") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isField(k string) bool {
|
||||
return strings.HasPrefix(k, "field.cattle.io/")
|
||||
}
|
||||
|
||||
func mergeProtected(dest, src map[string]interface{}) map[string]interface{} {
|
||||
if src == nil {
|
||||
return dest
|
||||
}
|
||||
|
||||
result := copyMap(dest)
|
||||
|
||||
for k, v := range src {
|
||||
if isProtected(k) {
|
||||
continue
|
||||
}
|
||||
result[k] = v
|
||||
}
|
||||
|
||||
for k := range dest {
|
||||
if isProtected(k) || isField(k) {
|
||||
continue
|
||||
}
|
||||
if _, ok := src[k]; !ok {
|
||||
delete(result, k)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func mergeMetadata(dest map[string]interface{}, src map[string]interface{}) map[string]interface{} {
|
||||
result := copyMap(dest)
|
||||
|
||||
labels := convert2.ToMapInterface(dest["labels"])
|
||||
srcLabels := convert2.ToMapInterface(src["labels"])
|
||||
labels = mergeProtected(labels, srcLabels)
|
||||
|
||||
annotations := convert2.ToMapInterface(dest["annotations"])
|
||||
srcAnnotation := convert2.ToMapInterface(src["annotations"])
|
||||
annotations = mergeProtected(annotations, srcAnnotation)
|
||||
|
||||
result["labels"] = labels
|
||||
result["annotations"] = annotations
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func merge(field, fieldType string, parentSchema, schema *types.Schema, schemas *types.Schemas, replace bool, dest, src interface{}) interface{} {
|
||||
if isMap(field, schema, schemas) {
|
||||
return src
|
||||
}
|
||||
|
||||
sm, smOk := src.(map[string]interface{})
|
||||
dm, dmOk := dest.(map[string]interface{})
|
||||
if smOk && dmOk {
|
||||
fieldType, fieldSchema := getSchema(field, fieldType, parentSchema, schema, schemas)
|
||||
return mergeMaps(fieldType, schema, fieldSchema, schemas, replace, dm, sm)
|
||||
}
|
||||
return src
|
||||
}
|
||||
|
||||
func getSchema(field, parentFieldType string, parentSchema, schema *types.Schema, schemas *types.Schemas) (string, *types.Schema) {
|
||||
if schema == nil {
|
||||
if definition2.IsMapType(parentFieldType) && parentSchema != nil {
|
||||
subType := definition2.SubType(parentFieldType)
|
||||
s := schemas.Schema(subType)
|
||||
if s != nil && s.InternalSchema != nil {
|
||||
s = s.InternalSchema
|
||||
}
|
||||
return subType, s
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
fieldType := schema.ResourceFields[field].Type
|
||||
s := schemas.Schema(fieldType)
|
||||
if s != nil && s.InternalSchema != nil {
|
||||
return fieldType, s.InternalSchema
|
||||
}
|
||||
return fieldType, s
|
||||
}
|
||||
|
||||
func isMap(field string, schema *types.Schema, schemas *types.Schemas) bool {
|
||||
if schema == nil {
|
||||
return false
|
||||
}
|
||||
f := schema.ResourceFields[field]
|
||||
mapType := definition2.IsMapType(f.Type)
|
||||
if !mapType {
|
||||
return false
|
||||
}
|
||||
|
||||
subType := definition2.SubType(f.Type)
|
||||
return schemas.Schema(subType) == nil
|
||||
}
|
||||
|
||||
func mergeMaps(fieldType string, parentSchema, schema *types.Schema, schemas *types.Schemas, replace bool, dest map[string]interface{}, src map[string]interface{}) map[string]interface{} {
|
||||
result := copyMapReplace(schema, dest, replace)
|
||||
for k, v := range src {
|
||||
result[k] = merge(k, fieldType, parentSchema, schema, schemas, replace, dest[k], v)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func copyMap(src map[string]interface{}) map[string]interface{} {
|
||||
result := map[string]interface{}{}
|
||||
for k, v := range src {
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func copyMapReplace(schema *types.Schema, src map[string]interface{}, replace bool) map[string]interface{} {
|
||||
result := map[string]interface{}{}
|
||||
for k, v := range src {
|
||||
if replace && schema != nil {
|
||||
f := schema.ResourceFields[k]
|
||||
if f.Update {
|
||||
continue
|
||||
}
|
||||
}
|
||||
result[k] = v
|
||||
}
|
||||
return result
|
||||
}
|
9
vendor/github.com/rancher/norman/pkg/urlbuilder/base.go
generated
vendored
9
vendor/github.com/rancher/norman/pkg/urlbuilder/base.go
generated
vendored
@@ -45,7 +45,14 @@ func getHost(r *http.Request, scheme string) string {
|
||||
func getScheme(r *http.Request) string {
|
||||
scheme := r.Header.Get(ForwardedProtoHeader)
|
||||
if scheme != "" {
|
||||
return scheme
|
||||
switch scheme {
|
||||
case "ws":
|
||||
return "http"
|
||||
case "wss":
|
||||
return "https"
|
||||
default:
|
||||
return scheme
|
||||
}
|
||||
} else if r.TLS != nil {
|
||||
return "https"
|
||||
}
|
||||
|
Reference in New Issue
Block a user