2019-08-13 23:36:03 +00:00
|
|
|
package schema
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
2020-02-08 20:03:57 +00:00
|
|
|
"time"
|
2019-08-13 23:36:03 +00:00
|
|
|
|
2020-06-12 04:50:59 +00:00
|
|
|
"github.com/rancher/apiserver/pkg/builtin"
|
|
|
|
"github.com/rancher/apiserver/pkg/types"
|
2019-09-11 21:05:00 +00:00
|
|
|
"github.com/rancher/steve/pkg/accesscontrol"
|
|
|
|
"github.com/rancher/steve/pkg/attributes"
|
2019-08-13 23:36:03 +00:00
|
|
|
"k8s.io/apiserver/pkg/authentication/user"
|
|
|
|
)
|
|
|
|
|
2020-01-31 05:37:59 +00:00
|
|
|
func newSchemas() (*types.APISchemas, error) {
|
|
|
|
apiSchemas := types.EmptyAPISchemas()
|
|
|
|
if err := apiSchemas.AddSchemas(builtin.Schemas); err != nil {
|
2019-08-13 23:36:03 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-31 05:37:59 +00:00
|
|
|
return apiSchemas, nil
|
2019-08-13 23:36:03 +00:00
|
|
|
}
|
|
|
|
|
2020-01-31 05:37:59 +00:00
|
|
|
func (c *Collection) Schemas(user user.Info) (*types.APISchemas, error) {
|
2019-08-13 23:36:03 +00:00
|
|
|
access := c.as.AccessFor(user)
|
2022-08-26 14:27:57 +00:00
|
|
|
c.removeOldRecords(access, user)
|
2020-02-08 20:03:57 +00:00
|
|
|
val, ok := c.cache.Get(access.ID)
|
|
|
|
if ok {
|
|
|
|
schemas, _ := val.(*types.APISchemas)
|
|
|
|
return schemas, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
schemas, err := c.schemasForSubject(access)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-08-26 14:27:57 +00:00
|
|
|
c.addToCache(access, user, schemas)
|
|
|
|
return schemas, nil
|
|
|
|
}
|
2020-02-08 20:03:57 +00:00
|
|
|
|
2022-08-26 14:27:57 +00:00
|
|
|
func (c *Collection) removeOldRecords(access *accesscontrol.AccessSet, user user.Info) {
|
|
|
|
current, ok := c.userCache.Get(user.GetName())
|
|
|
|
if ok {
|
2022-10-14 20:21:17 +00:00
|
|
|
currentID, cOk := current.(string)
|
|
|
|
if cOk && currentID != access.ID {
|
2022-08-26 14:27:57 +00:00
|
|
|
// we only want to keep around one record per user. If our current access record is invalid, purge the
|
|
|
|
//record of it from the cache, so we don't keep duplicates
|
2022-10-14 20:21:17 +00:00
|
|
|
c.purgeUserRecords(currentID)
|
2022-08-26 14:27:57 +00:00
|
|
|
c.userCache.Remove(user.GetName())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Collection) addToCache(access *accesscontrol.AccessSet, user user.Info, schemas *types.APISchemas) {
|
2020-02-08 20:03:57 +00:00
|
|
|
c.cache.Add(access.ID, schemas, 24*time.Hour)
|
2022-08-26 14:27:57 +00:00
|
|
|
c.userCache.Add(user.GetName(), access.ID, 24*time.Hour)
|
|
|
|
}
|
|
|
|
|
|
|
|
// PurgeUserRecords removes a record from the backing LRU cache before expiry
|
|
|
|
func (c *Collection) purgeUserRecords(id string) {
|
|
|
|
c.cache.Remove(id)
|
|
|
|
c.as.PurgeUserData(id)
|
2019-08-13 23:36:03 +00:00
|
|
|
}
|
|
|
|
|
2020-01-31 05:37:59 +00:00
|
|
|
func (c *Collection) schemasForSubject(access *accesscontrol.AccessSet) (*types.APISchemas, error) {
|
2020-02-08 20:03:57 +00:00
|
|
|
c.lock.RLock()
|
|
|
|
defer c.lock.RUnlock()
|
|
|
|
|
2019-08-13 23:36:03 +00:00
|
|
|
result, err := newSchemas()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-01-31 05:37:59 +00:00
|
|
|
if err := result.AddSchemas(c.baseSchema); err != nil {
|
2019-08-13 23:36:03 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, s := range c.schemas {
|
|
|
|
gr := attributes.GR(s)
|
|
|
|
|
|
|
|
if gr.Resource == "" {
|
|
|
|
if err := result.AddSchema(*s); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
verbs := attributes.Verbs(s)
|
2020-01-31 05:37:59 +00:00
|
|
|
verbAccess := accesscontrol.AccessListByVerb{}
|
2019-08-13 23:36:03 +00:00
|
|
|
|
|
|
|
for _, verb := range verbs {
|
|
|
|
a := access.AccessListFor(verb, gr)
|
2020-02-11 03:53:45 +00:00
|
|
|
if !attributes.Namespaced(s) {
|
|
|
|
// trim out bad data where we are granted namespaced access to cluster scoped object
|
|
|
|
result := accesscontrol.AccessList{}
|
|
|
|
for _, access := range a {
|
|
|
|
if access.Namespace == accesscontrol.All {
|
|
|
|
result = append(result, access)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
a = result
|
|
|
|
}
|
2019-08-13 23:36:03 +00:00
|
|
|
if len(a) > 0 {
|
|
|
|
verbAccess[verb] = a
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(verbAccess) == 0 {
|
2020-02-10 17:18:20 +00:00
|
|
|
if gr.Group == "" && gr.Resource == "namespaces" {
|
|
|
|
var accessList accesscontrol.AccessList
|
|
|
|
for _, ns := range access.Namespaces() {
|
|
|
|
accessList = append(accessList, accesscontrol.Access{
|
2020-02-11 03:54:33 +00:00
|
|
|
Namespace: accesscontrol.All,
|
2020-02-10 17:18:20 +00:00
|
|
|
ResourceName: ns,
|
|
|
|
})
|
|
|
|
}
|
2020-09-19 00:19:14 +00:00
|
|
|
verbAccess["get"] = accessList
|
2020-02-10 17:18:20 +00:00
|
|
|
verbAccess["watch"] = accessList
|
2020-09-19 00:19:14 +00:00
|
|
|
if len(accessList) == 0 {
|
|
|
|
// always allow list
|
|
|
|
s.CollectionMethods = append(s.CollectionMethods, http.MethodGet)
|
|
|
|
}
|
2020-02-10 17:18:20 +00:00
|
|
|
}
|
2019-08-13 23:36:03 +00:00
|
|
|
}
|
|
|
|
|
2021-08-13 18:02:38 +00:00
|
|
|
allowed := func(method string) string {
|
|
|
|
if attributes.DisallowMethods(s)[method] {
|
|
|
|
return "blocked-" + method
|
|
|
|
}
|
|
|
|
return method
|
|
|
|
}
|
|
|
|
|
2019-08-13 23:36:03 +00:00
|
|
|
s = s.DeepCopy()
|
|
|
|
attributes.SetAccess(s, verbAccess)
|
|
|
|
if verbAccess.AnyVerb("list", "get") {
|
2021-08-13 18:02:38 +00:00
|
|
|
s.ResourceMethods = append(s.ResourceMethods, allowed(http.MethodGet))
|
|
|
|
s.CollectionMethods = append(s.CollectionMethods, allowed(http.MethodGet))
|
2019-08-13 23:36:03 +00:00
|
|
|
}
|
|
|
|
if verbAccess.AnyVerb("delete") {
|
2021-08-13 18:02:38 +00:00
|
|
|
s.ResourceMethods = append(s.ResourceMethods, allowed(http.MethodDelete))
|
2019-08-13 23:36:03 +00:00
|
|
|
}
|
|
|
|
if verbAccess.AnyVerb("update") {
|
2021-08-13 18:02:38 +00:00
|
|
|
s.ResourceMethods = append(s.ResourceMethods, allowed(http.MethodPut))
|
|
|
|
s.ResourceMethods = append(s.ResourceMethods, allowed(http.MethodPatch))
|
2019-08-13 23:36:03 +00:00
|
|
|
}
|
|
|
|
if verbAccess.AnyVerb("create") {
|
2021-08-13 18:02:38 +00:00
|
|
|
s.CollectionMethods = append(s.CollectionMethods, allowed(http.MethodPost))
|
2019-08-13 23:36:03 +00:00
|
|
|
}
|
|
|
|
|
2020-10-23 00:03:41 +00:00
|
|
|
if len(s.CollectionMethods) == 0 && len(s.ResourceMethods) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-08-13 23:36:03 +00:00
|
|
|
if err := result.AddSchema(*s); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-19 05:34:46 +00:00
|
|
|
result.Attributes = map[string]interface{}{
|
|
|
|
"accessSet": access,
|
|
|
|
}
|
2019-08-13 23:36:03 +00:00
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
2021-05-19 05:34:46 +00:00
|
|
|
func (c *Collection) defaultStore() types.Store {
|
|
|
|
templates := c.templates[""]
|
|
|
|
if len(templates) > 0 {
|
|
|
|
return templates[0].Store
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-02-08 20:03:57 +00:00
|
|
|
func (c *Collection) applyTemplates(schema *types.APISchema) {
|
|
|
|
c.lock.RLock()
|
|
|
|
defer c.lock.RUnlock()
|
|
|
|
|
2021-03-02 14:30:00 +00:00
|
|
|
templates := [][]*Template{
|
2019-08-13 23:36:03 +00:00
|
|
|
c.templates[schema.ID],
|
|
|
|
c.templates[fmt.Sprintf("%s/%s", attributes.Group(schema), attributes.Kind(schema))],
|
|
|
|
c.templates[""],
|
|
|
|
}
|
|
|
|
|
2021-03-02 14:30:00 +00:00
|
|
|
for _, templates := range templates {
|
|
|
|
for _, t := range templates {
|
|
|
|
if t == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if schema.Formatter == nil {
|
|
|
|
schema.Formatter = t.Formatter
|
|
|
|
} else if t.Formatter != nil {
|
|
|
|
schema.Formatter = types.FormatterChain(t.Formatter, schema.Formatter)
|
|
|
|
}
|
|
|
|
if schema.Store == nil {
|
|
|
|
if t.StoreFactory == nil {
|
|
|
|
schema.Store = t.Store
|
|
|
|
} else {
|
2021-05-19 05:34:46 +00:00
|
|
|
schema.Store = t.StoreFactory(c.defaultStore())
|
2021-03-02 14:30:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if t.Customize != nil {
|
|
|
|
t.Customize(schema)
|
2020-01-31 05:01:21 +00:00
|
|
|
}
|
2019-08-14 18:08:34 +00:00
|
|
|
}
|
2019-08-13 23:36:03 +00:00
|
|
|
}
|
|
|
|
}
|