add support for authorizing subresources

This commit is contained in:
deads2k
2015-04-10 12:42:52 -04:00
parent 8510fc67ff
commit 72817a0801
2 changed files with 68 additions and 61 deletions

View File

@@ -19,7 +19,6 @@ package apiserver
import (
"fmt"
"net/http"
"path"
"regexp"
"runtime/debug"
"strings"
@@ -43,6 +42,11 @@ var specialVerbs = map[string]bool{
"watch": true,
}
// namespaceSubresouces is a set of all the subresources available on a namespace resource. This is a special case because
// URLs look like api/v1beta3/namspaces/<namespace name>/[subresource | resource]. We need to be able to distinguish the two
// different cases.
var namespaceSubresources = util.NewStringSet("status", "finalize")
// Constant for the retry-after interval on rate limiting.
// TODO: maybe make this dynamic? or user-adjustable?
const RetryAfter = "1"
@@ -240,6 +244,8 @@ type APIRequestInfo struct {
Namespace string
// Resource is the name of the resource being requested. This is not the kind. For example: pods
Resource string
// Subresource is the name of the subresource being requested. This is not the kind or the resource. For example: status for a pods/pod-name/status
Subresource string
// Kind is the type of object being manipulated. For example: Pod
Kind string
// Name is empty for some verbs, but if the request directly indicates a name (not in body content) then this field is filled in.
@@ -251,17 +257,6 @@ type APIRequestInfo struct {
Raw []string
}
// URLPath returns the URL path for this request, including /{resource}/{name} if present but nothing
// following that.
func (info APIRequestInfo) URLPath() string {
p := info.Parts
if n := len(p); n > 2 {
// Only take resource and name
p = p[:2]
}
return path.Join("/", path.Join(info.Raw...), path.Join(p...))
}
type APIRequestInfoResolver struct {
APIPrefixes util.StringSet
RestMapper meta.RESTMapper
@@ -340,22 +335,20 @@ func (r *APIRequestInfoResolver) GetAPIRequestInfo(req *http.Request) (APIReques
// URL forms: /namespaces/{namespace}/{kind}/*, where parts are adjusted to be relative to kind
if currentParts[0] == "namespaces" {
if len(currentParts) < 3 {
requestInfo.Resource = "namespaces"
if len(currentParts) > 1 {
requestInfo.Namespace = currentParts[1]
}
} else {
requestInfo.Resource = currentParts[2]
if len(currentParts) > 1 {
requestInfo.Namespace = currentParts[1]
currentParts = currentParts[2:]
// if there is another step after the namespace name and it is not a known namespace subresource
// move currentParts to include it as a resource in its own right
if len(currentParts) > 2 && !namespaceSubresources.Has(currentParts[2]) {
currentParts = currentParts[2:]
}
}
} else {
// URL forms: /{resource}/*
// URL forms: POST /{resource} is a legacy API convention to create in "default" namespace
// URL forms: /{resource}/{resourceName} use the "default" namespace if omitted from query param
// URL forms: /{resource} assume cross-namespace operation if omitted from query param
requestInfo.Resource = currentParts[0]
requestInfo.Namespace = req.URL.Query().Get("namespace")
if len(requestInfo.Namespace) == 0 {
if len(currentParts) > 1 || req.Method == "POST" {
@@ -371,9 +364,16 @@ func (r *APIRequestInfoResolver) GetAPIRequestInfo(req *http.Request) (APIReques
// Raw should have everything not in Parts
requestInfo.Raw = requestInfo.Raw[:len(requestInfo.Raw)-len(currentParts)]
// if there's another part remaining after the kind, then that's the resource name
if len(requestInfo.Parts) >= 2 {
// parts look like: resource/resourceName/subresource/other/stuff/we/don't/interpret
switch {
case len(requestInfo.Parts) >= 3:
requestInfo.Subresource = requestInfo.Parts[2]
fallthrough
case len(requestInfo.Parts) == 2:
requestInfo.Name = requestInfo.Parts[1]
fallthrough
case len(requestInfo.Parts) == 1:
requestInfo.Resource = requestInfo.Parts[0]
}
// if there's no name on the request and we thought it was a get before, then the actual verb is a list