diff --git a/api/server.go b/api/server.go index ef7a2fee..4f1a6790 100644 --- a/api/server.go +++ b/api/server.go @@ -195,12 +195,12 @@ func (s *Server) handle(rw http.ResponseWriter, req *http.Request) (*types.APICo } handler = apiRequest.Schema.CreateHandler case http.MethodPut: - if !apiRequest.AccessControl.CanUpdate(apiRequest, apiRequest.Schema) { + if !apiRequest.AccessControl.CanUpdate(apiRequest, nil, apiRequest.Schema) { return apiRequest, httperror.NewAPIError(httperror.PermissionDenied, "Can not update "+apiRequest.Schema.Type) } handler = apiRequest.Schema.UpdateHandler case http.MethodDelete: - if !apiRequest.AccessControl.CanDelete(apiRequest, apiRequest.Schema) { + if !apiRequest.AccessControl.CanDelete(apiRequest, nil, apiRequest.Schema) { return apiRequest, httperror.NewAPIError(httperror.PermissionDenied, "Can not delete "+apiRequest.Schema.Type) } handler = apiRequest.Schema.DeleteHandler diff --git a/api/writer/json.go b/api/writer/json.go index da867602..5ee34bab 100644 --- a/api/writer/json.go +++ b/api/writer/json.go @@ -127,15 +127,15 @@ func (j *JSONResponseWriter) addLinks(b *builder.Builder, schema *types.Schema, self := context.URLBuilder.ResourceLink(rawResource) rawResource.Links["self"] = self - if schema.CanUpdate() { + if context.AccessControl.CanUpdate(context, input, schema) { rawResource.Links["update"] = self } - if schema.CanDelete() { + if context.AccessControl.CanDelete(context, input, schema) { rawResource.Links["remove"] = self } for _, backRef := range context.Schemas.References(schema) { - if !backRef.Schema.CanList() { + if !backRef.Schema.CanList(context) { continue } diff --git a/authorization/all.go b/authorization/all.go index 1fa09934..2e6e4d48 100644 --- a/authorization/all.go +++ b/authorization/all.go @@ -18,11 +18,11 @@ func (*AllAccess) CanList(apiContext *types.APIContext, schema *types.Schema) bo return slice.ContainsString(schema.CollectionMethods, http.MethodGet) } -func (*AllAccess) CanUpdate(apiContext *types.APIContext, schema *types.Schema) bool { +func (*AllAccess) CanUpdate(apiContext *types.APIContext, obj map[string]interface{}, schema *types.Schema) bool { return slice.ContainsString(schema.ResourceMethods, http.MethodPut) } -func (*AllAccess) CanDelete(apiContext *types.APIContext, schema *types.Schema) bool { +func (*AllAccess) CanDelete(apiContext *types.APIContext, obj map[string]interface{}, schema *types.Schema) bool { return slice.ContainsString(schema.ResourceMethods, http.MethodDelete) } diff --git a/store/crd/crd_store.go b/store/crd/crd_store.go index f00fcaf7..91da62f3 100644 --- a/store/crd/crd_store.go +++ b/store/crd/crd_store.go @@ -105,7 +105,7 @@ func (c *Store) AddSchemas(ctx context.Context, schemas ...*types.Schema) error var allSchemas []*types.Schema for _, schema := range schemas { - if schema.Store != nil || !schema.CanList() { + if schema.Store != nil || !schema.CanList(nil) { continue } diff --git a/store/schema/schema_store.go b/store/schema/schema_store.go index 6b27616c..814daaad 100644 --- a/store/schema/schema_store.go +++ b/store/schema/schema_store.go @@ -2,12 +2,13 @@ package schema import ( "encoding/json" - + "net/http" "strings" "github.com/rancher/norman/store/empty" "github.com/rancher/norman/types" "github.com/rancher/norman/types/definition" + "github.com/rancher/norman/types/slice" ) type Store struct { @@ -23,7 +24,7 @@ func (s *Store) ByID(apiContext *types.APIContext, schema *types.Schema, id stri if strings.EqualFold(schema.ID, id) { schemaData := map[string]interface{}{} - data, err := json.Marshal(schema) + data, err := json.Marshal(s.modifyForAccessControl(apiContext, *schema)) if err != nil { return nil, err } @@ -34,6 +35,29 @@ func (s *Store) ByID(apiContext *types.APIContext, schema *types.Schema, id stri return nil, nil } +func (s *Store) modifyForAccessControl(context *types.APIContext, schema types.Schema) *types.Schema { + var resourceMethods []string + if slice.ContainsString(schema.ResourceMethods, http.MethodPut) && schema.CanUpdate(context) { + resourceMethods = append(resourceMethods, http.MethodPut) + } + if slice.ContainsString(schema.ResourceMethods, http.MethodDelete) && schema.CanDelete(context) { + resourceMethods = append(resourceMethods, http.MethodDelete) + } + + var collectionMethods []string + if slice.ContainsString(schema.CollectionMethods, http.MethodPost) && schema.CanCreate(context) { + collectionMethods = append(collectionMethods, http.MethodPost) + } + if slice.ContainsString(schema.CollectionMethods, http.MethodGet) && schema.CanList(context) { + collectionMethods = append(collectionMethods, http.MethodGet) + } + + schema.ResourceMethods = resourceMethods + schema.CollectionMethods = collectionMethods + + return &schema +} + func (s *Store) Watch(apiContext *types.APIContext, schema *types.Schema, opt *types.QueryOptions) (chan map[string]interface{}, error) { return nil, nil } @@ -50,8 +74,8 @@ func (s *Store) List(apiContext *types.APIContext, schema *types.Schema, opt *ty continue } - if schema.CanList() { - schemas = addSchema(schema, schemaMap, schemas, included) + if schema.CanList(apiContext) { + schemas = s.addSchema(apiContext, schema, schemaMap, schemas, included) } } @@ -63,14 +87,14 @@ func (s *Store) List(apiContext *types.APIContext, schema *types.Schema, opt *ty return schemaData, json.Unmarshal(data, &schemaData) } -func addSchema(schema *types.Schema, schemaMap map[string]*types.Schema, schemas []*types.Schema, included map[string]bool) []*types.Schema { +func (s *Store) addSchema(apiContext *types.APIContext, schema *types.Schema, schemaMap map[string]*types.Schema, schemas []*types.Schema, included map[string]bool) []*types.Schema { included[schema.ID] = true - schemas = traverseAndAdd(schema, schemaMap, schemas, included) - schemas = append(schemas, schema) + schemas = s.traverseAndAdd(apiContext, schema, schemaMap, schemas, included) + schemas = append(schemas, s.modifyForAccessControl(apiContext, *schema)) return schemas } -func traverseAndAdd(schema *types.Schema, schemaMap map[string]*types.Schema, schemas []*types.Schema, included map[string]bool) []*types.Schema { +func (s *Store) traverseAndAdd(apiContext *types.APIContext, schema *types.Schema, schemaMap map[string]*types.Schema, schemas []*types.Schema, included map[string]bool) []*types.Schema { for _, field := range schema.ResourceFields { t := "" subType := field.Type @@ -80,7 +104,7 @@ func traverseAndAdd(schema *types.Schema, schemaMap map[string]*types.Schema, sc } if refSchema, ok := schemaMap[t]; ok && !included[t] { - schemas = addSchema(refSchema, schemaMap, schemas, included) + schemas = s.addSchema(apiContext, refSchema, schemaMap, schemas, included) } } @@ -91,7 +115,7 @@ func traverseAndAdd(schema *types.Schema, schemaMap map[string]*types.Schema, sc } if refSchema, ok := schemaMap[t]; ok && !included[t] { - schemas = addSchema(refSchema, schemaMap, schemas, included) + schemas = s.addSchema(apiContext, refSchema, schemaMap, schemas, included) } } } diff --git a/types/reflection.go b/types/reflection.go index 550b7692..a909be29 100644 --- a/types/reflection.go +++ b/types/reflection.go @@ -155,7 +155,7 @@ func (s *Schemas) importType(version *APIVersion, t reflect.Type, overrides ...r mappers := s.mapper(&schema.Version, schema.ID) if s.DefaultMappers != nil { - if schema.CanList() { + if schema.CanList(nil) { mappers = append(s.DefaultMappers(), mappers...) } } @@ -179,7 +179,7 @@ func (s *Schemas) importType(version *APIVersion, t reflect.Type, overrides ...r mapper := &typeMapper{ Mappers: mappers, - root: schema.CanList(), + root: schema.CanList(nil), } if err := mapper.ModifySchema(schema, s); err != nil { diff --git a/types/schema_funcs.go b/types/schema_funcs.go index 837e0ee9..130f0e98 100644 --- a/types/schema_funcs.go +++ b/types/schema_funcs.go @@ -21,18 +21,30 @@ func (v *APIVersion) Equals(other *APIVersion) bool { v.Path == other.Path } -func (s *Schema) CanList() bool { - return slice.ContainsString(s.CollectionMethods, http.MethodGet) +func (s *Schema) CanList(context *APIContext) bool { + if context == nil { + return slice.ContainsString(s.CollectionMethods, http.MethodGet) + } + return context.AccessControl.CanList(context, s) } -func (s *Schema) CanCreate() bool { - return slice.ContainsString(s.CollectionMethods, http.MethodPost) +func (s *Schema) CanCreate(context *APIContext) bool { + if context == nil { + return slice.ContainsString(s.CollectionMethods, http.MethodPost) + } + return context.AccessControl.CanCreate(context, s) } -func (s *Schema) CanUpdate() bool { - return slice.ContainsString(s.ResourceMethods, http.MethodPut) +func (s *Schema) CanUpdate(context *APIContext) bool { + if context == nil { + return slice.ContainsString(s.ResourceMethods, http.MethodPut) + } + return context.AccessControl.CanUpdate(context, nil, s) } -func (s *Schema) CanDelete() bool { - return slice.ContainsString(s.ResourceMethods, http.MethodDelete) +func (s *Schema) CanDelete(context *APIContext) bool { + if context == nil { + return slice.ContainsString(s.ResourceMethods, http.MethodDelete) + } + return context.AccessControl.CanDelete(context, nil, s) } diff --git a/types/server_types.go b/types/server_types.go index 66f27451..b10fcbed 100644 --- a/types/server_types.go +++ b/types/server_types.go @@ -68,8 +68,8 @@ type ResponseWriter interface { type AccessControl interface { CanCreate(apiContext *APIContext, schema *Schema) bool CanList(apiContext *APIContext, schema *Schema) bool - CanUpdate(apiContext *APIContext, schema *Schema) bool - CanDelete(apiContext *APIContext, schema *Schema) bool + CanUpdate(apiContext *APIContext, obj map[string]interface{}, schema *Schema) bool + CanDelete(apiContext *APIContext, obj map[string]interface{}, schema *Schema) bool Filter(apiContext *APIContext, obj map[string]interface{}, context map[string]string) map[string]interface{} FilterList(apiContext *APIContext, obj []map[string]interface{}, context map[string]string) []map[string]interface{}